KeplerianOrbit.java

  1. /* Copyright 2002-2022 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.orbits;

  18. import java.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.UnivariateDerivative1;
  23. import org.hipparchus.analysis.interpolation.HermiteInterpolator;
  24. import org.hipparchus.exception.MathIllegalStateException;
  25. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.MathUtils;
  28. import org.hipparchus.util.SinCos;
  29. import org.orekit.annotation.DefaultDataContext;
  30. import org.orekit.data.DataContext;
  31. import org.orekit.errors.OrekitException;
  32. import org.orekit.errors.OrekitIllegalArgumentException;
  33. import org.orekit.errors.OrekitInternalError;
  34. import org.orekit.errors.OrekitMessages;
  35. import org.orekit.frames.Frame;
  36. import org.orekit.time.AbsoluteDate;
  37. import org.orekit.utils.PVCoordinates;
  38. import org.orekit.utils.TimeStampedPVCoordinates;


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

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

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

  77.     /** Serializable UID. */
  78.     private static final long serialVersionUID = 20170414L;

  79.     /** Name of the eccentricity parameter. */
  80.     private static final String ECCENTRICITY = "eccentricity";

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

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

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

  92.     /** Semi-major axis (m). */
  93.     private final double a;

  94.     /** Eccentricity. */
  95.     private final double e;

  96.     /** Inclination (rad). */
  97.     private final double i;

  98.     /** Perigee Argument (rad). */
  99.     private final double pa;

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

  102.     /** True anomaly (rad). */
  103.     private final double v;

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

  106.     /** Eccentricity derivative. */
  107.     private final double eDot;

  108.     /** Inclination derivative (rad/s). */
  109.     private final double iDot;

  110.     /** Perigee Argument derivative (rad/s). */
  111.     private final double paDot;

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

  114.     /** True anomaly derivative (rad/s). */
  115.     private final double vDot;

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

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

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

  174.         if (a * (1 - e) < 0) {
  175.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a, e);
  176.         }

  177.         // Checking eccentricity range
  178.         checkParameterRangeInclusive(ECCENTRICITY, e, 0.0, Double.POSITIVE_INFINITY);

  179.         this.a       = a;
  180.         this.aDot    = aDot;
  181.         this.e       = e;
  182.         this.eDot    = eDot;
  183.         this.i       = i;
  184.         this.iDot    = iDot;
  185.         this.pa      = pa;
  186.         this.paDot   = paDot;
  187.         this.raan    = raan;
  188.         this.raanDot = raanDot;

  189.         if (hasDerivatives()) {
  190.             final UnivariateDerivative1 eUD        = new UnivariateDerivative1(e, eDot);
  191.             final UnivariateDerivative1 anomalyUD  = new UnivariateDerivative1(anomaly,  anomalyDot);
  192.             final UnivariateDerivative1 vUD;
  193.             switch (type) {
  194.                 case MEAN :
  195.                     vUD = (a < 0) ?
  196.                           FieldKeplerianOrbit.hyperbolicEccentricToTrue(FieldKeplerianOrbit.meanToHyperbolicEccentric(anomalyUD, eUD), eUD) :
  197.                           FieldKeplerianOrbit.ellipticEccentricToTrue(FieldKeplerianOrbit.meanToEllipticEccentric(anomalyUD, eUD), eUD);
  198.                     break;
  199.                 case ECCENTRIC :
  200.                     vUD = (a < 0) ?
  201.                           FieldKeplerianOrbit.hyperbolicEccentricToTrue(anomalyUD, eUD) :
  202.                           FieldKeplerianOrbit.ellipticEccentricToTrue(anomalyUD, eUD);
  203.                     break;
  204.                 case TRUE :
  205.                     vUD = anomalyUD;
  206.                     break;
  207.                 default : // this should never happen
  208.                     throw new OrekitInternalError(null);
  209.             }
  210.             this.v    = vUD.getValue();
  211.             this.vDot = vUD.getDerivative(1);
  212.         } else {
  213.             switch (type) {
  214.                 case MEAN :
  215.                     this.v = (a < 0) ?
  216.                              hyperbolicEccentricToTrue(meanToHyperbolicEccentric(anomaly, e), e) :
  217.                              ellipticEccentricToTrue(meanToEllipticEccentric(anomaly, e), e);
  218.                     break;
  219.                 case ECCENTRIC :
  220.                     this.v = (a < 0) ?
  221.                              hyperbolicEccentricToTrue(anomaly, e) :
  222.                              ellipticEccentricToTrue(anomaly, e);
  223.                     break;
  224.                 case TRUE :
  225.                     this.v = anomaly;
  226.                     break;
  227.                 default : // this should never happen
  228.                     throw new OrekitInternalError(null);
  229.             }
  230.             this.vDot = Double.NaN;
  231.         }

  232.         // check true anomaly range
  233.         if (1 + e * FastMath.cos(v) <= 0) {
  234.             final double vMax = FastMath.acos(-1 / e);
  235.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE,
  236.                                                      v, e, -vMax, vMax);
  237.         }

  238.         this.partialPV = null;

  239.     }

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

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

  279.         // compute inclination
  280.         final Vector3D momentum = pvCoordinates.getMomentum();
  281.         final double m2 = momentum.getNormSq();
  282.         i = Vector3D.angle(momentum, Vector3D.PLUS_K);

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

  285.         // preliminary computations for parameters depending on orbit shape (elliptic or hyperbolic)
  286.         final Vector3D pvP     = pvCoordinates.getPosition();
  287.         final Vector3D pvV     = pvCoordinates.getVelocity();
  288.         final Vector3D pvA     = pvCoordinates.getAcceleration();
  289.         final double   r2      = pvP.getNormSq();
  290.         final double   r       = FastMath.sqrt(r2);
  291.         final double   V2      = pvV.getNormSq();
  292.         final double   rV2OnMu = r * V2 / mu;

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

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

  310.         // Checking eccentricity range
  311.         checkParameterRangeInclusive(ECCENTRICITY, e, 0.0, Double.POSITIVE_INFINITY);

  312.         // compute perigee argument
  313.         final Vector3D node = new Vector3D(raan, 0.0);
  314.         final double px = Vector3D.dotProduct(pvP, node);
  315.         final double py = Vector3D.dotProduct(pvP, Vector3D.crossProduct(momentum, node)) / FastMath.sqrt(m2);
  316.         pa = FastMath.atan2(py, px) - v;

  317.         partialPV = pvCoordinates;

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

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

  322.             final Vector3D keplerianAcceleration    = new Vector3D(-mu / (r * r2), pvP);
  323.             final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  324.             final double   aX                       = nonKeplerianAcceleration.getX();
  325.             final double   aY                       = nonKeplerianAcceleration.getY();
  326.             final double   aZ                       = nonKeplerianAcceleration.getZ();
  327.             aDot    = jacobian[0][3] * aX + jacobian[0][4] * aY + jacobian[0][5] * aZ;
  328.             eDot    = jacobian[1][3] * aX + jacobian[1][4] * aY + jacobian[1][5] * aZ;
  329.             iDot    = jacobian[2][3] * aX + jacobian[2][4] * aY + jacobian[2][5] * aZ;
  330.             paDot   = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ;
  331.             raanDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ;

  332.             // in order to compute true anomaly derivative, we must compute
  333.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  334.             final double MDot = getKeplerianMeanMotion() +
  335.                                 jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ;
  336.             final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  337.             final UnivariateDerivative1 MUD = new UnivariateDerivative1(getMeanAnomaly(), MDot);
  338.             final UnivariateDerivative1 vUD = (a < 0) ?
  339.                                             FieldKeplerianOrbit.hyperbolicEccentricToTrue(FieldKeplerianOrbit.meanToHyperbolicEccentric(MUD, eUD), eUD) :
  340.                                             FieldKeplerianOrbit.ellipticEccentricToTrue(FieldKeplerianOrbit.meanToEllipticEccentric(MUD, eUD), eUD);
  341.             vDot = vUD.getDerivative(1);

  342.         } else {
  343.             // acceleration is either almost zero or NaN,
  344.             // we assume acceleration was not known
  345.             // we don't set up derivatives
  346.             aDot    = Double.NaN;
  347.             eDot    = Double.NaN;
  348.             iDot    = Double.NaN;
  349.             paDot   = Double.NaN;
  350.             raanDot = Double.NaN;
  351.             vDot    = Double.NaN;
  352.         }

  353.     }

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

  374.     /** Constructor from any kind of orbital parameters.
  375.      * @param op orbital parameters to copy
  376.      */
  377.     public KeplerianOrbit(final Orbit op) {
  378.         this(op.getPVCoordinates(), op.getFrame(), op.getMu(), op.hasDerivatives());
  379.     }

  380.     /** {@inheritDoc} */
  381.     public OrbitType getType() {
  382.         return OrbitType.KEPLERIAN;
  383.     }

  384.     /** {@inheritDoc} */
  385.     public double getA() {
  386.         return a;
  387.     }

  388.     /** {@inheritDoc} */
  389.     public double getADot() {
  390.         return aDot;
  391.     }

  392.     /** {@inheritDoc} */
  393.     public double getE() {
  394.         return e;
  395.     }

  396.     /** {@inheritDoc} */
  397.     public double getEDot() {
  398.         return eDot;
  399.     }

  400.     /** {@inheritDoc} */
  401.     public double getI() {
  402.         return i;
  403.     }

  404.     /** {@inheritDoc} */
  405.     public double getIDot() {
  406.         return iDot;
  407.     }

  408.     /** Get the perigee argument.
  409.      * @return perigee argument (rad)
  410.      */
  411.     public double getPerigeeArgument() {
  412.         return pa;
  413.     }

  414.     /** Get the perigee argument derivative.
  415.      * <p>
  416.      * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
  417.      * </p>
  418.      * @return perigee argument derivative (rad/s)
  419.      * @since 9.0
  420.      */
  421.     public double getPerigeeArgumentDot() {
  422.         return paDot;
  423.     }

  424.     /** Get the right ascension of the ascending node.
  425.      * @return right ascension of the ascending node (rad)
  426.      */
  427.     public double getRightAscensionOfAscendingNode() {
  428.         return raan;
  429.     }

  430.     /** Get the right ascension of the ascending node derivative.
  431.      * <p>
  432.      * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
  433.      * </p>
  434.      * @return right ascension of the ascending node derivative (rad/s)
  435.      * @since 9.0
  436.      */
  437.     public double getRightAscensionOfAscendingNodeDot() {
  438.         return raanDot;
  439.     }

  440.     /** Get the true anomaly.
  441.      * @return true anomaly (rad)
  442.      */
  443.     public double getTrueAnomaly() {
  444.         return v;
  445.     }

  446.     /** Get the true anomaly derivative.
  447.      * @return true anomaly derivative (rad/s)
  448.      */
  449.     public double getTrueAnomalyDot() {
  450.         return vDot;
  451.     }

  452.     /** Get the eccentric anomaly.
  453.      * @return eccentric anomaly (rad)
  454.      */
  455.     public double getEccentricAnomaly() {
  456.         return (a < 0) ? trueToHyperbolicEccentric(v, e) : trueToEllipticEccentric(v, e);
  457.     }

  458.     /** Get the eccentric anomaly derivative.
  459.      * @return eccentric anomaly derivative (rad/s)
  460.      * @since 9.0
  461.      */
  462.     public double getEccentricAnomalyDot() {
  463.         final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  464.         final UnivariateDerivative1 vUD = new UnivariateDerivative1(v, vDot);
  465.         final UnivariateDerivative1 EUD = (a < 0) ?
  466.                                         FieldKeplerianOrbit.trueToHyperbolicEccentric(vUD, eUD) :
  467.                                         FieldKeplerianOrbit.trueToEllipticEccentric(vUD, eUD);
  468.         return EUD.getDerivative(1);
  469.     }

  470.     /** Get the mean anomaly.
  471.      * @return mean anomaly (rad)
  472.      */
  473.     public double getMeanAnomaly() {
  474.         return (a < 0) ?
  475.                hyperbolicEccentricToMean(trueToHyperbolicEccentric(v, e), e) :
  476.                ellipticEccentricToMean(trueToEllipticEccentric(v, e), e);
  477.     }

  478.     /** Get the mean anomaly derivative.
  479.      * @return mean anomaly derivative (rad/s)
  480.      * @since 9.0
  481.      */
  482.     public double getMeanAnomalyDot() {
  483.         final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  484.         final UnivariateDerivative1 vUD = new UnivariateDerivative1(v, vDot);
  485.         final UnivariateDerivative1 MUD = (a < 0) ?
  486.                                         FieldKeplerianOrbit.hyperbolicEccentricToMean(FieldKeplerianOrbit.trueToHyperbolicEccentric(vUD, eUD), eUD) :
  487.                                         FieldKeplerianOrbit.ellipticEccentricToMean(FieldKeplerianOrbit.trueToEllipticEccentric(vUD, eUD), eUD);
  488.         return MUD.getDerivative(1);
  489.     }

  490.     /** Get the anomaly.
  491.      * @param type type of the angle
  492.      * @return anomaly (rad)
  493.      */
  494.     public double getAnomaly(final PositionAngle type) {
  495.         return (type == PositionAngle.MEAN) ? getMeanAnomaly() :
  496.                                               ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomaly() :
  497.                                                                                    getTrueAnomaly());
  498.     }

  499.     /** Get the anomaly derivative.
  500.      * @param type type of the angle
  501.      * @return anomaly derivative (rad/s)
  502.      * @since 9.0
  503.      */
  504.     public double getAnomalyDot(final PositionAngle type) {
  505.         return (type == PositionAngle.MEAN) ? getMeanAnomalyDot() :
  506.                                               ((type == PositionAngle.ECCENTRIC) ? getEccentricAnomalyDot() :
  507.                                                                                    getTrueAnomalyDot());
  508.     }

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

  520.     /** Computes the elliptic eccentric anomaly from the true anomaly.
  521.      * @param v true anomaly (rad)
  522.      * @param e eccentricity
  523.      * @return E the elliptic eccentric anomaly
  524.      * @since 9.0
  525.      */
  526.     public static double trueToEllipticEccentric(final double v, final double e) {
  527.         final double beta = e / (1 + FastMath.sqrt(1 - e * e));
  528.         final SinCos scv = FastMath.sinCos(v);
  529.         return v - 2 * FastMath.atan(beta * scv.sin() / (1 + beta * scv.cos()));
  530.     }

  531.     /** Computes the true anomaly from the hyperbolic eccentric anomaly.
  532.      * @param H hyperbolic eccentric anomaly (rad)
  533.      * @param e eccentricity
  534.      * @return v the true anomaly
  535.      */
  536.     public static double hyperbolicEccentricToTrue(final double H, final double e) {
  537.         return 2 * FastMath.atan(FastMath.sqrt((e + 1) / (e - 1)) * FastMath.tanh(H / 2));
  538.     }

  539.     /** Computes the hyperbolic eccentric anomaly from the true anomaly.
  540.      * @param v true anomaly (rad)
  541.      * @param e eccentricity
  542.      * @return H the hyperbolic eccentric anomaly
  543.      * @since 9.0
  544.      */
  545.     public static double trueToHyperbolicEccentric(final double v, final double e) {
  546.         final SinCos scv = FastMath.sinCos(v);
  547.         final double sinhH = FastMath.sqrt(e * e - 1) * scv.sin() / (1 + e * scv.cos());
  548.         return FastMath.asinh(sinhH);
  549.     }

  550.     /** Computes the elliptic eccentric anomaly from the mean anomaly.
  551.      * <p>
  552.      * The algorithm used here for solving Kepler equation has been published
  553.      * in: "Procedures for  solving Kepler's Equation", A. W. Odell and
  554.      * R. H. Gooding, Celestial Mechanics 38 (1986) 307-334
  555.      * </p>
  556.      * @param M mean anomaly (rad)
  557.      * @param e eccentricity
  558.      * @return E the eccentric anomaly
  559.      */
  560.     public static double meanToEllipticEccentric(final double M, final double e) {

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

  563.         // compute start value according to A. W. Odell and R. H. Gooding S12 starter
  564.         double E;
  565.         if (FastMath.abs(reducedM) < 1.0 / 6.0) {
  566.             E = reducedM + e * (FastMath.cbrt(6 * reducedM) - reducedM);
  567.         } else {
  568.             if (reducedM < 0) {
  569.                 final double w = FastMath.PI + reducedM;
  570.                 E = reducedM + e * (A * w / (B - w) - FastMath.PI - reducedM);
  571.             } else {
  572.                 final double w = FastMath.PI - reducedM;
  573.                 E = reducedM + e * (FastMath.PI - A * w / (B - w) - reducedM);
  574.             }
  575.         }

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

  578.         // perform two iterations, each consisting of one Halley step and one Newton-Raphson step
  579.         for (int j = 0; j < 2; ++j) {
  580.             final double f;
  581.             double fd;
  582.             final SinCos sc   = FastMath.sinCos(E);
  583.             final double fdd  = e * sc.sin();
  584.             final double fddd = e * sc.cos();
  585.             if (noCancellationRisk) {
  586.                 f  = (E - fdd) - reducedM;
  587.                 fd = 1 - fddd;
  588.             } else {
  589.                 f  = eMeSinE(E, e) - reducedM;
  590.                 final double s = FastMath.sin(0.5 * E);
  591.                 fd = e1 + 2 * e * s * s;
  592.             }
  593.             final double dee = f * fd / (0.5 * f * fdd - fd * fd);

  594.             // update eccentric anomaly, using expressions that limit underflow problems
  595.             final double w = fd + 0.5 * dee * (fdd + dee * fddd / 3);
  596.             fd += dee * (fdd + 0.5 * dee * fddd);
  597.             E  -= (f - dee * (fd - w)) / fd;

  598.         }

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

  601.         return E;

  602.     }

  603.     /** Accurate computation of E - e sin(E).
  604.      * <p>
  605.      * This method is used when E is close to 0 and e close to 1,
  606.      * i.e. near the perigee of almost parabolic orbits
  607.      * </p>
  608.      * @param E eccentric anomaly
  609.      * @param e eccentricity
  610.      * @return E - e sin(E)
  611.      */
  612.     private static double eMeSinE(final double E, final double e) {
  613.         double x = (1 - e) * FastMath.sin(E);
  614.         final double mE2 = -E * E;
  615.         double term = E;
  616.         double d    = 0;
  617.         // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance
  618.         for (double x0 = Double.NaN; !Double.valueOf(x).equals(Double.valueOf(x0));) {
  619.             d += 2;
  620.             term *= mE2 / (d * (d + 1));
  621.             x0 = x;
  622.             x = x - term;
  623.         }
  624.         return x;
  625.     }

  626.     /** Computes the hyperbolic eccentric anomaly from the mean anomaly.
  627.      * <p>
  628.      * The algorithm used here for solving hyperbolic Kepler equation is
  629.      * Danby's iterative method (3rd order) with Vallado's initial guess.
  630.      * </p>
  631.      * @param M mean anomaly (rad)
  632.      * @param ecc eccentricity
  633.      * @return H the hyperbolic eccentric anomaly
  634.      */
  635.     public static double meanToHyperbolicEccentric(final double M, final double ecc) {

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

  637.         // Initial guess
  638.         double H;
  639.         if (ecc < 1.6) {
  640.             if (-FastMath.PI < M && M < 0. || M > FastMath.PI) {
  641.                 H = M - ecc;
  642.             } else {
  643.                 H = M + ecc;
  644.             }
  645.         } else {
  646.             if (ecc < 3.6 && FastMath.abs(M) > FastMath.PI) {
  647.                 H = M - FastMath.copySign(ecc, M);
  648.             } else {
  649.                 H = M / (ecc - 1.);
  650.             }
  651.         }

  652.         // Iterative computation
  653.         int iter = 0;
  654.         do {
  655.             final double f3  = ecc * FastMath.cosh(H);
  656.             final double f2  = ecc * FastMath.sinh(H);
  657.             final double f1  = f3 - 1.;
  658.             final double f0  = f2 - H - M;
  659.             final double f12 = 2. * f1;
  660.             final double d   = f0 / f12;
  661.             final double fdf = f1 - d * f2;
  662.             final double ds  = f0 / fdf;

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

  664.             H -= shift;

  665.             if (FastMath.abs(shift) <= 1.0e-12) {
  666.                 return H;
  667.             }

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

  669.         throw new MathIllegalStateException(OrekitMessages.UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY,
  670.                                             iter);
  671.     }

  672.     /** Computes the mean anomaly from the elliptic eccentric anomaly.
  673.      * @param E eccentric anomaly (rad)
  674.      * @param e eccentricity
  675.      * @return M the mean anomaly
  676.      * @since 9.0
  677.      */
  678.     public static double ellipticEccentricToMean(final double E, final double e) {
  679.         return E - e * FastMath.sin(E);
  680.     }

  681.     /** Computes the mean anomaly from the hyperbolic eccentric anomaly.
  682.      * @param H hyperbolic eccentric anomaly (rad)
  683.      * @param e eccentricity
  684.      * @return M the mean anomaly
  685.      * @since 9.0
  686.      */
  687.     public static double hyperbolicEccentricToMean(final double H, final double e) {
  688.         return e * FastMath.sinh(H) - H;
  689.     }

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

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

  700.     /** {@inheritDoc} */
  701.     public double getEquinoctialEy() {
  702.         return e * FastMath.sin(pa + raan);
  703.     }

  704.     /** {@inheritDoc} */
  705.     public double getEquinoctialEyDot() {
  706.         final double paPraan = pa + raan;
  707.         final SinCos sc      = FastMath.sinCos(paPraan);
  708.         return eDot * sc.sin() + e * sc.cos() * (paDot + raanDot);
  709.     }

  710.     /** {@inheritDoc} */
  711.     public double getHx() {
  712.         // Check for equatorial retrograde orbit
  713.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  714.             return Double.NaN;
  715.         }
  716.         return FastMath.cos(raan) * FastMath.tan(0.5 * i);
  717.     }

  718.     /** {@inheritDoc} */
  719.     public double getHxDot() {
  720.         // Check for equatorial retrograde orbit
  721.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  722.             return Double.NaN;
  723.         }
  724.         final SinCos sc      = FastMath.sinCos(raan);
  725.         final double tan     = FastMath.tan(0.5 * i);
  726.         return 0.5 * (1 + tan * tan) * sc.cos() * iDot - tan * sc.sin() * raanDot;
  727.     }

  728.     /** {@inheritDoc} */
  729.     public double getHy() {
  730.         // Check for equatorial retrograde orbit
  731.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  732.             return Double.NaN;
  733.         }
  734.         return  FastMath.sin(raan) * FastMath.tan(0.5 * i);
  735.     }

  736.     /** {@inheritDoc} */
  737.     public double getHyDot() {
  738.         // Check for equatorial retrograde orbit
  739.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  740.             return Double.NaN;
  741.         }
  742.         final SinCos sc      = FastMath.sinCos(raan);
  743.         final double tan     = FastMath.tan(0.5 * i);
  744.         return 0.5 * (1 + tan * tan) * sc.sin() * iDot + tan * sc.cos() * raanDot;
  745.     }

  746.     /** {@inheritDoc} */
  747.     public double getLv() {
  748.         return pa + raan + v;
  749.     }

  750.     /** {@inheritDoc} */
  751.     public double getLvDot() {
  752.         return paDot + raanDot + vDot;
  753.     }

  754.     /** {@inheritDoc} */
  755.     public double getLE() {
  756.         return pa + raan + getEccentricAnomaly();
  757.     }

  758.     /** {@inheritDoc} */
  759.     public double getLEDot() {
  760.         return paDot + raanDot + getEccentricAnomalyDot();
  761.     }

  762.     /** {@inheritDoc} */
  763.     public double getLM() {
  764.         return pa + raan + getMeanAnomaly();
  765.     }

  766.     /** {@inheritDoc} */
  767.     public double getLMDot() {
  768.         return paDot + raanDot + getMeanAnomalyDot();
  769.     }

  770.     /** Compute position and velocity but not acceleration.
  771.      */
  772.     private void computePVWithoutA() {

  773.         if (partialPV != null) {
  774.             // already computed
  775.             return;
  776.         }

  777.         // preliminary variables
  778.         final SinCos scRaan  = FastMath.sinCos(raan);
  779.         final SinCos scPa    = FastMath.sinCos(pa);
  780.         final SinCos scI     = FastMath.sinCos(i);
  781.         final double cosRaan = scRaan.cos();
  782.         final double sinRaan = scRaan.sin();
  783.         final double cosPa   = scPa.cos();
  784.         final double sinPa   = scPa.sin();
  785.         final double cosI    = scI.cos();
  786.         final double sinI    = scI.sin();

  787.         final double crcp    = cosRaan * cosPa;
  788.         final double crsp    = cosRaan * sinPa;
  789.         final double srcp    = sinRaan * cosPa;
  790.         final double srsp    = sinRaan * sinPa;

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

  794.         if (a > 0) {

  795.             // elliptical case

  796.             // elliptic eccentric anomaly
  797.             final double uME2   = (1 - e) * (1 + e);
  798.             final double s1Me2  = FastMath.sqrt(uME2);
  799.             final SinCos scE    = FastMath.sinCos(getEccentricAnomaly());
  800.             final double cosE   = scE.cos();
  801.             final double sinE   = scE.sin();

  802.             // coordinates of position and velocity in the orbital plane
  803.             final double x      = a * (cosE - e);
  804.             final double y      = a * sinE * s1Me2;
  805.             final double factor = FastMath.sqrt(getMu() / a) / (1 - e * cosE);
  806.             final double xDot   = -sinE * factor;
  807.             final double yDot   =  cosE * s1Me2 * factor;

  808.             final Vector3D position = new Vector3D(x, p, y, q);
  809.             final Vector3D velocity = new Vector3D(xDot, p, yDot, q);
  810.             partialPV = new PVCoordinates(position, velocity);

  811.         } else {

  812.             // hyperbolic case

  813.             // compute position and velocity factors
  814.             final SinCos scV       = FastMath.sinCos(v);
  815.             final double sinV      = scV.sin();
  816.             final double cosV      = scV.cos();
  817.             final double f         = a * (1 - e * e);
  818.             final double posFactor = f / (1 + e * cosV);
  819.             final double velFactor = FastMath.sqrt(getMu() / f);

  820.             final double   x            =  posFactor * cosV;
  821.             final double   y            =  posFactor * sinV;
  822.             final double   xDot         = -velFactor * sinV;
  823.             final double   yDot         =  velFactor * (e + cosV);

  824.             final Vector3D position     = new Vector3D(x, p, y, q);
  825.             final Vector3D velocity     = new Vector3D(xDot, p, yDot, q);
  826.             partialPV = new PVCoordinates(position, velocity);

  827.         }

  828.     }

  829.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  830.      * <p>
  831.      * This method should be called only when {@link #hasDerivatives()} returns true.
  832.      * </p>
  833.      * @return non-Keplerian part of the acceleration
  834.      */
  835.     private Vector3D nonKeplerianAcceleration() {

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

  838.         final double nonKeplerianMeanMotion = getMeanAnomalyDot() - getKeplerianMeanMotion();
  839.         final double nonKeplerianAx = dCdP[3][0] * aDot    + dCdP[3][1] * eDot    + dCdP[3][2] * iDot    +
  840.                                       dCdP[3][3] * paDot   + dCdP[3][4] * raanDot + dCdP[3][5] * nonKeplerianMeanMotion;
  841.         final double nonKeplerianAy = dCdP[4][0] * aDot    + dCdP[4][1] * eDot    + dCdP[4][2] * iDot    +
  842.                                       dCdP[4][3] * paDot   + dCdP[4][4] * raanDot + dCdP[4][5] * nonKeplerianMeanMotion;
  843.         final double nonKeplerianAz = dCdP[5][0] * aDot    + dCdP[5][1] * eDot    + dCdP[5][2] * iDot    +
  844.                                       dCdP[5][3] * paDot   + dCdP[5][4] * raanDot + dCdP[5][5] * nonKeplerianMeanMotion;

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

  846.     }

  847.     /** {@inheritDoc} */
  848.     protected TimeStampedPVCoordinates initPVCoordinates() {

  849.         // position and velocity
  850.         computePVWithoutA();

  851.         // acceleration
  852.         final double r2 = partialPV.getPosition().getNormSq();
  853.         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
  854.         final Vector3D acceleration = hasDerivatives() ?
  855.                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
  856.                                       keplerianAcceleration;

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

  858.     }

  859.     /** {@inheritDoc} */
  860.     public KeplerianOrbit shiftedBy(final double dt) {

  861.         // use Keplerian-only motion
  862.         final KeplerianOrbit keplerianShifted = new KeplerianOrbit(a, e, i, pa, raan,
  863.                                                                    getMeanAnomaly() + getKeplerianMeanMotion() * dt,
  864.                                                                    PositionAngle.MEAN, getFrame(),
  865.                                                                    getDate().shiftedBy(dt), getMu());

  866.         if (hasDerivatives()) {

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

  869.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  870.             keplerianShifted.computePVWithoutA();
  871.             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
  872.                                                    0.5 * dt * dt, nonKeplerianAcceleration);
  873.             final double   fixedR2 = fixedP.getNormSq();
  874.             final double   fixedR  = FastMath.sqrt(fixedR2);
  875.             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
  876.                                                   dt, nonKeplerianAcceleration);
  877.             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
  878.                                                   1, nonKeplerianAcceleration);

  879.             // build a new orbit, taking non-Keplerian acceleration into account
  880.             return new KeplerianOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
  881.                                                                    fixedP, fixedV, fixedA),
  882.                                       keplerianShifted.getFrame(), keplerianShifted.getMu());

  883.         } else {
  884.             // Keplerian-only motion is all we can do
  885.             return keplerianShifted;
  886.         }

  887.     }

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

  909.         // first pass to check if derivatives are available throughout the sample
  910.         final List<Orbit> list = sample.collect(Collectors.toList());
  911.         boolean useDerivatives = true;
  912.         for (final Orbit orbit : list) {
  913.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  914.         }

  915.         // set up an interpolator
  916.         final HermiteInterpolator interpolator = new HermiteInterpolator();

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

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

  973.         // build a new interpolated instance
  974.         return new KeplerianOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  975.                                   interpolated[0][3], interpolated[0][4], interpolated[0][5],
  976.                                   interpolated[1][0], interpolated[1][1], interpolated[1][2],
  977.                                   interpolated[1][3], interpolated[1][4], interpolated[1][5],
  978.                                   PositionAngle.MEAN, getFrame(), date, getMu());

  979.     }

  980.     /** {@inheritDoc} */
  981.     protected double[][] computeJacobianMeanWrtCartesian() {
  982.         if (a > 0) {
  983.             return computeJacobianMeanWrtCartesianElliptical();
  984.         } else {
  985.             return computeJacobianMeanWrtCartesianHyperbolic();
  986.         }
  987.     }

  988.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  989.      * <p>
  990.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  991.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  992.      * yDot for j=4, zDot for j=5).
  993.      * </p>
  994.      * @return 6x6 Jacobian matrix
  995.      */
  996.     private double[][] computeJacobianMeanWrtCartesianElliptical() {

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

  998.         // compute various intermediate parameters
  999.         computePVWithoutA();
  1000.         final Vector3D position = partialPV.getPosition();
  1001.         final Vector3D velocity = partialPV.getVelocity();
  1002.         final Vector3D momentum = partialPV.getMomentum();
  1003.         final double v2         = velocity.getNormSq();
  1004.         final double r2         = position.getNormSq();
  1005.         final double r          = FastMath.sqrt(r2);
  1006.         final double r3         = r * r2;

  1007.         final double px         = position.getX();
  1008.         final double py         = position.getY();
  1009.         final double pz         = position.getZ();
  1010.         final double vx         = velocity.getX();
  1011.         final double vy         = velocity.getY();
  1012.         final double vz         = velocity.getZ();
  1013.         final double mx         = momentum.getX();
  1014.         final double my         = momentum.getY();
  1015.         final double mz         = momentum.getZ();

  1016.         final double mu         = getMu();
  1017.         final double sqrtMuA    = FastMath.sqrt(a * mu);
  1018.         final double sqrtAoMu   = FastMath.sqrt(a / mu);
  1019.         final double a2         = a * a;
  1020.         final double twoA       = 2 * a;
  1021.         final double rOnA       = r / a;

  1022.         final double oMe2       = 1 - e * e;
  1023.         final double epsilon    = FastMath.sqrt(oMe2);
  1024.         final double sqrtRec    = 1 / epsilon;

  1025.         final SinCos scI        = FastMath.sinCos(i);
  1026.         final SinCos scPA       = FastMath.sinCos(pa);
  1027.         final double cosI       = scI.cos();
  1028.         final double sinI       = scI.sin();
  1029.         final double cosPA      = scPA.cos();
  1030.         final double sinPA      = scPA.sin();

  1031.         final double pv         = Vector3D.dotProduct(position, velocity);
  1032.         final double cosE       = (a - r) / (a * e);
  1033.         final double sinE       = pv / (e * sqrtMuA);

  1034.         // da
  1035.         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
  1036.         final Vector3D vectorARDot = velocity.scalarMultiply(2 * a2 / mu);
  1037.         fillHalfRow(1, vectorAR,    jacobian[0], 0);
  1038.         fillHalfRow(1, vectorARDot, jacobian[0], 3);

  1039.         // de
  1040.         final double factorER3 = pv / twoA;
  1041.         final Vector3D vectorER   = new Vector3D(cosE * v2 / (r * mu), position,
  1042.                                                  sinE / sqrtMuA, velocity,
  1043.                                                  -factorER3 * sinE / sqrtMuA, vectorAR);
  1044.         final Vector3D vectorERDot = new Vector3D(sinE / sqrtMuA, position,
  1045.                                                   cosE * 2 * r / mu, velocity,
  1046.                                                   -factorER3 * sinE / sqrtMuA, vectorARDot);
  1047.         fillHalfRow(1, vectorER,    jacobian[1], 0);
  1048.         fillHalfRow(1, vectorERDot, jacobian[1], 3);

  1049.         // dE / dr (Eccentric anomaly)
  1050.         final double coefE = cosE / (e * sqrtMuA);
  1051.         final Vector3D  vectorEAnR =
  1052.             new Vector3D(-sinE * v2 / (e * r * mu), position, coefE, velocity,
  1053.                          -factorER3 * coefE, vectorAR);

  1054.         // dE / drDot
  1055.         final Vector3D  vectorEAnRDot =
  1056.             new Vector3D(-sinE * 2 * r / (e * mu), velocity, coefE, position,
  1057.                          -factorER3 * coefE, vectorARDot);

  1058.         // precomputing some more factors
  1059.         final double s1 = -sinE * pz / r - cosE * vz * sqrtAoMu;
  1060.         final double s2 = -cosE * pz / r3;
  1061.         final double s3 = -sinE * vz / (2 * sqrtMuA);
  1062.         final double t1 = sqrtRec * (cosE * pz / r - sinE * vz * sqrtAoMu);
  1063.         final double t2 = sqrtRec * (-sinE * pz / r3);
  1064.         final double t3 = sqrtRec * (cosE - e) * vz / (2 * sqrtMuA);
  1065.         final double t4 = sqrtRec * (e * sinI * cosPA * sqrtRec - vz * sqrtAoMu);
  1066.         final Vector3D s = new Vector3D(cosE / r, Vector3D.PLUS_K,
  1067.                                         s1,       vectorEAnR,
  1068.                                         s2,       position,
  1069.                                         s3,       vectorAR);
  1070.         final Vector3D sDot = new Vector3D(-sinE * sqrtAoMu, Vector3D.PLUS_K,
  1071.                                            s1,               vectorEAnRDot,
  1072.                                            s3,               vectorARDot);
  1073.         final Vector3D t =
  1074.             new Vector3D(sqrtRec * sinE / r, Vector3D.PLUS_K).add(new Vector3D(t1, vectorEAnR,
  1075.                                                                                t2, position,
  1076.                                                                                t3, vectorAR,
  1077.                                                                                t4, vectorER));
  1078.         final Vector3D tDot = new Vector3D(sqrtRec * (cosE - e) * sqrtAoMu, Vector3D.PLUS_K,
  1079.                                            t1,                              vectorEAnRDot,
  1080.                                            t3,                              vectorARDot,
  1081.                                            t4,                              vectorERDot);

  1082.         // di
  1083.         final double factorI1 = -sinI * sqrtRec / sqrtMuA;
  1084.         final double i1 =  factorI1;
  1085.         final double i2 = -factorI1 * mz / twoA;
  1086.         final double i3 =  factorI1 * mz * e / oMe2;
  1087.         final double i4 = cosI * sinPA;
  1088.         final double i5 = cosI * cosPA;
  1089.         fillHalfRow(i1, new Vector3D(vy, -vx, 0), i2, vectorAR, i3, vectorER, i4, s, i5, t,
  1090.                     jacobian[2], 0);
  1091.         fillHalfRow(i1, new Vector3D(-py, px, 0), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
  1092.                     jacobian[2], 3);

  1093.         // dpa
  1094.         fillHalfRow(cosPA / sinI, s,    -sinPA / sinI, t,    jacobian[3], 0);
  1095.         fillHalfRow(cosPA / sinI, sDot, -sinPA / sinI, tDot, jacobian[3], 3);

  1096.         // dRaan
  1097.         final double factorRaanR = 1 / (mu * a * oMe2 * sinI * sinI);
  1098.         fillHalfRow(-factorRaanR * my, new Vector3D(  0, vz, -vy),
  1099.                      factorRaanR * mx, new Vector3D(-vz,  0,  vx),
  1100.                      jacobian[4], 0);
  1101.         fillHalfRow(-factorRaanR * my, new Vector3D( 0, -pz,  py),
  1102.                      factorRaanR * mx, new Vector3D(pz,   0, -px),
  1103.                      jacobian[4], 3);

  1104.         // dM
  1105.         fillHalfRow(rOnA, vectorEAnR,    -sinE, vectorER,    jacobian[5], 0);
  1106.         fillHalfRow(rOnA, vectorEAnRDot, -sinE, vectorERDot, jacobian[5], 3);

  1107.         return jacobian;

  1108.     }

  1109.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1110.      * <p>
  1111.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1112.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1113.      * yDot for j=4, zDot for j=5).
  1114.      * </p>
  1115.      * @return 6x6 Jacobian matrix
  1116.      */
  1117.     private double[][] computeJacobianMeanWrtCartesianHyperbolic() {

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

  1119.         // compute various intermediate parameters
  1120.         computePVWithoutA();
  1121.         final Vector3D position = partialPV.getPosition();
  1122.         final Vector3D velocity = partialPV.getVelocity();
  1123.         final Vector3D momentum = partialPV.getMomentum();
  1124.         final double r2         = position.getNormSq();
  1125.         final double r          = FastMath.sqrt(r2);
  1126.         final double r3         = r * r2;

  1127.         final double x          = position.getX();
  1128.         final double y          = position.getY();
  1129.         final double z          = position.getZ();
  1130.         final double vx         = velocity.getX();
  1131.         final double vy         = velocity.getY();
  1132.         final double vz         = velocity.getZ();
  1133.         final double mx         = momentum.getX();
  1134.         final double my         = momentum.getY();
  1135.         final double mz         = momentum.getZ();

  1136.         final double mu         = getMu();
  1137.         final double absA       = -a;
  1138.         final double sqrtMuA    = FastMath.sqrt(absA * mu);
  1139.         final double a2         = a * a;
  1140.         final double rOa        = r / absA;

  1141.         final SinCos scI        = FastMath.sinCos(i);
  1142.         final double cosI       = scI.cos();
  1143.         final double sinI       = scI.sin();

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

  1145.         // da
  1146.         final Vector3D vectorAR = new Vector3D(-2 * a2 / r3, position);
  1147.         final Vector3D vectorARDot = velocity.scalarMultiply(-2 * a2 / mu);
  1148.         fillHalfRow(-1, vectorAR,    jacobian[0], 0);
  1149.         fillHalfRow(-1, vectorARDot, jacobian[0], 3);

  1150.         // differentials of the momentum
  1151.         final double m      = momentum.getNorm();
  1152.         final double oOm    = 1 / m;
  1153.         final Vector3D dcXP = new Vector3D(  0,  vz, -vy);
  1154.         final Vector3D dcYP = new Vector3D(-vz,   0,  vx);
  1155.         final Vector3D dcZP = new Vector3D( vy, -vx,   0);
  1156.         final Vector3D dcXV = new Vector3D(  0,  -z,   y);
  1157.         final Vector3D dcYV = new Vector3D(  z,   0,  -x);
  1158.         final Vector3D dcZV = new Vector3D( -y,   x,   0);
  1159.         final Vector3D dCP  = new Vector3D(mx * oOm, dcXP, my * oOm, dcYP, mz * oOm, dcZP);
  1160.         final Vector3D dCV  = new Vector3D(mx * oOm, dcXV, my * oOm, dcYV, mz * oOm, dcZV);

  1161.         // dp
  1162.         final double mOMu   = m / mu;
  1163.         final Vector3D dpP  = new Vector3D(2 * mOMu, dCP);
  1164.         final Vector3D dpV  = new Vector3D(2 * mOMu, dCV);

  1165.         // de
  1166.         final double p      = m * mOMu;
  1167.         final double moO2ae = 1 / (2 * absA * e);
  1168.         final double m2OaMu = -p / absA;
  1169.         fillHalfRow(moO2ae, dpP, m2OaMu * moO2ae, vectorAR,    jacobian[1], 0);
  1170.         fillHalfRow(moO2ae, dpV, m2OaMu * moO2ae, vectorARDot, jacobian[1], 3);

  1171.         // di
  1172.         final double cI1 = 1 / (m * sinI);
  1173.         final double cI2 = cosI * cI1;
  1174.         fillHalfRow(cI2, dCP, -cI1, dcZP, jacobian[2], 0);
  1175.         fillHalfRow(cI2, dCV, -cI1, dcZV, jacobian[2], 3);

  1176.         // dPA
  1177.         final double cP1     =  y * oOm;
  1178.         final double cP2     = -x * oOm;
  1179.         final double cP3     = -(mx * cP1 + my * cP2);
  1180.         final double cP4     = cP3 * oOm;
  1181.         final double cP5     = -1 / (r2 * sinI * sinI);
  1182.         final double cP6     = z  * cP5;
  1183.         final double cP7     = cP3 * cP5;
  1184.         final Vector3D dacP  = new Vector3D(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new Vector3D(-my, mx, 0));
  1185.         final Vector3D dacV  = new Vector3D(cP1, dcXV, cP2, dcYV, cP4, dCV);
  1186.         final Vector3D dpoP  = new Vector3D(cP6, dacP, cP7, Vector3D.PLUS_K);
  1187.         final Vector3D dpoV  = new Vector3D(cP6, dacV);

  1188.         final double re2     = r2 * e * e;
  1189.         final double recOre2 = (p - r) / re2;
  1190.         final double resOre2 = (pv * mOMu) / re2;
  1191.         final Vector3D dreP  = new Vector3D(mOMu, velocity, pv / mu, dCP);
  1192.         final Vector3D dreV  = new Vector3D(mOMu, position, pv / mu, dCV);
  1193.         final Vector3D davP  = new Vector3D(-resOre2, dpP, recOre2, dreP, resOre2 / r, position);
  1194.         final Vector3D davV  = new Vector3D(-resOre2, dpV, recOre2, dreV);
  1195.         fillHalfRow(1, dpoP, -1, davP, jacobian[3], 0);
  1196.         fillHalfRow(1, dpoV, -1, davV, jacobian[3], 3);

  1197.         // dRAAN
  1198.         final double cO0 = cI1 * cI1;
  1199.         final double cO1 =  mx * cO0;
  1200.         final double cO2 = -my * cO0;
  1201.         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
  1202.         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);

  1203.         // dM
  1204.         final double s2a    = pv / (2 * absA);
  1205.         final double oObux  = 1 / FastMath.sqrt(m * m + mu * absA);
  1206.         final double scasbu = pv * oObux;
  1207.         final Vector3D dauP = new Vector3D(1 / sqrtMuA, velocity, -s2a / sqrtMuA, vectorAR);
  1208.         final Vector3D dauV = new Vector3D(1 / sqrtMuA, position, -s2a / sqrtMuA, vectorARDot);
  1209.         final Vector3D dbuP = new Vector3D(oObux * mu / 2, vectorAR,    m * oObux, dCP);
  1210.         final Vector3D dbuV = new Vector3D(oObux * mu / 2, vectorARDot, m * oObux, dCV);
  1211.         final Vector3D dcuP = new Vector3D(oObux, velocity, -scasbu * oObux, dbuP);
  1212.         final Vector3D dcuV = new Vector3D(oObux, position, -scasbu * oObux, dbuV);
  1213.         fillHalfRow(1, dauP, -e / (1 + rOa), dcuP, jacobian[5], 0);
  1214.         fillHalfRow(1, dauV, -e / (1 + rOa), dcuV, jacobian[5], 3);

  1215.         return jacobian;

  1216.     }

  1217.     /** {@inheritDoc} */
  1218.     protected double[][] computeJacobianEccentricWrtCartesian() {
  1219.         if (a > 0) {
  1220.             return computeJacobianEccentricWrtCartesianElliptical();
  1221.         } else {
  1222.             return computeJacobianEccentricWrtCartesianHyperbolic();
  1223.         }
  1224.     }

  1225.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1226.      * <p>
  1227.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1228.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1229.      * yDot for j=4, zDot for j=5).
  1230.      * </p>
  1231.      * @return 6x6 Jacobian matrix
  1232.      */
  1233.     private double[][] computeJacobianEccentricWrtCartesianElliptical() {

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

  1236.         // Differentiating the Kepler equation M = E - e sin E leads to:
  1237.         // dM = (1 - e cos E) dE - sin E de
  1238.         // which is inverted and rewritten as:
  1239.         // dE = a/r dM + sin E a/r de
  1240.         final SinCos scE              = FastMath.sinCos(getEccentricAnomaly());
  1241.         final double aOr              = 1 / (1 - e * scE.cos());

  1242.         // update anomaly row
  1243.         final double[] eRow           = jacobian[1];
  1244.         final double[] anomalyRow     = jacobian[5];
  1245.         for (int j = 0; j < anomalyRow.length; ++j) {
  1246.             anomalyRow[j] = aOr * (anomalyRow[j] + scE.sin() * eRow[j]);
  1247.         }

  1248.         return jacobian;

  1249.     }

  1250.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1251.      * <p>
  1252.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1253.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1254.      * yDot for j=4, zDot for j=5).
  1255.      * </p>
  1256.      * @return 6x6 Jacobian matrix
  1257.      */
  1258.     private double[][] computeJacobianEccentricWrtCartesianHyperbolic() {

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

  1261.         // Differentiating the Kepler equation M = e sinh H - H leads to:
  1262.         // dM = (e cosh H - 1) dH + sinh H de
  1263.         // which is inverted and rewritten as:
  1264.         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
  1265.         final double H      = getEccentricAnomaly();
  1266.         final double coshH  = FastMath.cosh(H);
  1267.         final double sinhH  = FastMath.sinh(H);
  1268.         final double absaOr = 1 / (e * coshH - 1);

  1269.         // update anomaly row
  1270.         final double[] eRow       = jacobian[1];
  1271.         final double[] anomalyRow = jacobian[5];
  1272.         for (int j = 0; j < anomalyRow.length; ++j) {
  1273.             anomalyRow[j] = absaOr * (anomalyRow[j] - sinhH * eRow[j]);
  1274.         }

  1275.         return jacobian;

  1276.     }

  1277.     /** {@inheritDoc} */
  1278.     protected double[][] computeJacobianTrueWrtCartesian() {
  1279.         if (a > 0) {
  1280.             return computeJacobianTrueWrtCartesianElliptical();
  1281.         } else {
  1282.             return computeJacobianTrueWrtCartesianHyperbolic();
  1283.         }
  1284.     }

  1285.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1286.      * <p>
  1287.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1288.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1289.      * yDot for j=4, zDot for j=5).
  1290.      * </p>
  1291.      * @return 6x6 Jacobian matrix
  1292.      */
  1293.     private double[][] computeJacobianTrueWrtCartesianElliptical() {

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

  1296.         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
  1297.         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
  1298.         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
  1299.         // which is inverted and rewritten as:
  1300.         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
  1301.         final double e2           = e * e;
  1302.         final double oMe2         = 1 - e2;
  1303.         final double epsilon      = FastMath.sqrt(oMe2);
  1304.         final SinCos scE          = FastMath.sinCos(getEccentricAnomaly());
  1305.         final double aOr          = 1 / (1 - e * scE.cos());
  1306.         final double aFactor      = epsilon * aOr;
  1307.         final double eFactor      = scE.sin() * aOr / epsilon;

  1308.         // update anomaly row
  1309.         final double[] eRow       = jacobian[1];
  1310.         final double[] anomalyRow = jacobian[5];
  1311.         for (int j = 0; j < anomalyRow.length; ++j) {
  1312.             anomalyRow[j] = aFactor * anomalyRow[j] + eFactor * eRow[j];
  1313.         }

  1314.         return jacobian;

  1315.     }

  1316.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1317.      * <p>
  1318.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1319.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1320.      * yDot for j=4, zDot for j=5).
  1321.      * </p>
  1322.      * @return 6x6 Jacobian matrix
  1323.      */
  1324.     private double[][] computeJacobianTrueWrtCartesianHyperbolic() {

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

  1327.         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
  1328.         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
  1329.         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
  1330.         // which is inverted and rewritten as:
  1331.         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
  1332.         final double e2       = e * e;
  1333.         final double e2Mo     = e2 - 1;
  1334.         final double epsilon  = FastMath.sqrt(e2Mo);
  1335.         final double H        = getEccentricAnomaly();
  1336.         final double coshH    = FastMath.cosh(H);
  1337.         final double sinhH    = FastMath.sinh(H);
  1338.         final double aOr      = 1 / (e * coshH - 1);
  1339.         final double aFactor  = epsilon * aOr;
  1340.         final double eFactor  = sinhH * aOr / epsilon;

  1341.         // update anomaly row
  1342.         final double[] eRow           = jacobian[1];
  1343.         final double[] anomalyRow     = jacobian[5];
  1344.         for (int j = 0; j < anomalyRow.length; ++j) {
  1345.             anomalyRow[j] = aFactor * anomalyRow[j] - eFactor * eRow[j];
  1346.         }

  1347.         return jacobian;

  1348.     }

  1349.     /** {@inheritDoc} */
  1350.     public void addKeplerContribution(final PositionAngle type, final double gm,
  1351.                                       final double[] pDot) {
  1352.         final double oMe2;
  1353.         final double ksi;
  1354.         final double absA = FastMath.abs(a);
  1355.         final double n    = FastMath.sqrt(gm / absA) / absA;
  1356.         switch (type) {
  1357.             case MEAN :
  1358.                 pDot[5] += n;
  1359.                 break;
  1360.             case ECCENTRIC :
  1361.                 oMe2 = FastMath.abs(1 - e * e);
  1362.                 ksi  = 1 + e * FastMath.cos(v);
  1363.                 pDot[5] += n * ksi / oMe2;
  1364.                 break;
  1365.             case TRUE :
  1366.                 oMe2 = FastMath.abs(1 - e * e);
  1367.                 ksi  = 1 + e * FastMath.cos(v);
  1368.                 pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
  1369.                 break;
  1370.             default :
  1371.                 throw new OrekitInternalError(null);
  1372.         }
  1373.     }

  1374.     /**  Returns a string representation of this Keplerian parameters object.
  1375.      * @return a string representation of this object
  1376.      */
  1377.     public String toString() {
  1378.         return new StringBuilder().append("Keplerian parameters: ").append('{').
  1379.                                   append("a: ").append(a).
  1380.                                   append("; e: ").append(e).
  1381.                                   append("; i: ").append(FastMath.toDegrees(i)).
  1382.                                   append("; pa: ").append(FastMath.toDegrees(pa)).
  1383.                                   append("; raan: ").append(FastMath.toDegrees(raan)).
  1384.                                   append("; v: ").append(FastMath.toDegrees(v)).
  1385.                                   append(";}").toString();
  1386.     }

  1387.     /** Check if the given parameter is within an acceptable range.
  1388.      * The bounds are inclusive: an exception is raised when either of those conditions are met:
  1389.      * <ul>
  1390.      *     <li>The parameter is strictly greater than upperBound</li>
  1391.      *     <li>The parameter is strictly lower than lowerBound</li>
  1392.      * </ul>
  1393.      * <p>
  1394.      * In either of these cases, an OrekitException is raised.
  1395.      * </p>
  1396.      * @param parameterName name of the parameter
  1397.      * @param parameter value of the parameter
  1398.      * @param lowerBound lower bound of the acceptable range (inclusive)
  1399.      * @param upperBound upper bound of the acceptable range (inclusive)
  1400.      */
  1401.     private void checkParameterRangeInclusive(final String parameterName, final double parameter,
  1402.                                               final double lowerBound, final double upperBound) {
  1403.         if (parameter < lowerBound || parameter > upperBound) {
  1404.             throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName,
  1405.                                       parameter, lowerBound, upperBound);
  1406.         }
  1407.     }

  1408.     /** Replace the instance with a data transfer object for serialization.
  1409.      * @return data transfer object that will be serialized
  1410.      */
  1411.     @DefaultDataContext
  1412.     private Object writeReplace() {
  1413.         return new DTO(this);
  1414.     }

  1415.     /** Internal class used only for serialization. */
  1416.     @DefaultDataContext
  1417.     private static class DTO implements Serializable {

  1418.         /** Serializable UID. */
  1419.         private static final long serialVersionUID = 20170414L;

  1420.         /** Double values. */
  1421.         private double[] d;

  1422.         /** Frame in which are defined the orbital parameters. */
  1423.         private final Frame frame;

  1424.         /** Simple constructor.
  1425.          * @param orbit instance to serialize
  1426.          */
  1427.         private DTO(final KeplerianOrbit orbit) {

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

  1429.             // decompose date
  1430.             final AbsoluteDate j2000Epoch =
  1431.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  1432.             final double epoch  = FastMath.floor(pv.getDate().durationFrom(j2000Epoch));
  1433.             final double offset = pv.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));

  1434.             if (orbit.hasDerivatives()) {
  1435.                 // we have derivatives
  1436.                 this.d = new double[] {
  1437.                     epoch, offset, orbit.getMu(),
  1438.                     orbit.a, orbit.e, orbit.i,
  1439.                     orbit.pa, orbit.raan, orbit.v,
  1440.                     orbit.aDot, orbit.eDot, orbit.iDot,
  1441.                     orbit.paDot, orbit.raanDot, orbit.vDot
  1442.                 };
  1443.             } else {
  1444.                 // we don't have derivatives
  1445.                 this.d = new double[] {
  1446.                     epoch, offset, orbit.getMu(),
  1447.                     orbit.a, orbit.e, orbit.i,
  1448.                     orbit.pa, orbit.raan, orbit.v
  1449.                 };
  1450.             }

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

  1452.         }

  1453.         /** Replace the deserialized data transfer object with a {@link KeplerianOrbit}.
  1454.          * @return replacement {@link KeplerianOrbit}
  1455.          */
  1456.         private Object readResolve() {
  1457.             final AbsoluteDate j2000Epoch =
  1458.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  1459.             if (d.length >= 15) {
  1460.                 // we have derivatives
  1461.                 return new KeplerianOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8],
  1462.                                           d[ 9], d[10], d[11], d[12], d[13], d[14],
  1463.                                           PositionAngle.TRUE,
  1464.                                           frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  1465.                                           d[2]);
  1466.             } else {
  1467.                 // we don't have derivatives
  1468.                 return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngle.TRUE,
  1469.                                           frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  1470.                                           d[2]);
  1471.             }
  1472.         }

  1473.     }

  1474. }