1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.files.rinex.clock;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URISyntaxException;
22  import java.nio.file.Paths;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.BeforeEach;
30  import org.junit.jupiter.api.Test;
31  import org.orekit.Utils;
32  import org.orekit.data.DataContext;
33  import org.orekit.data.DataSource;
34  import org.orekit.data.FiltersManager;
35  import org.orekit.data.GzipFilter;
36  import org.orekit.errors.OrekitException;
37  import org.orekit.errors.OrekitIllegalArgumentException;
38  import org.orekit.errors.OrekitMessages;
39  import org.orekit.files.rinex.clock.RinexClock.ClockDataLine;
40  import org.orekit.files.rinex.clock.RinexClock.ClockDataType;
41  import org.orekit.files.rinex.clock.RinexClock.Receiver;
42  import org.orekit.files.rinex.clock.RinexClock.ReferenceClock;
43  import org.orekit.frames.Frame;
44  import org.orekit.frames.FramesFactory;
45  import org.orekit.frames.ITRFVersion;
46  import org.orekit.gnss.ObservationType;
47  import org.orekit.gnss.SatelliteSystem;
48  import org.orekit.gnss.TimeSystem;
49  import org.orekit.time.AbsoluteDate;
50  import org.orekit.time.ClockModel;
51  import org.orekit.time.ClockOffset;
52  import org.orekit.time.SampledClockModel;
53  import org.orekit.time.TimeScale;
54  import org.orekit.time.TimeScalesFactory;
55  import org.orekit.utils.IERSConventions;
56  import org.orekit.utils.TimeSpanMap;
57  
58  /** This class aims at validating the correct IGS clock file parsing and error handling. */
59  public class ClockFileParserTest {
60  
61      @BeforeEach
62      public void setUp() {
63          Utils.setDataRoot("regular-data");
64      }
65  
66      /** First example given in the 3.04 RINEX clock file format. */
67      @Test
68      public void testParseExple1V304() throws URISyntaxException {
69  
70          // Parse file
71          final String ex = "/gnss/clock/Exple_analysis_1_304.clk";
72  
73          final RinexClockParser parser = new RinexClockParser();
74          final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
75          final RinexClock file = parser.parse(fileName);
76  
77          // Check content of the file
78          final double version = 3.04;
79          final SatelliteSystem satelliteSystem = SatelliteSystem.GPS;
80          final TimeSystem timeSystem = TimeSystem.GPS;
81          final String programName = "TORINEXC V9.9";
82          final String agencyName = "USNO";
83          final String comments = "EXAMPLE OF A CLOCK DATA ANALYSIS FILE\n" +
84                                  "IN THIS CASE ANALYSIS RESULTS FROM GPS ONLY ARE INCLUDED\n" +
85                                  "No re-alignment of the clocks has been applied.\n";
86          final String stationName = "";
87          final String stationIdentifier = "";
88          final String analysisCenterID = "USN";
89          final String analysisCenterName = "USNO USING GIPSY/OASIS-II";
90          final String externalClockReference = "";
91          final String creationDateString = "19960403";
92          final String creationTimeString = "001000";
93          final String creationZoneString = "UTC";
94          final AbsoluteDate creationDate = new AbsoluteDate(1996, 4, 3, 0, 10, 0., TimeScalesFactory.getUTC());
95          final int numberOfLeapSeconds = 10;
96          final int numberOfLeapSecondsGPS = 0;
97          final int numberOfDBCS = 1;
98          final int numberOfPCVS = 1;
99          final int numberOfDataTypes = 2;
100         final int numberOfObservationTypes = 4;
101         final String frameString = "ITRF96";
102         final int numberOfReceivers = 5; // Should be 4 as recorded in the file, however, according to format description, correct number is 5.
103         final int numberOfSatellites = 27;
104         final int numberOfDataLines = 5;
105         final String id = "GOLD";
106         final ClockDataType type = ClockDataType.AR;
107         final TimeScale timeScale = TimeScalesFactory.getGPS();
108         final AbsoluteDate dataEpoch = new AbsoluteDate(1994, 7, 14, 20, 59, 0.0, timeScale);
109         final int numberOfValues = 4;
110         final double clockBias = -0.0123456789012;
111         final double clockBiasSigma = -0.00123456789012;
112         final double clockRate = -0.000123456789012;
113         final double clockRateSigma = -0.0000123456789012;
114         final double clockAcceleration = 0.0;
115         final double clockAccelerationSigma = 0.0;
116         checkClockFileContent(file, version, satelliteSystem, timeSystem,
117                               programName, agencyName, comments, stationName, stationIdentifier,
118                               analysisCenterID, analysisCenterName, externalClockReference,
119                               creationDateString, creationTimeString, creationZoneString, creationDate,
120                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
121                               numberOfDataTypes, numberOfObservationTypes, frameString,
122                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
123                               id, type, timeScale, dataEpoch, numberOfValues,
124                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
125 
126         // In this case, time system in properlydefined, check getEpoch() methods
127         // Get data line
128         final ClockDataLine dataLine = file.getClockData().get(id).get(0);
129         Assertions.assertEquals(dataLine.getEpoch(), dataLine.getEpoch(file.getTimeScale()));
130     }
131 
132     /** Second example given in the 3.04 RINEX clock file format.
133      * PCVS block is not placed where it should be.
134      */
135     @Test
136     public void testParseExple2V304() throws URISyntaxException {
137 
138         // Parse file
139         final String ex = "/gnss/clock/Exple_analysis_2_304.clk";
140 
141         final RinexClockParser parser = new RinexClockParser();
142         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
143         final RinexClock file = parser.parse(fileName);
144 
145 
146 
147         // Check content of the file
148         final double version = 3.04;
149         final SatelliteSystem satelliteSystem = SatelliteSystem.GPS;
150         final TimeSystem timeSystem = TimeSystem.GPS;
151         final String programName = "CCLOCK";
152         final String agencyName = "IGSACC @ GA & MIT";
153         final String comments = "GPS week: 1939   Day: 6   MJD: 57823\n" +
154                                 "THE COMBINED CLOCKS ARE A WEIGHTED AVERAGE OF:\n" +
155                                 "cod emr esa gfz jpl\n" +
156                                 "THE FOLLOWING REFERENCE CLOCKS WERE USED BY ACs:\n" +
157                                 "PIE1 HERS AMC2 ZECK\n" +
158                                 "THE COMBINED CLOCKS ARE ALIGNED TO GPS TIME\n" +
159                                 "USING THE SATELLITE BROADCAST EPHEMERIDES\n";
160         final String stationName = "";
161         final String stationIdentifier = "";
162         final String analysisCenterID = "IGS";
163         final String analysisCenterName = "IGSACC @ GA and MIT";
164         final String externalClockReference = "";
165         final String creationDateString = "20170312";
166         final String creationTimeString = "052227";
167         final String creationZoneString = "UTC";
168         final AbsoluteDate creationDate = new AbsoluteDate(2017, 3, 12, 5, 22, 27., TimeScalesFactory.getUTC());
169         final int numberOfLeapSeconds = 37;
170         final int numberOfLeapSecondsGPS = 18;
171         final int numberOfDBCS = 0;
172         final int numberOfPCVS = 1;
173         final int numberOfDataTypes = 2;
174         final int numberOfObservationTypes = 0;
175         final String frameString = "IGS14";
176         final int numberOfReceivers = 22;
177         final int numberOfSatellites = 31;
178         final int numberOfDataLines = 6;
179         final String id = "G02";
180         final ClockDataType type = ClockDataType.AS;
181         final TimeScale timeScale = TimeScalesFactory.getGPS();
182         final AbsoluteDate dataEpoch = new AbsoluteDate(2017, 3, 11, 0, 0, 0.0, timeScale);
183         final int numberOfValues = 2;
184         final double clockBias = 0.868606546478E-04;
185         final double clockBiasSigma = 0.104109157753E-10;
186         final double clockRate = 0.0;
187         final double clockRateSigma = 0.0;
188         final double clockAcceleration = 0.0;
189         final double clockAccelerationSigma = 0.0;
190         checkClockFileContent(file, version, satelliteSystem, timeSystem,
191                               programName, agencyName, comments, stationName, stationIdentifier,
192                               analysisCenterID, analysisCenterName, externalClockReference,
193                               creationDateString, creationTimeString, creationZoneString, creationDate,
194                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
195                               numberOfDataTypes, numberOfObservationTypes, frameString,
196                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
197                               id, type, timeScale, dataEpoch, numberOfValues,
198                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
199 
200     }
201 
202     /** Third example given in the 3.04 RINEX clock file format.
203      * It embeds calibration data and misses time system ID.
204      */
205     @Test
206     public void testParseExpleCalibrationV304() throws URISyntaxException {
207 
208         // Parse file
209         final String ex = "/gnss/clock/Exple_calibration_304.clk";
210 
211         final RinexClockParser parser = new RinexClockParser();
212         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
213         final RinexClock file = parser.parse(fileName);
214 
215         // Check content of the file
216         final double version = 3.04;
217         final SatelliteSystem satelliteSystem = null;
218         final TimeSystem timeSystem = null;
219         final String programName = "TORINEXC V9.9";
220         final String agencyName = "USNO";
221         final String comments = "EXAMPLE OF A CLOCK DATA FILE\n" +
222                                 "IN THIS CASE CALIBRATION/DISCONTINUITY DATA GIVEN\n";
223         final String stationName = "USNO";
224         final String stationIdentifier = "40451S003";
225         final String analysisCenterID = "";
226         final String analysisCenterName = "";
227         final String externalClockReference = "UTC(USNO) MASTER CLOCK VIA CONTINUOUS CABLE MONITOR";
228         final String creationDateString = "19960403";
229         final String creationTimeString = "001000";
230         final String creationZoneString = "UTC";
231         final AbsoluteDate creationDate = new AbsoluteDate(1996, 4, 3, 0, 10, 0., TimeScalesFactory.getUTC());
232         final int numberOfLeapSeconds = 0;
233         final int numberOfLeapSecondsGPS = 10;
234         final int numberOfDBCS = 0;
235         final int numberOfPCVS = 0;
236         final int numberOfDataTypes = 2;
237         final int numberOfObservationTypes = 0;
238         final String frameString = "";
239         final int numberOfReceivers = 0;
240         final int numberOfSatellites = 0;
241         final int numberOfDataLines = 4;
242         final String id = "USNO";
243         final ClockDataType type = ClockDataType.DR;
244         final TimeScale timeScale = TimeScalesFactory.getUTC();
245         final AbsoluteDate dataEpoch = new AbsoluteDate(1995, 7, 14, 22, 23, 14.5, timeScale);
246         final int numberOfValues = 2;
247         final double clockBias = -0.123456789012E+01;
248         final double clockBiasSigma = 0.123456789012E+00;
249         final double clockRate = 0.0;
250         final double clockRateSigma = 0.0;
251         final double clockAcceleration = 0.0;
252         final double clockAccelerationSigma = 0.0;
253         checkClockFileContent(file, version, satelliteSystem, timeSystem,
254                               programName, agencyName, comments, stationName, stationIdentifier,
255                               analysisCenterID, analysisCenterName, externalClockReference,
256                               creationDateString, creationTimeString, creationZoneString, creationDate,
257                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
258                               numberOfDataTypes, numberOfObservationTypes, frameString,
259                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
260                               id, type, timeScale, dataEpoch, numberOfValues,
261                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
262     }
263 
264     /** An example of the 3.00 RINEX clock file format.
265      * It is espicially missing satellite system, creation time zoneand time system ID.
266      * Creation date also does not match expected format.
267      */
268     @Test
269     public void testParseExple1V300() throws URISyntaxException {
270 
271         // Parse file
272         final String ex = "/gnss/clock/mit19044_truncated_300.clk";
273 
274         final RinexClockParser parser = new RinexClockParser();
275         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
276         final RinexClock file = parser.parse(fileName);
277 
278         // Check content of the file
279         final double version = 3.00;
280         final SatelliteSystem satelliteSystem = null;
281         final TimeSystem timeSystem = null;
282         final String programName = "autcln 3.33+MIG";
283         final String agencyName = "MIT";
284         final String comments = "";
285         final String stationName = "";
286         final String stationIdentifier = "";
287         final String analysisCenterID = "MIT";
288         final String analysisCenterName = "";
289         final String externalClockReference = "";
290         final String creationDateString = "2016-07-14";
291         final String creationTimeString = "16:28";
292         final String creationZoneString = "";
293         final AbsoluteDate creationDate = null;
294         final int numberOfLeapSeconds = 17;
295         final int numberOfLeapSecondsGPS = 0;
296         final int numberOfDBCS = 0;
297         final int numberOfPCVS = 1;
298         final int numberOfDataTypes = 2;
299         final int numberOfObservationTypes = 0;
300         final String frameString = "IGS08";
301         final int numberOfReceivers = 12;
302         final int numberOfSatellites = 31;
303         final int numberOfDataLines = 10;
304         final String id = "G19";
305         final ClockDataType type = ClockDataType.AS;
306         final TimeScale timeScale = TimeScalesFactory.getUTC();
307         final AbsoluteDate dataEpoch = new AbsoluteDate(2016, 7, 7, 0, 0, 30.0, timeScale);
308         final int numberOfValues = 1;
309         final double clockBias = -0.525123304077E-03;
310         final double clockBiasSigma = 0.200000000000E-09;
311         final double clockRate = 0.0;
312         final double clockRateSigma = 0.0;
313         final double clockAcceleration = 0.0;
314         final double clockAccelerationSigma = 0.0;
315         checkClockFileContent(file, version, satelliteSystem, timeSystem,
316                               programName, agencyName, comments, stationName, stationIdentifier,
317                               analysisCenterID, analysisCenterName, externalClockReference,
318                               creationDateString, creationTimeString, creationZoneString, creationDate,
319                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
320                               numberOfDataTypes, numberOfObservationTypes, frameString,
321                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
322                               id, type, timeScale, dataEpoch, numberOfValues,
323                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
324     }
325 
326     /** Another example of the 3.00 RINEX clock file format.
327      * It is espicially missing creation date, time system ID and satellite system.
328      * PCVS block is not placed where it should be.
329      */
330     @Test
331     public void testParseExple2V300() throws URISyntaxException {
332 
333         // Parse file
334         final String ex = "/gnss/clock/igr21101_truncated_300.clk";
335 
336         final RinexClockParser parser = new RinexClockParser();
337         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
338         final RinexClock file = parser.parse(fileName);
339 
340         // Check content of the file
341         final double version = 3.00;
342         final SatelliteSystem satelliteSystem = null;
343         final TimeSystem timeSystem = null;
344         final String programName = "CCLOCK";
345         final String agencyName = "IGSACC @ GA MIT";
346         final String comments = "GPS week: 2110   Day: 1   MJD: 59015\n" +
347                                 "THE COMBINED CLOCKS ARE A WEIGHTED AVERAGE OF:\n" +
348                                 "cod emr esa gfz jpl\n" +
349                                 "THE FOLLOWING REFERENCE CLOCKS WERE USED BY ACs:\n" +
350                                 "MGUE NRC1 HERS BRUX\n" +
351                                 "THE COMBINED CLOCKS ARE ALIGNED TO GPS TIME\n" +
352                                 "USING THE SATELLITE BROADCAST EPHEMERIDES\n" +
353                                 "All clocks have been re-aligned to the IGS time scale: IGST\n";
354         final String stationName = "";
355         final String stationIdentifier = "";
356         final String analysisCenterID = "IGS";
357         final String analysisCenterName = "IGSACC @ GA MIT";
358         final String externalClockReference = "";
359         final String creationDateString = "";
360         final String creationTimeString = "";
361         final String creationZoneString = "";
362         final AbsoluteDate creationDate = null;
363         final int numberOfLeapSeconds = 18;
364         final int numberOfLeapSecondsGPS = 0;
365         final int numberOfDBCS = 0;
366         final int numberOfPCVS = 1;
367         final int numberOfDataTypes = 2;
368         final int numberOfObservationTypes = 0;
369         final String frameString = "IGb14";
370         final int numberOfReceivers = 18;
371         final int numberOfSatellites = 31;
372         final int numberOfDataLines = 7;
373         final String id = "GPST";
374         final ClockDataType type = ClockDataType.AR;
375         final TimeScale timeScale = TimeScalesFactory.getUTC();
376         final AbsoluteDate dataEpoch = new AbsoluteDate(2020, 6, 15, 0, 0, 0.0, timeScale);
377         final int numberOfValues = 2;
378         final double clockBias = -5.447980630520e-09;
379         final double clockBiasSigma = 0.0;
380         final double clockRate = 0.0;
381         final double clockRateSigma = 0.0;
382         final double clockAcceleration = 0.0;
383         final double clockAccelerationSigma = 0.0;
384         checkClockFileContent(file, version, satelliteSystem, timeSystem,
385                               programName, agencyName, comments, stationName, stationIdentifier,
386                               analysisCenterID, analysisCenterName,  externalClockReference,
387                               creationDateString, creationTimeString, creationZoneString, creationDate,
388                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
389                               numberOfDataTypes, numberOfObservationTypes, frameString,
390                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
391                               id, type, timeScale, dataEpoch, numberOfValues,
392                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
393     }
394 
395     @Test
396     public void testClockModel() throws URISyntaxException {
397 
398         // Parse file
399         final String ex = "/gnss/clock/cod17381_truncated_200.clk";
400 
401         final RinexClockParser parser = new RinexClockParser();
402         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
403         final RinexClock file = parser.parse(fileName);
404         final ClockModel  clockModel  = file.extractClockModel("AMC2", 2);
405 
406         // points exactly on files entries
407         final ClockOffset c1 = clockModel.getOffset(new AbsoluteDate(2013, 4, 29, 0, 0,  0.0, file.getTimeScale()));
408         final ClockOffset c2 = clockModel.getOffset(new AbsoluteDate(2013, 4, 29, 0, 0, 30.0, file.getTimeScale()));
409         Assertions.assertEquals(0.192309152524E-08, c1.getOffset(), 1.0e-21);
410         Assertions.assertEquals(0.192333320310E-08, c2.getOffset(), 1.0e-21);
411 
412         // intermediate point
413         final ClockOffset c = clockModel.getOffset(new AbsoluteDate(2013, 4, 29, 0, 0, 12.0, file.getTimeScale()));
414         Assertions.assertEquals(0.1923188196384e-08, c.getOffset(),       1.0e-21);
415         Assertions.assertEquals(8.05592866666e-15,   c.getRate(),         1.0e-26);
416         Assertions.assertEquals( 0.0,                c.getAcceleration(), 1.0e-40);
417 
418     }
419 
420     /** An example of the 2.00 RINEX clock file format. */
421     @Test
422     public void testParseExple1V200() throws URISyntaxException {
423 
424         // Parse file
425         final String ex = "/gnss/clock/emr10491_truncated_200.clk";
426 
427         final RinexClockParser parser = new RinexClockParser();
428         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
429         final RinexClock file = parser.parse(fileName);
430 
431         // Check content of the file
432         final double version = 2.00;
433         final SatelliteSystem satelliteSystem = null;
434         final TimeSystem timeSystem = null;
435         final String programName = "CLKRINEX V1.0";
436         final String agencyName = "NRCan";
437         final String comments = "CLK ANT Z-OFFSET(M): II/IIA 1.023; IIR 0.000\n" +
438                                 "No re-alignment of the clocks has been applied.\n";
439         final String stationName = "";
440         final String stationIdentifier = "";
441         final String analysisCenterID = "EMR";
442         final String analysisCenterName = "NATURAL RESOURCES CANADA";
443         final String externalClockReference = "";
444         final String creationDateString = "1-Mar-2000";
445         final String creationTimeString = "20:36";
446         final String creationZoneString = "";
447         final AbsoluteDate creationDate = null;
448         final int numberOfLeapSeconds = -13;
449         final int numberOfLeapSecondsGPS = 0;
450         final int numberOfDBCS = 0;
451         final int numberOfPCVS = 0;
452         final int numberOfDataTypes = 2;
453         final int numberOfObservationTypes = 0;
454         final String frameString = "ITRF97";
455         final int numberOfReceivers = 13;
456         final int numberOfSatellites = 28;
457         final int numberOfDataLines = 7;
458         final String id = "CHUR";
459         final ClockDataType type = ClockDataType.AR;
460         final TimeScale timeScale = TimeScalesFactory.getUTC();
461         final AbsoluteDate dataEpoch = new AbsoluteDate(2000, 2, 14, 0, 0, 0.0, timeScale);
462         final int numberOfValues = 2;
463         final double clockBias = 2.301824851111E-05;
464         final double clockBiasSigma = 2.795267117761E-10;
465         final double clockRate = 0.0;
466         final double clockRateSigma = 0.0;
467         final double clockAcceleration = 0.0;
468         final double clockAccelerationSigma = 0.0;
469         checkClockFileContent(file, version, satelliteSystem, timeSystem,
470                               programName, agencyName, comments, stationName, stationIdentifier,
471                               analysisCenterID, analysisCenterName, externalClockReference,
472                               creationDateString, creationTimeString, creationZoneString, creationDate,
473                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
474                               numberOfDataTypes, numberOfObservationTypes, frameString,
475                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
476                               id, type, timeScale, dataEpoch, numberOfValues,
477                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
478     }
479 
480     /** Another example of the 2.00 RINEX clock file format with another date time zone foramt. */
481     @Test
482     public void testParseExple2V200() throws URISyntaxException {
483 
484         // Parse file
485         final String ex = "/gnss/clock/jpl11456_truncated_200.clk";
486 
487         final RinexClockParser parser = new RinexClockParser();
488         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
489         final RinexClock file = parser.parse(fileName);
490 
491         // Check content of the file
492         final double version = 2.00;
493         final SatelliteSystem satelliteSystem = null;
494         final TimeSystem timeSystem = null;
495         final String programName = "tdp2clk v1.13";
496         final String agencyName = "JPL";
497         final String comments = "G11 G13 G14 G18 G20 G28      clk ant z-offset (m):  0.000\n" +
498                                 "G01 G03 G04 G05 G06 G07 G08  clk ant z-offset (m):  1.023\n" +
499                                 "G09 G10 G21 G22 G23 G24 G25  clk ant z-offset (m):  1.023\n" +
500                                 "G26 G27 G29 G30 G31          clk ant z-offset (m):  1.023\n" +
501                                 "clk ant z-offset (m): II/IIA 1.023; IIR 0.000\n" +
502                                 "Re-alignment to GPS time by broadcast clocks and linear fit\n";
503         final String stationName = "";
504         final String stationIdentifier = "";
505         final String analysisCenterID = "JPL";
506         final String analysisCenterName = "Jet Propulsion Laboratory";
507         final String externalClockReference = "";
508         final String creationDateString = "2002 Jan 3";
509         final String creationTimeString = "13:36:17";
510         final String creationZoneString = "";
511         final AbsoluteDate creationDate = null;
512         final int numberOfLeapSeconds = 13;
513         final int numberOfLeapSecondsGPS = 0;
514         final int numberOfDBCS = 0;
515         final int numberOfPCVS = 0;
516         final int numberOfDataTypes = 1;
517         final int numberOfObservationTypes = 0;
518         final String frameString = "IGS00";
519         final int numberOfReceivers = 1;
520         final int numberOfSatellites = 0;
521         final int numberOfDataLines = 1;
522         final String id = "ALGO";
523         final ClockDataType type = ClockDataType.AR;
524         final TimeScale timeScale = TimeScalesFactory.getUTC();
525         final AbsoluteDate dataEpoch = new AbsoluteDate(2001, 12, 22, 0, 0, 0.0, timeScale);
526         final int numberOfValues = 2;
527         final double clockBias = 1.598690662191e-06;
528         final double clockBiasSigma = 1.067405104634e-10;
529         final double clockRate = 0.0;
530         final double clockRateSigma = 0.0;
531         final double clockAcceleration = 0.0;
532         final double clockAccelerationSigma = 0.0;
533         checkClockFileContent(file, version, satelliteSystem, timeSystem,
534                               programName, agencyName, comments, stationName, stationIdentifier,
535                               analysisCenterID, analysisCenterName, externalClockReference,
536                               creationDateString, creationTimeString, creationZoneString, creationDate,
537                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
538                               numberOfDataTypes, numberOfObservationTypes, frameString,
539                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
540                               id, type, timeScale, dataEpoch, numberOfValues,
541                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
542     }
543 
544     /** Another example of the 2.00 RINEX clock file format with another date time zone foramt. */
545     @Test
546     public void testParseExple3V200() throws URISyntaxException {
547 
548         // Parse file
549         final String ex = "/gnss/clock/cod17381_truncated_200.clk";
550 
551         final RinexClockParser parser = new RinexClockParser();
552         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
553         final RinexClock file = parser.parse(fileName);
554 
555         // Check content of the file
556         final double version = 2.00;
557         final SatelliteSystem satelliteSystem = null;
558         final TimeSystem timeSystem = TimeSystem.GPS;
559         final String programName = "CCRNXC V5.3";
560         final String agencyName = "AIUB";
561         final String comments = "CODE final GPS clock information for day 119, 2013, 3D-sol\n" +
562                                 "Clock information consistent with phase and P1/P2 code data\n" +
563                                 "Satellite/receiver clock values at intervals of 30/300 sec\n" +
564                                 "High-rate (30 sec) clock interpolation based on phase data\n";
565         final String stationName = "";
566         final String stationIdentifier = "";
567         final String analysisCenterID = "COD";
568         final String analysisCenterName = "Center for Orbit Determination in Europe";
569         final String externalClockReference = "";
570         final String creationDateString = "04-MAY-13";
571         final String creationTimeString = "04:39";
572         final String creationZoneString = "";
573         final AbsoluteDate creationDate = null;
574         final int numberOfLeapSeconds = 16;
575         final int numberOfLeapSecondsGPS = 0;
576         final int numberOfDBCS = 1;
577         final int numberOfPCVS = 1;
578         final int numberOfDataTypes = 2;
579         final int numberOfObservationTypes = 0;
580         final String frameString = "IGb08";
581         final int numberOfReceivers = 2;
582         final int numberOfSatellites = 2;
583         final int numberOfDataLines = 5;
584         final String id = "AMC2";
585         final ClockDataType type = ClockDataType.AR;
586         final TimeScale timeScale = TimeScalesFactory.getGPS();
587         final AbsoluteDate dataEpoch = new AbsoluteDate(2013, 4, 29, 0, 0, 30.0, timeScale);
588         final int numberOfValues = 1;
589         final double clockBias = 0.192333320310E-08;
590         final double clockBiasSigma = 0.0;
591         final double clockRate = 0.0;
592         final double clockRateSigma = 0.0;
593         final double clockAcceleration = 0.0;
594         final double clockAccelerationSigma = 0.0;
595         checkClockFileContent(file, version, satelliteSystem, timeSystem,
596                               programName, agencyName, comments, stationName, stationIdentifier,
597                               analysisCenterID, analysisCenterName, externalClockReference,
598                               creationDateString, creationTimeString, creationZoneString, creationDate,
599                               numberOfLeapSeconds, numberOfLeapSecondsGPS, numberOfDBCS, numberOfPCVS,
600                               numberOfDataTypes, numberOfObservationTypes, frameString,
601                               numberOfReceivers, numberOfSatellites, numberOfDataLines,
602                               id, type, timeScale, dataEpoch, numberOfValues,
603                               clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma);
604     }
605 
606     /** Test parsing file with observation type continuation line. */
607     @Test
608     public void testParseWithObsTypeContinuationLine() throws URISyntaxException {
609 
610         // Parse file
611         final String ex = "/gnss/clock/Exple_analysis_1_304_more_obs_types.clk";
612 
613         final RinexClockParser parser = new RinexClockParser();
614         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
615         final RinexClock file = parser.parse(fileName);
616 
617         // Theorical values
618         final int numberOfObservationTypesGPS = 16;
619         final int numberOfObservationTypesGAL = 0;
620         final int numberOfSatelliteSystems = 1;
621 
622         // Get the satellite systems - observation types map
623         final Map<SatelliteSystem, List<ObservationType>> obsTypeMap = file.getSystemObservationTypes();
624 
625         // Check map content
626         Assertions.assertEquals(numberOfSatelliteSystems, obsTypeMap.keySet().size());
627         Assertions.assertEquals(numberOfObservationTypesGPS, file.numberOfObsTypes(SatelliteSystem.GPS));
628         Assertions.assertEquals(numberOfObservationTypesGAL, file.numberOfObsTypes(SatelliteSystem.GALILEO));
629     }
630 
631     /** Check receiver inforamtion. */
632     @Test
633     public void testParsedReceivers() throws URISyntaxException {
634 
635         // Parse file
636         final String ex = "/gnss/clock/Exple_analysis_2_304.clk";
637         final RinexClockParser parser = new RinexClockParser();
638         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
639         final RinexClock file = parser.parse(fileName);
640 
641         // Get theorical values
642         final int index = 3;
643         final String designator = "GUAM";
644         final String identifier = "50501M002";
645         final double x = -5071312.680;
646         final double y = 3568363.624;
647         final double z = 1488904.394;
648 
649         // Get the receiver in the parsed clock file
650         final List<Receiver> receivers = file.getReceivers();
651         final Receiver receiver = receivers.get(index);
652 
653         //Check content
654         Assertions.assertEquals(designator, receiver.getDesignator());
655         Assertions.assertEquals(identifier, receiver.getReceiverIdentifier());
656         Assertions.assertEquals(x, receiver.getX(), 1E-4);
657         Assertions.assertEquals(y, receiver.getY(), 1E-4);
658         Assertions.assertEquals(z, receiver.getZ(), 1E-4);
659     }
660 
661     /** Test default frame loader. */
662     @Test
663     public void testDefaultFrameLoader() throws URISyntaxException {
664 
665         // Get frames corresponding to default frame loader
666         final Frame itrf1996 = FramesFactory.getITRF(ITRFVersion.ITRF_1996,
667                                                      IERSConventions.IERS_1996,
668                                                      false);
669         final Frame itrf2014 = FramesFactory.getITRF(ITRFVersion.ITRF_2014,
670                                                      IERSConventions.IERS_2010,
671                                                      false);
672 
673         // Get default clock file parser
674         final RinexClockParser parser = new RinexClockParser();
675 
676         // Parse file with expected frame ITRF96
677         final String ex1 = "/gnss/clock/Exple_analysis_1_304.clk";
678         final String fileName1 = Paths.get(getClass().getResource(ex1).toURI()).toString();
679         final RinexClock file1 = parser.parse(fileName1);
680 
681         // Parse file with default expected frame ITRF 2014
682         final String ex2 = "/gnss/clock/Exple_analysis_2_304.clk";
683         final String fileName2 = Paths.get(getClass().getResource(ex2).toURI()).toString();
684         final RinexClock file2 = parser.parse(fileName2);
685 
686         // Check frames
687         Assertions.assertSame(itrf1996, file1.getFrame());
688         Assertions.assertSame(itrf2014, file2.getFrame());
689     }
690 
691     /** Test the reference clocks.  */
692     @Test
693     public void testReferenceClocks() throws URISyntaxException {
694 
695         // Parse file
696         final String ex = "/gnss/clock/Exple_analysis_1_304.clk";
697         final RinexClockParser parser = new RinexClockParser();
698         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
699         final RinexClock file = parser.parse(fileName);
700 
701         // Get reference clocks
702         final TimeSpanMap<List<ReferenceClock>> referenceClocksMap = file.getReferenceClocks();
703 
704         // Theorical time scale
705         final TimeScale gps = TimeScalesFactory.getGPS();
706 
707         // First reference clock theoretical values
708         final String referenceName1 = "USNO";
709         final String clockId1 = "40451S003";
710         final double clockConstraint1 = -.123456789012E+00;
711         final AbsoluteDate startDate1 = new AbsoluteDate(1994, 7, 14, 0, 0, 0.0, gps);
712         final AbsoluteDate endDate1 = new AbsoluteDate(1994, 7, 14, 20, 59, 0.0, gps);
713 
714         // Second reference clock theoretical values
715         final String referenceName2 = "TIDB";
716         final String clockId2 = "50103M108";
717         final double clockConstraint2 = -0.123456789012E+00;
718         final AbsoluteDate startDate2 = new AbsoluteDate(1994, 7, 14, 21, 0, 0.0, gps);
719         final AbsoluteDate endDate2 = new AbsoluteDate(1994, 7, 14, 21, 59, 0.0, gps);
720 
721         // Check number of time spans
722         Assertions.assertEquals(3, referenceClocksMap.getSpansNumber());
723 
724         // Get the two lists of reference clocks
725         final List<ReferenceClock> referenceClocks1 = referenceClocksMap.get(new AbsoluteDate(1994, 7, 14, 15, 0, 0.0, gps));
726         final List<ReferenceClock> referenceClocks2 = referenceClocksMap.get(new AbsoluteDate(1994, 7, 14, 21, 30, 0.0, gps));
727 
728         // Check total number of reference clocks
729         final int totalReferenceClockNumber = referenceClocks1.size() + referenceClocks2.size();
730         Assertions.assertEquals(2, totalReferenceClockNumber);
731 
732         // Check contents
733         checkReferenceClock(referenceClocks1.get(0),
734                             referenceName1, clockId1, clockConstraint1, startDate1, endDate1);
735         checkReferenceClock(referenceClocks2.get(0),
736                             referenceName2, clockId2, clockConstraint2, startDate2, endDate2);
737     }
738 
739     /** Test the satelite list.  */
740     @Test
741     public void testSatelliteList() throws URISyntaxException {
742 
743         // Parse file
744         final String ex = "/gnss/clock/Exple_analysis_1_304.clk";
745         final RinexClockParser parser = new RinexClockParser();
746         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
747         final RinexClock file = parser.parse(fileName);
748 
749         // Get satellite list
750         final List<String> satellites = file.getSatellites();
751 
752         // Expected list
753         final String prnLine = "G01 G02 G03 G04 G05 G06 G07 G08 G09 G10 G13 G14 G15 G16 G17 G18 " +
754                                "G19 G21 G22 G23 G24 G25 G26 G27 G29 G30 G31";
755         final String[] expected = prnLine.split(" ");
756 
757         for (int i = 0; i < satellites.size(); i++) {
758             Assertions.assertArrayEquals(expected, satellites.toArray());
759         }
760     }
761 
762     /** Test two same receivers and satellite.  */
763     @Test
764     public void testSameReceiversAndSatellites() throws URISyntaxException {
765 
766         // Parse file
767         final String ex = "/gnss/clock/two_same_receivers_and_satellites.clk";
768         final RinexClockParser parser = new RinexClockParser();
769         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
770         final RinexClock file = parser.parse(fileName);
771 
772         Assertions.assertEquals(1, file.getNumberOfReceivers());
773         Assertions.assertEquals(1, file.getNumberOfSatellites());
774     }
775 
776     /** Test the clock data type list.  */
777     @Test
778     public void testClockDataTypes() throws URISyntaxException {
779 
780         // Parse file
781         final String ex = "/gnss/clock/Exple_calibration_304.clk";
782         final RinexClockParser parser = new RinexClockParser();
783         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
784         final RinexClock file = parser.parse(fileName);
785 
786         // Get satellite list
787         final List<ClockDataType> dataTypes = file.getClockDataTypes();
788 
789         // Expected list
790         final List<ClockDataType> expected =  new ArrayList<>();
791         expected.add(ClockDataType.CR);
792         expected.add(ClockDataType.DR);
793 
794         for (int i = 0; i < dataTypes.size(); i++) {
795             Assertions.assertArrayEquals(expected.toArray(), dataTypes.toArray());
796         }
797     }
798 
799     /** Test the reference clocks.  */
800     @Test
801     public void testSplice() {
802 
803         // Parse file&
804         final RinexClockParser parser = new RinexClockParser();
805         final String     ex1  = "/gnss/clock/part-1.clk";
806         final RinexClock clk1 = parser.parse(new DataSource(ex1, () -> getClass().getResourceAsStream(ex1)));
807         Assertions.assertEquals(0,
808                                 new AbsoluteDate(2020, 9, 1, 0,  0,  0.0, clk1.getTimeScale()).durationFrom(clk1.getEarliestEpoch()),
809                                 1.0e-15);
810         Assertions.assertEquals(0,
811                                 new AbsoluteDate(2020, 9, 1, 0,  4, 30.0, clk1.getTimeScale()).durationFrom(clk1.getLatestEpoch()),
812                                 1.0e-15);
813         Assertions.assertEquals(5, clk1.getNumberOfReceivers());
814         Assertions.assertEquals(7, clk1.getNumberOfSatellites());
815 
816         final String     ex2  = "/gnss/clock/part-2.clk";
817         final RinexClock clk2 = parser.parse(new DataSource(ex2, () -> getClass().getResourceAsStream(ex2)));
818         Assertions.assertEquals(0,
819                                 new AbsoluteDate(2020, 9, 1, 0,  5,  0.0, clk2.getTimeScale()).durationFrom(clk2.getEarliestEpoch()),
820                                 1.0e-15);
821         Assertions.assertEquals(0,
822                                 new AbsoluteDate(2020, 9, 1, 0,  9, 30.0, clk2.getTimeScale()).durationFrom(clk2.getLatestEpoch()),
823                                 1.0e-15);
824         Assertions.assertEquals(4, clk2.getNumberOfReceivers());
825         Assertions.assertEquals(7, clk2.getNumberOfSatellites());
826 
827         final String     ex3  = "/gnss/clock/part-3.clk";
828         final RinexClock clk3 = parser.parse(new DataSource(ex3, () -> getClass().getResourceAsStream(ex3)));
829         Assertions.assertEquals(0,
830                                 new AbsoluteDate(2020, 9, 1, 0, 10,  0.0, clk3.getTimeScale()).durationFrom(clk3.getEarliestEpoch()),
831                                 1.0e-15);
832         Assertions.assertEquals(0,
833                                 new AbsoluteDate(2020, 9, 1, 0, 14, 30.0, clk3.getTimeScale()).durationFrom(clk3.getLatestEpoch()),
834                                 1.0e-15);
835         Assertions.assertEquals(5, clk3.getNumberOfReceivers());
836         Assertions.assertEquals(6, clk3.getNumberOfSatellites());
837 
838         // Splice all files
839         try {
840             RinexClock.splice(Arrays.asList(clk1, clk2, clk3), 10.0);
841             Assertions.fail("an exception should have been thrown");
842         } catch (OrekitException oe) {
843             Assertions.assertEquals(OrekitMessages.TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS,
844                                     oe.getSpecifier());
845             Assertions.assertEquals(30.0, (Double)oe.getParts()[0], 1.0e-10);
846         }
847 
848         // we intentionally provide the files in non-chronological order for testing purposes
849         final RinexClock spliced = RinexClock.splice(Arrays.asList(clk3, clk1, clk2), 60.0);
850         Assertions.assertEquals(0,
851                                 new AbsoluteDate(2020, 9, 1, 0,  0,  0.0, spliced.getTimeScale()).durationFrom(spliced.getEarliestEpoch()),
852                                 1.0e-15);
853         Assertions.assertEquals(0,
854                                 new AbsoluteDate(2020, 9, 1, 0, 14, 30.0, spliced.getTimeScale()).durationFrom(spliced.getLatestEpoch()),
855                                 1.0e-15);
856         Assertions.assertEquals(4, spliced.getNumberOfReceivers());
857         Assertions.assertEquals(5, spliced.getNumberOfSatellites());
858         for (final String id : Arrays.asList("CHPI", "GLPS", "KITG", "OWMG",
859                                              "G17", "G27", "R17", "R23", "J01")) {
860             SampledClockModel cm = spliced.extractClockModel(id, 2);
861             Assertions.assertEquals(spliced.getEarliestEpoch(), cm.getValidityStart());
862             Assertions.assertEquals(spliced.getLatestEpoch(), cm.getValidityEnd());
863             Assertions.assertEquals(30, cm.getCache().getAll().size());
864         }
865 
866         final AbsoluteDate middleDate = new AbsoluteDate(2020, 9, 1, 0,  4, 45.0, spliced.getTimeScale());
867         try {
868             clk1.extractClockModel("J01", 2).getOffset(middleDate);
869             Assertions.fail("an exception should have been thrown");
870         } catch (OrekitException oe) {
871             Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, oe.getSpecifier());
872         }
873         try {
874             clk2.extractClockModel("J01", 2).getOffset(middleDate);
875             Assertions.fail("an exception should have been thrown");
876         } catch (OrekitException oe) {
877             Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, oe.getSpecifier());
878         }
879         Assertions.assertEquals(-1.83536264e-4,
880                                 spliced.extractClockModel("J01", 2).getOffset(middleDate).getOffset(),
881                                 1.0e-12);
882 
883     }
884 
885     /** Test parsing error exception. */
886     @Test
887     public void testParsingErrorException() {
888         try {
889             final String ex = "/gnss/clock/error_in_line_4.clk";
890             final RinexClockParser parser = new RinexClockParser();
891             final InputStream inEntry = getClass().getResourceAsStream(ex);
892             parser.parse(inEntry);
893             Assertions.fail("an exception should have been thrown");
894         } catch (OrekitException oe) {
895             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
896             Assertions.assertEquals(4, oe.getParts()[0]);
897         }
898     }
899 
900     /** Test missing block error exception. */
901     @Test
902     public void testMissingBlockException() {
903         try {
904             final String ex = "/gnss/clock/missing_block_end_of_header.clk";
905             final RinexClockParser parser = new RinexClockParser();
906             final InputStream inEntry = getClass().getResourceAsStream(ex);
907             parser.parse(inEntry);
908             Assertions.fail("an exception should have been thrown");
909         } catch (OrekitException oe) {
910             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
911             Assertions.assertEquals(38, oe.getParts()[0]);
912         }
913     }
914 
915     /** Unsupported clock file version exception throwing test. */
916     @Test
917     public void testUnsupportedVersion() {
918         try {
919             final String ex = "/gnss/clock/unsupported_clock_file_version.clk";
920             final RinexClockParser parser = new RinexClockParser();
921             final InputStream inEntry = getClass().getResourceAsStream(ex);
922             parser.parse(inEntry);
923             Assertions.fail("an exception should have been thrown");
924         } catch (OrekitException oe) {
925             Assertions.assertEquals(OrekitMessages.CLOCK_FILE_UNSUPPORTED_VERSION, oe.getSpecifier());
926             Assertions.assertEquals("0.05", Double.toString((double) oe.getParts()[0]));
927         }
928     }
929 
930     /** Wrong clock data type exception throwing test. */
931     @Test
932     public void testWrongClockDataType() {
933         try {
934             final String ex = "/gnss/clock/wrong_clock_data_type.clk";
935             final RinexClockParser parser = new RinexClockParser();
936             final InputStream inEntry = getClass().getResourceAsStream(ex);
937             parser.parse(inEntry);
938             Assertions.fail("an exception should have been thrown");
939         } catch (OrekitIllegalArgumentException oe) {
940             Assertions.assertEquals(OrekitMessages.UNKNOWN_CLOCK_DATA_TYPE, oe.getSpecifier());
941             Assertions.assertEquals("XX", oe.getParts()[0]);
942         }
943     }
944 
945     /** Unknown time system exception throwing test. */
946     @Test
947     public void testUnknownTimeSystem() {
948         try {
949             final String ex = "/gnss/clock/unknown_time_system.clk";
950             final RinexClockParser parser = new RinexClockParser();
951             final InputStream inEntry = getClass().getResourceAsStream(ex);
952             parser.parse(inEntry);
953             Assertions.fail("an exception should have been thrown");
954         } catch (OrekitIllegalArgumentException oe) {
955             Assertions.assertEquals(OrekitMessages.UNKNOWN_TIME_SYSTEM, oe.getSpecifier());
956             Assertions.assertEquals("WWW", oe.getParts()[0]);
957         }
958     }
959 
960     @Test
961     public void testTimeSystem() {
962         Assertions.assertEquals(TimeScalesFactory.getGPS(),
963                 TimeSystem.GPS.getTimeScale(DataContext.getDefault().getTimeScales()));
964         Assertions.assertEquals(TimeScalesFactory.getGST(),
965                 TimeSystem.GALILEO.getTimeScale(DataContext.getDefault().getTimeScales()));
966         Assertions.assertEquals(TimeScalesFactory.getGLONASS(),
967                 TimeSystem.GLONASS.getTimeScale(DataContext.getDefault().getTimeScales()));
968         Assertions.assertEquals(TimeScalesFactory.getQZSS(),
969                 TimeSystem.QZSS.getTimeScale(DataContext.getDefault().getTimeScales()));
970         Assertions.assertEquals(TimeScalesFactory.getTAI(),
971                 TimeSystem.TAI.getTimeScale(DataContext.getDefault().getTimeScales()));
972         Assertions.assertEquals(TimeScalesFactory.getUTC(),
973                 TimeSystem.UTC.getTimeScale(DataContext.getDefault().getTimeScales()));
974         Assertions.assertEquals(TimeScalesFactory.getBDT(),
975                 TimeSystem.BEIDOU.getTimeScale(DataContext.getDefault().getTimeScales()));
976         Assertions.assertEquals(TimeScalesFactory.getNavIC(),
977                 TimeSystem.NAVIC.getTimeScale(DataContext.getDefault().getTimeScales()));
978         Assertions.assertEquals(TimeScalesFactory.getUTC(),
979                                 TimeSystem.GMT.getTimeScale(DataContext.getDefault().getTimeScales()));
980         Assertions.assertEquals(TimeScalesFactory.getGPS(),
981                                 TimeSystem.UNKNOWN.getTimeScale(DataContext.getDefault().getTimeScales()));
982     }
983 
984     /** Test parsing file of issue #845 (https://gitlab.orekit.org/orekit/orekit/-/issues/845). */
985     @Test
986     public void testIssue845() throws URISyntaxException {
987 
988         // Parse file
989         final String ex = "/gnss/clock/issue845.clk";
990 
991         final RinexClockParser parser = new RinexClockParser();
992         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
993         final RinexClock file = parser.parse(fileName);
994 
995         Assertions.assertEquals(3.0, file.getFormatVersion(), 1.0e-3);
996         Assertions.assertEquals("GeoForschungsZentrum Potsdam", file.getAnalysisCenterName());
997         Assertions.assertEquals("GFZ", file.getAgencyName());
998         Assertions.assertEquals("IGS14", file.getFrameName());
999     }
1000 
1001     @Test
1002     public void testMixedSystem() throws IOException {
1003 
1004         // Parse file
1005         final String ex = "/gnss/clock/issue1356.clk.gz";
1006         final DataSource original = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
1007         final FiltersManager filtersManager = new FiltersManager();
1008         filtersManager.addFilter(new GzipFilter());
1009         final DataSource source = filtersManager.applyRelevantFilters(original);
1010 
1011         final RinexClock file = new RinexClockParser().parse(source);
1012 
1013         Assertions.assertEquals(3.0, file.getFormatVersion(), 1.0e-3);
1014         Assertions.assertEquals(SatelliteSystem.MIXED, file.getSatelliteSystem());
1015         Assertions.assertEquals(54, file.getSatellites().size());
1016         Assertions.assertEquals(10, file.getReceivers().size());
1017         Assertions.assertTrue(file.getListAppliedDCBS().isEmpty());
1018         Assertions.assertTrue(file.getListAppliedPCVS().isEmpty());
1019     }
1020 
1021     /** Check the content of a clock file. */
1022     private void checkClockFileContent(final RinexClock file,
1023                                        final double version, final SatelliteSystem satelliteSystem, final TimeSystem timeSystem,
1024                                        final String programName, final String agencyName, final String comments,
1025                                        final String stationName, final String stationIdentifier,
1026                                        final String analysisCenterID, final String analysisCenterName, final String externalClockReference,
1027                                        final String creationDateString, final String creationTimeString, final String creationZoneString,
1028                                        final AbsoluteDate creationDate, final int numberOfLeapSeconds, final int numberOfLeapSecondsGPS,
1029                                        final int numberOfDBCS, final int numberOfPCVS,
1030                                        final int numberOfDataTypes, final int numberOfObservationTypes, final String frameString,
1031                                        final int numberOfReceivers, final int numberOfSatellites,
1032                                        final int numberOfDataLines, final String id, final ClockDataType type, final TimeScale timeScale,
1033                                        final AbsoluteDate dataEpoch, final int numberOfValues,
1034                                        final double clockBias, final double clockBiasSigma,
1035                                        final double clockRate, final double clockRateSigma,
1036                                        final double clockAcceleration, final double clockAccelerationSigma) {
1037 
1038         // Check header
1039         Assertions.assertEquals(version, file.getFormatVersion(), 1E-3);
1040         Assertions.assertSame(satelliteSystem, file.getSatelliteSystem());
1041         Assertions.assertSame(timeSystem, file.getTimeSystem());
1042         Assertions.assertEquals(programName, file.getProgramName());
1043         Assertions.assertEquals(agencyName, file.getAgencyName());
1044         Assertions.assertEquals(comments, file.getComments());
1045         Assertions.assertEquals(stationName, file.getStationName());
1046         Assertions.assertEquals(stationIdentifier, file.getStationIdentifier());
1047         Assertions.assertEquals(analysisCenterID, file.getAnalysisCenterID());
1048         Assertions.assertEquals(analysisCenterName, file.getAnalysisCenterName());
1049         Assertions.assertEquals(externalClockReference, file.getExternalClockReference());
1050         Assertions.assertEquals(creationDateString, file.getCreationDateString());
1051         Assertions.assertEquals(creationTimeString, file.getCreationTimeString());
1052         Assertions.assertEquals(creationZoneString, file.getCreationTimeZoneString());
1053         if (null != creationDate) {
1054             Assertions.assertEquals(file.getCreationDate(), creationDate);
1055         }
1056         Assertions.assertEquals(numberOfLeapSeconds, file.getNumberOfLeapSeconds());
1057         Assertions.assertEquals(numberOfLeapSecondsGPS, file.getNumberOfLeapSecondsGNSS());
1058         Assertions.assertEquals(numberOfDBCS, file.getListAppliedDCBS().size());
1059         Assertions.assertEquals(numberOfPCVS, file.getListAppliedPCVS().size());
1060         Assertions.assertEquals(numberOfDataTypes, file.getNumberOfClockDataTypes());
1061         int observationTypes = 0;
1062         for (SatelliteSystem system : file.getSystemObservationTypes().keySet()) {
1063             observationTypes += file.getSystemObservationTypes().get(system).size();
1064         }
1065         Assertions.assertEquals(numberOfObservationTypes, observationTypes);
1066         Assertions.assertEquals(frameString, file.getFrameName());
1067         Assertions.assertEquals(numberOfReceivers, file.getNumberOfReceivers());
1068         Assertions.assertEquals(numberOfSatellites, file.getNumberOfSatellites());
1069         Assertions.assertEquals(timeScale.getName(), file.getTimeScale().getName());
1070 
1071         // Check total number of data lines
1072         Assertions.assertEquals(numberOfDataLines, file.getTotalNumberOfDataLines());
1073 
1074         // Look for a particular, random data line
1075         final List<ClockDataLine> clockDataLines = file.getClockData().get(id);
1076         boolean find = false;
1077         for (int i = 0; i < clockDataLines.size(); i++) {
1078             final ClockDataLine clockDataLine = clockDataLines.get(i);
1079             if (clockDataLine.getName().equals(id) &&
1080                 clockDataLine.getDataType().equals(type) &&
1081                 clockDataLine.getEpoch().equals(dataEpoch) &&
1082                 clockDataLine.getNumberOfValues() == numberOfValues &&
1083                 clockDataLine.getClockBias() == clockBias &&
1084                 clockDataLine.getClockBiasSigma() == clockBiasSigma &&
1085                 clockDataLine.getClockRate() == clockRate &&
1086                 clockDataLine.getClockRateSigma() == clockRateSigma &&
1087                 clockDataLine.getClockAcceleration() == clockAcceleration &&
1088                 clockDataLine.getClockAccelerationSigma() == clockAccelerationSigma) {
1089 
1090                 find = true;
1091             }
1092         }
1093 
1094         Assertions.assertTrue(find);
1095 
1096     }
1097 
1098     /** Check reference clock object content. */
1099     private void checkReferenceClock(final ReferenceClock referenceClock,
1100                                      final String referenceName, final String clockId,
1101                                      final double clockConstraint, final AbsoluteDate startDate, final AbsoluteDate endDate) {
1102 
1103         Assertions.assertEquals(referenceName, referenceClock.getReferenceName());
1104         Assertions.assertEquals(clockId, referenceClock.getClockID());
1105         Assertions.assertEquals(clockConstraint, referenceClock.getClockConstraint(), 1e-12);
1106         Assertions.assertEquals(startDate, referenceClock.getStartDate());
1107         Assertions.assertEquals(endDate, referenceClock.getEndDate());
1108     }
1109 
1110 }