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.analysis.differentiation.Gradient;
20 import org.hipparchus.analysis.differentiation.GradientField;
21 import org.hipparchus.linear.Array2DRowRealMatrix;
22 import org.hipparchus.linear.MatrixUtils;
23 import org.hipparchus.linear.RealMatrix;
24 import org.hipparchus.util.MathArrays;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitMessages;
27 import org.orekit.frames.Frame;
28 import org.orekit.frames.KinematicTransform;
29 import org.orekit.frames.LOF;
30 import org.orekit.orbits.FieldOrbit;
31 import org.orekit.orbits.Orbit;
32 import org.orekit.orbits.OrbitType;
33 import org.orekit.orbits.PositionAngleType;
34 import org.orekit.time.AbsoluteDate;
35 import org.orekit.time.FieldAbsoluteDate;
36 import org.orekit.time.TimeStamped;
37 import org.orekit.utils.CartesianCovarianceUtils;
38
39 /** This class is the representation of a covariance matrix at a given date.
40 * <p>
41 * Currently, the covariance only represents the orbital elements.
42 * <p>
43 * It is possible to change the covariance frame by using the
44 * {@link #changeCovarianceFrame(Orbit, Frame)} or {@link #changeCovarianceFrame(Orbit, LOF)} method.
45 * These methods are based on Equations (18) and (20) of <i>Covariance Transformations for Satellite
46 * Flight Dynamics Operations</i> by David A. SVallado.
47 * <p>
48 * Finally, covariance orbit type can be changed using the
49 * {@link #changeCovarianceType(Orbit, OrbitType, PositionAngleType)} method.
50 *
51 * @author Bryan Cazabonne
52 * @author Vincent Cucchietti
53 * @since 11.3
54 */
55 public class StateCovariance implements TimeStamped {
56
57 /** State dimension. */
58 public static final int STATE_DIMENSION = 6;
59
60 /** Default position angle for covariance expressed in Cartesian elements. */
61 private static final PositionAngleType DEFAULT_POSITION_ANGLE = PositionAngleType.TRUE;
62
63 /** Orbital covariance [6x6]. */
64 private final RealMatrix orbitalCovariance;
65
66 /** Covariance frame (can be null if LOF is defined). */
67 private final Frame frame;
68
69 /** Covariance LOF type (can be null if frame is defined). */
70 private final LOF lof;
71
72 /** Covariance epoch. */
73 private final AbsoluteDate epoch;
74
75 /** Covariance orbit type. */
76 private final OrbitType orbitType;
77
78 /** Covariance position angle type (not used if orbitType is CARTESIAN). */
79 private final PositionAngleType angleType;
80
81 /**
82 * Constructor.
83 * @param orbitalCovariance 6x6 orbital parameters covariance
84 * @param epoch epoch of the covariance
85 * @param lof covariance LOF type
86 */
87 public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch, final LOF lof) {
88 this(orbitalCovariance, epoch, null, lof, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE);
89 }
90
91 /**
92 * Constructor.
93 * @param orbitalCovariance 6x6 orbital parameters covariance
94 * @param epoch epoch of the covariance
95 * @param covarianceFrame covariance frame (inertial or Earth fixed)
96 * @param orbitType orbit type of the covariance (CARTESIAN if covarianceFrame is not pseudo-inertial)
97 * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN)
98 */
99 public StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch,
100 final Frame covarianceFrame,
101 final OrbitType orbitType, final PositionAngleType angleType) {
102 this(orbitalCovariance, epoch, covarianceFrame, null, orbitType, angleType);
103 }
104
105 /**
106 * Private constructor.
107 * @param orbitalCovariance 6x6 orbital parameters covariance
108 * @param epoch epoch of the covariance
109 * @param covarianceFrame covariance frame (inertial or Earth fixed)
110 * @param lof covariance LOF type
111 * @param orbitType orbit type of the covariance
112 * @param angleType position angle type of the covariance (not used if orbitType is CARTESIAN)
113 */
114 private StateCovariance(final RealMatrix orbitalCovariance, final AbsoluteDate epoch,
115 final Frame covarianceFrame, final LOF lof,
116 final OrbitType orbitType, final PositionAngleType angleType) {
117
118 checkFrameAndOrbitTypeConsistency(covarianceFrame, orbitType);
119
120 this.orbitalCovariance = orbitalCovariance;
121 this.epoch = epoch;
122 this.frame = covarianceFrame;
123 this.lof = lof;
124 this.orbitType = orbitType;
125 this.angleType = angleType;
126
127 }
128
129 /**
130 * Check constructor's inputs consistency.
131 *
132 * @param covarianceFrame covariance frame (inertial or Earth fixed)
133 * @param inputType orbit type of the covariance
134 *
135 * @throws OrekitException if input frame is not pseudo-inertial AND the orbit type is not Cartesian
136 */
137 public static void checkFrameAndOrbitTypeConsistency(final Frame covarianceFrame, final OrbitType inputType) {
138
139 // State covariance expressed in a celestial body frame
140 if (covarianceFrame != null) {
141
142 // Input frame is not pseudo-inertial
143 if (!covarianceFrame.isPseudoInertial() && inputType != OrbitType.CARTESIAN) {
144 throw new OrekitException(OrekitMessages.WRONG_ORBIT_PARAMETERS_TYPE,
145 inputType.name(),
146 OrbitType.CARTESIAN.name());
147 }
148 }
149 }
150
151 /**
152 * Checks if input/output orbit and angle types are identical.
153 *
154 * @param inOrbitType input orbit type
155 * @param inAngleType input angle type
156 * @param outOrbitType output orbit type
157 * @param outAngleType output angle type
158 * @return flag defining if input/output orbit and angle types are identical
159 */
160 public static boolean inputAndOutputAreIdentical(final OrbitType inOrbitType, final PositionAngleType inAngleType,
161 final OrbitType outOrbitType, final PositionAngleType outAngleType) {
162 return inOrbitType == outOrbitType && inAngleType == outAngleType;
163 }
164
165 /**
166 * Checks if input and output orbit types are both {@code OrbitType.CARTESIAN}.
167 *
168 * @param inOrbitType input orbit type
169 * @param outOrbitType output orbit type
170 * @return flag defining if input and output orbit types are both {@code OrbitType.CARTESIAN}
171 */
172 public static boolean inputAndOutputOrbitTypesAreCartesian(final OrbitType inOrbitType, final OrbitType outOrbitType) {
173 return inOrbitType == OrbitType.CARTESIAN && outOrbitType == OrbitType.CARTESIAN;
174 }
175
176 /** {@inheritDoc}. */
177 @Override
178 public AbsoluteDate getDate() {
179 return epoch;
180 }
181
182 /**
183 * Get the covariance matrix.
184 * @return the covariance matrix
185 */
186 public RealMatrix getMatrix() {
187 return orbitalCovariance;
188 }
189
190 /**
191 * Get the covariance orbit type.
192 * @return the covariance orbit type
193 */
194 public OrbitType getOrbitType() {
195 return orbitType;
196 }
197
198 /**
199 * Get the covariance angle type.
200 * @return the covariance angle type
201 */
202 public PositionAngleType getPositionAngleType() {
203 return angleType;
204 }
205
206 /**
207 * Get the covariance frame.
208 * @return the covariance frame (can be null)
209 * @see #getLOF()
210 */
211 public Frame getFrame() {
212 return frame;
213 }
214
215 /**
216 * Get the covariance LOF type.
217 * @return the covariance LOF type (can be null)
218 * @see #getFrame()
219 */
220 public LOF getLOF() {
221 return lof;
222 }
223
224 /**
225 * Get the covariance matrix in another orbit type.
226 * <p>
227 * The covariance orbit type <b>cannot</b> be changed if the covariance
228 * matrix is expressed in a {@link LOF local orbital frame} or a
229 * non-pseudo inertial frame.
230 * <p>
231 * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation.
232 * Hence, the current covariance matrix <b>will not exactly match</b> the new linearized case and the
233 * distribution will not follow a generalized Gaussian distribution anymore.
234 * <p>
235 * This is based on equation (1) to (6) from "Vallado, D. A. (2004). Covariance transformations for satellite flight
236 * dynamics operations."
237 *
238 * @param orbit orbit to which the covariance matrix should correspond
239 * @param outOrbitType target orbit type of the state covariance matrix
240 * @param outAngleType target position angle type of the state covariance matrix
241 * @return a new covariance state, expressed in the target orbit type with the target position angle
242 * @see #changeCovarianceFrame(Orbit, Frame)
243 */
244 public StateCovariance changeCovarianceType(final Orbit orbit, final OrbitType outOrbitType,
245 final PositionAngleType outAngleType) {
246
247 // Handle case where the covariance is already expressed in the output type
248 if (outOrbitType == orbitType && (outOrbitType == OrbitType.CARTESIAN || outAngleType == angleType)) {
249 if (lof == null) {
250 return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType);
251 }
252 else {
253 return new StateCovariance(orbitalCovariance, epoch, lof);
254 }
255 }
256
257 // Check if the covariance is expressed in a celestial body frame
258 if (frame != null) {
259
260 // Check if the covariance is defined in an inertial frame
261 if (frame.isPseudoInertial()) {
262 return changeTypeAndCreate(orbit, epoch, frame, orbitType, angleType, outOrbitType, outAngleType,
263 orbitalCovariance);
264 }
265
266 // The covariance is not defined in an inertial frame. The orbit type cannot be changed
267 throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME);
268
269 }
270
271 // The covariance is not expressed in a celestial body frame. The orbit type cannot be changed
272 throw new OrekitException(OrekitMessages.CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF);
273
274 }
275
276 /**
277 * Get the covariance in a given local orbital frame.
278 * <p>
279 * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
280 * in covariance orbit type is required.
281 * <p>
282 * This is based on equation (18) to (20) "from Vallado, D. A. (2004). Covariance transformations for satellite
283 * flight dynamics operations."
284 *
285 * @param orbit orbit to which the covariance matrix should correspond
286 * @param lofOut output local orbital frame
287 * @return a new covariance state, expressed in the output local orbital frame
288 */
289 public StateCovariance changeCovarianceFrame(final Orbit orbit, final LOF lofOut) {
290
291 // Verify current covariance frame
292 if (lof != null) {
293
294 // Check specific case where output covariance will be the same
295 if (lofOut == lof) {
296 return new StateCovariance(orbitalCovariance, epoch, lof);
297 }
298
299 // Change the covariance local orbital frame
300 return changeFrameAndCreate(orbit, epoch, lof, lofOut, orbitalCovariance);
301
302 } else {
303
304 // Covariance is expressed in celestial body frame
305 return changeFrameAndCreate(orbit, epoch, frame, lofOut, orbitalCovariance, orbitType, angleType);
306
307 }
308
309 }
310
311 /**
312 * Get the covariance in the output frame.
313 * <p>
314 * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
315 * in covariance orbit type is required.
316 * <p>
317 * This is based on equation (18) to (20) "from Vallado, D. A. (2004). Covariance transformations for satellite
318 * flight dynamics operations."
319 *
320 * @param orbit orbit to which the covariance matrix should correspond
321 * @param frameOut output frame
322 * @return a new covariance state, expressed in the output frame
323 */
324 public StateCovariance changeCovarianceFrame(final Orbit orbit, final Frame frameOut) {
325
326 // Verify current covariance frame
327 if (lof != null) {
328
329 // Covariance is expressed in local orbital frame
330 return changeFrameAndCreate(orbit, epoch, lof, frameOut, orbitalCovariance);
331
332 } else {
333
334 // Check specific case where output covariance will be the same
335 if (frame == frameOut) {
336 return new StateCovariance(orbitalCovariance, epoch, frame, orbitType, angleType);
337 }
338
339 // Change covariance frame
340 return changeFrameAndCreate(orbit, epoch, frame, frameOut, orbitalCovariance, orbitType, angleType);
341
342 }
343
344 }
345
346 /**
347 * Get a time-shifted covariance matrix.
348 * <p>
349 * The shifting model is a linearized, Keplerian one. In other words, it is based on a state transition matrix that
350 * is computed assuming Keplerian motion.
351 * <p>
352 * Shifting is <em>not</em> intended as a replacement for proper covariance propagation, but should be sufficient
353 * for small time shifts or coarse accuracy.
354 *
355 * @param orbit orbit to which the covariance matrix should correspond
356 * @param dt time shift in seconds
357 * @return a new covariance state, shifted with respect to the instance
358 */
359 public StateCovariance shiftedBy(final Orbit orbit, final double dt) {
360
361 // Shifted orbit
362 final Orbit shifted = orbit.shiftedBy(dt);
363
364 // State covariance expressed in celestial body frame
365 if (frame != null) {
366
367 // State covariance expressed in a pseudo-inertial frame
368 if (frame.isPseudoInertial()) {
369
370 // Shift covariance by applying the STM
371 final RealMatrix stm = getKeplerianStm(orbit, dt);
372 return new StateCovariance(stm.multiply(getMatrix().multiplyTransposed(stm)), getDate().shiftedBy(dt),
373 frame, orbitType, getPositionAngleType());
374
375 }
376
377 // State covariance expressed in a non pseudo-inertial frame
378 else {
379
380 // Convert state covariance to orbit pseudo-inertial frame
381 final StateCovariance inOrbitFrame = changeCovarianceFrame(orbit, orbit.getFrame());
382
383 // Shift the state covariance
384 final StateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(orbit, dt);
385
386 // Restore the initial covariance frame
387 return shiftedCovariance.changeCovarianceFrame(shifted, frame);
388 }
389 }
390
391 // State covariance expressed in a commonly used local orbital frame (LOF)
392 else {
393
394 // Convert state covariance to orbit pseudo-inertial frame
395 final StateCovariance inOrbitFrame = changeCovarianceFrame(orbit, orbit.getFrame());
396
397 // Shift the state covariance
398 final StateCovariance shiftedCovariance = inOrbitFrame.shiftedBy(orbit, dt);
399
400 // Restore the initial covariance frame
401 return shiftedCovariance.changeCovarianceFrame(shifted, lof);
402 }
403
404 }
405
406 /**
407 * Create a covariance matrix in another orbit type.
408 * <p>
409 * As this type change uses the jacobian matrix of the transformation, it introduces a linear approximation.
410 * Hence, the input covariance matrix <b>will not exactly match</b> the new linearized case and the
411 * distribution will not follow a generalized Gaussian distribution anymore.
412 * <p>
413 * This is based on equation (1) to (6) from "Vallado, D. A. (2004). Covariance transformations for satellite flight
414 * dynamics operations."
415 *
416 * @param orbit orbit to which the covariance matrix should correspond
417 * @param date covariance epoch
418 * @param covFrame covariance frame
419 * @param inOrbitType initial orbit type of the state covariance matrix
420 * @param inAngleType initial position angle type of the state covariance matrix
421 * @param outOrbitType target orbit type of the state covariance matrix
422 * @param outAngleType target position angle type of the state covariance matrix
423 * @param inputCov input covariance
424 * @return the covariance expressed in the target orbit type with the target position angle
425 */
426 private static StateCovariance changeTypeAndCreate(final Orbit orbit,
427 final AbsoluteDate date,
428 final Frame covFrame,
429 final OrbitType inOrbitType, final PositionAngleType inAngleType,
430 final OrbitType outOrbitType, final PositionAngleType outAngleType,
431 final RealMatrix inputCov) {
432
433 // Check if type change is really necessary, if not then return input covariance
434 if (inputAndOutputOrbitTypesAreCartesian(inOrbitType, outOrbitType) ||
435 inputAndOutputAreIdentical(inOrbitType, inAngleType, outOrbitType, outAngleType)) {
436 return new StateCovariance(inputCov, date, covFrame, inOrbitType, inAngleType);
437 }
438
439 // Notations:
440 // I: Input orbit type
441 // O: Output orbit type
442 // C: Cartesian parameters
443
444 // Compute dOutputdCartesian
445 final double[][] aOC = new double[STATE_DIMENSION][STATE_DIMENSION];
446 final Orbit orbitInOutputType = outOrbitType.convertType(orbit);
447 orbitInOutputType.getJacobianWrtCartesian(outAngleType, aOC);
448 final RealMatrix dOdC = new Array2DRowRealMatrix(aOC, false);
449
450 // Compute dCartesiandInput
451 final double[][] aCI = new double[STATE_DIMENSION][STATE_DIMENSION];
452 final Orbit orbitInInputType = inOrbitType.convertType(orbit);
453 orbitInInputType.getJacobianWrtParameters(inAngleType, aCI);
454 final RealMatrix dCdI = new Array2DRowRealMatrix(aCI, false);
455
456 // Compute dOutputdInput
457 final RealMatrix dOdI = dOdC.multiply(dCdI);
458
459 // Conversion of the state covariance matrix in target orbit type
460 final RealMatrix outputCov = dOdI.multiply(inputCov.multiplyTransposed(dOdI));
461
462 // Return the converted covariance
463 return new StateCovariance(outputCov, date, covFrame, outOrbitType, outAngleType);
464
465 }
466
467 /**
468 * Create a covariance matrix from a {@link LOF local orbital frame} to another
469 * {@link LOF local orbital frame}.
470 * <p>
471 * Changing the covariance frame is a linear process, this method does not introduce approximation.
472 * <p>
473 * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics
474 * Operations" by David A. Vallado.
475 *
476 * @param orbit orbit to which the covariance matrix should correspond
477 * @param date covariance epoch
478 * @param lofIn the local orbital frame in which the input covariance matrix is expressed
479 * @param lofOut the target local orbital frame
480 * @param inputCartesianCov input covariance {@code CARTESIAN})
481 * @return the covariance matrix expressed in the target commonly used local orbital frame in Cartesian elements
482 */
483 private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
484 final LOF lofIn, final LOF lofOut,
485 final RealMatrix inputCartesianCov) {
486
487 // Builds the matrix to perform covariance transformation
488 final RealMatrix jacobianFromLofInToLofOut =
489 getJacobian(LOF.transformFromLOFInToLOFOut(lofIn, lofOut, date, orbit.getPVCoordinates()));
490
491 // Get the Cartesian covariance matrix converted to frameOut
492 final RealMatrix cartesianCovarianceOut =
493 jacobianFromLofInToLofOut.multiply(inputCartesianCov.multiplyTransposed(jacobianFromLofInToLofOut));
494
495 // Output converted covariance
496 return new StateCovariance(cartesianCovarianceOut, date, lofOut);
497
498 }
499
500 /**
501 * Convert the covariance matrix from a {@link Frame frame} to a {@link LOF local orbital frame}.
502 * <p>
503 * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
504 * in covariance orbit type is required.
505 * <p>
506 * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics
507 * Operations" by David A. Vallado.
508 * <p>
509 * As the frame transformation must be performed with the covariance expressed in Cartesian elements, both the orbit
510 * and position angle types of the input covariance must be provided.
511 * <p>
512 * <b>The output covariance matrix will provided in Cartesian orbit type and not converted back to
513 * its original expression (if input different from Cartesian elements).</b>
514 *
515 * @param orbit orbit to which the covariance matrix should correspond
516 * @param date covariance epoch
517 * @param frameIn the frame in which the input covariance matrix is expressed. In case the frame is <b>not</b>
518 * pseudo-inertial, the input covariance matrix is expected to be expressed in <b>Cartesian elements</b>.
519 * @param lofOut the target local orbital frame
520 * @param inputCov input covariance
521 * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial)
522 * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (not used
523 * if covOrbitType equals {@code CARTESIAN})
524 * @return the covariance matrix expressed in the target local orbital frame in Cartesian elements
525 * @throws OrekitException if input frame is <b>not</b> pseudo-inertial <b>and</b> the input covariance is
526 * <b>not</b> expressed in Cartesian elements.
527 */
528 private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
529 final Frame frameIn, final LOF lofOut,
530 final RealMatrix inputCov,
531 final OrbitType covOrbitType,
532 final PositionAngleType covAngleType) {
533
534 // Input frame is inertial
535 if (frameIn.isPseudoInertial()) {
536
537 // Convert input matrix to Cartesian parameters in input frame
538 final RealMatrix cartesianCovarianceIn =
539 changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType,
540 OrbitType.CARTESIAN, PositionAngleType.MEAN,
541 inputCov).getMatrix();
542
543 // Builds the matrix to perform covariance transformation
544 final RealMatrix jacobianFromFrameInToLofOut =
545 getJacobian(lofOut.transformFromInertial(date, orbit.getPVCoordinates(frameIn)));
546
547 // Get the Cartesian covariance matrix converted to frameOut
548 final RealMatrix cartesianCovarianceOut =
549 jacobianFromFrameInToLofOut.multiply(cartesianCovarianceIn.multiplyTransposed(jacobianFromFrameInToLofOut));
550
551 // Return converted covariance matrix expressed in Cartesian elements
552 return new StateCovariance(cartesianCovarianceOut, date, lofOut);
553
554 }
555
556 // Input frame is not inertial so the covariance matrix is expected to be in Cartesian elements
557 else {
558
559 // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type
560 final Frame orbitInertialFrame = orbit.getFrame();
561
562 // Compute rotation matrix from frameIn to orbit inertial frame
563 final RealMatrix cartesianCovarianceInOrbitFrame =
564 changeFrameAndCreate(orbit, date, frameIn, orbitInertialFrame, inputCov,
565 OrbitType.CARTESIAN, PositionAngleType.MEAN).getMatrix();
566
567 // Convert from orbit inertial frame to lofOut
568 return changeFrameAndCreate(orbit, date, orbitInertialFrame, lofOut, cartesianCovarianceInOrbitFrame,
569 OrbitType.CARTESIAN, PositionAngleType.MEAN);
570
571 }
572
573 }
574
575 /**
576 * Convert the covariance matrix from a {@link LOF local orbital frame} to a {@link Frame frame}.
577 * <p>
578 * Changing the covariance frame is a linear process, this method does not introduce approximation.
579 * <p>
580 * The transformation is based on Equation (18) to (20) of "Covariance Transformations for Satellite Flight Dynamics
581 * Operations" by David A. Vallado.
582 * <p>
583 * The <u>input</u> covariance matrix is necessarily provided in <b>Cartesian orbit type</b>.
584 * <p>
585 * The <u>output</u> covariance matrix will be expressed in <b>Cartesian elements</b>.
586 *
587 * @param orbit orbit to which the covariance matrix should correspond
588 * @param date covariance epoch
589 * @param lofIn the local orbital frame in which the input covariance matrix is expressed
590 * @param frameOut the target frame
591 * @param inputCartesianCov input covariance ({@code CARTESIAN})
592 * @return the covariance matrix expressed in the target frame in Cartesian elements
593 */
594 private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
595 final LOF lofIn, final Frame frameOut,
596 final RealMatrix inputCartesianCov) {
597
598 // Output frame is pseudo-inertial
599 if (frameOut.isPseudoInertial()) {
600
601 // Builds the matrix to perform covariance transformation
602 final RealMatrix jacobianFromLofInToFrameOut =
603 getJacobian(lofIn.transformFromInertial(date, orbit.getPVCoordinates(frameOut)).getInverse());
604
605 // Transform covariance
606 final RealMatrix transformedCovariance =
607 jacobianFromLofInToFrameOut.multiply(inputCartesianCov.multiplyTransposed(jacobianFromLofInToFrameOut));
608
609 // Get the Cartesian covariance matrix converted to frameOut
610 return new StateCovariance(transformedCovariance, date, frameOut, OrbitType.CARTESIAN,
611 DEFAULT_POSITION_ANGLE);
612
613 }
614
615 // Output frame is not pseudo-inertial
616 else {
617
618 // Builds the matrix to perform covariance transformation
619 final RealMatrix jacobianFromLofInToOrbitFrame =
620 getJacobian(lofIn.transformFromInertial(date, orbit.getPVCoordinates()).getInverse());
621
622 // Get the Cartesian covariance matrix converted to orbit inertial frame
623 final RealMatrix cartesianCovarianceInOrbitFrame = jacobianFromLofInToOrbitFrame.multiply(
624 inputCartesianCov.multiplyTransposed(jacobianFromLofInToOrbitFrame));
625
626 // Get the Cartesian covariance matrix converted to frameOut
627 return changeFrameAndCreate(orbit, date, orbit.getFrame(), frameOut, cartesianCovarianceInOrbitFrame,
628 OrbitType.CARTESIAN, PositionAngleType.MEAN);
629 }
630
631 }
632
633 /**
634 * Get the covariance matrix in another frame.
635 * <p>
636 * The transformation is based on Equation (18) of "Covariance Transformations for Satellite Flight Dynamics
637 * Operations" by David A. Vallado.
638 * <p>
639 * Changing the covariance frame is a linear process, this method does not introduce approximation unless a change
640 * in covariance orbit type is required.
641 * <p>
642 * As the frame transformation must be performed with the covariance expressed in Cartesian elements, both the orbit
643 * and position angle types of the input covariance must be provided.
644 * <p>
645 * In case the <u>input</u> frame is <b>not</b> pseudo-inertial, the <u>input</u> covariance matrix is necessarily
646 * expressed in <b>Cartesian elements</b>.
647 * <p>
648 * In case the <u>output</u> frame is <b>not</b> pseudo-inertial, the <u>output</u> covariance matrix will be
649 * expressed in <b>Cartesian elements</b>.
650 *
651 * @param orbit orbit to which the covariance matrix should correspond
652 * @param date covariance epoch
653 * @param frameIn the frame in which the input covariance matrix is expressed
654 * @param frameOut the target frame
655 * @param inputCov input covariance
656 * @param covOrbitType orbit type of the covariance matrix (used if frameIn is pseudo-inertial)
657 * @param covAngleType position angle type of the covariance matrix (used if frameIn is pseudo-inertial) (<b>not</b>
658 * used if covOrbitType equals {@code CARTESIAN})
659 * @return the covariance matrix expressed in the target frame
660 * @throws OrekitException if input frame is <b>not</b> pseudo-inertial <b>and</b> the input covariance is
661 * <b>not</b> expressed in Cartesian elements.
662 */
663 private static StateCovariance changeFrameAndCreate(final Orbit orbit, final AbsoluteDate date,
664 final Frame frameIn, final Frame frameOut,
665 final RealMatrix inputCov,
666 final OrbitType covOrbitType,
667 final PositionAngleType covAngleType) {
668
669 // Input frame pseudo-inertial
670 if (frameIn.isPseudoInertial()) {
671
672 // Convert input matrix to Cartesian parameters in input frame
673 final RealMatrix cartesianCovarianceIn =
674 changeTypeAndCreate(orbit, date, frameIn, covOrbitType, covAngleType,
675 OrbitType.CARTESIAN, PositionAngleType.MEAN,
676 inputCov).getMatrix();
677
678 // Get the Cartesian covariance matrix converted to frameOut
679 final RealMatrix cartesianCovarianceOut = CartesianCovarianceUtils.changeReferenceFrame(frameIn,
680 cartesianCovarianceIn, date, frameOut);
681
682 // Output frame is pseudo-inertial
683 if (frameOut.isPseudoInertial()) {
684
685 // Convert output Cartesian matrix to initial orbit type and position angle
686 return changeTypeAndCreate(orbit, date, frameOut, OrbitType.CARTESIAN, PositionAngleType.MEAN,
687 covOrbitType, covAngleType, cartesianCovarianceOut);
688
689 }
690
691 // Output frame is not pseudo-inertial
692 else {
693
694 // Output Cartesian matrix
695 return new StateCovariance(cartesianCovarianceOut, date, frameOut, OrbitType.CARTESIAN,
696 DEFAULT_POSITION_ANGLE);
697
698 }
699 }
700
701 // Input frame is not pseudo-inertial
702 else {
703
704 // Method checkInputConsistency already verify that input covariance is defined in CARTESIAN type
705
706 // Convert covariance matrix to frameOut
707 final RealMatrix covInFrameOut = CartesianCovarianceUtils.changeReferenceFrame(frameIn,
708 inputCov, date, frameOut);
709
710 // Output the Cartesian covariance matrix converted to frameOut
711 return new StateCovariance(covInFrameOut, date, frameOut, OrbitType.CARTESIAN, DEFAULT_POSITION_ANGLE);
712
713 }
714
715 }
716
717 /**
718 * Builds the matrix to perform covariance frame transformation.
719 * @param transform input transformation
720 * @return the matrix to perform the covariance frame transformation
721 */
722 private static RealMatrix getJacobian(final KinematicTransform transform) {
723 return MatrixUtils.createRealMatrix(transform.getPVJacobian());
724 }
725
726 /**
727 * Get the state transition matrix considering Keplerian contribution only and assuming equinoctial elements with mean anomaly.
728 *
729 * @param initialOrbit orbit to which the initial covariance matrix should correspond
730 * @param dt time difference between the two orbits
731 * @return the state transition matrix used to shift the covariance matrix
732 * @deprecated since 13.1. If you must, do:
733 * final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION);
734 * double contribution = initialOrbit.getMeanAnomalyDotWrtA() * dt;
735 * stm.setEntry(5, 0, contribution);
736 */
737 @Deprecated
738 public static RealMatrix getStm(final Orbit initialOrbit, final double dt) {
739 // initialize the STM
740 final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION);
741
742 // State transition matrix using Keplerian contribution only
743 final double contribution = initialOrbit.getMeanAnomalyDotWrtA() * dt;
744 stm.setEntry(5, 0, contribution);
745
746 // Return
747 return stm;
748 }
749
750 /**
751 * Get the state transition matrix of Keplerian motion.
752 * The coordinates used are those of the covariance itself.
753 *
754 * @param initialOrbit orbit to which the initial covariance matrix should correspond
755 * @param dt time difference between the two orbits
756 * @return the state transition matrix used to shift the covariance matrix
757 * @since 13.1
758 */
759 RealMatrix getKeplerianStm(final Orbit initialOrbit, final double dt) {
760
761 // compute derivatives of Keplerian motion
762 final GradientField field = GradientField.getField(STATE_DIMENSION);
763 final double[] stateVector = new double[STATE_DIMENSION];
764 orbitType.mapOrbitToArray(initialOrbit, getPositionAngleType(), stateVector, null);
765 final Gradient[] fieldGradientStateVector = MathArrays.buildArray(field, STATE_DIMENSION);
766 for (int i = 0; i < STATE_DIMENSION; i++) {
767 fieldGradientStateVector[i] = Gradient.variable(STATE_DIMENSION, i, stateVector[i]);
768 }
769 final FieldAbsoluteDate<Gradient> fieldGradientDate = new FieldAbsoluteDate<>(field, initialOrbit.getDate());
770 final FieldOrbit<Gradient> fieldOrbit = orbitType.mapArrayToOrbit(fieldGradientStateVector, null, getPositionAngleType(),
771 fieldGradientDate, field.getOne().newInstance(initialOrbit.getMu()), initialOrbit.getFrame());
772 final FieldOrbit<Gradient> shiftedOrbit = fieldOrbit.shiftedBy(dt); // automatic differentiation
773 final Gradient[] gradient = MathArrays.buildArray(field, STATE_DIMENSION);
774 orbitType.mapOrbitToArray(shiftedOrbit, getPositionAngleType(), gradient, null);
775
776 // Return state transition matrix
777 final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(STATE_DIMENSION);
778 for (int i = 0; i < gradient.length; i++) {
779 stm.setRow(i, gradient[i].getGradient());
780 }
781 return stm;
782
783 }
784
785 }