1 /* Copyright 2002-2024 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.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.SortedSet;
26 import java.util.TreeSet;
27 import java.util.function.Function;
28
29 import org.orekit.errors.OrekitException;
30 import org.orekit.errors.OrekitIllegalArgumentException;
31 import org.orekit.errors.OrekitMessages;
32 import org.orekit.files.rinex.AppliedDCBS;
33 import org.orekit.files.rinex.AppliedPCVS;
34 import org.orekit.frames.Frame;
35 import org.orekit.gnss.ObservationType;
36 import org.orekit.gnss.SatelliteSystem;
37 import org.orekit.gnss.TimeSystem;
38 import org.orekit.time.AbsoluteDate;
39 import org.orekit.time.ChronologicalComparator;
40 import org.orekit.time.ClockOffset;
41 import org.orekit.time.DateComponents;
42 import org.orekit.time.SampledClockModel;
43 import org.orekit.time.TimeComponents;
44 import org.orekit.time.TimeScale;
45 import org.orekit.utils.TimeSpanMap;
46
47 /** Represents a parsed clock file from the IGS.
48 * <p> A time system should be specified in the file. However, if it is not, default time system will be chosen
49 * regarding the satellite system. If it is mixed or not specified, default time system will be UTC. </p>
50 * <p> Some fields might be null after parsing. It is expected because of the numerous kind of data that can be stored in clock data file. </p>
51 * <p> Caution, files with missing information in header can lead to wrong data dates and station positions.
52 * It is adviced to check the correctness and format compliance of the clock file to be parsed.
53 * Some values such as file time scale still can be set by user. </p>
54 * @see <a href="ftp://igs.org/pub/data/format/rinex_clock300.txt"> 3.00 clock file format</a>
55 * @see <a href="ftp://igs.org/pub/data/format/rinex_clock302.txt"> 3.02 clock file format</a>
56 * @see <a href="ftp://igs.org/pub/data/format/rinex_clock304.txt"> 3.04 clock file format</a>
57 *
58 * @author Thomas Paulet
59 * @since 11.0
60 */
61 public class RinexClock {
62
63 /** Format version. */
64 private double formatVersion;
65
66 /** Satellite system. */
67 private SatelliteSystem satelliteSystem;
68
69 /** Name of the program creating current file. */
70 private String programName;
71
72 /** Name of the agency creating the current file. */
73 private String agencyName;
74
75 /** Date of the file creation as a string. */
76 private String creationDateString;
77
78 /** Time of the file creation as a string. */
79 private String creationTimeString;
80
81 /** Time zone of the file creation as a string. */
82 private String creationTimeZoneString;
83
84 /** Creation date as absolute date. */
85 private AbsoluteDate creationDate;
86
87 /** Comments. */
88 private String comments;
89
90 /** Satellite system code. */
91 private final Map<SatelliteSystem, List<ObservationType>> systemObservationTypes;
92
93 /** Time system. */
94 private TimeSystem timeSystem;
95
96 /** Data time scale related to time system. */
97 private TimeScale timeScale;
98
99 /** Number of leap seconds separating UTC and TAI (UTC = TAI - numberOfLeapSeconds). */
100 private int numberOfLeapSeconds;
101
102 /** Number of leap seconds separating UTC and GNSS time systems. */
103 private int numberOfLeapSecondsGNSS;
104
105 /** List of applied differential code bias corrections. */
106 private final List<AppliedDCBS> listAppliedDCBS;
107
108 /** List of antenna center variation corrections. */
109 private final List<AppliedPCVS> listAppliedPCVS;
110
111 /** List of the data types in the file. */
112 private final List<ClockDataType> clockDataTypes;
113
114 /** Station name for calibration and discontinuity data. */
115 private String stationName;
116
117 /** Station identifier for calibration and discontinuity data. */
118 private String stationIdentifier;
119
120 /** External reference clock identifier for calibration. */
121 private String externalClockReference;
122
123 /** Analysis center ID. */
124 private String analysisCenterID;
125
126 /** Full analysis center name. */
127 private String analysisCenterName;
128
129 /** Reference clocks. */
130 private final TimeSpanMap<List<ReferenceClock>> referenceClocks;
131
132 /** Earth centered frame name as a string. */
133 private String frameName;
134
135 /** Maps {@link #frameName} to a {@link Frame}. */
136 private final Function<? super String, ? extends Frame> frameBuilder;
137
138 /** List of the receivers in the file. */
139 private final List<Receiver> receivers;
140
141 /** List of the satellites in the file. */
142 private final List<String> satellites;
143
144 /** A map containing receiver/satellite information. */
145 private final Map<String, List<ClockDataLine>> clockData;
146
147 /** Earliest epoch.
148 * @since 12.1
149 */
150 private AbsoluteDate earliestEpoch;
151
152 /** Latest epoch.
153 * @since 12.1
154 */
155 private AbsoluteDate latestEpoch;
156
157 /** Constructor.
158 * @param frameBuilder for constructing a reference frame from the identifier
159 */
160 public RinexClock(final Function<? super String, ? extends Frame> frameBuilder) {
161 // Initialize fields with default data
162 this.systemObservationTypes = new HashMap<>();
163 this.listAppliedDCBS = new ArrayList<>();
164 this.listAppliedPCVS = new ArrayList<>();
165 this.clockDataTypes = new ArrayList<>();
166 this.receivers = new ArrayList<>();
167 this.satellites = new ArrayList<>();
168 this.clockData = new HashMap<>();
169 this.agencyName = "";
170 this.analysisCenterID = "";
171 this.analysisCenterName = "";
172 this.comments = "";
173 this.creationDate = null;
174 this.creationDateString = "";
175 this.creationTimeString = "";
176 this.creationTimeZoneString = "";
177 this.externalClockReference = "";
178 this.formatVersion = 0.0;
179 this.frameBuilder = frameBuilder;
180 this.frameName = "";
181 this.numberOfLeapSeconds = 0;
182 this.numberOfLeapSecondsGNSS = 0;
183 this.programName = "";
184 this.referenceClocks = new TimeSpanMap<>(null);
185 this.satelliteSystem = null;
186 this.stationIdentifier = "";
187 this.stationName = "";
188 this.timeScale = null;
189 this.timeSystem = null;
190 this.earliestEpoch = AbsoluteDate.FUTURE_INFINITY;
191 this.latestEpoch = AbsoluteDate.PAST_INFINITY;
192 }
193
194 /** Add a new satellite with a given identifier to the list of stored satellites.
195 * @param satId the satellite identifier
196 */
197 public void addSatellite(final String satId) {
198 // only add satellites which have not been added before
199 if (!satellites.contains(satId)) {
200 satellites.add(satId);
201 }
202 }
203
204 /** Add a new receiver to the list of stored receivers.
205 * @param receiver the receiver
206 */
207 public void addReceiver(final Receiver receiver) {
208
209 boolean notInList = true;
210 for (Receiver rec : receivers) {
211 if (rec.designator.equals(receiver.designator)) {
212 notInList = false;
213 break;
214 }
215 }
216 // only add satellites which have not been added before
217 if (notInList) {
218 receivers.add(receiver);
219 }
220 }
221
222 /** Get the number of different clock data types in the file.
223 * @return the number of different clock data types
224 */
225 public int getNumberOfClockDataTypes() {
226 return clockDataTypes.size();
227 }
228
229 /** Get the total number of complete data lines in the file.
230 * @return the total number of complete data lines in the file
231 */
232 public int getTotalNumberOfDataLines() {
233 int result = 0;
234 final Map<String, List<ClockDataLine>> data = getClockData();
235 for (final Map.Entry<String, List<ClockDataLine>> entry : data.entrySet()) {
236 result += entry.getValue().size();
237 }
238 return result;
239 }
240
241 /** Get the number of observation types for a given system.
242 * @param system the satellite system to consider
243 * @return the number of observation types for a given system
244 */
245 public int numberOfObsTypes(final SatelliteSystem system) {
246 if (systemObservationTypes.containsKey(system)) {
247 return systemObservationTypes.get(system).size();
248 } else {
249 return 0;
250 }
251 }
252
253 /** Get the number of receivers that are considered in the file.
254 * @return the number of receivers that are considered in the file
255 */
256 public int getNumberOfReceivers() {
257 return receivers.size();
258 }
259
260 /** Get the number of satellites that are considered in the file.
261 * @return the number of satellites that are considered in the file
262 */
263 public int getNumberOfSatellites() {
264 return satellites.size();
265 }
266
267 /** Getter for the format version.
268 * @return the format version
269 */
270 public double getFormatVersion() {
271 return formatVersion;
272 }
273
274 /** Setter for the format version.
275 * @param formatVersion the format version to set
276 */
277 public void setFormatVersion(final double formatVersion) {
278 this.formatVersion = formatVersion;
279 }
280
281 /** Getter for the satellite system.
282 * @return the satellite system
283 */
284 public SatelliteSystem getSatelliteSystem() {
285 return satelliteSystem;
286 }
287
288 /** Setter for the satellite system.
289 * @param satelliteSystem the satellite system to set
290 */
291 public void setSatelliteSystem(final SatelliteSystem satelliteSystem) {
292 this.satelliteSystem = satelliteSystem;
293 }
294
295 /** Getter for the program name.
296 * @return the program name
297 */
298 public String getProgramName() {
299 return programName;
300 }
301
302 /** Setter for the program name.
303 * @param programName the program name to set
304 */
305 public void setProgramName(final String programName) {
306 this.programName = programName;
307 }
308
309 /** Getter for the agency name.
310 * @return the agencyName
311 */
312 public String getAgencyName() {
313 return agencyName;
314 }
315
316 /** Setter for the agency name.
317 * @param agencyName the agency name to set
318 */
319 public void setAgencyName(final String agencyName) {
320 this.agencyName = agencyName;
321 }
322
323 /** Getter for the creation date of the file as a string.
324 * @return the creation date as a string
325 */
326 public String getCreationDateString() {
327 return creationDateString;
328 }
329
330 /** Setter for the creation date as a string.
331 * @param creationDateString the creation date as a string to set
332 */
333 public void setCreationDateString(final String creationDateString) {
334 this.creationDateString = creationDateString;
335 }
336
337 /** Getter for the creation time of the file as a string.
338 * @return the creation time as a string
339 */
340 public String getCreationTimeString() {
341 return creationTimeString;
342 }
343
344 /** Setter for the creation time as a string.
345 * @param creationTimeString the creation time as a string to set
346 */
347 public void setCreationTimeString(final String creationTimeString) {
348 this.creationTimeString = creationTimeString;
349 }
350
351 /** Getter for the creation time zone of the file as a string.
352 * @return the creation time zone as a string
353 */
354 public String getCreationTimeZoneString() {
355 return creationTimeZoneString;
356 }
357
358 /** Setter for the creation time zone.
359 * @param creationTimeZoneString the creation time zone as a string to set
360 */
361 public void setCreationTimeZoneString(final String creationTimeZoneString) {
362 this.creationTimeZoneString = creationTimeZoneString;
363 }
364
365 /** Getter for the creation date.
366 * @return the creation date
367 */
368 public AbsoluteDate getCreationDate() {
369 return creationDate;
370 }
371
372 /** Setter for the creation date.
373 * @param creationDate the creation date to set
374 */
375 public void setCreationDate(final AbsoluteDate creationDate) {
376 this.creationDate = creationDate;
377 }
378
379 /** Getter for the comments.
380 * @return the comments
381 */
382 public String getComments() {
383 return comments;
384 }
385
386 /** Add a comment line.
387 * @param comment the comment line to add
388 */
389 public void addComment(final String comment) {
390 this.comments = comments.concat(comment + "\n");
391 }
392
393 /** Getter for the different observation type for each satellite system.
394 * @return the map of the different observation type per satellite system
395 */
396 public Map<SatelliteSystem, List<ObservationType>> getSystemObservationTypes() {
397 return Collections.unmodifiableMap(systemObservationTypes);
398 }
399
400 /** Add an observation type for a specified satellite system.
401 * @param satSystem the satellite system to add observation type
402 * @param observationType the system observation type to set
403 */
404 public void addSystemObservationType(final SatelliteSystem satSystem,
405 final ObservationType observationType) {
406 systemObservationTypes.
407 computeIfAbsent(satSystem, s -> new ArrayList<>()).
408 add(observationType);
409 }
410
411 /** Getter for the file time system.
412 * @return the file time system
413 */
414 public TimeSystem getTimeSystem() {
415 return timeSystem;
416 }
417
418 /** Setter for the file time system.
419 * @param timeSystem the file time system to set
420 */
421 public void setTimeSystem(final TimeSystem timeSystem) {
422 this.timeSystem = timeSystem;
423 }
424
425 /** Getter for the data time scale.
426 * @return the data time scale
427 */
428 public TimeScale getTimeScale() {
429 return timeScale;
430 }
431
432 /** Setter for the data time scale.
433 * @param timeScale the data time scale to set
434 */
435 public void setTimeScale(final TimeScale timeScale) {
436 this.timeScale = timeScale;
437 }
438
439 /** Getter for the number of leap seconds.
440 * @return the number of leap seconds
441 */
442 public int getNumberOfLeapSeconds() {
443 return numberOfLeapSeconds;
444 }
445
446 /** Setter for the number of leap seconds.
447 * @param numberOfLeapSeconds the number of leap seconds to set
448 */
449 public void setNumberOfLeapSeconds(final int numberOfLeapSeconds) {
450 this.numberOfLeapSeconds = numberOfLeapSeconds;
451 }
452
453 /** Getter for the number of leap second for GNSS time scales.
454 * @return the number of leap seconds for GNSS time scales
455 */
456 public int getNumberOfLeapSecondsGNSS() {
457 return numberOfLeapSecondsGNSS;
458 }
459
460 /** Setter for the number of leap seconds for GNSS time scales.
461 * @param numberOfLeapSecondsGNSS the number of leap seconds for GNSS time scales to set
462 */
463 public void setNumberOfLeapSecondsGNSS(final int numberOfLeapSecondsGNSS) {
464 this.numberOfLeapSecondsGNSS = numberOfLeapSecondsGNSS;
465 }
466
467 /** Getter for the applied differential code bias corrections.
468 * @return the list of applied differential code bias corrections
469 */
470 public List<AppliedDCBS> getListAppliedDCBS() {
471 return Collections.unmodifiableList(listAppliedDCBS);
472 }
473
474 /** Add an applied differencial code bias corrections.
475 * @param appliedDCBS the applied differencial code bias corrections to add
476 */
477 public void addAppliedDCBS(final AppliedDCBS appliedDCBS) {
478 listAppliedDCBS.add(appliedDCBS);
479 }
480
481 /** Getter for the applied phase center variations.
482 * @return the list of the applied phase center variations
483 */
484 public List<AppliedPCVS> getListAppliedPCVS() {
485 return Collections.unmodifiableList(listAppliedPCVS);
486 }
487
488 /** Add an applied phase center variations.
489 * @param appliedPCVS the phase center variations to add
490 */
491 public void addAppliedPCVS(final AppliedPCVS appliedPCVS) {
492 listAppliedPCVS.add(appliedPCVS);
493 }
494
495 /** Getter for the different clock data types.
496 * @return the list of the different clock data types
497 */
498 public List<ClockDataType> getClockDataTypes() {
499 return Collections.unmodifiableList(clockDataTypes);
500 }
501
502 /** Add a clock data types.
503 * @param clockDataType the clock data types to add
504 */
505 public void addClockDataType(final ClockDataType clockDataType) {
506 clockDataTypes.add(clockDataType);
507 }
508
509 /** Getter for the station name.
510 * @return the station name
511 */
512 public String getStationName() {
513 return stationName;
514 }
515
516 /** Setter for the station name.
517 * @param stationName the station name to set
518 */
519 public void setStationName(final String stationName) {
520 this.stationName = stationName;
521 }
522
523 /** Getter for the station identifier.
524 * @return the station identifier
525 */
526 public String getStationIdentifier() {
527 return stationIdentifier;
528 }
529
530 /** Setter for the station identifier.
531 * @param stationIdentifier the station identifier to set
532 */
533 public void setStationIdentifier(final String stationIdentifier) {
534 this.stationIdentifier = stationIdentifier;
535 }
536
537 /** Getter for the external clock reference.
538 * @return the external clock reference
539 */
540 public String getExternalClockReference() {
541 return externalClockReference;
542 }
543
544 /** Setter for the external clock reference.
545 * @param externalClockReference the external clock reference to set
546 */
547 public void setExternalClockReference(final String externalClockReference) {
548 this.externalClockReference = externalClockReference;
549 }
550
551 /** Getter for the analysis center ID.
552 * @return the analysis center ID
553 */
554 public String getAnalysisCenterID() {
555 return analysisCenterID;
556 }
557
558 /** Setter for the analysis center ID.
559 * @param analysisCenterID the analysis center ID to set
560 */
561 public void setAnalysisCenterID(final String analysisCenterID) {
562 this.analysisCenterID = analysisCenterID;
563 }
564
565 /** Getter for the analysis center name.
566 * @return the analysis center name
567 */
568 public String getAnalysisCenterName() {
569 return analysisCenterName;
570 }
571
572 /** Setter for the analysis center name.
573 * @param analysisCenterName the analysis center name to set
574 */
575 public void setAnalysisCenterName(final String analysisCenterName) {
576 this.analysisCenterName = analysisCenterName;
577 }
578
579 /** Getter for the reference clocks.
580 * @return the time span map of the different refence clocks
581 */
582 public TimeSpanMap<List<ReferenceClock>> getReferenceClocks() {
583 return referenceClocks;
584 }
585
586 /** Add a list of reference clocks which will be used after a specified date.
587 * If the reference map has not been already created, it will be.
588 * @param referenceClockList the reference clock list
589 * @param startDate the date the list will be valid after.
590 */
591 public void addReferenceClockList(final List<ReferenceClock> referenceClockList,
592 final AbsoluteDate startDate) {
593 referenceClocks.addValidAfter(referenceClockList, startDate, false);
594 }
595
596 /** Getter for the frame name.
597 * @return the frame name
598 */
599 public String getFrameName() {
600 return frameName;
601 }
602
603
604 /** Setter for the frame name.
605 * @param frameName the frame name to set
606 */
607 public void setFrameName(final String frameName) {
608 this.frameName = frameName;
609 }
610
611 /** Getter for the receivers.
612 * @return the list of the receivers
613 */
614 public List<Receiver> getReceivers() {
615 return Collections.unmodifiableList(receivers);
616 }
617
618 /** Getter for the satellites.
619 * @return the list of the satellites
620 */
621 public List<String> getSatellites() {
622 return Collections.unmodifiableList(satellites);
623 }
624
625 /** Get the reference frame for the station positions.
626 * @return the reference frame for station positions
627 */
628 public Frame getFrame() {
629 return frameBuilder.apply(frameName);
630 }
631
632 /** Extract the clock model.
633 * @param name receiver/satellite name
634 * @param nbInterpolationPoints number of points to use in interpolation
635 * @return extracted clock model
636 * @since 12.1
637 */
638 public SampledClockModel extractClockModel(final String name,
639 final int nbInterpolationPoints) {
640 final List<ClockOffset> sample = new ArrayList<>();
641 clockData.
642 get(name).
643 forEach(c -> {
644 final double offset = c.clockBias;
645 final double rate = c.numberOfValues > 2 ? c.clockRate : Double.NaN;
646 final double acceleration = c.numberOfValues > 4 ? c.clockAcceleration : Double.NaN;
647 sample.add(new ClockOffset(c.getEpoch(), offset, rate, acceleration));
648 });
649 return new SampledClockModel(sample, nbInterpolationPoints);
650 }
651
652 /** Getter for an unmodifiable map of clock data.
653 * @return the clock data
654 */
655 public Map<String, List<ClockDataLine>> getClockData() {
656 return Collections.unmodifiableMap(clockData);
657 }
658
659
660 /** Add a clock data line to a specified receiver/satellite.
661 * @param id the satellite system to add observation type
662 * @param clockDataLine the clock data line to add
663 */
664 public void addClockData(final String id,
665 final ClockDataLine clockDataLine) {
666 clockData.computeIfAbsent(id, i -> new ArrayList<>()).add(clockDataLine);
667 final AbsoluteDate epoch = clockDataLine.getEpoch();
668 if (epoch.isBefore(earliestEpoch)) {
669 earliestEpoch = epoch;
670 }
671 if (epoch.isAfter(latestEpoch)) {
672 latestEpoch = epoch;
673 }
674 }
675
676 /** Get earliest epoch from the {@link #getClockData() clock data}.
677 * @return earliest epoch from the {@link #getClockData() clock data},
678 * or {@link AbsoluteDate#FUTURE_INFINITY} if no data has been added
679 * @since 12.1
680 */
681 public AbsoluteDate getEarliestEpoch() {
682 return earliestEpoch;
683 }
684
685 /** Get latest epoch from the {@link #getClockData() clock data}.
686 * @return latest epoch from the {@link #getClockData() clock data},
687 * or {@link AbsoluteDate#PAST_INFINITY} if no data has been added
688 * @since 12.1
689 */
690 public AbsoluteDate getLatestEpoch() {
691 return latestEpoch;
692 }
693
694 /** Splice several Rinex clock files together.
695 * <p>
696 * Splicing Rinex clock files is intended to be used when continuous computation
697 * covering more than one file is needed. The metadata (version number, agency, …)
698 * will be retrieved from the earliest file only. Receivers and satellites
699 * will be merged from all files. Some receivers or satellites may be missing
700 * in some files… Once sorted (which is done internally), if the gap between
701 * segments from two file is larger than {@code maxGap}, then an error
702 * will be triggered.
703 * </p>
704 * <p>
705 * The spliced file only contains the receivers and satellites that were present
706 * in all files. Receivers and satellites present in some files and absent from
707 * other files are silently dropped.
708 * </p>
709 * <p>
710 * Depending on producer, successive clock files either have a gap between the last
711 * entry of one file and the first entry of the next file (for example files with
712 * a 5 minutes epoch interval may end at 23:55 and the next file start at 00:00),
713 * or both files have one point exactly at the splicing date (i.e. 24:00 one day
714 * and 00:00 next day). In the later case, the last point of the early file is dropped
715 * and the first point of the late file takes precedence, hence only one point remains
716 * in the spliced file ; this design choice is made to enforce continuity and
717 * regular interpolation.
718 * </p>
719 * @param clocks clock files to merge
720 * @param maxGap maximum time gap between files
721 * @return merged clock file
722 * @since 12.1
723 */
724 public static RinexClock splice(final Collection<RinexClock> clocks,
725 final double maxGap) {
726
727 // sort the files
728 final ChronologicalComparator comparator = new ChronologicalComparator();
729 final SortedSet<RinexClock> sorted =
730 new TreeSet<>((c1, c2) -> comparator.compare(c1.earliestEpoch, c2.earliestEpoch));
731 sorted.addAll(clocks);
732
733 // prepare spliced file
734 final RinexClock first = sorted.first();
735 final RinexClock spliced = new RinexClock(first.frameBuilder);
736 spliced.setFormatVersion(first.getFormatVersion());
737 spliced.setSatelliteSystem(first.satelliteSystem);
738 spliced.setProgramName(first.getProgramName());
739 spliced.setAgencyName(first.getAgencyName());
740 spliced.setCreationDateString(first.getCreationDateString());
741 spliced.setCreationTimeString(first.getCreationTimeString());
742 spliced.setCreationTimeZoneString(first.getCreationTimeZoneString());
743 spliced.setCreationDate(first.getCreationDate());
744 spliced.addComment(first.getComments());
745 first.
746 getSystemObservationTypes().
747 forEach((s, l) -> l.forEach(o -> spliced.addSystemObservationType(s, o)));
748 spliced.setTimeSystem(first.getTimeSystem());
749 spliced.setTimeScale(first.getTimeScale());
750 spliced.setNumberOfLeapSeconds(first.getNumberOfLeapSeconds());
751 spliced.setNumberOfLeapSecondsGNSS(first.getNumberOfLeapSecondsGNSS());
752 first.getListAppliedDCBS().forEach(spliced::addAppliedDCBS);
753 first.getListAppliedPCVS().forEach(spliced::addAppliedPCVS);
754 first.getClockDataTypes().forEach(spliced::addClockDataType);
755 spliced.setStationName(first.getStationName());
756 spliced.setStationIdentifier(first.getStationIdentifier());
757 spliced.setExternalClockReference(first.getExternalClockReference());
758 spliced.setAnalysisCenterID(first.getAnalysisCenterID());
759 spliced.setAnalysisCenterName(first.getAnalysisCenterName());
760 spliced.setFrameName(first.getFrameName());
761
762 // merge reference clocks maps
763 sorted.forEach(rc -> {
764 TimeSpanMap.Span<List<ReferenceClock>> span = rc.getReferenceClocks().getFirstSpan();
765 while (span != null) {
766 if (span.getData() != null) {
767 spliced.addReferenceClockList(span.getData(), span.getStart());
768 }
769 span = span.next();
770 }
771 });
772
773 final List<String> clockIds = new ArrayList<>();
774
775 // identify the receivers that are present in all files
776 first.
777 getReceivers().
778 stream().
779 filter(r -> availableInAllFiles(r.getDesignator(), sorted)).
780 forEach(r -> {
781 spliced.addReceiver(r);
782 clockIds.add(r.getDesignator());
783 });
784
785 // identify the satellites that are present in all files
786 first.
787 getSatellites().
788 stream().
789 filter(s -> availableInAllFiles(s, sorted)).
790 forEach(s -> {
791 spliced.addSatellite(s);
792 clockIds.add(s);
793 });
794
795 // add the clock lines
796 for (final String clockId : clockIds) {
797 AbsoluteDate previous = null;
798 for (final RinexClock rc : sorted) {
799 if (previous != null) {
800 if (rc.getEarliestEpoch().durationFrom(previous) > maxGap) {
801 throw new OrekitException(OrekitMessages.TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS,
802 rc.getEarliestEpoch().durationFrom(previous));
803 }
804 }
805 previous = rc.getLatestEpoch();
806 rc.getClockData().get(clockId).forEach(cd -> spliced.addClockData(clockId, cd));
807 }
808 }
809
810 return spliced;
811
812 }
813
814 /** Check if clock data is available in all files.
815 * @param clockId clock id
816 * @param files clock files
817 * @return true if clock is available in all files
818 */
819 private static boolean availableInAllFiles(final String clockId, final Collection<RinexClock> files) {
820 for (final RinexClock rc : files) {
821 if (!rc.getClockData().containsKey(clockId)) {
822 return false;
823 }
824 }
825 return true;
826 }
827
828 /** Clock data for a single station.
829 * <p> Data epoch is not linked to any time system in order to pars files with missing lines.
830 * Though, the default version of the getEpoch() method links the data time components with the clock file object time scale.
831 * The latter can be set with a default value (UTC). Caution is recommanded.
832 */
833 public class ClockDataLine {
834
835 /** Clock data type. */
836 private final ClockDataType dataType;
837
838 /** Receiver/Satellite name. */
839 private final String name;
840
841 /** Epoch date components. */
842 private final DateComponents dateComponents;
843
844 /** Epoch time components. */
845 private final TimeComponents timeComponents;
846
847 /** Number of data values to follow.
848 * This number might not represent the non zero values in the line.
849 */
850 private final int numberOfValues;
851
852 /** Clock bias (seconds). */
853 private final double clockBias;
854
855 /** Clock bias sigma (seconds). */
856 private final double clockBiasSigma;
857
858 /** Clock rate (dimensionless). */
859 private final double clockRate;
860
861 /** Clock rate sigma (dimensionless). */
862 private final double clockRateSigma;
863
864 /** Clock acceleration (seconds^-1). */
865 private final double clockAcceleration;
866
867 /** Clock acceleration sigma (seconds^-1). */
868 private final double clockAccelerationSigma;
869
870 /** Constructor.
871 * @param type the clock data type
872 * @param name the receiver/satellite name
873 * @param dateComponents the epoch date components
874 * @param timeComponents the epoch time components
875 * @param numberOfValues the number of values to follow
876 * @param clockBias the clock bias in seconds
877 * @param clockBiasSigma the clock bias sigma in seconds
878 * @param clockRate the clock rate
879 * @param clockRateSigma the clock rate sigma
880 * @param clockAcceleration the clock acceleration in seconds^-1
881 * @param clockAccelerationSigma the clock acceleration in seconds^-1
882 */
883 public ClockDataLine (final ClockDataType type, final String name,
884 final DateComponents dateComponents,
885 final TimeComponents timeComponents,
886 final int numberOfValues,
887 final double clockBias, final double clockBiasSigma,
888 final double clockRate, final double clockRateSigma,
889 final double clockAcceleration, final double clockAccelerationSigma) {
890
891 this.dataType = type;
892 this.name = name;
893 this.dateComponents = dateComponents;
894 this.timeComponents = timeComponents;
895 this.numberOfValues = numberOfValues;
896 this.clockBias = clockBias;
897 this.clockBiasSigma = clockBiasSigma;
898 this.clockRate = clockRate;
899 this.clockRateSigma = clockRateSigma;
900 this.clockAcceleration = clockAcceleration;
901 this.clockAccelerationSigma = clockAccelerationSigma;
902 }
903
904 /** Getter for the clock data type.
905 * @return the clock data type
906 */
907 public ClockDataType getDataType() {
908 return dataType;
909 }
910
911 /** Getter for the receiver/satellite name.
912 * @return the receiver/satellite name
913 */
914 public String getName() {
915 return name;
916 }
917
918 /** Getter for the number of values to follow.
919 * @return the number of values to follow
920 */
921 public int getNumberOfValues() {
922 return numberOfValues;
923 }
924
925 /** Get data line epoch.
926 * This method should be used if Time System ID line is present in the clock file.
927 * If it is missing, UTC time scale will be applied.
928 * To specify tim scale, use {@link #getEpoch(TimeScale) getEpoch(TimeScale)} method.
929 * @return the data line epoch
930 */
931 public AbsoluteDate getEpoch() {
932 return new AbsoluteDate(dateComponents, timeComponents, timeScale);
933 }
934
935 /** Get data line epoch.
936 * This method should be used in case Time System ID line is missing.
937 * Otherwise, it is adviced to rather use {@link #getEpoch() getEpoch()} method.
938 * @param epochTimeScale the time scale in which the epoch is defined
939 * @return the data line epoch set in the specified time scale
940 */
941 public AbsoluteDate getEpoch(final TimeScale epochTimeScale) {
942 return new AbsoluteDate(dateComponents, timeComponents, epochTimeScale);
943 }
944
945 /** Getter for the clock bias.
946 * @return the clock bias in seconds
947 */
948 public double getClockBias() {
949 return clockBias;
950 }
951
952 /** Getter for the clock bias sigma.
953 * @return the clock bias sigma in seconds
954 */
955 public double getClockBiasSigma() {
956 return clockBiasSigma;
957 }
958
959 /** Getter for the clock rate.
960 * @return the clock rate
961 */
962 public double getClockRate() {
963 return clockRate;
964 }
965
966 /** Getter for the clock rate sigma.
967 * @return the clock rate sigma
968 */
969 public double getClockRateSigma() {
970 return clockRateSigma;
971 }
972
973 /** Getter for the clock acceleration.
974 * @return the clock acceleration in seconds^-1
975 */
976 public double getClockAcceleration() {
977 return clockAcceleration;
978 }
979
980 /** Getter for the clock acceleration sigma.
981 * @return the clock acceleration sigma in seconds^-1
982 */
983 public double getClockAccelerationSigma() {
984 return clockAccelerationSigma;
985 }
986
987 }
988
989 /** Represents a reference clock with its validity time span. */
990 public static class ReferenceClock {
991
992 /** Receiver/satellite embedding the reference clock name. */
993 private final String referenceName;
994
995 /** Clock ID. */
996 private final String clockID;
997
998 /** A priori clock constraint (in seconds). */
999 private final double clockConstraint;
1000
1001 /** Start date of the validity period. */
1002 private final AbsoluteDate startDate;
1003
1004 /** End date of the validity period. */
1005 private final AbsoluteDate endDate;
1006
1007 /** Constructor.
1008 * @param referenceName the name of the receiver/satellite embedding the reference clock
1009 * @param clockID the clock ID
1010 * @param clockConstraint the a priori clock constraint
1011 * @param startDate the validity period start date
1012 * @param endDate the validity period end date
1013 */
1014 public ReferenceClock (final String referenceName, final String clockID, final double clockConstraint,
1015 final AbsoluteDate startDate, final AbsoluteDate endDate) {
1016 this.referenceName = referenceName;
1017 this.clockID = clockID;
1018 this.clockConstraint = clockConstraint;
1019 this.startDate = startDate;
1020 this.endDate = endDate;
1021 }
1022
1023 /** Getter for the name of the receiver/satellite embedding the reference clock.
1024 * @return the name of the receiver/satellite embedding the reference clock
1025 */
1026 public String getReferenceName() {
1027 return referenceName;
1028 }
1029
1030 /** Getter for the clock ID.
1031 * @return the clock ID
1032 */
1033 public String getClockID() {
1034 return clockID;
1035 }
1036
1037 /** Getter for the clock constraint.
1038 * @return the clock constraint
1039 */
1040 public double getClockConstraint() {
1041 return clockConstraint;
1042 }
1043
1044 /** Getter for the validity period start date.
1045 * @return the validity period start date
1046 */
1047 public AbsoluteDate getStartDate() {
1048 return startDate;
1049 }
1050
1051 /** Getter for the validity period end date.
1052 * @return the validity period end date
1053 */
1054 public AbsoluteDate getEndDate() {
1055 return endDate;
1056 }
1057
1058 }
1059
1060 /** Represents a receiver or a satellite with its position in the considered frame. */
1061 public static class Receiver {
1062
1063 /** Designator. */
1064 private final String designator;
1065
1066 /** Receiver identifier. */
1067 private final String receiverIdentifier;
1068
1069 /** X coordinates in file considered Earth centered frame (in meters). */
1070 private final double x;
1071
1072 /** Y coordinates in file considered Earth centered frame (in meters). */
1073 private final double y;
1074
1075 /** Z coordinates in file considered Earth centered frame (in meters). */
1076 private final double z;
1077
1078 /** Constructor.
1079 * @param designator the designator
1080 * @param receiverIdentifier the receiver identifier
1081 * @param x the X coordinate in meters in considered Earth centered frame
1082 * @param y the Y coordinate in meters in considered Earth centered frame
1083 * @param z the Z coordinate in meters in considered Earth centered frame
1084 */
1085 public Receiver(final String designator, final String receiverIdentifier,
1086 final double x, final double y, final double z) {
1087 this.designator = designator;
1088 this.receiverIdentifier = receiverIdentifier;
1089 this.x = x;
1090 this.y = y;
1091 this.z = z;
1092 }
1093
1094 /** Getter for the designator.
1095 * @return the designator
1096 */
1097 public String getDesignator() {
1098 return designator;
1099 }
1100
1101 /** Getter for the receiver identifier.
1102 * @return the receiver identifier
1103 */
1104 public String getReceiverIdentifier() {
1105 return receiverIdentifier;
1106 }
1107
1108 /** Getter for the X coordinate in meters in considered Earth centered frame.
1109 * @return the X coordinate in meters in considered Earth centered frame
1110 */
1111 public double getX() {
1112 return x;
1113 }
1114
1115 /** Getter for the Y coordinate in meters in considered Earth centered frame.
1116 * @return the Y coordinate in meters in considered Earth centered frame
1117 */
1118 public double getY() {
1119 return y;
1120 }
1121
1122 /** Getter for the Z coordinate in meters in considered Earth centered frame.
1123 * @return the Z coordinate in meters in considered Earth centered frame
1124 */
1125 public double getZ() {
1126 return z;
1127 }
1128 }
1129
1130 /** Clock data type.
1131 * In case of a DR type, clock data are in the sense of clock value after discontinuity minus prior.
1132 * In other cases, clock data are in the sense of reported station/satellite clock minus reference clock value. */
1133 public enum ClockDataType {
1134
1135 /** Data analysis for receiver clocks. Clock Data are*/
1136 AR("AR"),
1137
1138 /** Data analysis for satellite clocks. */
1139 AS("AS"),
1140
1141 /** Calibration measurement for a single GPS receiver. */
1142 CR("CR"),
1143
1144 /** Discontinuity measurements for a single GPS receiver. */
1145 DR("DR"),
1146
1147 /** Monitor measurements for the broadcast satellite clocks. */
1148 MS("MS");
1149
1150 /** Parsing map. */
1151 private static final Map<String, ClockDataType> KEYS_MAP = new HashMap<>();
1152 static {
1153 for (final ClockDataType timeSystem : values()) {
1154 KEYS_MAP.put(timeSystem.getKey(), timeSystem);
1155 }
1156 }
1157
1158 /** Key for the system. */
1159 private final String key;
1160
1161 /** Simple constructor.
1162 * @param key key letter
1163 */
1164 ClockDataType(final String key) {
1165 this.key = key;
1166 }
1167
1168 /** Get the key for the system.
1169 * @return key for the system
1170 */
1171 public String getKey() {
1172 return key;
1173 }
1174
1175 /** Parse a string to get the time system.
1176 * <p>
1177 * The string must be the time system.
1178 * </p>
1179 * @param s string to parse
1180 * @return the time system
1181 * @exception OrekitIllegalArgumentException if the string does not correspond to a time system key
1182 */
1183 public static ClockDataType parseClockDataType(final String s)
1184 throws OrekitIllegalArgumentException {
1185 final ClockDataType clockDataType = KEYS_MAP.get(s);
1186 if (clockDataType == null) {
1187 throw new OrekitIllegalArgumentException(OrekitMessages.UNKNOWN_CLOCK_DATA_TYPE, s);
1188 }
1189 return clockDataType;
1190 }
1191 }
1192 }