FieldEquinoctialOrbit.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.orbits;

  18. import org.hipparchus.Field;
  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.FieldSinCos;
  24. import org.hipparchus.util.MathArrays;
  25. import org.orekit.errors.OrekitIllegalArgumentException;
  26. import org.orekit.errors.OrekitInternalError;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.time.FieldAbsoluteDate;
  30. import org.orekit.utils.FieldPVCoordinates;
  31. import org.orekit.utils.TimeStampedFieldPVCoordinates;


  32. /**
  33.  * This class handles equinoctial orbital parameters, which can support both
  34.  * circular and equatorial orbits.
  35.  * <p>
  36.  * The parameters used internally are the equinoctial elements which can be
  37.  * related to Keplerian elements as follows:
  38.  *   <pre>
  39.  *     a
  40.  *     ex = e cos(ω + Ω)
  41.  *     ey = e sin(ω + Ω)
  42.  *     hx = tan(i/2) cos(Ω)
  43.  *     hy = tan(i/2) sin(Ω)
  44.  *     lv = v + ω + Ω
  45.  *   </pre>
  46.  * where ω stands for the Perigee Argument and Ω stands for the
  47.  * Right Ascension of the Ascending Node.
  48.  * <p>
  49.  * The conversion equations from and to Keplerian elements given above hold only
  50.  * when both sides are unambiguously defined, i.e. when orbit is neither equatorial
  51.  * nor circular. When orbit is either equatorial or circular, the equinoctial
  52.  * parameters are still unambiguously defined whereas some Keplerian elements
  53.  * (more precisely ω and Ω) become ambiguous. For this reason, equinoctial
  54.  * parameters are the recommended way to represent orbits. Note however than
  55.  * the present implementation does not handle non-elliptical cases.
  56.  * </p>
  57.  * <p>
  58.  * The instance <code>EquinoctialOrbit</code> is guaranteed to be immutable.
  59.  * </p>
  60.  * @see    Orbit
  61.  * @see    KeplerianOrbit
  62.  * @see    CircularOrbit
  63.  * @see    CartesianOrbit
  64.  * @author Mathieu Rom&eacute;ro
  65.  * @author Luc Maisonobe
  66.  * @author Guylaine Prat
  67.  * @author Fabien Maussion
  68.  * @author V&eacute;ronique Pommier-Maurussane
  69.  * @since 9.0
  70.  * @param <T> type of the field elements
  71.  */
  72. public class FieldEquinoctialOrbit<T extends CalculusFieldElement<T>> extends FieldOrbit<T>
  73.         implements PositionAngleBased {

  74.     /** Semi-major axis (m). */
  75.     private final T a;

  76.     /** First component of the eccentricity vector. */
  77.     private final T ex;

  78.     /** Second component of the eccentricity vector. */
  79.     private final T ey;

  80.     /** First component of the inclination vector. */
  81.     private final T hx;

  82.     /** Second component of the inclination vector. */
  83.     private final T hy;

  84.     /** Cached longitude argument (rad). */
  85.     private final T cachedL;

  86.     /** Cache type of position angle (longitude argument). */
  87.     private final PositionAngleType cachedPositionAngleType;

  88.     /** Semi-major axis derivative (m/s). */
  89.     private final T aDot;

  90.     /** First component of the eccentricity vector derivative. */
  91.     private final T exDot;

  92.     /** Second component of the eccentricity vector derivative. */
  93.     private final T eyDot;

  94.     /** First component of the inclination vector derivative. */
  95.     private final T hxDot;

  96.     /** Second component of the inclination vector derivative. */
  97.     private final T hyDot;

  98.     /** Derivative of cached longitude argument (rad/s). */
  99.     private final T cachedLDot;

  100.     /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */
  101.     private FieldPVCoordinates<T> partialPV;

  102.     /** Creates a new instance.
  103.      * @param a  semi-major axis (m)
  104.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  105.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  106.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  107.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  108.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  109.      * @param type type of longitude argument
  110.      * @param cachedPositionAngleType type of cached longitude argument
  111.      * @param frame the frame in which the parameters are defined
  112.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  113.      * @param date date of the orbital parameters
  114.      * @param mu central attraction coefficient (m³/s²)
  115.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  116.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  117.      * @since 12.1
  118.      */
  119.     public FieldEquinoctialOrbit(final T a, final T ex, final T ey,
  120.                                  final T hx, final T hy, final T l,
  121.                                  final PositionAngleType type, final PositionAngleType cachedPositionAngleType,
  122.                                  final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  123.         throws IllegalArgumentException {
  124.         this(a, ex, ey, hx, hy, l,
  125.              null, null, null, null, null, null,
  126.              type, cachedPositionAngleType, frame, date, mu);
  127.     }

  128.     /** Creates a new instance.
  129.      * @param a  semi-major axis (m)
  130.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  131.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  132.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  133.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  134.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  135.      * @param type type of longitude argument
  136.      * @param frame the frame in which the parameters are defined
  137.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  138.      * @param date date of the orbital parameters
  139.      * @param mu central attraction coefficient (m³/s²)
  140.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  141.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  142.      */
  143.     public FieldEquinoctialOrbit(final T a, final T ex, final T ey,
  144.                                  final T hx, final T hy, final T l,
  145.                                  final PositionAngleType type,
  146.                                  final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  147.             throws IllegalArgumentException {
  148.         this(a, ex, ey, hx, hy, l,
  149.                 null, null, null, null, null, null,
  150.                 type, type, frame, date, mu);
  151.     }

  152.     /** Creates a new instance.
  153.      * @param a  semi-major axis (m)
  154.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  155.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  156.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  157.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  158.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  159.      * @param aDot  semi-major axis derivative (m/s)
  160.      * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative
  161.      * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative
  162.      * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative
  163.      * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative
  164.      * @param lDot  d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument  derivative (rad/s)
  165.      * @param type type of longitude argument
  166.      * @param cachedPositionAngleType of cached longitude argument
  167.      * @param frame the frame in which the parameters are defined
  168.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  169.      * @param date date of the orbital parameters
  170.      * @param mu central attraction coefficient (m³/s²)
  171.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  172.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  173.      * @since 12.1
  174.      */
  175.     public FieldEquinoctialOrbit(final T a, final T ex, final T ey,
  176.                                  final T hx, final T hy, final T l,
  177.                                  final T aDot, final T exDot, final T eyDot,
  178.                                  final T hxDot, final T hyDot, final T lDot,
  179.                                  final PositionAngleType type, final PositionAngleType cachedPositionAngleType,
  180.                                  final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  181.         throws IllegalArgumentException {
  182.         super(frame, date, mu);

  183.         if (ex.getReal() * ex.getReal() + ey.getReal() * ey.getReal() >= 1.0) {
  184.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  185.                                                      getClass().getName());
  186.         }
  187.         this.cachedPositionAngleType = cachedPositionAngleType;
  188.         this.a     = a;
  189.         this.aDot  = aDot;
  190.         this.ex    = ex;
  191.         this.exDot = exDot;
  192.         this.ey    = ey;
  193.         this.eyDot = eyDot;
  194.         this.hx    = hx;
  195.         this.hxDot = hxDot;
  196.         this.hy    = hy;
  197.         this.hyDot = hyDot;

  198.         if (hasDerivatives()) {
  199.             final FieldUnivariateDerivative1<T> lUD = initializeCachedL(l, lDot, type);
  200.             this.cachedL = lUD.getValue();
  201.             this.cachedLDot = lUD.getFirstDerivative();
  202.         } else {
  203.             this.cachedL = initializeCachedL(l, type);
  204.             this.cachedLDot = null;
  205.         }

  206.         this.partialPV = null;

  207.     }

  208.     /** Creates a new instance.
  209.      * @param a  semi-major axis (m)
  210.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  211.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  212.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  213.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  214.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  215.      * @param aDot  semi-major axis derivative (m/s)
  216.      * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative
  217.      * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative
  218.      * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative
  219.      * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative
  220.      * @param lDot  d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument  derivative (rad/s)
  221.      * @param type type of longitude argument
  222.      * @param frame the frame in which the parameters are defined
  223.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  224.      * @param date date of the orbital parameters
  225.      * @param mu central attraction coefficient (m³/s²)
  226.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  227.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  228.      * @since 12.1
  229.      */
  230.     public FieldEquinoctialOrbit(final T a, final T ex, final T ey,
  231.                                  final T hx, final T hy, final T l,
  232.                                  final T aDot, final T exDot, final T eyDot,
  233.                                  final T hxDot, final T hyDot, final T lDot,
  234.                                  final PositionAngleType type,
  235.                                  final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  236.             throws IllegalArgumentException {
  237.         this(a, ex, ey, hx, hy, l, aDot, exDot, eyDot, hxDot, hyDot, lDot, type, type, frame, date, mu);
  238.     }

  239.     /** Constructor from Cartesian parameters.
  240.      *
  241.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  242.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  243.      * use {@code mu} and the position to compute the acceleration, including
  244.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  245.      *
  246.      * @param pvCoordinates the position, velocity and acceleration
  247.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  248.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  249.      * @param mu central attraction coefficient (m³/s²)
  250.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  251.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  252.      */
  253.     public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
  254.                                  final Frame frame, final T mu)
  255.         throws IllegalArgumentException {
  256.         super(pvCoordinates, frame, mu);

  257.         //  compute semi-major axis
  258.         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
  259.         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
  260.         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
  261.         final T r2 = pvP.getNormSq();
  262.         final T r  = r2.sqrt();
  263.         final T V2 = pvV.getNormSq();
  264.         final T rV2OnMu = r.multiply(V2).divide(mu);

  265.         // compute semi-major axis
  266.         a = r.divide(rV2OnMu.negate().add(2));

  267.         if (!isElliptical()) {
  268.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  269.                                                      getClass().getName());
  270.         }

  271.         // compute inclination vector
  272.         final FieldVector3D<T> w = pvCoordinates.getMomentum().normalize();
  273.         final T d = getOne().divide(getOne().add(w.getZ()));
  274.         hx =  d.negate().multiply(w.getY());
  275.         hy =  d.multiply(w.getX());

  276.         // compute true longitude argument
  277.         cachedPositionAngleType = PositionAngleType.TRUE;
  278.         final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r);
  279.         final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r);
  280.         cachedL = sLv.atan2(cLv);

  281.         // compute eccentricity vector
  282.         final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
  283.         final T eCE = rV2OnMu.subtract(1);
  284.         final T e2  = eCE.square().add(eSE.square());
  285.         final T f   = eCE.subtract(e2);
  286.         final T g   = e2.negate().add(1).sqrt().multiply(eSE);
  287.         ex = a.multiply(f.multiply(cLv).add( g.multiply(sLv))).divide(r);
  288.         ey = a.multiply(f.multiply(sLv).subtract(g.multiply(cLv))).divide(r);

  289.         partialPV = pvCoordinates;

  290.         if (hasNonKeplerianAcceleration(pvCoordinates, mu)) {
  291.             // we have a relevant acceleration, we can compute derivatives

  292.             final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6);
  293.             getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian);

  294.             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP);
  295.             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  296.             final T   aX                       = nonKeplerianAcceleration.getX();
  297.             final T   aY                       = nonKeplerianAcceleration.getY();
  298.             final T   aZ                       = nonKeplerianAcceleration.getZ();
  299.             aDot  = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
  300.             exDot = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
  301.             eyDot = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
  302.             hxDot = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
  303.             hyDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));

  304.             // in order to compute true longitude argument derivative, we must compute
  305.             // mean longitude argument derivative including Keplerian motion and convert to true anomaly
  306.             final T lMDot = getKeplerianMeanMotion().
  307.                             add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  308.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  309.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  310.             final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(getLM(), lMDot);
  311.             final FieldUnivariateDerivative1<T> lvUD = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lMUD);
  312.             cachedLDot = lvUD.getFirstDerivative();

  313.         } else {
  314.             // acceleration is either almost zero or NaN,
  315.             // we assume acceleration was not known
  316.             // we don't set up derivatives
  317.             aDot  = null;
  318.             exDot = null;
  319.             eyDot = null;
  320.             hxDot = null;
  321.             hyDot = null;
  322.             cachedLDot = null;
  323.         }

  324.     }

  325.     /** Constructor from Cartesian parameters.
  326.      *
  327.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  328.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  329.      * use {@code mu} and the position to compute the acceleration, including
  330.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  331.      *
  332.      * @param pvCoordinates the position end velocity
  333.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  334.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  335.      * @param date date of the orbital parameters
  336.      * @param mu central attraction coefficient (m³/s²)
  337.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  338.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  339.      */
  340.     public FieldEquinoctialOrbit(final FieldPVCoordinates<T> pvCoordinates, final Frame frame,
  341.                             final FieldAbsoluteDate<T> date, final T mu)
  342.         throws IllegalArgumentException {
  343.         this(new TimeStampedFieldPVCoordinates<>(date, pvCoordinates), frame, mu);
  344.     }

  345.     /** Constructor from any kind of orbital parameters.
  346.      * @param op orbital parameters to copy
  347.      */
  348.     public FieldEquinoctialOrbit(final FieldOrbit<T> op) {
  349.         super(op.getFrame(), op.getDate(), op.getMu());

  350.         a     = op.getA();
  351.         ex    = op.getEquinoctialEx();
  352.         ey    = op.getEquinoctialEy();
  353.         hx    = op.getHx();
  354.         hy    = op.getHy();
  355.         cachedPositionAngleType = PositionAngleType.TRUE;
  356.         cachedL    = op.getLv();

  357.         aDot  = op.getADot();
  358.         exDot = op.getEquinoctialExDot();
  359.         eyDot = op.getEquinoctialEyDot();
  360.         hxDot = op.getHxDot();
  361.         hyDot = op.getHyDot();
  362.         cachedLDot = op.getLvDot();
  363.     }

  364.     /** Constructor from Field and EquinoctialOrbit.
  365.      * <p>Build a FieldEquinoctialOrbit from non-Field EquinoctialOrbit.</p>
  366.      * @param field CalculusField to base object on
  367.      * @param op non-field orbit with only "constant" terms
  368.      * @since 12.0
  369.      */
  370.     public FieldEquinoctialOrbit(final Field<T> field, final EquinoctialOrbit op) {
  371.         super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().newInstance(op.getMu()));

  372.         a     = getZero().newInstance(op.getA());
  373.         ex    = getZero().newInstance(op.getEquinoctialEx());
  374.         ey    = getZero().newInstance(op.getEquinoctialEy());
  375.         hx    = getZero().newInstance(op.getHx());
  376.         hy    = getZero().newInstance(op.getHy());
  377.         cachedPositionAngleType = op.getCachedPositionAngleType();
  378.         cachedL    = getZero().newInstance(op.getL(cachedPositionAngleType));

  379.         if (op.hasDerivatives()) {
  380.             aDot  = getZero().newInstance(op.getADot());
  381.             exDot = getZero().newInstance(op.getEquinoctialExDot());
  382.             eyDot = getZero().newInstance(op.getEquinoctialEyDot());
  383.             hxDot = getZero().newInstance(op.getHxDot());
  384.             hyDot = getZero().newInstance(op.getHyDot());
  385.             cachedLDot = getZero().newInstance(op.getLDot(cachedPositionAngleType));
  386.         } else {
  387.             aDot  = null;
  388.             exDot = null;
  389.             eyDot = null;
  390.             hxDot = null;
  391.             hyDot = null;
  392.             cachedLDot = null;
  393.         }

  394.     }

  395.     /** Constructor from Field and Orbit.
  396.      * <p>Build a FieldEquinoctialOrbit from any non-Field Orbit.</p>
  397.      * @param field CalculusField to base object on
  398.      * @param op non-field orbit with only "constant" terms
  399.      * @since 12.0
  400.      */
  401.     public FieldEquinoctialOrbit(final Field<T> field, final Orbit op) {
  402.         this(field, (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(op));
  403.     }

  404.     /** {@inheritDoc} */
  405.     @Override
  406.     public OrbitType getType() {
  407.         return OrbitType.EQUINOCTIAL;
  408.     }

  409.     /** {@inheritDoc} */
  410.     @Override
  411.     public T getA() {
  412.         return a;
  413.     }

  414.     /** {@inheritDoc} */
  415.     @Override
  416.     public T getADot() {
  417.         return aDot;
  418.     }

  419.     /** {@inheritDoc} */
  420.     @Override
  421.     public T getEquinoctialEx() {
  422.         return ex;
  423.     }

  424.     /** {@inheritDoc} */
  425.     @Override
  426.     public T getEquinoctialExDot() {
  427.         return exDot;
  428.     }

  429.     /** {@inheritDoc} */
  430.     @Override
  431.     public T getEquinoctialEy() {
  432.         return ey;
  433.     }

  434.     /** {@inheritDoc} */
  435.     @Override
  436.     public T getEquinoctialEyDot() {
  437.         return eyDot;
  438.     }

  439.     /** {@inheritDoc} */
  440.     @Override
  441.     public T getHx() {
  442.         return hx;
  443.     }

  444.     /** {@inheritDoc} */
  445.     @Override
  446.     public T getHxDot() {
  447.         return hxDot;
  448.     }

  449.     /** {@inheritDoc} */
  450.     @Override
  451.     public T getHy() {
  452.         return hy;
  453.     }

  454.     /** {@inheritDoc} */
  455.     @Override
  456.     public T getHyDot() {
  457.         return hyDot;
  458.     }

  459.     /** {@inheritDoc} */
  460.     @Override
  461.     public T getLv() {
  462.         switch (cachedPositionAngleType) {
  463.             case TRUE:
  464.                 return cachedL;

  465.             case ECCENTRIC:
  466.                 return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, cachedL);

  467.             case MEAN:
  468.                 return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, cachedL);

  469.             default:
  470.                 throw new OrekitInternalError(null);
  471.         }
  472.     }

  473.     /** {@inheritDoc} */
  474.     @Override
  475.     public T getLvDot() {

  476.         if (!hasDerivatives()) {
  477.             return null;
  478.         }
  479.         switch (cachedPositionAngleType) {
  480.             case ECCENTRIC:
  481.                 final FieldUnivariateDerivative1<T> lEUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  482.                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
  483.                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
  484.                 final FieldUnivariateDerivative1<T> lvUD = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD,
  485.                         lEUD);
  486.                 return lvUD.getFirstDerivative();

  487.             case TRUE:
  488.                 return cachedLDot;

  489.             case MEAN:
  490.                 final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  491.                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
  492.                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
  493.                 final FieldUnivariateDerivative1<T> lvUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD2,
  494.                         eyUD2, lMUD);
  495.                 return lvUD2.getFirstDerivative();

  496.             default:
  497.                 throw new OrekitInternalError(null);
  498.         }
  499.     }

  500.     /** {@inheritDoc} */
  501.     @Override
  502.     public T getLE() {
  503.         switch (cachedPositionAngleType) {
  504.             case TRUE:
  505.                 return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, cachedL);

  506.             case ECCENTRIC:
  507.                 return cachedL;

  508.             case MEAN:
  509.                 return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, cachedL);

  510.             default:
  511.                 throw new OrekitInternalError(null);
  512.         }
  513.     }

  514.     /** {@inheritDoc} */
  515.     @Override
  516.     public T getLEDot() {

  517.         if (!hasDerivatives()) {
  518.             return null;
  519.         }
  520.         switch (cachedPositionAngleType) {
  521.             case TRUE:
  522.                 final FieldUnivariateDerivative1<T> lvUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  523.                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
  524.                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
  525.                 final FieldUnivariateDerivative1<T> lEUD = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD,
  526.                         lvUD);
  527.                 return lEUD.getFirstDerivative();

  528.             case ECCENTRIC:
  529.                 return cachedLDot;

  530.             case MEAN:
  531.                 final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  532.                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
  533.                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
  534.                 final FieldUnivariateDerivative1<T> lEUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD2,
  535.                         eyUD2, lMUD);
  536.                 return lEUD2.getFirstDerivative();

  537.             default:
  538.                 throw new OrekitInternalError(null);
  539.         }
  540.     }

  541.     /** {@inheritDoc} */
  542.     @Override
  543.     public T getLM() {
  544.         switch (cachedPositionAngleType) {
  545.             case TRUE:
  546.                 return FieldEquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, cachedL);

  547.             case MEAN:
  548.                 return cachedL;

  549.             case ECCENTRIC:
  550.                 return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, cachedL);

  551.             default:
  552.                 throw new OrekitInternalError(null);
  553.         }
  554.     }

  555.     /** {@inheritDoc} */
  556.     @Override
  557.     public T getLMDot() {

  558.         if (!hasDerivatives()) {
  559.             return null;
  560.         }
  561.         switch (cachedPositionAngleType) {
  562.             case TRUE:
  563.                 final FieldUnivariateDerivative1<T> lvUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  564.                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
  565.                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
  566.                 final FieldUnivariateDerivative1<T> lMUD = FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lvUD);
  567.                 return lMUD.getFirstDerivative();

  568.             case MEAN:
  569.                 return cachedLDot;

  570.             case ECCENTRIC:
  571.                 final FieldUnivariateDerivative1<T> lEUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  572.                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
  573.                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
  574.                 final FieldUnivariateDerivative1<T> lMUD2 = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD2,
  575.                         eyUD2, lEUD);
  576.                 return lMUD2.getFirstDerivative();

  577.             default:
  578.                 throw new OrekitInternalError(null);
  579.         }
  580.     }

  581.     /** Get the longitude argument.
  582.      * @param type type of the angle
  583.      * @return longitude argument (rad)
  584.      */
  585.     public T getL(final PositionAngleType type) {
  586.         return (type == PositionAngleType.MEAN) ? getLM() :
  587.                                               ((type == PositionAngleType.ECCENTRIC) ? getLE() :
  588.                                                                                    getLv());
  589.     }

  590.     /** Get the longitude argument derivative.
  591.      * @param type type of the angle
  592.      * @return longitude argument derivative (rad/s)
  593.      */
  594.     public T getLDot(final PositionAngleType type) {
  595.         return (type == PositionAngleType.MEAN) ? getLMDot() :
  596.                                               ((type == PositionAngleType.ECCENTRIC) ? getLEDot() :
  597.                                                                                    getLvDot());
  598.     }

  599.     /** {@inheritDoc} */
  600.     @Override
  601.     public boolean hasDerivatives() {
  602.         return aDot != null;
  603.     }

  604.     /** Computes the true longitude argument from the eccentric longitude argument.
  605.      * @param lE = E + ω + Ω eccentric longitude argument (rad)
  606.      * @param ex first component of the eccentricity vector
  607.      * @param ey second component of the eccentricity vector
  608.      * @param <T> Type of the field elements
  609.      * @return the true longitude argument
  610.      */
  611.     @Deprecated
  612.     public static <T extends CalculusFieldElement<T>> T eccentricToTrue(final T lE, final T ex, final T ey) {
  613.         return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, lE);
  614.     }

  615.     /** Computes the eccentric longitude argument from the true longitude argument.
  616.      * @param lv = v + ω + Ω true longitude argument (rad)
  617.      * @param ex first component of the eccentricity vector
  618.      * @param ey second component of the eccentricity vector
  619.      * @param <T> Type of the field elements
  620.      * @return the eccentric longitude argument
  621.      */
  622.     @Deprecated
  623.     public static <T extends CalculusFieldElement<T>> T trueToEccentric(final T lv, final T ex, final T ey) {
  624.         return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, lv);
  625.     }

  626.     /** Computes the eccentric longitude argument from the mean longitude argument.
  627.      * @param lM = M + ω + Ω mean longitude argument (rad)
  628.      * @param ex first component of the eccentricity vector
  629.      * @param ey second component of the eccentricity vector
  630.      * @param <T> Type of the field elements
  631.      * @return the eccentric longitude argument
  632.      */
  633.     @Deprecated
  634.     public static <T extends CalculusFieldElement<T>> T meanToEccentric(final T lM, final T ex, final T ey) {
  635.         return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, lM);
  636.     }

  637.     /** Computes the mean longitude argument from the eccentric longitude argument.
  638.      * @param lE = E + ω + Ω mean longitude argument (rad)
  639.      * @param ex first component of the eccentricity vector
  640.      * @param ey second component of the eccentricity vector
  641.      * @param <T> Type of the field elements
  642.      * @return the mean longitude argument
  643.      */
  644.     @Deprecated
  645.     public static <T extends CalculusFieldElement<T>> T eccentricToMean(final T lE, final T ex, final T ey) {
  646.         return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, lE);
  647.     }

  648.     /** {@inheritDoc} */
  649.     @Override
  650.     public T getE() {
  651.         return ex.square().add(ey.square()).sqrt();
  652.     }

  653.     /** {@inheritDoc} */
  654.     @Override
  655.     public T getEDot() {

  656.         if (!hasDerivatives()) {
  657.             return null;
  658.         }

  659.         return ex.multiply(exDot).add(ey.multiply(eyDot)).divide(ex.square().add(ey.square()).sqrt());

  660.     }

  661.     /** {@inheritDoc} */
  662.     @Override
  663.     public T getI() {
  664.         return hx.square().add(hy.square()).sqrt().atan().multiply(2);
  665.     }

  666.     /** {@inheritDoc} */
  667.     @Override
  668.     public T getIDot() {

  669.         if (!hasDerivatives()) {
  670.             return null;
  671.         }

  672.         final T h2 = hx.square().add(hy.square());
  673.         final T h  = h2.sqrt();
  674.         return hx.multiply(hxDot).add(hy.multiply(hyDot)).multiply(2).divide(h.multiply(h2.add(1)));

  675.     }

  676.     /** Compute position and velocity but not acceleration.
  677.      */
  678.     private void computePVWithoutA() {

  679.         if (partialPV != null) {
  680.             // already computed
  681.             return;
  682.         }

  683.         // get equinoctial parameters
  684.         final T lE = getLE();

  685.         // inclination-related intermediate parameters
  686.         final T hx2   = hx.square();
  687.         final T hy2   = hy.square();
  688.         final T factH = getOne().divide(hx2.add(1.0).add(hy2));

  689.         // reference axes defining the orbital plane
  690.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  691.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  692.         final T uz = hy.multiply(-2).multiply(factH);

  693.         final T vx = uy;
  694.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  695.         final T vz =  hx.multiply(factH).multiply(2);

  696.         // eccentricity-related intermediate parameters
  697.         final T ex2  = ex.square();
  698.         final T exey = ex.multiply(ey);
  699.         final T ey2  = ey.square();
  700.         final T e2   = ex2.add(ey2);
  701.         final T eta  = getOne().subtract(e2).sqrt().add(1);
  702.         final T beta = getOne().divide(eta);

  703.         // eccentric longitude argument
  704.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  705.         final T cLe    = scLe.cos();
  706.         final T sLe    = scLe.sin();
  707.         final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe));

  708.         // coordinates of position and velocity in the orbital plane
  709.         final T x      = a.multiply(getOne().subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex));
  710.         final T y      = a.multiply(getOne().subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey));

  711.         final T factor = getMu().divide(a).sqrt().divide(getOne().subtract(exCeyS));
  712.         final T xdot   = factor.multiply(sLe.negate().add(beta.multiply(ey).multiply(exCeyS)));
  713.         final T ydot   = factor.multiply(cLe.subtract(beta.multiply(ex).multiply(exCeyS)));

  714.         final FieldVector3D<T> position =
  715.                         new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  716.                                             x.multiply(uy).add(y.multiply(vy)),
  717.                                             x.multiply(uz).add(y.multiply(vz)));
  718.         final FieldVector3D<T> velocity =
  719.                         new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)), xdot.multiply(uy).add(ydot.multiply(vy)), xdot.multiply(uz).add(ydot.multiply(vz)));

  720.         partialPV = new FieldPVCoordinates<>(position, velocity);

  721.     }

  722.     /** Initialize cached argument of longitude with rate.
  723.      * @param l input argument of longitude
  724.      * @param lDot rate of input argument of longitude
  725.      * @param inputType position angle type passed as input
  726.      * @return argument of longitude to cache with rate
  727.      * @since 12.1
  728.      */
  729.     private FieldUnivariateDerivative1<T> initializeCachedL(final T l, final T lDot,
  730.                                                             final PositionAngleType inputType) {
  731.         if (cachedPositionAngleType == inputType) {
  732.             return new FieldUnivariateDerivative1<>(l, lDot);

  733.         } else {
  734.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  735.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  736.             final FieldUnivariateDerivative1<T> lUD = new FieldUnivariateDerivative1<>(l, lDot);

  737.             switch (cachedPositionAngleType) {

  738.                 case ECCENTRIC:
  739.                     if (inputType == PositionAngleType.MEAN) {
  740.                         return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD, eyUD, lUD);
  741.                     } else {
  742.                         return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, lUD);
  743.                     }

  744.                 case TRUE:
  745.                     if (inputType == PositionAngleType.MEAN) {
  746.                         return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lUD);
  747.                     } else {
  748.                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, lUD);
  749.                     }

  750.                 case MEAN:
  751.                     if (inputType == PositionAngleType.TRUE) {
  752.                         return FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lUD);
  753.                     } else {
  754.                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD, eyUD, lUD);
  755.                     }

  756.                 default:
  757.                     throw new OrekitInternalError(null);

  758.             }

  759.         }

  760.     }

  761.     /** Initialize cached argument of longitude.
  762.      * @param l input argument of longitude
  763.      * @param positionAngleType position angle type passed as input
  764.      * @return argument of longitude to cache
  765.      * @since 12.1
  766.      */
  767.     private T initializeCachedL(final T l, final PositionAngleType positionAngleType) {
  768.         return FieldEquinoctialLongitudeArgumentUtility.convertL(positionAngleType, l, ex, ey, cachedPositionAngleType);
  769.     }

  770.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  771.      * <p>
  772.      * This method should be called only when {@link #hasDerivatives()} returns true.
  773.      * </p>
  774.      * @return non-Keplerian part of the acceleration
  775.      */
  776.     private FieldVector3D<T> nonKeplerianAcceleration() {

  777.         final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6);
  778.         getJacobianWrtParameters(PositionAngleType.MEAN, dCdP);

  779.         final T nonKeplerianMeanMotion = getLMDot().subtract(getKeplerianMeanMotion());
  780.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  781.                                  add(dCdP[3][1].multiply(exDot)).
  782.                                  add(dCdP[3][2].multiply(eyDot)).
  783.                                  add(dCdP[3][3].multiply(hxDot)).
  784.                                  add(dCdP[3][4].multiply(hyDot)).
  785.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  786.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  787.                                  add(dCdP[4][1].multiply(exDot)).
  788.                                  add(dCdP[4][2].multiply(eyDot)).
  789.                                  add(dCdP[4][3].multiply(hxDot)).
  790.                                  add(dCdP[4][4].multiply(hyDot)).
  791.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  792.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  793.                                  add(dCdP[5][1].multiply(exDot)).
  794.                                  add(dCdP[5][2].multiply(eyDot)).
  795.                                  add(dCdP[5][3].multiply(hxDot)).
  796.                                  add(dCdP[5][4].multiply(hyDot)).
  797.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

  798.         return new FieldVector3D<>(nonKeplerianAx, nonKeplerianAy, nonKeplerianAz);

  799.     }

  800.     /** {@inheritDoc} */
  801.     @Override
  802.     protected FieldVector3D<T> initPosition() {

  803.         // get equinoctial parameters
  804.         final T lE = getLE();

  805.         // inclination-related intermediate parameters
  806.         final T hx2   = hx.square();
  807.         final T hy2   = hy.square();
  808.         final T factH = getOne().divide(hx2.add(1.0).add(hy2));

  809.         // reference axes defining the orbital plane
  810.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  811.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  812.         final T uz = hy.multiply(-2).multiply(factH);

  813.         final T vx = uy;
  814.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  815.         final T vz =  hx.multiply(factH).multiply(2);

  816.         // eccentricity-related intermediate parameters
  817.         final T ex2  = ex.square();
  818.         final T exey = ex.multiply(ey);
  819.         final T ey2  = ey.square();
  820.         final T e2   = ex2.add(ey2);
  821.         final T eta  = getOne().subtract(e2).sqrt().add(1);
  822.         final T beta = getOne().divide(eta);

  823.         // eccentric longitude argument
  824.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  825.         final T cLe    = scLe.cos();
  826.         final T sLe    = scLe.sin();

  827.         // coordinates of position and velocity in the orbital plane
  828.         final T x      = a.multiply(getOne().subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex));
  829.         final T y      = a.multiply(getOne().subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey));

  830.         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  831.                                    x.multiply(uy).add(y.multiply(vy)),
  832.                                    x.multiply(uz).add(y.multiply(vz)));

  833.     }

  834.     /** {@inheritDoc} */
  835.     @Override
  836.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  837.         // position and velocity
  838.         computePVWithoutA();

  839.         // acceleration
  840.         final T r2 = partialPV.getPosition().getNormSq();
  841.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(getMu().negate()),
  842.                                                                            partialPV.getPosition());
  843.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  844.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  845.                                               keplerianAcceleration;

  846.         return new TimeStampedFieldPVCoordinates<>(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);

  847.     }

  848.     /** {@inheritDoc} */
  849.     @Override
  850.     public FieldEquinoctialOrbit<T> shiftedBy(final double dt) {
  851.         return shiftedBy(getZero().newInstance(dt));
  852.     }

  853.     /** {@inheritDoc} */
  854.     @Override
  855.     public FieldEquinoctialOrbit<T> shiftedBy(final T dt) {

  856.         // use Keplerian-only motion
  857.         final FieldEquinoctialOrbit<T> keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy,
  858.                                                                                       getLM().add(getKeplerianMeanMotion().multiply(dt)),
  859.                                                                                       PositionAngleType.MEAN, cachedPositionAngleType, getFrame(),
  860.                                                                                       getDate().shiftedBy(dt), getMu());

  861.         if (hasDerivatives()) {

  862.             // extract non-Keplerian acceleration from first time derivatives
  863.             final FieldVector3D<T> nonKeplerianAcceleration = nonKeplerianAcceleration();

  864.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  865.             keplerianShifted.computePVWithoutA();
  866.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(),
  867.                                                                   dt.square().multiply(0.5), nonKeplerianAcceleration);
  868.             final T   fixedR2 = fixedP.getNormSq();
  869.             final T   fixedR  = fixedR2.sqrt();
  870.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(),
  871.                                                                  dt, nonKeplerianAcceleration);
  872.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
  873.                                                                  keplerianShifted.partialPV.getPosition(),
  874.                                                                  getOne(), nonKeplerianAcceleration);

  875.             // build a new orbit, taking non-Keplerian acceleration into account
  876.             return new FieldEquinoctialOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  877.                                                                                    fixedP, fixedV, fixedA),
  878.                                                keplerianShifted.getFrame(), keplerianShifted.getMu());

  879.         } else {
  880.             // Keplerian-only motion is all we can do
  881.             return keplerianShifted;
  882.         }

  883.     }

  884.     /** {@inheritDoc} */
  885.     @Override
  886.     protected T[][] computeJacobianMeanWrtCartesian() {

  887.         final T[][] jacobian = MathArrays.buildArray(getField(), 6, 6);

  888.         // compute various intermediate parameters
  889.         computePVWithoutA();
  890.         final FieldVector3D<T> position = partialPV.getPosition();
  891.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  892.         final T r2         = position.getNormSq();
  893.         final T r          = r2.sqrt();
  894.         final T r3         = r.multiply(r2);

  895.         final T mu         = getMu();
  896.         final T sqrtMuA    = a.multiply(mu).sqrt();
  897.         final T a2         = a.square();

  898.         final T e2         = ex.square().add(ey.square());
  899.         final T oMe2       = getOne().subtract(e2);
  900.         final T epsilon    = oMe2.sqrt();
  901.         final T beta       = getOne().divide(epsilon.add(1));
  902.         final T ratio      = epsilon.multiply(beta);

  903.         final T hx2        = hx.square();
  904.         final T hy2        = hy.square();
  905.         final T hxhy       = hx.multiply(hy);

  906.         // precomputing equinoctial frame unit vectors (f, g, w)
  907.         final FieldVector3D<T> f  = new FieldVector3D<>(hx2.subtract(hy2).add(1), hxhy.multiply(2), hy.multiply(-2)).normalize();
  908.         final FieldVector3D<T> g  = new FieldVector3D<>(hxhy.multiply(2), hy2.add(1).subtract(hx2), hx.multiply(2)).normalize();
  909.         final FieldVector3D<T> w  = FieldVector3D.crossProduct(position, velocity).normalize();

  910.         // coordinates of the spacecraft in the equinoctial frame
  911.         final T x    = FieldVector3D.dotProduct(position, f);
  912.         final T y    = FieldVector3D.dotProduct(position, g);
  913.         final T xDot = FieldVector3D.dotProduct(velocity, f);
  914.         final T yDot = FieldVector3D.dotProduct(velocity, g);

  915.         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
  916.         final T c1  = a.divide(sqrtMuA.multiply(epsilon));
  917.         final T c1N = c1.negate();
  918.         final T c2  = a.multiply(sqrtMuA).multiply(beta).divide(r3);
  919.         final T c3  = sqrtMuA.divide(r3.multiply(epsilon));
  920.         final FieldVector3D<T> drDotSdEx = new FieldVector3D<>(c1.multiply(xDot).multiply(yDot).subtract(c2.multiply(ey).multiply(x)).subtract(c3.multiply(x).multiply(y)), f,
  921.                                                                c1N.multiply(xDot).multiply(xDot).subtract(c2.multiply(ey).multiply(y)).add(c3.multiply(x).multiply(x)), g);

  922.         // drDot / dEy = dXDot / dEy * f + dYDot / dEy * g
  923.         final FieldVector3D<T> drDotSdEy = new FieldVector3D<>(c1.multiply(yDot).multiply(yDot).add(c2.multiply(ex).multiply(x)).subtract(c3.multiply(y).multiply(y)), f,
  924.                                                                c1N.multiply(xDot).multiply(yDot).add(c2.multiply(ex).multiply(y)).add(c3.multiply(x).multiply(y)), g);

  925.         // da
  926.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
  927.         final FieldVector3D<T> vectorARDot = new FieldVector3D<>(a2.multiply(2).divide(mu), velocity);
  928.         fillHalfRow(getOne(), vectorAR,    jacobian[0], 0);
  929.         fillHalfRow(getOne(), vectorARDot, jacobian[0], 3);

  930.         // dEx
  931.         final T d1 = a.negate().multiply(ratio).divide(r3);
  932.         final T d2 = (hy.multiply(xDot).subtract(hx.multiply(yDot))).divide(sqrtMuA.multiply(epsilon));
  933.         final T d3 = hx.multiply(y).subtract(hy.multiply(x)).divide(sqrtMuA);
  934.         final FieldVector3D<T> vectorExRDot =
  935.             new FieldVector3D<>(x.multiply(2).multiply(yDot).subtract(xDot.multiply(y)).divide(mu), g, y.negate().multiply(yDot).divide(mu), f, ey.negate().multiply(d3).divide(epsilon), w);
  936.         fillHalfRow(ex.multiply(d1), position, ey.negate().multiply(d2), w, epsilon.divide(sqrtMuA), drDotSdEy, jacobian[1], 0);
  937.         fillHalfRow(getOne(), vectorExRDot, jacobian[1], 3);

  938.         // dEy
  939.         final FieldVector3D<T> vectorEyRDot =
  940.             new FieldVector3D<>(xDot.multiply(2).multiply(y).subtract(x.multiply(yDot)).divide(mu), f, x.negate().multiply(xDot).divide(mu), g, ex.multiply(d3).divide(epsilon), w);
  941.         fillHalfRow(ey.multiply(d1), position, ex.multiply(d2), w, epsilon.negate().divide(sqrtMuA), drDotSdEx, jacobian[2], 0);
  942.         fillHalfRow(getOne(), vectorEyRDot, jacobian[2], 3);

  943.         // dHx
  944.         final T h = (hx2.add(1).add(hy2)).divide(sqrtMuA.multiply(2).multiply(epsilon));
  945.         fillHalfRow( h.negate().multiply(xDot), w, jacobian[3], 0);
  946.         fillHalfRow( h.multiply(x),    w, jacobian[3], 3);

  947.        // dHy
  948.         fillHalfRow( h.negate().multiply(yDot), w, jacobian[4], 0);
  949.         fillHalfRow( h.multiply(y),    w, jacobian[4], 3);

  950.         // dLambdaM
  951.         final T l = ratio.negate().divide(sqrtMuA);
  952.         fillHalfRow(getOne().negate().divide(sqrtMuA), velocity, d2, w, l.multiply(ex), drDotSdEx, l.multiply(ey), drDotSdEy, jacobian[5], 0);
  953.         fillHalfRow(getZero().newInstance(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3);

  954.         return jacobian;

  955.     }

  956.     /** {@inheritDoc} */
  957.     @Override
  958.     protected T[][] computeJacobianEccentricWrtCartesian() {

  959.         // start by computing the Jacobian with mean angle
  960.         final T[][] jacobian = computeJacobianMeanWrtCartesian();

  961.         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
  962.         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
  963.         // which is inverted and rewritten as:
  964.         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
  965.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  966.         final T cosLe = scLe.cos();
  967.         final T sinLe = scLe.sin();
  968.         final T aOr   = getOne().divide(getOne().subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe)));

  969.         // update longitude row
  970.         final T[] rowEx = jacobian[1];
  971.         final T[] rowEy = jacobian[2];
  972.         final T[] rowL  = jacobian[5];

  973.         for (int j = 0; j < 6; ++j) {
  974.             rowL[j] = aOr.multiply(rowL[j].add(sinLe.multiply(rowEx[j])).subtract(cosLe.multiply(rowEy[j])));
  975.         }
  976.         return jacobian;

  977.     }

  978.     /** {@inheritDoc} */
  979.     @Override
  980.     protected T[][] computeJacobianTrueWrtCartesian() {

  981.         // start by computing the Jacobian with eccentric angle
  982.         final T[][] jacobian = computeJacobianEccentricWrtCartesian();

  983.         // Differentiating the eccentric longitude equation
  984.         // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
  985.         // leads to
  986.         // cT (dlV - dlE) = cE dlE + cX dex + cY dey
  987.         // with
  988.         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
  989.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
  990.         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  991.         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  992.         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  993.         // which can be solved to find the differential of the true longitude
  994.         // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
  995.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  996.         final T cosLe     = scLe.cos();
  997.         final T sinLe     = scLe.sin();
  998.         final T eSinE     = ex.multiply(sinLe).subtract(ey.multiply(cosLe));
  999.         final T ecosE     = ex.multiply(cosLe).add(ey.multiply(sinLe));
  1000.         final T e2        = ex.square().add(ey.square());
  1001.         final T epsilon   = getOne().subtract(e2).sqrt();
  1002.         final T onePeps   = epsilon.add(1);
  1003.         final T d         = onePeps.subtract(ecosE);
  1004.         final T cT        = d.multiply(d).add(eSinE.multiply(eSinE)).divide(2);
  1005.         final T cE        = ecosE.multiply(onePeps).subtract(e2);
  1006.         final T cX        = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinLe.multiply(onePeps));
  1007.         final T cY        = ey.multiply(eSinE).divide(epsilon).add( ex).subtract(cosLe.multiply(onePeps));
  1008.         final T factorLe  = cT.add(cE).divide(cT);
  1009.         final T factorEx  = cX.divide(cT);
  1010.         final T factorEy  = cY.divide(cT);

  1011.         // update longitude row
  1012.         final T[] rowEx = jacobian[1];
  1013.         final T[] rowEy = jacobian[2];
  1014.         final T[] rowL = jacobian[5];
  1015.         for (int j = 0; j < 6; ++j) {
  1016.             rowL[j] = factorLe.multiply(rowL[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  1017.         }

  1018.         return jacobian;

  1019.     }

  1020.     /** {@inheritDoc} */
  1021.     @Override
  1022.     public void addKeplerContribution(final PositionAngleType type, final T gm,
  1023.                                       final T[] pDot) {
  1024.         pDot[5] = pDot[5].add(computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType));
  1025.     }

  1026.     /**
  1027.      * Compute rate of argument of longitude.
  1028.      * @param type position angle type of rate
  1029.      * @param a semi major axis
  1030.      * @param ex ex
  1031.      * @param ey ey
  1032.      * @param mu mu
  1033.      * @param l argument of longitude
  1034.      * @param cachedType position angle type of passed l
  1035.      * @param <T> field type
  1036.      * @return first-order time derivative for l
  1037.      * @since 12.2
  1038.      */
  1039.     private static <T extends CalculusFieldElement<T>> T computeKeplerianLDot(final PositionAngleType type, final T a, final T ex,
  1040.                                                                               final T ey, final T mu, final T l, final PositionAngleType cachedType) {
  1041.         final T n               = mu.divide(a).sqrt().divide(a);
  1042.         if (type == PositionAngleType.MEAN) {
  1043.             return n;
  1044.         }
  1045.         final FieldSinCos<T> sc;
  1046.         final T ksi;
  1047.         if (type == PositionAngleType.ECCENTRIC) {
  1048.             sc = FastMath.sinCos(FieldEquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
  1049.             ksi = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal();
  1050.             return n.multiply(ksi);
  1051.         } else {
  1052.             sc = FastMath.sinCos(FieldEquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
  1053.             final T oMe2 = a.getField().getOne().subtract(ex.square()).subtract(ey.square());
  1054.             ksi  =  ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin()));
  1055.             return n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()));
  1056.         }
  1057.     }

  1058.     /**  Returns a string representation of this equinoctial parameters object.
  1059.      * @return a string representation of this object
  1060.      */
  1061.     public String toString() {
  1062.         return new StringBuilder().append("equinoctial parameters: ").append('{').
  1063.                                   append("a: ").append(a.getReal()).
  1064.                                   append("; ex: ").append(ex.getReal()).append("; ey: ").append(ey.getReal()).
  1065.                                   append("; hx: ").append(hx.getReal()).append("; hy: ").append(hy.getReal()).
  1066.                                   append("; lv: ").append(FastMath.toDegrees(getLv().getReal())).
  1067.                                   append(";}").toString();
  1068.     }

  1069.     /** {@inheritDoc} */
  1070.     @Override
  1071.     public PositionAngleType getCachedPositionAngleType() {
  1072.         return cachedPositionAngleType;
  1073.     }

  1074.     /** {@inheritDoc} */
  1075.     @Override
  1076.     public boolean hasRates() {
  1077.         return hasDerivatives();
  1078.     }

  1079.     /** {@inheritDoc} */
  1080.     @Override
  1081.     public FieldEquinoctialOrbit<T> removeRates() {
  1082.         return new FieldEquinoctialOrbit<>(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(),
  1083.                 cachedL, cachedPositionAngleType, getFrame(), getDate(), getMu());
  1084.     }

  1085.     /** {@inheritDoc} */
  1086.     @Override
  1087.     public EquinoctialOrbit toOrbit() {
  1088.         final double cachedPositionAngle = cachedL.getReal();
  1089.         if (hasDerivatives()) {
  1090.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1091.                                         hx.getReal(), hy.getReal(), cachedPositionAngle,
  1092.                                         aDot.getReal(), exDot.getReal(), eyDot.getReal(),
  1093.                                         hxDot.getReal(), hyDot.getReal(), cachedLDot.getReal(),
  1094.                                         cachedPositionAngleType, getFrame(),
  1095.                                         getDate().toAbsoluteDate(), getMu().getReal());
  1096.         } else {
  1097.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1098.                                         hx.getReal(), hy.getReal(), cachedPositionAngle,
  1099.                                         cachedPositionAngleType, getFrame(),
  1100.                                         getDate().toAbsoluteDate(), getMu().getReal());
  1101.         }
  1102.     }

  1103. }