1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS Systèmes d'Information (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.files.ccsds.ndm;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.Map;
23  import java.util.stream.Stream;
24  
25  import org.hipparchus.complex.Quaternion;
26  import org.hipparchus.geometry.euclidean.threed.Rotation;
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.hipparchus.linear.RealMatrix;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.Precision;
31  import org.junit.jupiter.api.Assertions;
32  import org.opentest4j.AssertionFailedError;
33  import org.orekit.data.DataContext;
34  import org.orekit.errors.OrekitException;
35  import org.orekit.errors.OrekitMessages;
36  import org.orekit.files.ccsds.definitions.BodyFacade;
37  import org.orekit.files.ccsds.definitions.CcsdsFrameMapper;
38  import org.orekit.files.ccsds.definitions.FrameFacade;
39  import org.orekit.files.ccsds.definitions.OdMethodFacade;
40  import org.orekit.files.ccsds.definitions.PocMethodFacade;
41  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
42  import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints;
43  import org.orekit.files.ccsds.ndm.adm.acm.AcmSatelliteEphemeris;
44  import org.orekit.files.ccsds.ndm.adm.acm.AttitudeCovariance;
45  import org.orekit.files.ccsds.ndm.adm.acm.AttitudeCovarianceHistory;
46  import org.orekit.files.ccsds.ndm.adm.acm.AttitudeState;
47  import org.orekit.files.ccsds.ndm.adm.acm.AttitudeStateHistory;
48  import org.orekit.files.ccsds.ndm.adm.aem.AemSatelliteEphemeris;
49  import org.orekit.files.ccsds.ndm.adm.apm.ApmQuaternion;
50  import org.orekit.files.ccsds.ndm.cdm.CdmRelativeMetadata;
51  import org.orekit.files.ccsds.ndm.odm.ocm.OcmSatelliteEphemeris;
52  import org.orekit.files.ccsds.ndm.odm.ocm.OrbitCovariance;
53  import org.orekit.files.ccsds.ndm.odm.ocm.OrbitCovarianceHistory;
54  import org.orekit.files.ccsds.ndm.odm.ocm.OrbitManeuver;
55  import org.orekit.files.ccsds.ndm.odm.ocm.OrbitManeuverHistory;
56  import org.orekit.files.ccsds.ndm.odm.ocm.TrajectoryState;
57  import org.orekit.files.ccsds.ndm.odm.ocm.TrajectoryStateHistory;
58  import org.orekit.files.ccsds.ndm.odm.oem.OemSatelliteEphemeris;
59  import org.orekit.files.ccsds.ndm.tdm.Observation;
60  import org.orekit.files.ccsds.section.CommentsContainer;
61  import org.orekit.files.ccsds.section.Section;
62  import org.orekit.files.ccsds.section.Segment;
63  import org.orekit.frames.Frame;
64  import org.orekit.time.AbsoluteDate;
65  import org.orekit.utils.AngularCoordinates;
66  import org.orekit.utils.PVCoordinates;
67  import org.orekit.utils.units.Unit;
68  
69  public class NdmTestUtils {
70  
71      private static final int ULPS = 207;
72  
73      public static void checkEquals(final NdmConstituent<?, ?> original, final NdmConstituent<?, ?> rebuilt) {
74          checkContainer(original.getHeader(), rebuilt.getHeader());
75          Assertions.assertEquals(original.getSegments().size(), rebuilt.getSegments().size());
76          for (int i = 0; i < original.getSegments().size(); ++i) {
77              checkContainer(original.getSegments().get(i).getMetadata(), rebuilt.getSegments().get(i).getMetadata());
78              checkContainer(original.getSegments().get(i).getData(), rebuilt.getSegments().get(i).getData());
79          }
80      }
81  
82      public static boolean recurseCheck(final Object original, final Object rebuilt) {
83  
84          if (original == null) {
85              return rebuilt == null;
86          } else if (original instanceof String    ||
87                     original instanceof Boolean   ||
88                     original instanceof Character ||
89                     original instanceof Integer   ||
90                     original instanceof Enum) {
91              return original.equals(rebuilt);
92          } else if (original instanceof Double) {
93              checkDouble((Double) original, (Double) rebuilt);
94              return true;
95          } else if (original instanceof int[]) {
96              checkIntArray((int[]) original, (int[]) rebuilt);
97              return true;
98          } else if (original instanceof double[]) {
99              checkDoubleArray((double[]) original, (double[]) rebuilt);
100             return true;
101         } else if (original instanceof List) {
102             checkList((List<?>) original, (List<?>) rebuilt);
103             return true;
104         } else if (original instanceof Map) {
105             checkMap((Map<?, ?>) original, (Map<?, ?>) rebuilt);
106             return true;
107         } else if (original instanceof NdmConstituent            ||
108                    original instanceof Segment                   ||
109                    original instanceof Section                   ||
110                    original instanceof CommentsContainer         ||
111                    original instanceof ApmQuaternion             ||
112                    original instanceof AttitudeEndpoints         ||
113                    original instanceof OcmSatelliteEphemeris     ||
114                    original instanceof OemSatelliteEphemeris     ||
115                    original instanceof AemSatelliteEphemeris     ||
116                    original instanceof OrbitCovarianceHistory    ||
117                    original instanceof OrbitManeuverHistory      ||
118                    original instanceof TrajectoryState           ||
119                    original instanceof OrbitCovariance           ||
120                    original instanceof OrbitManeuver             ||
121                    original instanceof Observation               ||
122                    original instanceof SpacecraftBodyFrame       ||
123                    original instanceof PVCoordinates             ||
124                    original instanceof AngularCoordinates        ||
125                    original instanceof CdmRelativeMetadata       ||
126                    original instanceof AttitudeStateHistory      ||
127                    original instanceof AttitudeState             ||
128                    original instanceof AttitudeCovarianceHistory ||
129                    original instanceof AttitudeCovariance        ||
130                    original instanceof AcmSatelliteEphemeris) {
131             checkContainer(original, rebuilt);
132             return true;
133         } else if (original instanceof FrameFacade) {
134             checkFrameFacade((FrameFacade) original, (FrameFacade) rebuilt);
135             return true;
136         } else if (original instanceof BodyFacade) {
137             checkBodyFacade((BodyFacade) original, (BodyFacade) rebuilt);
138             return true;
139         } else if (original instanceof OdMethodFacade) {
140             checkOdMethodFacade((OdMethodFacade) original, (OdMethodFacade) rebuilt);
141             return true;
142         } else if (original instanceof PocMethodFacade) {
143             checkPocMethodFacade((PocMethodFacade) original, (PocMethodFacade) rebuilt);
144             return true;
145         } else if (original instanceof TrajectoryStateHistory) {
146             checkOrbitStateHistory((TrajectoryStateHistory) original, (TrajectoryStateHistory) rebuilt);
147             return true;
148         } else if (original instanceof DataContext) {
149             return true;
150         } else if (original instanceof Frame) {
151             checkFrame((Frame) original, (Frame) rebuilt);
152             return true;
153         } else if (original instanceof AbsoluteDate) {
154             checkDate((AbsoluteDate) original, (AbsoluteDate) rebuilt);
155             return true;
156         } else if (original instanceof Unit) {
157             checkUnit((Unit) original, (Unit) rebuilt);
158             return true;
159         } else if (original instanceof Vector3D) {
160             checkVector3D((Vector3D) original, (Vector3D) rebuilt);
161             return true;
162         } else if (original instanceof Quaternion) {
163             checkQuaternion((Quaternion) original, (Quaternion) rebuilt);
164             return true;
165         } else if (original instanceof RealMatrix) {
166             checkRealMatrix((RealMatrix) original, (RealMatrix) rebuilt);
167             return true;
168         } else if (original instanceof Rotation) {
169             checkRotation((Rotation) original, (Rotation) rebuilt);
170             return true;
171         } else if (original instanceof CcsdsFrameMapper) {
172             Assertions.assertEquals(original, rebuilt);
173             return true;
174         } else {
175             return false;
176         }
177 
178     }
179 
180     public static void checkContainer(final Object original, final Object rebuilt) {
181         Assertions.assertEquals(original.getClass(), rebuilt.getClass());
182         final Class<?> cls = original.getClass();
183         Stream.of(cls.getMethods()).
184         filter(m -> m.getName().startsWith("get")              &&
185                     !m.getName().equals("getClass")            &&
186                     !m.getName().equals("getPropagator")       &&
187                     !m.getName().equals("getLaunchYear")       &&
188                     !m.getName().equals("getLaunchNumber")     &&
189                     !m.getName().equals("getLaunchPiece")      &&
190                     !m.getName().equals("getAttitudeProvider") &&
191                     m.getParameterCount() == 0).
192         forEach(getter -> {
193             try {
194                 Assertions.assertTrue(recurseCheck(getter.invoke(original), getter.invoke(rebuilt)),
195                         "failed to compare " + getter.getReturnType() + " from "
196                                 + getter.getName());
197             } catch (InvocationTargetException e) {
198                 if (!((getter.getName().equals("getFrame") ||
199                        getter.getName().equals("getReferenceFrame") ||
200                        getter.getName().equals("getInertialFrame") ||
201                        getter.getReturnType().equals(Frame.class) ||
202                        getter.getName().equals("getAngularCoordinates")) &&
203                       e.getCause() instanceof OrekitException &&
204                       (((OrekitException) e.getCause()).getSpecifier() == OrekitMessages.NO_DATA_LOADED_FOR_CELESTIAL_BODY ||
205                        ((OrekitException) e.getCause()).getSpecifier() == OrekitMessages.CCSDS_INVALID_FRAME ||
206                        ((OrekitException) e.getCause()).getSpecifier() == OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE))) {
207                     throw new Error("failed to invoke getter: " + e.getLocalizedMessage(), e);
208                 }
209             } catch (IllegalAccessException | IllegalArgumentException e) {
210                 Assertions.fail(e.getLocalizedMessage());
211             }
212         });
213     }
214 
215     public static void checkIntArray(final int[] original, final int[] rebuilt) {
216         Assertions.assertEquals(original.length, rebuilt.length);
217         for (int i = 0; i < original.length; ++i) {
218             Assertions.assertEquals(original[i], rebuilt[i]);
219         }
220     }
221 
222     public static void checkDoubleArray(final double[] original, final double[] rebuilt) {
223         Assertions.assertEquals(original.length, rebuilt.length);
224         for (int i = 0; i < original.length; ++i) {
225             Assertions.assertTrue(Precision.equalsIncludingNaN(original[i], rebuilt[i], 1));
226         }
227     }
228 
229     public static void checkList(final List<?> original, final List<?> rebuilt) {
230         Assertions.assertEquals(original.size(), rebuilt.size());
231         for (int i = 0; i < original.size(); ++i) {
232             Assertions.assertTrue(recurseCheck(original.get(i), rebuilt.get(i)));
233         }
234     }
235 
236     public static void checkMap(final Map<?, ?> original, final Map<?, ?> rebuilt) {
237         Assertions.assertEquals(original.size(), rebuilt.size());
238         for (final Map.Entry<?, ?> entry : original.entrySet()) {
239             Assertions.assertTrue(rebuilt.containsKey(entry.getKey()));
240             Assertions.assertTrue(recurseCheck(entry.getValue(), rebuilt.get(entry.getKey())));
241         }
242     }
243 
244     public static void checkFrameFacade(final FrameFacade original, final FrameFacade rebuilt) {
245         if (original.asFrame() == null) {
246             Assertions.assertNull(rebuilt.asFrame());
247         } else {
248             Assertions.assertEquals(original.asFrame().getName(),
249                                 rebuilt.asFrame().getName());
250         }
251         Assertions.assertEquals(original.asCelestialBodyFrame(),
252                             rebuilt.asCelestialBodyFrame());
253         if (original.asOrbitRelativeFrame() == null) {
254             Assertions.assertNull(rebuilt.asOrbitRelativeFrame());
255         } else {
256             Assertions.assertEquals(original.asOrbitRelativeFrame().getLofType(),
257                                 rebuilt.asOrbitRelativeFrame().getLofType());
258         }
259         if (original.asSpacecraftBodyFrame() == null) {
260             Assertions.assertNull(rebuilt.asSpacecraftBodyFrame());
261         } else {
262             Assertions.assertEquals(original.asSpacecraftBodyFrame().getBaseEquipment(),
263                                 rebuilt.asSpacecraftBodyFrame().getBaseEquipment());
264             Assertions.assertEquals(original.asSpacecraftBodyFrame().getLabel(),
265                                 rebuilt.asSpacecraftBodyFrame().getLabel());
266         }
267         Assertions.assertEquals(original.getName(), rebuilt.getName());
268     }
269 
270     public static void checkBodyFacade(final BodyFacade original, final BodyFacade rebuilt) {
271         if (original.getBody() == null) {
272             Assertions.assertNull(rebuilt.getBody());
273         } else {
274             Assertions.assertEquals(original.getBody().getName(),
275                                 rebuilt.getBody().getName());
276         }
277         Assertions.assertEquals(original.getName().toUpperCase(Locale.US), rebuilt.getName().toUpperCase(Locale.US));
278     }
279 
280     public static void checkOdMethodFacade(final OdMethodFacade original, final OdMethodFacade rebuilt) {
281         Assertions.assertEquals(original.getName(), rebuilt.getName());
282         Assertions.assertEquals(original.getType(), rebuilt.getType());
283         Assertions.assertEquals(original.getTool(), rebuilt.getTool());
284     }
285 
286     public static void checkPocMethodFacade(final PocMethodFacade original, final PocMethodFacade rebuilt) {
287         Assertions.assertEquals(original.getName(), rebuilt.getName());
288         Assertions.assertEquals(original.getType(), rebuilt.getType());
289     }
290 
291     public static void checkOrbitStateHistory(final TrajectoryStateHistory original, final TrajectoryStateHistory rebuilt) {
292         // we don't use checkContainer here because the history getters are redundant
293         // with embedded metadata and states, and because the getFrame() method
294         // that would be called automatically may throw an exception
295         // so we just jump down to metadata and states
296         Assertions.assertTrue(recurseCheck(original.getMetadata(), rebuilt.getMetadata()));
297         checkList(original.getTrajectoryStates(), rebuilt.getTrajectoryStates());
298     }
299 
300     public static void checkDate(final AbsoluteDate original, final AbsoluteDate rebuilt) {
301         Assertions.assertEquals(0.0, rebuilt.durationFrom(original), 4.0e-12);
302     }
303 
304     public static void checkUnit(final Unit original, final Unit rebuilt) {
305         Assertions.assertTrue(Precision.equals(original.getScale(), rebuilt.getScale(), 1));
306         Assertions.assertTrue(rebuilt.sameDimension(original));
307     }
308 
309     public static void checkFrame(final Frame original, final Frame rebuilt) {
310         Assertions.assertEquals(original.getName(), rebuilt.getName());
311     }
312 
313     public static void checkVector3D(final Vector3D original, final Vector3D rebuilt) {
314         double eps = ULPS * FastMath.ulp(FastMath.max(1.0, original.getNorm()));
315         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getX(), rebuilt.getX(), eps));
316         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getY(), rebuilt.getY(), eps));
317         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getZ(), rebuilt.getZ(), eps));
318     }
319 
320     public static void checkQuaternion(final Quaternion original, final Quaternion rebuilt) {
321         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getQ0(), rebuilt.getQ0(), ULPS));
322         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getQ1(), rebuilt.getQ1(), ULPS));
323         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getQ2(), rebuilt.getQ2(), ULPS));
324         Assertions.assertTrue(Precision.equalsIncludingNaN(original.getQ3(), rebuilt.getQ3(), ULPS));
325     }
326 
327     public static void checkRealMatrix(final RealMatrix original, final RealMatrix rebuilt) {
328         Assertions.assertEquals(original.getRowDimension(), rebuilt.getRowDimension());
329         Assertions.assertEquals(original.getColumnDimension(), rebuilt.getColumnDimension());
330         for (int i = 0; i < original.getRowDimension(); ++i) {
331             for (int j = 0; j < original.getColumnDimension(); ++j) {
332                 Assertions.assertTrue(Precision.equalsIncludingNaN(original.getEntry(i, j), rebuilt.getEntry(i, j), ULPS));
333             }
334         }
335     }
336 
337     public static void checkRotation(final Rotation original, final Rotation rebuilt) {
338         Assertions.assertEquals(0.0, Rotation.distance(original, rebuilt), 1.0e-12);
339     }
340 
341     public static void checkDouble(final Double original, final Double rebuilt) {
342         Assertions.assertTrue(Precision.equalsIncludingNaN(original.doubleValue(), rebuilt.doubleValue(), ULPS));
343     }
344 
345 
346 }