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.files.stk;
19  
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.BeforeAll;
23  import org.junit.jupiter.api.Test;
24  import org.orekit.Utils;
25  import org.orekit.data.DataSource;
26  import org.orekit.errors.OrekitException;
27  import org.orekit.files.stk.STKEphemerisFile.STKCoordinateSystem;
28  import org.orekit.files.stk.STKEphemerisFile.STKEphemeris;
29  import org.orekit.files.stk.STKEphemerisFile.STKEphemerisSegment;
30  import org.orekit.frames.Frame;
31  import org.orekit.frames.FramesFactory;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.time.TimeScalesFactory;
34  import org.orekit.time.UTCScale;
35  import org.orekit.utils.CartesianDerivativesFilter;
36  import org.orekit.utils.Constants;
37  import org.orekit.utils.TimeStampedPVCoordinates;
38  
39  import java.io.StringReader;
40  import java.util.EnumMap;
41  import java.util.List;
42  
43  import static org.junit.jupiter.api.Assertions.assertEquals;
44  import static org.junit.jupiter.api.Assertions.assertThrows;
45  
46  /**
47   * Unit tests for {@link STKEphemerisFileParser}.
48   */
49  public final class STKEphemerisFileParserTest {
50  
51    private static final double MU = Constants.WGS84_EARTH_MU;
52    private static UTCScale UTC;
53  
54    @BeforeAll
55    static void setUp() {
56      Utils.setDataRoot("regular-data");
57      UTC = TimeScalesFactory.getUTC();
58    }
59  
60    /**
61     * Tests {@link STKEphemerisFileParser#parse(DataSource)} throws an exception if the file is empty.
62     */
63    @Test
64    void testParseEmptyFile() {
65      final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
66      final Frame frame = FramesFactory.getGCRF();
67      frameMapping.put(STKCoordinateSystem.ICRF, frame);
68      final DataSource source = new DataSource("", () -> new StringReader(""));
69      final STKEphemerisFileParser parser = new STKEphemerisFileParser("99999", MU, UTC, frameMapping);
70      assertThrows(OrekitException.class, () -> parser.parse(source));
71    }
72  
73    /**
74     * Tests {@link STKEphemerisFileParser#parse(DataSource)} correctly parses a file using the
75     * EphemerisTimePos format and which has a single segment.
76     */
77    @Test
78    void testParseEphemerisTimePosWithSingleSegment() {
79      final String ex = "/stk/stk_02674_p.e";
80      final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
81  
82      final String satelliteId = "02674";
83      final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
84      final Frame frame = FramesFactory.getEME2000();
85      frameMapping.put(STKCoordinateSystem.J2000, frame);
86  
87      final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
88  
89      final STKEphemerisFile file = parser.parse(source);
90  
91      assertEquals("stk.v.12.0", file.getSTKVersion());
92      assertEquals(1, file.getSatellites().size());
93  
94      final AbsoluteDate startDate = new AbsoluteDate(2007, 1, 12, 0, 0, 00.000883, UTC);
95      final AbsoluteDate stopDate = new AbsoluteDate(2007, 1, 12, 0, 10, 00.000883, UTC);
96  
97      final STKEphemeris ephemeris = file.getSatellites().get(satelliteId);
98      assertEquals(satelliteId, ephemeris.getId());
99      assertEquals(MU, ephemeris.getMu());
100     assertEquals(startDate, ephemeris.getStart());
101     assertEquals(stopDate, ephemeris.getStop());
102     assertEquals(1, ephemeris.getSegments().size());
103 
104     final STKEphemerisSegment segment = ephemeris.getSegments().get(0);
105     assertEquals(frame, segment.getFrame());
106     assertEquals(MU, segment.getMu());
107     assertEquals(startDate, segment.getStart());
108     assertEquals(stopDate, segment.getStop());
109     assertEquals(6, segment.getInterpolationSamples());
110     assertEquals(CartesianDerivativesFilter.USE_P, segment.getAvailableDerivatives());
111     assertEquals(11, segment.getCoordinates().size());
112 
113     final List<TimeStampedPVCoordinates> coordinates = segment.getCoordinates();
114 
115     assertEquals(startDate, coordinates.get(0).getDate());
116     assertEquals(new Vector3D(-4.2001828159554983e+06, -3.9105939267270239e+06, -4.5819301444368772e+06),
117             coordinates.get(0).getPosition());
118     assertEquals(Vector3D.ZERO, coordinates.get(0).getVelocity());
119     assertEquals(Vector3D.ZERO, coordinates.get(0).getAcceleration());
120 
121     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), coordinates.get(5).getDate());
122     assertEquals(new Vector3D(-2.3930847437297828e+06, -5.1030332553920103e+06, -4.6952642374704480e+06),
123             coordinates.get(5).getPosition());
124     assertEquals(Vector3D.ZERO, coordinates.get(5).getVelocity());
125     assertEquals(Vector3D.ZERO, coordinates.get(5).getAcceleration());
126 
127     assertEquals(stopDate, coordinates.get(10).getDate());
128     assertEquals(new Vector3D(-3.7051401425713405e+05, -5.8354092318012062e+06, -4.3842831794793038e+06),
129             coordinates.get(10).getPosition());
130     assertEquals(Vector3D.ZERO, coordinates.get(10).getVelocity());
131     assertEquals(Vector3D.ZERO, coordinates.get(10).getAcceleration());
132   }
133 
134   /**
135    * Tests {@link STKEphemerisFileParser#parse(DataSource)} correctly parses a file using the
136    * EphemerisTimePosVel format and which has a single segment.
137    */
138   @Test
139   void testParseEphemerisTimePosVelWithSingleSegment() {
140     final String ex = "/stk/stk_02674_pv.e";
141     final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
142 
143     final String satelliteId = "02674";
144     final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
145     final Frame frame = FramesFactory.getEME2000();
146     frameMapping.put(STKCoordinateSystem.J2000, frame);
147 
148     final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
149 
150     final STKEphemerisFile file = parser.parse(source);
151 
152     assertEquals("stk.v.12.0", file.getSTKVersion());
153     assertEquals(1, file.getSatellites().size());
154 
155     final AbsoluteDate startDate = new AbsoluteDate(2007, 1, 12, 0, 0, 00.000883, UTC);
156     final AbsoluteDate stopDate = new AbsoluteDate(2007, 1, 12, 0, 10, 00.000883, UTC);
157 
158     final STKEphemeris ephemeris = file.getSatellites().get(satelliteId);
159     assertEquals(satelliteId, ephemeris.getId());
160     assertEquals(MU, ephemeris.getMu());
161     assertEquals(startDate, ephemeris.getStart());
162     assertEquals(stopDate, ephemeris.getStop());
163     assertEquals(1, ephemeris.getSegments().size());
164 
165     final STKEphemerisSegment segment = ephemeris.getSegments().get(0);
166     assertEquals(frame, segment.getFrame());
167     assertEquals(MU, segment.getMu());
168     assertEquals(startDate, segment.getStart());
169     assertEquals(stopDate, segment.getStop());
170     assertEquals(6, segment.getInterpolationSamples());
171     assertEquals(CartesianDerivativesFilter.USE_PV, segment.getAvailableDerivatives());
172     assertEquals(11, segment.getCoordinates().size());
173 
174     final List<TimeStampedPVCoordinates> coordinates = segment.getCoordinates();
175 
176     assertEquals(startDate, coordinates.get(0).getDate());
177     assertEquals(new Vector3D(-4.2001828159554983e+06, -3.9105939267270239e+06, -4.5819301444368772e+06),
178             coordinates.get(0).getPosition());
179     assertEquals(new Vector3D(5.4770282903204152e+03, -4.6296785954320931e+03, -1.0817325337227874e+03),
180             coordinates.get(0).getVelocity());
181     assertEquals(Vector3D.ZERO, coordinates.get(0).getAcceleration());
182 
183     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), coordinates.get(5).getDate());
184     assertEquals(new Vector3D(-2.3930847437297828e+06, -5.1030332553920103e+06, -4.6952642374704480e+06),
185             coordinates.get(5).getPosition());
186     assertEquals(new Vector3D(6.4794691416813621e+03, -3.2589590411954355e+03, 3.3269516839203544e+02),
187             coordinates.get(5).getVelocity());
188     assertEquals(Vector3D.ZERO, coordinates.get(5).getAcceleration());
189 
190     assertEquals(stopDate, coordinates.get(10).getDate());
191     assertEquals(new Vector3D(-3.7051401425713405e+05, -5.8354092318012062e+06, -4.3842831794793038e+06),
192             coordinates.get(10).getPosition());
193     assertEquals(new Vector3D(6.9022331002597584e+03, -1.5830164360578574e+03, 1.7272100173740068e+03),
194             coordinates.get(10).getVelocity());
195     assertEquals(Vector3D.ZERO, coordinates.get(10).getAcceleration());
196   }
197 
198   /**
199    * Tests {@link STKEphemerisFileParser#parse(DataSource)} correctly parses a file using the
200    * EphemerisTimePosVel format and which has multiple segments.
201    */
202   @Test
203   void testParseEphemerisTimePosVelWithMultipleSegments() {
204     final String ex = "/stk/stk_impulsive_maneuver.e";
205     final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
206 
207     final String satelliteId = "02674";
208     final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
209     final Frame frame = FramesFactory.getEME2000();
210     frameMapping.put(STKCoordinateSystem.J2000, frame);
211 
212     final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
213 
214     final STKEphemerisFile file = parser.parse(source);
215 
216     assertEquals("stk.v.12.0", file.getSTKVersion());
217     assertEquals(1, file.getSatellites().size());
218 
219     final AbsoluteDate startDate = new AbsoluteDate(2007, 1, 12, 0, 0, 00.000883, UTC);
220     final AbsoluteDate stopDate = new AbsoluteDate(2007, 1, 12, 0, 10, 00.000883, UTC);
221 
222     final STKEphemeris ephemeris = file.getSatellites().get(satelliteId);
223     assertEquals(satelliteId, ephemeris.getId());
224     assertEquals(MU, ephemeris.getMu());
225     assertEquals(startDate, ephemeris.getStart());
226     assertEquals(stopDate, ephemeris.getStop());
227     assertEquals(2, ephemeris.getSegments().size());
228 
229     final STKEphemerisSegment segment1 = ephemeris.getSegments().get(0);
230     assertEquals(frame, segment1.getFrame());
231     assertEquals(MU, segment1.getMu());
232     assertEquals(startDate, segment1.getStart());
233     assertEquals(5, segment1.getInterpolationSamples());
234     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), segment1.getStop());
235     assertEquals(CartesianDerivativesFilter.USE_PV, segment1.getAvailableDerivatives());
236     assertEquals(6, segment1.getCoordinates().size());
237 
238     final List<TimeStampedPVCoordinates> coordinates1 = segment1.getCoordinates();
239 
240     assertEquals(startDate, coordinates1.get(0).getDate());
241     assertEquals(new Vector3D(6.9999999999999553e+06, -1.2559721622877134e-03, -7.9186900106575397e-01),
242             coordinates1.get(0).getPosition());
243     assertEquals(new Vector3D(4.1824064674153972e-04, 6.7895303333415395e+03, 3.6864141134955967e+03),
244             coordinates1.get(0).getVelocity());
245     assertEquals(Vector3D.ZERO, coordinates1.get(0).getAcceleration());
246 
247     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 1, 00.000883, UTC), coordinates1.get(1).getDate());
248     assertEquals(new Vector3D(6.9853436721346313e+06, 4.0708747687269317e+05, 2.2102922871869511e+05),
249             coordinates1.get(1).getPosition());
250     assertEquals(new Vector3D(-4.8834963369038530e+02, 6.7753160807054328e+03, 3.6786747792111828e+03),
251             coordinates1.get(1).getVelocity());
252     assertEquals(Vector3D.ZERO, coordinates1.get(1).getAcceleration());
253 
254     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), coordinates1.get(5).getDate());
255     assertEquals(new Vector3D(6.6370788283674866e+06, 2.0015702515387577e+06, 1.0867112801511162e+06),
256             coordinates1.get(5).getPosition());
257     assertEquals(new Vector3D(-2.3954153910685013e+03, 6.4383927907853858e+03, 3.4952501591096920e+03),
258             coordinates1.get(5).getVelocity());
259     assertEquals(Vector3D.ZERO, coordinates1.get(5).getAcceleration());
260 
261     final STKEphemerisSegment segment2 = ephemeris.getSegments().get(1);
262     assertEquals(frame, segment2.getFrame());
263     assertEquals(MU, segment2.getMu());
264     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), segment2.getStart());
265     assertEquals(stopDate, segment2.getStop());
266     assertEquals(5, segment2.getInterpolationSamples());
267     assertEquals(CartesianDerivativesFilter.USE_PV, segment2.getAvailableDerivatives());
268     assertEquals(6, segment2.getCoordinates().size());
269 
270     final List<TimeStampedPVCoordinates> coordinates2 = segment2.getCoordinates();
271 
272     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), coordinates2.get(0).getDate());
273     assertEquals(new Vector3D(6.6370788283674866e+06, 2.0015702515387577e+06, 1.0867112801511162e+06),
274             coordinates2.get(0).getPosition());
275     assertEquals(new Vector3D(-2.4109546214707530e+03, 6.4801591037886537e+03, 3.5179240960554193e+03),
276             coordinates2.get(0).getVelocity());
277     assertEquals(Vector3D.ZERO, coordinates2.get(0).getAcceleration());
278 
279     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 6, 00.000883, UTC), coordinates2.get(1).getDate());
280     assertEquals(new Vector3D(6.4787418082331438e+06, 2.3859551936109182e+06, 1.2953780350291547e+06),
281             coordinates2.get(1).getPosition());
282     assertEquals(new Vector3D(-2.8648687835270284e+03, 6.3283234273332882e+03, 3.4352673416780117e+03),
283             coordinates2.get(1).getVelocity());
284     assertEquals(Vector3D.ZERO, coordinates2.get(1).getAcceleration());
285 
286     assertEquals(stopDate, coordinates2.get(5).getDate());
287     assertEquals(new Vector3D(5.5862903748328788e+06, 3.8099552373593030e+06, 2.0682393977866683e+06),
288             coordinates2.get(5).getPosition());
289     assertEquals(new Vector3D(-4.5259006372314416e+03, 5.4761825144401137e+03, 2.9714200584292998e+03),
290             coordinates2.get(5).getVelocity());
291     assertEquals(Vector3D.ZERO, coordinates2.get(5).getAcceleration());
292   }
293 
294   /**
295    * Tests {@link STKEphemerisFileParser#parse(DataSource)} correctly parses a file using the
296    * EphemerisTimePosVelAcc format and which has a single segment.
297    */
298   @Test
299   void testParseEphemerisTimePosVelAccWithSingleSegment() {
300     final String ex = "/stk/stk_02674_pva.e";
301     final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
302 
303     final String satelliteId = "02674";
304     final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
305     final Frame frame = FramesFactory.getEME2000();
306     frameMapping.put(STKCoordinateSystem.J2000, frame);
307 
308     final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
309 
310     final STKEphemerisFile file = parser.parse(source);
311 
312     assertEquals("stk.v.12.0", file.getSTKVersion());
313     assertEquals(1, file.getSatellites().size());
314 
315     final AbsoluteDate startDate = new AbsoluteDate(2007, 1, 12, 0, 0, 00.000883, UTC);
316     final AbsoluteDate stopDate = new AbsoluteDate(2007, 1, 12, 0, 10, 00.000883, UTC);
317 
318     final STKEphemeris ephemeris = file.getSatellites().get(satelliteId);
319     assertEquals(satelliteId, ephemeris.getId());
320     assertEquals(MU, ephemeris.getMu());
321     assertEquals(startDate, ephemeris.getStart());
322     assertEquals(stopDate, ephemeris.getStop());
323     assertEquals(1, ephemeris.getSegments().size());
324 
325     final STKEphemerisSegment segment = ephemeris.getSegments().get(0);
326     assertEquals(frame, segment.getFrame());
327     assertEquals(MU, segment.getMu());
328     assertEquals(startDate, segment.getStart());
329     assertEquals(stopDate, segment.getStop());
330     assertEquals(6, segment.getInterpolationSamples());
331     assertEquals(CartesianDerivativesFilter.USE_PVA, segment.getAvailableDerivatives());
332     assertEquals(11, segment.getCoordinates().size());
333 
334     final List<TimeStampedPVCoordinates> coordinates = segment.getCoordinates();
335 
336     assertEquals(startDate, coordinates.get(0).getDate());
337     assertEquals(new Vector3D(-4.2001828159554983e+06, -3.9105939267270239e+06, -4.5819301444368772e+06),
338             coordinates.get(0).getPosition());
339     assertEquals(new Vector3D(5.4770282903204152e+03, -4.6296785954320931e+03, -1.0817325337227874e+03),
340             coordinates.get(0).getVelocity());
341     assertEquals(new Vector3D(4.2195714111001097e+00, 3.9335215635670262e+00, 4.6186527996456137e+00),
342             coordinates.get(0).getAcceleration());
343 
344     assertEquals(new AbsoluteDate(2007, 1, 12, 0, 5, 00.000883, UTC), coordinates.get(5).getDate());
345     assertEquals(new Vector3D(-2.3930847437297828e+06, -5.1030332553920103e+06, -4.6952642374704480e+06),
346             coordinates.get(5).getPosition());
347     assertEquals(new Vector3D(6.4794691416813621e+03, -3.2589590411954355e+03, 3.3269516839203544e+02),
348             coordinates.get(5).getVelocity());
349     assertEquals(new Vector3D(2.4098478990321057e+00, 5.1474331925129251e+00, 4.7467936751027020e+00),
350             coordinates.get(5).getAcceleration());
351 
352     assertEquals(stopDate, coordinates.get(10).getDate());
353     assertEquals(new Vector3D(-3.7051401425713405e+05, -5.8354092318012062e+06, -4.3842831794793038e+06),
354             coordinates.get(10).getPosition());
355     assertEquals(new Vector3D(6.9022331002597584e+03, -1.5830164360578574e+03, 1.7272100173740068e+03),
356             coordinates.get(10).getVelocity());
357     assertEquals(new Vector3D(3.7440376244610168e-1, 5.9536789668958612e+00, 4.4833066969906747e+00),
358             coordinates.get(10).getAcceleration());
359   }
360 
361   /**
362    * Tests {@link STKEphemerisFileParser#parse(DataSource)} when the coordinate system is invalid.
363    */
364   @Test
365   void testParseInvalidCoordinateSystem() {
366     final String ex = "/stk/stk_invalid_coordinate_system.e";
367     final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
368 
369     final String satelliteId = "02674";
370     final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
371     final Frame frame = FramesFactory.getEME2000();
372     frameMapping.put(STKCoordinateSystem.J2000, frame);
373 
374     final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
375     assertThrows(OrekitException.class, () -> parser.parse(source));
376   }
377 
378   /**
379    * Tests {@link STKEphemerisFileParser#parse(DataSource)} when the coordinate system is not mapped.
380    */
381   @Test
382   void testParseUnmappedCoordinateSystem() {
383     final String ex = "/stk/stk_02674_p.e";
384     final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
385 
386     final String satelliteId = "02674";
387     final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
388     final Frame frame = FramesFactory.getGCRF();
389     frameMapping.put(STKCoordinateSystem.ICRF, frame);
390 
391     final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
392     final OrekitException exception = assertThrows(OrekitException.class, () -> parser.parse(source));
393     assertEquals("STK coordinate system \"J2000\" has not been mapped to an Orekit frame", exception.getMessage());
394   }
395 
396   /** Test that parsing the keyword "DistanceUnit" does not cause an exception.
397    * See issue 1930
398    */
399   @Test
400   void testIssue1930ParseDistanceUnit() {
401 
402     final String content = "stk.v.12.0\n" +
403             "BEGIN Ephemeris\n" +
404             "NumberOfEphemerisPoints 1\n" +
405             "ScenarioEpoch 01 Jan 2000 12:00:00.000000\n" +
406             "InterpolationMethod Lagrange\n" +
407             "InterpolationSamplesM1 7\n" +
408             "CentralBody Earth\n" +
409             "CoordinateSystem J2000\n" +
410             "DistanceUnit Kilometers\n" +
411             "EphemerisTimePosVel\n" +
412             "0.0000000000000000e+00  1.0000000000000000e+03  2.0000000000000000e+03  3.0000000000000000e+03  1.0000000000000000e+00  2.0000000000000000e+00  3.0000000000000000e+00\n" +
413             "END Ephemeris";
414 
415     final DataSource source = new DataSource("inline", () -> new StringReader(content));
416     final String satelliteId = "00000";
417     final EnumMap<STKCoordinateSystem, Frame> frameMapping = new EnumMap<>(STKCoordinateSystem.class);
418     final Frame frame = FramesFactory.getEME2000();
419     frameMapping.put(STKCoordinateSystem.J2000, frame);
420     final STKEphemerisFileParser parser = new STKEphemerisFileParser(satelliteId, MU, UTC, frameMapping);
421     Assertions.assertDoesNotThrow(() -> parser.parse(source));
422   }
423 
424 }