TurnAroundRangeTroposphericDelayModifier.java

  1. /* Copyright 2002-2024 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.modifiers;

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

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.Field;
  22. import org.hipparchus.analysis.differentiation.Gradient;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.orekit.attitudes.FrameAlignedProvider;
  26. import org.orekit.estimation.measurements.EstimatedMeasurement;
  27. import org.orekit.estimation.measurements.EstimatedMeasurementBase;
  28. import org.orekit.estimation.measurements.EstimationModifier;
  29. import org.orekit.estimation.measurements.GroundStation;
  30. import org.orekit.estimation.measurements.TurnAroundRange;
  31. import org.orekit.models.earth.troposphere.TroposphericModel;
  32. import org.orekit.propagation.FieldSpacecraftState;
  33. import org.orekit.propagation.SpacecraftState;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.utils.Differentiation;
  36. import org.orekit.utils.FieldTrackingCoordinates;
  37. import org.orekit.utils.ParameterDriver;
  38. import org.orekit.utils.ParameterFunction;
  39. import org.orekit.utils.TimeSpanMap.Span;
  40. import org.orekit.utils.TrackingCoordinates;

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

  55.     /** Tropospheric delay model. */
  56.     private final TroposphericModel tropoModel;

  57.     /** Constructor.
  58.      *
  59.      * @param model  Tropospheric delay model appropriate for the current TurnAroundRange measurement method.
  60.      * @deprecated as of 12.1, replaced  by {@link #TurnAroundRangeTroposphericDelayModifier(TroposphericModel)}
  61.      */
  62.     @Deprecated
  63.     public TurnAroundRangeTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) {
  64.         this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model));
  65.     }

  66.     /** Constructor.
  67.      *
  68.      * @param model  Tropospheric delay model appropriate for the current TurnAroundRange measurement method.
  69.      * @since 12.1
  70.      */
  71.     public TurnAroundRangeTroposphericDelayModifier(final TroposphericModel model) {
  72.         tropoModel = model;
  73.     }

  74.     /** Compute the measurement error due to Troposphere.
  75.      * @param station station
  76.      * @param state spacecraft state
  77.      * @return the measurement error due to Troposphere
  78.      */
  79.     private double rangeErrorTroposphericModel(final GroundStation station, final SpacecraftState state) {
  80.         //
  81.         final Vector3D position = state.getPosition();

  82.         // tracking
  83.         final TrackingCoordinates trackingCoordinates =
  84.                         station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate());

  85.         // only consider measures above the horizon
  86.         if (trackingCoordinates.getElevation() > 0) {
  87.             // Delay in meters
  88.             return tropoModel.pathDelay(trackingCoordinates,
  89.                                         station.getOffsetGeodeticPoint(state.getDate()),
  90.                                         station.getPressureTemperatureHumidity(state.getDate()),
  91.                                         tropoModel.getParameters(state.getDate()), state.getDate()).
  92.                                  getDelay();
  93.         }

  94.         return 0;
  95.     }

  96.     /** Compute the measurement error due to Troposphere.
  97.      * @param <T> type of the element
  98.      * @param station station
  99.      * @param state spacecraft state
  100.      * @param parameters tropospheric model parameters
  101.      * @return the measurement error due to Troposphere
  102.      */
  103.     private <T extends CalculusFieldElement<T>> T rangeErrorTroposphericModel(final GroundStation station,
  104.                                                                           final FieldSpacecraftState<T> state,
  105.                                                                           final T[] parameters) {
  106.         // Field
  107.         final Field<T> field = state.getDate().getField();
  108.         final T zero         = field.getZero();

  109.         //
  110.         final FieldVector3D<T> position = state.getPosition();
  111.         final FieldTrackingCoordinates<T> trackingCoordinates =
  112.                         station.getBaseFrame().getTrackingCoordinates(position,  state.getFrame(), state.getDate());

  113.         // only consider measures above the horizon
  114.         if (trackingCoordinates.getElevation().getReal() > 0) {
  115.             // Delay in meters
  116.             return tropoModel.pathDelay(trackingCoordinates,
  117.                                         station.getOffsetGeodeticPoint(state.getDate()),
  118.                                         station.getPressureTemperatureHumidity(state.getDate()),
  119.                                         parameters, state.getDate()).
  120.                             getDelay();
  121.         }

  122.         return zero;
  123.     }

  124.     /** Compute the Jacobian of the delay term wrt state using
  125.     * automatic differentiation.
  126.     *
  127.     * @param derivatives tropospheric delay derivatives
  128.     *
  129.     * @return Jacobian of the delay wrt state
  130.     */
  131.     private double[][] rangeErrorJacobianState(final double[] derivatives) {
  132.         final double[][] finiteDifferencesJacobian = new double[1][6];
  133.         System.arraycopy(derivatives, 0, finiteDifferencesJacobian[0], 0, 6);
  134.         return finiteDifferencesJacobian;
  135.     }


  136.     /** Compute the derivative of the delay term wrt parameters.
  137.      *
  138.      * @param station ground station
  139.      * @param driver driver for the station offset parameter
  140.      * @param state spacecraft state
  141.      * @return derivative of the delay wrt station offset parameter
  142.      */
  143.     private double rangeErrorParameterDerivative(final GroundStation station,
  144.                                                  final ParameterDriver driver,
  145.                                                  final SpacecraftState state) {

  146.         final ParameterFunction rangeError = new ParameterFunction() {
  147.             /** {@inheritDoc} */
  148.             @Override
  149.             public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) {
  150.                 return rangeErrorTroposphericModel(station, state);
  151.             }
  152.         };

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

  154.         return rangeErrorDerivative.value(driver, state.getDate());

  155.     }

  156.     /** Compute the derivative of the delay term wrt parameters using
  157.     * automatic differentiation.
  158.     *
  159.     * @param derivatives tropospheric delay derivatives
  160.     * @param freeStateParameters dimension of the state.
  161.     * @return derivative of the delay wrt tropospheric model parameters
  162.     */
  163.     private double[] rangeErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) {
  164.         // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state
  165.         // freeStateParameters ... n     -> derivatives of the delay wrt tropospheric parameters
  166.         return Arrays.copyOfRange(derivatives, freeStateParameters, derivatives.length);
  167.     }

  168.     /** {@inheritDoc} */
  169.     @Override
  170.     public List<ParameterDriver> getParametersDrivers() {
  171.         return tropoModel.getParametersDrivers();
  172.     }

  173.     /** {@inheritDoc} */
  174.     @Override
  175.     public void modifyWithoutDerivatives(final EstimatedMeasurementBase<TurnAroundRange> estimated) {

  176.         final TurnAroundRange measurement      = estimated.getObservedMeasurement();
  177.         final GroundStation   primaryStation   = measurement.getPrimaryStation();
  178.         final GroundStation   secondaryStation = measurement.getSecondaryStation();
  179.         final SpacecraftState state            = estimated.getStates()[0];

  180.         // Update estimated value taking into account the tropospheric delay.
  181.         // The tropospheric delay is directly added to the TurnAroundRange.
  182.         final double[] newValue       = estimated.getEstimatedValue();
  183.         final double   primaryDelay   = rangeErrorTroposphericModel(primaryStation, state);
  184.         final double   secondaryDelay = rangeErrorTroposphericModel(secondaryStation, state);
  185.         newValue[0] = newValue[0] + primaryDelay + secondaryDelay;
  186.         estimated.modifyEstimatedValue(this, newValue);

  187.     }
  188.     /** {@inheritDoc} */
  189.     @Override
  190.     public void modify(final EstimatedMeasurement<TurnAroundRange> estimated) {
  191.         final TurnAroundRange measurement      = estimated.getObservedMeasurement();
  192.         final GroundStation   primaryStation   = measurement.getPrimaryStation();
  193.         final GroundStation   secondaryStation = measurement.getSecondaryStation();
  194.         final SpacecraftState state            = estimated.getStates()[0];

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

  196.         // Update estimated derivatives with Jacobian of the measure wrt state
  197.         final ModifierGradientConverter converter =
  198.                 new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame()));
  199.         final FieldSpacecraftState<Gradient> gState = converter.getState(tropoModel);
  200.         final Gradient[] gParameters = converter.getParametersAtStateDate(gState, tropoModel);
  201.         final Gradient   primaryGDelay        = rangeErrorTroposphericModel(primaryStation, gState, gParameters);
  202.         final Gradient   secondaryGDelay      = rangeErrorTroposphericModel(secondaryStation, gState, gParameters);
  203.         final double[]   primaryDerivatives   = primaryGDelay.getGradient();
  204.         final double[]   secondaryDerivatives = secondaryGDelay.getGradient();

  205.         final double[][] primaryDjac      = rangeErrorJacobianState(primaryDerivatives);
  206.         final double[][] secondaryDjac    = rangeErrorJacobianState(secondaryDerivatives);
  207.         final double[][] stateDerivatives = estimated.getStateDerivatives(0);
  208.         for (int irow = 0; irow < stateDerivatives.length; ++irow) {
  209.             for (int jcol = 0; jcol < stateDerivatives[0].length; ++jcol) {
  210.                 stateDerivatives[irow][jcol] += primaryDjac[irow][jcol] + secondaryDjac[irow][jcol];
  211.             }
  212.         }
  213.         estimated.setStateDerivatives(0, stateDerivatives);

  214.         int indexPrimary = 0;
  215.         for (final ParameterDriver driver : getParametersDrivers()) {
  216.             if (driver.isSelected()) {
  217.                 // update estimated derivatives with derivative of the modification wrt tropospheric parameters
  218.                 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
  219.                     double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0];
  220.                     final double[] derivatives = rangeErrorParameterDerivative(primaryDerivatives, converter.getFreeStateParameters());
  221.                     parameterDerivative += derivatives[indexPrimary];
  222.                     estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative);
  223.                     indexPrimary += 1;
  224.                 }
  225.             }

  226.         }

  227.         int indexSecondary = 0;
  228.         for (final ParameterDriver driver : getParametersDrivers()) {
  229.             if (driver.isSelected()) {
  230.                 // update estimated derivatives with derivative of the modification wrt tropospheric parameters
  231.                 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
  232.                     double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0];
  233.                     final double[] derivatives = rangeErrorParameterDerivative(secondaryDerivatives, converter.getFreeStateParameters());
  234.                     parameterDerivative += derivatives[indexSecondary];
  235.                     estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative);
  236.                     indexSecondary += 1;
  237.                 }
  238.             }

  239.         }

  240.         // Update derivatives with respect to primary station position
  241.         for (final ParameterDriver driver : Arrays.asList(primaryStation.getClockOffsetDriver(),
  242.                                                           primaryStation.getEastOffsetDriver(),
  243.                                                           primaryStation.getNorthOffsetDriver(),
  244.                                                           primaryStation.getZenithOffsetDriver())) {
  245.             if (driver.isSelected()) {
  246.                 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {

  247.                     double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0];
  248.                     parameterDerivative += rangeErrorParameterDerivative(primaryStation, driver, state);
  249.                     estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative);
  250.                 }
  251.             }
  252.         }

  253.         // Update derivatives with respect to secondary station position
  254.         for (final ParameterDriver driver : Arrays.asList(secondaryStation.getEastOffsetDriver(),
  255.                                                           secondaryStation.getNorthOffsetDriver(),
  256.                                                           secondaryStation.getZenithOffsetDriver())) {
  257.             if (driver.isSelected()) {
  258.                 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {

  259.                     double parameterDerivative = estimated.getParameterDerivatives(driver, span.getStart())[0];
  260.                     parameterDerivative += rangeErrorParameterDerivative(secondaryStation, driver, state);
  261.                     estimated.setParameterDerivatives(driver, span.getStart(), parameterDerivative);
  262.                 }
  263.             }
  264.         }

  265.         // Update estimated value taking into account the tropospheric delay.
  266.         // The tropospheric delay is directly added to the TurnAroundRange.
  267.         final double[] newValue = oldValue.clone();
  268.         newValue[0] = newValue[0] + primaryGDelay.getReal() + secondaryGDelay.getReal();
  269.         estimated.modifyEstimatedValue(this, newValue);

  270.     }

  271. }