ObservationType.java
/* Copyright 2002-2024 CS GROUP
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.files.ccsds.ndm.tdm;
import java.util.regex.Pattern;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.definitions.Units;
import org.orekit.files.ccsds.utils.ContextBinding;
import org.orekit.files.ccsds.utils.lexical.ParseToken;
import org.orekit.files.ccsds.utils.lexical.TokenType;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.Constants;
import org.orekit.utils.units.Unit;
/** Keys for {@link Observation TDM observations} entries.
* @author Maxime Journot
* @since 11.0
*/
public enum ObservationType {
// Signal related keywords.
/** Data: Carrier power [dBW].<p>
* Strength of the radio signal transmitted by the spacecraft as received at the ground station or at another spacecraft.
*/
CARRIER_POWER(Unit.ONE),
/** Data: Doppler counts [n/a].<p>
* Count of signal cycles.
*/
DOPPLER_COUNT(Unit.ONE),
/** Data: Doppler instantaneous [km/s].<p>
* Instantaneous range rate of the spacecraft.
*/
DOPPLER_INSTANTANEOUS(Units.KM_PER_S),
/** Data: Doppler integrated [km/s].<p>
* Mean range rate of the spacecraft over the INTEGRATION_INTERVAL specified in the meta-data section.
*/
DOPPLER_INTEGRATED(Units.KM_PER_S),
/** Data: Carrier power to noise spectral density ratio (Pc/No) [dBHz]. */
PC_N0(Unit.ONE),
/** Data: Ranging power to noise spectral density ratio (Pr/No) [dBHz]. */
PR_N0(Unit.ONE),
/** Data: phase cycle count at receiver. */
RECEIVE_PHASE_CT_1(Unit.ONE),
/** Data: phase cycle count at receiver. */
RECEIVE_PHASE_CT_2(Unit.ONE),
/** Data: phase cycle count at receiver. */
RECEIVE_PHASE_CT_3(Unit.ONE),
/** Data: phase cycle count at receiver. */
RECEIVE_PHASE_CT_4(Unit.ONE),
/** Data: phase cycle count at receiver. */
RECEIVE_PHASE_CT_5(Unit.ONE),
/** Data: phase cycle count at transmitter. */
TRANSMIT_PHASE_CT_1(Unit.ONE),
/** Data: phase cycle count at transmitter. */
TRANSMIT_PHASE_CT_2(Unit.ONE),
/** Data: phase cycle count at transmitter. */
TRANSMIT_PHASE_CT_3(Unit.ONE),
/** Data: phase cycle count at transmitter. */
TRANSMIT_PHASE_CT_4(Unit.ONE),
/** Data: phase cycle count at transmitter. */
TRANSMIT_PHASE_CT_5(Unit.ONE),
/** Data: Range value [km, s or RU].
* @see RangeUnits
*/
RANGE(Unit.KILOMETRE) {
/** {@inheritDoc}*/
@Override
public double rawToSI(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
final AbsoluteDate date, final double rawValue) {
if (metadata.getRangeUnits() == RangeUnits.km) {
return Unit.KILOMETRE.toSI(rawValue);
} else if (metadata.getRangeUnits() == RangeUnits.s) {
return rawValue * Constants.SPEED_OF_LIGHT;
} else {
if (ruConverter == null) {
throw new OrekitException(OrekitMessages.CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER);
}
return ruConverter.ruToMeters(metadata, date, rawValue);
}
}
/** {@inheritDoc}*/
@Override
public double siToRaw(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
final AbsoluteDate date, final double siValue) {
if (metadata.getRangeUnits() == RangeUnits.km) {
return Unit.KILOMETRE.fromSI(siValue);
} else if (metadata.getRangeUnits() == RangeUnits.s) {
return siValue / Constants.SPEED_OF_LIGHT;
} else {
if (ruConverter == null) {
throw new OrekitException(OrekitMessages.CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER);
}
return ruConverter.metersToRu(metadata, date, siValue);
}
}
},
/** Data: Received frequencies [Hz].<p>
* The RECEIVE_FREQ keyword shall be used to indicate that the values represent measurements of the received frequency.<p>
* The keyword is indexed to accommodate a scenario in which multiple downlinks are used.<p>
* RECEIVE_FREQ_n (n = 1, 2, 3, 4, 5)
*/
RECEIVE_FREQ_1(Unit.HERTZ),
/** Received frequency 2. */
RECEIVE_FREQ_2(Unit.HERTZ),
/** Received frequency 3. */
RECEIVE_FREQ_3(Unit.HERTZ),
/** Received frequency 4. */
RECEIVE_FREQ_4(Unit.HERTZ),
/** Received frequency 5. */
RECEIVE_FREQ_5(Unit.HERTZ),
/** Data: Received frequency [Hz].<p>
* Case without an index; where the frequency cannot be associated with a particular participant.
*/
RECEIVE_FREQ(Unit.HERTZ),
/** Data: Transmitted frequencies [Hz].<p>
* 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>
* The TRANSMIT_FREQ keyword is indexed to accommodate scenarios in which multiple transmitters are used.<p>
* TRANSMIT_FREQ_n (n = 1, 2, 3, 4, 5)
*/
TRANSMIT_FREQ_1(Unit.HERTZ),
/** Transmitted frequency 2. */
TRANSMIT_FREQ_2(Unit.HERTZ),
/** Transmitted frequency 3. */
TRANSMIT_FREQ_3(Unit.HERTZ),
/** Transmitted frequency 4. */
TRANSMIT_FREQ_4(Unit.HERTZ),
/** Transmitted frequency 5. */
TRANSMIT_FREQ_5(Unit.HERTZ),
/** Data: Transmitted frequencies rates [Hz/s].<p>
* The value associated with the TRANSMIT_FREQ_RATE_n keyword is the linear rate of
* change of the frequency TRANSMIT_FREQ_n starting at the timetag and continuing
* until the next TRANSMIT_FREQ_RATE_n timetag (or until the end of the data).<p>
* TRANSMIT_FREQ_RATE_n (n = 1, 2, 3, 4, 5)
*/
TRANSMIT_FREQ_RATE_1(Units.HZ_PER_S),
/** Transmitted frequency rate 2. */
TRANSMIT_FREQ_RATE_2(Units.HZ_PER_S),
/** Transmitted frequency rate 3. */
TRANSMIT_FREQ_RATE_3(Units.HZ_PER_S),
/** Transmitted frequency rate 4. */
TRANSMIT_FREQ_RATE_4(Units.HZ_PER_S),
/** Transmitted frequency rate 5. */
TRANSMIT_FREQ_RATE_5(Units.HZ_PER_S),
// VLBI/Delta-DOR Related Keywords
/** Data: DOR [s].<p>
* the DOR keyword represents the range measured via PATH_2 minus the range measured via PATH_1.
*/
DOR(Unit.SECOND),
/** Data: VLBI delay [s].<p>
* The observable associated with the VLBI_DELAY keyword represents the time of signal
* arrival via PATH_2 minus the time of signal arrival via PATH_1.
*/
VLBI_DELAY(Unit.SECOND),
// Angle Related Keywords
/** Data: ANGLE_1 in degrees and in [-180, +360[ [deg].<p>
* The value assigned to the ANGLE_1 keyword represents the azimuth, right ascension, or ‘X’
* angle of the measurement, depending on the value of the ANGLE_TYPE keyword.<p>
* The angle measurement shall be a double precision value as follows: -180.0 ≤ ANGLE_1 < 360.0<p>
* Units shall be degrees.<p>
* See meta-data keyword ANGLE_TYPE for the definition of the angles.
*/
ANGLE_1(Unit.DEGREE),
/** Data: ANGLE_2 in degrees and in [-180, +360[ [deg].<p>
* The value assigned to the ANGLE_2 keyword represents the elevation, declination, or ‘Y’
* angle of the measurement, depending on the value of the ANGLE_TYPE keyword.<p>
* The angle measurement shall be a double precision value as follows: -180.0 ≤ ANGLE_2 < 360.0.<p>
* Units shall be degrees.<p>
* See meta-data keyword ANGLE_TYPE for the definition of the angles.
*/
ANGLE_2(Unit.DEGREE),
// Optical/Radar Related keywords
/** Data: visual magnitude. */
MAG(Unit.ONE),
/** Data: Radar Cross section [m²]. */
RCS(Units.M2),
// Time Related Keywords
/** Data: Clock bias [s].<p>
* The CLOCK_BIAS keyword can be used by the message recipient to adjust timetag
* measurements by a specified amount with respect to a common reference.
*/
CLOCK_BIAS(Unit.SECOND),
/** Data: Clock drift [s/s].<p>
* The CLOCK_DRIFT keyword should be used to adjust timetag measurements by an amount that is a function of time with
* respect to a common reference, normally UTC (as opposed to the CLOCK_BIAS, which is meant to be a constant adjustment).
*/
CLOCK_DRIFT(Unit.ONE),
// Media Related Keywords
/** Data: STEC - Slant Total Electron Count [TECU].
* The STEC keyword shall be used to convey the line of sight,
* one way charged particle delay or total electron count (TEC) at the timetag associated with a
* tracking measurement, which is calculated by integrating the electron density along the
* propagation path (electrons/m2).
*/
STEC(Unit.TOTAL_ELECTRON_CONTENT_UNIT),
/** Data: TROPO DRY [m].<p>
* Dry zenith delay through the troposphere measured at the timetag.
*/
TROPO_DRY(Unit.METRE),
/** Data: TROPO WET [m].<p>
* Wet zenith delay through the troposphere measured at the timetag.
*/
TROPO_WET(Unit.METRE),
// Meteorological Related Keywords
/** Data: Pressure [hPa].<p>
* Atmospheric pressure observable as measured at the tracking participant.
*/
PRESSURE(Units.HECTO_PASCAL),
/** Data: Relative humidity [%].<p>
* Relative humidity observable as measured at the tracking participant.
*/
RHUMIDITY(Unit.PERCENT),
/** Data: Temperature [K].<p>
* Temperature observable as measured at the tracking participant.
*/
TEMPERATURE(Unit.ONE);
/** Pattern for delimiting regular expressions. */
private static final Pattern SEPARATOR = Pattern.compile("\\s+");
/** Unit. */
private final Unit unit;
/** Simple constructor.
* @param unit observation unit
*/
ObservationType(final Unit unit) {
this.unit = unit;
}
/** Process an observation line.
* @param token parse token
* @param context context binding
* @param ruConverter converter for {@link RangeUnits#RU Range Units} (may be null)
* @param metadata metadata for current block
* @param observationsBlock observation block to fill
* @return true if token was accepted
*/
public boolean process(final ParseToken token, final ContextBinding context,
final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
final ObservationsBlock observationsBlock) {
if (token.getType() == TokenType.ENTRY) {
// in an XML file, an observation element contains only the value, the epoch has been parsed before
// in a KVN file, an observation line should contains both epoch and value
if (observationsBlock.getCurrentObservationEpoch() != null) {
// we are parsing an XML file with epoch already parsed
// parse the measurement
final AbsoluteDate epoch = observationsBlock.getCurrentObservationEpoch();
final double rawValue = token.getContentAsDouble();
observationsBlock.addObservationValue(this, rawToSI(ruConverter, metadata, epoch, rawValue));
} else {
// we are parsing a KVN file and need to parse both epoch and measurement
final String[] fields = SEPARATOR.split(token.getContentAsNormalizedString());
if (fields.length != 2) {
throw token.generateException(null);
}
// parse the epoch
final AbsoluteDate epoch = context.getTimeSystem().getConverter(context).parse(fields[0]);
observationsBlock.addObservationEpoch(epoch);
// parse the measurement
try {
final double rawValue = Double.parseDouble(fields[1]);
observationsBlock.addObservationValue(this, rawToSI(ruConverter, metadata, epoch, rawValue));
} catch (NumberFormatException nfe) {
throw token.generateException(nfe);
}
}
}
return true;
}
/** Convert a measurement to SI units.
* @param ruConverter converter for {@link RangeUnits#RU Range Units} (may be null)
* @param metadata metadata corresponding to the observation
* @param date observation date
* @param rawValue measurement raw value
* @return measurement in SI units
*/
public double rawToSI(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
final AbsoluteDate date, final double rawValue) {
return unit.toSI(rawValue);
}
/** Convert a measurement from SI units.
* @param ruConverter converter for {@link RangeUnits#RU Range Units} (may be null)
* @param metadata metadata corresponding to the observation
* @param date observation date
* @param siValue measurement value in SI units
* @return measurement raw value
*/
public double siToRaw(final RangeUnitsConverter ruConverter, final TdmMetadata metadata,
final AbsoluteDate date, final double siValue) {
return unit.fromSI(siValue);
}
}