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.bodies;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  import org.orekit.Utils;
24  import org.orekit.data.DataContext;
25  import org.orekit.frames.Frame;
26  import org.orekit.frames.FramesFactory;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.time.TimeScalesFactory;
29  import org.orekit.utils.Constants;
30  import org.orekit.utils.PVCoordinates;
31  
32  import java.io.IOException;
33  
34  public class JPLEphemeridesLoaderTest {
35  
36      @Test
37      void testConstantsJPL() {
38          Utils.setDataRoot("regular-data/de405-ephemerides");
39  
40          JPLEphemeridesLoader loader =
41              new JPLEphemeridesLoader(JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES,
42                                       JPLEphemeridesLoader.EphemerisType.SUN);
43          Assertions.assertEquals(149597870691.0, loader.getLoadedAstronomicalUnit(), 0.1);
44          Assertions.assertEquals(81.30056, loader.getLoadedEarthMoonMassRatio(), 1.0e-8);
45          Assertions.assertTrue(Double.isNaN(loader.getLoadedConstant("not-a-constant")));
46      }
47  
48      @Test
49      void testConstantsInpop() {
50          Utils.setDataRoot("inpop");
51          JPLEphemeridesLoader loader =
52              new JPLEphemeridesLoader(JPLEphemeridesLoader.DEFAULT_INPOP_SUPPORTED_NAMES,
53                                       JPLEphemeridesLoader.EphemerisType.SUN);
54          Assertions.assertEquals(149597870691.0, loader.getLoadedAstronomicalUnit(), 0.1);
55          Assertions.assertEquals(81.30057, loader.getLoadedEarthMoonMassRatio(), 1.0e-8);
56      }
57  
58      @Test
59      void testGMJPL() {
60          Utils.setDataRoot("regular-data/de405-ephemerides");
61  
62          JPLEphemeridesLoader loader =
63              new JPLEphemeridesLoader(JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES,
64                                       JPLEphemeridesLoader.EphemerisType.SUN);
65          Assertions.assertEquals(22032.080e9,
66                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.MERCURY),
67                              1.0e6);
68          Assertions.assertEquals(324858.599e9,
69                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.VENUS),
70                              1.0e6);
71          Assertions.assertEquals(42828.314e9,
72                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.MARS),
73                              1.0e6);
74          Assertions.assertEquals(126712767.863e9,
75                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.JUPITER),
76                              6.0e7);
77          Assertions.assertEquals(37940626.063e9,
78                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.SATURN),
79                              2.0e6);
80          Assertions.assertEquals(5794549.007e9,
81                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.URANUS),
82                              1.0e6);
83          Assertions.assertEquals(6836534.064e9,
84                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.NEPTUNE),
85                              1.0e6);
86          Assertions.assertEquals(981.601e9,
87                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.PLUTO),
88                              1.0e6);
89          Assertions.assertEquals(132712440017.987e9,
90                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.SUN),
91                              1.0e6);
92          Assertions.assertEquals(4902.801e9,
93                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.MOON),
94                              1.0e6);
95          Assertions.assertEquals(403503.233e9,
96                              loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.EARTH_MOON),
97                              1.0e6);
98      }
99  
100     @Test
101     void testGMInpop() {
102 
103         Utils.setDataRoot("inpop");
104 
105         JPLEphemeridesLoader loader =
106                 new JPLEphemeridesLoader("^inpop.*TCB.*littleendian.*\\.dat$",
107                                          JPLEphemeridesLoader.EphemerisType.SUN);
108         Assertions.assertEquals(22032.081e9,
109                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.MERCURY),
110                             1.0e6);
111         Assertions.assertEquals(324858.597e9,
112                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.VENUS),
113                             1.0e6);
114         Assertions.assertEquals(42828.376e9,
115                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.MARS),
116                             1.0e6);
117         Assertions.assertEquals(126712764.535e9,
118                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.JUPITER),
119                             6.0e7);
120         Assertions.assertEquals(37940585.443e9,
121                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.SATURN),
122                             2.0e6);
123         Assertions.assertEquals(5794549.099e9,
124                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.URANUS),
125                             1.0e6);
126         Assertions.assertEquals(6836527.128e9,
127                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.NEPTUNE),
128                             1.0e6);
129         Assertions.assertEquals(971.114e9,
130                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.PLUTO),
131                             1.0e6);
132         Assertions.assertEquals(132712442110.032e9,
133                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.SUN),
134                             1.0e6);
135         Assertions.assertEquals(4902.800e9,
136                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.MOON),
137                             1.0e6);
138         Assertions.assertEquals(403503.250e9,
139                             loader.getLoadedGravitationalCoefficient(JPLEphemeridesLoader.EphemerisType.EARTH_MOON),
140                             1.0e6);
141     }
142 
143     @Test
144     void testDerivative405() {
145         Utils.setDataRoot("regular-data/de405-ephemerides");
146         checkDerivative(JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES,
147                         new AbsoluteDate(1969, 6, 25, TimeScalesFactory.getTT()),
148                         691200.0);
149     }
150 
151     @Test
152     void testDerivative406() {
153         Utils.setDataRoot("regular-data:regular-data/de406-ephemerides");
154         checkDerivative(JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES,
155                         new AbsoluteDate(2964, 9, 26, TimeScalesFactory.getTT()),
156                         1382400.0);
157     }
158 
159     @Test
160     void testDummyEarth() {
161         Utils.setDataRoot("regular-data/de405-ephemerides");
162         JPLEphemeridesLoader loader =
163                 new JPLEphemeridesLoader(JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES,
164                                          JPLEphemeridesLoader.EphemerisType.EARTH);
165         CelestialBody body = loader.loadCelestialBody(CelestialBodyFactory.EARTH);
166         AbsoluteDate date = new AbsoluteDate(1950, 1, 12, TimeScalesFactory.getTT());
167         Frame eme2000 = FramesFactory.getEME2000();
168         for (double h = 0; h < 86400; h += 60.0) {
169             PVCoordinates pv = body.getPVCoordinates(date, eme2000);
170             Assertions.assertEquals(0, pv.getPosition().getNorm(), 1.0e-15);
171             Assertions.assertEquals(0, pv.getVelocity().getNorm(), 1.0e-15);
172         }
173     }
174 
175     @Test
176     void testEndianness() {
177         Utils.setDataRoot("inpop");
178         JPLEphemeridesLoader.EphemerisType type = JPLEphemeridesLoader.EphemerisType.MARS;
179         JPLEphemeridesLoader loaderInpopTCBBig =
180                 new JPLEphemeridesLoader("^inpop.*_TCB_.*_bigendian\\.dat$", type);
181         CelestialBody bodysInpopTCBBig = loaderInpopTCBBig.loadCelestialBody(CelestialBodyFactory.MARS);
182         Assertions.assertEquals(1.0, loaderInpopTCBBig.getLoadedConstant("TIMESC"), 1.0e-10);
183         JPLEphemeridesLoader loaderInpopTCBLittle =
184                 new JPLEphemeridesLoader("^inpop.*_TCB_.*_littleendian\\.dat$", type);
185         CelestialBody bodysInpopTCBLittle = loaderInpopTCBLittle.loadCelestialBody(CelestialBodyFactory.MARS);
186         Assertions.assertEquals(1.0, loaderInpopTCBLittle.getLoadedConstant("TIMESC"), 1.0e-10);
187         AbsoluteDate t0 = new AbsoluteDate(1969, 7, 17, 10, 43, 23.4, TimeScalesFactory.getTT());
188         Frame eme2000   = FramesFactory.getEME2000();
189         for (double dt = 0; dt < 30 * Constants.JULIAN_DAY; dt += 3600) {
190             AbsoluteDate date        = t0.shiftedBy(dt);
191             Vector3D pInpopTCBBig    = bodysInpopTCBBig.getPosition(date, eme2000);
192             Vector3D pInpopTCBLittle = bodysInpopTCBLittle.getPosition(date, eme2000);
193             Assertions.assertEquals(0.0, pInpopTCBBig.distance(pInpopTCBLittle), 1.0e-10);
194         }
195         for (String name : DataContext.getDefault().getDataProvidersManager().getLoadedDataNames()) {
196             Assertions.assertTrue(name.contains("inpop"));
197         }
198     }
199 
200     @Test
201     void testInpopvsJPL() {
202         Utils.setDataRoot("regular-data:inpop");
203         JPLEphemeridesLoader.EphemerisType type = JPLEphemeridesLoader.EphemerisType.MARS;
204         JPLEphemeridesLoader loaderDE405 =
205                 new JPLEphemeridesLoader("^unxp(\\d\\d\\d\\d)\\.405$", type);
206         CelestialBody bodysDE405 = loaderDE405.loadCelestialBody(CelestialBodyFactory.MARS);
207         JPLEphemeridesLoader loaderInpopTDBBig =
208                 new JPLEphemeridesLoader("^inpop.*_TDB_.*_bigendian\\.dat$", type);
209         CelestialBody bodysInpopTDBBig = loaderInpopTDBBig.loadCelestialBody(CelestialBodyFactory.MARS);
210         Assertions.assertEquals(0.0, loaderInpopTDBBig.getLoadedConstant("TIMESC"), 1.0e-10);
211         JPLEphemeridesLoader loaderInpopTCBBig =
212                 new JPLEphemeridesLoader("^inpop.*_TCB_.*_bigendian\\.dat$", type);
213         CelestialBody bodysInpopTCBBig = loaderInpopTCBBig.loadCelestialBody(CelestialBodyFactory.MARS);
214         Assertions.assertEquals(1.0, loaderInpopTCBBig.getLoadedConstant("TIMESC"), 1.0e-10);
215         AbsoluteDate t0 = new AbsoluteDate(1969, 7, 17, 10, 43, 23.4, TimeScalesFactory.getTT());
216         Frame eme2000   = FramesFactory.getEME2000();
217         for (double dt = 0; dt < 30 * Constants.JULIAN_DAY; dt += 3600) {
218             AbsoluteDate date = t0.shiftedBy(dt);
219             Vector3D pDE405          = bodysDE405.getPosition(date, eme2000);
220             Vector3D pInpopTDBBig    = bodysInpopTDBBig.getPosition(date, eme2000);
221             Vector3D pInpopTCBBig    = bodysInpopTCBBig.getPosition(date, eme2000);
222             Assertions.assertTrue(pDE405.distance(pInpopTDBBig) >  650.0);
223             Assertions.assertTrue(pDE405.distance(pInpopTDBBig) < 1050.0);
224             Assertions.assertTrue(pDE405.distance(pInpopTCBBig) > 1000.0);
225             Assertions.assertTrue(pDE405.distance(pInpopTCBBig) < 2000.0);
226         }
227 
228     }
229 
230     @Test
231     void testOverlappingEphemeridesData() throws IOException {
232         Utils.setDataRoot("overlapping-data/data.zip");
233 
234         // the data root contains two ephemerides files (JPL DE 405), which overlap in the period
235         // (1999-12-23T23:58:55.816, 2000-01-24T23:58:55.815)
236         // this test checks that the data in the overlapping and surrounding range is loaded correctly
237         // from both files (see issue #113).
238 
239         // as the bug only manifests if the DataLoader first loads the ephemerides file containing earlier
240         // data points, the data files are zipped to get a deterministic order when listing files
241 
242         CelestialBody moon = CelestialBodyFactory.getMoon();
243 
244         // 1999/12/31 0h00
245         final AbsoluteDate initDate = new AbsoluteDate(1999, 12, 31, 00, 00, 00, TimeScalesFactory.getUTC());
246         moon.getPVCoordinates(initDate, FramesFactory.getGCRF());
247 
248         // 2000/04/01 0h00
249         final AbsoluteDate otherDate = new AbsoluteDate(2000, 02, 01, 00, 00, 00, TimeScalesFactory.getUTC());
250         moon.getPVCoordinates(otherDate, FramesFactory.getGCRF());
251 
252         // 3 years from initDate
253         AbsoluteDate currentDate = new AbsoluteDate(1999, 12, 01, 00, 00, 00, TimeScalesFactory.getTAI());
254         AbsoluteDate finalDate = new AbsoluteDate(2000, 03, 14, 00, 00, 00, TimeScalesFactory.getTAI());
255 
256         while (currentDate.compareTo(finalDate) < 0)  {
257             currentDate = currentDate.shiftedBy(Constants.JULIAN_DAY);
258             moon.getPVCoordinates(currentDate, FramesFactory.getGCRF());
259         }
260 
261     }
262 
263     private void checkDerivative(String supportedNames, AbsoluteDate date, double maxChunkDuration)
264         {
265         JPLEphemeridesLoader loader =
266             new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MERCURY);
267         CelestialBody body = loader.loadCelestialBody(CelestialBodyFactory.MERCURY);
268         double h = 20;
269 
270         // eight points finite differences estimation of the velocity
271         Frame eme2000 = FramesFactory.getEME2000();
272         Vector3D pm4h = body.getPosition(date.shiftedBy(-4 * h), eme2000);
273         Vector3D pm3h = body.getPosition(date.shiftedBy(-3 * h), eme2000);
274         Vector3D pm2h = body.getPosition(date.shiftedBy(-2 * h), eme2000);
275         Vector3D pm1h = body.getPosition(date.shiftedBy(    -h), eme2000);
276         Vector3D pp1h = body.getPosition(date.shiftedBy(     h), eme2000);
277         Vector3D pp2h = body.getPosition(date.shiftedBy( 2 * h), eme2000);
278         Vector3D pp3h = body.getPosition(date.shiftedBy( 3 * h), eme2000);
279         Vector3D pp4h = body.getPosition(date.shiftedBy( 4 * h), eme2000);
280         Vector3D d4   = pp4h.subtract(pm4h);
281         Vector3D d3   = pp3h.subtract(pm3h);
282         Vector3D d2   = pp2h.subtract(pm2h);
283         Vector3D d1   = pp1h.subtract(pm1h);
284         double c = 1.0 / (840 * h);
285         Vector3D estimatedV = new Vector3D(-3 * c, d4, 32 * c, d3, -168 * c, d2, 672 * c, d1);
286 
287         Vector3D loadedV = body.getPVCoordinates(date, eme2000).getVelocity();
288         Assertions.assertEquals(0, loadedV.subtract(estimatedV).getNorm(), 3.5e-10 * loadedV.getNorm());
289         Assertions.assertEquals(maxChunkDuration, loader.getMaxChunksDuration(), 1.0e-10);
290     }
291 
292     @BeforeEach
293     public void setUp() {
294         Utils.setDataRoot("regular-data");
295     }
296 
297 }