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