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.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.RangeRate;
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  /** Class modifying theoretical range-rate measurements with tropospheric delay.
40   * The effect of tropospheric correction on the range-rate is directly computed
41   * through the computation of the tropospheric delay difference with respect to
42   * time.
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 Joris Olympio
48   * @since 8.0
49   */
50  public class RangeRateTroposphericDelayModifier implements EstimationModifier<RangeRate> {
51  
52      /** Tropospheric delay model. */
53      private final DiscreteTroposphericModel tropoModel;
54  
55      /** Two-way measurement factor. */
56      private final double fTwoWay;
57  
58      /** Constructor.
59       *
60       * @param model  Tropospheric delay model appropriate for the current range-rate measurement method.
61       * @param tw     Flag indicating whether the measurement is two-way.
62       */
63      public RangeRateTroposphericDelayModifier(final DiscreteTroposphericModel model, final boolean tw) {
64          tropoModel = model;
65          if (tw) {
66              fTwoWay = 2.;
67          } else {
68              fTwoWay = 1.;
69          }
70      }
71  
72      /** Compute the measurement error due to Troposphere.
73       * @param station station
74       * @param state spacecraft state
75       * @return the measurement error due to Troposphere
76       */
77      public double rangeRateErrorTroposphericModel(final GroundStation station,
78                                                    final SpacecraftState state) {
79          // The effect of tropospheric correction on the range rate is
80          // computed using finite differences.
81  
82          final double dt = 10; // s
83  
84          // spacecraft position and elevation as seen from the ground station
85          final Vector3D position = state.getPVCoordinates().getPosition();
86  
87          // elevation
88          final double elevation1 = station.getBaseFrame().getElevation(position,
89                                                                        state.getFrame(),
90                                                                        state.getDate());
91  
92          // only consider measures above the horizon
93          if (elevation1 > 0) {
94              // tropospheric delay in meters
95              final double d1 = tropoModel.pathDelay(elevation1, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state.getDate());
96  
97              // propagate spacecraft state forward by dt
98              final SpacecraftState state2 = state.shiftedBy(dt);
99  
100             // spacecraft position and elevation as seen from the ground station
101             final Vector3D position2 = state2.getPVCoordinates().getPosition();
102 
103             // elevation
104             final double elevation2 = station.getBaseFrame().getElevation(position2,
105                                                                           state2.getFrame(),
106                                                                           state2.getDate());
107 
108             // tropospheric delay dt after
109             final double d2 = tropoModel.pathDelay(elevation2, station.getBaseFrame().getPoint(), tropoModel.getParameters(), state2.getDate());
110 
111             return fTwoWay * (d2 - d1) / dt;
112         }
113 
114         return 0;
115     }
116 
117 
118     /** Compute the measurement error due to Troposphere.
119      * @param <T> type of the element
120      * @param station station
121      * @param state spacecraft state
122      * @param parameters tropospheric model parameters
123      * @return the measurement error due to Troposphere
124      */
125     public <T extends CalculusFieldElement<T>> T rangeRateErrorTroposphericModel(final GroundStation station,
126                                                                              final FieldSpacecraftState<T> state,
127                                                                              final T[] parameters) {
128         // Field
129         final Field<T> field = state.getDate().getField();
130         final T zero         = field.getZero();
131 
132         // The effect of tropospheric correction on the range rate is
133         // computed using finite differences.
134 
135         final double dt = 10; // s
136 
137         // spacecraft position and elevation as seen from the ground station
138         final FieldVector3D<T> position     = state.getPVCoordinates().getPosition();
139         final T elevation1                  = station.getBaseFrame().getElevation(position,
140                                                                                   state.getFrame(),
141                                                                                   state.getDate());
142 
143         // only consider measures above the horizon
144         if (elevation1.getReal() > 0) {
145             // tropospheric delay in meters
146             final T d1 = tropoModel.pathDelay(elevation1, station.getBaseFrame().getPoint(field), parameters, state.getDate());
147 
148             // propagate spacecraft state forward by dt
149             final FieldSpacecraftState<T> state2 = state.shiftedBy(dt);
150 
151             // spacecraft position and elevation as seen from the ground station
152             final FieldVector3D<T> position2     = state2.getPVCoordinates().getPosition();
153 
154             // elevation
155             final T elevation2 = station.getBaseFrame().getElevation(position2,
156                                                                      state2.getFrame(),
157                                                                      state2.getDate());
158 
159 
160             // tropospheric delay dt after
161             final T d2 = tropoModel.pathDelay(elevation2, station.getBaseFrame().getPoint(field), parameters, state2.getDate());
162 
163             return (d2.subtract(d1)).divide(dt).multiply(fTwoWay);
164         }
165 
166         return zero;
167     }
168 
169     /** Compute the Jacobian of the delay term wrt state using
170     * automatic differentiation.
171     *
172     * @param derivatives tropospheric delay derivatives
173     *
174     * @return Jacobian of the delay wrt state
175     */
176     private double[][] rangeRateErrorJacobianState(final double[] derivatives) {
177         final double[][] finiteDifferencesJacobian = new double[1][6];
178         System.arraycopy(derivatives, 0, finiteDifferencesJacobian[0], 0, 6);
179         return finiteDifferencesJacobian;
180     }
181 
182     /** Compute the derivative of the delay term wrt parameters.
183     *
184     * @param station ground station
185     * @param driver driver for the station offset parameter
186     * @param state spacecraft state
187     * @return derivative of the delay wrt station offset parameter
188     */
189     private double rangeRateErrorParameterDerivative(final GroundStation station,
190                                                      final ParameterDriver driver,
191                                                      final SpacecraftState state) {
192 
193         final ParameterFunction rangeError = new ParameterFunction() {
194             /** {@inheritDoc} */
195             @Override
196             public double value(final ParameterDriver parameterDriver) {
197                 return rangeRateErrorTroposphericModel(station, state);
198             }
199         };
200 
201         final ParameterFunction rangeErrorDerivative =
202                         Differentiation.differentiate(rangeError, 3, 10.0 * driver.getScale());
203 
204         return rangeErrorDerivative.value(driver);
205 
206     }
207 
208     /** Compute the derivative of the delay term wrt parameters using
209     * automatic differentiation.
210     *
211     * @param derivatives tropospheric delay derivatives
212     * @param freeStateParameters dimension of the state.
213     * @return derivative of the delay wrt tropospheric model parameters
214     */
215     private double[] rangeRateErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) {
216         // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state
217         // freeStateParameters ... n     -> derivatives of the delay wrt tropospheric parameters
218         final int dim = derivatives.length - freeStateParameters;
219         final double[] rangeError = new double[dim];
220 
221         for (int i = 0; i < dim; i++) {
222             rangeError[i] = derivatives[freeStateParameters + i];
223         }
224 
225         return rangeError;
226     }
227 
228     /** {@inheritDoc} */
229     @Override
230     public List<ParameterDriver> getParametersDrivers() {
231         return tropoModel.getParametersDrivers();
232     }
233 
234     /** {@inheritDoc} */
235     @Override
236     public void modify(final EstimatedMeasurement<RangeRate> estimated) {
237         final RangeRate       measurement = estimated.getObservedMeasurement();
238         final GroundStation   station     = measurement.getStation();
239         final SpacecraftState state       = estimated.getStates()[0];
240 
241         final double[] oldValue = estimated.getEstimatedValue();
242 
243         // update estimated derivatives with Jacobian of the measure wrt state
244         final TroposphericGradientConverter converter =
245                 new TroposphericGradientConverter(state, 6, new InertialProvider(state.getFrame()));
246         final FieldSpacecraftState<Gradient> gState = converter.getState(tropoModel);
247         final Gradient[] gParameters = converter.getParameters(gState, tropoModel);
248         final Gradient gDelay = rangeRateErrorTroposphericModel(station, gState, gParameters);
249         final double[] derivatives = gDelay.getGradient();
250 
251         final double[][] djac = rangeRateErrorJacobianState(derivatives);
252         final double[][] stateDerivatives = estimated.getStateDerivatives(0);
253         for (int irow = 0; irow < stateDerivatives.length; ++irow) {
254             for (int jcol = 0; jcol < stateDerivatives[0].length; ++jcol) {
255                 stateDerivatives[irow][jcol] += djac[irow][jcol];
256             }
257         }
258         estimated.setStateDerivatives(0, stateDerivatives);
259 
260         int index = 0;
261         for (final ParameterDriver driver : getParametersDrivers()) {
262             if (driver.isSelected()) {
263                 // update estimated derivatives with derivative of the modification wrt tropospheric parameters
264                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
265                 final double[] dDelaydP    = rangeRateErrorParameterDerivative(derivatives, converter.getFreeStateParameters());
266                 parameterDerivative += dDelaydP[index];
267                 estimated.setParameterDerivatives(driver, parameterDerivative);
268                 index += 1;
269             }
270 
271         }
272 
273         for (final ParameterDriver driver : Arrays.asList(station.getClockOffsetDriver(),
274                                                           station.getEastOffsetDriver(),
275                                                           station.getNorthOffsetDriver(),
276                                                           station.getZenithOffsetDriver())) {
277             if (driver.isSelected()) {
278                 // update estimated derivatives with derivative of the modification wrt station parameters
279                 double parameterDerivative = estimated.getParameterDerivatives(driver)[0];
280                 parameterDerivative += rangeRateErrorParameterDerivative(station, driver, state);
281                 estimated.setParameterDerivatives(driver, parameterDerivative);
282             }
283         }
284 
285         // update estimated value taking into account the tropospheric delay.
286         // The tropospheric delay is directly added to the range.
287         final double[] newValue = oldValue.clone();
288         newValue[0] = newValue[0] + gDelay.getReal();
289         estimated.setEstimatedValue(newValue);
290 
291     }
292 
293 }