FieldEquinoctialOrbit.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.HashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.stream.Collectors;
  22. import java.util.stream.Stream;

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


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

  78.     /** Factory for first time derivatives. */
  79.     private static final Map<Field<? extends RealFieldElement<?>>, FDSFactory<? extends RealFieldElement<?>>> FACTORIES =
  80.                     new HashMap<>();

  81.     /** Semi-major axis (m). */
  82.     private final T a;

  83.     /** First component of the eccentricity vector. */
  84.     private final T ex;

  85.     /** Second component of the eccentricity vector. */
  86.     private final T ey;

  87.     /** First component of the inclination vector. */
  88.     private final T hx;

  89.     /** Second component of the inclination vector. */
  90.     private final T hy;

  91.     /** True longitude argument (rad). */
  92.     private final T lv;

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

  95.     /** First component of the eccentricity vector derivative. */
  96.     private final T exDot;

  97.     /** Second component of the eccentricity vector derivative. */
  98.     private final T eyDot;

  99.     /** First component of the inclination vector derivative. */
  100.     private final T hxDot;

  101.     /** Second component of the inclination vector derivative. */
  102.     private final T hyDot;

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

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

  107.     /** Field used by this class.*/
  108.     private Field<T> field;

  109.     /**Zero.*/
  110.     private T zero;

  111.     /**One.*/
  112.     private T one;

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

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

  173.         if (!FACTORIES.containsKey(a.getField())) {
  174.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  175.         }

  176.         this.a     = a;
  177.         this.aDot  = aDot;
  178.         this.ex    = ex;
  179.         this.exDot = exDot;
  180.         this.ey    = ey;
  181.         this.eyDot = eyDot;
  182.         this.hx    = hx;
  183.         this.hxDot = hxDot;
  184.         this.hy    = hy;
  185.         this.hyDot = hyDot;

  186.         if (hasDerivatives()) {
  187.             @SuppressWarnings("unchecked")
  188.             final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  189.             final FieldDerivativeStructure<T> exDS = factory.build(ex, exDot);
  190.             final FieldDerivativeStructure<T> eyDS = factory.build(ey, eyDot);
  191.             final FieldDerivativeStructure<T> lDS  = factory.build(l,  lDot);
  192.             final FieldDerivativeStructure<T> lvDS;
  193.             switch (type) {
  194.                 case MEAN :
  195.                     lvDS = eccentricToTrue(meanToEccentric(lDS, exDS, eyDS), exDS, eyDS);
  196.                     break;
  197.                 case ECCENTRIC :
  198.                     lvDS = eccentricToTrue(lDS, exDS, eyDS);
  199.                     break;
  200.                 case TRUE :
  201.                     lvDS = lDS;
  202.                     break;
  203.                 default : // this should never happen
  204.                     throw new OrekitInternalError(null);
  205.             }
  206.             this.lv    = lvDS.getValue();
  207.             this.lvDot = lvDS.getPartialDerivative(1);
  208.         } else {
  209.             switch (type) {
  210.                 case MEAN :
  211.                     this.lv = eccentricToTrue(meanToEccentric(l, ex, ey), ex, ey);
  212.                     break;
  213.                 case ECCENTRIC :
  214.                     this.lv = eccentricToTrue(l, ex, ey);
  215.                     break;
  216.                 case TRUE :
  217.                     this.lv = l;
  218.                     break;
  219.                 default :
  220.                     throw new OrekitInternalError(null);
  221.             }
  222.             this.lvDot = null;
  223.         }

  224.         this.partialPV = null;

  225.     }

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

  244.         field = pvCoordinates.getPosition().getX().getField();
  245.         zero = field.getZero();
  246.         one = field.getOne();

  247.         //  compute semi-major axis
  248.         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
  249.         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
  250.         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
  251.         final T r2 = pvP.getNormSq();
  252.         final T r  = r2.sqrt();
  253.         final T V2 = pvV.getNormSq();
  254.         final T rV2OnMu = r.multiply(V2).divide(mu);

  255.         if (rV2OnMu.getReal() > 2) {
  256.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  257.                                                      getClass().getName());
  258.         }

  259.         // compute inclination vector
  260.         final FieldVector3D<T> w = pvCoordinates.getMomentum().normalize();
  261.         final T d = one.divide(one.add(w.getZ()));
  262.         hx =  d.negate().multiply(w.getY());
  263.         hy =  d.multiply(w.getX());

  264.         // compute true longitude argument
  265.         final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r);
  266.         final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r);
  267.         lv = sLv.atan2(cLv);


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

  270.         // compute eccentricity vector
  271.         final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
  272.         final T eCE = rV2OnMu.subtract(1);
  273.         final T e2  = eCE.multiply(eCE).add(eSE.multiply(eSE));
  274.         final T f   = eCE.subtract(e2);
  275.         final T g   = e2.negate().add(1).sqrt().multiply(eSE);
  276.         ex = a.multiply(f.multiply(cLv).add( g.multiply(sLv))).divide(r);
  277.         ey = a.multiply(f.multiply(sLv).subtract(g.multiply(cLv))).divide(r);

  278.         partialPV = pvCoordinates;

  279.         if (!FACTORIES.containsKey(a.getField())) {
  280.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  281.         }

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

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

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

  296.             // in order to compute true anomaly derivative, we must compute
  297.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  298.             final T lMDot = getKeplerianMeanMotion().
  299.                             add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  300.             @SuppressWarnings("unchecked")
  301.             final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  302.             final FieldDerivativeStructure<T> exDS = factory.build(ex, exDot);
  303.             final FieldDerivativeStructure<T> eyDS = factory.build(ey, eyDot);
  304.             final FieldDerivativeStructure<T> lMDS = factory.build(getLM(), lMDot);
  305.             final FieldDerivativeStructure<T> lvDS = eccentricToTrue(meanToEccentric(lMDS, exDS, eyDS), exDS, eyDS);
  306.             lvDot = lvDS.getPartialDerivative(1);

  307.         } else {
  308.             // acceleration is either almost zero or NaN,
  309.             // we assume acceleration was not known
  310.             // we don't set up derivatives
  311.             aDot  = null;
  312.             exDot = null;
  313.             eyDot = null;
  314.             hxDot = null;
  315.             hyDot = null;
  316.             lvDot = null;
  317.         }

  318.     }

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

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

  344.         a     = op.getA();
  345.         ex    = op.getEquinoctialEx();
  346.         ey    = op.getEquinoctialEy();
  347.         hx    = op.getHx();
  348.         hy    = op.getHy();
  349.         lv    = op.getLv();

  350.         if (!FACTORIES.containsKey(a.getField())) {
  351.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  352.         }

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

  359.         field = a.getField();
  360.         zero  = field.getZero();
  361.         one   = field.getOne();
  362.     }

  363.     /** {@inheritDoc} */
  364.     public OrbitType getType() {
  365.         return OrbitType.EQUINOCTIAL;
  366.     }

  367.     /** {@inheritDoc} */
  368.     public T getA() {
  369.         return a;
  370.     }

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

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

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

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

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

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

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

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

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

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

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

  415.     /** {@inheritDoc} */
  416.     public T getLE() {
  417.         return trueToEccentric(lv, ex, ey);
  418.     }

  419.     /** {@inheritDoc} */
  420.     public T getLEDot() {

  421.         if (!hasDerivatives()) {
  422.             return null;
  423.         }

  424.         @SuppressWarnings("unchecked")
  425.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  426.         final FieldDerivativeStructure<T> lVDS = factory.build(lv, lvDot);
  427.         final FieldDerivativeStructure<T> exDS = factory.build(ex, exDot);
  428.         final FieldDerivativeStructure<T> eyDS = factory.build(ey, eyDot);
  429.         final FieldDerivativeStructure<T> lEDS = trueToEccentric(lVDS, exDS, eyDS);
  430.         return lEDS.getPartialDerivative(1);

  431.     }

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

  436.     /** {@inheritDoc} */
  437.     public T getLMDot() {

  438.         if (!hasDerivatives()) {
  439.             return null;
  440.         }

  441.         @SuppressWarnings("unchecked")
  442.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  443.         final FieldDerivativeStructure<T> lVDS = factory.build(lv, lvDot);
  444.         final FieldDerivativeStructure<T> exDS = factory.build(ex, exDot);
  445.         final FieldDerivativeStructure<T> eyDS = factory.build(ey, eyDot);
  446.         final FieldDerivativeStructure<T> lMDS = eccentricToMean(trueToEccentric(lVDS, exDS, eyDS), exDS, eyDS);
  447.         return lMDS.getPartialDerivative(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 PositionAngle type) {
  454.         return (type == PositionAngle.MEAN) ? getLM() :
  455.                                               ((type == PositionAngle.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 PositionAngle type) {
  463.         return (type == PositionAngle.MEAN) ? getLMDot() :
  464.                                               ((type == PositionAngle.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 RealFieldElement<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 T cosLE   = lE.cos();
  482.         final T sinLE   = lE.sin();
  483.         final T num     = ex.multiply(sinLE).subtract(ey.multiply(cosLE));
  484.         final T den     = epsilon.add(1).subtract(ex.multiply(cosLE)).subtract(ey.multiply(sinLE));
  485.         return lE.add(num.divide(den).atan().multiply(2));
  486.     }

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

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

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

  525.             lEmlM = lEmlM.subtract(shift);
  526.             lE     = lM.add(lEmlM);
  527.             cosLE  = lE.cos();
  528.             sinLE  = lE.sin();

  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 RealFieldElement<T>> T eccentricToMean(final T lE, final T ex, final T ey) {
  540.         return lE.subtract(ex.multiply(lE.sin())).add(ey.multiply(lE.cos()));
  541.     }

  542.     /** Compute position from equinoctial parameters.
  543.      * @param a  semi-major axis (m)
  544.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  545.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  546.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  547.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  548.      * @param lv  v + ω + Ω true longitude argument (rad)
  549.      * @param mu central attraction coefficient (m³/s²)
  550.      * @param <T> type of the fiels elements
  551.      * @return position vector
  552.      */
  553.     public static <T extends RealFieldElement<T>> FieldVector3D<T> equinoctialToPosition(final T a, final T ex, final T ey,
  554.                                                                                          final T hx, final T hy, final T lv,
  555.                                                                                          final double mu) {

  556.         final T one  = a.getField().getOne();

  557.         // eccentric longitude argument
  558.         final T lE = trueToEccentric(lv, ex, ey);

  559.         // inclination-related intermediate parameters
  560.         final T hx2   = hx.multiply(hx);
  561.         final T hy2   = hy.multiply(hy);
  562.         final T factH = one.divide(hx2.add(1.0).add(hy2));

  563.         // reference axes defining the orbital plane
  564.         final T ux = hx2.add(1.0).subtract(hy2).multiply(factH);
  565.         final T uy = hx.multiply(hy).multiply(factH).multiply(2);
  566.         final T uz = hy.multiply(-2).multiply(factH);

  567.         final T vx = uy;
  568.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  569.         final T vz =  hx.multiply(factH).multiply(2);

  570.         // eccentricity-related intermediate parameters
  571.         final T ex2  = ex.multiply(ex);
  572.         final T exey = ex.multiply(ey);
  573.         final T ey2  = ey.multiply(ey);
  574.         final T e2   = ex2.add(ey2);
  575.         final T eta  = one.subtract(e2).sqrt().add(1);
  576.         final T beta = one.divide(eta);

  577.         // eccentric longitude argument
  578.         final T cLe    = lE.cos();
  579.         final T sLe    = lE.sin();

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

  583.         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  584.                                    x.multiply(uy).add(y.multiply(vy)),
  585.                                    x.multiply(uz).add(y.multiply(vz)));

  586.     }

  587.     /** {@inheritDoc} */
  588.     public T getE() {
  589.         return ex.multiply(ex).add(ey.multiply(ey)).sqrt();
  590.     }

  591.     /** {@inheritDoc} */
  592.     public T getEDot() {

  593.         if (!hasDerivatives()) {
  594.             return null;
  595.         }

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

  597.     }

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

  602.     /** {@inheritDoc} */
  603.     public T getIDot() {

  604.         if (!hasDerivatives()) {
  605.             return null;
  606.         }

  607.         final T h2 = hx.multiply(hx).add(hy.multiply(hy));
  608.         final T h  = h2.sqrt();
  609.         return hx.multiply(hxDot).add(hy.multiply(hyDot)).multiply(2).divide(h.multiply(h2.add(1)));

  610.     }

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

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

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

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

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

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

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

  638.         // eccentric longitude argument
  639.         final T cLe    = lE.cos();
  640.         final T sLe    = lE.sin();
  641.         final T exCeyS = ex.multiply(cLe).add(ey.multiply(sLe));

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

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

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

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

  655.     }

  656.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  657.      * <p>
  658.      * This method should be called only when {@link #hasDerivatives()} returns true.
  659.      * </p>
  660.      * @return non-Keplerian part of the acceleration
  661.      */
  662.     private FieldVector3D<T> nonKeplerianAcceleration() {

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

  665.         final T nonKeplerianMeanMotion = getLMDot().subtract(getKeplerianMeanMotion());
  666.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  667.                                  add(dCdP[3][1].multiply(exDot)).
  668.                                  add(dCdP[3][2].multiply(eyDot)).
  669.                                  add(dCdP[3][3].multiply(hxDot)).
  670.                                  add(dCdP[3][4].multiply(hyDot)).
  671.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  672.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  673.                                  add(dCdP[4][1].multiply(exDot)).
  674.                                  add(dCdP[4][2].multiply(eyDot)).
  675.                                  add(dCdP[4][3].multiply(hxDot)).
  676.                                  add(dCdP[4][4].multiply(hyDot)).
  677.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  678.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  679.                                  add(dCdP[5][1].multiply(exDot)).
  680.                                  add(dCdP[5][2].multiply(eyDot)).
  681.                                  add(dCdP[5][3].multiply(hxDot)).
  682.                                  add(dCdP[5][4].multiply(hyDot)).
  683.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

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

  685.     }

  686.     /** {@inheritDoc} */
  687.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  688.         // position and velocity
  689.         computePVWithoutA();

  690.         // acceleration
  691.         final T r2 = partialPV.getPosition().getNormSq();
  692.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(-getMu()),
  693.                                                                            partialPV.getPosition());
  694.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  695.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  696.                                               keplerianAcceleration;

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

  698.     }

  699.     /** {@inheritDoc} */
  700.     public FieldEquinoctialOrbit<T> shiftedBy(final double dt) {
  701.         return shiftedBy(getDate().getField().getZero().add(dt));
  702.     }

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

  705.         // use Keplerian-only motion
  706.         final FieldEquinoctialOrbit<T> keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy,
  707.                                                                                       getLM().add(getKeplerianMeanMotion().multiply(dt)),
  708.                                                                                       PositionAngle.MEAN, getFrame(),
  709.                                                                                       getDate().shiftedBy(dt), getMu());

  710.         if (hasDerivatives()) {

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

  713.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  714.             keplerianShifted.computePVWithoutA();
  715.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(),
  716.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  717.             final T   fixedR2 = fixedP.getNormSq();
  718.             final T   fixedR  = fixedR2.sqrt();
  719.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(),
  720.                                                                  dt, nonKeplerianAcceleration);
  721.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(-getMu()),
  722.                                                                  keplerianShifted.partialPV.getPosition(),
  723.                                                                  one, nonKeplerianAcceleration);

  724.             // build a new orbit, taking non-Keplerian acceleration into account
  725.             return new FieldEquinoctialOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  726.                                                                                    fixedP, fixedV, fixedA),
  727.                                                keplerianShifted.getFrame(), keplerianShifted.getMu());

  728.         } else {
  729.             // Keplerian-only motion is all we can do
  730.             return keplerianShifted;
  731.         }

  732.     }

  733.     /** {@inheritDoc}
  734.      * <p>
  735.      * The interpolated instance is created by polynomial Hermite interpolation
  736.      * on equinoctial elements, without derivatives (which means the interpolation
  737.      * falls back to Lagrange interpolation only).
  738.      * </p>
  739.      * <p>
  740.      * As this implementation of interpolation is polynomial, it should be used only
  741.      * with small samples (about 10-20 points) in order to avoid <a
  742.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  743.      * and numerical problems (including NaN appearing).
  744.      * </p>
  745.      * <p>
  746.      * If orbit interpolation on large samples is needed, using the {@link
  747.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  748.      * low-level interpolation. The Ephemeris class automatically handles selection of
  749.      * a neighboring sub-sample with a predefined number of point from a large global sample
  750.      * in a thread-safe way.
  751.      * </p>
  752.      */
  753.     public FieldEquinoctialOrbit<T> interpolate(final FieldAbsoluteDate<T> date, final Stream<FieldOrbit<T>> sample) {

  754.         // first pass to check if derivatives are available throughout the sample
  755.         final List<FieldOrbit<T>> list = sample.collect(Collectors.toList());
  756.         boolean useDerivatives = true;
  757.         for (final FieldOrbit<T> orbit : list) {
  758.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  759.         }

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

  762.         // second pass to feed interpolator
  763.         FieldAbsoluteDate<T> previousDate = null;
  764.         T                    previousLm   = zero.add(Double.NaN);
  765.         for (final FieldOrbit<T> orbit : list) {
  766.             final FieldEquinoctialOrbit<T> equi = (FieldEquinoctialOrbit<T>) OrbitType.EQUINOCTIAL.convertType(orbit);
  767.             final T continuousLm;
  768.             if (previousDate == null) {
  769.                 continuousLm = (T) equi.getLM();
  770.             } else {
  771.                 final T dt       = (T) equi.getDate().durationFrom(previousDate);
  772.                 final T keplerLm = previousLm.add((T) equi.getKeplerianMeanMotion().multiply(dt));
  773.                 continuousLm = normalizeAngle((T) equi.getLM(), keplerLm);
  774.             }
  775.             previousDate = equi.getDate();
  776.             previousLm   = continuousLm;
  777.             final T[] toAdd = MathArrays.buildArray(field, 6);
  778.             toAdd[0] = (T) equi.getA();
  779.             toAdd[1] = (T) equi.getEquinoctialEx();
  780.             toAdd[2] = (T) equi.getEquinoctialEy();
  781.             toAdd[3] = (T) equi.getHx();
  782.             toAdd[4] = (T) equi.getHy();
  783.             toAdd[5] = (T) continuousLm;
  784.             if (useDerivatives) {
  785.                 final T[] toAddDot = MathArrays.buildArray(one.getField(), 6);
  786.                 toAddDot[0] = equi.getADot();
  787.                 toAddDot[1] = equi.getEquinoctialExDot();
  788.                 toAddDot[2] = equi.getEquinoctialEyDot();
  789.                 toAddDot[3] = equi.getHxDot();
  790.                 toAddDot[4] = equi.getHyDot();
  791.                 toAddDot[5] = equi.getLMDot();
  792.                 interpolator.addSamplePoint(equi.getDate().durationFrom(date),
  793.                                             toAdd, toAddDot);
  794.             } else {
  795.                 interpolator.addSamplePoint((T) equi.getDate().durationFrom(date),
  796.                                             toAdd);
  797.             }
  798.         }

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

  801.         // build a new interpolated instance
  802.         return new FieldEquinoctialOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  803.                                            interpolated[0][3], interpolated[0][4], interpolated[0][5],
  804.                                            interpolated[1][0], interpolated[1][1], interpolated[1][2],
  805.                                            interpolated[1][3], interpolated[1][4], interpolated[1][5],
  806.                                            PositionAngle.MEAN, getFrame(), date, getMu());

  807.     }

  808.     /** {@inheritDoc} */
  809.     protected T[][] computeJacobianMeanWrtCartesian() {

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

  811.         // compute various intermediate parameters
  812.         computePVWithoutA();
  813.         final FieldVector3D<T> position = partialPV.getPosition();
  814.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  815.         final T r2         = position.getNormSq();
  816.         final T r          = r2.sqrt();
  817.         final T r3         = r.multiply(r2);

  818.         final double mu         = getMu();
  819.         final T sqrtMuA    = a.multiply(mu).sqrt();
  820.         final T a2         = a.multiply(a);

  821.         final T e2         = ex.multiply(ex).add(ey.multiply(ey));
  822.         final T oMe2       = one.subtract(e2);
  823.         final T epsilon    = oMe2.sqrt();
  824.         final T beta       = one.divide(epsilon.add(1));
  825.         final T ratio      = epsilon.multiply(beta);

  826.         final T hx2        = hx.multiply(hx);
  827.         final T hy2        = hy.multiply(hy);
  828.         final T hxhy       = hx.multiply(hy);

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

  833.         // coordinates of the spacecraft in the equinoctial frame
  834.         final T x    = FieldVector3D.dotProduct(position, f);
  835.         final T y    = FieldVector3D.dotProduct(position, g);
  836.         final T xDot = FieldVector3D.dotProduct(velocity, f);
  837.         final T yDot = FieldVector3D.dotProduct(velocity, g);

  838.         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
  839.         final T c1  = a.divide(sqrtMuA.multiply(epsilon));
  840.         final T c1N = c1.negate();
  841.         final T c2  = a.multiply(sqrtMuA).multiply(beta).divide(r3);
  842.         final T c3  = sqrtMuA.divide(r3.multiply(epsilon));
  843.         final FieldVector3D<T> drDotSdEx = new FieldVector3D<>(c1.multiply(xDot).multiply(yDot).subtract(c2.multiply(ey).multiply(x)).subtract(c3.multiply(x).multiply(y)), f,
  844.                                                                c1N.multiply(xDot).multiply(xDot).subtract(c2.multiply(ey).multiply(y)).add(c3.multiply(x).multiply(x)), g);

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

  848.         // da
  849.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
  850.         final FieldVector3D<T> vectorARDot = new FieldVector3D<>(a2.multiply(2).divide(mu), velocity);
  851.         fillHalfRow(one, vectorAR,    jacobian[0], 0);
  852.         fillHalfRow(one, vectorARDot, jacobian[0], 3);

  853.         // dEx
  854.         final T d1 = a.negate().multiply(ratio).divide(r3);
  855.         final T d2 = (hy.multiply(xDot).subtract(hx.multiply(yDot))).divide(sqrtMuA.multiply(epsilon));
  856.         final T d3 = hx.multiply(y).subtract(hy.multiply(x)).divide(sqrtMuA);
  857.         final FieldVector3D<T> vectorExRDot =
  858.             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);
  859.         fillHalfRow(ex.multiply(d1), position, ey.negate().multiply(d2), w, epsilon.divide(sqrtMuA), drDotSdEy, jacobian[1], 0);
  860.         fillHalfRow(one, vectorExRDot, jacobian[1], 3);

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

  866.         // dHx
  867.         final T h = (hx2.add(1).add(hy2)).divide(sqrtMuA.multiply(2).multiply(epsilon));
  868.         fillHalfRow( h.negate().multiply(xDot), w, jacobian[3], 0);
  869.         fillHalfRow( h.multiply(x),    w, jacobian[3], 3);

  870.        // dHy
  871.         fillHalfRow( h.negate().multiply(yDot), w, jacobian[4], 0);
  872.         fillHalfRow( h.multiply(y),    w, jacobian[4], 3);

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

  877.         return jacobian;

  878.     }

  879.     /** {@inheritDoc} */
  880.     protected T[][] computeJacobianEccentricWrtCartesian() {

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

  883.         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
  884.         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
  885.         // which is inverted and rewritten as:
  886.         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
  887.         final T le    = getLE();
  888.         final T cosLe = le.cos();
  889.         final T sinLe = le.sin();
  890.         final T aOr   = one.divide(one.subtract(ex.multiply(cosLe)).subtract(ey.multiply(sinLe)));

  891.         // update longitude row
  892.         final T[] rowEx = jacobian[1];
  893.         final T[] rowEy = jacobian[2];
  894.         final T[] rowL  = jacobian[5];

  895.         for (int j = 0; j < 6; ++j) {
  896.             rowL[j] = aOr.multiply(rowL[j].add(sinLe.multiply(rowEx[j])).subtract(cosLe.multiply(rowEy[j])));
  897.         }
  898.         return jacobian;

  899.     }

  900.     /** {@inheritDoc} */
  901.     protected T[][] computeJacobianTrueWrtCartesian() {

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

  904.         // Differentiating the eccentric longitude equation
  905.         // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
  906.         // leads to
  907.         // cT (dlV - dlE) = cE dlE + cX dex + cY dey
  908.         // with
  909.         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
  910.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
  911.         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  912.         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  913.         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  914.         // which can be solved to find the differential of the true longitude
  915.         // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
  916.         final T le        = getLE();
  917.         final T cosLe     = le.cos();
  918.         final T sinLe     = le.sin();
  919.         final T eSinE     = ex.multiply(sinLe).subtract(ey.multiply(cosLe));
  920.         final T ecosE     = ex.multiply(cosLe).add(ey.multiply(sinLe));
  921.         final T e2        = ex.multiply(ex).add(ey.multiply(ey));
  922.         final T epsilon   = one.subtract(e2).sqrt();
  923.         final T onePeps   = epsilon.add(1);
  924.         final T d         = onePeps.subtract(ecosE);
  925.         final T cT        = d.multiply(d).add(eSinE.multiply(eSinE)).divide(2);
  926.         final T cE        = ecosE.multiply(onePeps).subtract(e2);
  927.         final T cX        = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinLe.multiply(onePeps));
  928.         final T cY        = ey.multiply(eSinE).divide(epsilon).add( ex).subtract(cosLe.multiply(onePeps));
  929.         final T factorLe  = cT.add(cE).divide(cT);
  930.         final T factorEx  = cX.divide(cT);
  931.         final T factorEy  = cY.divide(cT);

  932.         // update longitude row
  933.         final T[] rowEx = jacobian[1];
  934.         final T[] rowEy = jacobian[2];
  935.         final T[] rowL = jacobian[5];
  936.         for (int j = 0; j < 6; ++j) {
  937.             rowL[j] = factorLe.multiply(rowL[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  938.         }

  939.         return jacobian;

  940.     }

  941.     /** {@inheritDoc} */
  942.     public void addKeplerContribution(final PositionAngle type, final double gm,
  943.                                       final T[] pDot) {
  944.         final T oMe2;
  945.         final T ksi;
  946.         final T n = zero.add(gm).divide(a).sqrt().divide(a);
  947.         switch (type) {
  948.             case MEAN :
  949.                 pDot[5] = pDot[5].add(n);
  950.                 break;
  951.             case ECCENTRIC :
  952.                 oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  953.                 ksi  = ex.multiply(lv.cos()).add(1).add(ey.multiply(lv.sin()));
  954.                 pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2));
  955.                 break;
  956.             case TRUE :
  957.                 oMe2 = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  958.                 ksi  =  ex.multiply(lv.cos()).add(1).add(ey.multiply(lv.sin()));
  959.                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
  960.                 break;
  961.             default :
  962.                 throw new OrekitInternalError(null);
  963.         }
  964.     }

  965.     /**  Returns a string representation of this equinoctial parameters object.
  966.      * @return a string representation of this object
  967.      */
  968.     public String toString() {
  969.         return new StringBuffer().append("equinoctial parameters: ").append('{').
  970.                                   append("a: ").append(a.getReal()).
  971.                                   append("; ex: ").append(ex.getReal()).append("; ey: ").append(ey.getReal()).
  972.                                   append("; hx: ").append(hx.getReal()).append("; hy: ").append(hy.getReal()).
  973.                                   append("; lv: ").append(FastMath.toDegrees(lv.getReal())).
  974.                                   append(";}").toString();
  975.     }

  976.     /**
  977.      * Normalize an angle in a 2&pi; wide interval around a center value.
  978.      * <p>This method has three main uses:</p>
  979.      * <ul>
  980.      *   <li>normalize an angle between 0 and 2&pi;:<br/>
  981.      *       {@code a = MathUtils.normalizeAngle(a, FastMath.PI);}</li>
  982.      *   <li>normalize an angle between -&pi; and +&pi;<br/>
  983.      *       {@code a = MathUtils.normalizeAngle(a, 0.0);}</li>
  984.      *   <li>compute the angle between two defining angular positions:<br>
  985.      *       {@code angle = MathUtils.normalizeAngle(end, start) - start;}</li>
  986.      * </ul>
  987.      * <p>Note that due to numerical accuracy and since &pi; cannot be represented
  988.      * exactly, the result interval is <em>closed</em>, it cannot be half-closed
  989.      * as would be more satisfactory in a purely mathematical view.</p>
  990.      * @param a angle to normalize
  991.      * @param center center of the desired 2&pi; interval for the result
  992.      * @param <T> the type of the field elements
  993.      * @return a-2k&pi; with integer k and center-&pi; &lt;= a-2k&pi; &lt;= center+&pi;
  994.      */
  995.     public static <T extends RealFieldElement<T>> T normalizeAngle(final T a, final T center) {
  996.         return a.subtract(2 * FastMath.PI * FastMath.floor((a.getReal() + FastMath.PI - center.getReal()) / (2 * FastMath.PI)));
  997.     }

  998.     @Override
  999.     public EquinoctialOrbit toOrbit() {
  1000.         if (hasDerivatives()) {
  1001.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1002.                                         hx.getReal(), hy.getReal(), lv.getReal(),
  1003.                                         aDot.getReal(), exDot.getReal(), eyDot.getReal(),
  1004.                                         hxDot.getReal(), hyDot.getReal(), lvDot.getReal(),
  1005.                                         PositionAngle.TRUE, getFrame(),
  1006.                                         getDate().toAbsoluteDate(), getMu());
  1007.         } else {
  1008.             return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1009.                                         hx.getReal(), hy.getReal(), lv.getReal(),
  1010.                                         PositionAngle.TRUE, getFrame(),
  1011.                                         getDate().toAbsoluteDate(), getMu());
  1012.         }
  1013.     }

  1014. }