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.junit.jupiter.api.Assertions;
20  import org.orekit.attitudes.AttitudeProvider;
21  import org.orekit.attitudes.FrameAlignedProvider;
22  import org.orekit.bodies.CelestialBodyFactory;
23  import org.orekit.data.DataContext;
24  import org.orekit.data.DataProvidersManager;
25  import org.orekit.data.DirectoryCrawler;
26  import org.orekit.data.LazyLoadedDataContext;
27  import org.orekit.forces.gravity.potential.GravityFieldFactory;
28  import org.orekit.frames.EOPEntry;
29  import org.orekit.frames.FramesFactory;
30  import org.orekit.frames.ITRFVersion;
31  import org.orekit.orbits.FieldCartesianOrbit;
32  import org.orekit.orbits.FieldCircularOrbit;
33  import org.orekit.orbits.FieldEquinoctialOrbit;
34  import org.orekit.orbits.FieldKeplerianOrbit;
35  import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
36  import org.orekit.propagation.semianalytical.dsst.utilities.NewcombOperators;
37  import org.orekit.time.AbsoluteDate;
38  import org.orekit.time.DateComponents;
39  import org.orekit.time.GNSSDate;
40  import org.orekit.time.TimeScale;
41  import org.orekit.time.TimeScalesFactory;
42  import org.orekit.utils.Constants;
43  import org.orekit.utils.IERSConventions;
44  import org.orekit.utils.ParameterDriversList;
45  
46  import java.io.File;
47  import java.lang.reflect.Field;
48  import java.lang.reflect.Modifier;
49  import java.net.URISyntaxException;
50  import java.util.ArrayList;
51  import java.util.List;
52  import java.util.Map;
53  
54  public class Utils {
55  
56      // epsilon for tests
57      public static final double epsilonTest  = 1.e-12;
58  
59      // epsilon for eccentricity
60      public static final double epsilonE     = 1.e+5 * epsilonTest;
61  
62      // epsilon for circular eccentricity
63      public static final double epsilonEcir  = 1.e+8 * epsilonTest;
64  
65      // epsilon for angles
66      public static final double epsilonAngle = 1.e+5 * epsilonTest;
67  
68      public static final double ae =  6378136.460;
69      public static final double mu =  3.986004415e+14;
70  
71      public static void clearFactories() {
72          DataContext.setDefault(new LazyLoadedDataContext());
73          clearFactoryMaps(CelestialBodyFactory.class);
74          CelestialBodyFactory.clearCelestialBodyLoaders();
75          clearFactoryMaps(FramesFactory.class);
76          clearFactoryMaps(TimeScalesFactory.class);
77          clearFactory(TimeScalesFactory.class, TimeScale.class);
78          clearFactoryMaps(FieldCartesianOrbit.class);
79          clearFactoryMaps(FieldKeplerianOrbit.class);
80          clearFactoryMaps(FieldCircularOrbit.class);
81          clearFactoryMaps(FieldEquinoctialOrbit.class);
82          clearFactoryMaps(JacobiPolynomials.class);
83          clearFactoryMaps(NewcombOperators.class);
84          for (final Class<?> c : NewcombOperators.class.getDeclaredClasses()) {
85              if (c.getName().endsWith("PolynomialsGenerator")) {
86                  clearFactoryMaps(c);
87              }
88          }
89          FramesFactory.clearEOPHistoryLoaders();
90          FramesFactory.setEOPContinuityThreshold(5 * Constants.JULIAN_DAY);
91          TimeScalesFactory.clearUTCTAIOffsetsLoaders();
92          GNSSDate.setRolloverReference(null);
93          GravityFieldFactory.clearPotentialCoefficientsReaders();
94          GravityFieldFactory.clearOceanTidesReaders();
95          DataContext.getDefault().getDataProvidersManager().clearProviders();
96          DataContext.getDefault().getDataProvidersManager().resetFiltersToDefault();
97          DataContext.getDefault().getDataProvidersManager().clearLoadedDataNames();
98  
99      }
100 
101     public static DataContext setDataRoot(String root) {
102         try {
103             clearFactories();
104             StringBuilder buffer = new StringBuilder();
105             for (String component : root.split(":")) {
106                 String componentPath;
107                 componentPath = Utils.class.getClassLoader().getResource(component).toURI().getPath();
108                 if (buffer.length() > 0) {
109                     buffer.append(File.pathSeparator);
110                 }
111                 buffer.append(componentPath);
112             }
113             System.setProperty(DataProvidersManager.OREKIT_DATA_PATH, buffer.toString());
114             return DataContext.getDefault();
115         } catch (URISyntaxException e) {
116             throw new RuntimeException(e);
117         }
118     }
119 
120     /**
121      * Creates a new data context, similar to {@link #setDataRoot(String)}
122      * except that this method does not modify any static variables.
123      *
124      * @param root of data context. Paths separated by {@code ":"}.
125      * @return new data context.
126      */
127     public static LazyLoadedDataContext newDataContext(String root) {
128         try {
129             LazyLoadedDataContext dataContext = new LazyLoadedDataContext();
130             DataProvidersManager manager = dataContext.getDataProvidersManager();
131             for (String component : root.split(":")) {
132                 String componentPath;
133                 componentPath = Utils.class.getClassLoader().getResource(component).toURI().getPath();
134                 manager.addProvider(new DirectoryCrawler(new File(componentPath)));
135             }
136             return dataContext;
137         } catch (URISyntaxException e) {
138             throw new RuntimeException(e);
139         }
140     }
141 
142     private static void clearFactoryMaps(Class<?> factoryClass) {
143         try {
144             for (Field field : factoryClass.getDeclaredFields()) {
145                 if (Modifier.isStatic(field.getModifiers()) &&
146                     Map.class.isAssignableFrom(field.getType())) {
147                     field.setAccessible(true);
148                     ((Map<?, ?>) field.get(null)).clear();
149                 }
150             }
151         } catch (IllegalAccessException iae) {
152             Assertions.fail(iae.getMessage());
153         }
154     }
155 
156     private static void clearFactory(Class<?> factoryClass, Class<?> cachedFieldsClass) {
157         try {
158             for (Field field : factoryClass.getDeclaredFields()) {
159                 if (Modifier.isStatic(field.getModifiers()) &&
160                     cachedFieldsClass.isAssignableFrom(field.getType())) {
161                     field.setAccessible(true);
162                     field.set(null, null);
163                 }
164             }
165         } catch (IllegalAccessException iae) {
166             Assertions.fail(iae.getMessage());
167         }
168     }
169 
170     public static List<EOPEntry> buildEOPList(IERSConventions conventions, ITRFVersion version,
171                                               double[][] data) {
172         IERSConventions.NutationCorrectionConverter converter =
173                 conventions.getNutationCorrectionConverter();
174         final TimeScale utc = DataContext.getDefault().getTimeScales().getUTC();
175         final List<EOPEntry> list = new ArrayList<>();
176         for (double[] row : data) {
177             final AbsoluteDate date =
178                     new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, (int) row[0]),
179                                      TimeScalesFactory.getUTC());
180             final double[] nro;
181             final double[] equinox;
182             if (Double.isNaN(row[7])) {
183                 equinox = new double[] {
184                     Constants.ARC_SECONDS_TO_RADIANS * row[5],
185                     Constants.ARC_SECONDS_TO_RADIANS * row[6]
186                 };
187                 nro     = converter.toNonRotating(date, equinox[0], equinox[1]);
188             } else if (Double.isNaN(row[5])) {
189                 nro     = new double[] {
190                     Constants.ARC_SECONDS_TO_RADIANS * row[7],
191                     Constants.ARC_SECONDS_TO_RADIANS * row[8]
192                 };
193                 equinox = converter.toEquinox(date, nro[0], nro[1]);
194             } else {
195                 equinox = new double[] {
196                     Constants.ARC_SECONDS_TO_RADIANS * row[5],
197                     Constants.ARC_SECONDS_TO_RADIANS * row[6]
198                 };
199                 nro     = new double[] {
200                     Constants.ARC_SECONDS_TO_RADIANS * row[7],
201                     Constants.ARC_SECONDS_TO_RADIANS * row[8]
202                 };
203             }
204             list.add(new EOPEntry((int) row[0], row[1], row[2],
205                                   Constants.ARC_SECONDS_TO_RADIANS * row[3],
206                                   Constants.ARC_SECONDS_TO_RADIANS * row[4],
207                                   Double.NaN, Double.NaN,
208                                   equinox[0], equinox[1],
209                                   nro[0], nro[1], version,
210                                   AbsoluteDate.createMJDDate((int) row[0], 0.0, utc)));
211         }
212         return list;
213     }
214 
215     public static void setLoaders(final IERSConventions conventions, final List<EOPEntry> eop) {
216 
217         clearFactories();
218 
219         FramesFactory.addEOPHistoryLoader(conventions, (converter, history) -> history.addAll(eop));
220 
221     }
222 
223     /**
224      * Assert that the normalized values of given expected and actual {@link ParameterDriversList} are identical.
225      *
226      * @param expected expected {@link ParameterDriversList}
227      * @param actual actual {@link ParameterDriversList}
228      */
229     public static void assertParametersDriversValues(final ParameterDriversList expected,
230                                                      final ParameterDriversList actual) {
231 
232         final List<ParameterDriversList.DelegatingDriver> expectedDriversList = expected.getDrivers();
233         final List<ParameterDriversList.DelegatingDriver> actualDriversList   = actual.getDrivers();
234         for (int i = 0; i < expectedDriversList.size(); i++) {
235             final ParameterDriversList.DelegatingDriver currentExpectedDriver = expectedDriversList.get(i);
236             final ParameterDriversList.DelegatingDriver currentActualDriver = actualDriversList.get(i);
237 
238             Assertions.assertArrayEquals(currentExpectedDriver.getValues(), currentActualDriver.getValues());
239             Assertions.assertEquals(currentExpectedDriver.getValue(), currentActualDriver.getValue());
240             Assertions.assertEquals(currentExpectedDriver.getNormalizedValue(), currentActualDriver.getNormalizedValue());
241             Assertions.assertEquals(currentExpectedDriver.getMaxValue(), currentActualDriver.getMaxValue());
242             Assertions.assertEquals(currentExpectedDriver.getMinValue(), currentActualDriver.getMinValue());
243             Assertions.assertEquals(currentExpectedDriver.getName(), currentActualDriver.getName());
244             Assertions.assertEquals(currentExpectedDriver.getNbOfValues(), currentActualDriver.getNbOfValues());
245             Assertions.assertEquals(currentExpectedDriver.getReferenceValue(), currentActualDriver.getReferenceValue());
246 
247         }
248     }
249 
250     /**
251      * An attitude law compatible with the old Propagator.DEFAULT_LAW. This is used so as
252      * not to change the results of tests written against the old implementation.
253      *
254      * @return an attitude law.
255      */
256     public static AttitudeProvider defaultLaw() {
257         return FrameAlignedProvider.of(FramesFactory.getEME2000());
258     }
259 
260 }