TurnAroundRangeTroposphericDelayModifier.java

  1. /* Copyright 2002-2019 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.modifiers;

  18. import java.util.Arrays;
  19. import java.util.List;

  20. import org.hipparchus.Field;
  21. import org.hipparchus.RealFieldElement;
  22. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.orekit.estimation.measurements.EstimatedMeasurement;
  26. import org.orekit.estimation.measurements.EstimationModifier;
  27. import org.orekit.estimation.measurements.GroundStation;
  28. import org.orekit.estimation.measurements.TurnAroundRange;
  29. import org.orekit.models.earth.DiscreteTroposphericModel;
  30. import org.orekit.models.earth.TroposphericModel;
  31. import org.orekit.orbits.OrbitType;
  32. import org.orekit.orbits.PositionAngle;
  33. import org.orekit.propagation.FieldSpacecraftState;
  34. import org.orekit.propagation.Propagator;
  35. import org.orekit.propagation.SpacecraftState;
  36. import org.orekit.utils.Differentiation;
  37. import org.orekit.utils.ParameterDriver;
  38. import org.orekit.utils.ParameterFunction;
  39. import org.orekit.utils.StateFunction;

  40. /** Class modifying theoretical turn-around TurnAroundRange measurement with tropospheric delay.
  41.  * The effect of tropospheric correction on the TurnAroundRange is directly computed
  42.  * through the computation of the tropospheric delay.
  43.  *
  44.  * In general, for GNSS, VLBI, ... there is hardly any frequency dependence in the delay.
  45.  * For SLR techniques however, the frequency dependence is sensitive.
  46.  *
  47.  * @author Maxime Journot
  48.  * @since 9.0
  49.  */
  50. public class TurnAroundRangeTroposphericDelayModifier implements EstimationModifier<TurnAroundRange> {

  51.     /** Tropospheric delay model. */
  52.     private final DiscreteTroposphericModel tropoModel;

  53.     /** Constructor.
  54.      *
  55.      * @param model  Tropospheric delay model appropriate for the current TurnAroundRange measurement method.
  56.      */
  57.     public TurnAroundRangeTroposphericDelayModifier(final DiscreteTroposphericModel model) {
  58.         tropoModel = model;
  59.     }

  60.     /** Get the station height above mean sea level.
  61.      *
  62.      * @param station  ground station (or measuring station)
  63.      * @return the measuring station height above sea level, m
  64.      */
  65.     private double getStationHeightAMSL(final GroundStation station) {
  66.         // FIXME height should be computed with respect to geoid WGS84+GUND = EGM2008 for example
  67.         final double height = station.getBaseFrame().getPoint().getAltitude();
  68.         return height;
  69.     }

  70.     /** Get the station height above mean sea level.
  71.     * @param <T> type of the elements
  72.     * @param field field of the elements
  73.     * @param station  ground station (or measuring station)
  74.     * @return the measuring station height above sea level, m
  75.     */
  76.     private <T extends RealFieldElement<T>> T getStationHeightAMSL(final Field<T> field,
  77.                                                                    final GroundStation station) {
  78.         // FIXME heigth should be computed with respect to geoid WGS84+GUND = EGM2008 for example
  79.         final T height = station.getBaseFrame().getPoint(field).getAltitude();
  80.         return height;
  81.     }

  82.     /** Compute the measurement error due to Troposphere.
  83.      * @param station station
  84.      * @param state spacecraft state
  85.      * @return the measurement error due to Troposphere
  86.      */
  87.     private double rangeErrorTroposphericModel(final GroundStation station, final SpacecraftState state) {
  88.         //
  89.         final Vector3D position = state.getPVCoordinates().getPosition();

  90.         // elevation
  91.         final double elevation = station.getBaseFrame().getElevation(position,
  92.                                                                      state.getFrame(),
  93.                                                                      state.getDate());

  94.         // only consider measures above the horizon
  95.         if (elevation > 0) {
  96.             // altitude AMSL in meters
  97.             final double height = getStationHeightAMSL(station);

  98.             // Delay in meters
  99.             final double delay = tropoModel.pathDelay(elevation, height, tropoModel.getParameters(), state.getDate());

  100.             return delay;
  101.         }

  102.         return 0;
  103.     }

  104.     /** Compute the measurement error due to Troposphere.
  105.      * @param <T> type of the element
  106.      * @param station station
  107.      * @param state spacecraft state
  108.      * @param parameters tropospheric model parameters
  109.      * @return the measurement error due to Troposphere
  110.      */
  111.     private <T extends RealFieldElement<T>> T rangeErrorTroposphericModel(final GroundStation station,
  112.                                                                           final FieldSpacecraftState<T> state,
  113.                                                                           final T[] parameters) {
  114.         // Field
  115.         final Field<T> field = state.getDate().getField();
  116.         final T zero         = field.getZero();

  117.         //
  118.         final FieldVector3D<T> position = state.getPVCoordinates().getPosition();
  119.         final T dsElevation             = station.getBaseFrame().getElevation(position,
  120.                                                                               state.getFrame(),
  121.                                                                               state.getDate());

  122.         // only consider measures above the horizon
  123.         if (dsElevation.getReal() > 0) {
  124.             // altitude AMSL in meters
  125.             final T height = getStationHeightAMSL(field, station);

  126.             // Delay in meters
  127.             final T delay = tropoModel.pathDelay(dsElevation, height, parameters, state.getDate());

  128.             return delay;
  129.         }

  130.         return zero;
  131.     }

  132.     /** Compute the Jacobian of the delay term wrt state.
  133.     *
  134.     * @param station station
  135.     * @param refstate reference spacecraft state
  136.     *
  137.     * @return Jacobian of the delay wrt state
  138.     */
  139.     private double[][] rangeErrorJacobianState(final GroundStation station, final SpacecraftState refstate) {
  140.         final double[][] finiteDifferencesJacobian =
  141.                         Differentiation.differentiate(new StateFunction() {
  142.                             public double[] value(final SpacecraftState state) {
  143.                                 // evaluate target's elevation with a changed target position
  144.                                 final double value = rangeErrorTroposphericModel(station, state);

  145.                                 return new double[] {value };
  146.                             }
  147.                         }, 1, Propagator.DEFAULT_LAW, OrbitType.CARTESIAN,
  148.                         PositionAngle.TRUE, 15.0, 3).value(refstate);

  149.         return finiteDifferencesJacobian;
  150.     }

  151.     /** Compute the Jacobian of the delay term wrt state using
  152.     * automatic differentiation.
  153.     *
  154.     * @param derivatives tropospheric delay derivatives
  155.     * @param freeStateParameters dimension of the state.
  156.     *
  157.     * @return Jacobian of the delay wrt state
  158.     */
  159.     private double[][] rangeErrorJacobianState(final double[] derivatives, final int freeStateParameters) {
  160.         final double[][] finiteDifferencesJacobian = new double[1][6];
  161.         for (int i = 0; i < freeStateParameters; i++) {
  162.             // First element is the value of the delay
  163.             finiteDifferencesJacobian[0][i] = derivatives[i + 1];
  164.         }
  165.         return finiteDifferencesJacobian;
  166.     }


  167.     /** Compute the derivative of the delay term wrt parameters.
  168.      *
  169.      * @param station ground station
  170.      * @param driver driver for the station offset parameter
  171.      * @param state spacecraft state
  172.      * @return derivative of the delay wrt station offset parameter
  173.      */
  174.     private double rangeErrorParameterDerivative(final GroundStation station,
  175.                                                  final ParameterDriver driver,
  176.                                                  final SpacecraftState state) {

  177.         final ParameterFunction rangeError = new ParameterFunction() {
  178.             /** {@inheritDoc} */
  179.             @Override
  180.             public double value(final ParameterDriver parameterDriver) {
  181.                 return rangeErrorTroposphericModel(station, state);
  182.             }
  183.         };

  184.         final ParameterFunction rangeErrorDerivative = Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale());

  185.         return rangeErrorDerivative.value(driver);

  186.     }

  187.     /** Compute the derivative of the delay term wrt parameters using
  188.     * automatic differentiation.
  189.     *
  190.     * @param derivatives tropospheric delay derivatives
  191.     * @param freeStateParameters dimension of the state.
  192.     * @return derivative of the delay wrt tropospheric model parameters
  193.     */
  194.     private double[] rangeErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) {
  195.         // 0                               -> value of the delay
  196.         // 1 ... freeStateParameters       -> derivatives of the delay wrt state
  197.         // freeStateParameters + 1 ... n   -> derivatives of the delay wrt tropospheric parameters
  198.         final int dim = derivatives.length - 1 - freeStateParameters;
  199.         final double[] rangeError = new double[dim];

  200.         for (int i = 0; i < dim; i++) {
  201.             rangeError[i] = derivatives[1 + freeStateParameters + i];
  202.         }

  203.         return rangeError;
  204.     }

  205.     /** {@inheritDoc} */
  206.     @Override
  207.     public List<ParameterDriver> getParametersDrivers() {
  208.         return tropoModel.getParametersDrivers();
  209.     }

  210.     /** {@inheritDoc} */
  211.     @Override
  212.     public void modify(final EstimatedMeasurement<TurnAroundRange> estimated) {
  213.         final TurnAroundRange measurement   = estimated.getObservedMeasurement();
  214.         final GroundStation   masterStation = measurement.getMasterStation();
  215.         final GroundStation   slaveStation  = measurement.getSlaveStation();
  216.         final SpacecraftState state         = estimated.getStates()[0];

  217.         final double[] oldValue = estimated.getEstimatedValue();

  218.         // Update estimated derivatives with Jacobian of the measure wrt state
  219.         final TroposphericDSConverter converter = new TroposphericDSConverter(state, 6, Propagator.DEFAULT_LAW);
  220.         final FieldSpacecraftState<DerivativeStructure> dsState = converter.getState(tropoModel);
  221.         final DerivativeStructure[] dsParameters = converter.getParameters(dsState, tropoModel);
  222.         final DerivativeStructure masterDSDelay = rangeErrorTroposphericModel(masterStation, dsState, dsParameters);
  223.         final DerivativeStructure slaveDSDelay = rangeErrorTroposphericModel(slaveStation, dsState, dsParameters);
  224.         final double[] masterDerivatives = masterDSDelay.getAllDerivatives();
  225.         final double[] slaveDerivatives  = masterDSDelay.getAllDerivatives();

  226.         double[][] masterDjac = new double[1][6];
  227.         double[][] slaveDjac  = new double[1][6];
  228.         // This implementation will disappear when the implementations of TroposphericModel
  229.         // will directly be implementations of DiscreteTroposphericModel
  230.         if (tropoModel instanceof TroposphericModel) {
  231.             masterDjac = rangeErrorJacobianState(masterStation, state);
  232.             slaveDjac  = rangeErrorJacobianState(slaveStation, state);
  233.         } else {
  234.             masterDjac = rangeErrorJacobianState(masterDerivatives, converter.getFreeStateParameters());
  235.             slaveDjac  = rangeErrorJacobianState(slaveDerivatives, converter.getFreeStateParameters());
  236.         }
  237.         final double[][] stateDerivatives = estimated.getStateDerivatives(0);
  238.         for (int irow = 0; irow < stateDerivatives.length; ++irow) {
  239.             for (int jcol = 0; jcol < stateDerivatives[0].length; ++jcol) {
  240.                 stateDerivatives[irow][jcol] += masterDjac[irow][jcol] + slaveDjac[irow][jcol];
  241.             }
  242.         }
  243.         estimated.setStateDerivatives(0, stateDerivatives);

  244.         int indexMaster = 0;
  245.         for (final ParameterDriver driver : getParametersDrivers()) {
  246.             if (driver.isSelected()) {
  247.                 // update estimated derivatives with derivative of the modification wrt tropospheric parameters
  248.                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
  249.                 final double[] derivatives = rangeErrorParameterDerivative(masterDerivatives, converter.getFreeStateParameters());
  250.                 parameterDerivative += derivatives[indexMaster];
  251.                 estimated.setParameterDerivatives(driver, parameterDerivative);
  252.                 indexMaster += 1;
  253.             }

  254.         }

  255.         int indexSlave = 0;
  256.         for (final ParameterDriver driver : getParametersDrivers()) {
  257.             if (driver.isSelected()) {
  258.                 // update estimated derivatives with derivative of the modification wrt tropospheric parameters
  259.                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
  260.                 final double[] derivatives = rangeErrorParameterDerivative(slaveDerivatives, converter.getFreeStateParameters());
  261.                 parameterDerivative += derivatives[indexSlave];
  262.                 estimated.setParameterDerivatives(driver, parameterDerivative);
  263.                 indexSlave += 1;
  264.             }

  265.         }

  266.         // Update derivatives with respect to master station position
  267.         for (final ParameterDriver driver : Arrays.asList(masterStation.getClockOffsetDriver(),
  268.                                                           masterStation.getEastOffsetDriver(),
  269.                                                           masterStation.getNorthOffsetDriver(),
  270.                                                           masterStation.getZenithOffsetDriver())) {
  271.             if (driver.isSelected()) {
  272.                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
  273.                 parameterDerivative += rangeErrorParameterDerivative(masterStation, driver, state);
  274.                 estimated.setParameterDerivatives(driver, parameterDerivative);
  275.             }
  276.         }

  277.         // Update derivatives with respect to slave station position
  278.         for (final ParameterDriver driver : Arrays.asList(slaveStation.getEastOffsetDriver(),
  279.                                                           slaveStation.getNorthOffsetDriver(),
  280.                                                           slaveStation.getZenithOffsetDriver())) {
  281.             if (driver.isSelected()) {
  282.                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
  283.                 parameterDerivative += rangeErrorParameterDerivative(slaveStation, driver, state);
  284.                 estimated.setParameterDerivatives(driver, parameterDerivative);
  285.             }
  286.         }

  287.         // Update estimated value taking into account the tropospheric delay.
  288.         // The tropospheric delay is directly added to the TurnAroundRange.
  289.         final double[] newValue = oldValue.clone();
  290.         newValue[0] = newValue[0] + masterDSDelay.getReal() + slaveDSDelay.getReal();
  291.         estimated.setEstimatedValue(newValue);

  292.     }

  293. }