ObservationType.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.files.ccsds.ndm.tdm;

  18. import java.util.regex.Pattern;

  19. import org.orekit.errors.OrekitException;
  20. import org.orekit.errors.OrekitMessages;
  21. import org.orekit.files.ccsds.definitions.Units;
  22. import org.orekit.files.ccsds.utils.ContextBinding;
  23. import org.orekit.files.ccsds.utils.lexical.ParseToken;
  24. import org.orekit.files.ccsds.utils.lexical.TokenType;
  25. import org.orekit.time.AbsoluteDate;
  26. import org.orekit.utils.Constants;
  27. import org.orekit.utils.units.Unit;


  28. /** Keys for {@link Observation TDM observations} entries.
  29.  * @author Maxime Journot
  30.  * @since 11.0
  31.  */
  32. public enum ObservationType {

  33.     // Signal related keywords.
  34.     /** Data: Carrier power [dBW].<p>
  35.      *  Strength of the radio signal transmitted by the spacecraft as received at the ground station or at another spacecraft.
  36.      */
  37.     CARRIER_POWER(Unit.ONE),
  38.     /** Data: Doppler counts [n/a].<p>
  39.      *  Count of signal cycles.
  40.      */
  41.     DOPPLER_COUNT(Unit.ONE),
  42.     /** Data: Doppler instantaneous [km/s].<p>
  43.      *  Instantaneous range rate of the spacecraft.
  44.      */
  45.     DOPPLER_INSTANTANEOUS(Units.KM_PER_S),
  46.     /** Data: Doppler integrated [km/s].<p>
  47.      *  Mean range rate of the spacecraft over the INTEGRATION_INTERVAL specified in the meta-data section.
  48.      */
  49.     DOPPLER_INTEGRATED(Units.KM_PER_S),
  50.     /** Data: Carrier power to noise spectral density ratio (Pc/No) [dBHz]. */
  51.     PC_N0(Unit.ONE),
  52.     /** Data: Ranging power to noise spectral density ratio (Pr/No) [dBHz]. */
  53.     PR_N0(Unit.ONE),
  54.     /** Data: phase cycle count at receiver. */
  55.     RECEIVE_PHASE_CT_1(Unit.ONE),
  56.     /** Data: phase cycle count at receiver. */
  57.     RECEIVE_PHASE_CT_2(Unit.ONE),
  58.     /** Data: phase cycle count at receiver. */
  59.     RECEIVE_PHASE_CT_3(Unit.ONE),
  60.     /** Data: phase cycle count at receiver. */
  61.     RECEIVE_PHASE_CT_4(Unit.ONE),
  62.     /** Data: phase cycle count at receiver. */
  63.     RECEIVE_PHASE_CT_5(Unit.ONE),
  64.     /** Data: phase cycle count at transmitter. */
  65.     TRANSMIT_PHASE_CT_1(Unit.ONE),
  66.     /** Data: phase cycle count at transmitter. */
  67.     TRANSMIT_PHASE_CT_2(Unit.ONE),
  68.     /** Data: phase cycle count at transmitter. */
  69.     TRANSMIT_PHASE_CT_3(Unit.ONE),
  70.     /** Data: phase cycle count at transmitter. */
  71.     TRANSMIT_PHASE_CT_4(Unit.ONE),
  72.     /** Data: phase cycle count at transmitter. */
  73.     TRANSMIT_PHASE_CT_5(Unit.ONE),
  74.     /** Data: Range value [km, s or RU].
  75.      * @see RangeUnits
  76.      */
  77.     RANGE(Unit.KILOMETRE) {

  78.         /** {@inheritDoc}*/
  79.         @Override
  80.         public double rawToSI(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
  81.                               final AbsoluteDate date, final double rawValue) {
  82.             if (metadata.getRangeUnits() == RangeUnits.km) {
  83.                 return Unit.KILOMETRE.toSI(rawValue);
  84.             } else if (metadata.getRangeUnits() == RangeUnits.s) {
  85.                 return rawValue * Constants.SPEED_OF_LIGHT;
  86.             } else {
  87.                 if (ruConverter == null) {
  88.                     throw new OrekitException(OrekitMessages.CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER);
  89.                 }
  90.                 return ruConverter.ruToMeters(metadata, date, rawValue);
  91.             }
  92.         }

  93.         /** {@inheritDoc}*/
  94.         @Override
  95.         public double siToRaw(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
  96.                               final AbsoluteDate date, final double siValue) {
  97.             if (metadata.getRangeUnits() == RangeUnits.km) {
  98.                 return Unit.KILOMETRE.fromSI(siValue);
  99.             } else if (metadata.getRangeUnits() == RangeUnits.s) {
  100.                 return siValue / Constants.SPEED_OF_LIGHT;
  101.             } else {
  102.                 if (ruConverter == null) {
  103.                     throw new OrekitException(OrekitMessages.CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER);
  104.                 }
  105.                 return ruConverter.metersToRu(metadata, date, siValue);
  106.             }
  107.         }

  108.     },

  109.     /** Data: Received frequencies [Hz].<p>
  110.      * The RECEIVE_FREQ keyword shall be used to indicate that the values represent measurements of the received frequency.<p>
  111.      * The keyword is indexed to accommodate a scenario in which multiple downlinks are used.<p>
  112.      * RECEIVE_FREQ_n (n = 1, 2, 3, 4, 5)
  113.      */
  114.     RECEIVE_FREQ_1(Unit.HERTZ),
  115.     /** Received frequency 2. */
  116.     RECEIVE_FREQ_2(Unit.HERTZ),
  117.     /** Received frequency 3. */
  118.     RECEIVE_FREQ_3(Unit.HERTZ),
  119.     /** Received frequency 4. */
  120.     RECEIVE_FREQ_4(Unit.HERTZ),
  121.     /** Received frequency 5. */
  122.     RECEIVE_FREQ_5(Unit.HERTZ),
  123.     /** Data: Received frequency [Hz].<p>
  124.      *  Case without an index; where the frequency cannot be associated with a particular participant.
  125.      */
  126.     RECEIVE_FREQ(Unit.HERTZ),
  127.     /** Data: Transmitted frequencies [Hz].<p>
  128.      * The TRANSMIT_FREQ keyword shall be used to indicate that the values represent measurements of a transmitted frequency, e.g., from an uplink operation.<p>
  129.      * The TRANSMIT_FREQ keyword is indexed to accommodate scenarios in which multiple transmitters are used.<p>
  130.      * TRANSMIT_FREQ_n (n = 1, 2, 3, 4, 5)
  131.      */
  132.     TRANSMIT_FREQ_1(Unit.HERTZ),
  133.     /** Transmitted frequency 2. */
  134.     TRANSMIT_FREQ_2(Unit.HERTZ),
  135.     /** Transmitted frequency 3. */
  136.     TRANSMIT_FREQ_3(Unit.HERTZ),
  137.     /** Transmitted frequency 4. */
  138.     TRANSMIT_FREQ_4(Unit.HERTZ),
  139.     /** Transmitted frequency 5. */
  140.     TRANSMIT_FREQ_5(Unit.HERTZ),
  141.     /** Data: Transmitted frequencies rates [Hz/s].<p>
  142.      * The value associated with the TRANSMIT_FREQ_RATE_n keyword is the linear rate of
  143.      * change of the frequency TRANSMIT_FREQ_n starting at the timetag and continuing
  144.      *  until the next TRANSMIT_FREQ_RATE_n timetag (or until the end of the data).<p>
  145.      * TRANSMIT_FREQ_RATE_n (n = 1, 2, 3, 4, 5)
  146.      */
  147.     TRANSMIT_FREQ_RATE_1(Units.HZ_PER_S),
  148.     /** Transmitted frequency rate 2. */
  149.     TRANSMIT_FREQ_RATE_2(Units.HZ_PER_S),
  150.     /** Transmitted frequency rate 3. */
  151.     TRANSMIT_FREQ_RATE_3(Units.HZ_PER_S),
  152.     /** Transmitted frequency rate 4. */
  153.     TRANSMIT_FREQ_RATE_4(Units.HZ_PER_S),
  154.     /** Transmitted frequency rate 5. */
  155.     TRANSMIT_FREQ_RATE_5(Units.HZ_PER_S),

  156.     // VLBI/Delta-DOR Related Keywords
  157.     /** Data: DOR [s].<p>
  158.      * the DOR keyword represents the range measured via PATH_2 minus the range measured via PATH_1.
  159.      */
  160.     DOR(Unit.SECOND),
  161.     /** Data: VLBI delay [s].<p>
  162.      * The observable associated with the VLBI_DELAY keyword represents the time of signal
  163.      * arrival via PATH_2 minus the time of signal arrival via PATH_1.
  164.      */
  165.     VLBI_DELAY(Unit.SECOND),

  166.     // Angle Related Keywords
  167.     /** Data: ANGLE_1 in degrees and in [-180, +360[ [deg].<p>
  168.      * The value assigned to the ANGLE_1 keyword represents the azimuth, right ascension, or ‘X’
  169.      * angle of the measurement, depending on the value of the ANGLE_TYPE keyword.<p>
  170.      * The angle measurement shall be a double precision value as follows: -180.0 &le; ANGLE_1 &lt; 360.0<p>
  171.      * Units shall be degrees.<p>
  172.      * See meta-data keyword ANGLE_TYPE for the definition of the angles.
  173.      */
  174.     ANGLE_1(Unit.DEGREE),
  175.     /** Data: ANGLE_2 in degrees and in [-180, +360[ [deg].<p>
  176.      * The value assigned to the ANGLE_2 keyword represents the elevation, declination, or ‘Y’
  177.      * angle of the measurement, depending on the value of the ANGLE_TYPE keyword.<p>
  178.      * The angle measurement shall be a double precision value as follows: -180.0 &le; ANGLE_2 &lt; 360.0.<p>
  179.      * Units shall be degrees.<p>
  180.      * See meta-data keyword ANGLE_TYPE for the definition of the angles.
  181.      */
  182.     ANGLE_2(Unit.DEGREE),

  183.     // Optical/Radar Related keywords
  184.     /** Data: visual magnitude. */
  185.     MAG(Unit.ONE),
  186.     /** Data: Radar Cross section [m²]. */
  187.     RCS(Units.M2),

  188.     // Time Related Keywords
  189.     /** Data: Clock bias [s].<p>
  190.      * The CLOCK_BIAS keyword can be used by the message recipient to adjust timetag
  191.      * measurements by a specified amount with respect to a common reference.
  192.      */
  193.     CLOCK_BIAS(Unit.SECOND),
  194.     /** Data: Clock drift [s/s].<p>
  195.      * The CLOCK_DRIFT keyword should be used to adjust timetag measurements by an amount that is a function of time with
  196.      * respect to a common reference, normally UTC (as opposed to the CLOCK_BIAS, which is meant to be a constant adjustment).
  197.      */
  198.     CLOCK_DRIFT(Unit.ONE),

  199.     // Media Related Keywords
  200.     /** Data: STEC - Slant Total Electron Count [TECU].
  201.      * The STEC keyword shall be used to convey the line of sight,
  202.      * one way charged particle delay or total electron count (TEC) at the timetag associated with a
  203.      * tracking measurement, which is calculated by integrating the electron density along the
  204.      * propagation path (electrons/m2).
  205.      */
  206.     STEC(Unit.TOTAL_ELECTRON_CONTENT_UNIT),
  207.     /** Data: TROPO DRY [m].<p>
  208.      * Dry zenith delay through the troposphere measured at the timetag.
  209.      */
  210.     TROPO_DRY(Unit.METRE),
  211.     /** Data: TROPO WET [m].<p>
  212.      * Wet zenith delay through the troposphere measured at the timetag.
  213.      */
  214.     TROPO_WET(Unit.METRE),

  215.     // Meteorological Related Keywords
  216.     /** Data: Pressure [hPa].<p>
  217.      * Atmospheric pressure observable as measured at the tracking participant.
  218.      */
  219.     PRESSURE(Units.HECTO_PASCAL),
  220.     /** Data: Relative humidity [%].<p>
  221.      * Relative humidity observable as measured at the tracking participant.
  222.      */
  223.     RHUMIDITY(Unit.PERCENT),
  224.     /** Data: Temperature [K].<p>
  225.      * Temperature observable as measured at the tracking participant.
  226.      */
  227.     TEMPERATURE(Unit.ONE);

  228.     /** Pattern for delimiting regular expressions. */
  229.     private static final Pattern SEPARATOR = Pattern.compile("\\s+");

  230.     /** Unit. */
  231.     private final Unit unit;

  232.     /** Simple constructor.
  233.      * @param unit observation unit
  234.      */
  235.     ObservationType(final Unit unit) {
  236.         this.unit = unit;
  237.     }

  238.     /** Process an observation line.
  239.      * @param token parse token
  240.      * @param context context binding
  241.      * @param ruConverter converter for {@link RangeUnits#RU Range Units} (may be null)
  242.      * @param metadata metadata for current block
  243.      * @param observationsBlock observation block to fill
  244.      * @return true if token was accepted
  245.      */
  246.     public boolean process(final ParseToken token, final ContextBinding context,
  247.                            final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
  248.                            final ObservationsBlock observationsBlock) {

  249.         if (token.getType() == TokenType.ENTRY) {
  250.             // in an XML file, an observation element contains only the value, the epoch has been parsed before
  251.             // in a KVN file, an observation line should contains both epoch and value

  252.             if (observationsBlock.getCurrentObservationEpoch() != null) {
  253.                 // we are parsing an XML file with epoch already parsed
  254.                 // parse the measurement
  255.                 final AbsoluteDate epoch    = observationsBlock.getCurrentObservationEpoch();
  256.                 final double       rawValue = token.getContentAsDouble();
  257.                 observationsBlock.addObservationValue(this, rawToSI(ruConverter, metadata, epoch, rawValue));
  258.             } else {

  259.                 // we are parsing a KVN file and need to parse both epoch and measurement
  260.                 final String[] fields = SEPARATOR.split(token.getContentAsNormalizedString());
  261.                 if (fields.length != 2) {
  262.                     throw token.generateException(null);
  263.                 }
  264.                 // parse the epoch
  265.                 final AbsoluteDate epoch = context.getTimeSystem().getConverter(context).parse(fields[0]);
  266.                 observationsBlock.addObservationEpoch(epoch);

  267.                 // parse the measurement
  268.                 try {
  269.                     final double rawValue = Double.parseDouble(fields[1]);
  270.                     observationsBlock.addObservationValue(this, rawToSI(ruConverter, metadata, epoch, rawValue));
  271.                 } catch (NumberFormatException nfe) {
  272.                     throw token.generateException(nfe);
  273.                 }
  274.             }
  275.         }

  276.         return true;

  277.     }

  278.     /** Convert a measurement to SI units.
  279.      * @param ruConverter converter for {@link RangeUnits#RU Range Units} (may be null)
  280.      * @param metadata metadata corresponding to the observation
  281.      * @param date observation date
  282.      * @param rawValue measurement raw value
  283.      * @return measurement in SI units
  284.      */
  285.     public double rawToSI(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
  286.                           final AbsoluteDate date, final double rawValue) {
  287.         return unit.toSI(rawValue);
  288.     }

  289.     /** Convert a measurement from SI units.
  290.      * @param ruConverter converter for {@link RangeUnits#RU Range Units} (may be null)
  291.      * @param metadata metadata corresponding to the observation
  292.      * @param date observation date
  293.      * @param siValue measurement value in SI units
  294.      * @return measurement raw value
  295.      */
  296.     public double siToRaw(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
  297.                           final AbsoluteDate date, final double siValue) {
  298.         return unit.fromSI(siValue);
  299.     }

  300. }