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.FieldMatrix;
27  import org.hipparchus.linear.RealMatrix;
28  import org.hipparchus.util.FastMath;
29  import org.hipparchus.util.MathArrays;
30  import org.junit.jupiter.api.Assertions;
31  import org.orekit.frames.Frame;
32  import org.orekit.frames.FramesFactory;
33  import org.orekit.orbits.CartesianOrbit;
34  import org.orekit.orbits.Orbit;
35  import org.orekit.propagation.AdditionalDataProvider;
36  import org.orekit.propagation.FieldAdditionalDataProvider;
37  import org.orekit.propagation.FieldSpacecraftState;
38  import org.orekit.propagation.SpacecraftState;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.FieldAbsoluteDate;
41  import org.orekit.utils.PVCoordinates;
42  
43  import java.util.Arrays;
44  import java.util.Collection;
45  
46  /** Utility class for tests to reduce code duplication. */
47  public class TestUtils {
48  
49      private TestUtils() {
50          //Empty
51      }
52  
53      /**
54       * Create and return a default equatorial circular orbit at 400km altitude.
55       *
56       * @param date date of the orbit
57       *
58       * @return default orbit
59       */
60      public static Orbit getDefaultOrbit(final AbsoluteDate date) {
61          final PVCoordinates pv = new PVCoordinates(
62                  new Vector3D(6378000 + 400000, 0, 0),
63                  new Vector3D(0, 7668.631425, 0));
64  
65          final Frame frame = FramesFactory.getGCRF();
66  
67          final double DEFAULT_MU = 398600e9; // m**3/s**2
68  
69          return new CartesianOrbit(pv, frame, date, DEFAULT_MU);
70      }
71  
72      /**
73       * Method created to test issue 949.
74       *
75       * @return additional state provider with custom init() method defined which use the initial state
76       */
77      public static AdditionalDataProvider<double[]> getAdditionalProviderWithInit() {
78          return new AdditionalDataProvider<double[]>() {
79              /**
80               * Custom init method which use the initial state instance.
81               *
82               * @param initialState initial state information at the start of propagation
83               * @param target date of propagation
84               */
85              @Override
86              public void init(final SpacecraftState initialState, final AbsoluteDate target) {
87                  // do nothing
88              }
89  
90              @Override
91              public String getName() {
92                  return "Test";
93              }
94  
95              @Override
96              public double[] getAdditionalData(final SpacecraftState state) {
97                  return new double[0];
98              }
99          };
100     }
101 
102     /**
103      * Method created to test issue 949.
104      *
105      * @return additional state provider with custom init() method defined which use the initial state
106      */
107     public static <T extends CalculusFieldElement<T>> FieldAdditionalDataProvider<T[], T> getFieldAdditionalProviderWithInit() {
108         return new FieldAdditionalDataProvider<T[], T>() {
109 
110             @Override
111             public void init(FieldSpacecraftState<T> initialState, FieldAbsoluteDate<T> target) {
112                 // do nothing
113             }
114 
115             @Override
116             public String getName() {
117                 return "Test";
118             }
119 
120             @Override
121             public T[] getAdditionalData(FieldSpacecraftState<T> state) {
122                 return MathArrays.buildArray(state.getDate().getField(), 0);
123             }
124         };
125     }
126 
127     /**
128      * Validate vector 3D.
129      *
130      * @param expected expected vector 3D
131      * @param computed computed vector 3D
132      * @param threshold absolute threshold
133      */
134     public static void validateVector3D(final Vector3D expected, final Vector3D computed, final double threshold) {
135         Assertions.assertEquals(expected.getX(), computed.getX(), threshold);
136         Assertions.assertEquals(expected.getY(), computed.getY(), threshold);
137         Assertions.assertEquals(expected.getZ(), computed.getZ(), threshold);
138 
139     }
140 
141     /**
142      * Validate field vector 3D.
143      *
144      * @param expected expected vector 3D
145      * @param computed computed vector 3D
146      * @param threshold absolute threshold
147      * @param <T> type of the vector
148      */
149     public static <T extends CalculusFieldElement<T>> void validateFieldVector3D(final Vector3D expected,
150                                                                                  final FieldVector3D<T> computed,
151                                                                                  final double threshold) {
152         Assertions.assertEquals(expected.getX(), computed.getX().getReal(), threshold);
153         Assertions.assertEquals(expected.getY(), computed.getY().getReal(), threshold);
154         Assertions.assertEquals(expected.getZ(), computed.getZ().getReal(), threshold);
155     }
156 
157     /**
158      * Validate vector 2D.
159      *
160      * @param expected expected vector 2D
161      * @param computed computed vector 2D
162      * @param threshold absolute threshold
163      */
164     public static void validateVector2D(final Vector2D expected, final Vector2D computed, final double threshold) {
165         Assertions.assertEquals(expected.getX(), computed.getX(), threshold);
166         Assertions.assertEquals(expected.getY(), computed.getY(), threshold);
167 
168     }
169 
170     /**
171      * Validate field vector 2D.
172      *
173      * @param expected expected vector 2D
174      * @param computed computed vector 2D
175      * @param threshold absolute threshold
176      * @param <T> type of the vector
177      */
178     public static <T extends CalculusFieldElement<T>> void validateFieldVector2D(final Vector2D expected,
179                                                                                  final FieldVector2D<T> computed,
180                                                                                  final double threshold) {
181         Assertions.assertEquals(expected.getX(), computed.getX().getReal(), threshold);
182         Assertions.assertEquals(expected.getY(), computed.getY().getReal(), threshold);
183 
184     }
185 
186     /**
187      * Compare two covariance matrices. Can work in two different modes :
188      * <ul>
189      *     <li>Relative threshold if reference is not equal to zero</li>
190      *     <li>Absolute threshold if reference is equal to zero. </li>
191      * </ul>
192      *
193      * @param reference reference covariance
194      * @param computed computed covariance
195      * @param threshold relative/absolute threshold for comparison depending on used mode
196      */
197     public static <T extends CalculusFieldElement<T>> void validateFieldMatrix(final RealMatrix reference,
198                                                                                final FieldMatrix<T> computed,
199                                                                                final double threshold) {
200         for (int row = 0; row < reference.getRowDimension(); row++) {
201             for (int column = 0; column < reference.getColumnDimension(); column++) {
202                 if (reference.getEntry(row, column) == 0) {
203                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column).getReal(),
204                                             threshold);
205                 } else {
206                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column).getReal(),
207                                             FastMath.abs(threshold * reference.getEntry(row, column)));
208                 }
209             }
210         }
211 
212     }
213 
214     /**
215      * Compare two covariance matrices. Can work in two different modes :
216      * <ul>
217      *     <li>Relative threshold if reference is not equal to zero</li>
218      *     <li>Absolute threshold if reference is equal to zero. </li>
219      * </ul>
220      *
221      * @param reference reference covariance
222      * @param computed computed covariance
223      * @param threshold relative/absolute threshold for comparison depending on used mode
224      */
225     public static void validateRealMatrix(final RealMatrix reference,
226                                           final RealMatrix computed,
227                                           final double threshold) {
228         for (int row = 0; row < reference.getRowDimension(); row++) {
229             for (int column = 0; column < reference.getColumnDimension(); column++) {
230                 if (reference.getEntry(row, column) == 0) {
231                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column),
232                                             threshold);
233                 }
234                 else {
235                     Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column),
236                                             FastMath.abs(threshold * reference.getEntry(row, column)));
237                 }
238             }
239         }
240     }
241 
242     /**
243      * Pretty print a matrix.
244      *
245      * @param matrix the matrix to print.
246      */
247     public static void prettyPrint(RealMatrix matrix) {
248         prettyPrint(matrix.getData());
249     }
250 
251     /**
252      * Pretty print a double[][].
253      *
254      * @param array the array to print.
255      */
256     public static void prettyPrint(double[][] array) {
257         for (double[] anArray : array) {
258             for (double value : anArray) {
259                 System.out.format("%20g ", value);
260             }
261             System.out.println();
262         }
263     }
264 
265     /**
266      * Pretty print an array
267      *
268      * @param array the array to print.
269      */
270     public static void prettyPrint(double[] array) {
271         System.out.println(Arrays.toString(array));
272     }
273 
274     /**
275      * Check if the given value contains a NaN. Useful as a condition for breakpoints.
276      * Knows how to check arrays, {@link DerivativeStructure}, and {@link Gradient}.
277      *
278      * @param o to check for NaNs.
279      * @return if the object contains any NaNs.
280      */
281     public static boolean isAnyNan(Object o) {
282         if (o instanceof Double) {
283             return Double.isNaN((Double) o);
284         } else if (o instanceof double[]) {
285             for (double v : (double[]) o) {
286                 if (Double.isNaN(v)) {
287                     return true;
288                 }
289             }
290         } else if (o instanceof Object[]) {
291             for (Object object : (Object[]) o) {
292                 if (isAnyNan(object)) {
293                     return true;
294                 }
295             }
296         } else if (o instanceof Collection) {
297             for (Object object : (Collection<?>) o) {
298                 if (isAnyNan(object)) {
299                     return true;
300                 }
301             }
302         } else if (o instanceof CalculusFieldElement) {
303             CalculusFieldElement<?> cfe = (CalculusFieldElement<?>) o;
304             if (cfe.isNaN()) {
305                 return true;
306             }
307             if (cfe instanceof Gradient) {
308                 return isAnyNan(((Gradient) cfe).getGradient());
309             }
310             if (cfe instanceof DerivativeStructure) {
311                 return isAnyNan(((DerivativeStructure) cfe).getAllDerivatives());
312             }
313         }
314         return false;
315     }
316 
317 }