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