FieldEquinoctialOrbit.java

  1. /* Copyright 2002-2022 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 java.util.List;
  19. import java.util.stream.Collectors;
  20. import java.util.stream.Stream;

  21. import org.hipparchus.Field;
  22. import org.hipparchus.CalculusFieldElement;
  23. import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
  24. import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
  25. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.FieldSinCos;
  28. import org.hipparchus.util.MathArrays;
  29. import org.hipparchus.util.MathUtils;
  30. import org.orekit.errors.OrekitIllegalArgumentException;
  31. import org.orekit.errors.OrekitInternalError;
  32. import org.orekit.errors.OrekitMessages;
  33. import org.orekit.frames.Frame;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.utils.FieldPVCoordinates;
  36. import org.orekit.utils.TimeStampedFieldPVCoordinates;


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

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

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

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

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

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

  86.     /** True longitude argument (rad). */
  87.     private final T lv;

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

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

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

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

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

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

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

  102.     /** Field used by this class.*/
  103.     private Field<T> field;

  104.     /**Zero.*/
  105.     private T zero;

  106.     /**One.*/
  107.     private T one;

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

  132.     /** Creates a new instance.
  133.      * @param a  semi-major axis (m)
  134.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  135.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  136.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  137.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  138.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  139.      * @param aDot  semi-major axis derivative (m/s)
  140.      * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative
  141.      * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative
  142.      * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative
  143.      * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative
  144.      * @param lDot  d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument  derivative (rad/s)
  145.      * @param type type of longitude argument
  146.      * @param frame the frame in which the parameters are defined
  147.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  148.      * @param date date of the orbital parameters
  149.      * @param mu central attraction coefficient (m³/s²)
  150.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  151.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  152.      */
  153.     public FieldEquinoctialOrbit(final T a, final T ex, final T ey,
  154.                                  final T hx, final T hy, final T l,
  155.                                  final T aDot, final T exDot, final T eyDot,
  156.                                  final T hxDot, final T hyDot, final T lDot,
  157.                                  final PositionAngle type,
  158.                                  final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  159.         throws IllegalArgumentException {
  160.         super(frame, date, mu);
  161.         field = a.getField();
  162.         zero = field.getZero();
  163.         one = field.getOne();
  164.         if (ex.getReal() * ex.getReal() + ey.getReal() * ey.getReal() >= 1.0) {
  165.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  166.                                                      getClass().getName());
  167.         }

  168.         this.a     = a;
  169.         this.aDot  = aDot;
  170.         this.ex    = ex;
  171.         this.exDot = exDot;
  172.         this.ey    = ey;
  173.         this.eyDot = eyDot;
  174.         this.hx    = hx;
  175.         this.hxDot = hxDot;
  176.         this.hy    = hy;
  177.         this.hyDot = hyDot;

  178.         if (hasDerivatives()) {
  179.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  180.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  181.             final FieldUnivariateDerivative1<T> lUD  = new FieldUnivariateDerivative1<>(l,  lDot);
  182.             final FieldUnivariateDerivative1<T> lvUD;
  183.             switch (type) {
  184.                 case MEAN :
  185.                     lvUD = eccentricToTrue(meanToEccentric(lUD, exUD, eyUD), exUD, eyUD);
  186.                     break;
  187.                 case ECCENTRIC :
  188.                     lvUD = eccentricToTrue(lUD, exUD, eyUD);
  189.                     break;
  190.                 case TRUE :
  191.                     lvUD = lUD;
  192.                     break;
  193.                 default : // this should never happen
  194.                     throw new OrekitInternalError(null);
  195.             }
  196.             this.lv    = lvUD.getValue();
  197.             this.lvDot = lvUD.getDerivative(1);
  198.         } else {
  199.             switch (type) {
  200.                 case MEAN :
  201.                     this.lv = eccentricToTrue(meanToEccentric(l, ex, ey), ex, ey);
  202.                     break;
  203.                 case ECCENTRIC :
  204.                     this.lv = eccentricToTrue(l, ex, ey);
  205.                     break;
  206.                 case TRUE :
  207.                     this.lv = l;
  208.                     break;
  209.                 default :
  210.                     throw new OrekitInternalError(null);
  211.             }
  212.             this.lvDot = null;
  213.         }

  214.         this.partialPV = null;

  215.     }

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

  234.         field = pvCoordinates.getPosition().getX().getField();
  235.         zero = field.getZero();
  236.         one = field.getOne();

  237.         //  compute semi-major axis
  238.         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
  239.         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
  240.         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
  241.         final T r2 = pvP.getNormSq();
  242.         final T r  = r2.sqrt();
  243.         final T V2 = pvV.getNormSq();
  244.         final T rV2OnMu = r.multiply(V2).divide(mu);

  245.         if (rV2OnMu.getReal() > 2) {
  246.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  247.                                                      getClass().getName());
  248.         }

  249.         // compute inclination vector
  250.         final FieldVector3D<T> w = pvCoordinates.getMomentum().normalize();
  251.         final T d = one.divide(one.add(w.getZ()));
  252.         hx =  d.negate().multiply(w.getY());
  253.         hy =  d.multiply(w.getX());

  254.         // compute true longitude argument
  255.         final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r);
  256.         final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r);
  257.         lv = sLv.atan2(cLv);


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

  260.         // compute eccentricity vector
  261.         final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
  262.         final T eCE = rV2OnMu.subtract(1);
  263.         final T e2  = eCE.multiply(eCE).add(eSE.multiply(eSE));
  264.         final T f   = eCE.subtract(e2);
  265.         final T g   = e2.negate().add(1).sqrt().multiply(eSE);
  266.         ex = a.multiply(f.multiply(cLv).add( g.multiply(sLv))).divide(r);
  267.         ey = a.multiply(f.multiply(sLv).subtract(g.multiply(cLv))).divide(r);

  268.         partialPV = pvCoordinates;

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

  271.             final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6);
  272.             getJacobianWrtCartesian(PositionAngle.MEAN, jacobian);

  273.             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP);
  274.             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  275.             final T   aX                       = nonKeplerianAcceleration.getX();
  276.             final T   aY                       = nonKeplerianAcceleration.getY();
  277.             final T   aZ                       = nonKeplerianAcceleration.getZ();
  278.             aDot  = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
  279.             exDot = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
  280.             eyDot = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
  281.             hxDot = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
  282.             hyDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));

  283.             // in order to compute true anomaly derivative, we must compute
  284.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  285.             final T lMDot = getKeplerianMeanMotion().
  286.                             add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  287.             final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  288.             final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  289.             final FieldUnivariateDerivative1<T> lMUD = new FieldUnivariateDerivative1<>(getLM(), lMDot);
  290.             final FieldUnivariateDerivative1<T> lvUD = eccentricToTrue(meanToEccentric(lMUD, exUD, eyUD), exUD, eyUD);
  291.             lvDot = lvUD.getDerivative(1);

  292.         } else {
  293.             // acceleration is either almost zero or NaN,
  294.             // we assume acceleration was not known
  295.             // we don't set up derivatives
  296.             aDot  = null;
  297.             exDot = null;
  298.             eyDot = null;
  299.             hxDot = null;
  300.             hyDot = null;
  301.             lvDot = null;
  302.         }

  303.     }

  304.     /** Constructor from Cartesian parameters.
  305.      *
  306.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  307.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  308.      * use {@code mu} and the position to compute the acceleration, including
  309.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  310.      *
  311.      * @param pvCoordinates the position end velocity
  312.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  313.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  314.      * @param date date of the orbital parameters
  315.      * @param mu central attraction coefficient (m³/s²)
  316.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  317.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  318.      */
  319.     public FieldEquinoctialOrbit(final FieldPVCoordinates<T> pvCoordinates, final Frame frame,
  320.                             final FieldAbsoluteDate<T> date, final T mu)
  321.         throws IllegalArgumentException {
  322.         this(new TimeStampedFieldPVCoordinates<>(date, pvCoordinates), frame, mu);
  323.     }

  324.     /** Constructor from any kind of orbital parameters.
  325.      * @param op orbital parameters to copy
  326.      */
  327.     public FieldEquinoctialOrbit(final FieldOrbit<T> op) {
  328.         super(op.getFrame(), op.getDate(), op.getMu());

  329.         a     = op.getA();
  330.         ex    = op.getEquinoctialEx();
  331.         ey    = op.getEquinoctialEy();
  332.         hx    = op.getHx();
  333.         hy    = op.getHy();
  334.         lv    = op.getLv();

  335.         aDot  = op.getADot();
  336.         exDot = op.getEquinoctialExDot();
  337.         eyDot = op.getEquinoctialEyDot();
  338.         hxDot = op.getHxDot();
  339.         hyDot = op.getHyDot();
  340.         lvDot = op.getLvDot();

  341.         field = a.getField();
  342.         zero  = field.getZero();
  343.         one   = field.getOne();
  344.     }

  345.     /** {@inheritDoc} */
  346.     public OrbitType getType() {
  347.         return OrbitType.EQUINOCTIAL;
  348.     }

  349.     /** {@inheritDoc} */
  350.     public T getA() {
  351.         return a;
  352.     }

  353.     /** {@inheritDoc} */
  354.     public T getADot() {
  355.         return aDot;
  356.     }

  357.     /** {@inheritDoc} */
  358.     public T getEquinoctialEx() {
  359.         return ex;
  360.     }

  361.     /** {@inheritDoc} */
  362.     public T getEquinoctialExDot() {
  363.         return exDot;
  364.     }

  365.     /** {@inheritDoc} */
  366.     public T getEquinoctialEy() {
  367.         return ey;
  368.     }

  369.     /** {@inheritDoc} */
  370.     public T getEquinoctialEyDot() {
  371.         return eyDot;
  372.     }

  373.     /** {@inheritDoc} */
  374.     public T getHx() {
  375.         return hx;
  376.     }

  377.     /** {@inheritDoc} */
  378.     public T getHxDot() {
  379.         return hxDot;
  380.     }

  381.     /** {@inheritDoc} */
  382.     public T getHy() {
  383.         return hy;
  384.     }

  385.     /** {@inheritDoc} */
  386.     public T getHyDot() {
  387.         return hyDot;
  388.     }

  389.     /** {@inheritDoc} */
  390.     public T getLv() {
  391.         return lv;
  392.     }

  393.     /** {@inheritDoc} */
  394.     public T getLvDot() {
  395.         return lvDot;
  396.     }

  397.     /** {@inheritDoc} */
  398.     public T getLE() {
  399.         return trueToEccentric(lv, ex, ey);
  400.     }

  401.     /** {@inheritDoc} */
  402.     public T getLEDot() {

  403.         if (!hasDerivatives()) {
  404.             return null;
  405.         }

  406.         final FieldUnivariateDerivative1<T> lVUD = new FieldUnivariateDerivative1<>(lv, lvDot);
  407.         final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  408.         final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  409.         final FieldUnivariateDerivative1<T> lEUD = trueToEccentric(lVUD, exUD, eyUD);
  410.         return lEUD.getDerivative(1);

  411.     }

  412.     /** {@inheritDoc} */
  413.     public T getLM() {
  414.         return eccentricToMean(trueToEccentric(lv, ex, ey), ex, ey);
  415.     }

  416.     /** {@inheritDoc} */
  417.     public T getLMDot() {

  418.         if (!hasDerivatives()) {
  419.             return null;
  420.         }

  421.         final FieldUnivariateDerivative1<T> lVUD = new FieldUnivariateDerivative1<>(lv, lvDot);
  422.         final FieldUnivariateDerivative1<T> exUD = new FieldUnivariateDerivative1<>(ex, exDot);
  423.         final FieldUnivariateDerivative1<T> eyUD = new FieldUnivariateDerivative1<>(ey, eyDot);
  424.         final FieldUnivariateDerivative1<T> lMUD = eccentricToMean(trueToEccentric(lVUD, exUD, eyUD), exUD, eyUD);
  425.         return lMUD.getDerivative(1);

  426.     }

  427.     /** Get the longitude argument.
  428.      * @param type type of the angle
  429.      * @return longitude argument (rad)
  430.      */
  431.     public T getL(final PositionAngle type) {
  432.         return (type == PositionAngle.MEAN) ? getLM() :
  433.                                               ((type == PositionAngle.ECCENTRIC) ? getLE() :
  434.                                                                                    getLv());
  435.     }

  436.     /** Get the longitude argument derivative.
  437.      * @param type type of the angle
  438.      * @return longitude argument derivative (rad/s)
  439.      */
  440.     public T getLDot(final PositionAngle type) {
  441.         return (type == PositionAngle.MEAN) ? getLMDot() :
  442.                                               ((type == PositionAngle.ECCENTRIC) ? getLEDot() :
  443.                                                                                    getLvDot());
  444.     }

  445.     /** {@inheritDoc} */
  446.     @Override
  447.     public boolean hasDerivatives() {
  448.         return aDot != null;
  449.     }

  450.     /** Computes the true longitude argument from the eccentric longitude argument.
  451.      * @param lE = E + ω + Ω eccentric longitude argument (rad)
  452.      * @param ex first component of the eccentricity vector
  453.      * @param ey second component of the eccentricity vector
  454.      * @param <T> Type of the field elements
  455.      * @return the true longitude argument
  456.      */
  457.     public static <T extends CalculusFieldElement<T>> T eccentricToTrue(final T lE, final T ex, final T ey) {
  458.         final T epsilon           = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt();
  459.         final FieldSinCos<T> scLE = FastMath.sinCos(lE);
  460.         final T cosLE             = scLE.cos();
  461.         final T sinLE             = scLE.sin();
  462.         final T num               = ex.multiply(sinLE).subtract(ey.multiply(cosLE));
  463.         final T den               = epsilon.add(1).subtract(ex.multiply(cosLE)).subtract(ey.multiply(sinLE));
  464.         return lE.add(num.divide(den).atan().multiply(2));
  465.     }

  466.     /** Computes the eccentric longitude argument from the true longitude argument.
  467.      * @param lv = v + ω + Ω true longitude argument (rad)
  468.      * @param ex first component of the eccentricity vector
  469.      * @param ey second component of the eccentricity vector
  470.      * @param <T> Type of the field elements
  471.      * @return the eccentric longitude argument
  472.      */
  473.     public static <T extends CalculusFieldElement<T>> T trueToEccentric(final T lv, final T ex, final T ey) {
  474.         final T epsilon           = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt();
  475.         final FieldSinCos<T> scLv = FastMath.sinCos(lv);
  476.         final T cosLv             = scLv.cos();
  477.         final T sinLv             = scLv.sin();
  478.         final T num               = ey.multiply(cosLv).subtract(ex.multiply(sinLv));
  479.         final T den               = epsilon.add(1).add(ex.multiply(cosLv)).add(ey.multiply(sinLv));
  480.         return lv.add(num.divide(den).atan().multiply(2));
  481.     }

  482.     /** Computes the eccentric longitude argument from the mean longitude argument.
  483.      * @param lM = M + ω + Ω mean longitude argument (rad)
  484.      * @param ex first component of the eccentricity vector
  485.      * @param ey second component of the eccentricity vector
  486.      * @param <T> Type of the field elements
  487.      * @return the eccentric longitude argument
  488.      */
  489.     public static <T extends CalculusFieldElement<T>> T meanToEccentric(final T lM, final T ex, final T ey) {
  490.         // Generalization of Kepler equation to equinoctial parameters
  491.         // with lE = PA + RAAN + E and
  492.         //      lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE)
  493.         T lE = lM;
  494.         T shift = lM.getField().getZero();
  495.         T lEmlM = lM.getField().getZero();
  496.         FieldSinCos<T> scLE = FastMath.sinCos(lE);
  497.         int iter = 0;
  498.         do {
  499.             final T f2 = ex.multiply(scLE.sin()).subtract(ey.multiply(scLE.cos()));
  500.             final T f1 = ex.multiply(scLE.cos()).add(ey.multiply(scLE.sin())).negate().add(1);
  501.             final T f0 = lEmlM.subtract(f2);

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

  504.             lEmlM = lEmlM.subtract(shift);
  505.             lE     = lM.add(lEmlM);
  506.             scLE = FastMath.sinCos(lE);

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

  508.         return lE;

  509.     }

  510.     /** Computes the mean longitude argument from the eccentric longitude argument.
  511.      * @param lE = E + ω + Ω mean longitude argument (rad)
  512.      * @param ex first component of the eccentricity vector
  513.      * @param ey second component of the eccentricity vector
  514.      * @param <T> Type of the field elements
  515.      * @return the mean longitude argument
  516.      */
  517.     public static <T extends CalculusFieldElement<T>> T eccentricToMean(final T lE, final T ex, final T ey) {
  518.         final FieldSinCos<T> scLE = FastMath.sinCos(lE);
  519.         return lE.subtract(ex.multiply(scLE.sin())).add(ey.multiply(scLE.cos()));
  520.     }

  521.     /** {@inheritDoc} */
  522.     public T getE() {
  523.         return ex.multiply(ex).add(ey.multiply(ey)).sqrt();
  524.     }

  525.     /** {@inheritDoc} */
  526.     public T getEDot() {

  527.         if (!hasDerivatives()) {
  528.             return null;
  529.         }

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

  531.     }

  532.     /** {@inheritDoc} */
  533.     public T getI() {
  534.         return hx.multiply(hx).add(hy.multiply(hy)).sqrt().atan().multiply(2);
  535.     }

  536.     /** {@inheritDoc} */
  537.     public T getIDot() {

  538.         if (!hasDerivatives()) {
  539.             return null;
  540.         }

  541.         final T h2 = hx.multiply(hx).add(hy.multiply(hy));
  542.         final T h  = h2.sqrt();
  543.         return hx.multiply(hxDot).add(hy.multiply(hyDot)).multiply(2).divide(h.multiply(h2.add(1)));

  544.     }

  545.     /** Compute position and velocity but not acceleration.
  546.      */
  547.     private void computePVWithoutA() {

  548.         if (partialPV != null) {
  549.             // already computed
  550.             return;
  551.         }

  552.         // get equinoctial parameters
  553.         final T lE = getLE();

  554.         // inclination-related intermediate parameters
  555.         final T hx2   = hx.multiply(hx);
  556.         final T hy2   = hy.multiply(hy);
  557.         final T factH = one.divide(hx2.add(1.0).add(hy2));

  558.         // reference axes defining the orbital plane
  559.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  560.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  561.         final T uz = hy.multiply(-2).multiply(factH);

  562.         final T vx = uy;
  563.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  564.         final T vz =  hx.multiply(factH).multiply(2);

  565.         // eccentricity-related intermediate parameters
  566.         final T ex2  = ex.multiply(ex);
  567.         final T exey = ex.multiply(ey);
  568.         final T ey2  = ey.multiply(ey);
  569.         final T e2   = ex2.add(ey2);
  570.         final T eta  = one.subtract(e2).sqrt().add(1);
  571.         final T beta = one.divide(eta);

  572.         // eccentric longitude argument
  573.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  574.         final T cLe    = scLe.cos();
  575.         final T sLe    = scLe.sin();
  576.         final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe));

  577.         // coordinates of position and velocity in the orbital plane
  578.         final T x      = a.multiply(one.subtract(beta.multiply(ey2)).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(ex));
  579.         final T y      = a.multiply(one.subtract(beta.multiply(ex2)).multiply(sLe).add(beta .multiply(exey).multiply(cLe)).subtract(ey));

  580.         final T factor = getMu().divide(a).sqrt().divide(one.subtract(exCeyS));
  581.         final T xdot   = factor.multiply(sLe.negate().add(beta.multiply(ey).multiply(exCeyS)));
  582.         final T ydot   = factor.multiply(cLe.subtract(beta.multiply(ex).multiply(exCeyS)));

  583.         final FieldVector3D<T> position =
  584.                         new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  585.                                             x.multiply(uy).add(y.multiply(vy)),
  586.                                             x.multiply(uz).add(y.multiply(vz)));
  587.         final FieldVector3D<T> velocity =
  588.                         new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)), xdot.multiply(uy).add(ydot.multiply(vy)), xdot.multiply(uz).add(ydot.multiply(vz)));

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

  590.     }

  591.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  592.      * <p>
  593.      * This method should be called only when {@link #hasDerivatives()} returns true.
  594.      * </p>
  595.      * @return non-Keplerian part of the acceleration
  596.      */
  597.     private FieldVector3D<T> nonKeplerianAcceleration() {

  598.         final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6);
  599.         getJacobianWrtParameters(PositionAngle.MEAN, dCdP);

  600.         final T nonKeplerianMeanMotion = getLMDot().subtract(getKeplerianMeanMotion());
  601.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  602.                                  add(dCdP[3][1].multiply(exDot)).
  603.                                  add(dCdP[3][2].multiply(eyDot)).
  604.                                  add(dCdP[3][3].multiply(hxDot)).
  605.                                  add(dCdP[3][4].multiply(hyDot)).
  606.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  607.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  608.                                  add(dCdP[4][1].multiply(exDot)).
  609.                                  add(dCdP[4][2].multiply(eyDot)).
  610.                                  add(dCdP[4][3].multiply(hxDot)).
  611.                                  add(dCdP[4][4].multiply(hyDot)).
  612.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  613.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  614.                                  add(dCdP[5][1].multiply(exDot)).
  615.                                  add(dCdP[5][2].multiply(eyDot)).
  616.                                  add(dCdP[5][3].multiply(hxDot)).
  617.                                  add(dCdP[5][4].multiply(hyDot)).
  618.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

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

  620.     }

  621.     /** {@inheritDoc} */
  622.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  623.         // position and velocity
  624.         computePVWithoutA();

  625.         // acceleration
  626.         final T r2 = partialPV.getPosition().getNormSq();
  627.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(getMu().negate()),
  628.                                                                            partialPV.getPosition());
  629.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  630.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  631.                                               keplerianAcceleration;

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

  633.     }

  634.     /** {@inheritDoc} */
  635.     public FieldEquinoctialOrbit<T> shiftedBy(final double dt) {
  636.         return shiftedBy(getDate().getField().getZero().add(dt));
  637.     }

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

  640.         // use Keplerian-only motion
  641.         final FieldEquinoctialOrbit<T> keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy,
  642.                                                                                       getLM().add(getKeplerianMeanMotion().multiply(dt)),
  643.                                                                                       PositionAngle.MEAN, getFrame(),
  644.                                                                                       getDate().shiftedBy(dt), getMu());

  645.         if (hasDerivatives()) {

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

  648.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  649.             keplerianShifted.computePVWithoutA();
  650.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(),
  651.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  652.             final T   fixedR2 = fixedP.getNormSq();
  653.             final T   fixedR  = fixedR2.sqrt();
  654.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(),
  655.                                                                  dt, nonKeplerianAcceleration);
  656.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
  657.                                                                  keplerianShifted.partialPV.getPosition(),
  658.                                                                  one, nonKeplerianAcceleration);

  659.             // build a new orbit, taking non-Keplerian acceleration into account
  660.             return new FieldEquinoctialOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  661.                                                                                    fixedP, fixedV, fixedA),
  662.                                                keplerianShifted.getFrame(), keplerianShifted.getMu());

  663.         } else {
  664.             // Keplerian-only motion is all we can do
  665.             return keplerianShifted;
  666.         }

  667.     }

  668.     /** {@inheritDoc}
  669.      * <p>
  670.      * The interpolated instance is created by polynomial Hermite interpolation
  671.      * on equinoctial elements, without derivatives (which means the interpolation
  672.      * falls back to Lagrange interpolation only).
  673.      * </p>
  674.      * <p>
  675.      * As this implementation of interpolation is polynomial, it should be used only
  676.      * with small samples (about 10-20 points) in order to avoid <a
  677.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  678.      * and numerical problems (including NaN appearing).
  679.      * </p>
  680.      * <p>
  681.      * If orbit interpolation on large samples is needed, using the {@link
  682.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  683.      * low-level interpolation. The Ephemeris class automatically handles selection of
  684.      * a neighboring sub-sample with a predefined number of point from a large global sample
  685.      * in a thread-safe way.
  686.      * </p>
  687.      */
  688.     public FieldEquinoctialOrbit<T> interpolate(final FieldAbsoluteDate<T> date, final Stream<FieldOrbit<T>> sample) {

  689.         // first pass to check if derivatives are available throughout the sample
  690.         final List<FieldOrbit<T>> list = sample.collect(Collectors.toList());
  691.         boolean useDerivatives = true;
  692.         for (final FieldOrbit<T> orbit : list) {
  693.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  694.         }

  695.         // set up an interpolator
  696.         final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();

  697.         // second pass to feed interpolator
  698.         FieldAbsoluteDate<T> previousDate = null;
  699.         T                    previousLm   = zero.add(Double.NaN);
  700.         for (final FieldOrbit<T> orbit : list) {
  701.             final FieldEquinoctialOrbit<T> equi = (FieldEquinoctialOrbit<T>) OrbitType.EQUINOCTIAL.convertType(orbit);
  702.             final T continuousLm;
  703.             if (previousDate == null) {
  704.                 continuousLm = (T) equi.getLM();
  705.             } else {
  706.                 final T dt       = (T) equi.getDate().durationFrom(previousDate);
  707.                 final T keplerLm = previousLm.add((T) equi.getKeplerianMeanMotion().multiply(dt));
  708.                 continuousLm = MathUtils.normalizeAngle((T) equi.getLM(), keplerLm);
  709.             }
  710.             previousDate = equi.getDate();
  711.             previousLm   = continuousLm;
  712.             final T[] toAdd = MathArrays.buildArray(field, 6);
  713.             toAdd[0] = (T) equi.getA();
  714.             toAdd[1] = (T) equi.getEquinoctialEx();
  715.             toAdd[2] = (T) equi.getEquinoctialEy();
  716.             toAdd[3] = (T) equi.getHx();
  717.             toAdd[4] = (T) equi.getHy();
  718.             toAdd[5] = (T) continuousLm;
  719.             if (useDerivatives) {
  720.                 final T[] toAddDot = MathArrays.buildArray(one.getField(), 6);
  721.                 toAddDot[0] = equi.getADot();
  722.                 toAddDot[1] = equi.getEquinoctialExDot();
  723.                 toAddDot[2] = equi.getEquinoctialEyDot();
  724.                 toAddDot[3] = equi.getHxDot();
  725.                 toAddDot[4] = equi.getHyDot();
  726.                 toAddDot[5] = equi.getLMDot();
  727.                 interpolator.addSamplePoint(equi.getDate().durationFrom(date),
  728.                                             toAdd, toAddDot);
  729.             } else {
  730.                 interpolator.addSamplePoint((T) equi.getDate().durationFrom(date),
  731.                                             toAdd);
  732.             }
  733.         }

  734.         // interpolate
  735.         final T[][] interpolated = interpolator.derivatives(zero, 1);

  736.         // build a new interpolated instance
  737.         return new FieldEquinoctialOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  738.                                            interpolated[0][3], interpolated[0][4], interpolated[0][5],
  739.                                            interpolated[1][0], interpolated[1][1], interpolated[1][2],
  740.                                            interpolated[1][3], interpolated[1][4], interpolated[1][5],
  741.                                            PositionAngle.MEAN, getFrame(), date, getMu());

  742.     }

  743.     /** {@inheritDoc} */
  744.     protected T[][] computeJacobianMeanWrtCartesian() {

  745.         final T[][] jacobian = MathArrays.buildArray(field, 6, 6);

  746.         // compute various intermediate parameters
  747.         computePVWithoutA();
  748.         final FieldVector3D<T> position = partialPV.getPosition();
  749.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  750.         final T r2         = position.getNormSq();
  751.         final T r          = r2.sqrt();
  752.         final T r3         = r.multiply(r2);

  753.         final T mu         = getMu();
  754.         final T sqrtMuA    = a.multiply(mu).sqrt();
  755.         final T a2         = a.multiply(a);

  756.         final T e2         = ex.multiply(ex).add(ey.multiply(ey));
  757.         final T oMe2       = one.subtract(e2);
  758.         final T epsilon    = oMe2.sqrt();
  759.         final T beta       = one.divide(epsilon.add(1));
  760.         final T ratio      = epsilon.multiply(beta);

  761.         final T hx2        = hx.multiply(hx);
  762.         final T hy2        = hy.multiply(hy);
  763.         final T hxhy       = hx.multiply(hy);

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

  768.         // coordinates of the spacecraft in the equinoctial frame
  769.         final T x    = FieldVector3D.dotProduct(position, f);
  770.         final T y    = FieldVector3D.dotProduct(position, g);
  771.         final T xDot = FieldVector3D.dotProduct(velocity, f);
  772.         final T yDot = FieldVector3D.dotProduct(velocity, g);

  773.         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
  774.         final T c1  = a.divide(sqrtMuA.multiply(epsilon));
  775.         final T c1N = c1.negate();
  776.         final T c2  = a.multiply(sqrtMuA).multiply(beta).divide(r3);
  777.         final T c3  = sqrtMuA.divide(r3.multiply(epsilon));
  778.         final FieldVector3D<T> drDotSdEx = new FieldVector3D<>(c1.multiply(xDot).multiply(yDot).subtract(c2.multiply(ey).multiply(x)).subtract(c3.multiply(x).multiply(y)), f,
  779.                                                                c1N.multiply(xDot).multiply(xDot).subtract(c2.multiply(ey).multiply(y)).add(c3.multiply(x).multiply(x)), g);

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

  783.         // da
  784.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
  785.         final FieldVector3D<T> vectorARDot = new FieldVector3D<>(a2.multiply(2).divide(mu), velocity);
  786.         fillHalfRow(one, vectorAR,    jacobian[0], 0);
  787.         fillHalfRow(one, vectorARDot, jacobian[0], 3);

  788.         // dEx
  789.         final T d1 = a.negate().multiply(ratio).divide(r3);
  790.         final T d2 = (hy.multiply(xDot).subtract(hx.multiply(yDot))).divide(sqrtMuA.multiply(epsilon));
  791.         final T d3 = hx.multiply(y).subtract(hy.multiply(x)).divide(sqrtMuA);
  792.         final FieldVector3D<T> vectorExRDot =
  793.             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);
  794.         fillHalfRow(ex.multiply(d1), position, ey.negate().multiply(d2), w, epsilon.divide(sqrtMuA), drDotSdEy, jacobian[1], 0);
  795.         fillHalfRow(one, vectorExRDot, jacobian[1], 3);

  796.         // dEy
  797.         final FieldVector3D<T> vectorEyRDot =
  798.             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);
  799.         fillHalfRow(ey.multiply(d1), position, ex.multiply(d2), w, epsilon.negate().divide(sqrtMuA), drDotSdEx, jacobian[2], 0);
  800.         fillHalfRow(one, vectorEyRDot, jacobian[2], 3);

  801.         // dHx
  802.         final T h = (hx2.add(1).add(hy2)).divide(sqrtMuA.multiply(2).multiply(epsilon));
  803.         fillHalfRow( h.negate().multiply(xDot), w, jacobian[3], 0);
  804.         fillHalfRow( h.multiply(x),    w, jacobian[3], 3);

  805.        // dHy
  806.         fillHalfRow( h.negate().multiply(yDot), w, jacobian[4], 0);
  807.         fillHalfRow( h.multiply(y),    w, jacobian[4], 3);

  808.         // dLambdaM
  809.         final T l = ratio.negate().divide(sqrtMuA);
  810.         fillHalfRow(one.negate().divide(sqrtMuA), velocity, d2, w, l.multiply(ex), drDotSdEx, l.multiply(ey), drDotSdEy, jacobian[5], 0);
  811.         fillHalfRow(zero.add(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3);

  812.         return jacobian;

  813.     }

  814.     /** {@inheritDoc} */
  815.     protected T[][] computeJacobianEccentricWrtCartesian() {

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

  818.         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
  819.         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
  820.         // which is inverted and rewritten as:
  821.         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
  822.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  823.         final T cosLe = scLe.cos();
  824.         final T sinLe = scLe.sin();
  825.         final T aOr   = one.divide(one.subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe)));

  826.         // update longitude row
  827.         final T[] rowEx = jacobian[1];
  828.         final T[] rowEy = jacobian[2];
  829.         final T[] rowL  = jacobian[5];

  830.         for (int j = 0; j < 6; ++j) {
  831.             rowL[j] = aOr.multiply(rowL[j].add(sinLe.multiply(rowEx[j])).subtract(cosLe.multiply(rowEy[j])));
  832.         }
  833.         return jacobian;

  834.     }

  835.     /** {@inheritDoc} */
  836.     protected T[][] computeJacobianTrueWrtCartesian() {

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

  839.         // Differentiating the eccentric longitude equation
  840.         // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
  841.         // leads to
  842.         // cT (dlV - dlE) = cE dlE + cX dex + cY dey
  843.         // with
  844.         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
  845.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
  846.         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  847.         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  848.         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  849.         // which can be solved to find the differential of the true longitude
  850.         // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
  851.         final FieldSinCos<T> scLe = FastMath.sinCos(getLE());
  852.         final T cosLe     = scLe.cos();
  853.         final T sinLe     = scLe.sin();
  854.         final T eSinE     = ex.multiply(sinLe).subtract(ey.multiply(cosLe));
  855.         final T ecosE     = ex.multiply(cosLe).add(ey.multiply(sinLe));
  856.         final T e2        = ex.multiply(ex).add(ey.multiply(ey));
  857.         final T epsilon   = one.subtract(e2).sqrt();
  858.         final T onePeps   = epsilon.add(1);
  859.         final T d         = onePeps.subtract(ecosE);
  860.         final T cT        = d.multiply(d).add(eSinE.multiply(eSinE)).divide(2);
  861.         final T cE        = ecosE.multiply(onePeps).subtract(e2);
  862.         final T cX        = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinLe.multiply(onePeps));
  863.         final T cY        = ey.multiply(eSinE).divide(epsilon).add( ex).subtract(cosLe.multiply(onePeps));
  864.         final T factorLe  = cT.add(cE).divide(cT);
  865.         final T factorEx  = cX.divide(cT);
  866.         final T factorEy  = cY.divide(cT);

  867.         // update longitude row
  868.         final T[] rowEx = jacobian[1];
  869.         final T[] rowEy = jacobian[2];
  870.         final T[] rowL = jacobian[5];
  871.         for (int j = 0; j < 6; ++j) {
  872.             rowL[j] = factorLe.multiply(rowL[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  873.         }

  874.         return jacobian;

  875.     }

  876.     /** {@inheritDoc} */
  877.     public void addKeplerContribution(final PositionAngle type, final T gm,
  878.                                       final T[] pDot) {
  879.         final T oMe2;
  880.         final T ksi;
  881.         final T n               = gm.divide(a).sqrt().divide(a);
  882.         final FieldSinCos<T> sc = FastMath.sinCos(lv);
  883.         switch (type) {
  884.             case MEAN :
  885.                 pDot[5] = pDot[5].add(n);
  886.                 break;
  887.             case ECCENTRIC :
  888.                 oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  889.                 ksi  = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin()));
  890.                 pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2));
  891.                 break;
  892.             case TRUE :
  893.                 oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  894.                 ksi  =  ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin()));
  895.                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
  896.                 break;
  897.             default :
  898.                 throw new OrekitInternalError(null);
  899.         }
  900.     }

  901.     /**  Returns a string representation of this equinoctial parameters object.
  902.      * @return a string representation of this object
  903.      */
  904.     public String toString() {
  905.         return new StringBuilder().append("equinoctial parameters: ").append('{').
  906.                                   append("a: ").append(a.getReal()).
  907.                                   append("; ex: ").append(ex.getReal()).append("; ey: ").append(ey.getReal()).
  908.                                   append("; hx: ").append(hx.getReal()).append("; hy: ").append(hy.getReal()).
  909.                                   append("; lv: ").append(FastMath.toDegrees(lv.getReal())).
  910.                                   append(";}").toString();
  911.     }

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

  929. }