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 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     /** {@inheritDoc} */
764     @Override
765     protected Vector3D initPosition() {
766 
767         // get equinoctial parameters
768         final double lE = getLE();
769 
770         // inclination-related intermediate parameters
771         final double hx2   = hx * hx;
772         final double hy2   = hy * hy;
773         final double factH = 1. / (1 + hx2 + hy2);
774 
775         // reference axes defining the orbital plane
776         final double ux = (1 + hx2 - hy2) * factH;
777         final double uy =  2 * hx * hy * factH;
778         final double uz = -2 * hy * factH;
779 
780         final double vx = uy;
781         final double vy = (1 - hx2 + hy2) * factH;
782         final double vz =  2 * hx * factH;
783 
784         // eccentricity-related intermediate parameters
785         final double exey = ex * ey;
786         final double ex2  = ex * ex;
787         final double ey2  = ey * ey;
788         final double e2   = ex2 + ey2;
789         final double eta  = 1 + FastMath.sqrt(1 - e2);
790         final double beta = 1. / eta;
791 
792         // eccentric longitude argument
793         final SinCos scLe   = FastMath.sinCos(lE);
794         final double cLe    = scLe.cos();
795         final double sLe    = scLe.sin();
796 
797         // coordinates of position and velocity in the orbital plane
798         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex);
799         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey);
800 
801         return new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
802 
803     }
804 
805     /** {@inheritDoc} */
806     @Override
807     protected TimeStampedPVCoordinates initPVCoordinates() {
808 
809         // position and velocity
810         computePVWithoutA();
811 
812         // acceleration
813         final double r2 = partialPV.getPosition().getNormSq();
814         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
815         final Vector3D acceleration = hasNonKeplerianRates() ?
816                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
817                                       keplerianAcceleration;
818 
819         return new TimeStampedPVCoordinates(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);
820 
821     }
822 
823     /** {@inheritDoc} */
824     @Override
825     public EquinoctialOrbit inFrame(final Frame inertialFrame) {
826         final PVCoordinates pvCoordinates;
827         if (hasNonKeplerianAcceleration()) {
828             pvCoordinates = getPVCoordinates(inertialFrame);
829         } else {
830             final KinematicTransform transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
831             pvCoordinates = transform.transformOnlyPV(getPVCoordinates());
832         }
833         final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(pvCoordinates, inertialFrame, getDate(), getMu());
834         if (equinoctialOrbit.getCachedPositionAngleType() == getCachedPositionAngleType()) {
835             return equinoctialOrbit;
836         } else {
837             return equinoctialOrbit.withCachedPositionAngleType(getCachedPositionAngleType());
838         }
839     }
840 
841     /** {@inheritDoc} */
842     @Override
843     public EquinoctialOrbit withCachedPositionAngleType(final PositionAngleType positionAngleType) {
844         return new EquinoctialOrbit(a, ex, ey, hx, hy, getL(positionAngleType), aDot, exDot, eyDot, hxDot, hyDot,
845                 getLDot(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
846     }
847 
848     /** {@inheritDoc} */
849     @Override
850     public EquinoctialOrbit shiftedBy(final double dt) {
851         return shiftedBy(new TimeOffset(dt));
852     }
853 
854     /** {@inheritDoc} */
855     @Override
856     public EquinoctialOrbit shiftedBy(final TimeOffset dt) {
857 
858         final double dtS = dt.toDouble();
859 
860         // use Keplerian-only motion
861         final EquinoctialOrbit keplerianShifted = new EquinoctialOrbit(a, ex, ey, hx, hy,
862                                                                        getLM() + getKeplerianMeanMotion() * dtS,
863                                                                        PositionAngleType.MEAN, cachedPositionAngleType,
864                                                                        getFrame(),
865                                                                        getDate().shiftedBy(dt), getMu());
866 
867         if (dtS != 0. && hasNonKeplerianRates()) {
868 
869             // extract non-Keplerian acceleration from first time derivatives
870             final Vector3D nonKeplerianAcceleration = nonKeplerianAcceleration();
871 
872             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
873             keplerianShifted.computePVWithoutA();
874             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
875                                                    0.5 * dtS * dtS, nonKeplerianAcceleration);
876             final double   fixedR2 = fixedP.getNormSq();
877             final double   fixedR  = FastMath.sqrt(fixedR2);
878             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
879                                                   dtS, nonKeplerianAcceleration);
880             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
881                                                   1, nonKeplerianAcceleration);
882 
883             // build a new orbit, taking non-Keplerian acceleration into account
884             return new EquinoctialOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
885                                                                      fixedP, fixedV, fixedA),
886                                         keplerianShifted.getFrame(), keplerianShifted.getMu());
887 
888         } else {
889             // Keplerian-only motion is all we can do
890             return keplerianShifted;
891         }
892 
893     }
894 
895     /** {@inheritDoc} */
896     @Override
897     protected double[][] computeJacobianMeanWrtCartesian() {
898 
899         final double[][] jacobian = new double[6][6];
900 
901         // compute various intermediate parameters
902         computePVWithoutA();
903         final Vector3D position = partialPV.getPosition();
904         final Vector3D velocity = partialPV.getVelocity();
905         final double r2         = position.getNormSq();
906         final double r          = FastMath.sqrt(r2);
907         final double r3         = r * r2;
908 
909         final double mu         = getMu();
910         final double sqrtMuA    = FastMath.sqrt(a * mu);
911         final double a2         = a * a;
912 
913         final double e2         = ex * ex + ey * ey;
914         final double oMe2       = 1 - e2;
915         final double epsilon    = FastMath.sqrt(oMe2);
916         final double beta       = 1 / (1 + epsilon);
917         final double ratio      = epsilon * beta;
918 
919         final double hx2        = hx * hx;
920         final double hy2        = hy * hy;
921         final double hxhy       = hx * hy;
922 
923         // precomputing equinoctial frame unit vectors (f, g, w)
924         final Vector3D f  = new Vector3D(1 - hy2 + hx2, 2 * hxhy, -2 * hy).normalize();
925         final Vector3D g  = new Vector3D(2 * hxhy, 1 + hy2 - hx2, 2 * hx).normalize();
926         final Vector3D w  = Vector3D.crossProduct(position, velocity).normalize();
927 
928         // coordinates of the spacecraft in the equinoctial frame
929         final double x    = Vector3D.dotProduct(position, f);
930         final double y    = Vector3D.dotProduct(position, g);
931         final double xDot = Vector3D.dotProduct(velocity, f);
932         final double yDot = Vector3D.dotProduct(velocity, g);
933 
934         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
935         final double c1 = a / (sqrtMuA * epsilon);
936         final double c2 = a * sqrtMuA * beta / r3;
937         final double c3 = sqrtMuA / (r3 * epsilon);
938         final Vector3D drDotSdEx = new Vector3D( c1 * xDot * yDot - c2 * ey * x - c3 * x * y, f,
939                                                 -c1 * xDot * xDot - c2 * ey * y + c3 * x * x, g);
940 
941         // drDot / dEy = dXDot / dEy * f + dYDot / dEy * g
942         final Vector3D drDotSdEy = new Vector3D( c1 * yDot * yDot + c2 * ex * x - c3 * y * y, f,
943                                                 -c1 * xDot * yDot + c2 * ex * y + c3 * x * y, g);
944 
945         // da
946         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
947         final Vector3D vectorARDot = new Vector3D(2 * a2 / mu, velocity);
948         fillHalfRow(1, vectorAR,    jacobian[0], 0);
949         fillHalfRow(1, vectorARDot, jacobian[0], 3);
950 
951         // dEx
952         final double d1 = -a * ratio / r3;
953         final double d2 = (hy * xDot - hx * yDot) / (sqrtMuA * epsilon);
954         final double d3 = (hx * y - hy * x) / sqrtMuA;
955         final Vector3D vectorExRDot =
956             new Vector3D((2 * x * yDot - xDot * y) / mu, g, -y * yDot / mu, f, -ey * d3 / epsilon, w);
957         fillHalfRow(ex * d1, position, -ey * d2, w, epsilon / sqrtMuA, drDotSdEy, jacobian[1], 0);
958         fillHalfRow(1, vectorExRDot, jacobian[1], 3);
959 
960         // dEy
961         final Vector3D vectorEyRDot =
962             new Vector3D((2 * xDot * y - x * yDot) / mu, f, -x * xDot / mu, g, ex * d3 / epsilon, w);
963         fillHalfRow(ey * d1, position, ex * d2, w, -epsilon / sqrtMuA, drDotSdEx, jacobian[2], 0);
964         fillHalfRow(1, vectorEyRDot, jacobian[2], 3);
965 
966         // dHx
967         final double h = (1 + hx2 + hy2) / (2 * sqrtMuA * epsilon);
968         fillHalfRow(-h * xDot, w, jacobian[3], 0);
969         fillHalfRow( h * x,    w, jacobian[3], 3);
970 
971         // dHy
972         fillHalfRow(-h * yDot, w, jacobian[4], 0);
973         fillHalfRow( h * y,    w, jacobian[4], 3);
974 
975         // dLambdaM
976         final double l = -ratio / sqrtMuA;
977         fillHalfRow(-1 / sqrtMuA, velocity, d2, w, l * ex, drDotSdEx, l * ey, drDotSdEy, jacobian[5], 0);
978         fillHalfRow(-2 / sqrtMuA, position, ex * beta, vectorEyRDot, -ey * beta, vectorExRDot, d3, w, jacobian[5], 3);
979 
980         return jacobian;
981 
982     }
983 
984     /** {@inheritDoc} */
985     @Override
986     protected double[][] computeJacobianEccentricWrtCartesian() {
987 
988         // start by computing the Jacobian with mean angle
989         final double[][] jacobian = computeJacobianMeanWrtCartesian();
990 
991         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
992         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
993         // which is inverted and rewritten as:
994         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
995         final SinCos scLe  = FastMath.sinCos(getLE());
996         final double cosLe = scLe.cos();
997         final double sinLe = scLe.sin();
998         final double aOr   = 1 / (1 - ex * cosLe - ey * sinLe);
999 
1000         // update longitude row
1001         final double[] rowEx = jacobian[1];
1002         final double[] rowEy = jacobian[2];
1003         final double[] rowL  = jacobian[5];
1004         for (int j = 0; j < 6; ++j) {
1005             rowL[j] = aOr * (rowL[j] + sinLe * rowEx[j] - cosLe * rowEy[j]);
1006         }
1007 
1008         return jacobian;
1009 
1010     }
1011 
1012     /** {@inheritDoc} */
1013     @Override
1014     protected double[][] computeJacobianTrueWrtCartesian() {
1015 
1016         // start by computing the Jacobian with eccentric angle
1017         final double[][] jacobian = computeJacobianEccentricWrtCartesian();
1018 
1019         // Differentiating the eccentric longitude equation
1020         // tan((lv - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
1021         // leads to
1022         // cT (dlv - dlE) = cE dlE + cX dex + cY dey
1023         // with
1024         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
1025         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
1026         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
1027         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
1028         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
1029         // which can be solved to find the differential of the true longitude
1030         // dlv = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
1031         final SinCos scLe      = FastMath.sinCos(getLE());
1032         final double cosLe     = scLe.cos();
1033         final double sinLe     = scLe.sin();
1034         final double eSinE     = ex * sinLe - ey * cosLe;
1035         final double ecosE     = ex * cosLe + ey * sinLe;
1036         final double e2        = ex * ex + ey * ey;
1037         final double epsilon   = FastMath.sqrt(1 - e2);
1038         final double onePeps   = 1 + epsilon;
1039         final double d         = onePeps - ecosE;
1040         final double cT        = (d * d + eSinE * eSinE) / 2;
1041         final double cE        = ecosE * onePeps - e2;
1042         final double cX        = ex * eSinE / epsilon - ey + sinLe * onePeps;
1043         final double cY        = ey * eSinE / epsilon + ex - cosLe * onePeps;
1044         final double factorLe  = (cT + cE) / cT;
1045         final double factorEx  = cX / cT;
1046         final double factorEy  = cY / cT;
1047 
1048         // update longitude row
1049         final double[] rowEx = jacobian[1];
1050         final double[] rowEy = jacobian[2];
1051         final double[] rowL = jacobian[5];
1052         for (int j = 0; j < 6; ++j) {
1053             rowL[j] = factorLe * rowL[j] + factorEx * rowEx[j] + factorEy * rowEy[j];
1054         }
1055 
1056         return jacobian;
1057 
1058     }
1059 
1060     /** {@inheritDoc} */
1061     @Override
1062     public void addKeplerContribution(final PositionAngleType type, final double gm,
1063                                       final double[] pDot) {
1064         pDot[5] += computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType);
1065     }
1066 
1067     /**
1068      * Compute rate of argument of longitude.
1069      * @param type position angle type of rate
1070      * @param a semi major axis
1071      * @param ex ex
1072      * @param ey ey
1073      * @param mu mu
1074      * @param l argument of longitude
1075      * @param cachedType position angle type of passed l
1076      * @return first-order time derivative for l
1077      * @since 12.2
1078      */
1079     private static double computeKeplerianLDot(final PositionAngleType type, final double a, final double ex,
1080                                                final double ey, final double mu,
1081                                                final double l, final PositionAngleType cachedType) {
1082         final double n  = FastMath.sqrt(mu / a) / a;
1083         if (type == PositionAngleType.MEAN) {
1084             return n;
1085         }
1086         final double oMe2;
1087         final double ksi;
1088         final SinCos sc;
1089         if (type == PositionAngleType.ECCENTRIC) {
1090             sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
1091             ksi  = 1. / (1 - ex * sc.cos() - ey * sc.sin());
1092             return n * ksi;
1093         } else { // TRUE
1094             sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
1095             oMe2 = 1 - ex * ex - ey * ey;
1096             ksi  = 1 + ex * sc.cos() + ey * sc.sin();
1097             return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
1098         }
1099     }
1100 
1101     /**  Returns a string representation of this equinoctial parameters object.
1102      * @return a string representation of this object
1103      */
1104     public String toString() {
1105         return new StringBuilder().append("equinoctial parameters: ").append('{').
1106                                   append("a: ").append(a).
1107                                   append("; ex: ").append(ex).append("; ey: ").append(ey).
1108                                   append("; hx: ").append(hx).append("; hy: ").append(hy).
1109                                   append("; lv: ").append(FastMath.toDegrees(getLv())).
1110                                   append(";}").toString();
1111     }
1112 
1113     /** {@inheritDoc} */
1114     @Override
1115     public PositionAngleType getCachedPositionAngleType() {
1116         return cachedPositionAngleType;
1117     }
1118 
1119     /** {@inheritDoc} */
1120     @Override
1121     public boolean hasNonKeplerianRates() {
1122         return hasNonKeplerianAcceleration();
1123     }
1124 
1125     /** {@inheritDoc} */
1126     @Override
1127     public EquinoctialOrbit withKeplerianRates() {
1128         final PositionAngleType positionAngleType = getCachedPositionAngleType();
1129         return new EquinoctialOrbit(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(),
1130                 getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
1131     }
1132 
1133 }