1 /* Copyright 2002-2026 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.ilrs;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.SortedSet;
24 import java.util.TreeSet;
25 import java.util.regex.Pattern;
26
27 import org.hipparchus.util.FastMath;
28 import org.orekit.annotation.DefaultDataContext;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.ChronologicalComparator;
31 import org.orekit.time.TimeScalesFactory;
32 import org.orekit.time.TimeStamped;
33 import org.orekit.utils.ImmutableTimeStampedCache;
34
35 /**
36 * This class stores all the information of the Consolidated laser ranging Data Format (CRD) parsed
37 * by CRDParser. It contains the header and a list of data records.
38 * @author Bryan Cazabonne
39 * @author Rongwang Li
40 * @since 10.3
41 */
42 public class CRD {
43
44 /** Value of 'not available' or 'not applicable' or 'no information'. */
45 public static final String STR_VALUE_NOT_AVAILABLE = "na";
46
47 /** String of "NaN". */
48 public static final String STR_NAN = "NaN";
49
50 /** Pattern of "NaN". */
51 public static final Pattern PATTERN_NAN = Pattern.compile(STR_NAN);
52
53 /** List of comments contained in the file. */
54 private final List<String> comments;
55
56 /** List of data blocks contain in the CDR file. */
57 private final List<CRDDataBlock> dataBlocks;
58
59 /**
60 * Constructor.
61 */
62 public CRD() {
63 // Initialise empty lists
64 this.comments = new ArrayList<>();
65 this.dataBlocks = new ArrayList<>();
66 }
67
68 /**
69 * Format the integer value as a string, or the string <code>VALUE_NOT_AVAILABLE</code>.
70 * @param value the value
71 * @param valueNotAvailable the value means not available
72 * @return a string
73 * @since 12.0
74 */
75 public static String formatIntegerOrNaN(final int value, final int valueNotAvailable) {
76 return value == valueNotAvailable ? STR_VALUE_NOT_AVAILABLE : String.format(Locale.US, "%d", value);
77 }
78
79 /**
80 * Replace all " NaN" with " na".
81 * @param crdString the original string
82 * @return the string
83 * @since 12.0
84 */
85 public static String handleNaN(final String crdString) {
86 return PATTERN_NAN.matcher(crdString).replaceAll(STR_VALUE_NOT_AVAILABLE);
87 }
88
89 /**
90 * Add a data block to the current list of data blocks.
91 * @param dataBlock data block to add
92 */
93 public void addDataBlock(final CRDDataBlock dataBlock) {
94 dataBlocks.add(dataBlock);
95 }
96
97 /**
98 * Get the comments contained in the file.
99 * @return the comments contained in the file
100 */
101 public List<String> getComments() {
102 return comments;
103 }
104
105 /**
106 * Get the data blocks contain in the file.
107 * @return the data blocks contain in the file
108 */
109 public List<CRDDataBlock> getDataBlocks() {
110 return Collections.unmodifiableList(dataBlocks);
111 }
112
113 /**
114 * Data block containing a set of data contain in the CRD file.
115 * <p>
116 * A data block consists of a header, configuration data and
117 * recorded data (range, angles, meteorological, etc.).
118 * </p>
119 */
120 public static class CRDDataBlock {
121
122 /** Data block header. */
123 private CRDHeader header;
124
125 /** Configuration record. */
126 private CRDConfiguration configurationRecords;
127
128 /** Range records. */
129 private final List<RangeMeasurement> rangeData;
130
131 /** Meteorological records. */
132 private final SortedSet<MeteorologicalMeasurement> meteoData;
133
134 /** Pointing angles records. */
135 private final List<AnglesMeasurement> anglesData;
136
137 /** RangeSupplement records. */
138 private final List<RangeSupplement> rangeSupplementData;
139
140 /** Session statistics record(s). */
141 private final List<SessionStatistics> sessionStatisticsData;
142
143 /** Calibration Record(s). */
144 private final List<Calibration> calibrationData;
145
146 /** Calibration detail record(s). */
147 private final List<CalibrationDetail> calibrationDetailData;
148
149 /**
150 * Constructor.
151 */
152 public CRDDataBlock() {
153 // Initialise empty lists
154 this.rangeData = new ArrayList<>();
155 this.meteoData = new TreeSet<>(new ChronologicalComparator());
156 this.anglesData = new ArrayList<>();
157 this.rangeSupplementData = new ArrayList<>();
158 this.sessionStatisticsData = new ArrayList<>();
159 this.calibrationData = new ArrayList<>();
160 this.calibrationDetailData = new ArrayList<>();
161 }
162
163 /**
164 * Get the header of the current data block.
165 * @return the header of the current data block
166 */
167 public CRDHeader getHeader() {
168 return header;
169 }
170
171 /**
172 * Set the header for the current data block.
173 * @param header the header to set
174 */
175 public void setHeader(final CRDHeader header) {
176 this.header = header;
177 }
178
179 /**
180 * Get the system configuration records.
181 * @return the system configuration records
182 */
183 public CRDConfiguration getConfigurationRecords() {
184 return configurationRecords;
185 }
186
187 /**
188 * Set the configuration records for the current data block.
189 * @param configurationRecords the configuration records to set
190 */
191 public void setConfigurationRecords(final CRDConfiguration configurationRecords) {
192 this.configurationRecords = configurationRecords;
193 }
194
195 /**
196 * Add an entry to the list of range data.
197 * @param range entry to add
198 */
199 public void addRangeData(final RangeMeasurement range) {
200 rangeData.add(range);
201 }
202
203 /**
204 * Add an entry to the list of meteorological data.
205 * @param meteorologicalMeasurement entry to add
206 */
207 public void addMeteoData(final MeteorologicalMeasurement meteorologicalMeasurement) {
208 meteoData.add(meteorologicalMeasurement);
209 }
210
211 /**
212 * Add an entry to the list of angles data.
213 * @param angles entry to add
214 */
215 public void addAnglesData(final AnglesMeasurement angles) {
216 anglesData.add(angles);
217 }
218
219 /**
220 * Get the range data for the data block.
221 * @return an unmodifiable list of range data
222 */
223 public List<RangeMeasurement> getRangeData() {
224 return Collections.unmodifiableList(rangeData);
225 }
226
227 /**
228 * Get the angles data for the data block.
229 * @return an unmodifiable list of angles data
230 */
231 public List<AnglesMeasurement> getAnglesData() {
232 return Collections.unmodifiableList(anglesData);
233 }
234
235 /**
236 * Get the meteorological data for the data block.
237 * @return an unmodifiable list of meteorological data
238 */
239 public Meteo getMeteoData() {
240 return new Meteo(meteoData);
241 }
242
243 /**
244 * Add an entry to the list of range supplement data.
245 * @param rangeSupplement entry to add
246 * @since 12.0
247 */
248 public void addRangeSupplementData(final RangeSupplement rangeSupplement) {
249 rangeSupplementData.add(rangeSupplement);
250 }
251
252 /**
253 * Get the range supplement data for the data block.
254 * @return an unmodifiable list of range supplement data
255 * @since 12.0
256 */
257 public List<RangeSupplement> getRangeSupplementData() {
258 return Collections.unmodifiableList(rangeSupplementData);
259 }
260
261 /**
262 * Add an entry to the list of session statistics data.
263 * @param sessionStatistics entry to add
264 * @since 12.0
265 */
266 public void addSessionStatisticsData(final SessionStatistics sessionStatistics) {
267 sessionStatisticsData.add(sessionStatistics);
268 }
269
270 /**
271 * Get the session statistics data for the data block.
272 * @return an unmodifiable list of session statistics data
273 * @since 12.0
274 */
275 public List<SessionStatistics> getSessionStatisticsData() {
276 return Collections.unmodifiableList(sessionStatisticsData);
277 }
278
279 /**
280 * Get the default (the first if there are many records) SessionStat record.
281 * @return the default (the first if there are many records) session statistics record
282 * @since 12.0
283 */
284 public SessionStatistics getSessionStatisticsRecord() {
285 return getSessionStatisticsRecord(null);
286 }
287
288 /**
289 * Get the session statistics record related to the systemConfigurationId.
290 * @param systemConfigurationId system configuration ID
291 * @return the session statistics record
292 * @since 12.0
293 */
294 public SessionStatistics getSessionStatisticsRecord(final String systemConfigurationId) {
295 if (sessionStatisticsData.isEmpty()) {
296 return null;
297 }
298
299 if (systemConfigurationId == null) {
300 // default (the first one)
301 return sessionStatisticsData.getFirst();
302 }
303
304 // Loop to find the appropriate one
305 for (SessionStatistics sessionStatistics : sessionStatisticsData) {
306 if (systemConfigurationId.equalsIgnoreCase(sessionStatistics.getSystemConfigurationId())) {
307 return sessionStatistics;
308 }
309 }
310
311 return null;
312 }
313
314 /**
315 * Add an entry to the list of calibration data.
316 * @param cal entry to add
317 * @since 12.0
318 */
319 public void addCalibrationData(final Calibration cal) {
320 calibrationData.add(cal);
321 }
322
323 /**
324 * Get the calibration data for the data block.
325 * @return an unmodifiable list of calibration data
326 * @since 12.0
327 */
328 public List<Calibration> getCalibrationData() {
329 return Collections.unmodifiableList(calibrationData);
330 }
331
332 /**
333 * Get the Calibration record(s) related to the default system configuration id.
334 * @return the Calibration record(s) related to the default system configuration id
335 * @since 12.0
336 */
337 public List<Calibration> getCalibrationRecords() {
338 return getCalibrationRecords(null);
339 }
340
341 /**
342 * Get the Calibration record(s) related to the given systemConfigurationId.
343 * @param systemConfigurationId system configuration ID
344 * @return the Calibration record(s)
345 * @since 12.0
346 */
347 public List<Calibration> getCalibrationRecords(final String systemConfigurationId) {
348 if (calibrationData.isEmpty()) {
349 return null;
350 }
351
352 final String systemConfigId = systemConfigurationId == null ? getConfigurationRecords().getSystemRecord().getConfigurationId() : systemConfigurationId;
353
354 final List<Calibration> list = new ArrayList<>();
355 // Loop to find the appropriate one
356 for (Calibration calibration : calibrationData) {
357 if (systemConfigId.equalsIgnoreCase(calibration.getSystemConfigurationId())) {
358 list.add(calibration);
359 }
360 }
361
362 return list;
363 }
364
365 /**
366 * Add an entry to the list of calibration detail data.
367 * @param cal entry to add
368 * @since 12.0
369 */
370 public void addCalibrationDetailData(final CalibrationDetail cal) {
371 calibrationDetailData.add(cal);
372 }
373
374 /**
375 * Get the calibration detail data for the data block.
376 * @return an unmodifiable list of calibration detail data
377 * @since 12.0
378 */
379 public List<CalibrationDetail> getCalibrationDetailData() {
380 return Collections.unmodifiableList(calibrationDetailData);
381 }
382
383 /**
384 * Get the CalibrationDetail record(s) related to the default system configuration id.
385 * @return the CalibrationDetail record(s) related to the default system configuration id
386 * @since 12.0
387 */
388 public List<CalibrationDetail> getCalibrationDetailRecords() {
389 return getCalibrationDetailRecords(null);
390 }
391
392 /**
393 * Get the CalibrationDetail record(s) related to the given systemConfigurationId.
394 * @param systemConfigurationId system configuration ID
395 * @return the CalibrationDetail record(s)
396 * @since 12.0
397 */
398 public List<CalibrationDetail> getCalibrationDetailRecords(final String systemConfigurationId) {
399 if (calibrationDetailData.isEmpty()) {
400 return null;
401 }
402
403 final String systemConfigId = systemConfigurationId == null ? getConfigurationRecords().getSystemRecord().getConfigurationId() : systemConfigurationId;
404
405 final List<CalibrationDetail> list = new ArrayList<>();
406 // Loop to find the appropriate one
407 for (CalibrationDetail calibration : calibrationDetailData) {
408 if (systemConfigId.equalsIgnoreCase(calibration.getSystemConfigurationId())) {
409 list.add(calibration);
410 }
411 }
412
413 return list;
414 }
415
416 /**
417 * Get the wavelength related to the given RangeMeasurement.
418 *
419 * @param range a RangeMeasurement
420 * @return the wavelength related to the given RangeMeasurement.
421 * @since 12.0
422 */
423 public double getWavelength(final RangeMeasurement range) {
424 return getConfigurationRecords().getSystemRecord(range.getSystemConfigurationId()).getWavelength();
425 }
426
427 }
428
429 /** Range record. */
430 public static class RangeMeasurement implements TimeStamped {
431
432 /** Data epoch. */
433 private final AbsoluteDate date;
434
435 /** Time of flight [s]. */
436 private final double timeOfFlight;
437
438 /** System configuration ID. */
439 private final String systemConfigurationId;
440
441 /** Time event reference indicator.
442 * 0 = ground receive time (at SRP) (two-way)
443 * 1 = spacecraft bounce time (two-way)
444 * 2 = ground transmit time (at SRP) (two-way)
445 * 3 = spacecraft receive time (one-way)
446 * 4 = spacecraft transmit time (one-way)
447 * 5 = ground transmit time (at SRP) and spacecraft receive time (one-way)
448 * 6 = spacecraft transmit time and ground receive time (at SRP) (one-way)
449 * Currently, only 1 and 2 are used for laser ranging data.
450 */
451 private final int epochEvent;
452
453 /** Signal to noise ration. */
454 private final double snr;
455
456 /**
457 * Constructor.
458 * @param date data epoch
459 * @param timeOfFlight time of flight in seconds
460 * @param epochEvent indicates the time event reference
461 */
462 public RangeMeasurement(final AbsoluteDate date,
463 final double timeOfFlight,
464 final int epochEvent) {
465 this(date, timeOfFlight, epochEvent, Double.NaN);
466 }
467
468 /**
469 * Constructor.
470 * @param date data epoch
471 * @param timeOfFlight time of flight in seconds
472 * @param epochEvent indicates the time event reference
473 * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
474 */
475 public RangeMeasurement(final AbsoluteDate date,
476 final double timeOfFlight,
477 final int epochEvent, final double snr) {
478 this(date, timeOfFlight, epochEvent, snr, null);
479 }
480
481 /**
482 * Constructor.
483 * @param date data epoch
484 * @param timeOfFlight time of flight in seconds
485 * @param epochEvent indicates the time event reference
486 * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
487 * @param systemConfigurationId system configuration id
488 * @since 12.0
489 */
490 public RangeMeasurement(final AbsoluteDate date,
491 final double timeOfFlight, final int epochEvent,
492 final double snr,
493 final String systemConfigurationId) {
494 this.date = date;
495 this.timeOfFlight = timeOfFlight;
496 this.epochEvent = epochEvent;
497 this.snr = snr;
498 this.systemConfigurationId = systemConfigurationId;
499 }
500
501 /**
502 * Get the time-of-flight.
503 * @return the time-of-flight in seconds
504 */
505 public double getTimeOfFlight() {
506 return timeOfFlight;
507 }
508
509 /**
510 * Get the indicator for the time event reference.
511 * <ul>
512 * <li>0 = ground receive time (at SRP) (two-way)</li>
513 * <li>1 = spacecraft bounce time (two-way)</li>
514 * <li>2 = ground transmit time (at SRP) (two-way)</li>
515 * <li>3 = spacecraft receive time (one-way)</li>
516 * <li>4 = spacecraft transmit time (one-way)</li>
517 * <li>5 = ground transmit time (at SRP) and spacecraft receive time (one-way)</li>
518 * <li>6 = spacecraft transmit time and ground receive time (at SRP) (one-way)</li>
519 * </ul>
520 * Currently, only 1 and 2 are used for laser ranging data
521 * @return the indicator for the time event reference
522 */
523 public int getEpochEvent() {
524 return epochEvent;
525 }
526
527 /**
528 * Get the signal to noise ratio.
529 * @return the signal to noise ratio
530 */
531 public double getSnr() {
532 return snr;
533 }
534
535 /** {@inheritDoc} */
536 @Override
537 public AbsoluteDate getDate() {
538 return date;
539 }
540
541 /**
542 * Get the system configuration id.
543 * @return the system configuration id
544 * @since 12.0
545 */
546 public String getSystemConfigurationId() {
547 return systemConfigurationId;
548 }
549
550 /**
551 * Get a string representation of the instance in the CRD format.
552 * @return a string representation of the instance, in the CRD format.
553 * @since 12.0
554 */
555 public String toCrdString() {
556 return "00 not supported. use NptRangeMeasurement or FrRangeMeasurement instead.";
557 }
558 }
559
560 /**
561 * Range record -- Full rate, Sampled Engineering/Quicklook.
562 * @since 12.0
563 */
564 public static class FrRangeMeasurement extends RangeMeasurement {
565
566 /** Filter flag. **/
567 private final int filterFlag;
568
569 /** Detector channel. **/
570 private final int detectorChannel;
571
572 /** Stop number (in multiple-stop system). **/
573 private final int stopNumber;
574
575 /** Receive amplitude - a positive linear scale value. **/
576 private final int receiveAmplitude;
577
578 /** Transmit amplitude - a positive linear scale value. **/
579 private final int transmitAmplitude;
580
581 /**
582 * Constructor.
583 * @param date data epoch
584 * @param timeOfFlight time of flight in seconds
585 * @param epochEvent indicates the time event reference
586 * @param systemConfigurationId system configuration id
587 * @param filterFlag filter flag
588 * @param detectorChannel detector channel
589 * @param stopNumber stop number
590 * @param receiveAmplitude receive amplitude
591 * @param transmitAmplitude transmit amplitude
592 */
593 public FrRangeMeasurement(final AbsoluteDate date,
594 final double timeOfFlight,
595 final int epochEvent,
596 final String systemConfigurationId,
597 final int filterFlag,
598 final int detectorChannel,
599 final int stopNumber,
600 final int receiveAmplitude,
601 final int transmitAmplitude) {
602 super(date, timeOfFlight, epochEvent, Double.NaN, systemConfigurationId);
603 this.filterFlag = filterFlag;
604 this.detectorChannel = detectorChannel;
605 this.stopNumber = stopNumber;
606 this.receiveAmplitude = receiveAmplitude;
607 this.transmitAmplitude = transmitAmplitude;
608 }
609
610 /**
611 * Get the filter flag.
612 * @return the filter flag
613 */
614 public int getFilterFlag() {
615 return filterFlag;
616 }
617
618 /**
619 * Get the detector channel.
620 * @return the detector channel
621 */
622 public int getDetectorChannel() {
623 return detectorChannel;
624 }
625
626 /**
627 * Get the stop number.
628 * @return the stop number
629 */
630 public int getStopNumber() {
631 return stopNumber;
632 }
633
634 /**
635 * Get the receive amplitude.
636 * @return the receive amplitude, -1 if not measured
637 */
638 public int getReceiveAmplitude() {
639 return receiveAmplitude;
640 }
641
642 /**
643 * Get the transmit amplitude.
644 * @return the transmit amplitude, -1 if not measured
645 */
646 public int getTransmitAmplitude() {
647 return transmitAmplitude;
648 }
649
650 /** {@inheritDoc} */
651 @Override
652 @DefaultDataContext
653 public String toCrdString() {
654 return String.format(Locale.US, "10 %s", toString());
655 }
656
657 @Override
658 @DefaultDataContext
659 public String toString() {
660 // CRD suggested format, excluding the record type
661 // 'local' is already utc.
662 // Seconds of day (sod) is typically to 1 milllisec precision.
663 // receiveAmplitude, transmitAmplitude: -1 if not available
664 final double sod = getDate().
665 getComponents(TimeScalesFactory.getUTC()).
666 roundIfNeeded(60, 12).
667 getTime().
668 getSecondsInLocalDay();
669
670 final String str = String.format(Locale.US,
671 "%18.12f %18.12f %4s %1d %1d %1d %1d %5s %5s", sod,
672 getTimeOfFlight(), getSystemConfigurationId(),
673 getEpochEvent(), filterFlag, detectorChannel, stopNumber,
674 formatIntegerOrNaN(receiveAmplitude, -1),
675 formatIntegerOrNaN(transmitAmplitude, -1));
676 return handleNaN(str).replace(',', '.');
677 }
678
679 }
680
681 /**
682 * Range record -- Normal Point.
683 * @since 12.0
684 */
685 public static class NptRangeMeasurement extends RangeMeasurement {
686
687 /** Normal point window length [s]. */
688 private final double windowLength;
689
690 /** Number of raw ranges (after editing) compressed into the normal point. */
691 private final int numberOfRawRanges;
692
693 /** Bin RMS from the mean of raw accepted time-of-flight values minus the trend function. */
694 private final double binRms;
695
696 /** Bin skew from the mean of raw accepted time-of-flight values minus the trend function. */
697 private final double binSkew;
698
699 /** Bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function. */
700 private final double binKurtosis;
701
702 /** Bin peak - mean value. */
703 private final double binPeakMinusMean;
704
705 /** Return rate [%]. */
706 private final double returnRate;
707
708 /** Detector channel. */
709 private final int detectorChannel;
710
711 /**
712 * Constructor.
713 * @param date data epoch
714 * @param timeOfFlight time of flight in seconds
715 * @param epochEvent indicates the time event reference
716 * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
717 * @param systemConfigurationId System configuration id
718 */
719 public NptRangeMeasurement(final AbsoluteDate date,
720 final double timeOfFlight,
721 final int epochEvent, final double snr,
722 final String systemConfigurationId) {
723 this(date, timeOfFlight, epochEvent, snr, systemConfigurationId, -1,
724 -1, Double.NaN, Double.NaN, Double.NaN, Double.NaN,
725 Double.NaN, 0);
726 }
727
728 /**
729 * Constructor.
730 * @param date data epoch
731 * @param timeOfFlight time of flight in seconds
732 * @param epochEvent indicates the time event reference
733 * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
734 * @param systemConfigurationId System configuration id
735 * @param windowLength normal point window length
736 * @param numberOfRawRanges number of raw ranges (after editing) compressed into the normal point
737 * @param binRms Bin RMS from the mean of raw accepted time-of-flight values minus the trend function
738 * @param binSkew Bin skew from the mean of raw accepted time-of-flight values minus the trend function
739 * @param binKurtosis Bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function
740 * @param binPeakMinusMean Bin peak - mean value
741 * @param returnRate Return rate [%]
742 * @param detectorChannel detector channel
743 */
744 public NptRangeMeasurement(final AbsoluteDate date,
745 final double timeOfFlight,
746 final int epochEvent, final double snr,
747 final String systemConfigurationId,
748 final double windowLength,
749 final int numberOfRawRanges,
750 final double binRms, final double binSkew,
751 final double binKurtosis,
752 final double binPeakMinusMean,
753 final double returnRate,
754 final int detectorChannel) {
755 super(date, timeOfFlight, epochEvent, snr, systemConfigurationId);
756
757 this.windowLength = windowLength;
758 this.numberOfRawRanges = numberOfRawRanges;
759 this.binSkew = binSkew;
760 this.binKurtosis = binKurtosis;
761 this.binPeakMinusMean = binPeakMinusMean;
762 this.detectorChannel = detectorChannel;
763 this.binRms = binRms == -1.0e-12 ? Double.NaN : binRms; // -1=na, ps --> s
764 this.returnRate = returnRate == -1 ? Double.NaN : returnRate; // -1=na
765 }
766
767 /**
768 * Get the normal point window length.
769 * @return the normal point window length
770 */
771 public double getWindowLength() {
772 return windowLength;
773 }
774
775 /**
776 * Get the umber of raw ranges (after editing) compressed into the normal point.
777 * @return the umber of raw ranges
778 */
779 public int getNumberOfRawRanges() {
780 return numberOfRawRanges;
781 }
782
783 /**
784 * Get the bin RMS from the mean of raw accepted time-of-flight values minus the trend function.
785 * @return the bin RMS
786 */
787 public double getBinRms() {
788 return binRms;
789 }
790
791 /**
792 * Get the bin skew from the mean of raw accepted time-of-flight values minus the trend function.
793 * @return the bin skew
794 */
795 public double getBinSkew() {
796 return binSkew;
797 }
798
799 /**
800 * Get the bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function.
801 * @return the bin kurtosis
802 */
803 public double getBinKurtosis() {
804 return binKurtosis;
805 }
806
807 /**
808 * Get the bin peak - mean value.
809 * @return the bin peak - mean value
810 */
811 public double getBinPeakMinusMean() {
812 return binPeakMinusMean;
813 }
814
815 /**
816 * Get the return rate.
817 * @return the return rate
818 */
819 public double getReturnRate() {
820 return returnRate;
821 }
822
823 /**
824 * Get the detector channel.
825 * @return the detector channel
826 */
827 public int getDetectorChannel() {
828 return detectorChannel;
829 }
830
831 /** {@inheritDoc} */
832 @Override
833 @DefaultDataContext
834 public String toCrdString() {
835 return String.format(Locale.US, "11 %s", toString());
836 }
837
838 @Override
839 @DefaultDataContext
840 public String toString() {
841 // CRD suggested format, excluding the record type
842 // binRms, binPeakMinusMean: s --> ps
843 // 'local' is already utc.
844 // Seconds of day (sod) is typically to 1 milllisec precision.
845 final double sod = getDate().
846 getComponents(TimeScalesFactory.getUTC()).
847 roundIfNeeded(60, 12).
848 getTime().
849 getSecondsInLocalDay();
850
851 final String str = String.format(Locale.US,
852 "%18.12f %18.12f %4s %1d %6.1f %6d %9.1f %7.3f %7.3f %9.1f %5.2f %1d %5.1f",
853 sod, getTimeOfFlight(), getSystemConfigurationId(),
854 getEpochEvent(), windowLength, numberOfRawRanges,
855 binRms * 1e12, binSkew, binKurtosis,
856 binPeakMinusMean * 1e12, returnRate, detectorChannel,
857 getSnr());
858 return handleNaN(str).replace(',', '.');
859 }
860
861 }
862
863 /**
864 * Range Supplement Record.
865 * @since 12.0
866 */
867 public static class RangeSupplement implements TimeStamped {
868
869 /** Data epoch. */
870 private final AbsoluteDate date;
871
872 /** System configuration ID. */
873 private final String systemConfigurationId;
874
875 /** Tropospheric refraction correction (one-way). */
876 private final double troposphericRefractionCorrection;
877
878 /** Target center of mass correction (one-way). */
879 private final double centerOfMassCorrection;
880
881 /** Neutral density (ND) filter value. */
882 private final double ndFilterValue;
883
884 /** Time bias applied. */
885 private final double timeBiasApplied;
886
887 /** Range rate. */
888 private final double rangeRate;
889
890 /**
891 * Constructor.
892 * @param date data epoch
893 * @param systemConfigurationId system configuration ID
894 * @param troposphericRefractionCorrection tropospheric refraction correction (one-way)
895 * @param centerOfMassCorrection target center of mass correction (one-way)
896 * @param ndFilterValue Neutral density (ND) filter value
897 * @param timeBiasApplied Time bias applied
898 * @param rangeRate Range rate
899 */
900 public RangeSupplement(final AbsoluteDate date,
901 final String systemConfigurationId,
902 final double troposphericRefractionCorrection,
903 final double centerOfMassCorrection,
904 final double ndFilterValue,
905 final double timeBiasApplied,
906 final double rangeRate) {
907 this.date = date;
908 this.systemConfigurationId = systemConfigurationId;
909 this.troposphericRefractionCorrection = troposphericRefractionCorrection;
910 this.centerOfMassCorrection = centerOfMassCorrection;
911 this.ndFilterValue = ndFilterValue;
912 this.timeBiasApplied = timeBiasApplied;
913 this.rangeRate = rangeRate;
914 }
915
916 @Override
917 public AbsoluteDate getDate() {
918 return date;
919 }
920
921 /**
922 * Get the system configuration id.
923 * @return the system configuration id
924 */
925 public String getSystemConfigurationId() {
926 return systemConfigurationId;
927 }
928
929 /**
930 * Get the tropospheric refraction correction.
931 * @return the tropospheric refraction correction
932 */
933 public double getTroposphericRefractionCorrection() {
934 return troposphericRefractionCorrection;
935 }
936
937 /**
938 * Get the target center of mass.
939 * @return the target center of mass
940 */
941 public double getCenterOfMassCorrection() {
942 return centerOfMassCorrection;
943 }
944
945 /**
946 * Get the neutral density (ND) filter value.
947 * @return the neutral density (ND) filter value
948 */
949 public double getNdFilterValue() {
950 return ndFilterValue;
951 }
952
953 /**
954 * Get the time bias applied.
955 * @return the time bias applied
956 */
957 public double getTimeBiasApplied() {
958 return timeBiasApplied;
959 }
960
961 /**
962 * Get the range rate.
963 * @return the range rate
964 */
965 public double getRangeRate() {
966 return rangeRate;
967 }
968
969 /**
970 * Get a string representation of the instance in the CRD format.
971 * @return a string representation of the instance, in the CRD format.
972 */
973 @DefaultDataContext
974 public String toCrdString() {
975 return String.format(Locale.US, "12 %s", toString());
976 }
977
978 @Override
979 @DefaultDataContext
980 public String toString() {
981 // CRD suggested format, excluding the record type
982 // troposphericRefractionCorrection: s --> ps
983 // 'local' is already utc.
984 // Seconds of day (sod) is typically to 1 milllisec precision.
985 final double sod = getDate().
986 getComponents(TimeScalesFactory.getUTC()).
987 roundIfNeeded(60, 12).
988 getTime().
989 getSecondsInLocalDay();
990
991 final String str = String.format(Locale.US,
992 "%18.12f %4s %6.1f %6.4f %5.2f %8.4f %f", sod,
993 getSystemConfigurationId(),
994 troposphericRefractionCorrection * 1e12,
995 centerOfMassCorrection, ndFilterValue, timeBiasApplied,
996 rangeRate);
997 return handleNaN(str).replace(',', '.');
998 }
999
1000 }
1001
1002 /** This data record contains a minimal set of meteorological data. */
1003 public static class MeteorologicalMeasurement implements TimeStamped {
1004
1005 /** Data epoch. */
1006 private final AbsoluteDate date;
1007
1008 /** Surface pressure [bar]. */
1009 private final double pressure;
1010
1011 /** Surface temperature [K]. */
1012 private final double temperature;
1013
1014 /** Relative humidity at the surface [%]. */
1015 private final double humidity;
1016
1017 /** Origin of values.
1018 * 0=measured values, 1=interpolated values
1019 */
1020 private final int originOfValues;
1021
1022 /**
1023 * Constructor.
1024 * @param date data epoch
1025 * @param pressure the surface pressure in bars
1026 * @param temperature the surface temperature in degrees Kelvin
1027 * @param humidity the relative humidity at the surface in percents
1028 */
1029 public MeteorologicalMeasurement(final AbsoluteDate date,
1030 final double pressure, final double temperature,
1031 final double humidity) {
1032 this(date, pressure, temperature, humidity, 0);
1033 }
1034
1035 /**
1036 * Constructor.
1037 * @param date data epoch
1038 * @param pressure the surface pressure in bars
1039 * @param temperature the surface temperature in degrees Kelvin
1040 * @param humidity the relative humidity at the surface in percents
1041 * @param originOfValues Origin of values
1042 */
1043 public MeteorologicalMeasurement(final AbsoluteDate date, final double pressure, final double temperature,
1044 final double humidity, final int originOfValues) {
1045 this.date = date;
1046 this.pressure = pressure;
1047 this.temperature = temperature;
1048 this.humidity = humidity;
1049 this.originOfValues = originOfValues;
1050 }
1051
1052 /**
1053 * Get the surface pressure.
1054 * @return the surface pressure in bars
1055 */
1056 public double getPressure() {
1057 return pressure;
1058 }
1059
1060 /**
1061 * Get the surface temperature.
1062 * @return the surface temperature in degrees Kelvin
1063 */
1064 public double getTemperature() {
1065 return temperature;
1066 }
1067
1068 /**
1069 * Get the relative humidity at the surface.
1070 * @return the relative humidity at the surface in percents
1071 */
1072 public double getHumidity() {
1073 return humidity;
1074 }
1075
1076 /** {@inheritDoc} */
1077 @Override
1078 public AbsoluteDate getDate() {
1079 return date;
1080 }
1081
1082 /** Get the origin of values.
1083 * 0=measure values
1084 * 1=interpolated values
1085 * @return the origin of values
1086 * @since 12.0
1087 */
1088 public int getOriginOfValues() {
1089 return originOfValues;
1090 }
1091
1092 /**
1093 * Get a string representation of the instance in the CRD format.
1094 * @return a string representation of the instance, in the CRD format.
1095 * @since 12.0
1096 */
1097 @DefaultDataContext
1098 public String toCrdString() {
1099 return String.format(Locale.US, "20 %s", toString());
1100 }
1101
1102 @Override
1103 @DefaultDataContext
1104 public String toString() {
1105 // CRD suggested format, excluding the record type
1106 // pressure: bar --> mbar
1107 // 'local' is already utc.
1108 // Seconds of day (sod) is typically to 1 milllisec precision.
1109 final double sod = getDate().
1110 getComponents(TimeScalesFactory.getUTC()).
1111 roundIfNeeded(60, 3).
1112 getTime().
1113 getSecondsInLocalDay();
1114
1115 final String str = String.format(Locale.US, "%9.3f %7.2f %6.2f %4.0f %1d",
1116 sod, pressure * 1e3, temperature, humidity, originOfValues);
1117 return handleNaN(str).replace(',', '.');
1118 }
1119 }
1120
1121 /** Pointing angles record. */
1122 public static class AnglesMeasurement implements TimeStamped {
1123
1124 /** Data epoch. */
1125 private final AbsoluteDate date;
1126
1127 /** Azimuth [rad]. */
1128 private final double azimuth;
1129
1130 /** Elevation [rad]. */
1131 private final double elevation;
1132
1133 /** Direction flag (0 = transmit & receive ; 1 = transmit ; 2 = receive). */
1134 private final int directionFlag;
1135
1136 /** Angle origin indicator.
1137 * 0 = unknown
1138 * 1 = computed
1139 * 2 = commanded (from predictions)
1140 * 3 = measured (from encoders)
1141 */
1142 private final int originIndicator;
1143
1144 /** Refraction corrected. */
1145 private final boolean refractionCorrected;
1146
1147 /** Azimuth rate [rad/sec]. */
1148 private final double azimuthRate;
1149
1150 /** Elevation rate [rad/sec]. */
1151 private final double elevationRate;
1152
1153 /**
1154 * Constructor.
1155 * @param date data epoch
1156 * @param azimuth azimuth angle in radians
1157 * @param elevation elevation angle in radians
1158 * @param directionFlag direction flag
1159 * @param originIndicator angle origin indicator
1160 * @param refractionCorrected flag to indicate if the refraction is corrected
1161 * @param azimuthRate azimuth rate in radians per second (equal to Double.NaN if unknown)
1162 * @param elevationRate elevation rate in radians per second (equal to Double.NaN if unknown)
1163 */
1164 public AnglesMeasurement(final AbsoluteDate date, final double azimuth,
1165 final double elevation, final int directionFlag,
1166 final int originIndicator,
1167 final boolean refractionCorrected,
1168 final double azimuthRate, final double elevationRate) {
1169 this.date = date;
1170 this.azimuth = azimuth;
1171 this.elevation = elevation;
1172 this.directionFlag = directionFlag;
1173 this.originIndicator = originIndicator;
1174 this.refractionCorrected = refractionCorrected;
1175 this.azimuthRate = azimuthRate;
1176 this.elevationRate = elevationRate;
1177 }
1178
1179 /**
1180 * Get the azimuth angle.
1181 * @return the azimuth angle in radians
1182 */
1183 public double getAzimuth() {
1184 return azimuth;
1185 }
1186
1187 /**
1188 * Get the elevation angle.
1189 * @return the elevation angle in radians
1190 */
1191 public double getElevation() {
1192 return elevation;
1193 }
1194
1195 /**
1196 * Get the direction flag (0 = transmit & receive ; 1 = transmit ; 2 = receive).
1197 * @return the direction flag
1198 */
1199 public int getDirectionFlag() {
1200 return directionFlag;
1201 }
1202
1203 /**
1204 * Get the angle origin indicator.
1205 * <p>
1206 * 0 = unknown;
1207 * 1 = computed;
1208 * 2 = commanded (from predictions);
1209 * 3 = measured (from encoders)
1210 * </p>
1211 * @return the angle origin indicator
1212 */
1213 public int getOriginIndicator() {
1214 return originIndicator;
1215 }
1216
1217 /**
1218 * Get the flag indicating if the refraction is corrected.
1219 * @return true if refraction is corrected
1220 */
1221 public boolean isRefractionCorrected() {
1222 return refractionCorrected;
1223 }
1224
1225 /**
1226 * Get the azimuth rate.
1227 * <p>
1228 * Is equal to Double.NaN if the value is unknown.
1229 * </p>
1230 * @return the azimuth rate in radians per second
1231 */
1232 public double getAzimuthRate() {
1233 return azimuthRate;
1234 }
1235
1236 /**
1237 * Get the elevation rate.
1238 * <p>
1239 * Is equal to Double.NaN if the value is unknown.
1240 * </p>
1241 * @return the elevation rate in radians per second
1242 */
1243 public double getElevationRate() {
1244 return elevationRate;
1245 }
1246
1247 /** {@inheritDoc} */
1248 @Override
1249 public AbsoluteDate getDate() {
1250 return date;
1251 }
1252
1253 /**
1254 * Get a string representation of the instance in the CRD format.
1255 * @return a string representation of the instance, in the CRD format.
1256 * @since 12.0
1257 */
1258 @DefaultDataContext
1259 public String toCrdString() {
1260 return String.format(Locale.US, "30 %s", toString());
1261 }
1262
1263 @Override
1264 @DefaultDataContext
1265 public String toString() {
1266 // CRD suggested format, excluding the record type
1267 // azimuth, elevation: rad --> deg
1268 // azimuthRate, elevationRate: rad/s --> deg/s
1269 // 'local' is already utc.
1270 // Seconds of day (sod) is typically to 1 milllisec precision.
1271 final double sod = getDate().
1272 getComponents(TimeScalesFactory.getUTC()).
1273 roundIfNeeded(60, 3).
1274 getTime().
1275 getSecondsInLocalDay();
1276
1277 final String str = String.format(Locale.US,
1278 "%9.3f %8.4f %8.4f %1d %1d %1d %10.7f %10.7f",
1279 sod,
1280 FastMath.toDegrees(azimuth), FastMath.toDegrees(elevation),
1281 directionFlag, originIndicator, refractionCorrected ? 1 : 0,
1282 FastMath.toDegrees(azimuthRate),
1283 FastMath.toDegrees(elevationRate));
1284 return handleNaN(str).replace(',', '.');
1285 }
1286 }
1287
1288 /** Meteorological data. */
1289 public static class Meteo {
1290
1291 /** Number of neighbors for meteo data interpolation. */
1292 private static final int N_NEIGHBORS = 2;
1293
1294 /** First available date. */
1295 private final AbsoluteDate firstDate;
1296
1297 /** Last available date. */
1298 private final AbsoluteDate lastDate;
1299
1300 /** Previous set of meteorological parameters. */
1301 private MeteorologicalMeasurement previousParam;
1302
1303 /** Next set of solar meteorological parameters. */
1304 private MeteorologicalMeasurement nextParam;
1305
1306 /** List of meteo data. */
1307 private final ImmutableTimeStampedCache<MeteorologicalMeasurement> meteo;
1308
1309 /**
1310 * Constructor.
1311 * @param meteoData list of meteo data
1312 */
1313 public Meteo(final SortedSet<MeteorologicalMeasurement> meteoData) {
1314
1315 // Size
1316 final int neighborsSize = (meteoData.size() < 2) ? meteoData.size() : N_NEIGHBORS;
1317
1318 // Check neighbors size
1319 if (neighborsSize == 0) {
1320
1321 // Meteo data -> empty cache
1322 this.meteo = ImmutableTimeStampedCache.emptyCache();
1323
1324 // Null epochs (will ne be used)
1325 this.firstDate = null;
1326 this.lastDate = null;
1327
1328 } else {
1329
1330 // Meteo data
1331 this.meteo = new ImmutableTimeStampedCache<>(neighborsSize, meteoData);
1332
1333 // Initialize first and last available dates
1334 this.firstDate = meteoData.getFirst().getDate();
1335 this.lastDate = meteoData.getLast().getDate();
1336
1337 }
1338
1339 }
1340
1341 /** Get an unmodifiable view of the tabulated meteorological data.
1342 * @return unmodifiable view of the tabulated meteorological data
1343 * @since 11.0
1344 */
1345 public List<MeteorologicalMeasurement> getData() {
1346 return meteo.getAll();
1347 }
1348
1349 /**
1350 * Get the meteorological parameters at a given date.
1351 * @param date date when user wants the meteorological parameters
1352 * @return the meteorological parameters at date (can be null if
1353 * meteorological data are empty).
1354 */
1355 public MeteorologicalMeasurement getMeteo(final AbsoluteDate date) {
1356
1357 // Check if meteorological data are available
1358 if (meteo.getMaxNeighborsSize() == 0) {
1359 return null;
1360 }
1361
1362 // Interpolating two neighboring meteorological parameters
1363 bracketDate(date);
1364 if (date.durationFrom(firstDate) <= 0 || date.durationFrom(lastDate) > 0) {
1365 // Date is outside file range
1366 return previousParam;
1367 } else {
1368 // Perform interpolations
1369 final double pressure = getLinearInterpolation(date, previousParam.getPressure(), nextParam.getPressure());
1370 final double temperature = getLinearInterpolation(date, previousParam.getTemperature(), nextParam.getTemperature());
1371 final double humidity = getLinearInterpolation(date, previousParam.getHumidity(), nextParam.getHumidity());
1372 return new MeteorologicalMeasurement(date, pressure, temperature, humidity);
1373 }
1374
1375 }
1376
1377 /**
1378 * Find the data bracketing a specified date.
1379 * @param date date to bracket
1380 */
1381 private void bracketDate(final AbsoluteDate date) {
1382
1383 // don't search if the cached selection is fine
1384 if (previousParam != null &&
1385 date.durationFrom(previousParam.getDate()) > 0 &&
1386 date.durationFrom(nextParam.getDate()) <= 0) {
1387 return;
1388 }
1389
1390 // Initialize previous and next parameters
1391 if (date.durationFrom(firstDate) <= 0) {
1392 // Current date is before the first date
1393 previousParam = meteo.getEarliest();
1394 nextParam = previousParam;
1395 } else if (date.durationFrom(lastDate) > 0) {
1396 // Current date is after the last date
1397 previousParam = meteo.getLatest();
1398 nextParam = previousParam;
1399 } else {
1400 // Current date is between first and last date
1401 final List<MeteorologicalMeasurement> neighbors = meteo.getNeighbors(date).toList();
1402 previousParam = neighbors.getFirst();
1403 nextParam = neighbors.get(1);
1404 }
1405
1406 }
1407
1408 /**
1409 * Performs a linear interpolation between two values The weights are computed
1410 * from the time delta between previous date, current date, next date.
1411 * @param date the current date
1412 * @param previousValue the value at previous date
1413 * @param nextValue the value at next date
1414 * @return the value interpolated for the current date
1415 */
1416 private double getLinearInterpolation(final AbsoluteDate date,
1417 final double previousValue,
1418 final double nextValue) {
1419 // Perform a linear interpolation
1420 final AbsoluteDate previousDate = previousParam.getDate();
1421 final AbsoluteDate currentDate = nextParam.getDate();
1422 final double dt = currentDate.durationFrom(previousDate);
1423 final double previousWeight = currentDate.durationFrom(date) / dt;
1424 final double nextWeight = date.durationFrom(previousDate) / dt;
1425
1426 // Returns the data interpolated at the date
1427 return previousValue * previousWeight + nextValue * nextWeight;
1428 }
1429
1430 }
1431
1432 /**
1433 * Calibration Record.
1434 * @since 12.0
1435 */
1436 public static class Calibration implements TimeStamped {
1437
1438 /** Data epoch. */
1439 private final AbsoluteDate date;
1440
1441 /**
1442 * Type of data.
1443 * 0=station combined transmit and receive calibration (“normal” SLR/LLR)
1444 * 1=station transmit calibration (e.g., one-way ranging to transponders)
1445 * 2=station receive calibration
1446 * 3=target combined transmit and receive calibrations
1447 * 4=target transmit calibration
1448 * 5=target receive calibration
1449 */
1450 private final int typeOfData;
1451
1452 /** System configuration ID. */
1453 private final String systemConfigurationId;
1454
1455 /** Number of data points recorded. */
1456 private final int numberOfPointsRecorded;
1457
1458 /** Number of data points used. */
1459 private final int numberOfPointsUsed;
1460
1461 /** One-way target distance (meters, nominal). */
1462 private final double oneWayDistance;
1463
1464 /** Calibration System Delay. */
1465 private final double systemDelay;
1466
1467 /** Calibration Delay Shift - a measure of calibration stability. */
1468 private final double delayShift;
1469
1470 /** RMS of raw system delay. */
1471 private final double rms;
1472
1473 /** Skew of raw system delay values from the mean. */
1474 private final double skew;
1475
1476 /** Kurtosis of raw system delay values from the mean. */
1477 private final double kurtosis;
1478
1479 /** System delay peak – mean value. */
1480 private final double peakMinusMean;
1481
1482 /**
1483 * Calibration Type Indicator.
1484 * 0=not used or undefined
1485 * 1=nominal (from once off assessment)
1486 * 2=external calibrations
1487 * 3=internal calibrations – telescope
1488 * 4=internal calibrations – building
1489 * 5=burst calibrations
1490 * 6=other
1491 */
1492 private final int typeIndicator;
1493
1494 /**
1495 * Calibration Shift Type Indicator.
1496 * 0=not used or undefined
1497 * 1=nominal (from once off assessment)
1498 * 2=pre- to post- Shift
1499 * 3=minimum to maximum
1500 * 4=other
1501 */
1502 private final int shiftTypeIndicator;
1503
1504 /** Detector Channel.
1505 * 0=not applicable or “all”
1506 * 1-4 for quadrant
1507 * 1-n for many channels
1508 */
1509 private final int detectorChannel;
1510
1511 /**
1512 * Calibration Span.
1513 * 0 = not applicable (e.g. Calibration type indicator is “nominal”)
1514 * 1 = Pre-calibration only
1515 * 2 = Post-calibration only
1516 * 3 = Combined (pre- and post-calibrations or multiple)
1517 * 4 = Real-time calibration (data taken while ranging to a satellite)
1518 */
1519 private final int span;
1520
1521 /** Return Rate (%). */
1522 private final double returnRate;
1523
1524 /**
1525 * Constructor.
1526 * @param date data epoch
1527 * @param typeOfData type of data
1528 * @param systemConfigurationId system configuration id
1529 * @param numberOfPointsRecorded number of data points recorded
1530 * @param numberOfPointsUsed number of data points used
1531 * @param oneWayDistance one-way target distance (nominal)
1532 * @param systemDelay calibration system delay
1533 * @param delayShift calibration delay shift - a measure of calibration stability
1534 * @param rms RMS of raw system delay
1535 * @param skew skew of raw system delay values from the mean.
1536 * @param kurtosis kurtosis of raw system delay values from the mean.
1537 * @param peakMinusMean system delay peak – mean value
1538 * @param typeIndicator calibration type indicator
1539 * @param shiftTypeIndicator calibration shift type indicator
1540 * @param detectorChannel detector channel
1541 * @param span calibration span
1542 * @param returnRate return rate (%)
1543 */
1544 public Calibration(final AbsoluteDate date, final int typeOfData,
1545 final String systemConfigurationId,
1546 final int numberOfPointsRecorded,
1547 final int numberOfPointsUsed,
1548 final double oneWayDistance,
1549 final double systemDelay, final double delayShift,
1550 final double rms, final double skew,
1551 final double kurtosis, final double peakMinusMean,
1552 final int typeIndicator, final int shiftTypeIndicator,
1553 final int detectorChannel, final int span,
1554 final double returnRate) {
1555 this.date = date;
1556 this.typeOfData = typeOfData;
1557 this.systemConfigurationId = systemConfigurationId;
1558 this.numberOfPointsRecorded = numberOfPointsRecorded;
1559 this.numberOfPointsUsed = numberOfPointsUsed;
1560 this.systemDelay = systemDelay;
1561 this.delayShift = delayShift;
1562 this.rms = rms;
1563 this.skew = skew;
1564 this.kurtosis = kurtosis;
1565 this.peakMinusMean = peakMinusMean;
1566 this.typeIndicator = typeIndicator;
1567 this.shiftTypeIndicator = shiftTypeIndicator;
1568 this.detectorChannel = detectorChannel;
1569 this.span = span;
1570 this.returnRate = returnRate;
1571 this.oneWayDistance = oneWayDistance == -1 ? Double.NaN : oneWayDistance; // -1=na
1572 }
1573
1574 @Override
1575 public AbsoluteDate getDate() {
1576 return date;
1577 }
1578
1579 /**
1580 * Get the type of data.
1581 *
1582 * <ul>
1583 * <li>0=station combined transmit and receive calibration (“normal” SLR/LLR)
1584 * <li>1=station transmit calibration (e.g., one-way ranging to transponders)
1585 * <li>2=station receive calibration
1586 * <li>3=target combined transmit and receive calibrations
1587 * <li>4=target transmit calibration
1588 * <li>5=target receive calibration
1589 * </ul>
1590 * @return the type of data
1591 */
1592 public int getTypeOfData() {
1593 return typeOfData;
1594 }
1595
1596 /**
1597 * Get the system configuration id.
1598 * @return the system configuration id
1599 */
1600 public String getSystemConfigurationId() {
1601 return systemConfigurationId;
1602 }
1603
1604 /**
1605 * Get the number of data points recorded.
1606 * @return the number of data points recorded, -1 if no information
1607 */
1608 public int getNumberOfPointsRecorded() {
1609 return numberOfPointsRecorded;
1610 }
1611
1612 /**
1613 * Get the number of data points used.
1614 * @return the number of data points used, -1 if no information
1615 */
1616 public int getNumberOfPointsUsed() {
1617 return numberOfPointsUsed;
1618 }
1619
1620 /**
1621 * Get the one-way target distance (nominal).
1622 * @return the one-way target distance (nominal)
1623 */
1624 public double getOneWayDistance() {
1625 return oneWayDistance;
1626 }
1627
1628 /**
1629 * Get the calibration system delay.
1630 * @return the calibration system delay
1631 */
1632 public double getSystemDelay() {
1633 return systemDelay;
1634 }
1635
1636 /**
1637 * Get the calibration delay shift.
1638 * @return the calibration delay shift
1639 */
1640 public double getDelayShift() {
1641 return delayShift;
1642 }
1643
1644 /**
1645 * Get the rms of raw system delay.
1646 * @return the rms of raw system delay
1647 */
1648 public double getRms() {
1649 return rms;
1650 }
1651
1652 /**
1653 * Get the skew of raw system delay values from the mean.
1654 * @return the skew of raw system delay values from the mean.
1655 */
1656 public double getSkew() {
1657 return skew;
1658 }
1659
1660 /**
1661 * Get the kurtosis of raw system delay values from the mean.
1662 * @return the kurtosis of raw system delay values from the mean.
1663 */
1664 public double getKurtosis() {
1665 return kurtosis;
1666 }
1667
1668 /**
1669 * Get the system delay peak – mean value.
1670 * @return the system delay peak – mean value
1671 */
1672 public double getPeakMinusMean() {
1673 return peakMinusMean;
1674 }
1675
1676 /**
1677 * Get the calibration type indicator.
1678 *
1679 * <ul>
1680 * <li>0=not used or undefined
1681 * <li>1=nominal (from once off assessment)
1682 * <li>2=external calibrations
1683 * <li>3=internal calibrations – telescope
1684 * <li>4=internal calibrations – building
1685 * <li>5=burst calibrations
1686 * <li>6=other
1687 * </ul>
1688 * @return the calibration type indicator
1689 */
1690 public int getTypeIndicator() {
1691 return typeIndicator;
1692 }
1693
1694 /**
1695 * Get the calibration shift type indicator.
1696 *
1697 * <ul>
1698 * <li>0=not used or undefined
1699 * <li>1=nominal (from once off assessment)
1700 * <li>2=pre- to post- Shift
1701 * <li>3=minimum to maximum
1702 * <li>4=other
1703 * </ul>
1704 * @return the calibration shift type indicator
1705 */
1706 public int getShiftTypeIndicator() {
1707 return shiftTypeIndicator;
1708 }
1709
1710 /**
1711 * Get the detector channel.
1712 *
1713 * <ul>
1714 * <li>0=not applicable or “all”
1715 * <li>1-4 for quadrant
1716 * <li>1-n for many channels
1717 * </ul>
1718 * @return the detector channel
1719 */
1720 public int getDetectorChannel() {
1721 return detectorChannel;
1722 }
1723
1724 /**
1725 * Get the calibration span.
1726 *
1727 * <ul>
1728 * <li>0 = not applicable (e.g. Calibration type indicator is “nominal”)
1729 * <li>1 = Pre-calibration only
1730 * <li>2 = Post-calibration only
1731 * <li>3 = Combined (pre- and post-calibrations or multiple)
1732 * <li>4 = Real-time calibration (data taken while ranging to a satellite)
1733 * </ul>
1734 * @return the calibration span
1735 */
1736 public int getSpan() {
1737 return span;
1738 }
1739
1740 /**
1741 * Get the return rate.
1742 * @return the return rate
1743 */
1744 public double getReturnRate() {
1745 return returnRate;
1746 }
1747
1748 /**
1749 * Get a string representation of the instance in the CRD format.
1750 * @return a string representation of the instance, in the CRD format.
1751 */
1752 @DefaultDataContext
1753 public String toCrdString() {
1754 return String.format(Locale.US, "40 %s", toString());
1755 }
1756
1757 @Override
1758 @DefaultDataContext
1759 public String toString() {
1760 // CRD suggested format, excluding the record type
1761 // systemDelay, delayShift: s --> ps
1762 // rms, peakMinusMean: s --> ps
1763 // 'local' is already utc.
1764 // Seconds of day (sod) is typically to 1 milllisec precision.
1765 final double sod = getDate().
1766 getComponents(TimeScalesFactory.getUTC()).
1767 roundIfNeeded(60, 12).
1768 getTime().
1769 getSecondsInLocalDay();
1770
1771 final String str = "%18.12f %1d %4s %8s %8s %8.4f %10.1f %8.1f %6.1f %7.3f %7.3f %6.1f %1d %1d %1d %1d %5.1f".formatted(
1772 sod, typeOfData, systemConfigurationId,
1773 formatIntegerOrNaN(numberOfPointsRecorded, -1),
1774 formatIntegerOrNaN(numberOfPointsUsed, -1), oneWayDistance,
1775 systemDelay * 1e12, delayShift * 1e12, rms * 1e12, skew,
1776 kurtosis, peakMinusMean * 1e12, typeIndicator,
1777 shiftTypeIndicator, detectorChannel, span, returnRate);
1778 return handleNaN(str).replace(',', '.');
1779 }
1780
1781 }
1782
1783 /**
1784 * Calibration Detail Record.
1785 * @since 12.0
1786 */
1787 public static class CalibrationDetail extends Calibration {
1788 // same as Calibration record except that the record type is '41' rather than '40'.
1789
1790 /**
1791 * Constructor.
1792 * @param date data epoch
1793 * @param typeOfData type of data
1794 * @param systemConfigurationId system configuration id
1795 * @param numberOfPointsRecorded number of data points recorded
1796 * @param numberOfPointsUsed number of data points used
1797 * @param oneWayDistance one-way target distance (nominal)
1798 * @param systemDelay calibration system delay
1799 * @param delayShift calibration delay shift - a measure of calibration stability
1800 * @param rms RMS of raw system delay
1801 * @param skew skew of raw system delay values from the mean.
1802 * @param kurtosis kurtosis of raw system delay values from the mean.
1803 * @param peakMinusMean system delay peak – mean value
1804 * @param typeIndicator calibration type indicator
1805 * @param shiftTypeIndicator calibration shift type indicator
1806 * @param detectorChannel detector channel
1807 * @param span calibration span
1808 * @param returnRate return rate (%)
1809 */
1810 public CalibrationDetail(final AbsoluteDate date, final int typeOfData,
1811 final String systemConfigurationId,
1812 final int numberOfPointsRecorded,
1813 final int numberOfPointsUsed, final double oneWayDistance,
1814 final double systemDelay, final double delayShift,
1815 final double rms, final double skew, final double kurtosis,
1816 final double peakMinusMean, final int typeIndicator,
1817 final int shiftTypeIndicator, final int detectorChannel,
1818 final int span, final double returnRate) {
1819 super(date, typeOfData, systemConfigurationId, numberOfPointsRecorded,
1820 numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew,
1821 kurtosis, peakMinusMean, typeIndicator, shiftTypeIndicator,
1822 detectorChannel, span, returnRate);
1823 }
1824
1825 /**
1826 * Get a string representation of the instance in the CRD format.
1827 * @return a string representation of the instance, in the CRD format.
1828 */
1829 @DefaultDataContext
1830 @Override
1831 public String toCrdString() {
1832 return String.format(Locale.US, "41 %s", toString());
1833 }
1834
1835 }
1836
1837 /**
1838 * Session (Pass) Statistics Record.
1839 * @since 12.0
1840 */
1841 public static class SessionStatistics {
1842
1843 /** System configuration ID. */
1844 private final String systemConfigurationId;
1845
1846 /** Session RMS from the mean of raw accepted time-of-flight values minus the trend function. */
1847 private final double rms;
1848
1849 /** Session skewness from the mean of raw accepted time-of-flight values minus the trend function. */
1850 private final double skewness;
1851
1852 /** Session kurtosis from the mean of raw accepted time-of-flight values minus the trend function. */
1853 private final double kurtosis;
1854
1855 /** Session peak – mean value. */
1856 private final double peakMinusMean;
1857
1858 /**
1859 * Data quality assessment indicator.
1860 * <ul>
1861 * <li>0=undefined or no comment</li>
1862 * <li>1=clear, easily filtered data, with little or no noise</li>
1863 * <li>2=clear data with some noise; filtering is slightly compromised by noise level</li>
1864 * <li>3=clear data with a significant amount of noise, or weak data with little noise. Data are certainly
1865 * present, but filtering is difficult.</li>
1866 * <li>4=unclear data; data appear marginally to be present, but are very difficult to separate from noise
1867 * during filtering. Signal to noise ratio can be less than 1:1.</li>
1868 * <li>5=no data apparent</li>
1869 * </ul>
1870 */
1871 private final int dataQulityIndicator;
1872
1873 /**
1874 * Constructor.
1875 * @param systemConfigurationId system configuration ID
1876 * @param rms session RMS from the mean of raw accepted time-of-flight values minus the trend function
1877 * @param skewness session skewness from the mean of raw accepted time-of-flight values minus the trend function
1878 * @param kurtosis session kurtosis from the mean of raw accepted time-of-flight values minus the trend function
1879 * @param peakMinusMean session peak – mean value
1880 * @param dataQulityIndicator data quality assessment indicator
1881 */
1882 public SessionStatistics(final String systemConfigurationId,
1883 final double rms, final double skewness,
1884 final double kurtosis,
1885 final double peakMinusMean,
1886 final int dataQulityIndicator) {
1887 this.systemConfigurationId = systemConfigurationId;
1888 this.rms = rms;
1889 this.skewness = skewness;
1890 this.kurtosis = kurtosis;
1891 this.peakMinusMean = peakMinusMean;
1892 this.dataQulityIndicator = dataQulityIndicator;
1893 }
1894
1895 /**
1896 * Get system configuration id.
1897 * @return the system configuration id
1898 */
1899 public String getSystemConfigurationId() {
1900 return systemConfigurationId;
1901 }
1902
1903 /**
1904 * Get the session RMS from the mean of raw accepted time-of-flight values minus the trend function.
1905 * @return the session RMS
1906 */
1907 public double getRms() {
1908 return rms;
1909 }
1910
1911 /**
1912 * Get the session skewness from the mean of raw accepted time-of-flight values minus the trend function.
1913 * @return the session skewness
1914 */
1915 public double getSkewness() {
1916 return skewness;
1917 }
1918
1919 /**
1920 * Get the session kurtosis from the mean of raw accepted time-of-flight values minus the trend function.
1921 * @return the session kurtosis
1922 */
1923 public double getKurtosis() {
1924 return kurtosis;
1925 }
1926
1927 /**
1928 * Get the session peak – mean value.
1929 * @return the session peak – mean value
1930 */
1931 public double getPeakMinusMean() {
1932 return peakMinusMean;
1933 }
1934
1935 /**
1936 * Get the data quality assessment indicator
1937 * <ul>
1938 * <li>0=undefined or no comment</li>
1939 * <li>1=clear, easily filtered data, with little or no noise</li>
1940 * <li>2=clear data with some noise; filtering is slightly compromised by noise level</li>
1941 * <li>3=clear data with a significant amount of noise, or weak data with little noise. Data are certainly
1942 * present, but filtering is difficult.</li>
1943 * <li>4=unclear data; data appear marginally to be present, but are very difficult to separate from noise
1944 * during filtering. Signal to noise ratio can be less than 1:1.</li>
1945 * <li>5=no data apparent</li>
1946 * </ul>
1947 * @return the data quality assessment indicator
1948 */
1949 public int getDataQulityIndicator() {
1950 return dataQulityIndicator;
1951 }
1952
1953 /**
1954 * Get a string representation of the instance in the CRD format.
1955 * @return a string representation of the instance, in the CRD format.
1956 */
1957 public String toCrdString() {
1958 return String.format(Locale.US, "50 %s", toString());
1959 }
1960
1961 @Override
1962 public String toString() {
1963 // CRD suggested format, excluding the record type
1964 // rms, peakMinusMean: s --> ps
1965 final String str = String.format(Locale.US, "%4s %6.1f %7.3f %7.3f %6.1f %1d",
1966 systemConfigurationId, rms * 1e12, skewness, kurtosis,
1967 peakMinusMean * 1e12, dataQulityIndicator);
1968 return handleNaN(str).replace(',', '.');
1969 }
1970
1971 }
1972
1973 }