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  
19  import org.hipparchus.linear.RealMatrix;
20  import org.orekit.frames.Frame;
21  import org.orekit.orbits.Orbit;
22  import org.orekit.orbits.OrbitType;
23  import org.orekit.orbits.PositionAngleType;
24  import org.orekit.time.AbsoluteDate;
25  
26  /**
27   * Additional state provider for state covariance matrix.
28   * <p>
29   * This additional state provider allows computing a propagated covariance matrix based on a user defined input state
30   * covariance matrix. The computation of the propagated covariance matrix uses the State Transition Matrix between the
31   * propagated spacecraft state and the initial state. As a result, the user must define the name
32   * {@link #stmName of the provider for the State Transition Matrix}.
33   * <p>
34   * As the State Transition Matrix and the input state covariance matrix can be expressed in different orbit types, the
35   * user must specify both orbit types when building the covariance provider. In addition, the position angle used in
36   * both matrices must also be specified.
37   * <p>
38   * In order to add this additional state provider to an orbit propagator, user must use the
39   * {@link Propagator#addAdditionalDataProvider(AdditionalDataProvider)} method.
40   * <p>
41   * For a given propagated spacecraft {@code state}, the propagated state covariance matrix is accessible through the
42   * method {@link #getStateCovariance(SpacecraftState)}
43   *
44   * @author Bryan Cazabonne
45   * @author Vincent Cucchietti
46   * @since 11.3
47   */
48  public class StateCovarianceMatrixProvider implements AdditionalDataProvider<RealMatrix> {
49  
50      /** Dimension of the state. */
51      private static final int STATE_DIMENSION = 6;
52  
53      /** Name of the state for State Transition Matrix. */
54      private final String stmName;
55  
56      /** Matrix harvester to access the State Transition Matrix. */
57      private final MatricesHarvester harvester;
58  
59      /** Name of the additional state. */
60      private final String additionalName;
61  
62      /** Orbit type used for the State Transition Matrix. */
63      private final OrbitType stmOrbitType;
64  
65      /** Position angle used for State Transition Matrix. */
66      private final PositionAngleType stmAngleType;
67  
68      /** Orbit type for the covariance matrix. */
69      private final OrbitType covOrbitType;
70  
71      /** Position angle used for the covariance matrix. */
72      private final PositionAngleType covAngleType;
73  
74      /** Initial state covariance. */
75      private StateCovariance covInit;
76  
77      /** Initial state covariance matrix. */
78      private RealMatrix covMatrixInit;
79  
80      /**
81       * Constructor.
82       *
83       * @param additionalName name of the additional state
84       * @param stmName name of the state for State Transition Matrix
85       * @param harvester matrix harvester as returned by
86       * {@code propagator.setupMatricesComputation(stmName, null, null)}
87       * @param covInit initial state covariance
88       */
89      public StateCovarianceMatrixProvider(final String additionalName, final String stmName,
90                                           final MatricesHarvester harvester, final StateCovariance covInit) {
91          // Initialize fields
92          this.additionalName = additionalName;
93          this.stmName = stmName;
94          this.harvester = harvester;
95          this.covInit = covInit;
96          this.covOrbitType = covInit.getOrbitType();
97          this.covAngleType = covInit.getPositionAngleType();
98          this.stmOrbitType = harvester.getOrbitType();
99          this.stmAngleType = harvester.getPositionAngleType();
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     public String getName() {
105         return additionalName;
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
111         // Convert the initial state covariance in the same orbit type as the STM
112         covInit = covInit.changeCovarianceType(initialState.getOrbit(), stmOrbitType, stmAngleType);
113 
114         // Express covariance matrix in the same frame as the STM
115         final Orbit           initialOrbit      = initialState.getOrbit();
116         final StateCovariance covInitInSTMFrame = covInit.changeCovarianceFrame(initialOrbit, initialOrbit.getFrame());
117 
118         covMatrixInit = covInitInSTMFrame.getMatrix();
119     }
120 
121     /**
122      * {@inheritDoc}
123      * <p>
124      * The covariance matrix can be computed only if the State Transition Matrix state is available.
125      * </p>
126      */
127     @Override
128     public boolean yields(final SpacecraftState state) {
129         return !state.hasAdditionalData(stmName);
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public RealMatrix getAdditionalData(final SpacecraftState state) {
135 
136         // State transition matrix for the input state
137         final RealMatrix dYdY0 = harvester.getStateTransitionMatrix(state);
138 
139         // Compute the propagated covariance matrix
140         RealMatrix propCov = dYdY0.multiply(covMatrixInit.multiplyTransposed(dYdY0));
141         final StateCovariance propagated = new StateCovariance(propCov, state.getDate(), state.getFrame(), stmOrbitType, stmAngleType);
142 
143         // Update to the user defined type
144         propCov = propagated.changeCovarianceType(state.getOrbit(), covOrbitType, covAngleType).getMatrix();
145 
146         // Return the propagated covariance matrix
147         return propCov;
148 
149     }
150 
151     /**
152      * Get the orbit type in which the covariance matrix is expressed.
153      *
154      * @return the orbit type
155      */
156     public OrbitType getCovarianceOrbitType() {
157         return covOrbitType;
158     }
159 
160     /**
161      * Get the state covariance in the same frame/local orbital frame, orbit type and position angle as the initial
162      * covariance.
163      *
164      * @param state spacecraft state to which the covariance matrix should correspond
165      * @return the state covariance
166      * @see #getStateCovariance(SpacecraftState, Frame)
167      * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngleType)
168      */
169     public StateCovariance getStateCovariance(final SpacecraftState state) {
170 
171         // Get the current propagated covariance
172         final RealMatrix covarianceMatrix = getAdditionalData(state);
173 
174         // Create associated state covariance
175         final StateCovariance covariance =
176                 new StateCovariance(covarianceMatrix, state.getDate(), state.getFrame(), covOrbitType, covAngleType);
177 
178         // Return the state covariance in same frame/lof as initial covariance
179         if (covInit.getLOF() == null) {
180             return covariance;
181         }
182         else {
183             return covariance.changeCovarianceFrame(state.getOrbit(), covInit.getLOF());
184         }
185 
186     }
187 
188     /**
189      * Get the state covariance expressed in a given frame.
190      * <p>
191      * The output covariance matrix is expressed in the same orbit type as {@link #getCovarianceOrbitType()}.
192      *
193      * @param state spacecraft state to which the covariance matrix should correspond
194      * @param frame output frame for which the output covariance matrix must be expressed (must be inertial)
195      * @return the state covariance expressed in <code>frame</code>
196      * @see #getStateCovariance(SpacecraftState)
197      * @see #getStateCovariance(SpacecraftState, OrbitType, PositionAngleType)
198      */
199     public StateCovariance getStateCovariance(final SpacecraftState state, final Frame frame) {
200         // Return the converted covariance
201         return getStateCovariance(state).changeCovarianceFrame(state.getOrbit(), frame);
202     }
203 
204     /**
205      * Get the state covariance expressed in a given orbit type.
206      *
207      * @param state spacecraft state to which the covariance matrix should correspond
208      * @param orbitType output orbit type
209      * @param angleType output position angle (not used if orbitType equals {@code CARTESIAN})
210      * @return the state covariance in <code>orbitType</code> and <code>angleType</code>
211      * @see #getStateCovariance(SpacecraftState)
212      * @see #getStateCovariance(SpacecraftState, Frame)
213      */
214     public StateCovariance getStateCovariance(final SpacecraftState state, final OrbitType orbitType,
215                                               final PositionAngleType angleType) {
216         // Return the converted covariance
217         return getStateCovariance(state).changeCovarianceType(state.getOrbit(), orbitType, angleType);
218     }
219 }