KalmanEstimator.java
- /* Copyright 2002-2018 CS Systèmes d'Information
- * Licensed to CS Systèmes d'Information (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.estimation.sequential;
- import java.util.List;
- import org.hipparchus.exception.MathRuntimeException;
- import org.hipparchus.filtering.kalman.ProcessEstimate;
- import org.hipparchus.filtering.kalman.extended.ExtendedKalmanFilter;
- import org.hipparchus.linear.MatrixDecomposer;
- import org.hipparchus.linear.MatrixUtils;
- import org.hipparchus.linear.RealMatrix;
- import org.hipparchus.linear.RealVector;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitExceptionWrapper;
- import org.orekit.estimation.measurements.ObservedMeasurement;
- import org.orekit.estimation.measurements.PV;
- import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
- import org.orekit.propagation.conversion.PropagatorBuilder;
- import org.orekit.propagation.numerical.NumericalPropagator;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.utils.ParameterDriver;
- import org.orekit.utils.ParameterDriversList;
- import org.orekit.utils.ParameterDriversList.DelegatingDriver;
- /**
- * Implementation of a Kalman filter to perform orbit determination.
- * <p>
- * The filter uses a {@link NumericalPropagatorBuilder} to initialize its reference trajectory {@link NumericalPropagator}.
- * </p>
- * <p>
- * The estimated parameters are driven by {@link ParameterDriver} objects. They are of 3 different types:<ol>
- * <li><b>Orbital parameters</b>:The position and velocity of the spacecraft, or, more generally, its orbit.<br>
- * These parameters are retrieved from the reference trajectory propagator builder when the filter is initialized.</li>
- * <li><b>Propagation parameters</b>: Some parameters modelling physical processes (SRP or drag coefficients etc...).<br>
- * They are also retrieved from the propagator builder during the initialization phase.</li>
- * <li><b>Measurements parameters</b>: Parameters related to measurements (station biases, positions etc...).<br>
- * They are passed down to the filter in its constructor.</li>
- * </ol>
- * </p>
- * <p>
- * The total number of estimated parameters is m, the size of the state vector.
- * </p>
- * <p>
- * The Kalman filter implementation used is provided by the underlying mathematical library Hipparchus.
- * All the variables seen by Hipparchus (states, covariances, measurement matrices...) are normalized
- * using a specific scale for each estimated parameters or standard deviation noise for each measurement components.
- * </p>
- *
- * <p>A {@link KalmanEstimator} object is built using the {@link KalmanEstimatorBuilder#build() build}
- * method of a {@link KalmanEstimatorBuilder}.</p>
- *
- * @author Romain Gerbaud
- * @author Maxime Journot
- * @author Luc Maisonobe
- * @since 9.2
- */
- public class KalmanEstimator {
- /** Builders for numerical propagators. */
- private List<NumericalPropagatorBuilder> propagatorBuilders;
- /** Reference date. */
- private final AbsoluteDate referenceDate;
- /** Kalman filter process model. */
- private final Model processModel;
- /** Filter. */
- private final ExtendedKalmanFilter<MeasurementDecorator> filter;
- /** Observer to retrieve current estimation info. */
- private KalmanObserver observer;
- /** Kalman filter estimator constructor (package private).
- * @param decomposer decomposer to use for the correction phase
- * @param propagatorBuilders propagators builders used to evaluate the orbit.
- * @param processNoiseMatricesProviders providers for process noise matrices
- * @param estimatedMeasurementParameters measurement parameters to estimate
- * @throws OrekitException propagation exception.
- */
- KalmanEstimator(final MatrixDecomposer decomposer,
- final List<NumericalPropagatorBuilder> propagatorBuilders,
- final List<CovarianceMatrixProvider> processNoiseMatricesProviders,
- final ParameterDriversList estimatedMeasurementParameters)
- throws OrekitException {
- this.propagatorBuilders = propagatorBuilders;
- this.referenceDate = propagatorBuilders.get(0).getInitialOrbitDate();
- this.observer = null;
- // Build the process model and measurement model
- this.processModel = new Model(propagatorBuilders, processNoiseMatricesProviders,
- estimatedMeasurementParameters);
- this.filter = new ExtendedKalmanFilter<>(decomposer, processModel, processModel.getEstimate());
- }
- /** Set the observer.
- * @param observer the observer
- */
- public void setObserver(final KalmanObserver observer) {
- this.observer = observer;
- }
- /** Get the current measurement number.
- * @return current measurement number
- */
- public int getCurrentMeasurementNumber() {
- return processModel.getCurrentMeasurementNumber();
- }
- /** Get the current date.
- * @return current date
- */
- public AbsoluteDate getCurrentDate() {
- return processModel.getCurrentDate();
- }
- /** Get the "physical" estimated state (i.e. not normalized)
- * @return the "physical" estimated state
- */
- public RealVector getPhysicalEstimatedState() {
- return processModel.getPhysicalEstimatedState();
- }
- /** Get the "physical" estimated covariance matrix (i.e. not normalized)
- * @return the "physical" estimated covariance matrix
- */
- public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
- return processModel.getPhysicalEstimatedCovarianceMatrix();
- }
- /** Get the orbital parameters supported by this estimator.
- * <p>
- * If there are more than one propagator builder, then the names
- * of the drivers have an index marker in square brackets appended
- * to them in order to distinguish the various orbits. So for example
- * with one builder generating Keplerian orbits the names would be
- * simply "a", "e", "i"... but if there are several builders the
- * names would be "a[0]", "e[0]", "i[0]"..."a[1]", "e[1]", "i[1]"...
- * </p>
- * @param estimatedOnly if true, only estimated parameters are returned
- * @return orbital parameters supported by this estimator
- * @exception OrekitException if different parameters have the same name
- */
- public ParameterDriversList getOrbitalParametersDrivers(final boolean estimatedOnly)
- throws OrekitException {
- final ParameterDriversList estimated = new ParameterDriversList();
- for (int i = 0; i < propagatorBuilders.size(); ++i) {
- final String suffix = propagatorBuilders.size() > 1 ? "[" + i + "]" : null;
- for (final ParameterDriver driver : propagatorBuilders.get(i).getOrbitalParametersDrivers().getDrivers()) {
- if (driver.isSelected() || !estimatedOnly) {
- if (suffix != null && !driver.getName().endsWith(suffix)) {
- // we add suffix only conditionally because the method may already have been called
- // and suffixes may have already been appended
- driver.setName(driver.getName() + suffix);
- }
- estimated.add(driver);
- }
- }
- }
- return estimated;
- }
- /** Get the propagator parameters supported by this estimator.
- * @param estimatedOnly if true, only estimated parameters are returned
- * @return propagator parameters supported by this estimator
- * @exception OrekitException if different parameters have the same name
- */
- public ParameterDriversList getPropagationParametersDrivers(final boolean estimatedOnly)
- throws OrekitException {
- final ParameterDriversList estimated = new ParameterDriversList();
- for (PropagatorBuilder builder : propagatorBuilders) {
- for (final DelegatingDriver delegating : builder.getPropagationParametersDrivers().getDrivers()) {
- if (delegating.isSelected() || !estimatedOnly) {
- for (final ParameterDriver driver : delegating.getRawDrivers()) {
- estimated.add(driver);
- }
- }
- }
- }
- return estimated;
- }
- /** Get the list of estimated measurements parameters.
- * @return the list of estimated measurements parameters
- */
- public ParameterDriversList getEstimatedMeasurementsParameters() {
- return processModel.getEstimatedMeasurementsParameters();
- }
- /** Process a single measurement.
- * <p>
- * Update the filter with the new measurement by calling the estimate method.
- * </p>
- * @param observedMeasurement the measurement to process
- * @return estimated propagators
- * @throws OrekitException if an error occurred during the estimation
- */
- public NumericalPropagator[] estimationStep(final ObservedMeasurement<?> observedMeasurement)
- throws OrekitException {
- try {
- final ProcessEstimate estimate = filter.estimationStep(decorate(observedMeasurement));
- processModel.finalizeEstimation(observedMeasurement, estimate);
- if (observer != null) {
- observer.evaluationPerformed(processModel);
- }
- return processModel.getEstimatedPropagators();
- } catch (MathRuntimeException mrte) {
- throw new OrekitException(mrte);
- } catch (OrekitExceptionWrapper oew) {
- throw oew.getException();
- }
- }
- /** Process several measurements.
- * @param observedMeasurements the measurements to process in <em>chronologically sorted</em> order
- * @return estimated propagators
- * @throws OrekitException if an error occurred during the estimation
- */
- public NumericalPropagator[] processMeasurements(final Iterable<ObservedMeasurement<?>> observedMeasurements)
- throws OrekitException {
- NumericalPropagator[] propagators = null;
- for (ObservedMeasurement<?> observedMeasurement : observedMeasurements) {
- propagators = estimationStep(observedMeasurement);
- }
- return propagators;
- }
- /** Decorate an observed measurement.
- * <p>
- * The "physical" measurement noise matrix is the covariance matrix of the measurement.
- * Normalizing it consists in applying the following equation: Rn[i,j] = R[i,j]/σ[i]/σ[j]
- * Thus the normalized measurement noise matrix is the matrix of the correlation coefficients
- * between the different components of the measurement.
- * </p>
- * @param observedMeasurement the measurement
- * @return decorated measurement
- */
- private MeasurementDecorator decorate(final ObservedMeasurement<?> observedMeasurement) {
- // Normalized measurement noise matrix contains 1 on its diagonal and correlation coefficients
- // of the measurement on its non-diagonal elements.
- // Indeed, the "physical" measurement noise matrix is the covariance matrix of the measurement
- // Normalizing it leaves us with the matrix of the correlation coefficients
- final RealMatrix covariance;
- if (observedMeasurement instanceof PV) {
- // For PV measurements we do have a covariance matrix and thus a correlation coefficients matrix
- final PV pv = (PV) observedMeasurement;
- covariance = MatrixUtils.createRealMatrix(pv.getCorrelationCoefficientsMatrix());
- } else {
- // For other measurements we do not have a covariance matrix.
- // Thus the correlation coefficients matrix is an identity matrix.
- covariance = MatrixUtils.createRealIdentityMatrix(observedMeasurement.getDimension());
- }
- return new MeasurementDecorator(observedMeasurement, covariance, referenceDate);
- }
- }