1   /* Copyright 2022-2026 Thales Alenia Space
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 org.orekit.files.rinex.section.CommonLabel;
20  import org.orekit.files.rinex.section.Label;
21  import org.orekit.files.rinex.section.RinexClockObsBaseHeader;
22  import org.orekit.files.rinex.utils.ParsingUtils;
23  import org.orekit.files.rinex.utils.RinexFileType;
24  import org.orekit.frames.Frame;
25  import org.orekit.gnss.ObservationType;
26  import org.orekit.gnss.SatInSystem;
27  import org.orekit.gnss.SatelliteSystem;
28  import org.orekit.gnss.TimeSystem;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.time.TimeScale;
31  import org.orekit.time.TimeScales;
32  import org.orekit.utils.TimeSpanMap;
33  
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  
40  /** Header for Rinex Clock.
41   * @author Luc Maisonobe
42   * @since 14.0
43   */
44  public class RinexClockHeader extends RinexClockObsBaseHeader {
45  
46      /** Index of label in header lines before 3.04. */
47      public static final int LABEL_INDEX_300_302 = 60;
48  
49      /** Index of label in header lines for version 3.04 and later. */
50      public static final int LABEL_INDEX_304_PLUS = 65;
51  
52      /** Indicator for header before version 3.04. */
53      private boolean before304;
54  
55      /** System time scale. */
56      private TimeScale timeScale;
57  
58      /** Number of leap seconds separating UTC and TAI. */
59      private int leapSecondsTAI;
60  
61      /** Earth centered frame. */
62      private Frame frame;
63  
64      /** Earth centered frame name as a string. */
65      private String frameName;
66  
67      /** Time system. */
68      private TimeSystem timeSystem;
69  
70      /** Station name for calibration and discontinuity data. */
71      private String stationName;
72  
73      /** Station identifier for calibration and discontinuity data. */
74      private String stationIdentifier;
75  
76      /** External reference clock identifier for calibration. */
77      private String externalClockReference;
78  
79      /** Analysis center ID. */
80      private String analysisCenterID;
81  
82      /** Full analysis center name. */
83      private String analysisCenterName;
84  
85      /** Reference clocks. */
86      private final TimeSpanMap<List<ReferenceClock>> referenceClocks;
87  
88      /** Satellite system code. */
89      private final Map<SatelliteSystem, List<ObservationType>> systemObservationTypes;
90  
91      /** List of the data types in the file. */
92      private final List<ClockDataType> clockDataTypes;
93  
94      /** List of the receivers in the file. */
95      private final List<Receiver> receivers;
96  
97      /** List of the satellites in the file. */
98      private final List<SatInSystem> satellites;
99  
100     /** Merged satellites systems. */
101     private SatelliteSystem mergedSystems;
102 
103     /** Simple constructor.
104      */
105     public RinexClockHeader() {
106         super(RinexFileType.CLOCK);
107         this.frameName              = "";
108         this.systemObservationTypes = new HashMap<>();
109         this.clockDataTypes         = new ArrayList<>();
110         this.timeSystem             = null;
111         this.stationIdentifier      = "";
112         this.stationName            = "";
113         this.externalClockReference = "";
114         this.analysisCenterID       = "";
115         this.analysisCenterName     = "";
116         this.referenceClocks        = new TimeSpanMap<>(null);
117         this.receivers              = new ArrayList<>();
118         this.satellites             = new ArrayList<>();
119     }
120 
121     /** {@inheritDoc} */
122     @Override
123     public void setFormatVersion(final double formatVersion) {
124         super.setFormatVersion(formatVersion);
125         before304 = formatVersion < 3.04;
126     }
127 
128     /** Check if header corresponds to a version before 3.04.
129      * @return true if header corresponds to a version before 3.04
130      */
131     boolean isBefore304() {
132         return before304;
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     public SatelliteSystem parseSatelliteSystem(final String line, final SatelliteSystem defaultSatelliteSystem) {
138         final String satSystemString = (getFormatVersion() < 3.04 ? line.substring(40, 41) : line.substring(42, 43)).trim();
139         return SatelliteSystem.parseSatelliteSystem(satSystemString, defaultSatelliteSystem);
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public void parseProgramRunByDate(final String line, final TimeScales timeScales) {
145         if (getFormatVersion() < 3.04) {
146             parseProgramRunByDate(ParsingUtils.parseString(line, 0, 20),
147                                   ParsingUtils.parseString(line, 20, 20),
148                                   ParsingUtils.parseString(line, 40, 20),
149                                   timeScales);
150         } else {
151             parseProgramRunByDate(ParsingUtils.parseString(line, 0, 19),
152                                   ParsingUtils.parseString(line, 21, 19),
153                                   ParsingUtils.parseString(line, 42, 21),
154                                   timeScales);
155         }
156     }
157 
158     /** Getter for the file time system.
159      * @return the file time system
160      */
161     public TimeSystem getTimeSystem() {
162         return timeSystem;
163     }
164 
165     /** Setter for the file time system.
166      * @param timeSystem the file time system to set
167      */
168     public void setTimeSystem(final TimeSystem timeSystem) {
169         this.timeSystem = timeSystem;
170     }
171 
172     /** Get the system time scale.
173      * @return system time scale
174      */
175     public TimeScale getTimeScale() {
176         return timeScale;
177     }
178 
179     /** Set the system time scale.
180      * @param timeScale system time scale
181      */
182     public void setTimeScale(final TimeScale timeScale) {
183         this.timeScale = timeScale;
184     }
185 
186     /** Getter for the station name.
187      * @return the station name
188      */
189     public String getStationName() {
190         return stationName;
191     }
192 
193     /** Setter for the station name.
194      * @param stationName the station name to set
195      */
196     public void setStationName(final String stationName) {
197         this.stationName = stationName;
198     }
199 
200     /** Getter for the station identifier.
201      * @return the station identifier
202      */
203     public String getStationIdentifier() {
204         return stationIdentifier;
205     }
206 
207     /** Setter for the station identifier.
208      * @param stationIdentifier the station identifier to set
209      */
210     public void setStationIdentifier(final String stationIdentifier) {
211         this.stationIdentifier = stationIdentifier;
212     }
213 
214     /** Getter for the external clock reference.
215      * @return the external clock reference
216      */
217     public String getExternalClockReference() {
218         return externalClockReference;
219     }
220 
221     /** Setter for the external clock reference.
222      * @param externalClockReference the external clock reference to set
223      */
224     public void setExternalClockReference(final String externalClockReference) {
225         this.externalClockReference = externalClockReference;
226     }
227 
228     /** Getter for the analysis center ID.
229      * @return the analysis center ID
230      */
231     public String getAnalysisCenterID() {
232         return analysisCenterID;
233     }
234 
235     /** Setter for the analysis center ID.
236      * @param analysisCenterID the analysis center ID to set
237      */
238     public void setAnalysisCenterID(final String analysisCenterID) {
239         this.analysisCenterID = analysisCenterID;
240     }
241 
242     /** Getter for the analysis center name.
243      * @return the analysis center name
244      */
245     public String getAnalysisCenterName() {
246         return analysisCenterName;
247     }
248 
249     /** Setter for the analysis center name.
250      * @param analysisCenterName the analysis center name to set
251      */
252     public void setAnalysisCenterName(final String analysisCenterName) {
253         this.analysisCenterName = analysisCenterName;
254     }
255 
256     /** Getter for the reference clocks.
257      * @return the time span map of the different refence clocks
258      */
259     public TimeSpanMap<List<ReferenceClock>> getReferenceClocks() {
260         return referenceClocks;
261     }
262 
263     /** Add a list of reference clocks which will be used after a specified date.
264      * If the reference map has not been already created, it will be.
265      * @param referenceClockList the reference clock list
266      * @param startDate          start date of list validity
267      * @param endDate            end date of validity
268      */
269     public void addReferenceClockList(final List<ReferenceClock> referenceClockList,
270                                       final AbsoluteDate startDate, final AbsoluteDate endDate) {
271         referenceClocks.addValidBetween(referenceClockList, startDate, endDate);
272     }
273 
274     /** Getter for the different observation type for each satellite system.
275      * @return the map of the different observation type per satellite system
276      */
277     public Map<SatelliteSystem, List<ObservationType>> getSystemObservationTypes() {
278         return Collections.unmodifiableMap(systemObservationTypes);
279     }
280 
281     /** Add an observation type for a specified satellite system.
282      * @param satSystem the satellite system to add observation type
283      * @param observationType the system observation type to set
284      */
285     public void addSystemObservationType(final SatelliteSystem satSystem,
286                                          final ObservationType observationType) {
287         final List<ObservationType> list;
288         synchronized (systemObservationTypes) {
289             list = systemObservationTypes.computeIfAbsent(satSystem, s -> new ArrayList<>());
290         }
291         list.add(observationType);
292     }
293 
294     /** Get the number of observation types for a given system.
295      * @param system the satellite system to consider
296      * @return the number of observation types for a given system
297      */
298     public int numberOfObsTypes(final SatelliteSystem system) {
299         if (systemObservationTypes.containsKey(system)) {
300             return systemObservationTypes.get(system).size();
301         } else {
302             return 0;
303         }
304     }
305 
306     /** Getter for the different clock data types.
307      * @return the list of the different clock data types
308      */
309     public List<ClockDataType> getClockDataTypes() {
310         return Collections.unmodifiableList(clockDataTypes);
311     }
312 
313     /** Add a clock data types.
314      * @param clockDataType the clock data types to add
315      */
316     public void addClockDataType(final ClockDataType clockDataType) {
317         clockDataTypes.add(clockDataType);
318     }
319 
320     /** Get the number of different clock data types in the file.
321      * @return the number of different clock data types
322      */
323     public int getNumberOfClockDataTypes() {
324         return clockDataTypes.size();
325     }
326 
327     /** Set the Number of leap seconds separating UTC and TAI.
328      * @param leapSecondsTAI Number of leap seconds separating UTC and TAI
329      */
330     public void setLeapSecondsTAI(final int leapSecondsTAI) {
331         this.leapSecondsTAI = leapSecondsTAI;
332     }
333 
334     /** Get the Number of leap seconds separating UTC and TAI.
335      * @return Number of leap seconds separating UTC and TAI
336      */
337     public int getLeapSecondsTAI() {
338         return leapSecondsTAI;
339     }
340 
341     /** Get the reference frame for the station positions.
342      * @return the reference frame for station positions
343      */
344     public Frame getFrame() {
345         return frame;
346     }
347 
348     /** Set the reference frame for the station positions.
349      * @param frame reference frame for station positions
350      */
351     public void setFrame(final Frame frame) {
352         this.frame = frame;
353     }
354 
355     /** Getter for the frame name.
356      * @return the frame name
357      */
358     public String getFrameName() {
359         return frameName;
360     }
361 
362     /** Setter for the frame name.
363      * @param frameName the frame name to set
364      */
365     public void setFrameName(final String frameName) {
366         this.frameName = frameName;
367     }
368 
369     /** Add a new satellite with a given identifier to the list of stored satellites.
370      * @param satId the satellite identifier
371      */
372     public void addSatellite(final SatInSystem satId) {
373 
374         // only add satellites which have not been added before
375         if (!satellites.contains(satId)) {
376             satellites.add(satId);
377 
378             // check if we have only one satellite system or mixed systems
379             if (mergedSystems == null) {
380                 mergedSystems = satId.getSystem();
381             } else if (satId.getSystem() != mergedSystems) {
382                 mergedSystems = SatelliteSystem.MIXED;
383             }
384 
385         }
386 
387     }
388 
389     /** Get the merged satellites systems.
390      * @return merged satellites systems
391      * @since 14.0
392      */
393     SatelliteSystem getMergedSystem() {
394         return mergedSystems;
395     }
396 
397     /** Add a new receiver to the list of stored receivers.
398      * @param receiver the receiver
399      */
400     public void addReceiver(final Receiver receiver) {
401 
402         boolean notInList = true;
403         for (Receiver rec : receivers) {
404             if (rec.getDesignator().equals(receiver.getDesignator())) {
405                 notInList = false;
406                 break;
407             }
408         }
409         // only add satellites which have not been added before
410         if (notInList) {
411             receivers.add(receiver);
412         }
413     }
414 
415     /** Get the number of receivers that are considered in the file.
416      * @return the number of receivers that are considered in the file
417      */
418     public int getNumberOfReceivers() {
419         return receivers.size();
420     }
421 
422     /** Get the number of satellites that are considered in the file.
423      * @return the number of satellites that are considered in the file
424      */
425     public int getNumberOfSatellites() {
426         return satellites.size();
427     }
428 
429     /** Getter for the receivers.
430      * @return the list of the receivers
431      */
432     public List<Receiver> getReceivers() {
433         return Collections.unmodifiableList(receivers);
434     }
435 
436     /** Getter for the satellites.
437      * @return the list of the satellites
438      */
439     public List<SatInSystem> getSatellites() {
440         return Collections.unmodifiableList(satellites);
441     }
442 
443     /** {@inheritDoc} */
444     @Override
445     public void checkType(final String line, final String name) {
446         checkType(line, getFormatVersion() < 3.04 ? 20 : 21, name);
447     }
448 
449     /** {@inheritDoc} */
450     @Override
451     public int getLabelIndex() {
452         return getFormatVersion() < 3.04 ? LABEL_INDEX_300_302 : LABEL_INDEX_304_PLUS;
453     }
454 
455     /** {@inheritDoc} */
456     @Override
457     public boolean matchFound(final Label label, final String line) {
458         // the position of the labels changes depending on version
459         if (label == CommonLabel.VERSION) {
460             // we are parsing the line RINEX VERSION / TYPE itself, the version is not known yet
461             // so we try both positions
462             return label.matches(line.substring(LABEL_INDEX_300_302).trim()) ||
463                    label.matches(line.substring(LABEL_INDEX_304_PLUS).trim());
464         } else {
465             return label.matches(line.substring(getLabelIndex()).trim());
466         }
467     }
468 
469 }