KalmanEstimatorUtil.java

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

  18. import java.util.Arrays;
  19. import java.util.List;

  20. import org.hipparchus.linear.MatrixUtils;
  21. import org.hipparchus.linear.RealMatrix;
  22. import org.hipparchus.linear.RealVector;
  23. import org.hipparchus.util.FastMath;
  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.estimation.measurements.EstimatedMeasurement;
  27. import org.orekit.estimation.measurements.EstimationModifier;
  28. import org.orekit.estimation.measurements.ObservableSatellite;
  29. import org.orekit.estimation.measurements.ObservedMeasurement;
  30. import org.orekit.estimation.measurements.PV;
  31. import org.orekit.estimation.measurements.Position;
  32. import org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter;
  33. import org.orekit.propagation.SpacecraftState;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.utils.ParameterDriver;
  36. import org.orekit.utils.ParameterDriversList;

  37. /**
  38.  * Utility class for Kalman Filter.
  39.  * <p>
  40.  * This class includes common methods used by the different Kalman
  41.  * models in Orekit (i.e., Extended, Unscented, and Semi-analytical)
  42.  * </p>
  43.  * @since 11.3
  44.  */
  45. public class KalmanEstimatorUtil {

  46.     /** Private constructor.
  47.      * <p>This class is a utility class, it should neither have a public
  48.      * nor a default constructor. This private constructor prevents
  49.      * the compiler from generating one automatically.</p>
  50.      */
  51.     private KalmanEstimatorUtil() {
  52.     }

  53.     /** Decorate an observed measurement.
  54.      * <p>
  55.      * The "physical" measurement noise matrix is the covariance matrix of the measurement.
  56.      * Normalizing it consists in applying the following equation: Rn[i,j] =  R[i,j]/σ[i]/σ[j]
  57.      * Thus the normalized measurement noise matrix is the matrix of the correlation coefficients
  58.      * between the different components of the measurement.
  59.      * </p>
  60.      * @param observedMeasurement the measurement
  61.      * @param referenceDate reference date
  62.      * @return decorated measurement
  63.      */
  64.     public static MeasurementDecorator decorate(final ObservedMeasurement<?> observedMeasurement,
  65.                                                 final AbsoluteDate referenceDate) {

  66.         // Normalized measurement noise matrix contains 1 on its diagonal and correlation coefficients
  67.         // of the measurement on its non-diagonal elements.
  68.         // Indeed, the "physical" measurement noise matrix is the covariance matrix of the measurement
  69.         // Normalizing it leaves us with the matrix of the correlation coefficients
  70.         final RealMatrix covariance;
  71.         if (observedMeasurement.getMeasurementType().equals(PV.MEASUREMENT_TYPE)) {
  72.             // For PV measurements we do have a covariance matrix and thus a correlation coefficients matrix
  73.             final PV pv = (PV) observedMeasurement;
  74.             covariance = MatrixUtils.createRealMatrix(pv.getCorrelationCoefficientsMatrix());
  75.         } else if (observedMeasurement.getMeasurementType().equals(Position.MEASUREMENT_TYPE)) {
  76.             // For Position measurements we do have a covariance matrix and thus a correlation coefficients matrix
  77.             final Position position = (Position) observedMeasurement;
  78.             covariance = MatrixUtils.createRealMatrix(position.getCorrelationCoefficientsMatrix());
  79.         } else {
  80.             // For other measurements we do not have a covariance matrix.
  81.             // Thus the correlation coefficients matrix is an identity matrix.
  82.             covariance = MatrixUtils.createRealIdentityMatrix(observedMeasurement.getDimension());
  83.         }

  84.         return new MeasurementDecorator(observedMeasurement, covariance, referenceDate);

  85.     }

  86.     /** Check dimension.
  87.      * @param dimension dimension to check
  88.      * @param orbitalParameters orbital parameters
  89.      * @param propagationParameters propagation parameters
  90.      * @param measurementParameters measurements parameters
  91.      */
  92.     public static void checkDimension(final int dimension,
  93.                                       final ParameterDriversList orbitalParameters,
  94.                                       final ParameterDriversList propagationParameters,
  95.                                       final ParameterDriversList measurementParameters) {

  96.         // count parameters
  97.         int requiredDimension = 0;
  98.         for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
  99.             if (driver.isSelected()) {
  100.                 ++requiredDimension;
  101.             }
  102.         }
  103.         for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  104.             if (driver.isSelected()) {
  105.                 ++requiredDimension;
  106.             }
  107.         }
  108.         for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  109.             if (driver.isSelected()) {
  110.                 ++requiredDimension;
  111.             }
  112.         }

  113.         if (dimension != requiredDimension) {
  114.             // there is a problem, set up an explicit error message
  115.             final StringBuilder sBuilder = new StringBuilder();
  116.             for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
  117.                 if (sBuilder.length() > 0) {
  118.                     sBuilder.append(", ");
  119.                 }
  120.                 sBuilder.append(driver.getName());
  121.             }
  122.             for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  123.                 if (driver.isSelected()) {
  124.                     sBuilder.append(driver.getName());
  125.                 }
  126.             }
  127.             for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  128.                 if (driver.isSelected()) {
  129.                     sBuilder.append(driver.getName());
  130.                 }
  131.             }
  132.             throw new OrekitException(OrekitMessages.DIMENSION_INCONSISTENT_WITH_PARAMETERS,
  133.                                       dimension, sBuilder.toString());
  134.         }

  135.     }

  136.     /** Filter relevant states for a measurement.
  137.      * @param observedMeasurement measurement to consider
  138.      * @param allStates all states
  139.      * @return array containing only the states relevant to the measurement
  140.      */
  141.     public static SpacecraftState[] filterRelevant(final ObservedMeasurement<?> observedMeasurement,
  142.                                                    final SpacecraftState[] allStates) {
  143.         final List<ObservableSatellite> satellites = observedMeasurement.getSatellites();
  144.         final SpacecraftState[] relevantStates = new SpacecraftState[satellites.size()];
  145.         for (int i = 0; i < relevantStates.length; ++i) {
  146.             relevantStates[i] = allStates[satellites.get(i).getPropagatorIndex()];
  147.         }
  148.         return relevantStates;
  149.     }

  150.     /** Set and apply a dynamic outlier filter on a measurement.<p>
  151.      * Loop on the modifiers to see if a dynamic outlier filter needs to be applied.<p>
  152.      * Compute the sigma array using the matrix in input and set the filter.<p>
  153.      * Apply the filter by calling the modify method on the estimated measurement.<p>
  154.      * Reset the filter.
  155.      * @param measurement measurement to filter
  156.      * @param innovationCovarianceMatrix So called innovation covariance matrix S, with:<p>
  157.      *        S = H.Ppred.Ht + R<p>
  158.      *        Where:<p>
  159.      *         - H is the normalized measurement matrix (Ht its transpose)<p>
  160.      *         - Ppred is the normalized predicted covariance matrix<p>
  161.      *         - R is the normalized measurement noise matrix
  162.      * @param <T> the type of measurement
  163.      */
  164.     public static <T extends ObservedMeasurement<T>> void applyDynamicOutlierFilter(final EstimatedMeasurement<T> measurement,
  165.                                                                                     final RealMatrix innovationCovarianceMatrix) {

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

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

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

  176.                 // Set the sigma value for each element of the measurement
  177.                 // Here we do use the value suggested by David A. Vallado (see [1]§10.6):
  178.                 // sigmaDynamic[i] = sqrt(diag(S))*sigma[i]
  179.                 // With S = H.Ppred.Ht + R
  180.                 // Where:
  181.                 //  - S is the measurement error matrix in input
  182.                 //  - H is the normalized measurement matrix (Ht its transpose)
  183.                 //  - Ppred is the normalized predicted covariance matrix
  184.                 //  - R is the normalized measurement noise matrix
  185.                 //  - sigma[i] is the theoretical standard deviation of the ith component of the measurement.
  186.                 //    It is used here to un-normalize the value before it is filtered
  187.                 for (int i = 0; i < sigmaDynamic.length; i++) {
  188.                     sigmaDynamic[i] = FastMath.sqrt(innovationCovarianceMatrix.getEntry(i, i)) * sigmaMeasurement[i];
  189.                 }
  190.                 dynamicOutlierFilter.setSigma(sigmaDynamic);

  191.                 // Apply the modifier on the estimated measurement
  192.                 modifier.modify(measurement);

  193.                 // Re-initialize the value of the filter for the next measurement of the same type
  194.                 dynamicOutlierFilter.setSigma(null);
  195.             }
  196.         }
  197.     }

  198.     /**
  199.      * Compute the unnormalized innovation vector from the given predicted measurement.
  200.      * @param predicted predicted measurement
  201.      * @return the innovation vector
  202.      */
  203.     public static RealVector computeInnovationVector(final EstimatedMeasurement<?> predicted) {
  204.         final double[] sigmas = new double[predicted.getObservedMeasurement().getDimension()];
  205.         Arrays.fill(sigmas, 1.0);
  206.         return computeInnovationVector(predicted, sigmas);
  207.     }

  208.     /**
  209.      * Compute the normalized innovation vector from the given predicted measurement.
  210.      * @param predicted predicted measurement
  211.      * @param sigma measurement standard deviation
  212.      * @return the innovation vector
  213.      */
  214.     public static RealVector computeInnovationVector(final EstimatedMeasurement<?> predicted, final double[] sigma) {

  215.         if (predicted.getStatus() == EstimatedMeasurement.Status.REJECTED)  {
  216.             // set innovation to null to notify filter measurement is rejected
  217.             return null;
  218.         } else {
  219.             // Normalized innovation of the measurement (Nx1)
  220.             final double[] observed  = predicted.getObservedMeasurement().getObservedValue();
  221.             final double[] estimated = predicted.getEstimatedValue();
  222.             final double[] residuals = new double[observed.length];

  223.             for (int i = 0; i < observed.length; i++) {
  224.                 residuals[i] = (observed[i] - estimated[i]) / sigma[i];
  225.             }
  226.             return MatrixUtils.createRealVector(residuals);
  227.         }

  228.     }

  229. }