FieldEquinoctialOrbit.java

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

  18. 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.FieldKinematicTransform;
  29. import org.orekit.frames.Frame;
  30. import org.orekit.time.FieldAbsoluteDate;
  31. import org.orekit.utils.FieldPVCoordinates;
  32. import org.orekit.utils.TimeStampedFieldPVCoordinates;


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  199.         final FieldUnivariateDerivative1<T> lUD = initializeCachedL(l, lDot, type);
  200.         this.cachedL = lUD.getValue();
  201.         this.cachedLDot = lUD.getFirstDerivative();

  202.         this.partialPV = null;

  203.     }

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

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

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

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

  263.         if (!isElliptical()) {
  264.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  265.                                                      getClass().getName());
  266.         }

  267.         // compute inclination vector
  268.         final FieldVector3D<T> w = pvCoordinates.getMomentum().normalize();
  269.         final T d = getOne().divide(getOne().add(w.getZ()));
  270.         hx =  d.negate().multiply(w.getY());
  271.         hy =  d.multiply(w.getX());

  272.         // compute true longitude argument
  273.         cachedPositionAngleType = PositionAngleType.TRUE;
  274.         final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r);
  275.         final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r);
  276.         cachedL = sLv.atan2(cLv);

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

  285.         partialPV = pvCoordinates;

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

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

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

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

  309.         } else {
  310.             // acceleration is either almost zero or NaN,
  311.             // we assume acceleration was not known
  312.             // we don't set up derivatives
  313.             aDot  = getZero();
  314.             exDot = getZero();
  315.             eyDot = getZero();
  316.             hxDot = getZero();
  317.             hyDot = getZero();
  318.             cachedLDot = computeKeplerianLDot(cachedPositionAngleType, a, ex, ey, mu, cachedL, cachedPositionAngleType);
  319.         }

  320.     }

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

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

  346.         a     = op.getA();
  347.         ex    = op.getEquinoctialEx();
  348.         ey    = op.getEquinoctialEy();
  349.         hx    = op.getHx();
  350.         hy    = op.getHy();
  351.         cachedPositionAngleType = PositionAngleType.TRUE;
  352.         cachedL    = op.getLv();

  353.         aDot  = op.getADot();
  354.         exDot = op.getEquinoctialExDot();
  355.         eyDot = op.getEquinoctialEyDot();
  356.         hxDot = op.getHxDot();
  357.         hyDot = op.getHyDot();
  358.         cachedLDot = op.getLvDot();
  359.     }

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

  368.         a     = getZero().newInstance(op.getA());
  369.         ex    = getZero().newInstance(op.getEquinoctialEx());
  370.         ey    = getZero().newInstance(op.getEquinoctialEy());
  371.         hx    = getZero().newInstance(op.getHx());
  372.         hy    = getZero().newInstance(op.getHy());
  373.         cachedPositionAngleType = op.getCachedPositionAngleType();
  374.         cachedL    = getZero().newInstance(op.getL(cachedPositionAngleType));

  375.         aDot  = getZero().newInstance(op.getADot());
  376.         exDot = getZero().newInstance(op.getEquinoctialExDot());
  377.         eyDot = getZero().newInstance(op.getEquinoctialEyDot());
  378.         hxDot = getZero().newInstance(op.getHxDot());
  379.         hyDot = getZero().newInstance(op.getHyDot());
  380.         cachedLDot = getZero().newInstance(op.getLDot(cachedPositionAngleType));
  381.     }

  382.     /** Constructor from Field and Orbit.
  383.      * <p>Build a FieldEquinoctialOrbit from any non-Field Orbit.</p>
  384.      * @param field CalculusField to base object on
  385.      * @param op non-field orbit with only "constant" terms
  386.      * @since 12.0
  387.      */
  388.     public FieldEquinoctialOrbit(final Field<T> field, final Orbit op) {
  389.         this(field, (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(op));
  390.     }

  391.     /** {@inheritDoc} */
  392.     @Override
  393.     public OrbitType getType() {
  394.         return OrbitType.EQUINOCTIAL;
  395.     }

  396.     /** {@inheritDoc} */
  397.     @Override
  398.     public T getA() {
  399.         return a;
  400.     }

  401.     /** {@inheritDoc} */
  402.     @Override
  403.     public T getADot() {
  404.         return aDot;
  405.     }

  406.     /** {@inheritDoc} */
  407.     @Override
  408.     public T getEquinoctialEx() {
  409.         return ex;
  410.     }

  411.     /** {@inheritDoc} */
  412.     @Override
  413.     public T getEquinoctialExDot() {
  414.         return exDot;
  415.     }

  416.     /** {@inheritDoc} */
  417.     @Override
  418.     public T getEquinoctialEy() {
  419.         return ey;
  420.     }

  421.     /** {@inheritDoc} */
  422.     @Override
  423.     public T getEquinoctialEyDot() {
  424.         return eyDot;
  425.     }

  426.     /** {@inheritDoc} */
  427.     @Override
  428.     public T getHx() {
  429.         return hx;
  430.     }

  431.     /** {@inheritDoc} */
  432.     @Override
  433.     public T getHxDot() {
  434.         return hxDot;
  435.     }

  436.     /** {@inheritDoc} */
  437.     @Override
  438.     public T getHy() {
  439.         return hy;
  440.     }

  441.     /** {@inheritDoc} */
  442.     @Override
  443.     public T getHyDot() {
  444.         return hyDot;
  445.     }

  446.     /** {@inheritDoc} */
  447.     @Override
  448.     public T getLv() {
  449.         switch (cachedPositionAngleType) {
  450.             case TRUE:
  451.                 return cachedL;

  452.             case ECCENTRIC:
  453.                 return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, cachedL);

  454.             case MEAN:
  455.                 return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, cachedL);

  456.             default:
  457.                 throw new OrekitInternalError(null);
  458.         }
  459.     }

  460.     /** {@inheritDoc} */
  461.     @Override
  462.     public T getLvDot() {
  463.         switch (cachedPositionAngleType) {
  464.             case ECCENTRIC:
  465.                 final FieldUnivariateDerivative1<T> lEUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  466.                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
  467.                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
  468.                 final FieldUnivariateDerivative1<T> lvUD = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD,
  469.                         lEUD);
  470.                 return lvUD.getFirstDerivative();

  471.             case TRUE:
  472.                 return cachedLDot;

  473.             case MEAN:
  474.                 final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  475.                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
  476.                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
  477.                 final FieldUnivariateDerivative1<T> lvUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD2,
  478.                         eyUD2, lMUD);
  479.                 return lvUD2.getFirstDerivative();

  480.             default:
  481.                 throw new OrekitInternalError(null);
  482.         }
  483.     }

  484.     /** {@inheritDoc} */
  485.     @Override
  486.     public T getLE() {
  487.         switch (cachedPositionAngleType) {
  488.             case TRUE:
  489.                 return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, cachedL);

  490.             case ECCENTRIC:
  491.                 return cachedL;

  492.             case MEAN:
  493.                 return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, cachedL);

  494.             default:
  495.                 throw new OrekitInternalError(null);
  496.         }
  497.     }

  498.     /** {@inheritDoc} */
  499.     @Override
  500.     public T getLEDot() {

  501.         switch (cachedPositionAngleType) {
  502.             case TRUE:
  503.                 final FieldUnivariateDerivative1<T> lvUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  504.                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
  505.                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
  506.                 final FieldUnivariateDerivative1<T> lEUD = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD,
  507.                         lvUD);
  508.                 return lEUD.getFirstDerivative();

  509.             case ECCENTRIC:
  510.                 return cachedLDot;

  511.             case MEAN:
  512.                 final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  513.                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
  514.                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
  515.                 final FieldUnivariateDerivative1<T> lEUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD2,
  516.                         eyUD2, lMUD);
  517.                 return lEUD2.getFirstDerivative();

  518.             default:
  519.                 throw new OrekitInternalError(null);
  520.         }
  521.     }

  522.     /** {@inheritDoc} */
  523.     @Override
  524.     public T getLM() {
  525.         switch (cachedPositionAngleType) {
  526.             case TRUE:
  527.                 return FieldEquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, cachedL);

  528.             case MEAN:
  529.                 return cachedL;

  530.             case ECCENTRIC:
  531.                 return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, cachedL);

  532.             default:
  533.                 throw new OrekitInternalError(null);
  534.         }
  535.     }

  536.     /** {@inheritDoc} */
  537.     @Override
  538.     public T getLMDot() {

  539.         switch (cachedPositionAngleType) {
  540.             case TRUE:
  541.                 final FieldUnivariateDerivative1<T> lvUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  542.                 final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex,     exDot);
  543.                 final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey,     eyDot);
  544.                 final FieldUnivariateDerivative1<T> lMUD = FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lvUD);
  545.                 return lMUD.getFirstDerivative();

  546.             case MEAN:
  547.                 return cachedLDot;

  548.             case ECCENTRIC:
  549.                 final FieldUnivariateDerivative1<T> lEUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot);
  550.                 final FieldUnivariateDerivative1<T> exUD2    = new FieldUnivariateDerivative1<>(ex,     exDot);
  551.                 final FieldUnivariateDerivative1<T> eyUD2    = new FieldUnivariateDerivative1<>(ey,     eyDot);
  552.                 final FieldUnivariateDerivative1<T> lMUD2 = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD2,
  553.                         eyUD2, lEUD);
  554.                 return lMUD2.getFirstDerivative();

  555.             default:
  556.                 throw new OrekitInternalError(null);
  557.         }
  558.     }

  559.     /** Get the longitude argument.
  560.      * @param type type of the angle
  561.      * @return longitude argument (rad)
  562.      */
  563.     public T getL(final PositionAngleType type) {
  564.         return (type == PositionAngleType.MEAN) ? getLM() :
  565.                                               ((type == PositionAngleType.ECCENTRIC) ? getLE() :
  566.                                                                                    getLv());
  567.     }

  568.     /** Get the longitude argument derivative.
  569.      * @param type type of the angle
  570.      * @return longitude argument derivative (rad/s)
  571.      */
  572.     public T getLDot(final PositionAngleType type) {
  573.         return (type == PositionAngleType.MEAN) ? getLMDot() :
  574.                                               ((type == PositionAngleType.ECCENTRIC) ? getLEDot() :
  575.                                                                                    getLvDot());
  576.     }

  577.     /** {@inheritDoc} */
  578.     @Override
  579.     public boolean hasNonKeplerianAcceleration() {
  580.         return aDot.getReal() != 0. || exDot.getReal() != 0 || hxDot.getReal() != 0. || eyDot.getReal() != 0. || hyDot.getReal() != 0. ||
  581.                 FastMath.abs(cachedLDot.subtract(computeKeplerianLDot(cachedPositionAngleType, a, ex, ey, getMu(), cachedL, cachedPositionAngleType)).getReal()) > TOLERANCE_POSITION_ANGLE_RATE;
  582.     }

  583.     /** {@inheritDoc} */
  584.     @Override
  585.     public T getE() {
  586.         return ex.square().add(ey.square()).sqrt();
  587.     }

  588.     /** {@inheritDoc} */
  589.     @Override
  590.     public T getEDot() {
  591.         if (!hasNonKeplerianRates()) {
  592.             return getZero();
  593.         }
  594.         return ex.multiply(exDot).add(ey.multiply(eyDot)).divide(ex.square().add(ey.square()).sqrt());

  595.     }

  596.     /** {@inheritDoc} */
  597.     @Override
  598.     public T getI() {
  599.         return hx.square().add(hy.square()).sqrt().atan().multiply(2);
  600.     }

  601.     /** {@inheritDoc} */
  602.     @Override
  603.     public T getIDot() {
  604.         if (!hasNonKeplerianRates()) {
  605.             return getZero();
  606.         }
  607.         final T h2 = hx.square().add(hy.square());
  608.         final T h  = h2.sqrt();
  609.         return hx.multiply(hxDot).add(hy.multiply(hyDot)).multiply(2).divide(h.multiply(h2.add(1)));

  610.     }

  611.     /** Compute position and velocity but not acceleration.
  612.      */
  613.     private void computePVWithoutA() {

  614.         if (partialPV != null) {
  615.             // already computed
  616.             return;
  617.         }

  618.         // get equinoctial parameters
  619.         final T lE = getLE();

  620.         // inclination-related intermediate parameters
  621.         final T hx2   = hx.square();
  622.         final T hy2   = hy.square();
  623.         final T factH = getOne().divide(hx2.add(1.0).add(hy2));

  624.         // reference axes defining the orbital plane
  625.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  626.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  627.         final T uz = hy.multiply(-2).multiply(factH);

  628.         final T vx = uy;
  629.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  630.         final T vz =  hx.multiply(factH).multiply(2);

  631.         // eccentricity-related intermediate parameters
  632.         final T ex2  = ex.square();
  633.         final T exey = ex.multiply(ey);
  634.         final T ey2  = ey.square();
  635.         final T e2   = ex2.add(ey2);
  636.         final T eta  = getOne().subtract(e2).sqrt().add(1);
  637.         final T beta = getOne().divide(eta);

  638.         // eccentric longitude argument
  639.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  640.         final T cLe    = scLe.cos();
  641.         final T sLe    = scLe.sin();
  642.         final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe));

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

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

  649.         final FieldVector3D<T> position =
  650.                         new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  651.                                             x.multiply(uy).add(y.multiply(vy)),
  652.                                             x.multiply(uz).add(y.multiply(vz)));
  653.         final FieldVector3D<T> velocity =
  654.                         new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)), xdot.multiply(uy).add(ydot.multiply(vy)), xdot.multiply(uz).add(ydot.multiply(vz)));

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

  656.     }

  657.     /** Initialize cached argument of longitude with rate.
  658.      * @param l input argument of longitude
  659.      * @param lDot rate of input argument of longitude
  660.      * @param inputType position angle type passed as input
  661.      * @return argument of longitude to cache with rate
  662.      * @since 12.1
  663.      */
  664.     private FieldUnivariateDerivative1<T> initializeCachedL(final T l, final T lDot,
  665.                                                             final PositionAngleType inputType) {
  666.         if (cachedPositionAngleType == inputType) {
  667.             return new FieldUnivariateDerivative1<>(l, lDot);

  668.         } else {
  669.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  670.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  671.             final FieldUnivariateDerivative1<T> lUD = new FieldUnivariateDerivative1<>(l, lDot);

  672.             switch (cachedPositionAngleType) {

  673.                 case ECCENTRIC:
  674.                     if (inputType == PositionAngleType.MEAN) {
  675.                         return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD, eyUD, lUD);
  676.                     } else {
  677.                         return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, lUD);
  678.                     }

  679.                 case TRUE:
  680.                     if (inputType == PositionAngleType.MEAN) {
  681.                         return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lUD);
  682.                     } else {
  683.                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, lUD);
  684.                     }

  685.                 case MEAN:
  686.                     if (inputType == PositionAngleType.TRUE) {
  687.                         return FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lUD);
  688.                     } else {
  689.                         return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD, eyUD, lUD);
  690.                     }

  691.                 default:
  692.                     throw new OrekitInternalError(null);

  693.             }

  694.         }

  695.     }

  696.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  697.      * @return non-Keplerian part of the acceleration
  698.      */
  699.     private FieldVector3D<T> nonKeplerianAcceleration() {

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

  702.         final T nonKeplerianMeanMotion = getLMDot().subtract(getKeplerianMeanMotion());
  703.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  704.                                  add(dCdP[3][1].multiply(exDot)).
  705.                                  add(dCdP[3][2].multiply(eyDot)).
  706.                                  add(dCdP[3][3].multiply(hxDot)).
  707.                                  add(dCdP[3][4].multiply(hyDot)).
  708.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  709.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  710.                                  add(dCdP[4][1].multiply(exDot)).
  711.                                  add(dCdP[4][2].multiply(eyDot)).
  712.                                  add(dCdP[4][3].multiply(hxDot)).
  713.                                  add(dCdP[4][4].multiply(hyDot)).
  714.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  715.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  716.                                  add(dCdP[5][1].multiply(exDot)).
  717.                                  add(dCdP[5][2].multiply(eyDot)).
  718.                                  add(dCdP[5][3].multiply(hxDot)).
  719.                                  add(dCdP[5][4].multiply(hyDot)).
  720.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

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

  722.     }

  723.     /** {@inheritDoc} */
  724.     @Override
  725.     protected FieldVector3D<T> initPosition() {

  726.         // get equinoctial parameters
  727.         final T lE = getLE();

  728.         // inclination-related intermediate parameters
  729.         final T hx2   = hx.square();
  730.         final T hy2   = hy.square();
  731.         final T factH = getOne().divide(hx2.add(1.0).add(hy2));

  732.         // reference axes defining the orbital plane
  733.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  734.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  735.         final T uz = hy.multiply(-2).multiply(factH);

  736.         final T vx = uy;
  737.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  738.         final T vz =  hx.multiply(factH).multiply(2);

  739.         // eccentricity-related intermediate parameters
  740.         final T ex2  = ex.square();
  741.         final T exey = ex.multiply(ey);
  742.         final T ey2  = ey.square();
  743.         final T e2   = ex2.add(ey2);
  744.         final T eta  = getOne().subtract(e2).sqrt().add(1);
  745.         final T beta = getOne().divide(eta);

  746.         // eccentric longitude argument
  747.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  748.         final T cLe    = scLe.cos();
  749.         final T sLe    = scLe.sin();

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

  753.         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  754.                                    x.multiply(uy).add(y.multiply(vy)),
  755.                                    x.multiply(uz).add(y.multiply(vz)));

  756.     }

  757.     /** {@inheritDoc} */
  758.     @Override
  759.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  760.         // position and velocity
  761.         computePVWithoutA();

  762.         // acceleration
  763.         final T r2 = partialPV.getPosition().getNormSq();
  764.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(getMu().negate()),
  765.                                                                            partialPV.getPosition());
  766.         final FieldVector3D<T> acceleration = hasNonKeplerianRates() ?
  767.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  768.                                               keplerianAcceleration;

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

  770.     }

  771.     /** {@inheritDoc} */
  772.     @Override
  773.     public FieldEquinoctialOrbit<T> inFrame(final Frame inertialFrame) {
  774.         final FieldPVCoordinates<T> fieldPVCoordinates;
  775.         if (hasNonKeplerianAcceleration()) {
  776.             fieldPVCoordinates = getPVCoordinates(inertialFrame);
  777.         } else {
  778.             final FieldKinematicTransform<T> transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
  779.             fieldPVCoordinates = transform.transformOnlyPV(getPVCoordinates());
  780.         }
  781.         final FieldEquinoctialOrbit<T> fieldOrbit = new FieldEquinoctialOrbit<>(fieldPVCoordinates, inertialFrame, getDate(), getMu());
  782.         if (fieldOrbit.getCachedPositionAngleType() == getCachedPositionAngleType()) {
  783.             return fieldOrbit;
  784.         } else {
  785.             return fieldOrbit.withCachedPositionAngleType(getCachedPositionAngleType());
  786.         }
  787.     }

  788.     /** {@inheritDoc} */
  789.     @Override
  790.     public FieldEquinoctialOrbit<T> withCachedPositionAngleType(final PositionAngleType positionAngleType) {
  791.         return new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy, getL(positionAngleType), aDot, exDot, eyDot, hxDot, hyDot,
  792.                 getLDot(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
  793.     }

  794.     /** {@inheritDoc} */
  795.     @Override
  796.     public FieldEquinoctialOrbit<T> shiftedBy(final double dt) {
  797.         return shiftedBy(getZero().newInstance(dt));
  798.     }

  799.     /** {@inheritDoc} */
  800.     @Override
  801.     public FieldEquinoctialOrbit<T> shiftedBy(final T dt) {

  802.         // use Keplerian-only motion
  803.         final FieldEquinoctialOrbit<T> keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy,
  804.                                                                                       getLM().add(getKeplerianMeanMotion().multiply(dt)),
  805.                                                                                       PositionAngleType.MEAN, cachedPositionAngleType, getFrame(),
  806.                                                                                       getDate().shiftedBy(dt), getMu());

  807.         if (hasNonKeplerianRates()) {

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

  810.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  811.             keplerianShifted.computePVWithoutA();
  812.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(),
  813.                                                                   dt.square().multiply(0.5), nonKeplerianAcceleration);
  814.             final T   fixedR2 = fixedP.getNormSq();
  815.             final T   fixedR  = fixedR2.sqrt();
  816.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(),
  817.                                                                  dt, nonKeplerianAcceleration);
  818.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
  819.                                                                  keplerianShifted.partialPV.getPosition(),
  820.                                                                  getOne(), nonKeplerianAcceleration);

  821.             // build a new orbit, taking non-Keplerian acceleration into account
  822.             return new FieldEquinoctialOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  823.                                                                                    fixedP, fixedV, fixedA),
  824.                                                keplerianShifted.getFrame(), keplerianShifted.getMu());

  825.         } else {
  826.             // Keplerian-only motion is all we can do
  827.             return keplerianShifted;
  828.         }

  829.     }

  830.     /** {@inheritDoc} */
  831.     @Override
  832.     protected T[][] computeJacobianMeanWrtCartesian() {

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

  834.         // compute various intermediate parameters
  835.         computePVWithoutA();
  836.         final FieldVector3D<T> position = partialPV.getPosition();
  837.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  838.         final T r2         = position.getNormSq();
  839.         final T r          = r2.sqrt();
  840.         final T r3         = r.multiply(r2);

  841.         final T mu         = getMu();
  842.         final T sqrtMuA    = a.multiply(mu).sqrt();
  843.         final T a2         = a.square();

  844.         final T e2         = ex.square().add(ey.square());
  845.         final T oMe2       = getOne().subtract(e2);
  846.         final T epsilon    = oMe2.sqrt();
  847.         final T beta       = getOne().divide(epsilon.add(1));
  848.         final T ratio      = epsilon.multiply(beta);

  849.         final T hx2        = hx.square();
  850.         final T hy2        = hy.square();
  851.         final T hxhy       = hx.multiply(hy);

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

  856.         // coordinates of the spacecraft in the equinoctial frame
  857.         final T x    = FieldVector3D.dotProduct(position, f);
  858.         final T y    = FieldVector3D.dotProduct(position, g);
  859.         final T xDot = FieldVector3D.dotProduct(velocity, f);
  860.         final T yDot = FieldVector3D.dotProduct(velocity, g);

  861.         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
  862.         final T c1  = a.divide(sqrtMuA.multiply(epsilon));
  863.         final T c1N = c1.negate();
  864.         final T c2  = a.multiply(sqrtMuA).multiply(beta).divide(r3);
  865.         final T c3  = sqrtMuA.divide(r3.multiply(epsilon));
  866.         final FieldVector3D<T> drDotSdEx = new FieldVector3D<>(c1.multiply(xDot).multiply(yDot).subtract(c2.multiply(ey).multiply(x)).subtract(c3.multiply(x).multiply(y)), f,
  867.                                                                c1N.multiply(xDot).multiply(xDot).subtract(c2.multiply(ey).multiply(y)).add(c3.multiply(x).multiply(x)), g);

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

  871.         // da
  872.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
  873.         final FieldVector3D<T> vectorARDot = new FieldVector3D<>(a2.multiply(2).divide(mu), velocity);
  874.         fillHalfRow(getOne(), vectorAR,    jacobian[0], 0);
  875.         fillHalfRow(getOne(), vectorARDot, jacobian[0], 3);

  876.         // dEx
  877.         final T d1 = a.negate().multiply(ratio).divide(r3);
  878.         final T d2 = (hy.multiply(xDot).subtract(hx.multiply(yDot))).divide(sqrtMuA.multiply(epsilon));
  879.         final T d3 = hx.multiply(y).subtract(hy.multiply(x)).divide(sqrtMuA);
  880.         final FieldVector3D<T> vectorExRDot =
  881.             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);
  882.         fillHalfRow(ex.multiply(d1), position, ey.negate().multiply(d2), w, epsilon.divide(sqrtMuA), drDotSdEy, jacobian[1], 0);
  883.         fillHalfRow(getOne(), vectorExRDot, jacobian[1], 3);

  884.         // dEy
  885.         final FieldVector3D<T> vectorEyRDot =
  886.             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);
  887.         fillHalfRow(ey.multiply(d1), position, ex.multiply(d2), w, epsilon.negate().divide(sqrtMuA), drDotSdEx, jacobian[2], 0);
  888.         fillHalfRow(getOne(), vectorEyRDot, jacobian[2], 3);

  889.         // dHx
  890.         final T h = (hx2.add(1).add(hy2)).divide(sqrtMuA.multiply(2).multiply(epsilon));
  891.         fillHalfRow( h.negate().multiply(xDot), w, jacobian[3], 0);
  892.         fillHalfRow( h.multiply(x),    w, jacobian[3], 3);

  893.        // dHy
  894.         fillHalfRow( h.negate().multiply(yDot), w, jacobian[4], 0);
  895.         fillHalfRow( h.multiply(y),    w, jacobian[4], 3);

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

  900.         return jacobian;

  901.     }

  902.     /** {@inheritDoc} */
  903.     @Override
  904.     protected T[][] computeJacobianEccentricWrtCartesian() {

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

  907.         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
  908.         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
  909.         // which is inverted and rewritten as:
  910.         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
  911.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  912.         final T cosLe = scLe.cos();
  913.         final T sinLe = scLe.sin();
  914.         final T aOr   = getOne().divide(getOne().subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe)));

  915.         // update longitude row
  916.         final T[] rowEx = jacobian[1];
  917.         final T[] rowEy = jacobian[2];
  918.         final T[] rowL  = jacobian[5];

  919.         for (int j = 0; j < 6; ++j) {
  920.             rowL[j] = aOr.multiply(rowL[j].add(sinLe.multiply(rowEx[j])).subtract(cosLe.multiply(rowEy[j])));
  921.         }
  922.         return jacobian;

  923.     }

  924.     /** {@inheritDoc} */
  925.     @Override
  926.     protected T[][] computeJacobianTrueWrtCartesian() {

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

  929.         // Differentiating the eccentric longitude equation
  930.         // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
  931.         // leads to
  932.         // cT (dlV - dlE) = cE dlE + cX dex + cY dey
  933.         // with
  934.         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
  935.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
  936.         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  937.         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  938.         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  939.         // which can be solved to find the differential of the true longitude
  940.         // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
  941.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  942.         final T cosLe     = scLe.cos();
  943.         final T sinLe     = scLe.sin();
  944.         final T eSinE     = ex.multiply(sinLe).subtract(ey.multiply(cosLe));
  945.         final T ecosE     = ex.multiply(cosLe).add(ey.multiply(sinLe));
  946.         final T e2        = ex.square().add(ey.square());
  947.         final T epsilon   = getOne().subtract(e2).sqrt();
  948.         final T onePeps   = epsilon.add(1);
  949.         final T d         = onePeps.subtract(ecosE);
  950.         final T cT        = d.multiply(d).add(eSinE.multiply(eSinE)).divide(2);
  951.         final T cE        = ecosE.multiply(onePeps).subtract(e2);
  952.         final T cX        = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinLe.multiply(onePeps));
  953.         final T cY        = ey.multiply(eSinE).divide(epsilon).add( ex).subtract(cosLe.multiply(onePeps));
  954.         final T factorLe  = cT.add(cE).divide(cT);
  955.         final T factorEx  = cX.divide(cT);
  956.         final T factorEy  = cY.divide(cT);

  957.         // update longitude row
  958.         final T[] rowEx = jacobian[1];
  959.         final T[] rowEy = jacobian[2];
  960.         final T[] rowL = jacobian[5];
  961.         for (int j = 0; j < 6; ++j) {
  962.             rowL[j] = factorLe.multiply(rowL[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  963.         }

  964.         return jacobian;

  965.     }

  966.     /** {@inheritDoc} */
  967.     @Override
  968.     public void addKeplerContribution(final PositionAngleType type, final T gm,
  969.                                       final T[] pDot) {
  970.         pDot[5] = pDot[5].add(computeKeplerianLDot(type, a, ex, ey, gm, cachedL, cachedPositionAngleType));
  971.     }

  972.     /**
  973.      * Compute rate of argument of longitude.
  974.      * @param type position angle type of rate
  975.      * @param a semi major axis
  976.      * @param ex ex
  977.      * @param ey ey
  978.      * @param mu mu
  979.      * @param l argument of longitude
  980.      * @param cachedType position angle type of passed l
  981.      * @param <T> field type
  982.      * @return first-order time derivative for l
  983.      * @since 12.2
  984.      */
  985.     private static <T extends CalculusFieldElement<T>> T computeKeplerianLDot(final PositionAngleType type, final T a, final T ex,
  986.                                                                               final T ey, final T mu, final T l, final PositionAngleType cachedType) {
  987.         final T n               = mu.divide(a).sqrt().divide(a);
  988.         if (type == PositionAngleType.MEAN) {
  989.             return n;
  990.         }
  991.         final FieldSinCos<T> sc;
  992.         final T ksi;
  993.         if (type == PositionAngleType.ECCENTRIC) {
  994.             sc = FastMath.sinCos(FieldEquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
  995.             ksi = ((ex.multiply(sc.cos())).add(ey.multiply(sc.sin()))).negate().add(1).reciprocal();
  996.             return n.multiply(ksi);
  997.         } else {
  998.             sc = FastMath.sinCos(FieldEquinoctialLongitudeArgumentUtility.convertL(cachedType, l, ex, ey, type));
  999.             final T oMe2 = a.getField().getOne().subtract(ex.square()).subtract(ey.square());
  1000.             ksi  =  ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin()));
  1001.             return n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt()));
  1002.         }
  1003.     }

  1004.     /**  Returns a string representation of this equinoctial parameters object.
  1005.      * @return a string representation of this object
  1006.      */
  1007.     public String toString() {
  1008.         return new StringBuilder().append("equinoctial parameters: ").append('{').
  1009.                                   append("a: ").append(a.getReal()).
  1010.                                   append("; ex: ").append(ex.getReal()).append("; ey: ").append(ey.getReal()).
  1011.                                   append("; hx: ").append(hx.getReal()).append("; hy: ").append(hy.getReal()).
  1012.                                   append("; lv: ").append(FastMath.toDegrees(getLv().getReal())).
  1013.                                   append(";}").toString();
  1014.     }

  1015.     /** {@inheritDoc} */
  1016.     @Override
  1017.     public PositionAngleType getCachedPositionAngleType() {
  1018.         return cachedPositionAngleType;
  1019.     }

  1020.     /** {@inheritDoc} */
  1021.     @Override
  1022.     public boolean hasNonKeplerianRates() {
  1023.         return hasNonKeplerianAcceleration();
  1024.     }

  1025.     /** {@inheritDoc} */
  1026.     @Override
  1027.     public FieldEquinoctialOrbit<T> withKeplerianRates() {
  1028.         return new FieldEquinoctialOrbit<>(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(),
  1029.                 cachedL, cachedPositionAngleType, getFrame(), getDate(), getMu());
  1030.     }

  1031.     /** {@inheritDoc} */
  1032.     @Override
  1033.     public EquinoctialOrbit toOrbit() {
  1034.         final double cachedPositionAngle = cachedL.getReal();
  1035.         if (hasNonKeplerianRates()) {
  1036.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1037.                                         hx.getReal(), hy.getReal(), cachedPositionAngle,
  1038.                                         aDot.getReal(), exDot.getReal(), eyDot.getReal(),
  1039.                                         hxDot.getReal(), hyDot.getReal(), cachedLDot.getReal(),
  1040.                                         cachedPositionAngleType, getFrame(),
  1041.                                         getDate().toAbsoluteDate(), getMu().getReal());
  1042.         } else {
  1043.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1044.                                         hx.getReal(), hy.getReal(), cachedPositionAngle,
  1045.                                         cachedPositionAngleType, getFrame(),
  1046.                                         getDate().toAbsoluteDate(), getMu().getReal());
  1047.         }
  1048.     }

  1049. }