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  
18  package org.orekit.models.earth.atmosphere.data;
19  
20  import org.hipparchus.ode.ODEIntegrator;
21  import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator;
22  import org.hipparchus.util.FastMath;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.Utils;
27  import org.orekit.bodies.CelestialBody;
28  import org.orekit.bodies.CelestialBodyFactory;
29  import org.orekit.bodies.OneAxisEllipsoid;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.forces.drag.DragForce;
33  import org.orekit.forces.drag.IsotropicDrag;
34  import org.orekit.frames.Frame;
35  import org.orekit.frames.FramesFactory;
36  import org.orekit.models.earth.atmosphere.Atmosphere;
37  import org.orekit.models.earth.atmosphere.JB2008;
38  import org.orekit.models.earth.atmosphere.JB2008InputParameters;
39  import org.orekit.orbits.KeplerianOrbit;
40  import org.orekit.orbits.Orbit;
41  import org.orekit.orbits.OrbitType;
42  import org.orekit.orbits.PositionAngleType;
43  import org.orekit.propagation.SpacecraftState;
44  import org.orekit.propagation.numerical.NumericalPropagator;
45  import org.orekit.propagation.sampling.OrekitStepHandler;
46  import org.orekit.time.AbsoluteDate;
47  import org.orekit.time.TimeScale;
48  import org.orekit.time.TimeScalesFactory;
49  import org.orekit.utils.Constants;
50  import org.orekit.utils.IERSConventions;
51  
52  import static org.hamcrest.MatcherAssert.assertThat;
53  import static org.orekit.OrekitMatchers.closeTo;
54  import static org.orekit.OrekitMatchers.pvCloseTo;
55  
56  /*
57   * Test code based on the CssiSpaceWeatherDataTest class
58   * by Clément Jonglez.
59   *
60   * @author Louis Aucouturier
61   * @since 11.2
62   */
63  
64  public class SOLFSMYDataLoaderTest {
65  
66      private TimeScale utc;
67  
68      @BeforeEach
69      public void setUp() {
70          Utils.setDataRoot("regular-data:atmosphere");
71          utc = TimeScalesFactory.getUTC();
72      }
73  
74      // DataLoader
75      private JB2008SpaceEnvironmentData loadJB() {
76          JB2008SpaceEnvironmentData JBData = new JB2008SpaceEnvironmentData("SOLFSMY_trunc.txt", "DTCFILE_trunc.TXT");
77          return JBData;
78      }
79  
80  
81      @Test
82      public void testNoDataException() {
83          try {
84              new JB2008SpaceEnvironmentData("SOLFSMY_nodata.txt", "DTCFILE_trunc.TXT");
85              Assertions.fail("No Data In File exception should have been raised");
86          } catch (OrekitException oe) {
87              Assertions.assertEquals(OrekitMessages.NO_DATA_IN_FILE, oe.getSpecifier());
88          }
89      }
90  
91      @Test
92      public void tesDuplicatedEntry() {
93          JB2008SpaceEnvironmentData JBData = new JB2008SpaceEnvironmentData("SOLFSMY_double.txt", "DTCFILE_trunc.TXT");
94          final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453000, Constants.JULIAN_DAY, utc);
95          assertThat(137.2, closeTo(JBData.getF10(julianDate), 1e-10));
96      }
97  
98      @Test
99      public void testUnableParse() {
100         try {
101             new JB2008SpaceEnvironmentData("SOLFSMY_badparse.txt", "DTCFILE_trunc.TXT");
102             Assertions.fail("UNABLE_TO_PARSE_LINE_IN_FILE exception should have been raised");
103         } catch (OrekitException oe) {
104             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
105         }
106     }
107 
108 
109 
110     @Test
111     public void testMinDate() {
112         JB2008SpaceEnvironmentData JBData = loadJB();
113         final AbsoluteDate startDate = new AbsoluteDate(2003, 12, 26, 12, 0, 0.0, utc);
114         Assertions.assertEquals(startDate, JBData.getMinDate());
115     }
116 
117     @Test
118     public void testMaxDate() {
119         JB2008SpaceEnvironmentData JBData = loadJB();
120         final AbsoluteDate lastDate = new AbsoluteDate(2007, 1, 1, 12, 0, 0.0, utc);
121         Assertions.assertEquals(lastDate, JBData.getMaxDate());
122     }
123 
124     @Test
125     public void testF10Interp() {
126         JB2008SpaceEnvironmentData JBData = loadJB();
127         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 12*3600, utc);
128         assertThat((116.0 + 105.6)/2, closeTo(JBData.getF10(julianDate), 1e-10));
129     }
130 
131     @Test
132     public void testF10() {
133         JB2008SpaceEnvironmentData JBData = loadJB();
134         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
135         assertThat(105.6, closeTo(JBData.getF10(julianDate), 1e-10));
136     }
137 
138     @Test
139     public void testF10B() {
140         JB2008SpaceEnvironmentData JBData = loadJB();
141         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
142         assertThat(120.6, closeTo(JBData.getF10B(julianDate), 1e-10));
143     }
144 
145     @Test
146     public void testS10() {
147         JB2008SpaceEnvironmentData JBData = loadJB();
148         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
149         assertThat(111.0, closeTo(JBData.getS10(julianDate), 1e-10));
150     }
151 
152     @Test
153     public void testS10B() {
154         JB2008SpaceEnvironmentData JBData = loadJB();
155         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
156         assertThat(116.8, closeTo(JBData.getS10B(julianDate), 1e-10));
157     }
158 
159     @Test
160     public void testM10() {
161         JB2008SpaceEnvironmentData JBData = loadJB();
162         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
163         assertThat(117.9, closeTo(JBData.getXM10(julianDate), 1e-10));
164     }
165 
166     @Test
167     public void testM10B() {
168         JB2008SpaceEnvironmentData JBData = loadJB();
169         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
170         assertThat(121.2, closeTo(JBData.getXM10B(julianDate), 1e-10));
171     }
172 
173     @Test
174     public void testY10() {
175         JB2008SpaceEnvironmentData JBData = loadJB();
176         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
177         assertThat(139.9, closeTo(JBData.getY10(julianDate), 1e-10));
178     }
179 
180     @Test
181     public void testY10B() {
182         JB2008SpaceEnvironmentData JBData = loadJB();
183         final AbsoluteDate julianDate = AbsoluteDate.createJDDate(2453006, 0, utc);
184         assertThat(129.5, closeTo(JBData.getY10B(julianDate), 1e-10));
185     }
186 
187 
188     /**
189      * Check integration error is small when integrating the same equations over the same
190      * interval.
191      */
192     @Test
193     public void testWithPropagator() {
194         CelestialBody sun = CelestialBodyFactory.getSun();
195         final Frame eci = FramesFactory.getGCRF();
196         final Frame ecef = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
197         AbsoluteDate date = new AbsoluteDate(2004, 1, 2, utc);
198         OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
199                 Constants.WGS84_EARTH_FLATTENING, ecef);
200         Orbit orbit = new KeplerianOrbit(6378137 + 400e3, 1e-3, FastMath.toRadians(50), 0, 0, 0, PositionAngleType.TRUE,
201                 eci, date, Constants.EIGEN5C_EARTH_MU);
202         final SpacecraftState ic = new SpacecraftState(orbit);
203 
204         final AbsoluteDate end = date.shiftedBy(5 * Constants.JULIAN_DAY);
205         final AbsoluteDate resetDate = date.shiftedBy(0.8 * Constants.JULIAN_DAY + 0.1);
206 
207         final SpacecraftState[] lastState = new SpacecraftState[1];
208         final OrekitStepHandler stepSaver = interpolator -> {
209             final AbsoluteDate start = interpolator.getPreviousState().getDate();
210             if (start.compareTo(resetDate) < 0) {
211                 lastState[0] = interpolator.getPreviousState();
212             }
213         };
214 
215         // propagate with state rest to take slightly different path
216         NumericalPropagator propagator = getNumericalPropagatorWithJB2008(sun, earth, ic);
217         propagator.setStepHandler(stepSaver);
218         propagator.propagate(resetDate);
219         propagator.resetInitialState(lastState[0]);
220         propagator.clearStepHandlers();
221         SpacecraftState actual = propagator.propagate(end);
222 
223         // propagate straight through
224         propagator = getNumericalPropagatorWithJB2008(sun, earth, ic);
225         propagator.resetInitialState(ic);
226         propagator.clearStepHandlers();
227         SpacecraftState expected = propagator.propagate(end);
228 
229         assertThat(actual.getPVCoordinates(), pvCloseTo(expected.getPVCoordinates(), 1.0));
230 
231     }
232 
233     /**
234      * Configure a numerical propagator with DTM2000 atmosphere.
235      *
236      * @param sun   Sun.
237      * @param earth Earth.
238      * @param ic    initial condition.
239      * @return a propagator with DTM2000 atmosphere.
240      */
241     private NumericalPropagator getNumericalPropagatorWithJB2008(CelestialBody sun, OneAxisEllipsoid earth,
242             SpacecraftState ic) {
243         // some non-integer step size to induce truncation error in flux interpolation
244         final ODEIntegrator integrator = new ClassicalRungeKuttaIntegrator(120 + 0.1);
245         NumericalPropagator propagator = new NumericalPropagator(integrator);
246         JB2008InputParameters JBData = loadJB();
247         final Atmosphere atmosphere = new JB2008(JBData, sun, earth);
248         final IsotropicDrag satellite = new IsotropicDrag(1, 3.2);
249         propagator.addForceModel(new DragForce(atmosphere, satellite));
250 
251         propagator.setInitialState(ic);
252         propagator.setOrbitType(OrbitType.CARTESIAN);
253 
254         return propagator;
255     }
256 
257     @Test
258     /**
259      * Testing for non-present day in the data
260      * Testing getF10
261      */
262     public void testF10EphemerisException() {
263         JB2008SpaceEnvironmentData JBData = loadJB();
264         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
265         try {
266             JBData.getF10(date);
267             Assertions.fail("an exception should have been thrown");
268         } catch (OrekitException oe) {
269             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
270         }
271     }
272 
273     @Test
274     /**
275      * Testing for non-present day in the data
276      * Testing getF10B
277      */
278     public void testF10BEphemerisException() {
279         JB2008SpaceEnvironmentData JBData = loadJB();
280         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
281         try {
282             JBData.getF10B(date);
283             Assertions.fail("an exception should have been thrown");
284         } catch (OrekitException oe) {
285             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
286         }
287     }
288 
289     @Test
290     /**
291      * Testing for non-present day in the data
292      * Testing getS10
293      */
294     public void testS10EphemerisException() {
295         JB2008SpaceEnvironmentData JBData = loadJB();
296         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
297         try {
298             JBData.getS10(date);
299             Assertions.fail("an exception should have been thrown");
300         } catch (OrekitException oe) {
301             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
302         }
303     }
304 
305     @Test
306     /**
307      * Testing for non-present day in the data
308      * Testing getS10B
309      */
310     public void testS10BEphemerisException() {
311         JB2008SpaceEnvironmentData JBData = loadJB();
312         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
313         try {
314             JBData.getS10(date);
315             Assertions.fail("an exception should have been thrown");
316         } catch (OrekitException oe) {
317             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
318         }
319     }
320 
321     @Test
322     /**
323      * Testing for non-present day in the data
324      * Testing getXM10
325      */
326     public void testXM10EphemerisException() {
327         JB2008SpaceEnvironmentData JBData = loadJB();
328         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
329         try {
330             JBData.getXM10(date);
331             Assertions.fail("an exception should have been thrown");
332         } catch (OrekitException oe) {
333             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
334         }
335     }
336 
337     @Test
338     /**
339      * Testing for non-present day in the data
340      * Testing getXM10B
341      */
342     public void testXM10BEphemerisException() {
343         JB2008SpaceEnvironmentData JBData = loadJB();
344         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
345         try {
346             JBData.getXM10B(date);
347             Assertions.fail("an exception should have been thrown");
348         } catch (OrekitException oe) {
349             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
350         }
351     }
352 
353     @Test
354     /**
355      * Testing for non-present day in the data
356      * Testing getY10
357      */
358     public void testY10EphemerisException() {
359         JB2008SpaceEnvironmentData JBData = loadJB();
360         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
361         try {
362             JBData.getY10(date);
363             Assertions.fail("an exception should have been thrown");
364         } catch (OrekitException oe) {
365             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
366         }
367     }
368 
369     @Test
370     /**
371      * Testing for non-present day in the data
372      * Testing getY10B
373      */
374     public void testY10BEphemerisException() {
375         JB2008SpaceEnvironmentData JBData = loadJB();
376         AbsoluteDate date = new AbsoluteDate(1957, 10, 1, 5, 17, 0.0, utc);
377         try {
378             JBData.getY10B(date);
379             Assertions.fail("an exception should have been thrown");
380         } catch (OrekitException oe) {
381             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, oe.getSpecifier());
382         }
383     }
384 
385     @Test
386     public void testBracketDateSOL_lastDate() {
387         JB2008SpaceEnvironmentData JBData = loadJB();
388         AbsoluteDate date = new AbsoluteDate(2050, 10, 1, 5, 17, 0.0, utc);
389         try {
390             JBData.getY10B(date);
391             Assertions.fail("an exception should have been thrown");
392         } catch (OrekitException oe) {
393             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER, oe.getSpecifier());
394         }
395     }
396 
397 }