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