1   /* Copyright 2002-2012 Space Applications Services
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.files.sp3;
18  
19  import java.util.Arrays;
20  import java.util.List;
21  
22  import org.hipparchus.analysis.polynomials.PolynomialFunction;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.BeforeEach;
26  import org.junit.jupiter.api.Test;
27  import org.orekit.Utils;
28  import org.orekit.attitudes.FrameAlignedProvider;
29  import org.orekit.data.DataSource;
30  import org.orekit.data.UnixCompressFilter;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitIllegalArgumentException;
33  import org.orekit.errors.OrekitMessages;
34  import org.orekit.frames.Frame;
35  import org.orekit.frames.FramesFactory;
36  import org.orekit.frames.ITRFVersion;
37  import org.orekit.frames.VersionedITRF;
38  import org.orekit.gnss.IGSUtils;
39  import org.orekit.gnss.TimeSystem;
40  import org.orekit.propagation.BoundedPropagator;
41  import org.orekit.propagation.SpacecraftState;
42  import org.orekit.time.AbsoluteDate;
43  import org.orekit.time.AggregatedClockModel;
44  import org.orekit.time.ClockModel;
45  import org.orekit.time.ClockOffset;
46  import org.orekit.time.SampledClockModel;
47  import org.orekit.time.TimeScale;
48  import org.orekit.time.TimeScalesFactory;
49  import org.orekit.utils.CartesianDerivativesFilter;
50  import org.orekit.utils.Constants;
51  import org.orekit.utils.IERSConventions;
52  import org.orekit.utils.PVCoordinates;
53  import org.orekit.utils.TimeSpanMap;
54  
55  public class SP3ParserTest {
56  
57      @Test
58      public void testIssue1614() {
59          Assertions.assertEquals(7, SP3Parser.DEFAULT_INTERPOLATION_SAMPLES);
60      }
61  
62      @Test
63      public void testParseSP3a1() {
64          // simple test for version sp3-a, only contains position entries
65          final String    ex     = "/sp3/example-a-1.sp3";
66          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
67          final SP3   file   = new SP3Parser().parse(source);
68  
69          Assertions.assertEquals('a', file.getHeader().getVersion());
70          Assertions.assertEquals(SP3OrbitType.FIT, file.getHeader().getOrbitType());
71          Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
72          Assertions.assertEquals(ITRFVersion.ITRF_1992,
73                                  ((VersionedITRF) file.getSatellites().get("1").getFrame()).getITRFVersion());
74  
75          Assertions.assertEquals(25, file.getSatelliteCount());
76  
77          final List<SP3Coordinate> coords = file.getSatellites().get("1").getSegments().get(0).getCoordinates();
78          Assertions.assertEquals(2, coords.size());
79  
80          final SP3Coordinate coord = coords.get(0);
81  
82          // 1994 12 17 0 0 0.00000000
83          Assertions.assertEquals(new AbsoluteDate(1994, 12, 17, 0, 0, 0,
84                  TimeScalesFactory.getGPS()), coord.getDate());
85  
86          // P 1 16258.524750 -3529.015750 -20611.427050 -62.540600
87          checkPVEntry(new PVCoordinates(new Vector3D(16258524.75, -3529015.75, -20611427.049),
88                                         Vector3D.ZERO),
89                       coord);
90          Assertions.assertEquals(-0.0000625406, coord.getClockCorrection(), 1.0e-15);
91          Assertions.assertEquals("NGS", file.getHeader().getAgency());
92          Assertions.assertEquals("ITR92", file.getHeader().getCoordinateSystem());
93          Assertions.assertEquals(1, file.getHeader().getDataUsed().size());
94          Assertions.assertEquals(DataUsed.TWO_RECEIVER_TWO_SATELLITE_CARRIER_PHASE, file.getHeader().getDataUsed().get(0));
95          Assertions.assertEquals(0.0, file.getHeader().getDayFraction(), 1.0e-15);
96          Assertions.assertEquals("1994-12-16T23:59:50.000", file.getHeader().getEpoch().toString(TimeScalesFactory.getUTC()));
97          Assertions.assertEquals(49703, file.getHeader().getModifiedJulianDay());
98          Assertions.assertEquals(3, file.getHeader().getNumberOfEpochs());
99          Assertions.assertEquals(900.0, file.getHeader().getEpochInterval(), 1.0e-15);
100         Assertions.assertEquals(779, file.getHeader().getGpsWeek());
101         Assertions.assertEquals(518400.0, file.getHeader().getSecondsOfWeek(), 1.0e-10);
102         Assertions.assertEquals(25, file.getSatellites().size());
103         Assertions.assertEquals(SP3FileType.UNDEFINED, file.getHeader().getType());
104         Assertions.assertNull(file.getSatellites().get(null));
105     }
106 
107     @Test
108     public void testClockModel() {
109         // simple test for version sp3-a, only contains position entries
110         final String     ex         = "/sp3/gbm18432.sp3.Z";
111         final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
112         final DataSource source     = new UnixCompressFilter().filter(compressed);
113         final SP3Parser  parser     = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 6, IGSUtils::guessFrame);
114         final SP3        sp3        = parser.parse(source);
115         final TimeScale  ts         = sp3.getHeader().getTimeSystem().getTimeScale(TimeScalesFactory.getTimeScales());
116         final AggregatedClockModel clockModel = sp3.getEphemeris("C02").extractClockModel();
117 
118         Assertions.assertEquals(3, clockModel.getModels().getSpansNumber());
119         Assertions.assertNull(clockModel.getModels().getFirstSpan().getData());
120         Assertions.assertNull(clockModel.getModels().getLastSpan().getData());
121         final ClockModel middle = clockModel.getModels().getFirstSpan().next().getData();
122         Assertions.assertEquals(0.0,
123                                 new AbsoluteDate(2015, 5, 5, 0, 0, 0.0, ts).durationFrom(middle.getValidityStart()),
124                                 1.0e-15);
125         Assertions.assertEquals(0.0,
126                                 new AbsoluteDate(2015, 5, 5, 23, 55, 0.0, ts).durationFrom(middle.getValidityEnd()),
127                                 1.0e-15);
128         Assertions.assertEquals(0.0,
129                                 new AbsoluteDate(2015, 5, 5, 0, 0, 0.0, ts).durationFrom(clockModel.getValidityStart()),
130                                 1.0e-15);
131         Assertions.assertEquals(0.0,
132                                 new AbsoluteDate(2015, 5, 5, 23, 55, 0.0, ts).durationFrom(clockModel.getValidityEnd()),
133                                 1.0e-15);
134 
135         // points exactly on files entries
136         Assertions.assertEquals(-9.16573060e-4,
137                                 clockModel.getOffset(new AbsoluteDate(2015, 5, 5, 0, 10, 0.0, ts)).getOffset(),
138                                 1.0e-16);
139         Assertions.assertEquals(-9.16566535e-4,
140                                 clockModel.getOffset(new AbsoluteDate(2015, 5, 5, 0, 15, 0.0, ts)).getOffset(),
141                                 1.0e-16);
142 
143         // intermediate point
144         final ClockOffset co = clockModel.getOffset(new AbsoluteDate(2015, 5, 5, 0, 12, 5.25, ts));
145         Assertions.assertEquals(-9.16570332e-04, co.getOffset(),       1.0e-12);
146         Assertions.assertEquals( 2.17288913e-11, co.getRate(),         1.0e-19);
147         Assertions.assertEquals(-4.31319472e-16, co.getAcceleration(), 1.0e-24);
148 
149     }
150 
151     @Test
152     public void testParseSP3a2() {
153         // simple test for version sp3-a, contains p/v entries
154         final String    ex     = "/sp3/example-a-2.sp3";
155         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
156         final SP3   file   = new SP3Parser().parse(source);
157 
158         Assertions.assertEquals('a', file.getHeader().getVersion());
159         Assertions.assertEquals(SP3OrbitType.FIT, file.getHeader().getOrbitType());
160         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
161         Assertions.assertEquals(CartesianDerivativesFilter.USE_PV, file.getHeader().getFilter());
162 
163         Assertions.assertEquals(25, file.getSatelliteCount());
164 
165         final List<SP3Coordinate> coords = file.getSatellites().get("1").getSegments().get(0).getCoordinates();
166         Assertions.assertEquals(2, coords.size());
167 
168         final SP3Coordinate coord = coords.get(0);
169 
170         // 1994 12 17 0 0 0.00000000
171         Assertions.assertEquals(new AbsoluteDate(1994, 12, 17, 0, 0, 0,
172                 TimeScalesFactory.getGPS()), coord.getDate());
173 
174         // P 1 16258.524750 -3529.015750 -20611.427050 -62.540600
175         // V 1  -6560.373522  25605.954994  -9460.427179     -0.024236
176         checkPVEntry(new PVCoordinates(new Vector3D(16258524.75, -3529015.75, -20611427.049),
177                                        new Vector3D(-656.0373, 2560.5954, -946.0427)),
178                      coord);
179         Assertions.assertEquals(-0.0000625406, coord.getClockCorrection(), 1.0e-15);
180         Assertions.assertEquals(-0.0000000000024236, coord.getClockRateChange(), 1.0e-15);
181     }
182 
183     @Test
184     public void testParseSP3c1() {
185         // simple test for version sp3-c, contains p entries
186         final String    ex     = "/sp3/example-c-1.sp3";
187         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
188         final SP3   file   = new SP3Parser().parse(source);
189 
190         Assertions.assertEquals('c', file.getHeader().getVersion());
191         Assertions.assertEquals(SP3OrbitType.HLM, file.getHeader().getOrbitType());
192         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
193         Assertions.assertEquals(CartesianDerivativesFilter.USE_P, file.getHeader().getFilter());
194 
195         Assertions.assertEquals(26, file.getSatelliteCount());
196 
197         final List<SP3Coordinate> coords = file.getSatellites().get("G01").getSegments().get(0).getCoordinates();
198         Assertions.assertEquals(1, coords.size());
199 
200         final SP3Coordinate coord = coords.get(0);
201 
202         // 2001  8  8  0  0  0.00000000
203         Assertions.assertEquals(new AbsoluteDate(2001, 8, 8, 0, 0, 0,
204                 TimeScalesFactory.getGPS()), coord.getDate());
205 
206         // PG01 -11044.805800 -10475.672350  21929.418200    189.163300 18 18 18 219
207         checkPVEntry(new PVCoordinates(new Vector3D(-11044805.8, -10475672.35, 21929418.2),
208                                        Vector3D.ZERO),
209                      coord);
210         Assertions.assertEquals(0.0001891633, coord.getClockCorrection(), 1.0e-15);
211     }
212 
213     @Test
214     public void testParseSP3c2() {
215         // simple test for version sp3-c, contains p/v entries and correlations
216         final String    ex     = "/sp3/example-c-2.sp3";
217         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
218         final SP3   file   = new SP3Parser().parse(source);
219 
220         Assertions.assertEquals('c', file.getHeader().getVersion());
221         Assertions.assertEquals(SP3OrbitType.HLM, file.getHeader().getOrbitType());
222         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
223 
224         Assertions.assertEquals(26, file.getSatelliteCount());
225 
226         Assertions.assertEquals(2, file.getSatellites().get("G01").getSegments().size());
227         final List<SP3Coordinate> coords = file.getSatellites().get("G01").getSegments().get(0).getCoordinates();
228         Assertions.assertEquals(1, coords.size());
229 
230         final SP3Coordinate coord = coords.get(0);
231 
232         // 2001  8  8  0  0  0.00000000
233         Assertions.assertEquals(new AbsoluteDate(2001, 8, 8, 0, 0, 0,
234                                                  TimeScalesFactory.getGPS()), coord.getDate());
235 
236         // PG01 -11044.805800 -10475.672350  21929.418200    189.163300 18 18 18 219
237         // VG01  20298.880364 -18462.044804   1381.387685     -4.534317 14 14 14 191
238         checkPVEntry(new PVCoordinates(new Vector3D(-11044805.8, -10475672.35, 21929418.2),
239                                        new Vector3D(2029.8880364, -1846.2044804, 138.1387685)),
240                      coord);
241         Assertions.assertEquals(0.0001891633,  coord.getClockCorrection(), 1.0e-15);
242         Assertions.assertEquals(-0.0000000004534317, coord.getClockRateChange(), 1.0e-15);
243     }
244 
245     @Test
246     public void testParseSP3cWithoutAgency() {
247         // simple test for version sp3-c, contains p/v entries and correlations
248         final String    ex     = "/sp3/missing-agency.sp3";
249         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
250         final SP3   file   = new SP3Parser().parse(source);
251 
252         Assertions.assertEquals('c', file.getHeader().getVersion());
253         Assertions.assertEquals("", file.getHeader().getAgency());
254     }
255 
256     @Test
257     public void testParseSP3d1() {
258         // simple test for version sp3-d, contains p entries
259         final String    ex     = "/sp3/example-d-1.sp3";
260         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
261         final SP3   file   = new SP3Parser().parse(source);
262 
263         Assertions.assertEquals('d', file.getHeader().getVersion());
264         Assertions.assertEquals(SP3OrbitType.BCT, file.getHeader().getOrbitType());
265         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
266 
267         Assertions.assertEquals(5, file.getHeader().getComments().size());
268         Assertions.assertEquals("Note: This is a simulated file, meant to illustrate what an SP3-d header",     file.getHeader().getComments().get(0));
269         Assertions.assertEquals("might look like with more than 85 satellites. Source for GPS and SBAS satel-", file.getHeader().getComments().get(1));
270         Assertions.assertEquals("lite positions: BRDM0930.13N. G=GPS,R=GLONASS,E=Galileo,C=BeiDou,J=QZSS,",     file.getHeader().getComments().get(2));
271         Assertions.assertEquals("I=IRNSS,S=SBAS. For definitions of SBAS satellites, refer to the website:",    file.getHeader().getComments().get(3));
272         Assertions.assertEquals("http://igs.org/mgex/status-SBAS",                                              file.getHeader().getComments().get(4));
273 
274         Assertions.assertEquals(140, file.getSatelliteCount());
275 
276         Assertions.assertEquals(2, file.getSatellites().get("S37").getSegments().size());
277         Assertions.assertEquals(1, file.getSatellites().get("S37").getSegments().get(0).getCoordinates().size());
278         Assertions.assertEquals(1, file.getSatellites().get("S37").getSegments().get(1).getCoordinates().size());
279 
280         final SP3Coordinate coord = file.getSatellites().get("S37").getSegments().get(0).getCoordinates().get(0);
281 
282         // 2013  4  3  0  0  0.00000000
283         Assertions.assertEquals(new AbsoluteDate(2013, 4, 3, 0, 0, 0,
284                 TimeScalesFactory.getGPS()), coord.getDate());
285 
286         // PS37 -34534.904566  24164.610955     29.812840      0.299420
287         checkPVEntry(new PVCoordinates(new Vector3D(-34534904.566, 24164610.955, 29812.840),
288                                        Vector3D.ZERO),
289                      coord);
290         Assertions.assertEquals(0.00000029942, coord.getClockCorrection(), 1.0e-15);
291     }
292 
293     @Test
294     public void testParseSP3d2() {
295         // simple test for version sp3-c, contains p/v entries and correlations
296         final String      ex    = "/sp3/example-d-2.sp3";
297         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
298         final SP3        file   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 1, IGSUtils::guessFrame).
299                                   parse(source);
300 
301         Assertions.assertEquals('d', file.getHeader().getVersion());
302         Assertions.assertEquals(SP3OrbitType.HLM, file.getHeader().getOrbitType());
303         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
304 
305         Assertions.assertEquals(26, file.getSatelliteCount());
306 
307         Assertions.assertEquals(2, file.getSatellites().get("G01").getSegments().size());
308         final List<SP3Coordinate> coords = file.getSatellites().get("G01").getSegments().get(0).getCoordinates();
309         Assertions.assertEquals(1, coords.size());
310 
311         final SP3Coordinate coord = coords.get(0);
312 
313         // 2001  8  8  0  0  0.00000000
314         Assertions.assertEquals(new AbsoluteDate(2001, 8, 8, 0, 0, 0,
315                 TimeScalesFactory.getGPS()), coord.getDate());
316 
317         // PG01 -11044.805800 -10475.672350  21929.418200    189.163300 18 18 18 219
318         // VG01  20298.880364 -18462.044804   1381.387685     -4.534317 14 14 14 191
319         checkPVEntry(new PVCoordinates(new Vector3D(-11044805.8, -10475672.35, 21929418.2),
320                                        new Vector3D(2029.8880364, -1846.2044804, 138.1387685)),
321                      coord);
322         Assertions.assertEquals(0.0001891633,        coord.getClockCorrection(),         1.0e-10);
323         Assertions.assertEquals(55.512e-3,           coord.getPositionAccuracy().getX(), 1.0e-6);
324         Assertions.assertEquals(55.512e-3,           coord.getPositionAccuracy().getY(), 1.0e-6);
325         Assertions.assertEquals(55.512e-3,           coord.getPositionAccuracy().getZ(), 1.0e-6);
326         Assertions.assertEquals(223.1138e-12,        coord.getClockAccuracy(),           1.0e-16);
327         Assertions.assertEquals(-0.0000000004534317, coord.getClockRateChange(),         1.0e-16);
328         Assertions.assertEquals(22.737e-7,           coord.getVelocityAccuracy().getX(), 1.0e-10);
329         Assertions.assertEquals(22.737e-7,           coord.getVelocityAccuracy().getY(), 1.0e-10);
330         Assertions.assertEquals(22.737e-7,           coord.getVelocityAccuracy().getZ(), 1.0e-10);
331         Assertions.assertEquals(111.75277e-16,       coord.getClockRateAccuracy(),       1.0e-21);
332         Assertions.assertFalse(coord.hasClockEvent());
333         Assertions.assertFalse(coord.hasClockPrediction());
334         Assertions.assertFalse(coord.hasOrbitManeuverEvent());
335         Assertions.assertFalse(coord.hasOrbitPrediction());
336 
337         final List<SP3Coordinate> coords2 = file.getSatellites().get("G01").getSegments().get(1).getCoordinates();
338         Assertions.assertFalse(coords2.get(0).hasClockEvent());
339         Assertions.assertTrue(coords2.get(0).hasClockPrediction());
340         Assertions.assertFalse(coords2.get(0).hasOrbitManeuverEvent());
341         Assertions.assertTrue(coords2.get(0).hasOrbitPrediction());
342 
343         final BoundedPropagator propagator = file.getEphemeris("G01").getPropagator();
344         final SpacecraftState s = propagator.propagate(coord.getDate());
345         final Frame frame = file.getSatellites().get("G01").getFrame();
346         Assertions.assertEquals(0.0,
347                                 Vector3D.distance(coord.getPosition(), s.getPVCoordinates(frame).getPosition()),
348                                 4.2e-8);
349         Assertions.assertEquals(0.0,
350                                 Vector3D.distance(coord.getVelocity(), s.getPVCoordinates(frame).getVelocity()),
351                                 3.9e-12);
352         Assertions.assertEquals(coord.getClockCorrection(),
353                                 s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0],
354                                 1.0e-18);
355 
356     }
357 
358     @Test
359     public void testSP3GFZ() {
360         // simple test for version sp3-c, contains more than 85 satellites
361         final String    ex     = "/sp3/gbm19500_truncated.sp3";
362         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
363         final SP3   file   = new SP3Parser().parse(source);
364 
365         Assertions.assertEquals(SP3OrbitType.FIT, file.getHeader().getOrbitType());
366         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
367 
368         Assertions.assertEquals(87, file.getSatelliteCount());
369 
370         final List<SP3Coordinate> coords = file.getSatellites().get("R23").getSegments().get(0).getCoordinates();
371         Assertions.assertEquals(2, coords.size());
372 
373         final SP3Coordinate coord = coords.get(0);
374 
375         Assertions.assertEquals(new AbsoluteDate(2017, 5, 21, 0, 0, 0,
376                 TimeScalesFactory.getGPS()), coord.getDate());
377 
378         // PG01 -11044.805800 -10475.672350  21929.418200    189.163300 18 18 18 219
379         // PR23  24552.470459   -242.899447   6925.437998     86.875825
380         checkPVEntry(new PVCoordinates(new Vector3D(24552470.459, -242899.447, 6925437.998),
381                                        Vector3D.ZERO),
382                      coord);
383         Assertions.assertEquals(0.000086875825, coord.getClockCorrection(), 1.0e-15);
384     }
385 
386     @Test
387     public void testSP3Propagator() {
388         // setup
389         final String     ex         = "/sp3/gbm18432.sp3.Z";
390         final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
391         final DataSource source     = new UnixCompressFilter().filter(compressed);
392         final SP3Parser  parser     = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, IGSUtils::guessFrame);
393 
394         // action
395         final SP3 file = parser.parse(source);
396 
397         // verify
398         TimeScale gps = TimeScalesFactory.getGPS();
399         Assertions.assertNull(file.getSatellites().get("XYZ"));
400         SP3Ephemeris ephemeris = file.getSatellites().get("C03");
401         Frame frame = ephemeris.getFrame();
402         BoundedPropagator propagator = ephemeris.getPropagator();
403         Assertions.assertEquals(propagator.getMinDate(), new AbsoluteDate(2015, 5, 5, gps));
404         Assertions.assertEquals(propagator.getMaxDate(), new AbsoluteDate(2015, 5, 5, 23, 55, 0, gps));
405         SP3Coordinate expected = ephemeris.getSegments().get(0).getCoordinates().get(0);
406         SpacecraftState s = propagator.propagate(propagator.getMinDate());
407         Assertions.assertEquals(0.0,
408                                 Vector3D.distance(s.getPVCoordinates(frame).getPosition(),
409                                                   expected.getPosition()),
410                                 3.0e-8);
411         Assertions.assertEquals(expected.getClockCorrection(),
412                                 s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0],
413                                 1.0e-15);
414         expected = ephemeris.getSegments().get(0).getCoordinates().get(1);
415         s = propagator.propagate(expected.getDate());
416         Assertions.assertEquals(0.0,
417                                 Vector3D.distance(s.getPVCoordinates(frame).getPosition(),
418                                                   expected.getPosition()),
419                                 3.0e-8);
420         Assertions.assertEquals(expected.getClockCorrection(),
421                                 s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0],
422                                 1.0e-15);
423         expected = ephemeris.getSegments().get(0).getCoordinates().get(ephemeris.getSegments().get(0).getCoordinates().size() - 1);
424         s = propagator.propagate(propagator.getMaxDate());
425         Assertions.assertEquals(0.0,
426                                 Vector3D.distance(s.getPVCoordinates(frame).getPosition(),
427                                                   expected.getPosition()),
428                                 3.0e-8);
429         Assertions.assertEquals(expected.getClockCorrection(),
430                                 s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0],
431                                 1.0e-15);
432         SP3Coordinate previous = ephemeris.getSegments().get(0).getCoordinates().get(ephemeris.getSegments().get(0).getCoordinates().size() - 2);
433         final double deltaClock = expected.getClockCorrection() - previous.getClockCorrection();
434         final double deltaT     = expected.getDate().durationFrom(previous.getDate());
435         Assertions.assertEquals(deltaClock / deltaT,
436                                 s.getAdditionalStateDerivative(SP3Utils.CLOCK_ADDITIONAL_STATE)[0],
437                                 1.0e-26);
438 
439         ephemeris = file.getSatellites().get("E19");
440         propagator = ephemeris.getPropagator(new FrameAlignedProvider(ephemeris.getFrame()));
441         Assertions.assertEquals(propagator.getMinDate(), new AbsoluteDate(2015, 5, 5, gps));
442         Assertions.assertEquals(propagator.getMaxDate(), new AbsoluteDate(2015, 5, 5, 23, 55, 0, gps));
443         expected = ephemeris.getSegments().get(0).getCoordinates().get(0);
444         Assertions.assertEquals(0.0,
445                                 Vector3D.distance(propagator.propagate(propagator.getMinDate()).getPVCoordinates(frame).getPosition(),
446                                                   expected.getPosition()),
447                                 3.0e-8);
448         expected = ephemeris.getSegments().get(0).getCoordinates().get(1);
449         Assertions.assertEquals(0.0,
450                                 Vector3D.distance(propagator.propagate(expected.getDate()).getPVCoordinates(frame).getPosition(),
451                                                   expected.getPosition()),
452                                 3.0e-8);
453         expected = ephemeris.getSegments().get(0).getCoordinates().get(ephemeris.getSegments().get(0).getCoordinates().size() - 1);
454         Assertions.assertEquals(0.0,
455                                 Vector3D.distance(propagator.propagate(propagator.getMaxDate()).getPVCoordinates(frame).getPosition(),
456                                                   expected.getPosition()),
457                                 3.1e-8);
458     }
459 
460     @Test
461     public void testSP3Compressed() {
462         final String ex = "/sp3/gbm18432.sp3.Z";
463 
464         final SP3Parser parser = new SP3Parser();
465         final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
466         final SP3 file = parser.parse(new UnixCompressFilter().filter(compressed));
467 
468         Assertions.assertEquals(SP3OrbitType.FIT, file.getHeader().getOrbitType());
469         Assertions.assertEquals("FIT",file.getHeader().getOrbitTypeKey());
470         Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem());
471 
472         Assertions.assertEquals(71, file.getSatelliteCount());
473 
474         final List<SP3Coordinate> coords = file.getSatellites().get("R13").getSegments().get(0).getCoordinates();
475         Assertions.assertEquals(288, coords.size());
476 
477         final SP3Coordinate coord = coords.get(228);
478 
479 
480         Assertions.assertEquals(new AbsoluteDate(2015, 5, 5, 19, 0, 0,
481                 TimeScalesFactory.getGPS()), coord.getDate());
482 
483         // PR13  25330.290321   -411.728000   2953.331527   -482.447619
484         checkPVEntry(new PVCoordinates(new Vector3D(25330290.321, -411728.000, 2953331.527),
485                                        Vector3D.ZERO),
486                      coord);
487         Assertions.assertEquals(-0.000482447619,  coord.getClockCorrection(), 1.0e-15);
488     }
489 
490     private void checkPVEntry(final PVCoordinates expected, final PVCoordinates actual) {
491         final Vector3D expectedPos = expected.getPosition();
492         final Vector3D expectedVel = expected.getVelocity();
493 
494         final Vector3D actualPos = actual.getPosition();
495         final Vector3D actualVel = actual.getVelocity();
496 
497         // sp3 files can have mm accuracy
498         final double eps = 1e-3;
499 
500         Assertions.assertEquals(expectedPos.getX(), actualPos.getX(), eps);
501         Assertions.assertEquals(expectedPos.getY(), actualPos.getY(), eps);
502         Assertions.assertEquals(expectedPos.getZ(), actualPos.getZ(), eps);
503 
504         Assertions.assertEquals(expectedVel.getX(), actualVel.getX(), eps);
505         Assertions.assertEquals(expectedVel.getY(), actualVel.getY(), eps);
506         Assertions.assertEquals(expectedVel.getZ(), actualVel.getZ(), eps);
507 
508         Assertions.assertEquals(Vector3D.ZERO, actual.getAcceleration());
509     }
510 
511     @Test
512     public void testTruncatedLine() {
513         try {
514             final String    ex     = "/sp3/truncated-line.sp3";
515             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
516             final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
517             parser.parse(source);
518             Assertions.fail("an exception should have been thrown");
519         } catch (OrekitException oe) {
520             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
521                                 oe.getSpecifier());
522             Assertions.assertEquals(27, ((Integer) oe.getParts()[0]).intValue());
523         }
524 
525     }
526 
527     @Test
528     public void testMissingEOF() {
529         final String    ex     = "/sp3/missing-eof.sp3";
530         try {
531             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
532             final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
533             parser.parse(source);
534             Assertions.fail("an exception should have been thrown");
535         } catch (OrekitException oe) {
536             Assertions.assertEquals(OrekitMessages.SP3_NUMBER_OF_EPOCH_MISMATCH, oe.getSpecifier());
537             Assertions.assertEquals(  1, ((Integer) oe.getParts()[0]).intValue());
538             Assertions.assertEquals( ex, oe.getParts()[1]);
539             Assertions.assertEquals(192, ((Integer) oe.getParts()[2]).intValue());
540         }
541 
542     }
543 
544     @Test
545     public void testMissingStandardDeviation() {
546         final String    ex     = "/sp3/missing-standard-deviation.sp3";
547         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
548         final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
549         final SP3 sp3 = parser.parse(source);
550         Assertions.assertEquals(32, sp3.getSatelliteCount());
551         List<SP3Coordinate> coordinates06 = sp3.getEphemeris("G06").getSegments().get(0).getCoordinates();
552         Assertions.assertEquals(21, coordinates06.size());
553         for (int i = 0; i < 21; ++i) {
554             final Vector3D positionAccuracy = coordinates06.get(i).getPositionAccuracy();
555             if (i == 7 || i == 8) {
556                 // some standard deviations are missing
557                 Assertions.assertNull(positionAccuracy);
558             } else {
559                 // other are present
560                 Assertions.assertTrue(positionAccuracy.getNorm() < 0.0122);
561                 Assertions.assertTrue(positionAccuracy.getNorm() > 0.0045);
562             }
563         }
564     }
565 
566     @Test
567     public void testWrongLineIdentifier() {
568         try {
569             final String    ex     = "/sp3/wrong-line-identifier.sp3";
570             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
571             final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
572             parser.parse(source);
573             Assertions.fail("an exception should have been thrown");
574         } catch (OrekitException oe) {
575             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
576                                 oe.getSpecifier());
577             Assertions.assertEquals(13, ((Integer) oe.getParts()[0]).intValue());
578         }
579 
580     }
581 
582     @Test
583     public void testBHN() {
584         final SP3Parser   parser       = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
585         final String      ex           = "/sp3/esaBHN.sp3.Z";
586         final DataSource   compressed   = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
587         final DataSource   uncompressed = new UnixCompressFilter().filter(compressed);
588         final SP3     file         = parser.parse(uncompressed);
589         Assertions.assertEquals(SP3OrbitType.FIT, file.getHeader().getOrbitType());
590         Assertions.assertEquals("BHN", file.getHeader().getOrbitTypeKey());
591     }
592 
593     @Test
594     public void testPRO() {
595         final SP3Parser   parser       = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
596         final String      ex           = "/sp3/esaPRO.sp3.Z";
597         final DataSource   compressed   = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
598         final DataSource   uncompressed = new UnixCompressFilter().filter(compressed);
599         final SP3     file         = parser.parse(uncompressed);
600         Assertions.assertEquals(SP3OrbitType.EXT, file.getHeader().getOrbitType());
601         Assertions.assertEquals("PRO", file.getHeader().getOrbitTypeKey());
602     }
603 
604     @Test
605     public void testUnknownType() {
606         final SP3Parser   parser       = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
607         final String      ex           = "/sp3/unknownType.sp3.Z";
608         final DataSource   compressed   = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
609         final DataSource   uncompressed = new UnixCompressFilter().filter(compressed);
610         final SP3     file         = parser.parse(uncompressed);
611         Assertions.assertEquals(SP3OrbitType.OTHER, file.getHeader().getOrbitType());
612         Assertions.assertEquals("UKN", file.getHeader().getOrbitTypeKey());
613     }
614 
615     @Test
616     public void testUnsupportedVersion() {
617         try {
618             final String    ex     = "/sp3/unsupported-version.sp3";
619             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
620             final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
621             parser.parse(source);
622             Assertions.fail("an exception should have been thrown");
623         } catch (OrekitException oe) {
624             Assertions.assertEquals(OrekitMessages.SP3_UNSUPPORTED_VERSION,
625                                 oe.getSpecifier());
626             Assertions.assertEquals('z', ((Character) oe.getParts()[0]).charValue());
627         }
628 
629     }
630 
631     @Test
632     public void testWrongNumberOfEpochs() {
633         try {
634             final String    ex     = "/sp3/wrong-number-of-epochs.sp3";
635             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
636             final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
637             parser.parse(source);
638             Assertions.fail("an exception should have been thrown");
639         } catch (OrekitException oe) {
640             Assertions.assertEquals(OrekitMessages.SP3_NUMBER_OF_EPOCH_MISMATCH,
641                                 oe.getSpecifier());
642             Assertions.assertEquals(  2, ((Integer) oe.getParts()[0]).intValue());
643             Assertions.assertEquals(192, ((Integer) oe.getParts()[2]).intValue());
644         }
645 
646     }
647 
648     @Test
649     public void testInconsistentSamplingDates() {
650         try {
651             final String    ex     = "/sp3/inconsistent-sampling-dates.sp3";
652             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
653             final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame);
654             parser.parse(source);
655             Assertions.fail("an exception should have been thrown");
656         } catch (OrekitException oe) {
657             Assertions.assertEquals(OrekitMessages.INCONSISTENT_SAMPLING_DATE, oe.getSpecifier());
658             Assertions.assertEquals(new AbsoluteDate(1994, 12, 17, 23, 45, 0.0, TimeScalesFactory.getGPS()), oe.getParts()[0]);
659             Assertions.assertEquals(new AbsoluteDate(1994, 12, 17, 23, 46, 0.0, TimeScalesFactory.getGPS()), oe.getParts()[1]);
660         }
661 
662     }
663 
664     @Test
665     public void testIssue803() {
666 
667         // Test issue 803 (see https://gitlab.orekit.org/orekit/orekit/-/issues/803)
668         final String    ex     = "/sp3/truncated-nsgf.orb.lageos2.160305.v35.sp3";
669         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
670         final SP3   file   = new SP3Parser().parse(source);
671 
672         // Coordinates
673         final List<SP3Coordinate> coords = file.getSatellites().get("L52").getSegments().get(0).getCoordinates();
674         Assertions.assertEquals(1, coords.size());
675         final SP3Coordinate coord = coords.get(0);
676 
677         // Verify
678         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
679 
680         // PL52   2228.470946   7268.265924   9581.471543
681         // VL52 -44856.945000  24321.151000  -7116.222800
682         checkPVEntry(new PVCoordinates(new Vector3D(2228470.946, 7268265.924, 9581471.543),
683                                        new Vector3D(-4485.6945000, 2432.1151000, -711.6222800)),
684                      coord);
685         Assertions.assertTrue(Double.isNaN(coord.getClockCorrection()));
686         Assertions.assertTrue(Double.isNaN(coord.getClockRateChange()));
687 
688     }
689 
690     @Test
691     public void testIssue827() {
692 
693         // Test issue 827 (see https://gitlab.orekit.org/orekit/orekit/-/issues/827)
694         final String    ex     = "/sp3/truncated-nsgf.orb.lageos2.160305.v35.sp3";
695         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
696         final SP3   file   = new SP3Parser().parse(source);
697 
698         // Coordinates
699         final List<SP3Coordinate> coords = file.getSatellites().get("L52").getSegments().get(0).getCoordinates();
700         Assertions.assertEquals(1, coords.size());
701         final SP3Coordinate coord = coords.get(0);
702 
703         // Verify
704         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
705         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
706 
707         // 2016  2 28 0 0 0.00000000
708         Assertions.assertEquals(new AbsoluteDate(2016, 2, 28, 0, 0, 0,
709                                                  TimeScalesFactory.getUTC()), coord.getDate());
710 
711 
712         // PL52   2228.470946   7268.265924   9581.471543
713         // VL52 -44856.945000  24321.151000  -7116.222800
714         checkPVEntry(new PVCoordinates(new Vector3D(2228470.946, 7268265.924, 9581471.543),
715                                        new Vector3D(-4485.6945000, 2432.1151000, -711.6222800)),
716                      coord);
717         Assertions.assertTrue(Double.isNaN(coord.getClockCorrection()));
718         Assertions.assertTrue(Double.isNaN(coord.getClockRateChange()));
719 
720     }
721 
722     @Test
723     public void testIssue828() {
724 
725         // Test issue 828 (see https://gitlab.orekit.org/orekit/orekit/-/issues/828)
726         final String    ex     = "/sp3/example-d-3.sp3";
727         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
728         final SP3   file   = new SP3Parser().parse(source);
729 
730         // Verify
731         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
732         Assertions.assertEquals(SP3FileType.NAVIC, file.getHeader().getType());
733 
734     }
735 
736     @Test
737     public void testIssue828Bis() {
738 
739         // Test issue 828 (see https://gitlab.orekit.org/orekit/orekit/-/issues/828)
740         final String    ex     = "/sp3/example-d-4.sp3";
741         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
742         final SP3   file   = new SP3Parser().parse(source);
743 
744         // Verify
745         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
746         Assertions.assertEquals(SP3FileType.SBAS, file.getHeader().getType());
747 
748     }
749 
750     @Test
751     public void testIssue1314() {
752 
753         // Test issue 828 (see https://gitlab.orekit.org/orekit/orekit/-/issues/1314)
754         final String    ex     = "/sp3/example-d-5.sp3";
755         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
756         final SP3   file   = new SP3Parser().parse(source);
757 
758         // Verify
759         Assertions.assertEquals(TimeSystem.GALILEO, file.getHeader().getTimeSystem());
760         Assertions.assertEquals(SP3FileType.SBAS, file.getHeader().getType());
761 
762     }
763 
764     @Test
765     public void testIssue895HeaderComment() {
766 
767         // Test issue 895
768         final String    ex     = "/sp3/issue895-header-comment.sp3";
769         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
770         final SP3   file   = new SP3Parser().parse(source);
771 
772         // Verify
773         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
774         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
775 
776     }
777 
778     @Test
779     public void testIssue895ClockRecord() {
780 
781         // Test issue 895
782         final String    ex     = "/sp3/issue895-clock-record.sp3";
783         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
784         final SP3   file   = new SP3Parser().parse(source);
785 
786         // Verify
787         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
788         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
789         Assertions.assertEquals(1, file.getSatelliteCount());
790 
791         final List<SP3Coordinate> coords = file.getSatellites().get("L51").getSegments().get(0).getCoordinates();
792         Assertions.assertEquals(1, coords.size());
793 
794         final SP3Coordinate coord = coords.get(0);
795 
796         // 2021 12 26  0  0  0.00000000
797         Assertions.assertEquals(new AbsoluteDate(2021, 12, 26, 0, 0, 0,
798                 TimeScalesFactory.getUTC()), coord.getDate());
799 
800         // PL51   5029.867893   1304.362160 -11075.527276 999999.999999
801         // VL51 -17720.521773 -55720.482742 -14441.695083 999999.999999
802         checkPVEntry(new PVCoordinates(new Vector3D(5029867.893, 1304362.160, -11075527.276),
803                                        new Vector3D(-1772.0521773, -5572.0482742, -1444.1695083)),
804                      coord);
805 
806     }
807 
808     @Test
809     public void testIssue895RolloverMinutes() {
810 
811         // Test issue 895
812         final String    ex     = "/sp3/issue895-minutes-increment.sp3";
813         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
814         final SP3   file   = new SP3Parser().parse(source);
815 
816         // Verify
817         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
818         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
819         Assertions.assertEquals(1, file.getSatelliteCount());
820 
821         final List<SP3Coordinate> coords = file.getSatellites().get("L51").getSegments().get(0).getCoordinates();
822         Assertions.assertEquals(91, coords.size());
823 
824         final SP3Coordinate coord30 = coords.get(30);
825 
826         // 2016  7  6 16 60  0.00000000
827         Assertions.assertEquals(new AbsoluteDate(2016, 7, 6, 17, 0, 0,
828                 TimeScalesFactory.getUTC()), coord30.getDate());
829 
830         // PL51  11948.228978   2986.113872   -538.901114 999999.999999
831         // VL51   4605.419303 -27972.588048 -53316.820671 999999.999999
832         checkPVEntry(new PVCoordinates(new Vector3D(11948228.978,   2986113.872,   -538901.114),
833                                        new Vector3D(460.5419303, -2797.2588048, -5331.6820671)),
834                      coord30);
835 
836         final SP3Coordinate coord31 = coords.get(31);
837 
838         // 2016  7  6 17  2  0.00000000
839         Assertions.assertEquals(new AbsoluteDate(2016, 7, 6, 17, 2, 0,
840                 TimeScalesFactory.getUTC()), coord31.getDate());
841 
842         // PL51  11982.652569   2645.786926  -1177.549463 999999.999999
843         // VL51   1128.248622 -28724.293303 -53097.358387 999999.999999
844         checkPVEntry(new PVCoordinates(new Vector3D(11982652.569,   2645786.926,  -1177549.463),
845                                        new Vector3D(112.8248622, -2872.4293303, -5309.7358387)),
846                      coord31);
847 
848         final SP3Coordinate coord60 = coords.get(60);
849 
850         // 2016  7  6 17 60  0.00000000
851         Assertions.assertEquals(new AbsoluteDate(2016, 7, 6, 18, 0, 0,
852                 TimeScalesFactory.getUTC()), coord60.getDate());
853 
854         // PL51  -1693.056569  -4123.276630 -11431.599723 999999.999999
855         // VL51 -59412.268951   4066.817074   7604.890337 999999.999999
856         checkPVEntry(new PVCoordinates(new Vector3D(-1693056.569,  -4123276.630, -11431599.723),
857                                        new Vector3D(-5941.2268951,   406.6817074,   760.4890337)),
858                      coord60);
859 
860     }
861 
862     @Test
863     public void testIssue895RolloverHours() {
864 
865         // Test issue 895
866         final String    ex     = "/sp3/issue895-hours-increment.sp3";
867         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
868         final SP3   file   = new SP3Parser().parse(source);
869 
870         // Verify
871         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
872         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
873         Assertions.assertEquals(1, file.getSatelliteCount());
874 
875         final List<SP3Coordinate> coords = file.getSatellites().get("L51").getSegments().get(0).getCoordinates();
876         Assertions.assertEquals(61, coords.size());
877 
878         final SP3Coordinate coord30 = coords.get(30);
879 
880         // 2016  7  7 24  0  0.00000000
881         Assertions.assertEquals(new AbsoluteDate(2016, 7, 7, 0, 0, 0,
882                 TimeScalesFactory.getUTC()), coord30.getDate());
883 
884         //PL51   2989.229334  -8494.421415   8385.068555
885         //VL51 -19617.027447 -43444.824985 -36706.159070
886         checkPVEntry(new PVCoordinates(new Vector3D(2989229.334,  -8494421.415,   8385068.555),
887                                        new Vector3D(-1961.7027447, -4344.4824985, -3670.6159070)),
888                      coord30);
889 
890         final SP3Coordinate coord31 = coords.get(31);
891 
892         // 2016  7  7  0  2  0.00000000
893         Assertions.assertEquals(new AbsoluteDate(2016, 7, 7, 0, 2, 0,
894                 TimeScalesFactory.getUTC()), coord31.getDate());
895 
896         // PL51   2744.983592  -9000.639164   7931.904779
897         // VL51 -21072.925764 -40899.633288 -38801.567078
898         checkPVEntry(new PVCoordinates(new Vector3D(2744983.592,  -9000639.164,   7931904.779),
899                                        new Vector3D(-2107.2925764, -4089.9633288, -3880.1567078)),
900                      coord31);
901 
902     }
903 
904     @Test
905     public void testIssue895SecondDigits() {
906 
907         // Test issue 895
908         final String    ex     = "/sp3/issue895-second-digits.sp3";
909         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
910         final SP3   file   = new SP3Parser().parse(source);
911 
912         // Verify
913         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
914         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
915         Assertions.assertEquals(1, file.getSatelliteCount());
916 
917         final List<SP3Coordinate> coords = file.getSatellites().get("L51").getSegments().get(0).getCoordinates();
918         Assertions.assertEquals(1, coords.size());
919 
920         final SP3Coordinate coord = coords.get(0);
921 
922         // 2016  7  3  0  0  0.1234
923         Assertions.assertEquals(new AbsoluteDate(2016, 7, 3, 0, 0, 0.1234,
924                 TimeScalesFactory.getUTC()), coord.getDate());
925 
926     }
927 
928     @Test
929     public void testIssue895NoEOF() {
930 
931         // Test issue 895
932         final String    ex     = "/sp3/issue895-no-eof.sp3";
933         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
934         final SP3   file   = new SP3Parser().parse(source);
935 
936         // Verify
937         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
938         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
939         Assertions.assertEquals(1, file.getSatelliteCount());
940 
941         final List<SP3Coordinate> coords = file.getSatellites().get("L51").getSegments().get(0).getCoordinates();
942         Assertions.assertEquals(1, coords.size());
943 
944         final SP3Coordinate coord = coords.get(0);
945 
946         // 2021 12 26  0  0  0.00000000
947         Assertions.assertEquals(new AbsoluteDate(2021, 12, 26, 0, 0, 0,
948                 TimeScalesFactory.getUTC()), coord.getDate());
949 
950         // PL51   5029.867893   1304.362160 -11075.527276 999999.999999
951         // VL51 -17720.521773 -55720.482742 -14441.695083 999999.999999
952         checkPVEntry(new PVCoordinates(new Vector3D(5029867.893, 1304362.160, -11075527.276),
953                                        new Vector3D(-1772.0521773, -5572.0482742, -1444.1695083)),
954                      coord);
955 
956     }
957 
958     @Test
959     public void testExceededSatCount() {
960         final String    ex     = "/sp3/exceeded-sat-count.sp3";
961         try {
962             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
963             new SP3Parser().parse(source);
964             Assertions.fail("an exception should have been thrown");
965         } catch (OrekitException oe) {
966             Assertions.assertEquals(OrekitMessages.SP3_TOO_MANY_SATELLITES_FOR_VERSION, oe.getSpecifier());
967             Assertions.assertEquals('c', ((Character) oe.getParts()[0]).charValue());
968             Assertions.assertEquals( 99, ((Integer) oe.getParts()[1]).intValue());
969             Assertions.assertEquals(140, ((Integer) oe.getParts()[2]).intValue());
970             Assertions.assertEquals(ex, oe.getParts()[3]);
971         }
972     }
973 
974     @Test
975     public void testIssue1014() {
976 
977         // Test issue 1014
978         final String    ex     = "/sp3/issue1014-days-increment.sp3";
979         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
980         final SP3   file   = new SP3Parser().parse(source);
981 
982         // Verify
983         Assertions.assertEquals(TimeSystem.UTC, file.getHeader().getTimeSystem());
984         Assertions.assertEquals(SP3FileType.LEO, file.getHeader().getType());
985         Assertions.assertEquals(1, file.getSatelliteCount());
986 
987         final List<SP3Coordinate> coords = file.getSatellites().get("L51").getSegments().get(0).getCoordinates();
988         Assertions.assertEquals(62, coords.size());
989 
990         final SP3Coordinate coord = coords.get(61);
991 
992         // following date is completely wrong (2022 january 0th instead of 2021 december 31st)
993         // 2022  1  0  1  2  0.00000000
994         Assertions.assertEquals(new AbsoluteDate(2021, 12, 31, 1, 2, 0,
995                               TimeScalesFactory.getUTC()), coord.getDate());
996 
997         // PL51  -5691.093473  -9029.216710  -5975.427658
998         // VL51 -39188.598237  -5856.539265  45893.223756
999         checkPVEntry(new PVCoordinates(new Vector3D(-5691093.473,  -9029216.710,  -5975427.658),
1000                                        new Vector3D(-3918.8598237,  -585.6539265,  4589.3223756)),
1001                      coord);
1002 
1003     }
1004 
1005     @Test
1006     public void testWrongPosVelBaseA() {
1007         doTestWrongHeaderEntry("/sp3/wrong-pos-vel-base-a.sp3", "pos/vel accuracy base");
1008     }
1009 
1010     @Test
1011     public void testWrongPosVelBaseD() {
1012         doTestWrongHeaderEntry("/sp3/wrong-pos-vel-base-d.sp3", "pos/vel accuracy base");
1013     }
1014 
1015     @Test
1016     public void testWrongClockBaseA() {
1017         doTestWrongHeaderEntry("/sp3/wrong-clock-base-a.sp3", "clock accuracy base");
1018     }
1019 
1020     @Test
1021     public void testWrongClockBaseD() {
1022         doTestWrongHeaderEntry("/sp3/wrong-clock-base-d.sp3", "clock accuracy base");
1023     }
1024 
1025     @Test
1026     public void testWrongTooManyCommentsA() {
1027         doTestWrongHeaderEntry("/sp3/too-many-comments-a.sp3", "comments");
1028     }
1029 
1030     @Test
1031     public void testWrongTooLongCommentA() {
1032         doTestWrongHeaderEntry("/sp3/too-long-comment-a.sp3", "comments");
1033     }
1034 
1035     @Test
1036     public void testWrongTooLongCommentD() {
1037         doTestWrongHeaderEntry("/sp3/too-long-comment-d.sp3", "comments");
1038     }
1039 
1040     private void doTestWrongHeaderEntry(final String ex, final String entry) {
1041         try {
1042             final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
1043             new SP3Parser().parse(source);
1044             Assertions.fail("an exception should have been thrown");
1045         } catch (OrekitException oe) {
1046             Assertions.assertEquals(OrekitMessages.SP3_INVALID_HEADER_ENTRY, oe.getSpecifier());
1047             Assertions.assertEquals(entry, oe.getParts()[0]);
1048             Assertions.assertEquals(ex, oe.getParts()[2]);
1049         }
1050     }
1051 
1052     @Test
1053     public void testSpliceWrongType() {
1054         try {
1055             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_type.sp3");
1056             Assertions.fail("an exception should have been thrown");
1057         } catch (OrekitException oe) {
1058             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA, oe.getSpecifier());
1059         }
1060     }
1061 
1062     @Test
1063     public void testSpliceWrongTimeSystem() {
1064         try {
1065             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_time_system.sp3");
1066             Assertions.fail("an exception should have been thrown");
1067         } catch (OrekitException oe) {
1068             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA, oe.getSpecifier());
1069         }
1070     }
1071 
1072     @Test
1073     public void testSpliceWrongSatelliteCount() {
1074         final SP3 spliced = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_satellite_count.sp3");
1075         Assertions.assertEquals(86, spliced.getSatelliteCount());
1076     }
1077 
1078     @Test
1079     public void testSpliceWrongOrbitType() {
1080         try {
1081             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_orbit_type.sp3");
1082             Assertions.fail("an exception should have been thrown");
1083         } catch (OrekitException oe) {
1084             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA, oe.getSpecifier());
1085         }
1086     }
1087 
1088     @Test
1089     public void testSpliceWrongCoordinateSystem() {
1090         try {
1091             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_coordinate_system.sp3");
1092             Assertions.fail("an exception should have been thrown");
1093         } catch (OrekitException oe) {
1094             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA, oe.getSpecifier());
1095         }
1096     }
1097 
1098     @Test
1099     public void testSpliceWrongDataUsed() {
1100         try {
1101             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_data_used.sp3");
1102             Assertions.fail("an exception should have been thrown");
1103         } catch (OrekitIllegalArgumentException oe) {
1104             Assertions.assertEquals(OrekitMessages.SP3_INVALID_HEADER_ENTRY, oe.getSpecifier());
1105             Assertions.assertEquals("data used", oe.getParts()[0]);
1106             Assertions.assertEquals("v", oe.getParts()[1]);
1107         }
1108     }
1109 
1110     @Test
1111     public void testSpliceWrongAgency() {
1112         try {
1113             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_agency.sp3");
1114             Assertions.fail("an exception should have been thrown");
1115         } catch (OrekitException oe) {
1116             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA, oe.getSpecifier());
1117         }
1118     }
1119 
1120     @Test
1121     public void testSpliceWrongSatelliteList() {
1122         final SP3 spliced = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_satellite_list.sp3");
1123         Assertions.assertEquals(86, spliced.getSatelliteCount());
1124     }
1125 
1126     @Test
1127     public void testSpliceWrongDerivatives() {
1128         try {
1129             splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_wrong_derivatives.sp3");
1130             Assertions.fail("an exception should have been thrown");
1131         } catch (OrekitException oe) {
1132             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_SATELLITE_MEDATADA, oe.getSpecifier());
1133         }
1134     }
1135 
1136     @Test
1137     public void testSpliceWrongFrame() {
1138         try {
1139             final String     name1 = "/sp3/gbm19500_truncated.sp3";
1140             final DataSource source1 = new DataSource(name1, () -> getClass().getResourceAsStream(name1));
1141             final SP3        file1   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 7,
1142                                                      s -> FramesFactory.getITRF(IERSConventions.IERS_2010, false)).
1143                                        parse(source1);
1144             final String     name2 = "/sp3/gbm19500_after_no_drop.sp3";
1145             final DataSource source2 = new DataSource(name2, () -> getClass().getResourceAsStream(name2));
1146             final SP3        file2   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 7,
1147                                                      s -> FramesFactory.getITRF(IERSConventions.IERS_1996, false)).
1148                                        parse(source2);
1149             SP3.splice(Arrays.asList(file1, file2));
1150             Assertions.fail("an exception should have been thrown");
1151         } catch (OrekitException oe) {
1152             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_SATELLITE_MEDATADA, oe.getSpecifier());
1153         }
1154     }
1155 
1156     @Test
1157     public void testSpliceWrongInterpolationSamples() {
1158         try {
1159             final String     name1 = "/sp3/gbm19500_truncated.sp3";
1160             final DataSource source1 = new DataSource(name1, () -> getClass().getResourceAsStream(name1));
1161             final SP3        file1   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 7,
1162                                                      s -> FramesFactory.getITRF(IERSConventions.IERS_2010, false)).
1163                                        parse(source1);
1164             final String     name2 = "/sp3/gbm19500_after_no_drop.sp3";
1165             final DataSource source2 = new DataSource(name2, () -> getClass().getResourceAsStream(name2));
1166             final SP3        file2   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 4,
1167                                                      s -> FramesFactory.getITRF(IERSConventions.IERS_2010, false)).
1168                                        parse(source2);
1169             SP3.splice(Arrays.asList(file1, file2));
1170              Assertions.fail("an exception should have been thrown");
1171         } catch (OrekitException oe) {
1172             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_SATELLITE_MEDATADA, oe.getSpecifier());
1173         }
1174     }
1175 
1176     @Test
1177     public void testSpliceWrongMu() {
1178         try {
1179             final String     name1 = "/sp3/gbm19500_truncated.sp3";
1180             final DataSource source1 = new DataSource(name1, () -> getClass().getResourceAsStream(name1));
1181             final SP3        file1   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 7,
1182                                                      s -> FramesFactory.getITRF(IERSConventions.IERS_2010, false)).
1183                                        parse(source1);
1184             final String     name2 = "/sp3/gbm19500_after_no_drop.sp3";
1185             final DataSource source2 = new DataSource(name2, () -> getClass().getResourceAsStream(name2));
1186             final SP3        file2   = new SP3Parser(1.00001 * Constants.EIGEN5C_EARTH_MU, 7,
1187                                                      s -> FramesFactory.getITRF(IERSConventions.IERS_2010, false)).
1188                                        parse(source2);
1189             SP3.splice(Arrays.asList(file1, file2));
1190             Assertions.fail("an exception should have been thrown");
1191         } catch (OrekitException oe) {
1192             Assertions.assertEquals(OrekitMessages.SP3_INCOMPATIBLE_SATELLITE_MEDATADA, oe.getSpecifier());
1193         }
1194     }
1195 
1196     @Test
1197     public void testSpliceNewSegment() {
1198         SP3 sp3 = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_large_gap.sp3");
1199         Assertions.assertEquals(2, sp3.getEphemeris("C01").getSegments().size());
1200         final TimeScale ts = sp3.getHeader().getTimeSystem().getTimeScale(TimeScalesFactory.getTimeScales());
1201 
1202         final AggregatedClockModel clockModel = sp3.getEphemeris("C01").extractClockModel();
1203         Assertions.assertEquals(0.0,
1204                                 new AbsoluteDate(2017, 5, 21, 0, 0, 0.0, ts).durationFrom(clockModel.getValidityStart()),
1205                                 1.0e-15);
1206         Assertions.assertEquals(0.0,
1207                                 new AbsoluteDate(2017, 5, 21, 0, 25, 0.0, ts).durationFrom(clockModel.getValidityEnd()),
1208                                 1.0e-15);
1209 
1210         // there are 5 spans after splicing:
1211         // one null    from      -∞             to 2017-05-21T00:00:00
1212         // one regular from 2017-05-21T00:00:00 to 2017-05-21T00:05:00
1213         // one null    from 2017-05-21T00:05:00 to 2017-05-21T00:20:00
1214         // one regular from 2017-05-21T00:20:00 to 2017-05-21T00:25:00
1215         // one null    from 2017-05-21T00:25:00 to        +∞
1216         Assertions.assertEquals(5, clockModel.getModels().getSpansNumber());
1217         TimeSpanMap.Span<ClockModel> span = clockModel.getModels().getFirstSpan();
1218         Assertions.assertNull(span.getData());
1219         span = span.next();
1220         Assertions.assertInstanceOf(SampledClockModel.class, span.getData());
1221         Assertions.assertEquals(0.0,
1222                                 new AbsoluteDate(2017, 5, 21, 0, 0, 0.0, ts).durationFrom(span.getData().getValidityStart()),
1223                                 1.0e-15);
1224         Assertions.assertEquals(0.0,
1225                                 new AbsoluteDate(2017, 5, 21, 0, 5, 0.0, ts).durationFrom(span.getData().getValidityEnd()),
1226                                 1.0e-15);
1227         span = span.next();
1228         Assertions.assertNull(span.getData());
1229         span = span.next();
1230         Assertions.assertInstanceOf(SampledClockModel.class, span.getData());
1231         Assertions.assertEquals(0.0,
1232                                 new AbsoluteDate(2017, 5, 21, 0, 20, 0.0, ts).durationFrom(span.getData().getValidityStart()),
1233                                 1.0e-15);
1234         Assertions.assertEquals(0.0,
1235                                 new AbsoluteDate(2017, 5, 21, 0, 25, 0.0, ts).durationFrom(span.getData().getValidityEnd()),
1236                                 1.0e-15);
1237         span = span.next();
1238         Assertions.assertNull(span.getData());
1239 
1240         Assertions.assertEquals(2.4314257e-5, clockModel.getOffset(new AbsoluteDate(2017, 5, 21, 0,  4, 59.5, ts)).getOffset(), 1.0e-12);
1241         Assertions.assertEquals(2.4301550e-5, clockModel.getOffset(new AbsoluteDate(2017, 5, 21, 0, 20,  0.5, ts)).getOffset(), 1.0e-12);
1242         try {
1243             clockModel.getOffset(new AbsoluteDate(2017, 5, 21, 0, 10, 0.0, ts));
1244             Assertions.fail("an exceptions should have been thrown");
1245         } catch (OrekitException oe) {
1246             Assertions.assertEquals(OrekitMessages.NO_DATA_GENERATED, oe.getSpecifier());
1247         }
1248 
1249     }
1250 
1251     @Test
1252     public void testSpliceDrop() {
1253 
1254         final SP3 spliced = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_after_drop.sp3");
1255         final TimeScale ts = spliced.getHeader().getTimeSystem().getTimeScale(TimeScalesFactory.getTimeScales());
1256 
1257         Assertions.assertEquals(SP3OrbitType.FIT, spliced.getHeader().getOrbitType());
1258         Assertions.assertEquals(TimeSystem.GPS, spliced.getHeader().getTimeSystem());
1259 
1260         Assertions.assertEquals(87, spliced.getSatelliteCount());
1261 
1262         final List<SP3Coordinate> coords = spliced.getSatellites().get("R23").getSegments().get(0).getCoordinates();
1263         Assertions.assertEquals(3, coords.size());
1264 
1265         final SP3Coordinate coord = coords.get(0);
1266 
1267         Assertions.assertEquals(new AbsoluteDate(2017, 5, 21, 0, 0, 0, TimeScalesFactory.getGPS()),
1268                                 coord.getDate());
1269 
1270         // PG01 -11044.805800 -10475.672350  21929.418200    189.163300 18 18 18 219
1271         // PR23  24552.470459   -242.899447   6925.437998     86.875825
1272         checkPVEntry(new PVCoordinates(new Vector3D(24552470.459, -242899.447, 6925437.998), Vector3D.ZERO),
1273                      coord);
1274         Assertions.assertEquals(0.000086875825, coord.getClockCorrection(), 1.0e-15);
1275 
1276         Assertions.assertEquals(new AbsoluteDate(2017, 5, 21, 0, 10, 0, TimeScalesFactory.getGPS()),
1277                                 coords.get(coords.size() - 1).getDate());
1278 
1279         Assertions.assertEquals(1.25,  spliced.getHeader().getPosVelBase(), 1.0e-15);
1280         Assertions.assertEquals(1.025, spliced.getHeader().getClockBase(),  1.0e-15);
1281 
1282         // since the gap between the two files is equal to the epoch interval
1283         // the segments are kept merged
1284         final AggregatedClockModel clockModel = spliced.getEphemeris("R23").extractClockModel();
1285         Assertions.assertEquals(3, clockModel.getModels().getSpansNumber());
1286         Assertions.assertEquals(0.0,
1287                                 new AbsoluteDate(2017, 5, 21, 0, 0, 0.0, ts).durationFrom(clockModel.getValidityStart()),
1288                                 1.0e-15);
1289         Assertions.assertEquals(0.0,
1290                                 new AbsoluteDate(2017, 5, 21, 0, 10, 0.0, ts).durationFrom(clockModel.getValidityEnd()),
1291                                 1.0e-15);
1292         try {
1293             clockModel.getOffset(clockModel.getValidityStart().shiftedBy(-1));
1294             Assertions.fail("an exceptions should have been thrown");
1295         } catch (OrekitException oe) {
1296             Assertions.assertEquals(OrekitMessages.NO_DATA_GENERATED, oe.getSpecifier());
1297         }
1298         try {
1299             clockModel.getOffset(clockModel.getValidityEnd().shiftedBy(1));
1300             Assertions.fail("an exceptions should have been thrown");
1301         } catch (OrekitException oe) {
1302             Assertions.assertEquals(OrekitMessages.NO_DATA_GENERATED, oe.getSpecifier());
1303         }
1304     }
1305 
1306     @Test
1307     public void testSpliceNoDrop() {
1308 
1309         final SP3 spliced = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_after_no_drop.sp3");
1310 
1311         Assertions.assertEquals(SP3OrbitType.FIT, spliced.getHeader().getOrbitType());
1312         Assertions.assertEquals(TimeSystem.GPS, spliced.getHeader().getTimeSystem());
1313 
1314         Assertions.assertEquals(87, spliced.getSatelliteCount());
1315 
1316         final List<SP3Coordinate> coords = spliced.getSatellites().get("R23").getSegments().get(0).getCoordinates();
1317         Assertions.assertEquals(4, coords.size());
1318 
1319         final SP3Coordinate coord = coords.get(0);
1320 
1321         Assertions.assertEquals(new AbsoluteDate(2017, 5, 21, 0, 0, 0, TimeScalesFactory.getGPS()),
1322                                 coord.getDate());
1323 
1324         // PG01 -11044.805800 -10475.672350  21929.418200    189.163300 18 18 18 219
1325         // PR23  24552.470459   -242.899447   6925.437998     86.875825
1326         checkPVEntry(new PVCoordinates(new Vector3D(24552470.459, -242899.447, 6925437.998), Vector3D.ZERO),
1327                      coord);
1328         Assertions.assertEquals(0.000086875825, coord.getClockCorrection(), 1.0e-15);
1329 
1330         Assertions.assertEquals(new AbsoluteDate(2017, 5, 21, 0, 15, 0, TimeScalesFactory.getGPS()),
1331                                 coords.get(coords.size() - 1).getDate());
1332 
1333         Assertions.assertEquals("R23", spliced.getEphemeris("R23").getId());
1334         try {
1335             spliced.getEphemeris(88);
1336             Assertions.fail("an exception should have been thrown");
1337         } catch (OrekitException oe) {
1338             Assertions.assertEquals(OrekitMessages.INVALID_SATELLITE_ID, oe.getSpecifier());
1339             Assertions.assertEquals(88, ((Integer) oe.getParts()[0]).intValue());
1340         }
1341         try {
1342             spliced.getEphemeris("Z00");
1343             Assertions.fail("an exception should have been thrown");
1344         } catch (OrekitException oe) {
1345             Assertions.assertEquals(OrekitMessages.INVALID_SATELLITE_ID, oe.getSpecifier());
1346             Assertions.assertEquals("Z00", oe.getParts()[0]);
1347         }
1348 
1349         Assertions.assertEquals(5,
1350                                 SP3Utils.indexAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, SP3Utils.POS_VEL_BASE_ACCURACY,
1351                                                        spliced.getHeader().getAccuracy("R23")));
1352 
1353     }
1354 
1355     @Test
1356     public void testIssue1552() {
1357         final String     ex     = "/sp3/three-hours.sp3";
1358         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
1359         final SP3        sp3    = new SP3Parser().parse(source);
1360 
1361         final AbsoluteDate start = new AbsoluteDate(2015, 5, 5, 0,  0, 0.0, TimeScalesFactory.getGPS());
1362         final AbsoluteDate end   = new AbsoluteDate(2015, 5, 5, 2, 55, 0.0, TimeScalesFactory.getGPS());
1363 
1364         checkPolynomialClock(sp3.getEphemeris("C01").extractClockModel(), start, end,
1365                              new PolynomialFunction(-4.34415678e-4, 3.15154873e-11, -2.19219167e-17),
1366                              2.2e-10);
1367         checkPolynomialClock(sp3.getEphemeris("C02").extractClockModel(), start, end,
1368                              new PolynomialFunction(-9.16586434e-4, 2.20479164e-11, 2.96650589e-17),
1369                              2.8e-10);
1370 
1371     }
1372 
1373     private void checkPolynomialClock(final AggregatedClockModel clockModel,
1374                                   final AbsoluteDate start, final AbsoluteDate end,
1375                                   final PolynomialFunction polynomialFunction, final double tolerance) {
1376 
1377         Assertions.assertEquals(start, clockModel.getValidityStart());
1378         Assertions.assertEquals(end,   clockModel.getValidityEnd());
1379 
1380         for (double dt = 0; dt < end.durationFrom(start); dt += 1) {
1381             final AbsoluteDate date   = start.shiftedBy(dt);
1382             final double       offset = clockModel.getOffset(date).getOffset();
1383             Assertions.assertEquals(polynomialFunction.value(dt), offset, tolerance);
1384         }
1385 
1386     }
1387 
1388     private SP3 splice(final String name1, final String name2) {
1389         final DataSource source1 = new DataSource(name1, () -> getClass().getResourceAsStream(name1));
1390         final SP3        file1   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, IGSUtils::guessFrame).parse(source1);
1391         final DataSource source2 = new DataSource(name2, () -> getClass().getResourceAsStream(name2));
1392         final SP3        file2   = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, IGSUtils::guessFrame).parse(source2);
1393         return SP3.splice(Arrays.asList(file1, file2));
1394     }
1395 
1396     @BeforeEach
1397     public void setUp() {
1398         Utils.setDataRoot("regular-data");
1399     }
1400 }