diff --git a/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java b/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java
index b7ed484de13ee8631be5a1c625885927caf86a94..d5ee8d812a6d6480b66c9d8559fd76768d1fde8a 100644
--- a/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java
+++ b/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java
@@ -248,6 +248,37 @@ public abstract class AbstractMeasurement>
final Vector3D receiverPosition,
final AbsoluteDate signalArrivalDate) {
+ return signalTimeOfFlightFixedReception(adjustableEmitterPV, receiverPosition, signalArrivalDate);
+
+ }
+
+ /** Compute propagation delay on a link leg (typically downlink or uplink).
+ * @param adjustableEmitterPV position/velocity of emitter that may be adjusted
+ * @param receiverPosition fixed position of receiver at {@code signalArrivalDate},
+ * in the same frame as {@code adjustableEmitterPV}
+ * @param signalArrivalDate date at which the signal arrives to receiver
+ * @return positive delay between signal emission and signal reception dates
+ * @param the type of the components
+ */
+ public static > T signalTimeOfFlight(final TimeStampedFieldPVCoordinates adjustableEmitterPV,
+ final FieldVector3D receiverPosition,
+ final FieldAbsoluteDate signalArrivalDate) {
+
+ return signalTimeOfFlightFixedReception(adjustableEmitterPV, receiverPosition, signalArrivalDate);
+
+ }
+
+ /** Compute propagation delay on a link leg (typically downlink or uplink).
+ * @param adjustableEmitterPV position/velocity of emitter that may be adjusted
+ * @param receiverPosition fixed position of receiver at {@code signalArrivalDate},
+ * in the same frame as {@code adjustableEmitterPV}
+ * @param signalArrivalDate date at which the signal arrives to receiver
+ * @return positive delay between signal emission and signal reception dates
+ */
+ public static double signalTimeOfFlightFixedReception(final TimeStampedPVCoordinates adjustableEmitterPV,
+ final Vector3D receiverPosition,
+ final AbsoluteDate signalArrivalDate) {
+
// initialize emission date search loop assuming the state is already correct
// this will be true for all but the first orbit determination iteration,
// and even for the first iteration the loop will converge very fast
@@ -277,9 +308,9 @@ public abstract class AbstractMeasurement>
* @return positive delay between signal emission and signal reception dates
* @param the type of the components
*/
- public static > T signalTimeOfFlight(final TimeStampedFieldPVCoordinates adjustableEmitterPV,
- final FieldVector3D receiverPosition,
- final FieldAbsoluteDate signalArrivalDate) {
+ public static > T signalTimeOfFlightFixedReception(final TimeStampedFieldPVCoordinates adjustableEmitterPV,
+ final FieldVector3D receiverPosition,
+ final FieldAbsoluteDate signalArrivalDate) {
// Initialize emission date search loop assuming the emitter PV is almost correct
// this will be true for all but the first orbit determination iteration,
@@ -348,4 +379,38 @@ public abstract class AbstractMeasurement>
}
+
+ /** Compute propagation delay on a link leg (typically downlink or uplink).
+ * @param adjustableReceiver position/velocity of receiver that may be adjusted
+ * @param emitterPosition fixed position of emitter at {@code signalArrivalDate},
+ * in the same frame as {@code adjustableReceiver}
+ * @param signalEmissionDate date at which the signal is emitted to receiver
+ * @return positive delay between signal emission and signal reception dates
+ * @param the type of the components
+ */
+ public static > T signalTimeOfFlightFixedEmission(final TimeStampedFieldPVCoordinates adjustableReceiver,
+ final FieldVector3D emitterPosition,
+ final FieldAbsoluteDate signalEmissionDate) {
+
+ // initialize reception date date search loop assuming the state is already correct
+ // this will be true for all but the first orbit determination iteration,
+ // and even for the first iteration the loop will converge very fast
+ final T offset = signalEmissionDate.getDate().durationFrom(adjustableReceiver.getDate());
+ T delay = offset.negate();
+
+ // search signal transit date, computing the signal travel in inertial frame
+ final double cReciprocal = 1.0 / Constants.SPEED_OF_LIGHT;
+ double delta;
+ int count = 0;
+ do {
+ final double previous = delay.getReal();
+ final FieldVector3D transitP = adjustableReceiver.shiftedBy(offset.add(delay)).getPosition();
+ delay = emitterPosition.distance(transitP).multiply(cReciprocal);
+ delta = FastMath.abs(delay.getReal() - previous);
+ } while (count++ < 10 && delta >= 2 * FastMath.ulp(delay.getReal()));
+
+ return delay;
+
+ }
+
}
diff --git a/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java b/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java
index cffe4c555f512744d9d7cf1cb1aaf99f8aec013c..8b6873d9cf27b333883c186d8cec070e80851e38 100644
--- a/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java
+++ b/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java
@@ -42,11 +42,15 @@ import org.orekit.utils.TimeStampedPVCoordinates;
* @author Thierry Ceolin
* @since 8.0
*/
-public class AngularAzEl extends AbstractMeasurement {
+public class AngularAzEl extends AbstractMeasurement
+{
/** Ground station from which measurement is performed. */
private final GroundStation station;
+ /** Enum indicating the time tag specification of a range observation. */
+ private final TimeTagSpecificationType timeTagSpecificationType;
+
/** Simple constructor.
* @param station ground station from which measurement is performed
* @param date date of the measurement
@@ -54,11 +58,12 @@ public class AngularAzEl extends AbstractMeasurement {
* @param sigma theoretical standard deviation
* @param baseWeight base weight
* @param satellite satellite related to this measurement
- * @since 9.3
+ * @param timeTagSpecificationType specify the timetag configuration of the provided angular AzEl observation
+ * @since xx.xx
*/
public AngularAzEl(final GroundStation station, final AbsoluteDate date,
final double[] angular, final double[] sigma, final double[] baseWeight,
- final ObservableSatellite satellite) {
+ final ObservableSatellite satellite, final TimeTagSpecificationType timeTagSpecificationType) {
super(date, angular, sigma, baseWeight, Collections.singletonList(satellite));
addParameterDriver(station.getClockOffsetDriver());
addParameterDriver(station.getEastOffsetDriver());
@@ -71,6 +76,23 @@ public class AngularAzEl extends AbstractMeasurement {
addParameterDriver(station.getPolarOffsetYDriver());
addParameterDriver(station.getPolarDriftYDriver());
this.station = station;
+ this.timeTagSpecificationType = timeTagSpecificationType;
+ }
+
+
+ /** Simple constructor.
+ * @param station ground station from which measurement is performed
+ * @param date date of the measurement
+ * @param angular observed value
+ * @param sigma theoretical standard deviation
+ * @param baseWeight base weight
+ * @param satellite satellite related to this measurement
+ * @since 9.3
+ */
+ public AngularAzEl(final GroundStation station, final AbsoluteDate date,
+ final double[] angular, final double[] sigma, final double[] baseWeight,
+ final ObservableSatellite satellite) {
+ this(station, date, angular, sigma, baseWeight, satellite, TimeTagSpecificationType.RX);
}
/** Get the ground station from which measurement is performed.
@@ -113,54 +135,109 @@ public class AngularAzEl extends AbstractMeasurement {
// Transform between station and inertial frame, expressed as a gradient
// The components of station's position in offset frame are the 3 last derivative parameters
- final FieldTransform offsetToInertialDownlink =
- station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
- final FieldAbsoluteDate downlinkDateDS =
- offsetToInertialDownlink.getFieldDate();
+ final FieldTransform offsetToInertialObsEpoch =
+ station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
+ final FieldAbsoluteDate obsEpochFieldDate =
+ offsetToInertialObsEpoch.getFieldDate();
// Station position/velocity in inertial frame at end of the downlink leg
- final TimeStampedFieldPVCoordinates stationDownlink =
- offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS,
- zero, zero, zero));
- // Station topocentric frame (east-north-zenith) in inertial frame expressed as Gradient
- final FieldVector3D east = offsetToInertialDownlink.transformVector(FieldVector3D.getPlusI(field));
- final FieldVector3D north = offsetToInertialDownlink.transformVector(FieldVector3D.getPlusJ(field));
- final FieldVector3D zenith = offsetToInertialDownlink.transformVector(FieldVector3D.getPlusK(field));
+ final TimeStampedFieldPVCoordinates stationObsEpoch =
+ offsetToInertialObsEpoch.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(obsEpochFieldDate,
+ zero, zero, zero));
+
- // Compute propagation times
- // (if state has already been set up to pre-compensate propagation delay,
- // we will have delta == tauD and transitState will be the same as state)
+ final Gradient delta = obsEpochFieldDate.durationFrom(state.getDate());
- // Downlink delay
- final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS);
+ final TimeStampedFieldPVCoordinates transitStateDS;
+ final TimeStampedFieldPVCoordinates stationDownlink;
- // Transit state
- final Gradient delta = downlinkDateDS.durationFrom(state.getDate());
- final Gradient deltaMTauD = tauD.negate().add(delta);
- final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
+ /* The station position for relative position vector calculation - set to downlink for transmit and transmit
+ * receive apparent (TXRX). For transit/bounce time tag specification we use the station at bounce time. For transmit
+ * apparent the station at time of transmission is used.
+ */
+ final TimeStampedFieldPVCoordinates stationPositionEstimated;
- // Transit state (re)computed with derivative structures
- final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+ final SpacecraftState transitState;
+ final Gradient tauD;
+
+ if (timeTagSpecificationType == TimeTagSpecificationType.TX || timeTagSpecificationType == TimeTagSpecificationType.TXRX) {
+ //Date = epoch of transmission.
+ //Vary position of receiver -> in case of uplink leg, receiver is satellite
+ final Gradient tauU = signalTimeOfFlightFixedEmission(pvaDS, stationObsEpoch.getPosition(), stationObsEpoch.getDate());
+ final Gradient deltaMTauU = tauU.add(delta);
+ //Get state at transit
+ transitStateDS = pvaDS.shiftedBy(deltaMTauU);
+ transitState = state.shiftedBy(deltaMTauU.getValue());
+
+ //Get station at transit - although this is effectively an initial seed for fitting the downlink delay
+ final TimeStampedFieldPVCoordinates stationTransit = stationObsEpoch.shiftedBy(tauU);
+
+ //project time of flight forwards with 0 offset.
+ tauD = signalTimeOfFlightFixedEmission(stationTransit, transitStateDS.getPosition(), transitStateDS.getDate());
+
+ stationDownlink = stationObsEpoch.shiftedBy(tauU.add(tauD));
+
+ //Decide whether observation is transmit or receive apparent.
+ if (timeTagSpecificationType == TimeTagSpecificationType.TXRX) {
+ stationPositionEstimated = stationDownlink;
+ } else {
+ stationPositionEstimated = stationObsEpoch;
+ }
+ }
+
+ else if (timeTagSpecificationType == TimeTagSpecificationType.TRANSIT) {
+ transitStateDS = pvaDS.shiftedBy(delta);
+ transitState = state.shiftedBy(delta.getValue());
+
+ tauD = signalTimeOfFlightFixedEmission(stationObsEpoch, transitStateDS.getPosition(), transitStateDS.getDate());
+
+ stationDownlink = stationObsEpoch.shiftedBy(tauD);
+ stationPositionEstimated = stationObsEpoch;
+ }
+
+ else {
+ // Compute propagation times
+ // (if state has already been set up to pre-compensate propagation delay,
+ // we will have delta == tauD and transitState will be the same as state)
+
+ // Downlink delay
+ tauD = signalTimeOfFlightFixedReception(pvaDS, stationObsEpoch.getPosition(), obsEpochFieldDate);
+
+ // Transit state
+ final Gradient deltaMTauD = tauD.negate().add(delta);
+ transitState = state.shiftedBy(deltaMTauD.getValue());
+
+ // Transit state (re)computed with gradients
+ transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+ stationDownlink = stationObsEpoch;
+ stationPositionEstimated = stationDownlink;
+ }
+
+ final FieldTransform offsetToInertialEstimationTime = station.getOffsetToInertial(state.getFrame(), stationPositionEstimated.getDate(), nbParams, indices);
+
+ // Station topocentric frame (east-north-zenith) in inertial frame expressed as Gradient
+ final FieldVector3D east = offsetToInertialEstimationTime.transformVector(FieldVector3D.getPlusI(field));
+ final FieldVector3D north = offsetToInertialEstimationTime.transformVector(FieldVector3D.getPlusJ(field));
+ final FieldVector3D zenith = offsetToInertialEstimationTime.transformVector(FieldVector3D.getPlusK(field));
// Station-satellite vector expressed in inertial frame
- final FieldVector3D staSat = transitStateDS.getPosition().subtract(stationDownlink.getPosition());
+ final FieldVector3D staSat = transitStateDS.getPosition().subtract(stationPositionEstimated.getPosition());
// Compute azimuth/elevation
final Gradient baseAzimuth = staSat.dotProduct(east).atan2(staSat.dotProduct(north));
final double twoPiWrap = MathUtils.normalizeAngle(baseAzimuth.getReal(), getObservedValue()[0]) -
- baseAzimuth.getReal();
+ baseAzimuth.getReal();
final Gradient azimuth = baseAzimuth.add(twoPiWrap);
final Gradient elevation = staSat.dotProduct(zenith).divide(staSat.getNorm()).asin();
// Prepare the estimation
final EstimatedMeasurement estimated =
- new EstimatedMeasurement<>(this, iteration, evaluation,
- new SpacecraftState[] {
- transitState
- }, new TimeStampedPVCoordinates[] {
- transitStateDS.toTimeStampedPVCoordinates(),
- stationDownlink.toTimeStampedPVCoordinates()
- });
+ new EstimatedMeasurement<>(this, iteration, evaluation,
+ new SpacecraftState[] { transitState },
+ new TimeStampedPVCoordinates[] {
+ transitStateDS.toTimeStampedPVCoordinates(),
+ stationDownlink.toTimeStampedPVCoordinates()
+ });
// azimuth - elevation values
estimated.setEstimatedValue(azimuth.getValue(), elevation.getValue());
@@ -170,7 +247,7 @@ public class AngularAzEl extends AbstractMeasurement {
final double[] azDerivatives = azimuth.getGradient();
final double[] elDerivatives = elevation.getGradient();
estimated.setStateDerivatives(0,
- Arrays.copyOfRange(azDerivatives, 0, 6), Arrays.copyOfRange(elDerivatives, 0, 6));
+ Arrays.copyOfRange(azDerivatives, 0, 6), Arrays.copyOfRange(elDerivatives, 0, 6));
// Set partial derivatives with respect to parameters
// (beware element at index 0 is the value, not a derivative)
diff --git a/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java b/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java
index b261fa334e9e02c6149436ccd308a1b42d609e91..3cfee102f693954e49473c15e4c20d66cc8dc73d 100644
--- a/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java
+++ b/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java
@@ -44,7 +44,8 @@ import org.orekit.utils.TimeStampedPVCoordinates;
* @author Maxime Journot
* @since 9.0
*/
-public class AngularRaDec extends AbstractMeasurement {
+public class AngularRaDec extends AbstractMeasurement
+{
/** Ground station from which measurement is performed. */
private final GroundStation station;
@@ -52,6 +53,9 @@ public class AngularRaDec extends AbstractMeasurement {
/** Reference frame in which the right ascension - declination angles are given. */
private final Frame referenceFrame;
+ /** Enum indicating the time tag specification of a range observation. */
+ private final TimeTagSpecificationType timeTagSpecificationType;
+
/** Simple constructor.
* @param station ground station from which measurement is performed
* @param referenceFrame Reference frame in which the right ascension - declination angles are given
@@ -60,11 +64,12 @@ public class AngularRaDec extends AbstractMeasurement {
* @param sigma theoretical standard deviation
* @param baseWeight base weight
* @param satellite satellite related to this measurement
- * @since 9.3
+ * @param timeTagSpecificationType specify the timetag configuration of the provided angular RaDec observation
+ * @since xx.xx
*/
public AngularRaDec(final GroundStation station, final Frame referenceFrame, final AbsoluteDate date,
final double[] angular, final double[] sigma, final double[] baseWeight,
- final ObservableSatellite satellite) {
+ final ObservableSatellite satellite, final TimeTagSpecificationType timeTagSpecificationType) {
super(date, angular, sigma, baseWeight, Collections.singletonList(satellite));
addParameterDriver(station.getClockOffsetDriver());
addParameterDriver(station.getEastOffsetDriver());
@@ -78,6 +83,24 @@ public class AngularRaDec extends AbstractMeasurement {
addParameterDriver(station.getPolarDriftYDriver());
this.station = station;
this.referenceFrame = referenceFrame;
+ this.timeTagSpecificationType = timeTagSpecificationType;
+ }
+
+
+ /** Simple constructor.
+ * @param station ground station from which measurement is performed
+ * @param referenceFrame Reference frame in which the right ascension - declination angles are given
+ * @param date date of the measurement
+ * @param angular observed value
+ * @param sigma theoretical standard deviation
+ * @param baseWeight base weight
+ * @param satellite satellite related to this measurement
+ * @since 9.3
+ */
+ public AngularRaDec(final GroundStation station, final Frame referenceFrame, final AbsoluteDate date,
+ final double[] angular, final double[] sigma, final double[] baseWeight,
+ final ObservableSatellite satellite) {
+ this(station, referenceFrame, date, angular, sigma, baseWeight, satellite, TimeTagSpecificationType.RX);
}
/** Get the ground station from which measurement is performed.
@@ -126,58 +149,107 @@ public class AngularRaDec extends AbstractMeasurement {
// Transform between station and inertial frame, expressed as a gradient
// The components of station's position in offset frame are the 3 last derivative parameters
- final FieldTransform offsetToInertialDownlink =
- station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
- final FieldAbsoluteDate downlinkDateDS =
- offsetToInertialDownlink.getFieldDate();
+ final FieldTransform offsetToInertialObsEpoch =
+ station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
+ final FieldAbsoluteDate obsEpochFieldDate =
+ offsetToInertialObsEpoch.getFieldDate();
// Station position/velocity in inertial frame at end of the downlink leg
- final TimeStampedFieldPVCoordinates stationDownlink =
- offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS,
- zero, zero, zero));
+ final TimeStampedFieldPVCoordinates stationObsEpoch =
+ offsetToInertialObsEpoch.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(obsEpochFieldDate,
+ zero, zero, zero));
+
+ final Gradient delta = obsEpochFieldDate.durationFrom(state.getDate());
+
+ final TimeStampedFieldPVCoordinates transitStateDS;
+ final TimeStampedFieldPVCoordinates stationDownlink;
+
+ /* The station position for relative position vector calculation - set to downlink for transmit and transmit
+ * receive apparent (TXRX). For transit/bounce time tag specification we use the station at bounce time. For transmit
+ * apparent the station at time of transmission is used.
+ */
+ final TimeStampedFieldPVCoordinates stationPositionEstimated;
+
+ final SpacecraftState transitState;
+ final Gradient tauD;
- // Compute propagation times
- // (if state has already been set up to pre-compensate propagation delay,
- // we will have delta == tauD and transitState will be the same as state)
+ if (timeTagSpecificationType == TimeTagSpecificationType.TX || timeTagSpecificationType == TimeTagSpecificationType.TXRX) {
+ //Date = epoch of transmission.
+ //Vary position of receiver -> in case of uplink leg, receiver is satellite
+ final Gradient tauU = signalTimeOfFlightFixedEmission(pvaDS, stationObsEpoch.getPosition(), stationObsEpoch.getDate());
+ final Gradient deltaMTauU = tauU.add(delta);
+ //Get state at transit
+ transitStateDS = pvaDS.shiftedBy(deltaMTauU);
+ transitState = state.shiftedBy(deltaMTauU.getValue());
- // Downlink delay
- final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS);
+ //Get station at transit - although this is effectively an initial seed for fitting the downlink delay
+ final TimeStampedFieldPVCoordinates stationTransit = stationObsEpoch.shiftedBy(tauU);
+
+ //project time of flight forwards with 0 offset.
+ tauD = signalTimeOfFlightFixedEmission(stationTransit, transitStateDS.getPosition(), transitStateDS.getDate());
+
+ stationDownlink = stationObsEpoch.shiftedBy(tauU.add(tauD));
+ //Decide whether observation is transmit or receive apparent.
+ if (timeTagSpecificationType == TimeTagSpecificationType.TXRX) {
+ stationPositionEstimated = stationDownlink;
+ } else {
+ stationPositionEstimated = stationObsEpoch;
+ }
+ }
- // Transit state
- final Gradient delta = downlinkDateDS.durationFrom(state.getDate());
- final Gradient deltaMTauD = tauD.negate().add(delta);
- final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
+ else if (timeTagSpecificationType == TimeTagSpecificationType.TRANSIT) {
- // Transit state (re)computed with gradients
- final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+ transitStateDS = pvaDS.shiftedBy(delta);
+ transitState = state.shiftedBy(delta.getValue());
+ tauD = signalTimeOfFlightFixedEmission(stationObsEpoch, transitStateDS.getPosition(), transitStateDS.getDate());
+
+ stationDownlink = stationObsEpoch.shiftedBy(tauD);
+ stationPositionEstimated = stationObsEpoch;
+ }
+
+ else {
+ // Compute propagation times
+ // (if state has already been set up to pre-compensate propagation delay,
+ // we will have delta == tauD and transitState will be the same as state)
+
+ // Downlink delay
+ tauD = signalTimeOfFlightFixedReception(pvaDS, stationObsEpoch.getPosition(), obsEpochFieldDate);
+
+ // Transit state
+ final Gradient deltaMTauD = tauD.negate().add(delta);
+ transitState = state.shiftedBy(deltaMTauD.getValue());
+
+ // Transit state (re)computed with gradients
+ transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+ stationDownlink = stationObsEpoch;
+ stationPositionEstimated = stationObsEpoch;
+ }
// Station-satellite vector expressed in inertial frame
- final FieldVector3D staSatInertial = transitStateDS.getPosition().subtract(stationDownlink.getPosition());
+ final FieldVector3D staSatInertial = transitStateDS.getPosition().subtract(stationPositionEstimated.getPosition());
// Field transform from inertial to reference frame at station's reception date
- final FieldTransform inertialToReferenceDownlink =
- state.getFrame().getTransformTo(referenceFrame, downlinkDateDS);
+ final FieldTransform inertialToReferenceEstimationTime =
+ state.getFrame().getTransformTo(referenceFrame, stationPositionEstimated.getDate());
// Station-satellite vector in reference frame
- final FieldVector3D staSatReference = inertialToReferenceDownlink.transformPosition(staSatInertial);
+ final FieldVector3D staSatReference = inertialToReferenceEstimationTime.transformPosition(staSatInertial);
// Compute right ascension and declination
final Gradient baseRightAscension = staSatReference.getAlpha();
final double twoPiWrap = MathUtils.normalizeAngle(baseRightAscension.getReal(),
- getObservedValue()[0]) - baseRightAscension.getReal();
+ getObservedValue()[0]) - baseRightAscension.getReal();
final Gradient rightAscension = baseRightAscension.add(twoPiWrap);
final Gradient declination = staSatReference.getDelta();
// Prepare the estimation
final EstimatedMeasurement estimated =
- new EstimatedMeasurement<>(this, iteration, evaluation,
- new SpacecraftState[] {
- transitState
- }, new TimeStampedPVCoordinates[] {
- transitStateDS.toTimeStampedPVCoordinates(),
- stationDownlink.toTimeStampedPVCoordinates()
- });
-
+ new EstimatedMeasurement<>(this, iteration, evaluation,
+ new SpacecraftState[] { transitState },
+ new TimeStampedPVCoordinates[] {
+ transitStateDS.toTimeStampedPVCoordinates(),
+ stationDownlink.toTimeStampedPVCoordinates()
+ });
// azimuth - elevation values
estimated.setEstimatedValue(rightAscension.getValue(), declination.getValue());
@@ -186,7 +258,7 @@ public class AngularRaDec extends AbstractMeasurement {
final double[] raDerivatives = rightAscension.getGradient();
final double[] decDerivatives = declination.getGradient();
estimated.setStateDerivatives(0,
- Arrays.copyOfRange(raDerivatives, 0, 6), Arrays.copyOfRange(decDerivatives, 0, 6));
+ Arrays.copyOfRange(raDerivatives, 0, 6), Arrays.copyOfRange(decDerivatives, 0, 6));
// Partial derivatives with respect to parameters
// (beware element at index 0 is the value, not a derivative)
diff --git a/src/main/java/org/orekit/estimation/measurements/Range.java b/src/main/java/org/orekit/estimation/measurements/Range.java
index d5d20915ac734155cd45c472a96b7edee704bb36..2e68cfe863d5ee70ccf6cd5d0c008c051b836780 100644
--- a/src/main/java/org/orekit/estimation/measurements/Range.java
+++ b/src/main/java/org/orekit/estimation/measurements/Range.java
@@ -48,8 +48,11 @@ import org.orekit.utils.TimeStampedPVCoordinates;
*
*
* The motion of both the station and the spacecraft during the signal
- * flight time are taken into account. The date of the measurement
- * corresponds to the reception on ground of the emitted or reflected signal.
+ * flight time are taken into account. For two way measurements the date of the measurement
+ * corresponds to either the time of signal reception at ground station,
+ * the time of signal bounce arrival or the date of transmission dependent on the time tag specification
+ * enumerated value. If not specified, the time tag specification defaults to receive time.
+ * For one way measurements the date of the measurement is assumed to be a time of signal reception.
*
*
* The clock offsets of both the ground station and the satellite are taken
@@ -58,7 +61,7 @@ import org.orekit.utils.TimeStampedPVCoordinates;
* date. These offsets have two effects:
*
*
- * - as measurement date is evaluated at reception time, the real physical date
+ *
- If measurement date is evaluated at reception time, the real physical date
* of the measurement is the observed date to which the receiving ground station
* clock offset is subtracted
* - as range is evaluated using the total signal time of flight, for one-way
@@ -75,7 +78,8 @@ import org.orekit.utils.TimeStampedPVCoordinates;
* @author Maxime Journot
* @since 8.0
*/
-public class Range extends AbstractMeasurement {
+public class Range extends AbstractMeasurement
+{
/** Ground station from which measurement is performed. */
private final GroundStation station;
@@ -83,6 +87,9 @@ public class Range extends AbstractMeasurement {
/** Flag indicating whether it is a two-way measurement. */
private final boolean twoway;
+ /** Enum indicating the time tag specification of a range observation. */
+ private final TimeTagSpecificationType timeTagSpecificationType;
+
/** Simple constructor.
* @param station ground station from which measurement is performed
* @param twoWay flag indicating whether it is a two-way measurement
@@ -91,11 +98,12 @@ public class Range extends AbstractMeasurement {
* @param sigma theoretical standard deviation
* @param baseWeight base weight
* @param satellite satellite related to this measurement
- * @since 9.3
+ * @param timeTagSpecificationType specify the timetag configuration of the provided range observation
+ * @since xx.xx
*/
- public Range(final GroundStation station, final boolean twoWay, final AbsoluteDate date,
- final double range, final double sigma, final double baseWeight,
- final ObservableSatellite satellite) {
+ private Range(final GroundStation station, final boolean twoWay, final AbsoluteDate date,
+ final double range, final double sigma, final double baseWeight,
+ final ObservableSatellite satellite, final TimeTagSpecificationType timeTagSpecificationType) {
super(date, range, sigma, baseWeight, Collections.singletonList(satellite));
addParameterDriver(station.getClockOffsetDriver());
addParameterDriver(station.getEastOffsetDriver());
@@ -113,8 +121,44 @@ public class Range extends AbstractMeasurement {
}
this.station = station;
this.twoway = twoWay;
+ this.timeTagSpecificationType = timeTagSpecificationType;
+ }
+
+ /** Range measurement constructor for one or two way measurements with timetag of observed value
+ * set to reception time.
+ * @param station ground station from which measurement is performed
+ * @param twoWay flag indicating whether it is a two-way measurement
+ * @param date date of the measurement
+ * @param range observed value
+ * @param sigma theoretical standard deviation
+ * @param baseWeight base weight
+ * @param satellite satellite related to this measurement
+ * @since 9.3
+ */
+ public Range(final GroundStation station, final boolean twoWay, final AbsoluteDate date,
+ final double range, final double sigma, final double baseWeight,
+ final ObservableSatellite satellite) {
+ this(station, twoWay, date, range, sigma, baseWeight, satellite, TimeTagSpecificationType.RX);
+ }
+
+ /** Range constructor for two-way measurements with a user specified observed value timetag specification.
+ * @param station ground station from which measurement is performed
+ * @param date date of the measurement
+ * @param range observed value
+ * @param sigma theoretical standard deviation
+ * @param baseWeight base weight
+ * @param satellite satellite related to this measurement
+ * @param timeTagSpecificationType specify the timetag configuration of the provided range observation
+ * @since xx.xx
+ */
+ public Range(final GroundStation station, final AbsoluteDate date,
+ final double range, final double sigma, final double baseWeight,
+ final ObservableSatellite satellite, final TimeTagSpecificationType timeTagSpecificationType) {
+ this(station, true, date, range, sigma, baseWeight, satellite, timeTagSpecificationType);
}
+
+
/** Get the ground station from which measurement is performed.
* @return ground station from which measurement is performed
*/
@@ -132,8 +176,8 @@ public class Range extends AbstractMeasurement {
/** {@inheritDoc} */
@Override
protected EstimatedMeasurement theoreticalEvaluation(final int iteration,
- final int evaluation,
- final SpacecraftState[] states) {
+ final int evaluation,
+ final SpacecraftState[] states) {
final SpacecraftState state = states[0];
@@ -156,70 +200,119 @@ public class Range extends AbstractMeasurement {
// Coordinates of the spacecraft expressed as a gradient
final TimeStampedFieldPVCoordinates pvaDS = getCoordinates(state, 0, nbParams);
-
// transform between station and inertial frame, expressed as a gradient
// The components of station's position in offset frame are the 3 last derivative parameters
- final FieldTransform offsetToInertialDownlink =
- station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
- final FieldAbsoluteDate downlinkDateDS = offsetToInertialDownlink.getFieldDate();
-
- // Station position in inertial frame at end of the downlink leg
- final TimeStampedFieldPVCoordinates stationDownlink =
- offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS,
- zero, zero, zero));
-
- // Compute propagation times
- // (if state has already been set up to pre-compensate propagation delay,
- // we will have delta == tauD and transitState will be the same as state)
-
- // Downlink delay
- final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS);
+ final FieldTransform offsetToInertialObsEpoch =
+ station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
+ final FieldAbsoluteDate obsEpochDateDS = offsetToInertialObsEpoch.getFieldDate();
- // Transit state & Transit state (re)computed with gradients
- final Gradient delta = downlinkDateDS.durationFrom(state.getDate());
- final Gradient deltaMTauD = tauD.negate().add(delta);
- final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
- final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+ // Station position in inertial frame at epoch of observation
+ final TimeStampedFieldPVCoordinates stationObsEpoch =
+ offsetToInertialObsEpoch.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(obsEpochDateDS,
+ zero, zero, zero));
+ final Gradient delta = obsEpochDateDS.durationFrom(state.getDate());
// prepare the evaluation
final EstimatedMeasurement estimated;
final Gradient range;
+ final double cOver2 = 0.5 * Constants.SPEED_OF_LIGHT;
+ final Gradient tauD;
if (twoway) {
- // Station at transit state date (derivatives of tauD taken into account)
- final TimeStampedFieldPVCoordinates stationAtTransitDate =
- stationDownlink.shiftedBy(tauD.negate());
- // Uplink delay
- final Gradient tauU =
- signalTimeOfFlight(stationAtTransitDate, transitStateDS.getPosition(), transitStateDS.getDate());
- final TimeStampedFieldPVCoordinates stationUplink =
- stationDownlink.shiftedBy(-tauD.getValue() - tauU.getValue());
+ final Gradient tauU;
- // Prepare the evaluation
- estimated = new EstimatedMeasurement(this, iteration, evaluation,
- new SpacecraftState[] {
- transitState
- }, new TimeStampedPVCoordinates[] {
- stationUplink.toTimeStampedPVCoordinates(),
- transitStateDS.toTimeStampedPVCoordinates(),
- stationDownlink.toTimeStampedPVCoordinates()
- });
+ if (timeTagSpecificationType == TimeTagSpecificationType.TX) {
+ //Date = epoch of transmission.
+ //Vary position of receiver -> in case of uplink leg, receiver is satellite
+ tauU = signalTimeOfFlightFixedEmission(pvaDS, stationObsEpoch.getPosition(), stationObsEpoch.getDate());
- // Range value
- final double cOver2 = 0.5 * Constants.SPEED_OF_LIGHT;
- final Gradient tau = tauD.add(tauU);
- range = tau.multiply(cOver2);
+ final Gradient deltaMTauU = tauU.add(delta);
+ //Get state at transit
+ final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauU);
+ final SpacecraftState transitState = state.shiftedBy(deltaMTauU.getValue());
+
+ //Get station at transit - although this is effectively an initial seed for fitting the downlink delay
+ final TimeStampedFieldPVCoordinates stationTransit = stationObsEpoch.shiftedBy(tauU);
+
+ //project time of flight forwards with 0 offset.
+ tauD = signalTimeOfFlightFixedEmission(stationTransit, transitStateDS.getPosition(), transitStateDS.getDate());
+
+ estimated = new EstimatedMeasurement(this, iteration, evaluation, new SpacecraftState[] {transitState},
+ new TimeStampedPVCoordinates[] {
+ stationObsEpoch.toTimeStampedPVCoordinates(),
+ transitStateDS.toTimeStampedPVCoordinates(),
+ stationTransit.shiftedBy(tauD).toTimeStampedPVCoordinates()});
+
+ // Range value
+ final Gradient tau = tauD.add(tauU);
+ range = tau.multiply(cOver2);
+
+ }
+ else if (timeTagSpecificationType == TimeTagSpecificationType.TRANSIT) {
+ final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(delta);
+ final SpacecraftState transitState = state.shiftedBy(delta.getValue());
+
+ //Calculate time of flight for return measurement participants
+ tauD = signalTimeOfFlightFixedEmission(stationObsEpoch, transitStateDS.getPosition(), transitStateDS.getDate());
+ tauU = signalTimeOfFlightFixedReception(stationObsEpoch, transitStateDS.getPosition(), transitStateDS.getDate());
+ estimated = new EstimatedMeasurement(this, iteration, evaluation, new SpacecraftState[] {transitState},
+ new TimeStampedPVCoordinates[] {
+ stationObsEpoch.shiftedBy(tauU.negate()).toTimeStampedPVCoordinates(),
+ transitStateDS.toTimeStampedPVCoordinates(),
+ stationObsEpoch.shiftedBy(tauD).toTimeStampedPVCoordinates()
+ });
+ //use transit state as corrected state
+ range = stationObsEpoch.getPosition().distance(transitStateDS.getPosition());
+ }
+ else {
+ // Downlink delay
+ tauD = signalTimeOfFlightFixedReception(pvaDS, stationObsEpoch.getPosition(), obsEpochDateDS);
+
+ // Transit state & Transit state (re)computed with gradients
+ final Gradient deltaMTauD = tauD.negate().add(delta);
+ final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
+ final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+
+ // Station at transit state date (derivatives of tauD taken into account)
+ final TimeStampedFieldPVCoordinates stationAtTransitDate = stationObsEpoch.shiftedBy(tauD.negate());
+ // Uplink delay
+ tauU = signalTimeOfFlightFixedReception(stationAtTransitDate, transitStateDS.getPosition(),
+ transitStateDS.getDate());
+ final TimeStampedFieldPVCoordinates stationUplink =
+ stationObsEpoch.shiftedBy(-tauD.getValue() - tauU.getValue());
+
+ // Prepare the evaluation
+ estimated = new EstimatedMeasurement(this, iteration, evaluation, new SpacecraftState[] {transitState},
+ new TimeStampedPVCoordinates[] {
+ stationUplink.toTimeStampedPVCoordinates(),
+ transitStateDS.toTimeStampedPVCoordinates(),
+ stationObsEpoch.toTimeStampedPVCoordinates()});
+
+ // Range value
+ final Gradient tau = tauD.add(tauU);
+ range = tau.multiply(cOver2);
+ }
} else {
+ // (if state has already been set up to pre-compensate propagation delay,
+ // we will have delta == tauD and transitState will be the same as state)
+
+ // Downlink delay
+ tauD = signalTimeOfFlightFixedReception(pvaDS, stationObsEpoch.getPosition(), obsEpochDateDS);
+
+ // Transit state & Transit state (re)computed with gradients
+ final Gradient deltaMTauD = tauD.negate().add(delta);
+ final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
+ final TimeStampedFieldPVCoordinates transitStateDS = pvaDS.shiftedBy(deltaMTauD);
+
estimated = new EstimatedMeasurement(this, iteration, evaluation,
- new SpacecraftState[] {
- transitState
- }, new TimeStampedPVCoordinates[] {
- transitStateDS.toTimeStampedPVCoordinates(),
- stationDownlink.toTimeStampedPVCoordinates()
- });
+ new SpacecraftState[] {transitState},
+ new TimeStampedPVCoordinates[] {
+ transitStateDS.toTimeStampedPVCoordinates(),
+ stationObsEpoch.toTimeStampedPVCoordinates()
+ });
// Clock offsets
final ObservableSatellite satellite = getSatellites().get(0);
diff --git a/src/main/java/org/orekit/estimation/measurements/RangeRate.java b/src/main/java/org/orekit/estimation/measurements/RangeRate.java
index 0eca1f81e7caad0c292000107905524cff4fe9ab..0403faf9d1b65b729a8cf3b4c2d8deeba1fc7090 100644
--- a/src/main/java/org/orekit/estimation/measurements/RangeRate.java
+++ b/src/main/java/org/orekit/estimation/measurements/RangeRate.java
@@ -47,7 +47,8 @@ import org.orekit.utils.TimeStampedPVCoordinates;
* @author Joris Olympio
* @since 8.0
*/
-public class RangeRate extends AbstractMeasurement {
+public class RangeRate extends AbstractMeasurement
+{
/** Ground station from which measurement is performed. */
private final GroundStation station;
@@ -55,6 +56,9 @@ public class RangeRate extends AbstractMeasurement {
/** Flag indicating whether it is a two-way measurement. */
private final boolean twoway;
+ /** Enum indicating the time tag specification of a range observation. */
+ private final TimeTagSpecificationType timeTagSpecificationType;
+
/** Simple constructor.
* @param station ground station from which measurement is performed
* @param date date of the measurement
@@ -63,11 +67,12 @@ public class RangeRate extends AbstractMeasurement {
* @param baseWeight base weight
* @param twoway if true, this is a two-way measurement
* @param satellite satellite related to this measurement
+ * @param timeTagSpecificationType specify the timetag configuration of the provided range rate observation
* @since 9.3
*/
- public RangeRate(final GroundStation station, final AbsoluteDate date,
+ private RangeRate(final GroundStation station, final AbsoluteDate date,
final double rangeRate, final double sigma, final double baseWeight,
- final boolean twoway, final ObservableSatellite satellite) {
+ final boolean twoway, final ObservableSatellite satellite, final TimeTagSpecificationType timeTagSpecificationType) {
super(date, rangeRate, sigma, baseWeight, Collections.singletonList(satellite));
addParameterDriver(station.getClockOffsetDriver());
addParameterDriver(station.getClockDriftDriver());
@@ -83,6 +88,42 @@ public class RangeRate extends AbstractMeasurement {
addParameterDriver(station.getPolarDriftYDriver());
this.station = station;
this.twoway = twoway;
+ this.timeTagSpecificationType = timeTagSpecificationType;
+ }
+
+ /**
+ * Range rate constructor for one or two-way measurements with timetag of
+ * observed value set to reception time.
+ * @param station ground station from which measurement is performed
+ * @param date date of the measurement
+ * @param rangeRate observed value, m/s
+ * @param sigma theoretical standard deviation
+ * @param baseWeight base weight
+ * @param twoway if true, this is a two-way measurement
+ * @param satellite satellite related to this measurement
+ * @since xx.xx
+ */
+ public RangeRate(final GroundStation station, final AbsoluteDate date,
+ final double rangeRate, final double sigma, final double baseWeight,
+ final boolean twoway, final ObservableSatellite satellite) {
+ this(station, date, rangeRate, sigma, baseWeight, twoway, satellite, TimeTagSpecificationType.RX);
+ }
+
+ /**
+ * Range rate constructor for two-way measurements with a user specified observed value timetag specification.
+ * @param station ground station from which measurement is performed
+ * @param date date of the measurement
+ * @param rangeRate observed value, m/s
+ * @param sigma theoretical standard deviation
+ * @param baseWeight base weight
+ * @param satellite satellite related to this measurement
+ * @param timeTagSpecificationType specify the timetag configuration of the provided range rate observation
+ * @since xx.xx
+ **/
+ public RangeRate(final GroundStation station, final AbsoluteDate date,
+ final double rangeRate, final double sigma, final double baseWeight,
+ final ObservableSatellite satellite, final TimeTagSpecificationType timeTagSpecificationType) {
+ this(station, date, rangeRate, sigma, baseWeight, true, satellite, timeTagSpecificationType);
}
/** Check if the instance represents a two-way measurement.
@@ -128,90 +169,159 @@ public class RangeRate extends AbstractMeasurement {
// transform between station and inertial frame, expressed as a gradient
// The components of station's position in offset frame are the 3 last derivative parameters
- final FieldTransform offsetToInertialDownlink =
- station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
- final FieldAbsoluteDate downlinkDateDS =
- offsetToInertialDownlink.getFieldDate();
+ final FieldTransform offsetToInertialObsEpoch =
+ station.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
+ final FieldAbsoluteDate obsEpochFieldDate =
+ offsetToInertialObsEpoch.getFieldDate();
// Station position in inertial frame at end of the downlink leg
- final TimeStampedFieldPVCoordinates stationDownlink =
- offsetToInertialDownlink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(downlinkDateDS,
- zero, zero, zero));
+ final TimeStampedFieldPVCoordinates stationObsEpoch =
+ offsetToInertialObsEpoch.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(obsEpochFieldDate,
+ zero, zero, zero));
- // Compute propagation times
- // (if state has already been set up to pre-compensate propagation delay,
- // we will have delta == tauD and transitState will be the same as state)
+ // Delta to account for the case in which the state is provided at a different time to the obs epoch
+ final Gradient delta = obsEpochFieldDate.durationFrom(state.getDate());
- // Downlink delay
- final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS);
+ final EstimatedMeasurement estimated;
+ final Gradient tauD;
+ final EstimatedMeasurement evalOneWay1;
+
+ if (twoway) {
+ final Gradient tauU;
+ final EstimatedMeasurement evalOneWay2;
+
+ if (timeTagSpecificationType == TimeTagSpecificationType.TX) {
+ //Date = epoch of transmission.
+ //Vary position of receiver -> in case of uplink leg, receiver is satellite
+ tauU = signalTimeOfFlightFixedEmission(pvaDS, stationObsEpoch.getPosition(), stationObsEpoch.getDate());
+ //Get state at transit
+ final Gradient deltaMTauU = tauU.add(delta);
+ final TimeStampedFieldPVCoordinates transitPV = pvaDS.shiftedBy(deltaMTauU);
+ final SpacecraftState transitState = state.shiftedBy(deltaMTauU.getValue());
+ evalOneWay2 = oneWayTheoreticalEvaluation(iteration, evaluation, false, stationObsEpoch, transitPV, transitState, indices, nbParams);
+ //Get station at transit - although this is effectively an initial seed for fitting the downlink delay
+ final TimeStampedFieldPVCoordinates stationTransit = stationObsEpoch.shiftedBy(tauU);
+
+ //project time of flight forwards with 0 offset.
+ tauD = signalTimeOfFlightFixedEmission(stationTransit, transitPV.getPosition(), transitPV.getDate());
+ evalOneWay1 = oneWayTheoreticalEvaluation(iteration, evaluation, true, stationTransit.shiftedBy(tauD), transitPV, transitState, indices, nbParams);
+
+ } else if (timeTagSpecificationType == TimeTagSpecificationType.TRANSIT) {
+ final TimeStampedFieldPVCoordinates transitPV = pvaDS.shiftedBy(delta);
+ final SpacecraftState transitState = state.shiftedBy(delta.getValue());
+
+ //In transit obs case, do not correct the value for motion during time of flight.
+ final Gradient transitRangeRate = getRangeRateValue(stationObsEpoch, transitPV);
+
+ //Calculate time of flight for return measurement participants
+ tauD = signalTimeOfFlightFixedEmission(stationObsEpoch, transitPV.getPosition(), transitPV.getDate());
+ tauU = signalTimeOfFlightFixedReception(stationObsEpoch, transitPV.getPosition(), transitPV.getDate());
+
+ //shift station forwards to get downlink
+ evalOneWay1 = oneWayTheoreticalEvaluation(iteration, evaluation, true, stationObsEpoch.shiftedBy(tauD), transitPV, transitState, indices, nbParams);
+ evalOneWay1.setEstimatedValue(transitRangeRate.getValue());
+ //shift station backwards to get uplink
+ evalOneWay2 = oneWayTheoreticalEvaluation(iteration, evaluation, false, stationObsEpoch.shiftedBy(tauU.negate()), transitPV, transitState, indices, nbParams);
+ evalOneWay2.setEstimatedValue(transitRangeRate.getValue());
+ }
+ else {
+ // Compute propagation times
+ // (if state has already been set up to pre-compensate propagation delay,
+ // we will have delta == tauD and transitState will be the same as state)
- // Transit state
- final Gradient delta = downlinkDateDS.durationFrom(state.getDate());
- final Gradient deltaMTauD = tauD.negate().add(delta);
- final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
+ // Downlink delay
+ tauD = signalTimeOfFlight(pvaDS, stationObsEpoch.getPosition(), obsEpochFieldDate);
- // Transit state (re)computed with gradients
- final TimeStampedFieldPVCoordinates transitPV = pvaDS.shiftedBy(deltaMTauD);
+ // Transit state
+ final Gradient deltaMTauD = tauD.negate().add(delta);
+ final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
- // one-way (downlink) range-rate
- final EstimatedMeasurement evalOneWay1 =
+ // Transit state (re)computed with gradients
+ final TimeStampedFieldPVCoordinates transitPV = pvaDS.shiftedBy(deltaMTauD);
+
+ evalOneWay1 =
oneWayTheoreticalEvaluation(iteration, evaluation, true,
- stationDownlink, transitPV, transitState, indices, nbParams);
- final EstimatedMeasurement estimated;
- if (twoway) {
- // one-way (uplink) light time correction
- final FieldTransform offsetToInertialApproxUplink =
- station.getOffsetToInertial(state.getFrame(),
- downlinkDateDS.shiftedBy(tauD.multiply(-2)), nbParams, indices);
- final FieldAbsoluteDate approxUplinkDateDS =
- offsetToInertialApproxUplink.getFieldDate();
+ stationObsEpoch, transitPV, transitState, indices, nbParams);
+
+ // one-way (uplink) light time correction
+ final FieldTransform offsetToInertialApproxUplink =
+ station.getOffsetToInertial(state.getFrame(), obsEpochFieldDate.shiftedBy(tauD.multiply(-2)),
+ nbParams, indices);
+ final FieldAbsoluteDate approxUplinkDateDS = offsetToInertialApproxUplink.getFieldDate();
- final TimeStampedFieldPVCoordinates stationApproxUplink =
- offsetToInertialApproxUplink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(approxUplinkDateDS,
- zero, zero, zero));
+ final TimeStampedFieldPVCoordinates stationApproxUplink =
+ offsetToInertialApproxUplink.transformPVCoordinates(
+ new TimeStampedFieldPVCoordinates<>(approxUplinkDateDS, zero, zero,
+ zero));
- final Gradient tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), transitPV.getDate());
+ tauU =
+ signalTimeOfFlightFixedReception(stationApproxUplink, transitPV.getPosition(), transitPV.getDate());
- final TimeStampedFieldPVCoordinates stationUplink =
- stationApproxUplink.shiftedBy(transitPV.getDate().durationFrom(approxUplinkDateDS).subtract(tauU));
+ final TimeStampedFieldPVCoordinates stationUplink = stationApproxUplink
+ .shiftedBy(transitPV.getDate().durationFrom(approxUplinkDateDS).subtract(tauU));
- final EstimatedMeasurement evalOneWay2 =
- oneWayTheoreticalEvaluation(iteration, evaluation, false,
- stationUplink, transitPV, transitState, indices, nbParams);
+ evalOneWay2 =
+ oneWayTheoreticalEvaluation(iteration, evaluation, false, stationUplink,
+ transitPV, transitState, indices, nbParams);
+ }
// combine uplink and downlink values
- estimated = new EstimatedMeasurement<>(this, iteration, evaluation,
- evalOneWay1.getStates(),
- new TimeStampedPVCoordinates[] {
- evalOneWay2.getParticipants()[0],
- evalOneWay1.getParticipants()[0],
- evalOneWay1.getParticipants()[1]
- });
+ estimated = new EstimatedMeasurement<>(this, iteration, evaluation, evalOneWay1.getStates(),
+ new TimeStampedPVCoordinates[] {evalOneWay2.getParticipants()[0],
+ evalOneWay1.getParticipants()[0],
+ evalOneWay1.getParticipants()[1]});
estimated.setEstimatedValue(0.5 * (evalOneWay1.getEstimatedValue()[0] + evalOneWay2.getEstimatedValue()[0]));
// combine uplink and downlink partial derivatives with respect to state
final double[][] sd1 = evalOneWay1.getStateDerivatives(0);
final double[][] sd2 = evalOneWay2.getStateDerivatives(0);
final double[][] sd = new double[sd1.length][sd1[0].length];
- for (int i = 0; i < sd.length; ++i) {
- for (int j = 0; j < sd[0].length; ++j) {
+ for (int i = 0; i < sd.length; ++i)
+ {
+ for (int j = 0; j < sd[0].length; ++j)
+ {
sd[i][j] = 0.5 * (sd1[i][j] + sd2[i][j]);
}
}
estimated.setStateDerivatives(0, sd);
// combine uplink and downlink partial derivatives with respect to parameters
- evalOneWay1.getDerivativesDrivers().forEach(driver -> {
- final double[] pd1 = evalOneWay1.getParameterDerivatives(driver);
- final double[] pd2 = evalOneWay2.getParameterDerivatives(driver);
+ evalOneWay1.getDerivativesDrivers().forEach(driver ->
+ {
+ final double[] pd1 = evalOneWay1.getParameterDerivatives(
+ driver);
+ final double[] pd2 = evalOneWay2.getParameterDerivatives(
+ driver);
final double[] pd = new double[pd1.length];
- for (int i = 0; i < pd.length; ++i) {
+ for (int i = 0; i < pd.length; ++i)
+ {
pd[i] = 0.5 * (pd1[i] + pd2[i]);
}
- estimated.setParameterDerivatives(driver, pd);
+ estimated.setParameterDerivatives(
+ driver, pd);
});
-
} else {
+
+ // Compute propagation times
+ // (if state has already been set up to pre-compensate propagation delay,
+ // we will have delta == tauD and transitState will be the same as state)
+
+ // Downlink delay
+ tauD = signalTimeOfFlightFixedReception(pvaDS, stationObsEpoch.getPosition(), obsEpochFieldDate);
+
+ // Transit state
+ final Gradient deltaMTauD = tauD.negate().add(delta);
+ final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());
+
+ // Transit state (re)computed with gradients
+ final TimeStampedFieldPVCoordinates transitPV = pvaDS.shiftedBy(deltaMTauD);
+
+ // one-way (downlink) range-rate
+ evalOneWay1 =
+ oneWayTheoreticalEvaluation(iteration, evaluation, true,
+ stationObsEpoch, transitPV, transitState, indices, nbParams);
+
+
estimated = evalOneWay1;
}
@@ -229,7 +339,7 @@ public class RangeRate extends AbstractMeasurement {
* @param indices indices of the estimated parameters in derivatives computations
* @param nbParams the number of estimated parameters in derivative computations
* @return theoretical value
- * @see #evaluate(SpacecraftStatet)
+ * //@see #evaluate(SpacecraftStatet)
*/
private EstimatedMeasurement oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink,
final TimeStampedFieldPVCoordinates stationPV,
@@ -240,29 +350,15 @@ public class RangeRate extends AbstractMeasurement {
// prepare the evaluation
final EstimatedMeasurement estimated =
- new EstimatedMeasurement(this, iteration, evaluation,
- new SpacecraftState[] {
- transitState
- }, new TimeStampedPVCoordinates[] {
- (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(),
- (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates()
- });
-
- // range rate value
- final FieldVector3D stationPosition = stationPV.getPosition();
- final FieldVector3D relativePosition = stationPosition.subtract(transitPV.getPosition());
-
- final FieldVector3D stationVelocity = stationPV.getVelocity();
- final FieldVector3D relativeVelocity = stationVelocity.subtract(transitPV.getVelocity());
-
- // radial direction
- final FieldVector3D lineOfSight = relativePosition.normalize();
-
- // line of sight velocity
- final Gradient lineOfSightVelocity = FieldVector3D.dotProduct(relativeVelocity, lineOfSight);
+ new EstimatedMeasurement(this, iteration, evaluation,
+ new SpacecraftState[] {transitState},
+ new TimeStampedPVCoordinates[] {
+ (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(),
+ (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates()
+ });
// range rate
- Gradient rangeRate = lineOfSightVelocity;
+ Gradient rangeRate = getRangeRateValue(stationPV, transitPV);
if (!twoway) {
// clock drifts, taken in account only in case of one way
@@ -294,4 +390,19 @@ public class RangeRate extends AbstractMeasurement {
}
+ private static Gradient getRangeRateValue(final TimeStampedFieldPVCoordinates stationPV, final TimeStampedFieldPVCoordinates transitPV) {
+ // range rate value
+ final FieldVector3D stationPosition = stationPV.getPosition();
+ final FieldVector3D relativePosition = stationPosition.subtract(transitPV.getPosition());
+
+ final FieldVector3D stationVelocity = stationPV.getVelocity();
+ final FieldVector3D relativeVelocity = stationVelocity.subtract(transitPV.getVelocity());
+
+ // radial direction
+ final FieldVector3D lineOfSight = relativePosition.normalize();
+
+ // line of sight velocity
+ return FieldVector3D.dotProduct(relativeVelocity, lineOfSight);
+ }
+
}
diff --git a/src/main/java/org/orekit/estimation/measurements/TimeTagSpecificationType.java b/src/main/java/org/orekit/estimation/measurements/TimeTagSpecificationType.java
new file mode 100644
index 0000000000000000000000000000000000000000..49a3266a0df2a08f6cf451af88d18d7a82b3c9a9
--- /dev/null
+++ b/src/main/java/org/orekit/estimation/measurements/TimeTagSpecificationType.java
@@ -0,0 +1,61 @@
+/* Copyright 2002-2022 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.estimation.measurements;
+
+/**
+ * Enumeration for different time tag specification types, specifying the point at which the
+ * measurement epoch is taken from.
+ *
+ * @author Tommy Fryer
+ * @since 11.1
+ */
+public enum TimeTagSpecificationType {
+
+ /** Time tag at transit/bounce time. */
+ TRANSIT("Transit"),
+
+ /** Time tag is at time of signal reception. */
+ RX("Receive"),
+
+ /** Time tag is at time of signal transmission. */
+ TX("Transmit"),
+
+ /** For Angles measurements where the time tag is at time of signal transmission but the observation is receive apparent. */
+ TXRX("Transmit (Receive Apparent)");
+
+ /** Name of time tag specification. */
+ private final String name;
+
+ /**
+ * Constructor.
+ * @param name name of the time tag specification
+ */
+ TimeTagSpecificationType(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of the time tag specification.
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+}
+
diff --git a/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java b/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java
index b2ad595dafc0104a411126d20ddb6ac52ae27a4e..fb4da742d1eb92b4c81694d374c342f0bf817dd5 100644
--- a/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java
+++ b/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java
@@ -22,20 +22,19 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.stat.descriptive.StreamingStatistics;
import org.hipparchus.stat.descriptive.rank.Median;
import org.hipparchus.util.FastMath;
+import org.hipparchus.util.MathUtils;
import org.junit.Assert;
import org.junit.Test;
import org.orekit.estimation.Context;
import org.orekit.estimation.EstimationTestUtils;
+import org.orekit.frames.Transform;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
import org.orekit.time.AbsoluteDate;
-import org.orekit.utils.Differentiation;
-import org.orekit.utils.ParameterDriver;
-import org.orekit.utils.ParameterFunction;
-import org.orekit.utils.StateFunction;
+import org.orekit.utils.*;
public class AngularAzElTest {
@@ -265,5 +264,105 @@ public class AngularAzElTest {
}
}
}
+
+ /**
+ * Test the estimated values when the observed angular AzEl value is provided at TX (Transmit),
+ * RX (Receive (default)), transit (bounce)
+ */
+ @Test
+ public void testTimeTagSpecifications(){
+ Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
+ SpacecraftState state = new SpacecraftState(context.initialOrbit);
+ ObservableSatellite obsSat = new ObservableSatellite(0);
+
+ for (GroundStation gs : context.getStations()) {
+
+ gs.getPrimeMeridianOffsetDriver().setReferenceDate(state.getDate());
+ gs.getPrimeMeridianDriftDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetXDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftXDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetYDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftYDriver().setReferenceDate(state.getDate());
+
+ Transform gsToInertialTransform = gs.getOffsetToInertial(state.getFrame(), state.getDate());
+ TimeStampedPVCoordinates stationPosition = gsToInertialTransform.transformPVCoordinates(new TimeStampedPVCoordinates(state.getDate(), Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO));
+
+ double staticDistance = stationPosition.getPosition().distance(state.getPVCoordinates().getPosition());
+ double staticTimeOfFlight = staticDistance / Constants.SPEED_OF_LIGHT;
+
+ //Transmit (TX)
+ AngularAzEl azElTx = new AngularAzEl(gs, state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat, TimeTagSpecificationType.TX);
+ //Transmit Receive Apparent (TXRX)
+ AngularAzEl azElTxRx = new AngularAzEl(gs, state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat, TimeTagSpecificationType.TXRX);
+ //Receive (RX)
+ AngularAzEl azElRx = new AngularAzEl(gs, state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat, TimeTagSpecificationType.RX);
+ //Transit (Bounce)
+ AngularAzEl azElT = new AngularAzEl(gs, state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat, TimeTagSpecificationType.TRANSIT);
+
+ EstimatedMeasurement estAzElTx = azElTx.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estAzElTxRx = azElTxRx.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estAzElRx = azElRx.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estAzElTransit = azElT.estimate(0, 0, new SpacecraftState[]{state});
+
+ //Calculate Range calculated at transit and receive by shifting the state forward/backwards assuming time of flight = r/c
+ SpacecraftState tx = state.shiftedBy(staticTimeOfFlight);
+ SpacecraftState rx = state.shiftedBy(-staticTimeOfFlight);
+
+ //Calculate the expected values, varying the estimated state + the time the ground station position is evaluated at.
+
+ //state.getDate() == transmit (TX) time, state.getDate().shiftedBy(2*staticTimeOfFlight) == reception time
+ double transitAzTxRx = MathUtils.normalizeAngle(gs.getBaseFrame().getAzimuth(tx.getPVCoordinates().getPosition(), tx.getFrame(), state.getDate().shiftedBy(2*staticTimeOfFlight)), 0.0);
+ double transitElTxRx = MathUtils.normalizeAngle(gs.getBaseFrame().getElevation(tx.getPVCoordinates().getPosition(), tx.getFrame(), state.getDate().shiftedBy(2*staticTimeOfFlight)), 0.0);
+
+ //state.getDate() == transmit (TX) time
+ double transitAzTx = MathUtils.normalizeAngle(gs.getBaseFrame().getAzimuth(tx.getPVCoordinates().getPosition(), tx.getFrame(), state.getDate()), 0.0);
+ double transitElTx = MathUtils.normalizeAngle(gs.getBaseFrame().getElevation(tx.getPVCoordinates().getPosition(), tx.getFrame(), state.getDate()), 0.0);
+
+ //state.getDate() == reception time
+ double transitAzRx = MathUtils.normalizeAngle(gs.getBaseFrame().getAzimuth(rx.getPVCoordinates().getPosition(), rx.getFrame(), state.getDate()), 0.0);
+ double transitElRx = MathUtils.normalizeAngle(gs.getBaseFrame().getElevation(rx.getPVCoordinates().getPosition(), rx.getFrame(), state.getDate()), 0.0);
+
+ //state.getDate() == transit time
+ double transitAzT = MathUtils.normalizeAngle(gs.getBaseFrame().getAzimuth(state.getPVCoordinates().getPosition(), state.getFrame(), state.getDate()), 0.0);
+ double transitElT = MathUtils.normalizeAngle(gs.getBaseFrame().getElevation(state.getPVCoordinates().getPosition(), state.getFrame(), state.getDate()), 0.0);
+
+
+ //Static time of flight does not take into account motion during tof. Very small differences expected however
+ //delta expected vs actual <<< difference between TX, RX and transit predictions by a few orders of magnitude.
+ Assert.assertEquals("TX", transitAzTx, estAzElTx.getEstimatedValue()[0], 2e-8);
+ Assert.assertEquals("TX", transitElTx, estAzElTx.getEstimatedValue()[1], 2e-8);
+
+ Assert.assertEquals("TXRX", transitAzTxRx, estAzElTxRx.getEstimatedValue()[0], 2e-8);
+ Assert.assertEquals("TXRX", transitElTxRx, estAzElTxRx.getEstimatedValue()[1], 2e-8);
+
+ Assert.assertEquals("RX", transitAzRx, estAzElRx.getEstimatedValue()[0], 2e-8);
+ Assert.assertEquals("RX", transitElRx, estAzElRx.getEstimatedValue()[1], 2e-8);
+
+ Assert.assertEquals("Transit", transitAzT, estAzElTransit.getEstimatedValue()[0], 2e-8);
+ Assert.assertEquals("Transit", transitElT, estAzElTransit.getEstimatedValue()[1], 2e-8);
+
+ //Test providing pre corrected states + an arbitarily shifted case - since this should have no significant effect on the value.
+ EstimatedMeasurement estAzElTxShifted = azElTx.estimate(0, 0, new SpacecraftState[]{state.shiftedBy(staticTimeOfFlight)});
+ EstimatedMeasurement estAzElRxShifted = azElRx.estimate(0, 0, new SpacecraftState[]{state.shiftedBy(-staticTimeOfFlight)});
+ EstimatedMeasurement estAzElTransitShifted = azElT.estimate(0, 0, new SpacecraftState[]{state.shiftedBy(0.1)});
+
+ //tolerances are required since shifting the state forwards and backwards produces slight estimated value changes
+ Assert.assertEquals("TX shifted", estAzElTxShifted.getEstimatedValue()[0], estAzElTx.getEstimatedValue()[0], 1e-11);
+ Assert.assertEquals("TX shifted", estAzElTxShifted.getEstimatedValue()[1], estAzElTx.getEstimatedValue()[1], 1e-11);
+
+ Assert.assertEquals("RX shifted", estAzElRxShifted.getEstimatedValue()[0], estAzElRx.getEstimatedValue()[0], 1e-11);
+ Assert.assertEquals("RX shifted", estAzElRxShifted.getEstimatedValue()[1], estAzElRx.getEstimatedValue()[1], 1e-11);
+
+ Assert.assertEquals("Transit shifted", estAzElTransitShifted.getEstimatedValue()[0], estAzElTransit.getEstimatedValue()[0], 1e-11);
+ Assert.assertEquals("Transit shifted", estAzElTransitShifted.getEstimatedValue()[1], estAzElTransit.getEstimatedValue()[1], 1e-11);
+
+ //Show the effect of the change in time tag specification is far greater than the test tolerance due to usage
+ //of a static time of flight correction.
+ Assert.assertTrue("Proof of difference - Azimuth", (Math.abs(transitAzRx - transitAzTx) > 1e-5));
+ Assert.assertTrue("Proof of difference - Elevation", (Math.abs(transitAzRx - transitAzTx) > 1e-5));
+ }
+ }
}
diff --git a/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java b/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java
index a62b572ec3c13c97d5112426245c5c39459316c3..d4e34339731c75ce0f9a0eb31152c9e8ba212a0d 100644
--- a/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java
+++ b/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java
@@ -22,20 +22,21 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.stat.descriptive.StreamingStatistics;
import org.hipparchus.stat.descriptive.rank.Median;
import org.hipparchus.util.FastMath;
+import org.hipparchus.util.MathUtils;
import org.junit.Assert;
import org.junit.Test;
import org.orekit.estimation.Context;
import org.orekit.estimation.EstimationTestUtils;
+import org.orekit.frames.Frame;
+import org.orekit.frames.FramesFactory;
+import org.orekit.frames.Transform;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
import org.orekit.time.AbsoluteDate;
-import org.orekit.utils.Differentiation;
-import org.orekit.utils.ParameterDriver;
-import org.orekit.utils.ParameterFunction;
-import org.orekit.utils.StateFunction;
+import org.orekit.utils.*;
public class AngularRaDecTest {
@@ -265,5 +266,107 @@ public class AngularRaDecTest {
}
}
}
-}
+ /**
+ * Test the estimated values when the observed angular ra dec value is provided at TX (Transmit),
+ * RX (Receive (default)), transit (bounce)
+ */
+ @Test
+ public void testTimeTagSpecifications(){
+ Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
+ SpacecraftState state = new SpacecraftState(context.initialOrbit);
+ ObservableSatellite obsSat = new ObservableSatellite(0);
+
+ for (GroundStation gs : context.getStations()) {
+
+ gs.getPrimeMeridianOffsetDriver().setReferenceDate(state.getDate());
+ gs.getPrimeMeridianDriftDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetXDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftXDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetYDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftYDriver().setReferenceDate(state.getDate());
+
+ Transform gsToInertialTransform = gs.getOffsetToInertial(state.getFrame(), state.getDate());
+ TimeStampedPVCoordinates stationPosition = gsToInertialTransform.transformPVCoordinates(new TimeStampedPVCoordinates(state.getDate(), Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO));
+
+ double staticDistance = stationPosition.getPosition().distance(state.getPVCoordinates().getPosition());
+ double staticTimeOfFlight = staticDistance / Constants.SPEED_OF_LIGHT;
+
+ AngularRaDec raDecTx = new AngularRaDec(gs, state.getFrame(), state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat,TimeTagSpecificationType.TX);
+ AngularRaDec raDecTxRx = new AngularRaDec(gs, state.getFrame(), state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat,TimeTagSpecificationType.TXRX);
+ AngularRaDec raDecRx = new AngularRaDec(gs, state.getFrame(), state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat,TimeTagSpecificationType.RX);
+ AngularRaDec raDecT = new AngularRaDec(gs, state.getFrame(), state.getDate(), new double[]{0.0,0.0}, new double[]{0.0,0.0},new double[]{0.0,0.0},obsSat,TimeTagSpecificationType.TRANSIT);
+
+ EstimatedMeasurement estRaDecTx = raDecTx.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estRaDecTxRx = raDecTxRx.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estRaDecRx = raDecRx.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estRaDecTransit = raDecT.estimate(0, 0, new SpacecraftState[]{state});
+
+ //Calculate Range calculated at transit and receive by shifting the state forward/backwards assuming time of flight = r/c
+ SpacecraftState tx = state.shiftedBy(staticTimeOfFlight);
+ SpacecraftState rx = state.shiftedBy(-staticTimeOfFlight);
+
+ //evaluate station position at different times for different obs time tags
+ //state.getDate()==transmit (TX)
+ double[] transitRaDecTx = calcRaDec(tx, stationPosition.getPosition(), tx.getFrame(), state.getDate());
+ //state.getDate()==transmit (TX). state.getDate().shiftedBy(2*staticTimeOfFlight) == receive.
+ double[] transitRaDecTxRx = calcRaDec(tx, stationPosition.shiftedBy(2*staticTimeOfFlight).getPosition(),
+ tx.getFrame(), state.getDate().shiftedBy(2*staticTimeOfFlight));
+ //state.getDate()==receive
+ double[] transitRaDecRx = calcRaDec(rx, stationPosition.getPosition(), tx.getFrame(), state.getDate());
+ //state.getDate()==transit
+ double[] transitRaDecT = calcRaDec(state, stationPosition.getPosition(), tx.getFrame(), state.getDate());
+
+ //Static time of flight does not take into account motion during tof. Very small differences expected however
+ //delta expected vs actual <<< difference between TX, RX and transit predictions by a few orders of magnitude.
+
+ Assert.assertEquals("TX", transitRaDecTx[0], estRaDecTx.getEstimatedValue()[0], 1e-9);
+ Assert.assertEquals("TX", transitRaDecTx[1], estRaDecTx.getEstimatedValue()[1], 1e-9);
+
+ Assert.assertEquals("TXRX", transitRaDecTxRx[0], estRaDecTxRx.getEstimatedValue()[0], 1e-9);
+ Assert.assertEquals("TXRX", transitRaDecTxRx[1], estRaDecTxRx.getEstimatedValue()[1], 1e-9);
+
+ Assert.assertEquals("RX", transitRaDecRx[0], estRaDecRx.getEstimatedValue()[0], 1e-9);
+ Assert.assertEquals("RX", transitRaDecRx[1], estRaDecRx.getEstimatedValue()[1], 1e-9);
+
+ Assert.assertEquals("Transit", transitRaDecT[0], estRaDecTransit.getEstimatedValue()[0], 1e-9);
+ Assert.assertEquals("Transit", transitRaDecT[1], estRaDecTransit.getEstimatedValue()[1], 1e-9);
+
+ //Test providing pre corrected states + an arbitarily shifted case - since this should have no significant effect on the value.
+ EstimatedMeasurement estRaDecTxShifted = raDecTx.estimate(0, 0, new SpacecraftState[]{state.shiftedBy(staticTimeOfFlight)});
+ EstimatedMeasurement estRaDecRxShifted = raDecRx.estimate(0, 0, new SpacecraftState[]{state.shiftedBy(-staticTimeOfFlight)});
+ EstimatedMeasurement estRaDecTransitShifted = raDecT.estimate(0, 0, new SpacecraftState[]{state.shiftedBy(0.1)});
+
+ //tolerances are required since shifting the state forwards and backwards produces slight estimated value changes
+ Assert.assertEquals("TX shifted", estRaDecTxShifted.getEstimatedValue()[0], estRaDecTx.getEstimatedValue()[0], 1e-11);
+ Assert.assertEquals("TX shifted", estRaDecTxShifted.getEstimatedValue()[1], estRaDecTx.getEstimatedValue()[1], 1e-11);
+
+ Assert.assertEquals("RX shifted", estRaDecRxShifted.getEstimatedValue()[0], estRaDecRx.getEstimatedValue()[0], 1e-11);
+ Assert.assertEquals("RX shifted", estRaDecRxShifted.getEstimatedValue()[1], estRaDecRx.getEstimatedValue()[1], 1e-11);
+
+ Assert.assertEquals("Transit shifted", estRaDecTransitShifted.getEstimatedValue()[0], estRaDecTransit.getEstimatedValue()[0], 1e-11);
+ Assert.assertEquals("Transit shifted", estRaDecTransitShifted.getEstimatedValue()[1], estRaDecTransit.getEstimatedValue()[1], 1e-11);
+
+
+ //Show the effect of the change in time tag specification is far greater than the test tolerance due to usage
+ //of a static time of flight correction.
+ Assert.assertTrue("Proof of difference - RA", (Math.abs(transitRaDecRx[0] - transitRaDecTx[0]) > 1e-5));
+ Assert.assertTrue("Proof of difference - DEC", (Math.abs(transitRaDecRx[1] - transitRaDecTx[1]) > 1e-5));
+ }
+ }
+
+ private static double[] calcRaDec(SpacecraftState state, Vector3D station, Frame referenceFrame, AbsoluteDate date){
+ Vector3D statePos = state.getPVCoordinates().getPosition();
+ Vector3D staSatInertial = statePos.subtract(station);
+
+ // Field transform from inertial to reference frame at station's reception date
+ Transform inertialToReferenceDownlink =
+ state.getFrame().getTransformTo(referenceFrame, date);
+
+ // Station-satellite vector in reference frame
+ Vector3D raDec = inertialToReferenceDownlink.transformPosition(staSatInertial);
+ return new double[]{raDec.getAlpha(), raDec.getDelta()};
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java b/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java
index 067ae79b6d6e4b8d3380cbb5bbc0cafba9f20107..de58841537454c8d51e8d9e5e28e43892f4d1d68 100644
--- a/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java
+++ b/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java
@@ -18,6 +18,7 @@ package org.orekit.estimation.measurements;
import java.util.List;
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.stat.descriptive.StreamingStatistics;
import org.hipparchus.util.FastMath;
import org.junit.Assert;
@@ -25,6 +26,7 @@ import org.junit.Test;
import org.orekit.estimation.Context;
import org.orekit.estimation.EstimationTestUtils;
import org.orekit.estimation.measurements.modifiers.RangeRateTroposphericDelayModifier;
+import org.orekit.frames.Transform;
import org.orekit.models.earth.troposphere.EstimatedTroposphericModel;
import org.orekit.models.earth.troposphere.GlobalMappingFunctionModel;
import org.orekit.models.earth.troposphere.SaastamoinenModel;
@@ -34,11 +36,7 @@ import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
import org.orekit.time.AbsoluteDate;
-import org.orekit.utils.Constants;
-import org.orekit.utils.Differentiation;
-import org.orekit.utils.ParameterDriver;
-import org.orekit.utils.ParameterFunction;
-import org.orekit.utils.StateFunction;
+import org.orekit.utils.*;
public class RangeRateTest {
@@ -722,6 +720,81 @@ public class RangeRateTest {
}
+ /**
+ * Test the estimated values when the observed range rate value is provided at TX (Transmit),
+ * RX (Receive (default)), transit (bounce)
+ */
+ @Test
+ public void testTimeTagSpecifications(){
+
+ Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
+ SpacecraftState state = new SpacecraftState(context.initialOrbit);
+ ObservableSatellite obsSat = new ObservableSatellite(0);
+
+ for (GroundStation gs : context.getStations()){
+
+ gs.getPrimeMeridianOffsetDriver().setReferenceDate(state.getDate());
+ gs.getPrimeMeridianDriftDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetXDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftXDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetYDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftYDriver().setReferenceDate(state.getDate());
+
+ Transform gsToInertialTransform = gs.getOffsetToInertial(state.getFrame(), state.getDate());
+ TimeStampedPVCoordinates stationPosition = gsToInertialTransform.transformPVCoordinates(new TimeStampedPVCoordinates(state.getDate(), Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO));
+
+ double staticDistance = stationPosition.getPosition().distance(state.getPVCoordinates().getPosition());
+ double staticTimeOfFlight = staticDistance / Constants.SPEED_OF_LIGHT;
+
+ RangeRate rangeRateTX = new RangeRate(gs, state.getDate(), 1.0, 1.0, 1.0, obsSat, TimeTagSpecificationType.TX);
+ RangeRate rangeRateRX = new RangeRate(gs, state.getDate(), 1.0, 1.0, 1.0, obsSat, TimeTagSpecificationType.RX);
+ RangeRate rangeRateTransit = new RangeRate(gs, state.getDate(), 1.0, 1.0, 1.0, obsSat, TimeTagSpecificationType.TRANSIT);
+
+ EstimatedMeasurement estRangeRateTX = rangeRateTX.estimate(0,0,new SpacecraftState[]{state});
+ EstimatedMeasurement estRangeRateRX = rangeRateRX.estimate(0,0,new SpacecraftState[]{state});
+ EstimatedMeasurement estRangeRateTransit = rangeRateTransit.estimate(0,0,new SpacecraftState[]{state});
+
+ //Calculate Range Rate calculated at transit and receive by shifting the state forward/backwards assuming time of flight = r/c
+ SpacecraftState tx = state.shiftedBy(staticTimeOfFlight);
+ SpacecraftState rx = state.shiftedBy(-staticTimeOfFlight);
+
+ double transitRangeRateTX = calcRangeRate(tx.getPVCoordinates(), stationPosition.shiftedBy(staticTimeOfFlight));
+ double transitRangeRateRX = calcRangeRate(rx.getPVCoordinates(), stationPosition.shiftedBy(-staticTimeOfFlight));
+ double transitRangeRateT = calcRangeRate(state.getPVCoordinates(), stationPosition);
+
+ //Static time of flight does not take into account motion during tof. Very small differences expected however
+ //delta expected vs actual <<< difference between TX, RX and transit predictions by a few orders of magnitude.
+ Assert.assertEquals("TX", transitRangeRateTX, estRangeRateTX.getEstimatedValue()[0],1e-6);
+ Assert.assertEquals("RX", transitRangeRateRX, estRangeRateRX.getEstimatedValue()[0],1e-6);
+ Assert.assertEquals("Transit", transitRangeRateT, estRangeRateTransit.getEstimatedValue()[0], 1e-11);
+
+ //Test for case in which the state has been precorrected for propagation delay / an arbitary shift
+ EstimatedMeasurement estRangeRateTXPrecorr = rangeRateTX.estimate(0,0,new SpacecraftState[]{state.shiftedBy(staticTimeOfFlight)});
+ EstimatedMeasurement estRangeRateRXPrecorr = rangeRateRX.estimate(0,0,new SpacecraftState[]{state.shiftedBy(-staticTimeOfFlight)});
+ EstimatedMeasurement estRangeRateTransitShift = rangeRateTransit.estimate(0,0,new SpacecraftState[]{state.shiftedBy(0.05)});
+
+ //tolerances are required since shifting the state forwards and backwards produces slight estimated value changes
+ Assert.assertEquals("TX shifted", estRangeRateTXPrecorr.getEstimatedValue()[0], estRangeRateTX.getEstimatedValue()[0],1e-6);
+ Assert.assertEquals("RX shifted", estRangeRateRXPrecorr.getEstimatedValue()[0], estRangeRateRX.getEstimatedValue()[0],1e-6);
+ Assert.assertEquals("Transit shifted", estRangeRateTransitShift.getEstimatedValue()[0], estRangeRateTransit.getEstimatedValue()[0], 1e-6);
+
+ //Show the effect of the change in time tag specification is far greater than the test tolerance due to usage
+ //of a static time of flight correction.
+ Assert.assertTrue(Math.abs(transitRangeRateRX - transitRangeRateTX) > 5e-3);
+ }
+
+ }
+
+ // range rate value
+ private double calcRangeRate(TimeStampedPVCoordinates state, TimeStampedPVCoordinates station){
+ Vector3D relativePosition = station.getPosition().subtract(state.getPosition());
+ Vector3D relativeVelocity = station.getVelocity().subtract(state.getVelocity());
+ Vector3D lineOfSight = relativePosition.normalize();
+ return Vector3D.dotProduct(relativeVelocity, lineOfSight);
+ }
+
}
diff --git a/src/test/java/org/orekit/estimation/measurements/RangeTest.java b/src/test/java/org/orekit/estimation/measurements/RangeTest.java
index 6e82d09cc3e20ed20df51c442e9569633cf473b9..61274470eb33de254dd827b7a562c62fd6dcab3b 100644
--- a/src/test/java/org/orekit/estimation/measurements/RangeTest.java
+++ b/src/test/java/org/orekit/estimation/measurements/RangeTest.java
@@ -32,6 +32,7 @@ import org.junit.Test;
import org.orekit.estimation.Context;
import org.orekit.estimation.EstimationTestUtils;
import org.orekit.estimation.measurements.modifiers.RangeTroposphericDelayModifier;
+import org.orekit.frames.Transform;
import org.orekit.models.earth.troposphere.EstimatedTroposphericModel;
import org.orekit.models.earth.troposphere.NiellMappingFunctionModel;
import org.orekit.models.earth.troposphere.SaastamoinenModel;
@@ -78,14 +79,14 @@ public class RangeTest {
// Run test
boolean isModifier = false;
double refErrorsPMedian = 6.5e-10;
- double refErrorsPMean = 4.1e-09;
- double refErrorsPMax = 2.1e-07;
+ double refErrorsPMean = 4.1e-09;
+ double refErrorsPMax = 2.1e-07;
double refErrorsVMedian = 2.2e-04;
- double refErrorsVMean = 6.2e-04;
- double refErrorsVMax = 1.3e-02;
+ double refErrorsVMean = 6.2e-04;
+ double refErrorsVMax = 1.3e-02;
this.genericTestStateDerivatives(isModifier, printResults,
- refErrorsPMedian, refErrorsPMean, refErrorsPMax,
- refErrorsVMedian, refErrorsVMean, refErrorsVMax);
+ refErrorsPMedian, refErrorsPMean, refErrorsPMax,
+ refErrorsVMedian, refErrorsVMean, refErrorsVMax);
}
/**
@@ -102,14 +103,14 @@ public class RangeTest {
// Run test
boolean isModifier = true;
double refErrorsPMedian = 6.2e-10;
- double refErrorsPMean = 3.5e-09;
- double refErrorsPMax = 1.6e-07;
+ double refErrorsPMean = 3.5e-09;
+ double refErrorsPMax = 1.6e-07;
double refErrorsVMedian = 2.2e-04;
- double refErrorsVMean = 6.2e-04;
- double refErrorsVMax = 1.3e-02;
+ double refErrorsVMean = 6.2e-04;
+ double refErrorsVMax = 1.3e-02;
this.genericTestStateDerivatives(isModifier, printResults,
- refErrorsPMedian, refErrorsPMean, refErrorsPMax,
- refErrorsVMedian, refErrorsVMean, refErrorsVMax);
+ refErrorsPMedian, refErrorsPMean, refErrorsPMax,
+ refErrorsVMedian, refErrorsVMean, refErrorsVMax);
}
/**
@@ -128,10 +129,10 @@ public class RangeTest {
// Run test
boolean isModifier = false;
double refErrorsMedian = 5.7e-9;
- double refErrorsMean = 6.4e-8;
- double refErrorsMax = 5.1e-6;
+ double refErrorsMean = 6.4e-8;
+ double refErrorsMax = 5.1e-6;
this.genericTestParameterDerivatives(isModifier, printResults,
- refErrorsMedian, refErrorsMean, refErrorsMax);
+ refErrorsMedian, refErrorsMean, refErrorsMax);
}
@@ -151,10 +152,10 @@ public class RangeTest {
// Run test
boolean isModifier = true;
double refErrorsMedian = 2.9e-8;
- double refErrorsMean = 4.9e-7;
- double refErrorsMax = 1.5e-5;
+ double refErrorsMean = 4.9e-7;
+ double refErrorsMax = 1.5e-5;
this.genericTestParameterDerivatives(isModifier, printResults,
- refErrorsMedian, refErrorsMean, refErrorsMax);
+ refErrorsMedian, refErrorsMean, refErrorsMax);
}
@@ -174,15 +175,16 @@ public class RangeTest {
// Run test
boolean isModifier = true;
double refErrorsMedian = 1.2e-9;
- double refErrorsMean = 1.9e-9;
- double refErrorsMax = 6.6e-9;
+ double refErrorsMean = 1.9e-9;
+ double refErrorsMax = 6.6e-9;
this.genericTestEstimatedParameterDerivatives(isModifier, printResults,
- refErrorsMedian, refErrorsMean, refErrorsMax);
+ refErrorsMedian, refErrorsMean, refErrorsMax);
}
/**
* Generic test function for values of the range
+ *
* @param printResults Print the results ?
*/
void genericTestValues(final boolean printResults) {
@@ -190,20 +192,20 @@ public class RangeTest {
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
- context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
- 1.0e-6, 60.0, 0.001);
+ context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
+ 1.0e-6, 60.0, 0.001);
// Create perfect range measurements
final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
- propagatorBuilder);
+ propagatorBuilder);
final double groundClockOffset = 1.234e-3;
for (final GroundStation station : context.stations) {
station.getClockOffsetDriver().setValue(groundClockOffset);
}
final List> measurements =
- EstimationTestUtils.createMeasurements(propagator,
- new RangeMeasurementCreator(context, Vector3D.ZERO),
- 1.0, 3.0, 300.0);
+ EstimationTestUtils.createMeasurements(propagator,
+ new RangeMeasurementCreator(context, Vector3D.ZERO),
+ 1.0, 3.0, 300.0);
// Lists for results' storage - Used only for derivatives with respect to state
// "final" value to be seen by "handleStep" function of the propagator
@@ -218,8 +220,8 @@ public class RangeTest {
// Play test if the measurement date is between interpolator previous and current date
if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) &&
- (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
- ) {
+ (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
+ ) {
// We intentionally propagate to a date which is close to the
// real spacecraft state but is *not* the accurate date, by
// compensating only part of the downlink delay. This is done
@@ -227,19 +229,19 @@ public class RangeTest {
// to velocity. If we had chosen the proper state date, the
// range would have depended only on the current position but
// not on the current velocity.
- final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
- final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
- final SpacecraftState state = interpolator.getInterpolatedState(date);
+ final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
+ final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
+ final SpacecraftState state = interpolator.getInterpolatedState(date);
// Values of the Range & errors
- final double RangeObserved = measurement.getObservedValue()[0];
- final EstimatedMeasurement> estimated = measurement.estimate(0, 0, new SpacecraftState[] { state });
+ final double RangeObserved = measurement.getObservedValue()[0];
+ final EstimatedMeasurement> estimated = measurement.estimate(0, 0, new SpacecraftState[]{state});
final TimeStampedPVCoordinates[] participants = estimated.getParticipants();
Assert.assertEquals(3, participants.length);
Assert.assertEquals(0.5 * Constants.SPEED_OF_LIGHT * participants[2].getDate().durationFrom(participants[0].getDate()),
- estimated.getEstimatedValue()[0],
- 2.0e-8);
+ estimated.getEstimatedValue()[0],
+ 2.0e-8);
// the real state used for estimation is adjusted according to downlink delay
double adjustment = state.getDate().durationFrom(estimated.getStates()[0].getDate());
@@ -247,9 +249,9 @@ public class RangeTest {
Assert.assertTrue(adjustment < 0.011);
final double RangeEstimated = estimated.getEstimatedValue()[0];
- final double absoluteError = RangeEstimated-RangeObserved;
+ final double absoluteError = RangeEstimated - RangeObserved;
absoluteErrors.add(absoluteError);
- relativeErrors.add(FastMath.abs(absoluteError)/FastMath.abs(RangeObserved));
+ relativeErrors.add(FastMath.abs(absoluteError) / FastMath.abs(RangeObserved));
// Print results on console ?
if (printResults) {
@@ -257,10 +259,10 @@ public class RangeTest {
String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
System.out.format(Locale.US, "%-15s %-23s %-23s %19.6f %19.6f %13.6e %13.6e%n",
- stationName, measurementDate, date,
- RangeObserved, RangeEstimated,
- FastMath.abs(RangeEstimated-RangeObserved),
- FastMath.abs((RangeEstimated-RangeObserved)/RangeObserved));
+ stationName, measurementDate, date,
+ RangeObserved, RangeEstimated,
+ FastMath.abs(RangeEstimated - RangeObserved),
+ FastMath.abs((RangeEstimated - RangeObserved) / RangeObserved));
}
} // End if measurement date between previous and current interpolator step
@@ -270,9 +272,9 @@ public class RangeTest {
// Print results on console ? Header
if (printResults) {
System.out.format(Locale.US, "%-15s %-23s %-23s %19s %19s %13s %13s%n",
- "Station", "Measurement Date", "State Date",
- "Range observed [m]", "Range estimated [m]",
- "ΔRange [m]", "rel ΔRange");
+ "Station", "Measurement Date", "State Date",
+ "Range observed [m]", "Range estimated [m]",
+ "ΔRange [m]", "rel ΔRange");
}
// Rewind the propagator to initial date
@@ -282,7 +284,7 @@ public class RangeTest {
measurements.sort(Comparator.naturalOrder());
// Propagate to final measurement's date
- propagator.propagate(measurements.get(measurements.size()-1).getDate());
+ propagator.propagate(measurements.get(measurements.size() - 1).getDate());
// Convert lists to double array
final double[] absErrors = absoluteErrors.stream().mapToDouble(Double::doubleValue).toArray();
@@ -290,26 +292,26 @@ public class RangeTest {
// Statistics' assertion
final double absErrorsMedian = new Median().evaluate(absErrors);
- final double absErrorsMin = new Min().evaluate(absErrors);
- final double absErrorsMax = new Max().evaluate(absErrors);
+ final double absErrorsMin = new Min().evaluate(absErrors);
+ final double absErrorsMax = new Max().evaluate(absErrors);
final double relErrorsMedian = new Median().evaluate(relErrors);
- final double relErrorsMax = new Max().evaluate(relErrors);
+ final double relErrorsMax = new Max().evaluate(relErrors);
// Print the results on console ? Final results
if (printResults) {
System.out.println();
- System.out.println("Absolute errors median: " + absErrorsMedian);
- System.out.println("Absolute errors min : " + absErrorsMin);
- System.out.println("Absolute errors max : " + absErrorsMax);
- System.out.println("Relative errors median: " + relErrorsMedian);
- System.out.println("Relative errors max : " + relErrorsMax);
+ System.out.println("Absolute errors median: " + absErrorsMedian);
+ System.out.println("Absolute errors min : " + absErrorsMin);
+ System.out.println("Absolute errors max : " + absErrorsMax);
+ System.out.println("Relative errors median: " + relErrorsMedian);
+ System.out.println("Relative errors max : " + relErrorsMax);
}
Assert.assertEquals(0.0, absErrorsMedian, 4.9e-8);
- Assert.assertEquals(0.0, absErrorsMin, 2.2e-7);
- Assert.assertEquals(0.0, absErrorsMax, 2.1e-7);
+ Assert.assertEquals(0.0, absErrorsMin, 2.2e-7);
+ Assert.assertEquals(0.0, absErrorsMax, 2.1e-7);
Assert.assertEquals(0.0, relErrorsMedian, 1.0e-14);
- Assert.assertEquals(0.0, relErrorsMax, 2.6e-14);
+ Assert.assertEquals(0.0, relErrorsMax, 2.6e-14);
}
@@ -321,16 +323,16 @@ public class RangeTest {
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
- context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
- 1.0e-6, 60.0, 0.001);
+ context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
+ 1.0e-6, 60.0, 0.001);
// Create perfect range measurements
final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
- propagatorBuilder);
+ propagatorBuilder);
final List> measurements =
- EstimationTestUtils.createMeasurements(propagator,
- new RangeMeasurementCreator(context),
- 1.0, 3.0, 300.0);
+ EstimationTestUtils.createMeasurements(propagator,
+ new RangeMeasurementCreator(context),
+ 1.0, 3.0, 300.0);
// Lists for results' storage - Used only for derivatives with respect to state
// "final" value to be seen by "handleStep" function of the propagator
@@ -345,8 +347,8 @@ public class RangeTest {
// Play test if the measurement date is between interpolator previous and current date
if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) &&
- (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
- ) {
+ (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
+ ) {
// Add modifiers if test implies it
final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(SaastamoinenModel.getStandardModel());
@@ -361,50 +363,53 @@ public class RangeTest {
// to velocity. If we had chosen the proper state date, the
// range would have depended only on the current position but
// not on the current velocity.
- final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
- final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
- final SpacecraftState state = interpolator.getInterpolatedState(date);
- final double[][] jacobian = measurement.estimate(0, 0, new SpacecraftState[] { state }).getStateDerivatives(0);
+ final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
+ final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
+ final SpacecraftState state = interpolator.getInterpolatedState(date);
+ final double[][] jacobian = measurement.estimate(0, 0, new SpacecraftState[]{state}).getStateDerivatives(0);
// Jacobian reference value
final double[][] jacobianRef;
// Compute a reference value using finite differences
jacobianRef = Differentiation.differentiate(new StateFunction() {
- public double[] value(final SpacecraftState state) {
- return measurement.estimate(0, 0, new SpacecraftState[] { state }).getEstimatedValue();
- }
- }, measurement.getDimension(), propagator.getAttitudeProvider(),
- OrbitType.CARTESIAN, PositionAngle.TRUE, 2.0, 3).value(state);
+ public double[] value(final SpacecraftState state) {
+ return measurement.estimate(0, 0, new SpacecraftState[]{state}).getEstimatedValue();
+ }
+ }, measurement.getDimension(), propagator.getAttitudeProvider(),
+ OrbitType.CARTESIAN, PositionAngle.TRUE, 2.0, 3).value(state);
Assert.assertEquals(jacobianRef.length, jacobian.length);
Assert.assertEquals(jacobianRef[0].length, jacobian[0].length);
// Errors & relative errors on the Jacobian
- double [][] dJacobian = new double[jacobian.length][jacobian[0].length];
- double [][] dJacobianRelative = new double[jacobian.length][jacobian[0].length];
+ double[][] dJacobian = new double[jacobian.length][jacobian[0].length];
+ double[][] dJacobianRelative = new double[jacobian.length][jacobian[0].length];
for (int i = 0; i < jacobian.length; ++i) {
for (int j = 0; j < jacobian[i].length; ++j) {
dJacobian[i][j] = jacobian[i][j] - jacobianRef[i][j];
- dJacobianRelative[i][j] = FastMath.abs(dJacobian[i][j]/jacobianRef[i][j]);
+ dJacobianRelative[i][j] = FastMath.abs(dJacobian[i][j] / jacobianRef[i][j]);
- if (j < 3) { errorsP.add(dJacobianRelative[i][j]);
- } else { errorsV.add(dJacobianRelative[i][j]); }
+ if (j < 3) {
+ errorsP.add(dJacobianRelative[i][j]);
+ } else {
+ errorsV.add(dJacobianRelative[i][j]);
+ }
}
}
// Print values in console ?
if (printResults) {
- String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
+ String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
System.out.format(Locale.US, "%-15s %-23s %-23s " +
"%10.3e %10.3e %10.3e " +
"%10.3e %10.3e %10.3e " +
"%10.3e %10.3e %10.3e " +
"%10.3e %10.3e %10.3e%n",
- stationName, measurement.getDate(), date,
- dJacobian[0][0], dJacobian[0][1], dJacobian[0][2],
- dJacobian[0][3], dJacobian[0][4], dJacobian[0][5],
- dJacobianRelative[0][0], dJacobianRelative[0][1], dJacobianRelative[0][2],
- dJacobianRelative[0][3], dJacobianRelative[0][4], dJacobianRelative[0][5]);
+ stationName, measurement.getDate(), date,
+ dJacobian[0][0], dJacobian[0][1], dJacobian[0][2],
+ dJacobian[0][3], dJacobian[0][4], dJacobian[0][5],
+ dJacobianRelative[0][0], dJacobianRelative[0][1], dJacobianRelative[0][2],
+ dJacobianRelative[0][3], dJacobianRelative[0][4], dJacobianRelative[0][5]);
}
} // End if measurement date between previous and current interpolator step
} // End for loop on the measurements
@@ -417,10 +422,10 @@ public class RangeTest {
"%10s %10s %10s " +
"%10s %10s %10s " +
"%10s %10s %10s%n",
- "Station", "Measurement Date", "State Date",
- "ΔdPx", "ΔdPy", "ΔdPz", "ΔdVx", "ΔdVy", "ΔdVz",
- "rel ΔdPx", "rel ΔdPy", "rel ΔdPz",
- "rel ΔdVx", "rel ΔdVy", "rel ΔdVz");
+ "Station", "Measurement Date", "State Date",
+ "ΔdPx", "ΔdPy", "ΔdPz", "ΔdVx", "ΔdVy", "ΔdVz",
+ "rel ΔdPx", "rel ΔdPy", "rel ΔdPz",
+ "rel ΔdVx", "rel ΔdVy", "rel ΔdVz");
}
// Rewind the propagator to initial date
@@ -430,26 +435,26 @@ public class RangeTest {
measurements.sort(Comparator.naturalOrder());
// Propagate to final measurement's date
- propagator.propagate(measurements.get(measurements.size()-1).getDate());
+ propagator.propagate(measurements.get(measurements.size() - 1).getDate());
// Convert lists to double[] and evaluate some statistics
final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray();
final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray();
final double errorsPMedian = new Median().evaluate(relErrorsP);
- final double errorsPMean = new Mean().evaluate(relErrorsP);
- final double errorsPMax = new Max().evaluate(relErrorsP);
+ final double errorsPMean = new Mean().evaluate(relErrorsP);
+ final double errorsPMax = new Max().evaluate(relErrorsP);
final double errorsVMedian = new Median().evaluate(relErrorsV);
- final double errorsVMean = new Mean().evaluate(relErrorsV);
- final double errorsVMax = new Max().evaluate(relErrorsV);
+ final double errorsVMean = new Mean().evaluate(relErrorsV);
+ final double errorsVMax = new Max().evaluate(relErrorsV);
// Print the results on console ?
if (printResults) {
System.out.println();
System.out.format(Locale.US, "Relative errors dR/dP -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n",
- errorsPMedian, errorsPMean, errorsPMax);
+ errorsPMedian, errorsPMean, errorsPMax);
System.out.format(Locale.US, "Relative errors dR/dV -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n",
- errorsVMedian, errorsVMean, errorsVMax);
+ errorsVMedian, errorsVMean, errorsVMax);
}
Assert.assertEquals(0.0, errorsPMedian, refErrorsPMedian);
@@ -466,8 +471,8 @@ public class RangeTest {
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
- context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
- 1.0e-6, 60.0, 0.001);
+ context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
+ 1.0e-6, 60.0, 0.001);
// Create perfect range measurements
for (final GroundStation station : context.stations) {
@@ -477,11 +482,11 @@ public class RangeTest {
station.getZenithOffsetDriver().setSelected(true);
}
final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
- propagatorBuilder);
+ propagatorBuilder);
final List> measurements =
- EstimationTestUtils.createMeasurements(propagator,
- new RangeMeasurementCreator(context),
- 1.0, 3.0, 300.0);
+ EstimationTestUtils.createMeasurements(propagator,
+ new RangeMeasurementCreator(context),
+ 1.0, 3.0, 300.0);
// List to store the results
final List relErrorList = new ArrayList();
@@ -494,8 +499,8 @@ public class RangeTest {
// Play test if the measurement date is between interpolator previous and current date
if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) &&
- (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
- ) {
+ (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
+ ) {
// Add modifiers if test implies it
final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(SaastamoinenModel.getStandardModel());
@@ -513,43 +518,43 @@ public class RangeTest {
// to velocity. If we had chosen the proper state date, the
// range would have depended only on the current position but
// not on the current velocity.
- final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
- final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
- final SpacecraftState state = interpolator.getInterpolatedState(date);
- final ParameterDriver[] drivers = new ParameterDriver[] {
- station.getClockOffsetDriver(),
- station.getEastOffsetDriver(),
- station.getNorthOffsetDriver(),
- station.getZenithOffsetDriver()
+ final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
+ final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
+ final SpacecraftState state = interpolator.getInterpolatedState(date);
+ final ParameterDriver[] drivers = new ParameterDriver[]{
+ station.getClockOffsetDriver(),
+ station.getEastOffsetDriver(),
+ station.getNorthOffsetDriver(),
+ station.getZenithOffsetDriver()
};
if (printResults) {
- String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
+ String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
System.out.format(Locale.US, "%-15s %-23s %-23s ",
- stationName, measurement.getDate(), date);
+ stationName, measurement.getDate(), date);
}
for (int i = 0; i < drivers.length; ++i) {
- final double[] gradient = measurement.estimate(0, 0, new SpacecraftState[] { state }).getParameterDerivatives(drivers[i]);
+ final double[] gradient = measurement.estimate(0, 0, new SpacecraftState[]{state}).getParameterDerivatives(drivers[i]);
Assert.assertEquals(1, measurement.getDimension());
Assert.assertEquals(1, gradient.length);
// Compute a reference value using finite differences
final ParameterFunction dMkdP =
- Differentiation.differentiate(new ParameterFunction() {
- /** {@inheritDoc} */
- @Override
- public double value(final ParameterDriver parameterDriver) {
- return measurement.estimate(0, 0, new SpacecraftState[] { state }).getEstimatedValue()[0];
- }
- }, 3, 20.0 * drivers[i].getScale());
+ Differentiation.differentiate(new ParameterFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(final ParameterDriver parameterDriver) {
+ return measurement.estimate(0, 0, new SpacecraftState[]{state}).getEstimatedValue()[0];
+ }
+ }, 3, 20.0 * drivers[i].getScale());
final double ref = dMkdP.value(drivers[i]);
if (printResults) {
- System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0]-ref, FastMath.abs((gradient[0]-ref)/ref));
+ System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0] - ref, FastMath.abs((gradient[0] - ref) / ref));
}
- final double relError = FastMath.abs((ref-gradient[0])/ref);
+ final double relError = FastMath.abs((ref - gradient[0]) / ref);
relErrorList.add(relError);
Assert.assertEquals(ref, gradient[0], 6.1e-5 * FastMath.abs(ref));
}
@@ -570,30 +575,30 @@ public class RangeTest {
// Print results ? Header
if (printResults) {
System.out.format(Locale.US, "%-15s %-23s %-23s " +
- "%10s %10s %10s %10s %10s %10s %10s %10s%n",
- "Station", "Measurement Date", "State Date",
- "Δt", "rel Δt",
- "ΔdQx", "rel ΔdQx",
- "ΔdQy", "rel ΔdQy",
- "ΔdQz", "rel ΔdQz");
- }
+ "%10s %10s %10s %10s %10s %10s %10s %10s%n",
+ "Station", "Measurement Date", "State Date",
+ "Δt", "rel Δt",
+ "ΔdQx", "rel ΔdQx",
+ "ΔdQy", "rel ΔdQy",
+ "ΔdQz", "rel ΔdQz");
+ }
// Propagate to final measurement's date
- propagator.propagate(measurements.get(measurements.size()-1).getDate());
+ propagator.propagate(measurements.get(measurements.size() - 1).getDate());
// Convert error list to double[]
final double relErrors[] = relErrorList.stream().mapToDouble(Double::doubleValue).toArray();
// Compute statistics
final double relErrorsMedian = new Median().evaluate(relErrors);
- final double relErrorsMean = new Mean().evaluate(relErrors);
- final double relErrorsMax = new Max().evaluate(relErrors);
+ final double relErrorsMean = new Mean().evaluate(relErrors);
+ final double relErrorsMax = new Max().evaluate(relErrors);
// Print the results on console ?
if (printResults) {
System.out.println();
System.out.format(Locale.US, "Relative errors dR/dQ -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n",
- relErrorsMedian, relErrorsMean, relErrorsMax);
+ relErrorsMedian, relErrorsMean, relErrorsMax);
}
Assert.assertEquals(0.0, relErrorsMedian, refErrorsMedian);
@@ -603,21 +608,20 @@ public class RangeTest {
}
void genericTestEstimatedParameterDerivatives(final boolean isModifier, final boolean printResults,
- final double refErrorsMedian, final double refErrorsMean, final double refErrorsMax)
- {
+ final double refErrorsMedian, final double refErrorsMean, final double refErrorsMax) {
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
- context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
- 1.0e-6, 60.0, 0.001);
+ context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
+ 1.0e-6, 60.0, 0.001);
final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
- propagatorBuilder);
+ propagatorBuilder);
final List> measurements =
- EstimationTestUtils.createMeasurements(propagator,
- new RangeMeasurementCreator(context),
- 1.0, 3.0, 300.0);
+ EstimationTestUtils.createMeasurements(propagator,
+ new RangeMeasurementCreator(context),
+ 1.0, 3.0, 300.0);
// List to store the results
final List relErrorList = new ArrayList();
@@ -630,15 +634,15 @@ public class RangeTest {
// Play test if the measurement date is between interpolator previous and current date
if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) &&
- (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
- ) {
+ (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)
+ ) {
- String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
+ String stationName = ((Range) measurement).getStation().getBaseFrame().getName();
// Add modifiers if test implies it
final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel();
- final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0);
-
+ final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0);
+
final List parameters = tropoModel.getParametersDrivers();
for (ParameterDriver driver : parameters) {
driver.setSelected(true);
@@ -657,39 +661,39 @@ public class RangeTest {
// to velocity. If we had chosen the proper state date, the
// range would have depended only on the current position but
// not on the current velocity.
- final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
- final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
- final SpacecraftState state = interpolator.getInterpolatedState(date);
- final ParameterDriver[] drivers = new ParameterDriver[] {
- parameters.get(0)
+ final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT;
+ final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay);
+ final SpacecraftState state = interpolator.getInterpolatedState(date);
+ final ParameterDriver[] drivers = new ParameterDriver[]{
+ parameters.get(0)
};
if (printResults) {
System.out.format(Locale.US, "%-15s %-23s %-23s ",
- stationName, measurement.getDate(), date);
+ stationName, measurement.getDate(), date);
}
for (int i = 0; i < 1; ++i) {
- final double[] gradient = measurement.estimate(0, 0, new SpacecraftState[] { state }).getParameterDerivatives(drivers[i]);
+ final double[] gradient = measurement.estimate(0, 0, new SpacecraftState[]{state}).getParameterDerivatives(drivers[i]);
Assert.assertEquals(1, measurement.getDimension());
Assert.assertEquals(1, gradient.length);
// Compute a reference value using finite differences
final ParameterFunction dMkdP =
- Differentiation.differentiate(new ParameterFunction() {
- /** {@inheritDoc} */
- @Override
- public double value(final ParameterDriver parameterDriver) {
- return measurement.estimate(0, 0, new SpacecraftState[] { state }).getEstimatedValue()[0];
- }
- }, 3, 0.1 * drivers[i].getScale());
+ Differentiation.differentiate(new ParameterFunction() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(final ParameterDriver parameterDriver) {
+ return measurement.estimate(0, 0, new SpacecraftState[]{state}).getEstimatedValue()[0];
+ }
+ }, 3, 0.1 * drivers[i].getScale());
final double ref = dMkdP.value(drivers[i]);
if (printResults) {
- System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0]-ref, FastMath.abs((gradient[0]-ref)/ref));
+ System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0] - ref, FastMath.abs((gradient[0] - ref) / ref));
}
- final double relError = FastMath.abs((ref-gradient[0])/ref);
+ final double relError = FastMath.abs((ref - gradient[0]) / ref);
relErrorList.add(relError);
// Assert.assertEquals(ref, gradient[0], 6.1e-5 * FastMath.abs(ref));
}
@@ -712,28 +716,28 @@ public class RangeTest {
System.out.format(Locale.US, "%-15s %-23s %-23s " +
"%10s %10s %10s " +
"%10s %10s %10s%n ",
- "Station", "Measurement Date", "State Date",
- "ΔdDelay", "rel ΔdDelay",
- "ΔdNorthG", "rel ΔdNorthG",
- "ΔdEastG", "rel ΔdEastG");
- }
+ "Station", "Measurement Date", "State Date",
+ "ΔdDelay", "rel ΔdDelay",
+ "ΔdNorthG", "rel ΔdNorthG",
+ "ΔdEastG", "rel ΔdEastG");
+ }
// Propagate to final measurement's date
- propagator.propagate(measurements.get(measurements.size()-1).getDate());
+ propagator.propagate(measurements.get(measurements.size() - 1).getDate());
// Convert error list to double[]
final double relErrors[] = relErrorList.stream().mapToDouble(Double::doubleValue).toArray();
// Compute statistics
final double relErrorsMedian = new Median().evaluate(relErrors);
- final double relErrorsMean = new Mean().evaluate(relErrors);
- final double relErrorsMax = new Max().evaluate(relErrors);
+ final double relErrorsMean = new Mean().evaluate(relErrors);
+ final double relErrorsMax = new Max().evaluate(relErrors);
// Print the results on console ?
if (printResults) {
System.out.println();
System.out.format(Locale.US, "Relative errors dR/dQ -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n",
- relErrorsMedian, relErrorsMean, relErrorsMax);
+ relErrorsMedian, relErrorsMean, relErrorsMax);
}
Assert.assertEquals(0.0, relErrorsMedian, refErrorsMedian);
@@ -742,4 +746,71 @@ public class RangeTest {
}
+
+ /**
+ * Test the estimated values when the observed range value is provided at TX (Transmit),
+ * RX (Receive (default)), transit (bounce)
+ */
+ @Test
+ public void testTimeTagSpecifications() {
+
+ Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
+ SpacecraftState state = new SpacecraftState(context.initialOrbit);
+ ObservableSatellite obsSat = new ObservableSatellite(0);
+
+ for (GroundStation gs : context.getStations()) {
+
+ gs.getPrimeMeridianOffsetDriver().setReferenceDate(state.getDate());
+ gs.getPrimeMeridianDriftDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetXDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftXDriver().setReferenceDate(state.getDate());
+
+ gs.getPolarOffsetYDriver().setReferenceDate(state.getDate());
+ gs.getPolarDriftYDriver().setReferenceDate(state.getDate());
+
+ Transform gsToInertialTransform = gs.getOffsetToInertial(state.getFrame(), state.getDate());
+ TimeStampedPVCoordinates stationPosition = gsToInertialTransform.transformPVCoordinates(new TimeStampedPVCoordinates(state.getDate(), Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO));
+
+ double staticDistance = stationPosition.getPosition().distance(state.getPVCoordinates().getPosition());
+ double staticTimeOfFlight = staticDistance / Constants.SPEED_OF_LIGHT;
+
+ Range rangeTX = new Range(gs, state.getDate(), 1.0, 1.0, 1.0, obsSat, TimeTagSpecificationType.TX);
+ Range rangeRX = new Range(gs, state.getDate(), 1.0, 1.0, 1.0, obsSat, TimeTagSpecificationType.RX);
+ Range rangeTransit = new Range(gs, state.getDate(), 1.0, 1.0, 1.0, obsSat, TimeTagSpecificationType.TRANSIT);
+
+ EstimatedMeasurement estRangeTX = rangeTX.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estRangeRX = rangeRX.estimate(0, 0, new SpacecraftState[]{state});
+ EstimatedMeasurement estRangeTransit = rangeTransit.estimate(0, 0, new SpacecraftState[]{state});
+
+ //Calculate Range calculated at transit and receive by shifting the state forward/backwards assuming time of flight = r/c
+ SpacecraftState tx = state.shiftedBy(staticTimeOfFlight);
+ SpacecraftState rx = state.shiftedBy(-staticTimeOfFlight);
+
+ double transitRangeTX = tx.getPVCoordinates().getPosition().distance(stationPosition.shiftedBy(staticTimeOfFlight).getPosition());
+ double transitRangeRX = rx.getPVCoordinates().getPosition().distance(stationPosition.shiftedBy(-staticTimeOfFlight).getPosition());
+ double transitRangeT = state.getPVCoordinates().getPosition().distance(stationPosition.getPosition());
+
+ //Static time of flight does not take into account motion during tof. Very small differences expected however
+ //delta expected vs actual <<< difference between TX, RX and transit predictions by a few orders of magnitude.
+ Assert.assertEquals("TX", transitRangeTX, estRangeTX.getEstimatedValue()[0], 1e-3);
+ Assert.assertEquals("RX", transitRangeRX, estRangeRX.getEstimatedValue()[0], 1e-3);
+ Assert.assertEquals("Transit", transitRangeT, estRangeTransit.getEstimatedValue()[0], 1e-6);
+
+ //Test providing pre corrected states + an arbitarily shifted case - since this should have no significant effect on the value.
+ EstimatedMeasurement estRangeTXPreCorr = rangeTX.estimate(0,0,new SpacecraftState[]{state.shiftedBy(staticTimeOfFlight)});
+ EstimatedMeasurement estRangeRXPreCorr = rangeRX.estimate(0,0,new SpacecraftState[]{state.shiftedBy(-staticTimeOfFlight)});
+ EstimatedMeasurement estRangeTransitPreCorr = rangeTransit.estimate(0,0,new SpacecraftState[]{state.shiftedBy(0.1)});
+
+ //tolerances are required since shifting the state forwards and backwards produces slight estimated value changes
+ Assert.assertEquals("TX shifted", estRangeTXPreCorr.getEstimatedValue()[0], estRangeTX.getEstimatedValue()[0],1e-7);
+ Assert.assertEquals("RX shifted", estRangeRXPreCorr.getEstimatedValue()[0], estRangeRX.getEstimatedValue()[0],1e-7);
+ Assert.assertEquals("Transit shifted", estRangeTransitPreCorr.getEstimatedValue()[0], estRangeTransit.getEstimatedValue()[0], 1e-7);
+
+ //Show the effect of the change in time tag specification is far greater than the test tolerance due to usage
+ //of a static time of flight correction.
+ Assert.assertTrue(Math.abs(transitRangeTX - transitRangeRX)>100.0);
+ }
+
+ }
}