StateCovariance.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.propagation;

  18. import org.hipparchus.linear.Array2DRowRealMatrix;
  19. import org.hipparchus.linear.MatrixUtils;
  20. import org.hipparchus.linear.RealMatrix;
  21. import org.orekit.errors.OrekitException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.frames.Frame;
  24. import org.orekit.frames.KinematicTransform;
  25. import org.orekit.frames.LOF;
  26. import org.orekit.orbits.Orbit;
  27. import org.orekit.orbits.OrbitType;
  28. import org.orekit.orbits.PositionAngleType;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.TimeStamped;
  31. import org.orekit.utils.CartesianCovarianceUtils;

  32. /** This class is the representation of a covariance matrix at a given date.
  33.  * <p>
  34.  * Currently, the covariance only represents the orbital elements.
  35.  * <p>
  36.  * It is possible to change the covariance frame by using the
  37.  * {@link #changeCovarianceFrame(Orbit, Frame)} or {@link #changeCovarianceFrame(Orbit, LOF)} method.
  38.  * These methods are based on Equations (18) and (20) of <i>Covariance Transformations for Satellite
  39.  * Flight Dynamics Operations</i> by David A. SVallado.
  40.  * <p>
  41.  * Finally, covariance orbit type can be changed using the
  42.  * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngleType)} method.
  43.  *
  44.  * @author Bryan Cazabonne
  45.  * @author Vincent Cucchietti
  46.  * @since 11.3
  47.  */
  48. public class StateCovariance implements TimeStamped {

  49.     /** State dimension. */
  50.     public static final int STATE_DIMENSION = 6;

  51.     /** Default position angle for covariance expressed in Cartesian elements. */
  52.     private static final PositionAngleType DEFAULT_POSITION_ANGLE = PositionAngleType.TRUE;

  53.     /** Orbital covariance [6x6]. */
  54.     private final RealMatrix orbitalCovariance;

  55.     /** Covariance frame (can be null if LOF is defined). */
  56.     private final Frame frame;

  57.     /** Covariance LOF type (can be null if frame is defined). */
  58.     private final LOF lof;

  59.     /** Covariance epoch. */
  60.     private final AbsoluteDate epoch;

  61.     /** Covariance orbit type. */
  62.     private final OrbitType orbitType;

  63.     /** Covariance position angle type (not used if orbitType is CARTESIAN). */
  64.     private final PositionAngleType angleType;

  65.     /**
  66.      * Constructor.
  67.      * @param orbitalCovariance 6x6 orbital parameters covariance
  68.      * @param epoch epoch of the covariance
  69.      * @param lof covariance LOF type
  70.      */
  71.     public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch, final LOF lof) {
  72.         this(orbitalCovariance, epoch, null, lof, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE);
  73.     }

  74.     /**
  75.      * Constructor.
  76.      * @param orbitalCovariance 6x6 orbital parameters covariance
  77.      * @param epoch epoch of the covariance
  78.      * @param covarianceFrame covariance frame (inertial or Earth fixed)
  79.      * @param orbitType orbit type of the covariance (CARTESIAN if covarianceFrame is not pseudo-inertial)
  80.      * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN)
  81.      */
  82.     public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch,
  83.                            final Frame covarianceFrame,
  84.                            final OrbitType orbitType, final PositionAngleType angleType) {
  85.         this(orbitalCovariance, epoch, covarianceFrame, null, orbitType, angleType);
  86.     }

  87.     /**
  88.      * Private constructor.
  89.      * @param orbitalCovariance 6x6 orbital parameters covariance
  90.      * @param epoch epoch of the covariance
  91.      * @param covarianceFrame covariance frame (inertial or Earth fixed)
  92.      * @param lof covariance LOF type
  93.      * @param orbitType orbit type of the covariance
  94.      * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN)
  95.      */
  96.     private StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch,
  97.                             final Frame covarianceFrame, final LOF lof,
  98.                             final OrbitType orbitType, final PositionAngleType angleType) {

  99.         checkFrameAndOrbitTypeConsistency(covarianceFrame, orbitType);

  100.         this.orbitalCovariance = orbitalCovariance;
  101.         this.epoch = epoch;
  102.         this.frame     = covarianceFrame;
  103.         this.lof       = lof;
  104.         this.orbitType = orbitType;
  105.         this.angleType = angleType;

  106.     }

  107.     /**
  108.      * Check constructor's inputs consistency.
  109.      *
  110.      * @param covarianceFrame covariance frame (inertial or Earth fixed)
  111.      * @param inputType orbit type of the covariance
  112.      *
  113.      * @throws OrekitException if input frame is not pseudo-inertial AND the orbit type is not Cartesian
  114.      */
  115.     public static void checkFrameAndOrbitTypeConsistency(final Frame covarianceFrame, final OrbitType inputType) {

  116.         // State covariance expressed in a celestial body frame
  117.         if (covarianceFrame != null) {

  118.             // Input frame is not pseudo-inertial
  119.             if (!covarianceFrame.isPseudoInertial() && inputType != OrbitType.CARTESIAN) {
  120.                 throw new OrekitException(OrekitMessages.WRONG_ORBIT_PARAMETERS_TYPE,
  121.                                           inputType.name(),
  122.                                           OrbitType.CARTESIAN.name());
  123.             }
  124.         }
  125.     }

  126.     /**
  127.      * Checks if input/output orbit and angle types are identical.
  128.      *
  129.      * @param inOrbitType input orbit type
  130.      * @param inAngleType input angle type
  131.      * @param outOrbitType output orbit type
  132.      * @param outAngleType output angle type
  133.      * @return flag defining if input/output orbit and angle types are identical
  134.      */
  135.     public static boolean inputAndOutputAreIdentical(final OrbitType inOrbitType, final PositionAngleType inAngleType,
  136.                                                      final OrbitType outOrbitType, final PositionAngleType outAngleType) {
  137.         return inOrbitType == outOrbitType && inAngleType == outAngleType;
  138.     }

  139.     /**
  140.      * Checks if input and output orbit types are both {@code OrbitType.CARTESIAN}.
  141.      *
  142.      * @param inOrbitType input orbit type
  143.      * @param outOrbitType output orbit type
  144.      * @return flag defining if input and output orbit types are both {@code OrbitType.CARTESIAN}
  145.      */
  146.     public static boolean inputAndOutputOrbitTypesAreCartesian(final OrbitType inOrbitType, final OrbitType outOrbitType) {
  147.         return inOrbitType == OrbitType.CARTESIAN && outOrbitType == OrbitType.CARTESIAN;
  148.     }

  149.     /** {@inheritDoc}. */
  150.     @Override
  151.     public AbsoluteDate getDate() {
  152.         return epoch;
  153.     }

  154.     /**
  155.      * Get the covariance matrix.
  156.      * @return the covariance matrix
  157.      */
  158.     public RealMatrix getMatrix() {
  159.         return orbitalCovariance;
  160.     }

  161.     /**
  162.      * Get the covariance orbit type.
  163.      * @return the covariance orbit type
  164.      */
  165.     public OrbitType getOrbitType() {
  166.         return orbitType;
  167.     }

  168.     /**
  169.      * Get the covariance angle type.
  170.      * @return the covariance angle type
  171.      */
  172.     public PositionAngleType getPositionAngleType() {
  173.         return angleType;
  174.     }

  175.     /**
  176.      * Get the covariance frame.
  177.      * @return the covariance frame (can be null)
  178.      * @see #getLOF()
  179.      */
  180.     public Frame getFrame() {
  181.         return frame;
  182.     }

  183.     /**
  184.      * Get the covariance LOF type.
  185.      * @return the covariance LOF type (can be null)
  186.      * @see #getFrame()
  187.      */
  188.     public LOF getLOF() {
  189.         return lof;
  190.     }

  191.     /**
  192.      * Get the covariance matrix in another orbit type.
  193.      * <p>
  194.      * The covariance orbit type <b>cannot</b> be changed if the covariance
  195.      * matrix is expressed in a {@link LOF local orbital frame} or a
  196.      * non-pseudo inertial frame.
  197.      * <p>
  198.      * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation.
  199.      * Hence, the current covariance matrix <b>will not exactly match</b> the new linearized case and the
  200.      * distribution will not follow a generalized Gaussian distribution anymore.
  201.      * <p>
  202.      * This is based on equation (1) to (6) from "Vallado, D. A. (2004). Covariance transformations for satellite flight
  203.      * dynamics operations."
  204.      *
  205.      * @param orbit orbit to which the covariance matrix should correspond
  206.      * @param outOrbitType target orbit type of the state covariance matrix
  207.      * @param outAngleType target position angle type of the state covariance matrix
  208.      * @return a new covariance state, expressed in the target orbit type with the target position angle
  209.      * @see #changeCovarianceFrame(Orbit, Frame)
  210.      */
  211.     public StateCovariance changeCovarianceType(final Orbit orbit, final OrbitType outOrbitType,
  212.                                                 final PositionAngleType outAngleType) {

  213.         // Handle case where the covariance is already expressed in the output type
  214.         if (outOrbitType == orbitType && (outOrbitType == OrbitType.CARTESIAN || outAngleType == angleType)) {
  215.             if (lof == null) {
  216.                 return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType);
  217.             }
  218.             else {
  219.                 return new StateCovariance(orbitalCovariance, epoch, lof);
  220.             }
  221.         }

  222.         // Check if the covariance is expressed in a celestial body frame
  223.         if (frame != null) {

  224.             // Check if the covariance is defined in an inertial frame
  225.             if (frame.isPseudoInertial()) {
  226.                 return changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType, outOrbitType, outAngleType,
  227.                                            orbitalCovariance);
  228.             }

  229.             // The covariance is not defined in an inertial frame. The orbit type cannot be changed
  230.             throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME);

  231.         }

  232.         // The covariance is not expressed in a celestial body frame. The orbit type cannot be changed
  233.         throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF);

  234.     }

  235.     /**
  236.      * Get the covariance in a given local orbital frame.
  237.      * <p>
  238.      * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
  239.      * in covariance orbit type is required.
  240.      * <p>
  241.      * This is based on equation (18) to (20) "from Vallado, D. A. (2004). Covariance transformations for satellite
  242.      * flight dynamics operations."
  243.      *
  244.      * @param orbit orbit to which the covariance matrix should correspond
  245.      * @param lofOut output local orbital frame
  246.      * @return a new covariance state, expressed in the output local orbital frame
  247.      */
  248.     public StateCovariance changeCovarianceFrame(final Orbit orbit, final LOF lofOut) {

  249.         // Verify current covariance frame
  250.         if (lof != null) {

  251.             // Check specific case where output covariance will be the same
  252.             if (lofOut == lof) {
  253.                 return new StateCovariance(orbitalCovariance, epoch, lof);
  254.             }

  255.             // Change the covariance local orbital frame
  256.             return changeFrameAndCreate(orbit, epoch, lof, lofOut, orbitalCovariance);

  257.         } else {

  258.             // Covariance is expressed in celestial body frame
  259.             return changeFrameAndCreate(orbit, epoch, frame, lofOut, orbitalCovariance, orbitType, angleType);

  260.         }

  261.     }

  262.     /**
  263.      * Get the covariance in the output frame.
  264.      * <p>
  265.      * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
  266.      * in covariance orbit type is required.
  267.      * <p>
  268.      * This is based on equation (18) to (20) "from Vallado, D. A. (2004). Covariance transformations for satellite
  269.      * flight dynamics operations."
  270.      *
  271.      * @param orbit orbit to which the covariance matrix should correspond
  272.      * @param frameOut output frame
  273.      * @return a new covariance state, expressed in the output frame
  274.      */
  275.     public StateCovariance changeCovarianceFrame(final Orbit orbit, final Frame frameOut) {

  276.         // Verify current covariance frame
  277.         if (lof != null) {

  278.             // Covariance is expressed in local orbital frame
  279.             return changeFrameAndCreate(orbit, epoch, lof, frameOut, orbitalCovariance);

  280.         } else {

  281.             // Check specific case where output covariance will be the same
  282.             if (frame == frameOut) {
  283.                 return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType);
  284.             }

  285.             // Change covariance frame
  286.             return changeFrameAndCreate(orbit, epoch, frame, frameOut, orbitalCovariance, orbitType, angleType);

  287.         }

  288.     }

  289.     /**
  290.      * Get a time-shifted covariance matrix.
  291.      * <p>
  292.      * The shifting model is a linearized, Keplerian one. In other words, it is based on a state transition matrix that
  293.      * is computed assuming Keplerian motion.
  294.      * <p>
  295.      * Shifting is <em>not</em> intended as a replacement for proper covariance propagation, but should be sufficient
  296.      * for small time shifts or coarse accuracy.
  297.      *
  298.      * @param orbit orbit to which the covariance matrix should correspond
  299.      * @param dt time shift in seconds
  300.      * @return a new covariance state, shifted with respect to the instance
  301.      */
  302.     public StateCovariance shiftedBy(final Orbit orbit, final double dt) {

  303.         // Shifted orbit
  304.         final Orbit shifted = orbit.shiftedBy(dt);

  305.         // State covariance expressed in celestial body frame
  306.         if (frame != null) {

  307.             // State covariance expressed in a pseudo-inertial frame
  308.             if (frame.isPseudoInertial()) {

  309.                 // Compute STM
  310.                 final RealMatrix stm = getStm(orbit, dt);

  311.                 // Convert covariance in STM type (i.e., Equinoctial elements)
  312.                 final StateCovariance inStmType = changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType,
  313.                                                                       OrbitType.EQUINOCTIAL, PositionAngleType.MEAN,
  314.                                                                       orbitalCovariance);

  315.                 // Shift covariance by applying the STM
  316.                 final RealMatrix shiftedCov = stm.multiply(inStmType.getMatrix().multiplyTransposed(stm));

  317.                 // Restore the initial covariance type
  318.                 return changeTypeAndCreate(shifted, shifted.getDate(), frame,
  319.                                            OrbitType.EQUINOCTIAL, PositionAngleType.MEAN,
  320.                                            orbitType, angleType, shiftedCov);
  321.             }

  322.             // State covariance expressed in a non pseudo-inertial frame
  323.             else {

  324.                 // Convert state covariance to orbit pseudo-inertial frame
  325.                 final StateCovariance inOrbitFrame = changeCovarianceFrame(orbit, orbit.getFrame());

  326.                 // Shift the state covariance
  327.                 final StateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(orbit, dt);

  328.                 // Restore the initial covariance frame
  329.                 return shiftedCovariance.changeCovarianceFrame(shifted, frame);
  330.             }
  331.         }

  332.         // State covariance expressed in a commonly used local orbital frame (LOF)
  333.         else {

  334.             // Convert state covariance to orbit pseudo-inertial frame
  335.             final StateCovariance inOrbitFrame = changeCovarianceFrame(orbit, orbit.getFrame());

  336.             // Shift the state covariance
  337.             final StateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(orbit, dt);

  338.             // Restore the initial covariance frame
  339.             return shiftedCovariance.changeCovarianceFrame(shifted, lof);
  340.         }

  341.     }

  342.     /**
  343.      * Create a covariance matrix in another orbit type.
  344.      * <p>
  345.      * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation.
  346.      * Hence, the input covariance matrix <b>will not exactly match</b> the new linearized case and the
  347.      * distribution will not follow a generalized Gaussian distribution anymore.
  348.      * <p>
  349.      * This is based on equation (1) to (6) from "Vallado, D. A. (2004). Covariance transformations for satellite flight
  350.      * dynamics operations."
  351.      *
  352.      * @param orbit orbit to which the covariance matrix should correspond
  353.      * @param date covariance epoch
  354.      * @param covFrame covariance frame
  355.      * @param inOrbitType initial orbit type of the state covariance matrix
  356.      * @param inAngleType initial position angle type of the state covariance matrix
  357.      * @param outOrbitType target orbit type of the state covariance matrix
  358.      * @param outAngleType target position angle type of the state covariance matrix
  359.      * @param inputCov input covariance
  360.      * @return the covariance expressed in the target orbit type with the target position angle
  361.      */
  362.     private static StateCovariance changeTypeAndCreate(final Orbit orbit,
  363.                                                        final AbsoluteDate date,
  364.                                                        final Frame covFrame,
  365.                                                        final OrbitType inOrbitType, final PositionAngleType inAngleType,
  366.                                                        final OrbitType outOrbitType, final PositionAngleType outAngleType,
  367.                                                        final RealMatrix inputCov) {

  368.         // Check if type change is really necessary, if not then return input covariance
  369.         if (inputAndOutputOrbitTypesAreCartesian(inOrbitType, outOrbitType) ||
  370.             inputAndOutputAreIdentical(inOrbitType, inAngleType, outOrbitType, outAngleType)) {
  371.             return new StateCovariance(inputCov, date, covFrame, inOrbitType, inAngleType);
  372.         }

  373.         // Notations:
  374.         // I: Input orbit type
  375.         // O: Output orbit type
  376.         // C: Cartesian parameters

  377.         // Compute dOutputdCartesian
  378.         final double[][] aOC               = new double[STATE_DIMENSION][STATE_DIMENSION];
  379.         final Orbit      orbitInOutputType = outOrbitType.convertType(orbit);
  380.         orbitInOutputType.getJacobianWrtCartesian(outAngleType, aOC);
  381.         final RealMatrix dOdC = new Array2DRowRealMatrix(aOC, false);

  382.         // Compute dCartesiandInput
  383.         final double[][] aCI              = new double[STATE_DIMENSION][STATE_DIMENSION];
  384.         final Orbit      orbitInInputType = inOrbitType.convertType(orbit);
  385.         orbitInInputType.getJacobianWrtParameters(inAngleType, aCI);
  386.         final RealMatrix dCdI = new Array2DRowRealMatrix(aCI, false);

  387.         // Compute dOutputdInput
  388.         final RealMatrix dOdI = dOdC.multiply(dCdI);

  389.         // Conversion of the state covariance matrix in target orbit type
  390.         final RealMatrix outputCov = dOdI.multiply(inputCov.multiplyTransposed(dOdI));

  391.         // Return the converted covariance
  392.         return new StateCovariance(outputCov, date, covFrame, outOrbitType, outAngleType);

  393.     }

  394.     /**
  395.      * Create a covariance matrix from a {@link LOF local orbital frame} to another
  396.      * {@link LOF local orbital frame}.
  397.      * <p>
  398.      * Changing the covariance frame is a linear process, this method does not introduce approximation.
  399.      * <p>
  400.      * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics
  401.      * Operations" by David A. Vallado.
  402.      *
  403.      * @param orbit orbit to which the covariance matrix should correspond
  404.      * @param date covariance epoch
  405.      * @param lofIn the local orbital frame in which the input covariance matrix is expressed
  406.      * @param lofOut the target local orbital frame
  407.      * @param inputCartesianCov input covariance {@code CARTESIAN})
  408.      * @return the covariance matrix expressed in the target commonly used local orbital frame in Cartesian elements
  409.      */
  410.     private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
  411.                                                         final LOF lofIn, final LOF lofOut,
  412.                                                         final RealMatrix inputCartesianCov) {

  413.         // Builds the matrix to perform covariance transformation
  414.         final RealMatrix jacobianFromLofInToLofOut =
  415.                 getJacobian(LOF.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates()));

  416.         // Get the Cartesian covariance matrix converted to frameOut
  417.         final RealMatrix cartesianCovarianceOut =
  418.                 jacobianFromLofInToLofOut.multiply(inputCartesianCov.multiplyTransposed(jacobianFromLofInToLofOut));

  419.         // Output converted covariance
  420.         return new StateCovariance(cartesianCovarianceOut, date, lofOut);

  421.     }

  422.     /**
  423.      * Convert the covariance matrix from a {@link Frame frame} to a {@link LOF local orbital frame}.
  424.      * <p>
  425.      * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
  426.      * in covariance orbit type is required.
  427.      * <p>
  428.      * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics
  429.      * Operations" by David A. Vallado.
  430.      * <p>
  431.      * As the frame transformation must be performed with the covariance expressed in Cartesian elements, both the orbit
  432.      * and position angle types of the input covariance must be provided.
  433.      * <p>
  434.      * <b>The output covariance matrix will provided in Cartesian orbit type and not converted back to
  435.      * its original expression (if input different from Cartesian elements).</b>
  436.      *
  437.      * @param orbit orbit to which the covariance matrix should correspond
  438.      * @param date covariance epoch
  439.      * @param frameIn the frame in which the input covariance matrix is expressed. In case the frame is <b>not</b>
  440.      * pseudo-inertial, the input covariance matrix is expected to be expressed in <b>Cartesian elements</b>.
  441.      * @param lofOut the target local orbital frame
  442.      * @param inputCov input covariance
  443.      * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial)
  444.      * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (not used
  445.      * if covOrbitType equals {@code CARTESIAN})
  446.      * @return the covariance matrix expressed in the target local orbital frame in Cartesian elements
  447.      * @throws OrekitException if input frame is <b>not</b> pseudo-inertial <b>and</b> the input covariance is
  448.      * <b>not</b> expressed in Cartesian elements.
  449.      */
  450.     private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
  451.                                                         final Frame frameIn, final LOF lofOut,
  452.                                                         final RealMatrix inputCov,
  453.                                                         final OrbitType covOrbitType,
  454.                                                         final PositionAngleType covAngleType) {

  455.         // Input frame is inertial
  456.         if (frameIn.isPseudoInertial()) {

  457.             // Convert input matrix to Cartesian parameters in input frame
  458.             final RealMatrix cartesianCovarianceIn =
  459.                     changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType,
  460.                                         OrbitType.CARTESIAN, PositionAngleType.MEAN,
  461.                                         inputCov).getMatrix();

  462.             // Builds the matrix to perform covariance transformation
  463.             final RealMatrix jacobianFromFrameInToLofOut =
  464.                             getJacobian(lofOut.transformFromInertial(date, orbit.getPVCoordinates(frameIn)));

  465.             // Get the Cartesian covariance matrix converted to frameOut
  466.             final RealMatrix cartesianCovarianceOut =
  467.                     jacobianFromFrameInToLofOut.multiply(cartesianCovarianceIn.multiplyTransposed(jacobianFromFrameInToLofOut));

  468.             // Return converted covariance matrix expressed in Cartesian elements
  469.             return new StateCovariance(cartesianCovarianceOut, date, lofOut);

  470.         }

  471.         // Input frame is not inertial so the covariance matrix is expected to be in Cartesian elements
  472.         else {

  473.             // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type
  474.             final Frame orbitInertialFrame = orbit.getFrame();

  475.             // Compute rotation matrix from frameIn to orbit inertial frame
  476.             final RealMatrix cartesianCovarianceInOrbitFrame =
  477.                    changeFrameAndCreate(orbit, date, frameIn, orbitInertialFrame, inputCov,
  478.                                          OrbitType.CARTESIAN, PositionAngleType.MEAN).getMatrix();

  479.             // Convert from orbit inertial frame to lofOut
  480.             return changeFrameAndCreate(orbit, date, orbitInertialFrame, lofOut, cartesianCovarianceInOrbitFrame,
  481.                                         OrbitType.CARTESIAN, PositionAngleType.MEAN);

  482.         }

  483.     }

  484.     /**
  485.      * Convert the covariance matrix from a {@link LOF  local orbital frame} to a {@link Frame frame}.
  486.      * <p>
  487.      * Changing the covariance frame is a linear process, this method does not introduce approximation.
  488.      * <p>
  489.      * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics
  490.      * Operations" by David A. Vallado.
  491.      * <p>
  492.      * The <u>input</u> covariance matrix is necessarily provided in <b>Cartesian orbit type</b>.
  493.      * <p>
  494.      * The <u>output</u> covariance matrix will be expressed in <b>Cartesian elements</b>.
  495.      *
  496.      * @param orbit orbit to which the covariance matrix should correspond
  497.      * @param date covariance epoch
  498.      * @param lofIn the local orbital frame in which the input covariance matrix is expressed
  499.      * @param frameOut the target frame
  500.      * @param inputCartesianCov input covariance ({@code CARTESIAN})
  501.      * @return the covariance matrix expressed in the target frame in Cartesian elements
  502.      */
  503.     private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
  504.                                                         final LOF lofIn, final Frame frameOut,
  505.                                                         final RealMatrix inputCartesianCov) {

  506.         // Output frame is pseudo-inertial
  507.         if (frameOut.isPseudoInertial()) {

  508.             // Builds the matrix to perform covariance transformation
  509.             final RealMatrix jacobianFromLofInToFrameOut =
  510.                     getJacobian(lofIn.transformFromInertial(date, orbit.getPVCoordinates(frameOut)).getInverse());

  511.             // Transform covariance
  512.             final RealMatrix transformedCovariance =
  513.                     jacobianFromLofInToFrameOut.multiply(inputCartesianCov.multiplyTransposed(jacobianFromLofInToFrameOut));

  514.             // Get the Cartesian covariance matrix converted to frameOut
  515.             return new StateCovariance(transformedCovariance, date, frameOut, OrbitType.CARTESIAN,
  516.                                        DEFAULT_POSITION_ANGLE);

  517.         }

  518.         // Output frame is not pseudo-inertial
  519.         else {

  520.             // Builds the matrix to perform covariance transformation
  521.             final RealMatrix jacobianFromLofInToOrbitFrame =
  522.                     getJacobian(lofIn.transformFromInertial(date, orbit.getPVCoordinates()).getInverse());

  523.             // Get the Cartesian covariance matrix converted to orbit inertial frame
  524.             final RealMatrix cartesianCovarianceInOrbitFrame = jacobianFromLofInToOrbitFrame.multiply(
  525.                     inputCartesianCov.multiplyTransposed(jacobianFromLofInToOrbitFrame));

  526.             // Get the Cartesian covariance matrix converted to frameOut
  527.             return changeFrameAndCreate(orbit, date, orbit.getFrame(), frameOut, cartesianCovarianceInOrbitFrame,
  528.                                         OrbitType.CARTESIAN, PositionAngleType.MEAN);
  529.         }

  530.     }

  531.     /**
  532.      * Get the covariance matrix in another frame.
  533.      * <p>
  534.      * The transformation is based on Equation (18) of "Covariance Transformations for Satellite Flight Dynamics
  535.      * Operations" by David A. Vallado.
  536.      * <p>
  537.      * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
  538.      * in covariance orbit type is required.
  539.      * <p>
  540.      * As the frame transformation must be performed with the covariance expressed in Cartesian elements, both the orbit
  541.      * and position angle types of the input covariance must be provided.
  542.      * <p>
  543.      * In case the <u>input</u> frame is <b>not</b> pseudo-inertial, the <u>input</u> covariance matrix is necessarily
  544.      * expressed in <b>Cartesian elements</b>.
  545.      * <p>
  546.      * In case the <u>output</u> frame is <b>not</b> pseudo-inertial, the <u>output</u> covariance matrix will be
  547.      * expressed in <b>Cartesian elements</b>.
  548.      *
  549.      * @param orbit orbit to which the covariance matrix should correspond
  550.      * @param date covariance epoch
  551.      * @param frameIn the frame in which the input covariance matrix is expressed
  552.      * @param frameOut the target frame
  553.      * @param inputCov input covariance
  554.      * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial)
  555.      * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (<b>not</b>
  556.      * used if covOrbitType equals {@code CARTESIAN})
  557.      * @return the covariance matrix expressed in the target frame
  558.      * @throws OrekitException if input frame is <b>not</b> pseudo-inertial <b>and</b> the input covariance is
  559.      * <b>not</b> expressed in Cartesian elements.
  560.      */
  561.     private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
  562.                                                         final Frame frameIn, final Frame frameOut,
  563.                                                         final RealMatrix inputCov,
  564.                                                         final OrbitType covOrbitType,
  565.                                                         final PositionAngleType covAngleType) {

  566.         // Input frame pseudo-inertial
  567.         if (frameIn.isPseudoInertial()) {

  568.             // Convert input matrix to Cartesian parameters in input frame
  569.             final RealMatrix cartesianCovarianceIn =
  570.                     changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType,
  571.                                         OrbitType.CARTESIAN, PositionAngleType.MEAN,
  572.                                         inputCov).getMatrix();

  573.             // Get the Cartesian covariance matrix converted to frameOut
  574.             final RealMatrix cartesianCovarianceOut = CartesianCovarianceUtils.changeReferenceFrame(frameIn,
  575.                     cartesianCovarianceIn, date, frameOut);

  576.             // Output frame is pseudo-inertial
  577.             if (frameOut.isPseudoInertial()) {

  578.                 // Convert output Cartesian matrix to initial orbit type and position angle
  579.                 return changeTypeAndCreate(orbit, date, frameOut, OrbitType.CARTESIAN, PositionAngleType.MEAN,
  580.                                            covOrbitType, covAngleType, cartesianCovarianceOut);

  581.             }

  582.             // Output frame is not pseudo-inertial
  583.             else {

  584.                 // Output Cartesian matrix
  585.                 return new StateCovariance(cartesianCovarianceOut, date, frameOut, OrbitType.CARTESIAN,
  586.                                            DEFAULT_POSITION_ANGLE);

  587.             }
  588.         }

  589.         // Input frame is not pseudo-inertial
  590.         else {

  591.             // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type

  592.             // Convert covariance matrix to frameOut
  593.             final RealMatrix covInFrameOut = CartesianCovarianceUtils.changeReferenceFrame(frameIn,
  594.                     inputCov, date, frameOut);

  595.             // Output the Cartesian covariance matrix converted to frameOut
  596.             return new StateCovariance(covInFrameOut, date, frameOut, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE);

  597.         }

  598.     }

  599.     /**
  600.      * Builds the matrix to perform covariance frame transformation.
  601.      * @param transform input transformation
  602.      * @return the matrix to perform the covariance frame transformation
  603.      */
  604.     private static RealMatrix getJacobian(final KinematicTransform transform) {
  605.         return MatrixUtils.createRealMatrix(transform.getPVJacobian());
  606.     }

  607.     /**
  608.      * Get the state transition matrix considering Keplerian contribution only.
  609.      *
  610.      * @param initialOrbit orbit to which the initial covariance matrix should correspond
  611.      * @param dt time difference between the two orbits
  612.      * @return the state transition matrix used to shift the covariance matrix
  613.      */
  614.     public static RealMatrix getStm(final Orbit initialOrbit, final double dt) {

  615.         // initialize the STM
  616.         final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION);

  617.         // State transition matrix using Keplerian contribution only
  618.         final double contribution = initialOrbit.getMeanAnomalyDotWrtA() * dt;
  619.         stm.setEntry(5, 0, contribution);

  620.         // Return
  621.         return stm;

  622.     }

  623. }