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;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.analysis.differentiation.DerivativeStructure;
21  import org.hipparchus.analysis.differentiation.Gradient;
22  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.geometry.euclidean.twod.FieldVector2D;
25  import org.hipparchus.geometry.euclidean.twod.Vector2D;
26  import org.hipparchus.linear.BlockRealMatrix;
27  import org.hipparchus.linear.FieldMatrix;
28  import org.hipparchus.linear.RealMatrix;
29  import org.hipparchus.util.Binary64;
30  import org.hipparchus.util.Binary64Field;
31  import org.hipparchus.util.FastMath;
32  import org.hipparchus.util.MathArrays;
33  import org.junit.jupiter.api.Assertions;
34  import org.orekit.frames.Frame;
35  import org.orekit.frames.FramesFactory;
36  import org.orekit.frames.LOFType;
37  import org.orekit.orbits.CartesianOrbit;
38  import org.orekit.orbits.EquinoctialOrbit;
39  import org.orekit.orbits.FieldCartesianOrbit;
40  import org.orekit.orbits.FieldOrbit;
41  import org.orekit.orbits.Orbit;
42  import org.orekit.propagation.AdditionalDataProvider;
43  import org.orekit.propagation.FieldAdditionalDataProvider;
44  import org.orekit.propagation.FieldSpacecraftState;
45  import org.orekit.propagation.SpacecraftState;
46  import org.orekit.propagation.StateCovariance;
47  import org.orekit.time.AbsoluteDate;
48  import org.orekit.time.FieldAbsoluteDate;
49  import org.orekit.utils.AbsolutePVCoordinates;
50  import org.orekit.utils.Constants;
51  import org.orekit.utils.FieldAbsolutePVCoordinates;
52  import org.orekit.utils.FieldPVCoordinates;
53  import org.orekit.utils.PVCoordinates;
54  import org.orekit.utils.TimeStampedPVCoordinates;
55  
56  import java.util.Arrays;
57  import java.util.Collection;
58  
59  /**
60   * Utility class for testing purposes, providing methods for generating
61   * fake data, validation of mathematical entities, and utility functions
62   * for debugging and testing scenarios.
63   */
64  public class TestUtils {
65  
66      private TestUtils() {
67          //Empty
68      }
69  
70      /**
71       * Create and return a default orbit.
72       *
73       * @param date date of the orbit
74       * @return default orbit
75       */
76      public static Orbit getDefaultOrbit(final AbsoluteDate date) {
77          return new CartesianOrbit(new PVCoordinates(
78                  new Vector3D(6778000, 0, 0),
79                  new Vector3D(0, 7668.631425, 0)),
80                                    FramesFactory.getGCRF(), date, 398600e9);
81      }
82  
83      /**
84       * Create and return a default orbit with non keplerian derivatives.
85       *
86       * @param date date of the orbit
87       * @return default orbit
88       */
89      public static Orbit getDefaultOrbitWithDerivatives(final AbsoluteDate date) {
90          final Vector3D position     = new Vector3D(6896874.444705, 1956581.072644, -147476.245054);
91          final Vector3D velocity     = new Vector3D(166.816407662, -1106.783301861, -7372.745712770);
92          final Vector3D acceleration = new Vector3D(-7.466182457944, -2.118153357345, 0.160004048437);
93          final TimeStampedPVCoordinates pv =
94                  new TimeStampedPVCoordinates(date, position, velocity, acceleration);
95          final Frame  frame = FramesFactory.getEME2000();
96          final double mu    = Constants.EIGEN5C_EARTH_MU;
97  
98          return new EquinoctialOrbit(pv, frame, mu);
99      }
100 
101     /**
102      * Create and return a default orbit.
103      *
104      * @param date date of the orbit
105      * @return default orbit
106      */
107     public static <T extends CalculusFieldElement<T>> FieldOrbit<T> getDefaultFieldOrbit(FieldAbsoluteDate<T> date) {
108         return new FieldCartesianOrbit<>(date.getField(), getDefaultOrbit(date.toAbsoluteDate()));
109     }
110 
111     /**
112      * Create and return a default field orbit with non keplerian derivatives.
113      *
114      * @param date date of the orbit
115      * @param <T>  type of the field elements
116      * @return default orbit
117      */
118     public static <T extends CalculusFieldElement<T>> FieldOrbit<T> getDefaultFieldOrbitWithDerivatives(final FieldAbsoluteDate<T> date) {
119         return new FieldCartesianOrbit<>(date.getField(), getDefaultOrbit(date.toAbsoluteDate()));
120     }
121 
122     /**
123      * Method created to test issue 949.
124      *
125      * @return additional state provider with custom init() method defined which use the initial state
126      */
127     public static AdditionalDataProvider<double[]> getAdditionalProviderWithInit() {
128         return new AdditionalDataProvider<double[]>() {
129             /**
130              * Custom init method which use the initial state instance.
131              *
132              * @param initialState initial state information at the start of propagation
133              * @param target date of propagation
134              */
135             @Override
136             public void init(final SpacecraftState initialState, final AbsoluteDate target) {
137                 // do nothing
138             }
139 
140             @Override
141             public String getName() {
142                 return "Test";
143             }
144 
145             @Override
146             public double[] getAdditionalData(final SpacecraftState state) {
147                 return new double[0];
148             }
149         };
150     }
151 
152     public static PVCoordinates getFakePVCoordinates() {
153         return new PVCoordinates(new Vector3D(1, 2, 3),
154                                  new Vector3D(4, 5, 6));
155     }
156 
157     public static AbsolutePVCoordinates getFakeAbsolutePVCoordinates() {
158         return new AbsolutePVCoordinates(FramesFactory.getGCRF(),
159                                          new AbsoluteDate(),
160                                          getFakePVCoordinates());
161     }
162 
163     public static Orbit getFakeOrbit() {
164         return new CartesianOrbit(getFakePVCoordinates(),
165                                   FramesFactory.getGCRF(),
166                                   new AbsoluteDate(),
167                                   1);
168     }
169 
170     public static FieldOrbit<Binary64> getFakeFieldOrbit() {
171         return new FieldCartesianOrbit<>(getFakeFieldPVCoordinates(),
172                                          FramesFactory.getGCRF(),
173                                          getFakeFieldAbsoluteDate(),
174                                          new Binary64(1));
175     }
176 
177     public static FieldPVCoordinates<Binary64> getFieldPVCoordinates() {
178         return new FieldPVCoordinates<>(new FieldVector3D<>(new Binary64(6878e3), new Binary64(0), new Binary64(0)),
179                                         new FieldVector3D<>(new Binary64(5400), new Binary64(0), new Binary64(5400)));
180     }
181 
182     public static FieldOrbit<Binary64> getTestFieldOrbit() {
183         return new FieldCartesianOrbit<>(getFieldPVCoordinates(),
184                                          FramesFactory.getGCRF(),
185                                          getFakeFieldAbsoluteDate(),
186                                          new Binary64(Constants.IERS2010_EARTH_MU));
187     }
188 
189     public static Orbit getTestOrbit() {
190         return new CartesianOrbit(getPVCoordinates(),
191                                   FramesFactory.getGCRF(),
192                                   new AbsoluteDate(),
193                                   Constants.IERS2010_EARTH_MU);
194     }
195 
196     private static PVCoordinates getPVCoordinates() {
197         return new PVCoordinates(new Vector3D(6878e3, 0, 0),
198                                  new Vector3D(5400, 0, 5400));
199     }
200 
201     public static FieldAbsoluteDate<Binary64> getFakeFieldAbsoluteDate() {
202         return new FieldAbsoluteDate<>(Binary64Field.getInstance());
203     }
204 
205     public static FieldPVCoordinates<Binary64> getFakeFieldPVCoordinates() {
206         final FieldVector3D<Binary64> fakePosition = new FieldVector3D<>(new Binary64(1),
207                                                                          new Binary64(2),
208                                                                          new Binary64(3));
209 
210         final FieldVector3D<Binary64> fakeVelocity = new FieldVector3D<>(new Binary64(4),
211                                                                          new Binary64(5),
212                                                                          new Binary64(6));
213 
214         return new FieldPVCoordinates<>(fakePosition, fakeVelocity);
215     }
216 
217     public static FieldAbsolutePVCoordinates<Binary64> getFakeFieldAbsolutePVACoordinates() {
218         final FieldVector3D<Binary64> fakePosition = new FieldVector3D<>(new Binary64(1),
219                                                                          new Binary64(2),
220                                                                          new Binary64(3));
221 
222         final FieldVector3D<Binary64> fakeVelocity = new FieldVector3D<>(new Binary64(4),
223                                                                          new Binary64(5),
224                                                                          new Binary64(6));
225 
226         return new FieldAbsolutePVCoordinates<>(FramesFactory.getGCRF(), getFakeFieldAbsoluteDate(), fakePosition,
227                                                 fakeVelocity);
228     }
229 
230     /**
231      * Method created to test issue 949.
232      *
233      * @return additional state provider with custom init() method defined which use the initial state
234      */
235     public static <T extends CalculusFieldElement<T>> FieldAdditionalDataProvider<T[], T> getFieldAdditionalProviderWithInit() {
236         return new FieldAdditionalDataProvider<T[], T>() {
237 
238             @Override
239             public void init(FieldSpacecraftState<T> initialState, FieldAbsoluteDate<T> target) {
240                 // do nothing
241             }
242 
243             @Override
244             public String getName() {
245                 return "Test";
246             }
247 
248             @Override
249             public T[] getAdditionalData(FieldSpacecraftState<T> state) {
250                 return MathArrays.buildArray(state.getDate().getField(), 0);
251             }
252         };
253     }
254 
255     /**
256      * Validate vector 3D.
257      *
258      * @param expected expected vector 3D
259      * @param computed computed vector 3D
260      * @param threshold absolute threshold
261      */
262     public static void validateVector3D(final Vector3D expected, final Vector3D computed, final double threshold) {
263         Assertions.assertEquals(expected.getX(), computed.getX(), threshold);
264         Assertions.assertEquals(expected.getY(), computed.getY(), threshold);
265         Assertions.assertEquals(expected.getZ(), computed.getZ(), threshold);
266 
267     }
268 
269     /**
270      * Validate field vector 3D.
271      *
272      * @param expected expected vector 3D
273      * @param computed computed vector 3D
274      * @param threshold absolute threshold
275      * @param <T> type of the vector
276      */
277     public static <T extends CalculusFieldElement<T>> void validateFieldVector3D(final Vector3D expected,
278                                                                                  final FieldVector3D<T> computed,
279                                                                                  final double threshold) {
280         Assertions.assertEquals(expected.getX(), computed.getX().getReal(), threshold);
281         Assertions.assertEquals(expected.getY(), computed.getY().getReal(), threshold);
282         Assertions.assertEquals(expected.getZ(), computed.getZ().getReal(), threshold);
283     }
284 
285     /**
286      * Validate vector 2D.
287      *
288      * @param expected expected vector 2D
289      * @param computed computed vector 2D
290      * @param threshold absolute threshold
291      */
292     public static void validateVector2D(final Vector2D expected, final Vector2D computed, final double threshold) {
293         Assertions.assertEquals(expected.getX(), computed.getX(), threshold);
294         Assertions.assertEquals(expected.getY(), computed.getY(), threshold);
295 
296     }
297 
298     /**
299      * Validate field vector 2D.
300      *
301      * @param expected expected vector 2D
302      * @param computed computed vector 2D
303      * @param threshold absolute threshold
304      * @param <T> type of the vector
305      */
306     public static <T extends CalculusFieldElement<T>> void validateFieldVector2D(final Vector2D expected,
307                                                                                  final FieldVector2D<T> computed,
308                                                                                  final double threshold) {
309         Assertions.assertEquals(expected.getX(), computed.getX().getReal(), threshold);
310         Assertions.assertEquals(expected.getY(), computed.getY().getReal(), threshold);
311 
312     }
313 
314     /**
315      * Compare two covariance matrices. Can work in two different modes :
316      * <ul>
317      *     <li>Relative threshold if reference is not equal to zero</li>
318      *     <li>Absolute threshold if reference is equal to zero. </li>
319      * </ul>
320      *
321      * @param reference reference covariance
322      * @param computed computed covariance
323      * @param threshold relative/absolute threshold for comparison depending on used mode
324      */
325     public static <T extends CalculusFieldElement<T>> void validateFieldMatrix(final RealMatrix reference,
326                                                                                final FieldMatrix<T> computed,
327                                                                                final double threshold) {
328         for (int row = 0; row < reference.getRowDimension(); row++) {
329             for (int column = 0; column < reference.getColumnDimension(); column++) {
330                 if (reference.getEntry(row, column) == 0) {
331                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column).getReal(),
332                                             threshold);
333                 } else {
334                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column).getReal(),
335                                             FastMath.abs(threshold * reference.getEntry(row, column)));
336                 }
337             }
338         }
339 
340     }
341 
342     /**
343      * Compare two covariance matrices. Can work in two different modes :
344      * <ul>
345      *     <li>Relative threshold if reference is not equal to zero</li>
346      *     <li>Absolute threshold if reference is equal to zero. </li>
347      * </ul>
348      *
349      * @param reference reference covariance
350      * @param computed computed covariance
351      * @param threshold relative/absolute threshold for comparison depending on used mode
352      */
353     public static void validateRealMatrix(final RealMatrix reference,
354                                           final RealMatrix computed,
355                                           final double threshold) {
356         for (int row = 0; row < reference.getRowDimension(); row++) {
357             for (int column = 0; column < reference.getColumnDimension(); column++) {
358                 if (reference.getEntry(row, column) == 0) {
359                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column),
360                                             threshold);
361                 }
362                 else {
363                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column),
364                                             FastMath.abs(threshold * reference.getEntry(row, column)));
365                 }
366             }
367         }
368     }
369 
370     /**
371      * Pretty print a matrix.
372      *
373      * @param matrix the matrix to print.
374      */
375     public static void prettyPrint(RealMatrix matrix) {
376         prettyPrint(matrix.getData());
377     }
378 
379     /**
380      * Pretty print a double[][].
381      *
382      * @param array the array to print.
383      */
384     public static void prettyPrint(double[][] array) {
385         for (double[] anArray : array) {
386             for (double value : anArray) {
387                 System.out.format("%20g ", value);
388             }
389             System.out.println();
390         }
391     }
392 
393     /**
394      * Pretty print an array
395      *
396      * @param array the array to print.
397      */
398     public static void prettyPrint(double[] array) {
399         System.out.println(Arrays.toString(array));
400     }
401 
402     /**
403      * Check if the given value contains a NaN. Useful as a condition for breakpoints.
404      * Knows how to check arrays, {@link DerivativeStructure}, and {@link Gradient}.
405      *
406      * @param o to check for NaNs.
407      * @return if the object contains any NaNs.
408      */
409     public static boolean isAnyNan(Object o) {
410         if (o instanceof Double) {
411             return Double.isNaN((Double) o);
412         } else if (o instanceof double[]) {
413             for (double v : (double[]) o) {
414                 if (Double.isNaN(v)) {
415                     return true;
416                 }
417             }
418         } else if (o instanceof Object[]) {
419             for (Object object : (Object[]) o) {
420                 if (isAnyNan(object)) {
421                     return true;
422                 }
423             }
424         } else if (o instanceof Collection) {
425             for (Object object : (Collection<?>) o) {
426                 if (isAnyNan(object)) {
427                     return true;
428                 }
429             }
430         } else if (o instanceof CalculusFieldElement) {
431             CalculusFieldElement<?> cfe = (CalculusFieldElement<?>) o;
432             if (cfe.isNaN()) {
433                 return true;
434             }
435             if (cfe instanceof Gradient) {
436                 return isAnyNan(((Gradient) cfe).getGradient());
437             }
438             if (cfe instanceof DerivativeStructure) {
439                 return isAnyNan(((DerivativeStructure) cfe).getAllDerivatives());
440             }
441         }
442         return false;
443     }
444 
445     public static StateCovariance getFakeStateCovarianceInLOF(AbsoluteDate absoluteDate,
446                                                               LOFType lofType) {
447         final RealMatrix covariance = new BlockRealMatrix(new double[][] {
448                 { 1e4, 0, 0, 0, 0, 0 },
449                 { 0, 1e4, 0, 0, 0, 0 },
450                 { 0, 0, 1e4, 0, 0, 0 },
451                 { 0, 0, 0, 1e-2, 0, 0 },
452                 { 0, 0, 0, 0, 1e-2, 0 },
453                 { 0, 0, 0, 0, 0, 1e-2 },
454                 });
455 
456         return new StateCovariance(covariance, absoluteDate, lofType);
457     }
458 }