AbstractRangeRelatedMeasurement.java
/* Copyright 2022-2026 Romain Serra
* 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;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import org.hipparchus.analysis.differentiation.Gradient;
import org.hipparchus.analysis.differentiation.GradientField;
import org.orekit.frames.Frame;
import org.orekit.propagation.SpacecraftState;
import org.orekit.signal.AdjustableEmitterSignalTimer;
import org.orekit.signal.SignalReceptionCondition;
import org.orekit.signal.SignalTravelTimeModel;
import org.orekit.signal.TwoLeggedSignalTimer;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.FieldAbsoluteDate;
import org.orekit.utils.FieldPVCoordinatesProvider;
import org.orekit.utils.PVCoordinatesProvider;
import org.orekit.utils.ParameterDriver;
import org.orekit.utils.TimeSpanMap.Span;
import org.orekit.utils.TimeStampedFieldPVCoordinates;
import org.orekit.utils.TimeStampedPVCoordinates;
/** Abstract class modeling one-way or two-way range-related measurement.
* For the latter, a signal is emitted by a sensor, reflected on the spacecraft and received by the same sensor.
* For the former, a signal is emitted by the spacecraft and receiver by the sensor.
*
* @author Romain Serra
* @since 14.0
*/
public abstract class AbstractRangeRelatedMeasurement<T extends AbstractRangeRelatedMeasurement<T>>
extends SignalBasedMeasurement<T> {
/** Sensor that receives signal from satellite. */
private final Observer observer;
/** Simple constructor.
* @param observer observer that performs the measurement
* @param date date of the measurement
* @param value observed value
* @param measurementQuality measurement quality data as used in orbit determination
* @param signalTravelTimeModel signal model
* @param twoWay if true, this is a two-way measurement
* @param satellite satellite related to this measurement
*/
protected AbstractRangeRelatedMeasurement(final Observer observer, final AbsoluteDate date,
final double value, final MeasurementQuality measurementQuality,
final boolean twoWay, final SignalTravelTimeModel signalTravelTimeModel,
final ObservableSatellite satellite) {
super(date, twoWay, new double[] { value }, measurementQuality, signalTravelTimeModel,
Collections.singletonList(satellite));
addParametersDrivers(observer.getParametersDrivers());
this.observer = observer;
}
/** Get receiving ground station.
* @return measurement ground station
* @deprecated as of 14.0, replaced by {@link #getObserver()}
*/
@Deprecated
public final GroundStation getStation() {
if (!(observer instanceof GroundStation)) {
return null;
}
return (GroundStation) observer;
}
/** Get receiving object.
* @return measurement observer
*/
public final Observer getObserver() {
return observer;
}
/**
* Compute participants for two-way case.
* @param measurement measurement object
* @param iteration iteration
* @param evaluation evaluation
* @param receptionDate signal reception date
* @param state estimated state
* @return partially filled estimated measurement
*/
protected EstimatedMeasurementBase<T> initializeTwoWayTheoreticalEvaluation(final T measurement,
final int iteration, final int evaluation,
final AbsoluteDate receptionDate,
final SpacecraftState state) {
// Compute light time delays
final Frame frame = state.getFrame();
final PVCoordinatesProvider observerPVProvider = getObserver().getPVCoordinatesProvider();
final TimeStampedPVCoordinates receiverPV = observerPVProvider.getPVCoordinates(receptionDate, frame);
final PVCoordinatesProvider satellitePVProvider = AbstractParticipant.extractPVCoordinatesProvider(state,
state.getPVCoordinates());
final TwoLeggedSignalTimer twoLeggedSignalTimer = new TwoLeggedSignalTimer(getSignalTravelTimeModel());
final SignalReceptionCondition receptionCondition = new SignalReceptionCondition(receptionDate, receiverPV.getPosition(),
frame);
final double[] delays = twoLeggedSignalTimer.computeDelays(receptionCondition, satellitePVProvider,
observerPVProvider);
// Prepare estimation
final AbsoluteDate transitDate = receptionDate.shiftedBy(-delays[1]);
final AbsoluteDate emissionDate = transitDate.shiftedBy(-delays[0]);
final SpacecraftState transitState = state.shiftedBy(transitDate.durationFrom(state));
final TimeStampedPVCoordinates emissionPV = observerPVProvider.getPVCoordinates(emissionDate, frame);
return new EstimatedMeasurementBase<>(measurement, iteration, evaluation,
new SpacecraftState[] { transitState },
new TimeStampedPVCoordinates[] {emissionPV, transitState.getPVCoordinates(), receiverPV});
}
/**
* Compute participants for one-way case.
* @param measurement measurement object
* @param iteration iteration
* @param evaluation evaluation
* @param receptionDate signal reception date
* @param state estimated state
* @return partially filled estimated measurement
*/
protected EstimatedMeasurementBase<T> initializeOneWayTheoreticalEvaluation(final T measurement,
final int iteration, final int evaluation,
final AbsoluteDate receptionDate,
final SpacecraftState state) {
// compute light time delay
final Frame frame = state.getFrame();
final PVCoordinatesProvider observablePVProvider = AbstractParticipant.extractPVCoordinatesProvider(state, state.getPVCoordinates());
final AdjustableEmitterSignalTimer adjustableEmitter = getSignalTravelTimeModel()
.getAdjustableEmitterComputer(observablePVProvider);
final TimeStampedPVCoordinates observerPVAtReception = getObserver().getPVCoordinatesProvider()
.getPVCoordinates(receptionDate, frame);
final double delay = adjustableEmitter.computeDelay(new SignalReceptionCondition(receptionDate,
observerPVAtReception.getPosition(), frame));
// prepare the evaluation
final AbsoluteDate emissionDate = receptionDate.shiftedBy(-delay);
final SpacecraftState emissionState = state.shiftedBy(emissionDate.durationFrom(state));
return new EstimatedMeasurementBase<>(measurement, iteration, evaluation,
new SpacecraftState[] { emissionState },
new TimeStampedPVCoordinates[] { emissionState.getPVCoordinates(), observerPVAtReception });
}
/** {@inheritDoc} */
@Override
protected EstimatedMeasurement<T> theoreticalEvaluation(final int iteration, final int evaluation,
final SpacecraftState[] states) {
// Derivatives are computed with respect to spacecraft state in inertial frame and measurement model params
// -------
//
// Parameters:
// - 0..2 - Position of the spacecraft in inertial frame
// - 3..5 - Velocity of the spacecraft in inertial frame
// - 6..n - observer parameters (clock offset, clock drift, ...)
final Map<String, Integer> paramIndices = getParameterIndices(states);
final int nbParams = 6 * states.length + paramIndices.size();
final SpacecraftState state = states[0];
final TimeStampedFieldPVCoordinates<Gradient> pva = AbstractMeasurement.getCoordinates(state, 0, nbParams);
final FieldPVCoordinatesProvider<Gradient> observablePVProvider = AbstractParticipant
.extractFieldPVCoordinatesProvider(state, pva);
if (isTwoWay()) {
return twoWayTheoreticalEvaluation(iteration, evaluation, observablePVProvider, state, paramIndices, nbParams);
} else {
return oneWayTheoreticalEvaluation(iteration, evaluation, observablePVProvider, state, paramIndices, nbParams);
}
}
/** Evaluate measurement in two-way.
* @param iteration iteration number
* @param evaluation evaluations counter
* @param observablePVProvider coordinates provider of observable for automatic differentiation
* @param state observable state
* @param paramIndices indices of the estimated parameters in derivatives computations
* @param nbParams the number of estimated parameters in derivative computations
* @return theoretical value
*/
protected abstract EstimatedMeasurement<T> twoWayTheoreticalEvaluation(int iteration, int evaluation,
FieldPVCoordinatesProvider<Gradient> observablePVProvider,
SpacecraftState state,
Map<String, Integer> paramIndices,
int nbParams);
/** Evaluate measurement in one-way.
* @param iteration iteration number
* @param evaluation evaluations counter
* @param observablePVProvider coordinates provider of observable for automatic differentiation
* @param state observable state
* @param paramIndices indices of the estimated parameters in derivatives computations
* @param nbParams the number of estimated parameters in derivative computations
* @return theoretical value
*/
protected abstract EstimatedMeasurement<T> oneWayTheoreticalEvaluation(int iteration, int evaluation,
FieldPVCoordinatesProvider<Gradient> observablePVProvider,
SpacecraftState state,
Map<String, Integer> paramIndices,
int nbParams);
/**
* Compute actual reception date taking into account clock offset.
* @param nbParams number of independent variables for automatic differentiation
* @param paramIndices mapping between parameter name and variable index
* @return reception date
*/
protected FieldAbsoluteDate<Gradient> getCorrectedReceptionDateField(final int nbParams,
final Map<String, Integer> paramIndices) {
final Gradient offset = getObserver().getFieldOffsetValue(nbParams, getDate(), paramIndices);
final FieldAbsoluteDate<Gradient> fieldDate = new FieldAbsoluteDate<>(GradientField.getField(nbParams), getDate());
return fieldDate.shiftedBy(offset.negate());
}
/**
* Fill estimated measurements with value and derivatives.
* @param quantity estimated quantity
* @param paramIndices indices mapping parameter names to derivative indices
* @param estimated theoretical measurement class
*/
protected void fillEstimation(final Gradient quantity, final Map<String, Integer> paramIndices,
final EstimatedMeasurement<T> estimated) {
estimated.setEstimatedValue(quantity.getValue());
// First order derivatives with respect to state
final double[] derivatives = quantity.getGradient();
estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6));
// Set first order derivatives with respect to parameters
for (final ParameterDriver driver : getParametersDrivers()) {
for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
final Integer index = paramIndices.get(span.getData());
if (index != null) {
estimated.setParameterDerivatives(driver, span.getStart(), derivatives[index]);
}
}
}
}
}