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.complex.Complex;
21  import org.hipparchus.complex.ComplexField;
22  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23  import org.hipparchus.geometry.euclidean.threed.Rotation;
24  import org.hipparchus.geometry.euclidean.threed.Vector3D;
25  import org.hipparchus.random.RandomGenerator;
26  import org.hipparchus.random.Well19937a;
27  import org.hipparchus.util.Binary64;
28  import org.hipparchus.util.Binary64Field;
29  import org.hipparchus.util.FastMath;
30  import org.junit.jupiter.api.Assertions;
31  import org.junit.jupiter.api.Test;
32  import org.mockito.Mockito;
33  import org.orekit.time.AbsoluteDate;
34  import org.orekit.time.FieldAbsoluteDate;
35  import org.orekit.utils.FieldPVCoordinates;
36  import org.orekit.utils.PVCoordinates;
37  
38  public class TransformProviderUtilTest {
39  
40      @Test
41      void testGetCombinedProviderGetKinematicTransform() {
42          // GIVEN
43          final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
44          final TransformProvider mockedProvider1 = Mockito.mock(TransformProvider.class);
45          final TransformProvider mockedProvider2 = Mockito.mock(TransformProvider.class);
46          final KinematicTransform kinematicTransform = KinematicTransform.of(date, new PVCoordinates(Vector3D.MINUS_I,
47                  Vector3D.PLUS_J));
48          Mockito.when(mockedProvider1.getKinematicTransform(date)).thenReturn(kinematicTransform);
49          Mockito.when(mockedProvider2.getKinematicTransform(date)).thenReturn(kinematicTransform.getInverse());
50          final TransformProvider combinedProvider = TransformProviderUtils.getCombinedProvider(mockedProvider1,
51                  mockedProvider2);
52          // WHEN
53          final KinematicTransform actualTransform = combinedProvider.getKinematicTransform(date);
54          // THEN
55          final KinematicTransform expectedTransform = KinematicTransform.getIdentity();
56          Assertions.assertEquals(expectedTransform.getDate(), actualTransform.getDate());
57          Assertions.assertEquals(expectedTransform.getTranslation(), actualTransform.getTranslation());
58          Assertions.assertEquals(expectedTransform.getVelocity(), actualTransform.getVelocity());
59      }
60  
61      @Test
62      void testGetCombinedProviderFieldGetKinematicTransform() {
63          // GIVEN
64          final ComplexField field = ComplexField.getInstance();
65          final FieldAbsoluteDate<Complex> date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH);
66          final TransformProvider mockedProvider1 = Mockito.mock(TransformProvider.class);
67          final TransformProvider mockedProvider2 = Mockito.mock(TransformProvider.class);
68          final FieldKinematicTransform<Complex> kinematicTransform = FieldKinematicTransform.of(date,
69                  new FieldPVCoordinates<>(field, new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_J)));
70          Mockito.when(mockedProvider1.getKinematicTransform(date)).thenReturn(kinematicTransform);
71          Mockito.when(mockedProvider2.getKinematicTransform(date)).thenReturn(kinematicTransform.getInverse());
72          final TransformProvider combinedProvider = TransformProviderUtils.getCombinedProvider(mockedProvider1,
73                  mockedProvider2);
74          // WHEN
75          final FieldKinematicTransform<Complex> actualTransform = combinedProvider.getKinematicTransform(date);
76          // THEN
77          final FieldKinematicTransform<Complex> expectedTransform = FieldKinematicTransform.getIdentity(field);
78          Assertions.assertEquals(expectedTransform.getDate(), actualTransform.getDate());
79          Assertions.assertEquals(expectedTransform.getTranslation().toVector3D(),
80                  actualTransform.getTranslation().toVector3D());
81          Assertions.assertEquals(expectedTransform.getVelocity().toVector3D(),
82                  actualTransform.getVelocity().toVector3D());
83      }
84  
85      @Test
86      void testGetCombinedProviderFieldGetStaticTransform() {
87          // GIVEN
88          final ComplexField field = ComplexField.getInstance();
89          final FieldAbsoluteDate<Complex> date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH);
90          final TransformProvider mockedProvider1 = Mockito.mock(TransformProvider.class);
91          final TransformProvider mockedProvider2 = Mockito.mock(TransformProvider.class);
92          final FieldStaticTransform<Complex> staticTransform = FieldStaticTransform.of(date,
93                  new FieldVector3D<>(field, Vector3D.PLUS_J));
94          Mockito.when(mockedProvider1.getStaticTransform(date)).thenReturn(staticTransform);
95          Mockito.when(mockedProvider2.getStaticTransform(date)).thenReturn(staticTransform.getInverse());
96          final TransformProvider combinedProvider = TransformProviderUtils.getCombinedProvider(mockedProvider1,
97                  mockedProvider2);
98          // WHEN
99          final FieldStaticTransform<Complex> actualTransform = combinedProvider.getStaticTransform(date);
100         // THEN
101         final FieldStaticTransform<Complex> expectedTransform = FieldStaticTransform.getIdentity(field);
102         Assertions.assertEquals(expectedTransform.getDate(), actualTransform.getDate());
103         Assertions.assertEquals(expectedTransform.getTranslation().toVector3D(),
104                 actualTransform.getTranslation().toVector3D());
105     }
106 
107     @Test
108     void testGetReversedProviderGetKinematicTransform() {
109         // GIVEN
110         final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
111         final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class);
112         final TransformProvider reversedProvider = TransformProviderUtils.getReversedProvider(mockedProvider);
113         final KinematicTransform expectedTransform = Mockito.mock(KinematicTransform.class);
114         final KinematicTransform mockedTransform = Mockito.mock(KinematicTransform.class);
115         Mockito.when(mockedTransform.getInverse()).thenReturn(expectedTransform);
116         Mockito.when(mockedProvider.getKinematicTransform(date)).thenReturn(mockedTransform);
117         // WHEN
118         final KinematicTransform actualTransform = reversedProvider.getKinematicTransform(date);
119         // THEN
120         Assertions.assertEquals(expectedTransform, actualTransform);
121     }
122 
123     @Test
124     @SuppressWarnings("unchecked")
125     void testGetReversedProviderFieldGetKinematicTransform() {
126         // GIVEN
127         final FieldAbsoluteDate<Complex> date = new FieldAbsoluteDate<>(ComplexField.getInstance(),
128                 AbsoluteDate.ARBITRARY_EPOCH);
129         final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class);
130         final TransformProvider reversedProvider = TransformProviderUtils.getReversedProvider(mockedProvider);
131         final FieldKinematicTransform<Complex> expectedTransform = Mockito.mock(FieldKinematicTransform.class);
132         final FieldKinematicTransform<Complex> mockedTransform = Mockito.mock(FieldKinematicTransform.class);
133         Mockito.when(mockedTransform.getInverse()).thenReturn(expectedTransform);
134         Mockito.when(mockedProvider.getKinematicTransform(date)).thenReturn(mockedTransform);
135         // WHEN
136         final FieldKinematicTransform<Complex> actualTransform = reversedProvider.getKinematicTransform(date);
137         // THEN
138         Assertions.assertEquals(expectedTransform, actualTransform);
139     }
140 
141     @Test
142     @SuppressWarnings("unchecked")
143     void testGetReversedProviderFieldGetStaticTransform() {
144         // GIVEN
145         final FieldAbsoluteDate<Complex> date = new FieldAbsoluteDate<>(ComplexField.getInstance(),
146                 AbsoluteDate.ARBITRARY_EPOCH);
147         final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class);
148         final TransformProvider reversedProvider = TransformProviderUtils.getReversedProvider(mockedProvider);
149         final FieldStaticTransform<Complex> expectedTransform = Mockito.mock(FieldStaticTransform.class);
150         final FieldStaticTransform<Complex> mockedTransform = Mockito.mock(FieldStaticTransform.class);
151         Mockito.when(mockedTransform.getInverse()).thenReturn(expectedTransform);
152         Mockito.when(mockedProvider.getStaticTransform(date)).thenReturn(mockedTransform);
153         // WHEN
154         final FieldStaticTransform<Complex> actualTransform = reversedProvider.getStaticTransform(date);
155         // THEN
156         Assertions.assertEquals(expectedTransform, actualTransform);
157     }
158 
159     @Test
160     void testStaticIdentity() {
161         // GIVEN
162 
163         // WHEN
164         final StaticTransform staticTransform = TransformProviderUtils.IDENTITY_PROVIDER.getStaticTransform(Mockito.mock(AbsoluteDate.class));
165         // THEN
166         Assertions.assertEquals(Rotation.IDENTITY, staticTransform.getRotation());
167         Assertions.assertEquals(Vector3D.ZERO, staticTransform.getTranslation());
168     }
169 
170     @Test
171     void testFieldStaticIdentity() {
172         // GIVEN
173         final FieldAbsoluteDate<Complex> fieldDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), AbsoluteDate.BEIDOU_EPOCH);
174         // WHEN
175         final FieldStaticTransform<Complex> staticTransform = TransformProviderUtils.IDENTITY_PROVIDER.getStaticTransform(fieldDate);
176         // THEN
177         Assertions.assertEquals(0., Rotation.distance(Rotation.IDENTITY, staticTransform.getRotation().toRotation()));
178         Assertions.assertEquals(Vector3D.ZERO, staticTransform.getTranslation().toVector3D());
179     }
180 
181     @Test
182     public void testIdentity() {
183         RandomGenerator random = new Well19937a(0x87c3a5c51fb0235el);
184         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
185         checkNoTransform(TransformProviderUtils.IDENTITY_PROVIDER.getTransform(date), random);
186     }
187 
188     @Test
189     public void testIdentityField() {
190         RandomGenerator random = new Well19937a(0x7086a8e4ad1265b0l);
191         final FieldAbsoluteDate<Binary64> date = new FieldAbsoluteDate<>(Binary64Field.getInstance(),
192                                                                           AbsoluteDate.J2000_EPOCH);
193         checkNoTransform(TransformProviderUtils.IDENTITY_PROVIDER.getTransform(date), random);
194     }
195 
196     @Test
197     public void testReverse() {
198         RandomGenerator random = new Well19937a(0xba49d4909717ec6cl);
199         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
200         for (int i = 0; i < 20; ++i) {
201             TransformProvider tp = constantProvider(random);
202             TransformProvider reversed = TransformProviderUtils.getReversedProvider(tp);
203             checkNoTransform(new Transform(date, tp.getTransform(date), reversed.getTransform(date)), random);
204             checkNoTransform(new Transform(date, reversed.getTransform(date), tp.getTransform(date)), random);
205         }
206     }
207 
208     @Test
209     public void testReverseField() {
210         RandomGenerator random = new Well19937a(0xd74443b3079403e7l);
211         final FieldAbsoluteDate<Binary64> date = new FieldAbsoluteDate<>(Binary64Field.getInstance(),
212                                                                           AbsoluteDate.J2000_EPOCH);
213         for (int i = 0; i < 20; ++i) {
214             TransformProvider tp = constantProvider(random);
215             TransformProvider reversed = TransformProviderUtils.getReversedProvider(tp);
216             checkNoTransform(new FieldTransform<>(date, tp.getTransform(date), reversed.getTransform(date)), random);
217             checkNoTransform(new FieldTransform<>(date, reversed.getTransform(date), tp.getTransform(date)), random);
218         }
219     }
220 
221     @Test
222     public void testCombine() {
223         RandomGenerator random = new Well19937a(0x6e3b2c793680e7e3l);
224         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
225         for (int i = 0; i < 20; ++i) {
226             TransformProvider first  = constantProvider(random);
227             TransformProvider second = constantProvider(random);
228             TransformProvider combined = TransformProviderUtils.getCombinedProvider(first, second);
229             checkNoTransform(new Transform(date,
230                                            new Transform(date, first.getTransform(date), second.getTransform(date)).getInverse(),
231                                            combined.getTransform(date)),
232                              random);
233         }
234     }
235 
236     @Test
237     public void testCombineField() {
238         RandomGenerator random = new Well19937a(0x1f8bf20bfa4b54eal);
239         final FieldAbsoluteDate<Binary64> date = new FieldAbsoluteDate<>(Binary64Field.getInstance(),
240                                                                           AbsoluteDate.J2000_EPOCH);
241         for (int i = 0; i < 20; ++i) {
242             TransformProvider first  = constantProvider(random);
243             TransformProvider second = constantProvider(random);
244             TransformProvider combined = TransformProviderUtils.getCombinedProvider(first, second);
245             checkNoTransform(new FieldTransform<>(date,
246                                                   new FieldTransform<>(date, first.getTransform(date), second.getTransform(date)).getInverse(),
247                                                   combined.getTransform(date)),
248                              random);
249         }
250     }
251 
252     private TransformProvider constantProvider(RandomGenerator random) {
253         final Transform combined = randomTransform(random);
254         return new TransformProvider() {
255             public <T extends CalculusFieldElement<T>> FieldTransform<T> getTransform(FieldAbsoluteDate<T> date)
256                 {
257                 return new FieldTransform<>(date.getField(), combined);
258             }
259             public Transform getTransform(AbsoluteDate date)
260                 {
261                 return combined;
262             }
263         };
264     }
265 
266     private Transform randomTransform(RandomGenerator random) {
267         // generate a random transform
268         Transform combined = Transform.IDENTITY;
269         for (int k = 0; k < 20; ++k) {
270             Transform t = random.nextBoolean() ?
271                           new Transform(AbsoluteDate.J2000_EPOCH, randomVector(1.0e3, random), randomVector(1.0, random), randomVector(1.0e-3, random)) :
272                           new Transform(AbsoluteDate.J2000_EPOCH, randomRotation(random), randomVector(0.01, random), randomVector(1.0e-4, random));
273             combined = new Transform(AbsoluteDate.J2000_EPOCH, combined, t);
274         }
275         return combined;
276     }
277 
278     private Vector3D randomVector(double scale, RandomGenerator random) {
279         return new Vector3D(random.nextDouble() * scale,
280                             random.nextDouble() * scale,
281                             random.nextDouble() * scale);
282     }
283 
284     private Rotation randomRotation(RandomGenerator random) {
285         double q0 = random.nextDouble() * 2 - 1;
286         double q1 = random.nextDouble() * 2 - 1;
287         double q2 = random.nextDouble() * 2 - 1;
288         double q3 = random.nextDouble() * 2 - 1;
289         double q  = FastMath.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
290         return new Rotation(q0 / q, q1 / q, q2 / q, q3 / q, false);
291     }
292 
293     private void checkNoTransform(FieldTransform<Binary64> transform, RandomGenerator random) {
294         for (int i = 0; i < 100; ++i) {
295             Vector3D a = randomVector(1.0e3, random);
296             Vector3D tA = transform.transformVector(a).toVector3D();
297             Assertions.assertEquals(0, a.subtract(tA).getNorm(), 1.0e-10 * a.getNorm());
298             Vector3D b = randomVector(1.0e3, random);
299             Vector3D tB = transform.transformPosition(b).toVector3D();
300             Assertions.assertEquals(0, b.subtract(tB).getNorm(), 1.0e-10 * b.getNorm());
301             PVCoordinates pv  = new PVCoordinates(randomVector(1.0e3, random), randomVector(1.0, random), randomVector(1.0e-3, random));
302             PVCoordinates tPv = transform.transformPVCoordinates(pv).toPVCoordinates();
303             checkVector(pv.getPosition(),     tPv.getPosition(), 1.0e-10);
304             checkVector(pv.getVelocity(),     tPv.getVelocity(), 3.0e-9);
305             checkVector(pv.getAcceleration(), tPv.getAcceleration(), 3.0e-9);
306         }
307     }
308 
309     private void checkNoTransform(Transform transform, RandomGenerator random) {
310         for (int i = 0; i < 100; ++i) {
311             Vector3D a = randomVector(1.0e3, random);
312             Vector3D tA = transform.transformVector(a);
313             Assertions.assertEquals(0, a.subtract(tA).getNorm(), 1.0e-10 * a.getNorm());
314             Vector3D b = randomVector(1.0e3, random);
315             Vector3D tB = transform.transformPosition(b);
316             Assertions.assertEquals(0, b.subtract(tB).getNorm(), 1.0e-10 * b.getNorm());
317             PVCoordinates pv  = new PVCoordinates(randomVector(1.0e3, random), randomVector(1.0, random), randomVector(1.0e-3, random));
318             PVCoordinates tPv = transform.transformPVCoordinates(pv);
319             checkVector(pv.getPosition(),     tPv.getPosition(), 1.0e-10);
320             checkVector(pv.getVelocity(),     tPv.getVelocity(), 3.0e-9);
321             checkVector(pv.getAcceleration(), tPv.getAcceleration(), 3.0e-9);
322         }
323     }
324 
325     private void checkVector(Vector3D reference, Vector3D result, double relativeTolerance) {
326         double refNorm = reference.getNorm();
327         double resNorm = result.getNorm();
328         double tolerance = relativeTolerance * (1 + FastMath.max(refNorm, resNorm));
329         Assertions.assertEquals(0, Vector3D.distance(reference, result), tolerance,"ref = " + reference + ", res = " + result + " -> " +
330                 (Vector3D.distance(reference, result) / (1 + FastMath.max(refNorm, resNorm))));
331     }
332 
333 }