1   /* Copyright 2002-2024 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.orbits;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
22  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
23  import org.hipparchus.util.FastMath;
24  import org.hipparchus.util.FieldSinCos;
25  import org.hipparchus.util.MathArrays;
26  import org.orekit.errors.OrekitIllegalArgumentException;
27  import org.orekit.errors.OrekitInternalError;
28  import org.orekit.errors.OrekitMessages;
29  import org.orekit.frames.Frame;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.utils.FieldPVCoordinates;
32  import org.orekit.utils.TimeStampedFieldPVCoordinates;
33  
34  
35  /**
36   * This class handles circular orbital parameters.
37  
38   * <p>
39   * The parameters used internally are the circular elements which can be
40   * related to Keplerian elements as follows:
41   *   <ul>
42   *     <li>a</li>
43   *     <li>e<sub>x</sub> = e cos(ω)</li>
44   *     <li>e<sub>y</sub> = e sin(ω)</li>
45   *     <li>i</li>
46   *     <li>Ω</li>
47   *     <li>α<sub>v</sub> = v + ω</li>
48   *   </ul>
49   * where Ω stands for the Right Ascension of the Ascending Node and
50   * α<sub>v</sub> stands for the true latitude argument
51   * <p>
52   * The conversion equations from and to Keplerian elements given above hold only
53   * when both sides are unambiguously defined, i.e. when orbit is neither equatorial
54   * nor circular. When orbit is circular (but not equatorial), the circular
55   * parameters are still unambiguously defined whereas some Keplerian elements
56   * (more precisely ω and Ω) become ambiguous. When orbit is equatorial,
57   * neither the Keplerian nor the circular parameters can be defined unambiguously.
58   * {@link EquinoctialOrbit equinoctial orbits} is the recommended way to represent
59   * orbits.
60   * </p>
61   * <p>
62   * The instance <code>CircularOrbit</code> is guaranteed to be immutable.
63   * </p>
64   * @see    Orbit
65   * @see    KeplerianOrbit
66   * @see    CartesianOrbit
67   * @see    EquinoctialOrbit
68   * @author Luc Maisonobe
69   * @author Fabien Maussion
70   * @author V&eacute;ronique Pommier-Maurussane
71   * @since 9.0
72   * @param <T> type of the field elements
73   */
74  
75  public class FieldCircularOrbit<T extends CalculusFieldElement<T>> extends FieldOrbit<T> implements PositionAngleBased {
76  
77      /** Semi-major axis (m). */
78      private final T a;
79  
80      /** First component of the circular eccentricity vector. */
81      private final T ex;
82  
83      /** Second component of the circular eccentricity vector. */
84      private final T ey;
85  
86      /** Inclination (rad). */
87      private final T i;
88  
89      /** Right Ascension of Ascending Node (rad). */
90      private final T raan;
91  
92      /** Cached latitude argument (rad). */
93      private final T cachedAlpha;
94  
95      /** Type of cached position angle (latitude argument). */
96      private final PositionAngleType cachedPositionAngleType;
97  
98      /** Semi-major axis derivative (m/s). */
99      private final T aDot;
100 
101     /** First component of the circular eccentricity vector derivative. */
102     private final T exDot;
103 
104     /** Second component of the circular eccentricity vector derivative. */
105     private final T eyDot;
106 
107     /** Inclination derivative (rad/s). */
108     private final T iDot;
109 
110     /** Right Ascension of Ascending Node derivative (rad/s). */
111     private final T raanDot;
112 
113     /** True latitude argument derivative (rad/s). */
114     private final T cachedAlphaDot;
115 
116     /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */
117     private FieldPVCoordinates<T> partialPV;
118 
119     /** Creates a new instance.
120      * @param a  semi-major axis (m)
121      * @param ex e cos(ω), first component of circular eccentricity vector
122      * @param ey e sin(ω), second component of circular eccentricity vector
123      * @param i inclination (rad)
124      * @param raan right ascension of ascending node (Ω, rad)
125      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
126      * @param type type of latitude argument
127      * @param cachedPositionAngleType type of cached latitude argument
128      * @param frame the frame in which are defined the parameters
129      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
130      * @param date date of the orbital parameters
131      * @param mu central attraction coefficient (m³/s²)
132      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
133      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
134      * @since 12.1
135      */
136     public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan,
137                               final T alpha, final PositionAngleType type,
138                               final PositionAngleType cachedPositionAngleType,
139                               final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
140         throws IllegalArgumentException {
141         this(a, ex, ey, i, raan, alpha,
142              null, null, null, null, null, null,
143              type, cachedPositionAngleType, frame, date, mu);
144     }
145 
146     /** Creates a new instance without derivatives and with cached position angle same as value inputted.
147      * @param a  semi-major axis (m)
148      * @param ex e cos(ω), first component of circular eccentricity vector
149      * @param ey e sin(ω), second component of circular eccentricity vector
150      * @param i inclination (rad)
151      * @param raan right ascension of ascending node (Ω, rad)
152      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
153      * @param type type of latitude argument
154      * @param frame the frame in which are defined the parameters
155      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
156      * @param date date of the orbital parameters
157      * @param mu central attraction coefficient (m³/s²)
158      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
159      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
160      */
161     public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan,
162                               final T alpha, final PositionAngleType type,
163                               final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
164             throws IllegalArgumentException {
165         this(a, ex, ey, i, raan, alpha, type, type, frame, date, mu);
166     }
167 
168     /** Creates a new instance.
169      * @param a  semi-major axis (m)
170      * @param ex e cos(ω), first component of circular eccentricity vector
171      * @param ey e sin(ω), second component of circular eccentricity vector
172      * @param i inclination (rad)
173      * @param raan right ascension of ascending node (Ω, rad)
174      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
175      * @param aDot  semi-major axis derivative (m/s)
176      * @param exDot d(e cos(ω))/dt, first component of circular eccentricity vector derivative
177      * @param eyDot d(e sin(ω))/dt, second component of circular eccentricity vector derivative
178      * @param iDot inclination  derivative(rad/s)
179      * @param raanDot right ascension of ascending node derivative (rad/s)
180      * @param alphaDot  d(an + ω), mean, eccentric or true latitude argument derivative (rad/s)
181      * @param type type of latitude argument
182      * @param frame the frame in which are defined the parameters
183      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
184      * @param date date of the orbital parameters
185      * @param mu central attraction coefficient (m³/s²)
186      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
187      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
188      */
189     public FieldCircularOrbit(final T a, final T ex, final T ey,
190                               final T i, final T raan, final T alpha,
191                               final T aDot, final T exDot, final T eyDot,
192                               final T iDot, final T raanDot, final T alphaDot, final PositionAngleType type,
193                               final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
194             throws IllegalArgumentException {
195         this(a, ex, ey, i, raan, alpha, aDot, exDot, eyDot, iDot, raanDot, alphaDot, type, type, frame, date, mu);
196     }
197 
198     /** Creates a new instance.
199      * @param a  semi-major axis (m)
200      * @param ex e cos(ω), first component of circular eccentricity vector
201      * @param ey e sin(ω), second component of circular eccentricity vector
202      * @param i inclination (rad)
203      * @param raan right ascension of ascending node (Ω, rad)
204      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
205      * @param aDot  semi-major axis derivative (m/s)
206      * @param exDot d(e cos(ω))/dt, first component of circular eccentricity vector derivative
207      * @param eyDot d(e sin(ω))/dt, second component of circular eccentricity vector derivative
208      * @param iDot inclination  derivative(rad/s)
209      * @param raanDot right ascension of ascending node derivative (rad/s)
210      * @param alphaDot  d(an + ω), mean, eccentric or true latitude argument derivative (rad/s)
211      * @param type type of latitude argument
212      * @param cachedPositionAngleType type of cached latitude argument
213      * @param frame the frame in which are defined the parameters
214      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
215      * @param date date of the orbital parameters
216      * @param mu central attraction coefficient (m³/s²)
217      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
218      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
219      * @since 12.1
220      */
221     public FieldCircularOrbit(final T a, final T ex, final T ey,
222                               final T i, final T raan, final T alpha,
223                               final T aDot, final T exDot, final T eyDot,
224                               final T iDot, final T raanDot, final T alphaDot,
225                               final PositionAngleType type, final PositionAngleType cachedPositionAngleType,
226                               final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
227         throws IllegalArgumentException {
228         super(frame, date, mu);
229         if (ex.getReal() * ex.getReal() + ey.getReal() * ey.getReal() >= 1.0) {
230             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
231                                                      getClass().getName());
232         }
233 
234         this.a       =  a;
235         this.aDot    =  aDot;
236         this.ex      = ex;
237         this.exDot   = exDot;
238         this.ey      = ey;
239         this.eyDot   = eyDot;
240         this.i       = i;
241         this.iDot    = iDot;
242         this.raan    = raan;
243         this.raanDot = raanDot;
244         this.cachedPositionAngleType = cachedPositionAngleType;
245 
246         if (hasDerivatives()) {
247             final FieldUnivariateDerivative1<T> alphaUD = initializeCachedAlpha(alpha, alphaDot, type);
248             this.cachedAlpha = alphaUD.getValue();
249             this.cachedAlphaDot = alphaUD.getFirstDerivative();
250         } else {
251             this.cachedAlpha = initializeCachedAlpha(alpha, type);
252             this.cachedAlphaDot = null;
253         }
254 
255         partialPV   = null;
256 
257     }
258 
259     /** Constructor from Cartesian parameters.
260      *
261      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
262      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
263      * use {@code mu} and the position to compute the acceleration, including
264      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
265      *
266      * @param pvCoordinates the {@link FieldPVCoordinates} in inertial frame
267      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
268      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
269      * @param mu central attraction coefficient (m³/s²)
270      * @exception IllegalArgumentException if frame is not a {@link
271      * Frame#isPseudoInertial pseudo-inertial frame}
272      */
273     public FieldCircularOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
274                               final Frame frame, final T mu)
275         throws IllegalArgumentException {
276         super(pvCoordinates, frame, mu);
277         this.cachedPositionAngleType = PositionAngleType.TRUE;
278 
279         // compute semi-major axis
280         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
281         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
282         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
283         final T r2 = pvP.getNormSq();
284         final T r  = r2.sqrt();
285         final T V2 = pvV.getNormSq();
286         final T rV2OnMu = r.multiply(V2).divide(mu);
287 
288         a = r.divide(rV2OnMu.negate().add(2));
289 
290         if (!isElliptical()) {
291             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
292                                                      getClass().getName());
293         }
294 
295         // compute inclination
296         final FieldVector3D<T> momentum = pvCoordinates.getMomentum();
297         final FieldVector3D<T> plusK = FieldVector3D.getPlusK(r.getField());
298         i = FieldVector3D.angle(momentum, plusK);
299 
300         // compute right ascension of ascending node
301         final FieldVector3D<T> node  = FieldVector3D.crossProduct(plusK, momentum);
302         raan = node.getY().atan2(node.getX());
303 
304         // 2D-coordinates in the canonical frame
305         final FieldSinCos<T> scRaan = FastMath.sinCos(raan);
306         final FieldSinCos<T> scI    = FastMath.sinCos(i);
307         final T xP      = pvP.getX();
308         final T yP      = pvP.getY();
309         final T zP      = pvP.getZ();
310         final T x2      = (xP.multiply(scRaan.cos()).add(yP .multiply(scRaan.sin()))).divide(a);
311         final T y2      = (yP.multiply(scRaan.cos()).subtract(xP.multiply(scRaan.sin()))).multiply(scI.cos()).add(zP.multiply(scI.sin())).divide(a);
312 
313         // compute eccentricity vector
314         final T eSE    = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
315         final T eCE    = rV2OnMu.subtract(1);
316         final T e2     = eCE.multiply(eCE).add(eSE.multiply(eSE));
317         final T f      = eCE.subtract(e2);
318         final T g      = eSE.multiply(e2.negate().add(1).sqrt());
319         final T aOnR   = a.divide(r);
320         final T a2OnR2 = aOnR.square();
321         ex = a2OnR2.multiply(f.multiply(x2).add(g.multiply(y2)));
322         ey = a2OnR2.multiply(f.multiply(y2).subtract(g.multiply(x2)));
323 
324         // compute latitude argument
325         final T beta = (ex.multiply(ex).add(ey.multiply(ey)).negate().add(1)).sqrt().add(1).reciprocal();
326         cachedAlpha = FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, y2.add(ey).add(eSE.multiply(beta).multiply(ex)).atan2(x2.add(ex).subtract(eSE.multiply(beta).multiply(ey)))
327         );
328 
329         partialPV = pvCoordinates;
330 
331         if (hasNonKeplerianAcceleration(pvCoordinates, mu)) {
332             // we have a relevant acceleration, we can compute derivatives
333 
334             final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6);
335             getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian);
336 
337             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP);
338             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
339             final T   aX                       = nonKeplerianAcceleration.getX();
340             final T   aY                       = nonKeplerianAcceleration.getY();
341             final T   aZ                       = nonKeplerianAcceleration.getZ();
342             aDot    = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
343             exDot   = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
344             eyDot   = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
345             iDot    = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
346             raanDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));
347 
348             // in order to compute true anomaly derivative, we must compute
349             // mean anomaly derivative including Keplerian motion and convert to true anomaly
350             final T alphaMDot = getKeplerianMeanMotion().
351                                 add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
352             final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex, exDot);
353             final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey, eyDot);
354             final FieldUnivariateDerivative1<T> alphaMUD = new FieldUnivariateDerivative1<>(getAlphaM(), alphaMDot);
355             final FieldUnivariateDerivative1<T> alphavUD = FieldCircularLatitudeArgumentUtility.meanToTrue(exUD, eyUD, alphaMUD);
356             cachedAlphaDot = alphavUD.getFirstDerivative();
357 
358         } else {
359             // acceleration is either almost zero or NaN,
360             // we assume acceleration was not known
361             // we don't set up derivatives
362             aDot      = null;
363             exDot     = null;
364             eyDot     = null;
365             iDot      = null;
366             raanDot   = null;
367             cachedAlphaDot = null;
368         }
369 
370     }
371 
372     /** Constructor from Cartesian parameters.
373      *
374      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
375      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
376      * use {@code mu} and the position to compute the acceleration, including
377      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
378      *
379      * @param PVCoordinates the {@link FieldPVCoordinates} in inertial frame
380      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
381      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
382      * @param date date of the orbital parameters
383      * @param mu central attraction coefficient (m³/s²)
384      * @exception IllegalArgumentException if frame is not a {@link
385      * Frame#isPseudoInertial pseudo-inertial frame}
386      */
387     public FieldCircularOrbit(final FieldPVCoordinates<T> PVCoordinates, final Frame frame,
388                               final FieldAbsoluteDate<T> date, final T mu)
389         throws IllegalArgumentException {
390         this(new TimeStampedFieldPVCoordinates<>(date, PVCoordinates), frame, mu);
391     }
392 
393     /** Constructor from any kind of orbital parameters.
394      * @param op orbital parameters to copy
395      */
396     public FieldCircularOrbit(final FieldOrbit<T> op) {
397         super(op.getFrame(), op.getDate(), op.getMu());
398         a    = op.getA();
399         i    = op.getI();
400         final T hx = op.getHx();
401         final T hy = op.getHy();
402         final T h2 = hx.square().add(hy.square());
403         final T h  = h2.sqrt();
404         raan = hy.atan2(hx);
405         final FieldSinCos<T> scRaan = FastMath.sinCos(raan);
406         final T cosRaan = h.getReal() == 0 ? scRaan.cos() : hx.divide(h);
407         final T sinRaan = h.getReal() == 0 ? scRaan.sin() : hy.divide(h);
408         final T equiEx = op.getEquinoctialEx();
409         final T equiEy = op.getEquinoctialEy();
410         ex   = equiEx.multiply(cosRaan).add(equiEy.multiply(sinRaan));
411         ey   = equiEy.multiply(cosRaan).subtract(equiEx.multiply(sinRaan));
412         cachedPositionAngleType = PositionAngleType.TRUE;
413         cachedAlpha = op.getLv().subtract(raan);
414 
415         if (op.hasDerivatives()) {
416             aDot      = op.getADot();
417             final T      hxDot = op.getHxDot();
418             final T      hyDot = op.getHyDot();
419             iDot      = cosRaan.multiply(hxDot).add(sinRaan.multiply(hyDot)).multiply(2).divide(h2.add(1));
420             raanDot   = hx.multiply(hyDot).subtract(hy.multiply(hxDot)).divide(h2);
421             final T equiExDot = op.getEquinoctialExDot();
422             final T equiEyDot = op.getEquinoctialEyDot();
423             exDot     = equiExDot.add(equiEy.multiply(raanDot)).multiply(cosRaan).
424                         add(equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(sinRaan));
425             eyDot     = equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(cosRaan).
426                         subtract(equiExDot.add(equiEy.multiply(raanDot)).multiply(sinRaan));
427             cachedAlphaDot = op.getLvDot().subtract(raanDot);
428         } else {
429             aDot      = null;
430             exDot     = null;
431             eyDot     = null;
432             iDot      = null;
433             raanDot   = null;
434             cachedAlphaDot = null;
435         }
436 
437         partialPV = null;
438 
439     }
440 
441     /** Constructor from Field and CircularOrbit.
442      * <p>Build a FieldCircularOrbit from non-Field CircularOrbit.</p>
443      * @param field CalculusField to base object on
444      * @param op non-field orbit with only "constant" terms
445      * @since 12.0
446      */
447     public FieldCircularOrbit(final Field<T> field, final CircularOrbit op) {
448         super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().newInstance(op.getMu()));
449 
450         a    = getZero().newInstance(op.getA());
451         i    = getZero().newInstance(op.getI());
452         raan = getZero().newInstance(op.getRightAscensionOfAscendingNode());
453         ex   = getZero().newInstance(op.getCircularEx());
454         ey   = getZero().newInstance(op.getCircularEy());
455         cachedPositionAngleType = op.getCachedPositionAngleType();
456         cachedAlpha = getZero().newInstance(op.getAlpha(cachedPositionAngleType));
457 
458         if (op.hasDerivatives()) {
459             aDot      = getZero().newInstance(op.getADot());
460             iDot      = getZero().newInstance(op.getIDot());
461             raanDot   = getZero().newInstance(op.getRightAscensionOfAscendingNodeDot());
462             exDot     = getZero().newInstance(op.getCircularExDot());
463             eyDot     = getZero().newInstance(op.getCircularEyDot());
464             cachedAlphaDot = getZero().newInstance(op.getAlphaDot(cachedPositionAngleType));
465         } else {
466             aDot      = null;
467             exDot     = null;
468             eyDot     = null;
469             iDot      = null;
470             raanDot   = null;
471             cachedAlphaDot = null;
472         }
473 
474         partialPV = null;
475 
476     }
477 
478     /** Constructor from Field and Orbit.
479      * <p>Build a FieldCircularOrbit from any non-Field Orbit.</p>
480      * @param field CalculusField to base object on
481      * @param op non-field orbit with only "constant" terms
482      * @since 12.0
483      */
484     public FieldCircularOrbit(final Field<T> field, final Orbit op) {
485         this(field, (CircularOrbit) OrbitType.CIRCULAR.convertType(op));
486     }
487 
488     /** {@inheritDoc} */
489     @Override
490     public OrbitType getType() {
491         return OrbitType.CIRCULAR;
492     }
493 
494     /** {@inheritDoc} */
495     @Override
496     public T getA() {
497         return a;
498     }
499 
500     /** {@inheritDoc} */
501     @Override
502     public T getADot() {
503         return aDot;
504     }
505 
506     /** {@inheritDoc} */
507     @Override
508     public T getEquinoctialEx() {
509         final FieldSinCos<T> sc = FastMath.sinCos(raan);
510         return ex.multiply(sc.cos()).subtract(ey.multiply(sc.sin()));
511     }
512 
513     /** {@inheritDoc} */
514     @Override
515     public T getEquinoctialExDot() {
516 
517         if (!hasDerivatives()) {
518             return null;
519         }
520 
521         final FieldSinCos<T> sc = FastMath.sinCos(raan);
522         return exDot.subtract(ey.multiply(raanDot)).multiply(sc.cos()).
523                subtract(eyDot.add(ex.multiply(raanDot)).multiply(sc.sin()));
524 
525     }
526 
527     /** {@inheritDoc} */
528     @Override
529     public T getEquinoctialEy() {
530         final FieldSinCos<T> sc = FastMath.sinCos(raan);
531         return ey.multiply(sc.cos()).add(ex.multiply(sc.sin()));
532     }
533 
534     /** {@inheritDoc} */
535     @Override
536     public T getEquinoctialEyDot() {
537 
538         if (!hasDerivatives()) {
539             return null;
540         }
541 
542         final FieldSinCos<T> sc = FastMath.sinCos(raan);
543         return eyDot.add(ex.multiply(raanDot)).multiply(sc.cos()).
544                add(exDot.subtract(ey.multiply(raanDot)).multiply(sc.sin()));
545 
546     }
547 
548     /** Get the first component of the circular eccentricity vector.
549      * @return ex = e cos(ω), first component of the circular eccentricity vector
550      */
551     public T getCircularEx() {
552         return ex;
553     }
554 
555     /** Get the first component of the circular eccentricity vector derivative.
556      * @return d(ex)/dt = d(e cos(ω))/dt, first component of the circular eccentricity vector derivative
557      */
558     public T getCircularExDot() {
559         return exDot;
560     }
561 
562     /** Get the second component of the circular eccentricity vector.
563      * @return ey = e sin(ω), second component of the circular eccentricity vector
564      */
565     public T getCircularEy() {
566         return ey;
567     }
568 
569     /** Get the second component of the circular eccentricity vector derivative.
570      * @return d(ey)/dt = d(e sin(ω))/dt, second component of the circular eccentricity vector derivative
571      */
572     public T getCircularEyDot() {
573         return eyDot;
574     }
575 
576     /** {@inheritDoc} */
577     @Override
578     public T getHx() {
579         // Check for equatorial retrograde orbit
580         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
581             return getZero().add(Double.NaN);
582         }
583         return raan.cos().multiply(i.divide(2).tan());
584     }
585 
586     /** {@inheritDoc} */
587     @Override
588     public T getHxDot() {
589 
590         if (!hasDerivatives()) {
591             return null;
592         }
593 
594         // Check for equatorial retrograde orbit
595         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
596             return getZero().add(Double.NaN);
597         }
598 
599         final FieldSinCos<T> sc = FastMath.sinCos(raan);
600         final T tan             = i.multiply(0.5).tan();
601         return sc.cos().multiply(0.5).multiply(tan.multiply(tan).add(1)).multiply(iDot).
602                subtract(sc.sin().multiply(tan).multiply(raanDot));
603 
604     }
605 
606     /** {@inheritDoc} */
607     @Override
608     public T getHy() {
609         // Check for equatorial retrograde orbit
610         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
611             return getZero().add(Double.NaN);
612         }
613         return raan.sin().multiply(i.divide(2).tan());
614     }
615 
616     /** {@inheritDoc} */
617     @Override
618     public T getHyDot() {
619 
620         if (!hasDerivatives()) {
621             return null;
622         }
623 
624         // Check for equatorial retrograde orbit
625         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
626             return getZero().add(Double.NaN);
627         }
628 
629         final FieldSinCos<T> sc = FastMath.sinCos(raan);
630         final T tan     = i.multiply(0.5).tan();
631         return sc.sin().multiply(0.5).multiply(tan.multiply(tan).add(1)).multiply(iDot).
632                add(sc.cos().multiply(tan).multiply(raanDot));
633 
634     }
635 
636     /** Get the true latitude argument.
637      * @return v + ω true latitude argument (rad)
638      */
639     public T getAlphaV() {
640         switch (cachedPositionAngleType) {
641             case TRUE:
642                 return cachedAlpha;
643 
644             case ECCENTRIC:
645                 return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, cachedAlpha);
646 
647             case MEAN:
648                 return FieldCircularLatitudeArgumentUtility.meanToTrue(ex, ey, cachedAlpha);
649 
650             default:
651                 throw new OrekitInternalError(null);
652         }
653     }
654 
655     /** Get the true latitude argument derivative.
656      * @return d(v + ω)/dt true latitude argument derivative (rad/s)
657      */
658     public T getAlphaVDot() {
659 
660         if (!hasDerivatives()) {
661             return null;
662         }
663         switch (cachedPositionAngleType) {
664             case ECCENTRIC:
665                 final FieldUnivariateDerivative1<T> alphaEUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot);
666                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
667                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
668                 final FieldUnivariateDerivative1<T> alphaVUD = FieldCircularLatitudeArgumentUtility.eccentricToTrue(exUD, eyUD,
669                         alphaEUD);
670                 return alphaVUD.getFirstDerivative();
671 
672             case TRUE:
673                 return cachedAlphaDot;
674 
675             case MEAN:
676                 final FieldUnivariateDerivative1<T> alphaMUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot);
677                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
678                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
679                 final FieldUnivariateDerivative1<T> alphaVUD2 = FieldCircularLatitudeArgumentUtility.meanToTrue(exUD2,
680                         eyUD2, alphaMUD);
681                 return alphaVUD2.getFirstDerivative();
682 
683             default:
684                 throw new OrekitInternalError(null);
685         }
686     }
687 
688     /** Get the eccentric latitude argument.
689      * @return E + ω eccentric latitude argument (rad)
690      */
691     public T getAlphaE() {
692         switch (cachedPositionAngleType) {
693             case TRUE:
694                 return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, cachedAlpha);
695 
696             case ECCENTRIC:
697                 return cachedAlpha;
698 
699             case MEAN:
700                 return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, cachedAlpha);
701 
702             default:
703                 throw new OrekitInternalError(null);
704         }
705     }
706 
707     /** Get the eccentric latitude argument derivative.
708      * @return d(E + ω)/dt eccentric latitude argument derivative (rad/s)
709      */
710     public T getAlphaEDot() {
711 
712         if (!hasDerivatives()) {
713             return null;
714         }
715         switch (cachedPositionAngleType) {
716             case TRUE:
717                 final FieldUnivariateDerivative1<T> alphaVUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot);
718                 final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
719                 final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
720                 final FieldUnivariateDerivative1<T> alphaEUD = FieldCircularLatitudeArgumentUtility.trueToEccentric(exUD, eyUD,
721                         alphaVUD);
722                 return alphaEUD.getFirstDerivative();
723 
724             case ECCENTRIC:
725                 return cachedAlphaDot;
726 
727             case MEAN:
728                 final FieldUnivariateDerivative1<T> alphaMUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot);
729                 final FieldUnivariateDerivative1<T> exUD2 = new FieldUnivariateDerivative1<>(ex, exDot);
730                 final FieldUnivariateDerivative1<T> eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot);
731                 final FieldUnivariateDerivative1<T> alphaVUD2 = FieldCircularLatitudeArgumentUtility.meanToEccentric(exUD2,
732                         eyUD2, alphaMUD);
733                 return alphaVUD2.getFirstDerivative();
734 
735             default:
736                 throw new OrekitInternalError(null);
737         }
738 
739     }
740 
741     /** Get the mean latitude argument.
742      * @return M + ω mean latitude argument (rad)
743      */
744     public T getAlphaM() {
745         switch (cachedPositionAngleType) {
746             case TRUE:
747                 return FieldCircularLatitudeArgumentUtility.trueToMean(ex, ey, cachedAlpha);
748 
749             case MEAN:
750                 return cachedAlpha;
751 
752             case ECCENTRIC:
753                 return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, cachedAlpha);
754 
755             default:
756                 throw new OrekitInternalError(null);
757         }
758     }
759 
760     /** Get the mean latitude argument derivative.
761      * @return d(M + ω)/dt mean latitude argument derivative (rad/s)
762      */
763     public T getAlphaMDot() {
764 
765         if (!hasDerivatives()) {
766             return null;
767         }
768         switch (cachedPositionAngleType) {
769             case TRUE:
770                 final FieldUnivariateDerivative1<T> alphaVUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot);
771                 final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
772                 final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
773                 final FieldUnivariateDerivative1<T> alphaMUD = FieldCircularLatitudeArgumentUtility.trueToMean(exUD, eyUD,
774                         alphaVUD);
775                 return alphaMUD.getFirstDerivative();
776 
777             case MEAN:
778                 return cachedAlphaDot;
779 
780             case ECCENTRIC:
781                 final FieldUnivariateDerivative1<T> alphaEUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot);
782                 final FieldUnivariateDerivative1<T> exUD2 = new FieldUnivariateDerivative1<>(ex, exDot);
783                 final FieldUnivariateDerivative1<T> eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot);
784                 final FieldUnivariateDerivative1<T> alphaMUD2 = FieldCircularLatitudeArgumentUtility.eccentricToMean(exUD2,
785                         eyUD2, alphaEUD);
786                 return alphaMUD2.getFirstDerivative();
787 
788             default:
789                 throw new OrekitInternalError(null);
790         }
791     }
792 
793     /** Get the latitude argument.
794      * @param type type of the angle
795      * @return latitude argument (rad)
796      */
797     public T getAlpha(final PositionAngleType type) {
798         return (type == PositionAngleType.MEAN) ? getAlphaM() :
799                                               ((type == PositionAngleType.ECCENTRIC) ? getAlphaE() :
800                                                                                    getAlphaV());
801     }
802 
803     /** Get the latitude argument derivative.
804      * @param type type of the angle
805      * @return latitude argument derivative (rad/s)
806      */
807     public T getAlphaDot(final PositionAngleType type) {
808         return (type == PositionAngleType.MEAN) ? getAlphaMDot() :
809                                               ((type == PositionAngleType.ECCENTRIC) ? getAlphaEDot() :
810                                                                                    getAlphaVDot());
811     }
812 
813     /** Computes the true latitude argument from the eccentric latitude argument.
814      * @param alphaE = E + ω eccentric latitude argument (rad)
815      * @param ex e cos(ω), first component of circular eccentricity vector
816      * @param ey e sin(ω), second component of circular eccentricity vector
817      * @param <T> Type of the field elements
818      * @return the true latitude argument.
819      */
820     @Deprecated
821     public static <T extends CalculusFieldElement<T>> T eccentricToTrue(final T alphaE, final T ex, final T ey) {
822         return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alphaE);
823     }
824 
825     /** Computes the eccentric latitude argument from the true latitude argument.
826      * @param alphaV = v + ω true latitude argument (rad)
827      * @param ex e cos(ω), first component of circular eccentricity vector
828      * @param ey e sin(ω), second component of circular eccentricity vector
829      * @param <T> Type of the field elements
830      * @return the eccentric latitude argument.
831      */
832     @Deprecated
833     public static <T extends CalculusFieldElement<T>> T trueToEccentric(final T alphaV, final T ex, final T ey) {
834         return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alphaV);
835     }
836 
837     /** Computes the eccentric latitude argument from the mean latitude argument.
838      * @param alphaM = M + ω  mean latitude argument (rad)
839      * @param ex e cos(ω), first component of circular eccentricity vector
840      * @param ey e sin(ω), second component of circular eccentricity vector
841      * @param <T> Type of the field elements
842      * @return the eccentric latitude argument.
843      */
844     @Deprecated
845     public static <T extends CalculusFieldElement<T>> T meanToEccentric(final T alphaM, final T ex, final T ey) {
846         return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alphaM);
847     }
848 
849     /** Computes the mean latitude argument from the eccentric latitude argument.
850      * @param alphaE = E + ω  eccentric latitude argument (rad)
851      * @param ex e cos(ω), first component of circular eccentricity vector
852      * @param ey e sin(ω), second component of circular eccentricity vector
853      * @param <T> Type of the field elements
854      * @return the mean latitude argument.
855      */
856     @Deprecated
857     public static <T extends CalculusFieldElement<T>> T eccentricToMean(final T alphaE, final T ex, final T ey) {
858         return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alphaE);
859     }
860 
861     /** {@inheritDoc} */
862     @Override
863     public T getE() {
864         return ex.multiply(ex).add(ey.multiply(ey)).sqrt();
865     }
866 
867     /** {@inheritDoc} */
868     @Override
869     public T getEDot() {
870 
871         if (!hasDerivatives()) {
872             return null;
873         }
874 
875         return ex.multiply(exDot).add(ey.multiply(eyDot)).divide(getE());
876 
877     }
878 
879     /** {@inheritDoc} */
880     @Override
881     public T getI() {
882         return i;
883     }
884 
885     /** {@inheritDoc} */
886     @Override
887     public T getIDot() {
888         return iDot;
889     }
890 
891     /** Get the right ascension of the ascending node.
892      * @return right ascension of the ascending node (rad)
893      */
894     public T getRightAscensionOfAscendingNode() {
895         return raan;
896     }
897 
898     /** Get the right ascension of the ascending node derivative.
899      * @return right ascension of the ascending node derivative (rad/s)
900      */
901     public T getRightAscensionOfAscendingNodeDot() {
902         return raanDot;
903     }
904 
905     /** {@inheritDoc} */
906     @Override
907     public T getLv() {
908         return getAlphaV().add(raan);
909     }
910 
911     /** {@inheritDoc} */
912     @Override
913     public T getLvDot() {
914         return hasDerivatives() ? getAlphaVDot().add(raanDot) : null;
915     }
916 
917     /** {@inheritDoc} */
918     @Override
919     public T getLE() {
920         return getAlphaE().add(raan);
921     }
922 
923     /** {@inheritDoc} */
924     @Override
925     public T getLEDot() {
926         return hasDerivatives() ? getAlphaEDot().add(raanDot) : null;
927     }
928 
929     /** {@inheritDoc} */
930     @Override
931     public T getLM() {
932         return getAlphaM().add(raan);
933     }
934 
935     /** {@inheritDoc} */
936     @Override
937     public T getLMDot() {
938         return hasDerivatives() ? getAlphaMDot().add(raanDot) : null;
939     }
940 
941     /** {@inheritDoc} */
942     @Override
943     public boolean hasDerivatives() {
944         return aDot != null;
945     }
946 
947     /** Compute position and velocity but not acceleration.
948      */
949     private void computePVWithoutA() {
950 
951         if (partialPV != null) {
952             // already computed
953             return;
954         }
955 
956         // get equinoctial parameters
957         final T equEx = getEquinoctialEx();
958         final T equEy = getEquinoctialEy();
959         final T hx = getHx();
960         final T hy = getHy();
961         final T lE = getLE();
962         // inclination-related intermediate parameters
963         final T hx2   = hx.multiply(hx);
964         final T hy2   = hy.multiply(hy);
965         final T factH = (hx2.add(1).add(hy2)).reciprocal();
966 
967         // reference axes defining the orbital plane
968         final T ux = (hx2.add(1).subtract(hy2)).multiply(factH);
969         final T uy =  hx.multiply(2).multiply(hy).multiply(factH);
970         final T uz = hy.multiply(-2).multiply(factH);
971 
972         final T vx = uy;
973         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
974         final T vz =  hx.multiply(factH).multiply(2);
975 
976         // eccentricity-related intermediate parameters
977         final T exey = equEx.multiply(equEy);
978         final T ex2  = equEx.square();
979         final T ey2  = equEy.square();
980         final T e2   = ex2.add(ey2);
981         final T eta  = e2.negate().add(1).sqrt().add(1);
982         final T beta = eta.reciprocal();
983 
984         // eccentric latitude argument
985         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
986         final T cLe    = scLe.cos();
987         final T sLe    = scLe.sin();
988         final T exCeyS = equEx.multiply(cLe).add(equEy.multiply(sLe));
989         // coordinates of position and velocity in the orbital plane
990         final T x      = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx));
991         final T y      = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy));
992 
993         final T factor = getOne().add(getMu()).divide(a).sqrt().divide(exCeyS.negate().add(1));
994         final T xdot   = factor.multiply( beta.multiply(equEy).multiply(exCeyS).subtract(sLe ));
995         final T ydot   = factor.multiply( cLe.subtract(beta.multiply(equEx).multiply(exCeyS)));
996 
997         final FieldVector3D<T> position = new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
998                                                               x.multiply(uy).add(y.multiply(vy)),
999                                                               x.multiply(uz).add(y.multiply(vz)));
1000         final FieldVector3D<T> velocity = new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)),
1001                                                               xdot.multiply(uy).add(ydot.multiply(vy)),
1002                                                               xdot.multiply(uz).add(ydot.multiply(vz)));
1003 
1004         partialPV = new FieldPVCoordinates<>(position, velocity);
1005 
1006     }
1007 
1008 
1009     /** Initialize cached alpha with rate.
1010      * @param alpha input alpha
1011      * @param alphaDot rate of input alpha
1012      * @param inputType position angle type passed as input
1013      * @return alpha to cache with rate
1014      * @since 12.1
1015      */
1016     private FieldUnivariateDerivative1<T> initializeCachedAlpha(final T alpha, final T alphaDot,
1017                                                                 final PositionAngleType inputType) {
1018         if (cachedPositionAngleType == inputType) {
1019             return new FieldUnivariateDerivative1<>(alpha, alphaDot);
1020 
1021         } else {
1022             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
1023             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
1024             final FieldUnivariateDerivative1<T> alphaUD = new FieldUnivariateDerivative1<>(alpha, alphaDot);
1025 
1026             switch (cachedPositionAngleType) {
1027 
1028                 case ECCENTRIC:
1029                     if (inputType == PositionAngleType.MEAN) {
1030                         return FieldCircularLatitudeArgumentUtility.meanToEccentric(exUD, eyUD, alphaUD);
1031                     } else {
1032                         return FieldCircularLatitudeArgumentUtility.trueToEccentric(exUD, eyUD, alphaUD);
1033                     }
1034 
1035                 case TRUE:
1036                     if (inputType == PositionAngleType.MEAN) {
1037                         return FieldCircularLatitudeArgumentUtility.meanToTrue(exUD, eyUD, alphaUD);
1038                     } else {
1039                         return FieldCircularLatitudeArgumentUtility.eccentricToTrue(exUD, eyUD, alphaUD);
1040                     }
1041 
1042                 case MEAN:
1043                     if (inputType == PositionAngleType.TRUE) {
1044                         return FieldCircularLatitudeArgumentUtility.trueToMean(exUD, eyUD, alphaUD);
1045                     } else {
1046                         return FieldCircularLatitudeArgumentUtility.eccentricToMean(exUD, eyUD, alphaUD);
1047                     }
1048 
1049                 default:
1050                     throw new OrekitInternalError(null);
1051 
1052             }
1053 
1054         }
1055 
1056     }
1057 
1058     /** Initialize cached alpha.
1059      * @param alpha input alpha
1060      * @param positionAngleType position angle type passed as input
1061      * @return alpha to cache
1062      * @since 12.1
1063      */
1064     private T initializeCachedAlpha(final T alpha, final PositionAngleType positionAngleType) {
1065         if (positionAngleType == cachedPositionAngleType) {
1066             return alpha;
1067 
1068         } else {
1069             switch (cachedPositionAngleType) {
1070 
1071                 case ECCENTRIC:
1072                     if (positionAngleType == PositionAngleType.MEAN) {
1073                         return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alpha);
1074                     } else {
1075                         return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alpha);
1076                     }
1077 
1078                 case MEAN:
1079                     if (positionAngleType == PositionAngleType.TRUE) {
1080                         return FieldCircularLatitudeArgumentUtility.trueToMean(ex, ey, alpha);
1081                     } else {
1082                         return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alpha);
1083                     }
1084 
1085                 case TRUE:
1086                     if (positionAngleType == PositionAngleType.MEAN) {
1087                         return FieldCircularLatitudeArgumentUtility.meanToTrue(ex, ey, alpha);
1088                     } else {
1089                         return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alpha);
1090                     }
1091 
1092                 default:
1093                     throw new OrekitInternalError(null);
1094             }
1095         }
1096     }
1097 
1098     /** Compute non-Keplerian part of the acceleration from first time derivatives.
1099      * <p>
1100      * This method should be called only when {@link #hasDerivatives()} returns true.
1101      * </p>
1102      * @return non-Keplerian part of the acceleration
1103      */
1104     private FieldVector3D<T> nonKeplerianAcceleration() {
1105 
1106         final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6);
1107         getJacobianWrtParameters(PositionAngleType.MEAN, dCdP);
1108 
1109         final T nonKeplerianMeanMotion = getAlphaMDot().subtract(getKeplerianMeanMotion());
1110         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
1111                                  add(dCdP[3][1].multiply(exDot)).
1112                                  add(dCdP[3][2].multiply(eyDot)).
1113                                  add(dCdP[3][3].multiply(iDot)).
1114                                  add(dCdP[3][4].multiply(raanDot)).
1115                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
1116         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
1117                                  add(dCdP[4][1].multiply(exDot)).
1118                                  add(dCdP[4][2].multiply(eyDot)).
1119                                  add(dCdP[4][3].multiply(iDot)).
1120                                  add(dCdP[4][4].multiply(raanDot)).
1121                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
1122         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
1123                                  add(dCdP[5][1].multiply(exDot)).
1124                                  add(dCdP[5][2].multiply(eyDot)).
1125                                  add(dCdP[5][3].multiply(iDot)).
1126                                  add(dCdP[5][4].multiply(raanDot)).
1127                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));
1128 
1129         return new FieldVector3D<>(nonKeplerianAx, nonKeplerianAy, nonKeplerianAz);
1130 
1131     }
1132 
1133     /** {@inheritDoc} */
1134     @Override
1135     protected FieldVector3D<T> initPosition() {
1136         // get equinoctial parameters
1137         final T equEx = getEquinoctialEx();
1138         final T equEy = getEquinoctialEy();
1139         final T hx = getHx();
1140         final T hy = getHy();
1141         final T lE = getLE();
1142         // inclination-related intermediate parameters
1143         final T hx2   = hx.multiply(hx);
1144         final T hy2   = hy.multiply(hy);
1145         final T factH = (hx2.add(1).add(hy2)).reciprocal();
1146 
1147         // reference axes defining the orbital plane
1148         final T ux = (hx2.add(1).subtract(hy2)).multiply(factH);
1149         final T uy =  hx.multiply(2).multiply(hy).multiply(factH);
1150         final T uz = hy.multiply(-2).multiply(factH);
1151 
1152         final T vx = uy;
1153         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
1154         final T vz =  hx.multiply(factH).multiply(2);
1155 
1156         // eccentricity-related intermediate parameters
1157         final T exey = equEx.multiply(equEy);
1158         final T ex2  = equEx.square();
1159         final T ey2  = equEy.square();
1160         final T e2   = ex2.add(ey2);
1161         final T eta  = e2.negate().add(1).sqrt().add(1);
1162         final T beta = eta.reciprocal();
1163 
1164         // eccentric latitude argument
1165         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
1166         final T cLe    = scLe.cos();
1167         final T sLe    = scLe.sin();
1168 
1169         // coordinates of position and velocity in the orbital plane
1170         final T x      = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx));
1171         final T y      = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy));
1172 
1173         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
1174                                    x.multiply(uy).add(y.multiply(vy)),
1175                                    x.multiply(uz).add(y.multiply(vz)));
1176 
1177     }
1178 
1179     /** {@inheritDoc} */
1180     @Override
1181     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {
1182 
1183         // position and velocity
1184         computePVWithoutA();
1185 
1186         // acceleration
1187         final T r2 = partialPV.getPosition().getNormSq();
1188         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(getMu().negate()),
1189                                                                            partialPV.getPosition());
1190         final FieldVector3D<T> acceleration = hasDerivatives() ?
1191                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
1192                                               keplerianAcceleration;
1193 
1194         return new TimeStampedFieldPVCoordinates<>(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);
1195 
1196     }
1197 
1198     /** {@inheritDoc} */
1199     @Override
1200     public FieldCircularOrbit<T> shiftedBy(final double dt) {
1201         return shiftedBy(getZero().newInstance(dt));
1202     }
1203 
1204     /** {@inheritDoc} */
1205     @Override
1206     public FieldCircularOrbit<T> shiftedBy(final T dt) {
1207 
1208         // use Keplerian-only motion
1209         final FieldCircularOrbit<T> keplerianShifted = new FieldCircularOrbit<>(a, ex, ey, i, raan,
1210                                                                                 getAlphaM().add(getKeplerianMeanMotion().multiply(dt)),
1211                                                                                 PositionAngleType.MEAN, cachedPositionAngleType, getFrame(),
1212                                                                                 getDate().shiftedBy(dt), getMu());
1213 
1214         if (hasDerivatives()) {
1215 
1216             // extract non-Keplerian acceleration from first time derivatives
1217             final FieldVector3D<T> nonKeplerianAcceleration = nonKeplerianAcceleration();
1218 
1219             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
1220             keplerianShifted.computePVWithoutA();
1221             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(),
1222                                                                   dt.square().multiply(0.5), nonKeplerianAcceleration);
1223             final T   fixedR2 = fixedP.getNormSq();
1224             final T   fixedR  = fixedR2.sqrt();
1225             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(),
1226                                                                  dt, nonKeplerianAcceleration);
1227             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
1228                                                                  keplerianShifted.partialPV.getPosition(),
1229                                                                  getOne(), nonKeplerianAcceleration);
1230 
1231             // build a new orbit, taking non-Keplerian acceleration into account
1232             return new FieldCircularOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
1233                                                                                 fixedP, fixedV, fixedA),
1234                                             keplerianShifted.getFrame(), keplerianShifted.getMu());
1235 
1236         } else {
1237             // Keplerian-only motion is all we can do
1238             return keplerianShifted;
1239         }
1240 
1241     }
1242 
1243     /** {@inheritDoc} */
1244     @Override
1245     protected T[][] computeJacobianMeanWrtCartesian() {
1246 
1247         final T[][] jacobian = MathArrays.buildArray(getOne().getField(), 6, 6);
1248 
1249         // compute various intermediate parameters
1250         computePVWithoutA();
1251         final FieldVector3D<T> position = partialPV.getPosition();
1252         final FieldVector3D<T> velocity = partialPV.getVelocity();
1253 
1254         final T x          = position.getX();
1255         final T y          = position.getY();
1256         final T z          = position.getZ();
1257         final T vx         = velocity.getX();
1258         final T vy         = velocity.getY();
1259         final T vz         = velocity.getZ();
1260         final T pv         = FieldVector3D.dotProduct(position, velocity);
1261         final T r2         = position.getNormSq();
1262         final T r          = r2.sqrt();
1263         final T v2         = velocity.getNormSq();
1264 
1265         final T mu         = getMu();
1266         final T oOsqrtMuA  = getOne().divide(a.multiply(mu).sqrt());
1267         final T rOa        = r.divide(a);
1268         final T aOr        = a.divide(r);
1269         final T aOr2       = a.divide(r2);
1270         final T a2         = a.square();
1271 
1272         final T ex2        = ex.square();
1273         final T ey2        = ey.square();
1274         final T e2         = ex2.add(ey2);
1275         final T epsilon    = e2.negate().add(1.0).sqrt();
1276         final T beta       = epsilon.add(1).reciprocal();
1277 
1278         final T eCosE      = rOa.negate().add(1);
1279         final T eSinE      = pv.multiply(oOsqrtMuA);
1280 
1281         final FieldSinCos<T> scI    = FastMath.sinCos(i);
1282         final FieldSinCos<T> scRaan = FastMath.sinCos(raan);
1283         final T cosI       = scI.cos();
1284         final T sinI       = scI.sin();
1285         final T cosRaan    = scRaan.cos();
1286         final T sinRaan    = scRaan.sin();
1287 
1288         // da
1289         fillHalfRow(aOr.multiply(2.0).multiply(aOr2), position, jacobian[0], 0);
1290         fillHalfRow(a2.multiply(mu.divide(2.).reciprocal()), velocity, jacobian[0], 3);
1291 
1292         // differentials of the normalized momentum
1293         final FieldVector3D<T> danP = new FieldVector3D<>(v2, position, pv.negate(), velocity);
1294         final FieldVector3D<T> danV = new FieldVector3D<>(r2, velocity, pv.negate(), position);
1295         final T recip   = partialPV.getMomentum().getNorm().reciprocal();
1296         final T recip2  = recip.multiply(recip);
1297         final T recip2N = recip2.negate();
1298         final FieldVector3D<T> dwXP = new FieldVector3D<>(recip,
1299                                                           new FieldVector3D<>(getZero(), vz, vy.negate()),
1300                                                           recip2N.multiply(sinRaan).multiply(sinI),
1301                                                           danP);
1302         final FieldVector3D<T> dwYP = new FieldVector3D<>(recip,
1303                                                           new FieldVector3D<>(vz.negate(), getZero(), vx),
1304                                                           recip2.multiply(cosRaan).multiply(sinI),
1305                                                           danP);
1306         final FieldVector3D<T> dwZP = new FieldVector3D<>(recip,
1307                                                           new FieldVector3D<>(vy, vx.negate(), getZero()),
1308                                                           recip2N.multiply(cosI),
1309                                                           danP);
1310         final FieldVector3D<T> dwXV = new FieldVector3D<>(recip,
1311                                                           new FieldVector3D<>(getZero(), z.negate(), y),
1312                                                           recip2N.multiply(sinRaan).multiply(sinI),
1313                                                           danV);
1314         final FieldVector3D<T> dwYV = new FieldVector3D<>(recip,
1315                                                           new FieldVector3D<>(z, getZero(), x.negate()),
1316                                                           recip2.multiply(cosRaan).multiply(sinI),
1317                                                           danV);
1318         final FieldVector3D<T> dwZV = new FieldVector3D<>(recip,
1319                                                           new FieldVector3D<>(y.negate(), x, getZero()),
1320                                                           recip2N.multiply(cosI),
1321                                                           danV);
1322 
1323         // di
1324         fillHalfRow(sinRaan.multiply(cosI), dwXP, cosRaan.negate().multiply(cosI), dwYP, sinI.negate(), dwZP, jacobian[3], 0);
1325         fillHalfRow(sinRaan.multiply(cosI), dwXV, cosRaan.negate().multiply(cosI), dwYV, sinI.negate(), dwZV, jacobian[3], 3);
1326 
1327         // dRaan
1328         fillHalfRow(sinRaan.divide(sinI), dwYP, cosRaan.divide(sinI), dwXP, jacobian[4], 0);
1329         fillHalfRow(sinRaan.divide(sinI), dwYV, cosRaan.divide(sinI), dwXV, jacobian[4], 3);
1330 
1331         // orbital frame: (p, q, w) p along ascending node, w along momentum
1332         // the coordinates of the spacecraft in this frame are: (u, v, 0)
1333         final T u     =  x.multiply(cosRaan).add(y.multiply(sinRaan));
1334         final T cv    =  x.negate().multiply(sinRaan).add(y.multiply(cosRaan));
1335         final T v     = cv.multiply(cosI).add(z.multiply(sinI));
1336 
1337         // du
1338         final FieldVector3D<T> duP = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXP,
1339                                                          cv.multiply(sinRaan).divide(sinI), dwYP,
1340                                                          getOne(), new FieldVector3D<>(cosRaan, sinRaan, getZero()));
1341         final FieldVector3D<T> duV = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXV,
1342                                                          cv.multiply(sinRaan).divide(sinI), dwYV);
1343 
1344         // dv
1345         final FieldVector3D<T> dvP = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXP,
1346                                                          u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYP,
1347                                                          cv, dwZP,
1348                                                          getOne(), new FieldVector3D<>(sinRaan.negate().multiply(cosI), cosRaan.multiply(cosI), sinI));
1349         final FieldVector3D<T> dvV = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXV,
1350                                                          u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYV,
1351                                                          cv, dwZV);
1352 
1353         final FieldVector3D<T> dc1P = new FieldVector3D<>(aOr2.multiply(eSinE.multiply(eSinE).multiply(2).add(1).subtract(eCosE)).divide(r2), position,
1354                                                           aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), velocity);
1355         final FieldVector3D<T> dc1V = new FieldVector3D<>(aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), position,
1356                                                           getZero().newInstance(2).divide(mu), velocity);
1357         final FieldVector3D<T> dc2P = new FieldVector3D<>(aOr2.multiply(eSinE).multiply(eSinE.multiply(eSinE).subtract(e2.negate().add(1))).divide(r2.multiply(epsilon)), position,
1358                                                           aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), velocity);
1359         final FieldVector3D<T> dc2V = new FieldVector3D<>(aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), position,
1360                                                           eSinE.divide(epsilon.multiply(mu)), velocity);
1361 
1362         final T cof1   = aOr2.multiply(eCosE.subtract(e2));
1363         final T cof2   = aOr2.multiply(epsilon).multiply(eSinE);
1364         final FieldVector3D<T> dexP = new FieldVector3D<>(u, dc1P,  v, dc2P, cof1, duP,  cof2, dvP);
1365         final FieldVector3D<T> dexV = new FieldVector3D<>(u, dc1V,  v, dc2V, cof1, duV,  cof2, dvV);
1366         final FieldVector3D<T> deyP = new FieldVector3D<>(v, dc1P, u.negate(), dc2P, cof1, dvP, cof2.negate(), duP);
1367         final FieldVector3D<T> deyV = new FieldVector3D<>(v, dc1V, u.negate(), dc2V, cof1, dvV, cof2.negate(), duV);
1368         fillHalfRow(getOne(), dexP, jacobian[1], 0);
1369         fillHalfRow(getOne(), dexV, jacobian[1], 3);
1370         fillHalfRow(getOne(), deyP, jacobian[2], 0);
1371         fillHalfRow(getOne(), deyV, jacobian[2], 3);
1372 
1373         final T cle = u.divide(a).add(ex).subtract(eSinE.multiply(beta).multiply(ey));
1374         final T sle = v.divide(a).add(ey).add(eSinE.multiply(beta).multiply(ex));
1375         final T m1  = beta.multiply(eCosE);
1376         final T m2  = m1.multiply(eCosE).negate().add(1);
1377         final T m3  = (u.multiply(ey).subtract(v.multiply(ex))).add(eSinE.multiply(beta).multiply(u.multiply(ex).add(v.multiply(ey))));
1378         final T m4  = sle.negate().add(cle.multiply(eSinE).multiply(beta));
1379         final T m5  = cle.add(sle.multiply(eSinE).multiply(beta));
1380         final T kk = m3.multiply(2).divide(r).add(aOr.multiply(eSinE)).add(m1.multiply(eSinE).multiply(m1.add(1).subtract(aOr.add(1).multiply(m2))).divide(epsilon)).divide(r2);
1381         final T jj = (m1.multiply(m2).divide(epsilon).subtract(1)).multiply(oOsqrtMuA);
1382         fillHalfRow(kk, position,
1383                     jj, velocity,
1384                     m4, dexP,
1385                     m5, deyP,
1386                     sle.negate().divide(a), duP,
1387                     cle.divide(a), dvP,
1388                     jacobian[5], 0);
1389         final T ll = (m1.multiply(m2).divide(epsilon ).subtract(1)).multiply(oOsqrtMuA);
1390         final T mm = m3.multiply(2).add(eSinE.multiply(a)).add(m1.multiply(eSinE).multiply(r).multiply(eCosE.multiply(beta).multiply(2).subtract(aOr.multiply(m2))).divide(epsilon)).divide(mu);
1391 
1392         fillHalfRow(ll, position,
1393                     mm, velocity,
1394                     m4, dexV,
1395                     m5, deyV,
1396                     sle.negate().divide(a), duV,
1397                     cle.divide(a), dvV,
1398                     jacobian[5], 3);
1399         return jacobian;
1400 
1401     }
1402 
1403     /** {@inheritDoc} */
1404     @Override
1405     protected T[][] computeJacobianEccentricWrtCartesian() {
1406 
1407         // start by computing the Jacobian with mean angle
1408         final T[][] jacobian = computeJacobianMeanWrtCartesian();
1409 
1410         // Differentiating the Kepler equation aM = aE - ex sin aE + ey cos aE leads to:
1411         // daM = (1 - ex cos aE - ey sin aE) dE - sin aE dex + cos aE dey
1412         // which is inverted and rewritten as:
1413         // daE = a/r daM + sin aE a/r dex - cos aE a/r dey
1414         final T alphaE            = getAlphaE();
1415         final FieldSinCos<T> scAe = FastMath.sinCos(alphaE);
1416         final T cosAe             = scAe.cos();
1417         final T sinAe             = scAe.sin();
1418         final T aOr               = getOne().divide(getOne().subtract(ex.multiply(cosAe)).subtract(ey.multiply(sinAe)));
1419 
1420         // update longitude row
1421         final T[] rowEx = jacobian[1];
1422         final T[] rowEy = jacobian[2];
1423         final T[] rowL  = jacobian[5];
1424         for (int j = 0; j < 6; ++j) {
1425          // rowL[j] = aOr * (      rowL[j] +   sinAe *        rowEx[j]   -         cosAe *        rowEy[j]);
1426             rowL[j] = aOr.multiply(rowL[j].add(sinAe.multiply(rowEx[j])).subtract( cosAe.multiply(rowEy[j])));
1427         }
1428         jacobian[5] = rowL;
1429         return jacobian;
1430 
1431     }
1432     /** {@inheritDoc} */
1433     @Override
1434     protected T[][] computeJacobianTrueWrtCartesian() {
1435 
1436         // start by computing the Jacobian with eccentric angle
1437         final T[][] jacobian = computeJacobianEccentricWrtCartesian();
1438         // Differentiating the eccentric latitude equation
1439         // tan((aV - aE)/2) = [ex sin aE - ey cos aE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos aE - ey sin aE]
1440         // leads to
1441         // cT (daV - daE) = cE daE + cX dex + cY dey
1442         // with
1443         // cT = [d^2 + (ex sin aE - ey cos aE)^2] / 2
1444         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos aE - ey sin aE
1445         // cE = (ex cos aE + ey sin aE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
1446         // cX =  sin aE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
1447         // cY = -cos aE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
1448         // which can be solved to find the differential of the true latitude
1449         // daV = (cT + cE) / cT daE + cX / cT deX + cY / cT deX
1450         final T alphaE            = getAlphaE();
1451         final FieldSinCos<T> scAe = FastMath.sinCos(alphaE);
1452         final T cosAe             = scAe.cos();
1453         final T sinAe             = scAe.sin();
1454         final T eSinE             = ex.multiply(sinAe).subtract(ey.multiply(cosAe));
1455         final T ecosE             = ex.multiply(cosAe).add(ey.multiply(sinAe));
1456         final T e2                = ex.multiply(ex).add(ey.multiply(ey));
1457         final T epsilon           = (getOne().subtract(e2)).sqrt();
1458         final T onePeps           = getOne().add(epsilon);
1459         final T d                 = onePeps.subtract(ecosE);
1460         final T cT                = (d.multiply(d).add(eSinE.multiply(eSinE))).divide(2);
1461         final T cE                = ecosE.multiply(onePeps).subtract(e2);
1462         final T cX                = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinAe.multiply(onePeps));
1463         final T cY                = ey.multiply(eSinE).divide(epsilon).add(ex).subtract(cosAe.multiply(onePeps));
1464         final T factorLe          = (cT.add(cE)).divide(cT);
1465         final T factorEx          = cX.divide(cT);
1466         final T factorEy          = cY.divide(cT);
1467 
1468         // update latitude row
1469         final T[] rowEx = jacobian[1];
1470         final T[] rowEy = jacobian[2];
1471         final T[] rowA = jacobian[5];
1472         for (int j = 0; j < 6; ++j) {
1473             rowA[j] = factorLe.multiply(rowA[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
1474         }
1475         return jacobian;
1476 
1477     }
1478 
1479     /** {@inheritDoc} */
1480     @Override
1481     public void addKeplerContribution(final PositionAngleType type, final T gm,
1482                                       final T[] pDot) {
1483         final T oMe2;
1484         final T ksi;
1485         final T n = a.reciprocal().multiply(gm).sqrt().divide(a);
1486         final FieldSinCos<T> sc;
1487         switch (type) {
1488             case MEAN :
1489                 pDot[5] = pDot[5].add(n);
1490                 break;
1491             case ECCENTRIC :
1492                 sc = FastMath.sinCos(getAlphaE());
1493                 ksi  = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal();
1494                 pDot[5] = pDot[5].add(n.multiply(ksi));
1495                 break;
1496             case TRUE :
1497                 sc = FastMath.sinCos(getAlphaV());
1498                 oMe2  = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
1499                 ksi   = getOne().add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin()));
1500                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
1501                 break;
1502             default :
1503                 throw new OrekitInternalError(null);
1504         }
1505     }
1506 
1507     /**  Returns a string representation of this Orbit object.
1508      * @return a string representation of this object
1509      */
1510     public String toString() {
1511         return new StringBuilder().append("circular parameters: ").append('{').
1512                                   append("a: ").append(a.getReal()).
1513                                   append(", ex: ").append(ex.getReal()).append(", ey: ").append(ey.getReal()).
1514                                   append(", i: ").append(FastMath.toDegrees(i.getReal())).
1515                                   append(", raan: ").append(FastMath.toDegrees(raan.getReal())).
1516                                   append(", alphaV: ").append(FastMath.toDegrees(getAlphaV().getReal())).
1517                                   append(";}").toString();
1518     }
1519 
1520     /** {@inheritDoc} */
1521     @Override
1522     public PositionAngleType getCachedPositionAngleType() {
1523         return cachedPositionAngleType;
1524     }
1525 
1526     /** {@inheritDoc} */
1527     @Override
1528     public boolean hasRates() {
1529         return hasDerivatives();
1530     }
1531 
1532     /** {@inheritDoc} */
1533     @Override
1534     public FieldCircularOrbit<T> removeRates() {
1535         return new FieldCircularOrbit<>(getA(), getCircularEx(), getCircularEy(),
1536                 getI(), getRightAscensionOfAscendingNode(), cachedAlpha,
1537                 cachedPositionAngleType, getFrame(), getDate(), getMu());
1538     }
1539 
1540     /** {@inheritDoc} */
1541     @Override
1542     public CircularOrbit toOrbit() {
1543         final double cachedPositionAngle = cachedAlpha.getReal();
1544         if (hasDerivatives()) {
1545             return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(),
1546                                      i.getReal(), raan.getReal(), cachedPositionAngle,
1547                                      aDot.getReal(), exDot.getReal(), eyDot.getReal(),
1548                                      iDot.getReal(), raanDot.getReal(), cachedAlphaDot.getReal(),
1549                                      cachedPositionAngleType, getFrame(),
1550                                      getDate().toAbsoluteDate(), getMu().getReal());
1551         } else {
1552             return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(),
1553                                      i.getReal(), raan.getReal(), cachedPositionAngle,
1554                                      cachedPositionAngleType, getFrame(),
1555                                      getDate().toAbsoluteDate(), getMu().getReal());
1556         }
1557     }
1558 
1559 
1560 }