DSSTKalmanModel.java

  1. /* Copyright 2002-2020 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.Arrays;
  20. import java.util.Comparator;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;

  24. import org.hipparchus.filtering.kalman.ProcessEstimate;
  25. import org.hipparchus.filtering.kalman.extended.NonLinearEvolution;
  26. import org.hipparchus.linear.Array2DRowRealMatrix;
  27. import org.hipparchus.linear.ArrayRealVector;
  28. import org.hipparchus.linear.MatrixUtils;
  29. import org.hipparchus.linear.RealMatrix;
  30. import org.hipparchus.linear.RealVector;
  31. import org.hipparchus.util.FastMath;
  32. import org.orekit.errors.OrekitException;
  33. import org.orekit.errors.OrekitMessages;
  34. import org.orekit.estimation.measurements.EstimatedMeasurement;
  35. import org.orekit.estimation.measurements.EstimationModifier;
  36. import org.orekit.estimation.measurements.ObservableSatellite;
  37. import org.orekit.estimation.measurements.ObservedMeasurement;
  38. import org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter;
  39. import org.orekit.orbits.Orbit;
  40. import org.orekit.propagation.PropagationType;
  41. import org.orekit.propagation.SpacecraftState;
  42. import org.orekit.propagation.conversion.IntegratedPropagatorBuilder;
  43. import org.orekit.propagation.semianalytical.dsst.DSSTJacobiansMapper;
  44. import org.orekit.propagation.semianalytical.dsst.DSSTPartialDerivativesEquations;
  45. import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
  46. import org.orekit.time.AbsoluteDate;
  47. import org.orekit.utils.ParameterDriver;
  48. import org.orekit.utils.ParameterDriversList;
  49. import org.orekit.utils.ParameterDriversList.DelegatingDriver;

  50. /** Class defining the process model dynamics to use with a {@link KalmanEstimator}.
  51.  * <p>
  52.  * This class is an adaption of the {@link KalmanModel} class
  53.  * but for the {@link DSSTPropagator DSST propagator}.
  54.  * </p>
  55.  * @author Romain Gerbaud
  56.  * @author Maxime Journot
  57.  * @author Bryan Cazabonne
  58.  * @since 10.0
  59.  */
  60. public class DSSTKalmanModel implements KalmanODModel {

  61.     /** Builders for propagators. */
  62.     private final List<IntegratedPropagatorBuilder> builders;

  63.     /** Estimated orbital parameters. */
  64.     private final ParameterDriversList allEstimatedOrbitalParameters;

  65.     /** Estimated propagation drivers. */
  66.     private final ParameterDriversList allEstimatedPropagationParameters;

  67.     /** Per-builder estimated propagation drivers. */
  68.     private final ParameterDriversList[] estimatedPropagationParameters;

  69.     /** Estimated measurements parameters. */
  70.     private final ParameterDriversList estimatedMeasurementsParameters;

  71.     /** Start columns for each estimated orbit. */
  72.     private final int[] orbitsStartColumns;

  73.     /** End columns for each estimated orbit. */
  74.     private final int[] orbitsEndColumns;

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

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

  79.     /** Providers for covariance matrices. */
  80.     private final List<CovarianceMatrixProvider> covarianceMatricesProviders;

  81.     /** Process noise matrix provider for measurement parameters. */
  82.     private final CovarianceMatrixProvider measurementProcessNoiseMatrix;

  83.     /** Indirection arrays to extract the noise components for estimated parameters. */
  84.     private final int[][] covarianceIndirection;

  85.     /** Scaling factors. */
  86.     private final double[] scale;

  87.     /** Mappers for extracting Jacobians from integrated states. */
  88.     private final DSSTJacobiansMapper[] mappers;

  89.     /** Propagators for the reference trajectories, up to current date. */
  90.     private DSSTPropagator[] referenceTrajectories;

  91.     /** Current corrected estimate. */
  92.     private ProcessEstimate correctedEstimate;

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

  95.     /** Reference date. */
  96.     private AbsoluteDate referenceDate;

  97.     /** Current date. */
  98.     private AbsoluteDate currentDate;

  99.     /** Predicted spacecraft states. */
  100.     private SpacecraftState[] predictedSpacecraftStates;

  101.     /** Corrected spacecraft states. */
  102.     private SpacecraftState[] correctedSpacecraftStates;

  103.     /** Predicted measurement. */
  104.     private EstimatedMeasurement<?> predictedMeasurement;

  105.     /** Corrected measurement. */
  106.     private EstimatedMeasurement<?> correctedMeasurement;

  107.     /** Type of the orbit used for the propagation.*/
  108.     private PropagationType propagationType;

  109.     /** Type of the elements used to define the orbital state.*/
  110.     private PropagationType stateType;

  111.     /** Kalman process model constructor.
  112.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  113.      * @param covarianceMatricesProviders providers for covariance matrices
  114.      * @param estimatedMeasurementParameters measurement parameters to estimate
  115.      * @param propagationType type of the orbit used for the propagation (mean or osculating)
  116.      * @param stateType type of the elements used to define the orbital state (mean or osculating)
  117.      * @deprecated since 10.3, replaced by {@link
  118.      * #DSSTKalmanModel(List, List, ParameterDriversList, CovarianceMatrixProvider, PropagationType, PropagationType)}
  119.      */
  120.     @Deprecated
  121.     public DSSTKalmanModel(final List<IntegratedPropagatorBuilder> propagatorBuilders,
  122.                            final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  123.                            final ParameterDriversList estimatedMeasurementParameters,
  124.                            final PropagationType propagationType,
  125.                            final PropagationType stateType) {
  126.         this(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters,
  127.              null, propagationType, stateType);
  128.     }

  129.     /** Kalman process model constructor.
  130.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  131.      * @param covarianceMatricesProviders providers for covariance matrices
  132.      * @param estimatedMeasurementParameters measurement parameters to estimate
  133.      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
  134.      * @param propagationType type of the orbit used for the propagation (mean or osculating)
  135.      * @param stateType type of the elements used to define the orbital state (mean or osculating)
  136.      */
  137.     public DSSTKalmanModel(final List<IntegratedPropagatorBuilder> propagatorBuilders,
  138.                            final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  139.                            final ParameterDriversList estimatedMeasurementParameters,
  140.                            final CovarianceMatrixProvider measurementProcessNoiseMatrix,
  141.                            final PropagationType propagationType,
  142.                            final PropagationType stateType) {

  143.         this.builders                        = propagatorBuilders;
  144.         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
  145.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
  146.         this.currentMeasurementNumber        = 0;
  147.         this.referenceDate                   = propagatorBuilders.get(0).getInitialOrbitDate();
  148.         this.currentDate                     = referenceDate;
  149.         this.propagationType                 = propagationType;
  150.         this.stateType                       = stateType;

  151.         final Map<String, Integer> orbitalParameterColumns = new HashMap<>(6 * builders.size());
  152.         orbitsStartColumns      = new int[builders.size()];
  153.         orbitsEndColumns        = new int[builders.size()];
  154.         int columns = 0;
  155.         allEstimatedOrbitalParameters = new ParameterDriversList();
  156.         for (int k = 0; k < builders.size(); ++k) {
  157.             orbitsStartColumns[k] = columns;
  158.             final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null;
  159.             for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  160.                 if (driver.getReferenceDate() == null) {
  161.                     driver.setReferenceDate(currentDate);
  162.                 }
  163.                 if (suffix != null && !driver.getName().endsWith(suffix)) {
  164.                     // we add suffix only conditionally because the method may already have been called
  165.                     // and suffixes may have already been appended
  166.                     driver.setName(driver.getName() + suffix);
  167.                 }
  168.                 if (driver.isSelected()) {
  169.                     allEstimatedOrbitalParameters.add(driver);
  170.                     orbitalParameterColumns.put(driver.getName(), columns++);
  171.                 }
  172.             }
  173.             orbitsEndColumns[k] = columns;
  174.         }

  175.         // Gather all the propagation drivers names in a list
  176.         allEstimatedPropagationParameters = new ParameterDriversList();
  177.         estimatedPropagationParameters    = new ParameterDriversList[builders.size()];
  178.         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
  179.         for (int k = 0; k < builders.size(); ++k) {
  180.             estimatedPropagationParameters[k] = new ParameterDriversList();
  181.             for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) {
  182.                 if (driver.getReferenceDate() == null) {
  183.                     driver.setReferenceDate(currentDate);
  184.                 }
  185.                 if (driver.isSelected()) {
  186.                     allEstimatedPropagationParameters.add(driver);
  187.                     estimatedPropagationParameters[k].add(driver);
  188.                     final String driverName = driver.getName();
  189.                     // Add the driver name if it has not been added yet
  190.                     if (!estimatedPropagationParametersNames.contains(driverName)) {
  191.                         estimatedPropagationParametersNames.add(driverName);
  192.                     }
  193.                 }
  194.             }
  195.         }
  196.         estimatedPropagationParametersNames.sort(Comparator.naturalOrder());

  197.         // Populate the map of propagation drivers' columns and update the total number of columns
  198.         propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  199.         for (final String driverName : estimatedPropagationParametersNames) {
  200.             propagationParameterColumns.put(driverName, columns);
  201.             ++columns;
  202.         }

  203.         // Populate the map of measurement drivers' columns and update the total number of columns
  204.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  205.             if (parameter.getReferenceDate() == null) {
  206.                 parameter.setReferenceDate(currentDate);
  207.             }
  208.             measurementParameterColumns.put(parameter.getName(), columns);
  209.             ++columns;
  210.         }

  211.         // Store providers for process noise matrices
  212.         this.covarianceMatricesProviders   = covarianceMatricesProviders;
  213.         this.measurementProcessNoiseMatrix = measurementProcessNoiseMatrix;
  214.         this.covarianceIndirection         = new int[covarianceMatricesProviders.size()][columns];
  215.         for (int k = 0; k < covarianceIndirection.length; ++k) {
  216.             final ParameterDriversList orbitDrivers      = builders.get(k).getOrbitalParametersDrivers();
  217.             final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers();
  218.             Arrays.fill(covarianceIndirection[k], -1);
  219.             int i = 0;
  220.             for (final ParameterDriver driver : orbitDrivers.getDrivers()) {
  221.                 final Integer c = orbitalParameterColumns.get(driver.getName());
  222.                 covarianceIndirection[k][i++] = (c == null) ? -1 : c.intValue();
  223.             }
  224.             for (final ParameterDriver driver : parametersDrivers.getDrivers()) {
  225.                 final Integer c = propagationParameterColumns.get(driver.getName());
  226.                 if (c != null) {
  227.                     covarianceIndirection[k][i++] = c.intValue();
  228.                 }
  229.             }
  230.             for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) {
  231.                 final Integer c = measurementParameterColumns.get(driver.getName());
  232.                 if (c != null) {
  233.                     covarianceIndirection[k][i++] = c.intValue();
  234.                 }
  235.             }
  236.         }

  237.         // Compute the scale factors
  238.         this.scale = new double[columns];
  239.         int index = 0;
  240.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  241.             scale[index++] = driver.getScale();
  242.         }
  243.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  244.             scale[index++] = driver.getScale();
  245.         }
  246.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  247.             scale[index++] = driver.getScale();
  248.         }

  249.         // Build the reference propagators and add their partial derivatives equations implementation
  250.         mappers = new DSSTJacobiansMapper[builders.size()];
  251.         updateReferenceTrajectories(getEstimatedPropagators());
  252.         this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length];
  253.         for (int i = 0; i < predictedSpacecraftStates.length; ++i) {
  254.             predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState();
  255.         };
  256.         this.correctedSpacecraftStates = predictedSpacecraftStates.clone();

  257.         // Initialize the estimated normalized state and fill its values
  258.         final RealVector correctedState      = MatrixUtils.createRealVector(columns);

  259.         int p = 0;
  260.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  261.             correctedState.setEntry(p++, driver.getNormalizedValue());
  262.         }
  263.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  264.             correctedState.setEntry(p++, driver.getNormalizedValue());
  265.         }
  266.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  267.             correctedState.setEntry(p++, driver.getNormalizedValue());
  268.         }

  269.         // Set up initial covariance
  270.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns);
  271.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {

  272.             // Number of estimated measurement parameters
  273.             final int nbMeas = estimatedMeasurementParameters.getNbParams();

  274.             // Number of estimated dynamic parameters (orbital + propagation)
  275.             final int nbDyn  = orbitsEndColumns[k] - orbitsStartColumns[k] +
  276.                                estimatedPropagationParameters[k].getNbParams();

  277.             // Covariance matrix
  278.             final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  279.             final RealMatrix noiseP = covarianceMatricesProviders.get(k).
  280.                                       getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  281.             noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  282.             if (measurementProcessNoiseMatrix != null) {
  283.                 final RealMatrix noiseM = measurementProcessNoiseMatrix.
  284.                                           getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  285.                 noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  286.             }

  287.             checkDimension(noiseK.getRowDimension(),
  288.                            builders.get(k).getOrbitalParametersDrivers(),
  289.                            builders.get(k).getPropagationParametersDrivers(),
  290.                            estimatedMeasurementsParameters);

  291.             final int[] indK = covarianceIndirection[k];
  292.             for (int i = 0; i < indK.length; ++i) {
  293.                 if (indK[i] >= 0) {
  294.                     for (int j = 0; j < indK.length; ++j) {
  295.                         if (indK[j] >= 0) {
  296.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  297.                         }
  298.                     }
  299.                 }
  300.             }

  301.         }
  302.         final RealMatrix correctedCovariance = normalizeCovarianceMatrix(physicalProcessNoise);

  303.         correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance);

  304.     }

  305.     /** Check dimension.
  306.      * @param dimension dimension to check
  307.      * @param orbitalParameters orbital parameters
  308.      * @param propagationParameters propagation parameters
  309.      * @param measurementParameters measurements parameters
  310.      */
  311.     private void checkDimension(final int dimension,
  312.                                 final ParameterDriversList orbitalParameters,
  313.                                 final ParameterDriversList propagationParameters,
  314.                                 final ParameterDriversList measurementParameters) {

  315.         // count parameters, taking care of counting all orbital parameters
  316.         // regardless of them being estimated or not
  317.         int requiredDimension = orbitalParameters.getNbParams();
  318.         for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  319.             if (driver.isSelected()) {
  320.                 ++requiredDimension;
  321.             }
  322.         }
  323.         for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  324.             if (driver.isSelected()) {
  325.                 ++requiredDimension;
  326.             }
  327.         }

  328.         if (dimension != requiredDimension) {
  329.             // there is a problem, set up an explicit error message
  330.             final StringBuilder builder = new StringBuilder();
  331.             for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
  332.                 if (builder.length() > 0) {
  333.                     builder.append(", ");
  334.                 }
  335.                 builder.append(driver.getName());
  336.             }
  337.             for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  338.                 if (driver.isSelected()) {
  339.                     builder.append(driver.getName());
  340.                 }
  341.             }
  342.             for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  343.                 if (driver.isSelected()) {
  344.                     builder.append(driver.getName());
  345.                 }
  346.             }
  347.             throw new OrekitException(OrekitMessages.DIMENSION_INCONSISTENT_WITH_PARAMETERS,
  348.                                       dimension, builder.toString());
  349.         }

  350.     }

  351.     /** {@inheritDoc} */
  352.     @Override
  353.     public RealMatrix getPhysicalStateTransitionMatrix() {
  354.         //  Un-normalize the state transition matrix (φ) from Hipparchus and return it.
  355.         // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  356.         // For each element [i,j] of normalized φ (φn), the corresponding physical value is:
  357.         // φ[i,j] = φn[i,j] * scale[i] / scale[j]

  358.         // Normalized matrix
  359.         final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix();

  360.         if (normalizedSTM == null) {
  361.             return null;
  362.         } else {
  363.             // Initialize physical matrix
  364.             final int nbParams = normalizedSTM.getRowDimension();
  365.             final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams);

  366.             // Un-normalize the matrix
  367.             for (int i = 0; i < nbParams; ++i) {
  368.                 for (int j = 0; j < nbParams; ++j) {
  369.                     physicalSTM.setEntry(i, j,
  370.                                          normalizedSTM.getEntry(i, j) * scale[i] / scale[j]);
  371.                 }
  372.             }
  373.             return physicalSTM;
  374.         }
  375.     }

  376.     /** {@inheritDoc} */
  377.     @Override
  378.     public RealMatrix getPhysicalMeasurementJacobian() {
  379.         // Un-normalize the measurement matrix (H) from Hipparchus and return it.
  380.         // H is an nxm matrix where:
  381.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  382.         //  - n is the size of the measurement being processed by the filter
  383.         // For each element [i,j] of normalized H (Hn) the corresponding physical value is:
  384.         // H[i,j] = Hn[i,j] * σ[i] / scale[j]

  385.         // Normalized matrix
  386.         final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian();

  387.         if (normalizedH == null) {
  388.             return null;
  389.         } else {
  390.             // Get current measurement sigmas
  391.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  392.             // Initialize physical matrix
  393.             final int nbLine = normalizedH.getRowDimension();
  394.             final int nbCol  = normalizedH.getColumnDimension();
  395.             final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol);

  396.             // Un-normalize the matrix
  397.             for (int i = 0; i < nbLine; ++i) {
  398.                 for (int j = 0; j < nbCol; ++j) {
  399.                     physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]);
  400.                 }
  401.             }
  402.             return physicalH;
  403.         }
  404.     }

  405.     /** {@inheritDoc} */
  406.     @Override
  407.     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
  408.         // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it.
  409.         // S is an nxn matrix where n is the size of the measurement being processed by the filter
  410.         // For each element [i,j] of normalized S (Sn) the corresponding physical value is:
  411.         // S[i,j] = Sn[i,j] * σ[i] * σ[j]

  412.         // Normalized matrix
  413.         final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance();

  414.         if (normalizedS == null) {
  415.             return null;
  416.         } else {
  417.             // Get current measurement sigmas
  418.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  419.             // Initialize physical matrix
  420.             final int nbMeas = sigmas.length;
  421.             final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas);

  422.             // Un-normalize the matrix
  423.             for (int i = 0; i < nbMeas; ++i) {
  424.                 for (int j = 0; j < nbMeas; ++j) {
  425.                     physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] *   sigmas[j]);
  426.                 }
  427.             }
  428.             return physicalS;
  429.         }
  430.     }

  431.     /** {@inheritDoc} */
  432.     @Override
  433.     public RealMatrix getPhysicalKalmanGain() {
  434.         // Un-normalize the Kalman gain (K) from Hipparchus and return it.
  435.         // K is an mxn matrix where:
  436.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  437.         //  - n is the size of the measurement being processed by the filter
  438.         // For each element [i,j] of normalized K (Kn) the corresponding physical value is:
  439.         // K[i,j] = Kn[i,j] * scale[i] / σ[j]

  440.         // Normalized matrix
  441.         final RealMatrix normalizedK = correctedEstimate.getKalmanGain();

  442.         if (normalizedK == null) {
  443.             return null;
  444.         } else {
  445.             // Get current measurement sigmas
  446.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  447.             // Initialize physical matrix
  448.             final int nbLine = normalizedK.getRowDimension();
  449.             final int nbCol  = normalizedK.getColumnDimension();
  450.             final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol);

  451.             // Un-normalize the matrix
  452.             for (int i = 0; i < nbLine; ++i) {
  453.                 for (int j = 0; j < nbCol; ++j) {
  454.                     physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]);
  455.                 }
  456.             }
  457.             return physicalK;
  458.         }
  459.     }

  460.     /** {@inheritDoc} */
  461.     @Override
  462.     public SpacecraftState[] getPredictedSpacecraftStates() {
  463.         return predictedSpacecraftStates.clone();
  464.     }

  465.     /** {@inheritDoc} */
  466.     @Override
  467.     public SpacecraftState[] getCorrectedSpacecraftStates() {
  468.         return correctedSpacecraftStates.clone();
  469.     }

  470.     /** {@inheritDoc} */
  471.     @Override
  472.     public int getCurrentMeasurementNumber() {
  473.         return currentMeasurementNumber;
  474.     }

  475.     /** {@inheritDoc} */
  476.     @Override
  477.     public AbsoluteDate getCurrentDate() {
  478.         return currentDate;
  479.     }

  480.     /** {@inheritDoc} */
  481.     @Override
  482.     public EstimatedMeasurement<?> getPredictedMeasurement() {
  483.         return predictedMeasurement;
  484.     }

  485.     /** {@inheritDoc} */
  486.     @Override
  487.     public EstimatedMeasurement<?> getCorrectedMeasurement() {
  488.         return correctedMeasurement;
  489.     }

  490.     /** {@inheritDoc} */
  491.     @Override
  492.     public RealVector getPhysicalEstimatedState() {
  493.         // Method {@link ParameterDriver#getValue()} is used to get
  494.         // the physical values of the state.
  495.         // The scales'array is used to get the size of the state vector
  496.         final RealVector physicalEstimatedState = new ArrayRealVector(scale.length);
  497.         int i = 0;
  498.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  499.             physicalEstimatedState.setEntry(i++, driver.getValue());
  500.         }
  501.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  502.             physicalEstimatedState.setEntry(i++, driver.getValue());
  503.         }
  504.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  505.             physicalEstimatedState.setEntry(i++, driver.getValue());
  506.         }

  507.         return physicalEstimatedState;
  508.     }

  509.     /** {@inheritDoc} */
  510.     @Override
  511.     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
  512.         // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it.
  513.         // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  514.         // For each element [i,j] of P the corresponding normalized value is:
  515.         // Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  516.         // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j]

  517.         // Normalized covariance matrix
  518.         final RealMatrix normalizedP = correctedEstimate.getCovariance();

  519.         // Initialize physical covariance matrix
  520.         final int nbParams = normalizedP.getRowDimension();
  521.         final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams);

  522.         // Un-normalize the covairance matrix
  523.         for (int i = 0; i < nbParams; ++i) {
  524.             for (int j = 0; j < nbParams; ++j) {
  525.                 physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]);
  526.             }
  527.         }
  528.         return physicalP;
  529.     }

  530.     /** {@inheritDoc} */
  531.     @Override
  532.     public ParameterDriversList getEstimatedOrbitalParameters() {
  533.         return allEstimatedOrbitalParameters;
  534.     }

  535.     /** {@inheritDoc} */
  536.     @Override
  537.     public ParameterDriversList getEstimatedPropagationParameters() {
  538.         return allEstimatedPropagationParameters;
  539.     }

  540.     /** {@inheritDoc} */
  541.     @Override
  542.     public ParameterDriversList getEstimatedMeasurementsParameters() {
  543.         return estimatedMeasurementsParameters;
  544.     }

  545.     /** {@inheritDoc} */
  546.     public ProcessEstimate getEstimate() {
  547.         return correctedEstimate;
  548.     }

  549.     /** {@inheritDoc} */
  550.     public DSSTPropagator[] getEstimatedPropagators() {

  551.         // Return propagators built with current instantiation of the propagator builders
  552.         final DSSTPropagator[] propagators = new DSSTPropagator[builders.size()];
  553.         for (int k = 0; k < builders.size(); ++k) {
  554.             propagators[k] = (DSSTPropagator) builders.get(k).buildPropagator(builders.get(k).getSelectedNormalizedParameters());
  555.         }
  556.         return propagators;
  557.     }

  558.     /** Get the normalized error state transition matrix (STM) from previous point to current point.
  559.      * The STM contains the partial derivatives of current state with respect to previous state.
  560.      * The  STM is an mxm matrix where m is the size of the state vector.
  561.      * m = nbOrb + nbPropag + nbMeas
  562.      * @return the normalized error state transition matrix
  563.      */
  564.     private RealMatrix getErrorStateTransitionMatrix() {

  565.         /* The state transition matrix is obtained as follows, with:
  566.          *  - Y  : Current state vector
  567.          *  - Y0 : Initial state vector
  568.          *  - Pp : Current propagation parameter
  569.          *  - Pp0: Initial propagation parameter
  570.          *  - Mp : Current measurement parameter
  571.          *  - Mp0: Initial measurement parameter
  572.          *
  573.          *       |        |         |         |   |        |        |   .    |
  574.          *       | dY/dY0 | dY/dPp  | dY/dMp  |   | dY/dY0 | dY/dPp | ..0..  |
  575.          *       |        |         |         |   |        |        |   .    |
  576.          *       |--------|---------|---------|   |--------|--------|--------|
  577.          *       |        |         |         |   |   .    | 1 0 0..|   .    |
  578.          * STM = | dP/dY0 | dP/dPp0 | dP/dMp  | = | ..0..  | 0 1 0..| ..0..  |
  579.          *       |        |         |         |   |   .    | 0 0 1..|   .    |
  580.          *       |--------|---------|---------|   |--------|--------|--------|
  581.          *       |        |         |         |   |   .    |   .    | 1 0 0..|
  582.          *       | dM/dY0 | dM/dPp0 | dM/dMp0 |   | ..0..  | ..0..  | 0 1 0..|
  583.          *       |        |         |         |   |   .    |   .    | 0 0 1..|
  584.          */

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

  587.         // loop over all orbits
  588.         for (int k = 0; k < predictedSpacecraftStates.length; ++k) {

  589.             // Short period derivatives
  590.             mappers[k].setShortPeriodJacobians(predictedSpacecraftStates[k]);

  591.             // Derivatives of the state vector with respect to initial state vector
  592.             final double[][] dYdY0 = new double[6][6];
  593.             mappers[k].getStateJacobian(predictedSpacecraftStates[k], dYdY0 );

  594.             // Fill upper left corner (dY/dY0)
  595.             final List<ParameterDriversList.DelegatingDriver> drivers =
  596.                             builders.get(k).getOrbitalParametersDrivers().getDrivers();
  597.             for (int i = 0; i < dYdY0.length; ++i) {
  598.                 if (drivers.get(i).isSelected()) {
  599.                     int jOrb = orbitsStartColumns[k];
  600.                     for (int j = 0; j < dYdY0[i].length; ++j) {
  601.                         if (drivers.get(j).isSelected()) {
  602.                             stm.setEntry(i, jOrb++, dYdY0[i][j]);
  603.                         }
  604.                     }
  605.                 }
  606.             }

  607.             // Derivatives of the state vector with respect to propagation parameters
  608.             final int nbParams = estimatedPropagationParameters[k].getNbParams();
  609.             if (nbParams > 0) {
  610.                 final double[][] dYdPp  = new double[6][nbParams];
  611.                 mappers[k].getParametersJacobian(predictedSpacecraftStates[k], dYdPp);

  612.                 // Fill 1st row, 2nd column (dY/dPp)
  613.                 for (int i = 0; i < dYdPp.length; ++i) {
  614.                     for (int j = 0; j < nbParams; ++j) {
  615.                         stm.setEntry(i, orbitsEndColumns[k] + j, dYdPp[i][j]);
  616.                     }
  617.                 }

  618.             }

  619.         }

  620.         // Normalization of the STM
  621.         // normalized(STM)ij = STMij*Sj/Si
  622.         for (int i = 0; i < scale.length; i++) {
  623.             for (int j = 0; j < scale.length; j++ ) {
  624.                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
  625.             }
  626.         }

  627.         // Return the error state transition matrix
  628.         return stm;

  629.     }

  630.     /** Get the normalized measurement matrix H.
  631.      * H contains the partial derivatives of the measurement with respect to the state.
  632.      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
  633.      * @return the normalized measurement matrix H
  634.      */
  635.     private RealMatrix getMeasurementMatrix() {

  636.         // Observed measurement characteristics
  637.         final SpacecraftState[]      evaluationStates    = predictedMeasurement.getStates();
  638.         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
  639.         final double[] sigma  = observedMeasurement.getTheoreticalStandardDeviation();

  640.         // Initialize measurement matrix H: nxm
  641.         // n: Number of measurements in current measurement
  642.         // m: State vector size
  643.         final RealMatrix measurementMatrix = MatrixUtils.
  644.                         createRealMatrix(observedMeasurement.getDimension(),
  645.                                          correctedEstimate.getState().getDimension());

  646.         // loop over all orbits involved in the measurement
  647.         for (int k = 0; k < evaluationStates.length; ++k) {
  648.             final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex();

  649.             // Predicted orbit
  650.             final Orbit predictedOrbit = evaluationStates[k].getOrbit();

  651.             // Measurement matrix's columns related to orbital parameters
  652.             // ----------------------------------------------------------

  653.             // Partial derivatives of the current Cartesian coordinates with respect to current orbital state
  654.             final double[][] aCY = new double[6][6];
  655.             predictedOrbit.getJacobianWrtParameters(builders.get(p).getPositionAngle(), aCY);   //dC/dY
  656.             final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);

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

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

  661.             // Fill the normalized measurement matrix's columns related to estimated orbital parameters
  662.             for (int i = 0; i < dMdY.getRowDimension(); ++i) {
  663.                 int jOrb = orbitsStartColumns[p];
  664.                 for (int j = 0; j < dMdY.getColumnDimension(); ++j) {
  665.                     final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j);
  666.                     if (driver.isSelected()) {
  667.                         measurementMatrix.setEntry(i, jOrb++,
  668.                                                    dMdY.getEntry(i, j) / sigma[i] * driver.getScale());
  669.                     }
  670.                 }
  671.             }

  672.             // Normalized measurement matrix's columns related to propagation parameters
  673.             // --------------------------------------------------------------

  674.             // Jacobian of the measurement with respect to propagation parameters
  675.             final int nbParams = estimatedPropagationParameters[p].getNbParams();
  676.             if (nbParams > 0) {
  677.                 // Short period derivatives
  678.                 mappers[p].setShortPeriodJacobians(evaluationStates[k]);
  679.                 final double[][] aYPp  = new double[6][nbParams];
  680.                 mappers[p].getParametersJacobian(evaluationStates[k], aYPp);
  681.                 final RealMatrix dYdPp = new Array2DRowRealMatrix(aYPp, false);
  682.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);
  683.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  684.                     for (int j = 0; j < nbParams; ++j) {
  685.                         final ParameterDriver delegating = estimatedPropagationParameters[p].getDrivers().get(j);
  686.                         measurementMatrix.setEntry(i, propagationParameterColumns.get(delegating.getName()),
  687.                                                    dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale());
  688.                     }
  689.                 }
  690.             }

  691.             // Normalized measurement matrix's columns related to measurement parameters
  692.             // --------------------------------------------------------------

  693.             // Jacobian of the measurement with respect to measurement parameters
  694.             // Gather the measurement parameters linked to current measurement
  695.             for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  696.                 if (driver.isSelected()) {
  697.                     // Derivatives of current measurement w/r to selected measurement parameter
  698.                     final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);

  699.                     // Check that the measurement parameter is managed by the filter
  700.                     if (measurementParameterColumns.get(driver.getName()) != null) {
  701.                         // Column of the driver in the measurement matrix
  702.                         final int driverColumn = measurementParameterColumns.get(driver.getName());

  703.                         // Fill the corresponding indexes of the measurement matrix
  704.                         for (int i = 0; i < aMPm.length; ++i) {
  705.                             measurementMatrix.setEntry(i, driverColumn,
  706.                                                        aMPm[i] / sigma[i] * driver.getScale());
  707.                         }
  708.                     }
  709.                 }
  710.             }
  711.         }

  712.         // Return the normalized measurement matrix
  713.         return measurementMatrix;

  714.     }


  715.     /** Update the reference trajectories using the propagators as input.
  716.      * @param propagators The new propagators to use
  717.      */
  718.     private void updateReferenceTrajectories(final DSSTPropagator[] propagators) {

  719.         // Update the reference trajectory propagator
  720.         referenceTrajectories = propagators;

  721.         for (int k = 0; k < propagators.length; ++k) {
  722.             // Link the partial derivatives to this new propagator
  723.             final String equationName = KalmanEstimator.class.getName() + "-derivatives-" + k;
  724.             final DSSTPartialDerivativesEquations pde = new DSSTPartialDerivativesEquations(equationName, referenceTrajectories[k], propagationType);

  725.             // Reset the Jacobians
  726.             final SpacecraftState rawState = referenceTrajectories[k].getInitialState();
  727.             final SpacecraftState stateWithDerivatives = pde.setInitialJacobians(rawState);
  728.             referenceTrajectories[k].setInitialState(stateWithDerivatives, stateType);
  729.             mappers[k] = pde.getMapper();
  730.         }

  731.     }

  732.     /** Normalize a covariance matrix.
  733.      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  734.      * For each element [i,j] of P the corresponding normalized value is:
  735.      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  736.      * @param physicalCovarianceMatrix The "physical" covariance matrix in input
  737.      * @return the normalized covariance matrix
  738.      */
  739.     private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) {

  740.         // Initialize output matrix
  741.         final int nbParams = physicalCovarianceMatrix.getRowDimension();
  742.         final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);

  743.         // Normalize the state matrix
  744.         for (int i = 0; i < nbParams; ++i) {
  745.             for (int j = 0; j < nbParams; ++j) {
  746.                 normalizedCovarianceMatrix.setEntry(i, j,
  747.                                                     physicalCovarianceMatrix.getEntry(i, j) /
  748.                                                     (scale[i] * scale[j]));
  749.             }
  750.         }
  751.         return normalizedCovarianceMatrix;
  752.     }

  753.     /** Set and apply a dynamic outlier filter on a measurement.<p>
  754.      * Loop on the modifiers to see if a dynamic outlier filter needs to be applied.<p>
  755.      * Compute the sigma array using the matrix in input and set the filter.<p>
  756.      * Apply the filter by calling the modify method on the estimated measurement.<p>
  757.      * Reset the filter.
  758.      * @param measurement measurement to filter
  759.      * @param innovationCovarianceMatrix So called innovation covariance matrix S, with:<p>
  760.      *        S = H.Ppred.Ht + R<p>
  761.      *        Where:<p>
  762.      *         - H is the normalized measurement matrix (Ht its transpose)<p>
  763.      *         - Ppred is the normalized predicted covariance matrix<p>
  764.      *         - R is the normalized measurement noise matrix
  765.      * @param <T> the type of measurement
  766.      */
  767.     private <T extends ObservedMeasurement<T>> void applyDynamicOutlierFilter(final EstimatedMeasurement<T> measurement,
  768.                                                                               final RealMatrix innovationCovarianceMatrix) {

  769.         // Observed measurement associated to the predicted measurement
  770.         final ObservedMeasurement<T> observedMeasurement = measurement.getObservedMeasurement();

  771.         // Check if a dynamic filter was added to the measurement
  772.         // If so, update its sigma value and apply it
  773.         for (EstimationModifier<T> modifier : observedMeasurement.getModifiers()) {
  774.             if (modifier instanceof DynamicOutlierFilter<?>) {
  775.                 final DynamicOutlierFilter<T> dynamicOutlierFilter = (DynamicOutlierFilter<T>) modifier;

  776.                 // Initialize the values of the sigma array used in the dynamic filter
  777.                 final double[] sigmaDynamic     = new double[innovationCovarianceMatrix.getColumnDimension()];
  778.                 final double[] sigmaMeasurement = observedMeasurement.getTheoreticalStandardDeviation();

  779.                 // Set the sigma value for each element of the measurement
  780.                 // Here we do use the value suggested by David A. Vallado (see [1]§10.6):
  781.                 // sigmaDynamic[i] = sqrt(diag(S))*sigma[i]
  782.                 // With S = H.Ppred.Ht + R
  783.                 // Where:
  784.                 //  - S is the measurement error matrix in input
  785.                 //  - H is the normalized measurement matrix (Ht its transpose)
  786.                 //  - Ppred is the normalized predicted covariance matrix
  787.                 //  - R is the normalized measurement noise matrix
  788.                 //  - sigma[i] is the theoretical standard deviation of the ith component of the measurement.
  789.                 //    It is used here to un-normalize the value before it is filtered
  790.                 for (int i = 0; i < sigmaDynamic.length; i++) {
  791.                     sigmaDynamic[i] = FastMath.sqrt(innovationCovarianceMatrix.getEntry(i, i)) * sigmaMeasurement[i];
  792.                 }
  793.                 dynamicOutlierFilter.setSigma(sigmaDynamic);

  794.                 // Apply the modifier on the estimated measurement
  795.                 modifier.modify(measurement);

  796.                 // Re-initialize the value of the filter for the next measurement of the same type
  797.                 dynamicOutlierFilter.setSigma(null);
  798.             }
  799.         }
  800.     }

  801.     /** {@inheritDoc} */
  802.     @Override
  803.     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
  804.                                            final MeasurementDecorator measurement) {

  805.         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  806.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  807.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  808.             if (driver.getReferenceDate() == null) {
  809.                 driver.setReferenceDate(builders.get(0).getInitialOrbitDate());
  810.             }
  811.         }

  812.         ++currentMeasurementNumber;
  813.         currentDate = measurement.getObservedMeasurement().getDate();

  814.         // Note:
  815.         // - n = size of the current measurement
  816.         //  Example:
  817.         //   * 1 for Range, RangeRate and TurnAroundRange
  818.         //   * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination)
  819.         //   * 6 for Position/Velocity
  820.         // - m = size of the state vector. n = nbOrb + nbPropag + nbMeas

  821.         // Predict the state vector (mx1)
  822.         final RealVector predictedState = predictState(observedMeasurement.getDate());

  823.         // Get the error state transition matrix (mxm)
  824.         final RealMatrix stateTransitionMatrix = getErrorStateTransitionMatrix();

  825.         // Predict the measurement based on predicted spacecraft state
  826.         // Compute the innovations (i.e. residuals of the predicted measurement)
  827.         // ------------------------------------------------------------

  828.         // Predicted measurement
  829.         // Note: here the "iteration/evaluation" formalism from the batch LS method
  830.         // is twisted to fit the need of the Kalman filter.
  831.         // The number of "iterations" is actually the number of measurements processed by the filter
  832.         // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement.
  833.         predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  834.                                                             currentMeasurementNumber,
  835.                                                             filterRelevant(observedMeasurement, predictedSpacecraftStates));

  836.         // Normalized measurement matrix (nxm)
  837.         final RealMatrix measurementMatrix = getMeasurementMatrix();

  838.         // compute process noise matrix
  839.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(),
  840.                                                                              previousState.getDimension());
  841.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {

  842.             // Number of estimated measurement parameters
  843.             final int nbMeas = estimatedMeasurementsParameters.getNbParams();

  844.             // Number of estimated dynamic parameters (orbital + propagation)
  845.             final int nbDyn  = orbitsEndColumns[k] - orbitsStartColumns[k] +
  846.                                estimatedPropagationParameters[k].getNbParams();

  847.             // Covariance matrix
  848.             final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  849.             final RealMatrix noiseP = covarianceMatricesProviders.get(k).
  850.                                       getProcessNoiseMatrix(correctedSpacecraftStates[k],
  851.                                                             predictedSpacecraftStates[k]);
  852.             noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  853.             if (measurementProcessNoiseMatrix != null) {
  854.                 final RealMatrix noiseM = measurementProcessNoiseMatrix.
  855.                                           getProcessNoiseMatrix(correctedSpacecraftStates[k],
  856.                                                                 predictedSpacecraftStates[k]);
  857.                 noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  858.             }

  859.             checkDimension(noiseK.getRowDimension(),
  860.                            builders.get(k).getOrbitalParametersDrivers(),
  861.                            builders.get(k).getPropagationParametersDrivers(),
  862.                            estimatedMeasurementsParameters);
  863.             final int[] indK = covarianceIndirection[k];
  864.             for (int i = 0; i < indK.length; ++i) {
  865.                 if (indK[i] >= 0) {
  866.                     for (int j = 0; j < indK.length; ++j) {
  867.                         if (indK[j] >= 0) {
  868.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  869.                         }
  870.                     }
  871.                 }
  872.             }

  873.         }
  874.         final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(physicalProcessNoise);

  875.         return new NonLinearEvolution(measurement.getTime(), predictedState,
  876.                                       stateTransitionMatrix, normalizedProcessNoise, measurementMatrix);

  877.     }

  878.     /** {@inheritDoc} */
  879.     @Override
  880.     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
  881.                                     final RealMatrix innovationCovarianceMatrix) {

  882.         // Apply the dynamic outlier filter, if it exists
  883.         applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
  884.         if (predictedMeasurement.getStatus() == EstimatedMeasurement.Status.REJECTED)  {
  885.             // set innovation to null to notify filter measurement is rejected
  886.             return null;
  887.         } else {
  888.             // Normalized innovation of the measurement (Nx1)
  889.             final double[] observed  = predictedMeasurement.getObservedMeasurement().getObservedValue();
  890.             final double[] estimated = predictedMeasurement.getEstimatedValue();
  891.             final double[] sigma     = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
  892.             final double[] residuals = new double[observed.length];

  893.             for (int i = 0; i < observed.length; i++) {
  894.                 residuals[i] = (observed[i] - estimated[i]) / sigma[i];
  895.             }
  896.             return MatrixUtils.createRealVector(residuals);
  897.         }
  898.     }

  899.     /** {@inheritDoc} */
  900.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  901.                                    final ProcessEstimate estimate) {
  902.         // Update the parameters with the estimated state
  903.         // The min/max values of the parameters are handled by the ParameterDriver implementation
  904.         correctedEstimate = estimate;
  905.         updateParameters();

  906.         // Get the estimated propagator (mirroring parameter update in the builder)
  907.         // and the estimated spacecraft state
  908.         final DSSTPropagator[] estimatedPropagators = getEstimatedPropagators();
  909.         for (int k = 0; k < estimatedPropagators.length; ++k) {
  910.             correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState();
  911.         }

  912.         // Compute the estimated measurement using estimated spacecraft state
  913.         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  914.                                                             currentMeasurementNumber,
  915.                                                             filterRelevant(observedMeasurement, correctedSpacecraftStates));
  916.         // Update the trajectory
  917.         // ---------------------
  918.         updateReferenceTrajectories(estimatedPropagators);

  919.     }

  920.     /** Filter relevant states for a measurement.
  921.      * @param observedMeasurement measurement to consider
  922.      * @param allStates all states
  923.      * @return array containing only the states relevant to the measurement
  924.      * @since 10.1
  925.      */
  926.     private SpacecraftState[] filterRelevant(final ObservedMeasurement<?> observedMeasurement, final SpacecraftState[] allStates) {
  927.         final List<ObservableSatellite> satellites = observedMeasurement.getSatellites();
  928.         final SpacecraftState[] relevantStates = new SpacecraftState[satellites.size()];
  929.         for (int i = 0; i < relevantStates.length; ++i) {
  930.             relevantStates[i] = allStates[satellites.get(i).getPropagatorIndex()];
  931.         }
  932.         return relevantStates;
  933.     }

  934.     /** Set the predicted normalized state vector.
  935.      * The predicted/propagated orbit is used to update the state vector
  936.      * @param date prediction date
  937.      * @return predicted state
  938.      */
  939.     private RealVector predictState(final AbsoluteDate date) {

  940.         // Predicted state is initialized to previous estimated state
  941.         final RealVector predictedState = correctedEstimate.getState().copy();

  942.         // Orbital parameters counter
  943.         int jOrb = 0;

  944.         for (int k = 0; k < predictedSpacecraftStates.length; ++k) {

  945.             // Propagate the reference trajectory to measurement date
  946.             predictedSpacecraftStates[k] = referenceTrajectories[k].propagate(date);

  947.             // Update the builder with the predicted orbit
  948.             // This updates the orbital drivers with the values of the predicted orbit
  949.             builders.get(k).resetOrbit(predictedSpacecraftStates[k].getOrbit());

  950.             // The orbital parameters in the state vector are replaced with their predicted values
  951.             // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation)
  952.             // As the propagator builder was previously updated with the predicted orbit,
  953.             // the selected orbital drivers are already up to date with the prediction
  954.             for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  955.                 if (orbitalDriver.isSelected()) {
  956.                     predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue());
  957.                 }
  958.             }

  959.         }

  960.         return predictedState;

  961.     }

  962.     /** Update the estimated parameters after the correction phase of the filter.
  963.      * The min/max allowed values are handled by the parameter themselves.
  964.      */
  965.     private void updateParameters() {
  966.         final RealVector correctedState = correctedEstimate.getState();
  967.         int i = 0;
  968.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  969.             // let the parameter handle min/max clipping
  970.             driver.setNormalizedValue(correctedState.getEntry(i));
  971.             correctedState.setEntry(i++, driver.getNormalizedValue());
  972.         }
  973.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  974.             // let the parameter handle min/max clipping
  975.             driver.setNormalizedValue(correctedState.getEntry(i));
  976.             correctedState.setEntry(i++, driver.getNormalizedValue());
  977.         }
  978.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  979.             // let the parameter handle min/max clipping
  980.             driver.setNormalizedValue(correctedState.getEntry(i));
  981.             correctedState.setEntry(i++, driver.getNormalizedValue());
  982.         }
  983.     }

  984. }