KeplerianOrbit.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.io.Serializable;
  19. import java.util.List;
  20. import java.util.stream.Collectors;
  21. import java.util.stream.Stream;

  22. import org.hipparchus.analysis.differentiation.DSFactory;
  23. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  24. import org.hipparchus.analysis.interpolation.HermiteInterpolator;
  25. import org.hipparchus.exception.MathIllegalStateException;
  26. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  27. import org.hipparchus.util.FastMath;
  28. import org.hipparchus.util.MathUtils;
  29. import org.orekit.errors.OrekitIllegalArgumentException;
  30. import org.orekit.errors.OrekitInternalError;
  31. import org.orekit.errors.OrekitMessages;
  32. import org.orekit.frames.Frame;
  33. import org.orekit.time.AbsoluteDate;
  34. import org.orekit.utils.PVCoordinates;
  35. import org.orekit.utils.TimeStampedPVCoordinates;


  36. /**
  37.  * This class handles traditional Keplerian orbital parameters.

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

  61.  * <p>
  62.  * The instance <code>KeplerianOrbit</code> is guaranteed to be immutable.
  63.  * </p>
  64.  * @see     Orbit
  65.  * @see    CircularOrbit
  66.  * @see    CartesianOrbit
  67.  * @see    EquinoctialOrbit
  68.  * @author Luc Maisonobe
  69.  * @author Guylaine Prat
  70.  * @author Fabien Maussion
  71.  * @author V&eacute;ronique Pommier-Maurussane
  72.  */
  73. public class KeplerianOrbit extends Orbit {

  74.     /** Serializable UID. */
  75.     private static final long serialVersionUID = 20170414L;

  76.     /** Factory for first time derivatives. */
  77.     private static final DSFactory FACTORY = new DSFactory(1, 1);

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

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

  82.     static {
  83.         final double k1 = 3 * FastMath.PI + 2;
  84.         final double k2 = FastMath.PI - 1;
  85.         final double k3 = 6 * FastMath.PI - 1;
  86.         A  = 3 * k2 * k2 / k1;
  87.         B  = k3 * k3 / (6 * k1);
  88.     }

  89.     /** Semi-major axis (m). */
  90.     private final double a;

  91.     /** Eccentricity. */
  92.     private final double e;

  93.     /** Inclination (rad). */
  94.     private final double i;

  95.     /** Perigee Argument (rad). */
  96.     private final double pa;

  97.     /** Right Ascension of Ascending Node (rad). */
  98.     private final double raan;

  99.     /** True anomaly (rad). */
  100.     private final double v;

  101.     /** Semi-major axis derivative (m/s). */
  102.     private final double aDot;

  103.     /** Eccentricity derivative. */
  104.     private final double eDot;

  105.     /** Inclination derivative (rad/s). */
  106.     private final double iDot;

  107.     /** Perigee Argument derivative (rad/s). */
  108.     private final double paDot;

  109.     /** Right Ascension of Ascending Node derivative (rad/s). */
  110.     private final double raanDot;

  111.     /** True anomaly derivative (rad/s). */
  112.     private final double vDot;

  113.     /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */
  114.     private transient PVCoordinates partialPV;

  115.     /** Creates a new instance.
  116.      * @param a  semi-major axis (m), negative for hyperbolic orbits
  117.      * @param e eccentricity
  118.      * @param i inclination (rad)
  119.      * @param pa perigee argument (ω, rad)
  120.      * @param raan right ascension of ascending node (Ω, rad)
  121.      * @param anomaly mean, eccentric or true anomaly (rad)
  122.      * @param type type of anomaly
  123.      * @param frame the frame in which the parameters are defined
  124.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  125.      * @param date date of the orbital parameters
  126.      * @param mu central attraction coefficient (m³/s²)
  127.      * @exception IllegalArgumentException if frame is not a {@link
  128.      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
  129.      * or v is out of range for hyperbolic orbits
  130.      */
  131.     public KeplerianOrbit(final double a, final double e, final double i,
  132.                           final double pa, final double raan, final double anomaly,
  133.                           final PositionAngle type,
  134.                           final Frame frame, final AbsoluteDate date, final double mu)
  135.         throws IllegalArgumentException {
  136.         this(a, e, i, pa, raan, anomaly,
  137.              Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN,
  138.              type, frame, date, mu);
  139.     }

  140.     /** Creates a new instance.
  141.      * @param a  semi-major axis (m), negative for hyperbolic orbits
  142.      * @param e eccentricity
  143.      * @param i inclination (rad)
  144.      * @param pa perigee argument (ω, rad)
  145.      * @param raan right ascension of ascending node (Ω, rad)
  146.      * @param anomaly mean, eccentric or true anomaly (rad)
  147.      * @param aDot  semi-major axis derivative (m/s)
  148.      * @param eDot eccentricity derivative
  149.      * @param iDot inclination derivative (rad/s)
  150.      * @param paDot perigee argument derivative (rad/s)
  151.      * @param raanDot right ascension of ascending node derivative (rad/s)
  152.      * @param anomalyDot mean, eccentric or true anomaly derivative (rad/s)
  153.      * @param type type of anomaly
  154.      * @param frame the frame in which the parameters are defined
  155.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  156.      * @param date date of the orbital parameters
  157.      * @param mu central attraction coefficient (m³/s²)
  158.      * @exception IllegalArgumentException if frame is not a {@link
  159.      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
  160.      * or v is out of range for hyperbolic orbits
  161.      * @since 9.0
  162.      */
  163.     public KeplerianOrbit(final double a, final double e, final double i,
  164.                           final double pa, final double raan, final double anomaly,
  165.                           final double aDot, final double eDot, final double iDot,
  166.                           final double paDot, final double raanDot, final double anomalyDot,
  167.                           final PositionAngle type,
  168.                           final Frame frame, final AbsoluteDate date, final double mu)
  169.         throws IllegalArgumentException {
  170.         super(frame, date, mu);

  171.         if (a * (1 - e) < 0) {
  172.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a, e);
  173.         }

  174.         this.a       = a;
  175.         this.aDot    = aDot;
  176.         this.e       = e;
  177.         this.eDot    = eDot;
  178.         this.i       = i;
  179.         this.iDot    = iDot;
  180.         this.pa      = pa;
  181.         this.paDot   = paDot;
  182.         this.raan    = raan;
  183.         this.raanDot = raanDot;

  184.         if (hasDerivatives()) {
  185.             final DerivativeStructure eDS        = FACTORY.build(e, eDot);
  186.             final DerivativeStructure anomalyDS  = FACTORY.build(anomaly,  anomalyDot);
  187.             final DerivativeStructure vDS;
  188.             switch (type) {
  189.                 case MEAN :
  190.                     vDS = (a < 0) ?
  191.                           FieldKeplerianOrbit.hyperbolicEccentricToTrue(FieldKeplerianOrbit.meanToHyperbolicEccentric(anomalyDS, eDS), eDS) :
  192.                           FieldKeplerianOrbit.ellipticEccentricToTrue(FieldKeplerianOrbit.meanToEllipticEccentric(anomalyDS, eDS), eDS);
  193.                     break;
  194.                 case ECCENTRIC :
  195.                     vDS = (a < 0) ?
  196.                           FieldKeplerianOrbit.hyperbolicEccentricToTrue(anomalyDS, eDS) :
  197.                           FieldKeplerianOrbit.ellipticEccentricToTrue(anomalyDS, eDS);
  198.                     break;
  199.                 case TRUE :
  200.                     vDS = anomalyDS;
  201.                     break;
  202.                 default : // this should never happen
  203.                     throw new OrekitInternalError(null);
  204.             }
  205.             this.v    = vDS.getValue();
  206.             this.vDot = vDS.getPartialDerivative(1);
  207.         } else {
  208.             switch (type) {
  209.                 case MEAN :
  210.                     this.v = (a < 0) ?
  211.                              hyperbolicEccentricToTrue(meanToHyperbolicEccentric(anomaly, e), e) :
  212.                              ellipticEccentricToTrue(meanToEllipticEccentric(anomaly, e), e);
  213.                     break;
  214.                 case ECCENTRIC :
  215.                     this.v = (a < 0) ?
  216.                              hyperbolicEccentricToTrue(anomaly, e) :
  217.                              ellipticEccentricToTrue(anomaly, e);
  218.                     break;
  219.                 case TRUE :
  220.                     this.v = anomaly;
  221.                     break;
  222.                 default : // this should never happen
  223.                     throw new OrekitInternalError(null);
  224.             }
  225.             this.vDot = Double.NaN;
  226.         }

  227.         // check true anomaly range
  228.         if (1 + e * FastMath.cos(v) <= 0) {
  229.             final double vMax = FastMath.acos(-1 / e);
  230.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE,
  231.                                                      v, e, -vMax, vMax);
  232.         }

  233.         this.partialPV = null;

  234.     }

  235.     /** Constructor from Cartesian parameters.
  236.      *
  237.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  238.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  239.      * use {@code mu} and the position to compute the acceleration, including
  240.      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
  241.      *
  242.      * @param pvCoordinates the PVCoordinates of the satellite
  243.      * @param frame the frame in which are defined the {@link PVCoordinates}
  244.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  245.      * @param mu central attraction coefficient (m³/s²)
  246.      * @exception IllegalArgumentException if frame is not a {@link
  247.      * Frame#isPseudoInertial pseudo-inertial frame}
  248.      */
  249.     public KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates,
  250.                           final Frame frame, final double mu)
  251.         throws IllegalArgumentException {
  252.         this(pvCoordinates, frame, mu, hasNonKeplerianAcceleration(pvCoordinates, mu));
  253.     }

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

  274.         // compute inclination
  275.         final Vector3D momentum = pvCoordinates.getMomentum();
  276.         final double m2 = momentum.getNormSq();
  277.         i = Vector3D.angle(momentum, Vector3D.PLUS_K);

  278.         // compute right ascension of ascending node
  279.         raan = Vector3D.crossProduct(Vector3D.PLUS_K, momentum).getAlpha();

  280.         // preliminary computations for parameters depending on orbit shape (elliptic or hyperbolic)
  281.         final Vector3D pvP     = pvCoordinates.getPosition();
  282.         final Vector3D pvV     = pvCoordinates.getVelocity();
  283.         final Vector3D pvA     = pvCoordinates.getAcceleration();
  284.         final double   r2      = pvP.getNormSq();
  285.         final double   r       = FastMath.sqrt(r2);
  286.         final double   V2      = pvV.getNormSq();
  287.         final double   rV2OnMu = r * V2 / mu;

  288.         // compute semi-major axis (will be negative for hyperbolic orbits)
  289.         a = r / (2 - rV2OnMu);
  290.         final double muA = mu * a;

  291.         // compute true anomaly
  292.         if (a > 0) {
  293.             // elliptic or circular orbit
  294.             final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA);
  295.             final double eCE = rV2OnMu - 1;
  296.             e = FastMath.sqrt(eSE * eSE + eCE * eCE);
  297.             v = ellipticEccentricToTrue(FastMath.atan2(eSE, eCE), e);
  298.         } else {
  299.             // hyperbolic orbit
  300.             final double eSH = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(-muA);
  301.             final double eCH = rV2OnMu - 1;
  302.             e = FastMath.sqrt(1 - m2 / muA);
  303.             v = hyperbolicEccentricToTrue(FastMath.log((eCH + eSH) / (eCH - eSH)) / 2, e);
  304.         }

  305.         // compute perigee argument
  306.         final Vector3D node = new Vector3D(raan, 0.0);
  307.         final double px = Vector3D.dotProduct(pvP, node);
  308.         final double py = Vector3D.dotProduct(pvP, Vector3D.crossProduct(momentum, node)) / FastMath.sqrt(m2);
  309.         pa = FastMath.atan2(py, px) - v;

  310.         partialPV = pvCoordinates;

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

  313.             final double[][] jacobian = new double[6][6];
  314.             getJacobianWrtCartesian(PositionAngle.MEAN, jacobian);

  315.             final Vector3D keplerianAcceleration    = new Vector3D(-mu / (r * r2), pvP);
  316.             final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  317.             final double   aX                       = nonKeplerianAcceleration.getX();
  318.             final double   aY                       = nonKeplerianAcceleration.getY();
  319.             final double   aZ                       = nonKeplerianAcceleration.getZ();
  320.             aDot    = jacobian[0][3] * aX + jacobian[0][4] * aY + jacobian[0][5] * aZ;
  321.             eDot    = jacobian[1][3] * aX + jacobian[1][4] * aY + jacobian[1][5] * aZ;
  322.             iDot    = jacobian[2][3] * aX + jacobian[2][4] * aY + jacobian[2][5] * aZ;
  323.             paDot   = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ;
  324.             raanDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ;

  325.             // in order to compute true anomaly derivative, we must compute
  326.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  327.             final double MDot = getKeplerianMeanMotion() +
  328.                                 jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ;
  329.             final DerivativeStructure eDS = FACTORY.build(e, eDot);
  330.             final DerivativeStructure MDS = FACTORY.build(getMeanAnomaly(), MDot);
  331.             final DerivativeStructure vDS = (a < 0) ?
  332.                                             FieldKeplerianOrbit.hyperbolicEccentricToTrue(FieldKeplerianOrbit.meanToHyperbolicEccentric(MDS, eDS), eDS) :
  333.                                             FieldKeplerianOrbit.ellipticEccentricToTrue(FieldKeplerianOrbit.meanToEllipticEccentric(MDS, eDS), eDS);
  334.             vDot = vDS.getPartialDerivative(1);

  335.         } else {
  336.             // acceleration is either almost zero or NaN,
  337.             // we assume acceleration was not known
  338.             // we don't set up derivatives
  339.             aDot    = Double.NaN;
  340.             eDot    = Double.NaN;
  341.             iDot    = Double.NaN;
  342.             paDot   = Double.NaN;
  343.             raanDot = Double.NaN;
  344.             vDot    = Double.NaN;
  345.         }

  346.     }

  347.     /** Constructor from Cartesian parameters.
  348.      *
  349.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  350.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  351.      * use {@code mu} and the position to compute the acceleration, including
  352.      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
  353.      *
  354.      * @param pvCoordinates the PVCoordinates of the satellite
  355.      * @param frame the frame in which are defined the {@link PVCoordinates}
  356.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  357.      * @param date date of the orbital parameters
  358.      * @param mu central attraction coefficient (m³/s²)
  359.      * @exception IllegalArgumentException if frame is not a {@link
  360.      * Frame#isPseudoInertial pseudo-inertial frame}
  361.      */
  362.     public KeplerianOrbit(final PVCoordinates pvCoordinates,
  363.                           final Frame frame, final AbsoluteDate date, final double mu)
  364.         throws IllegalArgumentException {
  365.         this(new TimeStampedPVCoordinates(date, pvCoordinates), frame, mu);
  366.     }

  367.     /** Constructor from any kind of orbital parameters.
  368.      * @param op orbital parameters to copy
  369.      */
  370.     public KeplerianOrbit(final Orbit op) {
  371.         this(op.getPVCoordinates(), op.getFrame(), op.getMu(), op.hasDerivatives());
  372.     }

  373.     /** {@inheritDoc} */
  374.     public OrbitType getType() {
  375.         return OrbitType.KEPLERIAN;
  376.     }

  377.     /** {@inheritDoc} */
  378.     public double getA() {
  379.         return a;
  380.     }

  381.     /** {@inheritDoc} */
  382.     public double getADot() {
  383.         return aDot;
  384.     }

  385.     /** {@inheritDoc} */
  386.     public double getE() {
  387.         return e;
  388.     }

  389.     /** {@inheritDoc} */
  390.     public double getEDot() {
  391.         return eDot;
  392.     }

  393.     /** {@inheritDoc} */
  394.     public double getI() {
  395.         return i;
  396.     }

  397.     /** {@inheritDoc} */
  398.     public double getIDot() {
  399.         return iDot;
  400.     }

  401.     /** Get the perigee argument.
  402.      * @return perigee argument (rad)
  403.      */
  404.     public double getPerigeeArgument() {
  405.         return pa;
  406.     }

  407.     /** Get the perigee argument derivative.
  408.      * <p>
  409.      * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
  410.      * </p>
  411.      * @return perigee argument derivative (rad/s)
  412.      * @since 9.0
  413.      */
  414.     public double getPerigeeArgumentDot() {
  415.         return paDot;
  416.     }

  417.     /** Get the right ascension of the ascending node.
  418.      * @return right ascension of the ascending node (rad)
  419.      */
  420.     public double getRightAscensionOfAscendingNode() {
  421.         return raan;
  422.     }

  423.     /** Get the right ascension of the ascending node derivative.
  424.      * <p>
  425.      * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
  426.      * </p>
  427.      * @return right ascension of the ascending node derivative (rad/s)
  428.      * @since 9.0
  429.      */
  430.     public double getRightAscensionOfAscendingNodeDot() {
  431.         return raanDot;
  432.     }

  433.     /** Get the true anomaly.
  434.      * @return true anomaly (rad)
  435.      */
  436.     public double getTrueAnomaly() {
  437.         return v;
  438.     }

  439.     /** Get the true anomaly derivative.
  440.      * @return true anomaly derivative (rad/s)
  441.      */
  442.     public double getTrueAnomalyDot() {
  443.         return vDot;
  444.     }

  445.     /** Get the eccentric anomaly.
  446.      * @return eccentric anomaly (rad)
  447.      */
  448.     public double getEccentricAnomaly() {
  449.         return (a < 0) ? trueToHyperbolicEccentric(v, e) : trueToEllipticEccentric(v, e);
  450.     }

  451.     /** Get the eccentric anomaly derivative.
  452.      * @return eccentric anomaly derivative (rad/s)
  453.      * @since 9.0
  454.      */
  455.     public double getEccentricAnomalyDot() {
  456.         final DerivativeStructure eDS = FACTORY.build(e, eDot);
  457.         final DerivativeStructure vDS = FACTORY.build(v, vDot);
  458.         final DerivativeStructure EDS = (a < 0) ?
  459.                                         FieldKeplerianOrbit.trueToHyperbolicEccentric(vDS, eDS) :
  460.                                         FieldKeplerianOrbit.trueToEllipticEccentric(vDS, eDS);
  461.         return EDS.getPartialDerivative(1);
  462.     }

  463.     /** Get the mean anomaly.
  464.      * @return mean anomaly (rad)
  465.      */
  466.     public double getMeanAnomaly() {
  467.         return (a < 0) ?
  468.                hyperbolicEccentricToMean(trueToHyperbolicEccentric(v, e), e) :
  469.                ellipticEccentricToMean(trueToEllipticEccentric(v, e), e);
  470.     }

  471.     /** Get the mean anomaly derivative.
  472.      * @return mean anomaly derivative (rad/s)
  473.      * @since 9.0
  474.      */
  475.     public double getMeanAnomalyDot() {
  476.         final DerivativeStructure eDS = FACTORY.build(e, eDot);
  477.         final DerivativeStructure vDS = FACTORY.build(v, vDot);
  478.         final DerivativeStructure MDS = (a < 0) ?
  479.                                         FieldKeplerianOrbit.hyperbolicEccentricToMean(FieldKeplerianOrbit.trueToHyperbolicEccentric(vDS, eDS), eDS) :
  480.                                         FieldKeplerianOrbit.ellipticEccentricToMean(FieldKeplerianOrbit.trueToEllipticEccentric(vDS, eDS), eDS);
  481.         return MDS.getPartialDerivative(1);
  482.     }

  483.     /** Get the anomaly.
  484.      * @param type type of the angle
  485.      * @return anomaly (rad)
  486.      */
  487.     public double getAnomaly(final PositionAngle type) {
  488.         return (type == PositionAngle.MEAN) ? getMeanAnomaly() :
  489.                                               ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomaly() :
  490.                                                                                    getTrueAnomaly());
  491.     }

  492.     /** Get the anomaly derivative.
  493.      * @param type type of the angle
  494.      * @return anomaly derivative (rad/s)
  495.      * @since 9.0
  496.      */
  497.     public double getAnomalyDot(final PositionAngle type) {
  498.         return (type == PositionAngle.MEAN) ? getMeanAnomalyDot() :
  499.                                               ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomalyDot() :
  500.                                                                                    getTrueAnomalyDot());
  501.     }

  502.     /** Computes the true anomaly from the elliptic eccentric anomaly.
  503.      * @param E eccentric anomaly (rad)
  504.      * @param e eccentricity
  505.      * @return v the true anomaly
  506.      * @since 9.0
  507.      */
  508.     public static double ellipticEccentricToTrue(final double E, final double e) {
  509.         final double beta = e / (1 + FastMath.sqrt((1 - e) * (1 + e)));
  510.         return E + 2 * FastMath.atan(beta * FastMath.sin(E) / (1 - beta * FastMath.cos(E)));
  511.     }

  512.     /** Computes the elliptic eccentric anomaly from the true anomaly.
  513.      * @param v true anomaly (rad)
  514.      * @param e eccentricity
  515.      * @return E the elliptic eccentric anomaly
  516.      * @since 9.0
  517.      */
  518.     public static double trueToEllipticEccentric(final double v, final double e) {
  519.         final double beta = e / (1 + FastMath.sqrt(1 - e * e));
  520.         return v - 2 * FastMath.atan(beta * FastMath.sin(v) / (1 + beta * FastMath.cos(v)));
  521.     }

  522.     /** Computes the true anomaly from the hyperbolic eccentric anomaly.
  523.      * @param H hyperbolic eccentric anomaly (rad)
  524.      * @param e eccentricity
  525.      * @return v the true anomaly
  526.      */
  527.     public static double hyperbolicEccentricToTrue(final double H, final double e) {
  528.         return 2 * FastMath.atan(FastMath.sqrt((e + 1) / (e - 1)) * FastMath.tanh(H / 2));
  529.     }

  530.     /** Computes the hyperbolic eccentric anomaly from the true anomaly.
  531.      * @param v true anomaly (rad)
  532.      * @param e eccentricity
  533.      * @return H the hyperbolic eccentric anomaly
  534.      * @since 9.0
  535.      */
  536.     public static double trueToHyperbolicEccentric(final double v, final double e) {
  537.         final double sinhH = FastMath.sqrt(e * e - 1) * FastMath.sin(v) / (1 + e * FastMath.cos(v));
  538.         return FastMath.asinh(sinhH);
  539.     }

  540.     /** Computes the elliptic eccentric anomaly from the mean anomaly.
  541.      * <p>
  542.      * The algorithm used here for solving Kepler equation has been published
  543.      * in: "Procedures for  solving Kepler's Equation", A. W. Odell and
  544.      * R. H. Gooding, Celestial Mechanics 38 (1986) 307-334
  545.      * </p>
  546.      * @param M mean anomaly (rad)
  547.      * @param e eccentricity
  548.      * @return E the eccentric anomaly
  549.      */
  550.     public static double meanToEllipticEccentric(final double M, final double e) {

  551.         // reduce M to [-PI PI) interval
  552.         final double reducedM = MathUtils.normalizeAngle(M, 0.0);

  553.         // compute start value according to A. W. Odell and R. H. Gooding S12 starter
  554.         double E;
  555.         if (FastMath.abs(reducedM) < 1.0 / 6.0) {
  556.             E = reducedM + e * (FastMath.cbrt(6 * reducedM) - reducedM);
  557.         } else {
  558.             if (reducedM < 0) {
  559.                 final double w = FastMath.PI + reducedM;
  560.                 E = reducedM + e * (A * w / (B - w) - FastMath.PI - reducedM);
  561.             } else {
  562.                 final double w = FastMath.PI - reducedM;
  563.                 E = reducedM + e * (FastMath.PI - A * w / (B - w) - reducedM);
  564.             }
  565.         }

  566.         final double e1 = 1 - e;
  567.         final boolean noCancellationRisk = (e1 + E * E / 6) >= 0.1;

  568.         // perform two iterations, each consisting of one Halley step and one Newton-Raphson step
  569.         for (int j = 0; j < 2; ++j) {
  570.             final double f;
  571.             double fd;
  572.             final double fdd  = e * FastMath.sin(E);
  573.             final double fddd = e * FastMath.cos(E);
  574.             if (noCancellationRisk) {
  575.                 f  = (E - fdd) - reducedM;
  576.                 fd = 1 - fddd;
  577.             } else {
  578.                 f  = eMeSinE(E, e) - reducedM;
  579.                 final double s = FastMath.sin(0.5 * E);
  580.                 fd = e1 + 2 * e * s * s;
  581.             }
  582.             final double dee = f * fd / (0.5 * f * fdd - fd * fd);

  583.             // update eccentric anomaly, using expressions that limit underflow problems
  584.             final double w = fd + 0.5 * dee * (fdd + dee * fddd / 3);
  585.             fd += dee * (fdd + 0.5 * dee * fddd);
  586.             E  -= (f - dee * (fd - w)) / fd;

  587.         }

  588.         // expand the result back to original range
  589.         E += M - reducedM;

  590.         return E;

  591.     }

  592.     /** Accurate computation of E - e sin(E).
  593.      * <p>
  594.      * This method is used when E is close to 0 and e close to 1,
  595.      * i.e. near the perigee of almost parabolic orbits
  596.      * </p>
  597.      * @param E eccentric anomaly
  598.      * @param e eccentricity
  599.      * @return E - e sin(E)
  600.      */
  601.     private static double eMeSinE(final double E, final double e) {
  602.         double x = (1 - e) * FastMath.sin(E);
  603.         final double mE2 = -E * E;
  604.         double term = E;
  605.         double d    = 0;
  606.         // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance
  607.         for (double x0 = Double.NaN; x != x0;) {
  608.             d += 2;
  609.             term *= mE2 / (d * (d + 1));
  610.             x0 = x;
  611.             x = x - term;
  612.         }
  613.         return x;
  614.     }

  615.     /** Computes the hyperbolic eccentric anomaly from the mean anomaly.
  616.      * <p>
  617.      * The algorithm used here for solving hyperbolic Kepler equation is
  618.      * Danby's iterative method (3rd order) with Vallado's initial guess.
  619.      * </p>
  620.      * @param M mean anomaly (rad)
  621.      * @param ecc eccentricity
  622.      * @return H the hyperbolic eccentric anomaly
  623.      */
  624.     public static double meanToHyperbolicEccentric(final double M, final double ecc) {

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

  626.         // Initial guess
  627.         double H;
  628.         if (ecc < 1.6) {
  629.             if ((-FastMath.PI < M && M < 0.) || M > FastMath.PI) {
  630.                 H = M - ecc;
  631.             } else {
  632.                 H = M + ecc;
  633.             }
  634.         } else {
  635.             if (ecc < 3.6 && FastMath.abs(M) > FastMath.PI) {
  636.                 H = M - FastMath.copySign(ecc, M);
  637.             } else {
  638.                 H = M / (ecc - 1.);
  639.             }
  640.         }

  641.         // Iterative computation
  642.         int iter = 0;
  643.         do {
  644.             final double f3  = ecc * FastMath.cosh(H);
  645.             final double f2  = ecc * FastMath.sinh(H);
  646.             final double f1  = f3 - 1.;
  647.             final double f0  = f2 - H - M;
  648.             final double f12 = 2. * f1;
  649.             final double d   = f0 / f12;
  650.             final double fdf = f1 - d * f2;
  651.             final double ds  = f0 / fdf;

  652.             final double shift = f0 / (fdf + ds * ds * f3 / 6.);

  653.             H -= shift;

  654.             if (FastMath.abs(shift) <= 1.0e-12) {
  655.                 return H;
  656.             }

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

  658.         throw new MathIllegalStateException(OrekitMessages.UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY,
  659.                                             iter);
  660.     }

  661.     /** Computes the mean anomaly from the elliptic eccentric anomaly.
  662.      * @param E eccentric anomaly (rad)
  663.      * @param e eccentricity
  664.      * @return M the mean anomaly
  665.      * @since 9.0
  666.      */
  667.     public static double ellipticEccentricToMean(final double E, final double e) {
  668.         return E - e * FastMath.sin(E);
  669.     }

  670.     /** Computes the mean anomaly from the hyperbolic eccentric anomaly.
  671.      * @param H hyperbolic eccentric anomaly (rad)
  672.      * @param e eccentricity
  673.      * @return M the mean anomaly
  674.      * @since 9.0
  675.      */
  676.     public static double hyperbolicEccentricToMean(final double H, final double e) {
  677.         return e * FastMath.sinh(H) - H;
  678.     }

  679.     /** {@inheritDoc} */
  680.     public double getEquinoctialEx() {
  681.         return e * FastMath.cos(pa + raan);
  682.     }

  683.     /** {@inheritDoc} */
  684.     public double getEquinoctialExDot() {
  685.         final double paPraan = pa + raan;
  686.         final double cos     = FastMath.cos(paPraan);
  687.         final double sin     = FastMath.sin(paPraan);
  688.         return eDot * cos - e * sin * (paDot + raanDot);
  689.     }

  690.     /** {@inheritDoc} */
  691.     public double getEquinoctialEy() {
  692.         return e * FastMath.sin(pa + raan);
  693.     }

  694.     /** {@inheritDoc} */
  695.     public double getEquinoctialEyDot() {
  696.         final double paPraan = pa + raan;
  697.         final double cos     = FastMath.cos(paPraan);
  698.         final double sin     = FastMath.sin(paPraan);
  699.         return eDot * sin + e * cos * (paDot + raanDot);
  700.     }

  701.     /** {@inheritDoc} */
  702.     public double getHx() {
  703.         // Check for equatorial retrograde orbit
  704.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  705.             return Double.NaN;
  706.         }
  707.         return FastMath.cos(raan) * FastMath.tan(0.5 * i);
  708.     }

  709.     /** {@inheritDoc} */
  710.     public double getHxDot() {
  711.         // Check for equatorial retrograde orbit
  712.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  713.             return Double.NaN;
  714.         }
  715.         final double cosRaan = FastMath.cos(raan);
  716.         final double sinRaan = FastMath.sin(raan);
  717.         final double tan     = FastMath.tan(0.5 * i);
  718.         return 0.5 * (1 + tan * tan) * cosRaan * iDot - tan * sinRaan * raanDot;
  719.     }

  720.     /** {@inheritDoc} */
  721.     public double getHy() {
  722.         // Check for equatorial retrograde orbit
  723.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  724.             return Double.NaN;
  725.         }
  726.         return  FastMath.sin(raan) * FastMath.tan(0.5 * i);
  727.     }

  728.     /** {@inheritDoc} */
  729.     public double getHyDot() {
  730.         // Check for equatorial retrograde orbit
  731.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  732.             return Double.NaN;
  733.         }
  734.         final double cosRaan = FastMath.cos(raan);
  735.         final double sinRaan = FastMath.sin(raan);
  736.         final double tan     = FastMath.tan(0.5 * i);
  737.         return 0.5 * (1 + tan * tan) * sinRaan * iDot + tan * cosRaan * raanDot;
  738.     }

  739.     /** {@inheritDoc} */
  740.     public double getLv() {
  741.         return pa + raan + v;
  742.     }

  743.     /** {@inheritDoc} */
  744.     public double getLvDot() {
  745.         return paDot + raanDot + vDot;
  746.     }

  747.     /** {@inheritDoc} */
  748.     public double getLE() {
  749.         return pa + raan + getEccentricAnomaly();
  750.     }

  751.     /** {@inheritDoc} */
  752.     public double getLEDot() {
  753.         return paDot + raanDot + getEccentricAnomalyDot();
  754.     }

  755.     /** {@inheritDoc} */
  756.     public double getLM() {
  757.         return pa + raan + getMeanAnomaly();
  758.     }

  759.     /** {@inheritDoc} */
  760.     public double getLMDot() {
  761.         return paDot + raanDot + getMeanAnomalyDot();
  762.     }

  763.     /** Compute position and velocity but not acceleration.
  764.      */
  765.     private void computePVWithoutA() {

  766.         if (partialPV != null) {
  767.             // already computed
  768.             return;
  769.         }

  770.         // preliminary variables
  771.         final double cosRaan = FastMath.cos(raan);
  772.         final double sinRaan = FastMath.sin(raan);
  773.         final double cosPa   = FastMath.cos(pa);
  774.         final double sinPa   = FastMath.sin(pa);
  775.         final double cosI    = FastMath.cos(i);
  776.         final double sinI    = FastMath.sin(i);

  777.         final double crcp    = cosRaan * cosPa;
  778.         final double crsp    = cosRaan * sinPa;
  779.         final double srcp    = sinRaan * cosPa;
  780.         final double srsp    = sinRaan * sinPa;

  781.         // reference axes defining the orbital plane
  782.         final Vector3D p = new Vector3D( crcp - cosI * srsp,  srcp + cosI * crsp, sinI * sinPa);
  783.         final Vector3D q = new Vector3D(-crsp - cosI * srcp, -srsp + cosI * crcp, sinI * cosPa);

  784.         if (a > 0) {

  785.             // elliptical case

  786.             // elliptic eccentric anomaly
  787.             final double uME2   = (1 - e) * (1 + e);
  788.             final double s1Me2  = FastMath.sqrt(uME2);
  789.             final double E      = getEccentricAnomaly();
  790.             final double cosE   = FastMath.cos(E);
  791.             final double sinE   = FastMath.sin(E);

  792.             // coordinates of position and velocity in the orbital plane
  793.             final double x      = a * (cosE - e);
  794.             final double y      = a * sinE * s1Me2;
  795.             final double factor = FastMath.sqrt(getMu() / a) / (1 - e * cosE);
  796.             final double xDot   = -sinE * factor;
  797.             final double yDot   =  cosE * s1Me2 * factor;

  798.             final Vector3D position = new Vector3D(x, p, y, q);
  799.             final Vector3D velocity = new Vector3D(xDot, p, yDot, q);
  800.             partialPV = new PVCoordinates(position, velocity);

  801.         } else {

  802.             // hyperbolic case

  803.             // compute position and velocity factors
  804.             final double sinV      = FastMath.sin(v);
  805.             final double cosV      = FastMath.cos(v);
  806.             final double f         = a * (1 - e * e);
  807.             final double posFactor = f / (1 + e * cosV);
  808.             final double velFactor = FastMath.sqrt(getMu() / f);

  809.             final double   x            =  posFactor * cosV;
  810.             final double   y            =  posFactor * sinV;
  811.             final double   xDot         = -velFactor * sinV;
  812.             final double   yDot         =  velFactor * (e + cosV);

  813.             final Vector3D position     = new Vector3D(x, p, y, q);
  814.             final Vector3D velocity     = new Vector3D(xDot, p, yDot, q);
  815.             partialPV = new PVCoordinates(position, velocity);

  816.         }

  817.     }

  818.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  819.      * <p>
  820.      * This method should be called only when {@link #hasDerivatives()} returns true.
  821.      * </p>
  822.      * @return non-Keplerian part of the acceleration
  823.      */
  824.     private Vector3D nonKeplerianAcceleration() {

  825.         final double[][] dCdP = new double[6][6];
  826.         getJacobianWrtParameters(PositionAngle.MEAN, dCdP);

  827.         final double nonKeplerianMeanMotion = getMeanAnomalyDot() - getKeplerianMeanMotion();
  828.         final double nonKeplerianAx = dCdP[3][0] * aDot    + dCdP[3][1] * eDot    + dCdP[3][2] * iDot    +
  829.                                       dCdP[3][3] * paDot   + dCdP[3][4] * raanDot + dCdP[3][5] * nonKeplerianMeanMotion;
  830.         final double nonKeplerianAy = dCdP[4][0] * aDot    + dCdP[4][1] * eDot    + dCdP[4][2] * iDot    +
  831.                                       dCdP[4][3] * paDot   + dCdP[4][4] * raanDot + dCdP[4][5] * nonKeplerianMeanMotion;
  832.         final double nonKeplerianAz = dCdP[5][0] * aDot    + dCdP[5][1] * eDot    + dCdP[5][2] * iDot    +
  833.                                       dCdP[5][3] * paDot   + dCdP[5][4] * raanDot + dCdP[5][5] * nonKeplerianMeanMotion;

  834.         return new Vector3D(nonKeplerianAx, nonKeplerianAy, nonKeplerianAz);

  835.     }

  836.     /** {@inheritDoc} */
  837.     protected TimeStampedPVCoordinates initPVCoordinates() {

  838.         // position and velocity
  839.         computePVWithoutA();

  840.         // acceleration
  841.         final double r2 = partialPV.getPosition().getNormSq();
  842.         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
  843.         final Vector3D acceleration = hasDerivatives() ?
  844.                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
  845.                                       keplerianAcceleration;

  846.         return new TimeStampedPVCoordinates(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);

  847.     }

  848.     /** {@inheritDoc} */
  849.     public KeplerianOrbit shiftedBy(final double dt) {

  850.         // use Keplerian-only motion
  851.         final KeplerianOrbit keplerianShifted = new KeplerianOrbit(a, e, i, pa, raan,
  852.                                                                    getMeanAnomaly() + getKeplerianMeanMotion() * dt,
  853.                                                                    PositionAngle.MEAN, getFrame(),
  854.                                                                    getDate().shiftedBy(dt), getMu());

  855.         if (hasDerivatives()) {

  856.             // extract non-Keplerian acceleration from first time derivatives
  857.             final Vector3D nonKeplerianAcceleration = nonKeplerianAcceleration();

  858.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  859.             keplerianShifted.computePVWithoutA();
  860.             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
  861.                                                    0.5 * dt * dt, nonKeplerianAcceleration);
  862.             final double   fixedR2 = fixedP.getNormSq();
  863.             final double   fixedR  = FastMath.sqrt(fixedR2);
  864.             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
  865.                                                   dt, nonKeplerianAcceleration);
  866.             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
  867.                                                   1, nonKeplerianAcceleration);

  868.             // build a new orbit, taking non-Keplerian acceleration into account
  869.             return new KeplerianOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
  870.                                                                    fixedP, fixedV, fixedA),
  871.                                       keplerianShifted.getFrame(), keplerianShifted.getMu());

  872.         } else {
  873.             // Keplerian-only motion is all we can do
  874.             return keplerianShifted;
  875.         }

  876.     }

  877.     /** {@inheritDoc}
  878.      * <p>
  879.      * The interpolated instance is created by polynomial Hermite interpolation
  880.      * on Keplerian elements, without derivatives (which means the interpolation
  881.      * falls back to Lagrange interpolation only).
  882.      * </p>
  883.      * <p>
  884.      * As this implementation of interpolation is polynomial, it should be used only
  885.      * with small samples (about 10-20 points) in order to avoid <a
  886.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  887.      * and numerical problems (including NaN appearing).
  888.      * </p>
  889.      * <p>
  890.      * If orbit interpolation on large samples is needed, using the {@link
  891.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  892.      * low-level interpolation. The Ephemeris class automatically handles selection of
  893.      * a neighboring sub-sample with a predefined number of point from a large global sample
  894.      * in a thread-safe way.
  895.      * </p>
  896.      */
  897.     public KeplerianOrbit interpolate(final AbsoluteDate date, final Stream<Orbit> sample) {

  898.         // first pass to check if derivatives are available throughout the sample
  899.         final List<Orbit> list = sample.collect(Collectors.toList());
  900.         boolean useDerivatives = true;
  901.         for (final Orbit orbit : list) {
  902.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  903.         }

  904.         // set up an interpolator
  905.         final HermiteInterpolator interpolator = new HermiteInterpolator();

  906.         // second pass to feed interpolator
  907.         AbsoluteDate previousDate = null;
  908.         double       previousPA   = Double.NaN;
  909.         double       previousRAAN = Double.NaN;
  910.         double       previousM    = Double.NaN;
  911.         for (final Orbit orbit : list) {
  912.             final KeplerianOrbit kep = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit);
  913.             final double continuousPA;
  914.             final double continuousRAAN;
  915.             final double continuousM;
  916.             if (previousDate == null) {
  917.                 continuousPA   = kep.getPerigeeArgument();
  918.                 continuousRAAN = kep.getRightAscensionOfAscendingNode();
  919.                 continuousM    = kep.getMeanAnomaly();
  920.             } else {
  921.                 final double dt      = kep.getDate().durationFrom(previousDate);
  922.                 final double keplerM = previousM + kep.getKeplerianMeanMotion() * dt;
  923.                 continuousPA   = MathUtils.normalizeAngle(kep.getPerigeeArgument(), previousPA);
  924.                 continuousRAAN = MathUtils.normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN);
  925.                 continuousM    = MathUtils.normalizeAngle(kep.getMeanAnomaly(), keplerM);
  926.             }
  927.             previousDate = kep.getDate();
  928.             previousPA   = continuousPA;
  929.             previousRAAN = continuousRAAN;
  930.             previousM    = continuousM;
  931.             if (useDerivatives) {
  932.                 interpolator.addSamplePoint(kep.getDate().durationFrom(date),
  933.                                             new double[] {
  934.                                                 kep.getA(),
  935.                                                 kep.getE(),
  936.                                                 kep.getI(),
  937.                                                 continuousPA,
  938.                                                 continuousRAAN,
  939.                                                 continuousM
  940.                                             }, new double[] {
  941.                                                 kep.getADot(),
  942.                                                 kep.getEDot(),
  943.                                                 kep.getIDot(),
  944.                                                 kep.getPerigeeArgumentDot(),
  945.                                                 kep.getRightAscensionOfAscendingNodeDot(),
  946.                                                 kep.getMeanAnomalyDot()
  947.                                             });
  948.             } else {
  949.                 interpolator.addSamplePoint(kep.getDate().durationFrom(date),
  950.                                             new double[] {
  951.                                                 kep.getA(),
  952.                                                 kep.getE(),
  953.                                                 kep.getI(),
  954.                                                 continuousPA,
  955.                                                 continuousRAAN,
  956.                                                 continuousM
  957.                                             });
  958.             }
  959.         }

  960.         // interpolate
  961.         final double[][] interpolated = interpolator.derivatives(0.0, 1);

  962.         // build a new interpolated instance
  963.         return new KeplerianOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  964.                                   interpolated[0][3], interpolated[0][4], interpolated[0][5],
  965.                                   interpolated[1][0], interpolated[1][1], interpolated[1][2],
  966.                                   interpolated[1][3], interpolated[1][4], interpolated[1][5],
  967.                                   PositionAngle.MEAN, getFrame(), date, getMu());

  968.     }

  969.     /** {@inheritDoc} */
  970.     protected double[][] computeJacobianMeanWrtCartesian() {
  971.         if (a > 0) {
  972.             return computeJacobianMeanWrtCartesianElliptical();
  973.         } else {
  974.             return computeJacobianMeanWrtCartesianHyperbolic();
  975.         }
  976.     }

  977.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  978.      * <p>
  979.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  980.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  981.      * yDot for j=4, zDot for j=5).
  982.      * </p>
  983.      * @return 6x6 Jacobian matrix
  984.      */
  985.     private double[][] computeJacobianMeanWrtCartesianElliptical() {

  986.         final double[][] jacobian = new double[6][6];

  987.         // compute various intermediate parameters
  988.         computePVWithoutA();
  989.         final Vector3D position = partialPV.getPosition();
  990.         final Vector3D velocity = partialPV.getVelocity();
  991.         final Vector3D momentum = partialPV.getMomentum();
  992.         final double v2         = velocity.getNormSq();
  993.         final double r2         = position.getNormSq();
  994.         final double r          = FastMath.sqrt(r2);
  995.         final double r3         = r * r2;

  996.         final double px         = position.getX();
  997.         final double py         = position.getY();
  998.         final double pz         = position.getZ();
  999.         final double vx         = velocity.getX();
  1000.         final double vy         = velocity.getY();
  1001.         final double vz         = velocity.getZ();
  1002.         final double mx         = momentum.getX();
  1003.         final double my         = momentum.getY();
  1004.         final double mz         = momentum.getZ();

  1005.         final double mu         = getMu();
  1006.         final double sqrtMuA    = FastMath.sqrt(a * mu);
  1007.         final double sqrtAoMu   = FastMath.sqrt(a / mu);
  1008.         final double a2         = a * a;
  1009.         final double twoA       = 2 * a;
  1010.         final double rOnA       = r / a;

  1011.         final double oMe2       = 1 - e * e;
  1012.         final double epsilon    = FastMath.sqrt(oMe2);
  1013.         final double sqrtRec    = 1 / epsilon;

  1014.         final double cosI       = FastMath.cos(i);
  1015.         final double sinI       = FastMath.sin(i);
  1016.         final double cosPA      = FastMath.cos(pa);
  1017.         final double sinPA      = FastMath.sin(pa);

  1018.         final double pv         = Vector3D.dotProduct(position, velocity);
  1019.         final double cosE       = (a - r) / (a * e);
  1020.         final double sinE       = pv / (e * sqrtMuA);

  1021.         // da
  1022.         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
  1023.         final Vector3D vectorARDot = velocity.scalarMultiply(2 * a2 / mu);
  1024.         fillHalfRow(1, vectorAR,    jacobian[0], 0);
  1025.         fillHalfRow(1, vectorARDot, jacobian[0], 3);

  1026.         // de
  1027.         final double factorER3 = pv / twoA;
  1028.         final Vector3D vectorER   = new Vector3D(cosE * v2 / (r * mu), position,
  1029.                                                  sinE / sqrtMuA, velocity,
  1030.                                                  -factorER3 * sinE / sqrtMuA, vectorAR);
  1031.         final Vector3D vectorERDot = new Vector3D(sinE / sqrtMuA, position,
  1032.                                                   cosE * 2 * r / mu, velocity,
  1033.                                                   -factorER3 * sinE / sqrtMuA, vectorARDot);
  1034.         fillHalfRow(1, vectorER,    jacobian[1], 0);
  1035.         fillHalfRow(1, vectorERDot, jacobian[1], 3);

  1036.         // dE / dr (Eccentric anomaly)
  1037.         final double coefE = cosE / (e * sqrtMuA);
  1038.         final Vector3D  vectorEAnR =
  1039.             new Vector3D(-sinE * v2 / (e * r * mu), position, coefE, velocity,
  1040.                          -factorER3 * coefE, vectorAR);

  1041.         // dE / drDot
  1042.         final Vector3D  vectorEAnRDot =
  1043.             new Vector3D(-sinE * 2 * r / (e * mu), velocity, coefE, position,
  1044.                          -factorER3 * coefE, vectorARDot);

  1045.         // precomputing some more factors
  1046.         final double s1 = -sinE * pz / r - cosE * vz * sqrtAoMu;
  1047.         final double s2 = -cosE * pz / r3;
  1048.         final double s3 = -sinE * vz / (2 * sqrtMuA);
  1049.         final double t1 = sqrtRec * (cosE * pz / r - sinE * vz * sqrtAoMu);
  1050.         final double t2 = sqrtRec * (-sinE * pz / r3);
  1051.         final double t3 = sqrtRec * (cosE - e) * vz / (2 * sqrtMuA);
  1052.         final double t4 = sqrtRec * (e * sinI * cosPA * sqrtRec - vz * sqrtAoMu);
  1053.         final Vector3D s = new Vector3D(cosE / r, Vector3D.PLUS_K,
  1054.                                         s1,       vectorEAnR,
  1055.                                         s2,       position,
  1056.                                         s3,       vectorAR);
  1057.         final Vector3D sDot = new Vector3D(-sinE * sqrtAoMu, Vector3D.PLUS_K,
  1058.                                            s1,               vectorEAnRDot,
  1059.                                            s3,               vectorARDot);
  1060.         final Vector3D t =
  1061.             new Vector3D(sqrtRec * sinE / r, Vector3D.PLUS_K).add(new Vector3D(t1, vectorEAnR,
  1062.                                                                                t2, position,
  1063.                                                                                t3, vectorAR,
  1064.                                                                                t4, vectorER));
  1065.         final Vector3D tDot = new Vector3D(sqrtRec * (cosE - e) * sqrtAoMu, Vector3D.PLUS_K,
  1066.                                            t1,                              vectorEAnRDot,
  1067.                                            t3,                              vectorARDot,
  1068.                                            t4,                              vectorERDot);

  1069.         // di
  1070.         final double factorI1 = -sinI * sqrtRec / sqrtMuA;
  1071.         final double i1 =  factorI1;
  1072.         final double i2 = -factorI1 * mz / twoA;
  1073.         final double i3 =  factorI1 * mz * e / oMe2;
  1074.         final double i4 = cosI * sinPA;
  1075.         final double i5 = cosI * cosPA;
  1076.         fillHalfRow(i1, new Vector3D(vy, -vx, 0), i2, vectorAR, i3, vectorER, i4, s, i5, t,
  1077.                     jacobian[2], 0);
  1078.         fillHalfRow(i1, new Vector3D(-py, px, 0), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
  1079.                     jacobian[2], 3);

  1080.         // dpa
  1081.         fillHalfRow(cosPA / sinI, s,    -sinPA / sinI, t,    jacobian[3], 0);
  1082.         fillHalfRow(cosPA / sinI, sDot, -sinPA / sinI, tDot, jacobian[3], 3);

  1083.         // dRaan
  1084.         final double factorRaanR = 1 / (mu * a * oMe2 * sinI * sinI);
  1085.         fillHalfRow(-factorRaanR * my, new Vector3D(  0, vz, -vy),
  1086.                      factorRaanR * mx, new Vector3D(-vz,  0,  vx),
  1087.                      jacobian[4], 0);
  1088.         fillHalfRow(-factorRaanR * my, new Vector3D( 0, -pz,  py),
  1089.                      factorRaanR * mx, new Vector3D(pz,   0, -px),
  1090.                      jacobian[4], 3);

  1091.         // dM
  1092.         fillHalfRow(rOnA, vectorEAnR,    -sinE, vectorER,    jacobian[5], 0);
  1093.         fillHalfRow(rOnA, vectorEAnRDot, -sinE, vectorERDot, jacobian[5], 3);

  1094.         return jacobian;

  1095.     }

  1096.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1097.      * <p>
  1098.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1099.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1100.      * yDot for j=4, zDot for j=5).
  1101.      * </p>
  1102.      * @return 6x6 Jacobian matrix
  1103.      */
  1104.     private double[][] computeJacobianMeanWrtCartesianHyperbolic() {

  1105.         final double[][] jacobian = new double[6][6];

  1106.         // compute various intermediate parameters
  1107.         computePVWithoutA();
  1108.         final Vector3D position = partialPV.getPosition();
  1109.         final Vector3D velocity = partialPV.getVelocity();
  1110.         final Vector3D momentum = partialPV.getMomentum();
  1111.         final double r2         = position.getNormSq();
  1112.         final double r          = FastMath.sqrt(r2);
  1113.         final double r3         = r * r2;

  1114.         final double x          = position.getX();
  1115.         final double y          = position.getY();
  1116.         final double z          = position.getZ();
  1117.         final double vx         = velocity.getX();
  1118.         final double vy         = velocity.getY();
  1119.         final double vz         = velocity.getZ();
  1120.         final double mx         = momentum.getX();
  1121.         final double my         = momentum.getY();
  1122.         final double mz         = momentum.getZ();

  1123.         final double mu         = getMu();
  1124.         final double absA       = -a;
  1125.         final double sqrtMuA    = FastMath.sqrt(absA * mu);
  1126.         final double a2         = a * a;
  1127.         final double rOa        = r / absA;

  1128.         final double cosI       = FastMath.cos(i);
  1129.         final double sinI       = FastMath.sin(i);

  1130.         final double pv         = Vector3D.dotProduct(position, velocity);

  1131.         // da
  1132.         final Vector3D vectorAR = new Vector3D(-2 * a2 / r3, position);
  1133.         final Vector3D vectorARDot = velocity.scalarMultiply(-2 * a2 / mu);
  1134.         fillHalfRow(-1, vectorAR,    jacobian[0], 0);
  1135.         fillHalfRow(-1, vectorARDot, jacobian[0], 3);

  1136.         // differentials of the momentum
  1137.         final double m      = momentum.getNorm();
  1138.         final double oOm    = 1 / m;
  1139.         final Vector3D dcXP = new Vector3D(  0,  vz, -vy);
  1140.         final Vector3D dcYP = new Vector3D(-vz,   0,  vx);
  1141.         final Vector3D dcZP = new Vector3D( vy, -vx,   0);
  1142.         final Vector3D dcXV = new Vector3D(  0,  -z,   y);
  1143.         final Vector3D dcYV = new Vector3D(  z,   0,  -x);
  1144.         final Vector3D dcZV = new Vector3D( -y,   x,   0);
  1145.         final Vector3D dCP  = new Vector3D(mx * oOm, dcXP, my * oOm, dcYP, mz * oOm, dcZP);
  1146.         final Vector3D dCV  = new Vector3D(mx * oOm, dcXV, my * oOm, dcYV, mz * oOm, dcZV);

  1147.         // dp
  1148.         final double mOMu   = m / mu;
  1149.         final Vector3D dpP  = new Vector3D(2 * mOMu, dCP);
  1150.         final Vector3D dpV  = new Vector3D(2 * mOMu, dCV);

  1151.         // de
  1152.         final double p      = m * mOMu;
  1153.         final double moO2ae = 1 / (2 * absA * e);
  1154.         final double m2OaMu = -p / absA;
  1155.         fillHalfRow(moO2ae, dpP, m2OaMu * moO2ae, vectorAR,    jacobian[1], 0);
  1156.         fillHalfRow(moO2ae, dpV, m2OaMu * moO2ae, vectorARDot, jacobian[1], 3);

  1157.         // di
  1158.         final double cI1 = 1 / (m * sinI);
  1159.         final double cI2 = cosI * cI1;
  1160.         fillHalfRow(cI2, dCP, -cI1, dcZP, jacobian[2], 0);
  1161.         fillHalfRow(cI2, dCV, -cI1, dcZV, jacobian[2], 3);

  1162.         // dPA
  1163.         final double cP1     =  y * oOm;
  1164.         final double cP2     = -x * oOm;
  1165.         final double cP3     = -(mx * cP1 + my * cP2);
  1166.         final double cP4     = cP3 * oOm;
  1167.         final double cP5     = -1 / (r2 * sinI * sinI);
  1168.         final double cP6     = z  * cP5;
  1169.         final double cP7     = cP3 * cP5;
  1170.         final Vector3D dacP  = new Vector3D(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new Vector3D(-my, mx, 0));
  1171.         final Vector3D dacV  = new Vector3D(cP1, dcXV, cP2, dcYV, cP4, dCV);
  1172.         final Vector3D dpoP  = new Vector3D(cP6, dacP, cP7, Vector3D.PLUS_K);
  1173.         final Vector3D dpoV  = new Vector3D(cP6, dacV);

  1174.         final double re2     = r2 * e * e;
  1175.         final double recOre2 = (p - r) / re2;
  1176.         final double resOre2 = (pv * mOMu) / re2;
  1177.         final Vector3D dreP  = new Vector3D(mOMu, velocity, pv / mu, dCP);
  1178.         final Vector3D dreV  = new Vector3D(mOMu, position, pv / mu, dCV);
  1179.         final Vector3D davP  = new Vector3D(-resOre2, dpP, recOre2, dreP, resOre2 / r, position);
  1180.         final Vector3D davV  = new Vector3D(-resOre2, dpV, recOre2, dreV);
  1181.         fillHalfRow(1, dpoP, -1, davP, jacobian[3], 0);
  1182.         fillHalfRow(1, dpoV, -1, davV, jacobian[3], 3);

  1183.         // dRAAN
  1184.         final double cO0 = cI1 * cI1;
  1185.         final double cO1 =  mx * cO0;
  1186.         final double cO2 = -my * cO0;
  1187.         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
  1188.         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);

  1189.         // dM
  1190.         final double s2a    = pv / (2 * absA);
  1191.         final double oObux  = 1 / FastMath.sqrt(m * m + mu * absA);
  1192.         final double scasbu = pv * oObux;
  1193.         final Vector3D dauP = new Vector3D(1 / sqrtMuA, velocity, -s2a / sqrtMuA, vectorAR);
  1194.         final Vector3D dauV = new Vector3D(1 / sqrtMuA, position, -s2a / sqrtMuA, vectorARDot);
  1195.         final Vector3D dbuP = new Vector3D(oObux * mu / 2, vectorAR,    m * oObux, dCP);
  1196.         final Vector3D dbuV = new Vector3D(oObux * mu / 2, vectorARDot, m * oObux, dCV);
  1197.         final Vector3D dcuP = new Vector3D(oObux, velocity, -scasbu * oObux, dbuP);
  1198.         final Vector3D dcuV = new Vector3D(oObux, position, -scasbu * oObux, dbuV);
  1199.         fillHalfRow(1, dauP, -e / (1 + rOa), dcuP, jacobian[5], 0);
  1200.         fillHalfRow(1, dauV, -e / (1 + rOa), dcuV, jacobian[5], 3);

  1201.         return jacobian;

  1202.     }

  1203.     /** {@inheritDoc} */
  1204.     protected double[][] computeJacobianEccentricWrtCartesian() {
  1205.         if (a > 0) {
  1206.             return computeJacobianEccentricWrtCartesianElliptical();
  1207.         } else {
  1208.             return computeJacobianEccentricWrtCartesianHyperbolic();
  1209.         }
  1210.     }

  1211.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1212.      * <p>
  1213.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1214.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1215.      * yDot for j=4, zDot for j=5).
  1216.      * </p>
  1217.      * @return 6x6 Jacobian matrix
  1218.      */
  1219.     private double[][] computeJacobianEccentricWrtCartesianElliptical() {

  1220.         // start by computing the Jacobian with mean angle
  1221.         final double[][] jacobian = computeJacobianMeanWrtCartesianElliptical();

  1222.         // Differentiating the Kepler equation M = E - e sin E leads to:
  1223.         // dM = (1 - e cos E) dE - sin E de
  1224.         // which is inverted and rewritten as:
  1225.         // dE = a/r dM + sin E a/r de
  1226.         final double eccentricAnomaly = getEccentricAnomaly();
  1227.         final double cosE             = FastMath.cos(eccentricAnomaly);
  1228.         final double sinE             = FastMath.sin(eccentricAnomaly);
  1229.         final double aOr              = 1 / (1 - e * cosE);

  1230.         // update anomaly row
  1231.         final double[] eRow           = jacobian[1];
  1232.         final double[] anomalyRow     = jacobian[5];
  1233.         for (int j = 0; j < anomalyRow.length; ++j) {
  1234.             anomalyRow[j] = aOr * (anomalyRow[j] + sinE * eRow[j]);
  1235.         }

  1236.         return jacobian;

  1237.     }

  1238.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1239.      * <p>
  1240.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1241.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1242.      * yDot for j=4, zDot for j=5).
  1243.      * </p>
  1244.      * @return 6x6 Jacobian matrix
  1245.      */
  1246.     private double[][] computeJacobianEccentricWrtCartesianHyperbolic() {

  1247.         // start by computing the Jacobian with mean angle
  1248.         final double[][] jacobian = computeJacobianMeanWrtCartesianHyperbolic();

  1249.         // Differentiating the Kepler equation M = e sinh H - H leads to:
  1250.         // dM = (e cosh H - 1) dH + sinh H de
  1251.         // which is inverted and rewritten as:
  1252.         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
  1253.         final double H      = getEccentricAnomaly();
  1254.         final double coshH  = FastMath.cosh(H);
  1255.         final double sinhH  = FastMath.sinh(H);
  1256.         final double absaOr = 1 / (e * coshH - 1);

  1257.         // update anomaly row
  1258.         final double[] eRow       = jacobian[1];
  1259.         final double[] anomalyRow = jacobian[5];
  1260.         for (int j = 0; j < anomalyRow.length; ++j) {
  1261.             anomalyRow[j] = absaOr * (anomalyRow[j] - sinhH * eRow[j]);
  1262.         }

  1263.         return jacobian;

  1264.     }

  1265.     /** {@inheritDoc} */
  1266.     protected double[][] computeJacobianTrueWrtCartesian() {
  1267.         if (a > 0) {
  1268.             return computeJacobianTrueWrtCartesianElliptical();
  1269.         } else {
  1270.             return computeJacobianTrueWrtCartesianHyperbolic();
  1271.         }
  1272.     }

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

  1282.         // start by computing the Jacobian with eccentric angle
  1283.         final double[][] jacobian = computeJacobianEccentricWrtCartesianElliptical();

  1284.         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
  1285.         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
  1286.         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
  1287.         // which is inverted and rewritten as:
  1288.         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
  1289.         final double e2               = e * e;
  1290.         final double oMe2             = 1 - e2;
  1291.         final double epsilon          = FastMath.sqrt(oMe2);
  1292.         final double eccentricAnomaly = getEccentricAnomaly();
  1293.         final double cosE             = FastMath.cos(eccentricAnomaly);
  1294.         final double sinE             = FastMath.sin(eccentricAnomaly);
  1295.         final double aOr              = 1 / (1 - e * cosE);
  1296.         final double aFactor          = epsilon * aOr;
  1297.         final double eFactor          = sinE * aOr / epsilon;

  1298.         // update anomaly row
  1299.         final double[] eRow           = jacobian[1];
  1300.         final double[] anomalyRow     = jacobian[5];
  1301.         for (int j = 0; j < anomalyRow.length; ++j) {
  1302.             anomalyRow[j] = aFactor * anomalyRow[j] + eFactor * eRow[j];
  1303.         }

  1304.         return jacobian;

  1305.     }

  1306.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1307.      * <p>
  1308.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1309.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1310.      * yDot for j=4, zDot for j=5).
  1311.      * </p>
  1312.      * @return 6x6 Jacobian matrix
  1313.      */
  1314.     private double[][] computeJacobianTrueWrtCartesianHyperbolic() {

  1315.         // start by computing the Jacobian with eccentric angle
  1316.         final double[][] jacobian = computeJacobianEccentricWrtCartesianHyperbolic();

  1317.         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
  1318.         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
  1319.         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
  1320.         // which is inverted and rewritten as:
  1321.         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
  1322.         final double e2       = e * e;
  1323.         final double e2Mo     = e2 - 1;
  1324.         final double epsilon  = FastMath.sqrt(e2Mo);
  1325.         final double H        = getEccentricAnomaly();
  1326.         final double coshH    = FastMath.cosh(H);
  1327.         final double sinhH    = FastMath.sinh(H);
  1328.         final double aOr      = 1 / (e * coshH - 1);
  1329.         final double aFactor  = epsilon * aOr;
  1330.         final double eFactor  = sinhH * aOr / epsilon;

  1331.         // update anomaly row
  1332.         final double[] eRow           = jacobian[1];
  1333.         final double[] anomalyRow     = jacobian[5];
  1334.         for (int j = 0; j < anomalyRow.length; ++j) {
  1335.             anomalyRow[j] = aFactor * anomalyRow[j] - eFactor * eRow[j];
  1336.         }

  1337.         return jacobian;

  1338.     }

  1339.     /** {@inheritDoc} */
  1340.     public void addKeplerContribution(final PositionAngle type, final double gm,
  1341.                                       final double[] pDot) {
  1342.         final double oMe2;
  1343.         final double ksi;
  1344.         final double absA = FastMath.abs(a);
  1345.         final double n    = FastMath.sqrt(gm / absA) / absA;
  1346.         switch (type) {
  1347.             case MEAN :
  1348.                 pDot[5] += n;
  1349.                 break;
  1350.             case ECCENTRIC :
  1351.                 oMe2 = FastMath.abs(1 - e * e);
  1352.                 ksi  = 1 + e * FastMath.cos(v);
  1353.                 pDot[5] += n * ksi / oMe2;
  1354.                 break;
  1355.             case TRUE :
  1356.                 oMe2 = FastMath.abs(1 - e * e);
  1357.                 ksi  = 1 + e * FastMath.cos(v);
  1358.                 pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
  1359.                 break;
  1360.             default :
  1361.                 throw new OrekitInternalError(null);
  1362.         }
  1363.     }

  1364.     /**  Returns a string representation of this Keplerian parameters object.
  1365.      * @return a string representation of this object
  1366.      */
  1367.     public String toString() {
  1368.         return new StringBuffer().append("Keplerian parameters: ").append('{').
  1369.                                   append("a: ").append(a).
  1370.                                   append("; e: ").append(e).
  1371.                                   append("; i: ").append(FastMath.toDegrees(i)).
  1372.                                   append("; pa: ").append(FastMath.toDegrees(pa)).
  1373.                                   append("; raan: ").append(FastMath.toDegrees(raan)).
  1374.                                   append("; v: ").append(FastMath.toDegrees(v)).
  1375.                                   append(";}").toString();
  1376.     }

  1377.     /** Replace the instance with a data transfer object for serialization.
  1378.      * @return data transfer object that will be serialized
  1379.      */
  1380.     private Object writeReplace() {
  1381.         return new DTO(this);
  1382.     }

  1383.     /** Internal class used only for serialization. */
  1384.     private static class DTO implements Serializable {

  1385.         /** Serializable UID. */
  1386.         private static final long serialVersionUID = 20170414L;

  1387.         /** Double values. */
  1388.         private double[] d;

  1389.         /** Frame in which are defined the orbital parameters. */
  1390.         private final Frame frame;

  1391.         /** Simple constructor.
  1392.          * @param orbit instance to serialize
  1393.          */
  1394.         private DTO(final KeplerianOrbit orbit) {

  1395.             final TimeStampedPVCoordinates pv = orbit.getPVCoordinates();

  1396.             // decompose date
  1397.             final double epoch  = FastMath.floor(pv.getDate().durationFrom(AbsoluteDate.J2000_EPOCH));
  1398.             final double offset = pv.getDate().durationFrom(AbsoluteDate.J2000_EPOCH.shiftedBy(epoch));

  1399.             if (orbit.hasDerivatives()) {
  1400.                 // we have derivatives
  1401.                 this.d = new double[] {
  1402.                     epoch, offset, orbit.getMu(),
  1403.                     orbit.a, orbit.e, orbit.i,
  1404.                     orbit.pa, orbit.raan, orbit.v,
  1405.                     orbit.aDot, orbit.eDot, orbit.iDot,
  1406.                     orbit.paDot, orbit.raanDot, orbit.vDot
  1407.                 };
  1408.             } else {
  1409.                 // we don't have derivatives
  1410.                 this.d = new double[] {
  1411.                     epoch, offset, orbit.getMu(),
  1412.                     orbit.a, orbit.e, orbit.i,
  1413.                     orbit.pa, orbit.raan, orbit.v
  1414.                 };
  1415.             }

  1416.             this.frame = orbit.getFrame();

  1417.         }

  1418.         /** Replace the deserialized data transfer object with a {@link KeplerianOrbit}.
  1419.          * @return replacement {@link KeplerianOrbit}
  1420.          */
  1421.         private Object readResolve() {
  1422.             if (d.length >= 15) {
  1423.                 // we have derivatives
  1424.                 return new KeplerianOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8],
  1425.                                           d[ 9], d[10], d[11], d[12], d[13], d[14],
  1426.                                           PositionAngle.TRUE,
  1427.                                           frame, AbsoluteDate.J2000_EPOCH.shiftedBy(d[0]).shiftedBy(d[1]),
  1428.                                           d[2]);
  1429.             } else {
  1430.                 // we don't have derivatives
  1431.                 return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngle.TRUE,
  1432.                                           frame, AbsoluteDate.J2000_EPOCH.shiftedBy(d[0]).shiftedBy(d[1]),
  1433.                                           d[2]);
  1434.             }
  1435.         }

  1436.     }

  1437. }