FieldKeplerianOrbit.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.exception.MathIllegalArgumentException;
  29. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  30. import org.hipparchus.util.FastMath;
  31. import org.hipparchus.util.MathArrays;
  32. import org.hipparchus.util.Precision;
  33. import org.orekit.errors.OrekitIllegalArgumentException;
  34. import org.orekit.errors.OrekitInternalError;
  35. import org.orekit.errors.OrekitMessages;
  36. import org.orekit.frames.Frame;
  37. import org.orekit.time.FieldAbsoluteDate;
  38. import org.orekit.utils.FieldPVCoordinates;
  39. import org.orekit.utils.TimeStampedFieldPVCoordinates;


  40. /**
  41.  * This class handles traditional Keplerian orbital parameters.

  42.  * <p>
  43.  * The parameters used internally are the classical Keplerian elements:
  44.  *   <pre>
  45.  *     a
  46.  *     e
  47.  *     i
  48.  *     ω
  49.  *     Ω
  50.  *     v
  51.  *   </pre>
  52.  * where ω stands for the Perigee Argument, Ω stands for the
  53.  * Right Ascension of the Ascending Node and v stands for the true anomaly.
  54.  * </p>
  55.  * <p>
  56.  * This class supports hyperbolic orbits, using the convention that semi major
  57.  * axis is negative for such orbits (and of course eccentricity is greater than 1).
  58.  * </p>
  59.  * <p>
  60.  * When orbit is either equatorial or circular, some Keplerian elements
  61.  * (more precisely ω and Ω) become ambiguous so this class should not
  62.  * be used for such orbits. For this reason, {@link EquinoctialOrbit equinoctial
  63.  * orbits} is the recommended way to represent orbits.
  64.  * </p>

  65.  * <p>
  66.  * The instance <code>KeplerianOrbit</code> is guaranteed to be immutable.
  67.  * </p>
  68.  * @see     Orbit
  69.  * @see    CircularOrbit
  70.  * @see    CartesianOrbit
  71.  * @see    EquinoctialOrbit
  72.  * @author Luc Maisonobe
  73.  * @author Guylaine Prat
  74.  * @author Fabien Maussion
  75.  * @author V&eacute;ronique Pommier-Maurussane
  76.  * @author Andrea Antolino
  77.  * @since 9.0
  78.  */
  79. public class FieldKeplerianOrbit<T extends RealFieldElement<T>> extends FieldOrbit<T> {

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

  83.     /** First coefficient to compute Kepler equation solver starter. */
  84.     private static final double A;

  85.     /** Second coefficient to compute Kepler equation solver starter. */
  86.     private static final double B;

  87.     static {
  88.         final double k1 = 3 * FastMath.PI + 2;
  89.         final double k2 = FastMath.PI - 1;
  90.         final double k3 = 6 * FastMath.PI - 1;
  91.         A  = 3 * k2 * k2 / k1;
  92.         B  = k3 * k3 / (6 * k1);
  93.     }

  94.     /** Semi-major axis (m). */
  95.     private final T a;

  96.     /** Eccentricity. */
  97.     private final T e;

  98.     /** Inclination (rad). */
  99.     private final T i;

  100.     /** Perigee Argument (rad). */
  101.     private final T pa;

  102.     /** Right Ascension of Ascending Node (rad). */
  103.     private final T raan;

  104.     /** True anomaly (rad). */
  105.     private final T v;

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

  108.     /** Eccentricity derivative. */
  109.     private final T eDot;

  110.     /** Inclination derivative (rad/s). */
  111.     private final T iDot;

  112.     /** Perigee Argument derivative (rad/s). */
  113.     private final T paDot;

  114.     /** Right Ascension of Ascending Node derivative (rad/s). */
  115.     private final T raanDot;

  116.     /** True anomaly derivative (rad/s). */
  117.     private final T vDot;

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

  120.     /** Identity element. */
  121.     private final T one;

  122.     /** Zero element. */
  123.     private final T zero;

  124.     /** Third Canonical Vector. */
  125.     private final FieldVector3D<T> PLUS_K;

  126.     /** Creates a new instance.
  127.      * @param a  semi-major axis (m), negative for hyperbolic orbits
  128.      * @param e eccentricity
  129.      * @param i inclination (rad)
  130.      * @param pa perigee argument (ω, rad)
  131.      * @param raan right ascension of ascending node (Ω, rad)
  132.      * @param anomaly mean, eccentric or true anomaly (rad)
  133.      * @param type type of anomaly
  134.      * @param frame the frame in which the parameters are defined
  135.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  136.      * @param date date of the orbital parameters
  137.      * @param mu central attraction coefficient (m³/s²)
  138.      * @exception IllegalArgumentException if frame is not a {@link
  139.      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
  140.      * or v is out of range for hyperbolic orbits
  141.      */
  142.     public FieldKeplerianOrbit(final T a, final T e, final T i,
  143.                                final T pa, final T raan,
  144.                                final T anomaly, final PositionAngle type,
  145.                                final Frame frame, final FieldAbsoluteDate<T> date, final double mu)
  146.         throws IllegalArgumentException {
  147.         this(a, e, i, pa, raan, anomaly,
  148.              null, null, null, null, null, null,
  149.              type, frame, date, mu);
  150.     }

  151.     /** Creates a new instance.
  152.      * @param a  semi-major axis (m), negative for hyperbolic orbits
  153.      * @param e eccentricity
  154.      * @param i inclination (rad)
  155.      * @param pa perigee argument (ω, rad)
  156.      * @param raan right ascension of ascending node (Ω, rad)
  157.      * @param anomaly mean, eccentric or true anomaly (rad)
  158.      * @param aDot  semi-major axis derivative, null if unknown (m/s)
  159.      * @param eDot eccentricity derivative, null if unknown
  160.      * @param iDot inclination derivative, null if unknown (rad/s)
  161.      * @param paDot perigee argument derivative, null if unknown (rad/s)
  162.      * @param raanDot right ascension of ascending node derivative, null if unknown (rad/s)
  163.      * @param anomalyDot mean, eccentric or true anomaly derivative, null if unknown (rad/s)
  164.      * @param type type of anomaly
  165.      * @param frame the frame in which the parameters are defined
  166.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  167.      * @param date date of the orbital parameters
  168.      * @param mu central attraction coefficient (m³/s²)
  169.      * @exception IllegalArgumentException if frame is not a {@link
  170.      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
  171.      * or v is out of range for hyperbolic orbits
  172.      */
  173.     public FieldKeplerianOrbit(final T a, final T e, final T i,
  174.                                final T pa, final T raan, final T anomaly,
  175.                                final T aDot, final T eDot, final T iDot,
  176.                                final T paDot, final T raanDot, final T anomalyDot,
  177.                                final PositionAngle type,
  178.                                final Frame frame, final FieldAbsoluteDate<T> date, final double mu)
  179.         throws IllegalArgumentException {
  180.         super(frame, date, mu);
  181.         if (a.multiply(e.negate().add(1)).getReal() < 0) {
  182.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a.getReal(), e.getReal());
  183.         }

  184.         if (!FACTORIES.containsKey(a.getField())) {
  185.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  186.         }

  187.         this.a       =    a;
  188.         this.aDot    =    aDot;
  189.         this.e       =    e;
  190.         this.eDot    =    eDot;
  191.         this.i       =    i;
  192.         this.iDot    =    iDot;
  193.         this.pa      =   pa;
  194.         this.paDot   =   paDot;
  195.         this.raan    = raan;
  196.         this.raanDot = raanDot;

  197.         /** Identity element. */
  198.         this.one = a.getField().getOne();

  199.         /** Zero element. */
  200.         this.zero = a.getField().getZero();

  201.         /**Third canonical vector. */
  202.         this.PLUS_K = FieldVector3D.getPlusK(a.getField());

  203.         if (hasDerivatives()) {
  204.             @SuppressWarnings("unchecked")
  205.             final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  206.             final FieldDerivativeStructure<T> eDS = factory.build(e, eDot);
  207.             final FieldDerivativeStructure<T> anomalyDS  = factory.build(anomaly,  anomalyDot);
  208.             final FieldDerivativeStructure<T> vDS;
  209.             switch (type) {
  210.                 case MEAN :
  211.                     vDS = (a.getReal() < 0) ?
  212.                           hyperbolicEccentricToTrue(meanToHyperbolicEccentric(anomalyDS, eDS), eDS) :
  213.                           ellipticEccentricToTrue(meanToEllipticEccentric(anomalyDS, eDS), eDS);
  214.                     break;
  215.                 case ECCENTRIC :
  216.                     vDS = (a.getReal() < 0) ?
  217.                           hyperbolicEccentricToTrue(anomalyDS, eDS) :
  218.                           ellipticEccentricToTrue(anomalyDS, eDS);
  219.                     break;
  220.                 case TRUE :
  221.                     vDS = anomalyDS;
  222.                     break;
  223.                 default : // this should never happen
  224.                     throw new OrekitInternalError(null);
  225.             }
  226.             this.v    = vDS.getValue();
  227.             this.vDot = vDS.getPartialDerivative(1);
  228.         } else {
  229.             switch (type) {
  230.                 case MEAN :

  231.                     this.v = (a.getReal() < 0) ?
  232.                              hyperbolicEccentricToTrue(meanToHyperbolicEccentric(anomaly, e), e) :
  233.                              ellipticEccentricToTrue(meanToEllipticEccentric(anomaly, e), e);

  234.                     break;
  235.                 case ECCENTRIC :
  236.                     this.v = (a.getReal() < 0) ?
  237.                              hyperbolicEccentricToTrue(anomaly, e) :
  238.                              ellipticEccentricToTrue(anomaly, e);

  239.                     break;
  240.                 case TRUE :
  241.                     this.v = anomaly;
  242.                     break;
  243.                 default : // this should never happen
  244.                     throw new OrekitInternalError(null);
  245.             }
  246.             this.vDot = null;
  247.         }

  248.         // check true anomaly range
  249.         if (e.multiply(v.cos()).add(1).getReal() <= 0) {
  250.             final double vMax = e.reciprocal().negate().acos().getReal();
  251.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE,
  252.                                                      v.getReal(), e.getReal(), -vMax, vMax);
  253.         }

  254.         this.partialPV = null;

  255.     }

  256.     /** Constructor from Cartesian parameters.
  257.      *
  258.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  259.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  260.      * use {@code mu} and the position to compute the acceleration, including
  261.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  262.      *
  263.      * @param pvCoordinates the PVCoordinates of the satellite
  264.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  265.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  266.      * @param mu central attraction coefficient (m³/s²)
  267.      * @exception IllegalArgumentException if frame is not a {@link
  268.      * Frame#isPseudoInertial pseudo-inertial frame}
  269.      */
  270.     public FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
  271.                                final Frame frame, final double mu)
  272.         throws IllegalArgumentException {
  273.         this(pvCoordinates, frame, mu, hasNonKeplerianAcceleration(pvCoordinates, mu));
  274.     }

  275.     /** Constructor from Cartesian parameters.
  276.      *
  277.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  278.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  279.      * use {@code mu} and the position to compute the acceleration, including
  280.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  281.      *
  282.      * @param pvCoordinates the PVCoordinates of the satellite
  283.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  284.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  285.      * @param mu central attraction coefficient (m³/s²)
  286.      * @param reliableAcceleration if true, the acceleration is considered to be reliable
  287.      * @exception IllegalArgumentException if frame is not a {@link
  288.      * Frame#isPseudoInertial pseudo-inertial frame}
  289.      */
  290.     private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
  291.                                 final Frame frame, final double mu,
  292.                                 final boolean reliableAcceleration)
  293.         throws IllegalArgumentException {

  294.         super(pvCoordinates, frame, mu);

  295.         // identity element
  296.         this.one = pvCoordinates.getPosition().getX().getField().getOne();

  297.         // zero element
  298.         this.zero = one.getField().getZero();

  299.         // third canonical vector
  300.         this.PLUS_K = FieldVector3D.getPlusK(one.getField());

  301.         // compute inclination
  302.         final FieldVector3D<T> momentum = pvCoordinates.getMomentum();
  303.         final T m2 = momentum.getNormSq();

  304.         i = FieldVector3D.angle(momentum, PLUS_K);
  305.         // compute right ascension of ascending node
  306.         raan = FieldVector3D.crossProduct(PLUS_K, momentum).getAlpha();
  307.         // preliminary computations for parameters depending on orbit shape (elliptic or hyperbolic)
  308.         final FieldVector3D<T> pvP     = pvCoordinates.getPosition();
  309.         final FieldVector3D<T> pvV     = pvCoordinates.getVelocity();
  310.         final FieldVector3D<T> pvA     = pvCoordinates.getAcceleration();

  311.         final T   r2      = pvP.getNormSq();
  312.         final T   r       = r2.sqrt();
  313.         final T   V2      = pvV.getNormSq();
  314.         final T   rV2OnMu = r.multiply(V2).divide(mu);

  315.         // compute semi-major axis (will be negative for hyperbolic orbits)
  316.         a = r.divide(rV2OnMu.negate().add(2.0));
  317.         final T muA = a.multiply(mu);

  318.         // compute true anomaly
  319.         if (a.getReal() > 0) {
  320.             // elliptic or circular orbit
  321.             final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt());
  322.             final T eCE = rV2OnMu.subtract(1);
  323.             e = (eSE.multiply(eSE).add(eCE.multiply(eCE))).sqrt();
  324.             v = ellipticEccentricToTrue(eSE.atan2(eCE), e); //(atan2(eSE, eCE));
  325.         } else {
  326.             // hyperbolic orbit
  327.             final T eSH = FieldVector3D.dotProduct(pvP, pvV).divide(muA.negate().sqrt());
  328.             final T eCH = rV2OnMu.subtract(1);
  329.             e = (m2.negate().divide(muA).add(1)).sqrt();
  330.             v = hyperbolicEccentricToTrue((eCH.add(eSH)).divide(eCH.subtract(eSH)).log().divide(2), e);
  331.         }

  332.         // compute perigee argument
  333.         final FieldVector3D<T> node = new FieldVector3D<>(raan, zero);
  334.         final T px = FieldVector3D.dotProduct(pvP, node);
  335.         final T py = FieldVector3D.dotProduct(pvP, FieldVector3D.crossProduct(momentum, node)).divide(m2.sqrt());
  336.         pa = py.atan2(px).subtract(v);

  337.         partialPV = pvCoordinates;

  338.         if (!FACTORIES.containsKey(a.getField())) {
  339.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  340.         }

  341.         if (reliableAcceleration) {
  342.             // we have a relevant acceleration, we can compute derivatives

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

  345.             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(-mu), pvP);
  346.             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  347.             final T   aX                       = nonKeplerianAcceleration.getX();
  348.             final T   aY                       = nonKeplerianAcceleration.getY();
  349.             final T   aZ                       = nonKeplerianAcceleration.getZ();
  350.             aDot    = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
  351.             eDot    = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
  352.             iDot    = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
  353.             paDot   = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
  354.             raanDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));

  355.             // in order to compute true anomaly derivative, we must compute
  356.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  357.             final T MDot = getKeplerianMeanMotion().
  358.                            add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  359.             @SuppressWarnings("unchecked")
  360.             final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  361.             final FieldDerivativeStructure<T> eDS = factory.build(e, eDot);
  362.             final FieldDerivativeStructure<T> MDS = factory.build(getMeanAnomaly(), MDot);
  363.             final FieldDerivativeStructure<T> vDS = (a.getReal() < 0) ?
  364.                                             FieldKeplerianOrbit.hyperbolicEccentricToTrue(FieldKeplerianOrbit.meanToHyperbolicEccentric(MDS, eDS), eDS) :
  365.                                             FieldKeplerianOrbit.ellipticEccentricToTrue(FieldKeplerianOrbit.meanToEllipticEccentric(MDS, eDS), eDS);
  366.             vDot = vDS.getPartialDerivative(1);

  367.         } else {
  368.             // acceleration is either almost zero or NaN,
  369.             // we assume acceleration was not known
  370.             // we don't set up derivatives
  371.             aDot    = null;
  372.             eDot    = null;
  373.             iDot    = null;
  374.             paDot   = null;
  375.             raanDot = null;
  376.             vDot    = null;
  377.         }

  378.     }

  379.     /** Constructor from Cartesian parameters.
  380.      *
  381.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  382.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  383.      * use {@code mu} and the position to compute the acceleration, including
  384.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  385.      *
  386.      * @param FieldPVCoordinates the PVCoordinates of the satellite
  387.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  388.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  389.      * @param date date of the orbital parameters
  390.      * @param mu central attraction coefficient (m³/s²)
  391.      * @exception IllegalArgumentException if frame is not a {@link
  392.      * Frame#isPseudoInertial pseudo-inertial frame}
  393.      */
  394.     public FieldKeplerianOrbit(final FieldPVCoordinates<T> FieldPVCoordinates,
  395.                                final Frame frame, final FieldAbsoluteDate<T> date, final double mu)
  396.         throws IllegalArgumentException {
  397.         this(new TimeStampedFieldPVCoordinates<>(date, FieldPVCoordinates), frame, mu);
  398.     }

  399.     /** Constructor from any kind of orbital parameters.
  400.      * @param op orbital parameters to copy
  401.      */
  402.     public FieldKeplerianOrbit(final FieldOrbit<T> op) {
  403.         this(op.getPVCoordinates(), op.getFrame(), op.getMu(), op.hasDerivatives());
  404.     }

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

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

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

  417.     /** {@inheritDoc} */
  418.     public T getE() {
  419.         return e;
  420.     }

  421.     /** {@inheritDoc} */
  422.     public T getEDot() {
  423.         return eDot;
  424.     }

  425.     /** {@inheritDoc} */
  426.     public T getI() {
  427.         return i;
  428.     }

  429.     /** {@inheritDoc} */
  430.     public T getIDot() {
  431.         return iDot;
  432.     }

  433.     /** Get the perigee argument.
  434.      * @return perigee argument (rad)
  435.      */
  436.     public T getPerigeeArgument() {
  437.         return pa;
  438.     }

  439.     /** Get the perigee argument derivative.
  440.      * <p>
  441.      * If the orbit was created without derivatives, the value returned is null.
  442.      * </p>
  443.      * @return perigee argument derivative (rad/s)
  444.      */
  445.     public T getPerigeeArgumentDot() {
  446.         return paDot;
  447.     }

  448.     /** Get the right ascension of the ascending node.
  449.      * @return right ascension of the ascending node (rad)
  450.      */
  451.     public T getRightAscensionOfAscendingNode() {
  452.         return raan;
  453.     }

  454.     /** Get the right ascension of the ascending node derivative.
  455.      * <p>
  456.      * If the orbit was created without derivatives, the value returned is null.
  457.      * </p>
  458.      * @return right ascension of the ascending node derivative (rad/s)
  459.      */
  460.     public T getRightAscensionOfAscendingNodeDot() {
  461.         return raanDot;
  462.     }

  463.     /** Get the true anomaly.
  464.      * @return true anomaly (rad)
  465.      */
  466.     public T getTrueAnomaly() {
  467.         return v;
  468.     }

  469.     /** Get the true anomaly derivative.
  470.      * <p>
  471.      * If the orbit was created without derivatives, the value returned is null.
  472.      * </p>
  473.      * @return true anomaly derivative (rad/s)
  474.      */
  475.     public T getTrueAnomalyDot() {
  476.         return vDot;
  477.     }

  478.     /** Get the eccentric anomaly.
  479.      * @return eccentric anomaly (rad)
  480.      */
  481.     public T getEccentricAnomaly() {
  482.         return (a.getReal() < 0) ? trueToHyperbolicEccentric(v, e) : trueToEllipticEccentric(v, e);
  483.     }

  484.     /** Get the eccentric anomaly derivative.
  485.      * <p>
  486.      * If the orbit was created without derivatives, the value returned is null.
  487.      * </p>
  488.      * @return eccentric anomaly derivative (rad/s)
  489.      */
  490.     public T getEccentricAnomalyDot() {

  491.         if (!hasDerivatives()) {
  492.             return null;
  493.         }

  494.         @SuppressWarnings("unchecked")
  495.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  496.         final FieldDerivativeStructure<T> eDS = factory.build(e, eDot);
  497.         final FieldDerivativeStructure<T> vDS = factory.build(v, vDot);
  498.         final FieldDerivativeStructure<T> EDS = (a.getReal() < 0) ?
  499.                                                 trueToHyperbolicEccentric(vDS, eDS) :
  500.                                                 trueToEllipticEccentric(vDS, eDS);
  501.         return EDS.getPartialDerivative(1);

  502.     }

  503.     /** Get the mean anomaly.
  504.      * @return mean anomaly (rad)
  505.      */
  506.     public T getMeanAnomaly() {
  507.         return (a.getReal() < 0) ?
  508.                hyperbolicEccentricToMean(trueToHyperbolicEccentric(v, e), e) :
  509.                ellipticEccentricToMean(trueToEllipticEccentric(v, e), e);
  510.     }

  511.     /** Get the mean anomaly derivative.
  512.      * <p>
  513.      * If the orbit was created without derivatives, the value returned is null.
  514.      * </p>
  515.      * @return mean anomaly derivative (rad/s)
  516.      */
  517.     public T getMeanAnomalyDot() {

  518.         if (!hasDerivatives()) {
  519.             return null;
  520.         }

  521.         @SuppressWarnings("unchecked")
  522.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  523.         final FieldDerivativeStructure<T> eDS = factory.build(e, eDot);
  524.         final FieldDerivativeStructure<T> vDS = factory.build(v, vDot);
  525.         final FieldDerivativeStructure<T> MDS = (a.getReal() < 0) ?
  526.                                                 hyperbolicEccentricToMean(trueToHyperbolicEccentric(vDS, eDS), eDS) :
  527.                                                 ellipticEccentricToMean(trueToEllipticEccentric(vDS, eDS), eDS);
  528.         return MDS.getPartialDerivative(1);

  529.     }

  530.     /** Get the anomaly.
  531.      * @param type type of the angle
  532.      * @return anomaly (rad)
  533.      */
  534.     public T getAnomaly(final PositionAngle type) {
  535.         return (type == PositionAngle.MEAN) ? getMeanAnomaly() :
  536.                                               ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomaly() :
  537.                                                                                    getTrueAnomaly());
  538.     }

  539.     /** Get the anomaly derivative.
  540.      * <p>
  541.      * If the orbit was created without derivatives, the value returned is null.
  542.      * </p>
  543.      * @param type type of the angle
  544.      * @return anomaly derivative (rad/s)
  545.      */
  546.     public T getAnomalyDot(final PositionAngle type) {
  547.         return (type == PositionAngle.MEAN) ? getMeanAnomalyDot() :
  548.                                               ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomalyDot() :
  549.                                                                                    getTrueAnomalyDot());
  550.     }

  551.     /** {@inheritDoc} */
  552.     @Override
  553.     public boolean hasDerivatives() {
  554.         return aDot != null;
  555.     }

  556.     /** Computes the true anomaly from the elliptic eccentric anomaly.
  557.      * @param E eccentric anomaly (rad)
  558.      * @param e eccentricity
  559.      * @param <T> type of the field elements
  560.      * @return v the true anomaly
  561.      */
  562.     public static <T extends RealFieldElement<T>> T ellipticEccentricToTrue(final T E, final T e) {
  563.         final T beta = e.divide(e.multiply(e).negate().add(1).sqrt().add(1));
  564.         return E.add(beta.multiply(E.sin()).divide(beta.multiply(E.cos()).subtract(1).negate()).atan().multiply(2));
  565.     }

  566.     /** Computes the elliptic eccentric anomaly from the true anomaly.
  567.      * @param v true anomaly (rad)
  568.      * @param e eccentricity
  569.      * @param <T> type of the field elements
  570.      * @return E the elliptic eccentric anomaly
  571.      */
  572.     public static <T extends RealFieldElement<T>> T trueToEllipticEccentric(final T v, final T e) {
  573.         final T beta = e.divide(e.multiply(e).negate().add(1).sqrt().add(1));
  574.         return v.subtract((beta.multiply(v.sin()).divide(beta.multiply(v.cos()).add(1))).atan().multiply(2));
  575.     }

  576.     /** Computes the true anomaly from the hyperbolic eccentric anomaly.
  577.      * @param H hyperbolic eccentric anomaly (rad)
  578.      * @param e eccentricity
  579.      * @param <T> type of the field elements
  580.      * @return v the true anomaly
  581.      */
  582.     public static <T extends RealFieldElement<T>> T hyperbolicEccentricToTrue(final T H, final T e) {
  583.         final T s    = e.add(1).divide(e.subtract(1)).sqrt();
  584.         final T tanH = H.multiply(0.5).tanh();
  585.         return s.multiply(tanH).atan().multiply(2);
  586.     }

  587.     /** Computes the hyperbolic eccentric anomaly from the true anomaly.
  588.      * @param v true anomaly (rad)
  589.      * @param e eccentricity
  590.      * @param <T> type of the field elements
  591.      * @return H the hyperbolic eccentric anomaly
  592.      */
  593.     public static <T extends RealFieldElement<T>> T trueToHyperbolicEccentric(final T v, final T e) {
  594.         final T sinhH = e.multiply(e).subtract(1).sqrt().multiply(v.sin()).divide(e.multiply(v.cos()).add(1));
  595.         return sinhH.asinh();
  596.     }

  597.     /** Computes the mean anomaly from the hyperbolic eccentric anomaly.
  598.      * @param H hyperbolic eccentric anomaly (rad)
  599.      * @param e eccentricity
  600.      * @param <T> type of the field elements
  601.      * @return M the mean anomaly
  602.      */
  603.     public static <T extends RealFieldElement<T>> T hyperbolicEccentricToMean(final T H, final T e) {
  604.         return e.multiply(H.sinh()).subtract(H);
  605.     }

  606.     /** Computes the elliptic eccentric anomaly from the mean anomaly.
  607.      * <p>
  608.      * The algorithm used here for solving Kepler equation has been published
  609.      * in: "Procedures for  solving Kepler's Equation", A. W. Odell and
  610.      * R. H. Gooding, Celestial Mechanics 38 (1986) 307-334
  611.      * </p>
  612.      * @param M mean anomaly (rad)
  613.      * @param e eccentricity
  614.      * @param <T> type of the field elements
  615.      * @return E the eccentric anomaly
  616.      */
  617.     public static <T extends RealFieldElement<T>> T meanToEllipticEccentric(final T M, final T e) {
  618.         // reduce M to [-PI PI) interval
  619.         final T reducedM = normalizeAngle(M, M.getField().getZero());

  620.         // compute start value according to A. W. Odell and R. H. Gooding S12 starter
  621.         T E;
  622.         if (reducedM.abs().getReal() < 1.0 / 6.0) {
  623.             if (FastMath.abs(reducedM.getReal()) < Precision.SAFE_MIN) {
  624.                 // this is an Orekit change to the S12 starter, mainly used when T is some kind of derivative structure.
  625.                 // If reducedM is 0.0, the derivative of cbrt is infinite which induces NaN appearing later in
  626.                 // the computation. As in this case E and M are almost equal, we initialize E with reducedM
  627.                 E = reducedM;
  628.             } else {
  629.                 // this is the standard S12 starter
  630.                 E = reducedM.add(e.multiply( (reducedM.multiply(6).cbrt()).subtract(reducedM)));
  631.             }
  632.         } else {
  633.             if (reducedM.getReal() < 0) {
  634.                 final T w = reducedM.add(FastMath.PI);
  635.                 E = reducedM.add(e.multiply(w.multiply(A).divide(w.negate().add(B)).subtract(FastMath.PI).subtract(reducedM)));
  636.             } else {
  637.                 final T w = reducedM.negate().add(FastMath.PI);
  638.                 E = reducedM.add(e.multiply(w.multiply(A).divide(w.negate().add(B)).negate().subtract(reducedM).add(FastMath.PI)));
  639.             }
  640.         }

  641.         final T e1 = e.negate().add(1);
  642.         final boolean noCancellationRisk = (e1.getReal() + E.getReal() * E.getReal() / 6) >= 0.1;

  643.         // perform two iterations, each consisting of one Halley step and one Newton-Raphson step
  644.         for (int j = 0; j < 2; ++j) {

  645.             final T f;
  646.             T fd;
  647.             final T fdd  = e.multiply(E.sin());
  648.             final T fddd = e.multiply(E.cos());

  649.             if (noCancellationRisk) {

  650.                 f  = (E.subtract(fdd)).subtract(reducedM);
  651.                 fd = fddd.negate().add(1);
  652.             } else {


  653.                 f  = eMeSinE(E, e).subtract(reducedM);
  654.                 final T s = E.multiply(0.5).sin();
  655.                 fd = e1.add(e.multiply(s).multiply(s).multiply(2));
  656.             }
  657.             final T dee = f.multiply(fd).divide(f.multiply(fdd).multiply(0.5).subtract(fd.multiply(fd)));

  658.             // update eccentric anomaly, using expressions that limit underflow problems
  659.             final T w = fd.add(dee.multiply(fdd.add(dee.multiply(fddd.divide(3)))).multiply(0.5));
  660.             fd = fd.add(dee.multiply(fdd.add(dee.multiply(fddd).multiply(0.5))));
  661.             E = E.subtract(f.subtract(dee.multiply(fd.subtract(w))).divide(fd));

  662.         }

  663.         // expand the result back to original range
  664.         E = E.add(M).subtract(reducedM);
  665.         return E;
  666.     }

  667.     /** Accurate computation of E - e sin(E).
  668.      * <p>
  669.      * This method is used when E is close to 0 and e close to 1,
  670.      * i.e. near the perigee of almost parabolic orbits
  671.      * </p>
  672.      * @param E eccentric anomaly
  673.      * @param e eccentricity
  674.      * @param <T> Type of the field elements
  675.      * @return E - e sin(E)
  676.      */
  677.     private static <T extends RealFieldElement<T>> T eMeSinE(final T E, final T e) {

  678.         T x = (e.negate().add(1)).multiply(E.sin());
  679.         final T mE2 = E.negate().multiply(E);
  680.         T term = E;
  681.         double d    = 0;
  682.         // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance
  683.         for (T x0 = E.getField().getZero().add(Double.NaN); x.getReal() != x0.getReal();) {
  684.             d += 2;
  685.             term = term.multiply(mE2.divide(d * (d + 1)));
  686.             x0 = x;
  687.             x = x.subtract(term);
  688.         }
  689.         return x;
  690.     }

  691.     /** Computes the hyperbolic eccentric anomaly from the mean anomaly.
  692.      * <p>
  693.      * The algorithm used here for solving hyperbolic Kepler equation is
  694.      * Danby's iterative method (3rd order) with Vallado's initial guess.
  695.      * </p>
  696.      * @param M mean anomaly (rad)
  697.      * @param e eccentricity
  698.      * @param <T> Type of the field elements
  699.      * @return H the hyperbolic eccentric anomaly
  700.      */
  701.     public static <T extends RealFieldElement<T>> T meanToHyperbolicEccentric(final T M, final T e) {

  702.         // Resolution of hyperbolic Kepler equation for Keplerian parameters

  703.         // Initial guess
  704.         T H;
  705.         if (e.getReal() < 1.6) {
  706.             if ((-FastMath.PI < M.getReal() && M.getReal() < 0.) || M.getReal() > FastMath.PI) {
  707.                 H = M.subtract(e);
  708.             } else {
  709.                 H = M.add(e);
  710.             }
  711.         } else {
  712.             if (e.getReal() < 3.6 && M.abs().getReal() > FastMath.PI) {
  713.                 H = M.subtract(e.copySign(M));
  714.             } else {
  715.                 H = M.divide(e.subtract(1));
  716.             }
  717.         }

  718.         // Iterative computation
  719.         int iter = 0;
  720.         do {
  721.             final T f3  = e.multiply(H.cosh());
  722.             final T f2  = e.multiply(H.sinh());
  723.             final T f1  = f3.subtract(1);
  724.             final T f0  = f2.subtract(H).subtract(M);
  725.             final T f12 = f1.multiply(2);
  726.             final T d   = f0.divide(f12);
  727.             final T fdf = f1.subtract(d.multiply(f2));
  728.             final T ds  = f0.divide(fdf);

  729.             final T shift = f0.divide(fdf.add(ds.multiply(ds.multiply(f3.divide(6)))));

  730.             H = H.subtract(shift);

  731.             if ((shift.abs().getReal()) <= 1.0e-12) {
  732.                 return H;
  733.             }

  734.         } while (++iter < 50);

  735.         throw new MathIllegalArgumentException(OrekitMessages.UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY,
  736.                                                iter);
  737.     }

  738.     /** Computes the mean anomaly from the elliptic eccentric anomaly.
  739.      * @param E eccentric anomaly (rad)
  740.      * @param e eccentricity
  741.      * @param <T> type of the field elements
  742.      * @return M the mean anomaly
  743.      */
  744.     public static <T extends RealFieldElement<T>> T ellipticEccentricToMean(final T E, final T e) {
  745.         return E.subtract(e.multiply(E.sin()));
  746.     }

  747.     /** Compute position from elliptic Keplerian parameters.
  748.      * @param a semi-major axis (m)
  749.      * @param e eccentricity
  750.      * @param i inclination (rad)
  751.      * @param pa Perigee Argument (rad)
  752.      * @param raan Right Ascension of Ascending Node (rad)
  753.      * @param v true anomaly (rad)
  754.      * @param mu central attraction coefficient (m³/s²)
  755.      * @param <T> type of the fiels elements
  756.      * @return position vector
  757.      */
  758.     public static <T extends RealFieldElement<T>> FieldVector3D<T> ellipticKeplerianToPosition(final T a, final T e, final T i,
  759.                                                                                                final T pa, final T raan, final T v,
  760.                                                                                                final double mu) {

  761.         // preliminary variables
  762.         final T cosRaan = raan.cos();
  763.         final T sinRaan = raan.sin();
  764.         final T cosPa   = pa.cos();
  765.         final T sinPa   = pa.sin();
  766.         final T cosI    = i.cos();
  767.         final T sinI    = i.sin();
  768.         final T crcp    = cosRaan.multiply(cosPa);
  769.         final T crsp    = cosRaan.multiply(sinPa);
  770.         final T srcp    = sinRaan.multiply(cosPa);
  771.         final T srsp    = sinRaan.multiply(sinPa);

  772.         // reference axes defining the orbital plane
  773.         final FieldVector3D<T> p = new FieldVector3D<>(crcp.subtract(cosI.multiply(srsp)),  srcp.add(cosI.multiply(crsp)), sinI.multiply(sinPa));
  774.         final FieldVector3D<T> q = new FieldVector3D<>(crsp.add(cosI.multiply(srcp)).negate(), cosI.multiply(crcp).subtract(srsp), sinI.multiply(cosPa));

  775.         // elliptic eccentric anomaly
  776.         final T uME2   = e.negate().add(1).multiply(e.add(1));
  777.         final T s1Me2  = uME2.sqrt();
  778.         final T E      = (a.getReal() < 0) ? trueToHyperbolicEccentric(v, e) : trueToEllipticEccentric(v, e);
  779.         final T cosE   = E.cos();
  780.         final T sinE   = E.sin();

  781.         // coordinates of position in the orbital plane
  782.         final T x      = a.multiply(cosE.subtract(e));
  783.         final T y      = a.multiply(sinE).multiply(s1Me2);

  784.         return new FieldVector3D<>(x, p, y, q);

  785.     }

  786.     /** Compute position from hyperbolic Keplerian parameters.
  787.      * @param a semi-major axis (m)
  788.      * @param e eccentricity
  789.      * @param i inclination (rad)
  790.      * @param pa Perigee Argument (rad)
  791.      * @param raan Right Ascension of Ascending Node (rad)
  792.      * @param v true anomaly (rad)
  793.      * @param mu central attraction coefficient (m³/s²)
  794.      * @param <T> type of the fiels elements
  795.      * @return position vector
  796.      */
  797.     public static <T extends RealFieldElement<T>> FieldVector3D<T> hyperbolicKeplerianToPosition(final T a, final T e, final T i,
  798.                                                                                                  final T pa, final T raan, final T v,
  799.                                                                                                  final double mu) {

  800.         // preliminary variables
  801.         final T cosRaan = raan.cos();
  802.         final T sinRaan = raan.sin();
  803.         final T cosPa   = pa.cos();
  804.         final T sinPa   = pa.sin();
  805.         final T cosI    = i.cos();
  806.         final T sinI    = i.sin();
  807.         final T crcp    = cosRaan.multiply(cosPa);
  808.         final T crsp    = cosRaan.multiply(sinPa);
  809.         final T srcp    = sinRaan.multiply(cosPa);
  810.         final T srsp    = sinRaan.multiply(sinPa);

  811.         // reference axes defining the orbital plane
  812.         final FieldVector3D<T> p = new FieldVector3D<>(crcp.subtract(cosI.multiply(srsp)),  srcp.add(cosI.multiply(crsp)), sinI.multiply(sinPa));
  813.         final FieldVector3D<T> q = new FieldVector3D<>(crsp.add(cosI.multiply(srcp)).negate(), cosI.multiply(crcp).subtract(srsp), sinI.multiply(cosPa));


  814.         // coordinates of position in the orbital plane
  815.         final T sinV      = v.sin();
  816.         final T cosV      = v.cos();
  817.         final T f         = a.multiply(e.multiply(e).negate().add(1));
  818.         final T posFactor = f.divide(e.multiply(cosV).add(1));
  819.         final T x         = posFactor.multiply(cosV);
  820.         final T y         = posFactor.multiply(sinV);

  821.         return new FieldVector3D<>(x, p, y, q);

  822.     }

  823.     /** {@inheritDoc} */
  824.     public T getEquinoctialEx() {
  825.         return e.multiply(pa.add(raan).cos());
  826.     }

  827.     /** {@inheritDoc} */
  828.     public T getEquinoctialExDot() {

  829.         if (!hasDerivatives()) {
  830.             return null;
  831.         }

  832.         @SuppressWarnings("unchecked")
  833.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  834.         final FieldDerivativeStructure<T> eDS    = factory.build(e,    eDot);
  835.         final FieldDerivativeStructure<T> paDS   = factory.build(pa,   paDot);
  836.         final FieldDerivativeStructure<T> raanDS = factory.build(raan, raanDot);
  837.         return eDS.multiply(paDS.add(raanDS).cos()).getPartialDerivative(1);

  838.     }

  839.     /** {@inheritDoc} */
  840.     public T getEquinoctialEy() {
  841.         return  e.multiply((pa.add(raan)).sin());
  842.     }

  843.     /** {@inheritDoc} */
  844.     public T getEquinoctialEyDot() {

  845.         if (!hasDerivatives()) {
  846.             return null;
  847.         }

  848.         @SuppressWarnings("unchecked")
  849.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  850.         final FieldDerivativeStructure<T> eDS    = factory.build(e,    eDot);
  851.         final FieldDerivativeStructure<T> paDS   = factory.build(pa,   paDot);
  852.         final FieldDerivativeStructure<T> raanDS = factory.build(raan, raanDot);
  853.         return eDS.multiply(paDS.add(raanDS).sin()).getPartialDerivative(1);

  854.     }

  855.     /** {@inheritDoc} */
  856.     public T getHx() {
  857.         // Check for equatorial retrograde orbit
  858.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  859.             return this.zero.add(Double.NaN);
  860.         }
  861.         return  raan.cos().multiply(i.divide(2).tan());
  862.     }

  863.     /** {@inheritDoc} */
  864.     public T getHxDot() {

  865.         if (!hasDerivatives()) {
  866.             return null;
  867.         }

  868.         // Check for equatorial retrograde orbit
  869.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  870.             return this.zero.add(Double.NaN);
  871.         }

  872.         @SuppressWarnings("unchecked")
  873.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  874.         final FieldDerivativeStructure<T> iDS    = factory.build(i,    iDot);
  875.         final FieldDerivativeStructure<T> raanDS = factory.build(raan, raanDot);
  876.         return raanDS.cos().multiply(iDS.multiply(0.5).tan()).getPartialDerivative(1);

  877.     }

  878.     /** {@inheritDoc} */
  879.     public T getHy() {
  880.         // Check for equatorial retrograde orbit
  881.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  882.             return this.zero.add(Double.NaN);
  883.         }
  884.         return  raan.sin().multiply(i.divide(2).tan());
  885.     }

  886.     /** {@inheritDoc} */
  887.     public T getHyDot() {

  888.         if (!hasDerivatives()) {
  889.             return null;
  890.         }

  891.         // Check for equatorial retrograde orbit
  892.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  893.             return this.zero.add(Double.NaN);
  894.         }

  895.         @SuppressWarnings("unchecked")
  896.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  897.         final FieldDerivativeStructure<T> iDS    = factory.build(i,    iDot);
  898.         final FieldDerivativeStructure<T> raanDS = factory.build(raan, raanDot);
  899.         return raanDS.sin().multiply(iDS.multiply(0.5).tan()).getPartialDerivative(1);

  900.     }

  901.     /** {@inheritDoc} */
  902.     public T getLv() {
  903.         return pa.add(raan).add(v);
  904.     }

  905.     /** {@inheritDoc} */
  906.     public T getLvDot() {
  907.         return hasDerivatives() ?
  908.                paDot.add(raanDot).add(vDot) :
  909.                null;
  910.     }

  911.     /** {@inheritDoc} */
  912.     public T getLE() {
  913.         return pa.add(raan).add(getEccentricAnomaly());
  914.     }

  915.     /** {@inheritDoc} */
  916.     public T getLEDot() {
  917.         return hasDerivatives() ?
  918.                paDot.add(raanDot).add(getEccentricAnomalyDot()) :
  919.                null;
  920.     }

  921.     /** {@inheritDoc} */
  922.     public T getLM() {
  923.         return pa.add(raan).add(getMeanAnomaly());
  924.     }

  925.     /** {@inheritDoc} */
  926.     public T getLMDot() {
  927.         return hasDerivatives() ?
  928.                paDot.add(raanDot).add(getMeanAnomalyDot()) :
  929.                null;
  930.     }

  931.     /** Compute position and velocity but not acceleration.
  932.      */
  933.     private void computePVWithoutA() {

  934.         if (partialPV != null) {
  935.             // already computed
  936.             return;
  937.         }

  938.         // preliminary variables
  939.         final T cosRaan = raan.cos();
  940.         final T sinRaan = raan.sin();
  941.         final T cosPa   = pa.cos();
  942.         final T sinPa   = pa.sin();
  943.         final T cosI    = i.cos();
  944.         final T sinI    = i.sin();
  945.         final T crcp    = cosRaan.multiply(cosPa);
  946.         final T crsp    = cosRaan.multiply(sinPa);
  947.         final T srcp    = sinRaan.multiply(cosPa);
  948.         final T srsp    = sinRaan.multiply(sinPa);

  949.         // reference axes defining the orbital plane
  950.         final FieldVector3D<T> p = new FieldVector3D<>(crcp.subtract(cosI.multiply(srsp)),  srcp.add(cosI.multiply(crsp)), sinI.multiply(sinPa));
  951.         final FieldVector3D<T> q = new FieldVector3D<>(crsp.add(cosI.multiply(srcp)).negate(), cosI.multiply(crcp).subtract(srsp), sinI.multiply(cosPa));

  952.         if (a.getReal() > 0) {

  953.             // elliptical case

  954.             // elliptic eccentric anomaly
  955.             final T uME2   = e.negate().add(1).multiply(e.add(1));
  956.             final T s1Me2  = uME2.sqrt();
  957.             final T E      = getEccentricAnomaly();
  958.             final T cosE   = E.cos();
  959.             final T sinE   = E.sin();

  960.             // coordinates of position and velocity in the orbital plane
  961.             final T x      = a.multiply(cosE.subtract(e));
  962.             final T y      = a.multiply(sinE).multiply(s1Me2);
  963.             final T factor = (a.reciprocal().multiply(getMu())).sqrt().divide(e.negate().multiply(cosE).add(1));
  964.             final T xDot   = sinE.negate().multiply(factor);
  965.             final T yDot   = cosE.multiply(s1Me2).multiply(factor);

  966.             final FieldVector3D<T> position = new FieldVector3D<>(x, p, y, q);
  967.             final FieldVector3D<T> velocity = new FieldVector3D<>(xDot, p, yDot, q);
  968.             partialPV = new FieldPVCoordinates<>(position, velocity);

  969.         } else {

  970.             // hyperbolic case

  971.             // compute position and velocity factors
  972.             final T sinV      = v.sin();
  973.             final T cosV      = v.cos();
  974.             final T f         = a.multiply(e.multiply(e).negate().add(1));
  975.             final T posFactor = f.divide(e.multiply(cosV).add(1));
  976.             final T velFactor = f.reciprocal().multiply(getMu()).sqrt();

  977.             final FieldVector3D<T> position     = new FieldVector3D<>(posFactor.multiply(cosV), p, posFactor.multiply(sinV), q);
  978.             final FieldVector3D<T> velocity     = new FieldVector3D<>(velFactor.multiply(sinV).negate(), p, velFactor.multiply(e.add(cosV)), q);
  979.             partialPV = new FieldPVCoordinates<>(position, velocity);

  980.         }

  981.     }

  982.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  983.      * <p>
  984.      * This method should be called only when {@link #hasDerivatives()} returns true.
  985.      * </p>
  986.      * @return non-Keplerian part of the acceleration
  987.      */
  988.     private FieldVector3D<T> nonKeplerianAcceleration() {

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

  991.         final T nonKeplerianMeanMotion = getMeanAnomalyDot().subtract(getKeplerianMeanMotion());
  992.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  993.                                  add(dCdP[3][1].multiply(eDot)).
  994.                                  add(dCdP[3][2].multiply(iDot)).
  995.                                  add(dCdP[3][3].multiply(paDot)).
  996.                                  add(dCdP[3][4].multiply(raanDot)).
  997.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  998.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  999.                                  add(dCdP[4][1].multiply(eDot)).
  1000.                                  add(dCdP[4][2].multiply(iDot)).
  1001.                                  add(dCdP[4][3].multiply(paDot)).
  1002.                                  add(dCdP[4][4].multiply(raanDot)).
  1003.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  1004.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  1005.                                  add(dCdP[5][1].multiply(eDot)).
  1006.                                  add(dCdP[5][2].multiply(iDot)).
  1007.                                  add(dCdP[5][3].multiply(paDot)).
  1008.                                  add(dCdP[5][4].multiply(raanDot)).
  1009.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

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

  1011.     }

  1012.     /** {@inheritDoc} */
  1013.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  1014.         // position and velocity
  1015.         computePVWithoutA();

  1016.         // acceleration
  1017.         final T r2 = partialPV.getPosition().getNormSq();
  1018.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(-getMu()),
  1019.                                                                            partialPV.getPosition());
  1020.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  1021.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  1022.                                               keplerianAcceleration;

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

  1024.     }

  1025.     /** {@inheritDoc} */
  1026.     public FieldKeplerianOrbit<T> shiftedBy(final double dt) {
  1027.         return shiftedBy(getDate().getField().getZero().add(dt));
  1028.     }

  1029.     /** {@inheritDoc} */
  1030.     public FieldKeplerianOrbit<T> shiftedBy(final T dt) {

  1031.         // use Keplerian-only motion
  1032.         final FieldKeplerianOrbit<T> keplerianShifted = new FieldKeplerianOrbit<>(a, e, i, pa, raan,
  1033.                                                                                   getKeplerianMeanMotion().multiply(dt).add(getMeanAnomaly()),
  1034.                                                                                   PositionAngle.MEAN, getFrame(), getDate().shiftedBy(dt), getMu());

  1035.         if (hasDerivatives()) {

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

  1038.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  1039.             keplerianShifted.computePVWithoutA();
  1040.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(),
  1041.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  1042.             final T   fixedR2 = fixedP.getNormSq();
  1043.             final T   fixedR  = fixedR2.sqrt();
  1044.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(),
  1045.                                                                  dt, nonKeplerianAcceleration);
  1046.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(-getMu()),
  1047.                                                                  keplerianShifted.partialPV.getPosition(),
  1048.                                                                  one, nonKeplerianAcceleration);

  1049.             // build a new orbit, taking non-Keplerian acceleration into account
  1050.             return new FieldKeplerianOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  1051.                                                                                  fixedP, fixedV, fixedA),
  1052.                                              keplerianShifted.getFrame(), keplerianShifted.getMu());

  1053.         } else {
  1054.             // Keplerian-only motion is all we can do
  1055.             return keplerianShifted;
  1056.         }

  1057.     }

  1058.     /** {@inheritDoc}
  1059.      * <p>
  1060.      * The interpolated instance is created by polynomial Hermite interpolation
  1061.      * on Keplerian elements, without derivatives (which means the interpolation
  1062.      * falls back to Lagrange interpolation only).
  1063.      * </p>
  1064.      * <p>
  1065.      * As this implementation of interpolation is polynomial, it should be used only
  1066.      * with small samples (about 10-20 points) in order to avoid <a
  1067.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  1068.      * and numerical problems (including NaN appearing).
  1069.      * </p>
  1070.      * <p>
  1071.      * If orbit interpolation on large samples is needed, using the {@link
  1072.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  1073.      * low-level interpolation. The Ephemeris class automatically handles selection of
  1074.      * a neighboring sub-sample with a predefined number of point from a large global sample
  1075.      * in a thread-safe way.
  1076.      * </p>
  1077.      */
  1078.     public FieldKeplerianOrbit<T> interpolate(final FieldAbsoluteDate<T> date, final Stream<FieldOrbit<T>> sample) {

  1079.         // first pass to check if derivatives are available throughout the sample
  1080.         final List<FieldOrbit<T>> list = sample.collect(Collectors.toList());
  1081.         boolean useDerivatives = true;
  1082.         for (final FieldOrbit<T> orbit : list) {
  1083.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  1084.         }

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

  1087.         // second pass to feed interpolator
  1088.         FieldAbsoluteDate<T> previousDate = null;
  1089.         T                    previousPA   = zero.add(Double.NaN);
  1090.         T                    previousRAAN = zero.add(Double.NaN);
  1091.         T                    previousM    = zero.add(Double.NaN);
  1092.         for (final FieldOrbit<T> orbit : list) {
  1093.             final FieldKeplerianOrbit<T> kep = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(orbit);
  1094.             final T continuousPA;
  1095.             final T continuousRAAN;
  1096.             final T continuousM;
  1097.             if (previousDate == null) {
  1098.                 continuousPA   = kep.getPerigeeArgument();
  1099.                 continuousRAAN = kep.getRightAscensionOfAscendingNode();
  1100.                 continuousM    = kep.getMeanAnomaly();
  1101.             } else {
  1102.                 final T dt      = kep.getDate().durationFrom(previousDate);
  1103.                 final T keplerM = previousM.add(kep.getKeplerianMeanMotion().multiply(dt));
  1104.                 continuousPA   = normalizeAngle(kep.getPerigeeArgument(), previousPA);
  1105.                 continuousRAAN = normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN);
  1106.                 continuousM    = normalizeAngle(kep.getMeanAnomaly(), keplerM);
  1107.             }
  1108.             previousDate = kep.getDate();
  1109.             previousPA   = continuousPA;
  1110.             previousRAAN = continuousRAAN;
  1111.             previousM    = continuousM;
  1112.             final T[] toAdd = MathArrays.buildArray(getA().getField(), 6);
  1113.             toAdd[0] = kep.getA();
  1114.             toAdd[1] = kep.getE();
  1115.             toAdd[2] = kep.getI();
  1116.             toAdd[3] = continuousPA;
  1117.             toAdd[4] = continuousRAAN;
  1118.             toAdd[5] = continuousM;
  1119.             if (useDerivatives) {
  1120.                 final T[] toAddDot = MathArrays.buildArray(one.getField(), 6);
  1121.                 toAddDot[0] = kep.getADot();
  1122.                 toAddDot[1] = kep.getEDot();
  1123.                 toAddDot[2] = kep.getIDot();
  1124.                 toAddDot[3] = kep.getPerigeeArgumentDot();
  1125.                 toAddDot[4] = kep.getRightAscensionOfAscendingNodeDot();
  1126.                 toAddDot[5] = kep.getMeanAnomalyDot();
  1127.                 interpolator.addSamplePoint(kep.getDate().durationFrom(date),
  1128.                                             toAdd, toAddDot);
  1129.             } else {
  1130.                 interpolator.addSamplePoint(this.zero.add(kep.getDate().durationFrom(date)),
  1131.                                             toAdd);
  1132.             }
  1133.         }

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

  1136.         // build a new interpolated instance
  1137.         return new FieldKeplerianOrbit<>(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  1138.                                          interpolated[0][3], interpolated[0][4], interpolated[0][5],
  1139.                                          interpolated[1][0], interpolated[1][1], interpolated[1][2],
  1140.                                          interpolated[1][3], interpolated[1][4], interpolated[1][5],
  1141.                                          PositionAngle.MEAN, getFrame(), date, getMu());

  1142.     }

  1143.     /** {@inheritDoc} */
  1144.     protected T[][] computeJacobianMeanWrtCartesian() {
  1145.         if (a.getReal() > 0) {
  1146.             return computeJacobianMeanWrtCartesianElliptical();
  1147.         } else {
  1148.             return computeJacobianMeanWrtCartesianHyperbolic();
  1149.         }
  1150.     }

  1151.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1152.      * <p>
  1153.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1154.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1155.      * yDot for j=4, zDot for j=5).
  1156.      * </p>
  1157.      * @return 6x6 Jacobian matrix
  1158.      */
  1159.     private T[][] computeJacobianMeanWrtCartesianElliptical() {

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

  1161.         // compute various intermediate parameters
  1162.         computePVWithoutA();
  1163.         final FieldVector3D<T> position = partialPV.getPosition();
  1164.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  1165.         final FieldVector3D<T> momentum = partialPV.getMomentum();
  1166.         final T v2         = velocity.getNormSq();
  1167.         final T r2         = position.getNormSq();
  1168.         final T r          = r2.sqrt();
  1169.         final T r3         = r.multiply(r2);

  1170.         final T px         = position.getX();
  1171.         final T py         = position.getY();
  1172.         final T pz         = position.getZ();
  1173.         final T vx         = velocity.getX();
  1174.         final T vy         = velocity.getY();
  1175.         final T vz         = velocity.getZ();
  1176.         final T mx         = momentum.getX();
  1177.         final T my         = momentum.getY();
  1178.         final T mz         = momentum.getZ();

  1179.         final double mu         = getMu();
  1180.         final T sqrtMuA    = a.multiply(mu).sqrt();
  1181.         final T sqrtAoMu   = a.divide(mu).sqrt();
  1182.         final T a2         = a.multiply(a);
  1183.         final T twoA       = a.multiply(2);
  1184.         final T rOnA       = r.divide(a);

  1185.         final T oMe2       = e.multiply(e).negate().add(1);
  1186.         final T epsilon    = oMe2.sqrt();
  1187.         final T sqrtRec    = epsilon.reciprocal();

  1188.         final T cosI       = i.cos();
  1189.         final T sinI       = i.sin();
  1190.         final T cosPA      = pa.cos();
  1191.         final T sinPA      = pa.sin();

  1192.         final T pv         = FieldVector3D.dotProduct(position, velocity);
  1193.         final T cosE       = a.subtract(r).divide(a.multiply(e));
  1194.         final T sinE       = pv.divide(e.multiply(sqrtMuA));

  1195.         // da
  1196.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(2).divide(r3), position);
  1197.         final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(2 / mu));
  1198.         fillHalfRow(this.one, vectorAR,    jacobian[0], 0);
  1199.         fillHalfRow(this.one, vectorARDot, jacobian[0], 3);

  1200.         // de
  1201.         final T factorER3 = pv.divide(twoA);
  1202.         final FieldVector3D<T> vectorER   = new FieldVector3D<>(cosE.multiply(v2).divide(r.multiply(mu)), position,
  1203.                                                                 sinE.divide(sqrtMuA), velocity,
  1204.                                                                 factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorAR);
  1205.         final FieldVector3D<T> vectorERDot = new FieldVector3D<>(sinE.divide(sqrtMuA), position,
  1206.                                                                  cosE.multiply(2 / mu).multiply(r), velocity,
  1207.                                                                  factorER3.negate().multiply(sinE).divide(sqrtMuA), vectorARDot);
  1208.         fillHalfRow(this.one, vectorER,    jacobian[1], 0);
  1209.         fillHalfRow(this.one, vectorERDot, jacobian[1], 3);

  1210.         // dE / dr (Eccentric anomaly)
  1211.         final T coefE = cosE.divide(e.multiply(sqrtMuA));
  1212.         final FieldVector3D<T>  vectorEAnR =
  1213.             new FieldVector3D<>(sinE.negate().multiply(v2).divide(e.multiply(r).multiply(mu)), position, coefE, velocity,
  1214.                                 factorER3.negate().multiply(coefE), vectorAR);

  1215.         // dE / drDot
  1216.         final FieldVector3D<T>  vectorEAnRDot =
  1217.             new FieldVector3D<>(sinE.multiply(-2).multiply(r).divide(e.multiply(mu)), velocity, coefE, position,
  1218.                                 factorER3.negate().multiply(coefE), vectorARDot);

  1219.         // precomputing some more factors
  1220.         final T s1 = sinE.negate().multiply(pz).divide(r).subtract(cosE.multiply(vz).multiply(sqrtAoMu));
  1221.         final T s2 = cosE.negate().multiply(pz).divide(r3);
  1222.         final T s3 = sinE.multiply(vz).divide(sqrtMuA.multiply(-2));
  1223.         final T t1 = sqrtRec.multiply(cosE.multiply(pz).divide(r).subtract(sinE.multiply(vz).multiply(sqrtAoMu)));
  1224.         final T t2 = sqrtRec.multiply(sinE.negate().multiply(pz).divide(r3));
  1225.         final T t3 = sqrtRec.multiply(cosE.subtract(e)).multiply(vz).divide(sqrtMuA.multiply(2));
  1226.         final T t4 = sqrtRec.multiply(e.multiply(sinI).multiply(cosPA).multiply(sqrtRec).subtract(vz.multiply(sqrtAoMu)));
  1227.         final FieldVector3D<T> s = new FieldVector3D<>(cosE.divide(r), this.PLUS_K,
  1228.                                                        s1,       vectorEAnR,
  1229.                                                        s2,       position,
  1230.                                                        s3,       vectorAR);
  1231.         final FieldVector3D<T> sDot = new FieldVector3D<>(sinE.negate().multiply(sqrtAoMu), this.PLUS_K,
  1232.                                                           s1,               vectorEAnRDot,
  1233.                                                           s3,               vectorARDot);
  1234.         final FieldVector3D<T> t =
  1235.             new FieldVector3D<>(sqrtRec.multiply(sinE).divide(r), this.PLUS_K).add(new FieldVector3D<>(t1, vectorEAnR,
  1236.                                                                                                        t2, position,
  1237.                                                                                                        t3, vectorAR,
  1238.                                                                                                        t4, vectorER));
  1239.         final FieldVector3D<T> tDot = new FieldVector3D<>(sqrtRec.multiply(cosE.subtract(e)).multiply(sqrtAoMu), this.PLUS_K,
  1240.                                                           t1,                                                    vectorEAnRDot,
  1241.                                                           t3,                                                    vectorARDot,
  1242.                                                           t4,                                                    vectorERDot);

  1243.         // di
  1244.         final T factorI1 = sinI.negate().multiply(sqrtRec).divide(sqrtMuA);
  1245.         final T i1 =  factorI1;
  1246.         final T i2 =  factorI1.negate().multiply(mz).divide(twoA);
  1247.         final T i3 =  factorI1.multiply(mz).multiply(e).divide(oMe2);
  1248.         final T i4 = cosI.multiply(sinPA);
  1249.         final T i5 = cosI.multiply(cosPA);
  1250.         fillHalfRow(i1, new FieldVector3D<>(vy, vx.negate(), this.zero), i2, vectorAR, i3, vectorER, i4, s, i5, t,
  1251.                     jacobian[2], 0);
  1252.         fillHalfRow(i1, new FieldVector3D<>(py.negate(), px, this.zero), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
  1253.                     jacobian[2], 3);

  1254.         // dpa
  1255.         fillHalfRow(cosPA.divide(sinI), s,    sinPA.negate().divide(sinI), t,    jacobian[3], 0);
  1256.         fillHalfRow(cosPA.divide(sinI), sDot, sinPA.negate().divide(sinI), tDot, jacobian[3], 3);

  1257.         // dRaan
  1258.         final T factorRaanR = (a.multiply(mu).multiply(oMe2).multiply(sinI).multiply(sinI)).reciprocal();
  1259.         fillHalfRow( factorRaanR.negate().multiply(my), new FieldVector3D<>(zero, vz, vy.negate()),
  1260.                      factorRaanR.multiply(mx), new FieldVector3D<>(vz.negate(), zero,  vx),
  1261.                      jacobian[4], 0);
  1262.         fillHalfRow(factorRaanR.negate().multiply(my), new FieldVector3D<>(zero, pz.negate(),  py),
  1263.                      factorRaanR.multiply(mx), new FieldVector3D<>(pz, zero, px.negate()),
  1264.                      jacobian[4], 3);

  1265.         // dM
  1266.         fillHalfRow(rOnA, vectorEAnR,    sinE.negate(), vectorER,    jacobian[5], 0);
  1267.         fillHalfRow(rOnA, vectorEAnRDot, sinE.negate(), vectorERDot, jacobian[5], 3);

  1268.         return jacobian;

  1269.     }

  1270.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1271.      * <p>
  1272.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1273.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1274.      * yDot for j=4, zDot for j=5).
  1275.      * </p>
  1276.      * @return 6x6 Jacobian matrix
  1277.      */
  1278.     private T[][] computeJacobianMeanWrtCartesianHyperbolic() {

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

  1280.         // compute various intermediate parameters
  1281.         computePVWithoutA();
  1282.         final FieldVector3D<T> position = partialPV.getPosition();
  1283.         final FieldVector3D<T> velocity = partialPV.getVelocity();
  1284.         final FieldVector3D<T> momentum = partialPV.getMomentum();
  1285.         final T r2         = position.getNormSq();
  1286.         final T r          = r2.sqrt();
  1287.         final T r3         = r.multiply(r2);

  1288.         final T x          = position.getX();
  1289.         final T y          = position.getY();
  1290.         final T z          = position.getZ();
  1291.         final T vx         = velocity.getX();
  1292.         final T vy         = velocity.getY();
  1293.         final T vz         = velocity.getZ();
  1294.         final T mx         = momentum.getX();
  1295.         final T my         = momentum.getY();
  1296.         final T mz         = momentum.getZ();

  1297.         final double mu         = getMu();
  1298.         final T absA       = a.negate();
  1299.         final T sqrtMuA    = absA.multiply(mu).sqrt();
  1300.         final T a2         = a.multiply(a);
  1301.         final T rOa        = r.divide(absA);

  1302.         final T cosI       = i.cos();
  1303.         final T sinI       = i.sin();

  1304.         final T pv         = FieldVector3D.dotProduct(position, velocity);

  1305.         // da
  1306.         final FieldVector3D<T> vectorAR = new FieldVector3D<>(a2.multiply(-2).divide(r3), position);
  1307.         final FieldVector3D<T> vectorARDot = velocity.scalarMultiply(a2.multiply(-2 / mu));
  1308.         fillHalfRow(this.one.negate(), vectorAR,    jacobian[0], 0);
  1309.         fillHalfRow(this.one.negate(), vectorARDot, jacobian[0], 3);

  1310.         // differentials of the momentum
  1311.         final T m      = momentum.getNorm();
  1312.         final T oOm    = m.reciprocal();
  1313.         final FieldVector3D<T> dcXP = new FieldVector3D<>(this.zero, vz, vy.negate());
  1314.         final FieldVector3D<T> dcYP = new FieldVector3D<>(vz.negate(),   this.zero,  vx);
  1315.         final FieldVector3D<T> dcZP = new FieldVector3D<>( vy, vx.negate(),   this.zero);
  1316.         final FieldVector3D<T> dcXV = new FieldVector3D<>(  this.zero,  z.negate(),   y);
  1317.         final FieldVector3D<T> dcYV = new FieldVector3D<>(  z,   this.zero,  x.negate());
  1318.         final FieldVector3D<T> dcZV = new FieldVector3D<>( y.negate(),   x,   this.zero);
  1319.         final FieldVector3D<T> dCP  = new FieldVector3D<>(mx.multiply(oOm), dcXP, my.multiply(oOm), dcYP, mz.multiply(oOm), dcZP);
  1320.         final FieldVector3D<T> dCV  = new FieldVector3D<>(mx.multiply(oOm), dcXV, my.multiply(oOm), dcYV, mz.multiply(oOm), dcZV);

  1321.         // dp
  1322.         final T mOMu   = m.divide(mu);
  1323.         final FieldVector3D<T> dpP  = new FieldVector3D<>(mOMu.multiply(2), dCP);
  1324.         final FieldVector3D<T> dpV  = new FieldVector3D<>(mOMu.multiply(2), dCV);

  1325.         // de
  1326.         final T p      = m.multiply(mOMu);
  1327.         final T moO2ae = absA.multiply(2).multiply(e).reciprocal();
  1328.         final T m2OaMu = p.negate().divide(absA);
  1329.         fillHalfRow(moO2ae, dpP, m2OaMu.multiply(moO2ae), vectorAR,    jacobian[1], 0);
  1330.         fillHalfRow(moO2ae, dpV, m2OaMu.multiply(moO2ae), vectorARDot, jacobian[1], 3);

  1331.         // di
  1332.         final T cI1 = m.multiply(sinI).reciprocal();
  1333.         final T cI2 = cosI.multiply(cI1);
  1334.         fillHalfRow(cI2, dCP, cI1.negate(), dcZP, jacobian[2], 0);
  1335.         fillHalfRow(cI2, dCV, cI1.negate(), dcZV, jacobian[2], 3);


  1336.         // dPA
  1337.         final T cP1     =  y.multiply(oOm);
  1338.         final T cP2     =  x.negate().multiply(oOm);
  1339.         final T cP3     =  mx.multiply(cP1).add(my.multiply(cP2)).negate();
  1340.         final T cP4     =  cP3.multiply(oOm);
  1341.         final T cP5     =  r2.multiply(sinI).multiply(sinI).negate().reciprocal();
  1342.         final T cP6     = z.multiply(cP5);
  1343.         final T cP7     = cP3.multiply(cP5);
  1344.         final FieldVector3D<T> dacP  = new FieldVector3D<>(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new FieldVector3D<>(my.negate(), mx, this.zero));
  1345.         final FieldVector3D<T> dacV  = new FieldVector3D<>(cP1, dcXV, cP2, dcYV, cP4, dCV);
  1346.         final FieldVector3D<T> dpoP  = new FieldVector3D<>(cP6, dacP, cP7, this.PLUS_K);
  1347.         final FieldVector3D<T> dpoV  = new FieldVector3D<>(cP6, dacV);

  1348.         final T re2     = r2.multiply(e).multiply(e);
  1349.         final T recOre2 = p.subtract(r).divide(re2);
  1350.         final T resOre2 = pv.multiply(mOMu).divide(re2);
  1351.         final FieldVector3D<T> dreP  = new FieldVector3D<>(mOMu, velocity, pv.divide(mu), dCP);
  1352.         final FieldVector3D<T> dreV  = new FieldVector3D<>(mOMu, position, pv.divide(mu), dCV);
  1353.         final FieldVector3D<T> davP  = new FieldVector3D<>(resOre2.negate(), dpP, recOre2, dreP, resOre2.divide(r), position);
  1354.         final FieldVector3D<T> davV  = new FieldVector3D<>(resOre2.negate(), dpV, recOre2, dreV);
  1355.         fillHalfRow(this.one, dpoP, this.one.negate(), davP, jacobian[3], 0);
  1356.         fillHalfRow(this.one, dpoV, this.one.negate(), davV, jacobian[3], 3);

  1357.         // dRAAN
  1358.         final T cO0 = cI1.multiply(cI1);
  1359.         final T cO1 =  mx.multiply(cO0);
  1360.         final T cO2 =  my.negate().multiply(cO0);
  1361.         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
  1362.         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);

  1363.         // dM
  1364.         final T s2a    = pv.divide(absA.multiply(2));
  1365.         final T oObux  = m.multiply(m).add(absA.multiply(mu)).sqrt().reciprocal();
  1366.         final T scasbu = pv.multiply(oObux);
  1367.         final FieldVector3D<T> dauP = new FieldVector3D<>(sqrtMuA.reciprocal(), velocity, s2a.negate().divide(sqrtMuA), vectorAR);
  1368.         final FieldVector3D<T> dauV = new FieldVector3D<>(sqrtMuA.reciprocal(), position, s2a.negate().divide(sqrtMuA), vectorARDot);
  1369.         final FieldVector3D<T> dbuP = new FieldVector3D<>(oObux.multiply(mu / 2), vectorAR,    m.multiply(oObux), dCP);
  1370.         final FieldVector3D<T> dbuV = new FieldVector3D<>(oObux.multiply(mu / 2), vectorARDot, m.multiply(oObux), dCV);
  1371.         final FieldVector3D<T> dcuP = new FieldVector3D<>(oObux, velocity, scasbu.negate().multiply(oObux), dbuP);
  1372.         final FieldVector3D<T> dcuV = new FieldVector3D<>(oObux, position, scasbu.negate().multiply(oObux), dbuV);
  1373.         fillHalfRow(this.one, dauP, e.negate().divide(rOa.add(1)), dcuP, jacobian[5], 0);
  1374.         fillHalfRow(this.one, dauV, e.negate().divide(rOa.add(1)), dcuV, jacobian[5], 3);

  1375.         return jacobian;

  1376.     }

  1377.     /** {@inheritDoc} */
  1378.     protected T[][] computeJacobianEccentricWrtCartesian() {
  1379.         if (a.getReal() > 0) {
  1380.             return computeJacobianEccentricWrtCartesianElliptical();
  1381.         } else {
  1382.             return computeJacobianEccentricWrtCartesianHyperbolic();
  1383.         }
  1384.     }

  1385.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1386.      * <p>
  1387.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1388.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1389.      * yDot for j=4, zDot for j=5).
  1390.      * </p>
  1391.      * @return 6x6 Jacobian matrix
  1392.      */
  1393.     private T[][] computeJacobianEccentricWrtCartesianElliptical() {

  1394.         // start by computing the Jacobian with mean angle
  1395.         final T[][] jacobian = computeJacobianMeanWrtCartesianElliptical();

  1396.         // Differentiating the Kepler equation M = E - e sin E leads to:
  1397.         // dM = (1 - e cos E) dE - sin E de
  1398.         // which is inverted and rewritten as:
  1399.         // dE = a/r dM + sin E a/r de
  1400.         final T eccentricAnomaly = getEccentricAnomaly();
  1401.         final T cosE             = eccentricAnomaly.cos();
  1402.         final T sinE             = eccentricAnomaly.sin();
  1403.         final T aOr              = e.negate().multiply(cosE).add(1).reciprocal();

  1404.         // update anomaly row
  1405.         final T[] eRow           = jacobian[1];
  1406.         final T[] anomalyRow     = jacobian[5];
  1407.         for (int j = 0; j < anomalyRow.length; ++j) {
  1408.             anomalyRow[j] = aOr.multiply(anomalyRow[j].add(sinE.multiply(eRow[j])));
  1409.         }

  1410.         return jacobian;

  1411.     }

  1412.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1413.      * <p>
  1414.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1415.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1416.      * yDot for j=4, zDot for j=5).
  1417.      * </p>
  1418.      * @return 6x6 Jacobian matrix
  1419.      */
  1420.     private T[][] computeJacobianEccentricWrtCartesianHyperbolic() {

  1421.         // start by computing the Jacobian with mean angle
  1422.         final T[][] jacobian = computeJacobianMeanWrtCartesianHyperbolic();

  1423.         // Differentiating the Kepler equation M = e sinh H - H leads to:
  1424.         // dM = (e cosh H - 1) dH + sinh H de
  1425.         // which is inverted and rewritten as:
  1426.         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
  1427.         final T H      = getEccentricAnomaly();
  1428.         final T coshH  = H.cosh();
  1429.         final T sinhH  = H.sinh();
  1430.         final T absaOr = e.multiply(coshH).subtract(1).reciprocal();
  1431.         // update anomaly row
  1432.         final T[] eRow       = jacobian[1];
  1433.         final T[] anomalyRow = jacobian[5];

  1434.         for (int j = 0; j < anomalyRow.length; ++j) {
  1435.             anomalyRow[j] = absaOr.multiply(anomalyRow[j].subtract(sinhH.multiply(eRow[j])));

  1436.         }

  1437.         return jacobian;

  1438.     }

  1439.     /** {@inheritDoc} */
  1440.     protected T[][] computeJacobianTrueWrtCartesian() {
  1441.         if (a.getReal() > 0) {
  1442.             return computeJacobianTrueWrtCartesianElliptical();
  1443.         } else {
  1444.             return computeJacobianTrueWrtCartesianHyperbolic();
  1445.         }
  1446.     }

  1447.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1448.      * <p>
  1449.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1450.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1451.      * yDot for j=4, zDot for j=5).
  1452.      * </p>
  1453.      * @return 6x6 Jacobian matrix
  1454.      */
  1455.     private T[][] computeJacobianTrueWrtCartesianElliptical() {

  1456.         // start by computing the Jacobian with eccentric angle
  1457.         final T[][] jacobian = computeJacobianEccentricWrtCartesianElliptical();
  1458.         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
  1459.         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
  1460.         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
  1461.         // which is inverted and rewritten as:
  1462.         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
  1463.         final T e2               = e.multiply(e);
  1464.         final T oMe2             = e2.negate().add(1);
  1465.         final T epsilon          = oMe2.sqrt();
  1466.         final T eccentricAnomaly = getEccentricAnomaly();
  1467.         final T cosE             = eccentricAnomaly.cos();
  1468.         final T sinE             = eccentricAnomaly.sin();
  1469.         final T aOr              = e.multiply(cosE).negate().add(1).reciprocal();
  1470.         final T aFactor          = epsilon.multiply(aOr);
  1471.         final T eFactor          = sinE.multiply(aOr).divide(epsilon);

  1472.         // update anomaly row
  1473.         final T[] eRow           = jacobian[1];
  1474.         final T[] anomalyRow     = jacobian[5];
  1475.         for (int j = 0; j < anomalyRow.length; ++j) {
  1476.             anomalyRow[j] = aFactor.multiply(anomalyRow[j]).add(eFactor.multiply(eRow[j]));
  1477.         }
  1478.         return jacobian;

  1479.     }

  1480.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1481.      * <p>
  1482.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1483.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1484.      * yDot for j=4, zDot for j=5).
  1485.      * </p>
  1486.      * @return 6x6 Jacobian matrix
  1487.      */
  1488.     private T[][] computeJacobianTrueWrtCartesianHyperbolic() {

  1489.         // start by computing the Jacobian with eccentric angle
  1490.         final T[][] jacobian = computeJacobianEccentricWrtCartesianHyperbolic();

  1491.         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
  1492.         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
  1493.         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
  1494.         // which is inverted and rewritten as:
  1495.         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
  1496.         final T e2       = e.multiply(e);
  1497.         final T e2Mo     = e2.subtract(1);
  1498.         final T epsilon  = e2Mo.sqrt();
  1499.         final T H        = getEccentricAnomaly();
  1500.         final T coshH    = H.cosh();
  1501.         final T sinhH    = H.sinh();
  1502.         final T aOr      = e.multiply(coshH).subtract(1).reciprocal();
  1503.         final T aFactor  = epsilon.multiply(aOr);
  1504.         final T eFactor  = sinhH .multiply(aOr).divide(epsilon);

  1505.         // update anomaly row
  1506.         final T[] eRow           = jacobian[1];
  1507.         final T[] anomalyRow     = jacobian[5];
  1508.         for (int j = 0; j < anomalyRow.length; ++j) {
  1509.             anomalyRow[j] = aFactor.multiply(anomalyRow[j]).subtract(eFactor.multiply(eRow[j]));
  1510.         }

  1511.         return jacobian;

  1512.     }

  1513.     /** {@inheritDoc} */
  1514.     public void addKeplerContribution(final PositionAngle type, final double gm,
  1515.                                       final T[] pDot) {
  1516.         final T oMe2;
  1517.         final T ksi;
  1518.         final T absA = a.abs();
  1519.         final T n    = absA.reciprocal().multiply(gm).sqrt().divide(absA);
  1520.         switch (type) {
  1521.             case MEAN :
  1522.                 pDot[5] = pDot[5].add(n);
  1523.                 break;
  1524.             case ECCENTRIC :
  1525.                 oMe2 = e.multiply(e).negate().add(1).abs();
  1526.                 ksi  = e.multiply(v.cos()).add(1);
  1527.                 pDot[5] = pDot[5].add( n.multiply(ksi).divide(oMe2));
  1528.                 break;
  1529.             case TRUE :
  1530.                 oMe2 = e.multiply(e).negate().add(1).abs();
  1531.                 ksi  = e.multiply(v.cos()).add(1);
  1532.                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
  1533.                 break;
  1534.             default :
  1535.                 throw new OrekitInternalError(null);
  1536.         }
  1537.     }

  1538.     /**  Returns a string representation of this Keplerian parameters object.
  1539.      * @return a string representation of this object
  1540.      */
  1541.     public String toString() {
  1542.         return new StringBuffer().append("Keplerian parameters: ").append('{').
  1543.                                   append("a: ").append(a.getReal()).
  1544.                                   append("; e: ").append(e.getReal()).
  1545.                                   append("; i: ").append(FastMath.toDegrees(i.getReal())).
  1546.                                   append("; pa: ").append(FastMath.toDegrees(pa.getReal())).
  1547.                                   append("; raan: ").append(FastMath.toDegrees(raan.getReal())).
  1548.                                   append("; v: ").append(FastMath.toDegrees(v.getReal())).
  1549.                                   append(";}").toString();
  1550.     }

  1551.     /**
  1552.      * Normalize an angle in a 2&pi; wide interval around a center value.
  1553.      * <p>This method has three main uses:</p>
  1554.      * <ul>
  1555.      *   <li>normalize an angle between 0 and 2&pi;:<br/>
  1556.      *       {@code a = MathUtils.normalizeAngle(a, FastMath.PI);}</li>
  1557.      *   <li>normalize an angle between -&pi; and +&pi;<br/>
  1558.      *       {@code a = MathUtils.normalizeAngle(a, 0.0);}</li>
  1559.      *   <li>compute the angle between two defining angular positions:<br>
  1560.      *       {@code angle = MathUtils.normalizeAngle(end, start) - start;}</li>
  1561.      * </ul>
  1562.      * <p>Note that due to numerical accuracy and since &pi; cannot be represented
  1563.      * exactly, the result interval is <em>closed</em>, it cannot be half-closed
  1564.      * as would be more satisfactory in a purely mathematical view.</p>
  1565.      * @param a angle to normalize
  1566.      * @param center center of the desired 2&pi; interval for the result
  1567.      * @param <T> the type of the field elements
  1568.      * @return a-2k&pi; with integer k and center-&pi; &lt;= a-2k&pi; &lt;= center+&pi;
  1569.      */
  1570.     public static <T extends RealFieldElement<T>> T normalizeAngle(final T a, final T center) {
  1571.         return a.subtract(2 * FastMath.PI * FastMath.floor((a.getReal() + FastMath.PI - center.getReal()) / (2 * FastMath.PI)));
  1572.     }

  1573.     @Override
  1574.     public KeplerianOrbit toOrbit() {
  1575.         if (hasDerivatives()) {
  1576.             return new KeplerianOrbit(a.getReal(), e.getReal(), i.getReal(),
  1577.                                       pa.getReal(), raan.getReal(), v.getReal(),
  1578.                                       aDot.getReal(), eDot.getReal(), iDot.getReal(),
  1579.                                       paDot.getReal(), raanDot.getReal(), vDot.getReal(),
  1580.                                       PositionAngle.TRUE,
  1581.                                       getFrame(), getDate().toAbsoluteDate(), getMu());
  1582.         } else {
  1583.             return new KeplerianOrbit(a.getReal(), e.getReal(), i.getReal(),
  1584.                                       pa.getReal(), raan.getReal(), v.getReal(),
  1585.                                       PositionAngle.TRUE,
  1586.                                       getFrame(), getDate().toAbsoluteDate(), getMu());
  1587.         }
  1588.     }


  1589. }