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,  5,  0.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, 4);
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 between1And2 = new AbsoluteDate(2020, 9, 1, 0, 4, 45.0, spliced.getTimeScale());
867         Assertions.assertEquals(-1.83536264242e-4,
868                                 spliced.extractClockModel("J01", 4).getOffset(between1And2).getOffset(),
869                                 1.0e-12);
870 
871         final AbsoluteDate between2And3 = new AbsoluteDate(2020, 9, 1, 0,  9, 45.0, spliced.getTimeScale());
872         try {
873             clk2.extractClockModel("J01", 4).getOffset(between2And3);
874             Assertions.fail("an exception should have been thrown");
875         } catch (OrekitException oe) {
876             Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, oe.getSpecifier());
877         }
878         try {
879             clk3.extractClockModel("J01", 4).getOffset(between2And3);
880             Assertions.fail("an exception should have been thrown");
881         } catch (OrekitException oe) {
882             Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, oe.getSpecifier());
883         }
884         Assertions.assertEquals(-1.83535202897e-4,
885                                 spliced.extractClockModel("J01", 2).getOffset(between2And3).getOffset(),
886                                 1.0e-12);
887 
888 
889     }
890 
891     /** Test parsing error exception. */
892     @Test
893     public void testParsingErrorException() {
894         try {
895             final String ex = "/gnss/clock/error_in_line_4.clk";
896             final RinexClockParser parser = new RinexClockParser();
897             final InputStream inEntry = getClass().getResourceAsStream(ex);
898             parser.parse(inEntry);
899             Assertions.fail("an exception should have been thrown");
900         } catch (OrekitException oe) {
901             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
902             Assertions.assertEquals(4, oe.getParts()[0]);
903         }
904     }
905 
906     /** Test missing block error exception. */
907     @Test
908     public void testMissingBlockException() {
909         try {
910             final String ex = "/gnss/clock/missing_block_end_of_header.clk";
911             final RinexClockParser parser = new RinexClockParser();
912             final InputStream inEntry = getClass().getResourceAsStream(ex);
913             parser.parse(inEntry);
914             Assertions.fail("an exception should have been thrown");
915         } catch (OrekitException oe) {
916             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
917             Assertions.assertEquals(38, oe.getParts()[0]);
918         }
919     }
920 
921     /** Unsupported clock file version exception throwing test. */
922     @Test
923     public void testUnsupportedVersion() {
924         try {
925             final String ex = "/gnss/clock/unsupported_clock_file_version.clk";
926             final RinexClockParser parser = new RinexClockParser();
927             final InputStream inEntry = getClass().getResourceAsStream(ex);
928             parser.parse(inEntry);
929             Assertions.fail("an exception should have been thrown");
930         } catch (OrekitException oe) {
931             Assertions.assertEquals(OrekitMessages.CLOCK_FILE_UNSUPPORTED_VERSION, oe.getSpecifier());
932             Assertions.assertEquals("0.05", Double.toString((double) oe.getParts()[0]));
933         }
934     }
935 
936     /** Wrong clock data type exception throwing test. */
937     @Test
938     public void testWrongClockDataType() {
939         try {
940             final String ex = "/gnss/clock/wrong_clock_data_type.clk";
941             final RinexClockParser parser = new RinexClockParser();
942             final InputStream inEntry = getClass().getResourceAsStream(ex);
943             parser.parse(inEntry);
944             Assertions.fail("an exception should have been thrown");
945         } catch (OrekitIllegalArgumentException oe) {
946             Assertions.assertEquals(OrekitMessages.UNKNOWN_CLOCK_DATA_TYPE, oe.getSpecifier());
947             Assertions.assertEquals("XX", oe.getParts()[0]);
948         }
949     }
950 
951     /** Unknown time system exception throwing test. */
952     @Test
953     public void testUnknownTimeSystem() {
954         try {
955             final String ex = "/gnss/clock/unknown_time_system.clk";
956             final RinexClockParser parser = new RinexClockParser();
957             final InputStream inEntry = getClass().getResourceAsStream(ex);
958             parser.parse(inEntry);
959             Assertions.fail("an exception should have been thrown");
960         } catch (OrekitIllegalArgumentException oe) {
961             Assertions.assertEquals(OrekitMessages.UNKNOWN_TIME_SYSTEM, oe.getSpecifier());
962             Assertions.assertEquals("WWW", oe.getParts()[0]);
963         }
964     }
965 
966     @Test
967     public void testTimeSystem() {
968         Assertions.assertEquals(TimeScalesFactory.getGPS(),
969                 TimeSystem.GPS.getTimeScale(DataContext.getDefault().getTimeScales()));
970         Assertions.assertEquals(TimeScalesFactory.getGST(),
971                 TimeSystem.GALILEO.getTimeScale(DataContext.getDefault().getTimeScales()));
972         Assertions.assertEquals(TimeScalesFactory.getGLONASS(),
973                 TimeSystem.GLONASS.getTimeScale(DataContext.getDefault().getTimeScales()));
974         Assertions.assertEquals(TimeScalesFactory.getQZSS(),
975                 TimeSystem.QZSS.getTimeScale(DataContext.getDefault().getTimeScales()));
976         Assertions.assertEquals(TimeScalesFactory.getTAI(),
977                 TimeSystem.TAI.getTimeScale(DataContext.getDefault().getTimeScales()));
978         Assertions.assertEquals(TimeScalesFactory.getUTC(),
979                 TimeSystem.UTC.getTimeScale(DataContext.getDefault().getTimeScales()));
980         Assertions.assertEquals(TimeScalesFactory.getBDT(),
981                 TimeSystem.BEIDOU.getTimeScale(DataContext.getDefault().getTimeScales()));
982         Assertions.assertEquals(TimeScalesFactory.getNavIC(),
983                 TimeSystem.NAVIC.getTimeScale(DataContext.getDefault().getTimeScales()));
984         Assertions.assertEquals(TimeScalesFactory.getUTC(),
985                                 TimeSystem.GMT.getTimeScale(DataContext.getDefault().getTimeScales()));
986         Assertions.assertEquals(TimeScalesFactory.getGPS(),
987                                 TimeSystem.UNKNOWN.getTimeScale(DataContext.getDefault().getTimeScales()));
988     }
989 
990     /** Test parsing file of issue #845 (https://gitlab.orekit.org/orekit/orekit/-/issues/845). */
991     @Test
992     public void testIssue845() throws URISyntaxException {
993 
994         // Parse file
995         final String ex = "/gnss/clock/issue845.clk";
996 
997         final RinexClockParser parser = new RinexClockParser();
998         final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString();
999         final RinexClock file = parser.parse(fileName);
1000 
1001         Assertions.assertEquals(3.0, file.getFormatVersion(), 1.0e-3);
1002         Assertions.assertEquals("GeoForschungsZentrum Potsdam", file.getAnalysisCenterName());
1003         Assertions.assertEquals("GFZ", file.getAgencyName());
1004         Assertions.assertEquals("IGS14", file.getFrameName());
1005     }
1006 
1007     @Test
1008     public void testMixedSystem() throws IOException {
1009 
1010         // Parse file
1011         final String ex = "/gnss/clock/issue1356.clk.gz";
1012         final DataSource original = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
1013         final FiltersManager filtersManager = new FiltersManager();
1014         filtersManager.addFilter(new GzipFilter());
1015         final DataSource source = filtersManager.applyRelevantFilters(original);
1016 
1017         final RinexClock file = new RinexClockParser().parse(source);
1018 
1019         Assertions.assertEquals(3.0, file.getFormatVersion(), 1.0e-3);
1020         Assertions.assertEquals(SatelliteSystem.MIXED, file.getSatelliteSystem());
1021         Assertions.assertEquals(54, file.getSatellites().size());
1022         Assertions.assertEquals(10, file.getReceivers().size());
1023         Assertions.assertTrue(file.getListAppliedDCBS().isEmpty());
1024         Assertions.assertTrue(file.getListAppliedPCVS().isEmpty());
1025     }
1026 
1027     /** Check the content of a clock file. */
1028     private void checkClockFileContent(final RinexClock file,
1029                                        final double version, final SatelliteSystem satelliteSystem, final TimeSystem timeSystem,
1030                                        final String programName, final String agencyName, final String comments,
1031                                        final String stationName, final String stationIdentifier,
1032                                        final String analysisCenterID, final String analysisCenterName, final String externalClockReference,
1033                                        final String creationDateString, final String creationTimeString, final String creationZoneString,
1034                                        final AbsoluteDate creationDate, final int numberOfLeapSeconds, final int numberOfLeapSecondsGPS,
1035                                        final int numberOfDBCS, final int numberOfPCVS,
1036                                        final int numberOfDataTypes, final int numberOfObservationTypes, final String frameString,
1037                                        final int numberOfReceivers, final int numberOfSatellites,
1038                                        final int numberOfDataLines, final String id, final ClockDataType type, final TimeScale timeScale,
1039                                        final AbsoluteDate dataEpoch, final int numberOfValues,
1040                                        final double clockBias, final double clockBiasSigma,
1041                                        final double clockRate, final double clockRateSigma,
1042                                        final double clockAcceleration, final double clockAccelerationSigma) {
1043 
1044         // Check header
1045         Assertions.assertEquals(version, file.getFormatVersion(), 1E-3);
1046         Assertions.assertSame(satelliteSystem, file.getSatelliteSystem());
1047         Assertions.assertSame(timeSystem, file.getTimeSystem());
1048         Assertions.assertEquals(programName, file.getProgramName());
1049         Assertions.assertEquals(agencyName, file.getAgencyName());
1050         Assertions.assertEquals(comments, file.getComments());
1051         Assertions.assertEquals(stationName, file.getStationName());
1052         Assertions.assertEquals(stationIdentifier, file.getStationIdentifier());
1053         Assertions.assertEquals(analysisCenterID, file.getAnalysisCenterID());
1054         Assertions.assertEquals(analysisCenterName, file.getAnalysisCenterName());
1055         Assertions.assertEquals(externalClockReference, file.getExternalClockReference());
1056         Assertions.assertEquals(creationDateString, file.getCreationDateString());
1057         Assertions.assertEquals(creationTimeString, file.getCreationTimeString());
1058         Assertions.assertEquals(creationZoneString, file.getCreationTimeZoneString());
1059         if (null != creationDate) {
1060             Assertions.assertEquals(file.getCreationDate(), creationDate);
1061         }
1062         Assertions.assertEquals(numberOfLeapSeconds, file.getNumberOfLeapSeconds());
1063         Assertions.assertEquals(numberOfLeapSecondsGPS, file.getNumberOfLeapSecondsGNSS());
1064         Assertions.assertEquals(numberOfDBCS, file.getListAppliedDCBS().size());
1065         Assertions.assertEquals(numberOfPCVS, file.getListAppliedPCVS().size());
1066         Assertions.assertEquals(numberOfDataTypes, file.getNumberOfClockDataTypes());
1067         int observationTypes = 0;
1068         for (SatelliteSystem system : file.getSystemObservationTypes().keySet()) {
1069             observationTypes += file.getSystemObservationTypes().get(system).size();
1070         }
1071         Assertions.assertEquals(numberOfObservationTypes, observationTypes);
1072         Assertions.assertEquals(frameString, file.getFrameName());
1073         Assertions.assertEquals(numberOfReceivers, file.getNumberOfReceivers());
1074         Assertions.assertEquals(numberOfSatellites, file.getNumberOfSatellites());
1075         Assertions.assertEquals(timeScale.getName(), file.getTimeScale().getName());
1076 
1077         // Check total number of data lines
1078         Assertions.assertEquals(numberOfDataLines, file.getTotalNumberOfDataLines());
1079 
1080         // Look for a particular, random data line
1081         final List<ClockDataLine> clockDataLines = file.getClockData().get(id);
1082         boolean find = false;
1083         for (int i = 0; i < clockDataLines.size(); i++) {
1084             final ClockDataLine clockDataLine = clockDataLines.get(i);
1085             if (clockDataLine.getName().equals(id) &&
1086                 clockDataLine.getDataType().equals(type) &&
1087                 clockDataLine.getEpoch().equals(dataEpoch) &&
1088                 clockDataLine.getNumberOfValues() == numberOfValues &&
1089                 clockDataLine.getClockBias() == clockBias &&
1090                 clockDataLine.getClockBiasSigma() == clockBiasSigma &&
1091                 clockDataLine.getClockRate() == clockRate &&
1092                 clockDataLine.getClockRateSigma() == clockRateSigma &&
1093                 clockDataLine.getClockAcceleration() == clockAcceleration &&
1094                 clockDataLine.getClockAccelerationSigma() == clockAccelerationSigma) {
1095 
1096                 find = true;
1097             }
1098         }
1099 
1100         Assertions.assertTrue(find);
1101 
1102     }
1103 
1104     /** Check reference clock object content. */
1105     private void checkReferenceClock(final ReferenceClock referenceClock,
1106                                      final String referenceName, final String clockId,
1107                                      final double clockConstraint, final AbsoluteDate startDate, final AbsoluteDate endDate) {
1108 
1109         Assertions.assertEquals(referenceName, referenceClock.getReferenceName());
1110         Assertions.assertEquals(clockId, referenceClock.getClockID());
1111         Assertions.assertEquals(clockConstraint, referenceClock.getClockConstraint(), 1e-12);
1112         Assertions.assertEquals(startDate, referenceClock.getStartDate());
1113         Assertions.assertEquals(endDate, referenceClock.getEndDate());
1114     }
1115 
1116 }