1   /* Copyright 2002-2021 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  
19  import java.util.List;
20  
21  import org.hipparchus.exception.MathRuntimeException;
22  import org.hipparchus.filtering.kalman.ProcessEstimate;
23  import org.hipparchus.filtering.kalman.extended.ExtendedKalmanFilter;
24  import org.hipparchus.linear.MatrixDecomposer;
25  import org.hipparchus.linear.MatrixUtils;
26  import org.hipparchus.linear.RealMatrix;
27  import org.hipparchus.linear.RealVector;
28  import org.orekit.errors.OrekitException;
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.propagation.Propagator;
33  import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder;
34  import org.orekit.propagation.conversion.PropagatorBuilder;
35  import org.orekit.propagation.numerical.NumericalPropagator;
36  import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
37  import org.orekit.time.AbsoluteDate;
38  import org.orekit.utils.ParameterDriver;
39  import org.orekit.utils.ParameterDriversList;
40  import org.orekit.utils.ParameterDriversList.DelegatingDriver;
41  
42  
43  /**
44   * Implementation of a Kalman filter to perform orbit determination.
45   * <p>
46   * The filter uses a {@link OrbitDeterminationPropagatorBuilder} to initialize its reference trajectory {@link NumericalPropagator}
47   * or {@link DSSTPropagator} .
48   * </p>
49   * <p>
50   * The estimated parameters are driven by {@link ParameterDriver} objects. They are of 3 different types:<ol>
51   *   <li><b>Orbital parameters</b>:The position and velocity of the spacecraft, or, more generally, its orbit.<br>
52   *       These parameters are retrieved from the reference trajectory propagator builder when the filter is initialized.</li>
53   *   <li><b>Propagation parameters</b>: Some parameters modelling physical processes (SRP or drag coefficients etc...).<br>
54   *       They are also retrieved from the propagator builder during the initialization phase.</li>
55   *   <li><b>Measurements parameters</b>: Parameters related to measurements (station biases, positions etc...).<br>
56   *       They are passed down to the filter in its constructor.</li>
57   * </ol>
58   * <p>
59   * The total number of estimated parameters is m, the size of the state vector.
60   * </p>
61   * <p>
62   * The Kalman filter implementation used is provided by the underlying mathematical library Hipparchus.
63   * All the variables seen by Hipparchus (states, covariances, measurement matrices...) are normalized
64   * using a specific scale for each estimated parameters or standard deviation noise for each measurement components.
65   * </p>
66   *
67   * <p>A {@link KalmanEstimator} object is built using the {@link KalmanEstimatorBuilder#build() build}
68   * method of a {@link KalmanEstimatorBuilder}.</p>
69   *
70   * @author Romain Gerbaud
71   * @author Maxime Journot
72   * @author Luc Maisonobe
73   * @since 9.2
74   */
75  public class KalmanEstimator {
76  
77      /** Builders for orbit propagators. */
78      private List<OrbitDeterminationPropagatorBuilder> propagatorBuilders;
79  
80      /** Reference date. */
81      private final AbsoluteDate referenceDate;
82  
83      /** Kalman filter process model. */
84      private final AbstractKalmanModel processModel;
85  
86      /** Filter. */
87      private final ExtendedKalmanFilter<MeasurementDecorator> filter;
88  
89      /** Observer to retrieve current estimation info. */
90      private KalmanObserver observer;
91  
92      /** Kalman filter estimator constructor (package private).
93       * @param decomposer decomposer to use for the correction phase
94       * @param propagatorBuilders propagators builders used to evaluate the orbit.
95       * @param processNoiseMatricesProviders providers for process noise matrices
96       * @param estimatedMeasurementParameters measurement parameters to estimate
97       * @param measurementProcessNoiseMatrix provider for measurement process noise matrix
98       * @since 10.3
99       */
100     KalmanEstimator(final MatrixDecomposer decomposer,
101                     final List<OrbitDeterminationPropagatorBuilder> propagatorBuilders,
102                     final List<CovarianceMatrixProvider> processNoiseMatricesProviders,
103                     final ParameterDriversList estimatedMeasurementParameters,
104                     final CovarianceMatrixProvider measurementProcessNoiseMatrix) {
105 
106         this.propagatorBuilders = propagatorBuilders;
107         this.referenceDate      = propagatorBuilders.get(0).getInitialOrbitDate();
108         this.observer           = null;
109 
110         // Build the process model and measurement model
111         this.processModel = propagatorBuilders.get(0).buildKalmanModel(propagatorBuilders,
112                                                                        processNoiseMatricesProviders,
113                                                                        estimatedMeasurementParameters,
114                                                                        measurementProcessNoiseMatrix);
115 
116         this.filter = new ExtendedKalmanFilter<>(decomposer, processModel, processModel.getEstimate());
117 
118     }
119 
120     /** Set the observer.
121      * @param observer the observer
122      */
123     public void setObserver(final KalmanObserver observer) {
124         this.observer = observer;
125     }
126 
127     /** Get the current measurement number.
128      * @return current measurement number
129      */
130     public int getCurrentMeasurementNumber() {
131         return processModel.getCurrentMeasurementNumber();
132     }
133 
134     /** Get the current date.
135      * @return current date
136      */
137     public AbsoluteDate getCurrentDate() {
138         return processModel.getCurrentDate();
139     }
140 
141     /** Get the "physical" estimated state (i.e. not normalized)
142      * @return the "physical" estimated state
143      */
144     public RealVector getPhysicalEstimatedState() {
145         return processModel.getPhysicalEstimatedState();
146     }
147 
148     /** Get the "physical" estimated covariance matrix (i.e. not normalized)
149      * @return the "physical" estimated covariance matrix
150      */
151     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
152         return processModel.getPhysicalEstimatedCovarianceMatrix();
153     }
154 
155     /** Get the orbital parameters supported by this estimator.
156      * <p>
157      * If there are more than one propagator builder, then the names
158      * of the drivers have an index marker in square brackets appended
159      * to them in order to distinguish the various orbits. So for example
160      * with one builder generating Keplerian orbits the names would be
161      * simply "a", "e", "i"... but if there are several builders the
162      * names would be "a[0]", "e[0]", "i[0]"..."a[1]", "e[1]", "i[1]"...
163      * </p>
164      * @param estimatedOnly if true, only estimated parameters are returned
165      * @return orbital parameters supported by this estimator
166      */
167     public ParameterDriversList getOrbitalParametersDrivers(final boolean estimatedOnly) {
168 
169         final ParameterDriversList estimated = new ParameterDriversList();
170         for (int i = 0; i < propagatorBuilders.size(); ++i) {
171             final String suffix = propagatorBuilders.size() > 1 ? "[" + i + "]" : null;
172             for (final ParameterDriver driver : propagatorBuilders.get(i).getOrbitalParametersDrivers().getDrivers()) {
173                 if (driver.isSelected() || !estimatedOnly) {
174                     if (suffix != null && !driver.getName().endsWith(suffix)) {
175                         // we add suffix only conditionally because the method may already have been called
176                         // and suffixes may have already been appended
177                         driver.setName(driver.getName() + suffix);
178                     }
179                     estimated.add(driver);
180                 }
181             }
182         }
183         return estimated;
184     }
185 
186     /** Get the propagator parameters supported by this estimator.
187      * @param estimatedOnly if true, only estimated parameters are returned
188      * @return propagator parameters supported by this estimator
189      */
190     public ParameterDriversList getPropagationParametersDrivers(final boolean estimatedOnly) {
191 
192         final ParameterDriversList estimated = new ParameterDriversList();
193         for (PropagatorBuilder builder : propagatorBuilders) {
194             for (final DelegatingDriver delegating : builder.getPropagationParametersDrivers().getDrivers()) {
195                 if (delegating.isSelected() || !estimatedOnly) {
196                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
197                         estimated.add(driver);
198                     }
199                 }
200             }
201         }
202         return estimated;
203     }
204 
205     /** Get the list of estimated measurements parameters.
206      * @return the list of estimated measurements parameters
207      */
208     public ParameterDriversList getEstimatedMeasurementsParameters() {
209         return processModel.getEstimatedMeasurementsParameters();
210     }
211 
212     /** Process a single measurement.
213      * <p>
214      * Update the filter with the new measurement by calling the estimate method.
215      * </p>
216      * @param observedMeasurement the measurement to process
217      * @return estimated propagators
218      */
219     public Propagator[] estimationStep(final ObservedMeasurement<?> observedMeasurement) {
220         try {
221             final ProcessEstimate estimate = filter.estimationStep(decorate(observedMeasurement));
222             processModel.finalizeEstimation(observedMeasurement, estimate);
223             if (observer != null) {
224                 observer.evaluationPerformed(processModel);
225             }
226             return processModel.getEstimatedPropagators();
227         } catch (MathRuntimeException mrte) {
228             throw new OrekitException(mrte);
229         }
230     }
231 
232     /** Process several measurements.
233      * @param observedMeasurements the measurements to process in <em>chronologically sorted</em> order
234      * @return estimated propagators
235      */
236     public Propagator[] processMeasurements(final Iterable<ObservedMeasurement<?>> observedMeasurements) {
237         Propagator[] propagators = null;
238         for (ObservedMeasurement<?> observedMeasurement : observedMeasurements) {
239             propagators = estimationStep(observedMeasurement);
240         }
241         return propagators;
242     }
243 
244     /** Decorate an observed measurement.
245      * <p>
246      * The "physical" measurement noise matrix is the covariance matrix of the measurement.
247      * Normalizing it consists in applying the following equation: Rn[i,j] =  R[i,j]/σ[i]/σ[j]
248      * Thus the normalized measurement noise matrix is the matrix of the correlation coefficients
249      * between the different components of the measurement.
250      * </p>
251      * @param observedMeasurement the measurement
252      * @return decorated measurement
253      */
254     private MeasurementDecorator decorate(final ObservedMeasurement<?> observedMeasurement) {
255 
256         // Normalized measurement noise matrix contains 1 on its diagonal and correlation coefficients
257         // of the measurement on its non-diagonal elements.
258         // Indeed, the "physical" measurement noise matrix is the covariance matrix of the measurement
259         // Normalizing it leaves us with the matrix of the correlation coefficients
260         final RealMatrix covariance;
261         if (observedMeasurement instanceof PV) {
262             // For PV measurements we do have a covariance matrix and thus a correlation coefficients matrix
263             final PV pv = (PV) observedMeasurement;
264             covariance = MatrixUtils.createRealMatrix(pv.getCorrelationCoefficientsMatrix());
265         } else if (observedMeasurement instanceof Position) {
266             // For Position measurements we do have a covariance matrix and thus a correlation coefficients matrix
267             final Position position = (Position) observedMeasurement;
268             covariance = MatrixUtils.createRealMatrix(position.getCorrelationCoefficientsMatrix());
269         } else {
270             // For other measurements we do not have a covariance matrix.
271             // Thus the correlation coefficients matrix is an identity matrix.
272             covariance = MatrixUtils.createRealIdentityMatrix(observedMeasurement.getDimension());
273         }
274 
275         return new MeasurementDecorator(observedMeasurement, covariance, referenceDate);
276 
277     }
278 
279 }