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