AbstractKalmanModel.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.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.filtering.kalman.extended.NonLinearProcess;
  27. import org.hipparchus.linear.Array2DRowRealMatrix;
  28. import org.hipparchus.linear.ArrayRealVector;
  29. import org.hipparchus.linear.MatrixUtils;
  30. import org.hipparchus.linear.RealMatrix;
  31. import org.hipparchus.linear.RealVector;
  32. import org.hipparchus.util.FastMath;
  33. import org.orekit.errors.OrekitException;
  34. import org.orekit.errors.OrekitMessages;
  35. import org.orekit.estimation.measurements.EstimatedMeasurement;
  36. import org.orekit.estimation.measurements.EstimationModifier;
  37. import org.orekit.estimation.measurements.ObservableSatellite;
  38. import org.orekit.estimation.measurements.ObservedMeasurement;
  39. import org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter;
  40. import org.orekit.orbits.Orbit;
  41. import org.orekit.propagation.MatricesHarvester;
  42. import org.orekit.propagation.PropagationType;
  43. import org.orekit.propagation.Propagator;
  44. import org.orekit.propagation.SpacecraftState;
  45. import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder;
  46. import org.orekit.propagation.integration.AbstractJacobiansMapper;
  47. import org.orekit.time.AbsoluteDate;
  48. import org.orekit.utils.ParameterDriver;
  49. import org.orekit.utils.ParameterDriversList;
  50. import org.orekit.utils.ParameterDriversList.DelegatingDriver;

  51. /** Abstract class defining the process model dynamics to use with a {@link KalmanEstimator}.
  52.  * @author Romain Gerbaud
  53.  * @author Maxime Journot
  54.  * @author Bryan Cazabonne
  55.  * @author Thomas Paulet
  56.  * @since 11.0
  57.  */
  58. public abstract class AbstractKalmanModel implements KalmanEstimation, NonLinearProcess<MeasurementDecorator> {

  59.     /** Builders for propagators. */
  60.     private final List<OrbitDeterminationPropagatorBuilder> builders;

  61.     /** Estimated orbital parameters. */
  62.     private final ParameterDriversList allEstimatedOrbitalParameters;

  63.     /** Estimated propagation drivers. */
  64.     private final ParameterDriversList allEstimatedPropagationParameters;

  65.     /** Per-builder estimated orbita parameters drivers.
  66.      * @since 11.1
  67.      */
  68.     private final ParameterDriversList[] estimatedOrbitalParameters;

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

  71.     /** Estimated measurements parameters. */
  72.     private final ParameterDriversList estimatedMeasurementsParameters;

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

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

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

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

  81.     /** Providers for covariance matrices. */
  82.     private final List<CovarianceMatrixProvider> covarianceMatricesProviders;

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

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

  87.     /** Scaling factors. */
  88.     private final double[] scale;

  89.     /** Harvesters for extracting Jacobians from integrated states. */
  90.     private MatricesHarvester[] harvesters;

  91.     /** Propagators for the reference trajectories, up to current date. */
  92.     private Propagator[] referenceTrajectories;

  93.     /** Current corrected estimate. */
  94.     private ProcessEstimate correctedEstimate;

  95.     /** Current number of measurement. */
  96.     private int currentMeasurementNumber;

  97.     /** Reference date. */
  98.     private AbsoluteDate referenceDate;

  99.     /** Current date. */
  100.     private AbsoluteDate currentDate;

  101.     /** Predicted spacecraft states. */
  102.     private SpacecraftState[] predictedSpacecraftStates;

  103.     /** Corrected spacecraft states. */
  104.     private SpacecraftState[] correctedSpacecraftStates;

  105.     /** Predicted measurement. */
  106.     private EstimatedMeasurement<?> predictedMeasurement;

  107.     /** Corrected measurement. */
  108.     private EstimatedMeasurement<?> correctedMeasurement;

  109.     /** Type of the orbit used for the propagation.*/
  110.     private PropagationType propagationType;

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

  113.     /** Kalman process model constructor (package private).
  114.      * This constructor is used whenever state type and propagation type do not matter.
  115.      * It is used for {@link KalmanModel} and {@link TLEKalmanModel}.
  116.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  117.      * @param covarianceMatricesProviders providers for covariance matrices
  118.      * @param estimatedMeasurementParameters measurement parameters to estimate
  119.      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
  120.      * @param harvesters harvesters for extracting Jacobians from integrated states
  121.      */
  122.     protected AbstractKalmanModel(final List<OrbitDeterminationPropagatorBuilder> propagatorBuilders,
  123.                                   final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  124.                                   final ParameterDriversList estimatedMeasurementParameters,
  125.                                   final CovarianceMatrixProvider measurementProcessNoiseMatrix,
  126.                                   final MatricesHarvester[] harvesters) {
  127.         this(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters,
  128.              measurementProcessNoiseMatrix, harvesters, PropagationType.MEAN, PropagationType.MEAN);
  129.     }

  130.     /** Kalman process model constructor (package private).
  131.      * This constructor is used whenever propagation type and/or state type are to be specified.
  132.      * It is used for {@link DSSTKalmanModel}.
  133.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  134.      * @param covarianceMatricesProviders providers for covariance matrices
  135.      * @param estimatedMeasurementParameters measurement parameters to estimate
  136.      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
  137.      * @param harvesters harvesters for extracting Jacobians from integrated states
  138.      * @param propagationType type of the orbit used for the propagation (mean or osculating), applicable only for DSST
  139.      * @param stateType type of the elements used to define the orbital state (mean or osculating), applicable only for DSST
  140.      */
  141.     protected AbstractKalmanModel(final List<OrbitDeterminationPropagatorBuilder> propagatorBuilders,
  142.                                   final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  143.                                   final ParameterDriversList estimatedMeasurementParameters,
  144.                                   final CovarianceMatrixProvider measurementProcessNoiseMatrix,
  145.                                   final MatricesHarvester[] harvesters,
  146.                                   final PropagationType propagationType,
  147.                                   final PropagationType stateType) {

  148.         this.builders                        = propagatorBuilders;
  149.         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
  150.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
  151.         this.currentMeasurementNumber        = 0;
  152.         this.referenceDate                   = propagatorBuilders.get(0).getInitialOrbitDate();
  153.         this.currentDate                     = referenceDate;
  154.         this.propagationType                 = propagationType;
  155.         this.stateType                       = stateType;

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

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

  205.         // Populate the map of propagation drivers' columns and update the total number of columns
  206.         propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  207.         for (final String driverName : estimatedPropagationParametersNames) {
  208.             propagationParameterColumns.put(driverName, columns);
  209.             ++columns;
  210.         }

  211.         // Populate the map of measurement drivers' columns and update the total number of columns
  212.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  213.             if (parameter.getReferenceDate() == null) {
  214.                 parameter.setReferenceDate(currentDate);
  215.             }
  216.             measurementParameterColumns.put(parameter.getName(), columns);
  217.             ++columns;
  218.         }

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

  245.         // Compute the scale factors
  246.         this.scale = new double[columns];
  247.         int index = 0;
  248.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  249.             scale[index++] = driver.getScale();
  250.         }
  251.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  252.             scale[index++] = driver.getScale();
  253.         }
  254.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  255.             scale[index++] = driver.getScale();
  256.         }

  257.         // Build the reference propagators and add their partial derivatives equations implementation
  258.         this.harvesters = harvesters.clone();
  259.         updateReferenceTrajectories(getEstimatedPropagators(), propagationType, stateType);
  260.         this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length];
  261.         for (int i = 0; i < predictedSpacecraftStates.length; ++i) {
  262.             predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState();
  263.         };
  264.         this.correctedSpacecraftStates = predictedSpacecraftStates.clone();

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

  267.         int p = 0;
  268.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  269.             correctedState.setEntry(p++, driver.getNormalizedValue());
  270.         }
  271.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  272.             correctedState.setEntry(p++, driver.getNormalizedValue());
  273.         }
  274.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  275.             correctedState.setEntry(p++, driver.getNormalizedValue());
  276.         }

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

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

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

  285.             // Covariance matrix
  286.             final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  287.             final RealMatrix noiseP = covarianceMatricesProviders.get(k).
  288.                                       getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  289.             noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  290.             if (measurementProcessNoiseMatrix != null) {
  291.                 final RealMatrix noiseM = measurementProcessNoiseMatrix.
  292.                                           getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  293.                 noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  294.             }

  295.             checkDimension(noiseK.getRowDimension(),
  296.                            builders.get(k).getOrbitalParametersDrivers(),
  297.                            builders.get(k).getPropagationParametersDrivers(),
  298.                            estimatedMeasurementsParameters);

  299.             final int[] indK = covarianceIndirection[k];
  300.             for (int i = 0; i < indK.length; ++i) {
  301.                 if (indK[i] >= 0) {
  302.                     for (int j = 0; j < indK.length; ++j) {
  303.                         if (indK[j] >= 0) {
  304.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  305.                         }
  306.                     }
  307.                 }
  308.             }

  309.         }
  310.         final RealMatrix correctedCovariance = normalizeCovarianceMatrix(physicalProcessNoise);

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

  312.     }

  313.     /** Update the reference trajectories using the propagators as input.
  314.      * @param propagators The new propagators to use
  315.      * @param pType propagationType type of the orbit used for the propagation (mean or osculating)
  316.      * @param sType type of the elements used to define the orbital state (mean or osculating)
  317.      */
  318.     protected abstract void updateReferenceTrajectories(Propagator[] propagators,
  319.                                                         PropagationType pType,
  320.                                                         PropagationType sType);

  321.     /** Not used anymore.
  322.      * @param mapper Jacobian mapper to calculate short period perturbations
  323.      * @param state state used to calculate short period perturbations
  324.      * @deprecated as of 11.1, not used anymore
  325.      */
  326.     protected void analyticalDerivativeComputations(final AbstractJacobiansMapper mapper, final SpacecraftState state) {
  327.         // nothing by default
  328.     }

  329.     /** Check dimension.
  330.      * @param dimension dimension to check
  331.      * @param orbitalParameters orbital parameters
  332.      * @param propagationParameters propagation parameters
  333.      * @param measurementParameters measurements parameters
  334.      */
  335.     private void checkDimension(final int dimension,
  336.                                 final ParameterDriversList orbitalParameters,
  337.                                 final ParameterDriversList propagationParameters,
  338.                                 final ParameterDriversList measurementParameters) {

  339.         // count parameters, taking care of counting all orbital parameters
  340.         // regardless of them being estimated or not
  341.         int requiredDimension = orbitalParameters.getNbParams();
  342.         for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  343.             if (driver.isSelected()) {
  344.                 ++requiredDimension;
  345.             }
  346.         }
  347.         for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  348.             if (driver.isSelected()) {
  349.                 ++requiredDimension;
  350.             }
  351.         }

  352.         if (dimension != requiredDimension) {
  353.             // there is a problem, set up an explicit error message
  354.             final StringBuilder builder = new StringBuilder();
  355.             for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
  356.                 if (builder.length() > 0) {
  357.                     builder.append(", ");
  358.                 }
  359.                 builder.append(driver.getName());
  360.             }
  361.             for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  362.                 if (driver.isSelected()) {
  363.                     builder.append(driver.getName());
  364.                 }
  365.             }
  366.             for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  367.                 if (driver.isSelected()) {
  368.                     builder.append(driver.getName());
  369.                 }
  370.             }
  371.             throw new OrekitException(OrekitMessages.DIMENSION_INCONSISTENT_WITH_PARAMETERS,
  372.                                       dimension, builder.toString());
  373.         }

  374.     }

  375.     /** {@inheritDoc} */
  376.     @Override
  377.     public RealMatrix getPhysicalStateTransitionMatrix() {
  378.         //  Un-normalize the state transition matrix (φ) from Hipparchus and return it.
  379.         // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  380.         // For each element [i,j] of normalized φ (φn), the corresponding physical value is:
  381.         // φ[i,j] = φn[i,j] * scale[i] / scale[j]

  382.         // Normalized matrix
  383.         final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix();

  384.         if (normalizedSTM == null) {
  385.             return null;
  386.         } else {
  387.             // Initialize physical matrix
  388.             final int nbParams = normalizedSTM.getRowDimension();
  389.             final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams);

  390.             // Un-normalize the matrix
  391.             for (int i = 0; i < nbParams; ++i) {
  392.                 for (int j = 0; j < nbParams; ++j) {
  393.                     physicalSTM.setEntry(i, j,
  394.                                          normalizedSTM.getEntry(i, j) * scale[i] / scale[j]);
  395.                 }
  396.             }
  397.             return physicalSTM;
  398.         }
  399.     }

  400.     /** {@inheritDoc} */
  401.     @Override
  402.     public RealMatrix getPhysicalMeasurementJacobian() {
  403.         // Un-normalize the measurement matrix (H) from Hipparchus and return it.
  404.         // H is an nxm matrix where:
  405.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  406.         //  - n is the size of the measurement being processed by the filter
  407.         // For each element [i,j] of normalized H (Hn) the corresponding physical value is:
  408.         // H[i,j] = Hn[i,j] * σ[i] / scale[j]

  409.         // Normalized matrix
  410.         final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian();

  411.         if (normalizedH == null) {
  412.             return null;
  413.         } else {
  414.             // Get current measurement sigmas
  415.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  416.             // Initialize physical matrix
  417.             final int nbLine = normalizedH.getRowDimension();
  418.             final int nbCol  = normalizedH.getColumnDimension();
  419.             final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol);

  420.             // Un-normalize the matrix
  421.             for (int i = 0; i < nbLine; ++i) {
  422.                 for (int j = 0; j < nbCol; ++j) {
  423.                     physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]);
  424.                 }
  425.             }
  426.             return physicalH;
  427.         }
  428.     }

  429.     /** {@inheritDoc} */
  430.     @Override
  431.     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
  432.         // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it.
  433.         // S is an nxn matrix where n is the size of the measurement being processed by the filter
  434.         // For each element [i,j] of normalized S (Sn) the corresponding physical value is:
  435.         // S[i,j] = Sn[i,j] * σ[i] * σ[j]

  436.         // Normalized matrix
  437.         final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance();

  438.         if (normalizedS == null) {
  439.             return null;
  440.         } else {
  441.             // Get current measurement sigmas
  442.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  443.             // Initialize physical matrix
  444.             final int nbMeas = sigmas.length;
  445.             final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas);

  446.             // Un-normalize the matrix
  447.             for (int i = 0; i < nbMeas; ++i) {
  448.                 for (int j = 0; j < nbMeas; ++j) {
  449.                     physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] *   sigmas[j]);
  450.                 }
  451.             }
  452.             return physicalS;
  453.         }
  454.     }

  455.     /** {@inheritDoc} */
  456.     @Override
  457.     public RealMatrix getPhysicalKalmanGain() {
  458.         // Un-normalize the Kalman gain (K) from Hipparchus and return it.
  459.         // K is an mxn matrix where:
  460.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  461.         //  - n is the size of the measurement being processed by the filter
  462.         // For each element [i,j] of normalized K (Kn) the corresponding physical value is:
  463.         // K[i,j] = Kn[i,j] * scale[i] / σ[j]

  464.         // Normalized matrix
  465.         final RealMatrix normalizedK = correctedEstimate.getKalmanGain();

  466.         if (normalizedK == null) {
  467.             return null;
  468.         } else {
  469.             // Get current measurement sigmas
  470.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  471.             // Initialize physical matrix
  472.             final int nbLine = normalizedK.getRowDimension();
  473.             final int nbCol  = normalizedK.getColumnDimension();
  474.             final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol);

  475.             // Un-normalize the matrix
  476.             for (int i = 0; i < nbLine; ++i) {
  477.                 for (int j = 0; j < nbCol; ++j) {
  478.                     physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]);
  479.                 }
  480.             }
  481.             return physicalK;
  482.         }
  483.     }

  484.     /** {@inheritDoc} */
  485.     @Override
  486.     public SpacecraftState[] getPredictedSpacecraftStates() {
  487.         return predictedSpacecraftStates.clone();
  488.     }

  489.     /** {@inheritDoc} */
  490.     @Override
  491.     public SpacecraftState[] getCorrectedSpacecraftStates() {
  492.         return correctedSpacecraftStates.clone();
  493.     }

  494.     /** {@inheritDoc} */
  495.     @Override
  496.     public int getCurrentMeasurementNumber() {
  497.         return currentMeasurementNumber;
  498.     }

  499.     /** {@inheritDoc} */
  500.     @Override
  501.     public AbsoluteDate getCurrentDate() {
  502.         return currentDate;
  503.     }

  504.     /** {@inheritDoc} */
  505.     @Override
  506.     public EstimatedMeasurement<?> getPredictedMeasurement() {
  507.         return predictedMeasurement;
  508.     }

  509.     /** {@inheritDoc} */
  510.     @Override
  511.     public EstimatedMeasurement<?> getCorrectedMeasurement() {
  512.         return correctedMeasurement;
  513.     }

  514.     /** {@inheritDoc} */
  515.     @Override
  516.     public RealVector getPhysicalEstimatedState() {
  517.         // Method {@link ParameterDriver#getValue()} is used to get
  518.         // the physical values of the state.
  519.         // The scales'array is used to get the size of the state vector
  520.         final RealVector physicalEstimatedState = new ArrayRealVector(scale.length);
  521.         int i = 0;
  522.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  523.             physicalEstimatedState.setEntry(i++, driver.getValue());
  524.         }
  525.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  526.             physicalEstimatedState.setEntry(i++, driver.getValue());
  527.         }
  528.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  529.             physicalEstimatedState.setEntry(i++, driver.getValue());
  530.         }

  531.         return physicalEstimatedState;
  532.     }

  533.     /** {@inheritDoc} */
  534.     @Override
  535.     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
  536.         // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it.
  537.         // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  538.         // For each element [i,j] of P the corresponding normalized value is:
  539.         // Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  540.         // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j]

  541.         // Normalized covariance matrix
  542.         final RealMatrix normalizedP = correctedEstimate.getCovariance();

  543.         // Initialize physical covariance matrix
  544.         final int nbParams = normalizedP.getRowDimension();
  545.         final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams);

  546.         // Un-normalize the covairance matrix
  547.         for (int i = 0; i < nbParams; ++i) {
  548.             for (int j = 0; j < nbParams; ++j) {
  549.                 physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]);
  550.             }
  551.         }
  552.         return physicalP;
  553.     }

  554.     /** {@inheritDoc} */
  555.     @Override
  556.     public ParameterDriversList getEstimatedOrbitalParameters() {
  557.         return allEstimatedOrbitalParameters;
  558.     }

  559.     /** {@inheritDoc} */
  560.     @Override
  561.     public ParameterDriversList getEstimatedPropagationParameters() {
  562.         return allEstimatedPropagationParameters;
  563.     }

  564.     /** {@inheritDoc} */
  565.     @Override
  566.     public ParameterDriversList getEstimatedMeasurementsParameters() {
  567.         return estimatedMeasurementsParameters;
  568.     }

  569.     /** Get the current corrected estimate.
  570.      * @return current corrected estimate
  571.      */
  572.     public ProcessEstimate getEstimate() {
  573.         return correctedEstimate;
  574.     }

  575.     /** Get the normalized error state transition matrix (STM) from previous point to current point.
  576.      * The STM contains the partial derivatives of current state with respect to previous state.
  577.      * The  STM is an mxm matrix where m is the size of the state vector.
  578.      * m = nbOrb + nbPropag + nbMeas
  579.      * @return the normalized error state transition matrix
  580.      */
  581.     private RealMatrix getErrorStateTransitionMatrix() {

  582.         /* The state transition matrix is obtained as follows, with:
  583.          *  - Y  : Current state vector
  584.          *  - Y0 : Initial state vector
  585.          *  - Pp : Current propagation parameter
  586.          *  - Pp0: Initial propagation parameter
  587.          *  - Mp : Current measurement parameter
  588.          *  - Mp0: Initial measurement parameter
  589.          *
  590.          *       |        |         |         |   |        |        |   .    |
  591.          *       | dY/dY0 | dY/dPp  | dY/dMp  |   | dY/dY0 | dY/dPp | ..0..  |
  592.          *       |        |         |         |   |        |        |   .    |
  593.          *       |--------|---------|---------|   |--------|--------|--------|
  594.          *       |        |         |         |   |   .    | 1 0 0..|   .    |
  595.          * STM = | dP/dY0 | dP/dPp0 | dP/dMp  | = | ..0..  | 0 1 0..| ..0..  |
  596.          *       |        |         |         |   |   .    | 0 0 1..|   .    |
  597.          *       |--------|---------|---------|   |--------|--------|--------|
  598.          *       |        |         |         |   |   .    |   .    | 1 0 0..|
  599.          *       | dM/dY0 | dM/dPp0 | dM/dMp0 |   | ..0..  | ..0..  | 0 1 0..|
  600.          *       |        |         |         |   |   .    |   .    | 0 0 1..|
  601.          */

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

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

  606.             // Indexes
  607.             final int[] indK = covarianceIndirection[k];

  608.             // Reset reference (for example compute short periodic terms in DSST)
  609.             harvesters[k].setReferenceState(predictedSpacecraftStates[k]);

  610.             // Derivatives of the state vector with respect to initial state vector
  611.             final int nbOrbParams = estimatedOrbitalParameters[k].getNbParams();
  612.             if (nbOrbParams > 0) {
  613.                 final RealMatrix dYdY0 = harvesters[k].getStateTransitionMatrix(predictedSpacecraftStates[k]);

  614.                 // Fill upper left corner (dY/dY0)
  615.                 for (int i = 0; i < dYdY0.getRowDimension(); ++i) {
  616.                     for (int j = 0; j < nbOrbParams; ++j) {
  617.                         stm.setEntry(indK[i], indK[j], dYdY0.getEntry(i, j));
  618.                     }
  619.                 }
  620.             }

  621.             // Derivatives of the state vector with respect to propagation parameters
  622.             final int nbParams = estimatedPropagationParameters[k].getNbParams();
  623.             if (nbParams > 0) {
  624.                 final RealMatrix dYdPp = harvesters[k].getParametersJacobian(predictedSpacecraftStates[k]);

  625.                 // Fill 1st row, 2nd column (dY/dPp)
  626.                 for (int i = 0; i < dYdPp.getRowDimension(); ++i) {
  627.                     for (int j = 0; j < nbParams; ++j) {
  628.                         stm.setEntry(indK[i], indK[j + 6], dYdPp.getEntry(i, j));
  629.                     }
  630.                 }

  631.             }

  632.         }

  633.         // Normalization of the STM
  634.         // normalized(STM)ij = STMij*Sj/Si
  635.         for (int i = 0; i < scale.length; i++) {
  636.             for (int j = 0; j < scale.length; j++ ) {
  637.                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
  638.             }
  639.         }

  640.         // Return the error state transition matrix
  641.         return stm;

  642.     }

  643.     /** Get the normalized measurement matrix H.
  644.      * H contains the partial derivatives of the measurement with respect to the state.
  645.      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
  646.      * @return the normalized measurement matrix H
  647.      */
  648.     private RealMatrix getMeasurementMatrix() {

  649.         // Observed measurement characteristics
  650.         final SpacecraftState[]      evaluationStates    = predictedMeasurement.getStates();
  651.         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
  652.         final double[] sigma  = observedMeasurement.getTheoreticalStandardDeviation();

  653.         // Initialize measurement matrix H: nxm
  654.         // n: Number of measurements in current measurement
  655.         // m: State vector size
  656.         final RealMatrix measurementMatrix = MatrixUtils.
  657.                         createRealMatrix(observedMeasurement.getDimension(),
  658.                                          correctedEstimate.getState().getDimension());

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

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

  664.             // Measurement matrix's columns related to orbital parameters
  665.             // ----------------------------------------------------------

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

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

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

  674.             // Fill the normalized measurement matrix's columns related to estimated orbital parameters
  675.             for (int i = 0; i < dMdY.getRowDimension(); ++i) {
  676.                 int jOrb = orbitsStartColumns[p];
  677.                 for (int j = 0; j < dMdY.getColumnDimension(); ++j) {
  678.                     final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j);
  679.                     if (driver.isSelected()) {
  680.                         measurementMatrix.setEntry(i, jOrb++,
  681.                                                    dMdY.getEntry(i, j) / sigma[i] * driver.getScale());
  682.                     }
  683.                 }
  684.             }

  685.             // Normalized measurement matrix's columns related to propagation parameters
  686.             // --------------------------------------------------------------

  687.             // Jacobian of the measurement with respect to propagation parameters
  688.             final int nbParams = estimatedPropagationParameters[p].getNbParams();
  689.             if (nbParams > 0) {
  690.                 final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]);
  691.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);
  692.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  693.                     for (int j = 0; j < nbParams; ++j) {
  694.                         final ParameterDriver delegating = estimatedPropagationParameters[p].getDrivers().get(j);
  695.                         measurementMatrix.setEntry(i, propagationParameterColumns.get(delegating.getName()),
  696.                                                    dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale());
  697.                     }
  698.                 }
  699.             }

  700.             // Normalized measurement matrix's columns related to measurement parameters
  701.             // --------------------------------------------------------------

  702.             // Jacobian of the measurement with respect to measurement parameters
  703.             // Gather the measurement parameters linked to current measurement
  704.             for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  705.                 if (driver.isSelected()) {
  706.                     // Derivatives of current measurement w/r to selected measurement parameter
  707.                     final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);

  708.                     // Check that the measurement parameter is managed by the filter
  709.                     if (measurementParameterColumns.get(driver.getName()) != null) {
  710.                         // Column of the driver in the measurement matrix
  711.                         final int driverColumn = measurementParameterColumns.get(driver.getName());

  712.                         // Fill the corresponding indexes of the measurement matrix
  713.                         for (int i = 0; i < aMPm.length; ++i) {
  714.                             measurementMatrix.setEntry(i, driverColumn,
  715.                                                        aMPm[i] / sigma[i] * driver.getScale());
  716.                         }
  717.                     }
  718.                 }
  719.             }
  720.         }

  721.         // Return the normalized measurement matrix
  722.         return measurementMatrix;

  723.     }

  724.     /** Normalize a covariance matrix.
  725.      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  726.      * For each element [i,j] of P the corresponding normalized value is:
  727.      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  728.      * @param physicalCovarianceMatrix The "physical" covariance matrix in input
  729.      * @return the normalized covariance matrix
  730.      */
  731.     private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) {

  732.         // Initialize output matrix
  733.         final int nbParams = physicalCovarianceMatrix.getRowDimension();
  734.         final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);

  735.         // Normalize the state matrix
  736.         for (int i = 0; i < nbParams; ++i) {
  737.             for (int j = 0; j < nbParams; ++j) {
  738.                 normalizedCovarianceMatrix.setEntry(i, j,
  739.                                                     physicalCovarianceMatrix.getEntry(i, j) /
  740.                                                     (scale[i] * scale[j]));
  741.             }
  742.         }
  743.         return normalizedCovarianceMatrix;
  744.     }

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

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

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

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

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

  786.                 // Apply the modifier on the estimated measurement
  787.                 modifier.modify(measurement);

  788.                 // Re-initialize the value of the filter for the next measurement of the same type
  789.                 dynamicOutlierFilter.setSigma(null);
  790.             }
  791.         }
  792.     }

  793.     /** {@inheritDoc} */
  794.     @Override
  795.     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
  796.                                            final MeasurementDecorator measurement) {

  797.         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  798.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  799.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  800.             if (driver.getReferenceDate() == null) {
  801.                 driver.setReferenceDate(builders.get(0).getInitialOrbitDate());
  802.             }
  803.         }

  804.         ++currentMeasurementNumber;
  805.         currentDate = measurement.getObservedMeasurement().getDate();

  806.         // Note:
  807.         // - n = size of the current measurement
  808.         //  Example:
  809.         //   * 1 for Range, RangeRate and TurnAroundRange
  810.         //   * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination)
  811.         //   * 6 for Position/Velocity
  812.         // - m = size of the state vector. n = nbOrb + nbPropag + nbMeas

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

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

  817.         // Predict the measurement based on predicted spacecraft state
  818.         // Compute the innovations (i.e. residuals of the predicted measurement)
  819.         // ------------------------------------------------------------

  820.         // Predicted measurement
  821.         // Note: here the "iteration/evaluation" formalism from the batch LS method
  822.         // is twisted to fit the need of the Kalman filter.
  823.         // The number of "iterations" is actually the number of measurements processed by the filter
  824.         // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement.
  825.         predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  826.                                                             currentMeasurementNumber,
  827.                                                             filterRelevant(observedMeasurement, predictedSpacecraftStates));

  828.         // Normalized measurement matrix (nxm)
  829.         final RealMatrix measurementMatrix = getMeasurementMatrix();

  830.         // compute process noise matrix
  831.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(),
  832.                                                                              previousState.getDimension());
  833.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {

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

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

  839.             // Covariance matrix
  840.             final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  841.             final RealMatrix noiseP = covarianceMatricesProviders.get(k).
  842.                                       getProcessNoiseMatrix(correctedSpacecraftStates[k],
  843.                                                             predictedSpacecraftStates[k]);
  844.             noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  845.             if (measurementProcessNoiseMatrix != null) {
  846.                 final RealMatrix noiseM = measurementProcessNoiseMatrix.
  847.                                           getProcessNoiseMatrix(correctedSpacecraftStates[k],
  848.                                                                 predictedSpacecraftStates[k]);
  849.                 noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  850.             }

  851.             checkDimension(noiseK.getRowDimension(),
  852.                            builders.get(k).getOrbitalParametersDrivers(),
  853.                            builders.get(k).getPropagationParametersDrivers(),
  854.                            estimatedMeasurementsParameters);

  855.             final int[] indK = covarianceIndirection[k];
  856.             for (int i = 0; i < indK.length; ++i) {
  857.                 if (indK[i] >= 0) {
  858.                     for (int j = 0; j < indK.length; ++j) {
  859.                         if (indK[j] >= 0) {
  860.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  861.                         }
  862.                     }
  863.                 }
  864.             }

  865.         }
  866.         final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(physicalProcessNoise);

  867.         return new NonLinearEvolution(measurement.getTime(), predictedState,
  868.                                       stateTransitionMatrix, normalizedProcessNoise, measurementMatrix);

  869.     }


  870.     /** {@inheritDoc} */
  871.     @Override
  872.     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
  873.                                     final RealMatrix innovationCovarianceMatrix) {

  874.         // Apply the dynamic outlier filter, if it exists
  875.         applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
  876.         if (predictedMeasurement.getStatus() == EstimatedMeasurement.Status.REJECTED)  {
  877.             // set innovation to null to notify filter measurement is rejected
  878.             return null;
  879.         } else {
  880.             // Normalized innovation of the measurement (Nx1)
  881.             final double[] observed  = predictedMeasurement.getObservedMeasurement().getObservedValue();
  882.             final double[] estimated = predictedMeasurement.getEstimatedValue();
  883.             final double[] sigma     = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
  884.             final double[] residuals = new double[observed.length];

  885.             for (int i = 0; i < observed.length; i++) {
  886.                 residuals[i] = (observed[i] - estimated[i]) / sigma[i];
  887.             }
  888.             return MatrixUtils.createRealVector(residuals);
  889.         }
  890.     }

  891.     /** Finalize estimation.
  892.      * @param observedMeasurement measurement that has just been processed
  893.      * @param estimate corrected estimate
  894.      */
  895.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  896.                                    final ProcessEstimate estimate) {
  897.         // Update the parameters with the estimated state
  898.         // The min/max values of the parameters are handled by the ParameterDriver implementation
  899.         correctedEstimate = estimate;
  900.         updateParameters();

  901.         // Get the estimated propagator (mirroring parameter update in the builder)
  902.         // and the estimated spacecraft state
  903.         final Propagator[] estimatedPropagators = getEstimatedPropagators();
  904.         for (int k = 0; k < estimatedPropagators.length; ++k) {
  905.             correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState();
  906.         }

  907.         // Compute the estimated measurement using estimated spacecraft state
  908.         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  909.                                                             currentMeasurementNumber,
  910.                                                             filterRelevant(observedMeasurement, correctedSpacecraftStates));
  911.         // Update the trajectory
  912.         // ---------------------
  913.         updateReferenceTrajectories(estimatedPropagators, propagationType, stateType);

  914.     }

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

  929.     /** Set the predicted normalized state vector.
  930.      * The predicted/propagated orbit is used to update the state vector
  931.      * @param date prediction date
  932.      * @return predicted state
  933.      */
  934.     private RealVector predictState(final AbsoluteDate date) {

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

  937.         // Orbital parameters counter
  938.         int jOrb = 0;

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

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

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

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

  954.         }

  955.         return predictedState;

  956.     }

  957.     /** Update the estimated parameters after the correction phase of the filter.
  958.      * The min/max allowed values are handled by the parameter themselves.
  959.      */
  960.     private void updateParameters() {
  961.         final RealVector correctedState = correctedEstimate.getState();
  962.         int i = 0;
  963.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  964.             // let the parameter handle min/max clipping
  965.             driver.setNormalizedValue(correctedState.getEntry(i));
  966.             correctedState.setEntry(i++, driver.getNormalizedValue());
  967.         }
  968.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().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 : getEstimatedMeasurementsParameters().getDrivers()) {
  974.             // let the parameter handle min/max clipping
  975.             driver.setNormalizedValue(correctedState.getEntry(i));
  976.             correctedState.setEntry(i++, driver.getNormalizedValue());
  977.         }
  978.     }

  979.     /** Getter for the propagators.
  980.      * @return the propagators
  981.      */
  982.     public List<OrbitDeterminationPropagatorBuilder> getBuilders() {
  983.         return builders;
  984.     }

  985.     /** Getter for the reference trajectories.
  986.      * @return the referencetrajectories
  987.      */
  988.     public Propagator[] getReferenceTrajectories() {
  989.         return referenceTrajectories.clone();
  990.     }

  991.     /** Setter for the reference trajectories.
  992.      * @param referenceTrajectories the reference trajectories to be setted
  993.      */
  994.     public void setReferenceTrajectories(final Propagator[] referenceTrajectories) {
  995.         this.referenceTrajectories = referenceTrajectories.clone();
  996.     }

  997.     /** Getter for the jacobian mappers.
  998.      * @return the jacobian mappers
  999.      * @deprecated as of 11.1, not used anymore
  1000.      */
  1001.     @Deprecated
  1002.     public AbstractJacobiansMapper[] getMappers() {
  1003.         return null;
  1004.     }

  1005.     /** Setter for the jacobian mappers.
  1006.      * @param mappers the jacobian mappers to set
  1007.      * @deprecated as of 11.1, replaced by {@link #setHarvesters(MatricesHarvester[])}
  1008.      */
  1009.     @Deprecated
  1010.     public void setMappers(final AbstractJacobiansMapper[] mappers) {
  1011.         setHarvesters(mappers);
  1012.     }

  1013.     /** Setter for the jacobian harvesters.
  1014.      * @param harvesters the jacobian harvesters to set
  1015.      * @since 11.1
  1016.      */
  1017.     public void setHarvesters(final MatricesHarvester[] harvesters) {
  1018.         this.harvesters = harvesters.clone();
  1019.     }

  1020.     /** Get the propagators estimated with the values set in the propagators builders.
  1021.      * @return propagators based on the current values in the builder
  1022.      */
  1023.     public Propagator[] getEstimatedPropagators() {
  1024.         // Return propagators built with current instantiation of the propagator builders
  1025.         final Propagator[] propagators = new Propagator[getBuilders().size()];
  1026.         for (int k = 0; k < getBuilders().size(); ++k) {
  1027.             propagators[k] = getBuilders().get(k).buildPropagator(getBuilders().get(k).getSelectedNormalizedParameters());
  1028.         }
  1029.         return propagators;
  1030.     }

  1031. }