1   /* Copyright 2002-2025 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.analysis.differentiation.UnivariateDerivative1;
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.SinCos;
23  import org.orekit.errors.OrekitIllegalArgumentException;
24  import org.orekit.errors.OrekitInternalError;
25  import org.orekit.errors.OrekitMessages;
26  import org.orekit.frames.Frame;
27  import org.orekit.frames.KinematicTransform;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.TimeOffset;
30  import org.orekit.utils.PVCoordinates;
31  import org.orekit.utils.TimeStampedPVCoordinates;
32  
33  
34  /**
35   * This class handles equinoctial orbital parameters, which can support both
36   * circular and equatorial orbits.
37   * <p>
38   * The parameters used internally are the equinoctial elements which can be
39   * related to Keplerian elements as follows:
40   *   <pre>
41   *     a
42   *     ex = e cos(ω + Ω)
43   *     ey = e sin(ω + Ω)
44   *     hx = tan(i/2) cos(Ω)
45   *     hy = tan(i/2) sin(Ω)
46   *     lv = v + ω + Ω
47   *   </pre>
48   * where ω stands for the Perigee Argument and Ω stands for the
49   * Right Ascension of the Ascending Node.
50   *
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 either equatorial or circular, the equinoctial
55   * parameters are still unambiguously defined whereas some Keplerian elements
56   * (more precisely ω and Ω) become ambiguous. For this reason, equinoctial
57   * parameters are the recommended way to represent orbits. Note however than
58   * the present implementation does not handle non-elliptical cases.
59   * </p>
60   * <p>
61   * The instance <code>EquinoctialOrbit</code> is guaranteed to be immutable.
62   * </p>
63   * @see    Orbit
64   * @see    KeplerianOrbit
65   * @see    CircularOrbit
66   * @see    CartesianOrbit
67   * @author Mathieu Rom&eacute;ro
68   * @author Luc Maisonobe
69   * @author Guylaine Prat
70   * @author Fabien Maussion
71   * @author V&eacute;ronique Pommier-Maurussane
72   */
73  public class EquinoctialOrbit extends Orbit implements PositionAngleBased<EquinoctialOrbit> {
74  
75      /** Semi-major axis (m). */
76      private final double a;
77  
78      /** First component of the eccentricity vector. */
79      private final double ex;
80  
81      /** Second component of the eccentricity vector. */
82      private final double ey;
83  
84      /** First component of the inclination vector. */
85      private final double hx;
86  
87      /** Second component of the inclination vector. */
88      private final double hy;
89  
90      /** Cached longitude argument (rad). */
91      private final double cachedL;
92  
93      /** Cache type of position angle (longitude argument). */
94      private final PositionAngleType cachedPositionAngleType;
95  
96      /** Semi-major axis derivative (m/s). */
97      private final double aDot;
98  
99      /** First component of the eccentricity vector derivative. */
100     private final double exDot;
101 
102     /** Second component of the eccentricity vector derivative. */
103     private final double eyDot;
104 
105     /** First component of the inclination vector derivative. */
106     private final double hxDot;
107 
108     /** Second component of the inclination vector derivative. */
109     private final double hyDot;
110 
111     /** Derivative of cached longitude argument (rad/s). */
112     private final double cachedLDot;
113 
114     /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */
115     private transient PVCoordinates partialPV;
116 
117     /** Creates a new instance.
118      * @param a  semi-major axis (m)
119      * @param ex e cos(ω + Ω), first component of eccentricity vector
120      * @param ey e sin(ω + Ω), second component of eccentricity vector
121      * @param hx tan(i/2) cos(Ω), first component of inclination vector
122      * @param hy tan(i/2) sin(Ω), second component of inclination vector
123      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
124      * @param type type of longitude argument
125      * @param cachedPositionAngleType type of cached longitude argument
126      * @param frame the frame in which the parameters are defined
127      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
128      * @param date date of the orbital parameters
129      * @param mu central attraction coefficient (m³/s²)
130      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
131      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
132      * @since 12.1
133      */
134     public EquinoctialOrbit(final double a, final double ex, final double ey,
135                             final double hx, final double hy, final double l,
136                             final PositionAngleType type, final PositionAngleType cachedPositionAngleType,
137                             final Frame frame, final AbsoluteDate date, final double mu)
138         throws IllegalArgumentException {
139         this(a, ex, ey, hx, hy, l,
140              0., 0., 0., 0., 0., computeKeplerianLDot(type, a, ex, ey, mu, l, type),
141              type, cachedPositionAngleType, frame, date, mu);
142     }
143 
144     /** Creates a new instance without derivatives and with cached position angle same as value inputted.
145      * @param a  semi-major axis (m)
146      * @param ex e cos(ω + Ω), first component of eccentricity vector
147      * @param ey e sin(ω + Ω), second component of eccentricity vector
148      * @param hx tan(i/2) cos(Ω), first component of inclination vector
149      * @param hy tan(i/2) sin(Ω), second component of inclination vector
150      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
151      * @param type type of longitude argument
152      * @param frame the frame in which the parameters are defined
153      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
154      * @param date date of the orbital parameters
155      * @param mu central attraction coefficient (m³/s²)
156      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
157      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
158      */
159     public EquinoctialOrbit(final double a, final double ex, final double ey,
160                             final double hx, final double hy, final double l,
161                             final PositionAngleType type,
162                             final Frame frame, final AbsoluteDate date, final double mu)
163             throws IllegalArgumentException {
164         this(a, ex, ey, hx, hy, l, type, type, frame, date, mu);
165     }
166 
167     /** Creates a new instance.
168      * @param a  semi-major axis (m)
169      * @param ex e cos(ω + Ω), first component of eccentricity vector
170      * @param ey e sin(ω + Ω), second component of eccentricity vector
171      * @param hx tan(i/2) cos(Ω), first component of inclination vector
172      * @param hy tan(i/2) sin(Ω), second component of inclination vector
173      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
174      * @param aDot  semi-major axis derivative (m/s)
175      * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative
176      * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative
177      * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative
178      * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative
179      * @param lDot  d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument  derivative (rad/s)
180      * @param type type of longitude argument
181      * @param cachedPositionAngleType of cached longitude argument
182      * @param frame the frame in which the parameters are defined
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      * @since 12.1
189      */
190     public EquinoctialOrbit(final double a, final double ex, final double ey,
191                             final double hx, final double hy, final double l,
192                             final double aDot, final double exDot, final double eyDot,
193                             final double hxDot, final double hyDot, final double lDot,
194                             final PositionAngleType type, final PositionAngleType cachedPositionAngleType,
195                             final Frame frame, final AbsoluteDate date, final double mu)
196         throws IllegalArgumentException {
197         super(frame, date, mu);
198         if (ex * ex + ey * ey >= 1.0) {
199             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
200                                                      getClass().getName());
201         }
202         this.cachedPositionAngleType = cachedPositionAngleType;
203         this.a     = a;
204         this.aDot  = aDot;
205         this.ex    = ex;
206         this.exDot = exDot;
207         this.ey    = ey;
208         this.eyDot = eyDot;
209         this.hx    = hx;
210         this.hxDot = hxDot;
211         this.hy    = hy;
212         this.hyDot = hyDot;
213 
214         final UnivariateDerivative1 lUD = initializeCachedL(l, lDot, type);
215         this.cachedL = lUD.getValue();
216         this.cachedLDot = lUD.getFirstDerivative();
217 
218         this.partialPV = null;
219 
220     }
221 
222     /** Creates a new instance with derivatives and with cached position angle same as value inputted.
223      * @param a  semi-major axis (m)
224      * @param ex e cos(ω + Ω), first component of eccentricity vector
225      * @param ey e sin(ω + Ω), second component of eccentricity vector
226      * @param hx tan(i/2) cos(Ω), first component of inclination vector
227      * @param hy tan(i/2) sin(Ω), second component of inclination vector
228      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
229      * @param aDot  semi-major axis derivative (m/s)
230      * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative
231      * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative
232      * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative
233      * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative
234      * @param lDot  d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument  derivative (rad/s)
235      * @param type type of longitude argument
236      * @param frame the frame in which the parameters are defined
237      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
238      * @param date date of the orbital parameters
239      * @param mu central attraction coefficient (m³/s²)
240      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
241      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
242      */
243     public EquinoctialOrbit(final double a, final double ex, final double ey,
244                             final double hx, final double hy, final double l,
245                             final double aDot, final double exDot, final double eyDot,
246                             final double hxDot, final double hyDot, final double lDot,
247                             final PositionAngleType type,
248                             final Frame frame, final AbsoluteDate date, final double mu)
249             throws IllegalArgumentException {
250         this(a, ex, ey, hx, hy, l, aDot, exDot, eyDot, hxDot, hyDot, lDot, type, type, frame, date, mu);
251     }
252 
253     /** Constructor from Cartesian parameters.
254      *
255      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
256      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
257      * use {@code mu} and the position to compute the acceleration, including
258      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
259      *
260      * @param pvCoordinates the position, velocity and acceleration
261      * @param frame the frame in which are defined the {@link PVCoordinates}
262      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
263      * @param mu central attraction coefficient (m³/s²)
264      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
265      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
266      */
267     public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates,
268                             final Frame frame, final double mu)
269         throws IllegalArgumentException {
270         super(pvCoordinates, frame, mu);
271 
272         //  compute semi-major axis
273         final Vector3D pvP   = pvCoordinates.getPosition();
274         final Vector3D pvV   = pvCoordinates.getVelocity();
275         final Vector3D pvA   = pvCoordinates.getAcceleration();
276         final double r2      = pvP.getNormSq();
277         final double r       = FastMath.sqrt(r2);
278         final double V2      = pvV.getNormSq();
279         final double rV2OnMu = r * V2 / mu;
280 
281         // compute semi-major axis
282         a = r / (2 - rV2OnMu);
283 
284         if (!isElliptical()) {
285             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
286                                                      getClass().getName());
287         }
288 
289         // compute inclination vector
290         final Vector3D w = pvCoordinates.getMomentum().normalize();
291         final double d = 1.0 / (1 + w.getZ());
292         hx = -d * w.getY();
293         hy =  d * w.getX();
294 
295         // compute true longitude argument
296         cachedPositionAngleType = PositionAngleType.TRUE;
297         final double cLv = (pvP.getX() - d * pvP.getZ() * w.getX()) / r;
298         final double sLv = (pvP.getY() - d * pvP.getZ() * w.getY()) / r;
299         cachedL = FastMath.atan2(sLv, cLv);
300 
301         // compute eccentricity vector
302         final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(mu * a);
303         final double eCE = rV2OnMu - 1;
304         final double e2  = eCE * eCE + eSE * eSE;
305         final double f   = eCE - e2;
306         final double g   = FastMath.sqrt(1 - e2) * eSE;
307         ex = a * (f * cLv + g * sLv) / r;
308         ey = a * (f * sLv - g * cLv) / r;
309 
310         partialPV = pvCoordinates;
311 
312         if (hasNonKeplerianAcceleration(pvCoordinates, mu)) {
313             // we have a relevant acceleration, we can compute derivatives
314 
315             final double[][] jacobian = new double[6][6];
316             getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian);
317 
318             final Vector3D keplerianAcceleration    = new Vector3D(-mu / (r * r2), pvP);
319             final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
320             final double   aX                       = nonKeplerianAcceleration.getX();
321             final double   aY                       = nonKeplerianAcceleration.getY();
322             final double   aZ                       = nonKeplerianAcceleration.getZ();
323             aDot  = jacobian[0][3] * aX + jacobian[0][4] * aY + jacobian[0][5] * aZ;
324             exDot = jacobian[1][3] * aX + jacobian[1][4] * aY + jacobian[1][5] * aZ;
325             eyDot = jacobian[2][3] * aX + jacobian[2][4] * aY + jacobian[2][5] * aZ;
326             hxDot = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ;
327             hyDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ;
328 
329             // in order to compute true longitude argument derivative, we must compute
330             // mean longitude argument derivative including Keplerian motion and convert to true longitude argument
331             final double lMDot = getKeplerianMeanMotion() +
332                                  jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ;
333             final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
334             final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
335             final UnivariateDerivative1 lMUD = new UnivariateDerivative1(getLM(), lMDot);
336             final UnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lMUD);
337             cachedLDot = lvUD.getFirstDerivative();
338 
339         } else {
340             // acceleration is either almost zero or NaN,
341             // we assume acceleration was not known
342             // we don't set up derivatives
343             aDot  = 0.;
344             exDot = 0.;
345             eyDot = 0.;
346             hxDot = 0.;
347             hyDot = 0.;
348             cachedLDot = computeKeplerianLDot(cachedPositionAngleType, a, ex, ey, mu, cachedL, cachedPositionAngleType);
349         }
350 
351     }
352 
353     /** Constructor from Cartesian parameters.
354      *
355      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
356      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
357      * use {@code mu} and the position to compute the acceleration, including
358      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
359      *
360      * @param pvCoordinates the position end velocity
361      * @param frame the frame in which are defined the {@link PVCoordinates}
362      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
363      * @param date date of the orbital parameters
364      * @param mu central attraction coefficient (m³/s²)
365      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
366      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
367      */
368     public EquinoctialOrbit(final PVCoordinates pvCoordinates, final Frame frame,
369                             final AbsoluteDate date, final double mu)
370         throws IllegalArgumentException {
371         this(new TimeStampedPVCoordinates(date, pvCoordinates), frame, mu);
372     }
373 
374     /** Constructor from any kind of orbital parameters.
375      * @param op orbital parameters to copy
376      */
377     public EquinoctialOrbit(final Orbit op) {
378         super(op.getFrame(), op.getDate(), op.getMu());
379         a         = op.getA();
380         aDot      = op.getADot();
381         ex        = op.getEquinoctialEx();
382         exDot     = op.getEquinoctialExDot();
383         ey        = op.getEquinoctialEy();
384         eyDot     = op.getEquinoctialEyDot();
385         hx        = op.getHx();
386         hxDot     = op.getHxDot();
387         hy        = op.getHy();
388         hyDot     = op.getHyDot();
389         cachedPositionAngleType = PositionAngleType.TRUE;
390         cachedL   = op.getLv();
391         cachedLDot = op.hasNonKeplerianAcceleration() ? op.getLvDot() :
392                 computeKeplerianLDot(cachedPositionAngleType, a, ex, ey, op.getMu(), cachedL, cachedPositionAngleType);
393         partialPV = null;
394     }
395 
396     /** {@inheritDoc} */
397     @Override
398     public boolean hasNonKeplerianAcceleration() {
399         return aDot != 0. || exDot != 0. || eyDot != 0. || hxDot != 0. || hyDot != 0. ||
400                 FastMath.abs(cachedLDot - computeKeplerianLDot(cachedPositionAngleType, a, ex, ey, getMu(), cachedL, cachedPositionAngleType)) > TOLERANCE_POSITION_ANGLE_RATE;
401     }
402 
403     /** {@inheritDoc} */
404     @Override
405     public OrbitType getType() {
406         return OrbitType.EQUINOCTIAL;
407     }
408 
409     /** {@inheritDoc} */
410     @Override
411     public double getA() {
412         return a;
413     }
414 
415     /** {@inheritDoc} */
416     @Override
417     public double getADot() {
418         return aDot;
419     }
420 
421     /** {@inheritDoc} */
422     @Override
423     public double getEquinoctialEx() {
424         return ex;
425     }
426 
427     /** {@inheritDoc} */
428     @Override
429     public double getEquinoctialExDot() {
430         return exDot;
431     }
432 
433     /** {@inheritDoc} */
434     @Override
435     public double getEquinoctialEy() {
436         return ey;
437     }
438 
439     /** {@inheritDoc} */
440     @Override
441     public double getEquinoctialEyDot() {
442         return eyDot;
443     }
444 
445     /** {@inheritDoc} */
446     @Override
447     public double getHx() {
448         return hx;
449     }
450 
451     /** {@inheritDoc} */
452     @Override
453     public double getHxDot() {
454         return hxDot;
455     }
456 
457     /** {@inheritDoc} */
458     @Override
459     public double getHy() {
460         return hy;
461     }
462 
463     /** {@inheritDoc} */
464     @Override
465     public double getHyDot() {
466         return hyDot;
467     }
468 
469     /** {@inheritDoc} */
470     @Override
471     public double getLv() {
472         switch (cachedPositionAngleType) {
473             case TRUE:
474                 return cachedL;
475 
476             case ECCENTRIC:
477                 return EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, cachedL);
478 
479             case MEAN:
480                 return EquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, cachedL);
481 
482             default:
483                 throw new OrekitInternalError(null);
484         }
485     }
486 
487     /** {@inheritDoc} */
488     @Override
489     public double getLvDot() {
490         switch (cachedPositionAngleType) {
491             case ECCENTRIC:
492                 final UnivariateDerivative1 lEUD = new UnivariateDerivative1(cachedL, cachedLDot);
493                 final UnivariateDerivative1 exUD     = new UnivariateDerivative1(ex,     exDot);
494                 final UnivariateDerivative1 eyUD     = new UnivariateDerivative1(ey,     eyDot);
495                 final UnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD,
496                         lEUD);
497                 return lvUD.getFirstDerivative();
498 
499             case TRUE:
500                 return cachedLDot;
501 
502             case MEAN:
503                 final UnivariateDerivative1 lMUD = new UnivariateDerivative1(cachedL, cachedLDot);
504                 final UnivariateDerivative1 exUD2    = new UnivariateDerivative1(ex,     exDot);
505                 final UnivariateDerivative1 eyUD2    = new UnivariateDerivative1(ey,     eyDot);
506                 final UnivariateDerivative1 lvUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD2,
507                         eyUD2, lMUD);
508                 return lvUD2.getFirstDerivative();
509 
510             default:
511                 throw new OrekitInternalError(null);
512         }
513     }
514 
515     /** {@inheritDoc} */
516     @Override
517     public double getLE() {
518         switch (cachedPositionAngleType) {
519             case TRUE:
520                 return EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, cachedL);
521 
522             case ECCENTRIC:
523                 return cachedL;
524 
525             case MEAN:
526                 return EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, cachedL);
527 
528             default:
529                 throw new OrekitInternalError(null);
530         }
531     }
532 
533     /** {@inheritDoc} */
534     @Override
535     public double getLEDot() {
536         switch (cachedPositionAngleType) {
537             case TRUE:
538                 final UnivariateDerivative1 lvUD = new UnivariateDerivative1(cachedL, cachedLDot);
539                 final UnivariateDerivative1 exUD     = new UnivariateDerivative1(ex,     exDot);
540                 final UnivariateDerivative1 eyUD     = new UnivariateDerivative1(ey,     eyDot);
541                 final UnivariateDerivative1 lEUD = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD,
542                         lvUD);
543                 return lEUD.getFirstDerivative();
544 
545             case ECCENTRIC:
546                 return cachedLDot;
547 
548             case MEAN:
549                 final UnivariateDerivative1 lMUD = new UnivariateDerivative1(cachedL, cachedLDot);
550                 final UnivariateDerivative1 exUD2    = new UnivariateDerivative1(ex,     exDot);
551                 final UnivariateDerivative1 eyUD2    = new UnivariateDerivative1(ey,     eyDot);
552                 final UnivariateDerivative1 lEUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD2,
553                         eyUD2, lMUD);
554                 return lEUD2.getFirstDerivative();
555 
556             default:
557                 throw new OrekitInternalError(null);
558         }
559     }
560 
561     /** {@inheritDoc} */
562     @Override
563     public double getLM() {
564         switch (cachedPositionAngleType) {
565             case TRUE:
566                 return EquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, cachedL);
567 
568             case MEAN:
569                 return cachedL;
570 
571             case ECCENTRIC:
572                 return EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, cachedL);
573 
574             default:
575                 throw new OrekitInternalError(null);
576         }
577     }
578 
579     /** {@inheritDoc} */
580     @Override
581     public double getLMDot() {
582         switch (cachedPositionAngleType) {
583             case TRUE:
584                 final UnivariateDerivative1 lvUD = new UnivariateDerivative1(cachedL, cachedLDot);
585                 final UnivariateDerivative1 exUD     = new UnivariateDerivative1(ex,     exDot);
586                 final UnivariateDerivative1 eyUD     = new UnivariateDerivative1(ey,     eyDot);
587                 final UnivariateDerivative1 lMUD = FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lvUD);
588                 return lMUD.getFirstDerivative();
589 
590             case MEAN:
591                 return cachedLDot;
592 
593             case ECCENTRIC:
594                 final UnivariateDerivative1 lEUD = new UnivariateDerivative1(cachedL, cachedLDot);
595                 final UnivariateDerivative1 exUD2    = new UnivariateDerivative1(ex,     exDot);
596                 final UnivariateDerivative1 eyUD2    = new UnivariateDerivative1(ey,     eyDot);
597                 final UnivariateDerivative1 lMUD2 = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD2,
598                         eyUD2, lEUD);
599                 return lMUD2.getFirstDerivative();
600 
601             default:
602                 throw new OrekitInternalError(null);
603         }
604     }
605 
606     /** Get the longitude argument.
607      * @param type type of the angle
608      * @return longitude argument (rad)
609      */
610     public double getL(final PositionAngleType type) {
611         return (type == PositionAngleType.MEAN) ? getLM() :
612                                               ((type == PositionAngleType.ECCENTRIC) ? getLE() :
613                                                                                    getLv());
614     }
615 
616     /** Get the longitude argument derivative.
617      * @param type type of the angle
618      * @return longitude argument derivative (rad/s)
619      */
620     public double getLDot(final PositionAngleType type) {
621         return (type == PositionAngleType.MEAN) ? getLMDot() :
622                                               ((type == PositionAngleType.ECCENTRIC) ? getLEDot() :
623                                                                                    getLvDot());
624     }
625 
626     /** {@inheritDoc} */
627     @Override
628     public double getE() {
629         return FastMath.sqrt(ex * ex + ey * ey);
630     }
631 
632     /** {@inheritDoc} */
633     @Override
634     public double getEDot() {
635         if (!hasNonKeplerianAcceleration()) {
636             return 0.;
637         }
638         return (ex * exDot + ey * eyDot) / FastMath.sqrt(ex * ex + ey * ey);
639     }
640 
641     /** {@inheritDoc} */
642     @Override
643     public double getI() {
644         return 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy));
645     }
646 
647     /** {@inheritDoc} */
648     @Override
649     public double getIDot() {
650         if (!hasNonKeplerianAcceleration()) {
651             return 0.;
652         }
653         final double h2 = hx * hx + hy * hy;
654         final double h  = FastMath.sqrt(h2);
655         return 2 * (hx * hxDot + hy * hyDot) / (h * (1 + h2));
656     }
657 
658     /** Compute position and velocity but not acceleration.
659      */
660     private void computePVWithoutA() {
661 
662         if (partialPV != null) {
663             // already computed
664             return;
665         }
666 
667         // get equinoctial parameters
668         final double lE = getLE();
669 
670         // inclination-related intermediate parameters
671         final double hx2   = hx * hx;
672         final double hy2   = hy * hy;
673         final double factH = 1. / (1 + hx2 + hy2);
674 
675         // reference axes defining the orbital plane
676         final double ux = (1 + hx2 - hy2) * factH;
677         final double uy =  2 * hx * hy * factH;
678         final double uz = -2 * hy * factH;
679 
680         final double vx = uy;
681         final double vy = (1 - hx2 + hy2) * factH;
682         final double vz =  2 * hx * factH;
683 
684         // eccentricity-related intermediate parameters
685         final double exey = ex * ey;
686         final double ex2  = ex * ex;
687         final double ey2  = ey * ey;
688         final double e2   = ex2 + ey2;
689         final double eta  = 1 + FastMath.sqrt(1 - e2);
690         final double beta = 1. / eta;
691 
692         // eccentric longitude argument
693         final SinCos scLe   = FastMath.sinCos(lE);
694         final double cLe    = scLe.cos();
695         final double sLe    = scLe.sin();
696         final double exCeyS = ex * cLe + ey * sLe;
697 
698         // coordinates of position and velocity in the orbital plane
699         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex);
700         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey);
701 
702         final double factor = FastMath.sqrt(getMu() / a) / (1 - exCeyS);
703         final double xdot   = factor * (-sLe + beta * ey * exCeyS);
704         final double ydot   = factor * ( cLe - beta * ex * exCeyS);
705 
706         final Vector3D position =
707                         new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
708         final Vector3D velocity =
709                         new Vector3D(xdot * ux + ydot * vx, xdot * uy + ydot * vy, xdot * uz + ydot * vz);
710         partialPV = new PVCoordinates(position, velocity);
711 
712     }
713 
714     /** Initialize cached argument of longitude with rate.
715      * @param l input argument of longitude
716      * @param lDot rate of input argument of longitude
717      * @param inputType position angle type passed as input
718      * @return argument of longitude to cache with rate
719      * @since 12.1
720      */
721     private UnivariateDerivative1 initializeCachedL(final double l, final double lDot,
722                                                     final PositionAngleType inputType) {
723         if (cachedPositionAngleType == inputType) {
724             return new UnivariateDerivative1(l, lDot);
725 
726         } else {
727             final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
728             final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
729             final UnivariateDerivative1 lUD = new UnivariateDerivative1(l, lDot);
730 
731             switch (cachedPositionAngleType) {
732 
733                 case ECCENTRIC:
734                     if (inputType == PositionAngleType.MEAN) {
735                         return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD, eyUD, lUD);
736                     } else {
737                         return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, lUD);
738                     }
739 
740                 case TRUE:
741                     if (inputType == PositionAngleType.MEAN) {
742                         return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lUD);
743                     } else {
744                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, lUD);
745                     }
746 
747                 case MEAN:
748                     if (inputType == PositionAngleType.TRUE) {
749                         return FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lUD);
750                     } else {
751                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD, eyUD, lUD);
752                     }
753 
754                 default:
755                     throw new OrekitInternalError(null);
756 
757             }
758 
759         }
760 
761     }
762 
763     /** Compute non-Keplerian part of the acceleration from first time derivatives.
764      * @return non-Keplerian part of the acceleration
765      */
766     private Vector3D nonKeplerianAcceleration() {
767 
768         final double[][] dCdP = new double[6][6];
769         getJacobianWrtParameters(PositionAngleType.MEAN, dCdP);
770 
771         final double nonKeplerianMeanMotion = getLMDot() - getKeplerianMeanMotion();
772         final double nonKeplerianAx = dCdP[3][0] * aDot  + dCdP[3][1] * exDot + dCdP[3][2] * eyDot +
773                                       dCdP[3][3] * hxDot + dCdP[3][4] * hyDot + dCdP[3][5] * nonKeplerianMeanMotion;
774         final double nonKeplerianAy = dCdP[4][0] * aDot  + dCdP[4][1] * exDot + dCdP[4][2] * eyDot +
775                                       dCdP[4][3] * hxDot + dCdP[4][4] * hyDot + dCdP[4][5] * nonKeplerianMeanMotion;
776         final double nonKeplerianAz = dCdP[5][0] * aDot  + dCdP[5][1] * exDot + dCdP[5][2] * eyDot +
777                                       dCdP[5][3] * hxDot + dCdP[5][4] * hyDot + dCdP[5][5] * nonKeplerianMeanMotion;
778 
779         return new Vector3D(nonKeplerianAx, nonKeplerianAy, nonKeplerianAz);
780 
781     }
782 
783     /** {@inheritDoc} */
784     @Override
785     protected Vector3D initPosition() {
786 
787         // get equinoctial parameters
788         final double lE = getLE();
789 
790         // inclination-related intermediate parameters
791         final double hx2   = hx * hx;
792         final double hy2   = hy * hy;
793         final double factH = 1. / (1 + hx2 + hy2);
794 
795         // reference axes defining the orbital plane
796         final double ux = (1 + hx2 - hy2) * factH;
797         final double uy =  2 * hx * hy * factH;
798         final double uz = -2 * hy * factH;
799 
800         final double vx = uy;
801         final double vy = (1 - hx2 + hy2) * factH;
802         final double vz =  2 * hx * factH;
803 
804         // eccentricity-related intermediate parameters
805         final double exey = ex * ey;
806         final double ex2  = ex * ex;
807         final double ey2  = ey * ey;
808         final double e2   = ex2 + ey2;
809         final double eta  = 1 + FastMath.sqrt(1 - e2);
810         final double beta = 1. / eta;
811 
812         // eccentric longitude argument
813         final SinCos scLe   = FastMath.sinCos(lE);
814         final double cLe    = scLe.cos();
815         final double sLe    = scLe.sin();
816 
817         // coordinates of position and velocity in the orbital plane
818         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex);
819         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey);
820 
821         return new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
822 
823     }
824 
825     /** {@inheritDoc} */
826     @Override
827     protected TimeStampedPVCoordinates initPVCoordinates() {
828 
829         // position and velocity
830         computePVWithoutA();
831 
832         // acceleration
833         final double r2 = partialPV.getPosition().getNormSq();
834         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
835         final Vector3D acceleration = hasNonKeplerianRates() ?
836                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
837                                       keplerianAcceleration;
838 
839         return new TimeStampedPVCoordinates(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);
840 
841     }
842 
843     /** {@inheritDoc} */
844     @Override
845     public EquinoctialOrbit inFrame(final Frame inertialFrame) {
846         final PVCoordinates pvCoordinates;
847         if (hasNonKeplerianAcceleration()) {
848             pvCoordinates = getPVCoordinates(inertialFrame);
849         } else {
850             final KinematicTransform transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
851             pvCoordinates = transform.transformOnlyPV(getPVCoordinates());
852         }
853         final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(pvCoordinates, inertialFrame, getDate(), getMu());
854         if (equinoctialOrbit.getCachedPositionAngleType() == getCachedPositionAngleType()) {
855             return equinoctialOrbit;
856         } else {
857             return equinoctialOrbit.withCachedPositionAngleType(getCachedPositionAngleType());
858         }
859     }
860 
861     /** {@inheritDoc} */
862     @Override
863     public EquinoctialOrbit withCachedPositionAngleType(final PositionAngleType positionAngleType) {
864         return new EquinoctialOrbit(a, ex, ey, hx, hy, getL(positionAngleType), aDot, exDot, eyDot, hxDot, hyDot,
865                 getLDot(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
866     }
867 
868     /** {@inheritDoc} */
869     @Override
870     public EquinoctialOrbit shiftedBy(final double dt) {
871         return shiftedBy(new TimeOffset(dt));
872     }
873 
874     /** {@inheritDoc} */
875     @Override
876     public EquinoctialOrbit shiftedBy(final TimeOffset dt) {
877 
878         final double dtS = dt.toDouble();
879 
880         // use Keplerian-only motion
881         final EquinoctialOrbit keplerianShifted = new EquinoctialOrbit(a, ex, ey, hx, hy,
882                                                                        getLM() + getKeplerianMeanMotion() * dtS,
883                                                                        PositionAngleType.MEAN, cachedPositionAngleType,
884                                                                        getFrame(),
885                                                                        getDate().shiftedBy(dt), getMu());
886 
887         if (dtS != 0. && hasNonKeplerianRates()) {
888 
889             // extract non-Keplerian acceleration from first time derivatives
890             final Vector3D nonKeplerianAcceleration = nonKeplerianAcceleration();
891 
892             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
893             keplerianShifted.computePVWithoutA();
894             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
895                                                    0.5 * dtS * dtS, nonKeplerianAcceleration);
896             final double   fixedR2 = fixedP.getNormSq();
897             final double   fixedR  = FastMath.sqrt(fixedR2);
898             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
899                                                   dtS, nonKeplerianAcceleration);
900             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
901                                                   1, nonKeplerianAcceleration);
902 
903             // build a new orbit, taking non-Keplerian acceleration into account
904             return new EquinoctialOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
905                                                                      fixedP, fixedV, fixedA),
906                                         keplerianShifted.getFrame(), keplerianShifted.getMu());
907 
908         } else {
909             // Keplerian-only motion is all we can do
910             return keplerianShifted;
911         }
912 
913     }
914 
915     /** {@inheritDoc} */
916     @Override
917     protected double[][] computeJacobianMeanWrtCartesian() {
918 
919         final double[][] jacobian = new double[6][6];
920 
921         // compute various intermediate parameters
922         computePVWithoutA();
923         final Vector3D position = partialPV.getPosition();
924         final Vector3D velocity = partialPV.getVelocity();
925         final double r2         = position.getNormSq();
926         final double r          = FastMath.sqrt(r2);
927         final double r3         = r * r2;
928 
929         final double mu         = getMu();
930         final double sqrtMuA    = FastMath.sqrt(a * mu);
931         final double a2         = a * a;
932 
933         final double e2         = ex * ex + ey * ey;
934         final double oMe2       = 1 - e2;
935         final double epsilon    = FastMath.sqrt(oMe2);
936         final double beta       = 1 / (1 + epsilon);
937         final double ratio      = epsilon * beta;
938 
939         final double hx2        = hx * hx;
940         final double hy2        = hy * hy;
941         final double hxhy       = hx * hy;
942 
943         // precomputing equinoctial frame unit vectors (f, g, w)
944         final Vector3D f  = new Vector3D(1 - hy2 + hx2, 2 * hxhy, -2 * hy).normalize();
945         final Vector3D g  = new Vector3D(2 * hxhy, 1 + hy2 - hx2, 2 * hx).normalize();
946         final Vector3D w  = Vector3D.crossProduct(position, velocity).normalize();
947 
948         // coordinates of the spacecraft in the equinoctial frame
949         final double x    = Vector3D.dotProduct(position, f);
950         final double y    = Vector3D.dotProduct(position, g);
951         final double xDot = Vector3D.dotProduct(velocity, f);
952         final double yDot = Vector3D.dotProduct(velocity, g);
953 
954         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
955         final double c1 = a / (sqrtMuA * epsilon);
956         final double c2 = a * sqrtMuA * beta / r3;
957         final double c3 = sqrtMuA / (r3 * epsilon);
958         final Vector3D drDotSdEx = new Vector3D( c1 * xDot * yDot - c2 * ey * x - c3 * x * y, f,
959                                                 -c1 * xDot * xDot - c2 * ey * y + c3 * x * x, g);
960 
961         // drDot / dEy = dXDot / dEy * f + dYDot / dEy * g
962         final Vector3D drDotSdEy = new Vector3D( c1 * yDot * yDot + c2 * ex * x - c3 * y * y, f,
963                                                 -c1 * xDot * yDot + c2 * ex * y + c3 * x * y, g);
964 
965         // da
966         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
967         final Vector3D vectorARDot = new Vector3D(2 * a2 / mu, velocity);
968         fillHalfRow(1, vectorAR,    jacobian[0], 0);
969         fillHalfRow(1, vectorARDot, jacobian[0], 3);
970 
971         // dEx
972         final double d1 = -a * ratio / r3;
973         final double d2 = (hy * xDot - hx * yDot) / (sqrtMuA * epsilon);
974         final double d3 = (hx * y - hy * x) / sqrtMuA;
975         final Vector3D vectorExRDot =
976             new Vector3D((2 * x * yDot - xDot * y) / mu, g, -y * yDot / mu, f, -ey * d3 / epsilon, w);
977         fillHalfRow(ex * d1, position, -ey * d2, w, epsilon / sqrtMuA, drDotSdEy, jacobian[1], 0);
978         fillHalfRow(1, vectorExRDot, jacobian[1], 3);
979 
980         // dEy
981         final Vector3D vectorEyRDot =
982             new Vector3D((2 * xDot * y - x * yDot) / mu, f, -x * xDot / mu, g, ex * d3 / epsilon, w);
983         fillHalfRow(ey * d1, position, ex * d2, w, -epsilon / sqrtMuA, drDotSdEx, jacobian[2], 0);
984         fillHalfRow(1, vectorEyRDot, jacobian[2], 3);
985 
986         // dHx
987         final double h = (1 + hx2 + hy2) / (2 * sqrtMuA * epsilon);
988         fillHalfRow(-h * xDot, w, jacobian[3], 0);
989         fillHalfRow( h * x,    w, jacobian[3], 3);
990 
991         // dHy
992         fillHalfRow(-h * yDot, w, jacobian[4], 0);
993         fillHalfRow( h * y,    w, jacobian[4], 3);
994 
995         // dLambdaM
996         final double l = -ratio / sqrtMuA;
997         fillHalfRow(-1 / sqrtMuA, velocity, d2, w, l * ex, drDotSdEx, l * ey, drDotSdEy, jacobian[5], 0);
998         fillHalfRow(-2 / sqrtMuA, position, ex * beta, vectorEyRDot, -ey * beta, vectorExRDot, d3, w, jacobian[5], 3);
999 
1000         return jacobian;
1001 
1002     }
1003 
1004     /** {@inheritDoc} */
1005     @Override
1006     protected double[][] computeJacobianEccentricWrtCartesian() {
1007 
1008         // start by computing the Jacobian with mean angle
1009         final double[][] jacobian = computeJacobianMeanWrtCartesian();
1010 
1011         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
1012         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
1013         // which is inverted and rewritten as:
1014         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
1015         final SinCos scLe  = FastMath.sinCos(getLE());
1016         final double cosLe = scLe.cos();
1017         final double sinLe = scLe.sin();
1018         final double aOr   = 1 / (1 - ex * cosLe - ey * sinLe);
1019 
1020         // update longitude row
1021         final double[] rowEx = jacobian[1];
1022         final double[] rowEy = jacobian[2];
1023         final double[] rowL  = jacobian[5];
1024         for (int j = 0; j < 6; ++j) {
1025             rowL[j] = aOr * (rowL[j] + sinLe * rowEx[j] - cosLe * rowEy[j]);
1026         }
1027 
1028         return jacobian;
1029 
1030     }
1031 
1032     /** {@inheritDoc} */
1033     @Override
1034     protected double[][] computeJacobianTrueWrtCartesian() {
1035 
1036         // start by computing the Jacobian with eccentric angle
1037         final double[][] jacobian = computeJacobianEccentricWrtCartesian();
1038 
1039         // Differentiating the eccentric longitude equation
1040         // tan((lv - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
1041         // leads to
1042         // cT (dlv - dlE) = cE dlE + cX dex + cY dey
1043         // with
1044         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
1045         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
1046         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
1047         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
1048         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
1049         // which can be solved to find the differential of the true longitude
1050         // dlv = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
1051         final SinCos scLe      = FastMath.sinCos(getLE());
1052         final double cosLe     = scLe.cos();
1053         final double sinLe     = scLe.sin();
1054         final double eSinE     = ex * sinLe - ey * cosLe;
1055         final double ecosE     = ex * cosLe + ey * sinLe;
1056         final double e2        = ex * ex + ey * ey;
1057         final double epsilon   = FastMath.sqrt(1 - e2);
1058         final double onePeps   = 1 + epsilon;
1059         final double d         = onePeps - ecosE;
1060         final double cT        = (d * d + eSinE * eSinE) / 2;
1061         final double cE        = ecosE * onePeps - e2;
1062         final double cX        = ex * eSinE / epsilon - ey + sinLe * onePeps;
1063         final double cY        = ey * eSinE / epsilon + ex - cosLe * onePeps;
1064         final double factorLe  = (cT + cE) / cT;
1065         final double factorEx  = cX / cT;
1066         final double factorEy  = cY / cT;
1067 
1068         // update longitude row
1069         final double[] rowEx = jacobian[1];
1070         final double[] rowEy = jacobian[2];
1071         final double[] rowL = jacobian[5];
1072         for (int j = 0; j < 6; ++j) {
1073             rowL[j] = factorLe * rowL[j] + factorEx * rowEx[j] + factorEy * rowEy[j];
1074         }
1075 
1076         return jacobian;
1077 
1078     }
1079 
1080     /** {@inheritDoc} */
1081     @Override
1082     public void addKeplerContribution(final PositionAngleType type, final double gm,
1083                                       final double[] pDot) {
1084         pDot[5] += computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType);
1085     }
1086 
1087     /**
1088      * Compute rate of argument of longitude.
1089      * @param type position angle type of rate
1090      * @param a semi major axis
1091      * @param ex ex
1092      * @param ey ey
1093      * @param mu mu
1094      * @param l argument of longitude
1095      * @param cachedType position angle type of passed l
1096      * @return first-order time derivative for l
1097      * @since 12.2
1098      */
1099     private static double computeKeplerianLDot(final PositionAngleType type, final double a, final double ex,
1100                                                final double ey, final double mu,
1101                                                final double l, final PositionAngleType cachedType) {
1102         final double n  = FastMath.sqrt(mu / a) / a;
1103         if (type == PositionAngleType.MEAN) {
1104             return n;
1105         }
1106         final double oMe2;
1107         final double ksi;
1108         final SinCos sc;
1109         if (type == PositionAngleType.ECCENTRIC) {
1110             sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
1111             ksi  = 1. / (1 - ex * sc.cos() - ey * sc.sin());
1112             return n * ksi;
1113         } else { // TRUE
1114             sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
1115             oMe2 = 1 - ex * ex - ey * ey;
1116             ksi  = 1 + ex * sc.cos() + ey * sc.sin();
1117             return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
1118         }
1119     }
1120 
1121     /**  Returns a string representation of this equinoctial parameters object.
1122      * @return a string representation of this object
1123      */
1124     public String toString() {
1125         return new StringBuilder().append("equinoctial parameters: ").append('{').
1126                                   append("a: ").append(a).
1127                                   append("; ex: ").append(ex).append("; ey: ").append(ey).
1128                                   append("; hx: ").append(hx).append("; hy: ").append(hy).
1129                                   append("; lv: ").append(FastMath.toDegrees(getLv())).
1130                                   append(";}").toString();
1131     }
1132 
1133     /** {@inheritDoc} */
1134     @Override
1135     public PositionAngleType getCachedPositionAngleType() {
1136         return cachedPositionAngleType;
1137     }
1138 
1139     /** {@inheritDoc} */
1140     @Override
1141     public boolean hasNonKeplerianRates() {
1142         return hasNonKeplerianAcceleration();
1143     }
1144 
1145     /** {@inheritDoc} */
1146     @Override
1147     public EquinoctialOrbit withKeplerianRates() {
1148         final PositionAngleType positionAngleType = getCachedPositionAngleType();
1149         return new EquinoctialOrbit(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(),
1150                 getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
1151     }
1152 
1153 }