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.hamcrest.MatcherAssert;
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.Field;
22  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative1Field;
24  import org.hipparchus.complex.Complex;
25  import org.hipparchus.complex.ComplexField;
26  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27  import org.hipparchus.geometry.euclidean.threed.Rotation;
28  import org.hipparchus.geometry.euclidean.threed.Vector3D;
29  import org.hipparchus.util.Binary64Field;
30  import org.hipparchus.util.FastMath;
31  import org.junit.jupiter.api.Assertions;
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  import org.mockito.Mockito;
35  import org.orekit.OrekitMatchers;
36  import org.orekit.Utils;
37  import org.orekit.bodies.GeodeticPoint;
38  import org.orekit.bodies.OneAxisEllipsoid;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.FieldAbsoluteDate;
41  import org.orekit.time.TimeScalesFactory;
42  import org.orekit.utils.Constants;
43  import org.orekit.utils.IERSConventions;
44  import org.orekit.utils.PVCoordinates;
45  
46  import java.util.Random;
47  
48  class FrameTest {
49  
50      @Test
51      void testSameFrameRoot() {
52          Random random = new Random(0x29448c7d58b95565L);
53          Frame  frame  = FramesFactory.getEME2000();
54          checkNoTransform(frame.getTransformTo(frame, new AbsoluteDate()), random);
55          Assertions.assertTrue(frame.getDepth() > 0);
56          Assertions.assertEquals(frame.getParent().getDepth() + 1, frame.getDepth());
57      }
58  
59      @Test
60      void testSameFrameNoRoot() {
61          Random random = new Random(0xc6e88d0f53e29116L);
62          Transform t   = randomTransform(random);
63          Frame frame   = new Frame(FramesFactory.getEME2000(), t, null, true);
64          checkNoTransform(frame.getTransformTo(frame, new AbsoluteDate()), random);
65      }
66  
67      @Test
68      void testSimilarFrames() {
69          Random random = new Random(0x1b868f67a83666e5L);
70          Transform t   = randomTransform(random);
71          Frame frame1  = new Frame(FramesFactory.getEME2000(), t, null, true);
72          Frame frame2  = new Frame(FramesFactory.getEME2000(), t, null, false);
73          checkNoTransform(frame1.getTransformTo(frame2, new AbsoluteDate()), random);
74      }
75  
76      @Test
77      void testFromParent() {
78          Random random = new Random(0xb92fba1183fe11b8L);
79          Transform fromEME2000  = randomTransform(random);
80          Frame frame = new Frame(FramesFactory.getEME2000(), fromEME2000, null);
81          Transform toEME2000 = frame.getTransformTo(FramesFactory.getEME2000(), new AbsoluteDate());
82          checkNoTransform(new Transform(fromEME2000.getDate(), fromEME2000, toEME2000), random);
83      }
84  
85      @Test
86      void testDecomposedTransform() {
87          Random random = new Random(0xb7d1a155e726da57L);
88          Transform t1  = randomTransform(random);
89          Transform t2  = randomTransform(random);
90          Transform t3  = randomTransform(random);
91          Frame frame1 =
92              new Frame(FramesFactory.getEME2000(),
93                        new Transform(t1.getDate(), new Transform(t1.getDate(), t1, t2), t3),
94                        null);
95          Frame frame2 =
96              new Frame(new Frame(new Frame(FramesFactory.getEME2000(), t1, null), t2, null), t3, null);
97          checkNoTransform(frame1.getTransformTo(frame2, new AbsoluteDate()), random);
98      }
99  
100     @Test
101     void testFindCommon() {
102 
103         Random random = new Random(0xb7d1a155e726da57L);
104         Transform t1  = randomTransform(random);
105         Transform t2  = randomTransform(random);
106         Transform t3  = randomTransform(random);
107 
108         Frame R1 = new Frame(FramesFactory.getEME2000(), t1, "R1");
109         Frame R2 = new Frame(R1, t2, "R2");
110         Frame R3 = new Frame(R2, t3, "R3");
111         Assertions.assertTrue(R1.getDepth() > 0);
112         Assertions.assertEquals(R1.getDepth() + 1, R2.getDepth());
113         Assertions.assertEquals(R2.getDepth() + 1, R3.getDepth());
114 
115         Transform T = R1.getTransformTo(R3, new AbsoluteDate());
116 
117         Transform S = new Transform(t2.getDate(), t2, t3);
118 
119         checkNoTransform(new Transform(T.getDate(), T, S.getInverse()) , random);
120 
121     }
122 
123     @Test
124     void testDepthAndAncestor() {
125         Random random = new Random(0x01f8d3b944123044L);
126         Frame root = Frame.getRoot();
127 
128         Frame f1 = new Frame(root, randomTransform(random), "f1");
129         Frame f2 = new Frame(f1,   randomTransform(random), "f2");
130         Frame f3 = new Frame(f1,   randomTransform(random), "f3");
131         Frame f4 = new Frame(f2,   randomTransform(random), "f4");
132         Frame f5 = new Frame(f3,   randomTransform(random), "f5");
133         Frame f6 = new Frame(f5,   randomTransform(random), "f6");
134 
135         Assertions.assertEquals(0, root.getDepth());
136         Assertions.assertEquals(1, f1.getDepth());
137         Assertions.assertEquals(2, f2.getDepth());
138         Assertions.assertEquals(2, f3.getDepth());
139         Assertions.assertEquals(3, f4.getDepth());
140         Assertions.assertEquals(3, f5.getDepth());
141         Assertions.assertEquals(4, f6.getDepth());
142 
143         Assertions.assertSame(root, f1.getAncestor(1));
144         Assertions.assertSame(root, f6.getAncestor(4));
145         Assertions.assertSame(f1, f6.getAncestor(3));
146         Assertions.assertSame(f3, f6.getAncestor(2));
147         Assertions.assertSame(f5, f6.getAncestor(1));
148         Assertions.assertSame(f6, f6.getAncestor(0));
149 
150         try {
151             f6.getAncestor(5);
152             Assertions.fail("an exception should have been triggered");
153         } catch (IllegalArgumentException iae) {
154             // expected behavior
155         } catch (Exception e) {
156             Assertions.fail("wrong exception caught: " + e.getClass().getName());
157         }
158 
159     }
160 
161     @Test
162     void testIsChildOf() {
163         Random random = new Random(0xb7d1a155e726da78L);
164         Frame eme2000 = FramesFactory.getEME2000();
165 
166         Frame f1 = new Frame(eme2000, randomTransform(random), "f1");
167         Frame f2 = new Frame(f1     , randomTransform(random), "f2");
168         Frame f4 = new Frame(f2     , randomTransform(random), "f4");
169         Frame f5 = new Frame(f4     , randomTransform(random), "f5");
170         Frame f6 = new Frame(eme2000, randomTransform(random), "f6");
171         Frame f7 = new Frame(f6     , randomTransform(random), "f7");
172         Frame f8 = new Frame(f6     , randomTransform(random), "f8");
173         Frame f9 = new Frame(f7     , randomTransform(random), "f9");
174 
175         // check if the root frame can be an ancestor of another frame
176         Assertions.assertFalse(eme2000.isChildOf(f5));
177 
178         // check if a frame which belongs to the same branch than the 2nd frame is a branch of it
179         Assertions.assertTrue(f5.isChildOf(f1));
180 
181         // check if a random frame is the child of the root frame
182         Assertions.assertTrue(f9.isChildOf(eme2000));
183 
184         // check that a frame is not its own child
185         Assertions.assertFalse(f4.isChildOf(f4));
186 
187         // check if a frame which belongs to a different branch than the 2nd frame can be a child for it
188         Assertions.assertFalse(f9.isChildOf(f5));
189 
190         // check if the root frame is not a child of itself
191         Assertions.assertFalse(eme2000.isChildOf(eme2000));
192 
193         Assertions.assertFalse(f9.isChildOf(f8));
194 
195     }
196 
197     @Test
198     void testH0m9() {
199         AbsoluteDate h0         = new AbsoluteDate("2010-07-01T10:42:09", TimeScalesFactory.getUTC());
200         Frame itrf              = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
201         Frame rotatingPadFrame  = new TopocentricFrame(new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
202                                                                             Constants.WGS84_EARTH_FLATTENING,
203                                                                             itrf),
204                                                        new GeodeticPoint(FastMath.toRadians(5.0),
205                                                                                               FastMath.toRadians(-100.0),
206                                                                                               0.0),
207                                                        "launch pad");
208 
209         // create a new inertially oriented frame that is aligned with ITRF at h0 - 9 seconds
210         AbsoluteDate h0M9       = h0.shiftedBy(-9.0);
211         Frame eme2000           = FramesFactory.getEME2000();
212         Frame frozenLaunchFrame = rotatingPadFrame.getFrozenFrame(eme2000, h0M9, "launch frame");
213 
214         // check velocity module is unchanged
215         Vector3D pEme2000 = new Vector3D(-29536113.0, 30329259.0, -100125.0);
216         Vector3D vEme2000 = new Vector3D(-2194.0, -2141.0, -8.0);
217         PVCoordinates pvEme2000 = new PVCoordinates(pEme2000, vEme2000);
218         PVCoordinates pvH0m9 = eme2000.getTransformTo(frozenLaunchFrame, h0M9).transformPVCoordinates(pvEme2000);
219         Assertions.assertEquals(vEme2000.getNorm(), pvH0m9.getVelocity().getNorm(), 1.0e-6);
220         Vector3D pH0m9 = eme2000.getStaticTransformTo(frozenLaunchFrame, h0M9)
221                 .transformPosition(pvEme2000.getPosition());
222         MatcherAssert.assertThat(pH0m9,
223                 OrekitMatchers.vectorCloseTo(pvH0m9.getPosition(), 1e-15));
224 
225         // this frame is fixed with respect to EME2000 but rotates with respect to the non-frozen one
226         // the following loop should have a fixed angle a1 and an evolving angle a2
227         double minA1 = Double.POSITIVE_INFINITY;
228         double maxA1 = Double.NEGATIVE_INFINITY;
229         double minA2 = Double.POSITIVE_INFINITY;
230         double maxA2 = Double.NEGATIVE_INFINITY;
231         double dt;
232         for (dt = 0; dt < 86164; dt += 300.0) {
233             AbsoluteDate date = h0M9.shiftedBy(dt);
234             double a1 = frozenLaunchFrame.getTransformTo(eme2000,          date).getRotation().getAngle();
235             double a2 = frozenLaunchFrame.getTransformTo(rotatingPadFrame, date).getRotation().getAngle();
236             minA1 = FastMath.min(minA1, a1);
237             maxA1 = FastMath.max(maxA1, a1);
238             minA2 = FastMath.min(minA2, a2);
239             maxA2 = FastMath.max(maxA2, a2);
240         }
241         Assertions.assertEquals(0, maxA1 - minA1, 1.0e-12);
242         Assertions.assertEquals(FastMath.PI, maxA2 - minA2, 0.01);
243 
244     }
245 
246     private Transform randomTransform(Random random) {
247         Transform transform = Transform.IDENTITY;
248         for (int i = random.nextInt(10); i > 0; --i) {
249             if (random.nextBoolean()) {
250                 Vector3D u = new Vector3D(random.nextDouble() * 1000.0,
251                                           random.nextDouble() * 1000.0,
252                                           random.nextDouble() * 1000.0);
253                 transform = new Transform(transform.getDate(), transform, new Transform(transform.getDate(), u));
254             } else {
255                 double q0 = random.nextDouble() * 2 - 1;
256                 double q1 = random.nextDouble() * 2 - 1;
257                 double q2 = random.nextDouble() * 2 - 1;
258                 double q3 = random.nextDouble() * 2 - 1;
259                 double q  = FastMath.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
260                 Rotation r = new Rotation(q0 / q, q1 / q, q2 / q, q3 / q, false);
261                 transform = new Transform(transform.getDate(), transform, new Transform(transform.getDate(), r));
262             }
263         }
264         return transform;
265     }
266 
267     private void checkNoTransform(Transform transform, Random random) {
268         for (int i = 0; i < 100; ++i) {
269             Vector3D a = new Vector3D(random.nextDouble(),
270                                       random.nextDouble(),
271                                       random.nextDouble());
272             Vector3D b = transform.transformVector(a);
273             Assertions.assertEquals(0, a.subtract(b).getNorm(), 1.0e-10);
274             Vector3D c = transform.transformPosition(a);
275             Assertions.assertEquals(0, a.subtract(c).getNorm(), 1.0e-10);
276         }
277     }
278 
279     @Test
280     void testGetKinematicTransformTo() {
281         // GIVEN
282         final Frame oldFrame = FramesFactory.getEME2000();
283         final Frame newFrame = FramesFactory.getGCRF();
284         final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
285         // WHEN
286         final KinematicTransform kinematicTransform = oldFrame.getKinematicTransformTo(newFrame, date);
287         // THEN
288         final Transform transform = oldFrame.getTransformTo(newFrame, date);
289         Assertions.assertEquals(date, kinematicTransform.getDate());
290         Assertions.assertEquals(transform.getCartesian().getPosition(), kinematicTransform.getTranslation());
291         Assertions.assertEquals(transform.getCartesian().getVelocity(), kinematicTransform.getVelocity());
292         Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), kinematicTransform.getRotation()));
293         Assertions.assertEquals(transform.getRotationRate(), kinematicTransform.getRotationRate());
294     }
295 
296     @Test
297     void testGetStaticTransformIdentity() {
298         // GIVEN
299         final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
300         final Frame mockedFrame = Mockito.mock(Frame.class);
301         Mockito.when(mockedFrame.getStaticTransformTo(mockedFrame, date)).thenCallRealMethod();
302         // WHEN
303         final StaticTransform staticTransform = mockedFrame.getStaticTransformTo(mockedFrame, date);
304         // THEN
305         Assertions.assertEquals(staticTransform, staticTransform.getStaticInverse());
306     }
307 
308     @Test
309     void testGetStaticTransformIdentityField() {
310         // GIVEN
311         final FieldAbsoluteDate<Complex> fieldDate = FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance());
312         final Frame frame = FramesFactory.getGCRF();
313         // WHEN
314         final FieldStaticTransform<Complex> staticTransform = frame.getStaticTransformTo(frame, fieldDate);
315         // THEN
316         Assertions.assertEquals(staticTransform.getClass(), staticTransform.getStaticInverse().getClass());
317     }
318 
319     @Test
320     void testFieldGetKinematicTransformToWithConstantDate() {
321         templateTestFieldGetKinematicTransformTo(getComplexDate());
322     }
323 
324     @Test
325     void testFieldGetKinematicTransformToWithNonConstantDate() {
326         templateTestFieldGetKinematicTransformTo(getComplexDate().shiftedBy(Complex.I));
327     }
328 
329     private void templateTestFieldGetKinematicTransformTo(final FieldAbsoluteDate<Complex> fieldDate) {
330         // GIVEN
331         final Frame oldFrame = FramesFactory.getEME2000();
332         final Frame newFrame = FramesFactory.getGCRF();
333         // WHEN
334         final FieldKinematicTransform<Complex> fieldKinematicTransform = oldFrame.getKinematicTransformTo(newFrame,
335                 fieldDate);
336         // THEN
337         final KinematicTransform kinematicTransform = oldFrame.getKinematicTransformTo(newFrame,
338                 fieldDate.toAbsoluteDate());
339         Assertions.assertEquals(kinematicTransform.getDate(), fieldKinematicTransform.getDate());
340         Assertions.assertEquals(kinematicTransform.getTranslation(), fieldKinematicTransform.getTranslation().toVector3D());
341         Assertions.assertEquals(kinematicTransform.getVelocity(), fieldKinematicTransform.getVelocity().toVector3D());
342         Assertions.assertEquals(0., Rotation.distance(kinematicTransform.getRotation(),
343                 fieldKinematicTransform.getRotation().toRotation()));
344         Assertions.assertEquals(kinematicTransform.getRotationRate(),
345                 fieldKinematicTransform.getRotationRate().toVector3D());
346     }
347 
348     private FieldAbsoluteDate<Complex> getComplexDate() {
349         return FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance());
350     }
351 
352     @Test
353     void testGetTransformTo() {
354         // GIVEN
355         final Frame oldFrame = FramesFactory.getEME2000();
356         final Frame newFrame = FramesFactory.getGTOD(true);
357         final UnivariateDerivative1Field field = UnivariateDerivative1Field.getInstance();
358         final FieldAbsoluteDate<UnivariateDerivative1> fieldDate = FieldAbsoluteDate.getArbitraryEpoch(field);
359         final FieldAbsoluteDate<UnivariateDerivative1> shiftedDate = fieldDate.shiftedBy(new UnivariateDerivative1(0., 1));
360         // WHEN
361         final FieldTransform<UnivariateDerivative1> fieldTransform = oldFrame.getTransformTo(newFrame, shiftedDate);
362         // THEN
363         Assertions.assertEquals(shiftedDate, fieldTransform.getFieldDate());
364         final FieldTransform<UnivariateDerivative1> referenceTransform = oldFrame.getTransformTo(newFrame, fieldDate);
365         Assertions.assertEquals(fieldTransform.getDate(), referenceTransform.getDate());
366         Assertions.assertEquals(fieldTransform.getTranslation().toVector3D(),
367                 referenceTransform.getTranslation().toVector3D());
368         compareFieldVectorWithMargin(fieldTransform.getRotationRate(), referenceTransform.getRotationRate());
369         compareFieldVectorWithMargin(fieldTransform.getRotationAcceleration(), referenceTransform.getRotationAcceleration());
370         Assertions.assertEquals(0., Rotation.distance(fieldTransform.getRotation().toRotation(),
371                 referenceTransform.getRotation().toRotation()));
372     }
373 
374     @Test
375     public void testNoPeering() {
376 
377         Frame eme2000 = FramesFactory.getEME2000();
378         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
379         Assertions.assertNull(eme2000.getPeer());
380         Assertions.assertNull(itrf.getPeer());
381 
382         // with caching disabled, tB should be a new recomputed instance, similar to tA
383         final AbsoluteDate t0 = new AbsoluteDate(2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
384         final Transform tA = eme2000.getTransformTo(itrf, t0);
385         final Transform tB = eme2000.getTransformTo(itrf, t0);
386         final Transform backAndForth = new Transform(t0, tA, tB.getInverse());
387         Assertions.assertNotSame(tA, tB);
388         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle(), 1.0e-20);
389         Assertions.assertEquals(0.0, backAndForth.getCartesian().getPosition().getNorm(), 1.0e-20);
390 
391     }
392 
393     @Test
394     public void testNoPeeringKinematic() {
395 
396         Frame eme2000 = FramesFactory.getEME2000();
397         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
398         Assertions.assertNull(eme2000.getPeer());
399         Assertions.assertNull(itrf.getPeer());
400 
401         // with caching disabled, tB should be a new recomputed instance, similar to tA
402         final AbsoluteDate t0 = new AbsoluteDate(2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
403         final KinematicTransform tA = eme2000.getKinematicTransformTo(itrf, t0);
404         final KinematicTransform tB = eme2000.getKinematicTransformTo(itrf, t0);
405         final KinematicTransform backAndForth = KinematicTransform.compose(t0, tA, tB.getInverse());
406         Assertions.assertNotSame(tA, tB);
407         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle(), 1.0e-20);
408         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm(), 1.0e-20);
409 
410     }
411 
412     @Test
413     public void testNoPeeringStatic() {
414 
415         Frame eme2000 = FramesFactory.getEME2000();
416         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
417         Assertions.assertNull(eme2000.getPeer());
418         Assertions.assertNull(itrf.getPeer());
419 
420         // with caching disabled, tB should be a new recomputed instance, similar to tA
421         final AbsoluteDate t0 = new AbsoluteDate(2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
422         final StaticTransform tA = eme2000.getStaticTransformTo(itrf, t0);
423         final StaticTransform tB = eme2000.getStaticTransformTo(itrf, t0);
424         final StaticTransform backAndForth = StaticTransform.compose(t0, tA, tB.getInverse());
425         Assertions.assertNotSame(tA, tB);
426         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle(), 1.0e-20);
427         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm(), 1.0e-20);
428 
429     }
430 
431     @Test
432     public void testNoPeeringField() {
433         doTestNoPeeringField(Binary64Field.getInstance());
434     }
435 
436     private <T extends CalculusFieldElement<T>> void doTestNoPeeringField(final Field<T> field) {
437 
438         Frame eme2000 = FramesFactory.getEME2000();
439         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
440         Assertions.assertNull(eme2000.getPeer());
441         Assertions.assertNull(itrf.getPeer());
442 
443         // with caching disabled, tB should be a new recomputed instance, similar to tA
444         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
445         final FieldTransform<T> tA = eme2000.getTransformTo(itrf, t0);
446         final FieldTransform<T> tB = eme2000.getTransformTo(itrf, t0);
447         final FieldTransform<T> backAndForth = new FieldTransform<>(t0, tA, tB.getInverse());
448         Assertions.assertNotSame(tA, tB);
449         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle().getReal(), 1.0e-20);
450         Assertions.assertEquals(0.0, backAndForth.getCartesian().getPosition().getNorm().getReal(), 1.0e-20);
451 
452     }
453 
454     @Test
455     public void testNoPeeringFieldKinematic() {
456         doTestNoPeeringFieldKinematic(Binary64Field.getInstance());
457     }
458 
459     private <T extends CalculusFieldElement<T>> void doTestNoPeeringFieldKinematic(final Field<T> field) {
460 
461         Frame eme2000 = FramesFactory.getEME2000();
462         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
463         Assertions.assertNull(eme2000.getPeer());
464         Assertions.assertNull(itrf.getPeer());
465 
466         // with caching disabled, tB should be a new recomputed instance, similar to tA
467         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
468         final FieldKinematicTransform<T> tA = eme2000.getKinematicTransformTo(itrf, t0);
469         final FieldKinematicTransform<T> tB = eme2000.getKinematicTransformTo(itrf, t0);
470         final FieldKinematicTransform<T> backAndForth = FieldKinematicTransform.compose(t0, tA, tB.getInverse());
471         Assertions.assertNotSame(tA, tB);
472         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle().getReal(), 1.0e-20);
473         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm().getReal(), 1.0e-20);
474 
475     }
476 
477     @Test
478     public void testNoPeeringFieldStatic() {
479         doTestNoPeeringFieldStatic(Binary64Field.getInstance());
480     }
481 
482     private <T extends CalculusFieldElement<T>> void doTestNoPeeringFieldStatic(final Field<T> field) {
483 
484         Frame eme2000 = FramesFactory.getEME2000();
485         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
486         Assertions.assertNull(eme2000.getPeer());
487         Assertions.assertNull(itrf.getPeer());
488 
489         // with caching disabled, tB should be a new recomputed instance, similar to tA
490         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
491         final FieldStaticTransform<T> tA = eme2000.getStaticTransformTo(itrf, t0);
492         final FieldStaticTransform<T> tB = eme2000.getStaticTransformTo(itrf, t0);
493         final FieldStaticTransform<T> backAndForth = FieldStaticTransform.compose(t0, tA, tB.getInverse());
494         Assertions.assertNotSame(tA, tB);
495         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle().getReal(), 1.0e-20);
496         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm().getReal(), 1.0e-20);
497 
498     }
499 
500     @Test
501     public void testPeering() {
502 
503         Frame eme2000 = FramesFactory.getEME2000();
504         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
505         Assertions.assertNull(eme2000.getPeer());
506         Assertions.assertNull(itrf.getPeer());
507 
508         final int cachesize = 20;
509         eme2000.setPeerCaching(itrf, cachesize);
510         Assertions.assertSame(itrf,    eme2000.getPeer());
511         Assertions.assertNull(itrf.getPeer());
512 
513         // with caching activated, tB should be a reference to the same transform as tA
514         // without being recomputed
515         final AbsoluteDate t0 = new AbsoluteDate(2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
516         final Transform tA = eme2000.getTransformTo(itrf, t0);
517         final Transform tB = eme2000.getTransformTo(itrf, t0);
518         final Transform backAndForth = new Transform(t0, tA, tB.getInverse());
519         Assertions.assertSame(tA, tB);
520         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle(), 1.0e-20);
521         Assertions.assertEquals(0.0, backAndForth.getCartesian().getPosition().getNorm(), 1.0e-20);
522 
523         // non-peered frames should still be recomputed
524         Frame gcrf = FramesFactory.getGCRF();
525         Assertions.assertNotSame(eme2000.getTransformTo(gcrf, t0),
526                                  eme2000.getTransformTo(gcrf, t0));
527 
528         final Transform[] direct  = new Transform[cachesize];
529         for ( int i = 0; i < cachesize; i++ ) {
530             direct[i]  = eme2000.getTransformTo(itrf, t0.shiftedBy(i));
531         }
532 
533         // with caching activated, we should not recompute any transform, just retrieve existing ones
534         for (int i = 0; i < 10000; ++i) {
535             Assertions.assertSame(direct[i % cachesize], eme2000.getTransformTo(itrf, t0.shiftedBy(i % cachesize)));
536         }
537 
538     }
539 
540     @Test
541     public void testPeeringKinematic() {
542 
543         Frame eme2000 = FramesFactory.getEME2000();
544         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
545         Assertions.assertNull(eme2000.getPeer());
546         Assertions.assertNull(itrf.getPeer());
547 
548         final int cachesize = 20;
549         eme2000.setPeerCaching(itrf, cachesize);
550         Assertions.assertSame(itrf,    eme2000.getPeer());
551         Assertions.assertNull(itrf.getPeer());
552 
553         // with caching activated, tB should be a reference to the same transform as tA
554         // without being recomputed
555         final AbsoluteDate t0 = new AbsoluteDate(2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
556         final KinematicTransform tA = eme2000.getKinematicTransformTo(itrf, t0);
557         final KinematicTransform tB = eme2000.getKinematicTransformTo(itrf, t0);
558         final KinematicTransform backAndForth = KinematicTransform.compose(t0, tA, tB.getInverse());
559         Assertions.assertSame(tA, tB);
560         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle(), 1.0e-20);
561         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm(), 1.0e-20);
562 
563         // non-peered frames should still be recomputed
564         Frame gcrf = FramesFactory.getGCRF();
565         Assertions.assertNotSame(eme2000.getKinematicTransformTo(gcrf, t0),
566                                  eme2000.getKinematicTransformTo(gcrf, t0));
567 
568         final KinematicTransform[] direct  = new KinematicTransform[cachesize];
569         for ( int i = 0; i < cachesize; i++ ) {
570             direct[i]  = eme2000.getKinematicTransformTo(itrf, t0.shiftedBy(i));
571         }
572 
573         // with caching activated, we should not recompute any transform, just retrieve existing ones
574         for (int i = 0; i < 10000; ++i) {
575             Assertions.assertSame(direct[i % cachesize], eme2000.getKinematicTransformTo(itrf, t0.shiftedBy(i % cachesize)));
576         }
577 
578     }
579 
580     @Test
581     public void testPeeringStatic() {
582 
583         Frame eme2000 = FramesFactory.getEME2000();
584         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
585         Assertions.assertNull(eme2000.getPeer());
586         Assertions.assertNull(itrf.getPeer());
587 
588         final int cachesize = 20;
589         eme2000.setPeerCaching(itrf, cachesize);
590         Assertions.assertSame(itrf,    eme2000.getPeer());
591         Assertions.assertNull(itrf.getPeer());
592 
593         // with caching activated, tB should be a reference to the same transform as tA
594         // without being recomputed
595         final AbsoluteDate t0 = new AbsoluteDate(2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
596         final StaticTransform tA = eme2000.getStaticTransformTo(itrf, t0);
597         final StaticTransform tB = eme2000.getStaticTransformTo(itrf, t0);
598         final StaticTransform backAndForth = StaticTransform.compose(t0, tA, tB.getInverse());
599         Assertions.assertSame(tA, tB);
600         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle(), 1.0e-20);
601         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm(), 1.0e-20);
602 
603         // non-peered frames should still be recomputed
604         Frame gcrf = FramesFactory.getGCRF();
605         Assertions.assertNotSame(eme2000.getStaticTransformTo(gcrf, t0),
606                                  eme2000.getStaticTransformTo(gcrf, t0));
607 
608         final StaticTransform[] direct  = new StaticTransform[cachesize];
609         for ( int i = 0; i < cachesize; i++ ) {
610             direct[i]  = eme2000.getStaticTransformTo(itrf, t0.shiftedBy(i));
611         }
612 
613         // with caching activated, we should not recompute any transform, just retrieve existing ones
614         for (int i = 0; i < 10000; ++i) {
615             Assertions.assertSame(direct[i % cachesize], eme2000.getStaticTransformTo(itrf, t0.shiftedBy(i % cachesize)));
616         }
617 
618     }
619 
620     @Test
621     public void testPeeringField() {
622         doTestPeeringField(Binary64Field.getInstance());
623     }
624 
625     private <T extends CalculusFieldElement<T>> void doTestPeeringField(final Field<T> field) {
626 
627         Frame eme2000 = FramesFactory.getEME2000();
628         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
629         Assertions.assertNull(eme2000.getPeer());
630         Assertions.assertNull(itrf.getPeer());
631 
632         // with caching activated, tB should be a reference to the same transform as tA
633         // without being recomputed
634         final int cachesize = 20;
635         eme2000.setPeerCaching(itrf, cachesize);
636         Assertions.assertSame(itrf,    eme2000.getPeer());
637         Assertions.assertNull(itrf.getPeer());
638 
639         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
640         final FieldTransform<T> tA = eme2000.getTransformTo(itrf, t0);
641         final FieldTransform<T> tB = eme2000.getTransformTo(itrf, t0);
642         final FieldTransform<T> backAndForth = new FieldTransform<>(t0, tA, tB.getInverse());
643         Assertions.assertSame(tA, tB);
644         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle().getReal(), 1.0e-20);
645         Assertions.assertEquals(0.0, backAndForth.getCartesian().getPosition().getNorm().getReal(), 1.0e-20);
646 
647         // non-peered frames should still be recomputed
648         Frame gcrf = FramesFactory.getGCRF();
649         Assertions.assertNotSame(eme2000.getTransformTo(gcrf, t0),
650                                  eme2000.getTransformTo(gcrf, t0));
651 
652         final FieldTransform<T>[] direct  = new FieldTransform[cachesize];
653         for ( int i = 0; i < cachesize; i++ ) {
654             direct[i]  = eme2000.getTransformTo(itrf, t0.shiftedBy(i));
655         }
656 
657         // with caching activated, we should not recompute any transform, just retrieve existing ones
658         for (int i = 0; i < 10000; ++i) {
659             Assertions.assertSame(direct[i % cachesize], eme2000.getTransformTo(itrf, t0.shiftedBy(i % cachesize)));
660         }
661 
662     }
663 
664     @Test
665     public void testPeeringFieldKinematic() {
666         doTestPeeringFieldKinematic(Binary64Field.getInstance());
667     }
668 
669     private <T extends CalculusFieldElement<T>> void doTestPeeringFieldKinematic(final Field<T> field) {
670 
671         Frame eme2000 = FramesFactory.getEME2000();
672         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
673         Assertions.assertNull(eme2000.getPeer());
674         Assertions.assertNull(itrf.getPeer());
675 
676         // with caching activated, tB should be a reference to the same transform as tA
677         // without being recomputed
678         final int cachesize = 20;
679         eme2000.setPeerCaching(itrf, cachesize);
680         Assertions.assertSame(itrf,    eme2000.getPeer());
681         Assertions.assertNull(itrf.getPeer());
682 
683         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
684         final FieldKinematicTransform<T> tA = eme2000.getKinematicTransformTo(itrf, t0);
685         final FieldKinematicTransform<T> tB = eme2000.getKinematicTransformTo(itrf, t0);
686         final FieldKinematicTransform<T> backAndForth = FieldKinematicTransform.compose(t0, tA, tB.getInverse());
687         Assertions.assertSame(tA, tB);
688         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle().getReal(), 1.0e-20);
689         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm().getReal(), 1.0e-20);
690 
691         // non-peered frames should still be recomputed
692         Frame gcrf = FramesFactory.getGCRF();
693         Assertions.assertNotSame(eme2000.getKinematicTransformTo(gcrf, t0),
694                                  eme2000.getKinematicTransformTo(gcrf, t0));
695 
696         final FieldKinematicTransform<T>[] direct  = new FieldKinematicTransform[cachesize];
697         for ( int i = 0; i < cachesize; i++ ) {
698             direct[i]  = eme2000.getKinematicTransformTo(itrf, t0.shiftedBy(i));
699         }
700 
701         // with caching activated, we should not recompute any transform, just retrieve existing ones
702         for (int i = 0; i < 10000; ++i) {
703             Assertions.assertSame(direct[i % cachesize], eme2000.getKinematicTransformTo(itrf, t0.shiftedBy(i % cachesize)));
704         }
705 
706     }
707 
708     @Test
709     public void testPeeringFieldStatic() {
710         doTestPeeringFieldStatic(Binary64Field.getInstance());
711     }
712 
713     private <T extends CalculusFieldElement<T>> void doTestPeeringFieldStatic(final Field<T> field) {
714 
715         Frame eme2000 = FramesFactory.getEME2000();
716         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
717         Assertions.assertNull(eme2000.getPeer());
718         Assertions.assertNull(itrf.getPeer());
719 
720         // with caching activated, tB should be a reference to the same transform as tA
721         // without being recomputed
722         final int cachesize = 20;
723         eme2000.setPeerCaching(itrf, cachesize);
724         Assertions.assertSame(itrf,    eme2000.getPeer());
725         Assertions.assertNull(itrf.getPeer());
726 
727         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2004, 5, 7, 17, 42, 37.5, TimeScalesFactory.getUTC());
728         final FieldStaticTransform<T> tA = eme2000.getStaticTransformTo(itrf, t0);
729         final FieldStaticTransform<T> tB = eme2000.getStaticTransformTo(itrf, t0);
730         final FieldStaticTransform<T> backAndForth = FieldStaticTransform.compose(t0, tA, tB.getInverse());
731         Assertions.assertSame(tA, tB);
732         Assertions.assertEquals(0.0, backAndForth.getRotation().getAngle().getReal(), 1.0e-20);
733         Assertions.assertEquals(0.0, backAndForth.getTranslation().getNorm().getReal(), 1.0e-20);
734 
735         // non-peered frames should still be recomputed
736         Frame gcrf = FramesFactory.getGCRF();
737         Assertions.assertNotSame(eme2000.getStaticTransformTo(gcrf, t0),
738                                  eme2000.getStaticTransformTo(gcrf, t0));
739 
740         final FieldStaticTransform<T>[] direct  = new FieldStaticTransform[cachesize];
741         for ( int i = 0; i < cachesize; i++ ) {
742             direct[i]  = eme2000.getStaticTransformTo(itrf, t0.shiftedBy(i));
743         }
744 
745         // with caching activated, we should not recompute any transform, just retrieve existing ones
746         for (int i = 0; i < 10000; ++i) {
747             Assertions.assertSame(direct[i % cachesize], eme2000.getStaticTransformTo(itrf, t0.shiftedBy(i % cachesize)));
748         }
749 
750     }
751 
752     private static <T extends CalculusFieldElement<T>> void compareFieldVectorWithMargin(final FieldVector3D<T> expectedVector,
753                                                                                          final FieldVector3D<T> actualVector) {
754         Assertions.assertEquals(0., actualVector.toVector3D().subtract(expectedVector.toVector3D()).getNorm(),
755                 1e-12);
756     }
757 
758     @BeforeEach
759     public void setUp() {
760         Utils.setDataRoot("compressed-data");
761     }
762 
763 }