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 }