Model.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.OrekitExceptionWrapper;
  35. import org.orekit.errors.OrekitMessages;
  36. import org.orekit.estimation.measurements.EstimatedMeasurement;
  37. import org.orekit.estimation.measurements.EstimationModifier;
  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.SpacecraftState;
  42. import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
  43. import org.orekit.propagation.numerical.JacobiansMapper;
  44. import org.orekit.propagation.numerical.NumericalPropagator;
  45. import org.orekit.propagation.numerical.PartialDerivativesEquations;
  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.  * @author Romain Gerbaud
  52.  * @author Maxime Journot
  53.  * @since 9.2
  54.  */
  55. class Model implements KalmanEstimation, NonLinearProcess<MeasurementDecorator> {

  56.     /** Builders for propagators. */
  57.     private final List<NumericalPropagatorBuilder> builders;

  58.     /** Estimated orbital parameters. */
  59.     private final ParameterDriversList allEstimatedOrbitalParameters;

  60.     /** Estimated propagation drivers. */
  61.     private final ParameterDriversList allEstimatedPropagationParameters;

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

  64.     /** Estimated measurements parameters. */
  65.     private final ParameterDriversList estimatedMeasurementsParameters;

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

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

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

  72.     /** Providers for covariance matrices. */
  73.     private final List<CovarianceMatrixProvider> covarianceMatricesProviders;

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

  76.     /** Scaling factors. */
  77.     private final double[] scale;

  78.     /** Mappers for extracting Jacobians from integrated states. */
  79.     private final JacobiansMapper[] mappers;

  80.     /** Propagators for the reference trajectories, up to current date. */
  81.     private NumericalPropagator[] referenceTrajectories;

  82.     /** Current corrected estimate. */
  83.     private ProcessEstimate correctedEstimate;

  84.     /** Current number of measurement. */
  85.     private int currentMeasurementNumber;

  86.     /** Reference date. */
  87.     private AbsoluteDate referenceDate;

  88.     /** Current date. */
  89.     private AbsoluteDate currentDate;

  90.     /** Predicted spacecraft states. */
  91.     private SpacecraftState[] predictedSpacecraftStates;

  92.     /** Corrected spacecraft states. */
  93.     private SpacecraftState[] correctedSpacecraftStates;

  94.     /** Predicted measurement. */
  95.     private EstimatedMeasurement<?> predictedMeasurement;

  96.     /** Corrected measurement. */
  97.     private EstimatedMeasurement<?> correctedMeasurement;

  98.     /** Kalman process model constructor (package private).
  99.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  100.      * @param covarianceMatricesProviders providers for covariance matrices
  101.      * @param estimatedMeasurementParameters measurement parameters to estimate
  102.      * @throws OrekitException propagation exception.
  103.      */
  104.     Model(final List<NumericalPropagatorBuilder> propagatorBuilders,
  105.           final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  106.           final ParameterDriversList estimatedMeasurementParameters)
  107.         throws OrekitException {

  108.         this.builders                        = propagatorBuilders;
  109.         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
  110.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
  111.         this.currentMeasurementNumber        = 0;
  112.         this.referenceDate                   = propagatorBuilders.get(0).getInitialOrbitDate();
  113.         this.currentDate                     = referenceDate;

  114.         final Map<String, Integer> orbitalParameterColumns = new HashMap<>(6 * builders.size());
  115.         orbitsStartColumns      = new int[builders.size()];
  116.         orbitsEndColumns        = new int[builders.size()];
  117.         int columns = 0;
  118.         allEstimatedOrbitalParameters = new ParameterDriversList();
  119.         for (int k = 0; k < builders.size(); ++k) {
  120.             orbitsStartColumns[k] = columns;
  121.             final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null;
  122.             for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  123.                 if (driver.getReferenceDate() == null) {
  124.                     driver.setReferenceDate(currentDate);
  125.                 }
  126.                 if (suffix != null && !driver.getName().endsWith(suffix)) {
  127.                     // we add suffix only conditionally because the method may already have been called
  128.                     // and suffixes may have already been appended
  129.                     driver.setName(driver.getName() + suffix);
  130.                 }
  131.                 if (driver.isSelected()) {
  132.                     allEstimatedOrbitalParameters.add(driver);
  133.                     orbitalParameterColumns.put(driver.getName(), columns++);
  134.                 }
  135.             }
  136.             orbitsEndColumns[k] = columns;
  137.         }

  138.         // Gather all the propagation drivers names in a list
  139.         allEstimatedPropagationParameters = new ParameterDriversList();
  140.         estimatedPropagationParameters    = new ParameterDriversList[builders.size()];
  141.         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
  142.         for (int k = 0; k < builders.size(); ++k) {
  143.             estimatedPropagationParameters[k] = new ParameterDriversList();
  144.             for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) {
  145.                 if (driver.getReferenceDate() == null) {
  146.                     driver.setReferenceDate(currentDate);
  147.                 }
  148.                 if (driver.isSelected()) {
  149.                     allEstimatedPropagationParameters.add(driver);
  150.                     estimatedPropagationParameters[k].add(driver);
  151.                     final String driverName = driver.getName();
  152.                     // Add the driver name if it has not been added yet
  153.                     if (!estimatedPropagationParametersNames.contains(driverName)) {
  154.                         estimatedPropagationParametersNames.add(driverName);
  155.                     }
  156.                 }
  157.             }
  158.         }
  159.         estimatedPropagationParametersNames.sort(Comparator.naturalOrder());

  160.         // Populate the map of propagation drivers' columns and update the total number of columns
  161.         final Map<String, Integer> propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  162.         for (final String driverName : estimatedPropagationParametersNames) {
  163.             propagationParameterColumns.put(driverName, columns);
  164.             ++columns;
  165.         }

  166.         // Populate the map of measurement drivers' columns and update the total number of columns
  167.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  168.             if (parameter.getReferenceDate() == null) {
  169.                 parameter.setReferenceDate(currentDate);
  170.             }
  171.             measurementParameterColumns.put(parameter.getName(), columns);
  172.             ++columns;
  173.         }

  174.         // Store providers for process noise matrices
  175.         this.covarianceMatricesProviders = covarianceMatricesProviders;
  176.         this.covarianceIndirection       = new int[covarianceMatricesProviders.size()][columns];
  177.         for (int k = 0; k < covarianceIndirection.length; ++k) {
  178.             final ParameterDriversList orbitDrivers      = builders.get(k).getOrbitalParametersDrivers();
  179.             final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers();
  180.             Arrays.fill(covarianceIndirection[k], -1);
  181.             int i = 0;
  182.             for (final ParameterDriver driver : orbitDrivers.getDrivers()) {
  183.                 final Integer c = orbitalParameterColumns.get(driver.getName());
  184.                 covarianceIndirection[k][i++] = (c == null) ? -1 : c.intValue();
  185.             }
  186.             for (final ParameterDriver driver : parametersDrivers.getDrivers()) {
  187.                 final Integer c = propagationParameterColumns.get(driver.getName());
  188.                 if (c != null) {
  189.                     covarianceIndirection[k][i++] = c.intValue();
  190.                 }
  191.             }
  192.             for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) {
  193.                 final Integer c = measurementParameterColumns.get(driver.getName());
  194.                 if (c != null) {
  195.                     covarianceIndirection[k][i++] = c.intValue();
  196.                 }
  197.             }
  198.         }

  199.         // Compute the scale factors
  200.         this.scale = new double[columns];
  201.         int index = 0;
  202.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  203.             scale[index++] = driver.getScale();
  204.         }
  205.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  206.             scale[index++] = driver.getScale();
  207.         }
  208.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  209.             scale[index++] = driver.getScale();
  210.         }

  211.         // Build the reference propagators and add their partial derivatives equations implementation
  212.         mappers = new JacobiansMapper[builders.size()];
  213.         updateReferenceTrajectories(getEstimatedPropagators());
  214.         this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length];
  215.         for (int i = 0; i < predictedSpacecraftStates.length; ++i) {
  216.             predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState();
  217.         };
  218.         this.correctedSpacecraftStates = predictedSpacecraftStates.clone();

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

  221.         int p = 0;
  222.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  223.             correctedState.setEntry(p++, driver.getNormalizedValue());
  224.         }
  225.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  226.             correctedState.setEntry(p++, driver.getNormalizedValue());
  227.         }
  228.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  229.             correctedState.setEntry(p++, driver.getNormalizedValue());
  230.         }

  231.         // Set up initial covariance
  232.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns);
  233.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {
  234.             final RealMatrix noiseK = covarianceMatricesProviders.get(k).
  235.                                       getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  236.             checkDimension(noiseK.getRowDimension(),
  237.                            builders.get(k).getOrbitalParametersDrivers(),
  238.                            builders.get(k).getPropagationParametersDrivers(),
  239.                            estimatedMeasurementsParameters);
  240.             final int[] indK = covarianceIndirection[k];
  241.             for (int i = 0; i < indK.length; ++i) {
  242.                 if (indK[i] >= 0) {
  243.                     for (int j = 0; j < indK.length; ++j) {
  244.                         if (indK[j] >= 0) {
  245.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  246.                         }
  247.                     }
  248.                 }
  249.             }

  250.         }
  251.         final RealMatrix correctedCovariance = normalizeCovarianceMatrix(physicalProcessNoise);

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

  253.     }

  254.     /** Check dimension.
  255.      * @param dimension dimension to check
  256.      * @param orbitalParameters orbital parameters
  257.      * @param propagationParameters propagation parameters
  258.      * @param measurementParameters measurements parameters
  259.      * @exception OrekitException if dimension != requiredDimension
  260.      */
  261.     private void checkDimension(final int dimension,
  262.                                 final ParameterDriversList orbitalParameters,
  263.                                 final ParameterDriversList propagationParameters,
  264.                                 final ParameterDriversList measurementParameters) throws OrekitException {

  265.         // count parameters, taking care of counting all orbital parameters
  266.         // regardless of them being estimated or not
  267.         int requiredDimension = orbitalParameters.getNbParams();
  268.         for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  269.             if (driver.isSelected()) {
  270.                 ++requiredDimension;
  271.             }
  272.         }
  273.         for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  274.             if (driver.isSelected()) {
  275.                 ++requiredDimension;
  276.             }
  277.         }

  278.         if (dimension != requiredDimension) {
  279.             // there is a problem, set up an explicit error message
  280.             final StringBuilder builder = new StringBuilder();
  281.             for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
  282.                 if (builder.length() > 0) {
  283.                     builder.append(", ");
  284.                 }
  285.                 builder.append(driver.getName());
  286.             }
  287.             for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  288.                 if (driver.isSelected()) {
  289.                     builder.append(driver.getName());
  290.                 }
  291.             }
  292.             for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  293.                 if (driver.isSelected()) {
  294.                     builder.append(driver.getName());
  295.                 }
  296.             }
  297.             throw new OrekitException(OrekitMessages.DIMENSION_INCONSISTENT_WITH_PARAMETERS,
  298.                                       dimension, builder.toString());
  299.         }

  300.     }

  301.     /** {@inheritDoc} */
  302.     @Override
  303.     public SpacecraftState[] getPredictedSpacecraftStates() {
  304.         return predictedSpacecraftStates.clone();
  305.     }

  306.     /** {@inheritDoc} */
  307.     @Override
  308.     public SpacecraftState[] getCorrectedSpacecraftStates() {
  309.         return correctedSpacecraftStates.clone();
  310.     }

  311.     /** {@inheritDoc} */
  312.     @Override
  313.     public int getCurrentMeasurementNumber() {
  314.         return currentMeasurementNumber;
  315.     }

  316.     /** {@inheritDoc} */
  317.     @Override
  318.     public AbsoluteDate getCurrentDate() {
  319.         return currentDate;
  320.     }

  321.     /** {@inheritDoc} */
  322.     @Override
  323.     public EstimatedMeasurement<?> getPredictedMeasurement() {
  324.         return predictedMeasurement;
  325.     }

  326.     /** {@inheritDoc} */
  327.     @Override
  328.     public EstimatedMeasurement<?> getCorrectedMeasurement() {
  329.         return correctedMeasurement;
  330.     }

  331.     /** {@inheritDoc} */
  332.     @Override
  333.     public RealVector getPhysicalEstimatedState() {
  334.         return unNormalizeStateVector(correctedEstimate.getState());
  335.     }

  336.     /** {@inheritDoc} */
  337.     @Override
  338.     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
  339.         return unNormalizeCovarianceMatrix(correctedEstimate.getCovariance());
  340.     }

  341.     /** {@inheritDoc} */
  342.     @Override
  343.     public ParameterDriversList getEstimatedOrbitalParameters() {
  344.         return allEstimatedOrbitalParameters;
  345.     }

  346.     /** {@inheritDoc} */
  347.     @Override
  348.     public ParameterDriversList getEstimatedPropagationParameters() {
  349.         return allEstimatedPropagationParameters;
  350.     }

  351.     /** {@inheritDoc} */
  352.     @Override
  353.     public ParameterDriversList getEstimatedMeasurementsParameters() {
  354.         return estimatedMeasurementsParameters;
  355.     }

  356.     /** Get the current corrected estimate.
  357.      * @return current corrected estimate
  358.      */
  359.     public ProcessEstimate getEstimate() {
  360.         return correctedEstimate;
  361.     }

  362.     /** Get the propagators estimated with the values set in the propagators builders.
  363.      * @return numerical propagators based on the current values in the builder
  364.      * @throws OrekitException if propagators cannot be build
  365.      */
  366.     public NumericalPropagator[] getEstimatedPropagators()
  367.         throws OrekitException {

  368.         // Return propagators built with current instantiation of the propagator builders
  369.         final NumericalPropagator[] propagators = new NumericalPropagator[builders.size()];
  370.         for (int k = 0; k < builders.size(); ++k) {
  371.             propagators[k] = builders.get(k).buildPropagator(builders.get(k).getSelectedNormalizedParameters());
  372.         }
  373.         return propagators;
  374.     }

  375.     /** Get the normalized error state transition matrix (STM) from previous point to current point.
  376.      * The STM contains the partial derivatives of current state with respect to previous state.
  377.      * The  STM is an mxm matrix where m is the size of the state vector.
  378.      * m = nbOrb + nbPropag + nbMeas
  379.      * @return the normalized error state transition matrix
  380.      * @throws OrekitException if Jacobians cannot be computed
  381.      */
  382.     private RealMatrix getErrorStateTransitionMatrix()
  383.         throws OrekitException {

  384.         /* The state transition matrix is obtained as follows, with:
  385.          *  - Y  : Current state vector
  386.          *  - Y0 : Initial state vector
  387.          *  - Pp : Current propagation parameter
  388.          *  - Pp0: Initial propagation parameter
  389.          *  - Mp : Current measurement parameter
  390.          *  - Mp0: Initial measurement parameter
  391.          *
  392.          *       |        |         |         |   |        |        |   .    |
  393.          *       | dY/dY0 | dY/dPp  | dY/dMp  |   | dY/dY0 | dY/dPp | ..0..  |
  394.          *       |        |         |         |   |        |        |   .    |
  395.          *       |--------|---------|---------|   |--------|--------|--------|
  396.          *       |        |         |         |   |   .    | 1 0 0..|   .    |
  397.          * STM = | dP/dY0 | dP/dPp0 | dP/dMp  | = | ..0..  | 0 1 0..| ..0..  |
  398.          *       |        |         |         |   |   .    | 0 0 1..|   .    |
  399.          *       |--------|---------|---------|   |--------|--------|--------|
  400.          *       |        |         |         |   |   .    |   .    | 1 0 0..|
  401.          *       | dM/dY0 | dM/dPp0 | dM/dMp0 |   | ..0..  | ..0..  | 0 1 0..|
  402.          *       |        |         |         |   |   .    |   .    | 0 0 1..|
  403.          */

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

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

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

  411.             // Fill upper left corner (dY/dY0)
  412.             final List<ParameterDriversList.DelegatingDriver> drivers =
  413.                             builders.get(k).getOrbitalParametersDrivers().getDrivers();
  414.             for (int i = 0; i < dYdY0.length; ++i) {
  415.                 if (drivers.get(i).isSelected()) {
  416.                     int jOrb = orbitsStartColumns[k];
  417.                     for (int j = 0; j < dYdY0[i].length; ++j) {
  418.                         if (drivers.get(j).isSelected()) {
  419.                             stm.setEntry(i, jOrb++, dYdY0[i][j]);
  420.                         }
  421.                     }
  422.                 }
  423.             }

  424.             // Derivatives of the state vector with respect to propagation parameters
  425.             final int nbParams = estimatedPropagationParameters[k].getNbParams();
  426.             if (nbParams > 0) {
  427.                 final double[][] dYdPp  = new double[6][nbParams];
  428.                 mappers[k].getParametersJacobian(predictedSpacecraftStates[k], dYdPp);

  429.                 // Fill 1st row, 2nd column (dY/dPp)
  430.                 for (int i = 0; i < dYdPp.length; ++i) {
  431.                     for (int j = 0; j < nbParams; ++j) {
  432.                         stm.setEntry(i, orbitsEndColumns[k] + j, dYdPp[i][j]);
  433.                     }
  434.                 }

  435.             }

  436.         }

  437.         // Normalization of the STM
  438.         // normalized(STM)ij = STMij*Sj/Si
  439.         for (int i = 0; i < scale.length; i++) {
  440.             for (int j = 0; j < scale.length; j++ ) {
  441.                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
  442.             }
  443.         }

  444.         // Return the error state transition matrix
  445.         return stm;

  446.     }

  447.     /** Get the normalized measurement matrix H.
  448.      * H contains the partial derivatives of the measurement with respect to the state.
  449.      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
  450.      * @return the normalized measurement matrix H
  451.      * @throws OrekitException if Jacobians cannot be computed
  452.      */
  453.     private RealMatrix getMeasurementMatrix()
  454.         throws OrekitException {

  455.         // Observed measurement characteristics
  456.         final SpacecraftState[]      evaluationStates    = predictedMeasurement.getStates();
  457.         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
  458.         final double[] sigma  = observedMeasurement.getTheoreticalStandardDeviation();

  459.         // Initialize measurement matrix H: nxm
  460.         // n: Number of measurements in current measurement
  461.         // m: State vector size
  462.         final RealMatrix measurementMatrix = MatrixUtils.
  463.                         createRealMatrix(observedMeasurement.getDimension(),
  464.                                          correctedEstimate.getState().getDimension());

  465.         // loop over all orbits involved in the measurement
  466.         for (int k = 0; k < evaluationStates.length; ++k) {
  467.             final int p = observedMeasurement.getPropagatorsIndices().get(k);

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

  470.             // Measurement matrix's columns related to orbital parameters
  471.             // ----------------------------------------------------------

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

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

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

  480.             // Fill the normalized measurement matrix's columns related to estimated orbital parameters
  481.             for (int i = 0; i < dMdY.getRowDimension(); ++i) {
  482.                 int jOrb = orbitsStartColumns[p];
  483.                 for (int j = 0; j < dMdY.getColumnDimension(); ++j) {
  484.                     final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j);
  485.                     if (driver.isSelected()) {
  486.                         measurementMatrix.setEntry(i, jOrb++,
  487.                                                    dMdY.getEntry(i, j) / sigma[i] * driver.getScale());
  488.                     }
  489.                 }
  490.             }

  491.             // Normalized measurement matrix's columns related to propagation parameters
  492.             // --------------------------------------------------------------

  493.             // Jacobian of the measurement with respect to propagation parameters
  494.             final int nbParams = estimatedPropagationParameters[p].getNbParams();
  495.             if (nbParams > 0) {
  496.                 final double[][] aYPp  = new double[6][nbParams];
  497.                 mappers[p].getParametersJacobian(evaluationStates[k], aYPp);
  498.                 final RealMatrix dYdPp = new Array2DRowRealMatrix(aYPp, false);
  499.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);
  500.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  501.                     for (int j = 0; j < nbParams; ++j) {
  502.                         final ParameterDriver delegating = allEstimatedPropagationParameters.getDrivers().get(j);
  503.                         measurementMatrix.setEntry(i, orbitsEndColumns[p] + j,
  504.                                                    dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale());
  505.                     }
  506.                 }
  507.             }

  508.             // Normalized measurement matrix's columns related to measurement parameters
  509.             // --------------------------------------------------------------

  510.             // Jacobian of the measurement with respect to measurement parameters
  511.             // Gather the measurement parameters linked to current measurement
  512.             for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  513.                 if (driver.isSelected()) {
  514.                     // Derivatives of current measurement w/r to selected measurement parameter
  515.                     final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);

  516.                     // Check that the measurement parameter is managed by the filter
  517.                     if (measurementParameterColumns.get(driver.getName()) != null) {
  518.                         // Column of the driver in the measurement matrix
  519.                         final int driverColumn = measurementParameterColumns.get(driver.getName());

  520.                         // Fill the corresponding indexes of the measurement matrix
  521.                         for (int i = 0; i < aMPm.length; ++i) {
  522.                             measurementMatrix.setEntry(i, driverColumn,
  523.                                                        aMPm[i] / sigma[i] * driver.getScale());
  524.                         }
  525.                     }
  526.                 }
  527.             }
  528.         }

  529.         // Return the normalized measurement matrix
  530.         return measurementMatrix;

  531.     }


  532.     /** Update the reference trajectories using the propagators as input.
  533.      * @param propagators The new propagators to use
  534.      * @throws OrekitException if setting up the partial derivatives failed
  535.      */
  536.     private void updateReferenceTrajectories(final NumericalPropagator[] propagators)
  537.         throws OrekitException {

  538.         // Update the reference trajectory propagator
  539.         referenceTrajectories = propagators;

  540.         for (int k = 0; k < propagators.length; ++k) {
  541.             // Link the partial derivatives to this new propagator
  542.             final String equationName = KalmanEstimator.class.getName() + "-derivatives-" + k;
  543.             final PartialDerivativesEquations pde = new PartialDerivativesEquations(equationName, referenceTrajectories[k]);

  544.             // Reset the Jacobians
  545.             final SpacecraftState rawState = referenceTrajectories[k].getInitialState();
  546.             final SpacecraftState stateWithDerivatives = pde.setInitialJacobians(rawState);
  547.             referenceTrajectories[k].resetInitialState(stateWithDerivatives);
  548.             mappers[k] = pde.getMapper();
  549.         }

  550.     }

  551.     /** Un-normalize a state vector.
  552.      * A state vector S is of size m = nbOrb + nbPropag + nbMeas
  553.      * For each parameter i the normalized value of the state vector is:
  554.      * Sn[i] = S[i] / scale[i]
  555.      * @param normalizedStateVector The normalized state vector in input
  556.      * @return the "physical" state vector
  557.      */
  558.     private RealVector unNormalizeStateVector(final RealVector normalizedStateVector) {

  559.         // Initialize output matrix
  560.         final int nbParams = normalizedStateVector.getDimension();
  561.         final RealVector physicalStateVector = new ArrayRealVector(nbParams);

  562.         // Normalize the state matrix
  563.         for (int i = 0; i < nbParams; ++i) {
  564.             physicalStateVector.setEntry(i, normalizedStateVector.getEntry(i) * scale[i]);
  565.         }
  566.         return physicalStateVector;
  567.     }

  568.     /** Normalize a covariance matrix.
  569.      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  570.      * For each element [i,j] of P the corresponding normalized value is:
  571.      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  572.      * @param physicalCovarianceMatrix The "physical" covariance matrix in input
  573.      * @return the normalized covariance matrix
  574.      */
  575.     private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) {

  576.         // Initialize output matrix
  577.         final int nbParams = physicalCovarianceMatrix.getRowDimension();
  578.         final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);

  579.         // Normalize the state matrix
  580.         for (int i = 0; i < nbParams; ++i) {
  581.             for (int j = 0; j < nbParams; ++j) {
  582.                 normalizedCovarianceMatrix.setEntry(i, j,
  583.                                                     physicalCovarianceMatrix.getEntry(i, j) /
  584.                                                     (scale[i] * scale[j]));
  585.             }
  586.         }
  587.         return normalizedCovarianceMatrix;
  588.     }

  589.     /** Un-normalize a covariance matrix.
  590.      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  591.      * For each element [i,j] of P the corresponding normalized value is:
  592.      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  593.      * @param normalizedCovarianceMatrix The normalized covariance matrix in input
  594.      * @return the "physical" covariance matrix
  595.      */
  596.     private RealMatrix unNormalizeCovarianceMatrix(final RealMatrix normalizedCovarianceMatrix) {

  597.         // Initialize output matrix
  598.         final int nbParams = normalizedCovarianceMatrix.getRowDimension();
  599.         final RealMatrix physicalCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);

  600.         // Normalize the state matrix
  601.         for (int i = 0; i < nbParams; ++i) {
  602.             for (int j = 0; j < nbParams; ++j) {
  603.                 physicalCovarianceMatrix.setEntry(i, j,
  604.                                                   normalizedCovarianceMatrix.getEntry(i, j) *
  605.                                                   (scale[i] * scale[j]));
  606.             }
  607.         }
  608.         return physicalCovarianceMatrix;
  609.     }

  610.     /** Set and apply a dynamic outlier filter on a measurement.<p>
  611.      * Loop on the modifiers to see if a dynamic outlier filter needs to be applied.<p>
  612.      * Compute the sigma array using the matrix in input and set the filter.<p>
  613.      * Apply the filter by calling the modify method on the estimated measurement.<p>
  614.      * Reset the filter.
  615.      * @param measurement measurement to filter
  616.      * @param innovationCovarianceMatrix So called innovation covariance matrix S, with:<p>
  617.      *        S = H.Ppred.Ht + R<p>
  618.      *        Where:<p>
  619.      *         - H is the normalized measurement matrix (Ht its transpose)<p>
  620.      *         - Ppred is the normalized predicted covariance matrix<p>
  621.      *         - R is the normalized measurement noise matrix
  622.      * @param <T> the type of measurement
  623.      * @throws OrekitException if modifier cannot be applied
  624.      */
  625.     private <T extends ObservedMeasurement<T>> void applyDynamicOutlierFilter(final EstimatedMeasurement<T> measurement,
  626.                                                                               final RealMatrix innovationCovarianceMatrix)
  627.         throws OrekitException {

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

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

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

  638.                 // Set the sigma value for each element of the measurement
  639.                 // Here we do use the value suggested by David A. Vallado (see [1]§10.6):
  640.                 // sigmaDynamic[i] = sqrt(diag(S))*sigma[i]
  641.                 // With S = H.Ppred.Ht + R
  642.                 // Where:
  643.                 //  - S is the measurement error matrix in input
  644.                 //  - H is the normalized measurement matrix (Ht its transpose)
  645.                 //  - Ppred is the normalized predicted covariance matrix
  646.                 //  - R is the normalized measurement noise matrix
  647.                 //  - sigma[i] is the theoretical standard deviation of the ith component of the measurement.
  648.                 //    It is used here to un-normalize the value before it is filtered
  649.                 for (int i = 0; i < sigmaDynamic.length; i++) {
  650.                     sigmaDynamic[i] = FastMath.sqrt(innovationCovarianceMatrix.getEntry(i, i)) * sigmaMeasurement[i];
  651.                 }
  652.                 dynamicOutlierFilter.setSigma(sigmaDynamic);

  653.                 // Apply the modifier on the estimated measurement
  654.                 modifier.modify(measurement);

  655.                 // Re-initialize the value of the filter for the next measurement of the same type
  656.                 dynamicOutlierFilter.setSigma(null);
  657.             }
  658.         }
  659.     }

  660.     /** {@inheritDoc} */
  661.     @Override
  662.     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
  663.                                            final MeasurementDecorator measurement)
  664.         throws OrekitExceptionWrapper {
  665.         try {

  666.             // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  667.             final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  668.             for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  669.                 if (driver.getReferenceDate() == null) {
  670.                     driver.setReferenceDate(builders.get(0).getInitialOrbitDate());
  671.                 }
  672.             }

  673.             ++currentMeasurementNumber;
  674.             currentDate = measurement.getObservedMeasurement().getDate();

  675.             // Note:
  676.             // - n = size of the current measurement
  677.             //  Example:
  678.             //   * 1 for Range, RangeRate and TurnAroundRange
  679.             //   * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination)
  680.             //   * 6 for Position/Velocity
  681.             // - m = size of the state vector. n = nbOrb + nbPropag + nbMeas

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

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

  686.             // Predict the measurement based on predicted spacecraft state
  687.             // Compute the innovations (i.e. residuals of the predicted measurement)
  688.             // ------------------------------------------------------------

  689.             // Predicted measurement
  690.             // Note: here the "iteration/evaluation" formalism from the batch LS method
  691.             // is twisted to fit the need of the Kalman filter.
  692.             // The number of "iterations" is actually the number of measurements processed by the filter
  693.             // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement.
  694.             predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  695.                                                                 currentMeasurementNumber,
  696.                                                                 predictedSpacecraftStates);

  697.             // Normalized measurement matrix (nxm)
  698.             final RealMatrix measurementMatrix = getMeasurementMatrix();

  699.             // compute process noise matrix
  700.             final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(),
  701.                                                                                  previousState.getDimension());
  702.             for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {
  703.                 final RealMatrix noiseK = covarianceMatricesProviders.get(k).
  704.                                           getProcessNoiseMatrix(correctedSpacecraftStates[k],
  705.                                                                 predictedSpacecraftStates[k]);
  706.                 checkDimension(noiseK.getRowDimension(),
  707.                                builders.get(k).getOrbitalParametersDrivers(),
  708.                                builders.get(k).getPropagationParametersDrivers(),
  709.                                estimatedMeasurementsParameters);
  710.                 final int[] indK = covarianceIndirection[k];
  711.                 for (int i = 0; i < indK.length; ++i) {
  712.                     if (indK[i] >= 0) {
  713.                         for (int j = 0; j < indK.length; ++j) {
  714.                             if (indK[j] >= 0) {
  715.                                 physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  716.                             }
  717.                         }
  718.                     }
  719.                 }

  720.             }
  721.             final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(physicalProcessNoise);

  722.             return new NonLinearEvolution(measurement.getTime(), predictedState,
  723.                                           stateTransitionMatrix, normalizedProcessNoise, measurementMatrix);

  724.         } catch (OrekitException oe) {
  725.             throw new OrekitExceptionWrapper(oe);
  726.         }
  727.     }

  728.     /** {@inheritDoc} */
  729.     @Override
  730.     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
  731.                                     final RealMatrix innovationCovarianceMatrix)
  732.         throws OrekitExceptionWrapper {

  733.         try {
  734.             // Apply the dynamic outlier filter, if it exists
  735.             applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
  736.             if (predictedMeasurement.getStatus() == EstimatedMeasurement.Status.REJECTED)  {
  737.                 // set innovation to null to notify filter measurement is rejected
  738.                 return null;
  739.             } else {
  740.                 // Normalized innovation of the measurement (Nx1)
  741.                 final double[] observed  = predictedMeasurement.getObservedMeasurement().getObservedValue();
  742.                 final double[] estimated = predictedMeasurement.getEstimatedValue();
  743.                 final double[] sigma     = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
  744.                 final double[] residuals = new double[observed.length];

  745.                 for (int i = 0; i < observed.length; i++) {
  746.                     residuals[i] = (observed[i] - estimated[i]) / sigma[i];
  747.                 }
  748.                 return MatrixUtils.createRealVector(residuals);
  749.             }
  750.         } catch (OrekitException oe) {
  751.             throw new OrekitExceptionWrapper(oe);
  752.         }

  753.     }

  754.     /** Finalize estimation.
  755.      * @param observedMeasurement measurement that has just been processed
  756.      * @param estimate corrected estimate
  757.      * @exception OrekitException if measurement cannot be re-estimated from corrected state
  758.      */
  759.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  760.                                    final ProcessEstimate estimate)
  761.         throws OrekitException {
  762.         // Update the parameters with the estimated state
  763.         // The min/max values of the parameters are handled by the ParameterDriver implementation
  764.         correctedEstimate = estimate;
  765.         updateParameters();

  766.         // Get the estimated propagator (mirroring parameter update in the builder)
  767.         // and the estimated spacecraft state
  768.         final NumericalPropagator[] estimatedPropagators = getEstimatedPropagators();
  769.         for (int k = 0; k < estimatedPropagators.length; ++k) {
  770.             correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState();
  771.         }

  772.         // Compute the estimated measurement using estimated spacecraft state
  773.         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  774.                                                             currentMeasurementNumber,
  775.                                                             correctedSpacecraftStates);
  776.         // Update the trajectory
  777.         // ---------------------
  778.         updateReferenceTrajectories(estimatedPropagators);

  779.     }

  780.     /** Set the predicted normalized state vector.
  781.      * The predicted/propagated orbit is used to update the state vector
  782.      * @param date prediction date
  783.      * @return predicted state
  784.      * @throws OrekitException if the propagator builder could not be reset
  785.      */
  786.     private RealVector predictState(final AbsoluteDate date)
  787.         throws OrekitException {

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

  790.         // Orbital parameters counter
  791.         int jOrb = 0;

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

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

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

  798.             // The orbital parameters in the state vector are replaced with their predicted values
  799.             // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation)
  800.             // As the propagator builder was previously updated with the predicted orbit,
  801.             // the selected orbital drivers are already up to date with the prediction
  802.             for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  803.                 if (orbitalDriver.isSelected()) {
  804.                     predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue());
  805.                 }
  806.             }

  807.         }

  808.         return predictedState;

  809.     }

  810.     /** Update the estimated parameters after the correction phase of the filter.
  811.      * The min/max allowed values are handled by the parameter themselves.
  812.      * @throws OrekitException if setting the normalized values failed
  813.      */
  814.     private void updateParameters() throws OrekitException {
  815.         final RealVector correctedState = correctedEstimate.getState();
  816.         int i = 0;
  817.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  818.             // let the parameter handle min/max clipping
  819.             driver.setNormalizedValue(correctedState.getEntry(i));
  820.             correctedState.setEntry(i++, driver.getNormalizedValue());
  821.         }
  822.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  823.             // let the parameter handle min/max clipping
  824.             driver.setNormalizedValue(correctedState.getEntry(i));
  825.             correctedState.setEntry(i++, driver.getNormalizedValue());
  826.         }
  827.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  828.             // let the parameter handle min/max clipping
  829.             driver.setNormalizedValue(correctedState.getEntry(i));
  830.             correctedState.setEntry(i++, driver.getNormalizedValue());
  831.         }
  832.     }

  833. }