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 }