SemiAnalyticalKalmanModel.java

  1. /* Copyright 2002-2022 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.estimation.sequential;

  18. import java.util.ArrayList;
  19. import java.util.Comparator;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;

  23. import org.hipparchus.exception.MathRuntimeException;
  24. import org.hipparchus.filtering.kalman.ProcessEstimate;
  25. import org.hipparchus.filtering.kalman.extended.ExtendedKalmanFilter;
  26. import org.hipparchus.filtering.kalman.extended.NonLinearEvolution;
  27. import org.hipparchus.filtering.kalman.extended.NonLinearProcess;
  28. import org.hipparchus.linear.Array2DRowRealMatrix;
  29. import org.hipparchus.linear.ArrayRealVector;
  30. import org.hipparchus.linear.MatrixUtils;
  31. import org.hipparchus.linear.QRDecomposition;
  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.estimation.measurements.EstimatedMeasurement;
  37. import org.orekit.estimation.measurements.ObservedMeasurement;
  38. import org.orekit.orbits.Orbit;
  39. import org.orekit.orbits.OrbitType;
  40. import org.orekit.propagation.PropagationType;
  41. import org.orekit.propagation.SpacecraftState;
  42. import org.orekit.propagation.conversion.DSSTPropagatorBuilder;
  43. import org.orekit.propagation.semianalytical.dsst.DSSTHarvester;
  44. import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
  45. import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
  46. import org.orekit.propagation.semianalytical.dsst.forces.ShortPeriodTerms;
  47. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  48. import org.orekit.time.AbsoluteDate;
  49. import org.orekit.time.ChronologicalComparator;
  50. import org.orekit.utils.ParameterDriver;
  51. import org.orekit.utils.ParameterDriversList;
  52. import org.orekit.utils.ParameterDriversList.DelegatingDriver;

  53. /** Process model to use with a {@link SemiAnalyticalKalmanEstimator}.
  54.  *
  55.  * @see "Folcik Z., Orbit Determination Using Modern Filters/Smoothers and Continuous Thrust Modeling,
  56.  *       Master of Science Thesis, Department of Aeronautics and Astronautics, MIT, June, 2008."
  57.  *
  58.  * @see "Cazabonne B., Bayard J., Journot M., and Cefola P. J., A Semi-analytical Approach for Orbit
  59.  *       Determination based on Extended Kalman Filter, AAS Paper 21-614, AAS/AIAA Astrodynamics
  60.  *       Specialist Conference, Big Sky, August 2021."
  61.  *
  62.  * @author Julie Bayard
  63.  * @author Bryan Cazabonne
  64.  * @author Maxime Journot
  65.  * @since 11.1
  66.  */
  67. public  class SemiAnalyticalKalmanModel implements KalmanEstimation, NonLinearProcess<MeasurementDecorator>, SemiAnalyticalProcess {

  68.     /** Builders for DSST propagator. */
  69.     private final DSSTPropagatorBuilder builder;

  70.     /** Estimated orbital parameters. */
  71.     private final ParameterDriversList estimatedOrbitalParameters;

  72.     /** Per-builder estimated propagation drivers. */
  73.     private final ParameterDriversList estimatedPropagationParameters;

  74.     /** Estimated measurements parameters. */
  75.     private final ParameterDriversList estimatedMeasurementsParameters;

  76.     /** Map for propagation parameters columns. */
  77.     private final Map<String, Integer> propagationParameterColumns;

  78.     /** Map for measurements parameters columns. */
  79.     private final Map<String, Integer> measurementParameterColumns;

  80.     /** Scaling factors. */
  81.     private final double[] scale;

  82.     /** Provider for covariance matrix. */
  83.     private final CovarianceMatrixProvider covarianceMatrixProvider;

  84.     /** Process noise matrix provider for measurement parameters. */
  85.     private final CovarianceMatrixProvider measurementProcessNoiseMatrix;

  86.     /** Harvester between two-dimensional Jacobian matrices and one-dimensional additional state arrays. */
  87.     private DSSTHarvester harvester;

  88.     /** Propagators for the reference trajectories, up to current date. */
  89.     private DSSTPropagator dsstPropagator;

  90.     /** Observer to retrieve current estimation info. */
  91.     private KalmanObserver observer;

  92.     /** Current number of measurement. */
  93.     private int currentMeasurementNumber;

  94.     /** Current date. */
  95.     private AbsoluteDate currentDate;

  96.     /** Predicted mean element filter correction. */
  97.     private RealVector predictedFilterCorrection;

  98.     /** Corrected mean element filter correction. */
  99.     private RealVector correctedFilterCorrection;

  100.     /** Predicted measurement. */
  101.     private EstimatedMeasurement<?> predictedMeasurement;

  102.     /** Corrected measurement. */
  103.     private EstimatedMeasurement<?> correctedMeasurement;

  104.     /** Nominal mean spacecraft state. */
  105.     private SpacecraftState nominalMeanSpacecraftState;

  106.     /** Previous nominal mean spacecraft state. */
  107.     private SpacecraftState previousNominalMeanSpacecraftState;

  108.     /** Current corrected estimate. */
  109.     private ProcessEstimate correctedEstimate;

  110.     /** Inverse of the orbital part of the state transition matrix. */
  111.     private RealMatrix phiS;

  112.     /** Propagation parameters part of the state transition matrix. */
  113.     private RealMatrix psiS;

  114.     /** Kalman process model constructor (package private).
  115.      * @param propagatorBuilder propagators builders used to evaluate the orbits.
  116.      * @param covarianceMatrixProvider provider for covariance matrix
  117.      * @param estimatedMeasurementParameters measurement parameters to estimate
  118.      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
  119.      */
  120.     protected SemiAnalyticalKalmanModel(final DSSTPropagatorBuilder propagatorBuilder,
  121.                                         final CovarianceMatrixProvider covarianceMatrixProvider,
  122.                                         final ParameterDriversList estimatedMeasurementParameters,
  123.                                         final CovarianceMatrixProvider measurementProcessNoiseMatrix) {

  124.         this.builder                         = propagatorBuilder;
  125.         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
  126.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
  127.         this.observer                        = null;
  128.         this.currentMeasurementNumber        = 0;
  129.         this.currentDate                     = propagatorBuilder.getInitialOrbitDate();
  130.         this.covarianceMatrixProvider        = covarianceMatrixProvider;
  131.         this.measurementProcessNoiseMatrix   = measurementProcessNoiseMatrix;

  132.         // Number of estimated parameters
  133.         int columns = 0;

  134.         // Set estimated orbital parameters
  135.         estimatedOrbitalParameters = new ParameterDriversList();
  136.         for (final ParameterDriver driver : builder.getOrbitalParametersDrivers().getDrivers()) {

  137.             // Verify if the driver reference date has been set
  138.             if (driver.getReferenceDate() == null) {
  139.                 driver.setReferenceDate(currentDate);
  140.             }

  141.             // Verify if the driver is selected
  142.             if (driver.isSelected()) {
  143.                 estimatedOrbitalParameters.add(driver);
  144.                 columns++;
  145.             }

  146.         }

  147.         // Set estimated propagation parameters
  148.         estimatedPropagationParameters = new ParameterDriversList();
  149.         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
  150.         for (final ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {

  151.             // Verify if the driver reference date has been set
  152.             if (driver.getReferenceDate() == null) {
  153.                 driver.setReferenceDate(currentDate);
  154.             }

  155.             // Verify if the driver is selected
  156.             if (driver.isSelected()) {
  157.                 estimatedPropagationParameters.add(driver);
  158.                 final String driverName = driver.getName();
  159.                 // Add the driver name if it has not been added yet
  160.                 if (!estimatedPropagationParametersNames.contains(driverName)) {
  161.                     estimatedPropagationParametersNames.add(driverName);
  162.                 }
  163.             }

  164.         }
  165.         estimatedPropagationParametersNames.sort(Comparator.naturalOrder());

  166.         // Populate the map of propagation drivers' columns and update the total number of columns
  167.         propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  168.         for (final String driverName : estimatedPropagationParametersNames) {
  169.             propagationParameterColumns.put(driverName, columns);
  170.             ++columns;
  171.         }

  172.         // Set the estimated measurement parameters
  173.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  174.             if (parameter.getReferenceDate() == null) {
  175.                 parameter.setReferenceDate(currentDate);
  176.             }
  177.             measurementParameterColumns.put(parameter.getName(), columns);
  178.             ++columns;
  179.         }

  180.         // Compute the scale factors
  181.         this.scale = new double[columns];
  182.         int index = 0;
  183.         for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) {
  184.             scale[index++] = driver.getScale();
  185.         }
  186.         for (final ParameterDriver driver : estimatedPropagationParameters.getDrivers()) {
  187.             scale[index++] = driver.getScale();
  188.         }
  189.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  190.             scale[index++] = driver.getScale();
  191.         }

  192.         // Build the reference propagator and add its partial derivatives equations implementation
  193.         updateReferenceTrajectory(getEstimatedPropagator());
  194.         this.nominalMeanSpacecraftState = dsstPropagator.getInitialState();
  195.         this.previousNominalMeanSpacecraftState = nominalMeanSpacecraftState;

  196.         // Initialize "field" short periodic terms
  197.         harvester.initializeFieldShortPeriodTerms(nominalMeanSpacecraftState);

  198.         // Initialize the estimated normalized mean element filter correction (See Ref [1], Eq. 3.2a)
  199.         this.predictedFilterCorrection = MatrixUtils.createRealVector(columns);
  200.         this.correctedFilterCorrection = predictedFilterCorrection;

  201.         // Initialize propagation parameters part of the state transition matrix (See Ref [1], Eq. 3.2c)
  202.         this.psiS = null;
  203.         if (estimatedPropagationParameters.getNbParams() != 0) {
  204.             this.psiS = MatrixUtils.createRealMatrix(getNumberSelectedOrbitalDrivers(),
  205.                                                      getNumberSelectedPropagationDrivers());
  206.         }

  207.         // Initialize inverse of the orbital part of the state transition matrix (See Ref [1], Eq. 3.2d)
  208.         this.phiS = MatrixUtils.createRealIdentityMatrix(getNumberSelectedOrbitalDrivers());

  209.         // Number of estimated measurement parameters
  210.         final int nbMeas = getNumberSelectedMeasurementDrivers();

  211.         // Number of estimated dynamic parameters (orbital + propagation)
  212.         final int nbDyn  = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers();

  213.         // Covariance matrix
  214.         final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  215.         final RealMatrix noiseP = covarianceMatrixProvider.getInitialCovarianceMatrix(nominalMeanSpacecraftState);
  216.         noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  217.         if (measurementProcessNoiseMatrix != null) {
  218.             final RealMatrix noiseM = measurementProcessNoiseMatrix.getInitialCovarianceMatrix(nominalMeanSpacecraftState);
  219.             noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  220.         }

  221.         // Verify dimension
  222.         KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(),
  223.                                            builder.getOrbitalParametersDrivers(),
  224.                                            builder.getPropagationParametersDrivers(),
  225.                                            estimatedMeasurementsParameters);

  226.         final RealMatrix correctedCovariance = normalizeCovarianceMatrix(noiseK);

  227.         // Initialize corrected estimate
  228.         this.correctedEstimate = new ProcessEstimate(0.0, correctedFilterCorrection, correctedCovariance);

  229.     }

  230.     /** {@inheritDoc} */
  231.     @Override
  232.     public KalmanObserver getObserver() {
  233.         return observer;
  234.     }

  235.     /** Set the observer.
  236.      * @param observer the observer
  237.      */
  238.     public void setObserver(final KalmanObserver observer) {
  239.         this.observer = observer;
  240.     }

  241.     /** Get the current corrected estimate.
  242.      * @return current corrected estimate
  243.      */
  244.     public ProcessEstimate getEstimate() {
  245.         return correctedEstimate;
  246.     }

  247.     /** Process a single measurement.
  248.      * <p>
  249.      * Update the filter with the new measurements.
  250.      * </p>
  251.      * @param observedMeasurements the list of measurements to process
  252.      * @param filter Extended Kalman Filter
  253.      * @return estimated propagator
  254.      */
  255.     public DSSTPropagator processMeasurements(final List<ObservedMeasurement<?>> observedMeasurements,
  256.                                               final ExtendedKalmanFilter<MeasurementDecorator> filter) {
  257.         try {

  258.             // Sort the measurement
  259.             observedMeasurements.sort(new ChronologicalComparator());
  260.             final AbsoluteDate tStart             = observedMeasurements.get(0).getDate();
  261.             final AbsoluteDate tEnd               = observedMeasurements.get(observedMeasurements.size() - 1).getDate();
  262.             final double       overshootTimeRange = FastMath.nextAfter(tEnd.durationFrom(tStart),
  263.                                                     Double.POSITIVE_INFINITY);

  264.             // Initialize step handler and set it to the propagator
  265.             final SemiAnalyticalMeasurementHandler stepHandler = new SemiAnalyticalMeasurementHandler(this, filter, observedMeasurements, builder.getInitialOrbitDate());
  266.             dsstPropagator.getMultiplexer().add(stepHandler);
  267.             dsstPropagator.propagate(tStart, tStart.shiftedBy(overshootTimeRange));

  268.             // Return the last estimated propagator
  269.             return getEstimatedPropagator();

  270.         } catch (MathRuntimeException mrte) {
  271.             throw new OrekitException(mrte);
  272.         }
  273.     }

  274.     /** Get the propagator estimated with the values set in the propagator builder.
  275.      * @return propagator based on the current values in the builder
  276.      */
  277.     public DSSTPropagator getEstimatedPropagator() {
  278.         // Return propagator built with current instantiation of the propagator builder
  279.         return builder.buildPropagator(builder.getSelectedNormalizedParameters());
  280.     }

  281.     /** {@inheritDoc} */
  282.     @Override
  283.     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
  284.                                            final MeasurementDecorator measurement) {

  285.         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  286.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  287.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  288.             if (driver.getReferenceDate() == null) {
  289.                 driver.setReferenceDate(builder.getInitialOrbitDate());
  290.             }
  291.         }

  292.         // Increment measurement number
  293.         ++currentMeasurementNumber;

  294.         // Update the current date
  295.         currentDate = measurement.getObservedMeasurement().getDate();

  296.         // Normalized state transition matrix
  297.         final RealMatrix stm = getErrorStateTransitionMatrix();

  298.         // Predict filter correction
  299.         predictedFilterCorrection = predictFilterCorrection(stm);

  300.         // Short period term derivatives
  301.         analyticalDerivativeComputations(nominalMeanSpacecraftState);

  302.         // Calculate the predicted osculating elements
  303.         final double[] osculating = computeOsculatingElements(predictedFilterCorrection);
  304.         final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngle(),
  305.                                                                             currentDate, nominalMeanSpacecraftState.getMu(),
  306.                                                                             nominalMeanSpacecraftState.getFrame());

  307.         // Compute the predicted measurements  (See Ref [1], Eq. 3.8)
  308.         predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  309.                                                             currentMeasurementNumber,
  310.                                                             new SpacecraftState[] {
  311.                                                                 new SpacecraftState(osculatingOrbit,
  312.                                                                                     nominalMeanSpacecraftState.getAttitude(),
  313.                                                                                     nominalMeanSpacecraftState.getMass(),
  314.                                                                                     nominalMeanSpacecraftState.getAdditionalStatesValues(),
  315.                                                                                     nominalMeanSpacecraftState.getAdditionalStatesDerivatives())
  316.                                                             });

  317.         // Normalized measurement matrix
  318.         final RealMatrix measurementMatrix = getMeasurementMatrix();

  319.         // Number of estimated measurement parameters
  320.         final int nbMeas = getNumberSelectedMeasurementDrivers();

  321.         // Number of estimated dynamic parameters (orbital + propagation)
  322.         final int nbDyn  = getNumberSelectedOrbitalDrivers() + getNumberSelectedPropagationDrivers();

  323.         // Covariance matrix
  324.         final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  325.         final RealMatrix noiseP = covarianceMatrixProvider.getProcessNoiseMatrix(previousNominalMeanSpacecraftState, nominalMeanSpacecraftState);
  326.         noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  327.         if (measurementProcessNoiseMatrix != null) {
  328.             final RealMatrix noiseM = measurementProcessNoiseMatrix.getProcessNoiseMatrix(previousNominalMeanSpacecraftState, nominalMeanSpacecraftState);
  329.             noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  330.         }

  331.         // Verify dimension
  332.         KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(),
  333.                                            builder.getOrbitalParametersDrivers(),
  334.                                            builder.getPropagationParametersDrivers(),
  335.                                            estimatedMeasurementsParameters);

  336.         final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(noiseK);

  337.         // Return
  338.         return new NonLinearEvolution(measurement.getTime(), predictedFilterCorrection, stm,
  339.                                       normalizedProcessNoise, measurementMatrix);
  340.     }

  341.     /** {@inheritDoc} */
  342.     @Override
  343.     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
  344.                                     final RealMatrix innovationCovarianceMatrix) {

  345.         // Apply the dynamic outlier filter, if it exists
  346.         KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
  347.         // Compute the innovation vector
  348.         return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement, predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
  349.     }

  350.     /** {@inheritDoc} */
  351.     @Override
  352.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  353.                                    final ProcessEstimate estimate) {
  354.         // Update the process estimate
  355.         correctedEstimate = estimate;
  356.         // Corrected filter correction
  357.         correctedFilterCorrection = estimate.getState();
  358.         // Update the previous nominal mean spacecraft state
  359.         previousNominalMeanSpacecraftState = nominalMeanSpacecraftState;
  360.         // Calculate the corrected osculating elements
  361.         final double[] osculating = computeOsculatingElements(correctedFilterCorrection);
  362.         final Orbit osculatingOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(osculating, null, builder.getPositionAngle(),
  363.                                                                             currentDate, nominalMeanSpacecraftState.getMu(),
  364.                                                                             nominalMeanSpacecraftState.getFrame());

  365.         // Compute the corrected measurements
  366.         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  367.                                                             currentMeasurementNumber,
  368.                                                             new SpacecraftState[] {
  369.                                                                 new SpacecraftState(osculatingOrbit,
  370.                                                                                     nominalMeanSpacecraftState.getAttitude(),
  371.                                                                                     nominalMeanSpacecraftState.getMass(),
  372.                                                                                     nominalMeanSpacecraftState.getAdditionalStatesValues(),
  373.                                                                                     nominalMeanSpacecraftState.getAdditionalStatesDerivatives())
  374.                                                             });
  375.         // Call the observer if the user add one
  376.         if (observer != null) {
  377.             observer.evaluationPerformed(this);
  378.         }
  379.     }

  380.     /** {@inheritDoc} */
  381.     @Override
  382.     public void finalizeOperationsObservationGrid() {
  383.         // Update parameters
  384.         updateParameters();
  385.     }

  386.     /** {@inheritDoc} */
  387.     @Override
  388.     public ParameterDriversList getEstimatedOrbitalParameters() {
  389.         return estimatedOrbitalParameters;
  390.     }

  391.     /** {@inheritDoc} */
  392.     @Override
  393.     public ParameterDriversList getEstimatedPropagationParameters() {
  394.         return estimatedPropagationParameters;
  395.     }

  396.     /** {@inheritDoc} */
  397.     @Override
  398.     public ParameterDriversList getEstimatedMeasurementsParameters() {
  399.         return estimatedMeasurementsParameters;
  400.     }

  401.     /** {@inheritDoc} */
  402.     @Override
  403.     public SpacecraftState[] getPredictedSpacecraftStates() {
  404.         return new SpacecraftState[] {nominalMeanSpacecraftState};
  405.     }

  406.     /** {@inheritDoc} */
  407.     @Override
  408.     public SpacecraftState[] getCorrectedSpacecraftStates() {
  409.         return new SpacecraftState[] {getEstimatedPropagator().getInitialState()};
  410.     }

  411.     /** {@inheritDoc} */
  412.     @Override
  413.     public RealVector getPhysicalEstimatedState() {
  414.         // Method {@link ParameterDriver#getValue()} is used to get
  415.         // the physical values of the state.
  416.         // The scales'array is used to get the size of the state vector
  417.         final RealVector physicalEstimatedState = new ArrayRealVector(scale.length);
  418.         int i = 0;
  419.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  420.             physicalEstimatedState.setEntry(i++, driver.getValue());
  421.         }
  422.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  423.             physicalEstimatedState.setEntry(i++, driver.getValue());
  424.         }
  425.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  426.             physicalEstimatedState.setEntry(i++, driver.getValue());
  427.         }

  428.         return physicalEstimatedState;
  429.     }

  430.     /** {@inheritDoc} */
  431.     @Override
  432.     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
  433.         // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it.
  434.         // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  435.         // For each element [i,j] of P the corresponding normalized value is:
  436.         // Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  437.         // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j]

  438.         // Normalized covariance matrix
  439.         final RealMatrix normalizedP = correctedEstimate.getCovariance();

  440.         // Initialize physical covariance matrix
  441.         final int nbParams = normalizedP.getRowDimension();
  442.         final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams);

  443.         // Un-normalize the covairance matrix
  444.         for (int i = 0; i < nbParams; ++i) {
  445.             for (int j = 0; j < nbParams; ++j) {
  446.                 physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]);
  447.             }
  448.         }
  449.         return physicalP;
  450.     }

  451.     /** {@inheritDoc} */
  452.     @Override
  453.     public RealMatrix getPhysicalStateTransitionMatrix() {
  454.         //  Un-normalize the state transition matrix (φ) from Hipparchus and return it.
  455.         // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  456.         // For each element [i,j] of normalized φ (φn), the corresponding physical value is:
  457.         // φ[i,j] = φn[i,j] * scale[i] / scale[j]

  458.         // Normalized matrix
  459.         final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix();

  460.         if (normalizedSTM == null) {
  461.             return null;
  462.         } else {
  463.             // Initialize physical matrix
  464.             final int nbParams = normalizedSTM.getRowDimension();
  465.             final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams);

  466.             // Un-normalize the matrix
  467.             for (int i = 0; i < nbParams; ++i) {
  468.                 for (int j = 0; j < nbParams; ++j) {
  469.                     physicalSTM.setEntry(i, j,
  470.                                          normalizedSTM.getEntry(i, j) * scale[i] / scale[j]);
  471.                 }
  472.             }
  473.             return physicalSTM;
  474.         }
  475.     }

  476.     /** {@inheritDoc} */
  477.     @Override
  478.     public RealMatrix getPhysicalMeasurementJacobian() {
  479.         // Un-normalize the measurement matrix (H) from Hipparchus and return it.
  480.         // H is an nxm matrix where:
  481.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  482.         //  - n is the size of the measurement being processed by the filter
  483.         // For each element [i,j] of normalized H (Hn) the corresponding physical value is:
  484.         // H[i,j] = Hn[i,j] * σ[i] / scale[j]

  485.         // Normalized matrix
  486.         final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian();

  487.         if (normalizedH == null) {
  488.             return null;
  489.         } else {
  490.             // Get current measurement sigmas
  491.             final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  492.             // Initialize physical matrix
  493.             final int nbLine = normalizedH.getRowDimension();
  494.             final int nbCol  = normalizedH.getColumnDimension();
  495.             final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol);

  496.             // Un-normalize the matrix
  497.             for (int i = 0; i < nbLine; ++i) {
  498.                 for (int j = 0; j < nbCol; ++j) {
  499.                     physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]);
  500.                 }
  501.             }
  502.             return physicalH;
  503.         }
  504.     }

  505.     /** {@inheritDoc} */
  506.     @Override
  507.     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
  508.         // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it.
  509.         // S is an nxn matrix where n is the size of the measurement being processed by the filter
  510.         // For each element [i,j] of normalized S (Sn) the corresponding physical value is:
  511.         // S[i,j] = Sn[i,j] * σ[i] * σ[j]

  512.         // Normalized matrix
  513.         final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance();

  514.         if (normalizedS == null) {
  515.             return null;
  516.         } else {
  517.             // Get current measurement sigmas
  518.             final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  519.             // Initialize physical matrix
  520.             final int nbMeas = sigmas.length;
  521.             final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas);

  522.             // Un-normalize the matrix
  523.             for (int i = 0; i < nbMeas; ++i) {
  524.                 for (int j = 0; j < nbMeas; ++j) {
  525.                     physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] *   sigmas[j]);
  526.                 }
  527.             }
  528.             return physicalS;
  529.         }
  530.     }

  531.     /** {@inheritDoc} */
  532.     @Override
  533.     public RealMatrix getPhysicalKalmanGain() {
  534.         // Un-normalize the Kalman gain (K) from Hipparchus and return it.
  535.         // K is an mxn matrix where:
  536.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  537.         //  - n is the size of the measurement being processed by the filter
  538.         // For each element [i,j] of normalized K (Kn) the corresponding physical value is:
  539.         // K[i,j] = Kn[i,j] * scale[i] / σ[j]

  540.         // Normalized matrix
  541.         final RealMatrix normalizedK = correctedEstimate.getKalmanGain();

  542.         if (normalizedK == null) {
  543.             return null;
  544.         } else {
  545.             // Get current measurement sigmas
  546.             final double[] sigmas = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  547.             // Initialize physical matrix
  548.             final int nbLine = normalizedK.getRowDimension();
  549.             final int nbCol  = normalizedK.getColumnDimension();
  550.             final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol);

  551.             // Un-normalize the matrix
  552.             for (int i = 0; i < nbLine; ++i) {
  553.                 for (int j = 0; j < nbCol; ++j) {
  554.                     physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]);
  555.                 }
  556.             }
  557.             return physicalK;
  558.         }
  559.     }

  560.     /** {@inheritDoc} */
  561.     @Override
  562.     public int getCurrentMeasurementNumber() {
  563.         return currentMeasurementNumber;
  564.     }

  565.     /** {@inheritDoc} */
  566.     @Override
  567.     public AbsoluteDate getCurrentDate() {
  568.         return currentDate;
  569.     }

  570.     /** {@inheritDoc} */
  571.     @Override
  572.     public EstimatedMeasurement<?> getPredictedMeasurement() {
  573.         return predictedMeasurement;
  574.     }

  575.     /** {@inheritDoc} */
  576.     @Override
  577.     public EstimatedMeasurement<?> getCorrectedMeasurement() {
  578.         return correctedMeasurement;
  579.     }

  580.     /** {@inheritDoc} */
  581.     @Override
  582.     public void updateNominalSpacecraftState(final SpacecraftState nominal) {
  583.         this.nominalMeanSpacecraftState = nominal;
  584.         // Update the builder with the nominal mean elements orbit
  585.         builder.resetOrbit(nominal.getOrbit(), PropagationType.MEAN);
  586.     }

  587.     /** Update the reference trajectories using the propagator as input.
  588.      * @param propagator The new propagator to use
  589.      */
  590.     public void updateReferenceTrajectory(final DSSTPropagator propagator) {

  591.         dsstPropagator = propagator;

  592.         // Equation name
  593.         final String equationName = SemiAnalyticalKalmanEstimator.class.getName() + "-derivatives-";

  594.         // Mean state
  595.         final SpacecraftState meanState = dsstPropagator.initialIsOsculating() ?
  596.                        DSSTPropagator.computeMeanState(dsstPropagator.getInitialState(), dsstPropagator.getAttitudeProvider(), dsstPropagator.getAllForceModels()) :
  597.                        dsstPropagator.getInitialState();

  598.         // Update the jacobian harvester
  599.         dsstPropagator.setInitialState(meanState, PropagationType.MEAN);
  600.         harvester = (DSSTHarvester) dsstPropagator.setupMatricesComputation(equationName, null, null);

  601.     }

  602.     /** {@inheritDoc} */
  603.     @Override
  604.     public void updateShortPeriods(final SpacecraftState state) {
  605.         // Loop on DSST force models
  606.         for (final DSSTForceModel model : builder.getAllForceModels()) {
  607.             model.updateShortPeriodTerms(model.getParameters(), state);
  608.         }
  609.         harvester.updateFieldShortPeriodTerms(state);
  610.     }

  611.     /** {@inheritDoc} */
  612.     @Override
  613.     public void initializeShortPeriodicTerms(final SpacecraftState meanState) {
  614.         final List<ShortPeriodTerms> shortPeriodTerms = new ArrayList<ShortPeriodTerms>();
  615.         for (final DSSTForceModel force :  builder.getAllForceModels()) {
  616.             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(new AuxiliaryElements(meanState.getOrbit(), 1), PropagationType.OSCULATING, force.getParameters()));
  617.         }
  618.         dsstPropagator.setShortPeriodTerms(shortPeriodTerms);
  619.     }

  620.     /** Get the normalized state transition matrix (STM) from previous point to current point.
  621.      * The STM contains the partial derivatives of current state with respect to previous state.
  622.      * The  STM is an mxm matrix where m is the size of the state vector.
  623.      * m = nbOrb + nbPropag + nbMeas
  624.      * @return the normalized error state transition matrix
  625.      */
  626.     private RealMatrix getErrorStateTransitionMatrix() {

  627.         /* The state transition matrix is obtained as follows, with:
  628.          *  - Phi(k, k-1) : Transitional orbital matrix
  629.          *  - Psi(k, k-1) : Transitional propagation parameters matrix
  630.          *
  631.          *       |             |             |   .    |
  632.          *       | Phi(k, k-1) | Psi(k, k-1) | ..0..  |
  633.          *       |             |             |   .    |
  634.          *       |-------------|-------------|--------|
  635.          *       |      .      |    1 0 0    |   .    |
  636.          * STM = |    ..0..    |    0 1 0    | ..0..  |
  637.          *       |      .      |    0 0 1    |   .    |
  638.          *       |-------------|-------------|--------|
  639.          *       |      .      |      .      | 1 0 0..|
  640.          *       |    ..0..    |    ..0..    | 0 1 0..|
  641.          *       |      .      |      .      | 0 0 1..|
  642.          */

  643.         // Initialize to the proper size identity matrix
  644.         final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension());

  645.         // Derivatives of the state vector with respect to initial state vector
  646.         final int nbOrb = getNumberSelectedOrbitalDrivers();
  647.         final RealMatrix dYdY0 = harvester.getB2(nominalMeanSpacecraftState);

  648.         // Calculate transitional orbital matrix (See Ref [1], Eq. 3.4a)
  649.         final RealMatrix phi = dYdY0.multiply(phiS);

  650.         // Fill the state transition matrix with the orbital drivers
  651.         final List<DelegatingDriver> drivers = builder.getOrbitalParametersDrivers().getDrivers();
  652.         for (int i = 0; i < nbOrb; ++i) {
  653.             if (drivers.get(i).isSelected()) {
  654.                 int jOrb = 0;
  655.                 for (int j = 0; j < nbOrb; ++j) {
  656.                     if (drivers.get(j).isSelected()) {
  657.                         stm.setEntry(i, jOrb++, phi.getEntry(i, j));
  658.                     }
  659.                 }
  660.             }
  661.         }

  662.         // Update PhiS
  663.         phiS = new QRDecomposition(dYdY0).getSolver().getInverse();

  664.         // Derivatives of the state vector with respect to propagation parameters
  665.         if (psiS != null) {

  666.             final int nbProp = getNumberSelectedPropagationDrivers();
  667.             final RealMatrix dYdPp = harvester.getB3(nominalMeanSpacecraftState);

  668.             // Calculate transitional parameters matrix (See Ref [1], Eq. 3.4b)
  669.             final RealMatrix psi = dYdPp.subtract(phi.multiply(psiS));

  670.             // Fill 1st row, 2nd column (dY/dPp)
  671.             for (int i = 0; i < nbOrb; ++i) {
  672.                 for (int j = 0; j < nbProp; ++j) {
  673.                     stm.setEntry(i, j + nbOrb, psi.getEntry(i, j));
  674.                 }
  675.             }

  676.             // Update PsiS
  677.             psiS = dYdPp;

  678.         }

  679.         // Normalization of the STM
  680.         // normalized(STM)ij = STMij*Sj/Si
  681.         for (int i = 0; i < scale.length; i++) {
  682.             for (int j = 0; j < scale.length; j++ ) {
  683.                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
  684.             }
  685.         }

  686.         // Return the error state transition matrix
  687.         return stm;

  688.     }

  689.     /** Get the normalized measurement matrix H.
  690.      * H contains the partial derivatives of the measurement with respect to the state.
  691.      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
  692.      * @return the normalized measurement matrix H
  693.      */
  694.     private RealMatrix getMeasurementMatrix() {

  695.         // Observed measurement characteristics
  696.         final SpacecraftState        evaluationState     = predictedMeasurement.getStates()[0];
  697.         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
  698.         final double[] sigma  = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  699.         // Initialize measurement matrix H: nxm
  700.         // n: Number of measurements in current measurement
  701.         // m: State vector size
  702.         final RealMatrix measurementMatrix = MatrixUtils.
  703.                 createRealMatrix(observedMeasurement.getDimension(),
  704.                                  correctedEstimate.getState().getDimension());

  705.         // Predicted orbit
  706.         final Orbit predictedOrbit = evaluationState.getOrbit();

  707.         // Measurement matrix's columns related to orbital and propagation parameters
  708.         // ----------------------------------------------------------

  709.         // Partial derivatives of the current Cartesian coordinates with respect to current orbital state
  710.         final int nbOrb  = getNumberSelectedOrbitalDrivers();
  711.         final int nbProp = getNumberSelectedPropagationDrivers();
  712.         final double[][] aCY = new double[nbOrb][nbOrb];
  713.         predictedOrbit.getJacobianWrtParameters(builder.getPositionAngle(), aCY);
  714.         final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);

  715.         // Jacobian of the measurement with respect to current Cartesian coordinates
  716.         final RealMatrix dMdC = new Array2DRowRealMatrix(predictedMeasurement.getStateDerivatives(0), false);

  717.         // Jacobian of the measurement with respect to current orbital state
  718.         RealMatrix dMdY = dMdC.multiply(dCdY);

  719.         // Compute factor dShortPeriod_dMeanState = I+B1 | B4
  720.         final RealMatrix IpB1B4 = MatrixUtils.createRealMatrix(nbOrb, nbOrb + nbProp);

  721.         // B1
  722.         final RealMatrix B1 = harvester.getB1();

  723.         // I + B1
  724.         final RealMatrix I = MatrixUtils.createRealIdentityMatrix(nbOrb);
  725.         final RealMatrix IpB1 = I.add(B1);
  726.         IpB1B4.setSubMatrix(IpB1.getData(), 0, 0);

  727.         // If there are not propagation parameters, B4 is null
  728.         if (psiS != null) {
  729.             final RealMatrix B4 = harvester.getB4();
  730.             IpB1B4.setSubMatrix(B4.getData(), 0, nbOrb);
  731.         }

  732.         // Ref [1], Eq. 3.10
  733.         dMdY = dMdY.multiply(IpB1B4);

  734.         for (int i = 0; i < dMdY.getRowDimension(); i++) {
  735.             for (int j = 0; j < nbOrb; j++) {
  736.                 final double driverScale = builder.getOrbitalParametersDrivers().getDrivers().get(j).getScale();
  737.                 measurementMatrix.setEntry(i, j, dMdY.getEntry(i, j) / sigma[i] * driverScale);
  738.             }
  739.             for (int j = 0; j < nbProp; j++) {
  740.                 final double driverScale = estimatedPropagationParameters.getDrivers().get(j).getScale();
  741.                 measurementMatrix.setEntry(i, j + nbOrb,
  742.                                            dMdY.getEntry(i, j + nbOrb) / sigma[i] * driverScale);
  743.             }
  744.         }

  745.         // Normalized measurement matrix's columns related to measurement parameters
  746.         // --------------------------------------------------------------

  747.         // Jacobian of the measurement with respect to measurement parameters
  748.         // Gather the measurement parameters linked to current measurement
  749.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  750.             if (driver.isSelected()) {
  751.                 // Derivatives of current measurement w/r to selected measurement parameter
  752.                 final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);

  753.                 // Check that the measurement parameter is managed by the filter
  754.                 if (measurementParameterColumns.get(driver.getName()) != null) {
  755.                     // Column of the driver in the measurement matrix
  756.                     final int driverColumn = measurementParameterColumns.get(driver.getName());

  757.                     // Fill the corresponding indexes of the measurement matrix
  758.                     for (int i = 0; i < aMPm.length; ++i) {
  759.                         measurementMatrix.setEntry(i, driverColumn, aMPm[i] / sigma[i] * driver.getScale());
  760.                     }
  761.                 }
  762.             }
  763.         }

  764.         return measurementMatrix;
  765.     }

  766.     /** Predict the filter correction for the new observation.
  767.      * @param stm normalized state transition matrix
  768.      * @return the predicted filter correction for the new observation
  769.      */
  770.     private RealVector predictFilterCorrection(final RealMatrix stm) {
  771.         // Ref [1], Eq. 3.5a and 3.5b
  772.         return stm.operate(correctedFilterCorrection);
  773.     }

  774.     /** Compute the predicted osculating elements.
  775.      * @param filterCorrection kalman filter correction
  776.      * @return the predicted osculating element
  777.      */
  778.     private double[] computeOsculatingElements(final RealVector filterCorrection) {

  779.         // Number of estimated orbital parameters
  780.         final int nbOrb = getNumberSelectedOrbitalDrivers();

  781.         // B1
  782.         final RealMatrix B1 = harvester.getB1();

  783.         // Short periodic terms
  784.         final double[] shortPeriodTerms = dsstPropagator.getShortPeriodTermsValue(nominalMeanSpacecraftState);

  785.         // Physical filter correction
  786.         final RealVector physicalFilterCorrection = MatrixUtils.createRealVector(nbOrb);
  787.         for (int index = 0; index < nbOrb; index++) {
  788.             physicalFilterCorrection.addToEntry(index, filterCorrection.getEntry(index) * scale[index]);
  789.         }

  790.         // B1 * physicalCorrection
  791.         final RealVector B1Correction = B1.operate(physicalFilterCorrection);

  792.         // Nominal mean elements
  793.         final double[] nominalMeanElements = new double[nbOrb];
  794.         OrbitType.EQUINOCTIAL.mapOrbitToArray(nominalMeanSpacecraftState.getOrbit(), builder.getPositionAngle(), nominalMeanElements, null);

  795.         // Ref [1] Eq. 3.6
  796.         final double[] osculatingElements = new double[nbOrb];
  797.         for (int i = 0; i < nbOrb; i++) {
  798.             osculatingElements[i] = nominalMeanElements[i] +
  799.                                     physicalFilterCorrection.getEntry(i) +
  800.                                     shortPeriodTerms[i] +
  801.                                     B1Correction.getEntry(i);
  802.         }

  803.         // Return
  804.         return osculatingElements;

  805.     }

  806.     /** Analytical computation of derivatives.
  807.      * This method allow to compute analytical derivatives.
  808.      * @param state mean state used to calculate short period perturbations
  809.      */
  810.     private void analyticalDerivativeComputations(final SpacecraftState state) {
  811.         harvester.setReferenceState(state);
  812.     }

  813.     /** Normalize a covariance matrix.
  814.      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  815.      * For each element [i,j] of P the corresponding normalized value is:
  816.      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  817.      * @param physicalCovarianceMatrix The "physical" covariance matrix in input
  818.      * @return the normalized covariance matrix
  819.      */
  820.     private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) {

  821.         // Initialize output matrix
  822.         final int nbParams = physicalCovarianceMatrix.getRowDimension();
  823.         final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);

  824.         // Normalize the state matrix
  825.         for (int i = 0; i < nbParams; ++i) {
  826.             for (int j = 0; j < nbParams; ++j) {
  827.                 normalizedCovarianceMatrix.setEntry(i, j,
  828.                                                     physicalCovarianceMatrix.getEntry(i, j) /
  829.                                                     (scale[i] * scale[j]));
  830.             }
  831.         }
  832.         return normalizedCovarianceMatrix;
  833.     }

  834.     /** Get the number of estimated orbital parameters.
  835.      * @return the number of estimated orbital parameters
  836.      */
  837.     private int getNumberSelectedOrbitalDrivers() {
  838.         return estimatedOrbitalParameters.getNbParams();
  839.     }

  840.     /** Get the number of estimated propagation parameters.
  841.      * @return the number of estimated propagation parameters
  842.      */
  843.     private int getNumberSelectedPropagationDrivers() {
  844.         return estimatedPropagationParameters.getNbParams();
  845.     }

  846.     /** Get the number of estimated measurement parameters.
  847.      * @return the number of estimated measurement parameters
  848.      */
  849.     private int getNumberSelectedMeasurementDrivers() {
  850.         return estimatedMeasurementsParameters.getNbParams();
  851.     }

  852.     /** Update the estimated parameters after the correction phase of the filter.
  853.      * The min/max allowed values are handled by the parameter themselves.
  854.      */
  855.     private void updateParameters() {
  856.         final RealVector correctedState = correctedEstimate.getState();
  857.         int i = 0;
  858.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  859.             // let the parameter handle min/max clipping
  860.             driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++));
  861.         }
  862.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  863.             // let the parameter handle min/max clipping
  864.             driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++));
  865.         }
  866.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  867.             // let the parameter handle min/max clipping
  868.             driver.setNormalizedValue(driver.getNormalizedValue() + correctedState.getEntry(i++));
  869.         }
  870.     }

  871. }