1 /* Copyright 2002-2025 CS GROUP
2 * Licensed to CS GROUP (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.orekit.frames;
18
19 import org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.Field;
21 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
22 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23 import org.hipparchus.geometry.euclidean.threed.Rotation;
24 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.orekit.errors.OrekitException;
27 import org.orekit.errors.OrekitMessages;
28 import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.FieldAbsoluteDate;
31 import org.orekit.utils.FieldPVCoordinates;
32 import org.orekit.utils.PVCoordinates;
33
34 /**
35 * Enumerate for different types of Local Orbital Frames.
36 *
37 * @author Luc Maisonobe
38 * @author Maxime Journot
39 * @author Vincent Cucchietti
40 */
41 public enum LOFType implements LOF {
42
43 /** Constant for TNW frame
44 * (X axis aligned with velocity, Z axis aligned with orbital momentum).
45 * <p>
46 * The axes of this frame are parallel to the axes of the {@link #VNC}
47 * and {@link #NTW} frames:
48 * </p>
49 * <ul>
50 * <li>X<sub>TNW</sub> = X<sub>VNC</sub> = Y<sub>NTW</sub></li>
51 * <li>Y<sub>TNW</sub> = -Z<sub>VNC</sub> = -X<sub>NTW</sub></li>
52 * <li>Z<sub>TNW</sub> = Y<sub>VNC</sub> = Z<sub>NTW</sub></li>
53 * </ul>
54 *
55 * @see #VNC
56 * @see #NTW
57 */
58 TNW {
59 /** {@inheritDoc} */
60 @Override
61 public Rotation rotationFromInertial(final PVCoordinates pv) {
62 return new Rotation(pv.getVelocity(), pv.getMomentum(),
63 Vector3D.PLUS_I, Vector3D.PLUS_K);
64 }
65
66 /** {@inheritDoc} */
67 @Override
68 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
69 final FieldPVCoordinates<T> pv) {
70 return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(),
71 new FieldVector3D<>(field, Vector3D.PLUS_I),
72 new FieldVector3D<>(field, Vector3D.PLUS_K));
73 }
74
75 /** {@inheritDoc} */
76 @Override
77 public OrbitRelativeFrame toOrbitRelativeFrame() {
78 return OrbitRelativeFrame.TNW_ROTATING;
79 }
80
81 },
82
83 /**
84 * Constant for TNW frame considered inertial (X axis aligned with velocity, Z axis aligned with orbital momentum).
85 * <p>
86 * The axes of this frame are parallel to the axes of the {@link #VNC} and {@link #NTW} frames:
87 * </p>
88 * <ul>
89 * <li>X<sub>TNW</sub> = X<sub>VNC</sub> = Y<sub>NTW</sub></li>
90 * <li>Y<sub>TNW</sub> = -Z<sub>VNC</sub> = -X<sub>NTW</sub></li>
91 * <li>Z<sub>TNW</sub> = Y<sub>VNC</sub> = Z<sub>NTW</sub></li>
92 * </ul>
93 *
94 * @see #VNC
95 * @see #NTW
96 */
97 TNW_INERTIAL {
98 /** {@inheritDoc} */
99 @Override
100 public Rotation rotationFromInertial(final PVCoordinates pv) {
101 return TNW.rotationFromInertial(pv);
102 }
103
104 /** {@inheritDoc} */
105 @Override
106 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
107 final FieldPVCoordinates<T> pv) {
108 return TNW.rotationFromInertial(field, pv);
109 }
110
111 /** {@inheritDoc} */
112 @Override
113 public boolean isQuasiInertial() {
114 return true;
115 }
116
117 /** {@inheritDoc} */
118 @Override
119 public OrbitRelativeFrame toOrbitRelativeFrame() {
120 return OrbitRelativeFrame.TNW_INERTIAL;
121 }
122
123 },
124
125 /** Constant for QSW frame
126 * (X axis aligned with position, Z axis aligned with orbital momentum).
127 * <p>
128 * This frame is also known as the {@link #LVLH} frame, both constants are equivalent.
129 * </p>
130 * <p>
131 * The axes of these frames are parallel to the axes of the {@link #VVLH} frame:
132 * </p>
133 * <ul>
134 * <li>X<sub>QSW/LVLH</sub> = -Z<sub>VVLH</sub></li>
135 * <li>Y<sub>QSW/LVLH</sub> = X<sub>VVLH</sub></li>
136 * <li>Z<sub>QSW/LVLH</sub> = -Y<sub>VVLH</sub></li>
137 * </ul>
138 *
139 * @see #LVLH
140 * @see #VVLH
141 */
142 QSW {
143 /** {@inheritDoc} */
144 @Override
145 public Rotation rotationFromInertial(final PVCoordinates pv) {
146 return new Rotation(pv.getPosition(), pv.getMomentum(),
147 Vector3D.PLUS_I, Vector3D.PLUS_K);
148 }
149
150 /** {@inheritDoc} */
151 @Override
152 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
153 final FieldPVCoordinates<T> pv) {
154 return new FieldRotation<>(pv.getPosition(), pv.getMomentum(),
155 new FieldVector3D<>(field, Vector3D.PLUS_I),
156 new FieldVector3D<>(field, Vector3D.PLUS_K));
157 }
158
159 /** {@inheritDoc} */
160 @Override
161 public OrbitRelativeFrame toOrbitRelativeFrame() {
162 return OrbitRelativeFrame.RSW_ROTATING;
163 }
164
165 },
166
167 /**
168 * Constant for QSW frame considered inertial (X axis aligned with position, Z axis aligned with orbital momentum).
169 * <p>
170 * This frame is also known as the {@link #LVLH} frame, both constants are equivalent.
171 * </p>
172 * <p>
173 * The axes of these frames are parallel to the axes of the {@link #VVLH} frame:
174 * </p>
175 * <ul>
176 * <li>X<sub>QSW/LVLH</sub> = -Z<sub>VVLH</sub></li>
177 * <li>Y<sub>QSW/LVLH</sub> = X<sub>VVLH</sub></li>
178 * <li>Z<sub>QSW/LVLH</sub> = -Y<sub>VVLH</sub></li>
179 * </ul>
180 *
181 * @see #LVLH
182 * @see #VVLH
183 */
184 QSW_INERTIAL {
185 /** {@inheritDoc} */
186 @Override
187 public Rotation rotationFromInertial(final PVCoordinates pv) {
188 return QSW.rotationFromInertial(pv);
189 }
190
191 /** {@inheritDoc} */
192 @Override
193 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
194 final FieldPVCoordinates<T> pv) {
195 return QSW.rotationFromInertial(field, pv);
196 }
197
198 /** {@inheritDoc} */
199 @Override
200 public boolean isQuasiInertial() {
201 return true;
202 }
203
204 /** {@inheritDoc} */
205 @Override
206 public OrbitRelativeFrame toOrbitRelativeFrame() {
207 return OrbitRelativeFrame.RSW_INERTIAL;
208 }
209
210 },
211
212 /** Constant for Local Vertical, Local Horizontal frame
213 * (X axis aligned with position, Z axis aligned with orbital momentum).
214 * <p>
215 * BEWARE! Depending on the background (software used, textbook, community),
216 * different incompatible definitions for LVLH are used. This one is consistent
217 * with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and
218 * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}).
219 * </p>
220 * <p>
221 * This frame is also known as the {@link #QSW} frame, both constants are equivalent.
222 * </p>
223 * <p>
224 * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame:
225 * </p>
226 * <ul>
227 * <li>X<sub>LVLH/QSW</sub> = -Z<sub>LVLH_CCSDS</sub></li>
228 * <li>Y<sub>LVLH/QSW</sub> = X<sub>LVLH_CCSDS</sub></li>
229 * <li>Z<sub>LVLH/QSW</sub> = -Y<sub>LVLH_CCSDS</sub></li>
230 * </ul>
231 *
232 * @see #QSW
233 * @see #VVLH
234 */
235 LVLH {
236 /** {@inheritDoc} */
237 @Override
238 public Rotation rotationFromInertial(final PVCoordinates pv) {
239 return new Rotation(pv.getPosition(), pv.getMomentum(),
240 Vector3D.PLUS_I, Vector3D.PLUS_K);
241 }
242
243 /** {@inheritDoc} */
244 @Override
245 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
246 final FieldPVCoordinates<T> pv) {
247 return new FieldRotation<>(pv.getPosition(), pv.getMomentum(),
248 new FieldVector3D<>(field, Vector3D.PLUS_I),
249 new FieldVector3D<>(field, Vector3D.PLUS_K));
250 }
251
252 /** {@inheritDoc} */
253 @Override
254 public OrbitRelativeFrame toOrbitRelativeFrame() {
255 throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION);
256 }
257
258 },
259
260 /**
261 * Constant for Local Vertical, Local Horizontal frame considered inertial (X axis aligned with position, Z axis
262 * aligned with orbital momentum).
263 * <p>
264 * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for
265 * LVLH are used. This one is consistent with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and
266 * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}).
267 * </p>
268 * <p>
269 * This frame is also known as the {@link #QSW} frame, both constants are equivalent.
270 * </p>
271 * <p>
272 * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame:
273 * </p>
274 * <ul>
275 * <li>X<sub>LVLH/QSW</sub> = -Z<sub>LVLH_CCSDS</sub></li>
276 * <li>Y<sub>LVLH/QSW</sub> = X<sub>LVLH_CCSDS</sub></li>
277 * <li>Z<sub>LVLH/QSW</sub> = -Y<sub>LVLH_CCSDS</sub></li>
278 * </ul>
279 *
280 * @see #QSW
281 * @see #VVLH
282 */
283 LVLH_INERTIAL {
284 /** {@inheritDoc} */
285 @Override
286 public Rotation rotationFromInertial(final PVCoordinates pv) {
287 return LVLH.rotationFromInertial(pv);
288 }
289
290 /** {@inheritDoc} */
291 @Override
292 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
293 final FieldPVCoordinates<T> pv) {
294 return LVLH.rotationFromInertial(field, pv);
295 }
296
297 /** {@inheritDoc} */
298 @Override
299 public boolean isQuasiInertial() {
300 return true;
301 }
302
303 /** {@inheritDoc} */
304 @Override
305 public OrbitRelativeFrame toOrbitRelativeFrame() {
306 throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION);
307 }
308
309 },
310
311 /** Constant for Local Vertical, Local Horizontal frame as defined by CCSDS
312 * (Z axis aligned with opposite of position, Y axis aligned with opposite of orbital momentum).
313 * <p>
314 * BEWARE! Depending on the background (software used, textbook, community),
315 * different incompatible definitions for LVLH are used. This one is consistent
316 * with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However Vallado's
317 * book and with AGI's STK use another definition (see {@link #LVLH}).
318 * </p>
319 * <p>
320 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
321 * </p>
322 * <ul>
323 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li>
324 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
325 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
326 * </ul>
327 *
328 * @see #QSW
329 * @see #LVLH
330 * @since 11.0
331 */
332 LVLH_CCSDS {
333 /** {@inheritDoc} */
334 @Override
335 public Rotation rotationFromInertial(final PVCoordinates pv) {
336 return new Rotation(pv.getPosition(), pv.getMomentum(),
337 Vector3D.MINUS_K, Vector3D.MINUS_J);
338 }
339
340 /** {@inheritDoc} */
341 @Override
342 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
343 final FieldPVCoordinates<T> pv) {
344 return new FieldRotation<>(pv.getPosition(), pv.getMomentum(),
345 new FieldVector3D<>(field, Vector3D.MINUS_K),
346 new FieldVector3D<>(field, Vector3D.MINUS_J));
347 }
348
349 /** {@inheritDoc} */
350 @Override
351 public OrbitRelativeFrame toOrbitRelativeFrame() {
352 return OrbitRelativeFrame.LVLH_ROTATING;
353 }
354
355 },
356
357 /**
358 * Constant for Local Vertical, Local Horizontal frame as defined by CCSDS considered inertial (Z axis aligned with
359 * opposite of position, Y axis aligned with opposite of orbital momentum).
360 * <p>
361 * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for
362 * LVLH are used. This one is consistent with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However
363 * Vallado's book and with AGI's STK use another definition (see {@link #LVLH}).
364 * </p>
365 * <p>
366 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
367 * </p>
368 * <ul>
369 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li>
370 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
371 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
372 * </ul>
373 *
374 * @see #QSW
375 * @see #LVLH
376 * @since 11.0
377 */
378 LVLH_CCSDS_INERTIAL {
379 /** {@inheritDoc} */
380 @Override
381 public Rotation rotationFromInertial(final PVCoordinates pv) {
382 return LVLH_CCSDS.rotationFromInertial(pv);
383 }
384
385 /** {@inheritDoc} */
386 @Override
387 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
388 final FieldPVCoordinates<T> pv) {
389 return LVLH_CCSDS.rotationFromInertial(field, pv);
390 }
391
392 /** {@inheritDoc} */
393 @Override
394 public boolean isQuasiInertial() {
395 return true;
396 }
397
398 /** {@inheritDoc} */
399 @Override
400 public OrbitRelativeFrame toOrbitRelativeFrame() {
401 return OrbitRelativeFrame.LVLH_INERTIAL;
402 }
403
404 },
405
406 /** Constant for Vehicle Velocity, Local Horizontal frame
407 * (Z axis aligned with opposite of position, Y axis aligned with opposite of orbital momentum).
408 * <p>
409 * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK.
410 * </p>
411 * <p>
412 * Beware that the name is misleading: in the general case (i.e. not perfectly circular),
413 * none of the axes is perfectly aligned with velocity! The preferred name for this
414 * should be {@link #LVLH_CCSDS}.
415 * </p>
416 * <p>
417 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
418 * </p>
419 * <ul>
420 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li>
421 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
422 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
423 * </ul>
424 * @see #LVLH_CCSDS
425 */
426 VVLH {
427 /** {@inheritDoc} */
428 @Override
429 public Rotation rotationFromInertial(final PVCoordinates pv) {
430 return LVLH_CCSDS.rotationFromInertial(pv);
431 }
432
433 /** {@inheritDoc} */
434 @Override
435 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
436 final FieldPVCoordinates<T> pv) {
437 return LVLH_CCSDS.rotationFromInertial(field, pv);
438 }
439
440 /** {@inheritDoc} */
441 @Override
442 public OrbitRelativeFrame toOrbitRelativeFrame() {
443 return OrbitRelativeFrame.LVLH_ROTATING;
444 }
445
446 },
447
448 /**
449 * Constant for Vehicle Velocity, Local Horizontal frame considered inertial (Z axis aligned with opposite of
450 * position, Y axis aligned with opposite of orbital momentum).
451 * <p>
452 * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK.
453 * </p>
454 * <p>
455 * Beware that the name is misleading: in the general case (i.e. not perfectly circular), none of the axes is
456 * perfectly aligned with velocity! The preferred name for this should be {@link #LVLH_CCSDS}.
457 * </p>
458 * <p>
459 * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
460 * </p>
461 * <ul>
462 * <li>X<sub>LVLH_CCSDS/VVLH</sub> = Y<sub>QSW/LVLH</sub></li>
463 * <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
464 * <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
465 * </ul>
466 *
467 * @see #LVLH_CCSDS
468 */
469 VVLH_INERTIAL {
470 /** {@inheritDoc} */
471 @Override
472 public Rotation rotationFromInertial(final PVCoordinates pv) {
473 return VVLH.rotationFromInertial(pv);
474 }
475
476 /** {@inheritDoc} */
477 @Override
478 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
479 final FieldPVCoordinates<T> pv) {
480 return VVLH.rotationFromInertial(field, pv);
481 }
482
483 /** {@inheritDoc} */
484 @Override
485 public boolean isQuasiInertial() {
486 return true;
487 }
488
489 /** {@inheritDoc} */
490 @Override
491 public OrbitRelativeFrame toOrbitRelativeFrame() {
492 return OrbitRelativeFrame.LVLH_INERTIAL;
493 }
494
495 },
496
497 /** Constant for Velocity - Normal - Co-normal frame
498 * (X axis aligned with velocity, Y axis aligned with orbital momentum).
499 * <p>
500 * The axes of this frame are parallel to the axes of the {@link #TNW}
501 * and {@link #NTW} frames:
502 * </p>
503 * <ul>
504 * <li>X<sub>VNC</sub> = X<sub>TNW</sub> = Y<sub>NTW</sub></li>
505 * <li>Y<sub>VNC</sub> = Z<sub>TNW</sub> = Z<sub>NTW</sub></li>
506 * <li>Z<sub>VNC</sub> = -Y<sub>TNW</sub> = X<sub>NTW</sub></li>
507 * </ul>
508 *
509 * @see #TNW
510 * @see #NTW
511 */
512 VNC {
513 /** {@inheritDoc} */
514 @Override
515 public Rotation rotationFromInertial(final PVCoordinates pv) {
516 return new Rotation(pv.getVelocity(), pv.getMomentum(),
517 Vector3D.PLUS_I, Vector3D.PLUS_J);
518 }
519
520 /** {@inheritDoc} */
521 @Override
522 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
523 final FieldPVCoordinates<T> pv) {
524 return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(),
525 new FieldVector3D<>(field, Vector3D.PLUS_I),
526 new FieldVector3D<>(field, Vector3D.PLUS_J));
527 }
528
529 /** {@inheritDoc} */
530 @Override
531 public OrbitRelativeFrame toOrbitRelativeFrame() {
532 return OrbitRelativeFrame.VNC_ROTATING;
533 }
534
535 },
536
537 /**
538 * Constant for Velocity - Normal - Co-normal frame considered inertial (X axis aligned with velocity, Y axis
539 * aligned with orbital momentum).
540 * <p>
541 * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #NTW} frames:
542 * </p>
543 * <ul>
544 * <li>X<sub>VNC</sub> = X<sub>TNW</sub> = Y<sub>NTW</sub></li>
545 * <li>Y<sub>VNC</sub> = Z<sub>TNW</sub> = Z<sub>NTW</sub></li>
546 * <li>Z<sub>VNC</sub> = -Y<sub>TNW</sub> = X<sub>NTW</sub></li>
547 * </ul>
548 *
549 * @see #TNW
550 * @see #NTW
551 */
552 VNC_INERTIAL {
553 /** {@inheritDoc} */
554 @Override
555 public Rotation rotationFromInertial(final PVCoordinates pv) {
556 return VNC.rotationFromInertial(pv);
557 }
558
559 /** {@inheritDoc} */
560 @Override
561 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
562 final FieldPVCoordinates<T> pv) {
563 return VNC.rotationFromInertial(field, pv);
564 }
565
566 /** {@inheritDoc} */
567 @Override
568 public boolean isQuasiInertial() {
569 return true;
570 }
571
572 /** {@inheritDoc} */
573 @Override
574 public OrbitRelativeFrame toOrbitRelativeFrame() {
575 return OrbitRelativeFrame.VNC_INERTIAL;
576 }
577
578 },
579
580 /**
581 * Constant for Equinoctial Coordinate System (X axis aligned with ascending node, Z axis aligned with orbital
582 * momentum).
583 *
584 * @since 11.0
585 */
586 EQW {
587 /** {@inheritDoc} */
588 @Override
589 public Rotation rotationFromInertial(final PVCoordinates pv) {
590 final Vector3D m = pv.getMomentum();
591 return new Rotation(new Vector3D(-m.getY(), m.getX(), 0), m,
592 Vector3D.PLUS_I, Vector3D.PLUS_K);
593 }
594
595 /** {@inheritDoc} */
596 @Override
597 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
598 final FieldPVCoordinates<T> pv) {
599 final FieldVector3D<T> m = pv.getMomentum();
600 return new FieldRotation<>(new FieldVector3D<>(m.getY().negate(), m.getX(), field.getZero()),
601 m,
602 new FieldVector3D<>(field, Vector3D.PLUS_I),
603 new FieldVector3D<>(field, Vector3D.PLUS_K));
604 }
605
606 /** {@inheritDoc} */
607 @Override
608 public boolean isQuasiInertial() {
609 return true;
610 }
611
612 /** {@inheritDoc} */
613 @Override
614 public OrbitRelativeFrame toOrbitRelativeFrame() {
615 return OrbitRelativeFrame.EQW_INERTIAL;
616 }
617
618 },
619
620 /** Constant for Transverse Velocity Normal coordinate system
621 * (Y axis aligned with velocity, Z axis aligned with orbital momentum).
622 * <p>
623 * The axes of this frame are parallel to the axes of the {@link #TNW}
624 * and {@link #VNC} frames:
625 * </p>
626 * <ul>
627 * <li>X<sub>NTW</sub> = -Y<sub>TNW</sub> = Z<sub>VNC</sub></li>
628 * <li>Y<sub>NTW</sub> = X<sub>TNW</sub> = X<sub>VNC</sub></li>
629 * <li>Z<sub>NTW</sub> = Z<sub>TNW</sub> = Y<sub>VNC</sub></li>
630 * </ul>
631 * @see #TNW
632 * @see #VNC
633 * @since 11.0
634 */
635 NTW {
636 /** {@inheritDoc} */
637 @Override
638 public Rotation rotationFromInertial(final PVCoordinates pv) {
639 return new Rotation(pv.getVelocity(), pv.getMomentum(),
640 Vector3D.PLUS_J, Vector3D.PLUS_K);
641 }
642
643 /** {@inheritDoc} */
644 @Override
645 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
646 final FieldPVCoordinates<T> pv) {
647 return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(),
648 new FieldVector3D<>(field, Vector3D.PLUS_J),
649 new FieldVector3D<>(field, Vector3D.PLUS_K));
650 }
651
652 /** {@inheritDoc} */
653 @Override
654 public OrbitRelativeFrame toOrbitRelativeFrame() {
655 return OrbitRelativeFrame.NTW_ROTATING;
656 }
657
658 },
659
660 /**
661 * Constant for Transverse Velocity Normal coordinate system considered inertial (Y axis aligned with velocity, Z
662 * axis aligned with orbital momentum).
663 * <p>
664 * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #VNC} frames:
665 * </p>
666 * <ul>
667 * <li>X<sub>NTW</sub> = -Y<sub>TNW</sub> = Z<sub>VNC</sub></li>
668 * <li>Y<sub>NTW</sub> = X<sub>TNW</sub> = X<sub>VNC</sub></li>
669 * <li>Z<sub>NTW</sub> = Z<sub>TNW</sub> = Y<sub>VNC</sub></li>
670 * </ul>
671 *
672 * @see #TNW
673 * @see #VNC
674 * @since 11.0
675 */
676 NTW_INERTIAL {
677 /** {@inheritDoc} */
678 @Override
679 public Rotation rotationFromInertial(final PVCoordinates pv) {
680 return NTW.rotationFromInertial(pv);
681 }
682
683 /** {@inheritDoc} */
684 @Override
685 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
686 final FieldPVCoordinates<T> pv) {
687 return NTW.rotationFromInertial(field, pv);
688 }
689
690 /** {@inheritDoc} */
691 @Override
692 public boolean isQuasiInertial() {
693 return true;
694 }
695
696 /** {@inheritDoc} */
697 @Override
698 public OrbitRelativeFrame toOrbitRelativeFrame() {
699 return OrbitRelativeFrame.NTW_INERTIAL;
700 }
701
702 },
703
704 /**
705 * Constant for East-North-Up frame.
706 * (Z aligned with position, North Pole in the (+Y, ±Z) half-plane)
707 * @see #NED
708 * @since 13.0
709 */
710 ENU {
711 /** {@inheritDoc} */
712 @Override
713 public Rotation rotationFromInertial(final PVCoordinates pv) {
714 return new Rotation(pv.getPosition(), east(pv),
715 Vector3D.PLUS_K, Vector3D.PLUS_I);
716 }
717
718 /** {@inheritDoc} */
719 @Override
720 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
721 final FieldPVCoordinates<T> pv) {
722 return new FieldRotation<>(pv.getPosition(), east(pv),
723 FieldVector3D.getPlusK(field), FieldVector3D.getPlusI(field));
724 }
725
726 /** {@inheritDoc} */
727 @Override
728 public boolean isQuasiInertial() {
729 return false;
730 }
731
732 /** {@inheritDoc} */
733 @Override
734 public OrbitRelativeFrame toOrbitRelativeFrame() {
735 return null;
736 }
737
738 },
739
740 /**
741 * Constant for North-East-Down frame.
742 * (Z aligned with opposite of position, North Pole in the (+X, ±Z) half-plane)
743 * @see #ENU
744 * @since 13.0
745 */
746 NED {
747 /** {@inheritDoc} */
748 @Override
749 public Rotation rotationFromInertial(final PVCoordinates pv) {
750 return new Rotation(pv.getPosition(), east(pv),
751 Vector3D.MINUS_K, Vector3D.PLUS_J);
752 }
753
754 /** {@inheritDoc} */
755 @Override
756 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
757 final FieldPVCoordinates<T> pv) {
758 return new FieldRotation<>(pv.getPosition(), east(pv),
759 FieldVector3D.getMinusK(field), FieldVector3D.getPlusJ(field));
760 }
761
762 /** {@inheritDoc} */
763 @Override
764 public boolean isQuasiInertial() {
765 return false;
766 }
767
768 /** {@inheritDoc} */
769 @Override
770 public OrbitRelativeFrame toOrbitRelativeFrame() {
771 return null;
772 }
773
774 };
775
776 /** {@inheritDoc} */
777 public String getName() {
778 return this.name();
779 }
780
781 /**
782 * Get the rotation from input to output {@link LOFType local orbital frame}.
783 * <p>
784 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
785 * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)} method must be called and
786 * the complete rotation transform must be extracted from it.
787 *
788 * @param in input commonly used local orbital frame
789 * @param out output commonly used local orbital frame
790 * @param pv position-velocity of the spacecraft in some inertial frame
791 *
792 * @return rotation from input to output local orbital frame
793 */
794 static Rotation rotationFromLOFInToLOFOut(final LOFType in, final LOFType out, final PVCoordinates pv) {
795 return out.rotationFromLOF(in, pv);
796 }
797
798 /**
799 * Get the rotation from input to output {@link LOFType local orbital frame}.
800 * <p>
801 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
802 * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and
803 * the complete rotation transform must be extracted from it.
804 *
805 * @param field field to which the elements belong
806 * @param in input commonly used local orbital frame
807 * @param out output commonly used local orbital frame
808 * @param pv position-velocity of the spacecraft in some inertial frame
809 * @param <T> type of the field elements
810 *
811 * @return rotation from input to output local orbital frame
812 */
813 static <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOFInToLOFOut(final Field<T> field,
814 final LOFType in,
815 final LOFType out,
816 final FieldPVCoordinates<T> pv) {
817 return out.rotationFromLOF(field, in, pv);
818 }
819
820 /**
821 * Get the rotation from input {@link LOF local orbital frame} to the instance.
822 * <p>
823 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
824 * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)} method must be called and the complete rotation
825 * transform must be extracted from it.
826 *
827 * @param fromLOF input local orbital frame
828 * @param pv position-velocity of the spacecraft in some inertial frame
829 *
830 * @return rotation from input local orbital frame to the instance
831 */
832 public Rotation rotationFromLOF(final LOFType fromLOF, final PVCoordinates pv) {
833
834 // First compute the rotation from the input LOF to the pivot inertial
835 final Rotation fromLOFToInertial = fromLOF.rotationFromInertial(pv).revert();
836
837 // Then compute the rotation from the pivot inertial to the output LOF
838 final Rotation inertialToThis = this.rotationFromInertial(pv);
839
840 // Output composed rotation
841 return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM);
842 }
843
844 /**
845 * Get the rotation from input {@link LOFType local orbital frame} to the instance.
846 * <p>
847 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
848 * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and the complete
849 * rotation transform must be extracted from it.
850 *
851 * @param field field to which the elements belong
852 * @param fromLOF input local orbital frame
853 * @param pv position-velocity of the spacecraft in some inertial frame
854 * @param <T> type of the field elements
855 *
856 * @return rotation from input local orbital frame to the instance
857 */
858 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOF(final Field<T> field,
859 final LOFType fromLOF,
860 final FieldPVCoordinates<T> pv) {
861
862 // First compute the rotation from the input LOF to the pivot inertial
863 final FieldRotation<T> fromLOFToInertial = fromLOF.rotationFromInertial(field, pv).revert();
864
865 // Then compute the rotation from the pivot inertial to the output LOF
866 final FieldRotation<T> inertialToThis = this.rotationFromInertial(field, pv);
867
868 // Output composed rotation
869 return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM);
870 }
871
872 /**
873 * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use
874 * {@link #rotationFromInertial(PVCoordinates)} instead.
875 */
876 @Override
877 public Rotation rotationFromInertial(final AbsoluteDate date, final PVCoordinates pv) {
878 return rotationFromInertial(pv);
879 }
880
881 /**
882 * Get the rotation from inertial frame to local orbital frame.
883 * <p>
884 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
885 * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates)} method must be called and
886 * the complete rotation transform must be extracted from it.
887 * </p>
888 *
889 * @param pv position-velocity of the spacecraft in some inertial frame
890 *
891 * @return rotation from inertial frame to local orbital frame
892 */
893 public abstract Rotation rotationFromInertial(PVCoordinates pv);
894
895 /**
896 * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use
897 * {@link #rotationFromInertial(Field, FieldPVCoordinates)} instead.
898 */
899 @Override
900 public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
901 final FieldAbsoluteDate<T> date,
902 final FieldPVCoordinates<T> pv) {
903 return rotationFromInertial(field, pv);
904 }
905
906 /**
907 * Get the rotation from inertial frame to local orbital frame.
908 * <p>
909 * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
910 * the full {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be
911 * called and the complete rotation transform must be extracted from it.
912 * </p>
913 *
914 * @param field field to which the elements belong
915 * @param pv position-velocity of the spacecraft in some inertial frame
916 * @param <T> type of the field elements
917 *
918 * @return rotation from inertial frame to local orbital frame
919 */
920 public abstract <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(Field<T> field,
921 FieldPVCoordinates<T> pv);
922
923 /**
924 * Convert current local orbital frame to CCSDS equivalent orbit relative frame when possible, null otherwise.
925 *
926 * @return CCSDS equivalent orbit relative frame when possible, null otherwise
927 *
928 * @see OrbitRelativeFrame
929 */
930 public abstract OrbitRelativeFrame toOrbitRelativeFrame();
931
932 /** Compute East direction.
933 * @param pv position-velocity of the spacecraft in some inertial frame
934 * @return East direction
935 * @since 13.0
936 */
937 private static Vector3D east(final PVCoordinates pv) {
938 final Vector3D p = pv.getPosition();
939 final double px = p.getX();
940 final double py = p.getY();
941 return (px == 0.0 && py == 0.0) ? Vector3D.PLUS_J : new Vector3D(-py, px, 0);
942 }
943
944 /** Compute East direction.
945 * @param <T> type of the field elements
946 * @param pv position-velocity of the spacecraft in some inertial frame
947 * @return East direction
948 * @since 13.0
949 */
950 private static <T extends CalculusFieldElement<T>> FieldVector3D<T> east(final FieldPVCoordinates<T> pv) {
951 final FieldVector3D<T> p = pv.getPosition();
952 final T px = p.getX();
953 final T py = p.getY();
954 return (px.isZero() && py.isZero()) ?
955 FieldVector3D.getPlusJ(px.getField()) :
956 new FieldVector3D<>(py.negate(), px, px.getField().getZero());
957 }
958
959 }