UnscentedKalmanModel.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 org.hipparchus.filtering.kalman.ProcessEstimate;
  19. import org.hipparchus.filtering.kalman.unscented.UnscentedEvolution;
  20. import org.hipparchus.filtering.kalman.unscented.UnscentedProcess;
  21. import org.hipparchus.linear.ArrayRealVector;
  22. import org.hipparchus.linear.MatrixUtils;
  23. import org.hipparchus.linear.RealMatrix;
  24. import org.hipparchus.linear.RealVector;
  25. import org.orekit.estimation.measurements.EstimatedMeasurement;
  26. import org.orekit.estimation.measurements.EstimatedMeasurementBase;
  27. import org.orekit.estimation.measurements.ObservedMeasurement;
  28. import org.orekit.propagation.Propagator;
  29. import org.orekit.propagation.SpacecraftState;
  30. import org.orekit.propagation.conversion.AbstractPropagatorBuilder;
  31. import org.orekit.propagation.conversion.PropagatorBuilder;
  32. import org.orekit.time.AbsoluteDate;
  33. import org.orekit.utils.ParameterDriver;
  34. import org.orekit.utils.ParameterDriversList;
  35. import org.orekit.utils.ParameterDriversList.DelegatingDriver;

  36. import java.util.List;

  37. /** Class defining the process model dynamics to use with a {@link UnscentedKalmanEstimator}.
  38.  * @author GaĆ«tan Pierre
  39.  * @author Bryan Cazabonne
  40.  * @since 11.3
  41.  */
  42. public class UnscentedKalmanModel extends KalmanEstimationCommon implements UnscentedProcess<MeasurementDecorator> {

  43.     /** Reference values. */
  44.     private final double[] referenceValues;

  45.     /** Unscented Kalman process model constructor (package private).
  46.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  47.      * @param covarianceMatricesProviders provider for covariance matrix
  48.      * @param estimatedMeasurementParameters measurement parameters to estimate
  49.      * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
  50.      */
  51.     protected UnscentedKalmanModel(final List<PropagatorBuilder> propagatorBuilders,
  52.                                    final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  53.                                    final ParameterDriversList estimatedMeasurementParameters,
  54.                                    final CovarianceMatrixProvider measurementProcessNoiseMatrix) {

  55.         super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, measurementProcessNoiseMatrix);

  56.         // Record the initial reference values
  57.         int stateDimension = 0;
  58.         for (final ParameterDriver ignored : getEstimatedOrbitalParameters().getDrivers()) {
  59.             stateDimension += 1;
  60.         }
  61.         for (final ParameterDriver ignored : getEstimatedPropagationParameters().getDrivers()) {
  62.             stateDimension += 1;
  63.         }
  64.         for (final ParameterDriver ignored : getEstimatedMeasurementsParameters().getDrivers()) {
  65.             stateDimension += 1;
  66.         }

  67.         this.referenceValues = new double[stateDimension];
  68.         int index = 0;
  69.         for (final ParameterDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  70.             referenceValues[index++] = driver.getReferenceValue();
  71.         }
  72.         for (final ParameterDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  73.             referenceValues[index++] = driver.getReferenceValue();
  74.         }
  75.         for (final ParameterDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  76.             referenceValues[index++] = driver.getReferenceValue();
  77.         }
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public UnscentedEvolution getEvolution(final double previousTime, final RealVector[] sigmaPoints,
  82.                                            final MeasurementDecorator measurement) {

  83.         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  84.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  85.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  86.             if (driver.getReferenceDate() == null) {
  87.                 driver.setReferenceDate(getBuilders().get(0).getInitialOrbitDate());
  88.             }
  89.         }

  90.         // Increment measurement number
  91.         incrementCurrentMeasurementNumber();

  92.         // Update the current date
  93.         setCurrentDate(measurement.getObservedMeasurement().getDate());

  94.         // Initialize array of predicted sigma points
  95.         final RealVector[] predictedSigmaPoints = new RealVector[sigmaPoints.length];

  96.         // Propagate each sigma point
  97.         //
  98.         // We need to make a choice about what happens with the non-estimated parts of the orbital states.
  99.         // Here we've assumed that the zero'th sigma point is roughly the mean and keep those propagated
  100.         // orbital parameters.  This is why we loop backward through the sigma-points and don't reset the
  101.         // propagator builders on the last iteration (corresponding to the zero-th sigma point).
  102.         //
  103.         // Note that -not- resetting the builders on the last iteration means that their time-stamps correspond
  104.         // to the prediction time.  The assumption is that the unscented filter calls getEvolution, then
  105.         // getPredictedMeasurements, then getInnovation.
  106.         for (int i = sigmaPoints.length - 1; i >= 0; i--) {

  107.             // Set parameters for this sigma point
  108.             final RealVector sigmaPoint = sigmaPoints[i].copy();
  109.             updateParameters(sigmaPoint);

  110.             // Get propagators
  111.             final Propagator[] propagators = getEstimatedPropagators();

  112.             // Do prediction
  113.             predictedSigmaPoints[i] =
  114.                     predictState(observedMeasurement.getDate(), sigmaPoint, propagators, i != 0);
  115.         }

  116.         // Reset the driver reference values based on the first sigma point
  117.         int d = 0;
  118.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  119.             driver.setReferenceValue(referenceValues[d]);
  120.             driver.setNormalizedValue(predictedSigmaPoints[0].getEntry(d));
  121.             referenceValues[d] = driver.getValue();

  122.             // Make remaining sigma points relative to the first
  123.             for (int i = 1; i < predictedSigmaPoints.length; ++i) {
  124.                 predictedSigmaPoints[i].setEntry(d, predictedSigmaPoints[i].getEntry(d) - predictedSigmaPoints[0].getEntry(d));
  125.             }
  126.             predictedSigmaPoints[0].setEntry(d, 0.0);

  127.             d += 1;
  128.         }

  129.         // compute process noise matrix
  130.         final RealMatrix normalizedProcessNoise = getNormalizedProcessNoise(sigmaPoints[0].getDimension());

  131.         // Return
  132.         return new UnscentedEvolution(measurement.getTime(), predictedSigmaPoints, normalizedProcessNoise);
  133.     }

  134.     /** {@inheritDoc} */
  135.     @Override
  136.     public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPoints, final MeasurementDecorator measurement) {

  137.         // Observed measurement
  138.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();

  139.         // Standard deviation as a vector
  140.         final RealVector theoreticalStandardDeviation =
  141.                 MatrixUtils.createRealVector(observedMeasurement.getTheoreticalStandardDeviation());

  142.         // Initialize arrays of predicted states and measurements
  143.         final RealVector[] predictedMeasurements = new RealVector[predictedSigmaPoints.length];

  144.         // Loop on sigma points to predict measurements
  145.         for (int i = 0; i < predictedSigmaPoints.length; ++i) {
  146.             // Set parameters for this sigma point
  147.             final RealVector predictedSigmaPoint = predictedSigmaPoints[i].copy();
  148.             updateParameters(predictedSigmaPoint);

  149.             // Get propagators
  150.             final Propagator[] propagators = getEstimatedPropagators();

  151.             // Predicted states
  152.             final SpacecraftState[] predictedStates = new SpacecraftState[propagators.length];
  153.             for (int k = 0; k < propagators.length; ++k) {
  154.                 predictedStates[k] = propagators[k].getInitialState();
  155.             }

  156.             // Calculated estimated measurement from predicted sigma point
  157.             final EstimatedMeasurement<?> estimated = estimateMeasurement(observedMeasurement, getCurrentMeasurementNumber(),
  158.                                                                                    KalmanEstimatorUtil.filterRelevant(observedMeasurement,
  159.                                                                                                                       predictedStates));
  160.             predictedMeasurements[i] = new ArrayRealVector(estimated.getEstimatedValue())
  161.                     .ebeDivide(theoreticalStandardDeviation);
  162.         }

  163.         // Return the predicted measurements
  164.         return predictedMeasurements;

  165.     }

  166.     /** {@inheritDoc} */
  167.     @Override
  168.     public RealVector getInnovation(final MeasurementDecorator measurement, final RealVector predictedMeas,
  169.                                     final RealVector predictedState, final RealMatrix innovationCovarianceMatrix) {
  170.         // Set parameters from predicted state
  171.         final RealVector predictedStateCopy = predictedState.copy();
  172.         updateParameters(predictedStateCopy);

  173.         // Standard deviation as a vector
  174.         final RealVector theoreticalStandardDeviation =
  175.                 MatrixUtils.createRealVector(measurement.getObservedMeasurement().getTheoreticalStandardDeviation());

  176.         // Get propagators
  177.         final Propagator[] propagators = getEstimatedPropagators();

  178.         // Predicted states
  179.         for (int k = 0; k < propagators.length; ++k) {
  180.             setPredictedSpacecraftState(propagators[k].getInitialState(), k);
  181.         }

  182.         // set estimated value to the predicted value from the filter
  183.         final EstimatedMeasurement<?> predictedMeasurement =
  184.             estimateMeasurement(measurement.getObservedMeasurement(), getCurrentMeasurementNumber(),
  185.                                 KalmanEstimatorUtil.filterRelevant(measurement.getObservedMeasurement(),
  186.                                 getPredictedSpacecraftStates()));
  187.         setPredictedMeasurement(predictedMeasurement);
  188.         predictedMeasurement.setEstimatedValue(predictedMeas.ebeMultiply(theoreticalStandardDeviation).toArray());

  189.         // Check for outliers
  190.         KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);

  191.         // Compute the innovation vector
  192.         return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement,
  193.                 predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation());
  194.     }


  195.     private RealVector predictState(final AbsoluteDate date,
  196.                                     final RealVector previousState,
  197.                                     final Propagator[] propagators,
  198.                                     final boolean resetState) {

  199.         // Initialise predicted state
  200.         final RealVector predictedState = previousState.copy();

  201.         // Orbital parameters counter
  202.         int jOrb = 0;

  203.         // Loop over propagators
  204.         for (int k = 0; k < propagators.length; ++k) {

  205.             // Record original state
  206.             final SpacecraftState originalState = propagators[k].getInitialState();

  207.             // Propagate
  208.             final SpacecraftState predicted = propagators[k].propagate(date);

  209.             // Update the builder with the predicted orbit
  210.             // This updates the orbital drivers with the values of the predicted orbit
  211.             getBuilders().get(k).resetOrbit(predicted.getOrbit());

  212.             // Additionally, for PropagatorBuilders which use mass, update the builder with the predicted mass value.
  213.             // If any mass changes have occurred during this estimation step, such as maneuvers,
  214.             // the updated mass value must be carried over so that new Propagators from this builder start with the updated mass.
  215.             if (getBuilders().get(k) instanceof AbstractPropagatorBuilder) {
  216.                 ((AbstractPropagatorBuilder) (getBuilders().get(k))).setMass(predicted.getMass());
  217.             }

  218.             // The orbital parameters in the state vector are replaced with their predicted values
  219.             // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation)
  220.             // As the propagator builder was previously updated with the predicted orbit,
  221.             // the selected orbital drivers are already up to date with the prediction
  222.             for (DelegatingDriver orbitalDriver : getBuilders().get(k).getOrbitalParametersDrivers().getDrivers()) {
  223.                 if (orbitalDriver.isSelected()) {
  224.                     orbitalDriver.setReferenceValue(referenceValues[jOrb]);
  225.                     predictedState.setEntry(jOrb, orbitalDriver.getNormalizedValue());

  226.                     jOrb += 1;
  227.                 }
  228.             }

  229.             // Set the builder back to the original time
  230.             if (resetState) {
  231.                 getBuilders().get(k).resetOrbit(originalState.getOrbit());
  232.             }
  233.         }

  234.         return predictedState;
  235.     }


  236.     /** Finalize estimation.
  237.      * @param observedMeasurement measurement that has just been processed
  238.      * @param estimate corrected estimate
  239.      */
  240.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  241.                                    final ProcessEstimate estimate) {
  242.         // Update the parameters with the estimated state
  243.         // The min/max values of the parameters are handled by the ParameterDriver implementation
  244.         setCorrectedEstimate(estimate);
  245.         updateParameters(estimate.getState());

  246.         // Get the estimated propagator (mirroring parameter update in the builder)
  247.         // and the estimated spacecraft state
  248.         final Propagator[] estimatedPropagators = getEstimatedPropagators();
  249.         for (int k = 0; k < estimatedPropagators.length; ++k) {
  250.             setCorrectedSpacecraftState(estimatedPropagators[k].getInitialState(), k);
  251.         }

  252.         // Corrected measurement
  253.         setCorrectedMeasurement(estimateMeasurement(observedMeasurement, getCurrentMeasurementNumber(),
  254.                                                     KalmanEstimatorUtil.filterRelevant(observedMeasurement,
  255.                                                     getCorrectedSpacecraftStates())));
  256.     }

  257.     /**
  258.      * Estimate measurement (without derivatives).
  259.      * @param <T> measurement type
  260.      * @param observedMeasurement observed measurement
  261.      * @param measurementNumber measurement number
  262.      * @param spacecraftStates states
  263.      * @return estimated measurement
  264.      * @since 12.1
  265.      */
  266.     private static <T extends ObservedMeasurement<T>> EstimatedMeasurement<T> estimateMeasurement(final ObservedMeasurement<T> observedMeasurement,
  267.                                                                                                   final int measurementNumber,
  268.                                                                                                   final SpacecraftState[] spacecraftStates) {
  269.         final EstimatedMeasurementBase<T> estimatedMeasurementBase = observedMeasurement.
  270.                 estimateWithoutDerivatives(measurementNumber, measurementNumber,
  271.                 KalmanEstimatorUtil.filterRelevant(observedMeasurement, spacecraftStates));
  272.         return new EstimatedMeasurement<>(estimatedMeasurementBase);
  273.     }

  274.     /** Update parameter drivers with a normalised state, adjusting state according to the driver limits.
  275.      * @param normalizedState the input state
  276.      * The min/max allowed values are handled by the parameter themselves.
  277.      */
  278.     private void updateParameters(final RealVector normalizedState) {
  279.         int i = 0;
  280.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  281.             // let the parameter handle min/max clipping
  282.             driver.setReferenceValue(referenceValues[i]);
  283.             driver.setNormalizedValue(normalizedState.getEntry(i));
  284.             normalizedState.setEntry(i++, driver.getNormalizedValue());
  285.         }
  286.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  287.             // let the parameter handle min/max clipping
  288.             driver.setNormalizedValue(normalizedState.getEntry(i));
  289.             normalizedState.setEntry(i++, driver.getNormalizedValue());
  290.         }
  291.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  292.             // let the parameter handle min/max clipping
  293.             driver.setNormalizedValue(normalizedState.getEntry(i));
  294.             normalizedState.setEntry(i++, driver.getNormalizedValue());
  295.         }
  296.     }
  297. }