CRD.java

  1. /* Copyright 2002-2025 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. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;
  21. import java.util.Locale;
  22. import java.util.SortedSet;
  23. import java.util.TreeSet;
  24. import java.util.regex.Pattern;
  25. import java.util.stream.Collectors;

  26. import org.hipparchus.util.FastMath;
  27. import org.orekit.annotation.DefaultDataContext;
  28. import org.orekit.time.AbsoluteDate;
  29. import org.orekit.time.ChronologicalComparator;
  30. import org.orekit.time.TimeScalesFactory;
  31. import org.orekit.time.TimeStamped;
  32. import org.orekit.utils.ImmutableTimeStampedCache;

  33. /**
  34.  * This class stores all the information of the Consolidated laser ranging Data Format (CRD) parsed
  35.  * by CRDParser. It contains the header and a list of data records.
  36.  * @author Bryan Cazabonne
  37.  * @author Rongwang Li
  38.  * @since 10.3
  39.  */
  40. public class CRD {

  41.     /** Value of 'not available' or 'not applicable' or 'no information'. */
  42.     public static final String STR_VALUE_NOT_AVAILABLE = "na";

  43.     /** String of "NaN". */
  44.     public static final String STR_NAN = "NaN";

  45.     /** Pattern of "NaN". */
  46.     public static final Pattern PATTERN_NAN = Pattern.compile(STR_NAN);

  47.     /** List of comments contained in the file. */
  48.     private final List<String> comments;

  49.     /** List of data blocks contain in the CDR file. */
  50.     private final List<CRDDataBlock> dataBlocks;

  51.     /**
  52.      * Constructor.
  53.      */
  54.     public CRD() {
  55.         // Initialise empty lists
  56.         this.comments   = new ArrayList<>();
  57.         this.dataBlocks = new ArrayList<>();
  58.     }

  59.     /**
  60.      * Format the integer value as a string, or the string <code>VALUE_NOT_AVAILABLE</code>.
  61.      * @param value the value
  62.      * @param valueNotAvailable the value means not available
  63.      * @return a string
  64.      * @since 12.0
  65.      */
  66.     public static String formatIntegerOrNaN(final int value, final int valueNotAvailable) {
  67.         return value == valueNotAvailable ? STR_VALUE_NOT_AVAILABLE : String.format(Locale.US, "%d", value);
  68.     }

  69.     /**
  70.      * Replace all " NaN" with " na".
  71.      * @param crdString the original string
  72.      * @return the string
  73.      * @since 12.0
  74.      */
  75.     public static String handleNaN(final String crdString) {
  76.         return PATTERN_NAN.matcher(crdString).replaceAll(STR_VALUE_NOT_AVAILABLE);
  77.     }

  78.     /**
  79.      * Add a data block to the current list of data blocks.
  80.      * @param dataBlock data block to add
  81.      */
  82.     public void addDataBlock(final CRDDataBlock dataBlock) {
  83.         dataBlocks.add(dataBlock);
  84.     }

  85.     /**
  86.      * Get the comments contained in the file.
  87.      * @return the comments contained in the file
  88.      */
  89.     public List<String> getComments() {
  90.         return comments;
  91.     }

  92.     /**
  93.      * Get the data blocks contain in the file.
  94.      * @return the data blocks contain in the file
  95.      */
  96.     public List<CRDDataBlock> getDataBlocks() {
  97.         return Collections.unmodifiableList(dataBlocks);
  98.     }

  99.     /**
  100.      * Data block containing a set of data contain in the CRD file.
  101.      * <p>
  102.      * A data block consists of a header, configuration data and
  103.      * recorded data (range, angles, meteorological, etc.).
  104.      * </p>
  105.      */
  106.     public static class CRDDataBlock {

  107.         /** Data block header. */
  108.         private CRDHeader header;

  109.         /** Configuration record. */
  110.         private CRDConfiguration configurationRecords;

  111.         /** Range records. */
  112.         private final List<RangeMeasurement> rangeData;

  113.         /** Meteorological records. */
  114.         private final SortedSet<MeteorologicalMeasurement> meteoData;

  115.         /** Pointing angles records. */
  116.         private final List<AnglesMeasurement> anglesData;

  117.         /** RangeSupplement records. */
  118.         private final List<RangeSupplement> rangeSupplementData;

  119.         /** Session statistics record(s). */
  120.         private final List<SessionStatistics> sessionStatisticsData;

  121.         /** Calibration Record(s). */
  122.         private final List<Calibration> calibrationData;

  123.         /** Calibration detail record(s). */
  124.         private final List<CalibrationDetail> calibrationDetailData;

  125.         /**
  126.          * Constructor.
  127.          */
  128.         public CRDDataBlock() {
  129.             // Initialise empty lists
  130.             this.rangeData  = new ArrayList<>();
  131.             this.meteoData  = new TreeSet<>(new ChronologicalComparator());
  132.             this.anglesData = new ArrayList<>();
  133.             this.rangeSupplementData = new ArrayList<>();
  134.             this.sessionStatisticsData = new ArrayList<>();
  135.             this.calibrationData = new ArrayList<>();
  136.             this.calibrationDetailData = new ArrayList<>();
  137.         }

  138.         /**
  139.          * Get the header of the current data block.
  140.          * @return the header of the current data block
  141.          */
  142.         public CRDHeader getHeader() {
  143.             return header;
  144.         }

  145.         /**
  146.          * Set the header for the current data block.
  147.          * @param header the header to set
  148.          */
  149.         public void setHeader(final CRDHeader header) {
  150.             this.header = header;
  151.         }

  152.         /**
  153.          * Get the system configuration records.
  154.          * @return the system configuration records
  155.          */
  156.         public CRDConfiguration getConfigurationRecords() {
  157.             return configurationRecords;
  158.         }

  159.         /**
  160.          * Set the configuration records for the current data block.
  161.          * @param configurationRecords the configuration records to set
  162.          */
  163.         public void setConfigurationRecords(final CRDConfiguration configurationRecords) {
  164.             this.configurationRecords = configurationRecords;
  165.         }

  166.         /**
  167.          * Add an entry to the list of range data.
  168.          * @param range entry to add
  169.          */
  170.         public void addRangeData(final RangeMeasurement range) {
  171.             rangeData.add(range);
  172.         }

  173.         /**
  174.          * Add an entry to the list of meteorological data.
  175.          * @param meteorologicalMeasurement entry to add
  176.          */
  177.         public void addMeteoData(final MeteorologicalMeasurement meteorologicalMeasurement) {
  178.             meteoData.add(meteorologicalMeasurement);
  179.         }

  180.         /**
  181.          * Add an entry to the list of angles data.
  182.          * @param angles entry to add
  183.          */
  184.         public void addAnglesData(final AnglesMeasurement angles) {
  185.             anglesData.add(angles);
  186.         }

  187.         /**
  188.          * Get the range data for the data block.
  189.          * @return an unmodifiable list of range data
  190.          */
  191.         public List<RangeMeasurement> getRangeData() {
  192.             return Collections.unmodifiableList(rangeData);
  193.         }

  194.         /**
  195.          * Get the angles data for the data block.
  196.          * @return an unmodifiable list of angles data
  197.          */
  198.         public List<AnglesMeasurement> getAnglesData() {
  199.             return Collections.unmodifiableList(anglesData);
  200.         }

  201.         /**
  202.          * Get the meteorological data for the data block.
  203.          * @return an unmodifiable list of meteorological data
  204.          */
  205.         public Meteo getMeteoData() {
  206.             return new Meteo(meteoData);
  207.         }

  208.         /**
  209.          * Add an entry to the list of range supplement data.
  210.          * @param rangeSupplement entry to add
  211.          * @since 12.0
  212.          */
  213.         public void addRangeSupplementData(final RangeSupplement rangeSupplement) {
  214.             rangeSupplementData.add(rangeSupplement);
  215.         }

  216.         /**
  217.          * Get the range supplement data for the data block.
  218.          * @return an unmodifiable list of range supplement data
  219.          * @since 12.0
  220.          */
  221.         public List<RangeSupplement> getRangeSupplementData() {
  222.             return Collections.unmodifiableList(rangeSupplementData);
  223.         }

  224.         /**
  225.          * Add an entry to the list of session statistics data.
  226.          * @param sessionStatistics entry to add
  227.          * @since 12.0
  228.          */
  229.         public void addSessionStatisticsData(final SessionStatistics sessionStatistics) {
  230.             sessionStatisticsData.add(sessionStatistics);
  231.         }

  232.         /**
  233.          * Get the session statistics data for the data block.
  234.          * @return an unmodifiable list of session statistics data
  235.          * @since 12.0
  236.          */
  237.         public List<SessionStatistics> getSessionStatisticsData() {
  238.             return Collections.unmodifiableList(sessionStatisticsData);
  239.         }

  240.         /**
  241.          * Get the default (the first if there are many records) SessionStat record.
  242.          * @return the default (the first if there are many records) session statistics record
  243.          * @since 12.0
  244.          */
  245.         public SessionStatistics getSessionStatisticsRecord() {
  246.             return getSessionStatisticsRecord(null);
  247.         }

  248.         /**
  249.          * Get the session statistics record related to the systemConfigurationId.
  250.          * @param systemConfigurationId system configuration ID
  251.          * @return the session statistics record
  252.          * @since 12.0
  253.          */
  254.         public SessionStatistics getSessionStatisticsRecord(final String systemConfigurationId) {
  255.             if (sessionStatisticsData.isEmpty()) {
  256.                 return null;
  257.             }

  258.             if (systemConfigurationId == null) {
  259.                 // default (the first one)
  260.                 return sessionStatisticsData.get(0);
  261.             }

  262.             // Loop to find the appropriate one
  263.             for (SessionStatistics sessionStatistics : sessionStatisticsData) {
  264.                 if (systemConfigurationId.equalsIgnoreCase(sessionStatistics.getSystemConfigurationId())) {
  265.                     return sessionStatistics;
  266.                 }
  267.             }

  268.             return null;
  269.         }

  270.         /**
  271.          * Add an entry to the list of calibration data.
  272.          * @param cal entry to add
  273.          * @since 12.0
  274.          */
  275.         public void addCalibrationData(final Calibration cal) {
  276.             calibrationData.add(cal);
  277.         }

  278.         /**
  279.          * Get the calibration data for the data block.
  280.          * @return an unmodifiable list of calibration data
  281.          * @since 12.0
  282.          */
  283.         public List<Calibration> getCalibrationData() {
  284.             return Collections.unmodifiableList(calibrationData);
  285.         }

  286.         /**
  287.          * Get the Calibration record(s) related to the default system configuration id.
  288.          * @return the Calibration record(s) related to the default system configuration id
  289.          * @since 12.0
  290.          */
  291.         public List<Calibration> getCalibrationRecords() {
  292.             return getCalibrationRecords(null);
  293.         }

  294.         /**
  295.          * Get the Calibration record(s) related to the given systemConfigurationId.
  296.          * @param systemConfigurationId system configuration ID
  297.          * @return the Calibration record(s)
  298.          * @since 12.0
  299.          */
  300.         public List<Calibration> getCalibrationRecords(final String systemConfigurationId) {
  301.             if (calibrationData.isEmpty()) {
  302.                 return null;
  303.             }

  304.             final String systemConfigId = systemConfigurationId == null ? getConfigurationRecords().getSystemRecord().getConfigurationId() : systemConfigurationId;

  305.             final List<Calibration> list = new ArrayList<>();
  306.             // Loop to find the appropriate one
  307.             for (Calibration calibration : calibrationData) {
  308.                 if (systemConfigId.equalsIgnoreCase(calibration.getSystemConfigurationId())) {
  309.                     list.add(calibration);
  310.                 }
  311.             }

  312.             return list;
  313.         }

  314.         /**
  315.          * Add an entry to the list of calibration detail data.
  316.          * @param cal entry to add
  317.          * @since 12.0
  318.          */
  319.         public void addCalibrationDetailData(final CalibrationDetail cal) {
  320.             calibrationDetailData.add(cal);
  321.         }

  322.         /**
  323.          * Get the calibration detail data for the data block.
  324.          * @return an unmodifiable list of calibration detail data
  325.          * @since 12.0
  326.          */
  327.         public List<CalibrationDetail> getCalibrationDetailData() {
  328.             return Collections.unmodifiableList(calibrationDetailData);
  329.         }

  330.         /**
  331.          * Get the CalibrationDetail record(s) related to the default system configuration id.
  332.          * @return the CalibrationDetail record(s) related to the default system configuration id
  333.          * @since 12.0
  334.          */
  335.         public List<CalibrationDetail> getCalibrationDetailRecords() {
  336.             return getCalibrationDetailRecords(null);
  337.         }

  338.         /**
  339.          * Get the CalibrationDetail record(s) related to the given systemConfigurationId.
  340.          * @param systemConfigurationId system configuration ID
  341.          * @return the CalibrationDetail record(s)
  342.          * @since 12.0
  343.          */
  344.         public List<CalibrationDetail> getCalibrationDetailRecords(final String systemConfigurationId) {
  345.             if (calibrationDetailData.isEmpty()) {
  346.                 return null;
  347.             }

  348.             final String systemConfigId = systemConfigurationId == null ? getConfigurationRecords().getSystemRecord().getConfigurationId() : systemConfigurationId;

  349.             final List<CalibrationDetail> list = new ArrayList<>();
  350.             // Loop to find the appropriate one
  351.             for (CalibrationDetail calibration : calibrationDetailData) {
  352.                 if (systemConfigId.equalsIgnoreCase(calibration.getSystemConfigurationId())) {
  353.                     list.add(calibration);
  354.                 }
  355.             }

  356.             return list;
  357.         }

  358.         /**
  359.          * Get the wavelength related to the given RangeMeasurement.
  360.          *
  361.          * @param range a RangeMeasurement
  362.          * @return the wavelength related to the given RangeMeasurement.
  363.          * @since 12.0
  364.          */
  365.         public double getWavelength(final RangeMeasurement range) {
  366.             return getConfigurationRecords().getSystemRecord(range.getSystemConfigurationId()).getWavelength();
  367.         }

  368.     }

  369.     /** Range record. */
  370.     public static class RangeMeasurement implements TimeStamped {

  371.         /** Data epoch. */
  372.         private final AbsoluteDate date;

  373.         /** Time of flight [s]. */
  374.         private final double timeOfFlight;

  375.         /** System configuration ID. */
  376.         private final String systemConfigurationId;

  377.         /** Time event reference indicator.
  378.          * 0 = ground receive time (at SRP) (two-way)
  379.          * 1 = spacecraft bounce time (two-way)
  380.          * 2 = ground transmit time (at SRP) (two-way)
  381.          * 3 = spacecraft receive time (one-way)
  382.          * 4 = spacecraft transmit time (one-way)
  383.          * 5 = ground transmit time (at SRP) and spacecraft receive time (one-way)
  384.          * 6 = spacecraft transmit time and ground receive time (at SRP) (one-way)
  385.          * Currently, only 1 and 2 are used for laser ranging data.
  386.          */
  387.         private final int epochEvent;

  388.         /** Signal to noise ration. */
  389.         private final double snr;

  390.         /**
  391.          * Constructor.
  392.          * @param date data epoch
  393.          * @param timeOfFlight time of flight in seconds
  394.          * @param epochEvent indicates the time event reference
  395.          */
  396.         public RangeMeasurement(final AbsoluteDate date,
  397.                                 final double timeOfFlight,
  398.                                 final int epochEvent) {
  399.             this(date, timeOfFlight, epochEvent, Double.NaN);
  400.         }

  401.         /**
  402.          * Constructor.
  403.          * @param date data epoch
  404.          * @param timeOfFlight time of flight in seconds
  405.          * @param epochEvent indicates the time event reference
  406.          * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
  407.          */
  408.         public RangeMeasurement(final AbsoluteDate date,
  409.                                 final double timeOfFlight,
  410.                                 final int epochEvent, final double snr) {
  411.             this(date, timeOfFlight, epochEvent, snr, null);
  412.         }

  413.         /**
  414.          * Constructor.
  415.          * @param date data epoch
  416.          * @param timeOfFlight time of flight in seconds
  417.          * @param epochEvent indicates the time event reference
  418.          * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
  419.          * @param systemConfigurationId system configuration id
  420.          * @since 12.0
  421.          */
  422.         public RangeMeasurement(final AbsoluteDate date,
  423.                                 final double timeOfFlight, final int epochEvent,
  424.                                 final double snr,
  425.                                 final String systemConfigurationId) {
  426.             this.date                  = date;
  427.             this.timeOfFlight          = timeOfFlight;
  428.             this.epochEvent            = epochEvent;
  429.             this.snr                   = snr;
  430.             this.systemConfigurationId = systemConfigurationId;
  431.         }

  432.         /**
  433.          * Get the time-of-flight.
  434.          * @return the time-of-flight in seconds
  435.          */
  436.         public double getTimeOfFlight() {
  437.             return timeOfFlight;
  438.         }

  439.         /**
  440.          * Get the indicator for the time event reference.
  441.          * <ul>
  442.          * <li>0 = ground receive time (at SRP) (two-way)</li>
  443.          * <li>1 = spacecraft bounce time (two-way)</li>
  444.          * <li>2 = ground transmit time (at SRP) (two-way)</li>
  445.          * <li>3 = spacecraft receive time (one-way)</li>
  446.          * <li>4 = spacecraft transmit time (one-way)</li>
  447.          * <li>5 = ground transmit time (at SRP) and spacecraft receive time (one-way)</li>
  448.          * <li>6 = spacecraft transmit time and ground receive time (at SRP) (one-way)</li>
  449.          * </ul>
  450.          * Currently, only 1 and 2 are used for laser ranging data
  451.          * @return the indicator for the time event reference
  452.          */
  453.         public int getEpochEvent() {
  454.             return epochEvent;
  455.         }

  456.         /**
  457.          * Get the signal to noise ratio.
  458.          * @return the signal to noise ratio
  459.          */
  460.         public double getSnr() {
  461.             return snr;
  462.         }

  463.         /** {@inheritDoc} */
  464.         @Override
  465.         public AbsoluteDate getDate() {
  466.             return date;
  467.         }

  468.         /**
  469.          * Get the system configuration id.
  470.          * @return the system configuration id
  471.          * @since 12.0
  472.          */
  473.         public String getSystemConfigurationId() {
  474.             return systemConfigurationId;
  475.         }

  476.         /**
  477.          * Get a string representation of the instance in the CRD format.
  478.          * @return a string representation of the instance, in the CRD format.
  479.          * @since 12.0
  480.          */
  481.         public String toCrdString() {
  482.             return "00 not supported. use NptRangeMeasurement or FrRangeMeasurement instead.";
  483.         }
  484.     }

  485.     /**
  486.      * Range record -- Full rate, Sampled Engineering/Quicklook.
  487.      * @since 12.0
  488.      */
  489.     public static class FrRangeMeasurement extends RangeMeasurement {

  490.         /** Filter flag. **/
  491.         private final int filterFlag;

  492.         /** Detector channel. **/
  493.         private final int detectorChannel;

  494.         /** Stop number (in multiple-stop system). **/
  495.         private final int stopNumber;

  496.         /** Receive amplitude - a positive linear scale value. **/
  497.         private final int receiveAmplitude;

  498.         /** Transmit amplitude - a positive linear scale value. **/
  499.         private final int transmitAmplitude;

  500.         /**
  501.          * Constructor.
  502.          * @param date data epoch
  503.          * @param timeOfFlight time of flight in seconds
  504.          * @param epochEvent indicates the time event reference
  505.          * @param systemConfigurationId system configuration id
  506.          * @param filterFlag filter flag
  507.          * @param detectorChannel detector channel
  508.          * @param stopNumber stop number
  509.          * @param receiveAmplitude receive amplitude
  510.          * @param transmitAmplitude transmit amplitude
  511.          */
  512.         public FrRangeMeasurement(final AbsoluteDate date,
  513.                                   final double timeOfFlight,
  514.                                   final int epochEvent,
  515.                                   final String systemConfigurationId,
  516.                                   final int filterFlag,
  517.                                   final int detectorChannel,
  518.                                   final int stopNumber,
  519.                                   final int receiveAmplitude,
  520.                                   final int transmitAmplitude) {
  521.             super(date, timeOfFlight, epochEvent, Double.NaN, systemConfigurationId);
  522.             this.filterFlag        = filterFlag;
  523.             this.detectorChannel   = detectorChannel;
  524.             this.stopNumber        = stopNumber;
  525.             this.receiveAmplitude  = receiveAmplitude;
  526.             this.transmitAmplitude = transmitAmplitude;
  527.         }

  528.         /**
  529.          * Get the filter flag.
  530.          * @return the filter flag
  531.          */
  532.         public int getFilterFlag() {
  533.             return filterFlag;
  534.         }

  535.         /**
  536.          * Get the detector channel.
  537.          * @return the detector channel
  538.          */
  539.         public int getDetectorChannel() {
  540.             return detectorChannel;
  541.         }

  542.         /**
  543.          * Get the stop number.
  544.          * @return the stop number
  545.          */
  546.         public int getStopNumber() {
  547.             return stopNumber;
  548.         }

  549.         /**
  550.          * Get the receive amplitude.
  551.          * @return the receive amplitude, -1 if not measured
  552.          */
  553.         public int getReceiveAmplitude() {
  554.             return receiveAmplitude;
  555.         }

  556.         /**
  557.          * Get the transmit amplitude.
  558.          * @return the transmit amplitude, -1 if not measured
  559.          */
  560.         public int getTransmitAmplitude() {
  561.             return transmitAmplitude;
  562.         }

  563.         /** {@inheritDoc} */
  564.         @Override
  565.         @DefaultDataContext
  566.         public String toCrdString() {
  567.             return String.format(Locale.US, "10 %s", toString());
  568.         }

  569.         @Override
  570.         @DefaultDataContext
  571.         public String toString() {
  572.             // CRD suggested format, excluding the record type
  573.             // 'local' is already utc.
  574.             // Seconds of day (sod) is typically to 1 milllisec precision.
  575.             // receiveAmplitude, transmitAmplitude: -1 if not available
  576.             final double sod = getDate().
  577.                                getComponents(TimeScalesFactory.getUTC()).
  578.                                roundIfNeeded(60, 12).
  579.                                getTime().
  580.                                getSecondsInLocalDay();

  581.             final String str = String.format(Locale.US,
  582.                     "%18.12f %18.12f %4s %1d %1d %1d %1d %5s %5s", sod,
  583.                     getTimeOfFlight(), getSystemConfigurationId(),
  584.                     getEpochEvent(), filterFlag, detectorChannel, stopNumber,
  585.                     formatIntegerOrNaN(receiveAmplitude, -1),
  586.                     formatIntegerOrNaN(transmitAmplitude, -1));
  587.             return handleNaN(str).replace(',', '.');
  588.         }

  589.     }

  590.     /**
  591.      * Range record -- Normal Point.
  592.      * @since 12.0
  593.      */
  594.     public static class NptRangeMeasurement extends RangeMeasurement {

  595.         /** Normal point window length [s]. */
  596.         private final double windowLength;

  597.         /** Number of raw ranges (after editing) compressed into the normal point. */
  598.         private final int numberOfRawRanges;

  599.         /** Bin RMS from the mean of raw accepted time-of-flight values minus the trend function. */
  600.         private final double binRms;

  601.         /** Bin skew from the mean of raw accepted time-of-flight values minus the trend function. */
  602.         private final double binSkew;

  603.         /** Bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function. */
  604.         private final double binKurtosis;

  605.         /** Bin peak - mean value. */
  606.         private final double binPeakMinusMean;

  607.         /** Return rate [%]. */
  608.         private final double returnRate;

  609.         /** Detector channel. */
  610.         private final int detectorChannel;

  611.         /**
  612.          * Constructor.
  613.          * @param date data epoch
  614.          * @param timeOfFlight time of flight in seconds
  615.          * @param epochEvent indicates the time event reference
  616.          * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
  617.          * @param systemConfigurationId System configuration id
  618.          */
  619.         public NptRangeMeasurement(final AbsoluteDate date,
  620.                                    final double timeOfFlight,
  621.                                    final int epochEvent, final double snr,
  622.                                    final String systemConfigurationId) {
  623.             this(date, timeOfFlight, epochEvent, snr, systemConfigurationId, -1,
  624.                     -1, Double.NaN, Double.NaN, Double.NaN, Double.NaN,
  625.                     Double.NaN, 0);
  626.         }

  627.         /**
  628.          * Constructor.
  629.          * @param date data epoch
  630.          * @param timeOfFlight time of flight in seconds
  631.          * @param epochEvent indicates the time event reference
  632.          * @param snr signal to noise ratio (can be Double.NaN if unkonwn)
  633.          * @param systemConfigurationId System configuration id
  634.          * @param windowLength normal point window length
  635.          * @param numberOfRawRanges number of raw ranges (after editing) compressed into the normal point
  636.          * @param binRms Bin RMS from the mean of raw accepted time-of-flight values minus the trend function
  637.          * @param binSkew Bin skew from the mean of raw accepted time-of-flight values minus the trend function
  638.          * @param binKurtosis Bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function
  639.          * @param binPeakMinusMean Bin peak - mean value
  640.          * @param returnRate Return rate [%]
  641.          * @param detectorChannel detector channel
  642.          */
  643.         public NptRangeMeasurement(final AbsoluteDate date,
  644.                                    final double timeOfFlight,
  645.                                    final int epochEvent, final double snr,
  646.                                    final String systemConfigurationId,
  647.                                    final double windowLength,
  648.                                    final int numberOfRawRanges,
  649.                                    final double binRms, final double binSkew,
  650.                                    final double binKurtosis,
  651.                                    final double binPeakMinusMean,
  652.                                    final double returnRate,
  653.                                    final int detectorChannel) {
  654.             super(date, timeOfFlight, epochEvent, snr, systemConfigurationId);

  655.             this.windowLength      = windowLength;
  656.             this.numberOfRawRanges = numberOfRawRanges;
  657.             this.binSkew           = binSkew;
  658.             this.binKurtosis       = binKurtosis;
  659.             this.binPeakMinusMean  = binPeakMinusMean;
  660.             this.detectorChannel   = detectorChannel;
  661.             this.binRms            = binRms == -1.0e-12 ? Double.NaN : binRms; // -1=na, ps --> s
  662.             this.returnRate        = returnRate == -1 ? Double.NaN : returnRate; // -1=na
  663.         }

  664.         /**
  665.          * Get the normal point window length.
  666.          * @return the normal point window length
  667.          */
  668.         public double getWindowLength() {
  669.             return windowLength;
  670.         }

  671.         /**
  672.          * Get the umber of raw ranges (after editing) compressed into the normal point.
  673.          * @return the umber of raw ranges
  674.          */
  675.         public int getNumberOfRawRanges() {
  676.             return numberOfRawRanges;
  677.         }

  678.         /**
  679.          * Get the bin RMS from the mean of raw accepted time-of-flight values minus the trend function.
  680.          * @return the bin RMS
  681.          */
  682.         public double getBinRms() {
  683.             return binRms;
  684.         }

  685.         /**
  686.          * Get the bin skew from the mean of raw accepted time-of-flight values minus the trend function.
  687.          * @return the bin skew
  688.          */
  689.         public double getBinSkew() {
  690.             return binSkew;
  691.         }

  692.         /**
  693.          * Get the bin kurtosis from the mean of raw accepted time-of-flight values minus the trend function.
  694.          * @return the bin kurtosis
  695.          */
  696.         public double getBinKurtosis() {
  697.             return binKurtosis;
  698.         }

  699.         /**
  700.          * Get the bin peak - mean value.
  701.          * @return the bin peak - mean value
  702.          */
  703.         public double getBinPeakMinusMean() {
  704.             return binPeakMinusMean;
  705.         }

  706.         /**
  707.          * Get the return rate.
  708.          * @return the return rate
  709.          */
  710.         public double getReturnRate() {
  711.             return returnRate;
  712.         }

  713.         /**
  714.          * Get the detector channel.
  715.          * @return the detector channel
  716.          */
  717.         public int getDetectorChannel() {
  718.             return detectorChannel;
  719.         }

  720.         /** {@inheritDoc} */
  721.         @Override
  722.         @DefaultDataContext
  723.         public String toCrdString() {
  724.             return String.format(Locale.US, "11 %s", toString());
  725.         }

  726.         @Override
  727.         @DefaultDataContext
  728.         public String toString() {
  729.             // CRD suggested format, excluding the record type
  730.             // binRms, binPeakMinusMean: s --> ps
  731.             // 'local' is already utc.
  732.             // Seconds of day (sod) is typically to 1 milllisec precision.
  733.             final double sod = getDate().
  734.                                getComponents(TimeScalesFactory.getUTC()).
  735.                                roundIfNeeded(60, 12).
  736.                                getTime().
  737.                                getSecondsInLocalDay();

  738.             final String str = String.format(Locale.US,
  739.                     "%18.12f %18.12f %4s %1d %6.1f %6d %9.1f %7.3f %7.3f %9.1f %5.2f %1d %5.1f",
  740.                     sod, getTimeOfFlight(), getSystemConfigurationId(),
  741.                     getEpochEvent(), windowLength, numberOfRawRanges,
  742.                     binRms * 1e12, binSkew, binKurtosis,
  743.                     binPeakMinusMean * 1e12, returnRate, detectorChannel,
  744.                     getSnr());
  745.             return handleNaN(str).replace(',', '.');
  746.         }

  747.     }

  748.     /**
  749.      * Range Supplement Record.
  750.      * @since 12.0
  751.      */
  752.     public static class RangeSupplement implements TimeStamped {

  753.         /** Data epoch. */
  754.         private final AbsoluteDate date;

  755.         /** System configuration ID. */
  756.         private final String systemConfigurationId;

  757.         /** Tropospheric refraction correction (one-way). */
  758.         private final double troposphericRefractionCorrection;

  759.         /** Target center of mass correction (one-way). */
  760.         private final double centerOfMassCorrection;

  761.         /** Neutral density (ND) filter value. */
  762.         private final double ndFilterValue;

  763.         /** Time bias applied. */
  764.         private final double timeBiasApplied;

  765.         /** Range rate. */
  766.         private final double rangeRate;

  767.         /**
  768.          * Constructor.
  769.          * @param date data epoch
  770.          * @param systemConfigurationId system configuration ID
  771.          * @param troposphericRefractionCorrection tropospheric refraction correction (one-way)
  772.          * @param centerOfMassCorrection target center of mass correction (one-way)
  773.          * @param ndFilterValue Neutral density (ND) filter value
  774.          * @param timeBiasApplied Time bias applied
  775.          * @param rangeRate Range rate
  776.          */
  777.         public RangeSupplement(final AbsoluteDate date,
  778.                                final String systemConfigurationId,
  779.                                final double troposphericRefractionCorrection,
  780.                                final double centerOfMassCorrection,
  781.                                final double ndFilterValue,
  782.                                final double timeBiasApplied,
  783.                                final double rangeRate) {
  784.             this.date                             = date;
  785.             this.systemConfigurationId            = systemConfigurationId;
  786.             this.troposphericRefractionCorrection = troposphericRefractionCorrection;
  787.             this.centerOfMassCorrection           = centerOfMassCorrection;
  788.             this.ndFilterValue                    = ndFilterValue;
  789.             this.timeBiasApplied                  = timeBiasApplied;
  790.             this.rangeRate                        = rangeRate;
  791.         }

  792.         @Override
  793.         public AbsoluteDate getDate() {
  794.             return date;
  795.         }

  796.         /**
  797.          * Get the system configuration id.
  798.          * @return the system configuration id
  799.          */
  800.         public String getSystemConfigurationId() {
  801.             return systemConfigurationId;
  802.         }

  803.         /**
  804.          * Get the tropospheric refraction correction.
  805.          * @return the tropospheric refraction correction
  806.          */
  807.         public double getTroposphericRefractionCorrection() {
  808.             return troposphericRefractionCorrection;
  809.         }

  810.         /**
  811.          * Get the target center of mass.
  812.          * @return the target center of mass
  813.          */
  814.         public double getCenterOfMassCorrection() {
  815.             return centerOfMassCorrection;
  816.         }

  817.         /**
  818.          * Get the neutral density (ND) filter value.
  819.          * @return the neutral density (ND) filter value
  820.          */
  821.         public double getNdFilterValue() {
  822.             return ndFilterValue;
  823.         }

  824.         /**
  825.          * Get the time bias applied.
  826.          * @return the time bias applied
  827.          */
  828.         public double getTimeBiasApplied() {
  829.             return timeBiasApplied;
  830.         }

  831.         /**
  832.          * Get the range rate.
  833.          * @return the range rate
  834.          */
  835.         public double getRangeRate() {
  836.             return rangeRate;
  837.         }

  838.         /**
  839.          * Get a string representation of the instance in the CRD format.
  840.          * @return a string representation of the instance, in the CRD format.
  841.          */
  842.         @DefaultDataContext
  843.         public String toCrdString() {
  844.             return String.format(Locale.US, "12 %s", toString());
  845.         }

  846.         @Override
  847.         @DefaultDataContext
  848.         public String toString() {
  849.             // CRD suggested format, excluding the record type
  850.             // troposphericRefractionCorrection: s --> ps
  851.             // 'local' is already utc.
  852.             // Seconds of day (sod) is typically to 1 milllisec precision.
  853.             final double sod = getDate().
  854.                                getComponents(TimeScalesFactory.getUTC()).
  855.                                roundIfNeeded(60, 12).
  856.                                getTime().
  857.                                getSecondsInLocalDay();

  858.             final String str = String.format(Locale.US,
  859.                     "%18.12f %4s %6.1f %6.4f %5.2f %8.4f %f", sod,
  860.                     getSystemConfigurationId(),
  861.                     troposphericRefractionCorrection * 1e12,
  862.                     centerOfMassCorrection, ndFilterValue, timeBiasApplied,
  863.                     rangeRate);
  864.             return handleNaN(str).replace(',', '.');
  865.         }

  866.     }

  867.     /** This data record contains a minimal set of meteorological data. */
  868.     public static class MeteorologicalMeasurement implements TimeStamped {

  869.         /** Data epoch. */
  870.         private final AbsoluteDate date;

  871.         /** Surface pressure [bar]. */
  872.         private final double pressure;

  873.         /** Surface temperature [K]. */
  874.         private final double temperature;

  875.         /** Relative humidity at the surface [%]. */
  876.         private final double humidity;

  877.         /** Origin of values.
  878.          * 0=measured values, 1=interpolated values
  879.          */
  880.         private final int originOfValues;

  881.         /**
  882.          * Constructor.
  883.          * @param date data epoch
  884.          * @param pressure the surface pressure in bars
  885.          * @param temperature the surface temperature in degrees Kelvin
  886.          * @param humidity the relative humidity at the surface in percents
  887.          */
  888.         public MeteorologicalMeasurement(final AbsoluteDate date,
  889.                                          final double pressure, final double temperature,
  890.                                          final double humidity) {
  891.             this(date, pressure, temperature, humidity, 0);
  892.         }

  893.         /**
  894.          * Constructor.
  895.          * @param date data epoch
  896.          * @param pressure the surface pressure in bars
  897.          * @param temperature the surface temperature in degrees Kelvin
  898.          * @param humidity the relative humidity at the surface in percents
  899.          * @param originOfValues Origin of values
  900.          */
  901.         public MeteorologicalMeasurement(final AbsoluteDate date, final double pressure, final double temperature,
  902.                                          final double humidity, final int originOfValues) {
  903.             this.date           = date;
  904.             this.pressure       = pressure;
  905.             this.temperature    = temperature;
  906.             this.humidity       = humidity;
  907.             this.originOfValues = originOfValues;
  908.         }

  909.         /**
  910.          * Get the surface pressure.
  911.          * @return the surface pressure in bars
  912.          */
  913.         public double getPressure() {
  914.             return pressure;
  915.         }

  916.         /**
  917.          * Get the surface temperature.
  918.          * @return the surface temperature in degrees Kelvin
  919.          */
  920.         public double getTemperature() {
  921.             return temperature;
  922.         }

  923.         /**
  924.          * Get the relative humidity at the surface.
  925.          * @return the relative humidity at the surface in percents
  926.          */
  927.         public double getHumidity() {
  928.             return humidity;
  929.         }

  930.         /** {@inheritDoc} */
  931.         @Override
  932.         public AbsoluteDate getDate() {
  933.             return date;
  934.         }

  935.         /** Get the origin of values.
  936.          * 0=measure values
  937.          * 1=interpolated values
  938.          * @return the origin of values
  939.          * @since 12.0
  940.          */
  941.         public int getOriginOfValues() {
  942.             return originOfValues;
  943.         }

  944.         /**
  945.          * Get a string representation of the instance in the CRD format.
  946.          * @return a string representation of the instance, in the CRD format.
  947.          * @since 12.0
  948.          */
  949.         @DefaultDataContext
  950.         public String toCrdString() {
  951.             return String.format(Locale.US, "20 %s", toString());
  952.         }

  953.         @Override
  954.         @DefaultDataContext
  955.         public String toString() {
  956.             // CRD suggested format, excluding the record type
  957.             // pressure: bar --> mbar
  958.             // 'local' is already utc.
  959.             // Seconds of day (sod) is typically to 1 milllisec precision.
  960.             final double sod = getDate().
  961.                                getComponents(TimeScalesFactory.getUTC()).
  962.                                roundIfNeeded(60, 3).
  963.                                getTime().
  964.                                getSecondsInLocalDay();

  965.             final String str = String.format(Locale.US, "%9.3f %7.2f %6.2f %4.0f %1d",
  966.                                              sod, pressure * 1e3, temperature, humidity, originOfValues);
  967.             return handleNaN(str).replace(',', '.');
  968.         }
  969.     }

  970.     /** Pointing angles record. */
  971.     public static class AnglesMeasurement implements TimeStamped {

  972.         /** Data epoch. */
  973.         private final AbsoluteDate date;

  974.         /** Azimuth [rad]. */
  975.         private final double azimuth;

  976.         /** Elevation [rad]. */
  977.         private final double elevation;

  978.         /** Direction flag (0 = transmit &#38; receive ; 1 = transmit ; 2 = receive). */
  979.         private final int directionFlag;

  980.         /** Angle origin indicator.
  981.          * 0 = unknown
  982.          * 1 = computed
  983.          * 2 = commanded (from predictions)
  984.          * 3 = measured (from encoders)
  985.          */
  986.         private final int originIndicator;

  987.         /** Refraction corrected. */
  988.         private final boolean refractionCorrected;

  989.         /** Azimuth rate [rad/sec]. */
  990.         private final double azimuthRate;

  991.         /** Elevation rate [rad/sec]. */
  992.         private final double elevationRate;

  993.         /**
  994.          * Constructor.
  995.          * @param date data epoch
  996.          * @param azimuth azimuth angle in radians
  997.          * @param elevation elevation angle in radians
  998.          * @param directionFlag direction flag
  999.          * @param originIndicator angle origin indicator
  1000.          * @param refractionCorrected flag to indicate if the refraction is corrected
  1001.          * @param azimuthRate azimuth rate in radians per second (equal to Double.NaN if unknown)
  1002.          * @param elevationRate elevation rate in radians per second (equal to Double.NaN if unknown)
  1003.          */
  1004.         public AnglesMeasurement(final AbsoluteDate date, final double azimuth,
  1005.                                  final double elevation, final int directionFlag,
  1006.                                  final int originIndicator,
  1007.                                  final boolean refractionCorrected,
  1008.                                  final double azimuthRate, final double elevationRate) {
  1009.             this.date                = date;
  1010.             this.azimuth             = azimuth;
  1011.             this.elevation           = elevation;
  1012.             this.directionFlag       = directionFlag;
  1013.             this.originIndicator     = originIndicator;
  1014.             this.refractionCorrected = refractionCorrected;
  1015.             this.azimuthRate         = azimuthRate;
  1016.             this.elevationRate       = elevationRate;
  1017.         }

  1018.         /**
  1019.          * Get the azimuth angle.
  1020.          * @return the azimuth angle in radians
  1021.          */
  1022.         public double getAzimuth() {
  1023.             return azimuth;
  1024.         }

  1025.         /**
  1026.          * Get the elevation angle.
  1027.          * @return the elevation angle in radians
  1028.          */
  1029.         public double getElevation() {
  1030.             return elevation;
  1031.         }

  1032.         /**
  1033.          * Get the direction flag (0 = transmit &#38; receive ; 1 = transmit ; 2 = receive).
  1034.          * @return the direction flag
  1035.          */
  1036.         public int getDirectionFlag() {
  1037.             return directionFlag;
  1038.         }

  1039.         /**
  1040.          * Get the angle origin indicator.
  1041.          * <p>
  1042.          * 0 = unknown;
  1043.          * 1 = computed;
  1044.          * 2 = commanded (from predictions);
  1045.          * 3 = measured (from encoders)
  1046.          * </p>
  1047.          * @return the angle origin indicator
  1048.          */
  1049.         public int getOriginIndicator() {
  1050.             return originIndicator;
  1051.         }

  1052.         /**
  1053.          * Get the flag indicating if the refraction is corrected.
  1054.          * @return true if refraction is corrected
  1055.          */
  1056.         public boolean isRefractionCorrected() {
  1057.             return refractionCorrected;
  1058.         }

  1059.         /**
  1060.          * Get the azimuth rate.
  1061.          * <p>
  1062.          * Is equal to Double.NaN if the value is unknown.
  1063.          * </p>
  1064.          * @return the azimuth rate in radians per second
  1065.          */
  1066.         public double getAzimuthRate() {
  1067.             return azimuthRate;
  1068.         }

  1069.         /**
  1070.          * Get the elevation rate.
  1071.          * <p>
  1072.          * Is equal to Double.NaN if the value is unknown.
  1073.          * </p>
  1074.          * @return the elevation rate in radians per second
  1075.          */
  1076.         public double getElevationRate() {
  1077.             return elevationRate;
  1078.         }

  1079.         /** {@inheritDoc} */
  1080.         @Override
  1081.         public AbsoluteDate getDate() {
  1082.             return date;
  1083.         }

  1084.         /**
  1085.          * Get a string representation of the instance in the CRD format.
  1086.          * @return a string representation of the instance, in the CRD format.
  1087.          * @since 12.0
  1088.          */
  1089.         @DefaultDataContext
  1090.         public String toCrdString() {
  1091.             return String.format(Locale.US, "30 %s", toString());
  1092.         }

  1093.         @Override
  1094.         @DefaultDataContext
  1095.         public String toString() {
  1096.             // CRD suggested format, excluding the record type
  1097.             // azimuth, elevation: rad --> deg
  1098.             // azimuthRate, elevationRate: rad/s --> deg/s
  1099.             // 'local' is already utc.
  1100.             // Seconds of day (sod) is typically to 1 milllisec precision.
  1101.             final double sod = getDate().
  1102.                                getComponents(TimeScalesFactory.getUTC()).
  1103.                                roundIfNeeded(60, 3).
  1104.                                getTime().
  1105.                                getSecondsInLocalDay();

  1106.             final String str = String.format(Locale.US,
  1107.                                              "%9.3f %8.4f %8.4f %1d %1d %1d %10.7f %10.7f",
  1108.                                              sod,
  1109.                                              FastMath.toDegrees(azimuth), FastMath.toDegrees(elevation),
  1110.                                              directionFlag, originIndicator, refractionCorrected ? 1 : 0,
  1111.                                              FastMath.toDegrees(azimuthRate),
  1112.                                              FastMath.toDegrees(elevationRate));
  1113.             return handleNaN(str).replace(',', '.');
  1114.         }
  1115.     }

  1116.     /** Meteorological data. */
  1117.     public static class Meteo {

  1118.         /** Number of neighbors for meteo data interpolation. */
  1119.         private static final int N_NEIGHBORS = 2;

  1120.         /** First available date. */
  1121.         private final AbsoluteDate firstDate;

  1122.         /** Last available date. */
  1123.         private final AbsoluteDate lastDate;

  1124.         /** Previous set of meteorological parameters. */
  1125.         private transient MeteorologicalMeasurement previousParam;

  1126.         /** Next set of solar meteorological parameters. */
  1127.         private transient MeteorologicalMeasurement nextParam;

  1128.         /** List of meteo data. */
  1129.         private final transient ImmutableTimeStampedCache<MeteorologicalMeasurement> meteo;

  1130.         /**
  1131.          * Constructor.
  1132.          * @param meteoData list of meteo data
  1133.          */
  1134.         public Meteo(final SortedSet<MeteorologicalMeasurement> meteoData) {

  1135.             // Size
  1136.             final int neighborsSize = (meteoData.size() < 2) ? meteoData.size() : N_NEIGHBORS;

  1137.             // Check neighbors size
  1138.             if (neighborsSize == 0) {

  1139.                 // Meteo data -> empty cache
  1140.                 this.meteo = ImmutableTimeStampedCache.emptyCache();

  1141.                 // Null epochs (will ne be used)
  1142.                 this.firstDate = null;
  1143.                 this.lastDate  = null;

  1144.             } else {

  1145.                 // Meteo data
  1146.                 this.meteo = new ImmutableTimeStampedCache<>(neighborsSize, meteoData);

  1147.                 // Initialize first and last available dates
  1148.                 this.firstDate = meteoData.first().getDate();
  1149.                 this.lastDate  = meteoData.last().getDate();

  1150.             }

  1151.         }

  1152.         /** Get an unmodifiable view of the tabulated meteorological data.
  1153.          * @return unmodifiable view of the tabulated meteorological data
  1154.          * @since 11.0
  1155.          */
  1156.         public List<MeteorologicalMeasurement> getData() {
  1157.             return meteo.getAll();
  1158.         }

  1159.         /**
  1160.          * Get the meteorological parameters at a given date.
  1161.          * @param date date when user wants the meteorological parameters
  1162.          * @return the meteorological parameters at date (can be null if
  1163.          *         meteorological data are empty).
  1164.          */
  1165.         public MeteorologicalMeasurement getMeteo(final AbsoluteDate date) {

  1166.             // Check if meteorological data are available
  1167.             if (meteo.getMaxNeighborsSize() == 0) {
  1168.                 return null;
  1169.             }

  1170.             // Interpolating two neighboring meteorological parameters
  1171.             bracketDate(date);
  1172.             if (date.durationFrom(firstDate) <= 0 || date.durationFrom(lastDate) > 0) {
  1173.                 // Date is outside file range
  1174.                 return previousParam;
  1175.             } else {
  1176.                 // Perform interpolations
  1177.                 final double pressure    = getLinearInterpolation(date, previousParam.getPressure(), nextParam.getPressure());
  1178.                 final double temperature = getLinearInterpolation(date, previousParam.getTemperature(), nextParam.getTemperature());
  1179.                 final double humidity    = getLinearInterpolation(date, previousParam.getHumidity(), nextParam.getHumidity());
  1180.                 return new MeteorologicalMeasurement(date, pressure, temperature, humidity);
  1181.             }

  1182.         }

  1183.         /**
  1184.          * Find the data bracketing a specified date.
  1185.          * @param date date to bracket
  1186.          */
  1187.         private void bracketDate(final AbsoluteDate date) {

  1188.             // don't search if the cached selection is fine
  1189.             if (previousParam != null &&
  1190.                 date.durationFrom(previousParam.getDate()) > 0 &&
  1191.                 date.durationFrom(nextParam.getDate()) <= 0) {
  1192.                 return;
  1193.             }

  1194.             // Initialize previous and next parameters
  1195.             if (date.durationFrom(firstDate) <= 0) {
  1196.                 // Current date is before the first date
  1197.                 previousParam = meteo.getEarliest();
  1198.                 nextParam     = previousParam;
  1199.             } else if (date.durationFrom(lastDate) > 0) {
  1200.                 // Current date is after the last date
  1201.                 previousParam = meteo.getLatest();
  1202.                 nextParam     = previousParam;
  1203.             } else {
  1204.                 // Current date is between first and last date
  1205.                 final List<MeteorologicalMeasurement> neighbors = meteo.getNeighbors(date).collect(Collectors.toList());
  1206.                 previousParam = neighbors.get(0);
  1207.                 nextParam     = neighbors.get(1);
  1208.             }

  1209.         }

  1210.         /**
  1211.          * Performs a linear interpolation between two values The weights are computed
  1212.          * from the time delta between previous date, current date, next date.
  1213.          * @param date the current date
  1214.          * @param previousValue the value at previous date
  1215.          * @param nextValue the value at next date
  1216.          * @return the value interpolated for the current date
  1217.          */
  1218.         private double getLinearInterpolation(final AbsoluteDate date,
  1219.                                               final double previousValue,
  1220.                                               final double nextValue) {
  1221.             // Perform a linear interpolation
  1222.             final AbsoluteDate previousDate = previousParam.getDate();
  1223.             final AbsoluteDate currentDate = nextParam.getDate();
  1224.             final double dt = currentDate.durationFrom(previousDate);
  1225.             final double previousWeight = currentDate.durationFrom(date) / dt;
  1226.             final double nextWeight = date.durationFrom(previousDate) / dt;

  1227.             // Returns the data interpolated at the date
  1228.             return previousValue * previousWeight + nextValue * nextWeight;
  1229.         }

  1230.     }

  1231.     /**
  1232.      * Calibration Record.
  1233.      * @since 12.0
  1234.      */
  1235.     public static class Calibration implements TimeStamped {

  1236.         /** Data epoch. */
  1237.         private final AbsoluteDate date;

  1238.         /**
  1239.          * Type of data.
  1240.          * 0=station combined transmit and receive calibration (“normal” SLR/LLR)
  1241.          * 1=station transmit calibration (e.g., one-way ranging to transponders)
  1242.          * 2=station receive calibration
  1243.          * 3=target combined transmit and receive calibrations
  1244.          * 4=target transmit calibration
  1245.          * 5=target receive calibration
  1246.          */
  1247.         private final int typeOfData;

  1248.         /** System configuration ID. */
  1249.         private final String systemConfigurationId;

  1250.         /** Number of data points recorded. */
  1251.         private final int numberOfPointsRecorded;

  1252.         /** Number of data points used. */
  1253.         private final int numberOfPointsUsed;

  1254.         /** One-way target distance (meters, nominal). */
  1255.         private final double oneWayDistance;

  1256.         /** Calibration System Delay. */
  1257.         private final double systemDelay;

  1258.         /** Calibration Delay Shift - a measure of calibration stability. */
  1259.         private final double delayShift;

  1260.         /** RMS of raw system delay. */
  1261.         private final double rms;

  1262.         /** Skew of raw system delay values from the mean. */
  1263.         private final double skew;

  1264.         /** Kurtosis of raw system delay values from the mean. */
  1265.         private final double kurtosis;

  1266.         /** System delay peak – mean value. */
  1267.         private final double peakMinusMean;

  1268.         /**
  1269.          * Calibration Type Indicator.
  1270.          * 0=not used or undefined
  1271.          * 1=nominal (from once off assessment)
  1272.          * 2=external calibrations
  1273.          * 3=internal calibrations – telescope
  1274.          * 4=internal calibrations – building
  1275.          * 5=burst calibrations
  1276.          * 6=other
  1277.          */
  1278.         private final int typeIndicator;

  1279.         /**
  1280.          * Calibration Shift Type Indicator.
  1281.          * 0=not used or undefined
  1282.          * 1=nominal (from once off assessment)
  1283.          * 2=pre- to post- Shift
  1284.          * 3=minimum to maximum
  1285.          * 4=other
  1286.          */
  1287.         private final int shiftTypeIndicator;

  1288.         /** Detector Channel.
  1289.          * 0=not applicable or “all”
  1290.          * 1-4 for quadrant
  1291.          * 1-n for many channels
  1292.          */
  1293.         private final int detectorChannel;

  1294.         /**
  1295.          * Calibration Span.
  1296.          * 0 = not applicable (e.g. Calibration type indicator is “nominal”)
  1297.          * 1 = Pre-calibration only
  1298.          * 2 = Post-calibration only
  1299.          * 3 = Combined (pre- and post-calibrations or multiple)
  1300.          * 4 = Real-time calibration (data taken while ranging to a satellite)
  1301.          */
  1302.         private final int span;

  1303.         /** Return Rate (%). */
  1304.         private final double returnRate;

  1305.         /**
  1306.          * Constructor.
  1307.          * @param date data epoch
  1308.          * @param typeOfData type of data
  1309.          * @param systemConfigurationId system configuration id
  1310.          * @param numberOfPointsRecorded number of data points recorded
  1311.          * @param numberOfPointsUsed number of data points used
  1312.          * @param oneWayDistance one-way target distance (nominal)
  1313.          * @param systemDelay calibration system delay
  1314.          * @param delayShift calibration delay shift - a measure of calibration stability
  1315.          * @param rms RMS of raw system delay
  1316.          * @param skew skew of raw system delay values from the mean.
  1317.          * @param kurtosis kurtosis of raw system delay values from the mean.
  1318.          * @param peakMinusMean system delay peak – mean value
  1319.          * @param typeIndicator calibration type indicator
  1320.          * @param shiftTypeIndicator calibration shift type indicator
  1321.          * @param detectorChannel detector channel
  1322.          * @param span calibration span
  1323.          * @param returnRate return rate (%)
  1324.          */
  1325.         public Calibration(final AbsoluteDate date, final int typeOfData,
  1326.                            final String systemConfigurationId,
  1327.                            final int numberOfPointsRecorded,
  1328.                            final int numberOfPointsUsed,
  1329.                            final double oneWayDistance,
  1330.                            final double systemDelay, final double delayShift,
  1331.                            final double rms, final double skew,
  1332.                            final double kurtosis, final double peakMinusMean,
  1333.                            final int typeIndicator, final int shiftTypeIndicator,
  1334.                            final int detectorChannel, final int span,
  1335.                            final double returnRate) {
  1336.             this.date                   = date;
  1337.             this.typeOfData             = typeOfData;
  1338.             this.systemConfigurationId  = systemConfigurationId;
  1339.             this.numberOfPointsRecorded = numberOfPointsRecorded;
  1340.             this.numberOfPointsUsed     = numberOfPointsUsed;
  1341.             this.systemDelay            = systemDelay;
  1342.             this.delayShift             = delayShift;
  1343.             this.rms                    = rms;
  1344.             this.skew                   = skew;
  1345.             this.kurtosis               = kurtosis;
  1346.             this.peakMinusMean          = peakMinusMean;
  1347.             this.typeIndicator          = typeIndicator;
  1348.             this.shiftTypeIndicator     = shiftTypeIndicator;
  1349.             this.detectorChannel        = detectorChannel;
  1350.             this.span                   = span;
  1351.             this.returnRate             = returnRate;
  1352.             this.oneWayDistance         = oneWayDistance == -1 ? Double.NaN : oneWayDistance; // -1=na
  1353.         }

  1354.         @Override
  1355.         public AbsoluteDate getDate() {
  1356.             return date;
  1357.         }

  1358.         /**
  1359.          * Get the type of data.
  1360.          *
  1361.          * <ul>
  1362.          * <li>0=station combined transmit and receive calibration (“normal” SLR/LLR)
  1363.          * <li>1=station transmit calibration (e.g., one-way ranging to transponders)
  1364.          * <li>2=station receive calibration
  1365.          * <li>3=target combined transmit and receive calibrations
  1366.          * <li>4=target transmit calibration
  1367.          * <li>5=target receive calibration
  1368.          * </ul>
  1369.          * @return the type of data
  1370.          */
  1371.         public int getTypeOfData() {
  1372.             return typeOfData;
  1373.         }

  1374.         /**
  1375.          * Get the system configuration id.
  1376.          * @return the system configuration id
  1377.          */
  1378.         public String getSystemConfigurationId() {
  1379.             return systemConfigurationId;
  1380.         }

  1381.         /**
  1382.          * Get the number of data points recorded.
  1383.          * @return the number of data points recorded, -1 if no information
  1384.          */
  1385.         public int getNumberOfPointsRecorded() {
  1386.             return numberOfPointsRecorded;
  1387.         }

  1388.         /**
  1389.          * Get the number of data points used.
  1390.          * @return the number of data points used, -1 if no information
  1391.          */
  1392.         public int getNumberOfPointsUsed() {
  1393.             return numberOfPointsUsed;
  1394.         }

  1395.         /**
  1396.          * Get the one-way target distance (nominal).
  1397.          * @return the one-way target distance (nominal)
  1398.          */
  1399.         public double getOneWayDistance() {
  1400.             return oneWayDistance;
  1401.         }

  1402.         /**
  1403.          * Get the calibration system delay.
  1404.          * @return the calibration system delay
  1405.          */
  1406.         public double getSystemDelay() {
  1407.             return systemDelay;
  1408.         }

  1409.         /**
  1410.          * Get the calibration delay shift.
  1411.          * @return the calibration delay shift
  1412.          */
  1413.         public double getDelayShift() {
  1414.             return delayShift;
  1415.         }

  1416.         /**
  1417.          * Get the rms of raw system delay.
  1418.          * @return the rms of raw system delay
  1419.          */
  1420.         public double getRms() {
  1421.             return rms;
  1422.         }

  1423.         /**
  1424.          * Get the skew of raw system delay values from the mean.
  1425.          * @return the skew of raw system delay values from the mean.
  1426.          */
  1427.         public double getSkew() {
  1428.             return skew;
  1429.         }

  1430.         /**
  1431.          * Get the kurtosis of raw system delay values from the mean.
  1432.          * @return the kurtosis of raw system delay values from the mean.
  1433.          */
  1434.         public double getKurtosis() {
  1435.             return kurtosis;
  1436.         }

  1437.         /**
  1438.          * Get the system delay peak – mean value.
  1439.          * @return the system delay peak – mean value
  1440.          */
  1441.         public double getPeakMinusMean() {
  1442.             return peakMinusMean;
  1443.         }

  1444.         /**
  1445.          * Get the calibration type indicator.
  1446.          *
  1447.          * <ul>
  1448.          * <li>0=not used or undefined
  1449.          * <li>1=nominal (from once off assessment)
  1450.          * <li>2=external calibrations
  1451.          * <li>3=internal calibrations – telescope
  1452.          * <li>4=internal calibrations – building
  1453.          * <li>5=burst calibrations
  1454.          * <li>6=other
  1455.          * </ul>
  1456.          * @return the calibration type indicator
  1457.          */
  1458.         public int getTypeIndicator() {
  1459.             return typeIndicator;
  1460.         }

  1461.         /**
  1462.          * Get the calibration shift type indicator.
  1463.          *
  1464.          * <ul>
  1465.          * <li>0=not used or undefined
  1466.          * <li>1=nominal (from once off assessment)
  1467.          * <li>2=pre- to post- Shift
  1468.          * <li>3=minimum to maximum
  1469.          * <li>4=other
  1470.          * </ul>
  1471.          * @return the calibration shift type indicator
  1472.          */
  1473.         public int getShiftTypeIndicator() {
  1474.             return shiftTypeIndicator;
  1475.         }

  1476.         /**
  1477.          * Get the detector channel.
  1478.          *
  1479.          * <ul>
  1480.          * <li>0=not applicable or “all”
  1481.          * <li>1-4 for quadrant
  1482.          * <li>1-n for many channels
  1483.          * </ul>
  1484.          * @return the detector channel
  1485.          */
  1486.         public int getDetectorChannel() {
  1487.             return detectorChannel;
  1488.         }

  1489.         /**
  1490.          * Get the calibration span.
  1491.          *
  1492.          * <ul>
  1493.          * <li>0 = not applicable (e.g. Calibration type indicator is “nominal”)
  1494.          * <li>1 = Pre-calibration only
  1495.          * <li>2 = Post-calibration only
  1496.          * <li>3 = Combined (pre- and post-calibrations or multiple)
  1497.          * <li>4 = Real-time calibration (data taken while ranging to a satellite)
  1498.          * </ul>
  1499.          * @return the calibration span
  1500.          */
  1501.         public int getSpan() {
  1502.             return span;
  1503.         }

  1504.         /**
  1505.          * Get the return rate.
  1506.          * @return the return rate
  1507.          */
  1508.         public double getReturnRate() {
  1509.             return returnRate;
  1510.         }

  1511.         /**
  1512.          * Get a string representation of the instance in the CRD format.
  1513.          * @return a string representation of the instance, in the CRD format.
  1514.          */
  1515.         @DefaultDataContext
  1516.         public String toCrdString() {
  1517.             return String.format(Locale.US, "40 %s", toString());
  1518.         }

  1519.         @Override
  1520.         @DefaultDataContext
  1521.         public String toString() {
  1522.             // CRD suggested format, excluding the record type
  1523.             // systemDelay, delayShift: s --> ps
  1524.             // rms, peakMinusMean: s --> ps
  1525.             // 'local' is already utc.
  1526.             // Seconds of day (sod) is typically to 1 milllisec precision.
  1527.             final double sod = getDate().
  1528.                                getComponents(TimeScalesFactory.getUTC()).
  1529.                                roundIfNeeded(60, 12).
  1530.                                getTime().
  1531.                                getSecondsInLocalDay();

  1532.             final String str = String.format(
  1533.                     "%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",
  1534.                     sod, typeOfData, systemConfigurationId,
  1535.                     formatIntegerOrNaN(numberOfPointsRecorded, -1),
  1536.                     formatIntegerOrNaN(numberOfPointsUsed, -1), oneWayDistance,
  1537.                     systemDelay * 1e12, delayShift * 1e12, rms * 1e12, skew,
  1538.                     kurtosis, peakMinusMean * 1e12, typeIndicator,
  1539.                     shiftTypeIndicator, detectorChannel, span, returnRate);
  1540.             return handleNaN(str).replace(',', '.');
  1541.         }

  1542.     }

  1543.     /**
  1544.      * Calibration Detail Record.
  1545.      * @since 12.0
  1546.      */
  1547.     public static class CalibrationDetail extends Calibration {
  1548.         // same as Calibration record except that the record type is '41' rather than '40'.

  1549.         /**
  1550.          * Constructor.
  1551.          * @param date data epoch
  1552.          * @param typeOfData type of data
  1553.          * @param systemConfigurationId system configuration id
  1554.          * @param numberOfPointsRecorded number of data points recorded
  1555.          * @param numberOfPointsUsed number of data points used
  1556.          * @param oneWayDistance one-way target distance (nominal)
  1557.          * @param systemDelay calibration system delay
  1558.          * @param delayShift calibration delay shift - a measure of calibration stability
  1559.          * @param rms RMS of raw system delay
  1560.          * @param skew skew of raw system delay values from the mean.
  1561.          * @param kurtosis kurtosis of raw system delay values from the mean.
  1562.          * @param peakMinusMean system delay peak – mean value
  1563.          * @param typeIndicator calibration type indicator
  1564.          * @param shiftTypeIndicator calibration shift type indicator
  1565.          * @param detectorChannel detector channel
  1566.          * @param span calibration span
  1567.          * @param returnRate return rate (%)
  1568.          */
  1569.         public CalibrationDetail(final AbsoluteDate date, final int typeOfData,
  1570.                                  final String systemConfigurationId,
  1571.                                  final int numberOfPointsRecorded,
  1572.                                  final int numberOfPointsUsed, final double oneWayDistance,
  1573.                                  final double systemDelay, final double delayShift,
  1574.                                  final double rms, final double skew, final double kurtosis,
  1575.                                  final double peakMinusMean, final int typeIndicator,
  1576.                                  final int shiftTypeIndicator, final int detectorChannel,
  1577.                                  final int span, final double returnRate) {
  1578.             super(date, typeOfData, systemConfigurationId, numberOfPointsRecorded,
  1579.                     numberOfPointsUsed, oneWayDistance, systemDelay, delayShift, rms, skew,
  1580.                     kurtosis, peakMinusMean, typeIndicator, shiftTypeIndicator,
  1581.                     detectorChannel, span, returnRate);
  1582.         }

  1583.         /**
  1584.          * Get a string representation of the instance in the CRD format.
  1585.          * @return a string representation of the instance, in the CRD format.
  1586.          */
  1587.         @DefaultDataContext
  1588.         public String toCrdString() {
  1589.             return String.format(Locale.US, "41 %s", toString());
  1590.         }

  1591.     }

  1592.     /**
  1593.      * Session (Pass) Statistics Record.
  1594.      * @since 12.0
  1595.      */
  1596.     public static class SessionStatistics {

  1597.         /** System configuration ID. */
  1598.         private final String systemConfigurationId;

  1599.         /** Session RMS from the mean of raw accepted time-of-flight values minus the trend function. */
  1600.         private final double rms;

  1601.         /** Session skewness from the mean of raw accepted time-of-flight values minus the trend function. */
  1602.         private final double skewness;

  1603.         /** Session kurtosis from the mean of raw accepted time-of-flight values minus the trend function. */
  1604.         private final double kurtosis;

  1605.         /** Session peak – mean value. */
  1606.         private final double peakMinusMean;

  1607.         /**
  1608.          * Data quality assessment indicator.
  1609.          * <ul>
  1610.          * <li>0=undefined or no comment</li>
  1611.          * <li>1=clear, easily filtered data, with little or no noise</li>
  1612.          * <li>2=clear data with some noise; filtering is slightly compromised by noise level</li>
  1613.          * <li>3=clear data with a significant amount of noise, or weak data with little noise. Data are certainly
  1614.          * present, but filtering is difficult.</li>
  1615.          * <li>4=unclear data; data appear marginally to be present, but are very difficult to separate from noise
  1616.          * during filtering. Signal to noise ratio can be less than 1:1.</li>
  1617.          * <li>5=no data apparent</li>
  1618.          * </ul>
  1619.          */
  1620.         private final int dataQulityIndicator;

  1621.         /**
  1622.          * Constructor.
  1623.          * @param systemConfigurationId system configuration ID
  1624.          * @param rms session RMS from the mean of raw accepted time-of-flight values minus the trend function
  1625.          * @param skewness session skewness from the mean of raw accepted time-of-flight values minus the trend function
  1626.          * @param kurtosis session kurtosis from the mean of raw accepted time-of-flight values minus the trend function
  1627.          * @param peakMinusMean session peak – mean value
  1628.          * @param dataQulityIndicator data quality assessment indicator
  1629.          */
  1630.         public SessionStatistics(final String systemConfigurationId,
  1631.                                  final double rms, final double skewness,
  1632.                                  final double kurtosis,
  1633.                                  final double peakMinusMean,
  1634.                                  final int dataQulityIndicator) {
  1635.             this.systemConfigurationId = systemConfigurationId;
  1636.             this.rms                   = rms;
  1637.             this.skewness              = skewness;
  1638.             this.kurtosis              = kurtosis;
  1639.             this.peakMinusMean         = peakMinusMean;
  1640.             this.dataQulityIndicator   = dataQulityIndicator;
  1641.         }

  1642.         /**
  1643.          * Get system configuration id.
  1644.          * @return the system configuration id
  1645.          */
  1646.         public String getSystemConfigurationId() {
  1647.             return systemConfigurationId;
  1648.         }

  1649.         /**
  1650.          * Get the session RMS from the mean of raw accepted time-of-flight values minus the trend function.
  1651.          * @return the session RMS
  1652.          */
  1653.         public double getRms() {
  1654.             return rms;
  1655.         }

  1656.         /**
  1657.          * Get the session skewness from the mean of raw accepted time-of-flight values minus the trend function.
  1658.          * @return the session skewness
  1659.          */
  1660.         public double getSkewness() {
  1661.             return skewness;
  1662.         }

  1663.         /**
  1664.          * Get the session kurtosis from the mean of raw accepted time-of-flight values minus the trend function.
  1665.          * @return the session kurtosis
  1666.          */
  1667.         public double getKurtosis() {
  1668.             return kurtosis;
  1669.         }

  1670.         /**
  1671.          * Get the session peak – mean value.
  1672.          * @return the session peak – mean value
  1673.          */
  1674.         public double getPeakMinusMean() {
  1675.             return peakMinusMean;
  1676.         }

  1677.         /**
  1678.          * Get the data quality assessment indicator
  1679.          * <ul>
  1680.          * <li>0=undefined or no comment</li>
  1681.          * <li>1=clear, easily filtered data, with little or no noise</li>
  1682.          * <li>2=clear data with some noise; filtering is slightly compromised by noise level</li>
  1683.          * <li>3=clear data with a significant amount of noise, or weak data with little noise. Data are certainly
  1684.          * present, but filtering is difficult.</li>
  1685.          * <li>4=unclear data; data appear marginally to be present, but are very difficult to separate from noise
  1686.          * during filtering. Signal to noise ratio can be less than 1:1.</li>
  1687.          * <li>5=no data apparent</li>
  1688.          * </ul>
  1689.          * @return the data quality assessment indicator
  1690.          */
  1691.         public int getDataQulityIndicator() {
  1692.             return dataQulityIndicator;
  1693.         }

  1694.         /**
  1695.          * Get a string representation of the instance in the CRD format.
  1696.          * @return a string representation of the instance, in the CRD format.
  1697.          */
  1698.         public String toCrdString() {
  1699.             return String.format(Locale.US, "50 %s", toString());
  1700.         }

  1701.         @Override
  1702.         public String toString() {
  1703.             // CRD suggested format, excluding the record type
  1704.             // rms, peakMinusMean: s --> ps
  1705.             final String str = String.format(Locale.US, "%4s %6.1f %7.3f %7.3f %6.1f %1d",
  1706.                     systemConfigurationId, rms * 1e12, skewness, kurtosis,
  1707.                     peakMinusMean * 1e12, dataQulityIndicator);
  1708.             return handleNaN(str).replace(',', '.');
  1709.         }

  1710.     }

  1711. }