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    * The ASF 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.Field;
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.analysis.differentiation.Gradient;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.orekit.attitudes.InertialProvider;
28  import org.orekit.estimation.measurements.EstimatedMeasurement;
29  import org.orekit.estimation.measurements.EstimationModifier;
30  import org.orekit.estimation.measurements.GroundStation;
31  import org.orekit.estimation.measurements.gnss.Phase;
32  import org.orekit.models.earth.troposphere.DiscreteTroposphericModel;
33  import org.orekit.propagation.FieldSpacecraftState;
34  import org.orekit.propagation.SpacecraftState;
35  import org.orekit.utils.Differentiation;
36  import org.orekit.utils.ParameterDriver;
37  import org.orekit.utils.ParameterFunction;
38  
39  /**
40   * Class modifying theoretical phase measurement with tropospheric delay.
41   * The effect of tropospheric correction on the phase is directly computed
42   * through the computation of the tropospheric delay.
43   * @author David Soulard
44   * @author Bryan Cazabonne
45   * @since 10.2
46   */
47  public class PhaseTroposphericDelayModifier implements EstimationModifier<Phase> {
48  
49      /** Tropospheric delay model. */
50      private final DiscreteTroposphericModel tropoModel;
51  
52      /** Constructor.
53       *
54       * @param model  Tropospheric delay model appropriate for the current range measurement method.
55       */
56      public PhaseTroposphericDelayModifier(final DiscreteTroposphericModel model) {
57          tropoModel = model;
58      }
59  
60      /** Compute the measurement error due to Troposphere.
61       * @param station station
62       * @param state spacecraft state
63       * @param wavelength wavelength of the signal
64       * @return the measurement error due to Troposphere
65       */
66      private double phaseErrorTroposphericModel(final GroundStation station, final SpacecraftState state, final double wavelength) {
67          // satellite position
68          final Vector3D position = state.getPVCoordinates().getPosition();
69  
70          // elevation
71          final double elevation = station.getBaseFrame().getElevation(position,
72                                                                       state.getFrame(),
73                                                                       state.getDate());
74  
75          // only consider measures above the horizon
76          if (elevation > 0) {
77              // delay in meters
78              final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state.getDate());
79  
80              return delay / wavelength;
81          }
82  
83          return 0;
84      }
85  
86      /** Compute the measurement error due to Troposphere.
87       * @param <T> type of the element
88       * @param station station
89       * @param state spacecraft state
90       * @param parameters tropospheric model parameters
91       * @param wavelength of the measurements
92       * @return the measurement error due to Troposphere
93       */
94      private <T extends CalculusFieldElement<T>> T phaseErrorTroposphericModel(final GroundStation station,
95                                                                            final FieldSpacecraftState<T> state,
96                                                                            final T[] parameters, final double wavelength) {
97  
98          // Field
99          final Field<T> field = state.getDate().getField();
100         final T zero         = field.getZero();
101 
102         // satellite elevation
103         final FieldVector3D<T> position     = state.getPVCoordinates().getPosition();
104         final T elevation                   = station.getBaseFrame().getElevation(position,
105                                                                                   state.getFrame(),
106                                                                                   state.getDate());
107 
108 
109         // only consider measures above the horizon
110         if (elevation.getReal() > 0) {
111             // delay in meters
112             final T delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(field), parameters, state.getDate());
113 
114             return delay.divide(wavelength);
115         }
116 
117         return zero;
118     }
119 
120     /** Compute the Jacobian of the delay term wrt state using
121     * automatic differentiation.
122     *
123     * @param derivatives tropospheric delay derivatives
124     *
125     * @return Jacobian of the delay wrt state
126     */
127     private double[][] phaseErrorJacobianState(final double[] derivatives) {
128         final double[][] finiteDifferencesJacobian = new double[1][6];
129         System.arraycopy(derivatives, 0, finiteDifferencesJacobian[0], 0, 6);
130         return finiteDifferencesJacobian;
131     }
132 
133     /** Compute the derivative of the delay term wrt parameters.
134      *
135      * @param station ground station
136      * @param driver driver for the station offset parameter
137      * @param state spacecraft state
138      * @param wavelength wavelength of the signal
139      * @return derivative of the delay wrt station offset parameter
140      */
141     private double phaseErrorParameterDerivative(final GroundStation station,
142                                                  final ParameterDriver driver,
143                                                  final SpacecraftState state,
144                                                  final double wavelength) {
145         final ParameterFunction rangeError = parameterDriver -> phaseErrorTroposphericModel(station, state, wavelength);
146         final ParameterFunction phaseErrorDerivative =
147                         Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale());
148         return phaseErrorDerivative.value(driver);
149 
150     }
151 
152     /** Compute the derivative of the delay term wrt parameters using
153     * automatic differentiation.
154     *
155     * @param derivatives tropospheric delay derivatives
156     * @param freeStateParameters dimension of the state.
157     * @return derivative of the delay wrt tropospheric model parameters
158     */
159     private double[] phaseErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) {
160         // 0 ... freeStateParameters - 1   -> derivatives of the delay wrt state
161         // freeStateParameters ... n       -> derivatives of the delay wrt tropospheric parameters
162         final int dim = derivatives.length - freeStateParameters;
163         final double[] rangeError = new double[dim];
164 
165         for (int i = 0; i < dim; i++) {
166             rangeError[i] = derivatives[freeStateParameters + i];
167         }
168 
169         return rangeError;
170     }
171 
172     /** {@inheritDoc} */
173     @Override
174     public List<ParameterDriver> getParametersDrivers() {
175         return tropoModel.getParametersDrivers();
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     public void modify(final EstimatedMeasurement<Phase> estimated) {
181         final Phase           measurement = estimated.getObservedMeasurement();
182         final GroundStation   station     = measurement.getStation();
183         final SpacecraftState state       = estimated.getStates()[0];
184 
185         // Old range value
186         final double[] oldValue = estimated.getEstimatedValue();
187 
188         // update estimated derivatives with Jacobian of the measure wrt state
189         final TroposphericGradientConverter converter = new TroposphericGradientConverter(state, 6, new InertialProvider(state.getFrame()));
190         final FieldSpacecraftState<Gradient> gState = converter.getState(tropoModel);
191         final Gradient[] gParameters = converter.getParameters(gState, tropoModel);
192         final Gradient gDelay = phaseErrorTroposphericModel(station, gState, gParameters, measurement.getWavelength());
193         final double[] derivatives = gDelay.getGradient();
194 
195         // Update state derivatives
196         final double[][] djac = phaseErrorJacobianState(derivatives);
197         final double[][] stateDerivatives = estimated.getStateDerivatives(0);
198         for (int irow = 0; irow < stateDerivatives.length; ++irow) {
199             for (int jcol = 0; jcol < stateDerivatives[0].length; ++jcol) {
200                 stateDerivatives[irow][jcol] += djac[irow][jcol];
201             }
202         }
203         estimated.setStateDerivatives(0, stateDerivatives);
204 
205 
206         // Update tropospheric parameter derivatives
207         int index = 0;
208         for (final ParameterDriver driver : getParametersDrivers()) {
209             if (driver.isSelected()) {
210                 // update estimated derivatives with derivative of the modification wrt tropospheric parameters
211                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
212                 final double[] dDelaydP    = phaseErrorParameterDerivative(derivatives, converter.getFreeStateParameters());
213                 parameterDerivative += dDelaydP[index];
214                 estimated.setParameterDerivatives(driver, parameterDerivative);
215                 index = index + 1;
216             }
217         }
218 
219         // Update station parameter derivatives
220         for (final ParameterDriver driver : Arrays.asList(station.getClockOffsetDriver(),
221                                                           station.getEastOffsetDriver(),
222                                                           station.getNorthOffsetDriver(),
223                                                           station.getZenithOffsetDriver())) {
224             if (driver.isSelected()) {
225                 // update estimated derivatives with derivative of the modification wrt station parameters
226                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
227                 parameterDerivative += phaseErrorParameterDerivative(station, driver, state, measurement.getWavelength());
228                 estimated.setParameterDerivatives(driver, parameterDerivative);
229             }
230         }
231 
232         // Update estimated value taking into account the tropospheric delay.
233         // The tropospheric delay is directly added to the phase.
234         final double[] newValue = oldValue.clone();
235         newValue[0] = newValue[0] + gDelay.getReal();
236         estimated.setEstimatedValue(newValue);
237     }
238 
239 }
240