1   /* Copyright 2002-2024 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.estimation.sequential;
18  
19  import org.hipparchus.filtering.kalman.ProcessEstimate;
20  import org.hipparchus.filtering.kalman.unscented.UnscentedEvolution;
21  import org.hipparchus.filtering.kalman.unscented.UnscentedKalmanFilter;
22  import org.hipparchus.filtering.kalman.unscented.UnscentedProcess;
23  import org.hipparchus.linear.ArrayRealVector;
24  import org.hipparchus.linear.MatrixUtils;
25  import org.hipparchus.linear.RealMatrix;
26  import org.hipparchus.linear.RealVector;
27  import org.hipparchus.util.FastMath;
28  import org.orekit.estimation.measurements.EstimatedMeasurement;
29  import org.orekit.estimation.measurements.EstimatedMeasurementBase;
30  import org.orekit.estimation.measurements.ObservedMeasurement;
31  import org.orekit.orbits.Orbit;
32  import org.orekit.orbits.OrbitType;
33  import org.orekit.orbits.PositionAngleType;
34  import org.orekit.propagation.PropagationType;
35  import org.orekit.propagation.SpacecraftState;
36  import org.orekit.propagation.conversion.DSSTPropagatorBuilder;
37  import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
38  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
39  import org.orekit.propagation.semianalytical.dsst.forces.ShortPeriodTerms;
40  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
41  import org.orekit.time.AbsoluteDate;
42  import org.orekit.time.ChronologicalComparator;
43  import org.orekit.utils.ParameterDriver;
44  import org.orekit.utils.ParameterDriversList;
45  import org.orekit.utils.ParameterDriversList.DelegatingDriver;
46  
47  import java.util.ArrayList;
48  import java.util.Comparator;
49  import java.util.List;
50  
51  /** Class defining the process model dynamics to use with a {@link SemiAnalyticalUnscentedKalmanEstimator}.
52   * @author Gaƫtan Pierre
53   * @author Bryan Cazabonne
54   * @since 11.3
55   */
56  public class SemiAnalyticalUnscentedKalmanModel implements KalmanEstimation, UnscentedProcess<MeasurementDecorator>, SemiAnalyticalProcess {
57  
58      /** Initial builder for propagator. */
59      private final DSSTPropagatorBuilder builder;
60  
61      /** Estimated orbital parameters. */
62      private final ParameterDriversList estimatedOrbitalParameters;
63  
64      /** Estimated propagation parameters. */
65      private final ParameterDriversList estimatedPropagationParameters;
66  
67      /** Estimated measurements parameters. */
68      private final ParameterDriversList estimatedMeasurementsParameters;
69  
70      /** Provider for covariance matrice. */
71      private final CovarianceMatrixProvider covarianceMatrixProvider;
72  
73      /** Process noise matrix provider for measurement parameters. */
74      private final CovarianceMatrixProvider measurementProcessNoiseMatrix;
75  
76      /** Position angle type used during orbit determination. */
77      private final PositionAngleType angleType;
78  
79      /** Orbit type used during orbit determination. */
80      private final OrbitType orbitType;
81  
82      /** Current corrected estimate. */
83      private ProcessEstimate correctedEstimate;
84  
85      /** Observer to retrieve current estimation info. */
86      private KalmanObserver observer;
87  
88      /** Current number of measurement. */
89      private int currentMeasurementNumber;
90  
91      /** Current date. */
92      private AbsoluteDate currentDate;
93  
94      /** Nominal mean spacecraft state. */
95      private SpacecraftState nominalMeanSpacecraftState;
96  
97      /** Previous nominal mean spacecraft state. */
98      private SpacecraftState previousNominalMeanSpacecraftState;
99  
100     /** Predicted osculating spacecraft state. */
101     private SpacecraftState predictedSpacecraftState;
102 
103     /** Corrected mean spacecraft state. */
104     private SpacecraftState correctedSpacecraftState;
105 
106     /** Predicted measurement. */
107     private EstimatedMeasurement<?> predictedMeasurement;
108 
109     /** Corrected measurement. */
110     private EstimatedMeasurement<?> correctedMeasurement;
111 
112     /** Predicted mean element filter correction. */
113     private RealVector predictedFilterCorrection;
114 
115     /** Corrected mean element filter correction. */
116     private RealVector correctedFilterCorrection;
117 
118     /** Propagators for the reference trajectories, up to current date. */
119     private DSSTPropagator dsstPropagator;
120 
121     /** Short period terms for the nominal mean spacecraft state. */
122     private RealVector shortPeriodicTerms;
123 
124     /** Unscented Kalman process model constructor (package private).
125      * @param propagatorBuilder propagators builders used to evaluate the orbits.
126      * @param covarianceMatrixProvider provider for covariance matrix
127      * @param estimatedMeasurementParameters measurement parameters to estimate
128      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
129      */
130     protected SemiAnalyticalUnscentedKalmanModel(final DSSTPropagatorBuilder propagatorBuilder,
131                                                  final CovarianceMatrixProvider covarianceMatrixProvider,
132                                                  final ParameterDriversList estimatedMeasurementParameters,
133                                                  final CovarianceMatrixProvider measurementProcessNoiseMatrix) {
134 
135         this.builder                         = propagatorBuilder;
136         this.angleType                       = propagatorBuilder.getPositionAngleType();
137         this.orbitType                       = propagatorBuilder.getOrbitType();
138         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
139         this.currentMeasurementNumber        = 0;
140         this.currentDate                     = propagatorBuilder.getInitialOrbitDate();
141         this.covarianceMatrixProvider        = covarianceMatrixProvider;
142         this.measurementProcessNoiseMatrix   = measurementProcessNoiseMatrix;
143 
144         // Number of estimated parameters
145         int columns = 0;
146 
147         // Set estimated orbital parameters
148         this.estimatedOrbitalParameters = new ParameterDriversList();
149         for (final ParameterDriver driver : propagatorBuilder.getOrbitalParametersDrivers().getDrivers()) {
150 
151             // Verify if the driver reference date has been set
152             if (driver.getReferenceDate() == null) {
153                 driver.setReferenceDate(currentDate);
154             }
155 
156             // Verify if the driver is selected
157             if (driver.isSelected()) {
158                 estimatedOrbitalParameters.add(driver);
159                 columns++;
160             }
161 
162         }
163 
164         // Set estimated propagation parameters
165         this.estimatedPropagationParameters = new ParameterDriversList();
166         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
167         for (final ParameterDriver driver : propagatorBuilder.getPropagationParametersDrivers().getDrivers()) {
168 
169             // Verify if the driver reference date has been set
170             if (driver.getReferenceDate() == null) {
171                 driver.setReferenceDate(currentDate);
172             }
173 
174             // Verify if the driver is selected
175             if (driver.isSelected()) {
176                 estimatedPropagationParameters.add(driver);
177                 final String driverName = driver.getName();
178                 // Add the driver name if it has not been added yet
179                 if (!estimatedPropagationParametersNames.contains(driverName)) {
180                     estimatedPropagationParametersNames.add(driverName);
181                     ++columns;
182                 }
183             }
184 
185         }
186         estimatedPropagationParametersNames.sort(Comparator.naturalOrder());
187 
188         // Set the estimated measurement parameters
189         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
190             if (parameter.getReferenceDate() == null) {
191                 parameter.setReferenceDate(currentDate);
192             }
193             ++columns;
194         }
195 
196         // Number of estimated measurement parameters
197         final int nbMeas = estimatedMeasurementParameters.getNbParams();
198 
199         // Number of estimated dynamic parameters (orbital + propagation)
200         final int nbDyn  = estimatedOrbitalParameters.getNbParams() +
201                            estimatedPropagationParameters.getNbParams();
202 
203         // Build the reference propagator
204         this.dsstPropagator = getEstimatedPropagator();
205         final SpacecraftState meanState = dsstPropagator.initialIsOsculating() ?
206                          DSSTPropagator.computeMeanState(dsstPropagator.getInitialState(), dsstPropagator.getAttitudeProvider(), dsstPropagator.getAllForceModels()) :
207                          dsstPropagator.getInitialState();
208         this.nominalMeanSpacecraftState         = meanState;
209         this.predictedSpacecraftState           = meanState;
210         this.correctedSpacecraftState           = predictedSpacecraftState;
211         this.previousNominalMeanSpacecraftState = nominalMeanSpacecraftState;
212 
213         // Initialize the estimated mean element filter correction
214         this.predictedFilterCorrection = MatrixUtils.createRealVector(columns);
215         this.correctedFilterCorrection = predictedFilterCorrection;
216 
217         // Covariance matrix
218         final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
219         final RealMatrix noiseP = covarianceMatrixProvider.getInitialCovarianceMatrix(nominalMeanSpacecraftState);
220         noiseK.setSubMatrix(noiseP.getData(), 0, 0);
221         if (measurementProcessNoiseMatrix != null) {
222             final RealMatrix noiseM = measurementProcessNoiseMatrix.getInitialCovarianceMatrix(nominalMeanSpacecraftState);
223             noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
224         }
225 
226         KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(),
227                                            propagatorBuilder.getOrbitalParametersDrivers(),
228                                            propagatorBuilder.getPropagationParametersDrivers(),
229                                            estimatedMeasurementsParameters);
230 
231         // Initialize corrected estimate
232         this.correctedEstimate = new ProcessEstimate(0.0, correctedFilterCorrection, noiseK);
233 
234     }
235 
236     /** {@inheritDoc} */
237     @Override
238     public KalmanObserver getObserver() {
239         return observer;
240     }
241 
242     /** Set the observer.
243      * @param observer the observer
244      */
245     public void setObserver(final KalmanObserver observer) {
246         this.observer = observer;
247     }
248 
249     /** Get the current corrected estimate.
250      * <p>
251      * For the Unscented Semi-analytical Kalman Filter
252      * it corresponds to the corrected filter correction.
253      * In other words, it doesn't represent an orbital state.
254      * </p>
255      * @return current corrected estimate
256      */
257     public ProcessEstimate getEstimate() {
258         return correctedEstimate;
259     }
260 
261     /** Process measurements.
262      * @param observedMeasurements the list of measurements to process
263      * @param filter Unscented Kalman Filter
264      * @return estimated propagator
265      */
266     public DSSTPropagator processMeasurements(final List<ObservedMeasurement<?>> observedMeasurements,
267                                               final UnscentedKalmanFilter<MeasurementDecorator> filter) {
268 
269         // Sort the measurement
270         observedMeasurements.sort(new ChronologicalComparator());
271         final AbsoluteDate tStart             = observedMeasurements.get(0).getDate();
272         final AbsoluteDate tEnd               = observedMeasurements.get(observedMeasurements.size() - 1).getDate();
273         final double       overshootTimeRange = FastMath.nextAfter(tEnd.durationFrom(tStart),
274                                                 Double.POSITIVE_INFINITY);
275 
276         // Initialize step handler and set it to a parallelized propagator
277         final SemiAnalyticalMeasurementHandler  stepHandler = new SemiAnalyticalMeasurementHandler(this, filter, observedMeasurements, builder.getInitialOrbitDate(), true);
278         dsstPropagator.getMultiplexer().add(stepHandler);
279         dsstPropagator.propagate(tStart, tStart.shiftedBy(overshootTimeRange));
280 
281         // Return the last estimated propagator
282         return getEstimatedPropagator();
283 
284     }
285 
286     /** Get the propagator estimated with the values set in the propagator builder.
287      * @return propagator based on the current values in the builder
288      */
289     public DSSTPropagator getEstimatedPropagator() {
290         // Return propagator built with current instantiation of the propagator builder
291         return (DSSTPropagator) builder.buildPropagator();
292     }
293 
294     /** {@inheritDoc} */
295     @Override
296     public UnscentedEvolution getEvolution(final double previousTime, final RealVector[] sigmaPoints,
297                                            final MeasurementDecorator measurement) {
298 
299         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
300         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
301         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
302             if (driver.getReferenceDate() == null) {
303                 driver.setReferenceDate(builder.getInitialOrbitDate());
304             }
305         }
306 
307         // Increment measurement number
308         ++currentMeasurementNumber;
309 
310         // Update the current date
311         currentDate = measurement.getObservedMeasurement().getDate();
312 
313         // STM for the prediction of the filter correction
314         final RealMatrix stm = getStm();
315 
316         // Predicted states
317         final RealVector[] predictedStates = new RealVector[sigmaPoints.length];
318         for (int k = 0; k < sigmaPoints.length; ++k) {
319             // Predict filter correction for the current sigma point
320             final RealVector predicted = stm.operate(sigmaPoints[k]);
321             predictedStates[k] = predicted;
322         }
323 
324         // Number of estimated measurement parameters
325         final int nbMeas = getNumberSelectedMeasurementDrivers();
326 
327         // Number of estimated dynamic parameters (orbital + propagation)
328         final int nbDyn  = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers();
329 
330         // Covariance matrix
331         final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
332         final RealMatrix noiseP = covarianceMatrixProvider.getProcessNoiseMatrix(previousNominalMeanSpacecraftState, nominalMeanSpacecraftState);
333         noiseK.setSubMatrix(noiseP.getData(), 0, 0);
334         if (measurementProcessNoiseMatrix != null) {
335             final RealMatrix noiseM = measurementProcessNoiseMatrix.getProcessNoiseMatrix(previousNominalMeanSpacecraftState, nominalMeanSpacecraftState);
336             noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
337         }
338 
339         // Verify dimension
340         KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(),
341                                            builder.getOrbitalParametersDrivers(),
342                                            builder.getPropagationParametersDrivers(),
343                                            estimatedMeasurementsParameters);
344 
345         // Return
346         return new UnscentedEvolution(measurement.getTime(), predictedStates, noiseK);
347 
348     }
349 
350     /** {@inheritDoc} */
351     @Override
352     public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPoints, final MeasurementDecorator measurement) {
353 
354         // Observed measurement
355         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
356 
357         // Initialize arrays of predicted states and measurements
358         final RealVector[] predictedMeasurements = new RealVector[predictedSigmaPoints.length];
359 
360         // Loop on sigma points
361         for (int k = 0; k < predictedSigmaPoints.length; ++k) {
362 
363             // Calculate the predicted osculating elements for the current mean state
364             final RealVector osculating = computeOsculatingElements(predictedSigmaPoints[k], nominalMeanSpacecraftState, shortPeriodicTerms);
365             final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, angleType,
366                                                                     currentDate, builder.getMu(), builder.getFrame());
367 
368             // Then, estimate the measurement
369             final EstimatedMeasurement<?> estimated = estimateMeasurement(observedMeasurement, currentMeasurementNumber,
370                 new SpacecraftState[] { new SpacecraftState(osculatingOrbit) });
371             predictedMeasurements[k] = new ArrayRealVector(estimated.getEstimatedValue());
372 
373         }
374 
375         // Return
376         return predictedMeasurements;
377 
378     }
379 
380     /** {@inheritDoc} */
381     @Override
382     public RealVector getInnovation(final MeasurementDecorator measurement, final RealVector predictedMeas,
383                                     final RealVector predictedState, final RealMatrix innovationCovarianceMatrix) {
384 
385         // Predicted filter correction
386         predictedFilterCorrection = predictedState;
387 
388         // Predicted measurement
389         final RealVector osculating = computeOsculatingElements(predictedFilterCorrection, nominalMeanSpacecraftState, shortPeriodicTerms);
390         final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, angleType,
391                                                                 currentDate, builder.getMu(), builder.getFrame());
392         predictedSpacecraftState = new SpacecraftState(osculatingOrbit);
393         predictedMeasurement = estimateMeasurement(measurement.getObservedMeasurement(), currentMeasurementNumber,
394             getPredictedSpacecraftStates());
395         predictedMeasurement.setEstimatedValue(predictedMeas.toArray());
396 
397         // Apply the dynamic outlier filter, if it exists
398         KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
399 
400         // Compute the innovation vector (not normalized for unscented Kalman filter)
401         return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement);
402 
403     }
404 
405 
406     /** {@inheritDoc} */
407     @Override
408     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
409                                    final ProcessEstimate estimate) {
410         // Update the process estimate
411         correctedEstimate = estimate;
412         // Corrected filter correction
413         correctedFilterCorrection = estimate.getState();
414 
415         // Update the previous nominal mean spacecraft state
416         previousNominalMeanSpacecraftState = nominalMeanSpacecraftState;
417 
418         // Update the previous nominal mean spacecraft state
419         // Calculate the corrected osculating elements
420         final RealVector osculating = computeOsculatingElements(correctedFilterCorrection, nominalMeanSpacecraftState, shortPeriodicTerms);
421         final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, builder.getPositionAngleType(),
422                                                                 currentDate, builder.getMu(), builder.getFrame());
423 
424         // Compute the corrected measurements
425         correctedSpacecraftState = new SpacecraftState(osculatingOrbit);
426         correctedMeasurement = estimateMeasurement(observedMeasurement, currentMeasurementNumber,
427             getCorrectedSpacecraftStates());
428 
429         // Call the observer if the user add one
430         if (observer != null) {
431             observer.evaluationPerformed(this);
432         }
433     }
434 
435     /**
436      * Estimate measurement (without derivatives).
437      * @param <T> measurement type
438      * @param observedMeasurement observed measurement
439      * @param measurementNumber measurement number
440      * @param spacecraftStates states
441      * @return estimated measurements
442      * @since 12.1
443      */
444     private static <T extends ObservedMeasurement<T>> EstimatedMeasurement<?> estimateMeasurement(final ObservedMeasurement<T> observedMeasurement,
445                                                                                                   final int measurementNumber,
446                                                                                                   final SpacecraftState[] spacecraftStates) {
447         final EstimatedMeasurementBase<T> estimatedMeasurementBase = observedMeasurement.
448                 estimateWithoutDerivatives(measurementNumber, measurementNumber,
449                         KalmanEstimatorUtil.filterRelevant(observedMeasurement, spacecraftStates));
450         final EstimatedMeasurement<T> estimatedMeasurement = new EstimatedMeasurement<>(estimatedMeasurementBase.getObservedMeasurement(),
451                 estimatedMeasurementBase.getIteration(), estimatedMeasurementBase.getCount(),
452                 estimatedMeasurementBase.getStates(), estimatedMeasurementBase.getParticipants());
453         estimatedMeasurement.setEstimatedValue(estimatedMeasurementBase.getEstimatedValue());
454         return estimatedMeasurement;
455     }
456 
457     /** Get the state transition matrix used to predict the filter correction.
458      * <p>
459      * The state transition matrix is not computed by the DSST propagator.
460      * It is analytically calculated considering Keplerian contribution only
461      * </p>
462      * @return the state transition matrix used to predict the filter correction
463      */
464     private RealMatrix getStm() {
465 
466         // initialize the STM
467         final int nbDym  = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers();
468         final int nbMeas = getNumberSelectedMeasurementDrivers();
469         final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(nbDym + nbMeas);
470 
471         // State transition matrix using Keplerian contribution only
472         final double mu  = builder.getMu();
473         final double sma = previousNominalMeanSpacecraftState.getA();
474         final double dt  = currentDate.durationFrom(previousNominalMeanSpacecraftState.getDate());
475         final double contribution = -1.5 * dt * FastMath.sqrt(mu / FastMath.pow(sma, 5));
476         stm.setEntry(5, 0, contribution);
477 
478         // Return
479         return stm;
480 
481     }
482 
483     /** {@inheritDoc} */
484     @Override
485     public void finalizeOperationsObservationGrid() {
486         // Update parameters
487         updateParameters();
488     }
489 
490     /** {@inheritDoc} */
491     @Override
492     public ParameterDriversList getEstimatedOrbitalParameters() {
493         return estimatedOrbitalParameters;
494     }
495 
496     /** {@inheritDoc} */
497     @Override
498     public ParameterDriversList getEstimatedPropagationParameters() {
499         return estimatedPropagationParameters;
500     }
501 
502     /** {@inheritDoc} */
503     @Override
504     public ParameterDriversList getEstimatedMeasurementsParameters() {
505         return estimatedMeasurementsParameters;
506     }
507 
508     /** {@inheritDoc}
509      * <p>
510      * Predicted state is osculating.
511      * </p>
512      */
513     @Override
514     public SpacecraftState[] getPredictedSpacecraftStates() {
515         return new SpacecraftState[] {predictedSpacecraftState};
516     }
517 
518     /** {@inheritDoc}
519      * <p>
520      * Corrected state is osculating.
521      * </p>
522      */
523     @Override
524     public SpacecraftState[] getCorrectedSpacecraftStates() {
525         return new SpacecraftState[] {correctedSpacecraftState};
526     }
527 
528     /** {@inheritDoc} */
529     @Override
530     public RealVector getPhysicalEstimatedState() {
531         // Method {@link ParameterDriver#getValue()} is used to get
532         // the physical values of the state.
533         // The scales'array is used to get the size of the state vector
534         final RealVector physicalEstimatedState = new ArrayRealVector(getEstimate().getState().getDimension());
535         int i = 0;
536         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
537             physicalEstimatedState.setEntry(i++, driver.getValue());
538         }
539         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
540             physicalEstimatedState.setEntry(i++, driver.getValue());
541         }
542         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
543             physicalEstimatedState.setEntry(i++, driver.getValue());
544         }
545 
546         return physicalEstimatedState;
547     }
548 
549     /** {@inheritDoc} */
550     @Override
551     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
552         return correctedEstimate.getCovariance();
553     }
554 
555     /** {@inheritDoc} */
556     @Override
557     public RealMatrix getPhysicalStateTransitionMatrix() {
558         return null;
559     }
560 
561     /** {@inheritDoc} */
562     @Override
563     public RealMatrix getPhysicalMeasurementJacobian() {
564         return null;
565     }
566 
567     /** {@inheritDoc} */
568     @Override
569     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
570         return correctedEstimate.getInnovationCovariance();
571     }
572 
573     /** {@inheritDoc} */
574     @Override
575     public RealMatrix getPhysicalKalmanGain() {
576         return correctedEstimate.getKalmanGain();
577     }
578 
579     /** {@inheritDoc} */
580     @Override
581     public int getCurrentMeasurementNumber() {
582         return currentMeasurementNumber;
583     }
584 
585     /** {@inheritDoc} */
586     @Override
587     public AbsoluteDate getCurrentDate() {
588         return currentDate;
589     }
590 
591     /** {@inheritDoc} */
592     @Override
593     public EstimatedMeasurement<?> getPredictedMeasurement() {
594         return predictedMeasurement;
595     }
596 
597     /** {@inheritDoc} */
598     @Override
599     public EstimatedMeasurement<?> getCorrectedMeasurement() {
600         return correctedMeasurement;
601     }
602 
603     /** {@inheritDoc} */
604     @Override
605     public void updateNominalSpacecraftState(final SpacecraftState nominal) {
606         this.nominalMeanSpacecraftState = nominal;
607         // Short period terms
608         shortPeriodicTerms = new ArrayRealVector(dsstPropagator.getShortPeriodTermsValue(nominalMeanSpacecraftState));
609         // Update the builder with the nominal mean elements orbit
610         builder.resetOrbit(nominal.getOrbit(), PropagationType.MEAN);
611     }
612 
613     /** {@inheritDoc} */
614     @Override
615     public void updateShortPeriods(final SpacecraftState state) {
616         // Loop on DSST force models
617         for (final DSSTForceModel model : dsstPropagator.getAllForceModels()) {
618             model.updateShortPeriodTerms(model.getParameters(), state);
619         }
620     }
621 
622     /** {@inheritDoc} */
623     @Override
624     public void initializeShortPeriodicTerms(final SpacecraftState meanState) {
625         final List<ShortPeriodTerms> shortPeriodTerms = new ArrayList<>();
626         for (final DSSTForceModel force :  builder.getAllForceModels()) {
627             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(new AuxiliaryElements(meanState.getOrbit(), 1), PropagationType.OSCULATING, force.getParameters()));
628         }
629         dsstPropagator.setShortPeriodTerms(shortPeriodTerms);
630     }
631 
632     /** Compute the predicted osculating elements.
633      * @param filterCorrection physical kalman filter correction
634      * @param meanState mean spacecraft state
635      * @param shortPeriodTerms short period terms for the given mean state
636      * @return the predicted osculating element
637      */
638     private RealVector computeOsculatingElements(final RealVector filterCorrection,
639                                                  final SpacecraftState meanState,
640                                                  final RealVector shortPeriodTerms) {
641 
642         // Convert the input predicted mean state to a SpacecraftState
643         final RealVector stateVector = toRealVector(meanState);
644 
645         // Return
646         return stateVector.add(filterCorrection).add(shortPeriodTerms);
647 
648     }
649 
650     /** Convert a SpacecraftState to a RealVector.
651      * @param state the input SpacecraftState
652      * @return the corresponding RealVector
653      */
654     private RealVector toRealVector(final SpacecraftState state) {
655 
656         // Convert orbit to array
657         final double[] stateArray = new double[6];
658         orbitType.mapOrbitToArray(state.getOrbit(), angleType, stateArray, null);
659 
660         // Return the RealVector
661         return new ArrayRealVector(stateArray);
662 
663     }
664 
665     /** Get the number of estimated orbital parameters.
666      * @return the number of estimated orbital parameters
667      */
668     public int getNumberSelectedOrbitalDrivers() {
669         return estimatedOrbitalParameters.getNbParams();
670     }
671 
672     /** Get the number of estimated propagation parameters.
673      * @return the number of estimated propagation parameters
674      */
675     public int getNumberSelectedPropagationDrivers() {
676         return estimatedPropagationParameters.getNbParams();
677     }
678 
679     /** Get the number of estimated measurement parameters.
680      * @return the number of estimated measurement parameters
681      */
682     public int getNumberSelectedMeasurementDrivers() {
683         return estimatedMeasurementsParameters.getNbParams();
684     }
685 
686     /** Update the estimated parameters after the correction phase of the filter.
687      * The min/max allowed values are handled by the parameter themselves.
688      */
689     private void updateParameters() {
690         final RealVector correctedState = correctedEstimate.getState();
691         int i = 0;
692         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
693             // let the parameter handle min/max clipping
694             driver.setValue(driver.getValue() + correctedState.getEntry(i++));
695         }
696         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
697             // let the parameter handle min/max clipping
698             driver.setValue(driver.getValue() + correctedState.getEntry(i++));
699         }
700         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
701             // let the parameter handle min/max clipping
702             driver.setValue(driver.getValue() + correctedState.getEntry(i++));
703         }
704     }
705 
706 }