1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.DateTimeComponents;
43 import org.orekit.time.SampledClockModel;
44 import org.orekit.time.TimeComponents;
45 import org.orekit.time.TimeScale;
46 import org.orekit.utils.TimeSpanMap;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class RinexClock {
63
64
65 private double formatVersion;
66
67
68 private SatelliteSystem satelliteSystem;
69
70
71 private String programName;
72
73
74 private String agencyName;
75
76
77 private String creationDateString;
78
79
80 private String creationTimeString;
81
82
83 private String creationTimeZoneString;
84
85
86 private AbsoluteDate creationDate;
87
88
89 private String comments;
90
91
92 private final Map<SatelliteSystem, List<ObservationType>> systemObservationTypes;
93
94
95 private TimeSystem timeSystem;
96
97
98 private TimeScale timeScale;
99
100
101 private int numberOfLeapSeconds;
102
103
104 private int numberOfLeapSecondsGNSS;
105
106
107 private final List<AppliedDCBS> listAppliedDCBS;
108
109
110 private final List<AppliedPCVS> listAppliedPCVS;
111
112
113 private final List<ClockDataType> clockDataTypes;
114
115
116 private String stationName;
117
118
119 private String stationIdentifier;
120
121
122 private String externalClockReference;
123
124
125 private String analysisCenterID;
126
127
128 private String analysisCenterName;
129
130
131 private final TimeSpanMap<List<ReferenceClock>> referenceClocks;
132
133
134 private String frameName;
135
136
137 private final Function<? super String, ? extends Frame> frameBuilder;
138
139
140 private final List<Receiver> receivers;
141
142
143 private final List<String> satellites;
144
145
146 private final Map<String, List<ClockDataLine>> clockData;
147
148
149
150
151 private AbsoluteDate earliestEpoch;
152
153
154
155
156 private AbsoluteDate latestEpoch;
157
158
159
160
161 public RinexClock(final Function<? super String, ? extends Frame> frameBuilder) {
162
163 this.systemObservationTypes = new HashMap<>();
164 this.listAppliedDCBS = new ArrayList<>();
165 this.listAppliedPCVS = new ArrayList<>();
166 this.clockDataTypes = new ArrayList<>();
167 this.receivers = new ArrayList<>();
168 this.satellites = new ArrayList<>();
169 this.clockData = new HashMap<>();
170 this.agencyName = "";
171 this.analysisCenterID = "";
172 this.analysisCenterName = "";
173 this.comments = "";
174 this.creationDate = null;
175 this.creationDateString = "";
176 this.creationTimeString = "";
177 this.creationTimeZoneString = "";
178 this.externalClockReference = "";
179 this.formatVersion = 0.0;
180 this.frameBuilder = frameBuilder;
181 this.frameName = "";
182 this.numberOfLeapSeconds = 0;
183 this.numberOfLeapSecondsGNSS = 0;
184 this.programName = "";
185 this.referenceClocks = new TimeSpanMap<>(null);
186 this.satelliteSystem = null;
187 this.stationIdentifier = "";
188 this.stationName = "";
189 this.timeScale = null;
190 this.timeSystem = null;
191 this.earliestEpoch = AbsoluteDate.FUTURE_INFINITY;
192 this.latestEpoch = AbsoluteDate.PAST_INFINITY;
193 }
194
195
196
197
198 public void addSatellite(final String satId) {
199
200 if (!satellites.contains(satId)) {
201 satellites.add(satId);
202 }
203 }
204
205
206
207
208 public void addReceiver(final Receiver receiver) {
209
210 boolean notInList = true;
211 for (Receiver rec : receivers) {
212 if (rec.designator.equals(receiver.designator)) {
213 notInList = false;
214 break;
215 }
216 }
217
218 if (notInList) {
219 receivers.add(receiver);
220 }
221 }
222
223
224
225
226 public int getNumberOfClockDataTypes() {
227 return clockDataTypes.size();
228 }
229
230
231
232
233 public int getTotalNumberOfDataLines() {
234 int result = 0;
235 final Map<String, List<ClockDataLine>> data = getClockData();
236 for (final Map.Entry<String, List<ClockDataLine>> entry : data.entrySet()) {
237 result += entry.getValue().size();
238 }
239 return result;
240 }
241
242
243
244
245
246 public int numberOfObsTypes(final SatelliteSystem system) {
247 if (systemObservationTypes.containsKey(system)) {
248 return systemObservationTypes.get(system).size();
249 } else {
250 return 0;
251 }
252 }
253
254
255
256
257 public int getNumberOfReceivers() {
258 return receivers.size();
259 }
260
261
262
263
264 public int getNumberOfSatellites() {
265 return satellites.size();
266 }
267
268
269
270
271 public double getFormatVersion() {
272 return formatVersion;
273 }
274
275
276
277
278 public void setFormatVersion(final double formatVersion) {
279 this.formatVersion = formatVersion;
280 }
281
282
283
284
285 public SatelliteSystem getSatelliteSystem() {
286 return satelliteSystem;
287 }
288
289
290
291
292 public void setSatelliteSystem(final SatelliteSystem satelliteSystem) {
293 this.satelliteSystem = satelliteSystem;
294 }
295
296
297
298
299 public String getProgramName() {
300 return programName;
301 }
302
303
304
305
306 public void setProgramName(final String programName) {
307 this.programName = programName;
308 }
309
310
311
312
313 public String getAgencyName() {
314 return agencyName;
315 }
316
317
318
319
320 public void setAgencyName(final String agencyName) {
321 this.agencyName = agencyName;
322 }
323
324
325
326
327 public String getCreationDateString() {
328 return creationDateString;
329 }
330
331
332
333
334 public void setCreationDateString(final String creationDateString) {
335 this.creationDateString = creationDateString;
336 }
337
338
339
340
341 public String getCreationTimeString() {
342 return creationTimeString;
343 }
344
345
346
347
348 public void setCreationTimeString(final String creationTimeString) {
349 this.creationTimeString = creationTimeString;
350 }
351
352
353
354
355 public String getCreationTimeZoneString() {
356 return creationTimeZoneString;
357 }
358
359
360
361
362 public void setCreationTimeZoneString(final String creationTimeZoneString) {
363 this.creationTimeZoneString = creationTimeZoneString;
364 }
365
366
367
368
369 public AbsoluteDate getCreationDate() {
370 return creationDate;
371 }
372
373
374
375
376 public void setCreationDate(final AbsoluteDate creationDate) {
377 this.creationDate = creationDate;
378 }
379
380
381
382
383 public String getComments() {
384 return comments;
385 }
386
387
388
389
390 public void addComment(final String comment) {
391 this.comments = comments.concat(comment + "\n");
392 }
393
394
395
396
397 public Map<SatelliteSystem, List<ObservationType>> getSystemObservationTypes() {
398 return Collections.unmodifiableMap(systemObservationTypes);
399 }
400
401
402
403
404
405 public void addSystemObservationType(final SatelliteSystem satSystem,
406 final ObservationType observationType) {
407 final List<ObservationType> list;
408 synchronized (systemObservationTypes) {
409 list = systemObservationTypes.computeIfAbsent(satSystem, s -> new ArrayList<>());
410 }
411 list.add(observationType);
412 }
413
414
415
416
417 public TimeSystem getTimeSystem() {
418 return timeSystem;
419 }
420
421
422
423
424 public void setTimeSystem(final TimeSystem timeSystem) {
425 this.timeSystem = timeSystem;
426 }
427
428
429
430
431 public TimeScale getTimeScale() {
432 return timeScale;
433 }
434
435
436
437
438 public void setTimeScale(final TimeScale timeScale) {
439 this.timeScale = timeScale;
440 }
441
442
443
444
445 public int getNumberOfLeapSeconds() {
446 return numberOfLeapSeconds;
447 }
448
449
450
451
452 public void setNumberOfLeapSeconds(final int numberOfLeapSeconds) {
453 this.numberOfLeapSeconds = numberOfLeapSeconds;
454 }
455
456
457
458
459 public int getNumberOfLeapSecondsGNSS() {
460 return numberOfLeapSecondsGNSS;
461 }
462
463
464
465
466 public void setNumberOfLeapSecondsGNSS(final int numberOfLeapSecondsGNSS) {
467 this.numberOfLeapSecondsGNSS = numberOfLeapSecondsGNSS;
468 }
469
470
471
472
473 public List<AppliedDCBS> getListAppliedDCBS() {
474 return Collections.unmodifiableList(listAppliedDCBS);
475 }
476
477
478
479
480 public void addAppliedDCBS(final AppliedDCBS appliedDCBS) {
481 listAppliedDCBS.add(appliedDCBS);
482 }
483
484
485
486
487 public List<AppliedPCVS> getListAppliedPCVS() {
488 return Collections.unmodifiableList(listAppliedPCVS);
489 }
490
491
492
493
494 public void addAppliedPCVS(final AppliedPCVS appliedPCVS) {
495 listAppliedPCVS.add(appliedPCVS);
496 }
497
498
499
500
501 public List<ClockDataType> getClockDataTypes() {
502 return Collections.unmodifiableList(clockDataTypes);
503 }
504
505
506
507
508 public void addClockDataType(final ClockDataType clockDataType) {
509 clockDataTypes.add(clockDataType);
510 }
511
512
513
514
515 public String getStationName() {
516 return stationName;
517 }
518
519
520
521
522 public void setStationName(final String stationName) {
523 this.stationName = stationName;
524 }
525
526
527
528
529 public String getStationIdentifier() {
530 return stationIdentifier;
531 }
532
533
534
535
536 public void setStationIdentifier(final String stationIdentifier) {
537 this.stationIdentifier = stationIdentifier;
538 }
539
540
541
542
543 public String getExternalClockReference() {
544 return externalClockReference;
545 }
546
547
548
549
550 public void setExternalClockReference(final String externalClockReference) {
551 this.externalClockReference = externalClockReference;
552 }
553
554
555
556
557 public String getAnalysisCenterID() {
558 return analysisCenterID;
559 }
560
561
562
563
564 public void setAnalysisCenterID(final String analysisCenterID) {
565 this.analysisCenterID = analysisCenterID;
566 }
567
568
569
570
571 public String getAnalysisCenterName() {
572 return analysisCenterName;
573 }
574
575
576
577
578 public void setAnalysisCenterName(final String analysisCenterName) {
579 this.analysisCenterName = analysisCenterName;
580 }
581
582
583
584
585 public TimeSpanMap<List<ReferenceClock>> getReferenceClocks() {
586 return referenceClocks;
587 }
588
589
590
591
592
593
594 public void addReferenceClockList(final List<ReferenceClock> referenceClockList,
595 final AbsoluteDate startDate) {
596 referenceClocks.addValidAfter(referenceClockList, startDate, false);
597 }
598
599
600
601
602 public String getFrameName() {
603 return frameName;
604 }
605
606
607
608
609
610 public void setFrameName(final String frameName) {
611 this.frameName = frameName;
612 }
613
614
615
616
617 public List<Receiver> getReceivers() {
618 return Collections.unmodifiableList(receivers);
619 }
620
621
622
623
624 public List<String> getSatellites() {
625 return Collections.unmodifiableList(satellites);
626 }
627
628
629
630
631 public Frame getFrame() {
632 return frameBuilder.apply(frameName);
633 }
634
635
636
637
638
639
640
641 public SampledClockModel extractClockModel(final String name,
642 final int nbInterpolationPoints) {
643 final List<ClockOffset> sample = new ArrayList<>();
644 clockData.
645 get(name).
646 forEach(c -> {
647 final double offset = c.clockBias;
648 final double rate = c.numberOfValues > 2 ? c.clockRate : Double.NaN;
649 final double acceleration = c.numberOfValues > 4 ? c.clockAcceleration : Double.NaN;
650 sample.add(new ClockOffset(c.getEpoch(), offset, rate, acceleration));
651 });
652 return new SampledClockModel(sample, nbInterpolationPoints);
653 }
654
655
656
657
658 public Map<String, List<ClockDataLine>> getClockData() {
659 return Collections.unmodifiableMap(clockData);
660 }
661
662
663
664
665
666
667 public void addClockData(final String id,
668 final ClockDataLine clockDataLine) {
669 final List<ClockDataLine> list;
670 synchronized (clockData) {
671 list = clockData.computeIfAbsent(id, i -> new ArrayList<>());
672 }
673 list.add(clockDataLine);
674 final AbsoluteDate epoch = clockDataLine.getEpoch();
675 if (epoch.isBefore(earliestEpoch)) {
676 earliestEpoch = epoch;
677 }
678 if (epoch.isAfter(latestEpoch)) {
679 latestEpoch = epoch;
680 }
681 }
682
683
684
685
686
687
688 public AbsoluteDate getEarliestEpoch() {
689 return earliestEpoch;
690 }
691
692
693
694
695
696
697 public AbsoluteDate getLatestEpoch() {
698 return latestEpoch;
699 }
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731 public static RinexClock splice(final Collection<RinexClock> clocks,
732 final double maxGap) {
733
734
735 final ChronologicalComparator comparator = new ChronologicalComparator();
736 final SortedSet<RinexClock> sorted =
737 new TreeSet<>((c1, c2) -> comparator.compare(c1.earliestEpoch, c2.earliestEpoch));
738 sorted.addAll(clocks);
739
740
741 final RinexClock first = sorted.first();
742 final RinexClock spliced = new RinexClock(first.frameBuilder);
743 spliced.setFormatVersion(first.getFormatVersion());
744 spliced.setSatelliteSystem(first.satelliteSystem);
745 spliced.setProgramName(first.getProgramName());
746 spliced.setAgencyName(first.getAgencyName());
747 spliced.setCreationDateString(first.getCreationDateString());
748 spliced.setCreationTimeString(first.getCreationTimeString());
749 spliced.setCreationTimeZoneString(first.getCreationTimeZoneString());
750 spliced.setCreationDate(first.getCreationDate());
751 spliced.addComment(first.getComments());
752 first.
753 getSystemObservationTypes().
754 forEach((s, l) -> l.forEach(o -> spliced.addSystemObservationType(s, o)));
755 spliced.setTimeSystem(first.getTimeSystem());
756 spliced.setTimeScale(first.getTimeScale());
757 spliced.setNumberOfLeapSeconds(first.getNumberOfLeapSeconds());
758 spliced.setNumberOfLeapSecondsGNSS(first.getNumberOfLeapSecondsGNSS());
759 first.getListAppliedDCBS().forEach(spliced::addAppliedDCBS);
760 first.getListAppliedPCVS().forEach(spliced::addAppliedPCVS);
761 first.getClockDataTypes().forEach(spliced::addClockDataType);
762 spliced.setStationName(first.getStationName());
763 spliced.setStationIdentifier(first.getStationIdentifier());
764 spliced.setExternalClockReference(first.getExternalClockReference());
765 spliced.setAnalysisCenterID(first.getAnalysisCenterID());
766 spliced.setAnalysisCenterName(first.getAnalysisCenterName());
767 spliced.setFrameName(first.getFrameName());
768
769
770 sorted.forEach(rc -> {
771 TimeSpanMap.Span<List<ReferenceClock>> span = rc.getReferenceClocks().getFirstSpan();
772 while (span != null) {
773 if (span.getData() != null) {
774 spliced.addReferenceClockList(span.getData(), span.getStart());
775 }
776 span = span.next();
777 }
778 });
779
780 final List<String> clockIds = new ArrayList<>();
781
782
783 first.
784 getReceivers().
785 stream().
786 filter(r -> availableInAllFiles(r.getDesignator(), sorted)).
787 forEach(r -> {
788 spliced.addReceiver(r);
789 clockIds.add(r.getDesignator());
790 });
791
792
793 first.
794 getSatellites().
795 stream().
796 filter(s -> availableInAllFiles(s, sorted)).
797 forEach(s -> {
798 spliced.addSatellite(s);
799 clockIds.add(s);
800 });
801
802
803 for (final String clockId : clockIds) {
804 ClockDataLine pending = null;
805 for (final RinexClock rc : sorted) {
806 for (final ClockDataLine cd : rc.getClockData().get(clockId)) {
807 if (pending != null) {
808 final double dt = cd.dateTimeComponents.offsetFrom(pending.dateTimeComponents);
809 if (dt > maxGap) {
810 throw new OrekitException(OrekitMessages.TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS, dt);
811 }
812
813 if (dt > 1.0e-6) {
814
815 spliced.addClockData(clockId, pending);
816 }
817
818 }
819
820
821 pending = cd;
822
823 }
824 }
825
826 if (pending != null) {
827
828 spliced.addClockData(clockId, pending);
829 }
830
831 }
832
833 return spliced;
834
835 }
836
837
838
839
840
841
842 private static boolean availableInAllFiles(final String clockId, final Collection<RinexClock> files) {
843 for (final RinexClock rc : files) {
844 if (!rc.getClockData().containsKey(clockId)) {
845 return false;
846 }
847 }
848 return true;
849 }
850
851
852
853
854
855
856 public class ClockDataLine {
857
858
859 private final ClockDataType dataType;
860
861
862 private final String name;
863
864
865 private final DateTimeComponents dateTimeComponents;
866
867
868
869
870 private final int numberOfValues;
871
872
873 private final double clockBias;
874
875
876 private final double clockBiasSigma;
877
878
879 private final double clockRate;
880
881
882 private final double clockRateSigma;
883
884
885 private final double clockAcceleration;
886
887
888 private final double clockAccelerationSigma;
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903 public ClockDataLine(final ClockDataType type, final String name,
904 final DateComponents dateComponents, final TimeComponents timeComponents,
905 final int numberOfValues,
906 final double clockBias, final double clockBiasSigma,
907 final double clockRate, final double clockRateSigma,
908 final double clockAcceleration, final double clockAccelerationSigma) {
909
910 this.dataType = type;
911 this.name = name;
912 this.dateTimeComponents = new DateTimeComponents(dateComponents, timeComponents);
913 this.numberOfValues = numberOfValues;
914 this.clockBias = clockBias;
915 this.clockBiasSigma = clockBiasSigma;
916 this.clockRate = clockRate;
917 this.clockRateSigma = clockRateSigma;
918 this.clockAcceleration = clockAcceleration;
919 this.clockAccelerationSigma = clockAccelerationSigma;
920 }
921
922
923
924
925 public ClockDataType getDataType() {
926 return dataType;
927 }
928
929
930
931
932 public String getName() {
933 return name;
934 }
935
936
937
938
939 public int getNumberOfValues() {
940 return numberOfValues;
941 }
942
943
944
945
946
947
948
949 public AbsoluteDate getEpoch() {
950 return new AbsoluteDate(dateTimeComponents, timeScale);
951 }
952
953
954
955
956
957
958
959 public AbsoluteDate getEpoch(final TimeScale epochTimeScale) {
960 return new AbsoluteDate(dateTimeComponents, epochTimeScale);
961 }
962
963
964
965
966 public double getClockBias() {
967 return clockBias;
968 }
969
970
971
972
973 public double getClockBiasSigma() {
974 return clockBiasSigma;
975 }
976
977
978
979
980 public double getClockRate() {
981 return clockRate;
982 }
983
984
985
986
987 public double getClockRateSigma() {
988 return clockRateSigma;
989 }
990
991
992
993
994 public double getClockAcceleration() {
995 return clockAcceleration;
996 }
997
998
999
1000
1001 public double getClockAccelerationSigma() {
1002 return clockAccelerationSigma;
1003 }
1004
1005 }
1006
1007
1008 public static class ReferenceClock {
1009
1010
1011 private final String referenceName;
1012
1013
1014 private final String clockID;
1015
1016
1017 private final double clockConstraint;
1018
1019
1020 private final AbsoluteDate startDate;
1021
1022
1023 private final AbsoluteDate endDate;
1024
1025
1026
1027
1028
1029
1030
1031
1032 public ReferenceClock (final String referenceName, final String clockID, final double clockConstraint,
1033 final AbsoluteDate startDate, final AbsoluteDate endDate) {
1034 this.referenceName = referenceName;
1035 this.clockID = clockID;
1036 this.clockConstraint = clockConstraint;
1037 this.startDate = startDate;
1038 this.endDate = endDate;
1039 }
1040
1041
1042
1043
1044 public String getReferenceName() {
1045 return referenceName;
1046 }
1047
1048
1049
1050
1051 public String getClockID() {
1052 return clockID;
1053 }
1054
1055
1056
1057
1058 public double getClockConstraint() {
1059 return clockConstraint;
1060 }
1061
1062
1063
1064
1065 public AbsoluteDate getStartDate() {
1066 return startDate;
1067 }
1068
1069
1070
1071
1072 public AbsoluteDate getEndDate() {
1073 return endDate;
1074 }
1075
1076 }
1077
1078
1079 public static class Receiver {
1080
1081
1082 private final String designator;
1083
1084
1085 private final String receiverIdentifier;
1086
1087
1088 private final double x;
1089
1090
1091 private final double y;
1092
1093
1094 private final double z;
1095
1096
1097
1098
1099
1100
1101
1102
1103 public Receiver(final String designator, final String receiverIdentifier,
1104 final double x, final double y, final double z) {
1105 this.designator = designator;
1106 this.receiverIdentifier = receiverIdentifier;
1107 this.x = x;
1108 this.y = y;
1109 this.z = z;
1110 }
1111
1112
1113
1114
1115 public String getDesignator() {
1116 return designator;
1117 }
1118
1119
1120
1121
1122 public String getReceiverIdentifier() {
1123 return receiverIdentifier;
1124 }
1125
1126
1127
1128
1129 public double getX() {
1130 return x;
1131 }
1132
1133
1134
1135
1136 public double getY() {
1137 return y;
1138 }
1139
1140
1141
1142
1143 public double getZ() {
1144 return z;
1145 }
1146 }
1147
1148
1149
1150
1151 public enum ClockDataType {
1152
1153
1154 AR("AR"),
1155
1156
1157 AS("AS"),
1158
1159
1160 CR("CR"),
1161
1162
1163 DR("DR"),
1164
1165
1166 MS("MS");
1167
1168
1169 private static final Map<String, ClockDataType> KEYS_MAP = new HashMap<>();
1170 static {
1171 for (final ClockDataType timeSystem : values()) {
1172 KEYS_MAP.put(timeSystem.getKey(), timeSystem);
1173 }
1174 }
1175
1176
1177 private final String key;
1178
1179
1180
1181
1182 ClockDataType(final String key) {
1183 this.key = key;
1184 }
1185
1186
1187
1188
1189 public String getKey() {
1190 return key;
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 public static ClockDataType parseClockDataType(final String s)
1202 throws OrekitIllegalArgumentException {
1203 final ClockDataType clockDataType = KEYS_MAP.get(s);
1204 if (clockDataType == null) {
1205 throw new OrekitIllegalArgumentException(OrekitMessages.UNKNOWN_CLOCK_DATA_TYPE, s);
1206 }
1207 return clockDataType;
1208 }
1209 }
1210 }