diff --git a/src/main/java/org/orekit/errors/OrekitMessages.java b/src/main/java/org/orekit/errors/OrekitMessages.java index f8cf78f06ff107c4d0ad46b04e5c11105dd6a4c7..adeaa7fd875286e759c8b58fd01fb9d9e97a21a8 100644 --- a/src/main/java/org/orekit/errors/OrekitMessages.java +++ b/src/main/java/org/orekit/errors/OrekitMessages.java @@ -16,6 +16,8 @@ */ package org.orekit.errors; +import org.hipparchus.exception.Localizable; + import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -27,8 +29,6 @@ import java.util.MissingResourceException; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; -import org.hipparchus.exception.Localizable; - /** * Enumeration for localized messages formats. *
@@ -330,12 +330,15 @@ public enum OrekitMessages implements Localizable {
INCOMPATIBLE_UNITS("units {0} and {1} are not compatible"),
MISSING_VELOCITY("missing velocity data"),
ATTEMPT_TO_GENERATE_MALFORMED_FILE("attempt to generate file {0} with a formatting error"),
- FIND_ROOT("{0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0})"),
+ FIND_ROOT(
+ "{0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0})"),
BACKWARD_PROPAGATION_NOT_ALLOWED("backward propagation not allowed here"),
- NO_STATION_ECCENTRICITY_FOR_EPOCH("no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2}"),
+ NO_STATION_ECCENTRICITY_FOR_EPOCH(
+ "no station eccentricity values for the given epoch {0}, validity interval is between {1} and {2}"),
INCONSISTENT_SELECTION("inconsistent parameters selection between pairs {0}/{1} and {2}/{3}"),
NO_UNSCENTED_TRANSFORM_CONFIGURED("no unscented transform configured"),
- NOT_STRICTLY_POSITIVE("value is not strictly positive: {0}");
+ NOT_STRICTLY_POSITIVE("value is not strictly positive: {0}"),
+ WRONG_ORBIT_PARAMETERS_TYPE("orbital parameters type: {0} is different from expected orbital type : {1}");
// CHECKSTYLE: resume JavadocVariable check
diff --git a/src/main/java/org/orekit/frames/LOFType.java b/src/main/java/org/orekit/frames/LOFType.java
index 54e4f8c6ce6befec7c05691d81f60e69c59f3125..988611338063167d32caf81a42dd97858b98d208 100644
--- a/src/main/java/org/orekit/frames/LOFType.java
+++ b/src/main/java/org/orekit/frames/LOFType.java
@@ -16,18 +16,21 @@
*/
package org.orekit.frames;
-import org.hipparchus.Field;
import org.hipparchus.CalculusFieldElement;
+import org.hipparchus.Field;
import org.hipparchus.geometry.euclidean.threed.FieldRotation;
import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
import org.hipparchus.geometry.euclidean.threed.Rotation;
+import org.hipparchus.geometry.euclidean.threed.RotationConvention;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.FieldAbsoluteDate;
import org.orekit.utils.FieldPVCoordinates;
import org.orekit.utils.PVCoordinates;
-/** Enumerate for different types of Local Orbital Frames.
+/**
+ * Enumerate for different types of Local Orbital Frames.
+ *
* @author Luc Maisonobe
*/
public enum LOFType {
@@ -47,16 +50,23 @@ public enum LOFType {
* @see #NTW
*/
TNW {
-
/** {@inheritDoc} */
+ @Override
public Rotation rotationFromInertial(final PVCoordinates pv) {
return new Rotation(pv.getVelocity(), pv.getMomentum(),
Vector3D.PLUS_I, Vector3D.PLUS_K);
}
/** {@inheritDoc} */
+ @Override
+ public Rotation rotationFromLOFType(final LOFType fromLOF, final PVCoordinates pv) {
+ return rotationFromLOFInToLOFOut(fromLOF, TNW, pv);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public
+ * This rotation does not include any time derivatives. If first time derivatives (i.e. rotation rate) is needed as
+ * well, the full {@link #transformFromInertial(AbsoluteDate, PVCoordinates) transformFromInertial} method must be
+ * called and the complete rotation transform must be extracted from it.
+ *
- * This rotation does not include any time derivatives. If first
- * time derivatives (i.e. rotation rate) is needed as well, the full
- * {@link #transformFromInertial(AbsoluteDate, PVCoordinates) transformFromInertial}
- * method must be called and the complete rotation transform must be extracted
- * from it.
- *
diff --git a/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java b/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java
index c342d76301686e577a68ea6fcd35214a0f3fbac1..4de49bcd263507d66ec045763b8c6643cf0e19a6 100644
--- a/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java
+++ b/src/main/java/org/orekit/propagation/StateCovarianceMatrixProvider.java
@@ -16,12 +16,15 @@
*/
package org.orekit.propagation;
+import org.hipparchus.geometry.euclidean.threed.Rotation;
import org.hipparchus.linear.Array2DRowRealMatrix;
import org.hipparchus.linear.MatrixUtils;
import org.hipparchus.linear.RealMatrix;
+import org.orekit.errors.OrekitException;
+import org.orekit.errors.OrekitMessages;
import org.orekit.frames.Frame;
+import org.orekit.frames.LOFType;
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;
@@ -31,40 +34,32 @@ 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
+ * 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.
+ * 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.
+ * 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)}
+ * 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(Orbit, Frame, Frame, RealMatrix, OrbitType, PositionAngle)}
- * 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.
+ * {@link #changeCovarianceFrame(Orbit, Frame, Frame, RealMatrix, OrbitType, PositionAngle)} method. This method is
+ * based on Equation (18) of Covariance Transformations for Satellite Flight Dynamics Operations by David A.
+ * Vallado.
*
* Finally, covariance orbit type can be changed using the
- * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngle, OrbitType, PositionAngle, RealMatrix)}
- * method.
+ * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngle, OrbitType, PositionAngle, RealMatrix)} method.
*
+ * The transformation is based on Equation (20) of "Covariance Transformations for Satellite Flight Dynamics
+ * Operations" by David A. Vallado".
+ *
+ * As this method transforms from and to a {@link LOFType commonly used local orbital frame}, it necessarily takes
+ * in a covariance matrix expressed in cartesian elements and output a covariance matrix also expressed in
+ * the
+ * cartesian elements.
+ *
+ * @param orbit orbit to which the covariance matrix should correspond
+ * @param lofIn the local orbital frame in which the input covariance matrix is expressed
+ * @param lofOut the target local orbital frame
+ * @param inputCartesianCov input covariance {@code CARTESIAN})
+ * @return the covariance matrix expressed in the target commonly used local orbital frame in cartesian elements
*/
- public OrbitType getCovarianceOrbitType() {
- return covOrbitType;
+ public static RealMatrix changeCovarianceFrame(final Orbit orbit,
+ final LOFType lofIn, final LOFType lofOut,
+ final RealMatrix inputCartesianCov) {
+
+ // Compute rotation matrix from lofIn to lofOut
+ final Rotation rotationFromLofInToLofOut = LOFType.rotationFromLOFInToLOFOut(lofIn, lofOut,
+ orbit.getPVCoordinates());
+
+ // Builds the matrix to perform covariance transformation
+ final RealMatrix transformationMatrix = buildTransformationMatrixFromRotation(rotationFromLofInToLofOut);
+
+ // Get the Cartesian covariance matrix converted to frameOut
+ final RealMatrix cartesianCovarianceOut =
+ transformationMatrix.multiply(inputCartesianCov.multiplyTransposed(transformationMatrix));
+
+ // Output converted covariance
+ return cartesianCovarianceOut;
+
}
/** {@inheritDoc} */
@@ -148,14 +176,18 @@ public class StateCovarianceMatrixProvider implements AdditionalStateProvider {
covInit);
}
- /** {@inheritDoc}
- *
- * The covariance matrix can be computed only if the State Transition Matrix state is available.
- *
- * The output covariance matrix is expressed in the
- * same orbit type as {@link #getCovarianceOrbitType()}.
+ * The transformation is based on Equation (20) of "Covariance Transformations for Satellite Flight Dynamics
+ * Operations" by David A. Vallado".
*
- * It is possible to change the covariance frame by using the
- * {@link #changeCovarianceFrame(Orbit, Frame, Frame, RealMatrix, OrbitType, PositionAngle)}
- * method.
+ * The input covariance matrix is necessarily expressed in cartesian elements.
*
- * It is also possible to change the covariance orbit type by
- * using the {@link #changeCovarianceType(Orbit, OrbitType, PositionAngle, OrbitType, PositionAngle, RealMatrix)}
- * method.
- * @param state spacecraft state to which the covariance matrix should correspond
- * @return the state covariance matrix
- * @see #getStateCovariance(SpacecraftState, Frame)
- * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngle)
+ * The output covariance matrix will be expressed in cartesian elements.
+ *
+ * @param orbit orbit to which the covariance matrix should correspond
+ * @param lofIn the local orbital frame in which the input covariance matrix is expressed
+ * @param frameOut the target frame
+ * @param inputCartesianCov input covariance ({@code CARTESIAN})
+ * @return the covariance matrix expressed in the target frame in cartesian elements
*/
- public RealMatrix getStateCovariance(final SpacecraftState state) {
+ public static RealMatrix changeCovarianceFrame(final Orbit orbit,
+ final LOFType lofIn, final Frame frameOut,
+ final RealMatrix inputCartesianCov) {
- // Get the current propagated covariance
- final RealMatrix covariance = toRealMatrix(state.getAdditionalState(additionalName));
+ // Initialize output variable
+ final RealMatrix cartesianCovarianceOut;
- // Return the converted covariance
- return covariance;
+ // Get the orbit inertial frame
+ final Frame orbitInertialFrame = orbit.getFrame();
- }
+ if (frameOut.isPseudoInertial()) {
+ // Compute rotation matrix from lofIn to frameOut
+ final Rotation rotationFromLofInToFrameOut =
+ lofIn.rotationFromInertial(orbit.getPVCoordinates(frameOut)).revert();
- /** 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(Orbit, 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
- * The transformation is based on Equation (18) of
- * "Covariance Transformations for Satellite Flight Dynamics Operations"
- * by David A. Vallado".
+ * 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.
+ * 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.
+ *
+ * In case the input frame is not pseudo-inertial, the input covariance matrix is necessarily
+ * expressed in cartesian elements.
+ *
+ * In case the output frame is not pseudo-inertial, the output covariance matrix will be
+ * expressed in cartesian elements.
+ *
* @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 frameIn the frame in which the input covariance matrix is expressed
+ * @param frameOut the target frame
* @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})
+ * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial)
+ * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (not
+ * used if covOrbitType equals {@code CARTESIAN})
* @return the covariance matrix expressed in the target frame
+ * @throws OrekitException if input frame is not pseudo-inertial and the input covariance is
+ * not expressed in cartesian elements.
*/
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());
@@ -287,27 +315,62 @@ public class StateCovarianceMatrixProvider implements AdditionalStateProvider {
// 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));
+ // Input frame pseudo-inertial
+ if (frameIn.isPseudoInertial()) {
+
+ // Convert input matrix to Cartesian parameters in input frame
+ final RealMatrix cartesianCovarianceIn = changeCovarianceType(orbit, covOrbitType, covAngleType,
+ OrbitType.CARTESIAN, PositionAngle.MEAN,
+ inputCov);
+
+ // Get the Cartesian covariance matrix converted to frameOut
+ final RealMatrix cartesianCovarianceOut = j.multiply(cartesianCovarianceIn.multiplyTransposed(j));
+
+ // Output frame is pseudo-inertial
+ if (frameOut.isPseudoInertial()) {
+
+ // Convert output Cartesian matrix to initial orbit type and position angle
+ return changeCovarianceType(orbit, OrbitType.CARTESIAN, PositionAngle.MEAN,
+ covOrbitType, covAngleType, cartesianCovarianceOut);
+ }
+ // Output frame is not pseudo-inertial
+ else {
+
+ // Output cartesian matrix
+ return cartesianCovarianceOut;
+ }
+
+ }
+
+ // Input frame is not pseudo-inertial
+ else {
+ // Covariance is expressed in cartesian elements
+ if (covOrbitType.equals(OrbitType.CARTESIAN)) {
+
+ // Output the Cartesian covariance matrix converted to frameOut
+ return j.multiply(inputCov.multiplyTransposed(j));
- // Convert orbit frame to output frame
- final Orbit outOrbit = new CartesianOrbit(orbit.getPVCoordinates(frameOut), frameOut, orbit.getMu());
+ }
+ // Covariance is not expressed in cartesian elements
+ else {
+ throw new OrekitException(OrekitMessages.WRONG_ORBIT_PARAMETERS_TYPE, covOrbitType,
+ OrbitType.CARTESIAN);
+ }
- // 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.
+ /**
+ * 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
+ * @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,
@@ -320,14 +383,14 @@ public class StateCovarianceMatrixProvider implements AdditionalStateProvider {
// C: Cartesian parameters
// Compute dOutputdCartesian
- final double[][] aOC = new double[STATE_DIMENSION][STATE_DIMENSION];
- final Orbit orbitInOutputType = outOrbitType.convertType(orbit);
+ 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);
+ 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);
@@ -342,28 +405,204 @@ public class StateCovarianceMatrixProvider implements AdditionalStateProvider {
}
- /** Convert an array to a matrix (6x6 dimension).
+ /**
+ * Convert the covariance matrix from a {@link Frame frame} to a {@link LOFType commonly used local orbital frame}.
+ *
+ * The transformation is based on Equation (20) 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.
+ *
+ * The output covariance matrix will necessarily be expressed in cartesian elements and not converted back to
+ * its original expression (if input different from cartesian elements).
+ *
+ *
+ * @param orbit orbit to which the covariance matrix should correspond
+ * @param frameIn the frame in which the input covariance matrix is expressed. In case the frame is not
+ * pseudo-inertial, the input covariance matrix is expected to be expressed in cartesian elements.
+ * @param lofOut the target local orbital frame
+ * @param inputCov input covariance
+ * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial)
+ * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (not used
+ * if covOrbitType equals {@code CARTESIAN})
+ * @return the covariance matrix expressed in the target local orbital frame in cartesian elements
+ * @throws OrekitException if input frame is not pseudo-inertial and the input covariance is
+ * not expressed in cartesian elements.
+ */
+ public static RealMatrix changeCovarianceFrame(final Orbit orbit,
+ final Frame frameIn, final LOFType lofOut,
+ final RealMatrix inputCov,
+ final OrbitType covOrbitType, final PositionAngle covAngleType) {
+
+ // Input frame is inertial
+ if (frameIn.isPseudoInertial()) {
+
+ // Convert input matrix to Cartesian parameters in input frame
+ final RealMatrix cartesianCovarianceIn = changeCovarianceType(orbit, covOrbitType, covAngleType,
+ OrbitType.CARTESIAN, PositionAngle.MEAN,
+ inputCov);
+
+ // Compute rotation matrix from frameIn to lofOut
+ final Rotation rotationFromFrameInToLofOut = lofOut.rotationFromInertial(orbit.getPVCoordinates(frameIn));
+
+ // Builds the matrix to perform covariance transformation
+ final RealMatrix transformationMatrix = buildTransformationMatrixFromRotation(rotationFromFrameInToLofOut);
+
+ // Get the Cartesian covariance matrix converted to frameOut
+ final RealMatrix cartesianCovarianceOut =
+ transformationMatrix.multiply(cartesianCovarianceIn.multiplyTransposed(transformationMatrix));
+
+ // Return converted covariance matrix expressed in cartesian elements
+ return cartesianCovarianceOut;
+
+ }
+
+ // Input frame is not inertial so the covariance matrix is expected to be in cartesian elements
+ else {
+ if (covOrbitType.equals(OrbitType.CARTESIAN)) {
+ final Frame orbitInertialFrame = orbit.getFrame();
+
+ // Compute rotation matrix from frameIn to orbit inertial frame
+ final RealMatrix cartesianCovarianceInOrbitFrame =
+ changeCovarianceFrame(orbit, frameIn, orbitInertialFrame, inputCov,
+ OrbitType.CARTESIAN,
+ PositionAngle.MEAN);
+
+ // Convert from orbit inertial frame to lofOut
+ return changeCovarianceFrame(orbit, orbitInertialFrame, lofOut, cartesianCovarianceInOrbitFrame,
+ OrbitType.CARTESIAN,
+ PositionAngle.MEAN);
+
+ }
+ else {
+ throw new OrekitException(OrekitMessages.WRONG_ORBIT_PARAMETERS_TYPE, covOrbitType,
+ OrbitType.CARTESIAN);
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Get the orbit type in which the covariance matrix is expressed.
+ *
+ * @return the orbit type
+ */
+ public OrbitType getCovarianceOrbitType() {
+ return covOrbitType;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The covariance matrix can be computed only if the State Transition Matrix state is available.
+ *
+ * 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(Orbit, Frame, Frame, RealMatrix, OrbitType, PositionAngle)} method.
+ *
+ * It is also possible to change the covariance orbit type by using the
+ * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngle, OrbitType, PositionAngle, RealMatrix)} method.
+ *
+ * @param state spacecraft state to which the covariance matrix should correspond
+ * @return the state covariance matrix
+ * @see #getStateCovariance(SpacecraftState, Frame)
+ * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngle)
+ */
+ public RealMatrix getStateCovariance(final SpacecraftState state) {
+
+ // Get the current propagated covariance
+ final RealMatrix covariance = toRealMatrix(state.getAdditionalState(additionalName));
+
+ // Return the converted covariance
+ return covariance;
+
+ }
+
+ /**
+ * 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;
+ 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;
+
+ }
+
+ /**
+ * 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(Orbit, 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
+ * More specifically, we're using the initial covariance matrix from p.14 and compare the computed result with the
+ * cartesian covariance in RSW from p.19.
+ *
+ * Note that the followings local orbital frame are equivalent RSW=RTN=QSW.
*/
- 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++) {
- Assertions.assertEquals(reference.getEntry(row, column), computed.getEntry(row, column), FastMath.abs(threshold * reference.getEntry(row, column)));
- }
- }
+ @Test
+ @DisplayName("Test covariance conversion Vallado test case : ECI cartesian to RTN")
+ void should_return_Vallado_RSW_covariance_matrix_from_ECI() {
+
+ // Initialize Orekit
+ Utils.setDataRoot("regular-data");
+
+ // Given
+ final AbsoluteDate initialDate = getValladoInitialDate();
+ final PVCoordinates initialPV = getValladoInitialPV();
+ final Frame initialInertialFrame = FramesFactory.getGCRF();
+ final Orbit initialOrbit =
+ new CartesianOrbit(initialPV, initialInertialFrame, initialDate, getValladoMu());
+
+ final RealMatrix initialCovarianceMatrix = getValladoInitialCovarianceMatrix();
+
+ // When
+ final RealMatrix convertedCovarianceMatrixInRTN =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ initialInertialFrame, LOFType.QSW,
+ initialCovarianceMatrix,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Then
+ final RealMatrix expectedCovarianceMatrixInRTN = new BlockRealMatrix(new double[][] {
+ { 9.918921e-001, 6.700644e-003, -2.878187e-003, 1.892086e-005, 6.700644e-005, -2.878187e-005 },
+ { 6.700644e-003, 1.013730e+000, -1.019283e-002, 6.700644e-005, 2.372970e-004, -1.019283e-004 },
+ { -2.878187e-003, -1.019283e-002, 9.943782e-001, -2.878187e-005, -1.019283e-004, 4.378217e-005 },
+ { 1.892086e-005, 6.700644e-005, -2.878187e-005, 1.892086e-007, 6.700644e-007, -2.878187e-007 },
+ { 6.700644e-005, 2.372970e-004, -1.019283e-004, 6.700644e-007, 2.372970e-006, -1.019283e-006 },
+ { -2.878187e-005, -1.019283e-004, 4.378217e-005, -2.878187e-007, -1.019283e-006, 4.378217e-007 }
+ });
+
+ compareCovariance(expectedCovarianceMatrixInRTN, convertedCovarianceMatrixInRTN, DEFAULT_VALLADO_THRESHOLD);
+
+ }
+
+ private AbsoluteDate getValladoInitialDate() {
+ return new AbsoluteDate(2000, 12, 15, 16, 58, 50.208, TimeScalesFactory.getUTC());
+ }
+
+ private PVCoordinates getValladoInitialPV() {
+ return new PVCoordinates(
+ new Vector3D(-605792.21660, -5870229.51108, 3493053.19896),
+ new Vector3D(-1568.25429, -3702.34891, -6479.48395));
+ }
+
+ private double getValladoMu() {
+ return Constants.IERS2010_EARTH_MU;
+ }
+
+ private RealMatrix getValladoInitialCovarianceMatrix() {
+ return new BlockRealMatrix(new double[][] {
+ { 1, 1e-2, 1e-2, 1e-4, 1e-4, 1e-4 },
+ { 1e-2, 1, 1e-2, 1e-4, 1e-4, 1e-4 },
+ { 1e-2, 1e-2, 1, 1e-4, 1e-4, 1e-4 },
+ { 1e-4, 1e-4, 1e-4, 1e-6, 1e-6, 1e-6 },
+ { 1e-4, 1e-4, 1e-4, 1e-6, 1e-6, 1e-6 },
+ { 1e-4, 1e-4, 1e-4, 1e-6, 1e-6, 1e-6 }
+ });
+ }
+
+ /**
+ * This test is based on the following paper : Covariance Transformations for Satellite Flight Dynamics Operations
+ * from David A. Vallado.
+ *
+ * More specifically, we're using the initial covariance matrix from p.14 and compare the computed result with the
+ * cartesian covariance in NTW from p.19.
+ */
+ @Test
+ @DisplayName("Test covariance conversion Vallado test case : ECI cartesian to NTW")
+ void should_return_Vallado_NTW_covariance_matrix_from_ECI() {
+
+ // Initialize orekit
+ Utils.setDataRoot("regular-data");
+
+ // Given
+ final AbsoluteDate initialDate = getValladoInitialDate();
+ final PVCoordinates initialPV = getValladoInitialPV();
+ final Frame initialInertialFrame = FramesFactory.getGCRF();
+ final Orbit initialOrbit =
+ new CartesianOrbit(initialPV, initialInertialFrame, initialDate, getValladoMu());
+
+ final RealMatrix initialCovarianceMatrix = getValladoInitialCovarianceMatrix();
+
+ // When
+ final RealMatrix convertedCovarianceMatrixInNTW =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ initialInertialFrame, LOFType.NTW,
+ initialCovarianceMatrix,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Then
+ final RealMatrix expectedCovarianceMatrixInNTW = new BlockRealMatrix(new double[][] {
+ { 9.918792e-001, 6.679546e-003, -2.868345e-003, 1.879167e-005, 6.679546e-005, -2.868345e-005 },
+ { 6.679546e-003, 1.013743e+000, -1.019560e-002, 6.679546e-005, 2.374262e-004, -1.019560e-004 },
+ { -2.868345e-003, -1.019560e-002, 9.943782e-001, -2.868345e-005, -1.019560e-004, 4.378217e-005 },
+ { 1.879167e-005, 6.679546e-005, -2.868345e-005, 1.879167e-007, 6.679546e-007, -2.868345e-007 },
+ { 6.679546e-005, 2.374262e-004, -1.019560e-004, 6.679546e-007, 2.374262e-006, -1.019560e-006 },
+ { -2.868345e-005, -1.019560e-004, 4.378217e-005, -2.868345e-007, -1.019560e-006, 4.378217e-007 }
+ });
+
+ compareCovariance(expectedCovarianceMatrixInNTW, convertedCovarianceMatrixInNTW, DEFAULT_VALLADO_THRESHOLD);
+
+ }
+
+ /**
+ * This test is based on the following paper : Covariance Transformations for Satellite Flight Dynamics Operations
+ * from David A. Vallado.
+ *
+ * More specifically, we're using the initial covariance matrix from p.14 as a reference to test multiple
+ * conversions.
+ *
+ * This test aims to verify the numerical precision after various conversions and serves as a non regression test
+ * for future updates.
+ *
+ * Also, note that the conversion from the RTN to TEME tests the fact that the orbit is initially expressed in GCRF
+ * while we want the covariance expressed in TEME. Hence, it tests that the rotation from RTN to TEME needs to be
+ * obtained by expressing the orbit PVCoordinates in the TEME frame (hence the use of orbit.gtPVCoordinates(frameOut)
+ * ,see relevant changeCovarianceFrame method).
+ */
+ @Test
+ @DisplayName("Test custom covariance conversion Vallado test case : GCRF -> TEME -> IRTF -> NTW -> RTN -> ITRF -> GCRF")
+ void should_return_initial_covariance_after_multiple_conversion() {
+
+ // Initialize orekit
+ Utils.setDataRoot("regular-data");
+
+ // Given
+ final AbsoluteDate initialDate = getValladoInitialDate();
+ final PVCoordinates initialPV = getValladoInitialPV();
+ final Frame initialInertialFrame = FramesFactory.getGCRF();
+ final Orbit initialOrbit =
+ new CartesianOrbit(initialPV, initialInertialFrame, initialDate, getValladoMu());
+
+ final RealMatrix initialCovarianceMatrixInGCRF = getValladoInitialCovarianceMatrix();
+
+ final Frame teme = FramesFactory.getTEME();
+
+ final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
+
+ // When
+ // GCRF -> TEME
+ final RealMatrix convertedCovarianceMatrixInTEME =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ initialInertialFrame, teme,
+ initialCovarianceMatrixInGCRF,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // TEME -> ITRF
+ final RealMatrix convertedCovarianceMatrixInITRF =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ teme, itrf,
+ convertedCovarianceMatrixInTEME,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ // ITRF -> NTW
+ final RealMatrix convertedCovarianceMatrixInNTW =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ itrf, LOFType.NTW,
+ convertedCovarianceMatrixInITRF,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ // NTW -> RTN
+ final RealMatrix convertedCovarianceMatrixInRTN =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ LOFType.NTW, LOFType.QSW,
+ convertedCovarianceMatrixInNTW);
+ // RTN -> ITRF
+ final RealMatrix convertedCovarianceMatrixBackInITRF =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ LOFType.QSW, itrf,
+ convertedCovarianceMatrixInRTN);
+
+ // ITRF -> TEME
+ final RealMatrix convertedCovarianceMatrixBackInTEME =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ itrf, teme,
+ convertedCovarianceMatrixBackInITRF,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ // TEME -> GCRF
+ final RealMatrix convertedCovarianceMatrixInGCRF =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ teme, initialInertialFrame,
+ convertedCovarianceMatrixBackInTEME,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Then
+ final RealMatrix expectedCovarianceMatrixInGCRF = getValladoInitialCovarianceMatrix();
+
+ compareCovariance(expectedCovarianceMatrixInGCRF, convertedCovarianceMatrixInGCRF, 1e-12);
+
+ }
+
+ /**
+ * This test is based on the following paper : Covariance Transformations for Satellite Flight Dynamics Operations
+ * from David A. Vallado.
+ *
+ * More specifically, we're using the initial covariance matrix from p.14 and compare the computed result with the
+ * cartesian covariance in ECEF from p.18.
+ *
+ * BEWARE: It has been found that the earth rotation in this Vallado's case is given 1 million times slower than
+ * the expected value. This has been corrected and the expected covariance matrix is now the covariance matrix
+ * computed by Orekit given the similarities with Vallado's results. In addition, the small differences potentially
+ * come from the custom EOP that Vallado uses. Hence, this test can be considered as a non regression
+ * test.
+ *
+ * More specifically, we're using the initial covariance matrix from p.14 and compare the computed result with the
+ * cartesian covariance in PEF from p.18.
+ */
+ @Test
+ @DisplayName("Test covariance conversion Vallado test case : PEF cartesian to ECI")
+ void should_return_Vallado_ECI_covariance_matrix_from_PEF() {
+
+ // Initialize orekit
+ Utils.setDataRoot("regular-data");
+
+ // Given
+ final AbsoluteDate initialDate = getValladoInitialDate();
+ final PVCoordinates initialPV = getValladoInitialPV();
+ final Frame initialInertialFrame = FramesFactory.getGCRF();
+ final Orbit initialOrbit =
+ new CartesianOrbit(initialPV, initialInertialFrame, initialDate, getValladoMu());
+
+ final RealMatrix initialCovarianceMatrixInPEF = new BlockRealMatrix(new double[][] {
+ { 9.9340005761276870e-01, 7.5124999798868530e-03, 5.8312675007359050e-03, 3.4548396261054936e-05,
+ 2.6851237046859200e-06, 5.8312677693153940e-05 },
+ { 7.5124999798868025e-03, 1.0065990293034541e+00, 1.2884310200351924e-02, 1.4852736004690684e-04,
+ 1.6544247282904867e-04, 1.2884310644320954e-04 },
+ { 5.8312675007359040e-03, 1.2884310200351924e-02, 1.0000009130837746e+00, 5.9252211072590390e-05,
+ 1.2841787487219444e-04, 1.0000913090989617e-04 },
+ { 3.4548396261054936e-05, 1.4852736004690686e-04, 5.9252211072590403e-05, 3.5631474857130520e-07,
+ 7.6083489184819870e-07, 5.9252213790760030e-07 },
+ { 2.6851237046859150e-06, 1.6544247282904864e-04, 1.2841787487219447e-04, 7.6083489184819880e-07,
+ 1.6542289254142709e-06, 1.2841787929229964e-06 },
+ { 5.8312677693153934e-05, 1.2884310644320950e-04, 1.0000913090989616e-04, 5.9252213790760020e-07,
+ 1.2841787929229960e-06, 1.0000913098203875e-06 }
+ });
+
+ final Frame inputFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
+
+ // When
+ final RealMatrix convertedCovarianceMatrixInECI =
+ StateCovarianceMatrixProvider.changeCovarianceFrame(initialOrbit,
+ inputFrame, initialInertialFrame,
+ initialCovarianceMatrixInPEF,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+
+ // Then
+ final RealMatrix expectedCovarianceMatrixInECI = getValladoInitialCovarianceMatrix();
+
+ compareCovariance(expectedCovarianceMatrixInECI, convertedCovarianceMatrixInECI, 1e-7);
+
+ }
+
+ /**
+ * This test is based on the following paper : Covariance Transformations for Satellite Flight Dynamics Operations
+ * from David A. Vallado.
+ *
+ * More specifically, we're using the initial covariance matrix from p.14 and compare the computed result with the
+ * cartesian covariance in MOD from p.17.
+ *
+ * More specifically, we're using the initial NTW covariance matrix from p.19 and compare the computed result with
+ * the cartesian covariance in RSW from the same page.
+ *
+ * More specifically, we're using the initial NTW covariance matrix from p.19 and compare the computed result with
+ * the cartesian covariance in RSW from the same page.
+ * frame
- * @see #getStateCovariance(SpacecraftState)
- * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngle)
- */
- public RealMatrix getStateCovariance(final SpacecraftState state, final Frame frame) {
+ // Builds the matrix to perform covariance transformation
+ final RealMatrix transformationMatrix =
+ buildTransformationMatrixFromRotation(rotationFromLofInToFrameOut);
- // 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
- * @see #getStateCovariance(SpacecraftState)
- * @see #getStateCovariance(SpacecraftState, Frame)
- */
- public RealMatrix getStateCovariance(final SpacecraftState state, final OrbitType orbitType, final PositionAngle angleType) {
+ // Get the Cartesian covariance matrix converted to frameOut
+ cartesianCovarianceOut =
+ transformationMatrix.multiply(inputCartesianCov.multiplyTransposed(transformationMatrix));
- // Get the current propagated covariance
- final RealMatrix covariance = toRealMatrix(state.getAdditionalState(additionalName));
+ }
+ else {
+ // Compute rotation matrix from lofIn to orbit inertial frame
+ final Rotation rotationFromLofInToOrbitFrame =
+ lofIn.rotationFromInertial(orbit.getPVCoordinates()).revert();
+
+ // Builds the matrix to perform covariance transformation
+ final RealMatrix transformationMatrixFromLofInToOrbitFrame =
+ buildTransformationMatrixFromRotation(rotationFromLofInToOrbitFrame);
+
+ // Get the Cartesian covariance matrix converted to orbit inertial frame
+ final RealMatrix cartesianCovarianceInOrbitFrame = transformationMatrixFromLofInToOrbitFrame.multiply(
+ inputCartesianCov.multiplyTransposed(transformationMatrixFromLofInToOrbitFrame));
+
+ // Get the Cartesian covariance matrix converted to frameOut
+ cartesianCovarianceOut =
+ changeCovarianceFrame(orbit, orbitInertialFrame, frameOut, cartesianCovarianceInOrbitFrame,
+ OrbitType.CARTESIAN, PositionAngle.MEAN);
+ }
- // Return the converted covariance
- return changeCovarianceType(state.getOrbit(), covOrbitType, covAngleType, orbitType, angleType, covariance);
+ // Output converted covariance
+ return cartesianCovarianceOut;
}
- /** Get the covariance matrix in another frame.
+ /**
+ * Get the covariance matrix in another frame.
* frame
+ * @see #getStateCovariance(SpacecraftState)
+ * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngle)
+ */
+ 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
+ * @see #getStateCovariance(SpacecraftState)
+ * @see #getStateCovariance(SpacecraftState, Frame)
+ */
+ 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);
+
}
- /** Set the covariance data into an array.
+ /**
+ * 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;
+ 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);
diff --git a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8
index 896542aa4f954c8930fbf5f1109c8fe1c3f0d4ab..73e273c2a1c972573324578fba8eebddb4a9055c 100644
--- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8
+++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8
@@ -761,3 +761,6 @@ NO_UNSCENTED_TRANSFORM_CONFIGURED =