1   /* Copyright 2002-2024 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       * <ul>
49       *   <li>X<sub>TNW</sub> =  X<sub>VNC</sub> =  Y<sub>NTW</sub></li>
50       *   <li>Y<sub>TNW</sub> = -Z<sub>VNC</sub> = -X<sub>NTW</sub></li>
51       *   <li>Z<sub>TNW</sub> =  Y<sub>VNC</sub> =  Z<sub>NTW</sub></li>
52       * </ul>
53       *
54       * @see #VNC
55       * @see #NTW
56       */
57      TNW {
58          /** {@inheritDoc} */
59          @Override
60          public Rotation rotationFromInertial(final PVCoordinates pv) {
61              return new Rotation(pv.getVelocity(), pv.getMomentum(),
62                                  Vector3D.PLUS_I, Vector3D.PLUS_K);
63          }
64  
65          /** {@inheritDoc} */
66          @Override
67          public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
68                                                                                           final FieldPVCoordinates<T> pv) {
69              return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(),
70                                         new FieldVector3D<>(field, Vector3D.PLUS_I),
71                                         new FieldVector3D<>(field, Vector3D.PLUS_K));
72          }
73  
74          /** {@inheritDoc} */
75          @Override
76          public OrbitRelativeFrame toOrbitRelativeFrame() {
77              return OrbitRelativeFrame.TNW;
78          }
79  
80      },
81  
82      /**
83       * Constant for TNW frame considered inertial (X axis aligned with velocity, Z axis aligned with orbital momentum).
84       * <p>
85       * The axes of this frame are parallel to the axes of the {@link #VNC} and {@link #NTW} frames:
86       * <ul>
87       *   <li>X<sub>TNW</sub> =  X<sub>VNC</sub> =  Y<sub>NTW</sub></li>
88       *   <li>Y<sub>TNW</sub> = -Z<sub>VNC</sub> = -X<sub>NTW</sub></li>
89       *   <li>Z<sub>TNW</sub> =  Y<sub>VNC</sub> =  Z<sub>NTW</sub></li>
90       * </ul>
91       *
92       * @see #VNC
93       * @see #NTW
94       */
95      TNW_INERTIAL {
96          /** {@inheritDoc} */
97          @Override
98          public Rotation rotationFromInertial(final PVCoordinates pv) {
99              return TNW.rotationFromInertial(pv);
100         }
101 
102         /** {@inheritDoc} */
103         @Override
104         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
105                                                                                          final FieldPVCoordinates<T> pv) {
106             return TNW.rotationFromInertial(field, pv);
107         }
108 
109         /** {@inheritDoc} */
110         @Override
111         public boolean isQuasiInertial() {
112             return true;
113         }
114 
115         /** {@inheritDoc} */
116         @Override
117         public OrbitRelativeFrame toOrbitRelativeFrame() {
118             return OrbitRelativeFrame.TNW_INERTIAL;
119         }
120 
121     },
122 
123     /** Constant for QSW frame
124      * (X axis aligned with position, Z axis aligned with orbital momentum).
125      * <p>
126      * This frame is also known as the {@link #LVLH} frame, both constants are equivalent.
127      * </p>
128      * <p>
129      * The axes of these frames are parallel to the axes of the {@link #VVLH} frame:
130      * <ul>
131      *   <li>X<sub>QSW/LVLH</sub> = -Z<sub>VVLH</sub></li>
132      *   <li>Y<sub>QSW/LVLH</sub> =  X<sub>VVLH</sub></li>
133      *   <li>Z<sub>QSW/LVLH</sub> = -Y<sub>VVLH</sub></li>
134      * </ul>
135      *
136      * @see #LVLH
137      * @see #VVLH
138      */
139     QSW {
140         /** {@inheritDoc} */
141         @Override
142         public Rotation rotationFromInertial(final PVCoordinates pv) {
143             return new Rotation(pv.getPosition(), pv.getMomentum(),
144                                 Vector3D.PLUS_I, Vector3D.PLUS_K);
145         }
146 
147         /** {@inheritDoc} */
148         @Override
149         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
150                                                                                          final FieldPVCoordinates<T> pv) {
151             return new FieldRotation<>(pv.getPosition(), pv.getMomentum(),
152                                        new FieldVector3D<>(field, Vector3D.PLUS_I),
153                                        new FieldVector3D<>(field, Vector3D.PLUS_K));
154         }
155 
156         /** {@inheritDoc} */
157         @Override
158         public OrbitRelativeFrame toOrbitRelativeFrame() {
159             return OrbitRelativeFrame.QSW;
160         }
161 
162     },
163 
164     /**
165      * Constant for QSW frame considered inertial (X axis aligned with position, Z axis aligned with orbital momentum).
166      * <p>
167      * This frame is also known as the {@link #LVLH} frame, both constants are equivalent.
168      * </p>
169      * <p>
170      * The axes of these frames are parallel to the axes of the {@link #VVLH} frame:
171      * <ul>
172      *   <li>X<sub>QSW/LVLH</sub> = -Z<sub>VVLH</sub></li>
173      *   <li>Y<sub>QSW/LVLH</sub> =  X<sub>VVLH</sub></li>
174      *   <li>Z<sub>QSW/LVLH</sub> = -Y<sub>VVLH</sub></li>
175      * </ul>
176      *
177      * @see #LVLH
178      * @see #VVLH
179      */
180     QSW_INERTIAL {
181         /** {@inheritDoc} */
182         @Override
183         public Rotation rotationFromInertial(final PVCoordinates pv) {
184             return QSW.rotationFromInertial(pv);
185         }
186 
187         /** {@inheritDoc} */
188         @Override
189         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
190                                                                                          final FieldPVCoordinates<T> pv) {
191             return QSW.rotationFromInertial(field, pv);
192         }
193 
194         /** {@inheritDoc} */
195         @Override
196         public boolean isQuasiInertial() {
197             return true;
198         }
199 
200         /** {@inheritDoc} */
201         @Override
202         public OrbitRelativeFrame toOrbitRelativeFrame() {
203             return OrbitRelativeFrame.RSW_INERTIAL;
204         }
205 
206     },
207 
208     /** Constant for Local Vertical, Local Horizontal frame
209      * (X axis aligned with position, Z axis aligned with orbital momentum).
210      * <p>
211      * BEWARE! Depending on the background (software used, textbook, community),
212      * different incompatible definitions for LVLH are used. This one is consistent
213      * with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and
214      * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}).
215      * </p>
216      * <p>
217      * This frame is also known as the {@link #QSW} frame, both constants are equivalent.
218      * </p>
219      * <p>
220      * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame:
221      * <ul>
222      *   <li>X<sub>LVLH/QSW</sub> = -Z<sub>LVLH_CCSDS</sub></li>
223      *   <li>Y<sub>LVLH/QSW</sub> =  X<sub>LVLH_CCSDS</sub></li>
224      *   <li>Z<sub>LVLH/QSW</sub> = -Y<sub>LVLH_CCSDS</sub></li>
225      * </ul>
226      *
227      * @see #QSW
228      * @see #VVLH
229      */
230     LVLH {
231         /** {@inheritDoc} */
232         @Override
233         public Rotation rotationFromInertial(final PVCoordinates pv) {
234             return new Rotation(pv.getPosition(), pv.getMomentum(),
235                                 Vector3D.PLUS_I, Vector3D.PLUS_K);
236         }
237 
238         /** {@inheritDoc} */
239         @Override
240         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
241                                                                                          final FieldPVCoordinates<T> pv) {
242             return new FieldRotation<>(pv.getPosition(), pv.getMomentum(),
243                                        new FieldVector3D<>(field, Vector3D.PLUS_I),
244                                        new FieldVector3D<>(field, Vector3D.PLUS_K));
245         }
246 
247         /** {@inheritDoc} */
248         @Override
249         public OrbitRelativeFrame toOrbitRelativeFrame() {
250             throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION);
251         }
252 
253     },
254 
255     /**
256      * Constant for Local Vertical, Local Horizontal frame considered inertial (X axis aligned with position, Z axis
257      * aligned with orbital momentum).
258      * <p>
259      * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for
260      * LVLH are used. This one is consistent with Vallado's book and with AGI's STK. However CCSDS standard, Wertz, and
261      * a.i. solutions' FreeFlyer use another definition (see {@link #LVLH_CCSDS}).
262      * </p>
263      * <p>
264      * This frame is also known as the {@link #QSW} frame, both constants are equivalent.
265      * </p>
266      * <p>
267      * The axes of these frames are parallel to the axes of the {@link #LVLH_CCSDS} frame:
268      * <ul>
269      *   <li>X<sub>LVLH/QSW</sub> = -Z<sub>LVLH_CCSDS</sub></li>
270      *   <li>Y<sub>LVLH/QSW</sub> =  X<sub>LVLH_CCSDS</sub></li>
271      *   <li>Z<sub>LVLH/QSW</sub> = -Y<sub>LVLH_CCSDS</sub></li>
272      * </ul>
273      *
274      * @see #QSW
275      * @see #VVLH
276      */
277     LVLH_INERTIAL {
278         /** {@inheritDoc} */
279         @Override
280         public Rotation rotationFromInertial(final PVCoordinates pv) {
281             return LVLH.rotationFromInertial(pv);
282         }
283 
284         /** {@inheritDoc} */
285         @Override
286         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
287                                                                                          final FieldPVCoordinates<T> pv) {
288             return LVLH.rotationFromInertial(field, pv);
289         }
290 
291         /** {@inheritDoc} */
292         @Override
293         public boolean isQuasiInertial() {
294             return true;
295         }
296 
297         /** {@inheritDoc} */
298         @Override
299         public OrbitRelativeFrame toOrbitRelativeFrame() {
300             throw new OrekitException(OrekitMessages.CCSDS_DIFFERENT_LVLH_DEFINITION);
301         }
302 
303     },
304 
305     /** Constant for Local Vertical, Local Horizontal frame as defined by CCSDS
306      * (Z axis aligned with opposite of position, Y axis aligned with opposite of orbital momentum).
307      * <p>
308      * BEWARE! Depending on the background (software used, textbook, community),
309      * different incompatible definitions for LVLH are used. This one is consistent
310      * with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However Vallado's
311      * book and with AGI's STK use another definition (see {@link #LVLH}).
312      * </p>
313      * <p>
314      * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
315      * <ul>
316      *   <li>X<sub>LVLH_CCSDS/VVLH</sub> =  Y<sub>QSW/LVLH</sub></li>
317      *   <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
318      *   <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
319      * </ul>
320      *
321      * @see #QSW
322      * @see #LVLH
323      * @since 11.0
324      */
325     LVLH_CCSDS {
326         /** {@inheritDoc} */
327         @Override
328         public Rotation rotationFromInertial(final PVCoordinates pv) {
329             return new Rotation(pv.getPosition(), pv.getMomentum(),
330                                 Vector3D.MINUS_K, Vector3D.MINUS_J);
331         }
332 
333         /** {@inheritDoc} */
334         @Override
335         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
336                                                                                          final FieldPVCoordinates<T> pv) {
337             return new FieldRotation<>(pv.getPosition(), pv.getMomentum(),
338                                        new FieldVector3D<>(field, Vector3D.MINUS_K),
339                                        new FieldVector3D<>(field, Vector3D.MINUS_J));
340         }
341 
342         /** {@inheritDoc} */
343         @Override
344         public OrbitRelativeFrame toOrbitRelativeFrame() {
345             return OrbitRelativeFrame.LVLH;
346         }
347 
348     },
349 
350     /**
351      * Constant for Local Vertical, Local Horizontal frame as defined by CCSDS considered inertial (Z axis aligned with
352      * opposite of position, Y axis aligned with opposite of orbital momentum).
353      * <p>
354      * BEWARE! Depending on the background (software used, textbook, community), different incompatible definitions for
355      * LVLH are used. This one is consistent with CCSDS standard, Wertz, and a.i. solutions' FreeFlyer. However
356      * Vallado's book and with AGI's STK use another definition (see {@link #LVLH}).
357      * </p>
358      * <p>
359      * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
360      * <ul>
361      *   <li>X<sub>LVLH_CCSDS/VVLH</sub> =  Y<sub>QSW/LVLH</sub></li>
362      *   <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
363      *   <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
364      * </ul>
365      *
366      * @see #QSW
367      * @see #LVLH
368      * @since 11.0
369      */
370     LVLH_CCSDS_INERTIAL {
371         /** {@inheritDoc} */
372         @Override
373         public Rotation rotationFromInertial(final PVCoordinates pv) {
374             return LVLH_CCSDS.rotationFromInertial(pv);
375         }
376 
377         /** {@inheritDoc} */
378         @Override
379         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
380                                                                                          final FieldPVCoordinates<T> pv) {
381             return LVLH_CCSDS.rotationFromInertial(field, pv);
382         }
383 
384         /** {@inheritDoc} */
385         @Override
386         public boolean isQuasiInertial() {
387             return true;
388         }
389 
390         /** {@inheritDoc} */
391         @Override
392         public OrbitRelativeFrame toOrbitRelativeFrame() {
393             return OrbitRelativeFrame.LVLH_INERTIAL;
394         }
395 
396     },
397 
398     /** Constant for Vehicle Velocity, Local Horizontal frame
399      * (Z axis aligned with opposite of position, Y axis aligned with opposite of orbital momentum).
400      * <p>
401      * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK.
402      * </p>
403      * <p>
404      * Beware that the name is misleading: in the general case (i.e. not perfectly circular),
405      * none of the axes is perfectly aligned with velocity! The preferred name for this
406      * should be {@link #LVLH_CCSDS}.
407      * </p>
408      * <p>
409      * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
410      * <ul>
411      *   <li>X<sub>LVLH_CCSDS/VVLH</sub> =  Y<sub>QSW/LVLH</sub></li>
412      *   <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
413      *   <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
414      * </ul>
415      * @see #LVLH_CCSDS
416      */
417     VVLH {
418         /** {@inheritDoc} */
419         @Override
420         public Rotation rotationFromInertial(final PVCoordinates pv) {
421             return LVLH_CCSDS.rotationFromInertial(pv);
422         }
423 
424         /** {@inheritDoc} */
425         @Override
426         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
427                                                                                          final FieldPVCoordinates<T> pv) {
428             return LVLH_CCSDS.rotationFromInertial(field, pv);
429         }
430 
431         /** {@inheritDoc} */
432         @Override
433         public OrbitRelativeFrame toOrbitRelativeFrame() {
434             return OrbitRelativeFrame.LVLH;
435         }
436 
437     },
438 
439     /**
440      * Constant for Vehicle Velocity, Local Horizontal frame considered inertial (Z axis aligned with opposite of
441      * position, Y axis aligned with opposite of orbital momentum).
442      * <p>
443      * This is another name for {@link #LVLH_CCSDS}, kept here for compatibility with STK.
444      * </p>
445      * <p>
446      * Beware that the name is misleading: in the general case (i.e. not perfectly circular), none of the axes is
447      * perfectly aligned with velocity! The preferred name for this should be {@link #LVLH_CCSDS}.
448      * </p>
449      * <p>
450      * The axes of this frame are parallel to the axes of both the {@link #QSW} and {@link #LVLH} frames:
451      * <ul>
452      *   <li>X<sub>LVLH_CCSDS/VVLH</sub> =  Y<sub>QSW/LVLH</sub></li>
453      *   <li>Y<sub>LVLH_CCSDS/VVLH</sub> = -Z<sub>QSW/LVLH</sub></li>
454      *   <li>Z<sub>LVLH_CCSDS/VVLH</sub> = -X<sub>QSW/LVLH</sub></li>
455      * </ul>
456      *
457      * @see #LVLH_CCSDS
458      */
459     VVLH_INERTIAL {
460         /** {@inheritDoc} */
461         @Override
462         public Rotation rotationFromInertial(final PVCoordinates pv) {
463             return VVLH.rotationFromInertial(pv);
464         }
465 
466         /** {@inheritDoc} */
467         @Override
468         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
469                                                                                          final FieldPVCoordinates<T> pv) {
470             return VVLH.rotationFromInertial(field, pv);
471         }
472 
473         /** {@inheritDoc} */
474         @Override
475         public boolean isQuasiInertial() {
476             return true;
477         }
478 
479         /** {@inheritDoc} */
480         @Override
481         public OrbitRelativeFrame toOrbitRelativeFrame() {
482             return OrbitRelativeFrame.LVLH_INERTIAL;
483         }
484 
485     },
486 
487     /** Constant for Velocity - Normal - Co-normal frame
488      * (X axis aligned with velocity, Y axis aligned with orbital momentum).
489      * <p>
490      * The axes of this frame are parallel to the axes of the {@link #TNW}
491      * and {@link #NTW} frames:
492      * <ul>
493      *   <li>X<sub>VNC</sub> =  X<sub>TNW</sub> = Y<sub>NTW</sub></li>
494      *   <li>Y<sub>VNC</sub> =  Z<sub>TNW</sub> = Z<sub>NTW</sub></li>
495      *   <li>Z<sub>VNC</sub> = -Y<sub>TNW</sub> = X<sub>NTW</sub></li>
496      * </ul>
497      *
498      * @see #TNW
499      * @see #NTW
500      */
501     VNC {
502         /** {@inheritDoc} */
503         @Override
504         public Rotation rotationFromInertial(final PVCoordinates pv) {
505             return new Rotation(pv.getVelocity(), pv.getMomentum(),
506                                 Vector3D.PLUS_I, Vector3D.PLUS_J);
507         }
508 
509         /** {@inheritDoc} */
510         @Override
511         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
512                                                                                          final FieldPVCoordinates<T> pv) {
513             return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(),
514                                        new FieldVector3D<>(field, Vector3D.PLUS_I),
515                                        new FieldVector3D<>(field, Vector3D.PLUS_J));
516         }
517 
518         /** {@inheritDoc} */
519         @Override
520         public OrbitRelativeFrame toOrbitRelativeFrame() {
521             return OrbitRelativeFrame.VNC_ROTATING;
522         }
523 
524     },
525 
526     /**
527      * Constant for Velocity - Normal - Co-normal frame considered inertial (X axis aligned with velocity, Y axis
528      * aligned with orbital momentum).
529      * <p>
530      * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #NTW} frames:
531      * <ul>
532      *   <li>X<sub>VNC</sub> =  X<sub>TNW</sub> = Y<sub>NTW</sub></li>
533      *   <li>Y<sub>VNC</sub> =  Z<sub>TNW</sub> = Z<sub>NTW</sub></li>
534      *   <li>Z<sub>VNC</sub> = -Y<sub>TNW</sub> = X<sub>NTW</sub></li>
535      * </ul>
536      *
537      * @see #TNW
538      * @see #NTW
539      */
540     VNC_INERTIAL {
541         /** {@inheritDoc} */
542         @Override
543         public Rotation rotationFromInertial(final PVCoordinates pv) {
544             return VNC.rotationFromInertial(pv);
545         }
546 
547         /** {@inheritDoc} */
548         @Override
549         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
550                                                                                          final FieldPVCoordinates<T> pv) {
551             return VNC.rotationFromInertial(field, pv);
552         }
553 
554         /** {@inheritDoc} */
555         @Override
556         public boolean isQuasiInertial() {
557             return true;
558         }
559 
560         /** {@inheritDoc} */
561         @Override
562         public OrbitRelativeFrame toOrbitRelativeFrame() {
563             return OrbitRelativeFrame.VNC_INERTIAL;
564         }
565 
566     },
567 
568     /**
569      * Constant for Equinoctial Coordinate System (X axis aligned with ascending node, Z axis aligned with orbital
570      * momentum).
571      *
572      * @since 11.0
573      */
574     EQW {
575         /** {@inheritDoc} */
576         @Override
577         public Rotation rotationFromInertial(final PVCoordinates pv) {
578             final Vector3D m = pv.getMomentum();
579             return new Rotation(new Vector3D(-m.getY(), m.getX(), 0), m,
580                                 Vector3D.PLUS_I, Vector3D.PLUS_J);
581         }
582 
583         /** {@inheritDoc} */
584         @Override
585         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
586                                                                                          final FieldPVCoordinates<T> pv) {
587             final FieldVector3D<T> m = pv.getMomentum();
588             return new FieldRotation<>(new FieldVector3D<>(m.getY().negate(), m.getX(), field.getZero()),
589                                        m,
590                                        new FieldVector3D<>(field, Vector3D.PLUS_I),
591                                        new FieldVector3D<>(field, Vector3D.PLUS_J));
592         }
593 
594         /** {@inheritDoc} */
595         @Override
596         public boolean isQuasiInertial() {
597             return true;
598         }
599 
600         /** {@inheritDoc} */
601         @Override
602         public OrbitRelativeFrame toOrbitRelativeFrame() {
603             return OrbitRelativeFrame.EQW_INERTIAL;
604         }
605 
606     },
607 
608     /** Constant for Transverse Velocity Normal coordinate system
609      * (Y axis aligned with velocity, Z axis aligned with orbital momentum).
610      * <p>
611      * The axes of this frame are parallel to the axes of the {@link #TNW}
612      * and {@link #VNC} frames:
613      * <ul>
614      *   <li>X<sub>NTW</sub> = -Y<sub>TNW</sub> = Z<sub>VNC</sub></li>
615      *   <li>Y<sub>NTW</sub> =  X<sub>TNW</sub> = X<sub>VNC</sub></li>
616      *   <li>Z<sub>NTW</sub> =  Z<sub>TNW</sub> = Y<sub>VNC</sub></li>
617      * </ul>
618      * @see #TNW
619      * @see #VNC
620      * @since 11.0
621      */
622     NTW {
623         /** {@inheritDoc} */
624         @Override
625         public Rotation rotationFromInertial(final PVCoordinates pv) {
626             return new Rotation(pv.getVelocity(), pv.getMomentum(),
627                                 Vector3D.PLUS_J, Vector3D.PLUS_K);
628         }
629 
630         /** {@inheritDoc} */
631         @Override
632         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
633                                                                                          final FieldPVCoordinates<T> pv) {
634             return new FieldRotation<>(pv.getVelocity(), pv.getMomentum(),
635                                        new FieldVector3D<>(field, Vector3D.PLUS_J),
636                                        new FieldVector3D<>(field, Vector3D.PLUS_K));
637         }
638 
639         /** {@inheritDoc} */
640         @Override
641         public OrbitRelativeFrame toOrbitRelativeFrame() {
642             return OrbitRelativeFrame.NTW_ROTATING;
643         }
644 
645     },
646 
647     /**
648      * Constant for Transverse Velocity Normal coordinate system considered inertial (Y axis aligned with velocity, Z
649      * axis aligned with orbital momentum).
650      * <p>
651      * The axes of this frame are parallel to the axes of the {@link #TNW} and {@link #VNC} frames:
652      * <ul>
653      *   <li>X<sub>NTW</sub> = -Y<sub>TNW</sub> = Z<sub>VNC</sub></li>
654      *   <li>Y<sub>NTW</sub> =  X<sub>TNW</sub> = X<sub>VNC</sub></li>
655      *   <li>Z<sub>NTW</sub> =  Z<sub>TNW</sub> = Y<sub>VNC</sub></li>
656      * </ul>
657      *
658      * @see #TNW
659      * @see #VNC
660      * @since 11.0
661      */
662     NTW_INERTIAL {
663         /** {@inheritDoc} */
664         @Override
665         public Rotation rotationFromInertial(final PVCoordinates pv) {
666             return NTW.rotationFromInertial(pv);
667         }
668 
669         /** {@inheritDoc} */
670         @Override
671         public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
672                                                                                          final FieldPVCoordinates<T> pv) {
673             return NTW.rotationFromInertial(field, pv);
674         }
675 
676         /** {@inheritDoc} */
677         @Override
678         public boolean isQuasiInertial() {
679             return true;
680         }
681 
682         /** {@inheritDoc} */
683         @Override
684         public OrbitRelativeFrame toOrbitRelativeFrame() {
685             return OrbitRelativeFrame.NTW_INERTIAL;
686         }
687 
688     };
689 
690     /** {@inheritDoc} */
691     public String getName() {
692         return this.name();
693     };
694 
695     /**
696      * Get the rotation from input to output {@link LOFType local orbital frame}.
697      * <p>
698      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
699      * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)} method must be called and
700      * the complete rotation transform must be extracted from it.
701      *
702      * @param in input commonly used local orbital frame
703      * @param out output commonly used local orbital frame
704      * @param pv position-velocity of the spacecraft in some inertial frame
705      *
706      * @return rotation from input to output local orbital frame
707      */
708     static Rotation rotationFromLOFInToLOFOut(final LOFType in, final LOFType out, final PVCoordinates pv) {
709         return out.rotationFromLOF(in, pv);
710     }
711 
712     /**
713      * Get the rotation from input to output {@link LOFType local orbital frame}.
714      * <p>
715      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
716      * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)}  method must be called and
717      * the complete rotation transform must be extracted from it.
718      *
719      * @param field field to which the elements belong
720      * @param in input commonly used local orbital frame
721      * @param out output commonly used local orbital frame
722      * @param pv position-velocity of the spacecraft in some inertial frame
723      * @param <T> type of the field elements
724      *
725      * @return rotation from input to output local orbital frame
726      */
727     static <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOFInToLOFOut(final Field<T> field,
728                                                                                           final LOFType in,
729                                                                                           final LOFType out,
730                                                                                           final FieldPVCoordinates<T> pv) {
731         return out.rotationFromLOF(field, in, pv);
732     }
733 
734     /**
735      * Get the rotation from input {@link LOF local orbital frame} to the instance.
736      * <p>
737      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
738      * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)} method must be called and the complete rotation
739      * transform must be extracted from it.
740      *
741      * @param fromLOF input local orbital frame
742      * @param pv position-velocity of the spacecraft in some inertial frame
743      *
744      * @return rotation from input local orbital frame to the instance
745      */
746     public Rotation rotationFromLOF(final LOFType fromLOF, final PVCoordinates pv) {
747 
748         // First compute the rotation from the input LOF to the pivot inertial
749         final Rotation fromLOFToInertial = fromLOF.rotationFromInertial(pv).revert();
750 
751         // Then compute the rotation from the pivot inertial to the output LOF
752         final Rotation inertialToThis = this.rotationFromInertial(pv);
753 
754         // Output composed rotation
755         return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM);
756     }
757 
758     /**
759      * Get the rotation from input {@link LOFType local orbital frame} to the instance.
760      * <p>
761      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
762      * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and the complete
763      * rotation transform must be extracted from it.
764      *
765      * @param field field to which the elements belong
766      * @param fromLOF input local orbital frame
767      * @param pv position-velocity of the spacecraft in some inertial frame
768      * @param <T> type of the field elements
769      *
770      * @return rotation from input local orbital frame to the instance
771      */
772     public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOF(final Field<T> field,
773                                                                                 final LOFType fromLOF,
774                                                                                 final FieldPVCoordinates<T> pv) {
775 
776         // First compute the rotation from the input LOF to the pivot inertial
777         final FieldRotation<T> fromLOFToInertial = fromLOF.rotationFromInertial(field, pv).revert();
778 
779         // Then compute the rotation from the pivot inertial to the output LOF
780         final FieldRotation<T> inertialToThis = this.rotationFromInertial(field, pv);
781 
782         // Output composed rotation
783         return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM);
784     }
785 
786     /**
787      * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use
788      * {@link #rotationFromInertial(PVCoordinates)} instead.
789      */
790     @Override
791     public Rotation rotationFromInertial(final AbsoluteDate date, final PVCoordinates pv) {
792         return rotationFromInertial(pv);
793     }
794 
795     /**
796      * Get the rotation from inertial frame to local orbital frame.
797      * <p>
798      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
799      * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates)} method must be called and
800      * the complete rotation transform must be extracted from it.
801      * </p>
802      *
803      * @param pv position-velocity of the spacecraft in some inertial frame
804      *
805      * @return rotation from inertial frame to local orbital frame
806      */
807     public abstract Rotation rotationFromInertial(PVCoordinates pv);
808 
809     /**
810      * {@inheritDoc} It is unnecessary to use this method when dealing with {@link LOFType}, use
811      * {@link #rotationFromInertial(Field, FieldPVCoordinates)} instead.
812      */
813     @Override
814     public <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(final Field<T> field,
815                                                                                      final FieldAbsoluteDate<T> date,
816                                                                                      final FieldPVCoordinates<T> pv) {
817         return rotationFromInertial(field, pv);
818     }
819 
820     /**
821      * Get the rotation from inertial frame to local orbital frame.
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 #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be
825      * called and the complete rotation transform must be extracted from it.
826      * </p>
827      *
828      * @param field field to which the elements belong
829      * @param pv position-velocity of the spacecraft in some inertial frame
830      * @param <T> type of the field elements
831      *
832      * @return rotation from inertial frame to local orbital frame
833      */
834     public abstract <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(Field<T> field,
835                                                                                               FieldPVCoordinates<T> pv);
836 
837     /**
838      * Convert current local orbital frame to CCSDS equivalent orbit relative frame when possible, null otherwise.
839      *
840      * @return CCSDS equivalent orbit relative frame when possible, null otherwise
841      *
842      * @see OrbitRelativeFrame
843      */
844     public abstract OrbitRelativeFrame toOrbitRelativeFrame();
845 
846 }