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