1   /* Copyright 2002-2022 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.io.File;
20  import java.io.IOException;
21  import java.net.URISyntaxException;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.NoSuchElementException;
26  
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.hipparchus.linear.MatrixUtils;
29  import org.hipparchus.linear.RealMatrix;
30  import org.hipparchus.util.FastMath;
31  import org.junit.Assert;
32  import org.junit.Test;
33  import org.orekit.KeyValueFileParser;
34  import org.orekit.Utils;
35  import org.orekit.attitudes.AttitudeProvider;
36  import org.orekit.bodies.CelestialBody;
37  import org.orekit.bodies.OneAxisEllipsoid;
38  import org.orekit.estimation.common.AbstractOrbitDetermination;
39  import org.orekit.estimation.common.ParameterKey;
40  import org.orekit.estimation.common.ResultKalman;
41  import org.orekit.forces.ForceModel;
42  import org.orekit.forces.drag.DragForce;
43  import org.orekit.forces.drag.DragSensitive;
44  import org.orekit.forces.empirical.AccelerationModel;
45  import org.orekit.forces.empirical.ParametricAcceleration;
46  import org.orekit.forces.empirical.PolynomialAccelerationModel;
47  import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
48  import org.orekit.forces.gravity.OceanTides;
49  import org.orekit.forces.gravity.Relativity;
50  import org.orekit.forces.gravity.SolidTides;
51  import org.orekit.forces.gravity.ThirdBodyAttraction;
52  import org.orekit.forces.gravity.potential.GravityFieldFactory;
53  import org.orekit.forces.gravity.potential.ICGEMFormatReader;
54  import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
55  import org.orekit.forces.radiation.KnockeRediffusedForceModel;
56  import org.orekit.forces.radiation.RadiationSensitive;
57  import org.orekit.forces.radiation.SolarRadiationPressure;
58  import org.orekit.models.earth.atmosphere.Atmosphere;
59  import org.orekit.orbits.Orbit;
60  import org.orekit.orbits.OrbitType;
61  import org.orekit.orbits.PositionAngle;
62  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
63  import org.orekit.propagation.conversion.ODEIntegratorBuilder;
64  import org.orekit.time.TimeScalesFactory;
65  import org.orekit.utils.IERSConventions;
66  import org.orekit.utils.ParameterDriver;
67  import org.orekit.utils.ParameterDriversList;
68  import org.orekit.utils.ParameterDriversList.DelegatingDriver;
69  
70  public class KalmanNumericalOrbitDeterminationTest extends AbstractOrbitDetermination<NumericalPropagatorBuilder> {
71  
72      /** Gravity field. */
73      private NormalizedSphericalHarmonicsProvider gravityField;
74  
75      /** {@inheritDoc} */
76      @Override
77      protected void createGravityField(final KeyValueFileParser<ParameterKey> parser)
78          throws NoSuchElementException {
79  
80          final int degree = parser.getInt(ParameterKey.CENTRAL_BODY_DEGREE);
81          final int order  = FastMath.min(degree, parser.getInt(ParameterKey.CENTRAL_BODY_ORDER));
82          gravityField = GravityFieldFactory.getNormalizedProvider(degree, order);
83  
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      protected double getMu() {
89          return gravityField.getMu();
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      protected NumericalPropagatorBuilder createPropagatorBuilder(final Orbit referenceOrbit,
95                                                                   final ODEIntegratorBuilder builder,
96                                                                   final double positionScale) {
97          return new NumericalPropagatorBuilder(referenceOrbit, builder, PositionAngle.MEAN,
98                                                positionScale);
99      }
100 
101     /** {@inheritDoc} */
102     @Override
103     protected void setMass(final NumericalPropagatorBuilder propagatorBuilder,
104                                 final double mass) {
105         propagatorBuilder.setMass(mass);
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     protected List<ParameterDriver> setGravity(final NumericalPropagatorBuilder propagatorBuilder,
111                                                final OneAxisEllipsoid body) {
112         final ForceModel gravityModel = new HolmesFeatherstoneAttractionModel(body.getBodyFrame(), gravityField);
113         propagatorBuilder.addForceModel(gravityModel);
114         return gravityModel.getParametersDrivers();
115     }
116 
117     /** {@inheritDoc} */
118     @Override
119     protected List<ParameterDriver> setOceanTides(final NumericalPropagatorBuilder propagatorBuilder,
120                                                   final IERSConventions conventions,
121                                                   final OneAxisEllipsoid body,
122                                                   final int degree, final int order) {
123         final ForceModel tidesModel = new OceanTides(body.getBodyFrame(),
124                                                      gravityField.getAe(), gravityField.getMu(),
125                                                      degree, order, conventions,
126                                                      TimeScalesFactory.getUT1(conventions, true));
127         propagatorBuilder.addForceModel(tidesModel);
128         return tidesModel.getParametersDrivers();
129     }
130 
131     /** {@inheritDoc} */
132     @Override
133     protected List<ParameterDriver> setSolidTides(final NumericalPropagatorBuilder propagatorBuilder,
134                                                   final IERSConventions conventions,
135                                                   final OneAxisEllipsoid body,
136                                                   final CelestialBody[] solidTidesBodies) {
137         final ForceModel tidesModel = new SolidTides(body.getBodyFrame(),
138                                                      gravityField.getAe(), gravityField.getMu(),
139                                                      gravityField.getTideSystem(), conventions,
140                                                      TimeScalesFactory.getUT1(conventions, true),
141                                                      solidTidesBodies);
142         propagatorBuilder.addForceModel(tidesModel);
143         return tidesModel.getParametersDrivers();
144     }
145 
146     /** {@inheritDoc} */
147     @Override
148     protected List<ParameterDriver> setThirdBody(final NumericalPropagatorBuilder propagatorBuilder,
149                                                  final CelestialBody thirdBody) {
150         final ForceModel thirdBodyModel = new ThirdBodyAttraction(thirdBody);
151         propagatorBuilder.addForceModel(thirdBodyModel);
152         return thirdBodyModel.getParametersDrivers();
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     protected List<ParameterDriver> setDrag(final NumericalPropagatorBuilder propagatorBuilder,
158                                             final Atmosphere atmosphere, final DragSensitive spacecraft) {
159         final ForceModel dragModel = new DragForce(atmosphere, spacecraft);
160         propagatorBuilder.addForceModel(dragModel);
161         return dragModel.getParametersDrivers();
162     }
163 
164     /** {@inheritDoc} */
165     @Override
166     protected List<ParameterDriver> setSolarRadiationPressure(final NumericalPropagatorBuilder propagatorBuilder, final CelestialBody sun,
167                                                               final double equatorialRadius, final RadiationSensitive spacecraft) {
168         final ForceModel srpModel = new SolarRadiationPressure(sun, equatorialRadius, spacecraft);
169         propagatorBuilder.addForceModel(srpModel);
170         return srpModel.getParametersDrivers();
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     protected List<ParameterDriver> setAlbedoInfrared(final NumericalPropagatorBuilder propagatorBuilder,
176                                                       final CelestialBody sun, final double equatorialRadius,
177                                                       final double angularResolution,
178                                                       final RadiationSensitive spacecraft) {
179         final ForceModel albedoIR = new KnockeRediffusedForceModel(sun, spacecraft, equatorialRadius, angularResolution);
180         propagatorBuilder.addForceModel(albedoIR);
181         return albedoIR.getParametersDrivers();
182     }
183 
184     /** {@inheritDoc} */
185     @Override
186     protected List<ParameterDriver> setRelativity(final NumericalPropagatorBuilder propagatorBuilder) {
187         final ForceModel relativityModel = new Relativity(gravityField.getMu());
188         propagatorBuilder.addForceModel(relativityModel);
189         return relativityModel.getParametersDrivers();
190     }
191 
192     /** {@inheritDoc} */
193     @Override
194     protected List<ParameterDriver> setPolynomialAcceleration(final NumericalPropagatorBuilder propagatorBuilder,
195                                                               final String name, final Vector3D direction, final int degree) {
196         final AccelerationModel accModel = new PolynomialAccelerationModel(name, null, degree);
197         final ForceModel polynomialModel = new ParametricAcceleration(direction, true, accModel);
198         propagatorBuilder.addForceModel(polynomialModel);
199         return polynomialModel.getParametersDrivers();
200     }
201 
202     /** {@inheritDoc} */
203     @Override
204     protected void setAttitudeProvider(final NumericalPropagatorBuilder propagatorBuilder,
205                                        final AttitudeProvider attitudeProvider) {
206         propagatorBuilder.setAttitudeProvider(attitudeProvider);
207     }
208 
209     @Test
210     // Orbit determination for Lageos2 based on SLR (range) measurements
211     public void testLageos2() throws URISyntaxException, IOException {
212 
213         // Print results on console
214         final boolean print = false;
215         
216         // input in resources directory
217         final String inputPath = KalmanNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/kalman_od_test_Lageos2.in").toURI().getPath();
218         final File input  = new File(inputPath);
219 
220         // configure Orekit data acces
221         Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format");
222         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
223 
224         // Choice of an orbit type to use
225         // Default for test is Cartesian
226         final OrbitType orbitType = OrbitType.CARTESIAN;
227         
228         // Initial orbital Cartesian covariance matrix
229         // These covariances are derived from the deltas between initial and reference orbits
230         // So in a way they are "perfect"...
231         // Cartesian covariance matrix initialization
232         final RealMatrix cartesianOrbitalP = MatrixUtils.createRealDiagonalMatrix(new double [] {
233             1e4, 4e3, 1, 5e-3, 6e-5, 1e-4
234         });
235         
236         // Orbital Cartesian process noise matrix (Q)
237         final RealMatrix cartesianOrbitalQ = MatrixUtils.createRealDiagonalMatrix(new double [] {
238             1.e-4, 1.e-4, 1.e-4, 1.e-10, 1.e-10, 1.e-10
239         });
240         
241         // Initial measurement covariance matrix and process noise matrix
242         final RealMatrix measurementP = MatrixUtils.createRealDiagonalMatrix(new double [] {
243            1., 1., 1., 1. 
244         });
245         final RealMatrix measurementQ = MatrixUtils.createRealDiagonalMatrix(new double [] {
246             1e-6, 1e-6, 1e-6, 1e-6
247          });
248 
249         // Kalman orbit determination run.
250         ResultKalman kalmanLageos2 = runKalman(input, orbitType, print,
251                                                cartesianOrbitalP, cartesianOrbitalQ,
252                                                null, null,
253                                                measurementP, measurementQ);
254 
255         // Definition of the accuracy for the test
256         final double distanceAccuracy = 0.86;
257         final double velocityAccuracy = 4.12e-3;
258 
259         // Tests
260         // Note: The reference initial orbit is the same as in the batch LS tests
261         // -----
262         
263         // Number of measurements processed
264         final int numberOfMeas  = 258;
265         Assert.assertEquals(numberOfMeas, kalmanLageos2.getNumberOfMeasurements());
266 
267         // Estimated position and velocity
268         final Vector3D estimatedPos = kalmanLageos2.getEstimatedPV().getPosition();
269         final Vector3D estimatedVel = kalmanLageos2.getEstimatedPV().getVelocity();
270 
271         // Reference position and velocity at initial date (same as in batch LS test)
272         final Vector3D refPos0 = new Vector3D(-5532131.956902, 10025696.592156, -3578940.040009);
273         final Vector3D refVel0 = new Vector3D(-3871.275109, -607.880985, 4280.972530);
274         
275         // Run the reference until Kalman last date
276         final Orbit refOrbit = runReference(input, orbitType, refPos0, refVel0, null,
277                                             kalmanLageos2.getEstimatedPV().getDate());
278         final Vector3D refPos = refOrbit.getPVCoordinates().getPosition();
279         final Vector3D refVel = refOrbit.getPVCoordinates().getVelocity();
280         
281         // Check distances
282         final double dP = Vector3D.distance(refPos, estimatedPos);
283         final double dV = Vector3D.distance(refVel, estimatedVel);
284         Assert.assertEquals(0.0, dP, distanceAccuracy);
285         Assert.assertEquals(0.0, dV, velocityAccuracy);
286         
287         // Print orbit deltas
288         if (print) {
289             System.out.println("Test performances:");
290             System.out.format("\t%-30s\n",
291                             "ΔEstimated / Reference");
292             System.out.format(Locale.US, "\t%-10s %20.6f\n",
293                               "ΔP [m]", dP);
294             System.out.format(Locale.US, "\t%-10s %20.6f\n",
295                               "ΔV [m/s]", dV);
296         }
297 
298         // Test on measurements parameters
299         final List<DelegatingDriver> list = new ArrayList<DelegatingDriver>();
300         list.addAll(kalmanLageos2.getMeasurementsParameters().getDrivers());
301         sortParametersChanges(list);
302         // Batch LS values
303         //final double[] stationOffSet = { 1.659203,  0.861250,  -0.885352 };
304         //final double rangeBias = -0.286275;
305         final double[] stationOffSet = { 0.298867,  -0.137456,  0.013315 };
306         final double rangeBias = 0.002390;
307         Assert.assertEquals(stationOffSet[0], list.get(0).getValue(), distanceAccuracy);
308         Assert.assertEquals(stationOffSet[1], list.get(1).getValue(), distanceAccuracy);
309         Assert.assertEquals(stationOffSet[2], list.get(2).getValue(), distanceAccuracy);
310         Assert.assertEquals(rangeBias,        list.get(3).getValue(), distanceAccuracy);
311 
312         //test on statistic for the range residuals
313         final long nbRange = 258;
314         // Batch LS values
315         //final double[] RefStatRange = { -2.431135, 2.218644, 0.038483, 0.982017 };
316         final double[] RefStatRange = { -23.561314, 20.436464, 0.964164, 5.687187 };
317         Assert.assertEquals(nbRange, kalmanLageos2.getRangeStat().getN());
318         Assert.assertEquals(RefStatRange[0], kalmanLageos2.getRangeStat().getMin(),               distanceAccuracy);
319         Assert.assertEquals(RefStatRange[1], kalmanLageos2.getRangeStat().getMax(),               distanceAccuracy);
320         Assert.assertEquals(RefStatRange[2], kalmanLageos2.getRangeStat().getMean(),              distanceAccuracy);
321         Assert.assertEquals(RefStatRange[3], kalmanLageos2.getRangeStat().getStandardDeviation(), distanceAccuracy);
322 
323     }
324 
325     @Test
326     // Orbit determination for range, azimuth elevation measurements
327     public void testW3B() throws URISyntaxException, IOException {
328 
329         // Print results on console
330         final boolean print = false;
331         
332         // input in resources directory
333         final String inputPath = KalmanNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/W3B/od_test_W3.in").toURI().getPath();
334         final File input  = new File(inputPath);
335 
336         // Configure Orekit data access
337         Utils.setDataRoot("orbit-determination/W3B:potential/icgem-format");
338         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
339 
340         // Choice of an orbit type to use
341         // Default for test is Cartesian
342         final OrbitType orbitType = OrbitType.CARTESIAN;
343         
344         // Initial orbital Cartesian covariance matrix
345         // These covariances are derived from the deltas between initial and reference orbits
346         // So in a way they are "perfect"...
347         // Cartesian covariance matrix initialization
348         final RealMatrix cartesianOrbitalP = MatrixUtils.createRealDiagonalMatrix(new double [] {
349             FastMath.pow(2.4e4, 2), FastMath.pow(1.e5, 2), FastMath.pow(4.e4, 2),
350             FastMath.pow(3.5, 2), FastMath.pow(2., 2), FastMath.pow(0.6, 2)
351         });
352         
353         // Orbital Cartesian process noise matrix (Q)
354         final RealMatrix cartesianOrbitalQ = MatrixUtils.createRealDiagonalMatrix(new double [] {
355             1.e-4, 1.e-4, 1.e-4, 1.e-10, 1.e-10, 1.e-10
356         });
357         
358         // Propagation covariance and process noise matrices
359         final RealMatrix propagationP = MatrixUtils.createRealDiagonalMatrix(new double [] {
360             FastMath.pow(2., 2), // Cd
361             FastMath.pow(5.7e-6, 2), FastMath.pow(1.1e-11, 2),   // leak-X
362             FastMath.pow(7.68e-7, 2), FastMath.pow(1.26e-10, 2), // leak-Y
363             FastMath.pow(5.56e-6, 2), FastMath.pow(2.79e-10, 2)  // leak-Z
364         });
365         final RealMatrix propagationQ = MatrixUtils.createRealDiagonalMatrix(new double [] {
366             FastMath.pow(1e-3, 2), // Cd
367             0., 0., 0., 0., 0., 0.  // Leaks
368         });
369         
370         // Measurement covariance and process noise matrices
371         // az/el bias sigma = 0.06deg
372         // range bias sigma = 100m
373         final double angularVariance = FastMath.pow(FastMath.toRadians(0.06), 2);
374         final double rangeVariance   = FastMath.pow(500., 2);
375         final RealMatrix measurementP = MatrixUtils.createRealDiagonalMatrix(new double [] {
376             angularVariance, angularVariance, rangeVariance,
377             angularVariance, angularVariance, rangeVariance,
378             angularVariance, angularVariance, rangeVariance,
379             angularVariance, angularVariance, rangeVariance,
380             angularVariance, angularVariance, rangeVariance,
381         });
382         // Process noise sigma: 1e-6 for all
383         final double measQ = FastMath.pow(1e-6, 2);
384         final RealMatrix measurementQ = MatrixUtils.
385                         createRealIdentityMatrix(measurementP.getRowDimension()).
386                         scalarMultiply(measQ);
387         
388 
389         // Kalman orbit determination run.
390         ResultKalman kalmanW3B = runKalman(input, orbitType, print,
391                                            cartesianOrbitalP, cartesianOrbitalQ,
392                                            propagationP, propagationQ,
393                                            measurementP, measurementQ);
394 
395         // Tests
396         // -----
397         
398         // Definition of the accuracy for the test
399         final double distanceAccuracy = 0.1;
400         final double angleAccuracy    = 1e-5; // degrees
401         
402         // Number of measurements processed
403         final int numberOfMeas  = 521;
404         Assert.assertEquals(numberOfMeas, kalmanW3B.getNumberOfMeasurements());
405 
406 
407         // Test on propagator parameters
408         // -----------------------------
409         
410         // Batch LS result
411         // final double dragCoef  = -0.2154;
412         final double dragCoef  = 0.1931;
413         final ParameterDriversList propagatorParameters = kalmanW3B.getPropagatorParameters();
414         Assert.assertEquals(dragCoef, propagatorParameters.getDrivers().get(0).getValue(), 1e-3);
415         final Vector3D leakAcceleration0 =
416                         new Vector3D(propagatorParameters.getDrivers().get(1).getValue(),
417                                      propagatorParameters.getDrivers().get(3).getValue(),
418                                      propagatorParameters.getDrivers().get(5).getValue());
419         // Batch LS results
420         //Assert.assertEquals(8.002e-6, leakAcceleration0.getNorm(), 1.0e-8);
421         Assert.assertEquals(5.994e-6, leakAcceleration0.getNorm(), 1.0e-8);
422         final Vector3D leakAcceleration1 =
423                         new Vector3D(propagatorParameters.getDrivers().get(2).getValue(),
424                                      propagatorParameters.getDrivers().get(4).getValue(),
425                                      propagatorParameters.getDrivers().get(6).getValue());
426         // Batch LS results
427         //Assert.assertEquals(3.058e-10, leakAcceleration1.getNorm(), 1.0e-12);
428         Assert.assertEquals(1.831e-10, leakAcceleration1.getNorm(), 1.0e-12);
429 
430         // Test on measurements parameters
431         // -------------------------------
432         
433         final List<DelegatingDriver> list = new ArrayList<DelegatingDriver>();
434         list.addAll(kalmanW3B.getMeasurementsParameters().getDrivers());
435         sortParametersChanges(list);
436 
437         // Station CastleRock
438         // Batch LS results
439 //        final double[] CastleAzElBias  = { 0.062701342, -0.003613508 };
440 //        final double   CastleRangeBias = 11274.4677;
441         final double[] CastleAzElBias  = { 0.062635, -0.003672};
442         final double   CastleRangeBias = 11289.3678;
443         Assert.assertEquals(CastleAzElBias[0], FastMath.toDegrees(list.get(0).getValue()), angleAccuracy);
444         Assert.assertEquals(CastleAzElBias[1], FastMath.toDegrees(list.get(1).getValue()), angleAccuracy);
445         Assert.assertEquals(CastleRangeBias,   list.get(2).getValue(),                     distanceAccuracy);
446 
447         // Station Fucino
448         // Batch LS results
449 //        final double[] FucAzElBias  = { -0.053526137, 0.075483886 };
450 //        final double   FucRangeBias = 13467.8256;
451         final double[] FucAzElBias  = { -0.053298, 0.075589 };
452         final double   FucRangeBias = 13482.0715;
453         Assert.assertEquals(FucAzElBias[0], FastMath.toDegrees(list.get(3).getValue()), angleAccuracy);
454         Assert.assertEquals(FucAzElBias[1], FastMath.toDegrees(list.get(4).getValue()), angleAccuracy);
455         Assert.assertEquals(FucRangeBias,   list.get(5).getValue(),                     distanceAccuracy);
456 
457         // Station Kumsan
458         // Batch LS results
459 //        final double[] KumAzElBias  = { -0.023574208, -0.054520756 };
460 //        final double   KumRangeBias = 13512.57594;
461         final double[] KumAzElBias  = { -0.022805, -0.055057 };
462         final double   KumRangeBias = 13502.7459;
463         Assert.assertEquals(KumAzElBias[0], FastMath.toDegrees(list.get(6).getValue()), angleAccuracy);
464         Assert.assertEquals(KumAzElBias[1], FastMath.toDegrees(list.get(7).getValue()), angleAccuracy);
465         Assert.assertEquals(KumRangeBias,   list.get(8).getValue(),                     distanceAccuracy);
466 
467         // Station Pretoria
468         // Batch LS results
469 //        final double[] PreAzElBias = { 0.030201539, 0.009747877 };
470 //        final double PreRangeBias = 13594.11889;
471         final double[] PreAzElBias = { 0.030353, 0.009658 };
472         final double PreRangeBias = 13609.2516;
473         Assert.assertEquals(PreAzElBias[0], FastMath.toDegrees(list.get( 9).getValue()), angleAccuracy);
474         Assert.assertEquals(PreAzElBias[1], FastMath.toDegrees(list.get(10).getValue()), angleAccuracy);
475         Assert.assertEquals(PreRangeBias,   list.get(11).getValue(),                     distanceAccuracy);
476 
477         // Station Uralla
478         // Batch LS results
479 //        final double[] UraAzElBias = { 0.167814449, -0.12305252 };
480 //        final double UraRangeBias = 13450.26738;
481         final double[] UraAzElBias = { 0.167519, -0.122842 };
482         final double UraRangeBias = 13441.7019;
483         Assert.assertEquals(UraAzElBias[0], FastMath.toDegrees(list.get(12).getValue()), angleAccuracy);
484         Assert.assertEquals(UraAzElBias[1], FastMath.toDegrees(list.get(13).getValue()), angleAccuracy);
485         Assert.assertEquals(UraRangeBias,   list.get(14).getValue(),                     distanceAccuracy);
486 
487         // Test on statistic for the range residuals
488         final long nbRange = 182;
489         //statistics for the range residual (min, max, mean, std)
490         final double[] RefStatRange = { -12.981, 18.046, -1.133, 5.312 };
491         Assert.assertEquals(nbRange, kalmanW3B.getRangeStat().getN());
492         Assert.assertEquals(RefStatRange[0], kalmanW3B.getRangeStat().getMin(),               distanceAccuracy);
493         Assert.assertEquals(RefStatRange[1], kalmanW3B.getRangeStat().getMax(),               distanceAccuracy);
494         Assert.assertEquals(RefStatRange[2], kalmanW3B.getRangeStat().getMean(),              distanceAccuracy);
495         Assert.assertEquals(RefStatRange[3], kalmanW3B.getRangeStat().getStandardDeviation(), distanceAccuracy);
496 
497         //test on statistic for the azimuth residuals
498         final long nbAzi = 339;
499         //statistics for the azimuth residual (min, max, mean, std)
500         final double[] RefStatAzi = { -0.041441, 0.023473, -0.004426, 0.009911 };
501         Assert.assertEquals(nbAzi, kalmanW3B.getAzimStat().getN());
502         Assert.assertEquals(RefStatAzi[0], kalmanW3B.getAzimStat().getMin(),               angleAccuracy);
503         Assert.assertEquals(RefStatAzi[1], kalmanW3B.getAzimStat().getMax(),               angleAccuracy);
504         Assert.assertEquals(RefStatAzi[2], kalmanW3B.getAzimStat().getMean(),              angleAccuracy);
505         Assert.assertEquals(RefStatAzi[3], kalmanW3B.getAzimStat().getStandardDeviation(), angleAccuracy);
506 
507         //test on statistic for the elevation residuals
508         final long nbEle = 339;
509         final double[] RefStatEle = { -0.025399, 0.043345, 0.001011, 0.010636 };
510         Assert.assertEquals(nbEle, kalmanW3B.getElevStat().getN());
511         Assert.assertEquals(RefStatEle[0], kalmanW3B.getElevStat().getMin(),               angleAccuracy);
512         Assert.assertEquals(RefStatEle[1], kalmanW3B.getElevStat().getMax(),               angleAccuracy);
513         Assert.assertEquals(RefStatEle[2], kalmanW3B.getElevStat().getMean(),              angleAccuracy);
514         Assert.assertEquals(RefStatEle[3], kalmanW3B.getElevStat().getStandardDeviation(), angleAccuracy);
515 
516         RealMatrix covariances = kalmanW3B.getCovariances();
517         Assert.assertEquals(28, covariances.getRowDimension());
518         Assert.assertEquals(28, covariances.getColumnDimension());
519 
520         // drag coefficient variance
521         Assert.assertEquals(0.016349, covariances.getEntry(6, 6), 1.0e-5);
522 
523         // leak-X constant term variance
524         Assert.assertEquals(2.047303E-13, covariances.getEntry(7, 7), 1.0e-16);
525 
526         // leak-Y constant term variance
527         Assert.assertEquals(5.462497E-13, covariances.getEntry(9, 9), 1.0e-15);
528 
529         // leak-Z constant term variance
530         Assert.assertEquals(1.717781E-11, covariances.getEntry(11, 11), 1.0e-15);
531         
532         
533         // Test on orbital parameters
534         // Done at the end to avoid changing the estimated propagation parameters
535         // ----------------------------------------------------------------------
536         
537         // Estimated position and velocity
538         final Vector3D estimatedPos = kalmanW3B.getEstimatedPV().getPosition();
539         final Vector3D estimatedVel = kalmanW3B.getEstimatedPV().getVelocity();
540 
541         // Reference position and velocity at initial date (same as in batch LS test)
542         final Vector3D refPos0 = new Vector3D(-40541446.255, -9905357.41, 206777.413);
543         final Vector3D refVel0 = new Vector3D(759.0685, -1476.5156, 54.793);
544         
545         // Gather the selected propagation parameters and initialize them to the values found
546         // with the batch LS method
547         final ParameterDriversList refPropagationParameters = kalmanW3B.getPropagatorParameters();
548         final double dragCoefRef = -0.215433133145843;
549         final double[] leakXRef = {+5.69040439901955E-06, 1.09710906802403E-11};
550         final double[] leakYRef = {-7.66440256777678E-07, 1.25467464335066E-10};
551         final double[] leakZRef = {-5.574055079952E-06  , 2.78703463746911E-10};
552         
553         for (DelegatingDriver driver : refPropagationParameters.getDrivers()) {
554             switch (driver.getName()) {
555                 case "drag coefficient" : driver.setValue(dragCoefRef); break;
556                 case "leak-X[0]"        : driver.setValue(leakXRef[0]); break;
557                 case "leak-X[1]"        : driver.setValue(leakXRef[1]); break;
558                 case "leak-Y[0]"        : driver.setValue(leakYRef[0]); break;
559                 case "leak-Y[1]"        : driver.setValue(leakYRef[1]); break;
560                 case "leak-Z[0]"        : driver.setValue(leakZRef[0]); break;
561                 case "leak-Z[1]"        : driver.setValue(leakZRef[1]); break;
562             }
563         }
564         
565         // Run the reference until Kalman last date
566         final Orbit refOrbit = runReference(input, orbitType, refPos0, refVel0, refPropagationParameters,
567                                             kalmanW3B.getEstimatedPV().getDate());
568         
569         // Test on last orbit
570         final Vector3D refPos = refOrbit.getPVCoordinates().getPosition();
571         final Vector3D refVel = refOrbit.getPVCoordinates().getVelocity();
572         
573         // Check distances
574         final double dP = Vector3D.distance(refPos, estimatedPos);
575         final double dV = Vector3D.distance(refVel, estimatedVel);
576         
577         // FIXME: debug - Comparison with batch LS is bad
578         final double debugDistanceAccuracy = 234.82;
579         final double debugVelocityAccuracy = 0.086;
580         Assert.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), debugDistanceAccuracy);
581         Assert.assertEquals(0.0, Vector3D.distance(refVel, estimatedVel), debugVelocityAccuracy);
582         
583         // Print orbit deltas
584         if (print) {
585             System.out.println("Test performances:");
586             System.out.format("\t%-30s\n",
587                             "ΔEstimated / Reference");
588             System.out.format(Locale.US, "\t%-10s %20.6f\n",
589                               "ΔP [m]", dP);
590             System.out.format(Locale.US, "\t%-10s %20.6f\n",
591                               "ΔV [m/s]", dV);
592         }
593     }
594 
595 }