1   /* Copyright 2002-2022 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.estimation.sequential;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.linear.MatrixUtils;
25  import org.hipparchus.linear.RealMatrix;
26  import org.hipparchus.linear.RealVector;
27  import org.junit.Assert;
28  import org.junit.Before;
29  import org.junit.Test;
30  import org.orekit.estimation.DSSTContext;
31  import org.orekit.estimation.DSSTEstimationTestUtils;
32  import org.orekit.estimation.DSSTForce;
33  import org.orekit.estimation.measurements.GroundStation;
34  import org.orekit.estimation.measurements.ObservableSatellite;
35  import org.orekit.estimation.measurements.ObservedMeasurement;
36  import org.orekit.estimation.measurements.PV;
37  import org.orekit.estimation.measurements.Range;
38  import org.orekit.forces.radiation.RadiationSensitive;
39  import org.orekit.orbits.Orbit;
40  import org.orekit.orbits.OrbitType;
41  import org.orekit.orbits.PositionAngle;
42  import org.orekit.propagation.PropagationType;
43  import org.orekit.propagation.SpacecraftState;
44  import org.orekit.propagation.conversion.DSSTPropagatorBuilder;
45  import org.orekit.time.AbsoluteDate;
46  import org.orekit.utils.PVCoordinates;
47  import org.orekit.utils.ParameterDriver;
48  import org.orekit.utils.ParameterDriversList;
49  
50  @Deprecated
51  public class DSSTKalmanModelTest {
52  
53      /** Orbit type for propagation. */
54      private final OrbitType orbitType = OrbitType.EQUINOCTIAL;
55      
56      /** Position angle for propagation. */
57      private final PositionAngle positionAngle = PositionAngle.MEAN;
58      
59      /** Initial orbit. */
60      private Orbit orbit0;
61      
62      /** Propagator builder. */
63      private DSSTPropagatorBuilder propagatorBuilder;
64      
65      /** Covariance matrix provider. */
66      private CovarianceMatrixProvider covMatrixProvider;
67      
68      /** Estimated measurement parameters list. */
69      private ParameterDriversList estimatedMeasurementsParameters;
70      
71      /** Kalman extended estimator containing models. */
72      private KalmanEstimator kalman;
73      
74      /** Kalman observer. */
75      private ModelLogger modelLogger;
76      
77      /** State size. */
78      private int M;
79      
80      /** PV at t0. */
81      private PV pv;
82      
83      /** Range after t0. */
84      private Range range;
85      
86      /** Driver for SRP coefficient. */
87      private ParameterDriver srpCoefDriver;
88      
89      /** Tolerance for the test. */
90      private final double tol = 1e-16;
91  
92      @Before
93      public void setUp() {
94          // Create context
95          final DSSTContext context = DSSTEstimationTestUtils.eccentricContext("regular-data:potential:tides");
96          
97          // Initial orbit and date
98          this.orbit0 = context.initialOrbit;
99          ObservableSatellite sat = new ObservableSatellite(0);
100         
101         // Create propagator builder
102         this.propagatorBuilder = context.createBuilder(true, 1.0e-3, 6000.0, 10.,
103                                                        DSSTForce.SOLAR_RADIATION_PRESSURE);
104 
105         // Create PV at t0
106         final AbsoluteDate date0 = context.initialOrbit.getDate();
107         this.pv = new PV(date0,
108                              context.initialOrbit.getPVCoordinates().getPosition(),
109                              context.initialOrbit.getPVCoordinates().getVelocity(),
110                              new double[] {1., 2., 3., 1e-3, 2e-3, 3e-3}, 1.,
111                              sat);
112         
113         // Create one 0m range measurement at t0 + 10s
114         final AbsoluteDate date  = date0.shiftedBy(10.);
115         final GroundStation station = context.stations.get(0);
116         this.range = new Range(station, true, date, 18616150., 10., 1., sat);
117         // Exact range value is 1.8616150246470984E7 m
118 
119         // Gather list of meas parameters (only sat range bias here)
120         this.estimatedMeasurementsParameters = new ParameterDriversList();
121         for (final ParameterDriver driver : range.getParametersDrivers()) {
122             if (driver.isSelected()) {
123                 estimatedMeasurementsParameters.add(driver);
124             }
125         }
126         // Select SRP coefficient
127         this.srpCoefDriver = propagatorBuilder.getPropagationParametersDrivers().
128                         findByName(RadiationSensitive.REFLECTION_COEFFICIENT);
129         srpCoefDriver.setReferenceDate(date);
130         srpCoefDriver.setSelected(true);
131         
132         // Create a covariance matrix using the scales of the estimated parameters
133         final double[] scales = getParametersScale(propagatorBuilder, estimatedMeasurementsParameters);
134         this.M = scales.length;
135         this.covMatrixProvider = setInitialCovarianceMatrix(scales);
136 
137         // Initialize Kalman
138         final KalmanEstimatorBuilder kalmanBuilder = new KalmanEstimatorBuilder();
139         kalmanBuilder.addPropagationConfiguration(propagatorBuilder, covMatrixProvider);
140         kalmanBuilder.estimatedMeasurementsParameters(estimatedMeasurementsParameters, null);
141         this.kalman = kalmanBuilder.build();
142         this.modelLogger = new ModelLogger();
143         kalman.setObserver(modelLogger);
144     }
145 
146     @Test
147     public void ModelPhysicalOutputsTest() {
148         
149         // Check model at t0 before any measurement is added
150         // -------------------------------------------------
151         checkModelAtT0();
152 
153         // Check model after PV measurement at t0 is added
154         // -----------------------------------------------
155         
156         // Constant process noise covariance matrix Q
157         final RealMatrix Q = covMatrixProvider.getProcessNoiseMatrix(new SpacecraftState(orbit0),
158                                                                      new SpacecraftState(orbit0));
159         
160         // Initial covariance matrix
161         final RealMatrix P0 = covMatrixProvider.getInitialCovarianceMatrix(new SpacecraftState(orbit0));
162         
163         // Physical predicted covariance matrix at t0
164         // State transition matrix is the identity matrix at t0
165         RealMatrix Ppred = P0.add(Q);
166         
167         // Predicted orbit is equal to initial orbit at t0
168         Orbit orbitPred = orbit0;
169         
170         // Expected measurement matrix for a PV measurement is the 6-sized identity matrix for cartesian orbital parameters
171         // + zeros for other estimated parameters
172         RealMatrix expH = MatrixUtils.createRealMatrix(6, M);
173         for (int i = 0; i < 6; i++) {
174             expH.setEntry(i, i, 1.);
175         }
176         
177         // Expected state transition matrix
178         // State transition matrix is the identity matrix at t0
179         RealMatrix expPhi = MatrixUtils.createRealIdentityMatrix(M);
180         // Add PV measurement and check model afterwards
181         checkModelAfterMeasurementAdded(1, pv, Ppred, orbitPred, expPhi, expH);
182 
183     }
184 
185     private double[] getParametersScale(final DSSTPropagatorBuilder builder,
186                                        ParameterDriversList estimatedMeasurementsParameters) {
187         final List<Double> scaleList = new ArrayList<>();
188         
189         // Orbital parameters
190         for (ParameterDriver driver : builder.getOrbitalParametersDrivers().getDrivers()) {
191             if (driver.isSelected()) {
192                 scaleList.add(driver.getScale());
193             }
194         }
195         
196         // Propagation parameters
197         for (ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {
198             if (driver.isSelected()) {
199                 scaleList.add(driver.getScale());
200             }
201         }
202         
203         // Measurement parameters
204         for (ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
205             if (driver.isSelected()) {
206                 scaleList.add(driver.getScale());
207             }
208         }
209         
210         final double[] scales = new double[scaleList.size()];
211         for (int i = 0; i < scaleList.size(); i++) {
212             scales[i] = scaleList.get(i);
213         }
214         return scales;
215     }
216 
217     private void checkModelAtT0() {
218 
219         // Instantiate a Model from attributes
220         final DSSTKalmanModel model = new DSSTKalmanModel(Collections.singletonList(propagatorBuilder),
221                                                           Collections.singletonList(covMatrixProvider),
222                                                           estimatedMeasurementsParameters,
223                                                           null,
224                                                           PropagationType.MEAN,
225                                                           PropagationType.MEAN);
226 
227         // Evaluate at t0
228         // --------------
229         
230         // Time
231         Assert.assertEquals(0., model.getEstimate().getTime(), 0.);
232         Assert.assertEquals(0., model.getCurrentDate().durationFrom(orbit0.getDate()), 0.);
233         
234         // Measurement number
235         Assert.assertEquals(0, model.getCurrentMeasurementNumber());
236         
237         // Normalized state - is zeros
238         final RealVector stateN = model.getEstimate().getState();
239         Assert.assertArrayEquals(new double[M], stateN.toArray(), tol);
240         
241         // Physical state - = initialized
242         final RealVector x = model.getPhysicalEstimatedState();
243         final RealVector expX = MatrixUtils.createRealVector(M);
244         final double[] orbitState0 = new double[6];
245         orbitType.mapOrbitToArray(orbit0, positionAngle, orbitState0, null);
246         expX.setSubVector(0, MatrixUtils.createRealVector(orbitState0));
247         expX.setEntry(6, srpCoefDriver.getReferenceValue());
248         final double[] dX = x.subtract(expX).toArray();
249         Assert.assertArrayEquals(new double[M], dX, tol);
250         
251         // Normalized covariance - filled with 1
252         final double[][] Pn = model.getEstimate().getCovariance().getData();
253         final double[][] expPn = new double[M][M];
254         for (int i = 0; i < M; i++) {
255             for (int j = 0; j < M; j++) {
256                 expPn[i][j] = 1.;
257             }
258             Assert.assertArrayEquals("Failed on line " + i, expPn[i], Pn[i], tol);
259         }
260         
261         // Physical covariance = initialized
262         final RealMatrix P   = model.getPhysicalEstimatedCovarianceMatrix();
263         final RealMatrix expP = covMatrixProvider.getInitialCovarianceMatrix(new SpacecraftState(orbit0));
264         final double[][] dP = P.subtract(expP).getData();
265         for (int i = 0; i < M; i++) {
266             Assert.assertArrayEquals("Failed on line " + i, new double[M], dP[i], tol);
267         }
268         
269         // Check that other "physical" matrices are null
270         Assert.assertNull(model.getEstimate().getInnovationCovariance());
271         Assert.assertNull(model.getPhysicalInnovationCovarianceMatrix());
272         Assert.assertNull(model.getEstimate().getKalmanGain());
273         Assert.assertNull(model.getPhysicalKalmanGain());
274         Assert.assertNull(model.getEstimate().getMeasurementJacobian());
275         Assert.assertNull(model.getPhysicalMeasurementJacobian());
276         Assert.assertNull(model.getEstimate().getStateTransitionMatrix());
277         Assert.assertNull(model.getPhysicalStateTransitionMatrix());
278     }
279 
280     private void checkModelAfterMeasurementAdded(final int expMeasurementNumber,
281                                                 final ObservedMeasurement<?> meas,
282                                                 final RealMatrix expPpred,
283                                                 final Orbit expOrbitPred,
284                                                 final RealMatrix expPhi,
285                                                 final RealMatrix expH) {
286 
287         // Expected predicted measurement
288         final double[] expMeasPred = 
289                         meas.estimate(0, 0,
290                                       new SpacecraftState[] {new SpacecraftState(expOrbitPred)}).getEstimatedValue();
291 
292         // Process PV measurement in Kalman and get model
293         kalman.processMeasurements(Collections.singletonList(meas));
294         KalmanEstimation model = modelLogger.estimation;
295         
296         // Time
297         Assert.assertEquals(0., model.getCurrentDate().durationFrom(expOrbitPred.getDate()), 0.);
298         
299         // Measurement number
300         Assert.assertEquals(expMeasurementNumber, model.getCurrentMeasurementNumber());
301         
302         // State transition matrix
303         final RealMatrix phi    = model.getPhysicalStateTransitionMatrix();
304         final double[][] dPhi   = phi.subtract(expPhi).getData();
305         for (int i = 0; i < M; i++) {
306             Assert.assertArrayEquals("Failed on line " + i, new double[M], dPhi[i], tol*100);
307         }
308 
309         // Predicted orbit
310         final Orbit orbitPred = model.getPredictedSpacecraftStates()[0].getOrbit();
311         final PVCoordinates pvOrbitPred = orbitPred.getPVCoordinates();
312         final PVCoordinates expPVOrbitPred = expOrbitPred.getPVCoordinates();
313         final double dpOrbitPred = Vector3D.distance(expPVOrbitPred.getPosition(), pvOrbitPred.getPosition());
314         final double dvOrbitPred = Vector3D.distance(expPVOrbitPred.getVelocity(), pvOrbitPred.getVelocity());
315         Assert.assertEquals(0., dpOrbitPred, tol);
316         Assert.assertEquals(0., dvOrbitPred, tol);
317 
318         // Predicted measurement
319         final double[] measPred = model.getPredictedMeasurement().getEstimatedValue();
320         Assert.assertArrayEquals(expMeasPred, measPred, tol);
321 
322     }
323 
324     private CovarianceMatrixProvider setInitialCovarianceMatrix(final double[] scales) {
325 
326         final int n = scales.length;
327         final RealMatrix cov = MatrixUtils.createRealMatrix(n, n);
328         for (int i = 0; i < n; i++) {
329             for (int j = 0; j < n; j++) {
330                 cov.setEntry(i, j, scales[i] * scales[j]);
331             }
332         }
333         return new ConstantProcessNoise(cov); 
334     }
335 
336     /** Observer allowing to get Kalman model after a measurement was processed in the Kalman filter. */
337     public class ModelLogger implements KalmanObserver {
338         KalmanEstimation estimation;
339 
340         @Override
341         public void evaluationPerformed(KalmanEstimation estimation) {
342             this.estimation = estimation;
343         }
344     }
345 
346 }