KalmanModel.java

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

  18. 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.orekit.estimation.measurements.EstimatedMeasurement;
  33. import org.orekit.estimation.measurements.ObservedMeasurement;
  34. import org.orekit.orbits.Orbit;
  35. import org.orekit.propagation.MatricesHarvester;
  36. import org.orekit.propagation.Propagator;
  37. import org.orekit.propagation.SpacecraftState;
  38. import org.orekit.propagation.conversion.PropagatorBuilder;
  39. import org.orekit.time.AbsoluteDate;
  40. import org.orekit.utils.ParameterDriver;
  41. import org.orekit.utils.ParameterDriversList;
  42. import org.orekit.utils.ParameterDriversList.DelegatingDriver;

  43. /** Class defining the process model dynamics to use with a {@link KalmanEstimator}.
  44.  * @author Romain Gerbaud
  45.  * @author Maxime Journot
  46.  * @since 9.2
  47.  */
  48. public class KalmanModel implements KalmanEstimation, NonLinearProcess<MeasurementDecorator> {

  49.     /** Builders for propagators. */
  50.     private final List<PropagatorBuilder> builders;

  51.     /** Estimated orbital parameters. */
  52.     private final ParameterDriversList allEstimatedOrbitalParameters;

  53.     /** Estimated propagation drivers. */
  54.     private final ParameterDriversList allEstimatedPropagationParameters;

  55.     /** Per-builder estimated orbita parameters drivers.
  56.      * @since 11.1
  57.      */
  58.     private final ParameterDriversList[] estimatedOrbitalParameters;

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

  61.     /** Estimated measurements parameters. */
  62.     private final ParameterDriversList estimatedMeasurementsParameters;

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

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

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

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

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

  73.     /** Process noise matrix provider for measurement parameters. */
  74.     private final CovarianceMatrixProvider measurementProcessNoiseMatrix;

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

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

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

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

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

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

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

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

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

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

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

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

  99.     /** Kalman process model constructor.
  100.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  101.      * @param covarianceMatricesProviders providers for covariance matrices
  102.      * @param estimatedMeasurementParameters measurement parameters to estimate
  103.      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
  104.      */
  105.     public KalmanModel(final List<PropagatorBuilder> propagatorBuilders,
  106.                        final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  107.                        final ParameterDriversList estimatedMeasurementParameters,
  108.                        final CovarianceMatrixProvider measurementProcessNoiseMatrix) {

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

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

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

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

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

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

  206.         // Compute the scale factors
  207.         this.scale = new double[columns];
  208.         int index = 0;
  209.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  210.             scale[index++] = driver.getScale();
  211.         }
  212.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  213.             scale[index++] = driver.getScale();
  214.         }
  215.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  216.             scale[index++] = driver.getScale();
  217.         }

  218.         // Build the reference propagators and add their partial derivatives equations implementation
  219.         updateReferenceTrajectories(getEstimatedPropagators());
  220.         this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length];
  221.         for (int i = 0; i < predictedSpacecraftStates.length; ++i) {
  222.             predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState();
  223.         };
  224.         this.correctedSpacecraftStates = predictedSpacecraftStates.clone();

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

  227.         int p = 0;
  228.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  229.             correctedState.setEntry(p++, driver.getNormalizedValue());
  230.         }
  231.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  232.             correctedState.setEntry(p++, driver.getNormalizedValue());
  233.         }
  234.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  235.             correctedState.setEntry(p++, driver.getNormalizedValue());
  236.         }

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

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

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

  245.             // Covariance matrix
  246.             final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  247.             if (nbDyn > 0) {
  248.                 final RealMatrix noiseP = covarianceMatricesProviders.get(k).
  249.                         getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  250.                 noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  251.             }
  252.             if (measurementProcessNoiseMatrix != null) {
  253.                 final RealMatrix noiseM = measurementProcessNoiseMatrix.
  254.                                           getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  255.                 noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  256.             }

  257.             KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(),
  258.                                                builders.get(k).getOrbitalParametersDrivers(),
  259.                                                builders.get(k).getPropagationParametersDrivers(),
  260.                                                estimatedMeasurementsParameters);

  261.             final int[] indK = covarianceIndirection[k];
  262.             for (int i = 0; i < indK.length; ++i) {
  263.                 if (indK[i] >= 0) {
  264.                     for (int j = 0; j < indK.length; ++j) {
  265.                         if (indK[j] >= 0) {
  266.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  267.                         }
  268.                     }
  269.                 }
  270.             }

  271.         }
  272.         final RealMatrix correctedCovariance = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale);

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

  274.     }

  275.     /** Update the reference trajectories using the propagators as input.
  276.      * @param propagators The new propagators to use
  277.      */
  278.     protected void updateReferenceTrajectories(final Propagator[] propagators) {

  279.         // Update the reference trajectory propagator
  280.         setReferenceTrajectories(propagators);

  281.         // Jacobian harvesters
  282.         harvesters = new MatricesHarvester[propagators.length];

  283.         for (int k = 0; k < propagators.length; ++k) {
  284.             // Link the partial derivatives to this new propagator
  285.             final String equationName = KalmanEstimator.class.getName() + "-derivatives-" + k;
  286.             harvesters[k] = getReferenceTrajectories()[k].setupMatricesComputation(equationName, null, null);
  287.         }

  288.     }


  289.     /** {@inheritDoc} */
  290.     @Override
  291.     public RealMatrix getPhysicalStateTransitionMatrix() {
  292.         //  Un-normalize the state transition matrix (φ) from Hipparchus and return it.
  293.         // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  294.         // For each element [i,j] of normalized φ (φn), the corresponding physical value is:
  295.         // φ[i,j] = φn[i,j] * scale[i] / scale[j]
  296.         return correctedEstimate.getStateTransitionMatrix() == null ?
  297.                 null : KalmanEstimatorUtil.unnormalizeStateTransitionMatrix(correctedEstimate.getStateTransitionMatrix(), scale);
  298.     }

  299.     /** {@inheritDoc} */
  300.     @Override
  301.     public RealMatrix getPhysicalMeasurementJacobian() {
  302.         // Un-normalize the measurement matrix (H) from Hipparchus and return it.
  303.         // H is an nxm matrix where:
  304.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  305.         //  - n is the size of the measurement being processed by the filter
  306.         // For each element [i,j] of normalized H (Hn) the corresponding physical value is:
  307.         // H[i,j] = Hn[i,j] * σ[i] / scale[j]
  308.         return correctedEstimate.getMeasurementJacobian() == null ?
  309.                 null : KalmanEstimatorUtil.unnormalizeMeasurementJacobian(correctedEstimate.getMeasurementJacobian(),
  310.                                                                           scale,
  311.                                                                           correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
  312.     }

  313.     /** {@inheritDoc} */
  314.     @Override
  315.     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
  316.         // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it.
  317.         // S is an nxn matrix where n is the size of the measurement being processed by the filter
  318.         // For each element [i,j] of normalized S (Sn) the corresponding physical value is:
  319.         // S[i,j] = Sn[i,j] * σ[i] * σ[j]
  320.         return correctedEstimate.getInnovationCovariance() == null ?
  321.                 null : KalmanEstimatorUtil.unnormalizeInnovationCovarianceMatrix(correctedEstimate.getInnovationCovariance(),
  322.                                                                                  predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
  323.     }

  324.     /** {@inheritDoc} */
  325.     @Override
  326.     public RealMatrix getPhysicalKalmanGain() {
  327.         // Un-normalize the Kalman gain (K) from Hipparchus and return it.
  328.         // K is an mxn matrix where:
  329.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  330.         //  - n is the size of the measurement being processed by the filter
  331.         // For each element [i,j] of normalized K (Kn) the corresponding physical value is:
  332.         // K[i,j] = Kn[i,j] * scale[i] / σ[j]
  333.         return correctedEstimate.getKalmanGain() == null ?
  334.                 null : KalmanEstimatorUtil.unnormalizeKalmanGainMatrix(correctedEstimate.getKalmanGain(),
  335.                                                                        scale,
  336.                                                                        correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
  337.     }

  338.     /** {@inheritDoc} */
  339.     @Override
  340.     public SpacecraftState[] getPredictedSpacecraftStates() {
  341.         return predictedSpacecraftStates.clone();
  342.     }

  343.     /** {@inheritDoc} */
  344.     @Override
  345.     public SpacecraftState[] getCorrectedSpacecraftStates() {
  346.         return correctedSpacecraftStates.clone();
  347.     }

  348.     /** {@inheritDoc} */
  349.     @Override
  350.     public int getCurrentMeasurementNumber() {
  351.         return currentMeasurementNumber;
  352.     }

  353.     /** {@inheritDoc} */
  354.     @Override
  355.     public AbsoluteDate getCurrentDate() {
  356.         return currentDate;
  357.     }

  358.     /** {@inheritDoc} */
  359.     @Override
  360.     public EstimatedMeasurement<?> getPredictedMeasurement() {
  361.         return predictedMeasurement;
  362.     }

  363.     /** {@inheritDoc} */
  364.     @Override
  365.     public EstimatedMeasurement<?> getCorrectedMeasurement() {
  366.         return correctedMeasurement;
  367.     }

  368.     /** {@inheritDoc} */
  369.     @Override
  370.     public RealVector getPhysicalEstimatedState() {
  371.         // Method {@link ParameterDriver#getValue()} is used to get
  372.         // the physical values of the state.
  373.         // The scales'array is used to get the size of the state vector
  374.         final RealVector physicalEstimatedState = new ArrayRealVector(scale.length);
  375.         int i = 0;
  376.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  377.             physicalEstimatedState.setEntry(i++, driver.getValue());
  378.         }
  379.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  380.             physicalEstimatedState.setEntry(i++, driver.getValue());
  381.         }
  382.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  383.             physicalEstimatedState.setEntry(i++, driver.getValue());
  384.         }

  385.         return physicalEstimatedState;
  386.     }

  387.     /** {@inheritDoc} */
  388.     @Override
  389.     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
  390.         // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it.
  391.         // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  392.         // For each element [i,j] of P the corresponding normalized value is:
  393.         // Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  394.         // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j]
  395.         return KalmanEstimatorUtil.unnormalizeCovarianceMatrix(correctedEstimate.getCovariance(), scale);
  396.     }

  397.     /** {@inheritDoc} */
  398.     @Override
  399.     public ParameterDriversList getEstimatedOrbitalParameters() {
  400.         return allEstimatedOrbitalParameters;
  401.     }

  402.     /** {@inheritDoc} */
  403.     @Override
  404.     public ParameterDriversList getEstimatedPropagationParameters() {
  405.         return allEstimatedPropagationParameters;
  406.     }

  407.     /** {@inheritDoc} */
  408.     @Override
  409.     public ParameterDriversList getEstimatedMeasurementsParameters() {
  410.         return estimatedMeasurementsParameters;
  411.     }

  412.     /** Get the current corrected estimate.
  413.      * @return current corrected estimate
  414.      */
  415.     public ProcessEstimate getEstimate() {
  416.         return correctedEstimate;
  417.     }

  418.     /** Get the normalized error state transition matrix (STM) from previous point to current point.
  419.      * The STM contains the partial derivatives of current state with respect to previous state.
  420.      * The  STM is an mxm matrix where m is the size of the state vector.
  421.      * m = nbOrb + nbPropag + nbMeas
  422.      * @return the normalized error state transition matrix
  423.      */
  424.     private RealMatrix getErrorStateTransitionMatrix() {

  425.         /* The state transition matrix is obtained as follows, with:
  426.          *  - Y  : Current state vector
  427.          *  - Y0 : Initial state vector
  428.          *  - Pp : Current propagation parameter
  429.          *  - Pp0: Initial propagation parameter
  430.          *  - Mp : Current measurement parameter
  431.          *  - Mp0: Initial measurement parameter
  432.          *
  433.          *       |        |         |         |   |        |        |   .    |
  434.          *       | dY/dY0 | dY/dPp  | dY/dMp  |   | dY/dY0 | dY/dPp | ..0..  |
  435.          *       |        |         |         |   |        |        |   .    |
  436.          *       |--------|---------|---------|   |--------|--------|--------|
  437.          *       |        |         |         |   |   .    | 1 0 0..|   .    |
  438.          * STM = | dP/dY0 | dP/dPp0 | dP/dMp  | = | ..0..  | 0 1 0..| ..0..  |
  439.          *       |        |         |         |   |   .    | 0 0 1..|   .    |
  440.          *       |--------|---------|---------|   |--------|--------|--------|
  441.          *       |        |         |         |   |   .    |   .    | 1 0 0..|
  442.          *       | dM/dY0 | dM/dPp0 | dM/dMp0 |   | ..0..  | ..0..  | 0 1 0..|
  443.          *       |        |         |         |   |   .    |   .    | 0 0 1..|
  444.          */

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

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

  449.             // Indexes
  450.             final int[] indK = covarianceIndirection[k];

  451.             // Derivatives of the state vector with respect to initial state vector
  452.             final int nbOrbParams = estimatedOrbitalParameters[k].getNbParams();
  453.             if (nbOrbParams > 0) {

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

  456.                 final RealMatrix dYdY0 = harvesters[k].getStateTransitionMatrix(predictedSpacecraftStates[k]);

  457.                 // Fill upper left corner (dY/dY0)
  458.                 for (int i = 0; i < dYdY0.getRowDimension(); ++i) {
  459.                     for (int j = 0; j < nbOrbParams; ++j) {
  460.                         stm.setEntry(indK[i], indK[j], dYdY0.getEntry(i, j));
  461.                     }
  462.                 }
  463.             }

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

  468.                 // Fill 1st row, 2nd column (dY/dPp)
  469.                 for (int i = 0; i < dYdPp.getRowDimension(); ++i) {
  470.                     for (int j = 0; j < nbParams; ++j) {
  471.                         stm.setEntry(indK[i], indK[j + 6], dYdPp.getEntry(i, j));
  472.                     }
  473.                 }

  474.             }

  475.         }

  476.         // Normalization of the STM
  477.         // normalized(STM)ij = STMij*Sj/Si
  478.         for (int i = 0; i < scale.length; i++) {
  479.             for (int j = 0; j < scale.length; j++ ) {
  480.                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
  481.             }
  482.         }

  483.         // Return the error state transition matrix
  484.         return stm;

  485.     }

  486.     /** Get the normalized measurement matrix H.
  487.      * H contains the partial derivatives of the measurement with respect to the state.
  488.      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
  489.      * @return the normalized measurement matrix H
  490.      */
  491.     private RealMatrix getMeasurementMatrix() {

  492.         // Observed measurement characteristics
  493.         final SpacecraftState[]      evaluationStates    = predictedMeasurement.getStates();
  494.         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
  495.         final double[] sigma  = observedMeasurement.getTheoreticalStandardDeviation();

  496.         // Initialize measurement matrix H: nxm
  497.         // n: Number of measurements in current measurement
  498.         // m: State vector size
  499.         final RealMatrix measurementMatrix = MatrixUtils.
  500.                         createRealMatrix(observedMeasurement.getDimension(),
  501.                                          correctedEstimate.getState().getDimension());

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

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

  507.             // Measurement matrix's columns related to orbital parameters
  508.             // ----------------------------------------------------------

  509.             // Partial derivatives of the current Cartesian coordinates with respect to current orbital state
  510.             final double[][] aCY = new double[6][6];
  511.             predictedOrbit.getJacobianWrtParameters(builders.get(p).getPositionAngleType(), aCY);   //dC/dY
  512.             final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);

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

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

  517.             // Fill the normalized measurement matrix's columns related to estimated orbital parameters
  518.             for (int i = 0; i < dMdY.getRowDimension(); ++i) {
  519.                 int jOrb = orbitsStartColumns[p];
  520.                 for (int j = 0; j < dMdY.getColumnDimension(); ++j) {
  521.                     final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j);
  522.                     if (driver.isSelected()) {
  523.                         measurementMatrix.setEntry(i, jOrb++,
  524.                                                    dMdY.getEntry(i, j) / sigma[i] * driver.getScale());
  525.                     }
  526.                 }
  527.             }

  528.             // Normalized measurement matrix's columns related to propagation parameters
  529.             // --------------------------------------------------------------

  530.             // Jacobian of the measurement with respect to propagation parameters
  531.             final int nbParams = estimatedPropagationParameters[p].getNbParams();
  532.             if (nbParams > 0) {
  533.                 final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]);
  534.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);
  535.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  536.                     for (int j = 0; j < nbParams; ++j) {
  537.                         final ParameterDriver delegating = estimatedPropagationParameters[p].getDrivers().get(j);
  538.                         measurementMatrix.setEntry(i, propagationParameterColumns.get(delegating.getName()),
  539.                                                    dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale());
  540.                     }
  541.                 }
  542.             }

  543.             // Normalized measurement matrix's columns related to measurement parameters
  544.             // --------------------------------------------------------------

  545.             // Jacobian of the measurement with respect to measurement parameters
  546.             // Gather the measurement parameters linked to current measurement
  547.             for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  548.                 if (driver.isSelected()) {
  549.                     // Derivatives of current measurement w/r to selected measurement parameter
  550.                     final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);

  551.                     // Check that the measurement parameter is managed by the filter
  552.                     if (measurementParameterColumns.get(driver.getName()) != null) {
  553.                         // Column of the driver in the measurement matrix
  554.                         final int driverColumn = measurementParameterColumns.get(driver.getName());

  555.                         // Fill the corresponding indexes of the measurement matrix
  556.                         for (int i = 0; i < aMPm.length; ++i) {
  557.                             measurementMatrix.setEntry(i, driverColumn,
  558.                                                        aMPm[i] / sigma[i] * driver.getScale());
  559.                         }
  560.                     }
  561.                 }
  562.             }
  563.         }

  564.         // Return the normalized measurement matrix
  565.         return measurementMatrix;

  566.     }

  567.     /** {@inheritDoc} */
  568.     @Override
  569.     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
  570.                                            final MeasurementDecorator measurement) {

  571.         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  572.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  573.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  574.             if (driver.getReferenceDate() == null) {
  575.                 driver.setReferenceDate(builders.get(0).getInitialOrbitDate());
  576.             }
  577.         }

  578.         ++currentMeasurementNumber;
  579.         currentDate = measurement.getObservedMeasurement().getDate();

  580.         // Note:
  581.         // - n = size of the current measurement
  582.         //  Example:
  583.         //   * 1 for Range, RangeRate and TurnAroundRange
  584.         //   * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination)
  585.         //   * 6 for Position/Velocity
  586.         // - m = size of the state vector. n = nbOrb + nbPropag + nbMeas

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

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

  591.         // Predict the measurement based on predicted spacecraft state
  592.         // Compute the innovations (i.e. residuals of the predicted measurement)
  593.         // ------------------------------------------------------------

  594.         // Predicted measurement
  595.         // Note: here the "iteration/evaluation" formalism from the batch LS method
  596.         // is twisted to fit the need of the Kalman filter.
  597.         // The number of "iterations" is actually the number of measurements processed by the filter
  598.         // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement.
  599.         predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  600.                                                             currentMeasurementNumber,
  601.                                                             KalmanEstimatorUtil.filterRelevant(observedMeasurement, predictedSpacecraftStates));

  602.         // Normalized measurement matrix (nxm)
  603.         final RealMatrix measurementMatrix = getMeasurementMatrix();

  604.         // compute process noise matrix
  605.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(),
  606.                                                                              previousState.getDimension());
  607.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {

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

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

  613.             // Covariance matrix
  614.             final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas);
  615.             if (nbDyn > 0) {
  616.                 final RealMatrix noiseP = covarianceMatricesProviders.get(k).
  617.                                           getProcessNoiseMatrix(correctedSpacecraftStates[k],
  618.                                                                 predictedSpacecraftStates[k]);
  619.                 noiseK.setSubMatrix(noiseP.getData(), 0, 0);
  620.             }
  621.             if (measurementProcessNoiseMatrix != null) {
  622.                 final RealMatrix noiseM = measurementProcessNoiseMatrix.
  623.                                           getProcessNoiseMatrix(correctedSpacecraftStates[k],
  624.                                                                 predictedSpacecraftStates[k]);
  625.                 noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn);
  626.             }

  627.             KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(),
  628.                                                builders.get(k).getOrbitalParametersDrivers(),
  629.                                                builders.get(k).getPropagationParametersDrivers(),
  630.                                                estimatedMeasurementsParameters);

  631.             final int[] indK = covarianceIndirection[k];
  632.             for (int i = 0; i < indK.length; ++i) {
  633.                 if (indK[i] >= 0) {
  634.                     for (int j = 0; j < indK.length; ++j) {
  635.                         if (indK[j] >= 0) {
  636.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  637.                         }
  638.                     }
  639.                 }
  640.             }

  641.         }
  642.         final RealMatrix normalizedProcessNoise = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale);

  643.         return new NonLinearEvolution(measurement.getTime(), predictedState,
  644.                                       stateTransitionMatrix, normalizedProcessNoise, measurementMatrix);

  645.     }


  646.     /** {@inheritDoc} */
  647.     @Override
  648.     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
  649.                                     final RealMatrix innovationCovarianceMatrix) {

  650.         // Apply the dynamic outlier filter, if it exists
  651.         KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
  652.         // Compute the innovation vector
  653.         return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement, predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
  654.     }

  655.     /** Finalize estimation.
  656.      * @param observedMeasurement measurement that has just been processed
  657.      * @param estimate corrected estimate
  658.      */
  659.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  660.                                    final ProcessEstimate estimate) {
  661.         // Update the parameters with the estimated state
  662.         // The min/max values of the parameters are handled by the ParameterDriver implementation
  663.         correctedEstimate = estimate;
  664.         updateParameters();

  665.         // Get the estimated propagator (mirroring parameter update in the builder)
  666.         // and the estimated spacecraft state
  667.         final Propagator[] estimatedPropagators = getEstimatedPropagators();
  668.         for (int k = 0; k < estimatedPropagators.length; ++k) {
  669.             correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState();
  670.         }

  671.         // Compute the estimated measurement using estimated spacecraft state
  672.         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  673.                                                             currentMeasurementNumber,
  674.                                                             KalmanEstimatorUtil.filterRelevant(observedMeasurement, correctedSpacecraftStates));
  675.         // Update the trajectory
  676.         // ---------------------
  677.         updateReferenceTrajectories(estimatedPropagators);

  678.     }

  679.     /** Set the predicted normalized state vector.
  680.      * The predicted/propagated orbit is used to update the state vector
  681.      * @param date prediction date
  682.      * @return predicted state
  683.      */
  684.     private RealVector predictState(final AbsoluteDate date) {

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

  687.         // Orbital parameters counter
  688.         int jOrb = 0;

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

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

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

  695.             // The orbital parameters in the state vector are replaced with their predicted values
  696.             // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation)
  697.             // As the propagator builder was previously updated with the predicted orbit,
  698.             // the selected orbital drivers are already up to date with the prediction
  699.             for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  700.                 if (orbitalDriver.isSelected()) {
  701.                     predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue());
  702.                 }
  703.             }

  704.         }

  705.         return predictedState;

  706.     }

  707.     /** Update the estimated parameters after the correction phase of the filter.
  708.      * The min/max allowed values are handled by the parameter themselves.
  709.      */
  710.     private void updateParameters() {
  711.         final RealVector correctedState = correctedEstimate.getState();
  712.         int i = 0;
  713.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  714.             // let the parameter handle min/max clipping
  715.             driver.setNormalizedValue(correctedState.getEntry(i));
  716.             correctedState.setEntry(i++, driver.getNormalizedValue());
  717.         }
  718.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  719.             // let the parameter handle min/max clipping
  720.             driver.setNormalizedValue(correctedState.getEntry(i));
  721.             correctedState.setEntry(i++, driver.getNormalizedValue());
  722.         }
  723.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  724.             // let the parameter handle min/max clipping
  725.             driver.setNormalizedValue(correctedState.getEntry(i));
  726.             correctedState.setEntry(i++, driver.getNormalizedValue());
  727.         }
  728.     }

  729.     /** Getter for the propagators.
  730.      * @return the propagators
  731.      */
  732.     public List<PropagatorBuilder> getBuilders() {
  733.         return builders;
  734.     }

  735.     /** Getter for the reference trajectories.
  736.      * @return the referencetrajectories
  737.      */
  738.     public Propagator[] getReferenceTrajectories() {
  739.         return referenceTrajectories.clone();
  740.     }

  741.     /** Setter for the reference trajectories.
  742.      * @param referenceTrajectories the reference trajectories to be setted
  743.      */
  744.     public void setReferenceTrajectories(final Propagator[] referenceTrajectories) {
  745.         this.referenceTrajectories = referenceTrajectories.clone();
  746.     }

  747.     /** Get the propagators estimated with the values set in the propagators builders.
  748.      * @return propagators based on the current values in the builder
  749.      */
  750.     public Propagator[] getEstimatedPropagators() {
  751.         // Return propagators built with current instantiation of the propagator builders
  752.         final Propagator[] propagators = new Propagator[getBuilders().size()];
  753.         for (int k = 0; k < getBuilders().size(); ++k) {
  754.             propagators[k] = getBuilders().get(k).buildPropagator(getBuilders().get(k).getSelectedNormalizedParameters());
  755.         }
  756.         return propagators;
  757.     }

  758. }