FieldEquinoctialOrbit.java

  1. /* Copyright 2002-2023 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.     /** True longitude argument (rad). */
  85.     private final T lv;

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

  88.     /** First component of the eccentricity vector derivative. */
  89.     private final T exDot;

  90.     /** Second component of the eccentricity vector derivative. */
  91.     private final T eyDot;

  92.     /** First component of the inclination vector derivative. */
  93.     private final T hxDot;

  94.     /** Second component of the inclination vector derivative. */
  95.     private final T hyDot;

  96.     /** True longitude argument derivative (rad/s). */
  97.     private final T lvDot;

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

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

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

  153.         if (ex.getReal() * ex.getReal() + ey.getReal() * ey.getReal() >= 1.0) {
  154.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  155.                                                      getClass().getName());
  156.         }

  157.         this.a     = a;
  158.         this.aDot  = aDot;
  159.         this.ex    = ex;
  160.         this.exDot = exDot;
  161.         this.ey    = ey;
  162.         this.eyDot = eyDot;
  163.         this.hx    = hx;
  164.         this.hxDot = hxDot;
  165.         this.hy    = hy;
  166.         this.hyDot = hyDot;

  167.         if (hasDerivatives()) {
  168.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  169.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  170.             final FieldUnivariateDerivative1<T> lUD  = new FieldUnivariateDerivative1<>(l,  lDot);
  171.             final FieldUnivariateDerivative1<T> lvUD;
  172.             switch (type) {
  173.                 case MEAN :
  174.                     lvUD = eccentricToTrue(meanToEccentric(lUD, exUD, eyUD), exUD, eyUD);
  175.                     break;
  176.                 case ECCENTRIC :
  177.                     lvUD = eccentricToTrue(lUD, exUD, eyUD);
  178.                     break;
  179.                 case TRUE :
  180.                     lvUD = lUD;
  181.                     break;
  182.                 default : // this should never happen
  183.                     throw new OrekitInternalError(null);
  184.             }
  185.             this.lv    = lvUD.getValue();
  186.             this.lvDot = lvUD.getDerivative(1);
  187.         } else {
  188.             switch (type) {
  189.                 case MEAN :
  190.                     this.lv = eccentricToTrue(meanToEccentric(l, ex, ey), ex, ey);
  191.                     break;
  192.                 case ECCENTRIC :
  193.                     this.lv = eccentricToTrue(l, ex, ey);
  194.                     break;
  195.                 case TRUE :
  196.                     this.lv = l;
  197.                     break;
  198.                 default :
  199.                     throw new OrekitInternalError(null);
  200.             }
  201.             this.lvDot = null;
  202.         }

  203.         this.partialPV = null;

  204.     }

  205.     /** Constructor from Cartesian parameters.
  206.      *
  207.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  208.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  209.      * use {@code mu} and the position to compute the acceleration, including
  210.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  211.      *
  212.      * @param pvCoordinates the position, velocity and acceleration
  213.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  214.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  215.      * @param mu central attraction coefficient (m³/s²)
  216.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  217.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  218.      */
  219.     public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
  220.                                  final Frame frame, final T mu)
  221.         throws IllegalArgumentException {
  222.         super(pvCoordinates, frame, mu);

  223.         //  compute semi-major axis
  224.         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
  225.         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
  226.         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
  227.         final T r2 = pvP.getNormSq();
  228.         final T r  = r2.sqrt();
  229.         final T V2 = pvV.getNormSq();
  230.         final T rV2OnMu = r.multiply(V2).divide(mu);

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

  233.         if (!isElliptical()) {
  234.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  235.                                                      getClass().getName());
  236.         }

  237.         // compute inclination vector
  238.         final FieldVector3D<T> w = pvCoordinates.getMomentum().normalize();
  239.         final T d = getOne().divide(getOne().add(w.getZ()));
  240.         hx =  d.negate().multiply(w.getY());
  241.         hy =  d.multiply(w.getX());

  242.         // compute true longitude argument
  243.         final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r);
  244.         final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r);
  245.         lv = sLv.atan2(cLv);

  246.         // compute eccentricity vector
  247.         final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
  248.         final T eCE = rV2OnMu.subtract(1);
  249.         final T e2  = eCE.multiply(eCE).add(eSE.multiply(eSE));
  250.         final T f   = eCE.subtract(e2);
  251.         final T g   = e2.negate().add(1).sqrt().multiply(eSE);
  252.         ex = a.multiply(f.multiply(cLv).add( g.multiply(sLv))).divide(r);
  253.         ey = a.multiply(f.multiply(sLv).subtract(g.multiply(cLv))).divide(r);

  254.         partialPV = pvCoordinates;

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

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

  259.             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP);
  260.             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  261.             final T   aX                       = nonKeplerianAcceleration.getX();
  262.             final T   aY                       = nonKeplerianAcceleration.getY();
  263.             final T   aZ                       = nonKeplerianAcceleration.getZ();
  264.             aDot  = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
  265.             exDot = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
  266.             eyDot = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
  267.             hxDot = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
  268.             hyDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));

  269.             // in order to compute true anomaly derivative, we must compute
  270.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  271.             final T lMDot = getKeplerianMeanMotion().
  272.                             add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  273.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  274.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  275.             final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(getLM(), lMDot);
  276.             final FieldUnivariateDerivative1<T> lvUD = eccentricToTrue(meanToEccentric(lMUD, exUD, eyUD), exUD, eyUD);
  277.             lvDot = lvUD.getDerivative(1);

  278.         } else {
  279.             // acceleration is either almost zero or NaN,
  280.             // we assume acceleration was not known
  281.             // we don't set up derivatives
  282.             aDot  = null;
  283.             exDot = null;
  284.             eyDot = null;
  285.             hxDot = null;
  286.             hyDot = null;
  287.             lvDot = null;
  288.         }

  289.     }

  290.     /** Constructor from Cartesian parameters.
  291.      *
  292.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  293.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  294.      * use {@code mu} and the position to compute the acceleration, including
  295.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  296.      *
  297.      * @param pvCoordinates the position end velocity
  298.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  299.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  300.      * @param date date of the orbital parameters
  301.      * @param mu central attraction coefficient (m³/s²)
  302.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  303.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  304.      */
  305.     public FieldEquinoctialOrbit(final FieldPVCoordinates<T> pvCoordinates, final Frame frame,
  306.                             final FieldAbsoluteDate<T> date, final T mu)
  307.         throws IllegalArgumentException {
  308.         this(new TimeStampedFieldPVCoordinates<>(date, pvCoordinates), frame, mu);
  309.     }

  310.     /** Constructor from any kind of orbital parameters.
  311.      * @param op orbital parameters to copy
  312.      */
  313.     public FieldEquinoctialOrbit(final FieldOrbit<T> op) {
  314.         super(op.getFrame(), op.getDate(), op.getMu());

  315.         a     = op.getA();
  316.         ex    = op.getEquinoctialEx();
  317.         ey    = op.getEquinoctialEy();
  318.         hx    = op.getHx();
  319.         hy    = op.getHy();
  320.         lv    = op.getLv();

  321.         aDot  = op.getADot();
  322.         exDot = op.getEquinoctialExDot();
  323.         eyDot = op.getEquinoctialEyDot();
  324.         hxDot = op.getHxDot();
  325.         hyDot = op.getHyDot();
  326.         lvDot = op.getLvDot();
  327.     }

  328.     /** Constructor from Field and EquinoctialOrbit.
  329.      * <p>Build a FieldEquinoctialOrbit from non-Field EquinoctialOrbit.</p>
  330.      * @param field CalculusField to base object on
  331.      * @param op non-field orbit with only "constant" terms
  332.      * @since 12.0
  333.      */
  334.     public FieldEquinoctialOrbit(final Field<T> field, final EquinoctialOrbit op) {
  335.         super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu()));

  336.         a     = getZero().add(op.getA());
  337.         ex    = getZero().add(op.getEquinoctialEx());
  338.         ey    = getZero().add(op.getEquinoctialEy());
  339.         hx    = getZero().add(op.getHx());
  340.         hy    = getZero().add(op.getHy());
  341.         lv    = getZero().add(op.getLv());

  342.         if (op.hasDerivatives()) {
  343.             aDot  = getZero().add(op.getADot());
  344.             exDot = getZero().add(op.getEquinoctialExDot());
  345.             eyDot = getZero().add(op.getEquinoctialEyDot());
  346.             hxDot = getZero().add(op.getHxDot());
  347.             hyDot = getZero().add(op.getHyDot());
  348.             lvDot = getZero().add(op.getLvDot());
  349.         } else {
  350.             aDot  = null;
  351.             exDot = null;
  352.             eyDot = null;
  353.             hxDot = null;
  354.             hyDot = null;
  355.             lvDot = null;
  356.         }

  357.     }

  358.     /** Constructor from Field and Orbit.
  359.      * <p>Build a FieldEquinoctialOrbit from any non-Field Orbit.</p>
  360.      * @param field CalculusField to base object on
  361.      * @param op non-field orbit with only "constant" terms
  362.      * @since 12.0
  363.      */
  364.     public FieldEquinoctialOrbit(final Field<T> field, final Orbit op) {
  365.         this(field, new EquinoctialOrbit(op));
  366.     }

  367.     /** {@inheritDoc} */
  368.     public OrbitType getType() {
  369.         return OrbitType.EQUINOCTIAL;
  370.     }

  371.     /** {@inheritDoc} */
  372.     public T getA() {
  373.         return a;
  374.     }

  375.     /** {@inheritDoc} */
  376.     public T getADot() {
  377.         return aDot;
  378.     }

  379.     /** {@inheritDoc} */
  380.     public T getEquinoctialEx() {
  381.         return ex;
  382.     }

  383.     /** {@inheritDoc} */
  384.     public T getEquinoctialExDot() {
  385.         return exDot;
  386.     }

  387.     /** {@inheritDoc} */
  388.     public T getEquinoctialEy() {
  389.         return ey;
  390.     }

  391.     /** {@inheritDoc} */
  392.     public T getEquinoctialEyDot() {
  393.         return eyDot;
  394.     }

  395.     /** {@inheritDoc} */
  396.     public T getHx() {
  397.         return hx;
  398.     }

  399.     /** {@inheritDoc} */
  400.     public T getHxDot() {
  401.         return hxDot;
  402.     }

  403.     /** {@inheritDoc} */
  404.     public T getHy() {
  405.         return hy;
  406.     }

  407.     /** {@inheritDoc} */
  408.     public T getHyDot() {
  409.         return hyDot;
  410.     }

  411.     /** {@inheritDoc} */
  412.     public T getLv() {
  413.         return lv;
  414.     }

  415.     /** {@inheritDoc} */
  416.     public T getLvDot() {
  417.         return lvDot;
  418.     }

  419.     /** {@inheritDoc} */
  420.     public T getLE() {
  421.         return trueToEccentric(lv, ex, ey);
  422.     }

  423.     /** {@inheritDoc} */
  424.     public T getLEDot() {

  425.         if (!hasDerivatives()) {
  426.             return null;
  427.         }

  428.         final FieldUnivariateDerivative1<T> lVUD = new FieldUnivariateDerivative1<>(lv, lvDot);
  429.         final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  430.         final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  431.         final FieldUnivariateDerivative1<T> lEUD = trueToEccentric(lVUD, exUD, eyUD);
  432.         return lEUD.getDerivative(1);

  433.     }

  434.     /** {@inheritDoc} */
  435.     public T getLM() {
  436.         return eccentricToMean(trueToEccentric(lv, ex, ey), ex, ey);
  437.     }

  438.     /** {@inheritDoc} */
  439.     public T getLMDot() {

  440.         if (!hasDerivatives()) {
  441.             return null;
  442.         }

  443.         final FieldUnivariateDerivative1<T> lVUD = new FieldUnivariateDerivative1<>(lv, lvDot);
  444.         final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  445.         final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  446.         final FieldUnivariateDerivative1<T> lMUD = eccentricToMean(trueToEccentric(lVUD, exUD, eyUD), exUD, eyUD);
  447.         return lMUD.getDerivative(1);

  448.     }

  449.     /** Get the longitude argument.
  450.      * @param type type of the angle
  451.      * @return longitude argument (rad)
  452.      */
  453.     public T getL(final PositionAngleType type) {
  454.         return (type == PositionAngleType.MEAN) ? getLM() :
  455.                                               ((type == PositionAngleType.ECCENTRIC) ? getLE() :
  456.                                                                                    getLv());
  457.     }

  458.     /** Get the longitude argument derivative.
  459.      * @param type type of the angle
  460.      * @return longitude argument derivative (rad/s)
  461.      */
  462.     public T getLDot(final PositionAngleType type) {
  463.         return (type == PositionAngleType.MEAN) ? getLMDot() :
  464.                                               ((type == PositionAngleType.ECCENTRIC) ? getLEDot() :
  465.                                                                                    getLvDot());
  466.     }

  467.     /** {@inheritDoc} */
  468.     @Override
  469.     public boolean hasDerivatives() {
  470.         return aDot != null;
  471.     }

  472.     /** Computes the true longitude argument from the eccentric longitude argument.
  473.      * @param lE = E + ω + Ω eccentric longitude argument (rad)
  474.      * @param ex first component of the eccentricity vector
  475.      * @param ey second component of the eccentricity vector
  476.      * @param <T> Type of the field elements
  477.      * @return the true longitude argument
  478.      */
  479.     public static <T extends CalculusFieldElement<T>> T eccentricToTrue(final T lE, final T ex, final T ey) {
  480.         final T epsilon           = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt();
  481.         final FieldSinCos<T> scLE = FastMath.sinCos(lE);
  482.         final T cosLE             = scLE.cos();
  483.         final T sinLE             = scLE.sin();
  484.         final T num               = ex.multiply(sinLE).subtract(ey.multiply(cosLE));
  485.         final T den               = epsilon.add(1).subtract(ex.multiply(cosLE)).subtract(ey.multiply(sinLE));
  486.         return lE.add(num.divide(den).atan().multiply(2));
  487.     }

  488.     /** Computes the eccentric longitude argument from the true longitude argument.
  489.      * @param lv = v + ω + Ω true longitude argument (rad)
  490.      * @param ex first component of the eccentricity vector
  491.      * @param ey second component of the eccentricity vector
  492.      * @param <T> Type of the field elements
  493.      * @return the eccentric longitude argument
  494.      */
  495.     public static <T extends CalculusFieldElement<T>> T trueToEccentric(final T lv, final T ex, final T ey) {
  496.         final T epsilon           = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt();
  497.         final FieldSinCos<T> scLv = FastMath.sinCos(lv);
  498.         final T cosLv             = scLv.cos();
  499.         final T sinLv             = scLv.sin();
  500.         final T num               = ey.multiply(cosLv).subtract(ex.multiply(sinLv));
  501.         final T den               = epsilon.add(1).add(ex.multiply(cosLv)).add(ey.multiply(sinLv));
  502.         return lv.add(num.divide(den).atan().multiply(2));
  503.     }

  504.     /** Computes the eccentric longitude argument from the mean longitude argument.
  505.      * @param lM = M + ω + Ω mean longitude argument (rad)
  506.      * @param ex first component of the eccentricity vector
  507.      * @param ey second component of the eccentricity vector
  508.      * @param <T> Type of the field elements
  509.      * @return the eccentric longitude argument
  510.      */
  511.     public static <T extends CalculusFieldElement<T>> T meanToEccentric(final T lM, final T ex, final T ey) {
  512.         // Generalization of Kepler equation to equinoctial parameters
  513.         // with lE = PA + RAAN + E and
  514.         //      lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE)
  515.         T lE = lM;
  516.         T shift = lM.getField().getZero();
  517.         T lEmlM = lM.getField().getZero();
  518.         FieldSinCos<T> scLE = FastMath.sinCos(lE);
  519.         int iter = 0;
  520.         do {
  521.             final T f2 = ex.multiply(scLE.sin()).subtract(ey.multiply(scLE.cos()));
  522.             final T f1 = ex.multiply(scLE.cos()).add(ey.multiply(scLE.sin())).negate().add(1);
  523.             final T f0 = lEmlM.subtract(f2);

  524.             final T f12 = f1.multiply(2.0);
  525.             shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2)));

  526.             lEmlM = lEmlM.subtract(shift);
  527.             lE     = lM.add(lEmlM);
  528.             scLE = FastMath.sinCos(lE);

  529.         } while (++iter < 50 && FastMath.abs(shift.getReal()) > 1.0e-12);

  530.         return lE;

  531.     }

  532.     /** Computes the mean longitude argument from the eccentric longitude argument.
  533.      * @param lE = E + ω + Ω mean longitude argument (rad)
  534.      * @param ex first component of the eccentricity vector
  535.      * @param ey second component of the eccentricity vector
  536.      * @param <T> Type of the field elements
  537.      * @return the mean longitude argument
  538.      */
  539.     public static <T extends CalculusFieldElement<T>> T eccentricToMean(final T lE, final T ex, final T ey) {
  540.         final FieldSinCos<T> scLE = FastMath.sinCos(lE);
  541.         return lE.subtract(ex.multiply(scLE.sin())).add(ey.multiply(scLE.cos()));
  542.     }

  543.     /** {@inheritDoc} */
  544.     public T getE() {
  545.         return ex.multiply(ex).add(ey.multiply(ey)).sqrt();
  546.     }

  547.     /** {@inheritDoc} */
  548.     public T getEDot() {

  549.         if (!hasDerivatives()) {
  550.             return null;
  551.         }

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

  553.     }

  554.     /** {@inheritDoc} */
  555.     public T getI() {
  556.         return hx.multiply(hx).add(hy.multiply(hy)).sqrt().atan().multiply(2);
  557.     }

  558.     /** {@inheritDoc} */
  559.     public T getIDot() {

  560.         if (!hasDerivatives()) {
  561.             return null;
  562.         }

  563.         final T h2 = hx.multiply(hx).add(hy.multiply(hy));
  564.         final T h  = h2.sqrt();
  565.         return hx.multiply(hxDot).add(hy.multiply(hyDot)).multiply(2).divide(h.multiply(h2.add(1)));

  566.     }

  567.     /** Compute position and velocity but not acceleration.
  568.      */
  569.     private void computePVWithoutA() {

  570.         if (partialPV != null) {
  571.             // already computed
  572.             return;
  573.         }

  574.         // get equinoctial parameters
  575.         final T lE = getLE();

  576.         // inclination-related intermediate parameters
  577.         final T hx2   = hx.multiply(hx);
  578.         final T hy2   = hy.multiply(hy);
  579.         final T factH = getOne().divide(hx2.add(1.0).add(hy2));

  580.         // reference axes defining the orbital plane
  581.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  582.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  583.         final T uz = hy.multiply(-2).multiply(factH);

  584.         final T vx = uy;
  585.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  586.         final T vz =  hx.multiply(factH).multiply(2);

  587.         // eccentricity-related intermediate parameters
  588.         final T ex2  = ex.multiply(ex);
  589.         final T exey = ex.multiply(ey);
  590.         final T ey2  = ey.multiply(ey);
  591.         final T e2   = ex2.add(ey2);
  592.         final T eta  = getOne().subtract(e2).sqrt().add(1);
  593.         final T beta = getOne().divide(eta);

  594.         // eccentric longitude argument
  595.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  596.         final T cLe    = scLe.cos();
  597.         final T sLe    = scLe.sin();
  598.         final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe));

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

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

  605.         final FieldVector3D<T> position =
  606.                         new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  607.                                             x.multiply(uy).add(y.multiply(vy)),
  608.                                             x.multiply(uz).add(y.multiply(vz)));
  609.         final FieldVector3D<T> velocity =
  610.                         new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)), xdot.multiply(uy).add(ydot.multiply(vy)), xdot.multiply(uz).add(ydot.multiply(vz)));

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

  612.     }

  613.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  614.      * <p>
  615.      * This method should be called only when {@link #hasDerivatives()} returns true.
  616.      * </p>
  617.      * @return non-Keplerian part of the acceleration
  618.      */
  619.     private FieldVector3D<T> nonKeplerianAcceleration() {

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

  622.         final T nonKeplerianMeanMotion = getLMDot().subtract(getKeplerianMeanMotion());
  623.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  624.                                  add(dCdP[3][1].multiply(exDot)).
  625.                                  add(dCdP[3][2].multiply(eyDot)).
  626.                                  add(dCdP[3][3].multiply(hxDot)).
  627.                                  add(dCdP[3][4].multiply(hyDot)).
  628.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  629.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  630.                                  add(dCdP[4][1].multiply(exDot)).
  631.                                  add(dCdP[4][2].multiply(eyDot)).
  632.                                  add(dCdP[4][3].multiply(hxDot)).
  633.                                  add(dCdP[4][4].multiply(hyDot)).
  634.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  635.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  636.                                  add(dCdP[5][1].multiply(exDot)).
  637.                                  add(dCdP[5][2].multiply(eyDot)).
  638.                                  add(dCdP[5][3].multiply(hxDot)).
  639.                                  add(dCdP[5][4].multiply(hyDot)).
  640.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

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

  642.     }

  643.     /** {@inheritDoc} */
  644.     protected FieldVector3D<T> initPosition() {

  645.         // get equinoctial parameters
  646.         final T lE = getLE();

  647.         // inclination-related intermediate parameters
  648.         final T hx2   = hx.multiply(hx);
  649.         final T hy2   = hy.multiply(hy);
  650.         final T factH = getOne().divide(hx2.add(1.0).add(hy2));

  651.         // reference axes defining the orbital plane
  652.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  653.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  654.         final T uz = hy.multiply(-2).multiply(factH);

  655.         final T vx = uy;
  656.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  657.         final T vz =  hx.multiply(factH).multiply(2);

  658.         // eccentricity-related intermediate parameters
  659.         final T ex2  = ex.multiply(ex);
  660.         final T exey = ex.multiply(ey);
  661.         final T ey2  = ey.multiply(ey);
  662.         final T e2   = ex2.add(ey2);
  663.         final T eta  = getOne().subtract(e2).sqrt().add(1);
  664.         final T beta = getOne().divide(eta);

  665.         // eccentric longitude argument
  666.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  667.         final T cLe    = scLe.cos();
  668.         final T sLe    = scLe.sin();

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

  672.         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  673.                                    x.multiply(uy).add(y.multiply(vy)),
  674.                                    x.multiply(uz).add(y.multiply(vz)));

  675.     }

  676.     /** {@inheritDoc} */
  677.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  678.         // position and velocity
  679.         computePVWithoutA();

  680.         // acceleration
  681.         final T r2 = partialPV.getPosition().getNormSq();
  682.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(getMu().negate()),
  683.                                                                            partialPV.getPosition());
  684.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  685.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  686.                                               keplerianAcceleration;

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

  688.     }

  689.     /** {@inheritDoc} */
  690.     public FieldEquinoctialOrbit<T> shiftedBy(final double dt) {
  691.         return shiftedBy(getZero().add(dt));
  692.     }

  693.     /** {@inheritDoc} */
  694.     public FieldEquinoctialOrbit<T> shiftedBy(final T dt) {

  695.         // use Keplerian-only motion
  696.         final FieldEquinoctialOrbit<T> keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy,
  697.                                                                                       getLM().add(getKeplerianMeanMotion().multiply(dt)),
  698.                                                                                       PositionAngleType.MEAN, getFrame(),
  699.                                                                                       getDate().shiftedBy(dt), getMu());

  700.         if (hasDerivatives()) {

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

  703.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  704.             keplerianShifted.computePVWithoutA();
  705.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(),
  706.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  707.             final T   fixedR2 = fixedP.getNormSq();
  708.             final T   fixedR  = fixedR2.sqrt();
  709.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(),
  710.                                                                  dt, nonKeplerianAcceleration);
  711.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
  712.                                                                  keplerianShifted.partialPV.getPosition(),
  713.                                                                  getOne(), nonKeplerianAcceleration);

  714.             // build a new orbit, taking non-Keplerian acceleration into account
  715.             return new FieldEquinoctialOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  716.                                                                                    fixedP, fixedV, fixedA),
  717.                                                keplerianShifted.getFrame(), keplerianShifted.getMu());

  718.         } else {
  719.             // Keplerian-only motion is all we can do
  720.             return keplerianShifted;
  721.         }

  722.     }

  723.     /** {@inheritDoc} */
  724.     protected T[][] computeJacobianMeanWrtCartesian() {

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

  726.         // compute various intermediate parameters
  727.         computePVWithoutA();
  728.         final FieldVector3D<T> position = partialPV.getPosition();
  729.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  730.         final T r2         = position.getNormSq();
  731.         final T r          = r2.sqrt();
  732.         final T r3         = r.multiply(r2);

  733.         final T mu         = getMu();
  734.         final T sqrtMuA    = a.multiply(mu).sqrt();
  735.         final T a2         = a.multiply(a);

  736.         final T e2         = ex.multiply(ex).add(ey.multiply(ey));
  737.         final T oMe2       = getOne().subtract(e2);
  738.         final T epsilon    = oMe2.sqrt();
  739.         final T beta       = getOne().divide(epsilon.add(1));
  740.         final T ratio      = epsilon.multiply(beta);

  741.         final T hx2        = hx.multiply(hx);
  742.         final T hy2        = hy.multiply(hy);
  743.         final T hxhy       = hx.multiply(hy);

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

  748.         // coordinates of the spacecraft in the equinoctial frame
  749.         final T x    = FieldVector3D.dotProduct(position, f);
  750.         final T y    = FieldVector3D.dotProduct(position, g);
  751.         final T xDot = FieldVector3D.dotProduct(velocity, f);
  752.         final T yDot = FieldVector3D.dotProduct(velocity, g);

  753.         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
  754.         final T c1  = a.divide(sqrtMuA.multiply(epsilon));
  755.         final T c1N = c1.negate();
  756.         final T c2  = a.multiply(sqrtMuA).multiply(beta).divide(r3);
  757.         final T c3  = sqrtMuA.divide(r3.multiply(epsilon));
  758.         final FieldVector3D<T> drDotSdEx = new FieldVector3D<>(c1.multiply(xDot).multiply(yDot).subtract(c2.multiply(ey).multiply(x)).subtract(c3.multiply(x).multiply(y)), f,
  759.                                                                c1N.multiply(xDot).multiply(xDot).subtract(c2.multiply(ey).multiply(y)).add(c3.multiply(x).multiply(x)), g);

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

  763.         // da
  764.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
  765.         final FieldVector3D<T> vectorARDot = new FieldVector3D<>(a2.multiply(2).divide(mu), velocity);
  766.         fillHalfRow(getOne(), vectorAR,    jacobian[0], 0);
  767.         fillHalfRow(getOne(), vectorARDot, jacobian[0], 3);

  768.         // dEx
  769.         final T d1 = a.negate().multiply(ratio).divide(r3);
  770.         final T d2 = (hy.multiply(xDot).subtract(hx.multiply(yDot))).divide(sqrtMuA.multiply(epsilon));
  771.         final T d3 = hx.multiply(y).subtract(hy.multiply(x)).divide(sqrtMuA);
  772.         final FieldVector3D<T> vectorExRDot =
  773.             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);
  774.         fillHalfRow(ex.multiply(d1), position, ey.negate().multiply(d2), w, epsilon.divide(sqrtMuA), drDotSdEy, jacobian[1], 0);
  775.         fillHalfRow(getOne(), vectorExRDot, jacobian[1], 3);

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

  781.         // dHx
  782.         final T h = (hx2.add(1).add(hy2)).divide(sqrtMuA.multiply(2).multiply(epsilon));
  783.         fillHalfRow( h.negate().multiply(xDot), w, jacobian[3], 0);
  784.         fillHalfRow( h.multiply(x),    w, jacobian[3], 3);

  785.        // dHy
  786.         fillHalfRow( h.negate().multiply(yDot), w, jacobian[4], 0);
  787.         fillHalfRow( h.multiply(y),    w, jacobian[4], 3);

  788.         // dLambdaM
  789.         final T l = ratio.negate().divide(sqrtMuA);
  790.         fillHalfRow(getOne().negate().divide(sqrtMuA), velocity, d2, w, l.multiply(ex), drDotSdEx, l.multiply(ey), drDotSdEy, jacobian[5], 0);
  791.         fillHalfRow(getZero().add(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3);

  792.         return jacobian;

  793.     }

  794.     /** {@inheritDoc} */
  795.     protected T[][] computeJacobianEccentricWrtCartesian() {

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

  798.         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
  799.         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
  800.         // which is inverted and rewritten as:
  801.         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
  802.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  803.         final T cosLe = scLe.cos();
  804.         final T sinLe = scLe.sin();
  805.         final T aOr   = getOne().divide(getOne().subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe)));

  806.         // update longitude row
  807.         final T[] rowEx = jacobian[1];
  808.         final T[] rowEy = jacobian[2];
  809.         final T[] rowL  = jacobian[5];

  810.         for (int j = 0; j < 6; ++j) {
  811.             rowL[j] = aOr.multiply(rowL[j].add(sinLe.multiply(rowEx[j])).subtract(cosLe.multiply(rowEy[j])));
  812.         }
  813.         return jacobian;

  814.     }

  815.     /** {@inheritDoc} */
  816.     protected T[][] computeJacobianTrueWrtCartesian() {

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

  819.         // Differentiating the eccentric longitude equation
  820.         // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
  821.         // leads to
  822.         // cT (dlV - dlE) = cE dlE + cX dex + cY dey
  823.         // with
  824.         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
  825.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
  826.         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  827.         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  828.         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  829.         // which can be solved to find the differential of the true longitude
  830.         // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
  831.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  832.         final T cosLe     = scLe.cos();
  833.         final T sinLe     = scLe.sin();
  834.         final T eSinE     = ex.multiply(sinLe).subtract(ey.multiply(cosLe));
  835.         final T ecosE     = ex.multiply(cosLe).add(ey.multiply(sinLe));
  836.         final T e2        = ex.multiply(ex).add(ey.multiply(ey));
  837.         final T epsilon   = getOne().subtract(e2).sqrt();
  838.         final T onePeps   = epsilon.add(1);
  839.         final T d         = onePeps.subtract(ecosE);
  840.         final T cT        = d.multiply(d).add(eSinE.multiply(eSinE)).divide(2);
  841.         final T cE        = ecosE.multiply(onePeps).subtract(e2);
  842.         final T cX        = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinLe.multiply(onePeps));
  843.         final T cY        = ey.multiply(eSinE).divide(epsilon).add( ex).subtract(cosLe.multiply(onePeps));
  844.         final T factorLe  = cT.add(cE).divide(cT);
  845.         final T factorEx  = cX.divide(cT);
  846.         final T factorEy  = cY.divide(cT);

  847.         // update longitude row
  848.         final T[] rowEx = jacobian[1];
  849.         final T[] rowEy = jacobian[2];
  850.         final T[] rowL = jacobian[5];
  851.         for (int j = 0; j < 6; ++j) {
  852.             rowL[j] = factorLe.multiply(rowL[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  853.         }

  854.         return jacobian;

  855.     }

  856.     /** {@inheritDoc} */
  857.     public void addKeplerContribution(final PositionAngleType type, final T gm,
  858.                                       final T[] pDot) {
  859.         final T oMe2;
  860.         final T ksi;
  861.         final T n               = gm.divide(a).sqrt().divide(a);
  862.         final FieldSinCos<T> sc = FastMath.sinCos(lv);
  863.         switch (type) {
  864.             case MEAN :
  865.                 pDot[5] = pDot[5].add(n);
  866.                 break;
  867.             case ECCENTRIC :
  868.                 oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  869.                 ksi  = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin()));
  870.                 pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2));
  871.                 break;
  872.             case TRUE :
  873.                 oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  874.                 ksi  =  ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin()));
  875.                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
  876.                 break;
  877.             default :
  878.                 throw new OrekitInternalError(null);
  879.         }
  880.     }

  881.     /**  Returns a string representation of this equinoctial parameters object.
  882.      * @return a string representation of this object
  883.      */
  884.     public String toString() {
  885.         return new StringBuilder().append("equinoctial parameters: ").append('{').
  886.                                   append("a: ").append(a.getReal()).
  887.                                   append("; ex: ").append(ex.getReal()).append("; ey: ").append(ey.getReal()).
  888.                                   append("; hx: ").append(hx.getReal()).append("; hy: ").append(hy.getReal()).
  889.                                   append("; lv: ").append(FastMath.toDegrees(lv.getReal())).
  890.                                   append(";}").toString();
  891.     }

  892.     /** {@inheritDoc} */
  893.     @Override
  894.     public PositionAngleType getCachedPositionAngleType() {
  895.         return PositionAngleType.TRUE;
  896.     }

  897.     /** {@inheritDoc} */
  898.     @Override
  899.     public boolean hasRates() {
  900.         return hasDerivatives();
  901.     }

  902.     /** {@inheritDoc} */
  903.     @Override
  904.     public FieldEquinoctialOrbit<T> removeRates() {
  905.         final PositionAngleType positionAngleType = getCachedPositionAngleType();
  906.         return new FieldEquinoctialOrbit<>(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(),
  907.                 getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
  908.     }

  909.     /** {@inheritDoc} */
  910.     @Override
  911.     public EquinoctialOrbit toOrbit() {
  912.         if (hasDerivatives()) {
  913.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  914.                                         hx.getReal(), hy.getReal(), lv.getReal(),
  915.                                         aDot.getReal(), exDot.getReal(), eyDot.getReal(),
  916.                                         hxDot.getReal(), hyDot.getReal(), lvDot.getReal(),
  917.                                         PositionAngleType.TRUE, getFrame(),
  918.                                         getDate().toAbsoluteDate(), getMu().getReal());
  919.         } else {
  920.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  921.                                         hx.getReal(), hy.getReal(), lv.getReal(),
  922.                                         PositionAngleType.TRUE, getFrame(),
  923.                                         getDate().toAbsoluteDate(), getMu().getReal());
  924.         }
  925.     }

  926. }