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  
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.Field;
22  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
23  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.FieldSinCos;
26  import org.hipparchus.util.MathArrays;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitIllegalArgumentException;
29  import org.orekit.errors.OrekitInternalError;
30  import org.orekit.errors.OrekitMessages;
31  import org.orekit.frames.FieldKinematicTransform;
32  import org.orekit.frames.Frame;
33  import org.orekit.time.FieldAbsoluteDate;
34  import org.orekit.time.TimeOffset;
35  import org.orekit.utils.FieldPVCoordinates;
36  import org.orekit.utils.TimeStampedFieldPVCoordinates;
37  
38  
39  /**
40   * This class handles traditional Keplerian orbital parameters.
41  
42   * <p>
43   * The parameters used internally are the classical Keplerian elements:
44   *   <pre>
45   *     a
46   *     e
47   *     i
48   *     ω
49   *     Ω
50   *     v
51   *   </pre>
52   * where ω stands for the Periapsis Argument, Ω stands for the
53   * Right Ascension of the Ascending Node and v stands for the true anomaly.
54   * <p>
55   * This class supports hyperbolic orbits, using the convention that semi major
56   * axis is negative for such orbits (and of course eccentricity is greater than 1).
57   * </p>
58   * <p>
59   * When orbit is either equatorial or circular, some Keplerian elements
60   * (more precisely ω and Ω) become ambiguous so this class should not
61   * be used for such orbits. For this reason, {@link EquinoctialOrbit equinoctial
62   * orbits} is the recommended way to represent orbits.
63   * </p>
64  
65   * <p>
66   * The instance <code>KeplerianOrbit</code> is guaranteed to be immutable.
67   * </p>
68   * @see     Orbit
69   * @see    CircularOrbit
70   * @see    CartesianOrbit
71   * @see    EquinoctialOrbit
72   * @author Luc Maisonobe
73   * @author Guylaine Prat
74   * @author Fabien Maussion
75   * @author V&eacute;ronique Pommier-Maurussane
76   * @author Andrea Antolino
77   * @since 9.0
78   * @param <T> type of the field elements
79   */
80  public class FieldKeplerianOrbit<T extends CalculusFieldElement<T>> extends FieldOrbit<T>
81          implements PositionAngleBased<FieldKeplerianOrbit<T>> {
82  
83      /** Name of the eccentricity parameter. */
84      private static final String ECCENTRICITY = "eccentricity";
85  
86      /** Semi-major axis (m). */
87      private final T a;
88  
89      /** Eccentricity. */
90      private final T e;
91  
92      /** Inclination (rad). */
93      private final T i;
94  
95      /** Periapsis Argument (rad). */
96      private final T pa;
97  
98      /** Right Ascension of Ascending Node (rad). */
99      private final T raan;
100 
101     /** Cached anomaly (rad). */
102     private final T cachedAnomaly;
103 
104     /** Semi-major axis derivative (m/s). */
105     private final T aDot;
106 
107     /** Eccentricity derivative. */
108     private final T eDot;
109 
110     /** Inclination derivative (rad/s). */
111     private final T iDot;
112 
113     /** Periapsis Argument derivative (rad/s). */
114     private final T paDot;
115 
116     /** Right Ascension of Ascending Node derivative (rad/s). */
117     private final T raanDot;
118 
119     /** Derivative of cached anomaly (rad/s). */
120     private final T cachedAnomalyDot;
121 
122     /** Cached type of position angle. */
123     private final PositionAngleType cachedPositionAngleType;
124 
125     /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */
126     private FieldPVCoordinates<T> partialPV;
127 
128     /** Third Canonical Vector. */
129     private final FieldVector3D<T> plusK;
130 
131     /** Creates a new instance.
132      * @param elements Keplerian elements
133      * @param frame the frame in which the parameters are defined
134      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
135      * @param date date of the orbital parameters
136      * @param mu central attraction coefficient (m³/s²)
137      * @exception IllegalArgumentException if frame is not a {@link
138      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
139      * or v is out of range for hyperbolic orbits
140      * @since 14.0
141      */
142     public FieldKeplerianOrbit(final FieldKeplerianParameters<T> elements, final Frame frame,
143                                final FieldAbsoluteDate<T> date, final T mu)
144         throws IllegalArgumentException {
145         this(elements, mu.getField().getZero(), mu.getField().getZero(),
146                 mu.getField().getZero(), mu.getField().getZero(), mu.getField().getZero(),
147                 computeKeplerianAnomalyDot(elements.positionAngleType(), elements.a(), elements.e(), mu, elements.anomaly(), elements.positionAngleType()),
148                 elements.positionAngleType(), frame, date, mu);
149     }
150 
151     /** Creates a new instance.
152      * @param a  semi-major axis (m), negative for hyperbolic orbits
153      * @param e eccentricity (positive or equal to 0)
154      * @param i inclination (rad)
155      * @param pa periapsis argument (ω, rad)
156      * @param raan right ascension of ascending node (Ω, rad)
157      * @param anomaly mean, eccentric or true anomaly (rad)
158      * @param type type of anomaly
159      * @param frame the frame in which the parameters are defined
160      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
161      * @param date date of the orbital parameters
162      * @param mu central attraction coefficient (m³/s²)
163      * @exception IllegalArgumentException if frame is not a {@link
164      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
165      * or v is out of range for hyperbolic orbits
166      * @since 12.1
167      */
168     public FieldKeplerianOrbit(final T a, final T e, final T i, final T pa, final T raan,
169                                final T anomaly, final PositionAngleType type, final Frame frame,
170                                final FieldAbsoluteDate<T> date, final T mu)
171             throws IllegalArgumentException {
172         this(new FieldKeplerianParameters<>(a, e, i, pa, raan, anomaly, type), frame, date, mu);
173     }
174 
175     /** Creates a new instance.
176      * @param elements Keplerian elements
177      * @param aDot  semi-major axis derivative, null if unknown (m/s)
178      * @param eDot eccentricity derivative, null if unknown
179      * @param iDot inclination derivative, null if unknown (rad/s)
180      * @param paDot periapsis argument derivative, null if unknown (rad/s)
181      * @param raanDot right ascension of ascending node derivative, null if unknown (rad/s)
182      * @param anomalyDot mean, eccentric or true anomaly derivative, null if unknown (rad/s)
183      * @param cachedPositionAngleType type of cached anomaly
184      * @param frame the frame in which the parameters are defined
185      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
186      * @param date date of the orbital parameters
187      * @param mu central attraction coefficient (m³/s²)
188      * @exception IllegalArgumentException if frame is not a {@link
189      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
190      * or v is out of range for hyperbolic orbits
191      * @since 14.0
192      */
193     public FieldKeplerianOrbit(final FieldKeplerianParameters<T> elements,
194                                final T aDot, final T eDot, final T iDot,
195                                final T paDot, final T raanDot, final T anomalyDot,
196                                final PositionAngleType cachedPositionAngleType,
197                                final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
198         throws IllegalArgumentException {
199         super(frame, date, mu);
200         this.cachedPositionAngleType = cachedPositionAngleType;
201 
202         this.a = elements.a();
203         this.e = elements.e();
204         if (a.multiply(e.negate().add(1)).getReal() < 0) {
205             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a.getReal(), e.getReal());
206         }
207 
208         // Checking eccentricity range
209         checkParameterRangeInclusive(ECCENTRICITY, e.getReal(), 0.0, Double.POSITIVE_INFINITY);
210 
211         this.aDot    =    aDot;
212         this.eDot    =    eDot;
213         this.i       =    elements.i();
214         this.iDot    =    iDot;
215         this.pa      =    elements.pa();
216         this.paDot   =    paDot;
217         this.raan    =    elements.raan();
218         this.raanDot =    raanDot;
219 
220         this.plusK = FieldVector3D.getPlusK(a.getField());  // third canonical vector
221 
222         final FieldUnivariateDerivative1<T> cachedAnomalyUD = initializeCachedAnomaly(elements.anomaly(), anomalyDot,
223                 elements.positionAngleType());
224         this.cachedAnomaly = cachedAnomalyUD.getValue();
225         this.cachedAnomalyDot = cachedAnomalyUD.getFirstDerivative();
226 
227         // check true anomaly range
228         if (!isElliptical()) {
229             final T trueAnomaly = getTrueAnomaly();
230             if (e.multiply(trueAnomaly.cos()).add(1).getReal() <= 0) {
231                 final double vMax = e.reciprocal().negate().acos().getReal();
232                 throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE,
233                         trueAnomaly.getReal(), e.getReal(), -vMax, vMax);
234             }
235         }
236 
237         this.partialPV = null;
238 
239     }
240 
241     /** Creates a new instance.
242      * @param a  semi-major axis (m), negative for hyperbolic orbits
243      * @param e eccentricity (positive or equal to 0)
244      * @param i inclination (rad)
245      * @param pa periapsis argument (ω, rad)
246      * @param raan right ascension of ascending node (Ω, rad)
247      * @param anomaly mean, eccentric or true anomaly (rad)
248      * @param aDot  semi-major axis derivative, null if unknown (m/s)
249      * @param eDot eccentricity derivative, null if unknown
250      * @param iDot inclination derivative, null if unknown (rad/s)
251      * @param paDot periapsis argument derivative, null if unknown (rad/s)
252      * @param raanDot right ascension of ascending node derivative, null if unknown (rad/s)
253      * @param anomalyDot mean, eccentric or true anomaly derivative, null if unknown (rad/s)
254      * @param type type of anomaly
255      * @param frame the frame in which the parameters are defined
256      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
257      * @param date date of the orbital parameters
258      * @param mu central attraction coefficient (m³/s²)
259      * @exception IllegalArgumentException if frame is not a {@link
260      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
261      * or v is out of range for hyperbolic orbits
262      */
263     public FieldKeplerianOrbit(final T a, final T e, final T i,
264                                final T pa, final T raan, final T anomaly,
265                                final T aDot, final T eDot, final T iDot,
266                                final T paDot, final T raanDot, final T anomalyDot,
267                                final PositionAngleType type,
268                                final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
269             throws IllegalArgumentException {
270         this(new FieldKeplerianParameters<>(a, e, i, pa, raan, anomaly, type), aDot, eDot, iDot, paDot, raanDot, anomalyDot,
271                 type, frame, date, mu);
272     }
273 
274     /** Constructor from Cartesian parameters.
275      *
276      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
277      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
278      * use {@code mu} and the position to compute the acceleration, including
279      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
280      *
281      * @param pvCoordinates the PVCoordinates of the satellite
282      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
283      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
284      * @param mu central attraction coefficient (m³/s²)
285      * @exception IllegalArgumentException if frame is not a {@link
286      * Frame#isPseudoInertial pseudo-inertial frame}
287      */
288     public FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
289                                final Frame frame, final T mu)
290         throws IllegalArgumentException {
291         this(pvCoordinates, frame, mu, hasNonKeplerianAcceleration(pvCoordinates, mu));
292     }
293 
294     /** Constructor from Cartesian parameters.
295      *
296      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
297      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
298      * use {@code mu} and the position to compute the acceleration, including
299      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
300      *
301      * @param pvCoordinates the PVCoordinates of the satellite
302      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
303      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
304      * @param mu central attraction coefficient (m³/s²)
305      * @param reliableAcceleration if true, the acceleration is considered to be reliable
306      * @exception IllegalArgumentException if frame is not a {@link
307      * Frame#isPseudoInertial pseudo-inertial frame}
308      */
309     private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
310                                 final Frame frame, final T mu,
311                                 final boolean reliableAcceleration)
312         throws IllegalArgumentException {
313 
314         super(pvCoordinates, frame, mu);
315         plusK = FieldVector3D.getPlusK(mu.getField());
316 
317         // Convert Cartesian to Keplerian elements
318         final FieldKeplerianParametersConverter<T> converter = new FieldKeplerianParametersConverter<>(mu);
319         cachedPositionAngleType = PositionAngleType.ECCENTRIC;
320         final FieldKeplerianParameters<T> elements = converter.toParameters(pvCoordinates, cachedPositionAngleType);
321         a = elements.a();
322         e = elements.e();
323         i = elements.i();
324         pa = elements.pa();
325         raan = elements.raan();
326         cachedAnomaly = elements.anomaly();
327 
328         // Checking eccentricity range
329         checkParameterRangeInclusive(ECCENTRICITY, e.getReal(), 0.0, Double.POSITIVE_INFINITY);
330 
331         partialPV = pvCoordinates;
332 
333         if (reliableAcceleration) {
334             // we have a relevant acceleration, we can compute derivatives
335 
336             final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6);
337             getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian);
338 
339             final FieldVector3D<T> pvP = pvCoordinates.getPosition();
340             final T r2 = pvP.getNorm2Sq();
341             final T r  = FastMath.sqrt(r2);
342             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP);
343             final FieldVector3D<T> nonKeplerianAcceleration = pvCoordinates.getAcceleration().subtract(keplerianAcceleration);
344             final T   aX                       = nonKeplerianAcceleration.getX();
345             final T   aY                       = nonKeplerianAcceleration.getY();
346             final T   aZ                       = nonKeplerianAcceleration.getZ();
347             aDot    = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
348             eDot    = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
349             iDot    = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
350             paDot   = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
351             raanDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));
352 
353             // in order to compute true anomaly derivative, we must compute
354             // mean anomaly derivative including Keplerian motion and convert to true anomaly
355             final T MDot = getKeplerianMeanMotion().
356                            add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
357             final FieldUnivariateDerivative1<T> eUD = new FieldUnivariateDerivative1<>(e, eDot);
358             final FieldUnivariateDerivative1<T> MUD = new FieldUnivariateDerivative1<>(getMeanAnomaly(), MDot);
359             final FieldUnivariateDerivative1<T> EUD = (a.getReal() < 0) ?
360                      FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, MUD) :
361                      FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, MUD);
362             cachedAnomalyDot = EUD.getFirstDerivative();
363 
364         } else {
365             // acceleration is either almost zero or NaN,
366             // we assume acceleration was not known
367             // we don't set up derivatives
368             aDot    = getZero();
369             eDot    = getZero();
370             iDot    = getZero();
371             paDot   = getZero();
372             raanDot = getZero();
373             cachedAnomalyDot    = computeKeplerianAnomalyDot(cachedPositionAngleType, a, e, mu, cachedAnomaly, cachedPositionAngleType);
374         }
375 
376     }
377 
378     /** Constructor from Cartesian parameters.
379      *
380      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
381      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
382      * use {@code mu} and the position to compute the acceleration, including
383      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
384      *
385      * @param FieldPVCoordinates the PVCoordinates of the satellite
386      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
387      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
388      * @param date date of the orbital parameters
389      * @param mu central attraction coefficient (m³/s²)
390      * @exception IllegalArgumentException if frame is not a {@link
391      * Frame#isPseudoInertial pseudo-inertial frame}
392      */
393     public FieldKeplerianOrbit(final FieldPVCoordinates<T> FieldPVCoordinates,
394                                final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
395         throws IllegalArgumentException {
396         this(new TimeStampedFieldPVCoordinates<>(date, FieldPVCoordinates), frame, mu);
397     }
398 
399     /** Constructor from any kind of orbital parameters.
400      * @param op orbital parameters to copy
401      */
402     public FieldKeplerianOrbit(final FieldOrbit<T> op) {
403         this(op.getPVCoordinates(), op.getFrame(), op.getMu());
404     }
405 
406     /** Constructor from Field and KeplerianOrbit.
407      * <p>Build a FieldKeplerianOrbit from non-Field KeplerianOrbit.</p>
408      * @param field CalculusField to base object on
409      * @param op non-field orbit with only "constant" terms
410      * @since 12.0
411      */
412     public FieldKeplerianOrbit(final Field<T> field, final KeplerianOrbit op) {
413         this(new FieldKeplerianParameters<>(field, op.getKeplerianParameters()),
414                 field.getZero().newInstance(op.getADot()),
415                 field.getZero().newInstance(op.getEDot()),
416                 field.getZero().newInstance(op.getIDot()),
417                 field.getZero().newInstance(op.getPeriapsisArgumentDot()),
418                 field.getZero().newInstance(op.getRightAscensionOfAscendingNodeDot()),
419                 field.getZero().newInstance(op.getAnomalyDot(op.getCachedPositionAngleType())),
420                 op.getCachedPositionAngleType(), op.getFrame(),
421                 new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().newInstance(op.getMu()));
422     }
423 
424     /** Constructor from Field and Orbit.
425      * <p>Build a FieldKeplerianOrbit from any non-Field Orbit.</p>
426      * @param field CalculusField to base object on
427      * @param op non-field orbit with only "constant" terms
428      * @since 12.0
429      */
430     public FieldKeplerianOrbit(final Field<T> field, final Orbit op) {
431         this(field, (KeplerianOrbit) OrbitParamsType.KEPLERIAN.convertType(op));
432     }
433 
434     /** {@inheritDoc} */
435     @Override
436     public OrbitParamsType getType() {
437         return OrbitParamsType.KEPLERIAN;
438     }
439 
440     /** {@inheritDoc} */
441     @Override
442     public T getA() {
443         return a;
444     }
445 
446     /** {@inheritDoc} */
447     @Override
448     public T getADot() {
449         return aDot;
450     }
451 
452     /** {@inheritDoc} */
453     @Override
454     public T getE() {
455         return e;
456     }
457 
458     /** {@inheritDoc} */
459     @Override
460     public T getEDot() {
461         return eDot;
462     }
463 
464     /** {@inheritDoc} */
465     @Override
466     public T getI() {
467         return i;
468     }
469 
470     /** {@inheritDoc} */
471     @Override
472     public T getIDot() {
473         return iDot;
474     }
475 
476     /** Get the periapsis argument.
477      * @return periapsis argument (rad)
478      */
479     public T getPeriapsisArgument() {
480         return pa;
481     }
482 
483     /** Get the periapsis argument derivative.
484      * @return periapsis argument derivative (rad/s)
485      */
486     public T getPeriapsisArgumentDot() {
487         return paDot;
488     }
489 
490     /** Get the right ascension of the ascending node.
491      * @return right ascension of the ascending node (rad)
492      */
493     public T getRightAscensionOfAscendingNode() {
494         return raan;
495     }
496 
497     /** Get the right ascension of the ascending node derivative.
498      * @return right ascension of the ascending node derivative (rad/s)
499      */
500     public T getRightAscensionOfAscendingNodeDot() {
501         return raanDot;
502     }
503 
504     /** Get the true anomaly.
505      * @return true anomaly (rad)
506      */
507     public T getTrueAnomaly() {
508         return getAnomaly(PositionAngleType.TRUE);
509     }
510 
511     /** Get the true anomaly derivative.
512      * @return true anomaly derivative (rad/s)
513      */
514     public T getTrueAnomalyDot() {
515         return switch (cachedPositionAngleType) {
516             case MEAN -> {
517                 final FieldUnivariateDerivative1<T> eUD = new FieldUnivariateDerivative1<>(e, eDot);
518                 final FieldUnivariateDerivative1<T> MUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot);
519                 final FieldUnivariateDerivative1<T> vUD = (a.getReal() < 0) ?
520                         FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) :
521                         FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD);
522                 yield vUD.getFirstDerivative();
523             }
524 
525             case TRUE -> cachedAnomalyDot;
526 
527             case ECCENTRIC -> {
528                 final FieldUnivariateDerivative1<T> eUD2 = new FieldUnivariateDerivative1<>(e, eDot);
529                 final FieldUnivariateDerivative1<T> EUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot);
530                 final FieldUnivariateDerivative1<T> vUD2 = (a.getReal() < 0) ?
531                          FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD2, EUD) :
532                          FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD2, EUD);
533                 yield vUD2.getFirstDerivative();
534             }
535         };
536     }
537 
538     /** Get the eccentric anomaly.
539      * @return eccentric anomaly (rad)
540      */
541     public T getEccentricAnomaly() {
542         return getAnomaly(PositionAngleType.ECCENTRIC);
543     }
544 
545     /** Get the eccentric anomaly derivative.
546      * @return eccentric anomaly derivative (rad/s)
547      */
548     public T getEccentricAnomalyDot() {
549         return switch (cachedPositionAngleType) {
550             case ECCENTRIC -> cachedAnomalyDot;
551 
552             case TRUE -> {
553                 final FieldUnivariateDerivative1<T> eUD = new FieldUnivariateDerivative1<>(e, eDot);
554                 final FieldUnivariateDerivative1<T> vUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot);
555                 final FieldUnivariateDerivative1<T> EUD = (a.getReal() < 0) ?
556                         FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) :
557                         FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD);
558                 yield EUD.getFirstDerivative();
559             }
560 
561             case MEAN -> {
562                 final FieldUnivariateDerivative1<T> eUD2 = new FieldUnivariateDerivative1<>(e, eDot);
563                 final FieldUnivariateDerivative1<T> MUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot);
564                 final FieldUnivariateDerivative1<T> EUD2 = (a.getReal() < 0) ?
565                         FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD2, MUD) :
566                         FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD2, MUD);
567                 yield EUD2.getFirstDerivative();
568             }
569         };
570     }
571 
572     /** Get the mean anomaly.
573      * @return mean anomaly (rad)
574      */
575     public T getMeanAnomaly() {
576         return getAnomaly(PositionAngleType.MEAN);
577     }
578 
579     /** Get the mean anomaly derivative.
580 
581      * @return mean anomaly derivative (rad/s)
582      */
583     public T getMeanAnomalyDot() {
584         return switch (cachedPositionAngleType) {
585             case MEAN -> cachedAnomalyDot;
586 
587             case ECCENTRIC -> {
588                 final FieldUnivariateDerivative1<T> eUD = new FieldUnivariateDerivative1<>(e, eDot);
589                 final FieldUnivariateDerivative1<T> EUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot);
590                 final FieldUnivariateDerivative1<T> MUD = (a.getReal() < 0) ?
591                         FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, EUD) :
592                         FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, EUD);
593                 yield MUD.getFirstDerivative();
594             }
595 
596             case TRUE -> {
597                 final FieldUnivariateDerivative1<T> eUD2 = new FieldUnivariateDerivative1<>(e, eDot);
598                 final FieldUnivariateDerivative1<T> vUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot);
599                 final FieldUnivariateDerivative1<T> MUD2 = (a.getReal() < 0) ?
600                         FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD2, vUD) :
601                         FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD2, vUD);
602                 yield MUD2.getFirstDerivative();
603             }
604         };
605     }
606 
607     /** Get the anomaly.
608      * @param type type of the angle
609      * @return anomaly (rad)
610      */
611     public T getAnomaly(final PositionAngleType type) {
612         return getKeplerianParameters().withPositionAngleType(type).anomaly();
613     }
614 
615     /** Get the anomaly derivative.
616      * @param type type of the angle
617      * @return anomaly derivative (rad/s)
618      */
619     public T getAnomalyDot(final PositionAngleType type) {
620         return switch (type) {
621             case MEAN -> getMeanAnomalyDot();
622             case ECCENTRIC -> getEccentricAnomalyDot();
623             case TRUE -> getTrueAnomalyDot();
624         };
625     }
626 
627     /**
628      * Method providing with the Keplerian elements, using the cached type for the anomaly.
629      * @return Keplerian elements
630      * @since 14.0
631      */
632     public FieldKeplerianParameters<T> getKeplerianParameters() {
633         return new FieldKeplerianParameters<>(a, e, i, pa, raan, cachedAnomaly, cachedPositionAngleType);
634     }
635 
636     /** {@inheritDoc} */
637     @Override
638     public boolean hasNonKeplerianAcceleration() {
639         return aDot.getReal() != 0. || eDot.getReal() != 0 || iDot.getReal() != 0. || raanDot.getReal() != 0. || paDot.getReal() != 0. ||
640                 FastMath.abs(cachedAnomalyDot.subtract(computeKeplerianAnomalyDot(cachedPositionAngleType, a, e, getMu(), cachedAnomaly, cachedPositionAngleType)).getReal()) > TOLERANCE_POSITION_ANGLE_RATE;
641     }
642 
643     /** {@inheritDoc} */
644     @Override
645     public T getEquinoctialEx() {
646         return e.multiply(pa.add(raan).cos());
647     }
648 
649     /** {@inheritDoc} */
650     @Override
651     public T getEquinoctialExDot() {
652         if (!hasNonKeplerianRates()) {
653             return getZero();
654         }
655         final FieldUnivariateDerivative1<T> eUD    = new FieldUnivariateDerivative1<>(e,    eDot);
656         final FieldUnivariateDerivative1<T> paUD   = new FieldUnivariateDerivative1<>(pa,   paDot);
657         final FieldUnivariateDerivative1<T> raanUD = new FieldUnivariateDerivative1<>(raan, raanDot);
658         return eUD.multiply(paUD.add(raanUD).cos()).getFirstDerivative();
659 
660     }
661 
662     /** {@inheritDoc} */
663     @Override
664     public T getEquinoctialEy() {
665         return e.multiply((pa.add(raan)).sin());
666     }
667 
668     /** {@inheritDoc} */
669     @Override
670     public T getEquinoctialEyDot() {
671         if (!hasNonKeplerianRates()) {
672             return getZero();
673         }
674         final FieldUnivariateDerivative1<T> eUD    = new FieldUnivariateDerivative1<>(e,    eDot);
675         final FieldUnivariateDerivative1<T> paUD   = new FieldUnivariateDerivative1<>(pa,   paDot);
676         final FieldUnivariateDerivative1<T> raanUD = new FieldUnivariateDerivative1<>(raan, raanDot);
677         return eUD.multiply(paUD.add(raanUD).sin()).getFirstDerivative();
678 
679     }
680 
681     /** {@inheritDoc} */
682     @Override
683     public T getHx() {
684         // Check for equatorial retrograde orbit
685         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
686             return getZero().add(Double.NaN);
687         }
688         return raan.cos().multiply(i.divide(2).tan());
689     }
690 
691     /** {@inheritDoc} */
692     @Override
693     public T getHxDot() {
694 
695         // Check for equatorial retrograde orbit
696         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
697             return getZero().add(Double.NaN);
698         }
699         if (!hasNonKeplerianRates()) {
700             return getZero();
701         }
702 
703         final FieldUnivariateDerivative1<T> iUD    = new FieldUnivariateDerivative1<>(i,    iDot);
704         final FieldUnivariateDerivative1<T> raanUD = new FieldUnivariateDerivative1<>(raan, raanDot);
705         return raanUD.cos().multiply(iUD.multiply(0.5).tan()).getFirstDerivative();
706 
707     }
708 
709     /** {@inheritDoc} */
710     @Override
711     public T getHy() {
712         // Check for equatorial retrograde orbit
713         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
714             return getZero().add(Double.NaN);
715         }
716         return raan.sin().multiply(i.divide(2).tan());
717     }
718 
719     /** {@inheritDoc} */
720     @Override
721     public T getHyDot() {
722 
723         // Check for equatorial retrograde orbit
724         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
725             return getZero().add(Double.NaN);
726         }
727         if (!hasNonKeplerianRates()) {
728             return getZero();
729         }
730 
731         final FieldUnivariateDerivative1<T> iUD    = new FieldUnivariateDerivative1<>(i,    iDot);
732         final FieldUnivariateDerivative1<T> raanUD = new FieldUnivariateDerivative1<>(raan, raanDot);
733         return raanUD.sin().multiply(iUD.multiply(0.5).tan()).getFirstDerivative();
734 
735     }
736 
737     /** {@inheritDoc} */
738     @Override
739     public T getLv() {
740         return pa.add(raan).add(getTrueAnomaly());
741     }
742 
743     /** {@inheritDoc} */
744     @Override
745     public T getLvDot() {
746         return paDot.add(raanDot).add(getTrueAnomalyDot());
747     }
748 
749     /** {@inheritDoc} */
750     @Override
751     public T getLE() {
752         return pa.add(raan).add(getEccentricAnomaly());
753     }
754 
755     /** {@inheritDoc} */
756     @Override
757     public T getLEDot() {
758         return paDot.add(raanDot).add(getEccentricAnomalyDot());
759     }
760 
761     /** {@inheritDoc} */
762     @Override
763     public T getLM() {
764         return pa.add(raan).add(getMeanAnomaly());
765     }
766 
767     /** {@inheritDoc} */
768     @Override
769     public T getLMDot() {
770         return paDot.add(raanDot).add(getMeanAnomalyDot());
771     }
772 
773     /** Initialize cached anomaly with rate.
774      * @param anomaly input anomaly
775      * @param anomalyDot rate of input anomaly
776      * @param inputType position angle type passed as input
777      * @return anomaly to cache with rate
778      * @since 12.1
779      */
780     private FieldUnivariateDerivative1<T> initializeCachedAnomaly(final T anomaly, final T anomalyDot,
781                                                                   final PositionAngleType inputType) {
782         if (cachedPositionAngleType == inputType) {
783             return new FieldUnivariateDerivative1<>(anomaly, anomalyDot);
784 
785         } else {
786             final FieldUnivariateDerivative1<T> eUD = new FieldUnivariateDerivative1<>(e, eDot);
787             final FieldUnivariateDerivative1<T> anomalyUD = new FieldUnivariateDerivative1<>(anomaly, anomalyDot);
788 
789             if (a.getReal() < 0) {
790                 switch (cachedPositionAngleType) {
791                     case MEAN:
792                         if (inputType == PositionAngleType.ECCENTRIC) {
793                             return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, anomalyUD);
794                         } else {
795                             return FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, anomalyUD);
796                         }
797 
798                     case ECCENTRIC:
799                         if (inputType == PositionAngleType.MEAN) {
800                             return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, anomalyUD);
801                         } else {
802                             return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, anomalyUD);
803                         }
804 
805                     case TRUE:
806                         if (inputType == PositionAngleType.MEAN) {
807                             return FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD);
808                         } else {
809                             return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD);
810                         }
811 
812                     default:
813                         break;
814                 }
815 
816             } else {
817                 switch (cachedPositionAngleType) {
818                     case MEAN:
819                         if (inputType == PositionAngleType.ECCENTRIC) {
820                             return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, anomalyUD);
821                         } else {
822                             return FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, anomalyUD);
823                         }
824 
825                     case ECCENTRIC:
826                         if (inputType == PositionAngleType.MEAN) {
827                             return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, anomalyUD);
828                         } else {
829                             return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, anomalyUD);
830                         }
831 
832                     case TRUE:
833                         if (inputType == PositionAngleType.MEAN) {
834                             return FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD);
835                         } else {
836                             return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD);
837                         }
838 
839                     default:
840                         break;
841                 }
842 
843             }
844             throw new OrekitInternalError(null);
845         }
846 
847     }
848 
849     /** Compute position and velocity but not acceleration.
850      */
851     private void computePVWithoutA() {
852 
853         if (partialPV != null) {
854             // already computed
855             return;
856         }
857 
858         final FieldKeplerianParametersConverter<T> converter = new FieldKeplerianParametersConverter<>(getMu());
859         partialPV = converter.toCartesian(getKeplerianParameters());
860 
861     }
862 
863     /** {@inheritDoc} */
864     @Override
865     protected FieldVector3D<T> initPosition() {
866         final FieldVector3D<T>[] axes = FieldKeplerianParametersConverter.referenceAxes(i, pa, raan);
867 
868         if (isElliptical()) {
869 
870             // elliptical case
871 
872             // elliptic eccentric anomaly
873             final T uME2             = e.negate().add(1).multiply(e.add(1));
874             final T s1Me2            = uME2.sqrt();
875             final FieldSinCos<T> scE = FastMath.sinCos(getEccentricAnomaly());
876             final T cosE             = scE.cos();
877             final T sinE             = scE.sin();
878 
879             return new FieldVector3D<>(a.multiply(cosE.subtract(e)), axes[0], a.multiply(sinE).multiply(s1Me2), axes[1]);
880 
881         } else {
882 
883             // hyperbolic case
884 
885             // compute position and velocity factors
886             final FieldSinCos<T> scV = FastMath.sinCos(getTrueAnomaly());
887             final T sinV             = scV.sin();
888             final T cosV             = scV.cos();
889             final T f                = a.multiply(e.square().negate().add(1));
890             final T posFactor        = f.divide(e.multiply(cosV).add(1));
891 
892             return new FieldVector3D<>(posFactor.multiply(cosV), axes[0], posFactor.multiply(sinV), axes[1]);
893 
894         }
895 
896     }
897 
898     /** {@inheritDoc} */
899     @Override
900     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {
901 
902         // position and velocity
903         computePVWithoutA();
904 
905         // acceleration
906         final T r2 = partialPV.getPosition().getNorm2Sq();
907         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(FastMath.sqrt(r2)).reciprocal().multiply(getMu().negate()),
908                                                                            partialPV.getPosition());
909         final FieldVector3D<T> acceleration = hasNonKeplerianRates() ?
910                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
911                                               keplerianAcceleration;
912 
913         return new TimeStampedFieldPVCoordinates<>(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);
914 
915     }
916 
917     /** {@inheritDoc} */
918     @Override
919     public FieldKeplerianOrbit<T> inFrame(final Frame inertialFrame) {
920         final FieldPVCoordinates<T> fieldPVCoordinates;
921         if (hasNonKeplerianAcceleration()) {
922             fieldPVCoordinates = getPVCoordinates(inertialFrame);
923         } else {
924             final FieldKinematicTransform<T> transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
925             fieldPVCoordinates = transform.transformOnlyPV(getPVCoordinates());
926         }
927         final FieldKeplerianOrbit<T> fieldOrbit = new FieldKeplerianOrbit<>(fieldPVCoordinates, inertialFrame, getDate(), getMu());
928         if (fieldOrbit.getCachedPositionAngleType() == getCachedPositionAngleType()) {
929             return fieldOrbit;
930         } else {
931             return fieldOrbit.withCachedPositionAngleType(getCachedPositionAngleType());
932         }
933     }
934 
935     /** {@inheritDoc} */
936     @Override
937     public FieldKeplerianOrbit<T> withCachedPositionAngleType(final PositionAngleType positionAngleType) {
938         return new FieldKeplerianOrbit<>(a, e, i, pa, raan, getAnomaly(positionAngleType), aDot, eDot, iDot, paDot, raanDot,
939                 getAnomalyDot(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
940     }
941 
942     /** {@inheritDoc} */
943     @Override
944     public FieldKeplerianOrbit<T> shiftedBy(final double dt) {
945         return shiftedBy(getZero().newInstance(dt));
946     }
947 
948     /** {@inheritDoc} */
949     @Override
950     public FieldKeplerianOrbit<T> shiftedBy(final T dt) {
951 
952         // use Keplerian-only motion
953         final FieldKeplerianOrbit<T> keplerianShifted = keplerianShiftedBy(dt);
954 
955         // Non-Keplerian acceleration shall be considered
956         if (!dt.isZero() && hasNonKeplerianRates()) {
957             return new FieldKeplerianOrbit<>(shiftPVNonKeplerian(keplerianShifted.getPVCoordinates(), dt),
958                     getFrame(), getDate().shiftedBy(dt), getMu());
959         }
960         // Keplerian-only motion is all we can do
961         else {
962             return keplerianShifted;
963         }
964 
965     }
966 
967     /** {@inheritDoc} */
968     @Override
969     public FieldKeplerianOrbit<T> shiftedBy(final TimeOffset dt) {
970 
971         // Get field and express dt as T
972         final Field<T> field   = getField();
973         final T        dtValue = field.getOne().newInstance(dt.toDouble());
974 
975         // use Keplerian-only motion
976         final FieldKeplerianOrbit<T> keplerianShifted = keplerianShiftedBy(dt);
977 
978         // Non-Keplerian acceleration shall be considered
979         if (!dtValue.isZero() && hasNonKeplerianRates()) {
980             return new FieldKeplerianOrbit<>(shiftPVNonKeplerian(keplerianShifted.getPVCoordinates(), dtValue),
981                     getFrame(), getDate().shiftedBy(dt), getMu());
982         }
983         // Keplerian-only motion is all we can do
984         else {
985             return keplerianShifted;
986         }
987 
988     }
989 
990     /**
991      * Computes a new orbit by shifting the current orbit forward or backward in time using Keplerian motion.
992      *
993      * @param dt time delta
994      * @return shifted orbit
995      */
996     protected FieldKeplerianOrbit<T> keplerianShiftedBy(final T dt) {
997         final FieldKeplerianParameters<T> shiftedElements = new FieldKeplerianParameters<>(a, e, i, pa, raan,
998                 getKeplerianMeanMotion().multiply(dt).add(getMeanAnomaly()), PositionAngleType.MEAN);
999         return new FieldKeplerianOrbit<>(shiftedElements.withPositionAngleType(cachedPositionAngleType), getFrame(),
1000                 getDate().shiftedBy(dt), getMu());
1001     }
1002 
1003     /**
1004      * Computes a new orbit by shifting the current orbit forward or backward in time using Keplerian motion. This
1005      * implementation uses the more accurate {@link TimeOffset} to compute the shifted date.
1006      *
1007      * @param dt time delta
1008      * @return shifted orbit
1009      */
1010     private FieldKeplerianOrbit<T> keplerianShiftedBy(final TimeOffset dt) {
1011         final FieldKeplerianParameters<T> shiftedElements = new FieldKeplerianParameters<>(a, e, i, pa, raan,
1012                 getKeplerianMeanMotion().multiply(dt.toDouble()).add(getMeanAnomaly()), PositionAngleType.MEAN);
1013         return new FieldKeplerianOrbit<>(shiftedElements.withPositionAngleType(cachedPositionAngleType), getFrame(),
1014                 getDate().shiftedBy(dt), getMu());
1015     }
1016 
1017     /** {@inheritDoc} */
1018     @Override
1019     protected T[][] computeJacobianMeanWrtCartesian() {
1020         if (isElliptical()) {
1021             return computeJacobianMeanWrtCartesianElliptical();
1022         } else {
1023             return computeJacobianMeanWrtCartesianHyperbolic();
1024         }
1025     }
1026 
1027     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1028      * <p>
1029      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1030      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1031      * yDot for j=4, zDot for j=5).
1032      * </p>
1033      * @return 6x6 Jacobian matrix
1034      */
1035     private T[][] computeJacobianMeanWrtCartesianElliptical() {
1036 
1037         final T[][] jacobian = MathArrays.buildArray(getA().getField(), 6, 6);
1038 
1039         // compute various intermediate parameters
1040         computePVWithoutA();
1041         final FieldVector3D<T> position = partialPV.getPosition();
1042         final FieldVector3D<T> velocity = partialPV.getVelocity();
1043         final FieldVector3D<T> momentum = partialPV.getMomentum();
1044         final T v2         = velocity.getNorm2Sq();
1045         final T r2         = position.getNorm2Sq();
1046         final T r          = r2.sqrt();
1047         final T r3         = r.multiply(r2);
1048 
1049         final T px         = position.getX();
1050         final T py         = position.getY();
1051         final T pz         = position.getZ();
1052         final T vx         = velocity.getX();
1053         final T vy         = velocity.getY();
1054         final T vz         = velocity.getZ();
1055         final T mx         = momentum.getX();
1056         final T my         = momentum.getY();
1057         final T mz         = momentum.getZ();
1058 
1059         final T mu         = getMu();
1060         final T sqrtMuA    = FastMath.sqrt(a.multiply(mu));
1061         final T sqrtAoMu   = FastMath.sqrt(a.divide(mu));
1062         final T a2         = a.square();
1063         final T twoA       = a.multiply(2);
1064         final T rOnA       = r.divide(a);
1065 
1066         final T oMe2       = e.square().negate().add(1);
1067         final T epsilon    = oMe2.sqrt();
1068         final T sqrtRec    = epsilon.reciprocal();
1069 
1070         final FieldSinCos<T> scI  = FastMath.sinCos(i);
1071         final FieldSinCos<T> scPA = FastMath.sinCos(pa);
1072         final T cosI       = scI.cos();
1073         final T sinI       = scI.sin();
1074         final T cosPA      = scPA.cos();
1075         final T sinPA      = scPA.sin();
1076 
1077         final T pv         = FieldVector3D.dotProduct(position, velocity);
1078         final T cosE       = a.subtract(r).divide(a.multiply(e));
1079         final T sinE       = pv.divide(e.multiply(sqrtMuA));
1080 
1081         // da
1082         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
1083         final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(mu.divide(2.).reciprocal()));
1084         fillHalfRow(getOne(), vectorAR,    jacobian[0], 0);
1085         fillHalfRow(getOne(), vectorARDot, jacobian[0], 3);
1086 
1087         // de
1088         final T factorER3 = pv.divide(twoA);
1089         final FieldVector3D<T> vectorER   = new FieldVector3D<>(cosE.multiply(v2).divide(r.multiply(mu)), position,
1090                                                                 sinE.divide(sqrtMuA), velocity,
1091                                                                 factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorAR);
1092         final FieldVector3D<T> vectorERDot = new FieldVector3D<>(sinE.divide(sqrtMuA), position,
1093                                                                  cosE.multiply(mu.divide(2.).reciprocal()).multiply(r), velocity,
1094                                                                  factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorARDot);
1095         fillHalfRow(getOne(), vectorER,    jacobian[1], 0);
1096         fillHalfRow(getOne(), vectorERDot, jacobian[1], 3);
1097 
1098         // dE / dr (Eccentric anomaly)
1099         final T coefE = cosE.divide(e.multiply(sqrtMuA));
1100         final FieldVector3D<T>  vectorEAnR =
1101             new FieldVector3D<>(sinE.negate().multiply(v2).divide(e.multiply(r).multiply(mu)), position, coefE, velocity,
1102                                 factorER3.negate().multiply(coefE), vectorAR);
1103 
1104         // dE / drDot
1105         final FieldVector3D<T>  vectorEAnRDot =
1106             new FieldVector3D<>(sinE.multiply(-2).multiply(r).divide(e.multiply(mu)), velocity, coefE, position,
1107                                 factorER3.negate().multiply(coefE), vectorARDot);
1108 
1109         // precomputing some more factors
1110         final T s1 = sinE.negate().multiply(pz).divide(r).subtract(cosE.multiply(vz).multiply(sqrtAoMu));
1111         final T s2 = cosE.negate().multiply(pz).divide(r3);
1112         final T s3 = sinE.multiply(vz).divide(sqrtMuA.multiply(-2));
1113         final T t1 = sqrtRec.multiply(cosE.multiply(pz).divide(r).subtract(sinE.multiply(vz).multiply(sqrtAoMu)));
1114         final T t2 = sqrtRec.multiply(sinE.negate().multiply(pz).divide(r3));
1115         final T t3 = sqrtRec.multiply(cosE.subtract(e)).multiply(vz).divide(sqrtMuA.multiply(2));
1116         final T t4 = sqrtRec.multiply(e.multiply(sinI).multiply(cosPA).multiply(sqrtRec).subtract(vz.multiply(sqrtAoMu)));
1117         final FieldVector3D<T> s = new FieldVector3D<>(cosE.divide(r), this.plusK,
1118                                                        s1,       vectorEAnR,
1119                                                        s2,       position,
1120                                                        s3,       vectorAR);
1121         final FieldVector3D<T> sDot = new FieldVector3D<>(sinE.negate().multiply(sqrtAoMu), this.plusK,
1122                                                           s1,               vectorEAnRDot,
1123                                                           s3,               vectorARDot);
1124         final FieldVector3D<T> t =
1125             new FieldVector3D<>(sqrtRec.multiply(sinE).divide(r), this.plusK).add(new FieldVector3D<>(t1, vectorEAnR,
1126                                                                                                        t2, position,
1127                                                                                                        t3, vectorAR,
1128                                                                                                        t4, vectorER));
1129         final FieldVector3D<T> tDot = new FieldVector3D<>(sqrtRec.multiply(cosE.subtract(e)).multiply(sqrtAoMu), this.plusK,
1130                                                           t1,                                                    vectorEAnRDot,
1131                                                           t3,                                                    vectorARDot,
1132                                                           t4,                                                    vectorERDot);
1133 
1134         // di
1135         final T factorI1 = sinI.negate().multiply(sqrtRec).divide(sqrtMuA);
1136         final T i1 =  factorI1;
1137         final T i2 =  factorI1.negate().multiply(mz).divide(twoA);
1138         final T i3 =  factorI1.multiply(mz).multiply(e).divide(oMe2);
1139         final T i4 = cosI.multiply(sinPA);
1140         final T i5 = cosI.multiply(cosPA);
1141         fillHalfRow(i1, new FieldVector3D<>(vy, vx.negate(), getZero()), i2, vectorAR, i3, vectorER, i4, s, i5, t,
1142                     jacobian[2], 0);
1143         fillHalfRow(i1, new FieldVector3D<>(py.negate(), px, getZero()), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
1144                     jacobian[2], 3);
1145 
1146         // dpa
1147         fillHalfRow(cosPA.divide(sinI), s,    sinPA.negate().divide(sinI), t,    jacobian[3], 0);
1148         fillHalfRow(cosPA.divide(sinI), sDot, sinPA.negate().divide(sinI), tDot, jacobian[3], 3);
1149 
1150         // dRaan
1151         final T factorRaanR = (a.multiply(mu).multiply(oMe2).multiply(sinI).multiply(sinI)).reciprocal();
1152         fillHalfRow( factorRaanR.negate().multiply(my), new FieldVector3D<>(getZero(), vz, vy.negate()),
1153                      factorRaanR.multiply(mx), new FieldVector3D<>(vz.negate(), getZero(),  vx),
1154                      jacobian[4], 0);
1155         fillHalfRow(factorRaanR.negate().multiply(my), new FieldVector3D<>(getZero(), pz.negate(),  py),
1156                      factorRaanR.multiply(mx), new FieldVector3D<>(pz, getZero(), px.negate()),
1157                      jacobian[4], 3);
1158 
1159         // dM
1160         fillHalfRow(rOnA, vectorEAnR,    sinE.negate(), vectorER,    jacobian[5], 0);
1161         fillHalfRow(rOnA, vectorEAnRDot, sinE.negate(), vectorERDot, jacobian[5], 3);
1162 
1163         return jacobian;
1164 
1165     }
1166 
1167     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1168      * <p>
1169      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1170      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1171      * yDot for j=4, zDot for j=5).
1172      * </p>
1173      * @return 6x6 Jacobian matrix
1174      */
1175     private T[][] computeJacobianMeanWrtCartesianHyperbolic() {
1176 
1177         final T[][] jacobian = MathArrays.buildArray(getA().getField(), 6, 6);
1178 
1179         // compute various intermediate parameters
1180         computePVWithoutA();
1181         final FieldVector3D<T> position = partialPV.getPosition();
1182         final FieldVector3D<T> velocity = partialPV.getVelocity();
1183         final FieldVector3D<T> momentum = partialPV.getMomentum();
1184         final T r2         = position.getNorm2Sq();
1185         final T r          = r2.sqrt();
1186         final T r3         = r.multiply(r2);
1187 
1188         final T x          = position.getX();
1189         final T y          = position.getY();
1190         final T z          = position.getZ();
1191         final T vx         = velocity.getX();
1192         final T vy         = velocity.getY();
1193         final T vz         = velocity.getZ();
1194         final T mx         = momentum.getX();
1195         final T my         = momentum.getY();
1196         final T mz         = momentum.getZ();
1197 
1198         final T mu         = getMu();
1199         final T absA       = a.negate();
1200         final T sqrtMuA    = absA.multiply(mu).sqrt();
1201         final T a2         = a.square();
1202         final T rOa        = r.divide(absA);
1203 
1204         final FieldSinCos<T> scI = FastMath.sinCos(i);
1205         final T cosI       = scI.cos();
1206         final T sinI       = scI.sin();
1207 
1208         final T pv         = FieldVector3D.dotProduct(position, velocity);
1209 
1210         // da
1211         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(-2).divide(r3), position);
1212         final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(-2).divide(mu));
1213         fillHalfRow(getOne().negate(), vectorAR,    jacobian[0], 0);
1214         fillHalfRow(getOne().negate(), vectorARDot, jacobian[0], 3);
1215 
1216         // differentials of the momentum
1217         final T m      = momentum.getNorm();
1218         final T oOm    = m.reciprocal();
1219         final FieldVector3D<T> dcXP = new FieldVector3D<>(getZero(), vz, vy.negate());
1220         final FieldVector3D<T> dcYP = new FieldVector3D<>(vz.negate(),   getZero(),  vx);
1221         final FieldVector3D<T> dcZP = new FieldVector3D<>( vy, vx.negate(),   getZero());
1222         final FieldVector3D<T> dcXV = new FieldVector3D<>(  getZero(),  z.negate(),   y);
1223         final FieldVector3D<T> dcYV = new FieldVector3D<>(  z,   getZero(),  x.negate());
1224         final FieldVector3D<T> dcZV = new FieldVector3D<>( y.negate(),   x,   getZero());
1225         final FieldVector3D<T> dCP  = new FieldVector3D<>(mx.multiply(oOm), dcXP, my.multiply(oOm), dcYP, mz.multiply(oOm), dcZP);
1226         final FieldVector3D<T> dCV  = new FieldVector3D<>(mx.multiply(oOm), dcXV, my.multiply(oOm), dcYV, mz.multiply(oOm), dcZV);
1227 
1228         // dp
1229         final T mOMu   = m.divide(mu);
1230         final FieldVector3D<T> dpP  = new FieldVector3D<>(mOMu.multiply(2), dCP);
1231         final FieldVector3D<T> dpV  = new FieldVector3D<>(mOMu.multiply(2), dCV);
1232 
1233         // de
1234         final T p      = m.multiply(mOMu);
1235         final T moO2ae = absA.multiply(2).multiply(e).reciprocal();
1236         final T m2OaMu = p.negate().divide(absA);
1237         fillHalfRow(moO2ae, dpP, m2OaMu.multiply(moO2ae), vectorAR,    jacobian[1], 0);
1238         fillHalfRow(moO2ae, dpV, m2OaMu.multiply(moO2ae), vectorARDot, jacobian[1], 3);
1239 
1240         // di
1241         final T cI1 = m.multiply(sinI).reciprocal();
1242         final T cI2 = cosI.multiply(cI1);
1243         fillHalfRow(cI2, dCP, cI1.negate(), dcZP, jacobian[2], 0);
1244         fillHalfRow(cI2, dCV, cI1.negate(), dcZV, jacobian[2], 3);
1245 
1246 
1247         // dPA
1248         final T cP1     =  y.multiply(oOm);
1249         final T cP2     =  x.negate().multiply(oOm);
1250         final T cP3     =  mx.multiply(cP1).add(my.multiply(cP2)).negate();
1251         final T cP4     =  cP3.multiply(oOm);
1252         final T cP5     =  r2.multiply(sinI).multiply(sinI).negate().reciprocal();
1253         final T cP6     = z.multiply(cP5);
1254         final T cP7     = cP3.multiply(cP5);
1255         final FieldVector3D<T> dacP  = new FieldVector3D<>(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new FieldVector3D<>(my.negate(), mx, getZero()));
1256         final FieldVector3D<T> dacV  = new FieldVector3D<>(cP1, dcXV, cP2, dcYV, cP4, dCV);
1257         final FieldVector3D<T> dpoP  = new FieldVector3D<>(cP6, dacP, cP7, this.plusK);
1258         final FieldVector3D<T> dpoV  = new FieldVector3D<>(cP6, dacV);
1259 
1260         final T re2     = r2.multiply(e.square());
1261         final T recOre2 = p.subtract(r).divide(re2);
1262         final T resOre2 = pv.multiply(mOMu).divide(re2);
1263         final FieldVector3D<T> dreP  = new FieldVector3D<>(mOMu, velocity, pv.divide(mu), dCP);
1264         final FieldVector3D<T> dreV  = new FieldVector3D<>(mOMu, position, pv.divide(mu), dCV);
1265         final FieldVector3D<T> davP  = new FieldVector3D<>(resOre2.negate(), dpP, recOre2, dreP, resOre2.divide(r), position);
1266         final FieldVector3D<T> davV  = new FieldVector3D<>(resOre2.negate(), dpV, recOre2, dreV);
1267         fillHalfRow(getOne(), dpoP, getOne().negate(), davP, jacobian[3], 0);
1268         fillHalfRow(getOne(), dpoV, getOne().negate(), davV, jacobian[3], 3);
1269 
1270         // dRAAN
1271         final T cO0 = cI1.square();
1272         final T cO1 =  mx.multiply(cO0);
1273         final T cO2 =  my.negate().multiply(cO0);
1274         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
1275         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);
1276 
1277         // dM
1278         final T s2a    = pv.divide(absA.multiply(2));
1279         final T oObux  = m.square().add(absA.multiply(mu)).sqrt().reciprocal();
1280         final T scasbu = pv.multiply(oObux);
1281         final FieldVector3D<T> dauP = new FieldVector3D<>(sqrtMuA.reciprocal(), velocity, s2a.negate().divide(sqrtMuA), vectorAR);
1282         final FieldVector3D<T> dauV = new FieldVector3D<>(sqrtMuA.reciprocal(), position, s2a.negate().divide(sqrtMuA), vectorARDot);
1283         final FieldVector3D<T> dbuP = new FieldVector3D<>(oObux.multiply(mu.divide(2.)), vectorAR,    m.multiply(oObux), dCP);
1284         final FieldVector3D<T> dbuV = new FieldVector3D<>(oObux.multiply(mu.divide(2.)), vectorARDot, m.multiply(oObux), dCV);
1285         final FieldVector3D<T> dcuP = new FieldVector3D<>(oObux, velocity, scasbu.negate().multiply(oObux), dbuP);
1286         final FieldVector3D<T> dcuV = new FieldVector3D<>(oObux, position, scasbu.negate().multiply(oObux), dbuV);
1287         fillHalfRow(getOne(), dauP, e.negate().divide(rOa.add(1)), dcuP, jacobian[5], 0);
1288         fillHalfRow(getOne(), dauV, e.negate().divide(rOa.add(1)), dcuV, jacobian[5], 3);
1289 
1290         return jacobian;
1291 
1292     }
1293 
1294     /** {@inheritDoc} */
1295     @Override
1296     protected T[][] computeJacobianEccentricWrtCartesian() {
1297         if (isElliptical()) {
1298             return computeJacobianEccentricWrtCartesianElliptical();
1299         } else {
1300             return computeJacobianEccentricWrtCartesianHyperbolic();
1301         }
1302     }
1303 
1304     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1305      * <p>
1306      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1307      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1308      * yDot for j=4, zDot for j=5).
1309      * </p>
1310      * @return 6x6 Jacobian matrix
1311      */
1312     private T[][] computeJacobianEccentricWrtCartesianElliptical() {
1313 
1314         // start by computing the Jacobian with mean angle
1315         final T[][] jacobian = computeJacobianMeanWrtCartesianElliptical();
1316 
1317         // Differentiating the Kepler equation M = E - e sin E leads to:
1318         // dM = (1 - e cos E) dE - sin E de
1319         // which is inverted and rewritten as:
1320         // dE = a/r dM + sin E a/r de
1321         final FieldSinCos<T> scE = FastMath.sinCos(getEccentricAnomaly());
1322         final T aOr              = e.negate().multiply(scE.cos()).add(1).reciprocal();
1323 
1324         // update anomaly row
1325         final T[] eRow           = jacobian[1];
1326         final T[] anomalyRow     = jacobian[5];
1327         for (int j = 0; j < anomalyRow.length; ++j) {
1328             anomalyRow[j] = aOr.multiply(anomalyRow[j].add(scE.sin().multiply(eRow[j])));
1329         }
1330 
1331         return jacobian;
1332 
1333     }
1334 
1335     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1336      * <p>
1337      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1338      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1339      * yDot for j=4, zDot for j=5).
1340      * </p>
1341      * @return 6x6 Jacobian matrix
1342      */
1343     private T[][] computeJacobianEccentricWrtCartesianHyperbolic() {
1344 
1345         // start by computing the Jacobian with mean angle
1346         final T[][] jacobian = computeJacobianMeanWrtCartesianHyperbolic();
1347 
1348         // Differentiating the Kepler equation M = e sinh H - H leads to:
1349         // dM = (e cosh H - 1) dH + sinh H de
1350         // which is inverted and rewritten as:
1351         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
1352         final T H      = getEccentricAnomaly();
1353         final T coshH  = H.cosh();
1354         final T sinhH  = H.sinh();
1355         final T absaOr = e.multiply(coshH).subtract(1).reciprocal();
1356         // update anomaly row
1357         final T[] eRow       = jacobian[1];
1358         final T[] anomalyRow = jacobian[5];
1359 
1360         for (int j = 0; j < anomalyRow.length; ++j) {
1361             anomalyRow[j] = absaOr.multiply(anomalyRow[j].subtract(sinhH.multiply(eRow[j])));
1362 
1363         }
1364 
1365         return jacobian;
1366 
1367     }
1368 
1369     /** {@inheritDoc} */
1370     @Override
1371     protected T[][] computeJacobianTrueWrtCartesian() {
1372         if (isElliptical()) {
1373             return computeJacobianTrueWrtCartesianElliptical();
1374         } else {
1375             return computeJacobianTrueWrtCartesianHyperbolic();
1376         }
1377     }
1378 
1379     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1380      * <p>
1381      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1382      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1383      * yDot for j=4, zDot for j=5).
1384      * </p>
1385      * @return 6x6 Jacobian matrix
1386      */
1387     private T[][] computeJacobianTrueWrtCartesianElliptical() {
1388 
1389         // start by computing the Jacobian with eccentric angle
1390         final T[][] jacobian = computeJacobianEccentricWrtCartesianElliptical();
1391         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
1392         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
1393         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
1394         // which is inverted and rewritten as:
1395         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
1396         final T e2               = e.square();
1397         final T oMe2             = e2.negate().add(1);
1398         final T epsilon          = oMe2.sqrt();
1399         final FieldSinCos<T> scE = FastMath.sinCos(getEccentricAnomaly());
1400         final T aOr              = e.multiply(scE.cos()).negate().add(1).reciprocal();
1401         final T aFactor          = epsilon.multiply(aOr);
1402         final T eFactor          = scE.sin().multiply(aOr).divide(epsilon);
1403 
1404         // update anomaly row
1405         final T[] eRow           = jacobian[1];
1406         final T[] anomalyRow     = jacobian[5];
1407         for (int j = 0; j < anomalyRow.length; ++j) {
1408             anomalyRow[j] = aFactor.multiply(anomalyRow[j]).add(eFactor.multiply(eRow[j]));
1409         }
1410         return jacobian;
1411 
1412     }
1413 
1414     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1415      * <p>
1416      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1417      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1418      * yDot for j=4, zDot for j=5).
1419      * </p>
1420      * @return 6x6 Jacobian matrix
1421      */
1422     private T[][] computeJacobianTrueWrtCartesianHyperbolic() {
1423 
1424         // start by computing the Jacobian with eccentric angle
1425         final T[][] jacobian = computeJacobianEccentricWrtCartesianHyperbolic();
1426 
1427         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
1428         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
1429         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
1430         // which is inverted and rewritten as:
1431         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
1432         final T e2       = e.square();
1433         final T e2Mo     = e2.subtract(1);
1434         final T epsilon  = e2Mo.sqrt();
1435         final T H        = getEccentricAnomaly();
1436         final T coshH    = H.cosh();
1437         final T sinhH    = H.sinh();
1438         final T aOr      = e.multiply(coshH).subtract(1).reciprocal();
1439         final T aFactor  = epsilon.multiply(aOr);
1440         final T eFactor  = sinhH.multiply(aOr).divide(epsilon);
1441 
1442         // update anomaly row
1443         final T[] eRow           = jacobian[1];
1444         final T[] anomalyRow     = jacobian[5];
1445         for (int j = 0; j < anomalyRow.length; ++j) {
1446             anomalyRow[j] = aFactor.multiply(anomalyRow[j]).subtract(eFactor.multiply(eRow[j]));
1447         }
1448 
1449         return jacobian;
1450 
1451     }
1452 
1453     /** {@inheritDoc} */
1454     @Override
1455     public void addKeplerContribution(final PositionAngleType type, final T gm,
1456                                       final T[] pDot) {
1457         pDot[5] = pDot[5].add(computeKeplerianAnomalyDot(type, a, e, gm, cachedAnomaly, cachedPositionAngleType));
1458     }
1459 
1460     /**
1461      * Compute rate of argument of latitude.
1462      * @param type position angle type of output rate
1463      * @param a semi major axis
1464      * @param e eccentricity
1465      * @param mu mu
1466      * @param anomaly anomaly
1467      * @param cachedType position angle type of input anomaly
1468      * @param <T> field type
1469      * @return first-order time derivative for anomaly
1470      * @since 12.2
1471      */
1472     private static <T extends CalculusFieldElement<T>> T computeKeplerianAnomalyDot(final PositionAngleType type, final T a, final T e,
1473                                                                                     final T mu, final T anomaly, final PositionAngleType cachedType) {
1474         final T absA = a.abs();
1475         final T n    = absA.reciprocal().multiply(mu).sqrt().divide(absA);
1476         if (type == PositionAngleType.MEAN) {
1477             return n;
1478         }
1479         final T ksi;
1480         final T oMe2;
1481         final T trueAnomaly = FieldKeplerianAnomalyUtility.convertAnomaly(cachedType, anomaly, e, PositionAngleType.TRUE);
1482         if (type == PositionAngleType.ECCENTRIC) {
1483             oMe2 = e.square().negate().add(1).abs();
1484             ksi = e.multiply(trueAnomaly.cos()).add(1);
1485             return n.multiply(ksi).divide(oMe2);
1486         } else {
1487             oMe2 = e.square().negate().add(1).abs();
1488             ksi  = e.multiply(trueAnomaly.cos()).add(1);
1489             return n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()));
1490         }
1491     }
1492 
1493     /**  Returns a string representation of this Keplerian parameters object.
1494      * @return a string representation of this object
1495      */
1496     public String toString() {
1497         return "Keplerian parameters: " + '{' +
1498                 "a: " + a.getReal() +
1499                 "; e: " + e.getReal() +
1500                 "; i: " + FastMath.toDegrees(i.getReal()) +
1501                 "; pa: " + FastMath.toDegrees(pa.getReal()) +
1502                 "; raan: " + FastMath.toDegrees(raan.getReal()) +
1503                 "; v: " + FastMath.toDegrees(getTrueAnomaly().getReal()) +
1504                 ";}";
1505     }
1506 
1507     /** {@inheritDoc} */
1508     @Override
1509     public PositionAngleType getCachedPositionAngleType() {
1510         return cachedPositionAngleType;
1511     }
1512 
1513     /** {@inheritDoc} */
1514     @Override
1515     public boolean hasNonKeplerianRates() {
1516         return hasNonKeplerianAcceleration();
1517     }
1518 
1519     /** {@inheritDoc} */
1520     @Override
1521     public FieldKeplerianOrbit<T> withKeplerianRates() {
1522         return new FieldKeplerianOrbit<>(getKeplerianParameters(), getFrame(), getDate(), getMu());
1523     }
1524 
1525     /** Check if the given parameter is within an acceptable range.
1526      * The bounds are inclusive: an exception is raised when either of those conditions are met:
1527      * <ul>
1528      *     <li>The parameter is strictly greater than upperBound</li>
1529      *     <li>The parameter is strictly lower than lowerBound</li>
1530      * </ul>
1531      * <p>
1532      * In either of these cases, an OrekitException is raised.
1533      * </p>
1534      * @param parameterName name of the parameter
1535      * @param parameter value of the parameter
1536      * @param lowerBound lower bound of the acceptable range (inclusive)
1537      * @param upperBound upper bound of the acceptable range (inclusive)
1538      */
1539     private void checkParameterRangeInclusive(final String parameterName, final double parameter,
1540                                               final double lowerBound, final double upperBound) {
1541         if (parameter < lowerBound || parameter > upperBound) {
1542             throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName,
1543                                       parameter, lowerBound, upperBound);
1544         }
1545     }
1546 
1547     /** {@inheritDoc} */
1548     @Override
1549     public KeplerianOrbit toOrbit() {
1550         final KeplerianParameters elements = new KeplerianParameters(a.getReal(), e.getReal(), i.getReal(),
1551                 pa.getReal(), raan.getReal(), cachedAnomaly.getReal(), cachedPositionAngleType);
1552         return new KeplerianOrbit(elements,
1553                                   aDot.getReal(), eDot.getReal(), iDot.getReal(), paDot.getReal(), raanDot.getReal(),
1554                                   cachedAnomalyDot.getReal(), cachedPositionAngleType,
1555                                   getFrame(), getDate().toAbsoluteDate(), getMu().getReal());
1556     }
1557 
1558 
1559 }