1   /* Copyright 2002-2021 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  
19  import java.util.Arrays;
20  import java.util.List;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.analysis.differentiation.Gradient;
24  import org.orekit.attitudes.InertialProvider;
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.frames.TopocentricFrame;
30  import org.orekit.models.earth.ionosphere.IonosphericModel;
31  import org.orekit.propagation.FieldSpacecraftState;
32  import org.orekit.propagation.SpacecraftState;
33  import org.orekit.utils.Differentiation;
34  import org.orekit.utils.ParameterDriver;
35  import org.orekit.utils.ParameterFunction;
36  
37  /** Class modifying theoretical TurnAroundRange measurement with ionospheric delay.
38   * The effect of ionospheric correction on the TurnAroundRange is directly computed
39   * through the computation of the ionospheric delay.
40   *
41   * The ionospheric delay depends on the frequency of the signal (GNSS, VLBI, ...).
42   * For optical measurements (e.g. SLR), the ray is not affected by ionosphere charged particles.
43   * <p>
44   * Since 10.0, state derivatives and ionospheric parameters derivates are computed
45   * using automatic differentiation.
46   * </p>
47   * @author Maxime Journot
48   * @since 9.0
49   */
50  public class TurnAroundRangeIonosphericDelayModifier implements EstimationModifier<TurnAroundRange> {
51  
52      /** Ionospheric delay model. */
53      private final IonosphericModel ionoModel;
54  
55      /** Frequency [Hz]. */
56      private final double frequency;
57  
58      /** Constructor.
59       *
60       * @param model  Ionospheric delay model appropriate for the current TurnAroundRange measurement method.
61       * @param freq frequency of the signal in Hz
62       */
63      public TurnAroundRangeIonosphericDelayModifier(final IonosphericModel model,
64                                                     final double freq) {
65          ionoModel = model;
66          frequency = freq;
67      }
68  
69      /** Compute the measurement error due to ionosphere.
70       * @param station station
71       * @param state spacecraft state
72       * @return the measurement error due to ionosphere
73       */
74      private double rangeErrorIonosphericModel(final GroundStation station,
75                                                final SpacecraftState state) {
76          // Base frame associated with the station
77          final TopocentricFrame baseFrame = station.getBaseFrame();
78          // Delay in meters
79          final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters());
80          return delay;
81      }
82  
83      /** Compute the measurement error due to ionosphere.
84       * @param <T> type of the elements
85       * @param station station
86       * @param state spacecraft state
87       * @param parameters ionospheric model parameters
88       * @return the measurement error due to ionosphere
89       */
90      private <T extends CalculusFieldElement<T>> T rangeErrorIonosphericModel(final GroundStation station,
91                                                                               final FieldSpacecraftState<T> state,
92                                                                               final T[] parameters) {
93          // Base frame associated with the station
94          final TopocentricFrame baseFrame = station.getBaseFrame();
95          // Delay in meters
96          final T delay = ionoModel.pathDelay(state, baseFrame, frequency, parameters);
97          return delay;
98      }
99  
100     /** Compute the Jacobian of the delay term wrt state using
101     * automatic differentiation.
102     *
103     * @param derivatives ionospheric delay derivatives
104     *
105     * @return Jacobian of the delay wrt state
106     */
107     private double[][] rangeErrorJacobianState(final double[] derivatives) {
108         final double[][] finiteDifferencesJacobian = new double[1][6];
109         System.arraycopy(derivatives, 0, finiteDifferencesJacobian[0], 0, 6);
110         return finiteDifferencesJacobian;
111     }
112 
113 
114     /** Compute the derivative of the delay term wrt parameters.
115      *
116      * @param station ground station
117      * @param driver driver for the station offset parameter
118      * @param state spacecraft state
119      * @return derivative of the delay wrt station offset parameter
120      */
121     private double rangeErrorParameterDerivative(final GroundStation station,
122                                                  final ParameterDriver driver,
123                                                  final SpacecraftState state) {
124 
125         final ParameterFunction rangeError = new ParameterFunction() {
126             /** {@inheritDoc} */
127             @Override
128             public double value(final ParameterDriver parameterDriver) {
129                 return rangeErrorIonosphericModel(station, state);
130             }
131         };
132 
133         final ParameterFunction rangeErrorDerivative =
134                         Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale());
135 
136         return rangeErrorDerivative.value(driver);
137 
138     }
139 
140     /** Compute the derivative of the delay term wrt parameters using
141     * automatic differentiation.
142     *
143     * @param derivatives ionospheric delay derivatives
144     * @param freeStateParameters dimension of the state.
145     * @return derivative of the delay wrt ionospheric model parameters
146     */
147     private double[] rangeErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) {
148         // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state
149         // freeStateParameters ... n     -> derivatives of the delay wrt ionospheric parameters
150         final int dim = derivatives.length - freeStateParameters;
151         final double[] rangeError = new double[dim];
152 
153         for (int i = 0; i < dim; i++) {
154             rangeError[i] = derivatives[freeStateParameters + i];
155         }
156 
157         return rangeError;
158     }
159 
160     /** {@inheritDoc} */
161     @Override
162     public List<ParameterDriver> getParametersDrivers() {
163         return ionoModel.getParametersDrivers();
164     }
165 
166     @Override
167     public void modify(final EstimatedMeasurement<TurnAroundRange> estimated) {
168         final TurnAroundRange measurement      = estimated.getObservedMeasurement();
169         final GroundStation   primaryStation   = measurement.getPrimaryStation();
170         final GroundStation   secondaryStation = measurement.getSecondaryStation();
171         final SpacecraftState state            = estimated.getStates()[0];
172 
173         final double[] oldValue = estimated.getEstimatedValue();
174 
175         // Update estimated derivatives with Jacobian of the measure wrt state
176         final IonosphericGradientConverter converter =
177                 new IonosphericGradientConverter(state, 6, new InertialProvider(state.getFrame()));
178         final FieldSpacecraftState<Gradient> gState = converter.getState(ionoModel);
179         final Gradient[] gParameters        = converter.getParameters(gState, ionoModel);
180         final Gradient primaryGDelay        = rangeErrorIonosphericModel(primaryStation, gState, gParameters);
181         final Gradient secondaryGDelay      = rangeErrorIonosphericModel(secondaryStation, gState, gParameters);
182         final double[] primaryDerivatives   = primaryGDelay.getGradient();
183         final double[] secondaryDerivatives = secondaryGDelay.getGradient();
184 
185         final double[][] primaryDjac      = rangeErrorJacobianState(primaryDerivatives);
186         final double[][] secondaryDjac    = rangeErrorJacobianState(secondaryDerivatives);
187         final double[][] stateDerivatives = estimated.getStateDerivatives(0);
188         for (int irow = 0; irow < stateDerivatives.length; ++irow) {
189             for (int jcol = 0; jcol < stateDerivatives[0].length; ++jcol) {
190                 stateDerivatives[irow][jcol] += primaryDjac[irow][jcol] + secondaryDjac[irow][jcol];
191             }
192         }
193         estimated.setStateDerivatives(0, stateDerivatives);
194 
195         int indexPrimary = 0;
196         for (final ParameterDriver driver : getParametersDrivers()) {
197             if (driver.isSelected()) {
198                 // update estimated derivatives with derivative of the modification wrt ionospheric parameters
199                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
200                 final double[] derivatives = rangeErrorParameterDerivative(primaryDerivatives, converter.getFreeStateParameters());
201                 parameterDerivative += derivatives[indexPrimary];
202                 estimated.setParameterDerivatives(driver, parameterDerivative);
203                 indexPrimary += 1;
204             }
205 
206         }
207 
208         int indexSecondary = 0;
209         for (final ParameterDriver driver : getParametersDrivers()) {
210             if (driver.isSelected()) {
211                 // update estimated derivatives with derivative of the modification wrt ionospheric parameters
212                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
213                 final double[] derivatives = rangeErrorParameterDerivative(secondaryDerivatives, converter.getFreeStateParameters());
214                 parameterDerivative += derivatives[indexSecondary];
215                 estimated.setParameterDerivatives(driver, parameterDerivative);
216                 indexSecondary += 1;
217             }
218 
219         }
220 
221         // Update derivatives with respect to primary station position
222         for (final ParameterDriver driver : Arrays.asList(primaryStation.getClockOffsetDriver(),
223                                                           primaryStation.getEastOffsetDriver(),
224                                                           primaryStation.getNorthOffsetDriver(),
225                                                           primaryStation.getZenithOffsetDriver())) {
226             if (driver.isSelected()) {
227                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
228                 parameterDerivative += rangeErrorParameterDerivative(primaryStation, driver, state);
229                 estimated.setParameterDerivatives(driver, parameterDerivative);
230             }
231         }
232 
233         // Update derivatives with respect to secondary station position
234         for (final ParameterDriver driver : Arrays.asList(secondaryStation.getEastOffsetDriver(),
235                                                           secondaryStation.getNorthOffsetDriver(),
236                                                           secondaryStation.getZenithOffsetDriver())) {
237             if (driver.isSelected()) {
238                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
239                 parameterDerivative += rangeErrorParameterDerivative(secondaryStation, driver, state);
240                 estimated.setParameterDerivatives(driver, parameterDerivative);
241             }
242         }
243 
244         // Update estimated value taking into account the ionospheric delay.
245         // The ionospheric delay is directly added to the TurnAroundRange.
246         final double[] newValue = oldValue.clone();
247         newValue[0] = newValue[0] + primaryGDelay.getReal() + secondaryGDelay.getReal();
248         estimated.setEstimatedValue(newValue);
249     }
250 
251 }