1   /* Copyright 2002-2022 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 java.util.ArrayList;
20  import java.util.Comparator;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.hipparchus.exception.MathRuntimeException;
26  import org.hipparchus.filtering.kalman.ProcessEstimate;
27  import org.hipparchus.filtering.kalman.extended.ExtendedKalmanFilter;
28  import org.hipparchus.filtering.kalman.extended.NonLinearEvolution;
29  import org.hipparchus.filtering.kalman.extended.NonLinearProcess;
30  import org.hipparchus.linear.Array2DRowRealMatrix;
31  import org.hipparchus.linear.ArrayRealVector;
32  import org.hipparchus.linear.MatrixUtils;
33  import org.hipparchus.linear.QRDecomposition;
34  import org.hipparchus.linear.RealMatrix;
35  import org.hipparchus.linear.RealVector;
36  import org.hipparchus.util.FastMath;
37  import org.orekit.errors.OrekitException;
38  import org.orekit.errors.OrekitMessages;
39  import org.orekit.estimation.measurements.EstimatedMeasurement;
40  import org.orekit.estimation.measurements.EstimationModifier;
41  import org.orekit.estimation.measurements.ObservedMeasurement;
42  import org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter;
43  import org.orekit.orbits.Orbit;
44  import org.orekit.orbits.OrbitType;
45  import org.orekit.propagation.PropagationType;
46  import org.orekit.propagation.SpacecraftState;
47  import org.orekit.propagation.conversion.DSSTPropagatorBuilder;
48  import org.orekit.propagation.semianalytical.dsst.DSSTHarvester;
49  import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
50  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
51  import org.orekit.propagation.semianalytical.dsst.forces.ShortPeriodTerms;
52  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
53  import org.orekit.time.AbsoluteDate;
54  import org.orekit.time.ChronologicalComparator;
55  import org.orekit.utils.ParameterDriver;
56  import org.orekit.utils.ParameterDriversList;
57  import org.orekit.utils.ParameterDriversList.DelegatingDriver;
58  
59  /** Process model to use with a {@link SemiAnalyticalKalmanEstimator}.
60   *
61   * @see "Folcik Z., Orbit Determination Using Modern Filters/Smoothers and Continuous Thrust Modeling,
62   *       Master of Science Thesis, Department of Aeronautics and Astronautics, MIT, June, 2008."
63   *
64   * @see "Cazabonne B., Bayard J., Journot M., and Cefola P. J., A Semi-analytical Approach for Orbit
65   *       Determination based on Extended Kalman Filter, AAS Paper 21-614, AAS/AIAA Astrodynamics
66   *       Specialist Conference, Big Sky, August 2021."
67   *
68   * @author Julie Bayard
69   * @author Bryan Cazabonne
70   * @author Maxime Journot
71   * @since 11.1
72   */
73  public  class SemiAnalyticalKalmanModel implements KalmanEstimation, NonLinearProcess<MeasurementDecorator> {
74  
75      /** Builders for DSST propagator. */
76      private final DSSTPropagatorBuilder builder;
77  
78      /** Estimated orbital parameters. */
79      private final ParameterDriversList estimatedOrbitalParameters;
80  
81      /** Per-builder estimated propagation drivers. */
82      private final ParameterDriversList estimatedPropagationParameters;
83  
84      /** Estimated measurements parameters. */
85      private final ParameterDriversList estimatedMeasurementsParameters;
86  
87      /** Map for propagation parameters columns. */
88      private final Map<String, Integer> propagationParameterColumns;
89  
90      /** Map for measurements parameters columns. */
91      private final Map<String, Integer> measurementParameterColumns;
92  
93      /** Scaling factors. */
94      private final double[] scale;
95  
96      /** Provider for covariance matrix. */
97      private final CovarianceMatrixProvider covarianceMatrixProvider;
98  
99      /** Process noise matrix provider for measurement parameters. */
100     private final CovarianceMatrixProvider measurementProcessNoiseMatrix;
101 
102     /** Harvester between two-dimensional Jacobian matrices and one-dimensional additional state arrays. */
103     private DSSTHarvester harvester;
104 
105     /** Propagators for the reference trajectories, up to current date. */
106     private DSSTPropagator dsstPropagator;
107 
108     /** Observer to retrieve current estimation info. */
109     private KalmanObserver observer;
110 
111     /** Current number of measurement. */
112     private int currentMeasurementNumber;
113 
114     /** Current date. */
115     private AbsoluteDate currentDate;
116 
117     /** Predicted mean element filter correction. */
118     private RealVector predictedFilterCorrection;
119 
120     /** Corrected mean element filter correction. */
121     private RealVector correctedFilterCorrection;
122 
123     /** Predicted measurement. */
124     private EstimatedMeasurement<?> predictedMeasurement;
125 
126     /** Corrected measurement. */
127     private EstimatedMeasurement<?> correctedMeasurement;
128 
129     /** Nominal mean spacecraft state. */
130     private SpacecraftState nominalMeanSpacecraftState;
131 
132     /** Previous nominal mean spacecraft state. */
133     private SpacecraftState previousNominalMeanSpacecraftState;
134 
135     /** Current corrected estimate. */
136     private ProcessEstimate correctedEstimate;
137 
138     /** Inverse of the orbital part of the state transition matrix. */
139     private RealMatrix phiS;
140 
141     /** Propagation parameters part of the state transition matrix. */
142     private RealMatrix psiS;
143 
144     /** Kalman process model constructor (package private).
145      * @param propagatorBuilder propagators builders used to evaluate the orbits.
146      * @param covarianceMatrixProvider provider for covariance matrix
147      * @param estimatedMeasurementParameters measurement parameters to estimate
148      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
149      */
150     protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilder,
151                                         final CovarianceMatrixProvider covarianceMatrixProvider,
152                                         final ParameterDriversList estimatedMeasurementParameters,
153                                         final CovarianceMatrixProvider measurementProcessNoiseMatrix) {
154 
155         this.builder                         = propagatorBuilder;
156         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
157         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
158         this.observer                        = null;
159         this.currentMeasurementNumber        = 0;
160         this.currentDate                     = propagatorBuilder.getInitialOrbitDate();
161         this.covarianceMatrixProvider        = covarianceMatrixProvider;
162         this.measurementProcessNoiseMatrix   = measurementProcessNoiseMatrix;
163 
164         // Number of estimated parameters
165         int columns = 0;
166 
167         // Set estimated orbital parameters
168         estimatedOrbitalParameters = new ParameterDriversList();
169         for (final ParameterDriver driver : builder.getOrbitalParametersDrivers().getDrivers()) {
170 
171             // Verify if the driver reference date has been set
172             if (driver.getReferenceDate() == null) {
173                 driver.setReferenceDate(currentDate);
174             }
175 
176             // Verify if the driver is selected
177             if (driver.isSelected()) {
178                 estimatedOrbitalParameters.add(driver);
179                 columns++;
180             }
181 
182         }
183 
184         // Set estimated propagation parameters
185         estimatedPropagationParameters = new ParameterDriversList();
186         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
187         for (final ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {
188 
189             // Verify if the driver reference date has been set
190             if (driver.getReferenceDate() == null) {
191                 driver.setReferenceDate(currentDate);
192             }
193 
194             // Verify if the driver is selected
195             if (driver.isSelected()) {
196                 estimatedPropagationParameters.add(driver);
197                 final String driverName = driver.getName();
198                 // Add the driver name if it has not been added yet
199                 if (!estimatedPropagationParametersNames.contains(driverName)) {
200                     estimatedPropagationParametersNames.add(driverName);
201                 }
202             }
203 
204         }
205         estimatedPropagationParametersNames.sort(Comparator.naturalOrder());
206 
207         // Populate the map of propagation drivers' columns and update the total number of columns
208         propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
209         for (final String driverName : estimatedPropagationParametersNames) {
210             propagationParameterColumns.put(driverName, columns);
211             ++columns;
212         }
213 
214         // Set the estimated measurement parameters
215         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
216             if (parameter.getReferenceDate() == null) {
217                 parameter.setReferenceDate(currentDate);
218             }
219             measurementParameterColumns.put(parameter.getName(), columns);
220             ++columns;
221         }
222 
223         // Compute the scale factors
224         this.scale = new double[columns];
225         int index = 0;
226         for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) {
227             scale[index++] = driver.getScale();
228         }
229         for (final ParameterDriver driver : estimatedPropagationParameters.getDrivers()) {
230             scale[index++] = driver.getScale();
231         }
232         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
233             scale[index++] = driver.getScale();
234         }
235 
236         // Build the reference propagator and add its partial derivatives equations implementation
237         updateReferenceTrajectory(getEstimatedPropagator());
238         this.nominalMeanSpacecraftState = dsstPropagator.getInitialState();
239         this.previousNominalMeanSpacecraftState = nominalMeanSpacecraftState;
240 
241         // Initialize "field" short periodic terms
242         harvester.initializeFieldShortPeriodTerms(nominalMeanSpacecraftState);
243 
244         // Initialize the estimated normalized mean element filter correction (See Ref [1], Eq. 3.2a)
245         this.predictedFilterCorrection = MatrixUtils.createRealVector(columns);
246         this.correctedFilterCorrection = predictedFilterCorrection;
247 
248         // Initialize propagation parameters part of the state transition matrix (See Ref [1], Eq. 3.2c)
249         this.psiS = null;
250         if (estimatedPropagationParameters.getNbParams() != 0) {
251             this.psiS = MatrixUtils.createRealMatrix(getNumberSelectedOrbitalDrivers(),
252                                                      getNumberSelectedPropagationDrivers());
253         }
254 
255         // Initialize inverse of the orbital part of the state transition matrix (See Ref [1], Eq. 3.2d)
256         this.phiS = MatrixUtils.createRealIdentityMatrix(getNumberSelectedOrbitalDrivers());
257 
258         // Number of estimated measurement parameters
259         final int nbMeas = getNumberSelectedMeasurementDrivers();
260 
261         // Number of estimated dynamic parameters (orbital + propagation)
262         final int nbDyn  = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers();
263 
264         // Covariance matrix
265         final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
266         final RealMatrix noiseP = covarianceMatrixProvider.getInitialCovarianceMatrix(nominalMeanSpacecraftState);
267         noiseK.setSubMatrix(noiseP.getData(), 0, 0);
268         if (measurementProcessNoiseMatrix != null) {
269             final RealMatrix noiseM = measurementProcessNoiseMatrix.getInitialCovarianceMatrix(nominalMeanSpacecraftState);
270             noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
271         }
272 
273         // Verify dimension
274         checkDimension(noiseK.getRowDimension(),
275                        builder.getOrbitalParametersDrivers(),
276                        builder.getPropagationParametersDrivers(),
277                        estimatedMeasurementsParameters);
278 
279         final RealMatrix correctedCovariance = normalizeCovarianceMatrix(noiseK);
280 
281         // Initialize corrected estimate
282         this.correctedEstimate = new ProcessEstimate(0.0, correctedFilterCorrection, correctedCovariance);
283 
284     }
285 
286     /** Get the observer for Kalman Filter estimations.
287      * @return the observer for Kalman Filter estimations
288      */
289     public KalmanObserver getObserver() {
290         return observer;
291     }
292 
293     /** Set the observer.
294      * @param observer the observer
295      */
296     public void setObserver(final KalmanObserver observer) {
297         this.observer = observer;
298     }
299 
300     /** Get the current corrected estimate.
301      * @return current corrected estimate
302      */
303     public ProcessEstimate getEstimate() {
304         return correctedEstimate;
305     }
306 
307     /** Process a single measurement.
308      * <p>
309      * Update the filter with the new measurements.
310      * </p>
311      * @param observedMeasurements the list of measurements to process
312      * @param filter Extended Kalman Filter
313      * @return estimated propagator
314      */
315     public DSSTPropagator processMeasurements(final List<ObservedMeasurement<?>> observedMeasurements,
316                                               final ExtendedKalmanFilter<MeasurementDecorator> filter) {
317         try {
318 
319             // Sort the measurement
320             observedMeasurements.sort(new ChronologicalComparator());
321 
322             // Initialize step handler and set it to the propagator
323             final EskfMeasurementHandler stepHandler = new EskfMeasurementHandler(this, filter, observedMeasurements, builder.getInitialOrbitDate());
324             dsstPropagator.getMultiplexer().add(stepHandler);
325             dsstPropagator.propagate(observedMeasurements.get(0).getDate(), observedMeasurements.get(observedMeasurements.size() - 1).getDate());
326 
327             // Return the last estimated propagator
328             return getEstimatedPropagator();
329 
330         } catch (MathRuntimeException mrte) {
331             throw new OrekitException(mrte);
332         }
333     }
334 
335     /** Get the propagator estimated with the values set in the propagator builder.
336      * @return propagator based on the current values in the builder
337      */
338     public DSSTPropagator getEstimatedPropagator() {
339         // Return propagator built with current instantiation of the propagator builder
340         return builder.buildPropagator(builder.getSelectedNormalizedParameters());
341     }
342 
343     /** {@inheritDoc} */
344     @Override
345     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
346                                            final MeasurementDecorator measurement) {
347 
348         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
349         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
350         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
351             if (driver.getReferenceDate() == null) {
352                 driver.setReferenceDate(builder.getInitialOrbitDate());
353             }
354         }
355 
356         // Increment measurement number
357         ++currentMeasurementNumber;
358 
359         // Update the current date
360         currentDate = measurement.getObservedMeasurement().getDate();
361 
362         // Normalized state transition matrix
363         final RealMatrix stm = getErrorStateTransitionMatrix();
364 
365         // Predict filter correction
366         predictedFilterCorrection = predictFilterCorrection(stm);
367 
368         // Short period term derivatives
369         analyticalDerivativeComputations(nominalMeanSpacecraftState);
370 
371         // Calculate the predicted osculating elements
372         final double[] osculating = computeOsculatingElements(predictedFilterCorrection);
373         final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngle(),
374                                                                             currentDate, nominalMeanSpacecraftState.getMu(),
375                                                                             nominalMeanSpacecraftState.getFrame());
376 
377         // Compute the predicted measurements  (See Ref [1], Eq. 3.8)
378         predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
379                                                             currentMeasurementNumber,
380                                                             new SpacecraftState[] {
381                                                                 new SpacecraftState(osculatingOrbit,
382                                                                                     nominalMeanSpacecraftState.getAttitude(),
383                                                                                     nominalMeanSpacecraftState.getMass(),
384                                                                                     nominalMeanSpacecraftState.getAdditionalStatesValues(),
385                                                                                     nominalMeanSpacecraftState.getAdditionalStatesDerivatives())
386                                                             });
387 
388         // Normalized measurement matrix
389         final RealMatrix measurementMatrix = getMeasurementMatrix();
390 
391         // Number of estimated measurement parameters
392         final int nbMeas = getNumberSelectedMeasurementDrivers();
393 
394         // Number of estimated dynamic parameters (orbital + propagation)
395         final int nbDyn  = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers();
396 
397         // Covariance matrix
398         final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
399         final RealMatrix noiseP = covarianceMatrixProvider.getProcessNoiseMatrix(previousNominalMeanSpacecraftState, nominalMeanSpacecraftState);
400         noiseK.setSubMatrix(noiseP.getData(), 0, 0);
401         if (measurementProcessNoiseMatrix != null) {
402             final RealMatrix noiseM = measurementProcessNoiseMatrix.getProcessNoiseMatrix(previousNominalMeanSpacecraftState, nominalMeanSpacecraftState);
403             noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
404         }
405 
406         // Verify dimension
407         checkDimension(noiseK.getRowDimension(),
408                        builder.getOrbitalParametersDrivers(),
409                        builder.getPropagationParametersDrivers(),
410                        estimatedMeasurementsParameters);
411 
412         final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(noiseK);
413 
414         // Return
415         return new NonLinearEvolution(measurement.getTime(), predictedFilterCorrection, stm,
416                                       normalizedProcessNoise, measurementMatrix);
417     }
418 
419     /** {@inheritDoc} */
420     @Override
421     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
422                                     final RealMatrix innovationCovarianceMatrix) {
423 
424         // Apply the dynamic outlier filter, if it exists
425         applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
426         if (predictedMeasurement.getStatus() == EstimatedMeasurement.Status.REJECTED)  {
427             // set innovation to null to notify filter measurement is rejected
428             return null;
429         } else {
430             // Normalized innovation of the measurement (Nx1)
431             final double[] observed  = predictedMeasurement.getObservedMeasurement().getObservedValue();
432             final double[] estimated = predictedMeasurement.getEstimatedValue();
433             final double[] sigma     = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
434             final double[] residuals = new double[observed.length];
435 
436             for (int i = 0; i < observed.length; i++) {
437                 residuals[i] = (observed[i] - estimated[i]) / sigma[i];
438             }
439             return MatrixUtils.createRealVector(residuals);
440         }
441 
442     }
443 
444     /** Finalize estimation.
445      * @param observedMeasurement measurement that has just been processed
446      * @param estimate corrected estimate
447      */
448     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
449                                    final ProcessEstimate estimate) {
450         // Update the process estimate
451         correctedEstimate = estimate;
452         // Corrected filter correction
453         correctedFilterCorrection = estimate.getState();
454         // Update the previous nominal mean spacecraft state
455         previousNominalMeanSpacecraftState = nominalMeanSpacecraftState;
456         // Calculate the corrected osculating elements
457         final double[] osculating = computeOsculatingElements(correctedFilterCorrection);
458         final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngle(),
459                                                                             currentDate, nominalMeanSpacecraftState.getMu(),
460                                                                             nominalMeanSpacecraftState.getFrame());
461 
462         // Compute the corrected measurements
463         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
464                                                             currentMeasurementNumber,
465                                                             new SpacecraftState[] {
466                                                                 new SpacecraftState(osculatingOrbit,
467                                                                                     nominalMeanSpacecraftState.getAttitude(),
468                                                                                     nominalMeanSpacecraftState.getMass(),
469                                                                                     nominalMeanSpacecraftState.getAdditionalStatesValues(),
470                                                                                     nominalMeanSpacecraftState.getAdditionalStatesDerivatives())
471                                                             });
472     }
473 
474     /** Finalize estimation operations on the observation grid. */
475     public void finalizeOperationsObservationGrid() {
476         // Update parameters
477         updateParameters();
478     }
479 
480     /** {@inheritDoc} */
481     @Override
482     public ParameterDriversList getEstimatedOrbitalParameters() {
483         return estimatedOrbitalParameters;
484     }
485 
486     /** {@inheritDoc} */
487     @Override
488     public ParameterDriversList getEstimatedPropagationParameters() {
489         return estimatedPropagationParameters;
490     }
491 
492     /** {@inheritDoc} */
493     @Override
494     public ParameterDriversList getEstimatedMeasurementsParameters() {
495         return estimatedMeasurementsParameters;
496     }
497 
498     /** {@inheritDoc} */
499     @Override
500     public SpacecraftState[] getPredictedSpacecraftStates() {
501         return new SpacecraftState[] {nominalMeanSpacecraftState};
502     }
503 
504     /** {@inheritDoc} */
505     @Override
506     public SpacecraftState[] getCorrectedSpacecraftStates() {
507         return new SpacecraftState[] {getEstimatedPropagator().getInitialState()};
508     }
509 
510     /** {@inheritDoc} */
511     @Override
512     public RealVector getPhysicalEstimatedState() {
513         // Method {@link ParameterDriver#getValue()} is used to get
514         // the physical values of the state.
515         // The scales'array is used to get the size of the state vector
516         final RealVector physicalEstimatedState = new ArrayRealVector(scale.length);
517         int i = 0;
518         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
519             physicalEstimatedState.setEntry(i++, driver.getValue());
520         }
521         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
522             physicalEstimatedState.setEntry(i++, driver.getValue());
523         }
524         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
525             physicalEstimatedState.setEntry(i++, driver.getValue());
526         }
527 
528         return physicalEstimatedState;
529     }
530 
531     /** {@inheritDoc} */
532     @Override
533     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
534         // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it.
535         // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
536         // For each element [i,j] of P the corresponding normalized value is:
537         // Pn[i,j] = P[i,j] / (scale[i]*scale[j])
538         // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j]
539 
540         // Normalized covariance matrix
541         final RealMatrix normalizedP = correctedEstimate.getCovariance();
542 
543         // Initialize physical covariance matrix
544         final int nbParams = normalizedP.getRowDimension();
545         final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams);
546 
547         // Un-normalize the covairance matrix
548         for (int i = 0; i < nbParams; ++i) {
549             for (int j = 0; j < nbParams; ++j) {
550                 physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]);
551             }
552         }
553         return physicalP;
554     }
555 
556     /** {@inheritDoc} */
557     @Override
558     public RealMatrix getPhysicalStateTransitionMatrix() {
559         //  Un-normalize the state transition matrix (ฯ†) from Hipparchus and return it.
560         // ฯ† is an mxm matrix where m = nbOrb + nbPropag + nbMeas
561         // For each element [i,j] of normalized ฯ† (ฯ†n), the corresponding physical value is:
562         // ฯ†[i,j] = ฯ†n[i,j] * scale[i] / scale[j]
563 
564         // Normalized matrix
565         final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix();
566 
567         if (normalizedSTM == null) {
568             return null;
569         } else {
570             // Initialize physical matrix
571             final int nbParams = normalizedSTM.getRowDimension();
572             final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams);
573 
574             // Un-normalize the matrix
575             for (int i = 0; i < nbParams; ++i) {
576                 for (int j = 0; j < nbParams; ++j) {
577                     physicalSTM.setEntry(i, j,
578                                          normalizedSTM.getEntry(i, j) * scale[i] / scale[j]);
579                 }
580             }
581             return physicalSTM;
582         }
583     }
584 
585     /** {@inheritDoc} */
586     @Override
587     public RealMatrix getPhysicalMeasurementJacobian() {
588         // Un-normalize the measurement matrix (H) from Hipparchus and return it.
589         // H is an nxm matrix where:
590         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
591         //  - n is the size of the measurement being processed by the filter
592         // For each element [i,j] of normalized H (Hn) the corresponding physical value is:
593         // H[i,j] = Hn[i,j] * ฯƒ[i] / scale[j]
594 
595         // Normalized matrix
596         final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian();
597 
598         if (normalizedH == null) {
599             return null;
600         } else {
601             // Get current measurement sigmas
602             final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
603 
604             // Initialize physical matrix
605             final int nbLine = normalizedH.getRowDimension();
606             final int nbCol  = normalizedH.getColumnDimension();
607             final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol);
608 
609             // Un-normalize the matrix
610             for (int i = 0; i < nbLine; ++i) {
611                 for (int j = 0; j < nbCol; ++j) {
612                     physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]);
613                 }
614             }
615             return physicalH;
616         }
617     }
618 
619     /** {@inheritDoc} */
620     @Override
621     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
622         // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it.
623         // S is an nxn matrix where n is the size of the measurement being processed by the filter
624         // For each element [i,j] of normalized S (Sn) the corresponding physical value is:
625         // S[i,j] = Sn[i,j] * ฯƒ[i] * ฯƒ[j]
626 
627         // Normalized matrix
628         final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance();
629 
630         if (normalizedS == null) {
631             return null;
632         } else {
633             // Get current measurement sigmas
634             final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
635 
636             // Initialize physical matrix
637             final int nbMeas = sigmas.length;
638             final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas);
639 
640             // Un-normalize the matrix
641             for (int i = 0; i < nbMeas; ++i) {
642                 for (int j = 0; j < nbMeas; ++j) {
643                     physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] *   sigmas[j]);
644                 }
645             }
646             return physicalS;
647         }
648     }
649 
650     /** {@inheritDoc} */
651     @Override
652     public RealMatrix getPhysicalKalmanGain() {
653         // Un-normalize the Kalman gain (K) from Hipparchus and return it.
654         // K is an mxn matrix where:
655         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
656         //  - n is the size of the measurement being processed by the filter
657         // For each element [i,j] of normalized K (Kn) the corresponding physical value is:
658         // K[i,j] = Kn[i,j] * scale[i] / ฯƒ[j]
659 
660         // Normalized matrix
661         final RealMatrix normalizedK = correctedEstimate.getKalmanGain();
662 
663         if (normalizedK == null) {
664             return null;
665         } else {
666             // Get current measurement sigmas
667             final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
668 
669             // Initialize physical matrix
670             final int nbLine = normalizedK.getRowDimension();
671             final int nbCol  = normalizedK.getColumnDimension();
672             final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol);
673 
674             // Un-normalize the matrix
675             for (int i = 0; i < nbLine; ++i) {
676                 for (int j = 0; j < nbCol; ++j) {
677                     physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]);
678                 }
679             }
680             return physicalK;
681         }
682     }
683 
684     /** {@inheritDoc} */
685     @Override
686     public int getCurrentMeasurementNumber() {
687         return currentMeasurementNumber;
688     }
689 
690     /** {@inheritDoc} */
691     @Override
692     public AbsoluteDate getCurrentDate() {
693         return currentDate;
694     }
695 
696     /** {@inheritDoc} */
697     @Override
698     public EstimatedMeasurement<?> getPredictedMeasurement() {
699         return predictedMeasurement;
700     }
701 
702     /** {@inheritDoc} */
703     @Override
704     public EstimatedMeasurement<?> getCorrectedMeasurement() {
705         return correctedMeasurement;
706     }
707 
708     /** Update the nominal spacecraft state.
709      * @param nominal nominal spacecraft state
710      */
711     public void updateNominalSpacecraftState(final SpacecraftState nominal) {
712         this.nominalMeanSpacecraftState = nominal;
713         // Update the builder with the nominal mean elements orbit
714         builder.resetOrbit(nominal.getOrbit(), PropagationType.MEAN);
715     }
716 
717     /** Update the reference trajectories using the propagator as input.
718      * @param propagator The new propagator to use
719      */
720     public void updateReferenceTrajectory(final DSSTPropagator propagator) {
721 
722         dsstPropagator = propagator;
723 
724         // Equation name
725         final String equationName = SemiAnalyticalKalmanEstimator.class.getName() + "-derivatives-";
726 
727         // Mean state
728         final SpacecraftState meanState = dsstPropagator.initialIsOsculating() ?
729                        DSSTPropagator.computeMeanState(dsstPropagator.getInitialState(), dsstPropagator.getAttitudeProvider(), dsstPropagator.getAllForceModels()) :
730                        dsstPropagator.getInitialState();
731 
732         // Update the jacobian harvester
733         dsstPropagator.setInitialState(meanState, PropagationType.MEAN);
734         harvester = (DSSTHarvester) dsstPropagator.setupMatricesComputation(equationName, null, null);
735 
736     }
737 
738     /** Update the DSST short periodic terms.
739      * @param state current mean state
740      */
741     public void updateShortPeriods(final SpacecraftState state) {
742         // Loop on DSST force models
743         for (final DSSTForceModel model : builder.getAllForceModels()) {
744             model.updateShortPeriodTerms(model.getParameters(), state);
745         }
746         harvester.updateFieldShortPeriodTerms(state);
747     }
748 
749     /** Initialize the short periodic terms for the Kalman Filter.
750      * @param meanState mean state for auxiliary elements
751      */
752     public void initializeShortPeriodicTerms(final SpacecraftState meanState) {
753         final List<ShortPeriodTerms> shortPeriodTerms = new ArrayList<ShortPeriodTerms>();
754         for (final DSSTForceModel force :  builder.getAllForceModels()) {
755             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(new AuxiliaryElements(meanState.getOrbit(), 1), PropagationType.OSCULATING, force.getParameters()));
756         }
757         dsstPropagator.setShortPeriodTerms(shortPeriodTerms);
758     }
759 
760     /** Get the normalized state transition matrix (STM) from previous point to current point.
761      * The STM contains the partial derivatives of current state with respect to previous state.
762      * The  STM is an mxm matrix where m is the size of the state vector.
763      * m = nbOrb + nbPropag + nbMeas
764      * @return the normalized error state transition matrix
765      */
766     private RealMatrix getErrorStateTransitionMatrix() {
767 
768         /* The state transition matrix is obtained as follows, with:
769          *  - Phi(k, k-1) : Transitional orbital matrix
770          *  - Psi(k, k-1) : Transitional propagation parameters matrix
771          *
772          *       |             |             |   .    |
773          *       | Phi(k, k-1) | Psi(k, k-1) | ..0..  |
774          *       |             |             |   .    |
775          *       |-------------|-------------|--------|
776          *       |      .      |    1 0 0    |   .    |
777          * STM = |    ..0..    |    0 1 0    | ..0..  |
778          *       |      .      |    0 0 1    |   .    |
779          *       |-------------|-------------|--------|
780          *       |      .      |      .      | 1 0 0..|
781          *       |    ..0..    |    ..0..    | 0 1 0..|
782          *       |      .      |      .      | 0 0 1..|
783          */
784 
785         // Initialize to the proper size identity matrix
786         final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension());
787 
788         // Derivatives of the state vector with respect to initial state vector
789         final int nbOrb = getNumberSelectedOrbitalDrivers();
790         final RealMatrix dYdY0 = harvester.getB2(nominalMeanSpacecraftState);
791 
792         // Calculate transitional orbital matrix (See Ref [1], Eq. 3.4a)
793         final RealMatrix phi = dYdY0.multiply(phiS);
794 
795         // Fill the state transition matrix with the orbital drivers
796         final List<DelegatingDriver> drivers = builder.getOrbitalParametersDrivers().getDrivers();
797         for (int i = 0; i < nbOrb; ++i) {
798             if (drivers.get(i).isSelected()) {
799                 int jOrb = 0;
800                 for (int j = 0; j < nbOrb; ++j) {
801                     if (drivers.get(j).isSelected()) {
802                         stm.setEntry(i, jOrb++, phi.getEntry(i, j));
803                     }
804                 }
805             }
806         }
807 
808         // Update PhiS
809         phiS = new QRDecomposition(dYdY0).getSolver().getInverse();
810 
811         // Derivatives of the state vector with respect to propagation parameters
812         if (psiS != null) {
813 
814             final int nbProp = getNumberSelectedPropagationDrivers();
815             final RealMatrix dYdPp = harvester.getB3(nominalMeanSpacecraftState);
816 
817             // Calculate transitional parameters matrix (See Ref [1], Eq. 3.4b)
818             final RealMatrix psi = dYdPp.subtract(phi.multiply(psiS));
819 
820             // Fill 1st row, 2nd column (dY/dPp)
821             for (int i = 0; i < nbOrb; ++i) {
822                 for (int j = 0; j < nbProp; ++j) {
823                     stm.setEntry(i, j + nbOrb, psi.getEntry(i, j));
824                 }
825             }
826 
827             // Update PsiS
828             psiS = dYdPp;
829 
830         }
831 
832         // Normalization of the STM
833         // normalized(STM)ij = STMij*Sj/Si
834         for (int i = 0; i < scale.length; i++) {
835             for (int j = 0; j < scale.length; j++ ) {
836                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
837             }
838         }
839 
840         // Return the error state transition matrix
841         return stm;
842 
843     }
844 
845     /** Get the normalized measurement matrix H.
846      * H contains the partial derivatives of the measurement with respect to the state.
847      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
848      * @return the normalized measurement matrix H
849      */
850     private RealMatrix getMeasurementMatrix() {
851 
852         // Observed measurement characteristics
853         final SpacecraftState        evaluationState     = predictedMeasurement.getStates()[0];
854         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
855         final double[] sigma  = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
856 
857         // Initialize measurement matrix H: nxm
858         // n: Number of measurements in current measurement
859         // m: State vector size
860         final RealMatrix measurementMatrix = MatrixUtils.
861                 createRealMatrix(observedMeasurement.getDimension(),
862                                  correctedEstimate.getState().getDimension());
863 
864         // Predicted orbit
865         final Orbit predictedOrbit = evaluationState.getOrbit();
866 
867         // Measurement matrix's columns related to orbital and propagation parameters
868         // ----------------------------------------------------------
869 
870         // Partial derivatives of the current Cartesian coordinates with respect to current orbital state
871         final int nbOrb  = getNumberSelectedOrbitalDrivers();
872         final int nbProp = getNumberSelectedPropagationDrivers();
873         final double[][] aCY = new double[nbOrb][nbOrb];
874         predictedOrbit.getJacobianWrtParameters(builder.getPositionAngle(), aCY);
875         final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);
876 
877         // Jacobian of the measurement with respect to current Cartesian coordinates
878         final RealMatrix dMdC = new Array2DRowRealMatrix(predictedMeasurement.getStateDerivatives(0), false);
879 
880         // Jacobian of the measurement with respect to current orbital state
881         RealMatrix dMdY = dMdC.multiply(dCdY);
882 
883         // Compute factor dShortPeriod_dMeanState = I+B1 | B4
884         final RealMatrix IpB1B4 = MatrixUtils.createRealMatrix(nbOrb, nbOrb + nbProp);
885 
886         // B1
887         final RealMatrix B1 = harvester.getB1();
888 
889         // I + B1
890         final RealMatrix I = MatrixUtils.createRealIdentityMatrix(nbOrb);
891         final RealMatrix IpB1 = I.add(B1);
892         IpB1B4.setSubMatrix(IpB1.getData(), 0, 0);
893 
894         // If there are not propagation parameters, B4 is null
895         if (psiS != null) {
896             final RealMatrix B4 = harvester.getB4();
897             IpB1B4.setSubMatrix(B4.getData(), 0, nbOrb);
898         }
899 
900         // Ref [1], Eq. 3.10
901         dMdY = dMdY.multiply(IpB1B4);
902 
903         for (int i = 0; i < dMdY.getRowDimension(); i++) {
904             for (int j = 0; j < nbOrb; j++) {
905                 final double driverScale = builder.getOrbitalParametersDrivers().getDrivers().get(j).getScale();
906                 measurementMatrix.setEntry(i, j, dMdY.getEntry(i, j) / sigma[i] * driverScale);
907             }
908             for (int j = 0; j < nbProp; j++) {
909                 final double driverScale = estimatedPropagationParameters.getDrivers().get(j).getScale();
910                 measurementMatrix.setEntry(i, j + nbOrb,
911                                            dMdY.getEntry(i, j + nbOrb) / sigma[i] * driverScale);
912             }
913         }
914 
915         // Normalized measurement matrix's columns related to measurement parameters
916         // --------------------------------------------------------------
917 
918         // Jacobian of the measurement with respect to measurement parameters
919         // Gather the measurement parameters linked to current measurement
920         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
921             if (driver.isSelected()) {
922                 // Derivatives of current measurement w/r to selected measurement parameter
923                 final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);
924 
925                 // Check that the measurement parameter is managed by the filter
926                 if (measurementParameterColumns.get(driver.getName()) != null) {
927                     // Column of the driver in the measurement matrix
928                     final int driverColumn = measurementParameterColumns.get(driver.getName());
929 
930                     // Fill the corresponding indexes of the measurement matrix
931                     for (int i = 0; i < aMPm.length; ++i) {
932                         measurementMatrix.setEntry(i, driverColumn, aMPm[i] / sigma[i] * driver.getScale());
933                     }
934                 }
935             }
936         }
937 
938         return measurementMatrix;
939     }
940 
941     /** Predict the filter correction for the new observation.
942      * @param stm normalized state transition matrix
943      * @return the predicted filter correction for the new observation
944      */
945     private RealVector predictFilterCorrection(final RealMatrix stm) {
946         // Ref [1], Eq. 3.5a and 3.5b
947         return stm.operate(correctedFilterCorrection);
948     }
949 
950     /** Compute the predicted osculating elements.
951      * @param filterCorrection kalman filter correction
952      * @return the predicted osculating element
953      */
954     private double[] computeOsculatingElements(final RealVector filterCorrection) {
955 
956         // Number of estimated orbital parameters
957         final int nbOrb = getNumberSelectedOrbitalDrivers();
958 
959         // B1
960         final RealMatrix B1 = harvester.getB1();
961 
962         // Short periodic terms
963         final double[] shortPeriodTerms = dsstPropagator.getShortPeriodTermsValue(nominalMeanSpacecraftState);
964 
965         // Physical filter correction
966         final RealVector physicalFilterCorrection = MatrixUtils.createRealVector(nbOrb);
967         for (int index = 0; index < nbOrb; index++) {
968             physicalFilterCorrection.addToEntry(index, filterCorrection.getEntry(index) * scale[index]);
969         }
970 
971         // B1 * physicalCorrection
972         final RealVector B1Correction = B1.operate(physicalFilterCorrection);
973 
974         // Nominal mean elements
975         final double[] nominalMeanElements = new double[nbOrb];
976         OrbitType.EQUINOCTIAL.mapOrbitToArray(nominalMeanSpacecraftState.getOrbit(), builder.getPositionAngle(), nominalMeanElements, null);
977 
978         // Ref [1] Eq. 3.6
979         final double[] osculatingElements = new double[nbOrb];
980         for (int i = 0; i < nbOrb; i++) {
981             osculatingElements[i] = nominalMeanElements[i] +
982                                     physicalFilterCorrection.getEntry(i) +
983                                     shortPeriodTerms[i] +
984                                     B1Correction.getEntry(i);
985         }
986 
987         // Return
988         return osculatingElements;
989 
990     }
991 
992     /** Analytical computation of derivatives.
993      * This method allow to compute analytical derivatives.
994      * @param state mean state used to calculate short period perturbations
995      */
996     private void analyticalDerivativeComputations(final SpacecraftState state) {
997         harvester.setReferenceState(state);
998     }
999 
1000     /** Normalize a covariance matrix.
1001      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
1002      * For each element [i,j] of P the corresponding normalized value is:
1003      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
1004      * @param physicalCovarianceMatrix The "physical" covariance matrix in input
1005      * @return the normalized covariance matrix
1006      */
1007     private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) {
1008 
1009         // Initialize output matrix
1010         final int nbParams = physicalCovarianceMatrix.getRowDimension();
1011         final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);
1012 
1013         // Normalize the state matrix
1014         for (int i = 0; i < nbParams; ++i) {
1015             for (int j = 0; j < nbParams; ++j) {
1016                 normalizedCovarianceMatrix.setEntry(i, j,
1017                                                     physicalCovarianceMatrix.getEntry(i, j) /
1018                                                     (scale[i] * scale[j]));
1019             }
1020         }
1021         return normalizedCovarianceMatrix;
1022     }
1023 
1024     /** Check dimension.
1025      * @param dimension dimension to check
1026      * @param orbitalParameters orbital parameters
1027      * @param propagationParameters propagation parameters
1028      * @param measurementParameters measurements parameters
1029      */
1030     private void checkDimension(final int dimension,
1031                                 final ParameterDriversList orbitalParameters,
1032                                 final ParameterDriversList propagationParameters,
1033                                 final ParameterDriversList measurementParameters) {
1034 
1035         // count parameters, taking care of counting all orbital parameters
1036         // regardless of them being estimated or not
1037         int requiredDimension = orbitalParameters.getNbParams();
1038         for (final ParameterDriver driver : propagationParameters.getDrivers()) {
1039             if (driver.isSelected()) {
1040                 ++requiredDimension;
1041             }
1042         }
1043         for (final ParameterDriver driver : measurementParameters.getDrivers()) {
1044             if (driver.isSelected()) {
1045                 ++requiredDimension;
1046             }
1047         }
1048 
1049         if (dimension != requiredDimension) {
1050             // there is a problem, set up an explicit error message
1051             final StringBuilder strBuilder = new StringBuilder();
1052             for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
1053                 if (strBuilder.length() > 0) {
1054                     strBuilder.append(", ");
1055                 }
1056                 strBuilder.append(driver.getName());
1057             }
1058             for (final ParameterDriver driver : propagationParameters.getDrivers()) {
1059                 if (driver.isSelected()) {
1060                     strBuilder.append(driver.getName());
1061                 }
1062             }
1063             for (final ParameterDriver driver : measurementParameters.getDrivers()) {
1064                 if (driver.isSelected()) {
1065                     strBuilder.append(driver.getName());
1066                 }
1067             }
1068             throw new OrekitException(OrekitMessages.DIMENSION_INCONSISTENT_WITH_PARAMETERS,
1069                                       dimension, strBuilder.toString());
1070         }
1071 
1072     }
1073 
1074     /** Set and apply a dynamic outlier filter on a measurement.<p>
1075      * Loop on the modifiers to see if a dynamic outlier filter needs to be applied.<p>
1076      * Compute the sigma array using the matrix in input and set the filter.<p>
1077      * Apply the filter by calling the modify method on the estimated measurement.<p>
1078      * Reset the filter.
1079      * @param measurement measurement to filter
1080      * @param innovationCovarianceMatrix So called innovation covariance matrix S, with:<p>
1081      *        S = H.Ppred.Ht + R<p>
1082      *        Where:<p>
1083      *         - H is the normalized measurement matrix (Ht its transpose)<p>
1084      *         - Ppred is the normalized predicted covariance matrix<p>
1085      *         - R is the normalized measurement noise matrix
1086      * @param <T> the type of measurement
1087      */
1088     private <T extends ObservedMeasurement<T>> void applyDynamicOutlierFilter(final EstimatedMeasurement<T> measurement,
1089                                                                               final RealMatrix innovationCovarianceMatrix) {
1090 
1091         // Observed measurement associated to the predicted measurement
1092         final ObservedMeasurement<T> observedMeasurement = measurement.getObservedMeasurement();
1093 
1094         // Check if a dynamic filter was added to the measurement
1095         // If so, update its sigma value and apply it
1096         for (EstimationModifier<T> modifier : observedMeasurement.getModifiers()) {
1097             if (modifier instanceof DynamicOutlierFilter<?>) {
1098                 final DynamicOutlierFilter<T> dynamicOutlierFilter = (DynamicOutlierFilter<T>) modifier;
1099 
1100                 // Initialize the values of the sigma array used in the dynamic filter
1101                 final double[] sigmaDynamic     = new double[innovationCovarianceMatrix.getColumnDimension()];
1102                 final double[] sigmaMeasurement = observedMeasurement.getTheoreticalStandardDeviation();
1103 
1104                 // Set the sigma value for each element of the measurement
1105                 // Here we do use the value suggested by David A. Vallado (see [1]ยง10.6):
1106                 // sigmaDynamic[i] = sqrt(diag(S))*sigma[i]
1107                 // With S = H.Ppred.Ht + R
1108                 // Where:
1109                 //  - S is the measurement error matrix in input
1110                 //  - H is the normalized measurement matrix (Ht its transpose)
1111                 //  - Ppred is the normalized predicted covariance matrix
1112                 //  - R is the normalized measurement noise matrix
1113                 //  - sigma[i] is the theoretical standard deviation of the ith component of the measurement.
1114                 //    It is used here to un-normalize the value before it is filtered
1115                 for (int i = 0; i < sigmaDynamic.length; i++) {
1116                     sigmaDynamic[i] = FastMath.sqrt(innovationCovarianceMatrix.getEntry(i, i)) * sigmaMeasurement[i];
1117                 }
1118                 dynamicOutlierFilter.setSigma(sigmaDynamic);
1119 
1120                 // Apply the modifier on the estimated measurement
1121                 modifier.modify(measurement);
1122 
1123                 // Re-initialize the value of the filter for the next measurement of the same type
1124                 dynamicOutlierFilter.setSigma(null);
1125             }
1126         }
1127     }
1128 
1129     /** Get the number of estimated orbital parameters.
1130      * @return the number of estimated orbital parameters
1131      */
1132     private int getNumberSelectedOrbitalDrivers() {
1133         return estimatedOrbitalParameters.getNbParams();
1134     }
1135 
1136     /** Get the number of estimated propagation parameters.
1137      * @return the number of estimated propagation parameters
1138      */
1139     private int getNumberSelectedPropagationDrivers() {
1140         return estimatedPropagationParameters.getNbParams();
1141     }
1142 
1143     /** Get the number of estimated measurement parameters.
1144      * @return the number of estimated measurement parameters
1145      */
1146     private int getNumberSelectedMeasurementDrivers() {
1147         return estimatedMeasurementsParameters.getNbParams();
1148     }
1149 
1150     /** Update the estimated parameters after the correction phase of the filter.
1151      * The min/max allowed values are handled by the parameter themselves.
1152      */
1153     private void updateParameters() {
1154         final RealVector correctedState = correctedEstimate.getState();
1155         int i = 0;
1156         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
1157             // let the parameter handle min/max clipping
1158             driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++));
1159         }
1160         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
1161             // let the parameter handle min/max clipping
1162             driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++));
1163         }
1164         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
1165             // let the parameter handle min/max clipping
1166             driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++));
1167         }
1168     }
1169 
1170 }