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.sequential;
18  
19  import org.hipparchus.filtering.kalman.ProcessEstimate;
20  import org.hipparchus.filtering.kalman.unscented.UnscentedEvolution;
21  import org.hipparchus.filtering.kalman.unscented.UnscentedProcess;
22  import org.hipparchus.linear.ArrayRealVector;
23  import org.hipparchus.linear.MatrixUtils;
24  import org.hipparchus.linear.RealMatrix;
25  import org.hipparchus.linear.RealVector;
26  import org.orekit.estimation.measurements.EstimatedMeasurement;
27  import org.orekit.estimation.measurements.EstimatedMeasurementBase;
28  import org.orekit.estimation.measurements.ObservedMeasurement;
29  import org.orekit.orbits.CartesianOrbit;
30  import org.orekit.orbits.Orbit;
31  import org.orekit.propagation.Propagator;
32  import org.orekit.propagation.SpacecraftState;
33  import org.orekit.propagation.conversion.PropagatorBuilder;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.utils.ParameterDriver;
36  import org.orekit.utils.ParameterDriversList;
37  import org.orekit.utils.ParameterDriversList.DelegatingDriver;
38  
39  import java.util.List;
40  
41  /** Class defining the process model dynamics to use with a {@link UnscentedKalmanEstimator}.
42   * @author Gaƫtan Pierre
43   * @author Bryan Cazabonne
44   * @since 11.3
45   */
46  public class UnscentedKalmanModel extends KalmanEstimationCommon implements UnscentedProcess<MeasurementDecorator> {
47  
48      /** Reference values. */
49      private final double[] referenceValues;
50  
51      /** Unscented Kalman process model constructor (package private).
52       * @param propagatorBuilders propagators builders used to evaluate the orbits.
53       * @param covarianceMatricesProviders provider for covariance matrix
54       * @param estimatedMeasurementParameters measurement parameters to estimate
55       * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
56       */
57      protected UnscentedKalmanModel(final List<PropagatorBuilder> propagatorBuilders,
58                                     final List<CovarianceMatrixProvider> covarianceMatricesProviders,
59                                     final ParameterDriversList estimatedMeasurementParameters,
60                                     final CovarianceMatrixProvider measurementProcessNoiseMatrix) {
61  
62          super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, measurementProcessNoiseMatrix);
63  
64          // Record the initial reference values
65          int stateDimension = 0;
66          for (final ParameterDriver ignored : getEstimatedOrbitalParameters().getDrivers()) {
67              stateDimension += 1;
68          }
69          for (final ParameterDriver ignored : getEstimatedPropagationParameters().getDrivers()) {
70              stateDimension += 1;
71          }
72          for (final ParameterDriver ignored : getEstimatedMeasurementsParameters().getDrivers()) {
73              stateDimension += 1;
74          }
75  
76          this.referenceValues = new double[stateDimension];
77          int index = 0;
78          for (final ParameterDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
79              referenceValues[index++] = driver.getReferenceValue();
80          }
81          for (final ParameterDriver driver : getEstimatedPropagationParameters().getDrivers()) {
82              referenceValues[index++] = driver.getReferenceValue();
83          }
84          for (final ParameterDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
85              referenceValues[index++] = driver.getReferenceValue();
86          }
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public UnscentedEvolution getEvolution(final double previousTime, final RealVector[] sigmaPoints,
92                                             final MeasurementDecorator measurement) {
93  
94          // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
95          final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
96          for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
97              if (driver.getReferenceDate() == null) {
98                  driver.setReferenceDate(getBuilders().get(0).getInitialOrbitDate());
99              }
100         }
101 
102         // Increment measurement number
103         incrementCurrentMeasurementNumber();
104 
105         // Update the current date
106         setCurrentDate(measurement.getObservedMeasurement().getDate());
107 
108         // Initialize array of predicted sigma points
109         final RealVector[] predictedSigmaPoints = new RealVector[sigmaPoints.length];
110 
111         // Propagate each sigma point
112         for (int i = 0; i < sigmaPoints.length; i++) {
113 
114             // Set parameters for this sigma point
115             final RealVector sigmaPoint = sigmaPoints[i].copy();
116             updateParameters(sigmaPoint);
117 
118             // Get propagators
119             final Propagator[] propagators = getEstimatedPropagators();
120 
121             // Do prediction
122             predictedSigmaPoints[i] = predictState(observedMeasurement.getDate(), sigmaPoint, propagators);
123         }
124 
125         // Reset the driver reference values based on the first sigma point
126         int d = 0;
127         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
128             driver.setReferenceValue(referenceValues[d]);
129             driver.setNormalizedValue(predictedSigmaPoints[0].getEntry(d));
130             referenceValues[d] = driver.getValue();
131 
132             // Make remaining sigma points relative to the first
133             for (int i = 1; i < predictedSigmaPoints.length; ++i) {
134                 predictedSigmaPoints[i].setEntry(d, predictedSigmaPoints[i].getEntry(d) - predictedSigmaPoints[0].getEntry(d));
135             }
136             predictedSigmaPoints[0].setEntry(d, 0.0);
137 
138             d += 1;
139         }
140 
141         // compute process noise matrix
142         final RealMatrix normalizedProcessNoise = getNormalizedProcessNoise(sigmaPoints[0].getDimension());
143 
144         // Return
145         return new UnscentedEvolution(measurement.getTime(), predictedSigmaPoints, normalizedProcessNoise);
146     }
147 
148     /** {@inheritDoc} */
149     @Override
150     public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPoints, final MeasurementDecorator measurement) {
151 
152         // Observed measurement
153         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
154 
155         // Standard deviation as a vector
156         final RealVector theoreticalStandardDeviation =
157                 MatrixUtils.createRealVector(observedMeasurement.getTheoreticalStandardDeviation());
158 
159         // Initialize arrays of predicted states and measurements
160         final RealVector[] predictedMeasurements = new RealVector[predictedSigmaPoints.length];
161 
162         // Loop on sigma points to predict measurements
163         for (int i = 0; i < predictedSigmaPoints.length; i++) {
164             // Set parameters for this sigma point
165             final RealVector predictedSigmaPoint = predictedSigmaPoints[i].copy();
166             updateParameters(predictedSigmaPoint);
167 
168             // Get propagators
169             Propagator[] propagators = getEstimatedPropagators();
170 
171             // "updateParameters" sets the correct orbital and parameters info, but doesn't reset the time.
172             for (int k = 0; k < propagators.length; ++k) {
173                 final SpacecraftState predictedState = propagators[k].getInitialState();
174                 final Orbit predictedOrbit = getBuilders().get(k).getOrbitType().convertType(
175                         new CartesianOrbit(predictedState.getPVCoordinates(),
176                                 predictedState.getFrame(),
177                                 observedMeasurement.getDate(),
178                                 predictedState.getMu()
179                         )
180                 );
181                 getBuilders().get(k).resetOrbit(predictedOrbit);
182             }
183             propagators = getEstimatedPropagators();
184 
185             // Predicted states
186             final SpacecraftState[] predictedStates = new SpacecraftState[propagators.length];
187             for (int k = 0; k < propagators.length; ++k) {
188                 predictedStates[k] = propagators[k].getInitialState();
189             }
190 
191             // Calculated estimated measurement from predicted sigma point
192             final EstimatedMeasurement<?> estimated = estimateMeasurement(observedMeasurement, getCurrentMeasurementNumber(),
193                                                                                    KalmanEstimatorUtil.filterRelevant(observedMeasurement,
194                                                                                                                       predictedStates));
195             predictedMeasurements[i] = new ArrayRealVector(estimated.getEstimatedValue())
196                     .ebeDivide(theoreticalStandardDeviation);
197         }
198 
199         // Return the predicted measurements
200         return predictedMeasurements;
201 
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public RealVector getInnovation(final MeasurementDecorator measurement, final RealVector predictedMeas,
207                                     final RealVector predictedState, final RealMatrix innovationCovarianceMatrix) {
208         // Set parameters from predicted state
209         final RealVector predictedStateCopy = predictedState.copy();
210         updateParameters(predictedStateCopy);
211 
212         // Standard deviation as a vector
213         final RealVector theoreticalStandardDeviation =
214                 MatrixUtils.createRealVector(measurement.getObservedMeasurement().getTheoreticalStandardDeviation());
215 
216         // Get propagators
217         Propagator[] propagators = getEstimatedPropagators();
218 
219         // "updateParameters" sets the correct orbital info, but doesn't reset the time.
220         for (int k = 0; k < propagators.length; ++k) {
221             final SpacecraftState predicted = propagators[k].getInitialState();
222             final Orbit predictedOrbit = getBuilders().get(k).getOrbitType().convertType(
223                     new CartesianOrbit(predicted.getPVCoordinates(),
224                             predicted.getFrame(),
225                             measurement.getObservedMeasurement().getDate(),
226                             predicted.getMu()
227                     )
228             );
229             getBuilders().get(k).resetOrbit(predictedOrbit);
230         }
231         propagators = getEstimatedPropagators();
232 
233         // Predicted states
234         for (int k = 0; k < propagators.length; ++k) {
235             setPredictedSpacecraftState(propagators[k].getInitialState(), k);
236         }
237 
238         // set estimated value to the predicted value from the filter
239         final EstimatedMeasurement<?> predictedMeasurement =
240             estimateMeasurement(measurement.getObservedMeasurement(), getCurrentMeasurementNumber(),
241                                 KalmanEstimatorUtil.filterRelevant(measurement.getObservedMeasurement(),
242                                 getPredictedSpacecraftStates()));
243         setPredictedMeasurement(predictedMeasurement);
244         predictedMeasurement.setEstimatedValue(predictedMeas.ebeMultiply(theoreticalStandardDeviation).toArray());
245 
246         // Check for outliers
247         KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
248 
249         // Compute the innovation vector
250         return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement,
251                 predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
252     }
253 
254 
255     private RealVector predictState(final AbsoluteDate date,
256                                     final RealVector previousState,
257                                     final Propagator[] propagators) {
258 
259         // Initialise predicted state
260         final RealVector predictedState = previousState.copy();
261 
262         // Orbital parameters counter
263         int jOrb = 0;
264 
265         // Loop over propagators
266         for (int k = 0; k < propagators.length; ++k) {
267 
268             // Record original state
269             final SpacecraftState originalState = propagators[k].getInitialState();
270 
271             // Propagate
272             final SpacecraftState predicted = propagators[k].propagate(date);
273 
274             // Update the builder with the predicted orbit
275             // This updates the orbital drivers with the values of the predicted orbit
276             getBuilders().get(k).resetOrbit(predicted.getOrbit());
277 
278             // The orbital parameters in the state vector are replaced with their predicted values
279             // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation)
280             // As the propagator builder was previously updated with the predicted orbit,
281             // the selected orbital drivers are already up to date with the prediction
282             for (DelegatingDriver orbitalDriver : getBuilders().get(k).getOrbitalParametersDrivers().getDrivers()) {
283                 if (orbitalDriver.isSelected()) {
284                     orbitalDriver.setReferenceValue(referenceValues[jOrb]);
285                     predictedState.setEntry(jOrb, orbitalDriver.getNormalizedValue());
286 
287                     jOrb += 1;
288                 }
289             }
290 
291             // Set the builder back to the original time
292             getBuilders().get(k).resetOrbit(originalState.getOrbit());
293 
294         }
295 
296         return predictedState;
297     }
298 
299 
300     /** Finalize estimation.
301      * @param observedMeasurement measurement that has just been processed
302      * @param estimate corrected estimate
303      */
304     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
305                                    final ProcessEstimate estimate) {
306         // Update the parameters with the estimated state
307         // The min/max values of the parameters are handled by the ParameterDriver implementation
308         setCorrectedEstimate(estimate);
309         updateParameters(estimate.getState());
310 
311         // Get the estimated propagator (mirroring parameter update in the builder)
312         // and the estimated spacecraft state
313         final Propagator[] estimatedPropagators = getEstimatedPropagators();
314         for (int k = 0; k < estimatedPropagators.length; ++k) {
315             setCorrectedSpacecraftState(estimatedPropagators[k].getInitialState(), k);
316         }
317 
318         // Corrected measurement
319         setCorrectedMeasurement(estimateMeasurement(observedMeasurement, getCurrentMeasurementNumber(),
320                                                     KalmanEstimatorUtil.filterRelevant(observedMeasurement,
321                                                     getCorrectedSpacecraftStates())));
322     }
323 
324     /**
325      * Estimate measurement (without derivatives).
326      * @param <T> measurement type
327      * @param observedMeasurement observed measurement
328      * @param measurementNumber measurement number
329      * @param spacecraftStates states
330      * @return estimated measurement
331      * @since 12.1
332      */
333     private static <T extends ObservedMeasurement<T>> EstimatedMeasurement<T> estimateMeasurement(final ObservedMeasurement<T> observedMeasurement,
334                                                                                                   final int measurementNumber,
335                                                                                                   final SpacecraftState[] spacecraftStates) {
336         final EstimatedMeasurementBase<T> estimatedMeasurementBase = observedMeasurement.
337                 estimateWithoutDerivatives(measurementNumber, measurementNumber,
338                 KalmanEstimatorUtil.filterRelevant(observedMeasurement, spacecraftStates));
339         final EstimatedMeasurement<T> estimatedMeasurement = new EstimatedMeasurement<>(estimatedMeasurementBase.getObservedMeasurement(),
340                 estimatedMeasurementBase.getIteration(), estimatedMeasurementBase.getCount(),
341                 estimatedMeasurementBase.getStates(), estimatedMeasurementBase.getParticipants());
342         estimatedMeasurement.setEstimatedValue(estimatedMeasurementBase.getEstimatedValue());
343         return estimatedMeasurement;
344     }
345 
346     /** Update parameter drivers with a normalised state, adjusting state according to the driver limits.
347      * @param normalizedState the input state
348      * The min/max allowed values are handled by the parameter themselves.
349      */
350     private void updateParameters(final RealVector normalizedState) {
351         int i = 0;
352         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
353             // let the parameter handle min/max clipping
354             driver.setReferenceValue(referenceValues[i]);
355             driver.setNormalizedValue(normalizedState.getEntry(i));
356             normalizedState.setEntry(i++, driver.getNormalizedValue());
357         }
358         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
359             // let the parameter handle min/max clipping
360             driver.setNormalizedValue(normalizedState.getEntry(i));
361             normalizedState.setEntry(i++, driver.getNormalizedValue());
362         }
363         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
364             // let the parameter handle min/max clipping
365             driver.setNormalizedValue(normalizedState.getEntry(i));
366             normalizedState.setEntry(i++, driver.getNormalizedValue());
367         }
368     }
369 }