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