BistaticRangeRate.java

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

  18. import java.util.Arrays;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.Map;

  22. import org.hipparchus.analysis.differentiation.Gradient;
  23. import org.hipparchus.analysis.differentiation.GradientField;
  24. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  25. import org.orekit.frames.FieldTransform;
  26. import org.orekit.propagation.SpacecraftState;
  27. import org.orekit.time.AbsoluteDate;
  28. import org.orekit.time.FieldAbsoluteDate;
  29. import org.orekit.utils.ParameterDriver;
  30. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  31. import org.orekit.utils.TimeStampedPVCoordinates;

  32. /** Class modeling a bistatic range rate measurement using
  33.  *  an emitter ground station and a receiver ground station.
  34.  * <p>
  35.  * The measurement is considered to be a signal:
  36.  * <ul>
  37.  * <li>Emitted from the emitter ground station</li>
  38.  * <li>Reflected on the spacecraft</li>
  39.  * <li>Received on the receiver ground station</li>
  40.  * </ul>
  41.  * The date of the measurement corresponds to the reception on ground of the reflected signal.
  42.  * The quantity measured at the receiver is the bistatic radial velocity as the sum of the radial
  43.  * velocities with respect to the two stations.
  44.  * <p>
  45.  * The motion of the stations and the spacecraft during the signal flight time are taken into account.
  46.  * </p><p>
  47.  * The Doppler measurement can be obtained by multiplying the velocity by (fe/c), where
  48.  * fe is the emission frequency.
  49.  * </p>
  50.  *
  51.  * @author Pascal Parraud
  52.  * @since 11.2
  53.  */
  54. public class BistaticRangeRate extends AbstractMeasurement<BistaticRangeRate> {

  55.     /** Emitter ground station. */
  56.     private final GroundStation emitter;

  57.     /** Receiver ground station. */
  58.     private final GroundStation receiver;

  59.     /** Simple constructor.
  60.      * @param emitter emitter ground station
  61.      * @param receiver receiver ground station
  62.      * @param date date of the measurement
  63.      * @param rangeRate observed value, m/s
  64.      * @param sigma theoretical standard deviation
  65.      * @param baseWeight base weight
  66.      * @param satellite satellite related to this measurement
  67.      */
  68.     public BistaticRangeRate(final GroundStation emitter, final GroundStation receiver,
  69.                              final AbsoluteDate date, final double rangeRate, final double sigma,
  70.                              final double baseWeight, final ObservableSatellite satellite) {
  71.         super(date, rangeRate, sigma, baseWeight, Collections.singletonList(satellite));
  72.         // add parameter drivers for the emitter, clock offset is not used
  73.         addParameterDriver(emitter.getEastOffsetDriver());
  74.         addParameterDriver(emitter.getNorthOffsetDriver());
  75.         addParameterDriver(emitter.getZenithOffsetDriver());
  76.         addParameterDriver(emitter.getPrimeMeridianOffsetDriver());
  77.         addParameterDriver(emitter.getPrimeMeridianDriftDriver());
  78.         addParameterDriver(emitter.getPolarOffsetXDriver());
  79.         addParameterDriver(emitter.getPolarDriftXDriver());
  80.         addParameterDriver(emitter.getPolarOffsetYDriver());
  81.         addParameterDriver(emitter.getPolarDriftYDriver());
  82.         // add parameter drivers for the receiver
  83.         addParameterDriver(receiver.getClockOffsetDriver());
  84.         addParameterDriver(receiver.getEastOffsetDriver());
  85.         addParameterDriver(receiver.getNorthOffsetDriver());
  86.         addParameterDriver(receiver.getZenithOffsetDriver());
  87.         addParameterDriver(receiver.getPrimeMeridianOffsetDriver());
  88.         addParameterDriver(receiver.getPrimeMeridianDriftDriver());
  89.         addParameterDriver(receiver.getPolarOffsetXDriver());
  90.         addParameterDriver(receiver.getPolarDriftXDriver());
  91.         addParameterDriver(receiver.getPolarOffsetYDriver());
  92.         addParameterDriver(receiver.getPolarDriftYDriver());
  93.         this.emitter  = emitter;
  94.         this.receiver = receiver;
  95.     }

  96.     /** Get the emitter ground station.
  97.      * @return emitter ground station
  98.      */
  99.     public GroundStation getEmitterStation() {
  100.         return emitter;
  101.     }

  102.     /** Get the receiver ground station.
  103.      * @return receiver ground station
  104.      */
  105.     public GroundStation getReceiverStation() {
  106.         return receiver;
  107.     }

  108.     /** {@inheritDoc} */
  109.     @Override
  110.     protected EstimatedMeasurement<BistaticRangeRate> theoreticalEvaluation(final int iteration,
  111.                                                                             final int evaluation,
  112.                                                                             final SpacecraftState[] states) {

  113.         final SpacecraftState state = states[0];

  114.         // Bistatic range rate derivatives are computed with respect to:
  115.         // - Spacecraft state in inertial frame
  116.         // - Emitter station parameters
  117.         // - Receiver station parameters
  118.         // --------------------------
  119.         //  - 0..2 - Position of the spacecraft in inertial frame
  120.         //  - 3..5 - Velocity of the spacecraft in inertial frame
  121.         //  - 6..n - stations' parameters (stations' offsets, pole, prime meridian...)
  122.         int nbParams = 6;
  123.         final Map<String, Integer> indices = new HashMap<String, Integer>();
  124.         for (ParameterDriver driver : getParametersDrivers()) {
  125.             // we have to check for duplicate keys because emitter and receiver stations share
  126.             // pole and prime meridian parameters names that must be considered
  127.             // as one set only (they are combined together by the estimation engine)
  128.             if (driver.isSelected() && !indices.containsKey(driver.getName())) {
  129.                 indices.put(driver.getName(), nbParams++);
  130.             }
  131.         }
  132.         final FieldVector3D<Gradient> zero = FieldVector3D.getZero(GradientField.getField(nbParams));

  133.         // coordinates of the spacecraft as a gradient
  134.         final TimeStampedFieldPVCoordinates<Gradient> pvaG = getCoordinates(state, 0, nbParams);

  135.         // transform between receiver station frame and inertial frame
  136.         // at the real date of measurement, i.e. taking station clock offset into account
  137.         final FieldTransform<Gradient> receiverToInertial =
  138.                         receiver.getOffsetToInertial(state.getFrame(), getDate(), nbParams, indices);
  139.         final FieldAbsoluteDate<Gradient> measurementDateG = receiverToInertial.getFieldDate();

  140.         // Receiver PV in inertial frame at the end of the downlink leg
  141.         final TimeStampedFieldPVCoordinates<Gradient> receiverPV =
  142.                         receiverToInertial.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(measurementDateG,
  143.                                                                                                       zero, zero, zero));

  144.         // Compute propagation times
  145.         // (if state has already been set up to pre-compensate propagation delay,
  146.         //  we will have delta == tauD and transitState will be the same as state)

  147.         // Downlink delay
  148.         final Gradient tauD       = signalTimeOfFlight(pvaG, receiverPV.getPosition(), measurementDateG);
  149.         final Gradient delta      = measurementDateG.durationFrom(state.getDate());
  150.         final Gradient deltaMTauD = delta.subtract(tauD);

  151.         // Transit state
  152.         final SpacecraftState transitState = state.shiftedBy(deltaMTauD.getValue());

  153.         // Transit PV
  154.         final TimeStampedFieldPVCoordinates<Gradient> transitPV = pvaG.shiftedBy(deltaMTauD);

  155.         // Downlink range-rate
  156.         final EstimatedMeasurement<BistaticRangeRate> evalDownlink =
  157.                         oneWayTheoreticalEvaluation(iteration, evaluation, true,
  158.                                                     receiverPV, transitPV, transitState, indices);

  159.         // transform between emitter station frame and inertial frame at the transit date
  160.         // clock offset from receiver is already compensated
  161.         final FieldTransform<Gradient> emitterToInertial =
  162.                         emitter.getOffsetToInertial(state.getFrame(), transitPV.getDate(), nbParams, indices);

  163.         // emitter PV in inertial frame at the end of the uplink leg
  164.         final TimeStampedFieldPVCoordinates<Gradient> emitterPV =
  165.                         emitterToInertial.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(transitPV.getDate(),
  166.                                                                                                      zero, zero, zero));

  167.         // Uplink delay
  168.         final Gradient tauU = signalTimeOfFlight(emitterPV, transitPV.getPosition(), transitPV.getDate());

  169.         // emitter position in inertial frame at the end of the uplink leg
  170.         final TimeStampedFieldPVCoordinates<Gradient> emitterUplink = emitterPV.shiftedBy(tauU.negate());

  171.         // Uplink range-rate
  172.         final EstimatedMeasurement<BistaticRangeRate> evalUplink =
  173.                         oneWayTheoreticalEvaluation(iteration, evaluation, false,
  174.                                                     emitterUplink, transitPV, transitState, indices);

  175.         // combine uplink and downlink values
  176.         final EstimatedMeasurement<BistaticRangeRate> estimated =
  177.                         new EstimatedMeasurement<>(this, iteration, evaluation,
  178.                                                    evalDownlink.getStates(),
  179.                                                    new TimeStampedPVCoordinates[] {
  180.                                                        evalUplink.getParticipants()[0],
  181.                                                        evalDownlink.getParticipants()[0],
  182.                                                        evalDownlink.getParticipants()[1]
  183.                                                    });
  184.         estimated.setEstimatedValue(evalDownlink.getEstimatedValue()[0] + evalUplink.getEstimatedValue()[0]);

  185.         // combine uplink and downlink partial derivatives with respect to state
  186.         final double[][] sd1 = evalDownlink.getStateDerivatives(0);
  187.         final double[][] sd2 = evalUplink.getStateDerivatives(0);
  188.         final double[][] sd  = new double[sd1.length][sd1[0].length];
  189.         for (int i = 0; i < sd.length; ++i) {
  190.             for (int j = 0; j < sd[0].length; ++j) {
  191.                 sd[i][j] = sd1[i][j] + sd2[i][j];
  192.             }
  193.         }
  194.         estimated.setStateDerivatives(0, sd);

  195.         // combine uplink and downlink partial derivatives with respect to parameters
  196.         evalDownlink.getDerivativesDrivers().forEach(driver -> {
  197.             final double[] pd1 = evalDownlink.getParameterDerivatives(driver);
  198.             final double[] pd2 = evalUplink.getParameterDerivatives(driver);
  199.             final double[] pd  = new double[pd1.length];
  200.             for (int i = 0; i < pd.length; ++i) {
  201.                 pd[i] = pd1[i] + pd2[i];
  202.             }
  203.             estimated.setParameterDerivatives(driver, pd);
  204.         });

  205.         return estimated;

  206.     }


  207.     /** Evaluate range rate measurement in one-way.
  208.      * @param iteration iteration number
  209.      * @param evaluation evaluations counter
  210.      * @param downlink indicator for downlink leg
  211.      * @param stationPV station coordinates when signal is at station
  212.      * @param transitPV spacecraft coordinates at onboard signal transit
  213.      * @param transitState orbital state at onboard signal transit
  214.      * @param indices indices of the estimated parameters in derivatives computations
  215.      * @return theoretical value for the current leg
  216.      */
  217.     private EstimatedMeasurement<BistaticRangeRate> oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink,
  218.                                                                                 final TimeStampedFieldPVCoordinates<Gradient> stationPV,
  219.                                                                                 final TimeStampedFieldPVCoordinates<Gradient> transitPV,
  220.                                                                                 final SpacecraftState transitState,
  221.                                                                                 final Map<String, Integer> indices) {

  222.         // prepare the evaluation
  223.         final EstimatedMeasurement<BistaticRangeRate> estimated =
  224.             new EstimatedMeasurement<BistaticRangeRate>(this, iteration, evaluation,
  225.                                                         new SpacecraftState[] {
  226.                                                             transitState
  227.                                                         }, new TimeStampedPVCoordinates[] {
  228.                                                             (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(),
  229.                                                             (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates()
  230.                                                         });

  231.         // range rate value
  232.         final FieldVector3D<Gradient> stationPosition  = stationPV.getPosition();
  233.         final FieldVector3D<Gradient> relativePosition = stationPosition.subtract(transitPV.getPosition());

  234.         final FieldVector3D<Gradient> stationVelocity  = stationPV.getVelocity();
  235.         final FieldVector3D<Gradient> relativeVelocity = stationVelocity.subtract(transitPV.getVelocity());

  236.         // radial direction
  237.         final FieldVector3D<Gradient> lineOfSight      = relativePosition.normalize();

  238.         // range rate
  239.         final Gradient rangeRate = FieldVector3D.dotProduct(relativeVelocity, lineOfSight);

  240.         estimated.setEstimatedValue(rangeRate.getValue());

  241.         // compute partial derivatives of (rr) with respect to spacecraft state Cartesian coordinates
  242.         final double[] derivatives = rangeRate.getGradient();
  243.         estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6));

  244.         // set partial derivatives with respect to parameters
  245.         for (final ParameterDriver driver : getParametersDrivers()) {
  246.             final Integer index = indices.get(driver.getName());
  247.             if (index != null) {
  248.                 estimated.setParameterDerivatives(driver, derivatives[index]);
  249.             }
  250.         }

  251.         return estimated;

  252.     }
  253. }