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.hipparchus.util.MerweUnscentedTransform;
29 import org.hipparchus.util.UnscentedTransformProvider;
30 import org.junit.jupiter.api.Assertions;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33 import org.orekit.estimation.Context;
34 import org.orekit.estimation.EstimationTestUtils;
35 import org.orekit.estimation.Force;
36 import org.orekit.estimation.measurements.GroundStation;
37 import org.orekit.estimation.measurements.ObservableSatellite;
38 import org.orekit.estimation.measurements.ObservedMeasurement;
39 import org.orekit.estimation.measurements.PV;
40 import org.orekit.estimation.measurements.Range;
41 import org.orekit.estimation.measurements.modifiers.Bias;
42 import org.orekit.forces.maneuvers.ConstantThrustManeuver;
43 import org.orekit.forces.radiation.RadiationSensitive;
44 import org.orekit.orbits.Orbit;
45 import org.orekit.orbits.OrbitType;
46 import org.orekit.orbits.PositionAngleType;
47 import org.orekit.propagation.SpacecraftState;
48 import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
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 public class UnscentedKalmanModelTest {
55
56
57 private final OrbitType orbitType = OrbitType.CARTESIAN;
58
59
60 private final PositionAngleType positionAngleType = PositionAngleType.TRUE;
61
62
63 private Orbit orbit0;
64
65
66 private NumericalPropagatorBuilder propagatorBuilder;
67
68
69 private CovarianceMatrixProvider covMatrixProvider;
70
71
72 private ParameterDriversList estimatedMeasurementsParameters;
73
74
75 private UnscentedKalmanEstimator kalman;
76
77
78 private UnscentedTransformProvider utProvider;
79
80
81 private ModelLogger modelLogger;
82
83
84 private int M;
85
86
87 private PV pv;
88
89
90 private Range range;
91
92
93 private ParameterDriver satRangeBiasDriver;
94
95
96 private ParameterDriver srpCoefDriver;
97
98
99 private final double tol = 1.0e-16;
100
101 @BeforeEach
102 public void setup() {
103
104 final Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
105
106
107 this.orbit0 = context.initialOrbit;
108 ObservableSatellite sat = new ObservableSatellite(0);
109
110
111 this.propagatorBuilder = context.createBuilder(orbitType, positionAngleType, true,
112 1.0e-6, 60.0, 10., Force.SOLAR_RADIATION_PRESSURE);
113
114
115 final AbsoluteDate date0 = context.initialOrbit.getDate();
116 this.pv = new PV(date0,
117 context.initialOrbit.getPosition(),
118 context.initialOrbit.getPVCoordinates().getVelocity(),
119 new double[] {1., 2., 3., 1e-3, 2e-3, 3e-3}, 1.,
120 sat);
121
122
123 final AbsoluteDate date = date0.shiftedBy(10.);
124 final GroundStation station = context.stations.get(0);
125 this.range = new Range(station, true, date, 18616150., 10., 1., sat);
126
127
128
129 final Bias<Range> satRangeBias = new Bias<Range>(new String[] {"sat range bias"},
130 new double[] {100.},
131 new double[] {10.},
132 new double[] {0.},
133 new double[] {150.});
134 this.satRangeBiasDriver = satRangeBias.getParametersDrivers().get(0);
135 satRangeBiasDriver.setSelected(true);
136 satRangeBiasDriver.setReferenceDate(date);
137 range.addModifier(satRangeBias);
138 for (ParameterDriver driver : range.getParametersDrivers()) {
139 driver.setReferenceDate(date);
140 }
141
142
143 this.estimatedMeasurementsParameters = new ParameterDriversList();
144 for (final ParameterDriver driver : range.getParametersDrivers()) {
145 if (driver.isSelected()) {
146 estimatedMeasurementsParameters.add(driver);
147 }
148 }
149
150 this.srpCoefDriver = propagatorBuilder.getPropagationParametersDrivers().
151 findByName(RadiationSensitive.REFLECTION_COEFFICIENT);
152 srpCoefDriver.setReferenceDate(date);
153 srpCoefDriver.setSelected(true);
154
155
156 final double[] scales = getParametersScale(propagatorBuilder, estimatedMeasurementsParameters);
157 this.M = scales.length;
158 this.covMatrixProvider = setInitialCovarianceMatrix(scales);
159
160
161 this.utProvider = new MerweUnscentedTransform(8);
162
163
164 final UnscentedKalmanEstimatorBuilder kalmanBuilder = new UnscentedKalmanEstimatorBuilder();
165 kalmanBuilder.unscentedTransformProvider(utProvider);
166 kalmanBuilder.addPropagationConfiguration(propagatorBuilder, covMatrixProvider);
167 kalmanBuilder.estimatedMeasurementsParameters(estimatedMeasurementsParameters, null);
168 this.kalman = kalmanBuilder.build();
169 this.modelLogger = new ModelLogger();
170 kalman.setObserver(modelLogger);
171 }
172
173
174
175
176
177
178 @Test
179 public void ModelPhysicalOutputsTest() {
180
181
182
183 checkModelAtT0();
184
185
186
187
188
189
190 final RealMatrix Q = covMatrixProvider.getProcessNoiseMatrix(new SpacecraftState(orbit0),
191 new SpacecraftState(orbit0));
192
193
194 final RealMatrix P0 = covMatrixProvider.getInitialCovarianceMatrix(new SpacecraftState(orbit0));
195
196
197
198 RealMatrix Ppred = P0.add(Q);
199
200
201 Orbit orbitPred = orbit0;
202
203
204
205 RealMatrix expH = MatrixUtils.createRealMatrix(6, M);
206 for (int i = 0; i < 6; i++) {
207 expH.setEntry(i, i, 1.);
208 }
209
210
211
212 RealMatrix expPhi = MatrixUtils.createRealIdentityMatrix(M);
213
214 checkModelAfterMeasurementAdded(1, pv, Ppred, orbitPred, expPhi, expH);
215 }
216
217
218 private void checkModelAtT0() {
219
220
221 final UnscentedKalmanModel model = new UnscentedKalmanModel(Collections.singletonList(propagatorBuilder),
222 Collections.singletonList(covMatrixProvider),
223 estimatedMeasurementsParameters,
224 null);
225
226
227
228
229
230 Assertions.assertEquals(0., model.getEstimate().getTime(), 0.);
231 Assertions.assertEquals(0., model.getCurrentDate().durationFrom(orbit0.getDate()), 0.);
232
233
234 Assertions.assertEquals(0, model.getCurrentMeasurementNumber());
235
236
237 final RealVector stateN = model.getEstimate().getState();
238 Assertions.assertArrayEquals(new double[M], stateN.toArray(), tol);
239
240
241 final RealVector x = model.getPhysicalEstimatedState();
242 final RealVector expX = MatrixUtils.createRealVector(M);
243 final double[] orbitState0 = new double[6];
244 orbitType.mapOrbitToArray(orbit0, positionAngleType, orbitState0, null);
245 expX.setSubVector(0, MatrixUtils.createRealVector(orbitState0));
246 expX.setEntry(6, srpCoefDriver.getReferenceValue());
247 expX.setEntry(7, satRangeBiasDriver.getReferenceValue());
248 final double[] dX = x.subtract(expX).toArray();
249 Assertions.assertArrayEquals(new double[M], dX, tol);
250
251
252 final double[][] Pn = model.getEstimate().getCovariance().getData();
253 final double[][] expPn = new double[M][M];
254 for (int i = 0; i < M; i++) {
255 expPn[i][i] = 1.;
256 Assertions.assertArrayEquals(expPn[i], Pn[i], tol, "Failed on line " + i);
257 }
258
259
260 final RealMatrix P = model.getPhysicalEstimatedCovarianceMatrix();
261 final RealMatrix expP = covMatrixProvider.getInitialCovarianceMatrix(new SpacecraftState(orbit0));
262 final double[][] dP = P.subtract(expP).getData();
263 for (int i = 0; i < M; i++) {
264 Assertions.assertArrayEquals(new double[M], dP[i], tol, "Failed on line " + i);
265 }
266
267
268 Assertions.assertNull(model.getEstimate().getInnovationCovariance());
269 Assertions.assertNull(model.getPhysicalInnovationCovarianceMatrix());
270 Assertions.assertNull(model.getEstimate().getKalmanGain());
271 Assertions.assertNull(model.getPhysicalKalmanGain());
272 Assertions.assertNull(model.getEstimate().getMeasurementJacobian());
273 Assertions.assertNull(model.getPhysicalMeasurementJacobian());
274 Assertions.assertNull(model.getEstimate().getStateTransitionMatrix());
275 Assertions.assertNull(model.getPhysicalStateTransitionMatrix());
276 }
277
278
279
280
281 private void checkModelAfterMeasurementAdded(final int expMeasurementNumber,
282 final ObservedMeasurement<?> meas,
283 final RealMatrix expPpred,
284 final Orbit expOrbitPred,
285 final RealMatrix expPhi,
286 final RealMatrix expH) {
287
288
289
290 final double srpCoefPred = srpCoefDriver.getValue();
291 final double satRangeBiasPred = satRangeBiasDriver.getValue();
292
293
294 final double[] expMeasPred =
295 meas.estimate(0, 0,
296 new SpacecraftState[] {new SpacecraftState(expOrbitPred)}).getEstimatedValue();
297
298
299 kalman.processMeasurements(Collections.singletonList(meas));
300 KalmanEstimation model = modelLogger.estimation;
301
302
303 final int N = meas.getDimension();
304
305
306 Assertions.assertEquals(0., model.getCurrentDate().durationFrom(expOrbitPred.getDate()), 0.);
307
308
309 Assertions.assertEquals(expMeasurementNumber, model.getCurrentMeasurementNumber());
310
311
312 Assertions.assertNull(model.getPhysicalStateTransitionMatrix());
313
314
315 Assertions.assertNull(model.getPhysicalMeasurementJacobian());
316
317
318 final double[] measSigmas = meas.getTheoreticalStandardDeviation();
319 final RealMatrix R = MatrixUtils.createRealMatrix(N, N);
320 for (int i = 0; i < N; i++) {
321 R.setEntry(i, i, measSigmas[i] * measSigmas[i]);
322 }
323
324
325 final RealMatrix expS = expH.multiply(expPpred.multiplyTransposed(expH)).add(R);
326 final RealMatrix S = model.getPhysicalInnovationCovarianceMatrix();
327 final double[][] dS = S.subtract(expS).getData();
328 for (int i = 0; i < N; i++) {
329 Assertions.assertArrayEquals(new double[N], dS[i], tol*1e8, "Failed on line \" + i");
330 }
331
332
333 final RealMatrix expK = expPpred.multiplyTransposed(expH).multiply(new LUDecomposition(expS).getSolver().getInverse());
334 final RealMatrix K = model.getPhysicalKalmanGain();
335 final double[][] dK = K.subtract(expK).getData();
336 for (int i = 0; i < M; i++) {
337 Assertions.assertArrayEquals(new double[N], dK[i], tol*1e5, "Failed on line " + i);
338 }
339
340
341 final Orbit orbitPred = model.getPredictedSpacecraftStates()[0].getOrbit();
342 final PVCoordinates pvOrbitPred = orbitPred.getPVCoordinates();
343 final PVCoordinates expPVOrbitPred = expOrbitPred.getPVCoordinates();
344 final double dpOrbitPred = Vector3D.distance(expPVOrbitPred.getPosition(), pvOrbitPred.getPosition());
345 final double dvOrbitPred = Vector3D.distance(expPVOrbitPred.getVelocity(), pvOrbitPred.getVelocity());
346 Assertions.assertEquals(0., dpOrbitPred, tol);
347 Assertions.assertEquals(0., dvOrbitPred, tol);
348
349
350 final double[] measPred = model.getPredictedMeasurement().getEstimatedValue();
351 Assertions.assertArrayEquals(expMeasPred, measPred, 1e-6);
352
353
354 final double[] orbitPredState = new double[6];
355 orbitPred.getType().mapOrbitToArray(orbitPred, PositionAngleType.TRUE, orbitPredState, null);
356 final RealVector expXpred = MatrixUtils.createRealVector(M);
357 for (int i = 0; i < 6; i++) {
358 expXpred.setEntry(i, orbitPredState[i]);
359 }
360 expXpred.setEntry(6, srpCoefPred);
361 expXpred.setEntry(7, satRangeBiasPred);
362
363
364
365 final RealVector observedMeas = MatrixUtils.createRealVector(model.getPredictedMeasurement().getObservedValue());
366 final RealVector predictedMeas = MatrixUtils.createRealVector(model.getPredictedMeasurement().getEstimatedValue());
367 final RealVector innovation = observedMeas.subtract(predictedMeas);
368
369
370 final RealVector expectedXcor = expXpred.add(expK.operate(innovation));
371 final RealVector Xcor = model.getPhysicalEstimatedState();
372 final double[] dXcor = Xcor.subtract(expectedXcor).toArray();
373 Assertions.assertArrayEquals(new double[M], dXcor, tol);
374
375
376 final RealMatrix expectedPcor =
377 (MatrixUtils.createRealIdentityMatrix(M).
378 subtract(expK.multiply(expH))).multiply(expPpred);
379 final RealMatrix Pcor = model.getPhysicalEstimatedCovarianceMatrix();
380 final double[][] dPcor = Pcor.subtract(expectedPcor).getData();
381 for (int i = 0; i < M; i++) {
382 Assertions.assertArrayEquals(new double[M], dPcor[i], tol*1e8, "Failed on line " + i);
383 }
384 }
385
386
387
388
389
390
391 private double[] getParametersScale(final NumericalPropagatorBuilder builder,
392 ParameterDriversList estimatedMeasurementsParameters) {
393 final List<Double> scaleList = new ArrayList<>();
394
395
396 for (ParameterDriver driver : builder.getOrbitalParametersDrivers().getDrivers()) {
397 if (driver.isSelected()) {
398 scaleList.add(driver.getScale());
399 }
400 }
401
402
403 for (ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {
404 if (driver.isSelected()) {
405 scaleList.add(driver.getScale());
406 }
407 }
408
409
410 for (ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
411 if (driver.isSelected()) {
412 scaleList.add(driver.getScale());
413 }
414 }
415
416 final double[] scales = new double[scaleList.size()];
417 for (int i = 0; i < scaleList.size(); i++) {
418 scales[i] = scaleList.get(i);
419 }
420 return scales;
421 }
422
423
424
425
426
427
428
429 private CovarianceMatrixProvider setInitialCovarianceMatrix(final double[] scales) {
430
431 final int n = scales.length;
432 final RealMatrix cov = MatrixUtils.createRealMatrix(n, n);
433 for (int i = 0; i < n; i++) {
434 cov.setEntry(i, i, scales[i] * scales[i]);
435 }
436 return new ConstantProcessNoise(cov);
437 }
438
439
440
441 public class ModelLogger implements KalmanObserver {
442 KalmanEstimation estimation;
443
444 @Override
445 public void evaluationPerformed(KalmanEstimation estimation) {
446 this.estimation = estimation;
447 }
448 }
449
450
451
452
453
454 @Test
455 public void MassDepletionTest() {
456
457 AbsoluteDate initialDate = this.propagatorBuilder.getInitialOrbitDate();
458 double initialMass = this.propagatorBuilder.getMass();
459 AbsoluteDate maneuverDate = initialDate.shiftedBy(5.0);
460
461 ConstantThrustManeuver maneuver = new ConstantThrustManeuver(maneuverDate, 10.0, 10.0, 100.0, new Vector3D(1.0, 0.0, 0.0));
462 this.propagatorBuilder.addForceModel(maneuver);
463
464 kalman.processMeasurements(Collections.singletonList(pv));
465 kalman.processMeasurements(Collections.singletonList(range));
466
467 double postManeuverPredictedStateMass = modelLogger.estimation.getPredictedSpacecraftStates()[0].getMass();
468 double postManeuverCorrectedStateMass = modelLogger.estimation.getCorrectedSpacecraftStates()[0].getMass();
469 double postManeuverPropagatorMass = propagatorBuilder.getMass();
470
471
472 Assertions.assertTrue(postManeuverPredictedStateMass < initialMass);
473
474
475
476 Assertions.assertEquals(postManeuverPredictedStateMass, postManeuverPropagatorMass);
477
478
479 Assertions.assertEquals(postManeuverCorrectedStateMass, postManeuverPropagatorMass);
480 }
481
482
483 }