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