1   /* Copyright 2002-2026 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.orbits;
18  
19  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 Periapsis 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.getNorm2Sq();
277         final double r       = FastMath.sqrt(r2);
278         final double V2      = pvV.getNorm2Sq();
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 AbstractOrbitFactory<EquinoctialOrbit> factory(final PositionAngleType positionAngleType,
412                                                           final double positionScale) {
413         return new EquinoctialOrbitFactory(this, positionScale, positionAngleType);
414     }
415 
416     /** {@inheritDoc} */
417     @Override
418     public double getA() {
419         return a;
420     }
421 
422     /** {@inheritDoc} */
423     @Override
424     public double getADot() {
425         return aDot;
426     }
427 
428     /** {@inheritDoc} */
429     @Override
430     public double getEquinoctialEx() {
431         return ex;
432     }
433 
434     /** {@inheritDoc} */
435     @Override
436     public double getEquinoctialExDot() {
437         return exDot;
438     }
439 
440     /** {@inheritDoc} */
441     @Override
442     public double getEquinoctialEy() {
443         return ey;
444     }
445 
446     /** {@inheritDoc} */
447     @Override
448     public double getEquinoctialEyDot() {
449         return eyDot;
450     }
451 
452     /** {@inheritDoc} */
453     @Override
454     public double getHx() {
455         return hx;
456     }
457 
458     /** {@inheritDoc} */
459     @Override
460     public double getHxDot() {
461         return hxDot;
462     }
463 
464     /** {@inheritDoc} */
465     @Override
466     public double getHy() {
467         return hy;
468     }
469 
470     /** {@inheritDoc} */
471     @Override
472     public double getHyDot() {
473         return hyDot;
474     }
475 
476     /** {@inheritDoc} */
477     @Override
478     public double getLv() {
479         return switch (cachedPositionAngleType) {
480             case TRUE -> cachedL;
481             case ECCENTRIC -> EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, cachedL);
482             case MEAN -> EquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, cachedL);
483         };
484     }
485 
486     /** {@inheritDoc} */
487     @Override
488     public double getLvDot() {
489         switch (cachedPositionAngleType) {
490             case ECCENTRIC:
491                 final UnivariateDerivative1 lEUD = new UnivariateDerivative1(cachedL, cachedLDot);
492                 final UnivariateDerivative1 exUD     = new UnivariateDerivative1(ex,     exDot);
493                 final UnivariateDerivative1 eyUD     = new UnivariateDerivative1(ey,     eyDot);
494                 final UnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD,
495                         lEUD);
496                 return lvUD.getFirstDerivative();
497 
498             case TRUE:
499                 return cachedLDot;
500 
501             case MEAN:
502                 final UnivariateDerivative1 lMUD = new UnivariateDerivative1(cachedL, cachedLDot);
503                 final UnivariateDerivative1 exUD2    = new UnivariateDerivative1(ex,     exDot);
504                 final UnivariateDerivative1 eyUD2    = new UnivariateDerivative1(ey,     eyDot);
505                 final UnivariateDerivative1 lvUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD2,
506                         eyUD2, lMUD);
507                 return lvUD2.getFirstDerivative();
508 
509             default:
510                 throw new OrekitInternalError(null);
511         }
512     }
513 
514     /** {@inheritDoc} */
515     @Override
516     public double getLE() {
517         return switch (cachedPositionAngleType) {
518             case TRUE -> EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, cachedL);
519             case ECCENTRIC -> cachedL;
520             case MEAN -> EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, cachedL);
521         };
522     }
523 
524     /** {@inheritDoc} */
525     @Override
526     public double getLEDot() {
527         switch (cachedPositionAngleType) {
528             case TRUE:
529                 final UnivariateDerivative1 lvUD = new UnivariateDerivative1(cachedL, cachedLDot);
530                 final UnivariateDerivative1 exUD     = new UnivariateDerivative1(ex,     exDot);
531                 final UnivariateDerivative1 eyUD     = new UnivariateDerivative1(ey,     eyDot);
532                 final UnivariateDerivative1 lEUD = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD,
533                         lvUD);
534                 return lEUD.getFirstDerivative();
535 
536             case ECCENTRIC:
537                 return cachedLDot;
538 
539             case MEAN:
540                 final UnivariateDerivative1 lMUD = new UnivariateDerivative1(cachedL, cachedLDot);
541                 final UnivariateDerivative1 exUD2    = new UnivariateDerivative1(ex,     exDot);
542                 final UnivariateDerivative1 eyUD2    = new UnivariateDerivative1(ey,     eyDot);
543                 final UnivariateDerivative1 lEUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD2,
544                         eyUD2, lMUD);
545                 return lEUD2.getFirstDerivative();
546 
547             default:
548                 throw new OrekitInternalError(null);
549         }
550     }
551 
552     /** {@inheritDoc} */
553     @Override
554     public double getLM() {
555         return switch (cachedPositionAngleType) {
556             case TRUE -> EquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, cachedL);
557             case MEAN -> cachedL;
558             case ECCENTRIC -> EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, cachedL);
559         };
560     }
561 
562     /** {@inheritDoc} */
563     @Override
564     public double getLMDot() {
565         switch (cachedPositionAngleType) {
566             case TRUE:
567                 final UnivariateDerivative1 lvUD = new UnivariateDerivative1(cachedL, cachedLDot);
568                 final UnivariateDerivative1 exUD     = new UnivariateDerivative1(ex,     exDot);
569                 final UnivariateDerivative1 eyUD     = new UnivariateDerivative1(ey,     eyDot);
570                 final UnivariateDerivative1 lMUD = FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lvUD);
571                 return lMUD.getFirstDerivative();
572 
573             case MEAN:
574                 return cachedLDot;
575 
576             case ECCENTRIC:
577                 final UnivariateDerivative1 lEUD = new UnivariateDerivative1(cachedL, cachedLDot);
578                 final UnivariateDerivative1 exUD2    = new UnivariateDerivative1(ex,     exDot);
579                 final UnivariateDerivative1 eyUD2    = new UnivariateDerivative1(ey,     eyDot);
580                 final UnivariateDerivative1 lMUD2 = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD2,
581                         eyUD2, lEUD);
582                 return lMUD2.getFirstDerivative();
583 
584             default:
585                 throw new OrekitInternalError(null);
586         }
587     }
588 
589     /** Get the longitude argument.
590      * @param type type of the angle
591      * @return longitude argument (rad)
592      */
593     public double getL(final PositionAngleType type) {
594         return (type == PositionAngleType.MEAN) ? getLM() :
595                                               ((type == PositionAngleType.ECCENTRIC) ? getLE() :
596                                                                                    getLv());
597     }
598 
599     /** Get the longitude argument derivative.
600      * @param type type of the angle
601      * @return longitude argument derivative (rad/s)
602      */
603     public double getLDot(final PositionAngleType type) {
604         return (type == PositionAngleType.MEAN) ? getLMDot() :
605                                               ((type == PositionAngleType.ECCENTRIC) ? getLEDot() :
606                                                                                    getLvDot());
607     }
608 
609     /** {@inheritDoc} */
610     @Override
611     public double getE() {
612         return FastMath.sqrt(ex * ex + ey * ey);
613     }
614 
615     /** {@inheritDoc} */
616     @Override
617     public double getEDot() {
618         if (!hasNonKeplerianAcceleration()) {
619             return 0.;
620         }
621         return (ex * exDot + ey * eyDot) / FastMath.sqrt(ex * ex + ey * ey);
622     }
623 
624     /** {@inheritDoc} */
625     @Override
626     public double getI() {
627         return 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy));
628     }
629 
630     /** {@inheritDoc} */
631     @Override
632     public double getIDot() {
633         if (!hasNonKeplerianAcceleration()) {
634             return 0.;
635         }
636         final double h2 = hx * hx + hy * hy;
637         final double h  = FastMath.sqrt(h2);
638         return 2 * (hx * hxDot + hy * hyDot) / (h * (1 + h2));
639     }
640 
641     /** Compute position and velocity but not acceleration.
642      */
643     private void computePVWithoutA() {
644 
645         if (partialPV != null) {
646             // already computed
647             return;
648         }
649 
650         // get equinoctial parameters
651         final double lE = getLE();
652 
653         // inclination-related intermediate parameters
654         final double hx2   = hx * hx;
655         final double hy2   = hy * hy;
656         final double factH = 1. / (1 + hx2 + hy2);
657 
658         // reference axes defining the orbital plane
659         final double ux = (1 + hx2 - hy2) * factH;
660         final double uy =  2 * hx * hy * factH;
661         final double uz = -2 * hy * factH;
662 
663         final double vx = uy;
664         final double vy = (1 - hx2 + hy2) * factH;
665         final double vz =  2 * hx * factH;
666 
667         // eccentricity-related intermediate parameters
668         final double exey = ex * ey;
669         final double ex2  = ex * ex;
670         final double ey2  = ey * ey;
671         final double e2   = ex2 + ey2;
672         final double eta  = 1 + FastMath.sqrt(1 - e2);
673         final double beta = 1. / eta;
674 
675         // eccentric longitude argument
676         final SinCos scLe   = FastMath.sinCos(lE);
677         final double cLe    = scLe.cos();
678         final double sLe    = scLe.sin();
679         final double exCeyS = ex * cLe + ey * sLe;
680 
681         // coordinates of position and velocity in the orbital plane
682         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex);
683         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey);
684 
685         final double factor = FastMath.sqrt(getMu() / a) / (1 - exCeyS);
686         final double xdot   = factor * (-sLe + beta * ey * exCeyS);
687         final double ydot   = factor * ( cLe - beta * ex * exCeyS);
688 
689         final Vector3D position =
690                         new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
691         final Vector3D velocity =
692                         new Vector3D(xdot * ux + ydot * vx, xdot * uy + ydot * vy, xdot * uz + ydot * vz);
693         partialPV = new PVCoordinates(position, velocity);
694 
695     }
696 
697     /** Initialize cached argument of longitude with rate.
698      * @param l input argument of longitude
699      * @param lDot rate of input argument of longitude
700      * @param inputType position angle type passed as input
701      * @return argument of longitude to cache with rate
702      * @since 12.1
703      */
704     private UnivariateDerivative1 initializeCachedL(final double l, final double lDot,
705                                                     final PositionAngleType inputType) {
706         if (cachedPositionAngleType == inputType) {
707             return new UnivariateDerivative1(l, lDot);
708 
709         } else {
710             final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
711             final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
712             final UnivariateDerivative1 lUD = new UnivariateDerivative1(l, lDot);
713 
714             switch (cachedPositionAngleType) {
715 
716                 case ECCENTRIC:
717                     if (inputType == PositionAngleType.MEAN) {
718                         return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD, eyUD, lUD);
719                     } else {
720                         return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, lUD);
721                     }
722 
723                 case TRUE:
724                     if (inputType == PositionAngleType.MEAN) {
725                         return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lUD);
726                     } else {
727                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, lUD);
728                     }
729 
730                 case MEAN:
731                     if (inputType == PositionAngleType.TRUE) {
732                         return FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lUD);
733                     } else {
734                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD, eyUD, lUD);
735                     }
736 
737                 default:
738                     throw new OrekitInternalError(null);
739 
740             }
741 
742         }
743 
744     }
745 
746     /** {@inheritDoc} */
747     @Override
748     protected Vector3D initPosition() {
749 
750         // get equinoctial parameters
751         final double lE = getLE();
752 
753         // inclination-related intermediate parameters
754         final double hx2   = hx * hx;
755         final double hy2   = hy * hy;
756         final double factH = 1. / (1 + hx2 + hy2);
757 
758         // reference axes defining the orbital plane
759         final double ux = (1 + hx2 - hy2) * factH;
760         final double uy =  2 * hx * hy * factH;
761         final double uz = -2 * hy * factH;
762 
763         final double vx = uy;
764         final double vy = (1 - hx2 + hy2) * factH;
765         final double vz =  2 * hx * factH;
766 
767         // eccentricity-related intermediate parameters
768         final double exey = ex * ey;
769         final double ex2  = ex * ex;
770         final double ey2  = ey * ey;
771         final double e2   = ex2 + ey2;
772         final double eta  = 1 + FastMath.sqrt(1 - e2);
773         final double beta = 1. / eta;
774 
775         // eccentric longitude argument
776         final SinCos scLe   = FastMath.sinCos(lE);
777         final double cLe    = scLe.cos();
778         final double sLe    = scLe.sin();
779 
780         // coordinates of position and velocity in the orbital plane
781         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex);
782         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey);
783 
784         return new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
785 
786     }
787 
788     /** {@inheritDoc} */
789     @Override
790     protected TimeStampedPVCoordinates initPVCoordinates() {
791 
792         // position and velocity
793         computePVWithoutA();
794 
795         // acceleration
796         final double r2 = partialPV.getPosition().getNorm2Sq();
797         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
798         final Vector3D acceleration = hasNonKeplerianRates() ?
799                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
800                                       keplerianAcceleration;
801 
802         return new TimeStampedPVCoordinates(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);
803 
804     }
805 
806     /** {@inheritDoc} */
807     @Override
808     public EquinoctialOrbit inFrame(final Frame inertialFrame) {
809         final PVCoordinates pvCoordinates;
810         if (hasNonKeplerianAcceleration()) {
811             pvCoordinates = getPVCoordinates(inertialFrame);
812         } else {
813             final KinematicTransform transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
814             pvCoordinates = transform.transformOnlyPV(getPVCoordinates());
815         }
816         final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(pvCoordinates, inertialFrame, getDate(), getMu());
817         if (equinoctialOrbit.getCachedPositionAngleType() == getCachedPositionAngleType()) {
818             return equinoctialOrbit;
819         } else {
820             return equinoctialOrbit.withCachedPositionAngleType(getCachedPositionAngleType());
821         }
822     }
823 
824     /** {@inheritDoc} */
825     @Override
826     public EquinoctialOrbit withCachedPositionAngleType(final PositionAngleType positionAngleType) {
827         return new EquinoctialOrbit(a, ex, ey, hx, hy, getL(positionAngleType), aDot, exDot, eyDot, hxDot, hyDot,
828                 getLDot(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
829     }
830 
831     /** {@inheritDoc} */
832     @Override
833     public EquinoctialOrbit shiftedBy(final double dt) {
834         return shiftedBy(new TimeOffset(dt));
835     }
836 
837     /** {@inheritDoc} */
838     @Override
839     public EquinoctialOrbit shiftedBy(final TimeOffset dt) {
840 
841         final double dtS = dt.toDouble();
842 
843         // use Keplerian-only motion
844         final EquinoctialOrbit keplerianShifted = new EquinoctialOrbit(a, ex, ey, hx, hy,
845                                                                        getLM() + getKeplerianMeanMotion() * dtS,
846                                                                        PositionAngleType.MEAN, cachedPositionAngleType,
847                                                                        getFrame(),
848                                                                        getDate().shiftedBy(dt), getMu());
849 
850         if (dtS != 0. && hasNonKeplerianRates()) {
851             final PVCoordinates pvCoordinates = shiftNonKeplerian(keplerianShifted.getPVCoordinates(), dtS);
852 
853             // build a new orbit, taking non-Keplerian acceleration into account
854             return new EquinoctialOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(), pvCoordinates),
855                                         keplerianShifted.getFrame(), keplerianShifted.getMu());
856 
857         } else {
858             // Keplerian-only motion is all we can do
859             return keplerianShifted;
860         }
861 
862     }
863 
864     /** {@inheritDoc} */
865     @Override
866     protected double[][] computeJacobianMeanWrtCartesian() {
867 
868         final double[][] jacobian = new double[6][6];
869 
870         // compute various intermediate parameters
871         computePVWithoutA();
872         final Vector3D position = partialPV.getPosition();
873         final Vector3D velocity = partialPV.getVelocity();
874         final double r2         = position.getNorm2Sq();
875         final double r          = FastMath.sqrt(r2);
876         final double r3         = r * r2;
877 
878         final double mu         = getMu();
879         final double sqrtMuA    = FastMath.sqrt(a * mu);
880         final double a2         = a * a;
881 
882         final double e2         = ex * ex + ey * ey;
883         final double oMe2       = 1 - e2;
884         final double epsilon    = FastMath.sqrt(oMe2);
885         final double beta       = 1 / (1 + epsilon);
886         final double ratio      = epsilon * beta;
887 
888         final double hx2        = hx * hx;
889         final double hy2        = hy * hy;
890         final double hxhy       = hx * hy;
891 
892         // precomputing equinoctial frame unit vectors (f, g, w)
893         final Vector3D f  = new Vector3D(1 - hy2 + hx2, 2 * hxhy, -2 * hy).normalize();
894         final Vector3D g  = new Vector3D(2 * hxhy, 1 + hy2 - hx2, 2 * hx).normalize();
895         final Vector3D w  = Vector3D.crossProduct(position, velocity).normalize();
896 
897         // coordinates of the spacecraft in the equinoctial frame
898         final double x    = Vector3D.dotProduct(position, f);
899         final double y    = Vector3D.dotProduct(position, g);
900         final double xDot = Vector3D.dotProduct(velocity, f);
901         final double yDot = Vector3D.dotProduct(velocity, g);
902 
903         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
904         final double c1 = a / (sqrtMuA * epsilon);
905         final double c2 = a * sqrtMuA * beta / r3;
906         final double c3 = sqrtMuA / (r3 * epsilon);
907         final Vector3D drDotSdEx = new Vector3D( c1 * xDot * yDot - c2 * ey * x - c3 * x * y, f,
908                                                 -c1 * xDot * xDot - c2 * ey * y + c3 * x * x, g);
909 
910         // drDot / dEy = dXDot / dEy * f + dYDot / dEy * g
911         final Vector3D drDotSdEy = new Vector3D( c1 * yDot * yDot + c2 * ex * x - c3 * y * y, f,
912                                                 -c1 * xDot * yDot + c2 * ex * y + c3 * x * y, g);
913 
914         // da
915         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
916         final Vector3D vectorARDot = new Vector3D(2 * a2 / mu, velocity);
917         fillHalfRow(1, vectorAR,    jacobian[0], 0);
918         fillHalfRow(1, vectorARDot, jacobian[0], 3);
919 
920         // dEx
921         final double d1 = -a * ratio / r3;
922         final double d2 = (hy * xDot - hx * yDot) / (sqrtMuA * epsilon);
923         final double d3 = (hx * y - hy * x) / sqrtMuA;
924         final Vector3D vectorExRDot =
925             new Vector3D((2 * x * yDot - xDot * y) / mu, g, -y * yDot / mu, f, -ey * d3 / epsilon, w);
926         fillHalfRow(ex * d1, position, -ey * d2, w, epsilon / sqrtMuA, drDotSdEy, jacobian[1], 0);
927         fillHalfRow(1, vectorExRDot, jacobian[1], 3);
928 
929         // dEy
930         final Vector3D vectorEyRDot =
931             new Vector3D((2 * xDot * y - x * yDot) / mu, f, -x * xDot / mu, g, ex * d3 / epsilon, w);
932         fillHalfRow(ey * d1, position, ex * d2, w, -epsilon / sqrtMuA, drDotSdEx, jacobian[2], 0);
933         fillHalfRow(1, vectorEyRDot, jacobian[2], 3);
934 
935         // dHx
936         final double h = (1 + hx2 + hy2) / (2 * sqrtMuA * epsilon);
937         fillHalfRow(-h * xDot, w, jacobian[3], 0);
938         fillHalfRow( h * x,    w, jacobian[3], 3);
939 
940         // dHy
941         fillHalfRow(-h * yDot, w, jacobian[4], 0);
942         fillHalfRow( h * y,    w, jacobian[4], 3);
943 
944         // dLambdaM
945         final double l = -ratio / sqrtMuA;
946         fillHalfRow(-1 / sqrtMuA, velocity, d2, w, l * ex, drDotSdEx, l * ey, drDotSdEy, jacobian[5], 0);
947         fillHalfRow(-2 / sqrtMuA, position, ex * beta, vectorEyRDot, -ey * beta, vectorExRDot, d3, w, jacobian[5], 3);
948 
949         return jacobian;
950 
951     }
952 
953     /** {@inheritDoc} */
954     @Override
955     protected double[][] computeJacobianEccentricWrtCartesian() {
956 
957         // start by computing the Jacobian with mean angle
958         final double[][] jacobian = computeJacobianMeanWrtCartesian();
959 
960         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
961         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
962         // which is inverted and rewritten as:
963         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
964         final SinCos scLe  = FastMath.sinCos(getLE());
965         final double cosLe = scLe.cos();
966         final double sinLe = scLe.sin();
967         final double aOr   = 1 / (1 - ex * cosLe - ey * sinLe);
968 
969         // update longitude row
970         final double[] rowEx = jacobian[1];
971         final double[] rowEy = jacobian[2];
972         final double[] rowL  = jacobian[5];
973         for (int j = 0; j < 6; ++j) {
974             rowL[j] = aOr * (rowL[j] + sinLe * rowEx[j] - cosLe * rowEy[j]);
975         }
976 
977         return jacobian;
978 
979     }
980 
981     /** {@inheritDoc} */
982     @Override
983     protected double[][] computeJacobianTrueWrtCartesian() {
984 
985         // start by computing the Jacobian with eccentric angle
986         final double[][] jacobian = computeJacobianEccentricWrtCartesian();
987 
988         // Differentiating the eccentric longitude equation
989         // tan((lv - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
990         // leads to
991         // cT (dlv - dlE) = cE dlE + cX dex + cY dey
992         // with
993         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
994         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
995         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
996         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
997         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
998         // which can be solved to find the differential of the true longitude
999         // dlv = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
1000         final SinCos scLe      = FastMath.sinCos(getLE());
1001         final double cosLe     = scLe.cos();
1002         final double sinLe     = scLe.sin();
1003         final double eSinE     = ex * sinLe - ey * cosLe;
1004         final double ecosE     = ex * cosLe + ey * sinLe;
1005         final double e2        = ex * ex + ey * ey;
1006         final double epsilon   = FastMath.sqrt(1 - e2);
1007         final double onePeps   = 1 + epsilon;
1008         final double d         = onePeps - ecosE;
1009         final double cT        = (d * d + eSinE * eSinE) / 2;
1010         final double cE        = ecosE * onePeps - e2;
1011         final double cX        = ex * eSinE / epsilon - ey + sinLe * onePeps;
1012         final double cY        = ey * eSinE / epsilon + ex - cosLe * onePeps;
1013         final double factorLe  = (cT + cE) / cT;
1014         final double factorEx  = cX / cT;
1015         final double factorEy  = cY / cT;
1016 
1017         // update longitude row
1018         final double[] rowEx = jacobian[1];
1019         final double[] rowEy = jacobian[2];
1020         final double[] rowL = jacobian[5];
1021         for (int j = 0; j < 6; ++j) {
1022             rowL[j] = factorLe * rowL[j] + factorEx * rowEx[j] + factorEy * rowEy[j];
1023         }
1024 
1025         return jacobian;
1026 
1027     }
1028 
1029     /** {@inheritDoc} */
1030     @Override
1031     public void addKeplerContribution(final PositionAngleType type, final double gm,
1032                                       final double[] pDot) {
1033         pDot[5] += computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType);
1034     }
1035 
1036     /**
1037      * Compute rate of argument of longitude.
1038      * @param type position angle type of rate
1039      * @param a semi major axis
1040      * @param ex ex
1041      * @param ey ey
1042      * @param mu mu
1043      * @param l argument of longitude
1044      * @param cachedType position angle type of passed l
1045      * @return first-order time derivative for l
1046      * @since 12.2
1047      */
1048     private static double computeKeplerianLDot(final PositionAngleType type, final double a, final double ex,
1049                                                final double ey, final double mu,
1050                                                final double l, final PositionAngleType cachedType) {
1051         final double n  = FastMath.sqrt(mu / a) / a;
1052         if (type == PositionAngleType.MEAN) {
1053             return n;
1054         }
1055         final double oMe2;
1056         final double ksi;
1057         final SinCos sc;
1058         if (type == PositionAngleType.ECCENTRIC) {
1059             sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
1060             ksi  = 1. / (1 - ex * sc.cos() - ey * sc.sin());
1061             return n * ksi;
1062         } else { // TRUE
1063             sc = FastMath.sinCos(EquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
1064             oMe2 = 1 - ex * ex - ey * ey;
1065             ksi  = 1 + ex * sc.cos() + ey * sc.sin();
1066             return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
1067         }
1068     }
1069 
1070     /**  Returns a string representation of this equinoctial parameters object.
1071      * @return a string representation of this object
1072      */
1073     public String toString() {
1074         return new StringBuilder().append("equinoctial parameters: ").append('{').
1075                                   append("a: ").append(a).
1076                                   append("; ex: ").append(ex).append("; ey: ").append(ey).
1077                                   append("; hx: ").append(hx).append("; hy: ").append(hy).
1078                                   append("; lv: ").append(FastMath.toDegrees(getLv())).
1079                                   append(";}").toString();
1080     }
1081 
1082     /** {@inheritDoc} */
1083     @Override
1084     public PositionAngleType getCachedPositionAngleType() {
1085         return cachedPositionAngleType;
1086     }
1087 
1088     /** {@inheritDoc} */
1089     @Override
1090     public boolean hasNonKeplerianRates() {
1091         return hasNonKeplerianAcceleration();
1092     }
1093 
1094     /** {@inheritDoc} */
1095     @Override
1096     public EquinoctialOrbit withKeplerianRates() {
1097         final PositionAngleType positionAngleType = getCachedPositionAngleType();
1098         return new EquinoctialOrbit(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(),
1099                 getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
1100     }
1101 
1102 }