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 Perigee 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      /** Perigee 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     /** Perigee 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 perigee 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 perigee 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 perigee 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 perigee 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.getPerigeeArgumentDot()),
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) OrbitType.KEPLERIAN.convertType(op));
432     }
433 
434     /** {@inheritDoc} */
435     @Override
436     public OrbitType getType() {
437         return OrbitType.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 perigee argument.
477      * @return perigee argument (rad)
478      */
479     public T getPerigeeArgument() {
480         return pa;
481     }
482 
483     /** Get the perigee argument derivative.
484      * @return perigee argument derivative (rad/s)
485      */
486     public T getPerigeeArgumentDot() {
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 = shiftWithKeplerianMotion(dt);
954 
955         // Non-Keplerian acceleration shall be considered
956         if (!dt.isZero() && hasNonKeplerianRates()) {
957             return keplerianShifted.applyNonKeplerianAcceleration(nonKeplerianAcceleration(), dt);
958         }
959         // Keplerian-only motion is all we can do
960         else {
961             return keplerianShifted;
962         }
963 
964     }
965 
966     /** {@inheritDoc} */
967     @Override
968     public FieldKeplerianOrbit<T> shiftedBy(final TimeOffset dt) {
969 
970         // Get field and express dt as T
971         final Field<T> field   = getField();
972         final T        dtValue = field.getOne().newInstance(dt.toDouble());
973 
974         // use Keplerian-only motion
975         final FieldKeplerianOrbit<T> keplerianShifted = shiftWithKeplerianMotion(dt);
976 
977         // Non-Keplerian acceleration shall be considered
978         if (!dtValue.isZero() && hasNonKeplerianRates()) {
979             return keplerianShifted.applyNonKeplerianAcceleration(nonKeplerianAcceleration(), dtValue);
980         }
981         // Keplerian-only motion is all we can do
982         else {
983             return keplerianShifted;
984         }
985 
986     }
987 
988     /**
989      * Computes a new orbit by shifting the current orbit forward or backward in time using Keplerian motion.
990      *
991      * @param dt time delta
992      * @return shifted orbit
993      */
994     private FieldKeplerianOrbit<T> shiftWithKeplerianMotion(final T dt) {
995         final FieldKeplerianParameters<T> shiftedElements = new FieldKeplerianParameters<>(a, e, i, pa, raan,
996                 getKeplerianMeanMotion().multiply(dt).add(getMeanAnomaly()), PositionAngleType.MEAN);
997         return new FieldKeplerianOrbit<>(shiftedElements.withPositionAngleType(cachedPositionAngleType), getFrame(),
998                 getDate().shiftedBy(dt), getMu());
999     }
1000 
1001     /**
1002      * Computes a new orbit by shifting the current orbit forward or backward in time using Keplerian motion. This
1003      * implementation uses the more accurate {@link TimeOffset} to compute the shifted date.
1004      *
1005      * @param dt time delta
1006      * @return shifted orbit
1007      */
1008     private FieldKeplerianOrbit<T> shiftWithKeplerianMotion(final TimeOffset dt) {
1009         final FieldKeplerianParameters<T> shiftedElements = new FieldKeplerianParameters<>(a, e, i, pa, raan,
1010                 getKeplerianMeanMotion().multiply(dt.toDouble()).add(getMeanAnomaly()), PositionAngleType.MEAN);
1011         return new FieldKeplerianOrbit<>(shiftedElements.withPositionAngleType(cachedPositionAngleType), getFrame(),
1012                 getDate().shiftedBy(dt), getMu());
1013     }
1014 
1015     /**
1016      * Shifts the current orbit with consideration of non-Keplerian acceleration by including the quadratic effects of
1017      * the acceleration into the position, velocity, and acceleration calculations.
1018      *
1019      * @param nonKeplerianAcceleration non-Keplerian acceleration vector to apply
1020      * @param dt                       the time shift in seconds for which the orbit is to be shifted.
1021      * @return a new {@link FieldCircularOrbit} representing the shifted orbit, factoring in non-Keplerian acceleration
1022      * effects.
1023      */
1024     private FieldKeplerianOrbit<T> applyNonKeplerianAcceleration(final FieldVector3D<T> nonKeplerianAcceleration,
1025                                                                  final T dt) {
1026         // extract non-Keplerian acceleration from first time derivatives
1027 
1028         // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
1029         this.computePVWithoutA();
1030         final FieldVector3D<T> fixedP = new FieldVector3D<>(getOne(), this.partialPV.getPosition(),
1031                                                             dt.square().multiply(0.5), nonKeplerianAcceleration);
1032         final T fixedR2 = fixedP.getNorm2Sq();
1033         final T fixedR  = fixedR2.sqrt();
1034         final FieldVector3D<T> fixedV = new FieldVector3D<>(getOne(), this.partialPV.getVelocity(),
1035                                                             dt, nonKeplerianAcceleration);
1036         final FieldVector3D<T> fixedA =
1037                 new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
1038                                     this.partialPV.getPosition(),
1039                                     getOne(), nonKeplerianAcceleration);
1040 
1041         // build a new orbit, taking non-Keplerian acceleration into account
1042         return new FieldKeplerianOrbit<>(new TimeStampedFieldPVCoordinates<>(this.getDate(),
1043                                                                              fixedP, fixedV, fixedA),
1044                                          this.getFrame(), this.getMu());
1045     }
1046 
1047     /** {@inheritDoc} */
1048     @Override
1049     protected T[][] computeJacobianMeanWrtCartesian() {
1050         if (isElliptical()) {
1051             return computeJacobianMeanWrtCartesianElliptical();
1052         } else {
1053             return computeJacobianMeanWrtCartesianHyperbolic();
1054         }
1055     }
1056 
1057     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1058      * <p>
1059      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1060      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1061      * yDot for j=4, zDot for j=5).
1062      * </p>
1063      * @return 6x6 Jacobian matrix
1064      */
1065     private T[][] computeJacobianMeanWrtCartesianElliptical() {
1066 
1067         final T[][] jacobian = MathArrays.buildArray(getA().getField(), 6, 6);
1068 
1069         // compute various intermediate parameters
1070         computePVWithoutA();
1071         final FieldVector3D<T> position = partialPV.getPosition();
1072         final FieldVector3D<T> velocity = partialPV.getVelocity();
1073         final FieldVector3D<T> momentum = partialPV.getMomentum();
1074         final T v2         = velocity.getNorm2Sq();
1075         final T r2         = position.getNorm2Sq();
1076         final T r          = r2.sqrt();
1077         final T r3         = r.multiply(r2);
1078 
1079         final T px         = position.getX();
1080         final T py         = position.getY();
1081         final T pz         = position.getZ();
1082         final T vx         = velocity.getX();
1083         final T vy         = velocity.getY();
1084         final T vz         = velocity.getZ();
1085         final T mx         = momentum.getX();
1086         final T my         = momentum.getY();
1087         final T mz         = momentum.getZ();
1088 
1089         final T mu         = getMu();
1090         final T sqrtMuA    = FastMath.sqrt(a.multiply(mu));
1091         final T sqrtAoMu   = FastMath.sqrt(a.divide(mu));
1092         final T a2         = a.square();
1093         final T twoA       = a.multiply(2);
1094         final T rOnA       = r.divide(a);
1095 
1096         final T oMe2       = e.square().negate().add(1);
1097         final T epsilon    = oMe2.sqrt();
1098         final T sqrtRec    = epsilon.reciprocal();
1099 
1100         final FieldSinCos<T> scI  = FastMath.sinCos(i);
1101         final FieldSinCos<T> scPA = FastMath.sinCos(pa);
1102         final T cosI       = scI.cos();
1103         final T sinI       = scI.sin();
1104         final T cosPA      = scPA.cos();
1105         final T sinPA      = scPA.sin();
1106 
1107         final T pv         = FieldVector3D.dotProduct(position, velocity);
1108         final T cosE       = a.subtract(r).divide(a.multiply(e));
1109         final T sinE       = pv.divide(e.multiply(sqrtMuA));
1110 
1111         // da
1112         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
1113         final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(mu.divide(2.).reciprocal()));
1114         fillHalfRow(getOne(), vectorAR,    jacobian[0], 0);
1115         fillHalfRow(getOne(), vectorARDot, jacobian[0], 3);
1116 
1117         // de
1118         final T factorER3 = pv.divide(twoA);
1119         final FieldVector3D<T> vectorER   = new FieldVector3D<>(cosE.multiply(v2).divide(r.multiply(mu)), position,
1120                                                                 sinE.divide(sqrtMuA), velocity,
1121                                                                 factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorAR);
1122         final FieldVector3D<T> vectorERDot = new FieldVector3D<>(sinE.divide(sqrtMuA), position,
1123                                                                  cosE.multiply(mu.divide(2.).reciprocal()).multiply(r), velocity,
1124                                                                  factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorARDot);
1125         fillHalfRow(getOne(), vectorER,    jacobian[1], 0);
1126         fillHalfRow(getOne(), vectorERDot, jacobian[1], 3);
1127 
1128         // dE / dr (Eccentric anomaly)
1129         final T coefE = cosE.divide(e.multiply(sqrtMuA));
1130         final FieldVector3D<T>  vectorEAnR =
1131             new FieldVector3D<>(sinE.negate().multiply(v2).divide(e.multiply(r).multiply(mu)), position, coefE, velocity,
1132                                 factorER3.negate().multiply(coefE), vectorAR);
1133 
1134         // dE / drDot
1135         final FieldVector3D<T>  vectorEAnRDot =
1136             new FieldVector3D<>(sinE.multiply(-2).multiply(r).divide(e.multiply(mu)), velocity, coefE, position,
1137                                 factorER3.negate().multiply(coefE), vectorARDot);
1138 
1139         // precomputing some more factors
1140         final T s1 = sinE.negate().multiply(pz).divide(r).subtract(cosE.multiply(vz).multiply(sqrtAoMu));
1141         final T s2 = cosE.negate().multiply(pz).divide(r3);
1142         final T s3 = sinE.multiply(vz).divide(sqrtMuA.multiply(-2));
1143         final T t1 = sqrtRec.multiply(cosE.multiply(pz).divide(r).subtract(sinE.multiply(vz).multiply(sqrtAoMu)));
1144         final T t2 = sqrtRec.multiply(sinE.negate().multiply(pz).divide(r3));
1145         final T t3 = sqrtRec.multiply(cosE.subtract(e)).multiply(vz).divide(sqrtMuA.multiply(2));
1146         final T t4 = sqrtRec.multiply(e.multiply(sinI).multiply(cosPA).multiply(sqrtRec).subtract(vz.multiply(sqrtAoMu)));
1147         final FieldVector3D<T> s = new FieldVector3D<>(cosE.divide(r), this.plusK,
1148                                                        s1,       vectorEAnR,
1149                                                        s2,       position,
1150                                                        s3,       vectorAR);
1151         final FieldVector3D<T> sDot = new FieldVector3D<>(sinE.negate().multiply(sqrtAoMu), this.plusK,
1152                                                           s1,               vectorEAnRDot,
1153                                                           s3,               vectorARDot);
1154         final FieldVector3D<T> t =
1155             new FieldVector3D<>(sqrtRec.multiply(sinE).divide(r), this.plusK).add(new FieldVector3D<>(t1, vectorEAnR,
1156                                                                                                        t2, position,
1157                                                                                                        t3, vectorAR,
1158                                                                                                        t4, vectorER));
1159         final FieldVector3D<T> tDot = new FieldVector3D<>(sqrtRec.multiply(cosE.subtract(e)).multiply(sqrtAoMu), this.plusK,
1160                                                           t1,                                                    vectorEAnRDot,
1161                                                           t3,                                                    vectorARDot,
1162                                                           t4,                                                    vectorERDot);
1163 
1164         // di
1165         final T factorI1 = sinI.negate().multiply(sqrtRec).divide(sqrtMuA);
1166         final T i1 =  factorI1;
1167         final T i2 =  factorI1.negate().multiply(mz).divide(twoA);
1168         final T i3 =  factorI1.multiply(mz).multiply(e).divide(oMe2);
1169         final T i4 = cosI.multiply(sinPA);
1170         final T i5 = cosI.multiply(cosPA);
1171         fillHalfRow(i1, new FieldVector3D<>(vy, vx.negate(), getZero()), i2, vectorAR, i3, vectorER, i4, s, i5, t,
1172                     jacobian[2], 0);
1173         fillHalfRow(i1, new FieldVector3D<>(py.negate(), px, getZero()), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
1174                     jacobian[2], 3);
1175 
1176         // dpa
1177         fillHalfRow(cosPA.divide(sinI), s,    sinPA.negate().divide(sinI), t,    jacobian[3], 0);
1178         fillHalfRow(cosPA.divide(sinI), sDot, sinPA.negate().divide(sinI), tDot, jacobian[3], 3);
1179 
1180         // dRaan
1181         final T factorRaanR = (a.multiply(mu).multiply(oMe2).multiply(sinI).multiply(sinI)).reciprocal();
1182         fillHalfRow( factorRaanR.negate().multiply(my), new FieldVector3D<>(getZero(), vz, vy.negate()),
1183                      factorRaanR.multiply(mx), new FieldVector3D<>(vz.negate(), getZero(),  vx),
1184                      jacobian[4], 0);
1185         fillHalfRow(factorRaanR.negate().multiply(my), new FieldVector3D<>(getZero(), pz.negate(),  py),
1186                      factorRaanR.multiply(mx), new FieldVector3D<>(pz, getZero(), px.negate()),
1187                      jacobian[4], 3);
1188 
1189         // dM
1190         fillHalfRow(rOnA, vectorEAnR,    sinE.negate(), vectorER,    jacobian[5], 0);
1191         fillHalfRow(rOnA, vectorEAnRDot, sinE.negate(), vectorERDot, jacobian[5], 3);
1192 
1193         return jacobian;
1194 
1195     }
1196 
1197     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1198      * <p>
1199      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1200      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1201      * yDot for j=4, zDot for j=5).
1202      * </p>
1203      * @return 6x6 Jacobian matrix
1204      */
1205     private T[][] computeJacobianMeanWrtCartesianHyperbolic() {
1206 
1207         final T[][] jacobian = MathArrays.buildArray(getA().getField(), 6, 6);
1208 
1209         // compute various intermediate parameters
1210         computePVWithoutA();
1211         final FieldVector3D<T> position = partialPV.getPosition();
1212         final FieldVector3D<T> velocity = partialPV.getVelocity();
1213         final FieldVector3D<T> momentum = partialPV.getMomentum();
1214         final T r2         = position.getNorm2Sq();
1215         final T r          = r2.sqrt();
1216         final T r3         = r.multiply(r2);
1217 
1218         final T x          = position.getX();
1219         final T y          = position.getY();
1220         final T z          = position.getZ();
1221         final T vx         = velocity.getX();
1222         final T vy         = velocity.getY();
1223         final T vz         = velocity.getZ();
1224         final T mx         = momentum.getX();
1225         final T my         = momentum.getY();
1226         final T mz         = momentum.getZ();
1227 
1228         final T mu         = getMu();
1229         final T absA       = a.negate();
1230         final T sqrtMuA    = absA.multiply(mu).sqrt();
1231         final T a2         = a.square();
1232         final T rOa        = r.divide(absA);
1233 
1234         final FieldSinCos<T> scI = FastMath.sinCos(i);
1235         final T cosI       = scI.cos();
1236         final T sinI       = scI.sin();
1237 
1238         final T pv         = FieldVector3D.dotProduct(position, velocity);
1239 
1240         // da
1241         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(-2).divide(r3), position);
1242         final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(-2).divide(mu));
1243         fillHalfRow(getOne().negate(), vectorAR,    jacobian[0], 0);
1244         fillHalfRow(getOne().negate(), vectorARDot, jacobian[0], 3);
1245 
1246         // differentials of the momentum
1247         final T m      = momentum.getNorm();
1248         final T oOm    = m.reciprocal();
1249         final FieldVector3D<T> dcXP = new FieldVector3D<>(getZero(), vz, vy.negate());
1250         final FieldVector3D<T> dcYP = new FieldVector3D<>(vz.negate(),   getZero(),  vx);
1251         final FieldVector3D<T> dcZP = new FieldVector3D<>( vy, vx.negate(),   getZero());
1252         final FieldVector3D<T> dcXV = new FieldVector3D<>(  getZero(),  z.negate(),   y);
1253         final FieldVector3D<T> dcYV = new FieldVector3D<>(  z,   getZero(),  x.negate());
1254         final FieldVector3D<T> dcZV = new FieldVector3D<>( y.negate(),   x,   getZero());
1255         final FieldVector3D<T> dCP  = new FieldVector3D<>(mx.multiply(oOm), dcXP, my.multiply(oOm), dcYP, mz.multiply(oOm), dcZP);
1256         final FieldVector3D<T> dCV  = new FieldVector3D<>(mx.multiply(oOm), dcXV, my.multiply(oOm), dcYV, mz.multiply(oOm), dcZV);
1257 
1258         // dp
1259         final T mOMu   = m.divide(mu);
1260         final FieldVector3D<T> dpP  = new FieldVector3D<>(mOMu.multiply(2), dCP);
1261         final FieldVector3D<T> dpV  = new FieldVector3D<>(mOMu.multiply(2), dCV);
1262 
1263         // de
1264         final T p      = m.multiply(mOMu);
1265         final T moO2ae = absA.multiply(2).multiply(e).reciprocal();
1266         final T m2OaMu = p.negate().divide(absA);
1267         fillHalfRow(moO2ae, dpP, m2OaMu.multiply(moO2ae), vectorAR,    jacobian[1], 0);
1268         fillHalfRow(moO2ae, dpV, m2OaMu.multiply(moO2ae), vectorARDot, jacobian[1], 3);
1269 
1270         // di
1271         final T cI1 = m.multiply(sinI).reciprocal();
1272         final T cI2 = cosI.multiply(cI1);
1273         fillHalfRow(cI2, dCP, cI1.negate(), dcZP, jacobian[2], 0);
1274         fillHalfRow(cI2, dCV, cI1.negate(), dcZV, jacobian[2], 3);
1275 
1276 
1277         // dPA
1278         final T cP1     =  y.multiply(oOm);
1279         final T cP2     =  x.negate().multiply(oOm);
1280         final T cP3     =  mx.multiply(cP1).add(my.multiply(cP2)).negate();
1281         final T cP4     =  cP3.multiply(oOm);
1282         final T cP5     =  r2.multiply(sinI).multiply(sinI).negate().reciprocal();
1283         final T cP6     = z.multiply(cP5);
1284         final T cP7     = cP3.multiply(cP5);
1285         final FieldVector3D<T> dacP  = new FieldVector3D<>(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new FieldVector3D<>(my.negate(), mx, getZero()));
1286         final FieldVector3D<T> dacV  = new FieldVector3D<>(cP1, dcXV, cP2, dcYV, cP4, dCV);
1287         final FieldVector3D<T> dpoP  = new FieldVector3D<>(cP6, dacP, cP7, this.plusK);
1288         final FieldVector3D<T> dpoV  = new FieldVector3D<>(cP6, dacV);
1289 
1290         final T re2     = r2.multiply(e.square());
1291         final T recOre2 = p.subtract(r).divide(re2);
1292         final T resOre2 = pv.multiply(mOMu).divide(re2);
1293         final FieldVector3D<T> dreP  = new FieldVector3D<>(mOMu, velocity, pv.divide(mu), dCP);
1294         final FieldVector3D<T> dreV  = new FieldVector3D<>(mOMu, position, pv.divide(mu), dCV);
1295         final FieldVector3D<T> davP  = new FieldVector3D<>(resOre2.negate(), dpP, recOre2, dreP, resOre2.divide(r), position);
1296         final FieldVector3D<T> davV  = new FieldVector3D<>(resOre2.negate(), dpV, recOre2, dreV);
1297         fillHalfRow(getOne(), dpoP, getOne().negate(), davP, jacobian[3], 0);
1298         fillHalfRow(getOne(), dpoV, getOne().negate(), davV, jacobian[3], 3);
1299 
1300         // dRAAN
1301         final T cO0 = cI1.square();
1302         final T cO1 =  mx.multiply(cO0);
1303         final T cO2 =  my.negate().multiply(cO0);
1304         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
1305         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);
1306 
1307         // dM
1308         final T s2a    = pv.divide(absA.multiply(2));
1309         final T oObux  = m.square().add(absA.multiply(mu)).sqrt().reciprocal();
1310         final T scasbu = pv.multiply(oObux);
1311         final FieldVector3D<T> dauP = new FieldVector3D<>(sqrtMuA.reciprocal(), velocity, s2a.negate().divide(sqrtMuA), vectorAR);
1312         final FieldVector3D<T> dauV = new FieldVector3D<>(sqrtMuA.reciprocal(), position, s2a.negate().divide(sqrtMuA), vectorARDot);
1313         final FieldVector3D<T> dbuP = new FieldVector3D<>(oObux.multiply(mu.divide(2.)), vectorAR,    m.multiply(oObux), dCP);
1314         final FieldVector3D<T> dbuV = new FieldVector3D<>(oObux.multiply(mu.divide(2.)), vectorARDot, m.multiply(oObux), dCV);
1315         final FieldVector3D<T> dcuP = new FieldVector3D<>(oObux, velocity, scasbu.negate().multiply(oObux), dbuP);
1316         final FieldVector3D<T> dcuV = new FieldVector3D<>(oObux, position, scasbu.negate().multiply(oObux), dbuV);
1317         fillHalfRow(getOne(), dauP, e.negate().divide(rOa.add(1)), dcuP, jacobian[5], 0);
1318         fillHalfRow(getOne(), dauV, e.negate().divide(rOa.add(1)), dcuV, jacobian[5], 3);
1319 
1320         return jacobian;
1321 
1322     }
1323 
1324     /** {@inheritDoc} */
1325     @Override
1326     protected T[][] computeJacobianEccentricWrtCartesian() {
1327         if (isElliptical()) {
1328             return computeJacobianEccentricWrtCartesianElliptical();
1329         } else {
1330             return computeJacobianEccentricWrtCartesianHyperbolic();
1331         }
1332     }
1333 
1334     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1335      * <p>
1336      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1337      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1338      * yDot for j=4, zDot for j=5).
1339      * </p>
1340      * @return 6x6 Jacobian matrix
1341      */
1342     private T[][] computeJacobianEccentricWrtCartesianElliptical() {
1343 
1344         // start by computing the Jacobian with mean angle
1345         final T[][] jacobian = computeJacobianMeanWrtCartesianElliptical();
1346 
1347         // Differentiating the Kepler equation M = E - e sin E leads to:
1348         // dM = (1 - e cos E) dE - sin E de
1349         // which is inverted and rewritten as:
1350         // dE = a/r dM + sin E a/r de
1351         final FieldSinCos<T> scE = FastMath.sinCos(getEccentricAnomaly());
1352         final T aOr              = e.negate().multiply(scE.cos()).add(1).reciprocal();
1353 
1354         // update anomaly row
1355         final T[] eRow           = jacobian[1];
1356         final T[] anomalyRow     = jacobian[5];
1357         for (int j = 0; j < anomalyRow.length; ++j) {
1358             anomalyRow[j] = aOr.multiply(anomalyRow[j].add(scE.sin().multiply(eRow[j])));
1359         }
1360 
1361         return jacobian;
1362 
1363     }
1364 
1365     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1366      * <p>
1367      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1368      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1369      * yDot for j=4, zDot for j=5).
1370      * </p>
1371      * @return 6x6 Jacobian matrix
1372      */
1373     private T[][] computeJacobianEccentricWrtCartesianHyperbolic() {
1374 
1375         // start by computing the Jacobian with mean angle
1376         final T[][] jacobian = computeJacobianMeanWrtCartesianHyperbolic();
1377 
1378         // Differentiating the Kepler equation M = e sinh H - H leads to:
1379         // dM = (e cosh H - 1) dH + sinh H de
1380         // which is inverted and rewritten as:
1381         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
1382         final T H      = getEccentricAnomaly();
1383         final T coshH  = H.cosh();
1384         final T sinhH  = H.sinh();
1385         final T absaOr = e.multiply(coshH).subtract(1).reciprocal();
1386         // update anomaly row
1387         final T[] eRow       = jacobian[1];
1388         final T[] anomalyRow = jacobian[5];
1389 
1390         for (int j = 0; j < anomalyRow.length; ++j) {
1391             anomalyRow[j] = absaOr.multiply(anomalyRow[j].subtract(sinhH.multiply(eRow[j])));
1392 
1393         }
1394 
1395         return jacobian;
1396 
1397     }
1398 
1399     /** {@inheritDoc} */
1400     @Override
1401     protected T[][] computeJacobianTrueWrtCartesian() {
1402         if (isElliptical()) {
1403             return computeJacobianTrueWrtCartesianElliptical();
1404         } else {
1405             return computeJacobianTrueWrtCartesianHyperbolic();
1406         }
1407     }
1408 
1409     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1410      * <p>
1411      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1412      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1413      * yDot for j=4, zDot for j=5).
1414      * </p>
1415      * @return 6x6 Jacobian matrix
1416      */
1417     private T[][] computeJacobianTrueWrtCartesianElliptical() {
1418 
1419         // start by computing the Jacobian with eccentric angle
1420         final T[][] jacobian = computeJacobianEccentricWrtCartesianElliptical();
1421         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
1422         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
1423         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
1424         // which is inverted and rewritten as:
1425         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
1426         final T e2               = e.square();
1427         final T oMe2             = e2.negate().add(1);
1428         final T epsilon          = oMe2.sqrt();
1429         final FieldSinCos<T> scE = FastMath.sinCos(getEccentricAnomaly());
1430         final T aOr              = e.multiply(scE.cos()).negate().add(1).reciprocal();
1431         final T aFactor          = epsilon.multiply(aOr);
1432         final T eFactor          = scE.sin().multiply(aOr).divide(epsilon);
1433 
1434         // update anomaly row
1435         final T[] eRow           = jacobian[1];
1436         final T[] anomalyRow     = jacobian[5];
1437         for (int j = 0; j < anomalyRow.length; ++j) {
1438             anomalyRow[j] = aFactor.multiply(anomalyRow[j]).add(eFactor.multiply(eRow[j]));
1439         }
1440         return jacobian;
1441 
1442     }
1443 
1444     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
1445      * <p>
1446      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
1447      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
1448      * yDot for j=4, zDot for j=5).
1449      * </p>
1450      * @return 6x6 Jacobian matrix
1451      */
1452     private T[][] computeJacobianTrueWrtCartesianHyperbolic() {
1453 
1454         // start by computing the Jacobian with eccentric angle
1455         final T[][] jacobian = computeJacobianEccentricWrtCartesianHyperbolic();
1456 
1457         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
1458         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
1459         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
1460         // which is inverted and rewritten as:
1461         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
1462         final T e2       = e.square();
1463         final T e2Mo     = e2.subtract(1);
1464         final T epsilon  = e2Mo.sqrt();
1465         final T H        = getEccentricAnomaly();
1466         final T coshH    = H.cosh();
1467         final T sinhH    = H.sinh();
1468         final T aOr      = e.multiply(coshH).subtract(1).reciprocal();
1469         final T aFactor  = epsilon.multiply(aOr);
1470         final T eFactor  = sinhH.multiply(aOr).divide(epsilon);
1471 
1472         // update anomaly row
1473         final T[] eRow           = jacobian[1];
1474         final T[] anomalyRow     = jacobian[5];
1475         for (int j = 0; j < anomalyRow.length; ++j) {
1476             anomalyRow[j] = aFactor.multiply(anomalyRow[j]).subtract(eFactor.multiply(eRow[j]));
1477         }
1478 
1479         return jacobian;
1480 
1481     }
1482 
1483     /** {@inheritDoc} */
1484     @Override
1485     public void addKeplerContribution(final PositionAngleType type, final T gm,
1486                                       final T[] pDot) {
1487         pDot[5] = pDot[5].add(computeKeplerianAnomalyDot(type, a, e, gm, cachedAnomaly, cachedPositionAngleType));
1488     }
1489 
1490     /**
1491      * Compute rate of argument of latitude.
1492      * @param type position angle type of output rate
1493      * @param a semi major axis
1494      * @param e eccentricity
1495      * @param mu mu
1496      * @param anomaly anomaly
1497      * @param cachedType position angle type of input anomaly
1498      * @param <T> field type
1499      * @return first-order time derivative for anomaly
1500      * @since 12.2
1501      */
1502     private static <T extends CalculusFieldElement<T>> T computeKeplerianAnomalyDot(final PositionAngleType type, final T a, final T e,
1503                                                                                     final T mu, final T anomaly, final PositionAngleType cachedType) {
1504         final T absA = a.abs();
1505         final T n    = absA.reciprocal().multiply(mu).sqrt().divide(absA);
1506         if (type == PositionAngleType.MEAN) {
1507             return n;
1508         }
1509         final T ksi;
1510         final T oMe2;
1511         final T trueAnomaly = FieldKeplerianAnomalyUtility.convertAnomaly(cachedType, anomaly, e, PositionAngleType.TRUE);
1512         if (type == PositionAngleType.ECCENTRIC) {
1513             oMe2 = e.square().negate().add(1).abs();
1514             ksi = e.multiply(trueAnomaly.cos()).add(1);
1515             return n.multiply(ksi).divide(oMe2);
1516         } else {
1517             oMe2 = e.square().negate().add(1).abs();
1518             ksi  = e.multiply(trueAnomaly.cos()).add(1);
1519             return n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()));
1520         }
1521     }
1522 
1523     /**  Returns a string representation of this Keplerian parameters object.
1524      * @return a string representation of this object
1525      */
1526     public String toString() {
1527         return "Keplerian parameters: " + '{' +
1528                 "a: " + a.getReal() +
1529                 "; e: " + e.getReal() +
1530                 "; i: " + FastMath.toDegrees(i.getReal()) +
1531                 "; pa: " + FastMath.toDegrees(pa.getReal()) +
1532                 "; raan: " + FastMath.toDegrees(raan.getReal()) +
1533                 "; v: " + FastMath.toDegrees(getTrueAnomaly().getReal()) +
1534                 ";}";
1535     }
1536 
1537     /** {@inheritDoc} */
1538     @Override
1539     public PositionAngleType getCachedPositionAngleType() {
1540         return cachedPositionAngleType;
1541     }
1542 
1543     /** {@inheritDoc} */
1544     @Override
1545     public boolean hasNonKeplerianRates() {
1546         return hasNonKeplerianAcceleration();
1547     }
1548 
1549     /** {@inheritDoc} */
1550     @Override
1551     public FieldKeplerianOrbit<T> withKeplerianRates() {
1552         return new FieldKeplerianOrbit<>(getKeplerianParameters(), getFrame(), getDate(), getMu());
1553     }
1554 
1555     /** Check if the given parameter is within an acceptable range.
1556      * The bounds are inclusive: an exception is raised when either of those conditions are met:
1557      * <ul>
1558      *     <li>The parameter is strictly greater than upperBound</li>
1559      *     <li>The parameter is strictly lower than lowerBound</li>
1560      * </ul>
1561      * <p>
1562      * In either of these cases, an OrekitException is raised.
1563      * </p>
1564      * @param parameterName name of the parameter
1565      * @param parameter value of the parameter
1566      * @param lowerBound lower bound of the acceptable range (inclusive)
1567      * @param upperBound upper bound of the acceptable range (inclusive)
1568      */
1569     private void checkParameterRangeInclusive(final String parameterName, final double parameter,
1570                                               final double lowerBound, final double upperBound) {
1571         if (parameter < lowerBound || parameter > upperBound) {
1572             throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName,
1573                                       parameter, lowerBound, upperBound);
1574         }
1575     }
1576 
1577     /** {@inheritDoc} */
1578     @Override
1579     public KeplerianOrbit toOrbit() {
1580         final KeplerianParameters elements = new KeplerianParameters(a.getReal(), e.getReal(), i.getReal(),
1581                 pa.getReal(), raan.getReal(), cachedAnomaly.getReal(), cachedPositionAngleType);
1582         return new KeplerianOrbit(elements,
1583                                   aDot.getReal(), eDot.getReal(), iDot.getReal(), paDot.getReal(), raanDot.getReal(),
1584                                   cachedAnomalyDot.getReal(), cachedPositionAngleType,
1585                                   getFrame(), getDate().toAbsoluteDate(), getMu().getReal());
1586     }
1587 
1588 
1589 }