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 static org.junit.Assert.assertArrayEquals;
20  
21  import java.util.Arrays;
22  import java.util.Locale;
23  
24  import org.hipparchus.analysis.UnivariateFunction;
25  import org.hipparchus.analysis.polynomials.PolynomialFunction;
26  import org.hipparchus.linear.Array2DRowRealMatrix;
27  import org.hipparchus.linear.ArrayRealVector;
28  import org.hipparchus.linear.MatrixUtils;
29  import org.hipparchus.linear.RealMatrix;
30  import org.hipparchus.linear.RealVector;
31  import org.hipparchus.random.CorrelatedRandomVectorGenerator;
32  import org.hipparchus.random.GaussianRandomGenerator;
33  import org.hipparchus.random.Well1024a;
34  import org.hipparchus.stat.descriptive.StreamingStatistics;
35  import org.hipparchus.util.FastMath;
36  import org.junit.Assert;
37  import org.junit.Test;
38  import org.orekit.errors.OrekitIllegalArgumentException;
39  import org.orekit.estimation.Context;
40  import org.orekit.estimation.EstimationTestUtils;
41  import org.orekit.estimation.Force;
42  import org.orekit.frames.LOFType;
43  import org.orekit.frames.Transform;
44  import org.orekit.orbits.OrbitType;
45  import org.orekit.orbits.PositionAngle;
46  import org.orekit.propagation.Propagator;
47  import org.orekit.propagation.SpacecraftState;
48  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
49  import org.orekit.utils.CartesianDerivativesFilter;
50  import org.orekit.utils.PVCoordinates;
51  import org.orekit.utils.TimeStampedPVCoordinates;
52  
53  public class UnivariateprocessNoiseTest {
54      
55      /** Basic test for getters. */
56      @Test
57      public void testUnivariateProcessNoiseGetters() {
58          
59          Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
60          
61          // Define the univariate functions for the standard deviations      
62          final UnivariateFunction[] lofCartesianOrbitalParametersEvolution = new UnivariateFunction[6];
63          // Evolution for position error
64          lofCartesianOrbitalParametersEvolution[0] = new PolynomialFunction(new double[] {100., 0., 1e-4});
65          lofCartesianOrbitalParametersEvolution[1] = new PolynomialFunction(new double[] {100., 1e-1, 0.});
66          lofCartesianOrbitalParametersEvolution[2] = new PolynomialFunction(new double[] {100., 0., 0.});
67          // Evolution for velocity error
68          lofCartesianOrbitalParametersEvolution[3] = new PolynomialFunction(new double[] {1., 0., 1.e-6});
69          lofCartesianOrbitalParametersEvolution[4] = new PolynomialFunction(new double[] {1., 1e-3, 0.});
70          lofCartesianOrbitalParametersEvolution[5] = new PolynomialFunction(new double[] {1., 0., 0.});
71  
72          // Evolution for propagation parameters error
73          final UnivariateFunction[] propagationParametersEvolution =
74                          new UnivariateFunction[] {new PolynomialFunction(new double[] {10., 1., 1e-4}),
75                                                    new PolynomialFunction(new double[] {1000., 0., 0.})};
76          
77          // Evolution for measurements parameters error
78          final UnivariateFunction[] measurementsParametersEvolution =
79                          new UnivariateFunction[] {new PolynomialFunction(new double[] {100., 1., 1e-3})};
80          
81          // Create a dummy initial covariance matrix
82          final RealMatrix initialCovarianceMatrix = MatrixUtils.createRealIdentityMatrix(9);
83          
84          // Set the process noise object
85          // Define input LOF and output position angle
86          final LOFType lofType = LOFType.TNW;
87          final UnivariateProcessNoise processNoise = new UnivariateProcessNoise(initialCovarianceMatrix,
88                                                                                 lofType,
89                                                                                 PositionAngle.TRUE,
90                                                                                 lofCartesianOrbitalParametersEvolution,
91                                                                                 propagationParametersEvolution,
92                                                                                 measurementsParametersEvolution);
93          
94          Assert.assertEquals(LOFType.TNW, processNoise.getLofType());
95          Assert.assertEquals(PositionAngle.TRUE, processNoise.getPositionAngle());
96          Assert.assertEquals(initialCovarianceMatrix,
97                              processNoise.getInitialCovarianceMatrix(new SpacecraftState(context.initialOrbit)));
98          Assert.assertArrayEquals(lofCartesianOrbitalParametersEvolution, processNoise.getLofCartesianOrbitalParametersEvolution());
99          Assert.assertArrayEquals(propagationParametersEvolution, processNoise.getPropagationParametersEvolution());
100         Assert.assertArrayEquals(measurementsParametersEvolution, processNoise.getMeasurementsParametersEvolution());
101         
102         final UnivariateProcessNoise processNoiseOld = new UnivariateProcessNoise(initialCovarianceMatrix,
103                                                                                   lofType,
104                                                                                   PositionAngle.TRUE,
105                                                                                   lofCartesianOrbitalParametersEvolution,
106                                                                                   propagationParametersEvolution);
107         
108         Assert.assertEquals(LOFType.TNW, processNoiseOld.getLofType());
109         Assert.assertEquals(PositionAngle.TRUE, processNoiseOld.getPositionAngle());
110         Assert.assertEquals(initialCovarianceMatrix,
111                             processNoiseOld.getInitialCovarianceMatrix(new SpacecraftState(context.initialOrbit)));
112         Assert.assertArrayEquals(lofCartesianOrbitalParametersEvolution, processNoiseOld.getLofCartesianOrbitalParametersEvolution());
113         Assert.assertArrayEquals(propagationParametersEvolution, processNoiseOld.getPropagationParametersEvolution());
114     }
115     
116     /** Test UnivariateProcessNoise class.
117      * - Initialize with different univariate functions for orbital/propagation parameters variation
118      * - Check that the inertial process noise covariance matrix is consistent with the inputs
119      * - Propagation in Cartesian formalism
120      */
121     @Test
122     public void testProcessNoiseMatrixCartesian() {
123         
124         // Create context
125         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
126         
127         // Print result on console ?
128         final boolean print = false;
129 
130         // Create initial orbit and propagator builder
131         final OrbitType     orbitType     = OrbitType.CARTESIAN;
132         final PositionAngle positionAngle = PositionAngle.TRUE; // Not used here
133         final boolean       perfectStart  = true;
134         final double        minStep       = 1.e-6;
135         final double        maxStep       = 60.;
136         final double        dP            = 1.;
137         final NumericalPropagatorBuilder propagatorBuilder = context.createBuilder(orbitType, positionAngle, perfectStart,
138                                                                                    minStep, maxStep, dP,
139                                                                                    Force.POTENTIAL, Force.THIRD_BODY_MOON,
140                                                                                    Force.THIRD_BODY_SUN,
141                                                                                    Force.SOLAR_RADIATION_PRESSURE);
142         // Create a propagator
143         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
144                                                                            propagatorBuilder);
145         
146         // Define the univariate functions for the standard deviations      
147         final UnivariateFunction[] lofCartesianOrbitalParametersEvolution = new UnivariateFunction[6];
148         // Evolution for position error
149         lofCartesianOrbitalParametersEvolution[0] = new PolynomialFunction(new double[] {100., 0., 1e-4});
150         lofCartesianOrbitalParametersEvolution[1] = new PolynomialFunction(new double[] {100., 1e-1, 0.});
151         lofCartesianOrbitalParametersEvolution[2] = new PolynomialFunction(new double[] {100., 0., 0.});
152         // Evolution for velocity error
153         lofCartesianOrbitalParametersEvolution[3] = new PolynomialFunction(new double[] {1., 0., 1.e-6});
154         lofCartesianOrbitalParametersEvolution[4] = new PolynomialFunction(new double[] {1., 1e-3, 0.});
155         lofCartesianOrbitalParametersEvolution[5] = new PolynomialFunction(new double[] {1., 0., 0.});
156         
157         // Evolution for propagation parameters error
158         final UnivariateFunction[] propagationParametersEvolution =
159                         new UnivariateFunction[] {new PolynomialFunction(new double[] {10., 1., 1e-4}),
160                                                   new PolynomialFunction(new double[] {1000., 0., 0.})};
161         
162         
163         // Create a dummy initial covariance matrix
164         final RealMatrix initialCovarianceMatrix = MatrixUtils.createRealIdentityMatrix(8);
165         
166         // Set the process noise object
167         // Define input LOF and output position angle
168         final LOFType lofType = LOFType.TNW;
169         final UnivariateProcessNoise processNoise = new UnivariateProcessNoise(initialCovarianceMatrix,
170                                                                                lofType,
171                                                                                positionAngle,
172                                                                                lofCartesianOrbitalParametersEvolution,
173                                                                                propagationParametersEvolution);
174         // Test on initial value, after 1 hour and after one orbit
175         final SpacecraftState state0 = propagator.getInitialState();
176         final SpacecraftState state1 = propagator.propagate(context.initialOrbit.getDate().shiftedBy(3600.));
177         final SpacecraftState state2 = propagator.propagate(context.initialOrbit.getDate()
178                                                             .shiftedBy(2*context.initialOrbit.getKeplerianPeriod()));
179         
180         // Number of samples for the statistics
181         final int sampleNumber = 10000;
182         
183         // Relative tolerance on final standard deviations observed (< 1.5% for 10000 samples)
184         final double relativeTolerance = 1.5e-2;
185         
186         if (print) {
187             System.out.println("Orbit Type    : " + orbitType);
188             System.out.println("Position Angle: " + positionAngle + "\n");
189         }
190         checkCovarianceValue(print, state0, state0, processNoise, 2, sampleNumber, relativeTolerance);
191         checkCovarianceValue(print, state0, state1, processNoise, 2, sampleNumber, relativeTolerance);
192         checkCovarianceValue(print, state0, state2, processNoise, 2, sampleNumber, relativeTolerance);
193     }
194     
195     /** Test UnivariateProcessNoise class with its new constructor.
196      * - Initialize with different univariate functions for orbital/propagation parameters variation
197      * - Check that the inertial process noise covariance matrix is consistent with the inputs
198      * - Propagation in Cartesian formalism
199      */
200     @Test
201     public void testProcessNoiseMatrixCartesianNew() {
202         
203         // Create context
204         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
205         
206         // Print result on console ?
207         final boolean print = false;
208 
209         // Create initial orbit and propagator builder
210         final OrbitType     orbitType     = OrbitType.CARTESIAN;
211         final PositionAngle positionAngle = PositionAngle.TRUE; // Not used here
212         final boolean       perfectStart  = true;
213         final double        minStep       = 1.e-6;
214         final double        maxStep       = 60.;
215         final double        dP            = 1.;
216         final NumericalPropagatorBuilder propagatorBuilder = context.createBuilder(orbitType, positionAngle, perfectStart,
217                                                                                    minStep, maxStep, dP,
218                                                                                    Force.POTENTIAL, Force.THIRD_BODY_MOON,
219                                                                                    Force.THIRD_BODY_SUN,
220                                                                                    Force.SOLAR_RADIATION_PRESSURE);
221         // Create a propagator
222         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
223                                                                            propagatorBuilder);
224         
225         // Define the univariate functions for the standard deviations      
226         final UnivariateFunction[] lofCartesianOrbitalParametersEvolution = new UnivariateFunction[6];
227         // Evolution for position error
228         lofCartesianOrbitalParametersEvolution[0] = new PolynomialFunction(new double[] {500., 0., 1e-4});
229         lofCartesianOrbitalParametersEvolution[1] = new PolynomialFunction(new double[] {400., 1e-1, 0.});
230         lofCartesianOrbitalParametersEvolution[2] = new PolynomialFunction(new double[] {200., 2e-1, 0.});
231         // Evolution for velocity error
232         lofCartesianOrbitalParametersEvolution[3] = new PolynomialFunction(new double[] {1., 0., 1e-6});
233         lofCartesianOrbitalParametersEvolution[4] = new PolynomialFunction(new double[] {1., 1e-3, 0.});
234         lofCartesianOrbitalParametersEvolution[5] = new PolynomialFunction(new double[] {1., 0., 1e-5});
235         
236         // Evolution for propagation parameters error
237         final UnivariateFunction[] propagationParametersEvolution =
238                         new UnivariateFunction[] {new PolynomialFunction(new double[] {100., 1., 1e-4}),
239                                                   new PolynomialFunction(new double[] {2000., 3., 0.})};
240         
241         // Evolution for measurements parameters error
242         final UnivariateFunction[] measurementsParametersEvolution =
243                         new UnivariateFunction[] {new PolynomialFunction(new double[] {100., 1., 1e-3})};
244         
245         // Create a dummy initial covariance matrix
246         final RealMatrix initialCovarianceMatrix = MatrixUtils.createRealIdentityMatrix(9);
247         
248         // Set the process noise object
249         // Define input LOF and output position angle
250         final LOFType lofType = LOFType.TNW;
251         final UnivariateProcessNoise processNoise = new UnivariateProcessNoise(initialCovarianceMatrix,
252                                                                                lofType,
253                                                                                positionAngle,
254                                                                                lofCartesianOrbitalParametersEvolution,
255                                                                                propagationParametersEvolution,
256                                                                                measurementsParametersEvolution);
257         // Test on initial value, after 1 hour and after one orbit
258         final SpacecraftState state0 = propagator.getInitialState();
259         final SpacecraftState state1 = propagator.propagate(context.initialOrbit.getDate().shiftedBy(3600.));
260         final SpacecraftState state2 = propagator.propagate(context.initialOrbit.getDate()
261                                                             .shiftedBy(2*context.initialOrbit.getKeplerianPeriod()));
262         
263         // Number of samples for the statistics
264         final int sampleNumber = 10000;
265         
266         // Relative tolerance on final standard deviations observed (< 1.5% for 10000 samples)
267         final double relativeTolerance = 1.5e-2;
268         
269         if (print) {
270             System.out.println("Orbit Type    : " + orbitType);
271             System.out.println("Position Angle: " + positionAngle + "\n");
272         }
273         checkCovarianceValue(print, state0, state0, processNoise, 2, sampleNumber, relativeTolerance);
274         checkCovarianceValue(print, state0, state1, processNoise, 2, sampleNumber, relativeTolerance);
275         checkCovarianceValue(print, state0, state2, processNoise, 2, sampleNumber, relativeTolerance);
276     }
277     
278       // Note: This test does not work, probably due to the high values of the univariate functions when time increases.
279       // The jacobian matrices used to convert from Keplerian (equinoctial, circular) to Cartesian and back are based on first oder
280       // derivatives. When differences to the reference orbit are very large, these first order derivatives are not enough to physically
281       // represent the deviation of the orbit.
282       // Investigations are to be done on this test
283 //    /** Test process noise matrix computation.
284 //     * - Initialize with different univariate functions for orbital/propagation parameters variation
285 //     * - Check that the inertial process noise covariance matrix is consistent with the inputs
286 //     * - Propagation in Non-Cartesian formalism (Keplerian, Circular or Equinoctial)
287 //     *  TO DO: Find out why position in LOF frame is off after one hour of propagation
288 //     */
289 //    @Ignore
290 //    @Test
291 //    public void testProcessNoiseMatrixNonCartesian() {
292 //
293 //        // Create context
294 //        Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
295 //
296 //        // Print result on console ?
297 //        final boolean print = true;
298 //        
299 //        // Create initial orbit and propagator builder
300 //        final OrbitType     orbitType     = OrbitType.KEPLERIAN;
301 //        final PositionAngle positionAngle = PositionAngle.TRUE;
302 //        final boolean       perfectStart  = true;
303 //        final double        minStep       = 1.e-6;
304 //        final double        maxStep       = 60.;
305 //        final double        dP            = 1.;
306 //        final NumericalPropagatorBuilder propagatorBuilder = context.createBuilder(orbitType, positionAngle, perfectStart,
307 //                                                                                   minStep, maxStep, dP);//
308 ////                                                                                   Force.POTENTIAL, Force.THIRD_BODY_MOON,
309 ////                                                                                   Force.THIRD_BODY_SUN,
310 ////                                                                                   Force.SOLAR_RADIATION_PRESSURE);
311 //
312 //        // Create a propagator
313 //        final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
314 //                                                                           propagatorBuilder);
315 //        
316 //        // Define the univariate functions for the standard deviations      
317 //        final UnivariateFunction[] lofCartesianOrbitalParametersEvolution = new UnivariateFunction[6];
318 //        // Evolution for position error
319 //        lofCartesianOrbitalParametersEvolution[0] = new PolynomialFunction(new double[] {100., 0., 1e-4});
320 //        lofCartesianOrbitalParametersEvolution[1] = new PolynomialFunction(new double[] {100., 1e-1, 0.});
321 //        lofCartesianOrbitalParametersEvolution[2] = new PolynomialFunction(new double[] {100., 0., 0.});
322 //        // Evolution for velocity error
323 //        lofCartesianOrbitalParametersEvolution[3] = new PolynomialFunction(new double[] {1., 0., 1.e-6});
324 //        lofCartesianOrbitalParametersEvolution[4] = new PolynomialFunction(new double[] {1., 1e-3, 0.});
325 //        lofCartesianOrbitalParametersEvolution[5] = new PolynomialFunction(new double[] {1., 0., 0.});
326 //
327 //        final UnivariateFunction[] propagationParametersEvolution =
328 //                        new UnivariateFunction[] {new PolynomialFunction(new double[] {10, 1., 1e-4}),
329 //                                                  new PolynomialFunction(new double[] {1000., 0., 0.})};
330 //        
331 //        // Create a dummy initial covariance matrix
332 //        final RealMatrix initialCovarianceMatrix = MatrixUtils.createRealIdentityMatrix(7);
333 //        
334 //        // Set the process noise object
335 //        // Define input LOF and output position angle
336 //        final LOFType lofType = LOFType.TNW;
337 //        final UnivariateProcessNoise processNoise = new UnivariateProcessNoise(initialCovarianceMatrix,
338 //                                                                               lofType,
339 //                                                                               positionAngle,
340 //                                                                               lofCartesianOrbitalParametersEvolution,
341 //                                                                               propagationParametersEvolution);
342 //        // Test on initial value, after 1 hour and after 2 orbits
343 //        final SpacecraftState state0 = propagator.getInitialState();
344 //        final SpacecraftState state1 = propagator.propagate(context.initialOrbit.getDate().shiftedBy(3600.));
345 ////        final SpacecraftState state2 = propagator.propagate(context.initialOrbit.getDate()
346 ////                                                            .shiftedBy(2*context.initialOrbit.getKeplerianPeriod()));
347 //        
348 //        // Number of samples for the statistics
349 //        final int sampleNumber = 10000;
350 //        
351 //        // Relative tolerance on final standard deviations observed
352 //        final double relativeTolerance = 0.02;
353 //        
354 //        if (print) {
355 //            System.out.println("Orbit Type    : " + orbitType);
356 //            System.out.println("Position Angle: " + positionAngle + "\n");
357 //        }
358 //        checkCovarianceValue(print, state0, state0, processNoise, sampleNumber, relativeTolerance);
359 //        checkCovarianceValue(print, state0, state1, processNoise, sampleNumber, relativeTolerance);
360 //        
361 //        // Orbit too far off after 2 orbital periods
362 //        // It becomes inconsistent when applying random vector values
363 //        //checkCovarianceValue(print, state0, state2, processNoise, sampleNumber, relativeTolerance);
364 //    }
365     
366     
367     /** Check the values of the covariance given in inertial frame by the UnivariateProcessNoise object.
368      *  - Get the process noise covariance matrix P in inertial frame
369      *  - Use a random vector generator based on P to produce a set of N error vectors in inertial frame
370      *  - Compare the standard deviations of the noisy data to the univariate functions given in input of the UnivariateProcessNoise class.
371      *  - For orbit parameters this involve converting the inertial orbital vector to LOF frame and Cartesian formalism first
372      * @param print print results in console ?
373      * @param previous previous state
374      * @param current current state
375      * @param univariateProcessNoise UnivariateProcessNoise object to test
376      * @param propagationParametersSize size of propagation parameters submatrix in UnivariateProcessNoise
377      * @param sampleNumber number of sample vectors for the statistics
378      * @param relativeTolerance relative tolerance on the standard deviations for the tests
379      */
380     private void checkCovarianceValue(final boolean print,
381                                       final SpacecraftState previous,
382                                       final SpacecraftState current,
383                                       final UnivariateProcessNoise univariateProcessNoise,
384                                       final int propagationParametersSize,
385                                       final int sampleNumber,
386                                       final double relativeTolerance) {
387         
388         // Get the process noise matrix
389         final RealMatrix processNoiseMatrix = univariateProcessNoise.getProcessNoiseMatrix(previous, current);
390         
391         // Initialize a random vector generator
392         final CorrelatedRandomVectorGenerator randomVectorGenerator = createSampler(processNoiseMatrix);
393         
394         // Propagation parameters length
395         int measurementsParametersSize = processNoiseMatrix.getColumnDimension() - (6 + propagationParametersSize);
396         if ( measurementsParametersSize < 0) {
397             measurementsParametersSize = 0;
398         }
399         
400         // Prepare the statistics
401         final StreamingStatistics[] orbitalStatistics = new StreamingStatistics[6];
402         for (int i = 0; i < 6; i++) {
403             orbitalStatistics[i] = new StreamingStatistics();
404         }
405         StreamingStatistics[] propagationStatistics;
406         if (propagationParametersSize > 0) {
407             propagationStatistics = new StreamingStatistics[propagationParametersSize];
408             for (int i = 0; i < propagationParametersSize; i++) {
409                 propagationStatistics[i] = new StreamingStatistics();
410             }
411         } else {
412           propagationStatistics = null;  
413         }
414         StreamingStatistics[] measurementsStatistics;
415         if (propagationParametersSize > 0) {
416             measurementsStatistics = new StreamingStatistics[measurementsParametersSize];
417             for (int i = 0; i < measurementsParametersSize; i++) {
418                 measurementsStatistics[i] = new StreamingStatistics();
419             }
420         } else {
421           measurementsStatistics = null;  
422         }
423         
424         // Current orbit stored in an array
425         // With the position angle defined in the univariate process noise function
426         final double[] currentOrbitArray = new double[6];
427         current.getOrbit().getType().mapOrbitToArray(current.getOrbit(),
428                                                      univariateProcessNoise.getPositionAngle(),
429                                                      currentOrbitArray,
430                                                      null);
431         // Transform from inertial to current spacecraft LOF frame
432         final Transform inertialToLof = univariateProcessNoise.getLofType().transformFromInertial(current.getDate(),
433                                                                                                   current.getOrbit().getPVCoordinates());        
434         // Create the vectors and compute the states
435         for (int i = 0; i < sampleNumber; i++) {
436             
437             // Create a random vector
438             final double[] randomVector = randomVectorGenerator.nextVector();
439             
440             // Orbital parameters values
441             // -------------------------
442                         
443             // Get the full inertial orbit by adding up the values of current orbit and orbit error (first 6 components of random vector)
444             final double[] modifiedOrbitArray = new double[6];
445             for (int j = 0; j < 6; j++) {
446                 modifiedOrbitArray[j] = currentOrbitArray[j] + randomVector[j];
447             }
448             
449             // Get the corresponding PV coordinates
450             TimeStampedPVCoordinates inertialPV = null;
451             try {
452                 inertialPV = current.getOrbit().getType().mapArrayToOrbit(modifiedOrbitArray,
453                                                                           null,
454                                                                           univariateProcessNoise.getPositionAngle(),
455                                                                           current.getDate(),
456                                                                           current.getMu(),
457                                                                           current.getFrame()).getPVCoordinates();
458             } catch (OrekitIllegalArgumentException e) {
459                 // If orbit becomes inconsistent due to errors that are too large, print orbital values
460                 System.out.println("i = " + i + " / Inconsistent Orbit");
461                 System.out.println("\tCurrent Orbit  = " + Arrays.toString(currentOrbitArray));
462                 System.out.println("\tModified Orbit = " + Arrays.toString(modifiedOrbitArray));
463                 System.out.println("\tDelta Orbit    = " + Arrays.toString(randomVector));
464                 e.printStackTrace();
465                 System.err.println(e.getMessage());
466             }
467             
468             // Transform from inertial to current LOF
469             // The value obtained is the Cartesian error vector in LOF (w/r to current spacecraft position)
470             final PVCoordinates lofPV = inertialToLof.transformPVCoordinates(inertialPV);
471             
472             // Store the LOF PV values in the statistics summaries
473             orbitalStatistics[0].addValue(lofPV.getPosition().getX());
474             orbitalStatistics[1].addValue(lofPV.getPosition().getY());
475             orbitalStatistics[2].addValue(lofPV.getPosition().getZ());
476             orbitalStatistics[3].addValue(lofPV.getVelocity().getX());
477             orbitalStatistics[4].addValue(lofPV.getVelocity().getY());
478             orbitalStatistics[5].addValue(lofPV.getVelocity().getZ());
479 
480 
481             // Propagation parameters values
482             // -----------------------------
483             
484             // Extract the propagation parameters random vector and store them in the statistics
485             if (propagationParametersSize > 0) {
486                 for (int j = 6; j < randomVector.length - measurementsParametersSize; j++) {
487                     propagationStatistics[j - 6].addValue(randomVector[j]);
488                 }
489             }       
490             
491             // Measurements parameters values
492             // -----------------------------
493             
494             // Extract the measurements parameters random vector and store them in the statistics
495             if (measurementsParametersSize > 0) {
496                 for (int j = 6 + propagationParametersSize ; j < randomVector.length; j++) {
497                     measurementsStatistics[j - (6 + propagationParametersSize)].addValue(randomVector[j]);
498                 }
499             }
500         }
501         
502         // Get the real values and the statistics
503         // --------------------------------------
504         
505         // DT
506         final double dt = current.getDate().durationFrom(previous.getDate());
507         
508         // Get the values of the orbital functions and the statistics
509         final double[] orbitalValues = new double[6];
510         final double[] orbitalStatisticsValues = new double[6];
511         final double[] orbitalRelativeValues = new double[6];
512         
513         for (int i = 0; i < 6; i++) {
514             orbitalValues[i] = FastMath.abs(univariateProcessNoise.getLofCartesianOrbitalParametersEvolution()[i].value(dt));
515             orbitalStatisticsValues[i] = orbitalStatistics[i].getStandardDeviation();
516             
517             if (FastMath.abs(orbitalValues[i]) > 1e-15) {
518                 orbitalRelativeValues[i] = FastMath.abs(1. - orbitalStatisticsValues[i]/orbitalValues[i]);
519             } else {
520                 orbitalRelativeValues[i] = orbitalStatisticsValues[i];
521             }
522         }
523         
524         // Get the values of the propagation functions and statistics
525         final double[] propagationValues = new double[propagationParametersSize];
526         final double[] propagationStatisticsValues = new double[propagationParametersSize];
527         final double[] propagationRelativeValues = new double[propagationParametersSize];
528         for (int i = 0; i < propagationParametersSize; i++) {
529             propagationValues[i] = FastMath.abs(univariateProcessNoise.getPropagationParametersEvolution()[i].value(dt));
530             propagationStatisticsValues[i] = propagationStatistics[i].getStandardDeviation();
531             if (FastMath.abs(propagationValues[i]) > 1e-15) {
532                 propagationRelativeValues[i] = FastMath.abs(1. - propagationStatisticsValues[i]/propagationValues[i]);
533             } else {
534                 propagationRelativeValues[i] = propagationStatisticsValues[i];
535             }
536         }
537         
538         // Get the values of the measurements functions and statistics
539         final double[] measurementsValues = new double[measurementsParametersSize];
540         final double[] measurementsStatisticsValues = new double[measurementsParametersSize];
541         final double[] measurementsRelativeValues = new double[measurementsParametersSize];
542         for (int i = 0; i < measurementsParametersSize; i++) {
543             measurementsValues[i] = FastMath.abs(univariateProcessNoise.getMeasurementsParametersEvolution()[i].value(dt));
544             measurementsStatisticsValues[i] = measurementsStatistics[i].getStandardDeviation();
545             if (FastMath.abs(propagationValues[i]) > 1e-15) {
546                 measurementsRelativeValues[i] = FastMath.abs(1. - measurementsStatisticsValues[i]/measurementsValues[i]);
547             } else {
548                 measurementsRelativeValues[i] = measurementsStatisticsValues[i];
549             }
550         }
551         
552         // Print values
553         if (print) {
554             System.out.println("\tdt = " + dt + " / N = " + sampleNumber + "\n");
555             System.out.println("\tσ orbit ref   = " + Arrays.toString(orbitalValues));
556             System.out.println("\tσ orbit stat  = " + Arrays.toString(orbitalStatisticsValues));
557             System.out.println("\tσ orbit %     = " + Arrays.toString(Arrays.stream(orbitalRelativeValues).map(i -> i*100.).toArray()) + "\n");
558             
559             System.out.println("\tσ propag ref   = " + Arrays.toString(propagationValues));
560             System.out.println("\tσ propag stat  = " + Arrays.toString(propagationStatisticsValues));
561             System.out.println("\tσ propag %     = " + Arrays.toString(Arrays.stream(propagationRelativeValues).map(i -> i*100.).toArray()) + "\n");
562             
563             System.out.println("\tσ meas ref   = " + Arrays.toString(propagationValues));
564             System.out.println("\tσ meas stat  = " + Arrays.toString(propagationStatisticsValues));
565             System.out.println("\tσ meas %     = " + Arrays.toString(Arrays.stream(propagationRelativeValues).map(i -> i*100.).toArray()) + "\n");
566         }
567         
568         // Test the values
569         assertArrayEquals(new double[6], 
570                           orbitalRelativeValues,
571                           relativeTolerance);
572         assertArrayEquals(new double[propagationParametersSize],
573                           propagationRelativeValues,
574                           relativeTolerance);
575         assertArrayEquals(new double[measurementsParametersSize],
576                           measurementsRelativeValues,
577                           relativeTolerance);
578     }
579     
580     /** Create a gaussian random vector generator based on an input covariance matrix.
581      * @param covarianceMatrix input covariance matrix
582      * @return correlated gaussian random vectors generator
583      */
584     private CorrelatedRandomVectorGenerator createSampler(final RealMatrix covarianceMatrix) {
585         double small = 10e-20 * covarianceMatrix.getNorm1();
586         return new CorrelatedRandomVectorGenerator(
587                 covarianceMatrix,
588                 small,
589                 new GaussianRandomGenerator(new Well1024a(0x366a26b94e520f41l)));
590     }
591     
592     /** Test LOF orbital covariance matrix value against reference.
593      * 1. Initialize with different univariate functions for orbital/propagation parameters variation
594      * 2. Get the inertial process noise covariance matrix
595      * 3. Convert the inertial covariance from 2 in Cartesian and LOF
596      * 4. Compare values of 3 with reference values from 1
597      */
598     @Test
599     public void testLofCartesianOrbitalCovarianceFormal() {
600 
601         // Create context
602         Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
603 
604         // Print result on console ?
605         final boolean print = false;
606         
607         // Create initial orbit and propagator builder
608         final OrbitType     orbitType     = OrbitType.KEPLERIAN;
609         final PositionAngle positionAngle = PositionAngle.MEAN;
610         final boolean       perfectStart  = true;
611         final double        minStep       = 1.e-6;
612         final double        maxStep       = 60.;
613         final double        dP            = 1.;
614         final NumericalPropagatorBuilder propagatorBuilder = context.createBuilder(orbitType, positionAngle, perfectStart,
615                                                                                    minStep, maxStep, dP,
616                                                                                    Force.POTENTIAL, Force.THIRD_BODY_MOON,
617                                                                                    Force.THIRD_BODY_SUN,
618                                                                                    Force.SOLAR_RADIATION_PRESSURE);
619 
620         // Create a propagator
621         final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
622                                                                            propagatorBuilder);
623         
624         // Define the univariate functions for the standard deviations      
625         final UnivariateFunction[] lofCartesianOrbitalParametersEvolution = new UnivariateFunction[6];
626         // Evolution for position error
627         lofCartesianOrbitalParametersEvolution[0] = new PolynomialFunction(new double[] {100., 0., 1e-4});
628         lofCartesianOrbitalParametersEvolution[1] = new PolynomialFunction(new double[] {100., 1e-1, 0.});
629         lofCartesianOrbitalParametersEvolution[2] = new PolynomialFunction(new double[] {100., 0., 0.});
630         // Evolution for velocity error
631         lofCartesianOrbitalParametersEvolution[3] = new PolynomialFunction(new double[] {1., 0., 1.e-6});
632         lofCartesianOrbitalParametersEvolution[4] = new PolynomialFunction(new double[] {1., 1e-3, 0.});
633         lofCartesianOrbitalParametersEvolution[5] = new PolynomialFunction(new double[] {1., 0., 0.});
634 
635         // Evolution for propagation parameters error
636         final UnivariateFunction[] propagationParametersEvolution =
637                         new UnivariateFunction[] {new PolynomialFunction(new double[] {10., 1., 1e-4}),
638                                                   new PolynomialFunction(new double[] {1000., 0., 0.})};
639         
640         
641         // Create a dummy initial covariance matrix
642         final RealMatrix initialCovarianceMatrix = MatrixUtils.createRealIdentityMatrix(8);
643         
644         // Set the process noise object
645         // Define input LOF and output position angle
646         final LOFType lofType = LOFType.TNW;
647         final UnivariateProcessNoise processNoise = new UnivariateProcessNoise(initialCovarianceMatrix,
648                                                                                lofType,
649                                                                                positionAngle,
650                                                                                lofCartesianOrbitalParametersEvolution,
651                                                                                propagationParametersEvolution);
652         // Initial value
653         final SpacecraftState state0 = propagator.getInitialState();
654         
655         // 1 orbit
656         final SpacecraftState state1 = propagator.propagate(context.initialOrbit.getDate()
657                                                             .shiftedBy(context.initialOrbit.getKeplerianPeriod()));
658         // 1 day
659         final SpacecraftState state2 = propagator.propagate(context.initialOrbit.getDate().shiftedBy(86400.));
660         
661         if (print) {
662             System.out.println("Orbit Type    : " + orbitType);
663             System.out.println("Position Angle: " + positionAngle);
664         }
665         
666         // Worst case, KEPLERIAN MEAN - 1.62e-11
667         checkLofOrbitalCovarianceMatrix(print, state0, state0, processNoise, 1.62e-11);
668         // Worst case, CIRCULAR ECCENTRIC - 2.78e-10
669         checkLofOrbitalCovarianceMatrix(print, state0, state1, processNoise, 2.78e-10);
670         // Worst case, CIRCULAR TRUE - 9.15e-9
671         checkLofOrbitalCovarianceMatrix(print, state0, state2, processNoise, 9.15e-9);
672     }
673     
674     /** Check orbital covariance matrix in LOF wrt reference.
675      * Here we do the reverse calculcation
676      */
677     private void checkLofOrbitalCovarianceMatrix(final boolean print,
678                                                  final SpacecraftState previous,
679                                                  final SpacecraftState current,
680                                                  final UnivariateProcessNoise univariateProcessNoise,
681                                                  final double relativeTolerance) {
682         
683         // Get the process noise matrix in inertial frame 
684         final RealMatrix inertialQ = univariateProcessNoise.getProcessNoiseMatrix(previous, current);
685         
686         // Extract the orbit part
687         final RealMatrix inertialOrbitalQ = inertialQ.getSubMatrix(0, 5, 0, 5);
688         
689         // Jacobian from parameters to Cartesian
690         final double[][] dCdY = new double[6][6];
691         current.getOrbit().getJacobianWrtParameters(univariateProcessNoise.getPositionAngle(), dCdY);
692         final RealMatrix jacOrbToCar = new Array2DRowRealMatrix(dCdY, false);
693         
694         // Jacobian from inertial to LOF
695         final double[][] dLOFdI = new double[6][6];
696         univariateProcessNoise.getLofType().transformFromInertial(current.getDate(),
697                                                                   current.getOrbit().getPVCoordinates()).getJacobian(CartesianDerivativesFilter.USE_PV, dLOFdI);
698         final RealMatrix jacIToLOF = new Array2DRowRealMatrix(dLOFdI, false);
699         
700         // Complete Jacobian
701         final RealMatrix jac = jacIToLOF.multiply(jacOrbToCar);
702         
703         // Q in LOF and Cartesian
704         final RealMatrix lofQ = jac.multiply(inertialOrbitalQ).multiplyTransposed(jac);
705 
706         // Build reference LOF Cartesian orbital sigmas
707         final RealVector refLofSig = new ArrayRealVector(6); 
708         double dt = current.getDate().durationFrom(previous.getDate());
709         for (int i = 0; i < 6; i++) {
710             refLofSig.setEntry(i, univariateProcessNoise.getLofCartesianOrbitalParametersEvolution()[i].value(dt));
711         }
712         
713         // Compare diagonal values with reference (relative difference) and suppress them from the matrix
714         // Ensure non-diagonal values are into numerical noise sensitivity
715         RealVector dLofDiag = new ArrayRealVector(6);
716         for (int i = 0; i < 6; i++) {
717             for (int j = 0; j < 6; j++) {
718                 double refI = refLofSig.getEntry(i);
719                 if (i == j) {
720                     // Diagonal term
721                     dLofDiag.setEntry(i, FastMath.abs(1. - lofQ.getEntry(i, i) / refI / refI));
722                     lofQ.setEntry(i, i, 0.);
723                 } else {
724                     // Non-diagonal term, ref is 0.
725                     lofQ.setEntry(i, j, FastMath.abs(lofQ.getEntry(i,j) / refI / refLofSig.getEntry(j)));
726                 }
727             }
728             
729         }
730         
731         // Norm of diagonal and non-diagonal
732         double dDiag = dLofDiag.getNorm();
733         double dNonDiag = lofQ.getNorm1();
734         
735         // Print values ?
736         if (print) {
737             System.out.println("\tdt = " + dt);
738             System.out.format(Locale.US, "\tΔDiagonal norm in Cartesian LOF     = %10.4e%n", dDiag);
739             System.out.format(Locale.US, "\tΔNon-Diagonal norm in Cartesian LOF = %10.4e%n%n", dNonDiag);
740         }
741         Assert.assertEquals(0.,  dDiag   , relativeTolerance);
742         Assert.assertEquals(0.,  dNonDiag, relativeTolerance);
743     }
744 }