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


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

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

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

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

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

  80.     /** Semi-major axis (m). */
  81.     private final double a;

  82.     /** Eccentricity. */
  83.     private final double e;

  84.     /** Inclination (rad). */
  85.     private final double i;

  86.     /** Perigee Argument (rad). */
  87.     private final double pa;

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

  90.     /** True anomaly (rad). */
  91.     private final double v;

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

  94.     /** Eccentricity derivative. */
  95.     private final double eDot;

  96.     /** Inclination derivative (rad/s). */
  97.     private final double iDot;

  98.     /** Perigee Argument derivative (rad/s). */
  99.     private final double paDot;

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

  102.     /** True anomaly derivative (rad/s). */
  103.     private final double vDot;

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

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

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

  162.         if (a * (1 - e) < 0) {
  163.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a, e);
  164.         }

  165.         // Checking eccentricity range
  166.         checkParameterRangeInclusive(ECCENTRICITY, e, 0.0, Double.POSITIVE_INFINITY);

  167.         this.a       = a;
  168.         this.aDot    = aDot;
  169.         this.e       = e;
  170.         this.eDot    = eDot;
  171.         this.i       = i;
  172.         this.iDot    = iDot;
  173.         this.pa      = pa;
  174.         this.paDot   = paDot;
  175.         this.raan    = raan;
  176.         this.raanDot = raanDot;

  177.         if (hasDerivatives()) {
  178.             final UnivariateDerivative1 eUD        = new UnivariateDerivative1(e, eDot);
  179.             final UnivariateDerivative1 anomalyUD  = new UnivariateDerivative1(anomaly,  anomalyDot);
  180.             final UnivariateDerivative1 vUD;
  181.             switch (type) {
  182.                 case MEAN :
  183.                     vUD = (a < 0) ?
  184.                           FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD) :
  185.                           FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD);
  186.                     break;
  187.                 case ECCENTRIC :
  188.                     vUD = (a < 0) ?
  189.                           FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD) :
  190.                           FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD);
  191.                     break;
  192.                 case TRUE :
  193.                     vUD = anomalyUD;
  194.                     break;
  195.                 default : // this should never happen
  196.                     throw new OrekitInternalError(null);
  197.             }
  198.             this.v    = vUD.getValue();
  199.             this.vDot = vUD.getDerivative(1);
  200.         } else {
  201.             switch (type) {
  202.                 case MEAN :
  203.                     this.v = (a < 0) ?
  204.                              KeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly) :
  205.                              KeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly);
  206.                     break;
  207.                 case ECCENTRIC :
  208.                     this.v = (a < 0) ?
  209.                              KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly) :
  210.                              KeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly);
  211.                     break;
  212.                 case TRUE :
  213.                     this.v = anomaly;
  214.                     break;
  215.                 default : // this should never happen
  216.                     throw new OrekitInternalError(null);
  217.             }
  218.             this.vDot = Double.NaN;
  219.         }

  220.         // check true anomaly range
  221.         if (1 + e * FastMath.cos(v) <= 0) {
  222.             final double vMax = FastMath.acos(-1 / e);
  223.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE,
  224.                                                      v, e, -vMax, vMax);
  225.         }

  226.         this.partialPV = null;

  227.     }

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

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

  267.         // compute inclination
  268.         final Vector3D momentum = pvCoordinates.getMomentum();
  269.         final double m2 = momentum.getNormSq();
  270.         i = Vector3D.angle(momentum, Vector3D.PLUS_K);

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

  273.         // preliminary computations for parameters depending on orbit shape (elliptic or hyperbolic)
  274.         final Vector3D pvP     = pvCoordinates.getPosition();
  275.         final Vector3D pvV     = pvCoordinates.getVelocity();
  276.         final Vector3D pvA     = pvCoordinates.getAcceleration();
  277.         final double   r2      = pvP.getNormSq();
  278.         final double   r       = FastMath.sqrt(r2);
  279.         final double   V2      = pvV.getNormSq();
  280.         final double   rV2OnMu = r * V2 / mu;

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

  284.         // compute true anomaly
  285.         if (a > 0) {
  286.             // elliptic or circular orbit
  287.             final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA);
  288.             final double eCE = rV2OnMu - 1;
  289.             e = FastMath.sqrt(eSE * eSE + eCE * eCE);
  290.             v = KeplerianAnomalyUtility.ellipticEccentricToTrue(e, FastMath.atan2(eSE, eCE));
  291.         } else {
  292.             // hyperbolic orbit
  293.             final double eSH = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(-muA);
  294.             final double eCH = rV2OnMu - 1;
  295.             e = FastMath.sqrt(1 - m2 / muA);
  296.             v = KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, FastMath.log((eCH + eSH) / (eCH - eSH)) / 2);
  297.         }

  298.         // Checking eccentricity range
  299.         checkParameterRangeInclusive(ECCENTRICITY, e, 0.0, Double.POSITIVE_INFINITY);

  300.         // compute perigee argument
  301.         final Vector3D node = new Vector3D(raan, 0.0);
  302.         final double px = Vector3D.dotProduct(pvP, node);
  303.         final double py = Vector3D.dotProduct(pvP, Vector3D.crossProduct(momentum, node)) / FastMath.sqrt(m2);
  304.         pa = FastMath.atan2(py, px) - v;

  305.         partialPV = pvCoordinates;

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

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

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

  320.             // in order to compute true anomaly derivative, we must compute
  321.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  322.             final double MDot = getKeplerianMeanMotion() +
  323.                                 jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ;
  324.             final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  325.             final UnivariateDerivative1 MUD = new UnivariateDerivative1(getMeanAnomaly(), MDot);
  326.             final UnivariateDerivative1 vUD = (a < 0) ?
  327.                                             FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) :
  328.                                             FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD);
  329.             vDot = vUD.getDerivative(1);

  330.         } else {
  331.             // acceleration is either almost zero or NaN,
  332.             // we assume acceleration was not known
  333.             // we don't set up derivatives
  334.             aDot    = Double.NaN;
  335.             eDot    = Double.NaN;
  336.             iDot    = Double.NaN;
  337.             paDot   = Double.NaN;
  338.             raanDot = Double.NaN;
  339.             vDot    = Double.NaN;
  340.         }

  341.     }

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

  362.     /** Constructor from any kind of orbital parameters.
  363.      * @param op orbital parameters to copy
  364.      */
  365.     public KeplerianOrbit(final Orbit op) {
  366.         this(op.getPVCoordinates(), op.getFrame(), op.getMu(), op.hasDerivatives());
  367.     }

  368.     /** {@inheritDoc} */
  369.     public OrbitType getType() {
  370.         return OrbitType.KEPLERIAN;
  371.     }

  372.     /** {@inheritDoc} */
  373.     public double getA() {
  374.         return a;
  375.     }

  376.     /** {@inheritDoc} */
  377.     public double getADot() {
  378.         return aDot;
  379.     }

  380.     /** {@inheritDoc} */
  381.     public double getE() {
  382.         return e;
  383.     }

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

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

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

  396.     /** Get the perigee argument.
  397.      * @return perigee argument (rad)
  398.      */
  399.     public double getPerigeeArgument() {
  400.         return pa;
  401.     }

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

  412.     /** Get the right ascension of the ascending node.
  413.      * @return right ascension of the ascending node (rad)
  414.      */
  415.     public double getRightAscensionOfAscendingNode() {
  416.         return raan;
  417.     }

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

  428.     /** Get the true anomaly.
  429.      * @return true anomaly (rad)
  430.      */
  431.     public double getTrueAnomaly() {
  432.         return v;
  433.     }

  434.     /** Get the true anomaly derivative.
  435.      * @return true anomaly derivative (rad/s)
  436.      */
  437.     public double getTrueAnomalyDot() {
  438.         return vDot;
  439.     }

  440.     /** Get the eccentric anomaly.
  441.      * @return eccentric anomaly (rad)
  442.      */
  443.     public double getEccentricAnomaly() {
  444.         return (a < 0) ?
  445.                KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, v) :
  446.                KeplerianAnomalyUtility.ellipticTrueToEccentric(e, v);
  447.     }

  448.     /** Get the eccentric anomaly derivative.
  449.      * @return eccentric anomaly derivative (rad/s)
  450.      * @since 9.0
  451.      */
  452.     public double getEccentricAnomalyDot() {
  453.         final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  454.         final UnivariateDerivative1 vUD = new UnivariateDerivative1(v, vDot);
  455.         final UnivariateDerivative1 EUD = (a < 0) ?
  456.                                         FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) :
  457.                                         FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD);
  458.         return EUD.getDerivative(1);
  459.     }

  460.     /** Get the mean anomaly.
  461.      * @return mean anomaly (rad)
  462.      */
  463.     public double getMeanAnomaly() {
  464.         return (a < 0) ?
  465.                KeplerianAnomalyUtility.hyperbolicTrueToMean(e, v) :
  466.                KeplerianAnomalyUtility.ellipticTrueToMean(e, v);
  467.     }

  468.     /** Get the mean anomaly derivative.
  469.      * @return mean anomaly derivative (rad/s)
  470.      * @since 9.0
  471.      */
  472.     public double getMeanAnomalyDot() {
  473.         final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  474.         final UnivariateDerivative1 vUD = new UnivariateDerivative1(v, vDot);
  475.         final UnivariateDerivative1 MUD = (a < 0) ?
  476.                                         FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, vUD) :
  477.                                         FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, vUD);
  478.         return MUD.getDerivative(1);
  479.     }

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

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

  499.     /** Computes the true anomaly from the elliptic eccentric anomaly.
  500.      * @param E eccentric anomaly (rad)
  501.      * @param e eccentricity
  502.      * @return v the true anomaly
  503.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticEccentricToTrue(double, double)}.
  504.      */
  505.     public static double ellipticEccentricToTrue(final double E, final double e) {
  506.         return KeplerianAnomalyUtility.ellipticEccentricToTrue(e, E);
  507.     }

  508.     /** Computes the elliptic eccentric anomaly from the true anomaly.
  509.      * @param v true anomaly (rad)
  510.      * @param e eccentricity
  511.      * @return E the elliptic eccentric anomaly
  512.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticTrueToEccentric(double, double)}.
  513.      */
  514.     public static double trueToEllipticEccentric(final double v, final double e) {
  515.         return KeplerianAnomalyUtility.ellipticTrueToEccentric(e, v);
  516.     }

  517.     /** Computes the true anomaly from the hyperbolic eccentric anomaly.
  518.      * @param H hyperbolic eccentric anomaly (rad)
  519.      * @param e eccentricity
  520.      * @return v the true anomaly
  521.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#hyperbolicEccentricToTrue(double, double)}.
  522.      */
  523.     public static double hyperbolicEccentricToTrue(final double H, final double e) {
  524.         return KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, H);
  525.     }

  526.     /** Computes the hyperbolic eccentric anomaly from the true anomaly.
  527.      * @param v true anomaly (rad)
  528.      * @param e eccentricity
  529.      * @return H the hyperbolic eccentric anomaly
  530.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#hyperbolicTrueToEccentric(double, double)}.
  531.      */
  532.     public static double trueToHyperbolicEccentric(final double v, final double e) {
  533.         return KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, v);
  534.     }

  535.     /** Computes the elliptic eccentric anomaly from the mean anomaly.
  536.      * @param M mean anomaly (rad)
  537.      * @param e eccentricity
  538.      * @return E the eccentric anomaly
  539.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticMeanToEccentric(double, double)}.
  540.      */
  541.     public static double meanToEllipticEccentric(final double M, final double e) {
  542.         return KeplerianAnomalyUtility.ellipticEccentricToMean(e, M);
  543.     }

  544.     /** Computes the mean anomaly from the elliptic eccentric anomaly.
  545.      * @param E eccentric anomaly (rad)
  546.      * @param e eccentricity
  547.      * @return M the mean anomaly
  548.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#ellipticEccentricToMean(double, double)}.
  549.      */
  550.     public static double ellipticEccentricToMean(final double E, final double e) {
  551.         return KeplerianAnomalyUtility.ellipticEccentricToMean(e, E);
  552.     }

  553.     /** Computes the mean anomaly from the hyperbolic eccentric anomaly.
  554.      * @param H hyperbolic eccentric anomaly (rad)
  555.      * @param e eccentricity
  556.      * @return M the mean anomaly
  557.      * @deprecated As of 11.3, replaced by {@link KeplerianAnomalyUtility#hyperbolicEccentricToMean(double, double)}.
  558.      */
  559.     public static double hyperbolicEccentricToMean(final double H, final double e) {
  560.         return KeplerianAnomalyUtility.hyperbolicEccentricToMean(e, H);
  561.     }

  562.     /** {@inheritDoc} */
  563.     public double getEquinoctialEx() {
  564.         return e * FastMath.cos(pa + raan);
  565.     }

  566.     /** {@inheritDoc} */
  567.     public double getEquinoctialExDot() {
  568.         final double paPraan = pa + raan;
  569.         final SinCos sc      = FastMath.sinCos(paPraan);
  570.         return eDot * sc.cos() - e * sc.sin() * (paDot + raanDot);
  571.     }

  572.     /** {@inheritDoc} */
  573.     public double getEquinoctialEy() {
  574.         return e * FastMath.sin(pa + raan);
  575.     }

  576.     /** {@inheritDoc} */
  577.     public double getEquinoctialEyDot() {
  578.         final double paPraan = pa + raan;
  579.         final SinCos sc      = FastMath.sinCos(paPraan);
  580.         return eDot * sc.sin() + e * sc.cos() * (paDot + raanDot);
  581.     }

  582.     /** {@inheritDoc} */
  583.     public double getHx() {
  584.         // Check for equatorial retrograde orbit
  585.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  586.             return Double.NaN;
  587.         }
  588.         return FastMath.cos(raan) * FastMath.tan(0.5 * i);
  589.     }

  590.     /** {@inheritDoc} */
  591.     public double getHxDot() {
  592.         // Check for equatorial retrograde orbit
  593.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  594.             return Double.NaN;
  595.         }
  596.         final SinCos sc      = FastMath.sinCos(raan);
  597.         final double tan     = FastMath.tan(0.5 * i);
  598.         return 0.5 * (1 + tan * tan) * sc.cos() * iDot - tan * sc.sin() * raanDot;
  599.     }

  600.     /** {@inheritDoc} */
  601.     public double getHy() {
  602.         // Check for equatorial retrograde orbit
  603.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  604.             return Double.NaN;
  605.         }
  606.         return  FastMath.sin(raan) * FastMath.tan(0.5 * i);
  607.     }

  608.     /** {@inheritDoc} */
  609.     public double getHyDot() {
  610.         // Check for equatorial retrograde orbit
  611.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  612.             return Double.NaN;
  613.         }
  614.         final SinCos sc      = FastMath.sinCos(raan);
  615.         final double tan     = FastMath.tan(0.5 * i);
  616.         return 0.5 * (1 + tan * tan) * sc.sin() * iDot + tan * sc.cos() * raanDot;
  617.     }

  618.     /** {@inheritDoc} */
  619.     public double getLv() {
  620.         return pa + raan + v;
  621.     }

  622.     /** {@inheritDoc} */
  623.     public double getLvDot() {
  624.         return paDot + raanDot + vDot;
  625.     }

  626.     /** {@inheritDoc} */
  627.     public double getLE() {
  628.         return pa + raan + getEccentricAnomaly();
  629.     }

  630.     /** {@inheritDoc} */
  631.     public double getLEDot() {
  632.         return paDot + raanDot + getEccentricAnomalyDot();
  633.     }

  634.     /** {@inheritDoc} */
  635.     public double getLM() {
  636.         return pa + raan + getMeanAnomaly();
  637.     }

  638.     /** {@inheritDoc} */
  639.     public double getLMDot() {
  640.         return paDot + raanDot + getMeanAnomalyDot();
  641.     }

  642.     /** Compute position and velocity but not acceleration.
  643.      */
  644.     private void computePVWithoutA() {

  645.         if (partialPV != null) {
  646.             // already computed
  647.             return;
  648.         }

  649.         // preliminary variables
  650.         final SinCos scRaan  = FastMath.sinCos(raan);
  651.         final SinCos scPa    = FastMath.sinCos(pa);
  652.         final SinCos scI     = FastMath.sinCos(i);
  653.         final double cosRaan = scRaan.cos();
  654.         final double sinRaan = scRaan.sin();
  655.         final double cosPa   = scPa.cos();
  656.         final double sinPa   = scPa.sin();
  657.         final double cosI    = scI.cos();
  658.         final double sinI    = scI.sin();

  659.         final double crcp    = cosRaan * cosPa;
  660.         final double crsp    = cosRaan * sinPa;
  661.         final double srcp    = sinRaan * cosPa;
  662.         final double srsp    = sinRaan * sinPa;

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

  666.         if (a > 0) {

  667.             // elliptical case

  668.             // elliptic eccentric anomaly
  669.             final double uME2   = (1 - e) * (1 + e);
  670.             final double s1Me2  = FastMath.sqrt(uME2);
  671.             final SinCos scE    = FastMath.sinCos(getEccentricAnomaly());
  672.             final double cosE   = scE.cos();
  673.             final double sinE   = scE.sin();

  674.             // coordinates of position and velocity in the orbital plane
  675.             final double x      = a * (cosE - e);
  676.             final double y      = a * sinE * s1Me2;
  677.             final double factor = FastMath.sqrt(getMu() / a) / (1 - e * cosE);
  678.             final double xDot   = -sinE * factor;
  679.             final double yDot   =  cosE * s1Me2 * factor;

  680.             final Vector3D position = new Vector3D(x, p, y, q);
  681.             final Vector3D velocity = new Vector3D(xDot, p, yDot, q);
  682.             partialPV = new PVCoordinates(position, velocity);

  683.         } else {

  684.             // hyperbolic case

  685.             // compute position and velocity factors
  686.             final SinCos scV       = FastMath.sinCos(v);
  687.             final double sinV      = scV.sin();
  688.             final double cosV      = scV.cos();
  689.             final double f         = a * (1 - e * e);
  690.             final double posFactor = f / (1 + e * cosV);
  691.             final double velFactor = FastMath.sqrt(getMu() / f);

  692.             final double   x            =  posFactor * cosV;
  693.             final double   y            =  posFactor * sinV;
  694.             final double   xDot         = -velFactor * sinV;
  695.             final double   yDot         =  velFactor * (e + cosV);

  696.             final Vector3D position     = new Vector3D(x, p, y, q);
  697.             final Vector3D velocity     = new Vector3D(xDot, p, yDot, q);
  698.             partialPV = new PVCoordinates(position, velocity);

  699.         }

  700.     }

  701.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  702.      * <p>
  703.      * This method should be called only when {@link #hasDerivatives()} returns true.
  704.      * </p>
  705.      * @return non-Keplerian part of the acceleration
  706.      */
  707.     private Vector3D nonKeplerianAcceleration() {

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

  710.         final double nonKeplerianMeanMotion = getMeanAnomalyDot() - getKeplerianMeanMotion();
  711.         final double nonKeplerianAx = dCdP[3][0] * aDot    + dCdP[3][1] * eDot    + dCdP[3][2] * iDot    +
  712.                                       dCdP[3][3] * paDot   + dCdP[3][4] * raanDot + dCdP[3][5] * nonKeplerianMeanMotion;
  713.         final double nonKeplerianAy = dCdP[4][0] * aDot    + dCdP[4][1] * eDot    + dCdP[4][2] * iDot    +
  714.                                       dCdP[4][3] * paDot   + dCdP[4][4] * raanDot + dCdP[4][5] * nonKeplerianMeanMotion;
  715.         final double nonKeplerianAz = dCdP[5][0] * aDot    + dCdP[5][1] * eDot    + dCdP[5][2] * iDot    +
  716.                                       dCdP[5][3] * paDot   + dCdP[5][4] * raanDot + dCdP[5][5] * nonKeplerianMeanMotion;

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

  718.     }

  719.     /** {@inheritDoc} */
  720.     protected TimeStampedPVCoordinates initPVCoordinates() {

  721.         // position and velocity
  722.         computePVWithoutA();

  723.         // acceleration
  724.         final double r2 = partialPV.getPosition().getNormSq();
  725.         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
  726.         final Vector3D acceleration = hasDerivatives() ?
  727.                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
  728.                                       keplerianAcceleration;

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

  730.     }

  731.     /** {@inheritDoc} */
  732.     public KeplerianOrbit shiftedBy(final double dt) {

  733.         // use Keplerian-only motion
  734.         final KeplerianOrbit keplerianShifted = new KeplerianOrbit(a, e, i, pa, raan,
  735.                                                                    getMeanAnomaly() + getKeplerianMeanMotion() * dt,
  736.                                                                    PositionAngle.MEAN, getFrame(),
  737.                                                                    getDate().shiftedBy(dt), getMu());

  738.         if (hasDerivatives()) {

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

  741.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  742.             keplerianShifted.computePVWithoutA();
  743.             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
  744.                                                    0.5 * dt * dt, nonKeplerianAcceleration);
  745.             final double   fixedR2 = fixedP.getNormSq();
  746.             final double   fixedR  = FastMath.sqrt(fixedR2);
  747.             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
  748.                                                   dt, nonKeplerianAcceleration);
  749.             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
  750.                                                   1, nonKeplerianAcceleration);

  751.             // build a new orbit, taking non-Keplerian acceleration into account
  752.             return new KeplerianOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
  753.                                                                    fixedP, fixedV, fixedA),
  754.                                       keplerianShifted.getFrame(), keplerianShifted.getMu());

  755.         } else {
  756.             // Keplerian-only motion is all we can do
  757.             return keplerianShifted;
  758.         }

  759.     }

  760.     /** {@inheritDoc}
  761.      * <p>
  762.      * The interpolated instance is created by polynomial Hermite interpolation
  763.      * on Keplerian elements, without derivatives (which means the interpolation
  764.      * falls back to Lagrange interpolation only).
  765.      * </p>
  766.      * <p>
  767.      * As this implementation of interpolation is polynomial, it should be used only
  768.      * with small samples (about 10-20 points) in order to avoid <a
  769.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  770.      * and numerical problems (including NaN appearing).
  771.      * </p>
  772.      * <p>
  773.      * If orbit interpolation on large samples is needed, using the {@link
  774.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  775.      * low-level interpolation. The Ephemeris class automatically handles selection of
  776.      * a neighboring sub-sample with a predefined number of point from a large global sample
  777.      * in a thread-safe way.
  778.      * </p>
  779.      */
  780.     public KeplerianOrbit interpolate(final AbsoluteDate date, final Stream<Orbit> sample) {

  781.         // first pass to check if derivatives are available throughout the sample
  782.         final List<Orbit> list = sample.collect(Collectors.toList());
  783.         boolean useDerivatives = true;
  784.         for (final Orbit orbit : list) {
  785.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  786.         }

  787.         // set up an interpolator
  788.         final HermiteInterpolator interpolator = new HermiteInterpolator();

  789.         // second pass to feed interpolator
  790.         AbsoluteDate previousDate = null;
  791.         double       previousPA   = Double.NaN;
  792.         double       previousRAAN = Double.NaN;
  793.         double       previousM    = Double.NaN;
  794.         for (final Orbit orbit : list) {
  795.             final KeplerianOrbit kep = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit);
  796.             final double continuousPA;
  797.             final double continuousRAAN;
  798.             final double continuousM;
  799.             if (previousDate == null) {
  800.                 continuousPA   = kep.getPerigeeArgument();
  801.                 continuousRAAN = kep.getRightAscensionOfAscendingNode();
  802.                 continuousM    = kep.getMeanAnomaly();
  803.             } else {
  804.                 final double dt      = kep.getDate().durationFrom(previousDate);
  805.                 final double keplerM = previousM + kep.getKeplerianMeanMotion() * dt;
  806.                 continuousPA   = MathUtils.normalizeAngle(kep.getPerigeeArgument(), previousPA);
  807.                 continuousRAAN = MathUtils.normalizeAngle(kep.getRightAscensionOfAscendingNode(), previousRAAN);
  808.                 continuousM    = MathUtils.normalizeAngle(kep.getMeanAnomaly(), keplerM);
  809.             }
  810.             previousDate = kep.getDate();
  811.             previousPA   = continuousPA;
  812.             previousRAAN = continuousRAAN;
  813.             previousM    = continuousM;
  814.             if (useDerivatives) {
  815.                 interpolator.addSamplePoint(kep.getDate().durationFrom(date),
  816.                                             new double[] {
  817.                                                 kep.getA(),
  818.                                                 kep.getE(),
  819.                                                 kep.getI(),
  820.                                                 continuousPA,
  821.                                                 continuousRAAN,
  822.                                                 continuousM
  823.                                             }, new double[] {
  824.                                                 kep.getADot(),
  825.                                                 kep.getEDot(),
  826.                                                 kep.getIDot(),
  827.                                                 kep.getPerigeeArgumentDot(),
  828.                                                 kep.getRightAscensionOfAscendingNodeDot(),
  829.                                                 kep.getMeanAnomalyDot()
  830.                                             });
  831.             } else {
  832.                 interpolator.addSamplePoint(kep.getDate().durationFrom(date),
  833.                                             new double[] {
  834.                                                 kep.getA(),
  835.                                                 kep.getE(),
  836.                                                 kep.getI(),
  837.                                                 continuousPA,
  838.                                                 continuousRAAN,
  839.                                                 continuousM
  840.                                             });
  841.             }
  842.         }

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

  845.         // build a new interpolated instance
  846.         return new KeplerianOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  847.                                   interpolated[0][3], interpolated[0][4], interpolated[0][5],
  848.                                   interpolated[1][0], interpolated[1][1], interpolated[1][2],
  849.                                   interpolated[1][3], interpolated[1][4], interpolated[1][5],
  850.                                   PositionAngle.MEAN, getFrame(), date, getMu());

  851.     }

  852.     /** {@inheritDoc} */
  853.     protected double[][] computeJacobianMeanWrtCartesian() {
  854.         if (a > 0) {
  855.             return computeJacobianMeanWrtCartesianElliptical();
  856.         } else {
  857.             return computeJacobianMeanWrtCartesianHyperbolic();
  858.         }
  859.     }

  860.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  861.      * <p>
  862.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  863.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  864.      * yDot for j=4, zDot for j=5).
  865.      * </p>
  866.      * @return 6x6 Jacobian matrix
  867.      */
  868.     private double[][] computeJacobianMeanWrtCartesianElliptical() {

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

  870.         // compute various intermediate parameters
  871.         computePVWithoutA();
  872.         final Vector3D position = partialPV.getPosition();
  873.         final Vector3D velocity = partialPV.getVelocity();
  874.         final Vector3D momentum = partialPV.getMomentum();
  875.         final double v2         = velocity.getNormSq();
  876.         final double r2         = position.getNormSq();
  877.         final double r          = FastMath.sqrt(r2);
  878.         final double r3         = r * r2;

  879.         final double px         = position.getX();
  880.         final double py         = position.getY();
  881.         final double pz         = position.getZ();
  882.         final double vx         = velocity.getX();
  883.         final double vy         = velocity.getY();
  884.         final double vz         = velocity.getZ();
  885.         final double mx         = momentum.getX();
  886.         final double my         = momentum.getY();
  887.         final double mz         = momentum.getZ();

  888.         final double mu         = getMu();
  889.         final double sqrtMuA    = FastMath.sqrt(a * mu);
  890.         final double sqrtAoMu   = FastMath.sqrt(a / mu);
  891.         final double a2         = a * a;
  892.         final double twoA       = 2 * a;
  893.         final double rOnA       = r / a;

  894.         final double oMe2       = 1 - e * e;
  895.         final double epsilon    = FastMath.sqrt(oMe2);
  896.         final double sqrtRec    = 1 / epsilon;

  897.         final SinCos scI        = FastMath.sinCos(i);
  898.         final SinCos scPA       = FastMath.sinCos(pa);
  899.         final double cosI       = scI.cos();
  900.         final double sinI       = scI.sin();
  901.         final double cosPA      = scPA.cos();
  902.         final double sinPA      = scPA.sin();

  903.         final double pv         = Vector3D.dotProduct(position, velocity);
  904.         final double cosE       = (a - r) / (a * e);
  905.         final double sinE       = pv / (e * sqrtMuA);

  906.         // da
  907.         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
  908.         final Vector3D vectorARDot = velocity.scalarMultiply(2 * a2 / mu);
  909.         fillHalfRow(1, vectorAR,    jacobian[0], 0);
  910.         fillHalfRow(1, vectorARDot, jacobian[0], 3);

  911.         // de
  912.         final double factorER3 = pv / twoA;
  913.         final Vector3D vectorER   = new Vector3D(cosE * v2 / (r * mu), position,
  914.                                                  sinE / sqrtMuA, velocity,
  915.                                                  -factorER3 * sinE / sqrtMuA, vectorAR);
  916.         final Vector3D vectorERDot = new Vector3D(sinE / sqrtMuA, position,
  917.                                                   cosE * 2 * r / mu, velocity,
  918.                                                   -factorER3 * sinE / sqrtMuA, vectorARDot);
  919.         fillHalfRow(1, vectorER,    jacobian[1], 0);
  920.         fillHalfRow(1, vectorERDot, jacobian[1], 3);

  921.         // dE / dr (Eccentric anomaly)
  922.         final double coefE = cosE / (e * sqrtMuA);
  923.         final Vector3D  vectorEAnR =
  924.             new Vector3D(-sinE * v2 / (e * r * mu), position, coefE, velocity,
  925.                          -factorER3 * coefE, vectorAR);

  926.         // dE / drDot
  927.         final Vector3D  vectorEAnRDot =
  928.             new Vector3D(-sinE * 2 * r / (e * mu), velocity, coefE, position,
  929.                          -factorER3 * coefE, vectorARDot);

  930.         // precomputing some more factors
  931.         final double s1 = -sinE * pz / r - cosE * vz * sqrtAoMu;
  932.         final double s2 = -cosE * pz / r3;
  933.         final double s3 = -sinE * vz / (2 * sqrtMuA);
  934.         final double t1 = sqrtRec * (cosE * pz / r - sinE * vz * sqrtAoMu);
  935.         final double t2 = sqrtRec * (-sinE * pz / r3);
  936.         final double t3 = sqrtRec * (cosE - e) * vz / (2 * sqrtMuA);
  937.         final double t4 = sqrtRec * (e * sinI * cosPA * sqrtRec - vz * sqrtAoMu);
  938.         final Vector3D s = new Vector3D(cosE / r, Vector3D.PLUS_K,
  939.                                         s1,       vectorEAnR,
  940.                                         s2,       position,
  941.                                         s3,       vectorAR);
  942.         final Vector3D sDot = new Vector3D(-sinE * sqrtAoMu, Vector3D.PLUS_K,
  943.                                            s1,               vectorEAnRDot,
  944.                                            s3,               vectorARDot);
  945.         final Vector3D t =
  946.             new Vector3D(sqrtRec * sinE / r, Vector3D.PLUS_K).add(new Vector3D(t1, vectorEAnR,
  947.                                                                                t2, position,
  948.                                                                                t3, vectorAR,
  949.                                                                                t4, vectorER));
  950.         final Vector3D tDot = new Vector3D(sqrtRec * (cosE - e) * sqrtAoMu, Vector3D.PLUS_K,
  951.                                            t1,                              vectorEAnRDot,
  952.                                            t3,                              vectorARDot,
  953.                                            t4,                              vectorERDot);

  954.         // di
  955.         final double factorI1 = -sinI * sqrtRec / sqrtMuA;
  956.         final double i1 =  factorI1;
  957.         final double i2 = -factorI1 * mz / twoA;
  958.         final double i3 =  factorI1 * mz * e / oMe2;
  959.         final double i4 = cosI * sinPA;
  960.         final double i5 = cosI * cosPA;
  961.         fillHalfRow(i1, new Vector3D(vy, -vx, 0), i2, vectorAR, i3, vectorER, i4, s, i5, t,
  962.                     jacobian[2], 0);
  963.         fillHalfRow(i1, new Vector3D(-py, px, 0), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
  964.                     jacobian[2], 3);

  965.         // dpa
  966.         fillHalfRow(cosPA / sinI, s,    -sinPA / sinI, t,    jacobian[3], 0);
  967.         fillHalfRow(cosPA / sinI, sDot, -sinPA / sinI, tDot, jacobian[3], 3);

  968.         // dRaan
  969.         final double factorRaanR = 1 / (mu * a * oMe2 * sinI * sinI);
  970.         fillHalfRow(-factorRaanR * my, new Vector3D(  0, vz, -vy),
  971.                      factorRaanR * mx, new Vector3D(-vz,  0,  vx),
  972.                      jacobian[4], 0);
  973.         fillHalfRow(-factorRaanR * my, new Vector3D( 0, -pz,  py),
  974.                      factorRaanR * mx, new Vector3D(pz,   0, -px),
  975.                      jacobian[4], 3);

  976.         // dM
  977.         fillHalfRow(rOnA, vectorEAnR,    -sinE, vectorER,    jacobian[5], 0);
  978.         fillHalfRow(rOnA, vectorEAnRDot, -sinE, vectorERDot, jacobian[5], 3);

  979.         return jacobian;

  980.     }

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

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

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

  999.         final double x          = position.getX();
  1000.         final double y          = position.getY();
  1001.         final double z          = position.getZ();
  1002.         final double vx         = velocity.getX();
  1003.         final double vy         = velocity.getY();
  1004.         final double vz         = velocity.getZ();
  1005.         final double mx         = momentum.getX();
  1006.         final double my         = momentum.getY();
  1007.         final double mz         = momentum.getZ();

  1008.         final double mu         = getMu();
  1009.         final double absA       = -a;
  1010.         final double sqrtMuA    = FastMath.sqrt(absA * mu);
  1011.         final double a2         = a * a;
  1012.         final double rOa        = r / absA;

  1013.         final SinCos scI        = FastMath.sinCos(i);
  1014.         final double cosI       = scI.cos();
  1015.         final double sinI       = scI.sin();

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

  1017.         // da
  1018.         final Vector3D vectorAR = new Vector3D(-2 * a2 / r3, position);
  1019.         final Vector3D vectorARDot = velocity.scalarMultiply(-2 * a2 / mu);
  1020.         fillHalfRow(-1, vectorAR,    jacobian[0], 0);
  1021.         fillHalfRow(-1, vectorARDot, jacobian[0], 3);

  1022.         // differentials of the momentum
  1023.         final double m      = momentum.getNorm();
  1024.         final double oOm    = 1 / m;
  1025.         final Vector3D dcXP = new Vector3D(  0,  vz, -vy);
  1026.         final Vector3D dcYP = new Vector3D(-vz,   0,  vx);
  1027.         final Vector3D dcZP = new Vector3D( vy, -vx,   0);
  1028.         final Vector3D dcXV = new Vector3D(  0,  -z,   y);
  1029.         final Vector3D dcYV = new Vector3D(  z,   0,  -x);
  1030.         final Vector3D dcZV = new Vector3D( -y,   x,   0);
  1031.         final Vector3D dCP  = new Vector3D(mx * oOm, dcXP, my * oOm, dcYP, mz * oOm, dcZP);
  1032.         final Vector3D dCV  = new Vector3D(mx * oOm, dcXV, my * oOm, dcYV, mz * oOm, dcZV);

  1033.         // dp
  1034.         final double mOMu   = m / mu;
  1035.         final Vector3D dpP  = new Vector3D(2 * mOMu, dCP);
  1036.         final Vector3D dpV  = new Vector3D(2 * mOMu, dCV);

  1037.         // de
  1038.         final double p      = m * mOMu;
  1039.         final double moO2ae = 1 / (2 * absA * e);
  1040.         final double m2OaMu = -p / absA;
  1041.         fillHalfRow(moO2ae, dpP, m2OaMu * moO2ae, vectorAR,    jacobian[1], 0);
  1042.         fillHalfRow(moO2ae, dpV, m2OaMu * moO2ae, vectorARDot, jacobian[1], 3);

  1043.         // di
  1044.         final double cI1 = 1 / (m * sinI);
  1045.         final double cI2 = cosI * cI1;
  1046.         fillHalfRow(cI2, dCP, -cI1, dcZP, jacobian[2], 0);
  1047.         fillHalfRow(cI2, dCV, -cI1, dcZV, jacobian[2], 3);

  1048.         // dPA
  1049.         final double cP1     =  y * oOm;
  1050.         final double cP2     = -x * oOm;
  1051.         final double cP3     = -(mx * cP1 + my * cP2);
  1052.         final double cP4     = cP3 * oOm;
  1053.         final double cP5     = -1 / (r2 * sinI * sinI);
  1054.         final double cP6     = z  * cP5;
  1055.         final double cP7     = cP3 * cP5;
  1056.         final Vector3D dacP  = new Vector3D(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new Vector3D(-my, mx, 0));
  1057.         final Vector3D dacV  = new Vector3D(cP1, dcXV, cP2, dcYV, cP4, dCV);
  1058.         final Vector3D dpoP  = new Vector3D(cP6, dacP, cP7, Vector3D.PLUS_K);
  1059.         final Vector3D dpoV  = new Vector3D(cP6, dacV);

  1060.         final double re2     = r2 * e * e;
  1061.         final double recOre2 = (p - r) / re2;
  1062.         final double resOre2 = (pv * mOMu) / re2;
  1063.         final Vector3D dreP  = new Vector3D(mOMu, velocity, pv / mu, dCP);
  1064.         final Vector3D dreV  = new Vector3D(mOMu, position, pv / mu, dCV);
  1065.         final Vector3D davP  = new Vector3D(-resOre2, dpP, recOre2, dreP, resOre2 / r, position);
  1066.         final Vector3D davV  = new Vector3D(-resOre2, dpV, recOre2, dreV);
  1067.         fillHalfRow(1, dpoP, -1, davP, jacobian[3], 0);
  1068.         fillHalfRow(1, dpoV, -1, davV, jacobian[3], 3);

  1069.         // dRAAN
  1070.         final double cO0 = cI1 * cI1;
  1071.         final double cO1 =  mx * cO0;
  1072.         final double cO2 = -my * cO0;
  1073.         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
  1074.         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);

  1075.         // dM
  1076.         final double s2a    = pv / (2 * absA);
  1077.         final double oObux  = 1 / FastMath.sqrt(m * m + mu * absA);
  1078.         final double scasbu = pv * oObux;
  1079.         final Vector3D dauP = new Vector3D(1 / sqrtMuA, velocity, -s2a / sqrtMuA, vectorAR);
  1080.         final Vector3D dauV = new Vector3D(1 / sqrtMuA, position, -s2a / sqrtMuA, vectorARDot);
  1081.         final Vector3D dbuP = new Vector3D(oObux * mu / 2, vectorAR,    m * oObux, dCP);
  1082.         final Vector3D dbuV = new Vector3D(oObux * mu / 2, vectorARDot, m * oObux, dCV);
  1083.         final Vector3D dcuP = new Vector3D(oObux, velocity, -scasbu * oObux, dbuP);
  1084.         final Vector3D dcuV = new Vector3D(oObux, position, -scasbu * oObux, dbuV);
  1085.         fillHalfRow(1, dauP, -e / (1 + rOa), dcuP, jacobian[5], 0);
  1086.         fillHalfRow(1, dauV, -e / (1 + rOa), dcuV, jacobian[5], 3);

  1087.         return jacobian;

  1088.     }

  1089.     /** {@inheritDoc} */
  1090.     protected double[][] computeJacobianEccentricWrtCartesian() {
  1091.         if (a > 0) {
  1092.             return computeJacobianEccentricWrtCartesianElliptical();
  1093.         } else {
  1094.             return computeJacobianEccentricWrtCartesianHyperbolic();
  1095.         }
  1096.     }

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

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

  1108.         // Differentiating the Kepler equation M = E - e sin E leads to:
  1109.         // dM = (1 - e cos E) dE - sin E de
  1110.         // which is inverted and rewritten as:
  1111.         // dE = a/r dM + sin E a/r de
  1112.         final SinCos scE              = FastMath.sinCos(getEccentricAnomaly());
  1113.         final double aOr              = 1 / (1 - e * scE.cos());

  1114.         // update anomaly row
  1115.         final double[] eRow           = jacobian[1];
  1116.         final double[] anomalyRow     = jacobian[5];
  1117.         for (int j = 0; j < anomalyRow.length; ++j) {
  1118.             anomalyRow[j] = aOr * (anomalyRow[j] + scE.sin() * eRow[j]);
  1119.         }

  1120.         return jacobian;

  1121.     }

  1122.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1123.      * <p>
  1124.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1125.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1126.      * yDot for j=4, zDot for j=5).
  1127.      * </p>
  1128.      * @return 6x6 Jacobian matrix
  1129.      */
  1130.     private double[][] computeJacobianEccentricWrtCartesianHyperbolic() {

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

  1133.         // Differentiating the Kepler equation M = e sinh H - H leads to:
  1134.         // dM = (e cosh H - 1) dH + sinh H de
  1135.         // which is inverted and rewritten as:
  1136.         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
  1137.         final double H      = getEccentricAnomaly();
  1138.         final double coshH  = FastMath.cosh(H);
  1139.         final double sinhH  = FastMath.sinh(H);
  1140.         final double absaOr = 1 / (e * coshH - 1);

  1141.         // update anomaly row
  1142.         final double[] eRow       = jacobian[1];
  1143.         final double[] anomalyRow = jacobian[5];
  1144.         for (int j = 0; j < anomalyRow.length; ++j) {
  1145.             anomalyRow[j] = absaOr * (anomalyRow[j] - sinhH * eRow[j]);
  1146.         }

  1147.         return jacobian;

  1148.     }

  1149.     /** {@inheritDoc} */
  1150.     protected double[][] computeJacobianTrueWrtCartesian() {
  1151.         if (a > 0) {
  1152.             return computeJacobianTrueWrtCartesianElliptical();
  1153.         } else {
  1154.             return computeJacobianTrueWrtCartesianHyperbolic();
  1155.         }
  1156.     }

  1157.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1158.      * <p>
  1159.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1160.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1161.      * yDot for j=4, zDot for j=5).
  1162.      * </p>
  1163.      * @return 6x6 Jacobian matrix
  1164.      */
  1165.     private double[][] computeJacobianTrueWrtCartesianElliptical() {

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

  1168.         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
  1169.         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
  1170.         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
  1171.         // which is inverted and rewritten as:
  1172.         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
  1173.         final double e2           = e * e;
  1174.         final double oMe2         = 1 - e2;
  1175.         final double epsilon      = FastMath.sqrt(oMe2);
  1176.         final SinCos scE          = FastMath.sinCos(getEccentricAnomaly());
  1177.         final double aOr          = 1 / (1 - e * scE.cos());
  1178.         final double aFactor      = epsilon * aOr;
  1179.         final double eFactor      = scE.sin() * aOr / epsilon;

  1180.         // update anomaly row
  1181.         final double[] eRow       = jacobian[1];
  1182.         final double[] anomalyRow = jacobian[5];
  1183.         for (int j = 0; j < anomalyRow.length; ++j) {
  1184.             anomalyRow[j] = aFactor * anomalyRow[j] + eFactor * eRow[j];
  1185.         }

  1186.         return jacobian;

  1187.     }

  1188.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1189.      * <p>
  1190.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1191.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1192.      * yDot for j=4, zDot for j=5).
  1193.      * </p>
  1194.      * @return 6x6 Jacobian matrix
  1195.      */
  1196.     private double[][] computeJacobianTrueWrtCartesianHyperbolic() {

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

  1199.         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
  1200.         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
  1201.         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
  1202.         // which is inverted and rewritten as:
  1203.         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
  1204.         final double e2       = e * e;
  1205.         final double e2Mo     = e2 - 1;
  1206.         final double epsilon  = FastMath.sqrt(e2Mo);
  1207.         final double H        = getEccentricAnomaly();
  1208.         final double coshH    = FastMath.cosh(H);
  1209.         final double sinhH    = FastMath.sinh(H);
  1210.         final double aOr      = 1 / (e * coshH - 1);
  1211.         final double aFactor  = epsilon * aOr;
  1212.         final double eFactor  = sinhH * aOr / epsilon;

  1213.         // update anomaly row
  1214.         final double[] eRow           = jacobian[1];
  1215.         final double[] anomalyRow     = jacobian[5];
  1216.         for (int j = 0; j < anomalyRow.length; ++j) {
  1217.             anomalyRow[j] = aFactor * anomalyRow[j] - eFactor * eRow[j];
  1218.         }

  1219.         return jacobian;

  1220.     }

  1221.     /** {@inheritDoc} */
  1222.     public void addKeplerContribution(final PositionAngle type, final double gm,
  1223.                                       final double[] pDot) {
  1224.         final double oMe2;
  1225.         final double ksi;
  1226.         final double absA = FastMath.abs(a);
  1227.         final double n    = FastMath.sqrt(gm / absA) / absA;
  1228.         switch (type) {
  1229.             case MEAN :
  1230.                 pDot[5] += n;
  1231.                 break;
  1232.             case ECCENTRIC :
  1233.                 oMe2 = FastMath.abs(1 - e * e);
  1234.                 ksi  = 1 + e * FastMath.cos(v);
  1235.                 pDot[5] += n * ksi / oMe2;
  1236.                 break;
  1237.             case TRUE :
  1238.                 oMe2 = FastMath.abs(1 - e * e);
  1239.                 ksi  = 1 + e * FastMath.cos(v);
  1240.                 pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
  1241.                 break;
  1242.             default :
  1243.                 throw new OrekitInternalError(null);
  1244.         }
  1245.     }

  1246.     /**  Returns a string representation of this Keplerian parameters object.
  1247.      * @return a string representation of this object
  1248.      */
  1249.     public String toString() {
  1250.         return new StringBuilder().append("Keplerian parameters: ").append('{').
  1251.                                   append("a: ").append(a).
  1252.                                   append("; e: ").append(e).
  1253.                                   append("; i: ").append(FastMath.toDegrees(i)).
  1254.                                   append("; pa: ").append(FastMath.toDegrees(pa)).
  1255.                                   append("; raan: ").append(FastMath.toDegrees(raan)).
  1256.                                   append("; v: ").append(FastMath.toDegrees(v)).
  1257.                                   append(";}").toString();
  1258.     }

  1259.     /** Check if the given parameter is within an acceptable range.
  1260.      * The bounds are inclusive: an exception is raised when either of those conditions are met:
  1261.      * <ul>
  1262.      *     <li>The parameter is strictly greater than upperBound</li>
  1263.      *     <li>The parameter is strictly lower than lowerBound</li>
  1264.      * </ul>
  1265.      * <p>
  1266.      * In either of these cases, an OrekitException is raised.
  1267.      * </p>
  1268.      * @param parameterName name of the parameter
  1269.      * @param parameter value of the parameter
  1270.      * @param lowerBound lower bound of the acceptable range (inclusive)
  1271.      * @param upperBound upper bound of the acceptable range (inclusive)
  1272.      */
  1273.     private void checkParameterRangeInclusive(final String parameterName, final double parameter,
  1274.                                               final double lowerBound, final double upperBound) {
  1275.         if (parameter < lowerBound || parameter > upperBound) {
  1276.             throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName,
  1277.                                       parameter, lowerBound, upperBound);
  1278.         }
  1279.     }

  1280.     /** Replace the instance with a data transfer object for serialization.
  1281.      * @return data transfer object that will be serialized
  1282.      */
  1283.     @DefaultDataContext
  1284.     private Object writeReplace() {
  1285.         return new DTO(this);
  1286.     }

  1287.     /** Internal class used only for serialization. */
  1288.     @DefaultDataContext
  1289.     private static class DTO implements Serializable {

  1290.         /** Serializable UID. */
  1291.         private static final long serialVersionUID = 20170414L;

  1292.         /** Double values. */
  1293.         private double[] d;

  1294.         /** Frame in which are defined the orbital parameters. */
  1295.         private final Frame frame;

  1296.         /** Simple constructor.
  1297.          * @param orbit instance to serialize
  1298.          */
  1299.         private DTO(final KeplerianOrbit orbit) {

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

  1301.             // decompose date
  1302.             final AbsoluteDate j2000Epoch =
  1303.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  1304.             final double epoch  = FastMath.floor(pv.getDate().durationFrom(j2000Epoch));
  1305.             final double offset = pv.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));

  1306.             if (orbit.hasDerivatives()) {
  1307.                 // we have derivatives
  1308.                 this.d = new double[] {
  1309.                     epoch, offset, orbit.getMu(),
  1310.                     orbit.a, orbit.e, orbit.i,
  1311.                     orbit.pa, orbit.raan, orbit.v,
  1312.                     orbit.aDot, orbit.eDot, orbit.iDot,
  1313.                     orbit.paDot, orbit.raanDot, orbit.vDot
  1314.                 };
  1315.             } else {
  1316.                 // we don't have derivatives
  1317.                 this.d = new double[] {
  1318.                     epoch, offset, orbit.getMu(),
  1319.                     orbit.a, orbit.e, orbit.i,
  1320.                     orbit.pa, orbit.raan, orbit.v
  1321.                 };
  1322.             }

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

  1324.         }

  1325.         /** Replace the deserialized data transfer object with a {@link KeplerianOrbit}.
  1326.          * @return replacement {@link KeplerianOrbit}
  1327.          */
  1328.         private Object readResolve() {
  1329.             final AbsoluteDate j2000Epoch =
  1330.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  1331.             if (d.length >= 15) {
  1332.                 // we have derivatives
  1333.                 return new KeplerianOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8],
  1334.                                           d[ 9], d[10], d[11], d[12], d[13], d[14],
  1335.                                           PositionAngle.TRUE,
  1336.                                           frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  1337.                                           d[2]);
  1338.             } else {
  1339.                 // we don't have derivatives
  1340.                 return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngle.TRUE,
  1341.                                           frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  1342.                                           d[2]);
  1343.             }
  1344.         }

  1345.     }

  1346. }