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 }