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.measurements;
18  
19  import static org.junit.Assert.assertEquals;
20  
21  import java.util.List;
22  
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.linear.MatrixUtils;
25  import org.hipparchus.stat.descriptive.StreamingStatistics;
26  import org.hipparchus.stat.descriptive.rank.Median;
27  import org.hipparchus.util.FastMath;
28  import org.junit.Assert;
29  import org.junit.Test;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.estimation.Context;
32  import org.orekit.estimation.EstimationTestUtils;
33  import org.orekit.orbits.OrbitType;
34  import org.orekit.orbits.PositionAngle;
35  import org.orekit.propagation.Propagator;
36  import org.orekit.propagation.SpacecraftState;
37  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.utils.Differentiation;
40  import org.orekit.utils.StateFunction;
41  
42  public class PVTest {
43  
44      /** Compare observed values and estimated values.
45       *  Both are calculated with a different algorithm
46       */
47      @Test
48      public void testValues() {
49  
50          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
51  
52          final NumericalPropagatorBuilder propagatorBuilder =
53                          context.createBuilder(OrbitType.EQUINOCTIAL, PositionAngle.TRUE, false,
54                                                1.0e-6, 60.0, 0.001);
55  
56          // Create perfect right-ascension/declination measurements
57          final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
58                                                                             propagatorBuilder);
59          final List<ObservedMeasurement<?>> measurements =
60                          EstimationTestUtils.createMeasurements(propagator,
61                                                                 new PVMeasurementCreator(),
62                                                                 1.0, 3.0, 300.0);
63  
64          propagator.clearStepHandlers();
65  
66          // Prepare statistics for PV values difference
67          final StreamingStatistics[] pvDiffStat = new StreamingStatistics[6];
68          for (int i = 0; i < 6; i++) {
69              pvDiffStat[i] = new StreamingStatistics();  
70          }
71  
72          for (final ObservedMeasurement<?> measurement : measurements) {
73  
74              // Propagate to measurement date
75              final AbsoluteDate datemeas  = measurement.getDate();
76              SpacecraftState    state     = propagator.propagate(datemeas);
77              
78              // Estimate the PV value
79              final EstimatedMeasurement<?> estimated = measurement.estimate(0, 0, new SpacecraftState[] { state });
80              
81              // Store the difference between estimated and observed values in the stats
82              for (int i = 0; i < 6; i++) {
83                  pvDiffStat[i].addValue(FastMath.abs(estimated.getEstimatedValue()[i] - measurement.getObservedValue()[i]));    
84              }
85          }
86  
87          // Mean and std errors check
88          for (int i = 0; i < 3; i++) {
89              // Check position values
90              Assert.assertEquals(0.0, pvDiffStat[i].getMean(), 3.74e-7);
91              Assert.assertEquals(0.0, pvDiffStat[i].getStandardDeviation(), 2.21e-7);
92              
93              // Check velocity values
94              Assert.assertEquals(0.0, pvDiffStat[i+3].getMean(), 1.29e-10);
95              Assert.assertEquals(0.0, pvDiffStat[i+3].getStandardDeviation(), 7.82e-11);
96          }
97      }
98      
99      /** Test the values of the state derivatives using a numerical.
100      * finite differences calculation as a reference
101      */
102     @Test
103     public void testStateDerivatives() {
104 
105         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
106 
107         final NumericalPropagatorBuilder propagatorBuilder =
108                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
109                                               1.0e-6, 60.0, 0.001);
110 
111         // create perfect range measurements
112         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
113                                                                            propagatorBuilder);
114         final List<ObservedMeasurement<?>> measurements =
115                         EstimationTestUtils.createMeasurements(propagator,
116                                                                new PVMeasurementCreator(),
117                                                                1.0, 3.0, 300.0);
118         propagator.clearStepHandlers();
119 
120         double[] errorsP = new double[3 * 6 * measurements.size()];
121         double[] errorsV = new double[3 * 6 * measurements.size()];
122         int indexP = 0;
123         int indexV = 0;
124         for (final ObservedMeasurement<?> measurement : measurements) {
125 
126             final AbsoluteDate    date      = measurement.getDate();
127             final SpacecraftState state     = propagator.propagate(date);
128             final double[][]      jacobian  = measurement.estimate(0, 0, new SpacecraftState[] { state }).getStateDerivatives(0);
129 
130             // compute a reference value using finite differences
131             final double[][] finiteDifferencesJacobian =
132                 Differentiation.differentiate(new StateFunction() {
133                     public double[] value(final SpacecraftState state) {
134                         return measurement.estimate(0, 0, new SpacecraftState[] { state }).getEstimatedValue();
135                     }
136                                                   }, measurement.getDimension(),
137                                                   propagator.getAttitudeProvider(), OrbitType.CARTESIAN,
138                                                   PositionAngle.TRUE, 1.0, 3).value(state);
139 
140             Assert.assertEquals(finiteDifferencesJacobian.length, jacobian.length);
141             Assert.assertEquals(finiteDifferencesJacobian[0].length, jacobian[0].length);
142             for (int i = 0; i < jacobian.length; ++i) {
143                 for (int j = 0; j < jacobian[i].length; ++j) {
144                     final double relativeError = FastMath.abs((finiteDifferencesJacobian[i][j] - jacobian[i][j]) /
145                                                               finiteDifferencesJacobian[i][j]);
146                     if (j < 3) {
147                         errorsP[indexP++] = relativeError;
148                     } else {
149                         errorsV[indexV++] = relativeError;
150                     }
151                 }
152             }
153 
154         }
155 
156         // median errors
157         Assert.assertEquals(0.0, new Median().evaluate(errorsP), 2.1e-10);
158         Assert.assertEquals(0.0, new Median().evaluate(errorsV), 2.1e-10);
159 
160     }
161     
162     /** Test the PV constructor with standard deviations for position and velocity given as 2 double. 
163      */
164     @Test
165     public void testPVWithSingleStandardDeviations() {
166         
167         // Context
168         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
169         
170         // Dummy P, V, T
171         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
172         final Vector3D     velocity = context.initialOrbit.getPVCoordinates().getVelocity();
173         final AbsoluteDate date     = context.initialOrbit.getDate();
174         
175         // Initialize standard deviations and weight
176         final double sigmaP     = 10.;
177         final double sigmaV     = 0.1;
178         final double baseWeight = 0.5;
179         
180         // Reference covariance matrix and correlation coefficients
181         final double[][] Pref = new double[6][6];
182         for (int i = 0; i < 3; i++) {
183             Pref[i][i]     = FastMath.pow(sigmaP, 2);
184             Pref[i+3][i+3] = FastMath.pow(sigmaV, 2);
185         }
186         final double[][] corrCoefRef = MatrixUtils.createRealIdentityMatrix(6).getData();
187         
188         // Reference propagator numbers
189         final ObservableSatellite[] sats = {
190             new ObservableSatellite(0),
191             new ObservableSatellite(2)
192         };
193         
194         // Create PV measurements
195         final PV[] pvs = new PV[2];
196         pvs[0] = new PV(date, position, velocity, sigmaP, sigmaV, baseWeight, sats[0]);
197         pvs[1] = new PV(date, position, velocity, sigmaP, sigmaV, baseWeight, sats[1]);
198         
199         // Tolerance
200         final double eps = 1e-20; // tolerance
201         
202         // Check data
203         for (int k = 0; k < pvs.length; k++) {
204             final PV pv = pvs[k];
205 
206             // Propagator numbers
207             assertEquals(sats[k].getPropagatorIndex(), pv.getSatellites().get(0).getPropagatorIndex());
208             
209             // Weights
210             for (int i = 0; i < 6; i++) {
211                 assertEquals(baseWeight, pv.getBaseWeight()[i], eps);
212             }
213             // Sigmas
214             for (int i = 0; i < 3; i++) {
215                 assertEquals(sigmaP, pv.getTheoreticalStandardDeviation()[i]  , eps);
216                 assertEquals(sigmaV, pv.getTheoreticalStandardDeviation()[i+3], eps);
217             }
218             // Covariances
219             final double[][] P = pv.getCovarianceMatrix();
220             // Substract with ref and get the norm
221             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
222             assertEquals(0., normP, eps);
223             
224             // Correlation coef
225             final double[][] corrCoef = pv.getCorrelationCoefficientsMatrix();
226             // Substract with ref and get the norm
227             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
228             assertEquals(0., normCorrCoef, eps);
229         }
230     }
231     
232     /** Test the PV constructor with standard deviations for position and velocity given as a 6-sized vector. 
233      */
234     @Test
235     public void testPVWithVectorStandardDeviations() {
236         
237         // Context
238         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
239         
240         // Dummy P, V, T
241         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
242         final Vector3D     velocity = context.initialOrbit.getPVCoordinates().getVelocity();
243         final AbsoluteDate date     = context.initialOrbit.getDate();
244         
245         // Initialize standard deviations and weight
246         final double[] sigmaP     = {10., 20., 30.};
247         final double[] sigmaV     = {0.1, 0.2, 0.3};
248         final double[] sigmaPV    = {10., 20., 30., 0.1, 0.2, 0.3};
249         final double baseWeight = 0.5;
250         
251         // Reference covariance matrix and correlation coefficients
252         final double[][] Pref = new double[6][6];
253         for (int i = 0; i < 3; i++) {
254             Pref[i][i]     = FastMath.pow(sigmaP[i], 2);
255             Pref[i+3][i+3] = FastMath.pow(sigmaV[i], 2);
256         }
257         final double[][] corrCoefRef = MatrixUtils.createRealIdentityMatrix(6).getData();
258         
259         // Reference propagator numbers
260         final ObservableSatellite[] sats = {
261             new ObservableSatellite(0),
262             new ObservableSatellite(2),
263             new ObservableSatellite(0),
264             new ObservableSatellite(10)
265         };
266         
267         // Create PV measurements
268         final PV[] pvs = new PV[4];
269         pvs[0] = new PV(date, position, velocity, sigmaP, sigmaV, baseWeight, sats[0]);
270         pvs[1] = new PV(date, position, velocity, sigmaP, sigmaV, baseWeight, sats[1]);
271         pvs[2] = new PV(date, position, velocity, sigmaPV, baseWeight, sats[2]);
272         pvs[3] = new PV(date, position, velocity, sigmaPV, baseWeight, sats[3]);
273         
274         // Tolerance
275         final double eps = 1e-20; // tolerance
276         
277         // Check data
278         for (int k = 0; k < pvs.length; k++) {
279             final PV pv = pvs[k];
280 
281             // Propagator numbers
282             assertEquals(sats[k].getPropagatorIndex(), pv.getSatellites().get(0).getPropagatorIndex());
283                         
284             // Weights
285             for (int i = 0; i < 6; i++) {
286                 assertEquals(baseWeight, pv.getBaseWeight()[i], eps);
287             }
288             // Sigmas
289             for (int i = 0; i < 3; i++) {
290                 assertEquals(sigmaP[i], pv.getTheoreticalStandardDeviation()[i]  , eps);
291                 assertEquals(sigmaV[i], pv.getTheoreticalStandardDeviation()[i+3], eps);
292             }
293             // Covariances
294             final double[][] P = pv.getCovarianceMatrix();
295             // Substract with ref and get the norm
296             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
297             assertEquals(0., normP, eps);
298             
299             // Correlation coef
300             final double[][] corrCoef = pv.getCorrelationCoefficientsMatrix();
301             // Substract with ref and get the norm
302             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
303             assertEquals(0., normCorrCoef, eps);
304         }
305     }
306     
307     /** Test the PV constructor with two 3x3 covariance matrix (one for position, the other for velocity) as input. 
308      */
309     @Test
310     public void testPVWithTwoCovarianceMatrices() {
311         // Context
312         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
313         
314         // Dummy P, V, T
315         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
316         final Vector3D     velocity = context.initialOrbit.getPVCoordinates().getVelocity();
317         final AbsoluteDate date     = context.initialOrbit.getDate();
318         
319         // Initialize standard deviations and weight
320         final double[][] positionP = {{100., 400., 1200.}, {400., 400., 1800.}, {1200., 1800., 900.}};
321         final double[][] velocityP = {{0.01, 0.04, 0.12 }, {0.04, 0.04, 0.18 }, {0.12 , 0.18 , 0.09}};
322         final double baseWeight = 0.5;
323         
324         // Reference covariance matrix and correlation coefficients
325         final double[][] Pref = new double[6][6];
326         for (int i = 0; i < 3; i++) {
327             for (int j = i; j < 3; j++) {
328                 Pref[i][j]     = positionP[i][j];
329                 Pref[j][i]     = positionP[i][j];
330                 Pref[i+3][j+3] = velocityP[i][j];
331                 Pref[j+3][i+3] = velocityP[i][j];
332             }
333         }
334         final double[][] corrCoefRef3 = {{1., 2., 4.}, {2., 1., 3.}, {4., 3., 1.}};
335         final double[][] corrCoefRef  = new double[6][6];
336         for (int i = 0; i < 3; i++) {
337             for (int j = i; j < 3; j++) {
338                 corrCoefRef[i][j]     = corrCoefRef3[i][j];
339                 corrCoefRef[j][i]     = corrCoefRef3[i][j];
340                 corrCoefRef[i+3][j+3] = corrCoefRef3[i][j];
341                 corrCoefRef[j+3][i+3] = corrCoefRef3[i][j];
342             }
343         }
344         
345         // Reference propagator numbers
346         final ObservableSatellite[] sats = {
347             new ObservableSatellite(0),
348             new ObservableSatellite(2)
349         };
350         
351         // Reference standard deviations
352         final double[] sigmaP = {10., 20., 30.};
353         final double[] sigmaV = {0.1, 0.2, 0.3};
354         
355         // Create PV measurements
356         final PV[] pvs = new PV[2];
357         pvs[0] = new PV(date, position, velocity, positionP, velocityP, baseWeight, sats[0]);
358         pvs[1] = new PV(date, position, velocity, positionP, velocityP, baseWeight, sats[1]);
359         
360         // Tolerance
361         final double eps = 6.7e-16; // tolerance
362         
363         // Check data
364         for (int k = 0; k < pvs.length; k++) {
365             final PV pv = pvs[k];
366 
367             // Propagator numbers
368             assertEquals(sats[k].getPropagatorIndex(), pv.getSatellites().get(0).getPropagatorIndex());
369             
370             // Weights
371             for (int i = 0; i < 6; i++) {
372                 assertEquals(baseWeight, pv.getBaseWeight()[i], eps);
373             }
374             // Sigmas
375             for (int i = 0; i < 3; i++) {
376                 assertEquals(sigmaP[i], pv.getTheoreticalStandardDeviation()[i]  , eps);
377                 assertEquals(sigmaV[i], pv.getTheoreticalStandardDeviation()[i+3], eps);
378             }
379             // Covariances
380             final double[][] P = pv.getCovarianceMatrix();
381             // Substract with ref and get the norm
382             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
383             assertEquals(0., normP, eps);
384             
385             // Correlation coef
386             final double[][] corrCoef = pv.getCorrelationCoefficientsMatrix();
387             // Substract with ref and get the norm
388             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
389             assertEquals(0., normCorrCoef, eps);
390         }
391         
392     }
393     
394     /** Test the PV constructor with one 6x6 covariance matrix as input. 
395      */
396     @Test
397     public void testPVWithCovarianceMatrix() {
398         // Context
399         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
400         
401         // Dummy P, V, T
402         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
403         final Vector3D     velocity = context.initialOrbit.getPVCoordinates().getVelocity();
404         final AbsoluteDate date     = context.initialOrbit.getDate();
405         
406         // Initialize standard deviations, weight and corr coeff
407         final double[] sigmaPV = {10., 20., 30., 0.1, 0.2, 0.3};
408         final double baseWeight = 0.5;
409         final double[][] corrCoefRef = new double[6][6];
410         for (int i = 0; i < 6; i++) {
411             for (int j = 0; j < 6; j++) {
412                 if (i == j) {
413                     corrCoefRef[i][i] = 1.;
414                 } else {
415                     corrCoefRef[i][j] = i + j + 1;
416                 }
417             }
418         }
419         
420         // Reference covariance matrix
421         final double[][] Pref = new double[6][6];
422         for (int i = 0; i < 6; i++) {
423             for (int j = 0; j < 6; j++) {
424                 Pref[i][j] = corrCoefRef[i][j]*sigmaPV[i]*sigmaPV[j];
425             }
426         }
427         
428         // Reference propagator numbers
429         final ObservableSatellite[] sats = {
430             new ObservableSatellite(0),
431             new ObservableSatellite(2)
432         };
433         
434         // Create PV measurements
435         final PV[] pvs = new PV[2];
436         pvs[0] = new PV(date, position, velocity, Pref, baseWeight, sats[0]);
437         pvs[1] = new PV(date, position, velocity, Pref, baseWeight, sats[1]);
438         
439         // Tolerance
440         final double eps = 1.8e-15; // tolerance
441         
442         // Check data
443         for (int k = 0; k < pvs.length; k++) {
444             final PV pv = pvs[k];
445 
446             // Propagator numbers
447             assertEquals(sats[k].getPropagatorIndex(), pv.getSatellites().get(0).getPropagatorIndex());
448             
449             // Weights
450             for (int i = 0; i < 6; i++) {
451                 assertEquals(baseWeight, pv.getBaseWeight()[i], eps);
452             }
453             // Sigmas
454             for (int i = 0; i < 6; i++) {
455                 assertEquals(sigmaPV[i], pv.getTheoreticalStandardDeviation()[i]  , eps);
456             }
457             // Covariances
458             final double[][] P = pv.getCovarianceMatrix();
459             // Substract with ref and get the norm
460             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
461             assertEquals(0., normP, eps);
462             
463             // Correlation coef
464             final double[][] corrCoef = pv.getCorrelationCoefficientsMatrix();
465             // Substract with ref and get the norm
466             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
467             assertEquals(0., normCorrCoef, eps);
468         }
469         
470     }
471     
472     /** Test exceptions raised if the covariance matrix does not have the proper size. */
473     @Test
474     public void testExceptions() {
475         // Context
476         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
477         
478         // Dummy P, V, T
479         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
480         final Vector3D     velocity = context.initialOrbit.getPVCoordinates().getVelocity();
481         final AbsoluteDate date     = context.initialOrbit.getDate();
482         final double       weight   = 1.;
483         
484         // Build with two 3-sized vectors
485         try {
486             new PV(date, position, velocity, new double[] {0., 0., 0.}, new double[] {1.}, weight, new ObservableSatellite(0));
487             Assert.fail("An OrekitException should have been thrown");
488         } catch (OrekitException e) {
489             // An exception should indeed be raised here
490         }
491         
492         // Build with one 6-sized vector
493         try {
494             new PV(date, position, velocity, new double[] {0., 0., 0.}, weight, new ObservableSatellite(0));
495             Assert.fail("An OrekitException should have been thrown");
496         } catch (OrekitException e) {
497             // An exception should indeed be raised here
498         }
499         
500         // Build with two 3x3 matrices
501         try {
502             new PV(date, position, velocity, new double[][] {{0., 0.}, {0., 0.}},
503                    new double[][] {{0., 0.}, {0., 0.}}, weight, new ObservableSatellite(0));
504             Assert.fail("An OrekitException should have been thrown");
505         } catch (OrekitException e) {
506             // An exception should indeed be raised here
507         }
508         
509         // Build with one 6x6 matrix
510         try {
511             new PV(date, position, velocity, new double[][] {{0., 0.}, {0., 0.}}, weight, new ObservableSatellite(0));
512             Assert.fail("An OrekitException should have been thrown");
513         } catch (OrekitException e) {
514             // An exception should indeed be raised here
515         }
516     }
517 }
518 
519