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.orbits;
18
19 import org.hipparchus.geometry.euclidean.threed.Vector3D;
20 import org.hipparchus.linear.DecompositionSolver;
21 import org.hipparchus.linear.MatrixUtils;
22 import org.hipparchus.linear.QRDecomposition;
23 import org.hipparchus.linear.RealMatrix;
24 import org.hipparchus.linear.RealVector;
25 import org.hipparchus.util.FastMath;
26 import org.hipparchus.util.MathArrays;
27 import org.orekit.errors.OrekitIllegalArgumentException;
28 import org.orekit.errors.OrekitMessages;
29 import org.orekit.frames.Frame;
30 import org.orekit.time.AbsoluteDate;
31 import org.orekit.time.TimeOffset;
32 import org.orekit.utils.PVCoordinates;
33 import org.orekit.utils.ShiftablePVCoordinatesHolder;
34 import org.orekit.utils.TimeStampedPVCoordinates;
35
36 /**
37 * This class handles orbital parameters.
38
39 * <p>
40 * For user convenience, both the Cartesian and the equinoctial elements
41 * are provided by this class, regardless of the canonical representation
42 * implemented in the derived class (which may be classical Keplerian
43 * elements for example).
44 * </p>
45 * <p>
46 * The parameters are defined in a frame specified by the user. It is important
47 * to make sure this frame is consistent: it probably is inertial and centered
48 * on the central body. This information is used for example by some
49 * force models.
50 * </p>
51 * <p>
52 * Instance of this class are guaranteed to be immutable.
53 * </p>
54 * @author Luc Maisonobe
55 * @author Guylaine Prat
56 * @author Fabien Maussion
57 * @author Véronique Pommier-Maurussane
58 */
59 public abstract class Orbit
60 implements ShiftablePVCoordinatesHolder<Orbit>, OrbitalParameters {
61
62 /** Absolute tolerance when checking if the rate of the position angle is Keplerian or not. */
63 protected static final double TOLERANCE_POSITION_ANGLE_RATE = 1e-15;
64
65 /** Frame in which are defined the orbital parameters. */
66 private final Frame frame;
67
68 /** Date of the orbital parameters. */
69 private final AbsoluteDate date;
70
71 /** Value of mu used to compute position and velocity (m³/s²). */
72 private final double mu;
73
74 /** Computed position.
75 * @since 12.0
76 */
77 private Vector3D position;
78
79 /** Computed PVCoordinates. */
80 private TimeStampedPVCoordinates pvCoordinates;
81
82 /** Jacobian of the orbital parameters with mean angle with respect to the Cartesian coordinates. */
83 private double[][] jacobianMeanWrtCartesian;
84
85 /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with mean angle. */
86 private double[][] jacobianWrtParametersMean;
87
88 /** Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian coordinates. */
89 private double[][] jacobianEccentricWrtCartesian;
90
91 /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with eccentric angle. */
92 private double[][] jacobianWrtParametersEccentric;
93
94 /** Jacobian of the orbital parameters with true angle with respect to the Cartesian coordinates. */
95 private double[][] jacobianTrueWrtCartesian;
96
97 /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with true angle. */
98 private double[][] jacobianWrtParametersTrue;
99
100 /** Default constructor.
101 * Build a new instance with arbitrary default elements.
102 * @param frame the frame in which the parameters are defined
103 * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
104 * @param date date of the orbital parameters
105 * @param mu central attraction coefficient (m^3/s^2)
106 * @exception IllegalArgumentException if frame is not a {@link
107 * Frame#isPseudoInertial pseudo-inertial frame}
108 */
109 protected Orbit(final Frame frame, final AbsoluteDate date, final double mu)
110 throws IllegalArgumentException {
111 ensurePseudoInertialFrame(frame);
112 this.date = date;
113 this.mu = mu;
114 this.pvCoordinates = null;
115 this.frame = frame;
116 jacobianMeanWrtCartesian = null;
117 jacobianWrtParametersMean = null;
118 jacobianEccentricWrtCartesian = null;
119 jacobianWrtParametersEccentric = null;
120 jacobianTrueWrtCartesian = null;
121 jacobianWrtParametersTrue = null;
122 }
123
124 /** Set the orbit from Cartesian parameters.
125 *
126 * <p> The acceleration provided in {@code pvCoordinates} is accessible using
127 * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
128 * use {@code mu} and the position to compute the acceleration, including
129 * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
130 *
131 * @param pvCoordinates the position and velocity in the inertial frame
132 * @param frame the frame in which the {@link TimeStampedPVCoordinates} are defined
133 * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
134 * @param mu central attraction coefficient (m^3/s^2)
135 * @exception IllegalArgumentException if frame is not a {@link
136 * Frame#isPseudoInertial pseudo-inertial frame}
137 */
138 protected Orbit(final TimeStampedPVCoordinates pvCoordinates, final Frame frame, final double mu)
139 throws IllegalArgumentException {
140 ensurePseudoInertialFrame(frame);
141 this.date = pvCoordinates.getDate();
142 this.mu = mu;
143 if (pvCoordinates.getAcceleration().getNorm2Sq() == 0) {
144 // the acceleration was not provided,
145 // compute it from Newtonian attraction
146 final double r2 = pvCoordinates.getPosition().getNorm2Sq();
147 final double r3 = r2 * FastMath.sqrt(r2);
148 this.pvCoordinates = new TimeStampedPVCoordinates(pvCoordinates.getDate(),
149 pvCoordinates.getPosition(),
150 pvCoordinates.getVelocity(),
151 new Vector3D(-mu / r3, pvCoordinates.getPosition()));
152 } else {
153 this.pvCoordinates = pvCoordinates;
154 }
155 this.frame = frame;
156 }
157
158 /** Compute non-Keplerian part of the acceleration from first time derivatives.
159 * @return non-Keplerian part of the acceleration
160 * @since 13.1
161 */
162 protected Vector3D nonKeplerianAcceleration() {
163
164 final double[][] dPdC = new double[6][6];
165 final PositionAngleType positionAngleType = PositionAngleType.MEAN;
166 getJacobianWrtCartesian(positionAngleType, dPdC);
167 final RealMatrix subMatrix = MatrixUtils.createRealMatrix(dPdC);
168
169 final DecompositionSolver solver = getDecompositionSolver(subMatrix);
170
171 final double[] derivatives = new double[6];
172 getType().mapOrbitToArray(this, positionAngleType, new double[6], derivatives);
173 derivatives[5] -= getKeplerianMeanMotion();
174
175 final RealVector solution = solver.solve(MatrixUtils.createRealVector(derivatives));
176 return new Vector3D(solution.getEntry(3), solution.getEntry(4), solution.getEntry(5));
177
178 }
179
180 /** Check if Cartesian coordinates include non-Keplerian acceleration.
181 * @param pva Cartesian coordinates
182 * @param mu central attraction coefficient
183 * @return true if Cartesian coordinates include non-Keplerian acceleration
184 */
185 protected static boolean hasNonKeplerianAcceleration(final PVCoordinates pva, final double mu) {
186
187 final Vector3D a = pva.getAcceleration();
188 if (a == null) {
189 return false;
190 }
191
192 final Vector3D p = pva.getPosition();
193 final double r2 = p.getNorm2Sq();
194
195 // Check if acceleration is relatively close to 0 compared to the Keplerian acceleration
196 final double tolerance = mu * 1e-9;
197 final Vector3D aTimesR2 = a.scalarMultiply(r2);
198 if (aTimesR2.getNorm() < tolerance) {
199 return false;
200 }
201
202 if ((aTimesR2.add(p.normalize().scalarMultiply(mu))).getNorm() > tolerance) {
203 // we have a relevant acceleration, we can compute derivatives
204 return true;
205 } else {
206 // the provided acceleration is either too small to be reliable (probably even 0), or NaN
207 return false;
208 }
209 }
210
211 /**
212 * Compute corrected shift from non-Keplerian part.
213 * @param keplerianShifted Keplerian shift
214 * @param dt time shift
215 * @return corrected position-velocity-acceleration vector
216 * @since 14.0
217 */
218 protected PVCoordinates shiftNonKeplerian(final PVCoordinates keplerianShifted, final double dt) {
219 // extract non-Keplerian acceleration from first time derivatives
220 final Vector3D nonKeplerianAcceleration = nonKeplerianAcceleration();
221
222 // add second order effect of non-Keplerian acceleration to Keplerian-only shift
223 final Vector3D fixedV = nonKeplerianAcceleration.scalarMultiply(dt).add(keplerianShifted.getVelocity());
224 final Vector3D fixedP = nonKeplerianAcceleration.scalarMultiply(dt * dt / 2.).add(keplerianShifted.getPosition());
225 final double fixedR2 = fixedP.getNorm2Sq();
226 final double fixedR = FastMath.sqrt(fixedR2);
227 final Vector3D fixedA = nonKeplerianAcceleration.add(new Vector3D(-getMu() / (fixedR2 * fixedR),
228 keplerianShifted.getPosition()));
229 return new PVCoordinates(fixedP, fixedV, fixedA);
230 }
231
232 /** Returns true if and only if the orbit is elliptical i.e. has a non-negative semi-major axis.
233 * @return true if getA() is strictly greater than 0
234 * @since 12.0
235 */
236 public boolean isElliptical() {
237 return getA() > 0.;
238 }
239
240 /** Get the orbit type.
241 * @return orbit type
242 */
243 public abstract OrbitType getType();
244
245 /**
246 * Get a factory for the current orbit type.
247 *
248 * @param positionAngleType position angle type to use
249 * @param positionScale position scale used to scale the orbital drivers
250 * @return factory building orbit instance of the correct type
251 * @since 14.0
252 */
253 public abstract AbstractOrbitFactory<? extends Orbit> factory(PositionAngleType positionAngleType,
254 double positionScale);
255
256 /** Ensure the defining frame is a pseudo-inertial frame.
257 * @param frame frame to check
258 * @exception IllegalArgumentException if frame is not a {@link
259 * Frame#isPseudoInertial pseudo-inertial frame}
260 */
261 private static void ensurePseudoInertialFrame(final Frame frame)
262 throws IllegalArgumentException {
263 if (!frame.isPseudoInertial()) {
264 throw new OrekitIllegalArgumentException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
265 frame.getName());
266 }
267 }
268
269 /** Get the frame in which the orbital parameters are defined.
270 * @return frame in which the orbital parameters are defined
271 */
272 public Frame getFrame() {
273 return frame;
274 }
275
276 /** Get the semi-major axis.
277 * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
278 * @return semi-major axis (m)
279 */
280 public abstract double getA();
281
282 /** Get the semi-major axis derivative.
283 * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
284 * <p>
285 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
286 * </p>
287 * @return semi-major axis derivative (m/s)
288 * @since 9.0
289 */
290 public abstract double getADot();
291
292 /** Get the first component of the equinoctial eccentricity vector.
293 * @return first component of the equinoctial eccentricity vector
294 */
295 public abstract double getEquinoctialEx();
296
297 /** Get the first component of the equinoctial eccentricity vector derivative.
298 * <p>
299 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
300 * </p>
301 * @return first component of the equinoctial eccentricity vector derivative
302 * @since 9.0
303 */
304 public abstract double getEquinoctialExDot();
305
306 /** Get the second component of the equinoctial eccentricity vector.
307 * @return second component of the equinoctial eccentricity vector
308 */
309 public abstract double getEquinoctialEy();
310
311 /** Get the second component of the equinoctial eccentricity vector derivative.
312 * <p>
313 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
314 * </p>
315 * @return second component of the equinoctial eccentricity vector derivative
316 * @since 9.0
317 */
318 public abstract double getEquinoctialEyDot();
319
320 /** Get the first component of the inclination vector.
321 * @return first component of the inclination vector
322 */
323 public abstract double getHx();
324
325 /** Get the first component of the inclination vector derivative.
326 * <p>
327 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
328 * </p>
329 * @return first component of the inclination vector derivative
330
331 * @since 9.0
332 */
333 public abstract double getHxDot();
334
335 /** Get the second component of the inclination vector.
336 * @return second component of the inclination vector
337 */
338 public abstract double getHy();
339
340 /** Get the second component of the inclination vector derivative.
341 * <p>
342 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
343 * </p>
344 * @return second component of the inclination vector derivative
345 * @since 9.0
346 */
347 public abstract double getHyDot();
348
349 /** Get the eccentric longitude argument.
350 * @return E + ω + Ω eccentric longitude argument (rad)
351 */
352 public abstract double getLE();
353
354 /** Get the eccentric longitude argument derivative.
355 * <p>
356 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
357 * </p>
358 * @return d(E + ω + Ω)/dt eccentric longitude argument derivative (rad/s)
359 * @since 9.0
360 */
361 public abstract double getLEDot();
362
363 /** Get the true longitude argument.
364 * @return v + ω + Ω true longitude argument (rad)
365 */
366 public abstract double getLv();
367
368 /** Get the true longitude argument derivative.
369 * <p>
370 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
371 * </p>
372 * @return d(v + ω + Ω)/dt true longitude argument derivative (rad/s)
373 * @since 9.0
374 */
375 public abstract double getLvDot();
376
377 /** Get the mean longitude argument.
378 * @return M + ω + Ω mean longitude argument (rad)
379 */
380 public abstract double getLM();
381
382 /** Get the mean longitude argument derivative.
383 * <p>
384 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
385 * </p>
386 * @return d(M + ω + Ω)/dt mean longitude argument derivative (rad/s)
387 * @since 9.0
388 */
389 public abstract double getLMDot();
390
391 // Additional orbital elements
392
393 /** Get the eccentricity.
394 * @return eccentricity
395 */
396 public abstract double getE();
397
398 /** Get the eccentricity derivative.
399 * <p>
400 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
401 * </p>
402 * @return eccentricity derivative
403 * @since 9.0
404 */
405 public abstract double getEDot();
406
407 /** Get the inclination.
408 * @return inclination (rad)
409 */
410 public abstract double getI();
411
412 /** Get the inclination derivative.
413 * <p>
414 * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
415 * </p>
416 * @return inclination derivative (rad/s)
417 * @since 9.0
418 */
419 public abstract double getIDot();
420
421 /** Check if orbit includes non-Keplerian rates.
422 * @return true if orbit includes non-Keplerian derivatives
423 * @see #getADot()
424 * @see #getEquinoctialExDot()
425 * @see #getEquinoctialEyDot()
426 * @see #getHxDot()
427 * @see #getHyDot()
428 * @see #getLEDot()
429 * @see #getLvDot()
430 * @see #getLMDot()
431 * @see #getEDot()
432 * @see #getIDot()
433 * @since 13.0
434 */
435 public boolean hasNonKeplerianAcceleration() {
436 return hasNonKeplerianAcceleration(getPVCoordinates(), getMu());
437 }
438
439 /** Get the central acceleration constant.
440 * @return central acceleration constant
441 */
442 public double getMu() {
443 return mu;
444 }
445
446 /** Get the Keplerian period.
447 * <p>The Keplerian period is computed directly from semi major axis
448 * and central acceleration constant.</p>
449 * @return Keplerian period in seconds, or positive infinity for hyperbolic orbits
450 */
451 public double getKeplerianPeriod() {
452 final double a = getA();
453 return isElliptical() ? 2.0 * FastMath.PI * a * FastMath.sqrt(a / mu) : Double.POSITIVE_INFINITY;
454 }
455
456 /** Get the Keplerian mean motion.
457 * <p>The Keplerian mean motion is computed directly from semi major axis
458 * and central acceleration constant.</p>
459 * @return Keplerian mean motion in radians per second
460 */
461 public double getKeplerianMeanMotion() {
462 final double absA = FastMath.abs(getA());
463 return FastMath.sqrt(mu / absA) / absA;
464 }
465
466 /** Get the derivative of the mean anomaly with respect to the semi major axis.
467 * @return derivative of the mean anomaly with respect to the semi major axis
468 */
469 public double getMeanAnomalyDotWrtA() {
470 return -1.5 * getKeplerianMeanMotion() / getA();
471 }
472
473 /** Get the date of orbital parameters.
474 * @return date of the orbital parameters
475 */
476 public AbsoluteDate getDate() {
477 return date;
478 }
479
480 /** Get the position in definition frame.
481 * @return position in the definition frame
482 * @see #getPVCoordinates()
483 * @since 12.0
484 */
485 @Override
486 public Vector3D getPosition() {
487 if (position == null) {
488 position = initPosition();
489 }
490 return position;
491 }
492
493 /** Get the {@link TimeStampedPVCoordinates} in definition frame.
494 * @return pvCoordinates in the definition frame
495 * @see #getPVCoordinates(Frame)
496 */
497 public TimeStampedPVCoordinates getPVCoordinates() {
498 if (pvCoordinates == null) {
499 pvCoordinates = initPVCoordinates();
500 position = pvCoordinates.getPosition();
501 }
502 return pvCoordinates;
503 }
504
505 /** Compute the position coordinates from the canonical parameters.
506 * @return computed position coordinates
507 * @since 12.0
508 */
509 protected abstract Vector3D initPosition();
510
511 /** Compute the position/velocity coordinates from the canonical parameters.
512 * @return computed position/velocity coordinates
513 */
514 protected abstract TimeStampedPVCoordinates initPVCoordinates();
515
516 /**
517 * Create a new object representing the same physical orbital state, but attached to a different reference frame.
518 * If the new frame is not inertial, an exception will be thrown.
519 *
520 * @param inertialFrame reference frame of output orbit
521 * @return orbit with different frame
522 * @since 13.0
523 */
524 public abstract Orbit inFrame(Frame inertialFrame);
525
526 /** Get a time-shifted orbit.
527 * <p>
528 * The orbit can be slightly shifted to close dates. The shifting model is a
529 * Keplerian one if no derivatives are available in the orbit, or Keplerian
530 * plus quadratic effect of the non-Keplerian acceleration if derivatives are
531 * available. Shifting is <em>not</em> intended as a replacement for proper
532 * orbit propagation but should be sufficient for small time shifts or coarse
533 * accuracy.
534 * </p>
535 * @param dt time shift in seconds
536 * @return a new orbit, shifted with respect to the instance (which is immutable)
537 */
538 @Override
539 public abstract Orbit shiftedBy(double dt);
540
541 /** Get a time-shifted orbit.
542 * <p>
543 * The orbit can be slightly shifted to close dates. The shifting model is a
544 * Keplerian one if no derivatives are available in the orbit, or Keplerian
545 * plus quadratic effect of the non-Keplerian acceleration if derivatives are
546 * available. Shifting is <em>not</em> intended as a replacement for proper
547 * orbit propagation but should be sufficient for small time shifts or coarse
548 * accuracy.
549 * </p>
550 * @param dt time shift
551 * @return a new orbit, shifted with respect to the instance (which is immutable)
552 */
553 @Override
554 public abstract Orbit shiftedBy(TimeOffset dt);
555
556 /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
557 * <p>
558 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
559 * respect to Cartesian coordinate j. This means each row corresponds to one orbital parameter
560 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
561 * </p>
562 * @param type type of the position angle to use
563 * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
564 * is larger than 6x6, only the 6x6 upper left corner will be modified
565 */
566 public void getJacobianWrtCartesian(final PositionAngleType type, final double[][] jacobian) {
567
568 final double[][] cachedJacobian;
569 synchronized (this) {
570 cachedJacobian = switch (type) {
571 case MEAN -> {
572 if (jacobianMeanWrtCartesian == null) {
573 // first call, we need to compute the Jacobian and cache it
574 jacobianMeanWrtCartesian = computeJacobianMeanWrtCartesian();
575 }
576 yield jacobianMeanWrtCartesian;
577 }
578 case ECCENTRIC -> {
579 if (jacobianEccentricWrtCartesian == null) {
580 // first call, we need to compute the Jacobian and cache it
581 jacobianEccentricWrtCartesian = computeJacobianEccentricWrtCartesian();
582 }
583 yield jacobianEccentricWrtCartesian;
584 }
585 case TRUE -> {
586 if (jacobianTrueWrtCartesian == null) {
587 // first call, we need to compute the Jacobian and cache it
588 jacobianTrueWrtCartesian = computeJacobianTrueWrtCartesian();
589 }
590 yield jacobianTrueWrtCartesian;
591 }
592 };
593 }
594
595 // fill the user provided array
596 for (int i = 0; i < cachedJacobian.length; ++i) {
597 System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
598 }
599
600 }
601
602 /** Compute the Jacobian of the Cartesian parameters with respect to the orbital parameters.
603 * <p>
604 * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i of the orbit with
605 * respect to orbital parameter j. This means each row corresponds to one Cartesian coordinate
606 * x, y, z, xdot, ydot, zdot whereas columns 0 to 5 correspond to the orbital parameters.
607 * </p>
608 * @param type type of the position angle to use
609 * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
610 * is larger than 6x6, only the 6x6 upper left corner will be modified
611 */
612 public void getJacobianWrtParameters(final PositionAngleType type, final double[][] jacobian) {
613
614 final double[][] cachedJacobian;
615 synchronized (this) {
616 cachedJacobian = switch (type) {
617 case MEAN -> {
618 if (jacobianWrtParametersMean == null) {
619 // first call, we need to compute the Jacobian and cache it
620 jacobianWrtParametersMean = createInverseJacobian(type);
621 }
622 yield jacobianWrtParametersMean;
623 }
624 case ECCENTRIC -> {
625 if (jacobianWrtParametersEccentric == null) {
626 // first call, we need to compute the Jacobian and cache it
627 jacobianWrtParametersEccentric = createInverseJacobian(type);
628 }
629 yield jacobianWrtParametersEccentric;
630 }
631 case TRUE -> {
632 if (jacobianWrtParametersTrue == null) {
633 // first call, we need to compute the Jacobian and cache it
634 jacobianWrtParametersTrue = createInverseJacobian(type);
635 }
636 yield jacobianWrtParametersTrue;
637 }
638 };
639 }
640
641 // fill the user-provided array
642 for (int i = 0; i < cachedJacobian.length; ++i) {
643 System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
644 }
645
646 }
647
648 /** Create an inverse Jacobian.
649 * @param type type of the position angle to use
650 * @return inverse Jacobian
651 */
652 private double[][] createInverseJacobian(final PositionAngleType type) {
653
654 // get the direct Jacobian
655 final double[][] directJacobian = new double[6][6];
656 getJacobianWrtCartesian(type, directJacobian);
657
658 // invert the direct Jacobian
659 final RealMatrix matrix = MatrixUtils.createRealMatrix(directJacobian);
660 final DecompositionSolver solver = getDecompositionSolver(matrix);
661 return solver.getInverse().getData();
662
663 }
664
665 /**
666 * Method to build a matrix decomposition solver.
667 * @param realMatrix matrix
668 * @return solver
669 * @since 13.1
670 */
671 protected DecompositionSolver getDecompositionSolver(final RealMatrix realMatrix) {
672 return new QRDecomposition(realMatrix).getSolver();
673 }
674
675 /** Compute the Jacobian of the orbital parameters with mean angle with respect to the Cartesian parameters.
676 * <p>
677 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
678 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
679 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
680 * </p>
681 * <p>
682 * The array returned by this method will not be modified.
683 * </p>
684 * @return 6x6 Jacobian matrix
685 * @see #computeJacobianEccentricWrtCartesian()
686 * @see #computeJacobianTrueWrtCartesian()
687 */
688 protected abstract double[][] computeJacobianMeanWrtCartesian();
689
690 /** Compute the Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian parameters.
691 * <p>
692 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
693 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
694 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
695 * </p>
696 * <p>
697 * The array returned by this method will not be modified.
698 * </p>
699 * @return 6x6 Jacobian matrix
700 * @see #computeJacobianMeanWrtCartesian()
701 * @see #computeJacobianTrueWrtCartesian()
702 */
703 protected abstract double[][] computeJacobianEccentricWrtCartesian();
704
705 /** Compute the Jacobian of the orbital parameters with true angle with respect to the Cartesian parameters.
706 * <p>
707 * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
708 * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
709 * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
710 * </p>
711 * <p>
712 * The array returned by this method will not be modified.
713 * </p>
714 * @return 6x6 Jacobian matrix
715 * @see #computeJacobianMeanWrtCartesian()
716 * @see #computeJacobianEccentricWrtCartesian()
717 */
718 protected abstract double[][] computeJacobianTrueWrtCartesian();
719
720 /** Add the contribution of the Keplerian motion to parameters derivatives.
721 * <p>
722 * This method is used by integration-based propagators to evaluate the part of Keplerian
723 * motion to evolution of the orbital state.
724 * </p>
725 * @param type type of the position angle in the state
726 * @param gm attraction coefficient to use
727 * @param pDot array containing orbital state derivatives to update (the Keplerian
728 * part must be <em>added</em> to the array components, as the array may already
729 * contain some non-zero elements corresponding to non-Keplerian parts)
730 */
731 public abstract void addKeplerContribution(PositionAngleType type, double gm, double[] pDot);
732
733 /** Fill a Jacobian half row with a single vector.
734 * @param a coefficient of the vector
735 * @param v vector
736 * @param row Jacobian matrix row
737 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
738 */
739 protected static void fillHalfRow(final double a, final Vector3D v, final double[] row, final int j) {
740 row[j] = a * v.getX();
741 row[j + 1] = a * v.getY();
742 row[j + 2] = a * v.getZ();
743 }
744
745 /** Fill a Jacobian half row with a linear combination of vectors.
746 * @param a1 coefficient of the first vector
747 * @param v1 first vector
748 * @param a2 coefficient of the second vector
749 * @param v2 second vector
750 * @param row Jacobian matrix row
751 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
752 */
753 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
754 final double[] row, final int j) {
755 row[j] = MathArrays.linearCombination(a1, v1.getX(), a2, v2.getX());
756 row[j + 1] = MathArrays.linearCombination(a1, v1.getY(), a2, v2.getY());
757 row[j + 2] = MathArrays.linearCombination(a1, v1.getZ(), a2, v2.getZ());
758 }
759
760 /** Fill a Jacobian half row with a linear combination of vectors.
761 * @param a1 coefficient of the first vector
762 * @param v1 first vector
763 * @param a2 coefficient of the second vector
764 * @param v2 second vector
765 * @param a3 coefficient of the third vector
766 * @param v3 third vector
767 * @param row Jacobian matrix row
768 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
769 */
770 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
771 final double a3, final Vector3D v3,
772 final double[] row, final int j) {
773 row[j] = MathArrays.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX());
774 row[j + 1] = MathArrays.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY());
775 row[j + 2] = MathArrays.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ());
776 }
777
778 /** Fill a Jacobian half row with a linear combination of vectors.
779 * @param a1 coefficient of the first vector
780 * @param v1 first vector
781 * @param a2 coefficient of the second vector
782 * @param v2 second vector
783 * @param a3 coefficient of the third vector
784 * @param v3 third vector
785 * @param a4 coefficient of the fourth vector
786 * @param v4 fourth vector
787 * @param row Jacobian matrix row
788 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
789 */
790 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
791 final double a3, final Vector3D v3, final double a4, final Vector3D v4,
792 final double[] row, final int j) {
793 row[j] = MathArrays.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX());
794 row[j + 1] = MathArrays.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY());
795 row[j + 2] = MathArrays.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ());
796 }
797
798 /** Fill a Jacobian half row with a linear combination of vectors.
799 * @param a1 coefficient of the first vector
800 * @param v1 first vector
801 * @param a2 coefficient of the second vector
802 * @param v2 second vector
803 * @param a3 coefficient of the third vector
804 * @param v3 third vector
805 * @param a4 coefficient of the fourth vector
806 * @param v4 fourth vector
807 * @param a5 coefficient of the fifth vector
808 * @param v5 fifth vector
809 * @param row Jacobian matrix row
810 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
811 */
812 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
813 final double a3, final Vector3D v3, final double a4, final Vector3D v4,
814 final double a5, final Vector3D v5,
815 final double[] row, final int j) {
816 final double[] a = new double[] {
817 a1, a2, a3, a4, a5
818 };
819 row[j] = MathArrays.linearCombination(a, new double[] {
820 v1.getX(), v2.getX(), v3.getX(), v4.getX(), v5.getX()
821 });
822 row[j + 1] = MathArrays.linearCombination(a, new double[] {
823 v1.getY(), v2.getY(), v3.getY(), v4.getY(), v5.getY()
824 });
825 row[j + 2] = MathArrays.linearCombination(a, new double[] {
826 v1.getZ(), v2.getZ(), v3.getZ(), v4.getZ(), v5.getZ()
827 });
828 }
829
830 /** Fill a Jacobian half row with a linear combination of vectors.
831 * @param a1 coefficient of the first vector
832 * @param v1 first vector
833 * @param a2 coefficient of the second vector
834 * @param v2 second vector
835 * @param a3 coefficient of the third vector
836 * @param v3 third vector
837 * @param a4 coefficient of the fourth vector
838 * @param v4 fourth vector
839 * @param a5 coefficient of the fifth vector
840 * @param v5 fifth vector
841 * @param a6 coefficient of the sixth vector
842 * @param v6 sixth vector
843 * @param row Jacobian matrix row
844 * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
845 */
846 protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
847 final double a3, final Vector3D v3, final double a4, final Vector3D v4,
848 final double a5, final Vector3D v5, final double a6, final Vector3D v6,
849 final double[] row, final int j) {
850 final double[] a = new double[] {
851 a1, a2, a3, a4, a5, a6
852 };
853 row[j] = MathArrays.linearCombination(a, new double[] {
854 v1.getX(), v2.getX(), v3.getX(), v4.getX(), v5.getX(), v6.getX()
855 });
856 row[j + 1] = MathArrays.linearCombination(a, new double[] {
857 v1.getY(), v2.getY(), v3.getY(), v4.getY(), v5.getY(), v6.getY()
858 });
859 row[j + 2] = MathArrays.linearCombination(a, new double[] {
860 v1.getZ(), v2.getZ(), v3.getZ(), v4.getZ(), v5.getZ(), v6.getZ()
861 });
862 }
863
864 }