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 PositionTest {
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 PositionMeasurementCreator(),
62                                                                 1.0, 3.0, 300.0);
63  
64          propagator.clearStepHandlers();
65  
66          // Prepare statistics for position values difference
67          final StreamingStatistics[] pvDiffStat = new StreamingStatistics[3];
68          for (int i = 0; i < 3; 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 position 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 < 3; 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.8e-7);
91              Assert.assertEquals(0.0, pvDiffStat[i].getStandardDeviation(), 2.3e-7);
92          }
93      }
94      
95      /** Test the values of the state derivatives using a numerical.
96       * finite differences calculation as a reference
97       */
98      @Test
99      public void testStateDerivatives() {
100 
101         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
102 
103         final NumericalPropagatorBuilder propagatorBuilder =
104                         context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
105                                               1.0e-6, 60.0, 0.001);
106 
107         // create perfect range measurements
108         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
109                                                                            propagatorBuilder);
110         final List<ObservedMeasurement<?>> measurements =
111                         EstimationTestUtils.createMeasurements(propagator,
112                                                                new PositionMeasurementCreator(),
113                                                                1.0, 3.0, 300.0);
114         propagator.clearStepHandlers();
115 
116         double[] errorsP = new double[3 * 6 * measurements.size()];
117         int indexP = 0;
118         for (final ObservedMeasurement<?> measurement : measurements) {
119 
120             final AbsoluteDate    date      = measurement.getDate();
121             final SpacecraftState state     = propagator.propagate(date);
122             final double[][]      jacobian  = measurement.estimate(0, 0, new SpacecraftState[] { state }).getStateDerivatives(0);
123 
124             // compute a reference value using finite differences
125             final double[][] finiteDifferencesJacobian =
126                 Differentiation.differentiate(new StateFunction() {
127                     public double[] value(final SpacecraftState state) {
128                         return measurement.estimate(0, 0, new SpacecraftState[] { state }).getEstimatedValue();
129                     }
130                                                   }, measurement.getDimension(),
131                                                   propagator.getAttitudeProvider(), OrbitType.CARTESIAN,
132                                                   PositionAngle.TRUE, 1.0, 3).value(state);
133 
134             Assert.assertEquals(finiteDifferencesJacobian.length, jacobian.length);
135             Assert.assertEquals(finiteDifferencesJacobian[0].length, jacobian[0].length);
136             for (int i = 0; i < jacobian.length; ++i) {
137                 for (int j = 0; j < jacobian[i].length; ++j) {
138                     final double relativeError = FastMath.abs((finiteDifferencesJacobian[i][j] - jacobian[i][j]) /
139                                                               finiteDifferencesJacobian[i][j]);
140                     errorsP[indexP++] = relativeError;
141                 }
142             }
143 
144         }
145 
146         // median errors
147         Assert.assertEquals(0.0, new Median().evaluate(errorsP), 2.1e-100);
148 
149     }
150     
151     /** Test the position constructor with standard deviations for position given as one double. 
152      */
153     @Test
154     public void testPositionWithSingleStandardDeviations() {
155         
156         // Context
157         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
158         
159         // Dummy P, T
160         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
161         final AbsoluteDate date     = context.initialOrbit.getDate();
162         
163         // Initialize standard deviations and weight
164         final double sigmaP     = 10.;
165         final double baseWeight = 0.5;
166         
167         // Reference covariance matrix and correlation coefficients
168         final double[][] Pref = new double[3][3];
169         for (int i = 0; i < 3; i++) {
170             Pref[i][i]     = FastMath.pow(sigmaP, 2);
171         }
172         final double[][] corrCoefRef = MatrixUtils.createRealIdentityMatrix(3).getData();
173         
174         // Reference propagator numbers
175         final ObservableSatellite[] sats = {
176             new ObservableSatellite(0),
177             new ObservableSatellite(2)
178         };
179         
180         // Create PV measurements
181         final Position[] ps = new Position[2];
182         ps[0] = new Position(date, position, sigmaP, baseWeight, sats[0]);
183         ps[1] = new Position(date, position, sigmaP, baseWeight, sats[1]);
184         
185         // Tolerance
186         final double eps = 1e-20; // tolerance
187         
188         // Check data
189         for (int k = 0; k < ps.length; k++) {
190             final Position p = ps[k];
191 
192             // Propagator numbers
193             assertEquals(sats[k].getPropagatorIndex(), p.getSatellites().get(0).getPropagatorIndex());
194             
195             // Weights
196             for (int i = 0; i < 3; i++) {
197                 assertEquals(baseWeight, p.getBaseWeight()[i], eps);
198             }
199             // Sigmas
200             for (int i = 0; i < 3; i++) {
201                 assertEquals(sigmaP, p.getTheoreticalStandardDeviation()[i]  , eps);
202             }
203             // Covariances
204             final double[][] P = p.getCovarianceMatrix();
205             // Substract with ref and get the norm
206             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
207             assertEquals(0., normP, eps);
208             
209             // Correlation coef
210             final double[][] corrCoef = p.getCorrelationCoefficientsMatrix();
211             // Substract with ref and get the norm
212             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
213             assertEquals(0., normCorrCoef, eps);
214         }
215     }
216     
217     /** Test the Position constructor with standard deviations for position given as a 3-sized vector. 
218      */
219     @Test
220     public void testPositionWithVectorStandardDeviations() {
221         
222         // Context
223         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
224         
225         // Dummy P, T
226         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
227         final AbsoluteDate date     = context.initialOrbit.getDate();
228         
229         // Initialize standard deviations and weight
230         final double[] sigmaP  = {10., 20., 30.};
231         final double baseWeight = 0.5;
232         
233         // Reference covariance matrix and correlation coefficients
234         final double[][] Pref = new double[3][3];
235         for (int i = 0; i < 3; i++) {
236             Pref[i][i]     = FastMath.pow(sigmaP[i], 2);
237         }
238         final double[][] corrCoefRef = MatrixUtils.createRealIdentityMatrix(3).getData();
239         
240         // Reference propagator numbers
241         final ObservableSatellite[] sats = {
242             new ObservableSatellite(0),
243             new ObservableSatellite(2)
244         };
245         
246         // Create PV measurements
247         final Position[] ps = new Position[2];
248         ps[0] = new Position(date, position, sigmaP, baseWeight, sats[0]);
249         ps[1] = new Position(date, position, sigmaP, baseWeight, sats[1]);
250         
251         // Tolerance
252         final double eps = 1e-20; // tolerance
253         
254         // Check data
255         for (int k = 0; k < ps.length; k++) {
256             final Position p = ps[k];
257 
258             // Propagator numbers
259             assertEquals(sats[k].getPropagatorIndex(), p.getSatellites().get(0).getPropagatorIndex());
260             
261             // Weights
262             for (int i = 0; i < 3; i++) {
263                 assertEquals(baseWeight, p.getBaseWeight()[i], eps);
264             }
265             // Sigmas
266             for (int i = 0; i < 3; i++) {
267                 assertEquals(sigmaP[i], p.getTheoreticalStandardDeviation()[i]  , eps);
268             }
269             // Covariances
270             final double[][] P = p.getCovarianceMatrix();
271             // Substract with ref and get the norm
272             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
273             assertEquals(0., normP, eps);
274             
275             // Correlation coef
276             final double[][] corrCoef = p.getCorrelationCoefficientsMatrix();
277             // Substract with ref and get the norm
278             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
279             assertEquals(0., normCorrCoef, eps);
280         }
281     }
282     
283     /** Test the Position constructor with 3x3 covariance matrix as input. 
284      */
285     @Test
286     public void testPositionWithCovarianceMatrix() {
287         // Context
288         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
289         
290         // Dummy P, T
291         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
292         final AbsoluteDate date     = context.initialOrbit.getDate();
293         
294         // Initialize standard deviations and weight
295         final double[][] positionP = {{100., 400., 1200.}, {400., 400., 1800.}, {1200., 1800., 900.}};
296         final double baseWeight = 0.5;
297         
298         // Reference covariance matrix and correlation coefficients
299         final double[][] Pref = new double[3][3];
300         for (int i = 0; i < 3; i++) {
301             for (int j = i; j < 3; j++) {
302                 Pref[i][j]     = positionP[i][j];
303                 Pref[j][i]     = positionP[i][j];
304             }
305         }
306         final double[][] corrCoefRef3 = {{1., 2., 4.}, {2., 1., 3.}, {4., 3., 1.}};
307         final double[][] corrCoefRef  = new double[3][3];
308         for (int i = 0; i < 3; i++) {
309             for (int j = i; j < 3; j++) {
310                 corrCoefRef[i][j]     = corrCoefRef3[i][j];
311                 corrCoefRef[j][i]     = corrCoefRef3[i][j];
312             }
313         }
314         
315         // Reference propagator numbers
316         final ObservableSatellite[] sats = {
317             new ObservableSatellite(0),
318             new ObservableSatellite(2)
319         };
320         
321         // Reference standard deviations
322         final double[] sigmaP = {10., 20., 30.};
323         
324         // Create Position measurements
325         final Position[] ps = new Position[2];
326         ps[0] = new Position(date, position, positionP, baseWeight, sats[0]);
327         ps[1] = new Position(date, position, positionP, baseWeight, sats[1]);
328         
329         // Tolerance
330         final double eps = 6.7e-16; // tolerance
331         
332         // Check data
333         for (int k = 0; k < ps.length; k++) {
334             final Position p = ps[k];
335 
336             // Propagator numbers
337             assertEquals(sats[k].getPropagatorIndex(), p.getSatellites().get(0).getPropagatorIndex());
338             
339             // Weights
340             for (int i = 0; i < 3; i++) {
341                 assertEquals(baseWeight, p.getBaseWeight()[i], eps);
342             }
343             // Sigmas
344             for (int i = 0; i < 3; i++) {
345                 assertEquals(sigmaP[i], p.getTheoreticalStandardDeviation()[i]  , eps);
346             }
347             // Covariances
348             final double[][] P = p.getCovarianceMatrix();
349             // Substract with ref and get the norm
350             final double normP = MatrixUtils.createRealMatrix(P).subtract(MatrixUtils.createRealMatrix(Pref)).getNorm1();
351             assertEquals(0., normP, eps);
352             
353             // Correlation coef
354             final double[][] corrCoef = p.getCorrelationCoefficientsMatrix();
355             // Substract with ref and get the norm
356             final double normCorrCoef = MatrixUtils.createRealMatrix(corrCoef).subtract(MatrixUtils.createRealMatrix(corrCoefRef)).getNorm1();
357             assertEquals(0., normCorrCoef, eps);
358         }
359         
360     }
361     
362     /** Test exceptions raised if the covariance matrix does not have the proper size. */
363     @Test
364     public void testExceptions() {
365         // Context
366         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
367         
368         // Dummy P, T
369         final Vector3D     position = context.initialOrbit.getPVCoordinates().getPosition();
370         final AbsoluteDate date     = context.initialOrbit.getDate();
371         final double       weight   = 1.;
372         
373         // Build with one 3-sized vectors
374         try {
375             new Position(date, position, new double[] {1.}, weight, new ObservableSatellite(0));
376             Assert.fail("An OrekitException should have been thrown");
377         } catch (OrekitException e) {
378             // An exception should indeed be raised here
379         }
380         
381         // Build with one 3x3 matrix
382         try {
383             new Position(date, position, new double[][] {{0., 0.}, {0., 0.}}, weight, new ObservableSatellite(0));
384             Assert.fail("An OrekitException should have been thrown");
385         } catch (OrekitException e) {
386             // An exception should indeed be raised here
387         }
388     }
389 }
390 
391