1   /* Copyright 2002-2026 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.analysis.differentiation.FieldUnivariateDerivative2;
22  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2Field;
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
24  import org.hipparchus.analysis.differentiation.UnivariateDerivative2Field;
25  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
26  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27  import org.hipparchus.geometry.euclidean.threed.Rotation;
28  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
29  import org.hipparchus.geometry.euclidean.threed.Vector3D;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.FieldAbsoluteDate;
32  import org.orekit.utils.AngularCoordinates;
33  import org.orekit.utils.FieldAngularCoordinates;
34  import org.orekit.utils.FieldPVCoordinates;
35  import org.orekit.utils.PVCoordinates;
36  
37  /**
38   * Interface for local orbital frame.
39   *
40   * @author Vincent Cucchietti
41   * @author Jérôme Tabeaud
42   */
43  public interface LOF {
44  
45      /**
46       * Get the rotation from input to output {@link LOF local orbital frame}.
47       * <p>
48       * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
49       * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, FieldAbsoluteDate, FieldPVCoordinates)} method must be called and
50       * the complete rotation transform must be extracted from it.
51       *
52       * @param field field to which the elements belong
53       * @param in input commonly used local orbital frame
54       * @param out output commonly used local orbital frame
55       * @param date date of the rotation
56       * @param pv position-velocity of the spacecraft in some inertial frame
57       * @param <T> type of the field elements
58       *
59       * @return rotation from input to output local orbital frame
60       *
61       * @since 11.3
62       */
63      static <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOFInToLOFOut(final Field<T> field,
64                                                                                            final LOF in, final LOF out,
65                                                                                            final FieldAbsoluteDate<T> date,
66                                                                                            final FieldPVCoordinates<T> pv) {
67          return out.rotationFromLOF(field, in, date, pv);
68      }
69  
70      /**
71       * Get the transform from input to output {@link LOF local orbital frame}.
72       *
73       * @param in input commonly used local orbital frame
74       * @param out output commonly used local orbital frame
75       * @param date date of the transform
76       * @param pv position-velocity of the spacecraft in some inertial frame
77       * @param <T> type of the field elements
78       *
79       * @return rotation from input to output local orbital frame.
80       *
81       * @since 11.3
82       */
83      static <T extends CalculusFieldElement<T>> FieldTransform<T> transformFromLOFInToLOFOut(final LOF in, final LOF out,
84                                                                                              final FieldAbsoluteDate<T> date,
85                                                                                              final FieldPVCoordinates<T> pv) {
86          return out.transformFromLOF(in, date, pv);
87      }
88  
89      /**
90       * Get the rotation from input to output {@link LOF local orbital frame}.
91       * <p>
92       * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
93       * the full {@link #transformFromLOFInToLOFOut(LOF, LOF, AbsoluteDate, PVCoordinates)}   method must be called and
94       * the complete rotation transform must be extracted from it.
95       *
96       * @param in input commonly used local orbital frame
97       * @param out output commonly used local orbital frame
98       * @param date date of the rotation
99       * @param pv position-velocity of the spacecraft in some inertial frame
100      *
101      * @return rotation from input to output local orbital frame.
102      *
103      * @since 11.3
104      */
105     static Rotation rotationFromLOFInToLOFOut(final LOF in, final LOF out, final AbsoluteDate date, final PVCoordinates pv) {
106         return out.rotationFromLOF(in, date, pv);
107     }
108 
109     /**
110      * Get the transform from input to output {@link LOF local orbital frame}.
111      *
112      * @param in input commonly used local orbital frame
113      * @param out output commonly used local orbital frame
114      * @param date date of the transform
115      * @param pv position-velocity of the spacecraft in some inertial frame
116      *
117      * @return rotation from input to output local orbital frame
118      *
119      * @since 11.3
120      */
121     static Transform transformFromLOFInToLOFOut(final LOF in, final LOF out, final AbsoluteDate date,
122                                                 final PVCoordinates pv) {
123         return out.transformFromLOF(in, date, pv);
124     }
125 
126     /**
127      * Get the rotation from input {@link LOF local orbital frame} to the instance.
128      * <p>
129      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
130      * the full {@link #transformFromLOF(LOF, FieldAbsoluteDate, FieldPVCoordinates)}   method must be called and
131      * the complete rotation transform must be extracted from it.
132      *
133      * @param field field to which the elements belong
134      * @param fromLOF input local orbital frame
135      * @param date date of the rotation
136      * @param pv position-velocity of the spacecraft in some inertial frame
137      * @param <T> type of the field elements
138      *
139      * @return rotation from input local orbital frame to the instance
140      *
141      * @since 11.3
142      */
143     default <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromLOF(final Field<T> field,
144                                                                                  final LOF fromLOF,
145                                                                                  final FieldAbsoluteDate<T> date,
146                                                                                  final FieldPVCoordinates<T> pv) {
147 
148         // First compute the rotation from the input LOF to the pivot inertial
149         final FieldRotation<T> fromLOFToInertial = fromLOF.rotationToInertial(field, date, pv);
150 
151         // Then compute the rotation from the pivot inertial to the output LOF
152         final FieldRotation<T> inertialToThis = this.rotationFromInertial(field, date, pv);
153 
154         // Output composed rotation
155         return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM);
156     }
157 
158     /**
159      * Get the rotation from input {@link LOF commonly used local orbital frame} to the instance.
160      *
161      * @param fromLOF input local orbital frame
162      * @param date date of the transform
163      * @param pv position-velocity of the spacecraft in some inertial frame
164      * @param <T> type of the field elements
165      *
166      * @return rotation from input local orbital frame to the instance
167      *
168      * @since 11.3
169      */
170     default <T extends CalculusFieldElement<T>> FieldTransform<T> transformFromLOF(final LOF fromLOF,
171                                                                                    final FieldAbsoluteDate<T> date,
172                                                                                    final FieldPVCoordinates<T> pv) {
173 
174         // Get transform from input local orbital frame to inertial
175         final FieldTransform<T> fromLOFToInertial = fromLOF.transformToInertial(date, pv);
176 
177         // Get transform from inertial to output local orbital frame
178         final FieldTransform<T> inertialToLOFOut = this.transformFromInertial(date, pv);
179 
180         // Output composition of both transforms
181         return new FieldTransform<>(date, fromLOFToInertial, inertialToLOFOut);
182     }
183 
184     /**
185      * Get the transform from an inertial frame defining position-velocity and the local orbital frame.
186      *
187      * @param date current date
188      * @param pv position-velocity of the spacecraft in some inertial frame
189      * @param <T> type of the fields elements
190      *
191      * @return transform from the frame where position-velocity are defined to local orbital frame
192      *
193      * @since 9.0
194      */
195     default <T extends CalculusFieldElement<T>> FieldTransform<T> transformFromInertial(final FieldAbsoluteDate<T> date,
196                                                                                         final FieldPVCoordinates<T> pv) {
197         final Field<T> field = date.getField();
198         if (isQuasiInertial()) {
199             return new FieldTransform<>(date, pv.getPosition().negate(), rotationFromInertial(field, date, pv));
200 
201         } else if (pv.getAcceleration().equals(FieldVector3D.getZero(field))) {
202             // we consider that the acceleration is not known
203             // compute the translation part of the transform
204             final FieldTransform<T> translation = new FieldTransform<>(date, pv.negate());
205 
206             // compute the rotation part of the transform
207             final FieldRotation<T> r        = rotationFromInertial(date.getField(), date, pv);
208             final FieldVector3D<T> p        = pv.getPosition();
209             final FieldVector3D<T> momentum = pv.getMomentum();
210             final FieldTransform<T> rotation = new FieldTransform<>(date, r,
211                     new FieldVector3D<>(p.getNorm2Sq().reciprocal(), r.applyTo(momentum)));
212 
213             return new FieldTransform<>(date, translation, rotation);
214 
215         } else {
216             // use automatic differentiation
217             // create date with independent variable
218             final FieldUnivariateDerivative2Field<T> fud2Field = FieldUnivariateDerivative2Field.getUnivariateDerivative2Field(field);
219             final FieldAbsoluteDate<FieldUnivariateDerivative2<T>> fud2Date = date.toFUD2Field();
220             // create PV with independent variable
221             final FieldPVCoordinates<FieldUnivariateDerivative2<T>> fud2PV = pv.toUnivariateDerivative2PV();
222             // compute rotation
223             final FieldRotation<FieldUnivariateDerivative2<T>> fud2Rotation = rotationFromInertial(fud2Field, fud2Date,
224                 fud2PV);
225             // turn into FieldTransform whilst adding the translation
226             final FieldAngularCoordinates<T> fieldAngularCoordinates = new FieldAngularCoordinates<>(fud2Rotation);
227             return new FieldTransform<>(date, new FieldTransform<>(date, pv.negate()),
228                 new FieldTransform<>(date, fieldAngularCoordinates));
229         }
230     }
231 
232     /**
233      *  Get the transform from the local orbital frame to the inertial frame.
234      *
235      * @param date current date
236      * @param pv position-velocity of the spacecraft in the inertial frame
237      * @param <T> type of the fields elements
238      *
239      * @return transform from local orbital frame to the corresponding inertial frame.
240      *
241      * @since 14.0
242      */
243     default <T extends CalculusFieldElement<T>> FieldTransform<T> transformToInertial(final FieldAbsoluteDate<T> date,
244                                                                                         final FieldPVCoordinates<T> pv) {
245         return transformFromInertial(date, pv).getInverse();
246     }
247 
248     /**
249      * Get the rotation from inertial frame to local orbital frame.
250      * <p>
251      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
252      * the full {@link #transformFromInertial(FieldAbsoluteDate, FieldPVCoordinates)} method must be
253      * called and the complete rotation transform must be extracted from it.
254      * </p>
255      *
256      * @param field field to which the elements belong
257      * @param date date of the rotation
258      * @param pv position-velocity of the spacecraft in some inertial frame
259      * @param <T> type of the field elements
260      *
261      * @return rotation from inertial frame to local orbital frame
262      *
263      * @since 9.0
264      */
265     <T extends CalculusFieldElement<T>> FieldRotation<T> rotationFromInertial(Field<T> field, FieldAbsoluteDate<T> date,
266                                                                               FieldPVCoordinates<T> pv);
267 
268     /**
269      * Get the rotation from local orbital frame to inertial frame.
270      * <p>
271      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
272      * the full {@link #transformToInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be called and
273      * the complete rotation transform must be extracted from it.</p>
274      *
275      * @param field field to which the elements belong
276      * @param date date of the rotation
277      * @param pv position-velocity of the spacecraft in some inertial frame
278      * @param <T> type of the field elements
279      *
280      * @return rotation from local orbital frame to inertial frame
281      *
282      * @since 14.0
283      */
284     default <T extends CalculusFieldElement<T>> FieldRotation<T> rotationToInertial(final Field<T> field,
285                                                                                     final FieldAbsoluteDate<T> date,
286                                                                                     final FieldPVCoordinates<T> pv) {
287         return rotationFromInertial(field, date, pv).revert();
288     }
289 
290     /**
291      * Get the rotation from input {@link LOF local orbital frame} to the instance.
292      * <p>
293      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
294      * the full {@link #transformFromLOF(LOF, AbsoluteDate, PVCoordinates)}  method must be called and
295      * the complete rotation transform must be extracted from it.
296      *
297      * @param fromLOF input local orbital frame
298      * @param date date of the rotation
299      * @param pv position-velocity of the spacecraft in some inertial frame
300      *
301      * @return rotation from input local orbital frame to the instance
302      *
303      * @since 11.3
304      */
305     default Rotation rotationFromLOF(final LOF fromLOF, final AbsoluteDate date, final PVCoordinates pv) {
306 
307         // First compute the rotation from the input LOF to the pivot inertial
308         final Rotation fromLOFToInertial = fromLOF.rotationToInertial(date, pv);
309 
310         // Then compute the rotation from the pivot inertial to the output LOF
311         final Rotation inertialToThis = this.rotationFromInertial(date, pv);
312 
313         // Output composed rotation
314         return fromLOFToInertial.compose(inertialToThis, RotationConvention.FRAME_TRANSFORM);
315     }
316 
317     /**
318      * Get the rotation from input {@link LOF local orbital frame} to the instance.
319      *
320      * @param fromLOF input local orbital frame
321      * @param date date of the transform
322      * @param pv position-velocity of the spacecraft in some inertial frame
323      *
324      * @return rotation from input local orbital frame to the instance
325      *
326      * @since 11.3
327      */
328     default Transform transformFromLOF(final LOF fromLOF, final AbsoluteDate date, final PVCoordinates pv) {
329 
330         // First compute the rotation from the input LOF to the pivot inertial
331         final Transform fromLOFToInertial = fromLOF.transformToInertial(date, pv);
332 
333         // Then compute the rotation from the pivot inertial to the output LOF
334         final Transform inertialToThis = this.transformFromInertial(date, pv);
335 
336         // Output composed rotation
337         return new Transform(date, fromLOFToInertial, inertialToThis);
338     }
339 
340     /**
341      * Get the transform from an inertial frame defining position-velocity and the local orbital frame.
342      *
343      * @param date current date
344      * @param pv position-velocity of the spacecraft in some inertial frame
345      *
346      * @return transform from the frame where position-velocity are defined to local orbital frame
347      */
348     default Transform transformFromInertial(final AbsoluteDate date, final PVCoordinates pv) {
349         if (isQuasiInertial()) {
350             return new Transform(date, pv.getPosition().negate(), rotationFromInertial(date, pv));
351 
352         } else if (pv.getAcceleration().equals(Vector3D.ZERO)) {
353             // compute the rotation part of the transform assuming there is no known acceleration
354             final Rotation  r        = rotationFromInertial(date, pv);
355             final Vector3D  p        = pv.getPosition();
356             final Vector3D  momentum = pv.getMomentum();
357             final AngularCoordinates angularCoordinates = new AngularCoordinates(r,
358                 new Vector3D(1.0 / p.getNorm2Sq(), r.applyTo(momentum)));
359 
360             return new Transform(date, pv.negate(), angularCoordinates);
361 
362         } else {
363             // use automatic differentiation
364             // create date with independent variable
365             final UnivariateDerivative2Field ud2Field = UnivariateDerivative2Field.getInstance();
366             final UnivariateDerivative2 dt = new UnivariateDerivative2(0, 1, 0);
367             final FieldAbsoluteDate<UnivariateDerivative2> ud2Date = new FieldAbsoluteDate<>(ud2Field, date).shiftedBy(dt);
368             // create PV with independent variable
369             final FieldPVCoordinates<UnivariateDerivative2> ud2PVCoordinates = pv.toUnivariateDerivative2PV();
370             // compute Field rotation
371             final FieldRotation<UnivariateDerivative2> ud2Rotation = rotationFromInertial(ud2Field, ud2Date,
372                 ud2PVCoordinates);
373             // turn into Transform whilst adding translation
374             final AngularCoordinates angularCoordinates = new AngularCoordinates(ud2Rotation);
375             return new Transform(date, pv.negate(), angularCoordinates);
376         }
377     }
378 
379     /**
380      *  Get the transform from the local orbital frame to the inertial frame.
381      *
382      * @param date current date
383      * @param pv position-velocity of the spacecraft in the inertial frame
384      *
385      * @return transform from local orbital frame to the corresponding inertial frame.
386      *
387      * @since 14.0
388      */
389     default Transform transformToInertial(final AbsoluteDate date, final PVCoordinates pv) {
390         return transformFromInertial(date, pv).getInverse();
391     }
392 
393     /**
394      * Get the rotation from inertial frame to local orbital frame.
395      * <p>
396      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
397      * the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be called and
398      * the complete rotation transform must be extracted from it.</p>
399      *
400      * @param date date of the rotation
401      * @param pv position-velocity of the spacecraft in some inertial frame
402      *
403      * @return rotation from inertial frame to local orbital frame
404      */
405     Rotation rotationFromInertial(AbsoluteDate date, PVCoordinates pv);
406 
407     /**
408      * Get the rotation from local orbital frame to inertial frame.
409      * <p>
410      * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as well,
411      * the full {@link #transformToInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be called and
412      * the complete rotation transform must be extracted from it.</p>
413      *
414      * @param date date of the rotation
415      * @param pv position-velocity of the spacecraft in some inertial frame
416      *
417      * @return rotation from local orbital frame to inertial frame
418      *
419      * @since 14.0
420      */
421     default Rotation rotationToInertial(final AbsoluteDate date,
422                                         final PVCoordinates pv) {
423         return rotationFromInertial(date, pv).revert();
424     }
425 
426     /** Get flag that indicates if current local orbital frame shall be treated as pseudo-inertial.
427      * @return flag that indicates if current local orbital frame shall be treated as pseudo-inertial
428      */
429     default boolean isQuasiInertial() {
430         return false;
431     }
432 
433     /** Get name of the local orbital frame.
434      * @return name of the local orbital frame
435      */
436     String getName();
437 
438 }