1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.files.ilrs;
18
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.regex.Pattern;
25
26 import org.hipparchus.exception.LocalizedCoreFormats;
27 import org.hipparchus.util.FastMath;
28 import org.orekit.annotation.DefaultDataContext;
29 import org.orekit.data.DataContext;
30 import org.orekit.data.DataSource;
31 import org.orekit.errors.OrekitException;
32 import org.orekit.errors.OrekitMessages;
33 import org.orekit.files.ilrs.CRD.AnglesMeasurement;
34 import org.orekit.files.ilrs.CRD.CRDDataBlock;
35 import org.orekit.files.ilrs.CRD.Calibration;
36 import org.orekit.files.ilrs.CRD.CalibrationDetail;
37 import org.orekit.files.ilrs.CRD.FrRangeMeasurement;
38 import org.orekit.files.ilrs.CRD.MeteorologicalMeasurement;
39 import org.orekit.files.ilrs.CRD.NptRangeMeasurement;
40 import org.orekit.files.ilrs.CRD.RangeMeasurement;
41 import org.orekit.files.ilrs.CRD.RangeSupplement;
42 import org.orekit.files.ilrs.CRD.SessionStatistics;
43 import org.orekit.files.ilrs.CRDConfiguration.CalibrationTargetConfiguration;
44 import org.orekit.files.ilrs.CRDConfiguration.DetectorConfiguration;
45 import org.orekit.files.ilrs.CRDConfiguration.LaserConfiguration;
46 import org.orekit.files.ilrs.CRDConfiguration.MeteorologicalConfiguration;
47 import org.orekit.files.ilrs.CRDConfiguration.SoftwareConfiguration;
48 import org.orekit.files.ilrs.CRDConfiguration.SystemConfiguration;
49 import org.orekit.files.ilrs.CRDConfiguration.TimingSystemConfiguration;
50 import org.orekit.files.ilrs.CRDConfiguration.TransponderConfiguration;
51 import org.orekit.time.AbsoluteDate;
52 import org.orekit.time.DateComponents;
53 import org.orekit.time.TimeComponents;
54 import org.orekit.time.TimeScale;
55 import org.orekit.utils.Constants;
56 import org.orekit.utils.units.Unit;
57 import org.orekit.utils.units.UnitsConverter;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public class CRDParser {
73
74
75 public static final String DEFAULT_CRD_SUPPORTED_NAMES = "^(?!0+$)\\w{1,12}\\_\\d{6,8}.\\w{3}$";
76
77
78 private static final Unit NM = Unit.parse("nm");
79
80
81 private static final Unit KHZ = Unit.parse("kHz");
82
83
84 private static final Unit US = Unit.parse("µs");
85
86
87 private static final Unit NS = Unit.parse("ns");
88
89
90 private static final Unit PS = Unit.parse("ps");
91
92
93 private static final UnitsConverter MBAR_TO_BAR = new UnitsConverter(Unit.parse("mbar"), Unit.parse("bar"));
94
95
96 private static final String FILE_FORMAT = "CRD";
97
98
99 private static final Pattern SEPARATOR = Pattern.compile("\\s+");
100
101
102 private static final Pattern COMMA = Pattern.compile(",");
103
104
105 private static final String COMMENTS_IDENTIFIER = "00";
106
107
108 private static final Pattern PATTERN_NA = Pattern.compile(" [-]?(na)");
109
110
111 private final TimeScale timeScale;
112
113
114
115
116
117
118 @DefaultDataContext
119 public CRDParser() {
120 this(DataContext.getDefault().getTimeScales().getUTC());
121 }
122
123
124
125
126
127 public CRDParser(final TimeScale utc) {
128 this.timeScale = utc;
129 }
130
131
132
133
134
135 public TimeScale getTimeScale() {
136 return timeScale;
137 }
138
139
140
141
142
143
144
145 public CRD parse(final DataSource source) throws IOException {
146
147
148 final ParseInfo pi = new ParseInfo();
149
150 int lineNumber = 0;
151 Iterable<LineParser> crdParsers = Collections.singleton(LineParser.H1);
152 try (BufferedReader reader = new BufferedReader(source.getOpener().openReaderOnce())) {
153 nextLine:
154 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
155 ++lineNumber;
156
157 if (line.startsWith(COMMENTS_IDENTIFIER)) {
158
159 crdParsers = Arrays.asList(LineParser.COMMENTS);
160 }
161
162 for (final LineParser candidate : crdParsers) {
163 if (candidate.canHandle(line)) {
164 try {
165
166
167
168
169
170 line = PATTERN_NA.matcher(line).replaceAll(" " + CRD.STR_NAN);
171
172 candidate.parse(line, pi);
173 if (pi.done) {
174
175 return pi.file;
176 }
177 crdParsers = candidate.allowedNext();
178 continue nextLine;
179 } catch (StringIndexOutOfBoundsException | NumberFormatException e) {
180 throw new OrekitException(e,
181 OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
182 lineNumber, source.getName(), line);
183 }
184 }
185 }
186 }
187
188
189 throw new OrekitException(OrekitMessages.CRD_UNEXPECTED_END_OF_FILE, lineNumber);
190
191 } catch (IOException ioe) {
192 throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
193 }
194
195 }
196
197
198
199
200
201
202
203
204 private static AbsoluteDate checkRollover(final AbsoluteDate epoch, final AbsoluteDate startEpoch) {
205
206
207
208 return epoch.durationFrom(startEpoch) < -36000 ? epoch.shiftedBy(Constants.JULIAN_DAY) : epoch;
209 }
210
211
212
213
214
215
216 private class ParseInfo {
217
218
219 private CRD file;
220
221
222 private int version;
223
224
225 private CRDDataBlock dataBlock;
226
227
228 private CRDHeader header;
229
230
231 private CRDConfiguration configurationRecords;
232
233
234 private TimeScale timeScale;
235
236
237 private DateComponents startEpochDateComponents;
238
239
240 private boolean done;
241
242
243
244
245 protected ParseInfo() {
246
247
248 this.done = false;
249 this.version = 1;
250 this.startEpochDateComponents = DateComponents.J2000_EPOCH;
251
252
253 this.file = new CRD();
254 this.header = new CRDHeader();
255 this.configurationRecords = new CRDConfiguration();
256 this.dataBlock = new CRDDataBlock();
257
258
259 this.timeScale = CRDParser.this.timeScale;
260
261 }
262
263 }
264
265
266 private enum LineParser {
267
268
269 H1("H1", "h1") {
270
271
272 @Override
273 public void parse(final String line, final ParseInfo pi) {
274
275
276 final String[] values = SEPARATOR.split(line);
277
278
279 final String format = values[1];
280 pi.version = Integer.parseInt(values[2]);
281
282
283 if (!format.equalsIgnoreCase(FILE_FORMAT)) {
284 throw new OrekitException(OrekitMessages.UNEXPECTED_FORMAT_FOR_ILRS_FILE, FILE_FORMAT, format);
285 }
286
287
288 pi.header.setFormat(format);
289 pi.header.setVersion(pi.version);
290
291
292 final int year = Integer.parseInt(values[3]);
293 final int month = Integer.parseInt(values[4]);
294 final int day = Integer.parseInt(values[5]);
295 pi.header.setProductionEpoch(new DateComponents(year, month, day));
296
297
298 pi.header.setProductionHour(Integer.parseInt(values[6]));
299
300 }
301
302
303 @Override
304 public Iterable<LineParser> allowedNext() {
305 return Arrays.asList(H2, COMMENTS);
306 }
307
308 },
309
310
311 H2("H2", "h2") {
312
313
314 @Override
315 public void parse(final String line, final ParseInfo pi) {
316
317
318 final String[] values = SEPARATOR.split(line);
319
320
321 pi.header.setStationName(values[1]);
322
323
324 pi.header.setSystemIdentifier(Integer.parseInt(values[2]));
325 pi.header.setSystemNumber(Integer.parseInt(values[3]));
326 pi.header.setSystemOccupancy(Integer.parseInt(values[4]));
327
328
329 pi.header.setEpochIdentifier(Integer.parseInt(values[5]));
330
331
332 if (pi.version == 2) {
333 pi.header.setStationNetword(values[6]);
334 } else {
335 pi.header.setStationNetword(CRD.STR_VALUE_NOT_AVAILABLE);
336 }
337
338 }
339
340
341 @Override
342 public Iterable<LineParser> allowedNext() {
343 return Arrays.asList(H3, C0, C1, C2, C3, C4, C5, C6, C7, COMMENTS);
344 }
345
346 },
347
348
349 H3("H3", "h3") {
350
351
352 @Override
353 public void parse(final String line, final ParseInfo pi) {
354
355
356 final String[] values = SEPARATOR.split(line);
357
358
359 pi.header.setName(values[1]);
360
361
362 pi.header.setIlrsSatelliteId(values[2]);
363 pi.header.setSic(values[3]);
364 pi.header.setNoradId(values[4]);
365
366
367 pi.header.setSpacecraftEpochTimeScale(Integer.parseInt(values[5]));
368
369
370 pi.header.setTargetClass(Integer.parseInt(values[6]));
371 if (pi.version == 2) {
372
373
374 pi.header.setTargetLocation(readIntegerWithNaN(values[7], -1));
375 }
376
377 }
378
379
380 @Override
381 public Iterable<LineParser> allowedNext() {
382 return Arrays.asList(H4, C0, C1, C2, C3, C4, C5, C6, C7, COMMENTS);
383 }
384
385 },
386
387
388 H4("H4", "h4") {
389
390
391 @Override
392 public void parse(final String line, final ParseInfo pi) {
393
394
395 final String[] values = SEPARATOR.split(line);
396
397
398 pi.header.setDataType(Integer.parseInt(values[1]));
399
400
401 final int yearS = Integer.parseInt(values[2]);
402 final int monthS = Integer.parseInt(values[3]);
403 final int dayS = Integer.parseInt(values[4]);
404 final int hourS = Integer.parseInt(values[5]);
405 final int minuteS = Integer.parseInt(values[6]);
406 final double secondS = Integer.parseInt(values[7]);
407
408 pi.startEpochDateComponents = new DateComponents(yearS, monthS, dayS);
409
410 pi.header.setStartEpoch(new AbsoluteDate(yearS, monthS, dayS,
411 hourS, minuteS, secondS,
412 pi.timeScale));
413
414
415
416
417 if (pi.version == 2 && values[8].equalsIgnoreCase("")) {
418 pi.header.setEndEpoch(null);
419 } else {
420 final int yearE = Integer.parseInt(values[8]);
421 final int monthE = Integer.parseInt(values[9]);
422 final int dayE = Integer.parseInt(values[10]);
423 final int hourE = Integer.parseInt(values[11]);
424 final int minuteE = Integer.parseInt(values[12]);
425 final double secondE = Integer.parseInt(values[13]);
426
427
428
429 if (monthE == -1) {
430 pi.header.setEndEpoch(null);
431 } else {
432 pi.header.setEndEpoch(new AbsoluteDate(yearE, monthE, dayE, hourE, minuteE, secondE, pi.timeScale));
433 }
434 }
435
436
437 pi.header.setDataReleaseFlag(Integer.parseInt(values[14]));
438
439
440 pi.header.setIsTroposphericRefractionApplied(readBoolean(values[15]));
441 pi.header.setIsCenterOfMassCorrectionApplied(readBoolean(values[16]));
442 pi.header.setIsReceiveAmplitudeCorrectionApplied(readBoolean(values[17]));
443 pi.header.setIsStationSystemDelayApplied(readBoolean(values[18]));
444 pi.header.setIsTransponderDelayApplied(readBoolean(values[19]));
445
446
447 pi.header.setRangeType(Integer.parseInt(values[20]));
448
449
450 pi.header.setQualityIndicator(Integer.parseInt(values[21]));
451
452 }
453
454
455 @Override
456 public Iterable<LineParser> allowedNext() {
457 return Arrays.asList(H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES,
458 CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
459 }
460
461 },
462
463
464 H5("H5", "h5") {
465
466
467 @Override
468 public void parse(final String line, final ParseInfo pi) {
469
470
471 final String[] values = SEPARATOR.split(line);
472
473
474 pi.header.setPredictionType(Integer.parseInt(values[1]));
475 pi.header.setYearOfCentury(Integer.parseInt(values[2]));
476 pi.header.setDateAndTime(values[3]);
477 pi.header.setPredictionProvider(values[4]);
478 pi.header.setSequenceNumber(Integer.parseInt(values[5]));
479
480 }
481
482
483 @Override
484 public Iterable<LineParser> allowedNext() {
485 return Arrays.asList(C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB,
486 CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
487 }
488
489 },
490
491
492 C0("C0", "c0") {
493
494
495 @Override
496 public void parse(final String line, final ParseInfo pi) {
497
498
499 final SystemConfiguration systemRecord = new SystemConfiguration();
500
501
502 final String[] values = SEPARATOR.split(line);
503
504
505 systemRecord.setWavelength(NM.toSI(Double.parseDouble(values[2])));
506
507
508 systemRecord.setSystemId(values[3]);
509
510
511 systemRecord.setComponents(Arrays.copyOfRange(values, 4, values.length));
512
513
514 pi.configurationRecords.addConfigurationRecord(systemRecord);
515
516 }
517
518
519 @Override
520 public Iterable<LineParser> allowedNext() {
521 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
522 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
523 }
524
525 },
526
527
528
529 C1("C1", "c1") {
530
531
532 @Override
533 public void parse(final String line, final ParseInfo pi) {
534
535
536 final LaserConfiguration laserRecord = new LaserConfiguration();
537
538
539 final String[] values = SEPARATOR.split(line);
540
541
542 laserRecord.setLaserId(values[2]);
543 laserRecord.setLaserType(values[3]);
544 laserRecord.setPrimaryWavelength(NM.toSI(Double.parseDouble(values[4])));
545 laserRecord.setNominalFireRate(Double.parseDouble(values[5]));
546 laserRecord.setPulseEnergy(Double.parseDouble(values[6]));
547 laserRecord.setPulseWidth(Double.parseDouble(values[7]));
548 laserRecord.setBeamDivergence(Double.parseDouble(values[8]));
549 laserRecord.setPulseInOutgoingSemiTrain(readIntegerWithNaN(values[9], 1));
550
551
552 pi.configurationRecords.addConfigurationRecord(laserRecord);
553
554 }
555
556
557 @Override
558 public Iterable<LineParser> allowedNext() {
559 return Arrays.asList(C2, C3, C4, C5, C6, C7, TEN, ELEVEN, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS);
560 }
561
562 },
563
564
565 C2("C2", "c2") {
566
567
568 @Override
569 public void parse(final String line, final ParseInfo pi) {
570
571
572 final DetectorConfiguration detectorRecord = new DetectorConfiguration();
573
574
575 final String[] values = SEPARATOR.split(line);
576
577
578 detectorRecord.setDetectorId(values[2]);
579 detectorRecord.setDetectorType(values[3]);
580 detectorRecord.setApplicableWavelength(NM.toSI(Double.parseDouble(values[4])));
581 detectorRecord.setQuantumEfficiency(Double.parseDouble(values[5]));
582 detectorRecord.setAppliedVoltage(Double.parseDouble(values[6]));
583 detectorRecord.setDarkCount(KHZ.toSI(Double.parseDouble(values[7])));
584 detectorRecord.setOutputPulseType(values[8]);
585 detectorRecord.setOutputPulseWidth(Double.parseDouble(values[9]));
586 detectorRecord.setSpectralFilter(NM.toSI(Double.parseDouble(values[10])));
587 detectorRecord.setTransmissionOfSpectralFilter(Double.parseDouble(values[11]));
588 detectorRecord.setSpatialFilter(Double.parseDouble(values[12]));
589 detectorRecord.setExternalSignalProcessing(values[13]);
590
591
592 if (pi.version == 2) {
593 detectorRecord.setAmplifierGain(Double.parseDouble(values[14]));
594 detectorRecord.setAmplifierBandwidth(KHZ.toSI(Double.parseDouble(values[15])));
595 detectorRecord.setAmplifierInUse(values[16]);
596 } else {
597 detectorRecord.setAmplifierGain(Double.NaN);
598 detectorRecord.setAmplifierBandwidth(Double.NaN);
599 detectorRecord.setAmplifierInUse(CRD.STR_VALUE_NOT_AVAILABLE);
600 }
601
602
603 pi.configurationRecords.addConfigurationRecord(detectorRecord);
604
605 }
606
607
608 @Override
609 public Iterable<LineParser> allowedNext() {
610 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
611 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
612 }
613
614 },
615
616
617 C3("C3", "c3") {
618
619
620 @Override
621 public void parse(final String line, final ParseInfo pi) {
622
623
624 final TimingSystemConfiguration timingRecord = new TimingSystemConfiguration();
625
626
627 final String[] values = SEPARATOR.split(line);
628
629
630 timingRecord.setLocalTimingId(values[2]);
631 timingRecord.setTimeSource(values[3]);
632 timingRecord.setFrequencySource(values[4]);
633 timingRecord.setTimer(values[5]);
634 final String timerSerialNumber = values[6];
635 if (CRD.STR_NAN.equalsIgnoreCase(timerSerialNumber)) {
636
637 timingRecord.setTimerSerialNumber(CRD.STR_VALUE_NOT_AVAILABLE);
638 } else {
639 timingRecord.setTimerSerialNumber(timerSerialNumber);
640 }
641 timingRecord.setEpochDelayCorrection(US.toSI(Double.parseDouble(values[7])));
642
643
644 pi.configurationRecords.addConfigurationRecord(timingRecord);
645
646 }
647
648
649 @Override
650 public Iterable<LineParser> allowedNext() {
651 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
652 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
653 }
654
655 },
656
657
658 C4("C4", "c4") {
659
660
661 @Override
662 public void parse(final String line, final ParseInfo pi) {
663
664
665 final TransponderConfiguration transponderRecord = new TransponderConfiguration();
666
667
668 final String[] values = SEPARATOR.split(line);
669
670
671 transponderRecord.setTransponderId(values[2]);
672 transponderRecord.setStationUTCOffset(NS.toSI(Double.parseDouble(values[3])));
673 transponderRecord.setStationOscDrift(Double.parseDouble(values[4]));
674 transponderRecord.setTranspUTCOffset(NS.toSI(Double.parseDouble(values[5])));
675 transponderRecord.setTranspOscDrift(Double.parseDouble(values[6]));
676
677
678 transponderRecord.setTranspClkRefTime(Double.parseDouble(values[7]));
679
680
681 transponderRecord.setStationClockAndDriftApplied(Integer.parseInt(values[8]));
682 transponderRecord.setSpacecraftClockAndDriftApplied(Integer.parseInt(values[9]));
683
684
685 transponderRecord.setIsSpacecraftTimeSimplified(readBoolean(values[10]));
686
687
688 pi.configurationRecords.addConfigurationRecord(transponderRecord);
689
690 }
691
692
693 @Override
694 public Iterable<LineParser> allowedNext() {
695 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
696 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
697 }
698
699 },
700
701
702 C5("C5", "c5") {
703
704
705 @Override
706 public void parse(final String line, final ParseInfo pi) {
707
708
709 final SoftwareConfiguration softwareRecord = new SoftwareConfiguration();
710
711
712 final String[] values = SEPARATOR.split(line);
713
714
715 softwareRecord.setSoftwareId(values[2]);
716 softwareRecord.setTrackingSoftwares(COMMA.split(values[3]));
717 softwareRecord.setTrackingSoftwareVersions(COMMA.split(values[4]));
718 softwareRecord.setProcessingSoftwares(COMMA.split(values[5]));
719 softwareRecord.setProcessingSoftwareVersions(COMMA.split(values[6]));
720
721
722 pi.configurationRecords.addConfigurationRecord(softwareRecord);
723
724 }
725
726
727 @Override
728 public Iterable<LineParser> allowedNext() {
729 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
730 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
731 }
732
733 },
734
735
736 C6("C6", "c6") {
737
738
739 @Override
740 public void parse(final String line, final ParseInfo pi) {
741
742
743 final MeteorologicalConfiguration meteoRecord = new MeteorologicalConfiguration();
744
745
746 final String[] values = SEPARATOR.split(line);
747
748
749 meteoRecord.setMeteorologicalId(values[2]);
750 meteoRecord.setPressSensorManufacturer(values[3]);
751 meteoRecord.setPressSensorModel(values[4]);
752 meteoRecord.setPressSensorSerialNumber(values[5]);
753 meteoRecord.setTempSensorManufacturer(values[6]);
754 meteoRecord.setTempSensorModel(values[7]);
755 meteoRecord.setTempSensorSerialNumber(values[8]);
756 meteoRecord.setHumiSensorManufacturer(values[9]);
757 meteoRecord.setHumiSensorModel(values[10]);
758 meteoRecord.setHumiSensorSerialNumber(values[11]);
759
760
761 pi.configurationRecords.addConfigurationRecord(meteoRecord);
762
763 }
764
765
766 @Override
767 public Iterable<LineParser> allowedNext() {
768 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
769 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
770 }
771
772 },
773
774
775 C7("C7", "c7") {
776
777
778 @Override
779 public void parse(final String line, final ParseInfo pi) {
780
781
782 final CalibrationTargetConfiguration calibRecord = new CalibrationTargetConfiguration();
783
784
785 final String[] values = SEPARATOR.split(line);
786
787
788 calibRecord.setConfigurationId(values[2]);
789 calibRecord.setTargetName(values[3]);
790 calibRecord.setSurveyedTargetDistance(Double.parseDouble(values[4]));
791 calibRecord.setSurveyError(Double.parseDouble(values[5]) * 1e-3);
792 calibRecord.setSumOfAllConstantDelays(Double.parseDouble(values[6]));
793 calibRecord.setPulseEnergy(Double.parseDouble(values[7]));
794 calibRecord.setProcessingSoftwareName(values[8]);
795 calibRecord.setProcessingSoftwareVersion(values[9]);
796
797
798 pi.configurationRecords.addConfigurationRecord(calibRecord);
799 }
800
801
802 @Override
803 public Iterable<LineParser> allowedNext() {
804 return Arrays.asList(H3, H4, H5, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP,
805 ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
806 }
807
808 },
809
810
811 TEN("10") {
812
813
814 @Override
815 public void parse(final String line, final ParseInfo pi) {
816
817
818 final String[] values = SEPARATOR.split(line);
819
820
821 final double secOfDay = Double.parseDouble(values[1]);
822 final double timeOfFlight = Double.parseDouble(values[2]);
823 final String systemConfigId = values[3];
824 final int epochEvent = Integer.parseInt(values[4]);
825 final int filterFlag = Integer.parseInt(values[5]);
826 final int detectorChannel = Integer.parseInt(values[6]);
827 final int stopNumber = Integer.parseInt(values[7]);
828 final int receiveAmplitude = readIntegerWithNaN(values[8], -1);
829
830 int transmitAmplitude = -1;
831 if (pi.version == 2) {
832 transmitAmplitude = readIntegerWithNaN(values[9], -1);
833 }
834
835
836 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
837
838 epoch = checkRollover(epoch, pi.header.getStartEpoch());
839 final RangeMeasurement range = new FrRangeMeasurement(epoch, timeOfFlight, epochEvent, systemConfigId,
840 filterFlag, detectorChannel, stopNumber, receiveAmplitude, transmitAmplitude);
841 pi.dataBlock.addRangeData(range);
842
843 }
844
845
846 @Override
847 public Iterable<LineParser> allowedNext() {
848 return Arrays.asList(H8, TEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT,
849 COMPATIBILITY, COMMENTS, CUSTOM);
850 }
851
852 },
853
854
855 ELEVEN("11") {
856
857
858 @Override
859 public void parse(final String line, final ParseInfo pi) {
860
861
862 final String[] values = SEPARATOR.split(line);
863
864
865 final double secOfDay = Double.parseDouble(values[1]);
866 final double timeOfFlight = Double.parseDouble(values[2]);
867 final String systemConfigId = values[3];
868 final int epochEvent = Integer.parseInt(values[4]);
869 final double windowLength = Double.parseDouble(values[5]);
870 final int numberOfRawRanges = Integer.parseInt(values[6]);
871 final double binRms = PS.toSI(Double.parseDouble(values[7]));
872 final double binSkew = Double.parseDouble(values[8]);
873 final double binKurtosis = Double.parseDouble(values[9]);
874 final double binPeakMinusMean = PS.toSI(Double.parseDouble(values[10]));
875 final double returnRate = Double.parseDouble(values[11]);
876 final int detectorChannel = Integer.parseInt(values[12]);
877
878 double snr = Double.NaN;
879 if (pi.version == 2) {
880 snr = Double.parseDouble(values[13]);
881 }
882
883
884 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
885
886 epoch = checkRollover(epoch, pi.header.getStartEpoch());
887 final RangeMeasurement range = new NptRangeMeasurement(epoch, timeOfFlight, epochEvent, snr,
888 systemConfigId, windowLength, numberOfRawRanges, binRms, binSkew, binKurtosis, binPeakMinusMean,
889 returnRate, detectorChannel);
890 pi.dataBlock.addRangeData(range);
891
892 }
893
894
895 @Override
896 public Iterable<LineParser> allowedNext() {
897 return Arrays.asList(H8, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT,
898 COMPATIBILITY, COMMENTS, CUSTOM);
899 }
900
901 },
902
903
904 TWELVE("12") {
905
906
907 @Override
908 public void parse(final String line, final ParseInfo pi) {
909
910
911 final String[] values = SEPARATOR.split(line);
912
913
914 final double secOfDay = Double.parseDouble(values[1]);
915 final String systemConfigId = values[2];
916 final double troposphericRefractionCorr = PS.toSI(Double.parseDouble(values[3]));
917 final double centerOfMassCorr = Double.parseDouble(values[4]);
918 final double ndFilterValue = Double.parseDouble(values[5]);
919 final double timeBiasApplied = Double.parseDouble(values[6]);
920
921 double rangeRate = Double.NaN;
922 if (pi.version == 2) {
923 rangeRate = Double.parseDouble(values[7]);
924 }
925
926
927 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
928
929 epoch = checkRollover(epoch, pi.header.getStartEpoch());
930 final RangeSupplement rangeSup = new RangeSupplement(epoch, systemConfigId, troposphericRefractionCorr,
931 centerOfMassCorr, ndFilterValue, timeBiasApplied, rangeRate);
932 pi.dataBlock.addRangeSupplementData(rangeSup);
933
934 }
935
936
937 @Override
938 public Iterable<LineParser> allowedNext() {
939 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, ANGLES, CALIB, STAT, COMPATIBILITY, COMMENTS);
940 }
941
942 },
943
944
945 METEO("20") {
946
947
948 @Override
949 public void parse(final String line, final ParseInfo pi) {
950
951
952 final String[] values = SEPARATOR.split(line);
953
954
955 final double secOfDay = Double.parseDouble(values[1]);
956 final double pressure = MBAR_TO_BAR.convert(Double.parseDouble(values[2]));
957 final double temperature = Double.parseDouble(values[3]);
958 final double humidity = Double.parseDouble(values[4]);
959 final int originOfValues = Integer.parseInt(values[5]);
960
961
962 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
963
964 epoch = checkRollover(epoch, pi.header.getStartEpoch());
965 final MeteorologicalMeasurement meteo = new MeteorologicalMeasurement(epoch, pressure, temperature,
966 humidity, originOfValues);
967 pi.dataBlock.addMeteoData(meteo);
968
969 }
970
971
972 @Override
973 public Iterable<LineParser> allowedNext() {
974 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
975 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
976 }
977
978 },
979
980
981 METEO_SUPP("21") {
982
983
984 @Override
985 public void parse(final String line, final ParseInfo pi) {
986
987 }
988
989
990 @Override
991 public Iterable<LineParser> allowedNext() {
992 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
993 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
994 }
995
996 },
997
998
999 ANGLES("30") {
1000
1001
1002 @Override
1003 public void parse(final String line, final ParseInfo pi) {
1004
1005
1006 final String[] values = SEPARATOR.split(line);
1007
1008
1009 final double secOfDay = Double.parseDouble(values[1]);
1010 final double azmiuth = FastMath.toRadians(Double.parseDouble(values[2]));
1011 final double elevation = FastMath.toRadians(Double.parseDouble(values[3]));
1012 final int directionFlag = Integer.parseInt(values[4]);
1013 final int orginFlag = Integer.parseInt(values[5]);
1014 final boolean isRefractionCorrected = readBoolean(values[6]);
1015
1016
1017
1018 double azimuthRate = Double.NaN;
1019 double elevationRate = Double.NaN;
1020 if (pi.version == 2) {
1021
1022 azimuthRate = FastMath.toRadians(Double.parseDouble(values[7]));
1023 elevationRate = FastMath.toRadians(Double.parseDouble(values[8]));
1024 }
1025
1026
1027 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
1028
1029 epoch = checkRollover(epoch, pi.header.getStartEpoch());
1030 final AnglesMeasurement angles = new AnglesMeasurement(epoch, azmiuth, elevation,
1031 directionFlag, orginFlag,
1032 isRefractionCorrected,
1033 azimuthRate, elevationRate);
1034 pi.dataBlock.addAnglesData(angles);
1035
1036 }
1037
1038
1039 @Override
1040 public Iterable<LineParser> allowedNext() {
1041 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1042 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1043 }
1044
1045 },
1046
1047
1048 CALIB("40") {
1049
1050
1051 @Override
1052 public void parse(final String line, final ParseInfo pi) {
1053
1054
1055 final String[] values = SEPARATOR.split(line);
1056
1057
1058 final double secOfDay = Double.parseDouble(values[1]);
1059 final int typeOfData = Integer.parseInt(values[2]);
1060 final String systemConfigId = values[3];
1061 final int numberOfPointsRecorded = readIntegerWithNaN(values[4], -1);
1062 final int numberOfPointsUsed = readIntegerWithNaN(values[5], -1);
1063 final double oneWayDistance = Double.parseDouble(values[6]);
1064 final double systemDelay = PS.toSI(Double.parseDouble(values[7]));
1065 final double delayShift = PS.toSI(Double.parseDouble(values[8]));
1066 final double rms = PS.toSI(Double.parseDouble(values[9]));
1067 final double skew = Double.parseDouble(values[10]);
1068 final double kurtosis = Double.parseDouble(values[11]);
1069 final double peakMinusMean = PS.toSI(Double.parseDouble(values[12]));
1070 final int typeIndicator = Integer.parseInt(values[13]);
1071 final int shiftTypeIndicator = Integer.parseInt(values[14]);
1072 final int detectorChannel = Integer.parseInt(values[15]);
1073
1074
1075 int span = 0;
1076 double returnRate = Double.NaN;
1077 if (pi.version == 2) {
1078
1079
1080 span = readIntegerWithNaN(values[16], -1);
1081 returnRate = Double.parseDouble(values[17]);
1082 }
1083
1084
1085 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
1086
1087 epoch = checkRollover(epoch, pi.header.getStartEpoch());
1088 final Calibration cal = new Calibration(epoch, typeOfData, systemConfigId, numberOfPointsRecorded,
1089 numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew, kurtosis, peakMinusMean,
1090 typeIndicator, shiftTypeIndicator, detectorChannel, span, returnRate);
1091 pi.dataBlock.addCalibrationData(cal);
1092
1093 }
1094
1095
1096 @Override
1097 public Iterable<LineParser> allowedNext() {
1098 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1099 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1100 }
1101
1102 },
1103
1104
1105 CALIB_DETAILS("41") {
1106
1107
1108 @Override
1109 public void parse(final String line, final ParseInfo pi) {
1110
1111
1112 final String[] values = SEPARATOR.split(line);
1113
1114
1115 final double secOfDay = Double.parseDouble(values[1]);
1116 final int typeOfData = Integer.parseInt(values[2]);
1117 final String systemConfigId = values[3];
1118 final int numberOfPointsRecorded = readIntegerWithNaN(values[4], -1);
1119 final int numberOfPointsUsed = readIntegerWithNaN(values[5], -1);
1120 final double oneWayDistance = Double.parseDouble(values[6]);
1121 final double systemDelay = PS.toSI(Double.parseDouble(values[7]));
1122 final double delayShift = PS.toSI(Double.parseDouble(values[8]));
1123 final double rms = PS.toSI(Double.parseDouble(values[9]));
1124 final double skew = Double.parseDouble(values[10]);
1125 final double kurtosis = Double.parseDouble(values[11]);
1126 final double peakMinusMean = PS.toSI(Double.parseDouble(values[12]));
1127 final int typeIndicator = Integer.parseInt(values[13]);
1128 final int shiftTypeIndicator = Integer.parseInt(values[14]);
1129 final int detectorChannel = Integer.parseInt(values[15]);
1130
1131
1132 int span = 0;
1133 double returnRate = Double.NaN;
1134 if (pi.version == 2) {
1135 span = Integer.parseInt(values[16]);
1136 returnRate = Double.parseDouble(values[17]);
1137 }
1138
1139
1140 AbsoluteDate epoch = new AbsoluteDate(pi.startEpochDateComponents, new TimeComponents(secOfDay), pi.timeScale);
1141
1142 epoch = checkRollover(epoch, pi.header.getStartEpoch());
1143 final CalibrationDetail cal = new CalibrationDetail(epoch, typeOfData, systemConfigId,
1144 numberOfPointsRecorded, numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew,
1145 kurtosis, peakMinusMean, typeIndicator, shiftTypeIndicator, detectorChannel, span, returnRate);
1146 pi.dataBlock.addCalibrationDetailData(cal);
1147
1148 }
1149
1150
1151 @Override
1152 public Iterable<LineParser> allowedNext() {
1153 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1154 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1155 }
1156
1157 },
1158
1159
1160 CALIB_SHOT("42") {
1161
1162
1163 @Override
1164 public void parse(final String line, final ParseInfo pi) {
1165
1166 }
1167
1168
1169 @Override
1170 public Iterable<LineParser> allowedNext() {
1171 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1172 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1173 }
1174
1175 },
1176
1177
1178 STAT("50") {
1179
1180
1181 @Override
1182 public void parse(final String line, final ParseInfo pi) {
1183
1184
1185 final String[] values = SEPARATOR.split(line);
1186
1187
1188 final String systemConfigId = values[1];
1189 final double rms = PS.toSI(Double.parseDouble(values[2]));
1190 final double skewness = Double.parseDouble(values[3]);
1191 final double kurtosis = Double.parseDouble(values[4]);
1192
1193
1194
1195 final double peakMinusMean = values[5].contains("*") ? Double.NaN : PS.toSI(Double.parseDouble(values[5]));
1196
1197 final int dataQualityIndicator = Integer.parseInt(values[6]);
1198
1199 final SessionStatistics stat = new SessionStatistics(systemConfigId, rms, skewness, kurtosis, peakMinusMean,
1200 dataQualityIndicator);
1201 pi.dataBlock.addSessionStatisticsData(stat);
1202
1203 }
1204
1205
1206 @Override
1207 public Iterable<LineParser> allowedNext() {
1208 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1209 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1210 }
1211
1212 },
1213
1214
1215 COMPATIBILITY("60") {
1216
1217
1218 @Override
1219 public void parse(final String line, final ParseInfo pi) {
1220
1221 }
1222
1223
1224 @Override
1225 public Iterable<LineParser> allowedNext() {
1226 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1227 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1228 }
1229
1230 },
1231
1232
1233 COMMENTS(COMMENTS_IDENTIFIER) {
1234
1235
1236 @Override
1237 public void parse(final String line, final ParseInfo pi) {
1238
1239
1240 final String comment = line.substring(2).trim();
1241 pi.file.getComments().add(comment);
1242
1243 }
1244
1245
1246 @Override
1247 public Iterable<LineParser> allowedNext() {
1248 return Arrays.asList(H1, H2, H3, H4, H5, H8, H9, C0, C1, C2, C3, C4, C5, C6, C7, TEN, ELEVEN, TWELVE, METEO,
1249 METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT, STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1250
1251 }
1252
1253 },
1254
1255
1256 CUSTOM("9\\d") {
1257
1258
1259 @Override
1260 public void parse(final String line, final ParseInfo pi) {
1261
1262 }
1263
1264
1265 public Iterable<LineParser> allowedNext() {
1266 return Arrays.asList(H8, TEN, ELEVEN, TWELVE, METEO, METEO_SUPP, ANGLES, CALIB, CALIB_DETAILS, CALIB_SHOT,
1267 STAT, COMPATIBILITY, COMMENTS, CUSTOM);
1268
1269 }
1270
1271 },
1272
1273
1274 H8("H8", "h8") {
1275
1276
1277 @Override
1278 public void parse(final String line, final ParseInfo pi) {
1279
1280
1281
1282
1283 if (pi.header.getEndEpoch() == null) {
1284 final List<RangeMeasurement> rangeData = pi.dataBlock.getRangeData();
1285 pi.header.setEndEpoch(rangeData.get(rangeData.size() - 1).getDate());
1286 }
1287
1288
1289 pi.dataBlock.setHeader(pi.header);
1290 pi.dataBlock.setConfigurationRecords(pi.configurationRecords);
1291
1292
1293 pi.file.addDataBlock(pi.dataBlock);
1294
1295
1296 pi.startEpochDateComponents = DateComponents.J2000_EPOCH;
1297 final CRDHeader lastHeader = pi.header;
1298 pi.header = new CRDHeader();
1299 pi.configurationRecords = new CRDConfiguration();
1300 pi.dataBlock = new CRDDataBlock();
1301
1302
1303
1304 pi.header.setFormat(lastHeader.getFormat());
1305 pi.header.setVersion(lastHeader.getVersion());
1306 pi.header.setProductionEpoch(lastHeader.getProductionEpoch());
1307 pi.header.setProductionHour(lastHeader.getProductionHour());
1308
1309 pi.header.setStationName(lastHeader.getStationName());
1310 pi.header.setSystemIdentifier(lastHeader.getSystemIdentifier());
1311 pi.header.setSystemNumber(lastHeader.getSystemNumber());
1312 pi.header.setSystemOccupancy(lastHeader.getSystemOccupancy());
1313 pi.header.setEpochIdentifier(lastHeader.getEpochIdentifier());
1314 pi.header.setStationNetword(lastHeader.getStationNetword());
1315
1316 pi.header.setName(lastHeader.getName());
1317 pi.header.setIlrsSatelliteId(lastHeader.getIlrsSatelliteId());
1318 pi.header.setSic(lastHeader.getSic());
1319 pi.header.setNoradId(lastHeader.getNoradId());
1320 pi.header.setSpacecraftEpochTimeScale(lastHeader.getSpacecraftEpochTimeScale());
1321 pi.header.setTargetClass(lastHeader.getTargetClass());
1322 pi.header.setTargetLocation(lastHeader.getTargetLocation());
1323
1324 }
1325
1326
1327 @Override
1328 public Iterable<LineParser> allowedNext() {
1329 return Arrays.asList(H1, H4, H9, COMMENTS);
1330 }
1331
1332 },
1333
1334
1335 H9("H9", "h9") {
1336
1337
1338 @Override
1339 public void parse(final String line, final ParseInfo pi) {
1340 pi.done = true;
1341 }
1342
1343
1344 @Override
1345 public Iterable<LineParser> allowedNext() {
1346 return Collections.singleton(H9);
1347 }
1348
1349 };
1350
1351
1352 private final Pattern[] patterns;
1353
1354
1355 private final String[] identifiers;
1356
1357
1358
1359
1360 LineParser(final String... identifier) {
1361 this.identifiers = identifier;
1362
1363 this.patterns = new Pattern[identifiers.length];
1364 for (int index = 0; index < patterns.length; index++) {
1365 patterns[index] = Pattern.compile(identifiers[index]);
1366 }
1367 }
1368
1369
1370
1371
1372
1373 public abstract void parse(String line, ParseInfo pi);
1374
1375
1376
1377
1378 public abstract Iterable<LineParser> allowedNext();
1379
1380
1381
1382
1383
1384 public boolean canHandle(final String line) {
1385
1386 final String lineId = SEPARATOR.split(line)[0];
1387
1388 for (Pattern pattern : patterns) {
1389 if (pattern.matcher(lineId).matches()) {
1390 return true;
1391 }
1392 }
1393
1394 return false;
1395 }
1396
1397
1398
1399
1400
1401
1402 private static boolean readBoolean(final String value) {
1403 return Integer.parseInt(value) == 1;
1404 }
1405
1406
1407
1408
1409
1410
1411
1412
1413 private static int readIntegerWithNaN(final String value, final int defaultValue) {
1414 return CRD.STR_NAN.equalsIgnoreCase(value) ? defaultValue : Integer.parseInt(value);
1415 }
1416 }
1417
1418 }