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