RangeRate.java
/* Copyright 2002-2016 CS Systèmes d'Information
* Licensed to CS Systèmes d'Information (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 org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.orekit.errors.OrekitException;
import org.orekit.frames.Transform;
import org.orekit.propagation.SpacecraftState;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.AngularCoordinates;
import org.orekit.utils.PVCoordinates;
import org.orekit.utils.ParameterDriver;
/** Class modeling one-way or two-way range rate measurement between two vehicles.
* One-way range rate (or Doppler) measurements generally apply to specific satellites
* (e.g. GNSS, DORIS), where a signal is transmitted from a satellite to a
* measuring station.
* Two-way range rate measurements are applicable to any system. The signal is
* transmitted to the (non-spinning) satellite and returned by a transponder
* (or reflected back)to the same measuring station.
* The Doppler measurement can be obtained by multiplying the velocity by (fe/c), where
* fe is the emission frequency.
*
* @author Thierry Ceolin
* @author Joris Olympio
* @since 8.0
*/
public class RangeRate extends AbstractMeasurement<RangeRate> {
/** Ground station from which measurement is performed. */
private final GroundStation station;
/** Flag indicating whether it is a two-way measurement. */
private final boolean twoway;
/** Simple constructor.
* @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
* @exception OrekitException if a {@link org.orekit.utils.ParameterDriver}
* name conflict occurs
*/
public RangeRate(final GroundStation station, final AbsoluteDate date,
final double rangeRate,
final double sigma,
final double baseWeight,
final boolean twoway)
throws OrekitException {
super(date, rangeRate, sigma, baseWeight,
station.getEastOffsetDriver(),
station.getNorthOffsetDriver(),
station.getZenithOffsetDriver());
this.station = station;
this.twoway = twoway;
}
/** Get the ground station from which measurement is performed.
* @return ground station from which measurement is performed
*/
public GroundStation getStation() {
return station;
}
/** {@inheritDoc} */
@Override
protected EstimatedMeasurement<RangeRate> theoreticalEvaluation(final int iteration, final int evaluation,
final SpacecraftState state)
throws OrekitException {
// one-way (downlink) light time correction
// (if state has already been set up to pre-compensate propagation delay,
// we will have offset == downlinkDelay and compensatedState will be
// the same as state)
final double downlinkDelay = station.downlinkTimeOfFlight(state, getDate());
final double offset = getDate().durationFrom(state.getDate());
final SpacecraftState compensatedState = state.shiftedBy(offset - downlinkDelay);
final EstimatedMeasurement<RangeRate> estimated =
oneWayTheoreticalEvaluation(iteration, evaluation, state.getDate(), compensatedState);
if (twoway) {
// one-way (uplink) light time correction
final double uplinkDelay = station.uplinkTimeOfFlight(compensatedState);
final AbsoluteDate date = compensatedState.getDate().shiftedBy(uplinkDelay);
final EstimatedMeasurement<RangeRate> evalOneWay2 =
oneWayTheoreticalEvaluation(iteration, evaluation, date, compensatedState);
//evaluation
estimated.setEstimatedValue(0.5 * (estimated.getEstimatedValue()[0] + evalOneWay2.getEstimatedValue()[0]));
final double[][] sd1 = estimated.getStateDerivatives();
final double[][] sd2 = evalOneWay2.getStateDerivatives();
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) {
sd[i][j] = 0.5 * (sd1[i][j] + sd2[i][j]);
}
}
estimated.setStateDerivatives(sd);
for (final ParameterDriver driver : Arrays.asList(station.getEastOffsetDriver(),
station.getNorthOffsetDriver(),
station.getZenithOffsetDriver())) {
if (driver.isSelected()) {
final double[] pd1 = estimated.getParameterDerivatives(driver);
final double[] pd2 = evalOneWay2.getParameterDerivatives(driver);
final double[] pd = new double[pd1.length];
for (int i = 0; i < pd.length; ++i) {
pd[i] = 0.5 * (pd1[i] + pd2[i]);
}
estimated.setParameterDerivatives(driver, pd);
}
}
}
return estimated;
}
/** Evaluate measurement in one-way.
* @param iteration iteration number
* @param count evaluations counter
* @param date date at which signal is on ground station
* @param compensatedState orbital state used for measurement
* @return theoretical value
* @exception OrekitException if value cannot be computed
* @see #evaluate(SpacecraftStatet)
*/
private EstimatedMeasurement<RangeRate> oneWayTheoreticalEvaluation(final int iteration, final int count, final AbsoluteDate date,
final SpacecraftState compensatedState)
throws OrekitException {
// prepare the evaluation
final EstimatedMeasurement<RangeRate> evaluation =
new EstimatedMeasurement<RangeRate>(this, iteration, count, compensatedState);
// station coordinates at date in state frame
final PVCoordinates pvStation = station.getOffsetFrame().getPVCoordinates(date, compensatedState.getFrame());
// range rate value
final Vector3D stationPosition = pvStation.getPosition();
final Vector3D relativePosition = compensatedState.getPVCoordinates().getPosition().subtract(stationPosition);
final Vector3D stationVelocity = pvStation.getVelocity();
final Vector3D relativeVelocity = compensatedState.getPVCoordinates().getVelocity()
.subtract(stationVelocity);
// line of sight direction
final Vector3D lineOfSight = relativePosition.normalize();
// range rate
final double rr = Vector3D.dotProduct(relativeVelocity, lineOfSight);
evaluation.setEstimatedValue(rr);
// compute partial derivatives of (rr) with respect to spacecraft state Cartesian coordinates.
final double relnorm = relativePosition.getNorm();
final double den1 = relnorm; //relativePosition.getNorm();
final double den2 = FastMath.pow(relativePosition.getNorm(), 2);
final double fRx = 1. / den2 * relativeVelocity.dotProduct(Vector3D.PLUS_I.scalarMultiply(relnorm).subtract( relativePosition.scalarMultiply(relativePosition.getX() / den1)));
final double fRy = 1. / den2 * relativeVelocity.dotProduct(Vector3D.PLUS_J.scalarMultiply(relnorm).subtract( relativePosition.scalarMultiply(relativePosition.getY() / den1)));
final double fRz = 1. / den2 * relativeVelocity.dotProduct(Vector3D.PLUS_K.scalarMultiply(relnorm).subtract( relativePosition.scalarMultiply(relativePosition.getZ() / den1)));
final double fVx = lineOfSight.getX();
final double fVy = lineOfSight.getY();
final double fVz = lineOfSight.getZ();
evaluation.setStateDerivatives(new double[] {
fRx, fRy, fRz,
fVx, fVy, fVz
});
// compute sensitivity wrt station position when station bias needs
// to be estimated
if (station.getEastOffsetDriver().isSelected() ||
station.getNorthOffsetDriver().isSelected() ||
station.getZenithOffsetDriver().isSelected()) {
// station position at signal arrival
final Transform topoToInert =
station.getOffsetFrame().getTransformTo(compensatedState.getFrame(), getDate());
final AngularCoordinates ac = topoToInert.getAngular().revert();
//
final Transform tt = compensatedState.getFrame().getTransformTo(station.getBaseFrame().getParent(), date);
final Vector3D omega = tt.getRotationRate(); // earth angular velocity
// derivative of lineOfSight wrt rSta,
// d (relPos / ||relPos||) / d rStation
//
final double relnorm2 = relnorm * relnorm;
final double[][] m = new double[][] {
{
relativePosition.getX() * relativePosition.getX() / relnorm2 - 1.0,
relativePosition.getY() * relativePosition.getX() / relnorm2,
relativePosition.getZ() * relativePosition.getX() / relnorm2
}, {
relativePosition.getX() * relativePosition.getY() / relnorm2,
relativePosition.getY() * relativePosition.getY() / relnorm2 - 1.0,
relativePosition.getZ() * relativePosition.getY() / relnorm2
}, {
relativePosition.getX() * relativePosition.getZ() / relnorm2,
relativePosition.getY() * relativePosition.getZ() / relnorm2,
relativePosition.getZ() * relativePosition.getZ() / relnorm2 - 1.0
}
};
// derivatives of the dot product (relVelocity * lineOfSight) wrt rStation
// Note: relVelocity = rstation x omega
final Vector3D v = relativeVelocity;
final double dVUdPstax = (-omega.getZ() * lineOfSight.getY() + omega.getY() * lineOfSight.getZ()) +
(v.getX() * m[0][0] + v.getY() * m[1][0] + v.getZ() * m[2][0]) / relnorm;
final double dVUdPstay = ( omega.getZ() * lineOfSight.getX() - omega.getX() * lineOfSight.getZ()) +
(v.getX() * m[0][1] + v.getY() * m[1][1] + v.getZ() * m[2][1]) / relnorm;
final double dVUdPstaz = (-omega.getY() * lineOfSight.getX() + omega.getX() * lineOfSight.getY()) +
(v.getX() * m[0][2] + v.getY() * m[1][2] + v.getZ() * m[2][2]) / relnorm;
// range rate partial derivatives
// with respect to station position in inertial frame
final Vector3D dRdQI1 = new Vector3D(dVUdPstax, dVUdPstay, dVUdPstaz);
// convert to topocentric frame, as the station position
// offset parameter is expressed in this frame
final Vector3D dRdQT = ac.getRotation().applyTo(dRdQI1);
// partial derivatives with respect to parameter
// the parameter has 3 Cartesian coordinates for station offset position
// at measure time
if (station.getEastOffsetDriver().isSelected()) {
evaluation.setParameterDerivatives(station.getEastOffsetDriver(), dRdQT.getX());
}
if (station.getNorthOffsetDriver().isSelected()) {
evaluation.setParameterDerivatives(station.getNorthOffsetDriver(), dRdQT.getY());
}
if (station.getZenithOffsetDriver().isSelected()) {
evaluation.setParameterDerivatives(station.getZenithOffsetDriver(), dRdQT.getZ());
}
}
return evaluation;
}
}