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