1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.estimation.sequential;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.hipparchus.geometry.euclidean.threed.Vector3D;
24 import org.hipparchus.linear.LUDecomposition;
25 import org.hipparchus.linear.MatrixUtils;
26 import org.hipparchus.linear.RealMatrix;
27 import org.hipparchus.linear.RealVector;
28 import org.junit.Assert;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.orekit.estimation.Context;
32 import org.orekit.estimation.EstimationTestUtils;
33 import org.orekit.estimation.Force;
34 import org.orekit.estimation.measurements.EstimatedMeasurement;
35 import org.orekit.estimation.measurements.GroundStation;
36 import org.orekit.estimation.measurements.ObservableSatellite;
37 import org.orekit.estimation.measurements.ObservedMeasurement;
38 import org.orekit.estimation.measurements.PV;
39 import org.orekit.estimation.measurements.Range;
40 import org.orekit.estimation.measurements.modifiers.Bias;
41 import org.orekit.forces.radiation.RadiationSensitive;
42 import org.orekit.orbits.Orbit;
43 import org.orekit.orbits.OrbitType;
44 import org.orekit.orbits.PositionAngle;
45 import org.orekit.propagation.MatricesHarvester;
46 import org.orekit.propagation.SpacecraftState;
47 import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
48 import org.orekit.propagation.numerical.NumericalPropagator;
49 import org.orekit.time.AbsoluteDate;
50 import org.orekit.utils.PVCoordinates;
51 import org.orekit.utils.ParameterDriver;
52 import org.orekit.utils.ParameterDriversList;
53
54
55
56
57
58
59
60
61 public class KalmanModelTest {
62
63
64 private final OrbitType orbitType = OrbitType.CARTESIAN;
65
66
67 private final PositionAngle positionAngle = PositionAngle.TRUE;
68
69
70 private Orbit orbit0;
71
72
73 private NumericalPropagatorBuilder propagatorBuilder;
74
75
76 private CovarianceMatrixProvider covMatrixProvider;
77
78
79 private ParameterDriversList estimatedMeasurementsParameters;
80
81
82 private KalmanEstimator kalman;
83
84
85 private ModelLogger modelLogger;
86
87
88 private int M;
89
90
91 private PV pv;
92
93
94 private Range range;
95
96
97 private ParameterDriver satRangeBiasDriver;
98
99
100 private ParameterDriver srpCoefDriver;
101
102
103 private final double tol = 1e-16;
104
105
106
107
108
109
110
111
112
113
114 @Before
115 public void setup() {
116
117 final Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
118
119
120 this.orbit0 = context.initialOrbit;
121 ObservableSatellite sat = new ObservableSatellite(0);
122
123
124 this.propagatorBuilder = context.createBuilder(orbitType, positionAngle, true,
125 1.0e-6, 60.0, 10., Force.SOLAR_RADIATION_PRESSURE);
126
127
128 final AbsoluteDate date0 = context.initialOrbit.getDate();
129 this.pv = new PV(date0,
130 context.initialOrbit.getPVCoordinates().getPosition(),
131 context.initialOrbit.getPVCoordinates().getVelocity(),
132 new double[] {1., 2., 3., 1e-3, 2e-3, 3e-3}, 1.,
133 sat);
134
135
136 final AbsoluteDate date = date0.shiftedBy(10.);
137 final GroundStation station = context.stations.get(0);
138 this.range = new Range(station, true, date, 18616150., 10., 1., sat);
139
140
141
142 final Bias<Range> satRangeBias = new Bias<Range>(new String[] {"sat range bias"},
143 new double[] {100.},
144 new double[] {10.},
145 new double[] {0.},
146 new double[] {100.});
147 this.satRangeBiasDriver = satRangeBias.getParametersDrivers().get(0);
148 satRangeBiasDriver.setSelected(true);
149 satRangeBiasDriver.setReferenceDate(date);
150 range.addModifier(satRangeBias);
151 for (ParameterDriver driver : range.getParametersDrivers()) {
152 driver.setReferenceDate(date);
153 }
154
155
156 this.estimatedMeasurementsParameters = new ParameterDriversList();
157 for (final ParameterDriver driver : range.getParametersDrivers()) {
158 if (driver.isSelected()) {
159 estimatedMeasurementsParameters.add(driver);
160 }
161 }
162
163 this.srpCoefDriver = propagatorBuilder.getPropagationParametersDrivers().
164 findByName(RadiationSensitive.REFLECTION_COEFFICIENT);
165 srpCoefDriver.setReferenceDate(date);
166 srpCoefDriver.setSelected(true);
167
168
169 final double[] scales = getParametersScale(propagatorBuilder, estimatedMeasurementsParameters);
170 this.M = scales.length;
171 this.covMatrixProvider = setInitialCovarianceMatrix(scales);
172
173
174 final KalmanEstimatorBuilder kalmanBuilder = new KalmanEstimatorBuilder();
175 kalmanBuilder.addPropagationConfiguration(propagatorBuilder, covMatrixProvider);
176 kalmanBuilder.estimatedMeasurementsParameters(estimatedMeasurementsParameters, null);
177 this.kalman = kalmanBuilder.build();
178 this.modelLogger = new ModelLogger();
179 kalman.setObserver(modelLogger);
180 }
181
182
183
184
185
186
187 @Test
188 public void ModelPhysicalOutputsTest() {
189
190
191
192 checkModelAtT0();
193
194
195
196
197
198
199 final RealMatrix Q = covMatrixProvider.getProcessNoiseMatrix(new SpacecraftState(orbit0),
200 new SpacecraftState(orbit0));
201
202
203 final RealMatrix P0 = covMatrixProvider.getInitialCovarianceMatrix(new SpacecraftState(orbit0));
204
205
206
207 RealMatrix Ppred = P0.add(Q);
208
209
210 Orbit orbitPred = orbit0;
211
212
213
214 RealMatrix expH = MatrixUtils.createRealMatrix(6, M);
215 for (int i = 0; i < 6; i++) {
216 expH.setEntry(i, i, 1.);
217 }
218
219
220
221 RealMatrix expPhi = MatrixUtils.createRealIdentityMatrix(M);
222
223 checkModelAfterMeasurementAdded(1, pv, Ppred, orbitPred, expPhi, expH);
224
225
226
227
228
229
230
231 NumericalPropagator propagator =
232 propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters());
233
234
235 final String equationName = KalmanEstimator.class.getName() + "-derivatives-";
236 final MatricesHarvester harvester = propagator.setupMatricesComputation(equationName, null, null);
237
238
239 final SpacecraftState scPred = propagator.propagate(range.getDate());
240 orbitPred = scPred.getOrbit();
241
242
243 expPhi = MatrixUtils.createRealIdentityMatrix(M);
244
245
246 final double[][] dYdY0 = harvester.getStateTransitionMatrix(scPred).getData();
247 expPhi.setSubMatrix(dYdY0, 0, 0);
248
249
250 final double[][] dYdPp = harvester.getParametersJacobian(scPred).getData();
251 expPhi.setSubMatrix(dYdPp, 0, 6);
252
253
254 RealMatrix Pest = kalman.getPhysicalEstimatedCovarianceMatrix();
255
256
257 Ppred = expPhi.multiply(Pest.multiplyTransposed(expPhi)).add(Q);
258
259
260 expH = MatrixUtils.createRealMatrix(1, M);
261
262 EstimatedMeasurement<Range> rangeEstimated = range.estimate(0, 0, new SpacecraftState[] {scPred});
263 final RealMatrix dMdY = MatrixUtils.createRealMatrix(rangeEstimated.getStateDerivatives(0));
264 expH.setSubMatrix(dMdY.getData(), 0, 0);
265
266 final SpacecraftState scTransition = scPred.shiftedBy(-rangeEstimated.getTimeOffset());
267 final double[][] dYdPpTransition = harvester.getParametersJacobian(scTransition).getData();
268 final RealMatrix dMdCr = dMdY.multiply(MatrixUtils.createRealMatrix(dYdPpTransition));
269 expH.setEntry(0, 6, dMdCr.getEntry(0, 0));
270
271 expH.setEntry(0, 7, rangeEstimated.getParameterDerivatives(satRangeBiasDriver)[0]);
272
273
274 checkModelAfterMeasurementAdded(2, range, Ppred, orbitPred, expPhi, expH);
275 }
276
277
278 private void checkModelAtT0() {
279
280
281 final KalmanModel model = new KalmanModel(Collections.singletonList(propagatorBuilder),
282 Collections.singletonList(covMatrixProvider),
283 estimatedMeasurementsParameters,
284 null);
285
286
287
288
289
290 Assert.assertEquals(0., model.getEstimate().getTime(), 0.);
291 Assert.assertEquals(0., model.getCurrentDate().durationFrom(orbit0.getDate()), 0.);
292
293
294 Assert.assertEquals(0, model.getCurrentMeasurementNumber());
295
296
297 final RealVector stateN = model.getEstimate().getState();
298 Assert.assertArrayEquals(new double[M], stateN.toArray(), tol);
299
300
301 final RealVector x = model.getPhysicalEstimatedState();
302 final RealVector expX = MatrixUtils.createRealVector(M);
303 final double[] orbitState0 = new double[6];
304 orbitType.mapOrbitToArray(orbit0, positionAngle, orbitState0, null);
305 expX.setSubVector(0, MatrixUtils.createRealVector(orbitState0));
306 expX.setEntry(6, srpCoefDriver.getReferenceValue());
307 expX.setEntry(7, satRangeBiasDriver.getReferenceValue());
308 final double[] dX = x.subtract(expX).toArray();
309 Assert.assertArrayEquals(new double[M], dX, tol);
310
311
312 final double[][] Pn = model.getEstimate().getCovariance().getData();
313 final double[][] expPn = new double[M][M];
314 for (int i = 0; i < M; i++) {
315 for (int j = 0; j < M; j++) {
316 expPn[i][j] = 1.;
317 }
318 Assert.assertArrayEquals("Failed on line " + i, expPn[i], Pn[i], tol);
319 }
320
321
322 final RealMatrix P = model.getPhysicalEstimatedCovarianceMatrix();
323 final RealMatrix expP = covMatrixProvider.getInitialCovarianceMatrix(new SpacecraftState(orbit0));
324 final double[][] dP = P.subtract(expP).getData();
325 for (int i = 0; i < M; i++) {
326 Assert.assertArrayEquals("Failed on line " + i, new double[M], dP[i], tol);
327 }
328
329
330 Assert.assertNull(model.getEstimate().getInnovationCovariance());
331 Assert.assertNull(model.getPhysicalInnovationCovarianceMatrix());
332 Assert.assertNull(model.getEstimate().getKalmanGain());
333 Assert.assertNull(model.getPhysicalKalmanGain());
334 Assert.assertNull(model.getEstimate().getMeasurementJacobian());
335 Assert.assertNull(model.getPhysicalMeasurementJacobian());
336 Assert.assertNull(model.getEstimate().getStateTransitionMatrix());
337 Assert.assertNull(model.getPhysicalStateTransitionMatrix());
338 }
339
340
341
342
343 private void checkModelAfterMeasurementAdded(final int expMeasurementNumber,
344 final ObservedMeasurement<?> meas,
345 final RealMatrix expPpred,
346 final Orbit expOrbitPred,
347 final RealMatrix expPhi,
348 final RealMatrix expH) {
349
350
351
352 final double srpCoefPred = srpCoefDriver.getValue();
353 final double satRangeBiasPred = satRangeBiasDriver.getValue();
354
355
356 final double[] expMeasPred =
357 meas.estimate(0, 0,
358 new SpacecraftState[] {new SpacecraftState(expOrbitPred)}).getEstimatedValue();
359
360
361 kalman.processMeasurements(Collections.singletonList(meas));
362 KalmanEstimation model = modelLogger.estimation;
363
364
365 final int N = meas.getDimension();
366
367
368 Assert.assertEquals(0., model.getCurrentDate().durationFrom(expOrbitPred.getDate()), 0.);
369
370
371 Assert.assertEquals(expMeasurementNumber, model.getCurrentMeasurementNumber());
372
373
374 final RealMatrix phi = model.getPhysicalStateTransitionMatrix();
375 final double[][] dPhi = phi.subtract(expPhi).getData();
376 for (int i = 0; i < M; i++) {
377 Assert.assertArrayEquals("Failed on line " + i, new double[M], dPhi[i], tol*100);
378 }
379
380
381 final RealMatrix H = model.getPhysicalMeasurementJacobian();
382 final double[][] dH = H.subtract(expH).getData();
383 for (int i = 0; i < N; i++) {
384 Assert.assertArrayEquals("Failed on line " + i, new double[M], dH[i], tol);
385 }
386
387
388 final double[] measSigmas = meas.getTheoreticalStandardDeviation();
389 final RealMatrix R = MatrixUtils.createRealMatrix(N, N);
390 for (int i = 0; i < N; i++) {
391 R.setEntry(i, i, measSigmas[i] * measSigmas[i]);
392 }
393
394
395 final RealMatrix expS = expH.multiply(expPpred.multiplyTransposed(expH)).add(R);
396 final RealMatrix S = model.getPhysicalInnovationCovarianceMatrix();
397 final double[][] dS = S.subtract(expS).getData();
398 for (int i = 0; i < N; i++) {
399 Assert.assertArrayEquals("Failed on line " + i, new double[N], dS[i], tol*1e4);
400 }
401
402
403 final RealMatrix expK = expPpred.multiplyTransposed(expH).multiply(new LUDecomposition(expS).getSolver().getInverse());
404 final RealMatrix K = model.getPhysicalKalmanGain();
405 final double[][] dK = K.subtract(expK).getData();
406 for (int i = 0; i < M; i++) {
407 Assert.assertArrayEquals("Failed on line " + i, new double[N], dK[i], tol*1e5);
408 }
409
410
411 final Orbit orbitPred = model.getPredictedSpacecraftStates()[0].getOrbit();
412 final PVCoordinates pvOrbitPred = orbitPred.getPVCoordinates();
413 final PVCoordinates expPVOrbitPred = expOrbitPred.getPVCoordinates();
414 final double dpOrbitPred = Vector3D.distance(expPVOrbitPred.getPosition(), pvOrbitPred.getPosition());
415 final double dvOrbitPred = Vector3D.distance(expPVOrbitPred.getVelocity(), pvOrbitPred.getVelocity());
416 Assert.assertEquals(0., dpOrbitPred, tol);
417 Assert.assertEquals(0., dvOrbitPred, tol);
418
419
420 final double[] measPred = model.getPredictedMeasurement().getEstimatedValue();
421 Assert.assertArrayEquals(expMeasPred, measPred, tol);
422
423
424 final double[] orbitPredState = new double[6];
425 orbitPred.getType().mapOrbitToArray(orbitPred, PositionAngle.TRUE, orbitPredState, null);
426 final RealVector expXpred = MatrixUtils.createRealVector(M);
427 for (int i = 0; i < 6; i++) {
428 expXpred.setEntry(i, orbitPredState[i]);
429 }
430 expXpred.setEntry(6, srpCoefPred);
431 expXpred.setEntry(7, satRangeBiasPred);
432
433
434 final RealVector observedMeas = MatrixUtils.createRealVector(model.getPredictedMeasurement().getObservedValue());
435 final RealVector predictedMeas = MatrixUtils.createRealVector(model.getPredictedMeasurement().getEstimatedValue());
436 final RealVector innovation = observedMeas.subtract(predictedMeas);
437
438
439 final RealVector expectedXcor = expXpred.add(expK.operate(innovation));
440 final RealVector Xcor = model.getPhysicalEstimatedState();
441 final double[] dXcor = Xcor.subtract(expectedXcor).toArray();
442 Assert.assertArrayEquals(new double[M], dXcor, tol);
443
444
445 final RealMatrix expectedPcor =
446 (MatrixUtils.createRealIdentityMatrix(M).
447 subtract(expK.multiply(expH))).multiply(expPpred);
448 final RealMatrix Pcor = model.getPhysicalEstimatedCovarianceMatrix();
449 final double[][] dPcor = Pcor.subtract(expectedPcor).getData();
450 for (int i = 0; i < M; i++) {
451 Assert.assertArrayEquals("Failed on line " + i, new double[M], dPcor[i], tol*1e5);
452 }
453 }
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497 private double[] getParametersScale(final NumericalPropagatorBuilder builder,
498 ParameterDriversList estimatedMeasurementsParameters) {
499 final List<Double> scaleList = new ArrayList<>();
500
501
502 for (ParameterDriver driver : builder.getOrbitalParametersDrivers().getDrivers()) {
503 if (driver.isSelected()) {
504 scaleList.add(driver.getScale());
505 }
506 }
507
508
509 for (ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {
510 if (driver.isSelected()) {
511 scaleList.add(driver.getScale());
512 }
513 }
514
515
516 for (ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
517 if (driver.isSelected()) {
518 scaleList.add(driver.getScale());
519 }
520 }
521
522 final double[] scales = new double[scaleList.size()];
523 for (int i = 0; i < scaleList.size(); i++) {
524 scales[i] = scaleList.get(i);
525 }
526 return scales;
527 }
528
529
530
531
532
533
534
535 private CovarianceMatrixProvider setInitialCovarianceMatrix(final double[] scales) {
536
537 final int n = scales.length;
538 final RealMatrix cov = MatrixUtils.createRealMatrix(n, n);
539 for (int i = 0; i < n; i++) {
540 for (int j = 0; j < n; j++) {
541 cov.setEntry(i, j, scales[i] * scales[j]);
542 }
543 }
544 return new ConstantProcessNoise(cov);
545 }
546
547
548
549 public class ModelLogger implements KalmanObserver {
550 KalmanEstimation estimation;
551
552 @Override
553 public void evaluationPerformed(KalmanEstimation estimation) {
554 this.estimation = estimation;
555 }
556 }
557 }