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.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.net.URISyntaxException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Locale;
27
28 import org.hipparchus.geometry.euclidean.threed.Vector3D;
29 import org.hipparchus.linear.MatrixUtils;
30 import org.hipparchus.linear.RealMatrix;
31 import org.hipparchus.stat.descriptive.StreamingStatistics;
32 import org.hipparchus.util.FastMath;
33 import org.hipparchus.util.MerweUnscentedTransform;
34 import org.junit.jupiter.api.Assertions;
35 import org.junit.jupiter.api.Test;
36 import org.orekit.Utils;
37 import org.orekit.attitudes.FrameAlignedProvider;
38 import org.orekit.bodies.CelestialBody;
39 import org.orekit.bodies.CelestialBodyFactory;
40 import org.orekit.bodies.OneAxisEllipsoid;
41 import org.orekit.data.DataFilter;
42 import org.orekit.data.DataSource;
43 import org.orekit.data.GzipFilter;
44 import org.orekit.data.UnixCompressFilter;
45 import org.orekit.estimation.measurements.EstimatedMeasurement;
46 import org.orekit.estimation.measurements.ObservableSatellite;
47 import org.orekit.estimation.measurements.ObservedMeasurement;
48 import org.orekit.estimation.measurements.Position;
49 import org.orekit.files.ilrs.CPF;
50 import org.orekit.files.ilrs.CPF.CPFCoordinate;
51 import org.orekit.files.ilrs.CPF.CPFEphemeris;
52 import org.orekit.files.ilrs.CPFParser;
53 import org.orekit.files.rinex.HatanakaCompressFilter;
54 import org.orekit.forces.drag.DragForce;
55 import org.orekit.forces.drag.DragSensitive;
56 import org.orekit.forces.drag.IsotropicDrag;
57 import org.orekit.forces.gravity.potential.GravityFieldFactory;
58 import org.orekit.forces.gravity.potential.ICGEMFormatReader;
59 import org.orekit.forces.gravity.potential.SphericalHarmonicsProvider;
60 import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
61 import org.orekit.forces.radiation.IsotropicRadiationSingleCoefficient;
62 import org.orekit.forces.radiation.RadiationSensitive;
63 import org.orekit.frames.Frame;
64 import org.orekit.frames.FramesFactory;
65 import org.orekit.models.earth.atmosphere.Atmosphere;
66 import org.orekit.models.earth.atmosphere.NRLMSISE00;
67 import org.orekit.models.earth.atmosphere.data.MarshallSolarActivityFutureEstimation;
68 import org.orekit.orbits.CartesianOrbit;
69 import org.orekit.orbits.EquinoctialOrbit;
70 import org.orekit.orbits.Orbit;
71 import org.orekit.orbits.OrbitType;
72 import org.orekit.propagation.BoundedPropagator;
73 import org.orekit.propagation.PropagationType;
74 import org.orekit.propagation.conversion.ClassicalRungeKuttaIntegratorBuilder;
75 import org.orekit.propagation.conversion.DSSTPropagatorBuilder;
76 import org.orekit.propagation.conversion.ODEIntegratorBuilder;
77 import org.orekit.propagation.semianalytical.dsst.forces.DSSTAtmosphericDrag;
78 import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
79 import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction;
80 import org.orekit.propagation.semianalytical.dsst.forces.DSSTSolarRadiationPressure;
81 import org.orekit.propagation.semianalytical.dsst.forces.DSSTTesseral;
82 import org.orekit.propagation.semianalytical.dsst.forces.DSSTThirdBody;
83 import org.orekit.propagation.semianalytical.dsst.forces.DSSTZonal;
84 import org.orekit.time.AbsoluteDate;
85 import org.orekit.utils.Constants;
86 import org.orekit.utils.IERSConventions;
87 import org.orekit.utils.ParameterDriver;
88 import org.orekit.utils.TimeStampedPVCoordinates;
89
90 public class UnscentedSemiAnalyticalKalmanOrbitDeterminationTest {
91
92
93 private static final String HEADER = "%-25s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s\t%16s";
94
95
96 private static final String DATA_LINE = "%-25s\t%-16.6f\t%-16.6f\t%16.9f\t%-16.6f\t%-16.6f\t%16.9f\t%-16.6f\t%-16.6f\t%16.9f\t%16.9f\t%16.9f\t%16.9f\t%16.9f\t%16.9f\t%16.9f\t%16.9f\t%16.9f";
97
98
99 private static boolean print;
100
101
102
103
104
105
106
107
108 @Test
109 public void testLageos() throws URISyntaxException, IOException {
110
111
112 print = false;
113
114
115 Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format");
116 GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
117
118
119 final CPFEphemeris observations = initializeObservations("orbit-determination/Lageos2/lageos2_cpf_160213_5441.sgf");
120
121
122 final IERSConventions convention = IERSConventions.IERS_2010;
123 final boolean simpleEop = true;
124 final OneAxisEllipsoid centralBody = initializeBody(convention, simpleEop);
125
126
127 final int degree = 16;
128 final int order = 16;
129 final SphericalHarmonicsProvider gravityField = initializeGravityField(degree, order);
130
131
132 final Orbit initialOrbit = initializeOrbit(observations, gravityField);
133
134
135 final double step = 43200.0;
136 final double surface = 0.2831331;
137 final double mass = 405.38;
138 final boolean useDrag = false;
139 final boolean useSrp = true;
140 final boolean useMoon = true;
141 final boolean useSun = true;
142 final DSSTPropagatorBuilder propagator = initializePropagator(initialOrbit, centralBody, gravityField,
143 convention, simpleEop, step,
144 mass, surface, useDrag, useSrp,
145 useSun, useMoon);
146
147
148 final double sigma = 1.0;
149 final List<ObservedMeasurement<?>> measurements = initializeMeasurements(observations, initialOrbit, sigma);
150
151
152 final RealMatrix orbitalP = MatrixUtils.createRealDiagonalMatrix(new double[] {
153 100.0, 100.0, 100.0, 1.0e-3, 1.0e-3, 1.0e-3
154 });
155
156
157 final RealMatrix orbitalQ = MatrixUtils.createRealDiagonalMatrix(new double[] {1.0e-9, 1.0e-9, 1.0e-9, 1.0e-12, 1.0e-12, 1.0e-12});
158 final CovarianceMatrixProvider provider = buildCovarianceProvider(orbitalP, orbitalQ, propagator, initialOrbit);
159
160
161 final Observer observer = initializeEstimator(propagator, measurements, provider);
162
163
164 final KalmanEstimation estimation = observer.getEstimation();
165 final StreamingStatistics statX = observer.getXStatistics();
166 final StreamingStatistics statY = observer.getYStatistics();
167 final StreamingStatistics statZ = observer.getZStatistics();
168 Assertions.assertEquals(0.0, statX.getMean(), 1.365e-4);
169 Assertions.assertEquals(0.0, statY.getMean(), 4.931e-4);
170 Assertions.assertEquals(0.0, statZ.getMean(), 3.80e-4);
171 Assertions.assertEquals(0.0, statX.getMin(), 0.027);
172 Assertions.assertEquals(0.0, statY.getMin(), 0.028);
173 Assertions.assertEquals(0.0, statZ.getMin(), 0.026);
174 Assertions.assertEquals(0.0, statX.getMax(), 0.029);
175 Assertions.assertEquals(0.0, statY.getMax(), 0.027);
176 Assertions.assertEquals(0.0, statZ.getMax(), 0.026);
177
178
179 Assertions.assertNotNull(estimation.getPhysicalInnovationCovarianceMatrix());
180 Assertions.assertNotNull(estimation.getPhysicalKalmanGain());
181
182
183 Assertions.assertNull(estimation.getPhysicalMeasurementJacobian());
184 Assertions.assertNull(estimation.getPhysicalStateTransitionMatrix());
185
186 }
187
188
189
190
191
192
193
194
195 private CPFEphemeris initializeObservations(final String fileName) throws URISyntaxException, IOException {
196
197
198 final String inputPath = UnscentedSemiAnalyticalKalmanOrbitDeterminationTest.class.getClassLoader().
199 getResource(fileName).
200 toURI().
201 getPath();
202 final File file = new File(inputPath);
203
204
205 DataSource source = new DataSource(file.getName(), () -> new FileInputStream(new File(file.getParentFile(), file.getName())));
206 for (final DataFilter filter : Arrays.asList(new GzipFilter(),
207 new UnixCompressFilter(),
208 new HatanakaCompressFilter())) {
209 source = filter.filter(source);
210 }
211
212
213 final CPF cpfFile = new CPFParser().parse(source);
214 return cpfFile.getSatellites().get(cpfFile.getHeader().getIlrsSatelliteId());
215
216 }
217
218
219
220
221
222
223
224 private static OneAxisEllipsoid initializeBody(final IERSConventions convention, final boolean simpleEop) {
225
226 return new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
227 Constants.WGS84_EARTH_FLATTENING,
228 FramesFactory.getITRF(convention, simpleEop));
229 }
230
231
232
233
234
235
236
237 private static SphericalHarmonicsProvider initializeGravityField(final int degree, final int order) {
238 return GravityFieldFactory.getUnnormalizedProvider(degree, order);
239 }
240
241
242
243
244
245
246
247
248
249
250
251 private static Orbit initializeOrbit(final CPFEphemeris ephemeris, final SphericalHarmonicsProvider gravityField) {
252
253
254 final Frame orbitFrame = FramesFactory.getEME2000();
255
256
257 final BoundedPropagator bounded = ephemeris.getPropagator(new FrameAlignedProvider(orbitFrame));
258
259
260 final AbsoluteDate initialDate = bounded.getMinDate();
261
262
263 final TimeStampedPVCoordinates pvInitITRF = bounded.getPVCoordinates(initialDate, ephemeris.getFrame());
264 final TimeStampedPVCoordinates pvInitInertial = ephemeris.getFrame().getTransformTo(orbitFrame, initialDate).
265 transformPVCoordinates(pvInitITRF);
266
267
268 return new CartesianOrbit(new TimeStampedPVCoordinates(pvInitInertial.getDate(),
269 new Vector3D(pvInitInertial.getPosition().getX(),
270 pvInitInertial.getPosition().getY(),
271 pvInitInertial.getPosition().getZ()),
272 new Vector3D(pvInitInertial.getVelocity().getX(),
273 pvInitInertial.getVelocity().getY(),
274 pvInitInertial.getVelocity().getZ())),
275 orbitFrame, gravityField.getMu());
276
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294 private static DSSTPropagatorBuilder initializePropagator(final Orbit orbit,
295 final OneAxisEllipsoid centralBody,
296 final SphericalHarmonicsProvider gravityField,
297 final IERSConventions convention, final boolean simpleEop,
298 final double step, final double mass, final double surface,
299 final boolean useDrag, final boolean useSrp,
300 final boolean useSun, final boolean useMoon) {
301
302
303 final ODEIntegratorBuilder integrator = new ClassicalRungeKuttaIntegratorBuilder(step);
304
305
306 final EquinoctialOrbit equinoctial = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit);
307
308
309 final DSSTPropagatorBuilder propagator = new DSSTPropagatorBuilder(equinoctial, integrator, 1.0, PropagationType.MEAN, PropagationType.OSCULATING);
310
311
312 addDSSTForceModels(propagator, orbit, centralBody, gravityField, convention, simpleEop, surface, useDrag, useSrp, useSun, useMoon);
313
314
315 propagator.setMass(mass);
316
317
318 propagator.resetOrbit(orbit);
319
320
321 return propagator;
322
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339 private static void addDSSTForceModels(final DSSTPropagatorBuilder propagator,
340 final Orbit initialOrbit,
341 final OneAxisEllipsoid centralBody,
342 final SphericalHarmonicsProvider gravityField,
343 final IERSConventions convention, final boolean simpleEop,
344 final double surface,
345 final boolean useDrag, final boolean useSrp,
346 final boolean useSun, final boolean useMoon) {
347
348
349 final List<CelestialBody> solidTidesBodies = new ArrayList<>();
350
351
352 if (useDrag) {
353
354
355 final MarshallSolarActivityFutureEstimation msafe =
356 new MarshallSolarActivityFutureEstimation(MarshallSolarActivityFutureEstimation.DEFAULT_SUPPORTED_NAMES,
357 MarshallSolarActivityFutureEstimation.StrengthLevel.AVERAGE);
358 final Atmosphere atmosphere = new NRLMSISE00(msafe, CelestialBodyFactory.getSun(), centralBody);
359
360
361
362 final DSSTForceModel drag = new DSSTAtmosphericDrag(new DragForce(atmosphere, new IsotropicDrag(surface, 1.0)), gravityField.getMu());
363 for (final ParameterDriver driver : drag.getParametersDrivers()) {
364 if (driver.getName().equals(DragSensitive.DRAG_COEFFICIENT)) {
365 driver.setSelected(true);
366 }
367 }
368
369
370 propagator.addForceModel(drag);
371
372 }
373
374
375 if (useSrp) {
376
377
378 final RadiationSensitive spacecraft = new IsotropicRadiationSingleCoefficient(surface, 1.13);
379
380
381 final DSSTForceModel srp = new DSSTSolarRadiationPressure(CelestialBodyFactory.getSun(), centralBody, spacecraft, gravityField.getMu());
382 for (final ParameterDriver driver : srp.getParametersDrivers()) {
383 if (driver.getName().equals(RadiationSensitive.REFLECTION_COEFFICIENT)) {
384
385 }
386 }
387
388
389 propagator.addForceModel(srp);
390
391 }
392
393
394 if (useSun) {
395 solidTidesBodies.add(CelestialBodyFactory.getSun());
396 propagator.addForceModel(new DSSTThirdBody(CelestialBodyFactory.getSun(), gravityField.getMu()));
397 }
398
399
400 if (useMoon) {
401 solidTidesBodies.add(CelestialBodyFactory.getMoon());
402 propagator.addForceModel(new DSSTThirdBody(CelestialBodyFactory.getMoon(), gravityField.getMu()));
403 }
404
405
406 propagator.addForceModel(new DSSTTesseral(centralBody.getBodyFrame(), Constants.WGS84_EARTH_ANGULAR_VELOCITY, (UnnormalizedSphericalHarmonicsProvider) gravityField));
407 propagator.addForceModel(new DSSTZonal((UnnormalizedSphericalHarmonicsProvider) gravityField));
408
409
410 propagator.addForceModel(new DSSTNewtonianAttraction(gravityField.getMu()));
411
412 }
413
414
415
416
417
418
419
420
421 private static List<ObservedMeasurement<?>> initializeMeasurements(final CPFEphemeris ephemeris,
422 final Orbit orbit,
423 final double sigma) {
424
425
426 final ObservableSatellite satellite = new ObservableSatellite(0);
427
428
429 final List<ObservedMeasurement<?>> measurements = new ArrayList<>();
430
431
432 for (final CPFCoordinate coordinate : ephemeris.getCoordinates()) {
433
434
435 final Vector3D posInertial = ephemeris.getFrame().getStaticTransformTo(orbit.getFrame(), coordinate.getDate()).
436 transformPosition(coordinate.getPosition());
437
438
439 final Position measurement = new Position(coordinate.getDate(), posInertial, sigma, 1.0, satellite);
440
441
442 measurements.add(measurement);
443
444 }
445
446
447 return measurements;
448
449 }
450
451
452
453
454
455
456
457 private static Observer initializeEstimator(final DSSTPropagatorBuilder propagator,
458 final List<ObservedMeasurement<?>> measurements,
459 final CovarianceMatrixProvider provider) {
460
461
462 final SemiAnalyticalUnscentedKalmanEstimatorBuilder builder = new SemiAnalyticalUnscentedKalmanEstimatorBuilder();
463
464
465 builder.addPropagationConfiguration(propagator, provider);
466
467
468 builder.unscentedTransformProvider(new MerweUnscentedTransform(propagator.getSelectedNormalizedParameters().length));
469
470
471 final SemiAnalyticalUnscentedKalmanEstimator estimator = builder.build();
472
473 final Observer observer = new Observer();
474 estimator.setObserver(observer);
475
476
477 estimator.processMeasurements(measurements);
478
479
480 return observer;
481
482 }
483
484
485
486
487
488
489
490
491
492 private static CovarianceMatrixProvider buildCovarianceProvider(final RealMatrix initialNoiseMatrix, final RealMatrix processNoiseMatrix,
493 final DSSTPropagatorBuilder propagatorBuilder, final Orbit orbit) {
494
495 final Orbit initialOrbit = OrbitType.EQUINOCTIAL.convertType(orbit);
496 final double[][] dYdC = new double[6][6];
497 initialOrbit.getJacobianWrtCartesian(propagatorBuilder.getPositionAngleType(), dYdC);
498 final RealMatrix Jac = MatrixUtils.createRealMatrix(dYdC);
499
500
501 final RealMatrix orbitalP = Jac.multiply(initialNoiseMatrix.multiply(Jac.transpose()));
502
503
504 return new ConstantProcessNoise(orbitalP, processNoiseMatrix);
505 }
506
507
508 public static class Observer implements KalmanObserver {
509
510
511 private StreamingStatistics statX;
512
513
514 private StreamingStatistics statY;
515
516
517 private StreamingStatistics statZ;
518
519
520 private KalmanEstimation estimation;
521
522
523 public Observer() {
524 statX = new StreamingStatistics();
525 statY = new StreamingStatistics();
526 statZ = new StreamingStatistics();
527 if (print) {
528 final String header = String.format(Locale.US, HEADER, "Epoch",
529 "X Observed (m)", "X Estimated (m)", "X residual (m)",
530 "Y Observed (m)", "Y Estimated (m)", "Y residual (m)",
531 "Z Observed (m)", "Z Estimated (m)", "Z residual (m)",
532 "Cov(0;0)", "Cov(1;1)", "Cov(2;2)", "Cov(3;3)", "Cov(4;4)", "Cov(5;5)",
533 "3D Pos Cov (m)", "3D Vel Cov (m)");
534 System.out.println(header);
535 }
536
537 }
538
539
540 @Override
541 public void evaluationPerformed(final KalmanEstimation estimation) {
542
543
544 final EstimatedMeasurement<?> estimatedMeasurement = estimation.getCorrectedMeasurement();
545
546
547 if (estimatedMeasurement.getObservedMeasurement() instanceof Position) {
548
549 if (estimatedMeasurement.getStatus() == EstimatedMeasurement.Status.REJECTED) {
550 if (print) {
551 System.out.println("REJECTED");
552 }
553 } else {
554 final double[] estimated = estimatedMeasurement.getEstimatedValue();
555 final double[] observed = estimatedMeasurement.getObservedValue();
556
557
558 final double observedX = observed[0];
559 final double observedY = observed[1];
560 final double observedZ = observed[2];
561
562
563 final double estimatedX = estimated[0];
564 final double estimatedY = estimated[1];
565 final double estimatedZ = estimated[2];
566
567
568 final double resX = estimatedX - observedX;
569 final double resY = estimatedY - observedY;
570 final double resZ = estimatedZ - observedZ;
571 statX.addValue(resX);
572 statY.addValue(resY);
573 statZ.addValue(resZ);
574
575 if (print) {
576
577
578 final RealMatrix covariance = estimation.getPhysicalEstimatedCovarianceMatrix();
579 final double cov11 = covariance.getEntry(0, 0);
580 final double cov22 = covariance.getEntry(1, 1);
581 final double cov33 = covariance.getEntry(2, 2);
582 final double cov44 = covariance.getEntry(3, 3);
583 final double cov55 = covariance.getEntry(4, 4);
584 final double cov66 = covariance.getEntry(5, 5);
585 final double cPos = FastMath.sqrt(cov11 + cov22 + cov33);
586 final double cVel = FastMath.sqrt(cov44 + cov55 + cov66);
587
588
589 final String line = String.format(Locale.US, DATA_LINE, estimatedMeasurement.getDate(),
590 observedX, estimatedX, resX,
591 observedY, estimatedY, resY,
592 observedZ, estimatedZ, resZ,
593 cov11, cov22, cov33, cov44, cov55, cov66,
594 cPos, cVel);
595 System.out.println(line);
596 }
597
598 }
599
600 }
601
602 this.estimation = estimation;
603
604 }
605
606
607
608
609
610 public StreamingStatistics getXStatistics() {
611 if (print) {
612 System.out.println("Min X res (m): " + statX.getMin() + " Max X res (m): " + statX.getMax() + " Mean X res (m): " + statX.getMean() + " STD: " + statX.getStandardDeviation());
613 }
614 return statX;
615 }
616
617
618
619
620
621 public StreamingStatistics getYStatistics() {
622 if (print) {
623 System.out.println("Min Y res (m): " + statY.getMin() + " Max Y res (m): " + statY.getMax() + " Mean Y res (m): " + statY.getMean() + " STD: " + statY.getStandardDeviation());
624 }
625 return statY;
626 }
627
628
629
630
631
632 public StreamingStatistics getZStatistics() {
633 if (print) {
634 System.out.println("Min Z res (m): " + statZ.getMin() + " Max Z res (m): " + statZ.getMax() + " Mean Z res (m): " + statZ.getMean() + " STD: " + statZ.getStandardDeviation());
635 }
636 return statZ;
637 }
638
639
640
641
642
643 public KalmanEstimation getEstimation() {
644 return estimation;
645 }
646
647 }
648
649 }