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