diff --git a/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java b/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..d94bf4f8f9d5e61b48e7dd0f0b75ebc39e6039e2
--- /dev/null
+++ b/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java
@@ -0,0 +1,371 @@
+/* Copyright 2002-2022 CS GROUP
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.propagation;
+
+import org.hipparchus.linear.Array2DRowRealMatrix;
+import org.hipparchus.linear.MatrixUtils;
+import org.hipparchus.linear.RealMatrix;
+import org.orekit.frames.Frame;
+import org.orekit.frames.Transform;
+import org.orekit.orbits.CartesianOrbit;
+import org.orekit.orbits.Orbit;
+import org.orekit.orbits.OrbitType;
+import org.orekit.orbits.PositionAngle;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.utils.CartesianDerivativesFilter;
+
+/**
+ * Additional state provider for state covariance matrix.
+ *
+ * This additional state provider allows computing a propagated
+ * covariance matrix based on a user defined input state covariance
+ * matrix. The computation of the propagated covariance matrix uses
+ * the State Transition Matrix between the propagated spacecraft state
+ * and the initial state. As a result, the user must define the name
+ * {@link #stmName of the provider for the State Transition Matrix}.
+ *
+ * As the State Transition Matrix and the input state covariance
+ * matrix can be expressed in different orbit types, the user must
+ * specify both orbit types when building the covariance provider.
+ * In addition, the position angle used in both matrices must also
+ * be specified.
+ *
+ * In order to add this additional state provider to an orbit
+ * propagator, user must use the
+ * {@link Propagator#addAdditionalStateProvider(AdditionalStateProvider)}
+ * method.
+ *
+ * For a given propagated spacecraft {@code state}, the propagated state
+ * covariance matrix is accessible through the method
+ * {@link #getStateCovariance(SpacecraftState)}
+ *
+ * It is possible to change the covariance frame by using the
+ * {@link #changeCovarianceFrame(AbsoluteDate, Frame, Frame, RealMatrix)}
+ * method. This method is based on Equation (18) of Covariance
+ * Transformations for Satellite Flight Dynamics Operations
+ * by David A. Vallado. It is important to highlight that the frames
+ * must be inertial frames.
+ *
+ * Finally, covariance orbit type can be changed using the
+ * {@link #changeCovarianceType(SpacecraftState, OrbitType, PositionAngle, OrbitType, PositionAngle, RealMatrix)}
+ * method.
+ *
+ * @author Bryan Cazabonne
+ * @since 11.3
+ */
+public class StateCovarianceMatrixProvider implements AdditionalStateProvider {
+
+ /** Dimension of the state. */
+ private static final int STATE_DIMENSION = 6;
+
+ /** Name of the state for State Transition Matrix. */
+ private final String stmName;
+
+ /** Matrix harvester to access the State Transition Matrix. */
+ private final MatricesHarvester harvester;
+
+ /** Name of the additional state. */
+ private final String additionalName;
+
+ /** Orbit type used for the State Transition Matrix. */
+ private final OrbitType stmOrbitType;
+
+ /** Position angle used for State Transition Matrix. */
+ private final PositionAngle stmAngleType;
+
+ /** Orbit type for the covariance matrix. */
+ private final OrbitType covOrbitType;
+
+ /** Position angle used for the covariance matrix. */
+ private final PositionAngle covAngleType;
+
+ /** Initial state covariance matrix. */
+ private RealMatrix covInit;
+
+ /**
+ * Constructor.
+ * @param additionalName name of the additional state
+ * @param stmName name of the state for State Transition Matrix
+ * @param harvester matrix harvester as returned by {@code propagator.setupMatricesComputation(stmName, null, null)}
+ * @param stmOrbitType orbit type used for the State Transition Matrix computation
+ * @param stmAngleType position angle used for State Transition Matrix computation
+ * (not used if stmOrbitType equals {@code CARTESIAN})
+ * @param covInit initial state covariance matrix (6x6 dimension)
+ * @param covOrbitType orbit type for the covariance matrix
+ * @param covAngleType position angle used for the covariance matrix
+ * (not used if covOrbitType equals {@code CARTESIAN})
+ */
+ public StateCovarianceMatrixProvider(final String additionalName, final String stmName,
+ final MatricesHarvester harvester,
+ final OrbitType stmOrbitType, final PositionAngle stmAngleType,
+ final RealMatrix covInit,
+ final OrbitType covOrbitType, final PositionAngle covAngleType) {
+ // Initialize fields
+ this.additionalName = additionalName;
+ this.stmName = stmName;
+ this.harvester = harvester;
+ this.covInit = covInit;
+ this.stmOrbitType = stmOrbitType;
+ this.stmAngleType = stmAngleType;
+ this.covOrbitType = covOrbitType;
+ this.covAngleType = covAngleType;
+ }
+
+ /** Get the orbit type in which the covariance matrix is expressed.
+ * @return the orbit type
+ */
+ public OrbitType getCovarianceOrbitType() {
+ return covOrbitType;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getName() {
+ return additionalName;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void init(final SpacecraftState initialState, final AbsoluteDate target) {
+ // Convert the initial covariance matrix in the same orbit type as the STM
+ covInit = changeCovarianceType(initialState.getOrbit(),
+ covOrbitType, covAngleType,
+ stmOrbitType, stmAngleType,
+ covInit);
+ }
+
+ /** {@inheritDoc}
+ *
+ * The covariance matrix can be computed only if the State Transition Matrix state is available.
+ *
+ */
+ @Override
+ public boolean yield(final SpacecraftState state) {
+ return !state.hasAdditionalState(stmName);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getAdditionalState(final SpacecraftState state) {
+
+ // State transition matrix for the input state
+ final RealMatrix dYdY0 = harvester.getStateTransitionMatrix(state);
+
+ // Compute the propagated covariance matrix
+ RealMatrix propCov = dYdY0.multiply(covInit.multiplyTransposed(dYdY0));
+ // Update to the user defined type
+ propCov = changeCovarianceType(state.getOrbit(),
+ stmOrbitType, stmAngleType,
+ covOrbitType, covAngleType,
+ propCov);
+
+ // Return the propagated covariance matrix
+ return toArray(propCov);
+
+ }
+
+ /** Get the state covariance matrix (6x6 dimension).
+ *
+ * The output covariance matrix is expressed in the
+ * same orbit type as {@link #getCovarianceOrbitType()}.
+ *
+ * It is possible to change the covariance frame by using the
+ * {@link #changeCovarianceFrame(AbsoluteDate, Frame, Frame, RealMatrix)}
+ * method.
+ *
+ * It is also possible to change the covariance orbit type by
+ * using the {@link #changeCovarianceType(SpacecraftState, OrbitType, PositionAngle,
+ * OrbitType, PositionAngle, RealMatrix)}
+ * method.
+ * @param state spacecraft state to which the covariance matrix should correspond
+ * @return the state covariance matrix
+ */
+ public RealMatrix getStateCovariance(final SpacecraftState state) {
+
+ // Get the current propagated covariance
+ final RealMatrix covariance = toRealMatrix(state.getAdditionalState(additionalName));
+
+ // Return the converted covariance
+ return covariance;
+
+ }
+
+ /** Get the state covariance matrix (6x6 dimension) expressed in a given frame.
+ *
+ * The output covariance matrix is expressed in the
+ * same orbit type as {@link #getCovarianceOrbitType()}.
+ *
+ * It is also possible to change the covariance orbit type by
+ * using the {@link #changeCovarianceType(SpacecraftState, OrbitType, PositionAngle,
+ * OrbitType, PositionAngle, RealMatrix)}
+ * method.
+ * @param state spacecraft state to which the covariance matrix should correspond
+ * @param frame output frame for which the output covariance matrix must be expressed
+ * (must be inertial)
+ * @return the state covariance matrix expressed in frame
+ */
+ public RealMatrix getStateCovariance(final SpacecraftState state, final Frame frame) {
+
+ // Get the current propagated covariance
+ final RealMatrix covariance = toRealMatrix(state.getAdditionalState(additionalName));
+
+ // Return the converted covariance
+ return changeCovarianceFrame(state.getOrbit(), state.getFrame(), frame, covariance, covOrbitType, covAngleType);
+
+ }
+
+ /** Get the state covariance matrix (6x6 dimension) expressed in a given orbit type.
+ * @param state spacecraft state to which the covariance matrix should correspond
+ * @param orbitType output orbit type
+ * @param angleType output position angle
+ * (not used if orbitType equals {@code CARTESIAN})
+ * @return the state covariance matrix in orbitType
and angleType
+ */
+ public RealMatrix getStateCovariance(final SpacecraftState state, final OrbitType orbitType, final PositionAngle angleType) {
+
+ // Get the current propagated covariance
+ final RealMatrix covariance = toRealMatrix(state.getAdditionalState(additionalName));
+
+ // Return the converted covariance
+ return changeCovarianceType(state.getOrbit(), covOrbitType, covAngleType, orbitType, angleType, covariance);
+
+ }
+
+ /** Get the covariance matrix in another frame.
+ *
+ * The transformation is based on Equation (18) of
+ * "Covariance Transformations for Satellite Flight Dynamics Operations"
+ * by David A. Vallado".
+ *
+ * As the frame transformation must be performed with the covariance
+ * expressed in Cartesian elements, both the orbit and position angle
+ * types of the input covariance must be provided.
+ * @param orbit orbit to which the covariance matrix should correspond
+ * @param frameIn the frame in which the input covariance matrix is expressed (must be inertial)
+ * @param frameOut the target frame (must be inertial)
+ * @param inputCov input covariance
+ * @param covOrbitType orbit type of the covariance matrix
+ * @param covAngleType position angle type of the covariance matrix
+ * (not used if covOrbitType equals {@code CARTESIAN})
+ * @return the covariance matrix expressed in the target frame
+ */
+ public static RealMatrix changeCovarianceFrame(final Orbit orbit,
+ final Frame frameIn, final Frame frameOut,
+ final RealMatrix inputCov,
+ final OrbitType covOrbitType, final PositionAngle covAngleType) {
+
+ // Convert input matrix to Cartesian parameters in input frame
+ final RealMatrix cartesianCovarianceIn = changeCovarianceType(orbit, covOrbitType, covAngleType,
+ OrbitType.CARTESIAN, PositionAngle.MEAN,
+ inputCov);
+
+ // Get the transform from the covariance frame to the output frame
+ final Transform inToOut = frameIn.getTransformTo(frameOut, orbit.getDate());
+
+ // Get the Jacobian of the transform
+ final double[][] jacobian = new double[STATE_DIMENSION][STATE_DIMENSION];
+ inToOut.getJacobian(CartesianDerivativesFilter.USE_PV, jacobian);
+
+ // Matrix to perform the covariance transformation
+ final RealMatrix j = new Array2DRowRealMatrix(jacobian, false);
+
+ // Get the Cartesian covariance matrix converted to frameOut
+ final RealMatrix cartesianCovarianceOut = j.multiply(cartesianCovarianceIn.multiplyTransposed(j));
+
+ // Convert orbit frame to output frame
+ final Orbit outOrbit = new CartesianOrbit(orbit.getPVCoordinates(frameOut), frameOut, orbit.getMu());
+
+ // Convert output Cartesian matrix to initial orbit type and position angle
+ return changeCovarianceType(outOrbit, OrbitType.CARTESIAN, PositionAngle.MEAN,
+ covOrbitType, covAngleType, cartesianCovarianceOut);
+
+ }
+
+ /** Get the covariance matrix in another orbit type.
+ * @param orbit orbit to which the covariance matrix should correspond
+ * @param inOrbitType initial orbit type of the state covariance matrix
+ * @param inAngleType initial position angle type of the state covariance matrix
+ * @param outOrbitType target orbit type of the state covariance matrix
+ * @param outAngleType target position angle type of the state covariance matrix
+ * @param inputCov input covariance
+ * @return the covariance expressed in the target orbit type with the
+ * target position angle
+ */
+ public static RealMatrix changeCovarianceType(final Orbit orbit,
+ final OrbitType inOrbitType, final PositionAngle inAngleType,
+ final OrbitType outOrbitType, final PositionAngle outAngleType,
+ final RealMatrix inputCov) {
+
+ // Notations:
+ // I: Input orbit type
+ // O: Output orbit type
+ // C: Cartesian parameters
+
+ // Compute dOutputdCartesian
+ final double[][] aOC = new double[STATE_DIMENSION][STATE_DIMENSION];
+ final Orbit orbitInOutputType = outOrbitType.convertType(orbit);
+ orbitInOutputType.getJacobianWrtCartesian(outAngleType, aOC);
+ final RealMatrix dOdC = new Array2DRowRealMatrix(aOC, false);
+
+ // Compute dCartesiandInput
+ final double[][] aCI = new double[STATE_DIMENSION][STATE_DIMENSION];
+ final Orbit orbitInInputType = inOrbitType.convertType(orbit);
+ orbitInInputType.getJacobianWrtParameters(inAngleType, aCI);
+ final RealMatrix dCdI = new Array2DRowRealMatrix(aCI, false);
+
+ // Compute dOutputdInput
+ final RealMatrix dOdI = dOdC.multiply(dCdI);
+
+ // Conversion of the state covariance matrix in target orbit type
+ final RealMatrix outputCov = dOdI.multiply(inputCov.multiplyTransposed(dOdI));
+
+ // Return the converted covariance
+ return outputCov;
+
+ }
+
+ /** Convert an array to a matrix (6x6 dimension).
+ * @param array input array
+ * @return the corresponding matrix
+ */
+ private RealMatrix toRealMatrix(final double[] array) {
+ final RealMatrix matrix = MatrixUtils.createRealMatrix(STATE_DIMENSION, STATE_DIMENSION);
+ int index = 0;
+ for (int i = 0; i < STATE_DIMENSION; ++i) {
+ for (int j = 0; j < STATE_DIMENSION; ++j) {
+ matrix.setEntry(i, j, array[index++]);
+ }
+ }
+ return matrix;
+ }
+
+ /** Set the covariance data into an array.
+ * @param covariance covariance matrix
+ * @return an array containing the covariance data
+ */
+ private double[] toArray(final RealMatrix covariance) {
+ final double[] array = new double[STATE_DIMENSION * STATE_DIMENSION];
+ int index = 0;
+ for (int i = 0; i < STATE_DIMENSION; ++i) {
+ for (int j = 0; j < STATE_DIMENSION; ++j) {
+ array[index++] = covariance.getEntry(i, j);
+ }
+ }
+ return array;
+ }
+
+}
diff --git a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java
index 06a6adb9cde89455e67e5ac4900bcab4a56e787d..da72be72c07217536cc7b34596ad8132e98ad055 100644
--- a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java
+++ b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalMatricesHarvester.java
@@ -24,6 +24,7 @@ import org.hipparchus.linear.MatrixUtils;
import org.hipparchus.linear.RealMatrix;
import org.orekit.orbits.FieldOrbit;
import org.orekit.propagation.AbstractMatricesHarvester;
+import org.orekit.propagation.AdditionalStateProvider;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.SpacecraftState;
import org.orekit.time.AbsoluteDate;
@@ -39,7 +40,7 @@ import org.orekit.utils.ParameterDriver;
* @author Bryan Cazabonne
* @since 11.1
*/
-public abstract class AbstractAnalyticalMatricesHarvester extends AbstractMatricesHarvester {
+public abstract class AbstractAnalyticalMatricesHarvester extends AbstractMatricesHarvester implements AdditionalStateProvider {
/** Columns names for parameters. */
private List columnsNames;
@@ -94,11 +95,28 @@ public abstract class AbstractAnalyticalMatricesHarvester extends AbstractMatric
/** {@inheritDoc} */
@Override
- public RealMatrix getStateTransitionMatrix(final SpacecraftState state) {
+ public String getName() {
+ return getStmName();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getAdditionalState(final SpacecraftState state) {
// Update the partial derivatives if needed
updateDerivativesIfNeeded(state);
- //return the state transition matrix
- return MatrixUtils.createRealMatrix(analyticalDerivativesStm);
+ // Return the state transition matrix in an array
+ return toArray(analyticalDerivativesStm);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix getStateTransitionMatrix(final SpacecraftState state) {
+ // Check if additional state is defined
+ if (!state.hasAdditionalState(getName())) {
+ return null;
+ }
+ // Return the state transition matrix
+ return toRealMatrix(state.getAdditionalState(getName()));
}
/** {@inheritDoc} */
@@ -214,6 +232,37 @@ public abstract class AbstractAnalyticalMatricesHarvester extends AbstractMatric
}
}
+ /** Convert an array to a matrix (6x6 dimension).
+ * @param array input array
+ * @return the corresponding matrix
+ */
+ private RealMatrix toRealMatrix(final double[] array) {
+ final RealMatrix matrix = MatrixUtils.createRealMatrix(STATE_DIMENSION, STATE_DIMENSION);
+ int index = 0;
+ for (int i = 0; i < STATE_DIMENSION; ++i) {
+ for (int j = 0; j < STATE_DIMENSION; ++j) {
+ matrix.setEntry(i, j, array[index++]);
+ }
+ }
+ return matrix;
+ }
+
+ /** Set the STM data into an array.
+ * @param matrix STM matrix
+ * @return an array containing the STM data
+ */
+ private double[] toArray(final double[][] matrix) {
+ final double[] array = new double[STATE_DIMENSION * STATE_DIMENSION];
+ int index = 0;
+ for (int i = 0; i < STATE_DIMENSION; ++i) {
+ final double[] row = matrix[i];
+ for (int j = 0; j < STATE_DIMENSION; ++j) {
+ array[index++] = row[j];
+ }
+ }
+ return array;
+ }
+
/**
* Get the gradient converter related to the analytical orbit propagator.
* @return the gradient converter
diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java
index 84daef302b6c5efcf22b40304712063e35aef7c0..940d8e86dfd1513d91acf3bf08d735dc351c8798 100644
--- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java
+++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java
@@ -648,7 +648,12 @@ public class BrouwerLyddanePropagator extends AbstractAnalyticalPropagator {
@Override
protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
final DoubleArrayDictionary initialJacobianColumns) {
- return new BrouwerLyddaneHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Create the harvester
+ final BrouwerLyddaneHarvester harvester = new BrouwerLyddaneHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Update the list of additional state provider
+ addAdditionalStateProvider(harvester);
+ // Return the configured harvester
+ return harvester;
}
/**
diff --git a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java
index 4ce46c82a4ea8254042eb84c5e4db17882e59519..7287aaccc72a747a66a379032caf66aa863647be 100644
--- a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java
+++ b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java
@@ -583,7 +583,12 @@ public class EcksteinHechlerPropagator extends AbstractAnalyticalPropagator {
@Override
protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
final DoubleArrayDictionary initialJacobianColumns) {
- return new EcksteinHechlerHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Create the harvester
+ final EcksteinHechlerHarvester harvester = new EcksteinHechlerHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Update the list of additional state provider
+ addAdditionalStateProvider(harvester);
+ // Return the configured harvester
+ return harvester;
}
/** Local class for Eckstein-Hechler model, with fixed mean parameters. */
diff --git a/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java b/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java
index 9254260bf1667eb37d4f8453cd5f024e12fc66c5..fa07d16f1d5b8120d93fac6e14a72c58a1a6e272 100644
--- a/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java
+++ b/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java
@@ -198,7 +198,12 @@ public class KeplerianPropagator extends AbstractAnalyticalPropagator {
@Override
protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
final DoubleArrayDictionary initialJacobianColumns) {
- return new KeplerianHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Create the harvester
+ final KeplerianHarvester harvester = new KeplerianHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Update the list of additional state provider
+ addAdditionalStateProvider(harvester);
+ // Return the configured harvester
+ return harvester;
}
}
diff --git a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java
index 10ae0ec31bea7ae09920fcd36395cff4259615d4..e4d76404dd65b610de28fbd63d3f56313200f79b 100644
--- a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java
+++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java
@@ -594,7 +594,12 @@ public abstract class TLEPropagator extends AbstractAnalyticalPropagator {
@Override
protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
final DoubleArrayDictionary initialJacobianColumns) {
- return new TLEHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Create the harvester
+ final TLEHarvester harvester = new TLEHarvester(this, stmName, initialStm, initialJacobianColumns);
+ // Update the list of additional state provider
+ addAdditionalStateProvider(harvester);
+ // Return the configured harvester
+ return harvester;
}
/**
diff --git a/src/test/java/org/orekit/estimation/leastsquares/TLEBatchLSEstimatorTest.java b/src/test/java/org/orekit/estimation/leastsquares/TLEBatchLSEstimatorTest.java
index afd2924a6fbf8803b8412aa0ef2c575e68d9afed..2cb89cf7f35c0e80bb02d82691a411b7f811fa58 100644
--- a/src/test/java/org/orekit/estimation/leastsquares/TLEBatchLSEstimatorTest.java
+++ b/src/test/java/org/orekit/estimation/leastsquares/TLEBatchLSEstimatorTest.java
@@ -340,7 +340,7 @@ public class TLEBatchLSEstimatorTest {
// we have low correlation between the two types of measurement. We can expect a good estimate.
TLEEstimationTestUtils.checkFit(context, estimator, 4, 5,
0.0, 5.2e-6,
- 0.0, 3.3e-5,
+ 0.0, 3.4e-5,
0.0, 6.1e-6,
0.0, 2.5e-9);
}
diff --git a/src/test/java/org/orekit/propagation/StateCovarianceMatrixProviderTest.java b/src/test/java/org/orekit/propagation/StateCovarianceMatrixProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..12a4336ebb2921548a8f4077a53c70d8ddc88d59
--- /dev/null
+++ b/src/test/java/org/orekit/propagation/StateCovarianceMatrixProviderTest.java
@@ -0,0 +1,386 @@
+/* Copyright 2002-2022 CS GROUP
+ * Licensed to CS GROUP (CS) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.propagation;
+
+import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.linear.MatrixUtils;
+import org.hipparchus.linear.RealMatrix;
+import org.hipparchus.ode.ODEIntegrator;
+import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator;
+import org.hipparchus.util.FastMath;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.orekit.Utils;
+import org.orekit.forces.ForceModel;
+import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
+import org.orekit.forces.gravity.potential.GravityFieldFactory;
+import org.orekit.forces.gravity.potential.ICGEMFormatReader;
+import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
+import org.orekit.frames.Frame;
+import org.orekit.frames.FramesFactory;
+import org.orekit.orbits.CartesianOrbit;
+import org.orekit.orbits.Orbit;
+import org.orekit.orbits.OrbitType;
+import org.orekit.orbits.PositionAngle;
+import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
+import org.orekit.propagation.numerical.NumericalPropagator;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.TimeScalesFactory;
+import org.orekit.utils.Constants;
+import org.orekit.utils.IERSConventions;
+import org.orekit.utils.PVCoordinates;
+
+public class StateCovarianceMatrixProviderTest {
+
+ private SpacecraftState initialState;
+ private double[][] initCov;
+
+ @Before
+ public void setUp() {
+ Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format");
+ GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
+ Orbit initialOrbit = new CartesianOrbit(new PVCoordinates(new Vector3D(7526993.581890527, -9646310.10026971, 1464110.4928112086),
+ new Vector3D(3033.79456099698, 1715.265069098717, -4447.658745923895)),
+ FramesFactory.getEME2000(),
+ new AbsoluteDate("2016-02-13T16:00:00.000", TimeScalesFactory.getUTC()),
+ Constants.WGS84_EARTH_MU);
+ initialState = new SpacecraftState(initialOrbit);
+ initCov = new double[][] {
+ {8.651816029e+01, 5.689987127e+01, -2.763870764e+01, -2.435617201e-02, 2.058274137e-02, -5.872883051e-03},
+ {5.689987127e+01, 7.070624321e+01, 1.367120909e+01, -6.112622013e-03, 7.623626008e-03, -1.239413190e-02},
+ {-2.763870764e+01, 1.367120909e+01, 1.811858898e+02, 3.143798992e-02, -4.963106559e-02, -7.420114385e-04},
+ {-2.435617201e-02, -6.112622013e-03, 3.143798992e-02, 4.657077389e-05, 1.469943634e-05, 3.328475593e-05},
+ {2.058274137e-02, 7.623626008e-03, -4.963106559e-02, 1.469943634e-05, 3.950715934e-05, 2.516044258e-05},
+ {-5.872883051e-03, -1.239413190e-02, -7.420114385e-04, 3.328475593e-05, 2.516044258e-05, 3.547466120e-05}
+ };
+ }
+
+ /**
+ * Unit test for the covariance frame transformation.
+ */
+ @Test
+ public void testFrameConversion() {
+
+ // Reference
+ final RealMatrix referenceCov = MatrixUtils.createRealMatrix(initCov);
+
+ // Define frames
+ final Frame frameA = FramesFactory.getEME2000();
+ final Frame frameB = FramesFactory.getTEME();
+
+ // First transformation
+ RealMatrix transformedCov = StateCovarianceMatrixProvider.changeCovarianceFrame(initialState.getOrbit(), frameA, frameB, referenceCov, OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Second transformation
+ transformedCov = StateCovarianceMatrixProvider.changeCovarianceFrame(initialState.getOrbit(), frameB, frameA, transformedCov, OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Verify
+ compareCovariance(referenceCov, transformedCov, 5.2e-15);
+
+ }
+
+ /**
+ * Unit test for the covariance type transformation.
+ */
+ @Test
+ public void testTypeConversion() {
+
+ // Reference
+ final RealMatrix referenceCov = MatrixUtils.createRealMatrix(initCov);
+
+ // Define orbit types
+ final OrbitType cart = OrbitType.CARTESIAN;
+ final OrbitType kep = OrbitType.KEPLERIAN;
+
+ // First transformation
+ RealMatrix transformedCov = StateCovarianceMatrixProvider.changeCovarianceType(initialState.getOrbit(), cart, PositionAngle.MEAN,
+ kep, PositionAngle.MEAN, referenceCov);
+
+ // Second transformation
+ transformedCov = StateCovarianceMatrixProvider.changeCovarianceType(initialState.getOrbit(), kep, PositionAngle.MEAN,
+ cart, PositionAngle.MEAN, transformedCov);
+
+ // Verify
+ compareCovariance(referenceCov, transformedCov, 3.5e-12);
+
+ }
+
+ /**
+ * Unit test for covariance propagation in Cartesian elements.
+ */
+ @Test
+ public void testWithNumericalPropagatorCartesian() {
+
+ // Integrator
+ final double step = 60.0;
+ final ODEIntegrator integrator = new ClassicalRungeKuttaIntegrator(step);
+
+ // Numerical propagator
+ final String stmName = "STM";
+ final OrbitType propType = OrbitType.CARTESIAN;
+ final PositionAngle angleType = PositionAngle.MEAN;
+ final NumericalPropagator propagator = new NumericalPropagator(integrator);
+ // Add a force model
+ final NormalizedSphericalHarmonicsProvider gravity = GravityFieldFactory.getNormalizedProvider(2, 0);
+ final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), gravity);
+ propagator.addForceModel(holmesFeatherstone);
+ // Finalize setting
+ final MatricesHarvester harvester = propagator.setupMatricesComputation(stmName, null, null);
+ propagator.setOrbitType(propType);
+ propagator.setPositionAngleType(angleType);
+
+ // Create additional state
+ final String additionalName = "cartCov";
+ final RealMatrix initialCov = MatrixUtils.createRealMatrix(initCov);
+ final StateCovarianceMatrixProvider provider = new StateCovarianceMatrixProvider(additionalName, stmName, harvester,
+ propagator.getOrbitType(), propagator.getPositionAngleType(),
+ initialCov,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ propagator.setInitialState(initialState);
+ propagator.addAdditionalStateProvider(provider);
+
+ // Propagate
+ final SpacecraftState propagated = propagator.propagate(initialState.getDate().shiftedBy(Constants.JULIAN_DAY));
+
+ // Get the propagated covariance
+ final RealMatrix propagatedCov = provider.getStateCovariance(propagated);
+
+ // Reference (computed using a different solution)
+ final double[][] ref = new double[][] {
+ { 5.770543135e+02, 2.316979550e+02, -5.172369105e+02, -2.585893247e-01, 2.113809017e-01, -1.759509343e-01},
+ { 2.316979550e+02, 1.182942930e+02, -1.788422178e+02, -9.570305681e-02, 7.792155309e-02, -7.435822327e-02},
+ {-5.172369105e+02, -1.788422178e+02, 6.996248500e+02, 2.633605389e-01, -2.480144888e-01, 1.908427233e-01},
+ {-2.585893247e-01, -9.570305681e-02, 2.633605389e-01, 1.419148897e-04, -8.715858320e-05, 1.024944399e-04},
+ { 2.113809017e-01, 7.792155309e-02, -2.480144888e-01, -8.715858320e-05, 1.069566588e-04, -5.667563856e-05},
+ {-1.759509343e-01, -7.435822327e-02, 1.908427233e-01, 1.024944399e-04, -5.667563856e-05, 8.178356868e-05}
+ };
+ final RealMatrix referenceCov = MatrixUtils.createRealMatrix(ref);
+
+ // Verify
+ compareCovariance(referenceCov, propagatedCov, 4.0e-7);
+ Assert.assertEquals(OrbitType.CARTESIAN, provider.getCovarianceOrbitType());
+
+ ///////////
+ // Test the frame transformation
+ ///////////
+
+ // Define a new output frame
+ final Frame frameB = FramesFactory.getTEME();
+
+ // Get the covariance in TEME frame
+ RealMatrix transformedCovA = provider.getStateCovariance(propagated, frameB);
+
+ // Second transformation
+ RealMatrix transformedCovB = StateCovarianceMatrixProvider.changeCovarianceFrame(propagated.getOrbit(), propagated.getFrame(), frameB, propagatedCov, OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Verify
+ compareCovariance(transformedCovA, transformedCovB, 1.0e-15);
+
+ ///////////
+ // Test the orbit type transformation
+ ///////////
+
+ // Define a new output frame
+ final OrbitType outOrbitType = OrbitType.KEPLERIAN;
+ final PositionAngle outAngleType = PositionAngle.MEAN;
+
+ // Transformation using getStateJacobian() method
+ RealMatrix transformedCovC = provider.getStateCovariance(propagated, outOrbitType, outAngleType);
+
+ // Second transformation
+ RealMatrix transformedCovD = StateCovarianceMatrixProvider.changeCovarianceType(propagated.getOrbit(), OrbitType.CARTESIAN, PositionAngle.MEAN, outOrbitType, outAngleType, propagatedCov);
+
+ // Verify
+ compareCovariance(transformedCovC, transformedCovD, 1.0e-15);
+
+ }
+
+ /**
+ * Unit test for covariance propagation in Cartesian elements.
+ * The difference here is that the propagator uses its default orbit type: EQUINOCTIAL
+ */
+ @Test
+ public void testWithNumericalPropagatorDefault() {
+
+ // Integrator
+ final double step = 60.0;
+ final ODEIntegrator integrator = new ClassicalRungeKuttaIntegrator(step);
+
+ // Numerical propagator
+ final String stmName = "STM";
+ final NumericalPropagator propagator = new NumericalPropagator(integrator);
+ // Add a force model
+ final NormalizedSphericalHarmonicsProvider gravity = GravityFieldFactory.getNormalizedProvider(2, 0);
+ final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), gravity);
+ propagator.addForceModel(holmesFeatherstone);
+ // Finalize setting
+ final MatricesHarvester harvester = propagator.setupMatricesComputation(stmName, null, null);
+
+ // Create additional state
+ final String additionalName = "cartCov";
+ final RealMatrix initialCov = MatrixUtils.createRealMatrix(initCov);
+ final StateCovarianceMatrixProvider provider = new StateCovarianceMatrixProvider(additionalName, stmName, harvester,
+ propagator.getOrbitType(), propagator.getPositionAngleType(),
+ initialCov,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ propagator.setInitialState(initialState);
+ propagator.addAdditionalStateProvider(provider);
+
+ // Propagate
+ final SpacecraftState propagated = propagator.propagate(initialState.getDate().shiftedBy(Constants.JULIAN_DAY));
+
+ // Get the propagated covariance
+ final RealMatrix propagatedCov = provider.getStateCovariance(propagated);
+
+ // Reference (computed using a different solution)
+ final double[][] ref = new double[][] {
+ { 5.770543135e+02, 2.316979550e+02, -5.172369105e+02, -2.585893247e-01, 2.113809017e-01, -1.759509343e-01},
+ { 2.316979550e+02, 1.182942930e+02, -1.788422178e+02, -9.570305681e-02, 7.792155309e-02, -7.435822327e-02},
+ {-5.172369105e+02, -1.788422178e+02, 6.996248500e+02, 2.633605389e-01, -2.480144888e-01, 1.908427233e-01},
+ {-2.585893247e-01, -9.570305681e-02, 2.633605389e-01, 1.419148897e-04, -8.715858320e-05, 1.024944399e-04},
+ { 2.113809017e-01, 7.792155309e-02, -2.480144888e-01, -8.715858320e-05, 1.069566588e-04, -5.667563856e-05},
+ {-1.759509343e-01, -7.435822327e-02, 1.908427233e-01, 1.024944399e-04, -5.667563856e-05, 8.178356868e-05}
+ };
+ final RealMatrix referenceCov = MatrixUtils.createRealMatrix(ref);
+
+ // Verify
+ compareCovariance(referenceCov, propagatedCov, 3.0e-5);
+ Assert.assertEquals(OrbitType.CARTESIAN, provider.getCovarianceOrbitType());
+
+ ///////////
+ // Test the frame transformation
+ ///////////
+
+ // Define a new output frame
+ final Frame frameB = FramesFactory.getTEME();
+
+ // Get the covariance in TEME frame
+ RealMatrix transformedCovA = provider.getStateCovariance(propagated, frameB);
+
+ // Second transformation
+ RealMatrix transformedCovB = StateCovarianceMatrixProvider.changeCovarianceFrame(propagated.getOrbit(), propagated.getFrame(), frameB, propagatedCov, OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Verify
+ compareCovariance(transformedCovA, transformedCovB, 1.0e-15);
+
+ // Define a new output frame
+ final OrbitType outOrbitType = OrbitType.KEPLERIAN;
+ final PositionAngle outAngleType = PositionAngle.MEAN;
+
+ // Transformation using getStateJacobian() method
+ RealMatrix transformedCovC = provider.getStateCovariance(propagated, outOrbitType, outAngleType);
+
+ // Second transformation
+ RealMatrix transformedCovD = StateCovarianceMatrixProvider.changeCovarianceType(propagated.getOrbit(), OrbitType.CARTESIAN, PositionAngle.MEAN, outOrbitType, outAngleType, propagatedCov);
+
+ // Verify
+ compareCovariance(transformedCovC, transformedCovD, 1.0e-15);
+
+ }
+
+ /**
+ * Unit test for covariance propagation in Cartesian elements.
+ */
+ @Test
+ public void testWithAnalyticalPropagator() {
+
+ // Numerical propagator
+ final String stmName = "STM";
+ final OrbitType propType = OrbitType.CARTESIAN;
+ final PositionAngle angleType = PositionAngle.MEAN;
+ final EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(initialState.getOrbit(),
+ GravityFieldFactory.getUnnormalizedProvider(6, 0));
+ // Finalize setting
+ final MatricesHarvester harvester = propagator.setupMatricesComputation(stmName, null, null);
+
+ // Create additional state
+ final String additionalName = "cartCov";
+ final RealMatrix initialCov = MatrixUtils.createRealMatrix(initCov);
+ final StateCovarianceMatrixProvider provider = new StateCovarianceMatrixProvider(additionalName, stmName, harvester,
+ propType, angleType,
+ initialCov,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ propagator.addAdditionalStateProvider(provider);
+
+ // Propagate
+ final SpacecraftState propagated = propagator.propagate(initialState.getDate().shiftedBy(Constants.JULIAN_DAY));
+
+ // Get the propagated covariance
+ final RealMatrix propagatedCov = provider.getStateCovariance(propagated);
+
+ // Reference (computed using a numerical solution)
+ final double[][] ref = new double[][] {
+ { 5.770543135e+02, 2.316979550e+02, -5.172369105e+02, -2.585893247e-01, 2.113809017e-01, -1.759509343e-01},
+ { 2.316979550e+02, 1.182942930e+02, -1.788422178e+02, -9.570305681e-02, 7.792155309e-02, -7.435822327e-02},
+ {-5.172369105e+02, -1.788422178e+02, 6.996248500e+02, 2.633605389e-01, -2.480144888e-01, 1.908427233e-01},
+ {-2.585893247e-01, -9.570305681e-02, 2.633605389e-01, 1.419148897e-04, -8.715858320e-05, 1.024944399e-04},
+ { 2.113809017e-01, 7.792155309e-02, -2.480144888e-01, -8.715858320e-05, 1.069566588e-04, -5.667563856e-05},
+ {-1.759509343e-01, -7.435822327e-02, 1.908427233e-01, 1.024944399e-04, -5.667563856e-05, 8.178356868e-05}
+ };
+ final RealMatrix referenceCov = MatrixUtils.createRealMatrix(ref);
+
+ // Verify
+ compareCovariance(referenceCov, propagatedCov, 5.0e-4);
+ Assert.assertEquals(OrbitType.CARTESIAN, provider.getCovarianceOrbitType());
+
+ ///////////
+ // Test the frame transformation
+ ///////////
+
+ // Define a new output frame
+ final Frame frameB = FramesFactory.getTEME();
+
+ // Get the covariance in TEME frame
+ RealMatrix transformedCovA = provider.getStateCovariance(propagated, frameB);
+
+ // Second transformation
+ RealMatrix transformedCovB = StateCovarianceMatrixProvider.changeCovarianceFrame(propagated.getOrbit(), propagated.getFrame(), frameB, propagatedCov, OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Verify
+ compareCovariance(transformedCovA, transformedCovB, 1.0e-15);
+
+ // Define a new output frame
+ final OrbitType outOrbitType = OrbitType.KEPLERIAN;
+ final PositionAngle outAngleType = PositionAngle.MEAN;
+
+ // Transformation using getStateJacobian() method
+ RealMatrix transformedCovC = provider.getStateCovariance(propagated, outOrbitType, outAngleType);
+
+ // Second transformation
+ RealMatrix transformedCovD = StateCovarianceMatrixProvider.changeCovarianceType(propagated.getOrbit(), OrbitType.CARTESIAN, PositionAngle.MEAN, outOrbitType, outAngleType, propagatedCov);
+
+ // Verify
+ compareCovariance(transformedCovC, transformedCovD, 1.0e-15);
+
+ }
+
+ /**
+ * Compare two covariance matrices
+ * @param reference reference covariance
+ * @param computed computed covariance
+ * @param threshold threshold for comparison
+ */
+ private void compareCovariance(final RealMatrix reference, final RealMatrix computed, final double threshold) {
+ for (int row = 0; row < reference.getRowDimension(); row++) {
+ for (int column = 0; column < reference.getColumnDimension(); column++) {
+ Assert.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column), FastMath.abs(threshold * reference.getEntry(row, column)));
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java b/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java
index b37976b89a156688432f7dba7c41c3e7c3096495..7cd3487b9bf0f9f1d204b4c582330eef7f01bf6d 100644
--- a/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java
+++ b/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java
@@ -64,15 +64,7 @@ public class TLEStateTransitionMatrixTest {
final AbsoluteDate target = initialState.getDate().shiftedBy(initialState.getKeplerianPeriod());
MatricesHarvester harvester = propagator.setupMatricesComputation("stm", null, null);
RealMatrix dYdY0 = harvester.getStateTransitionMatrix(initialState);
- for (int i = 0; i < 6; ++i) {
- for (int j = 0; j < 6; ++j) {
- if (i == j) {
- Assert.assertEquals(1.0, dYdY0.getEntry(i, j), tolerance);
- } else {
- Assert.assertEquals(0.0, dYdY0.getEntry(i, j), tolerance);
- }
- }
- }
+ Assert.assertNull(dYdY0);
final SpacecraftState finalState = propagator.propagate(target);
dYdY0 = harvester.getStateTransitionMatrix(finalState);