KeplerianOrbit.java

  1. /* Copyright 2002-2025 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 org.hipparchus.analysis.differentiation.UnivariateDerivative1;
  19. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  20. import org.hipparchus.util.FastMath;
  21. import org.hipparchus.util.SinCos;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitIllegalArgumentException;
  24. import org.orekit.errors.OrekitInternalError;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.frames.KinematicTransform;
  28. import org.orekit.time.AbsoluteDate;
  29. import org.orekit.time.TimeOffset;
  30. import org.orekit.utils.PVCoordinates;
  31. import org.orekit.utils.TimeStampedPVCoordinates;


  32. /**
  33.  * This class handles traditional Keplerian orbital parameters.

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

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

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

  72.     /** Semi-major axis (m). */
  73.     private final double a;

  74.     /** Eccentricity. */
  75.     private final double e;

  76.     /** Inclination (rad). */
  77.     private final double i;

  78.     /** Perigee Argument (rad). */
  79.     private final double pa;

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

  82.     /** Cached anomaly (rad). */
  83.     private final double cachedAnomaly;

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

  86.     /** Eccentricity derivative. */
  87.     private final double eDot;

  88.     /** Inclination derivative (rad/s). */
  89.     private final double iDot;

  90.     /** Perigee Argument derivative (rad/s). */
  91.     private final double paDot;

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

  94.     /** Derivative of cached anomaly (rad/s). */
  95.     private final double cachedAnomalyDot;

  96.     /** Cached type of position angle. */
  97.     private final PositionAngleType cachedPositionAngleType;

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

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

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

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

  183.         if (a * (1 - e) < 0) {
  184.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a, e);
  185.         }

  186.         // Checking eccentricity range
  187.         checkParameterRangeInclusive(ECCENTRICITY, e, 0.0, Double.POSITIVE_INFINITY);

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

  198.         final UnivariateDerivative1 cachedAnomalyUD = initializeCachedAnomaly(anomaly, anomalyDot, type);
  199.         this.cachedAnomaly = cachedAnomalyUD.getValue();
  200.         this.cachedAnomalyDot = cachedAnomalyUD.getFirstDerivative();

  201.         // check true anomaly range
  202.         if (!isElliptical()) {
  203.             final double trueAnomaly = getTrueAnomaly();
  204.             if (1 + e * FastMath.cos(trueAnomaly) <= 0) {
  205.                 final double vMax = FastMath.acos(-1 / e);
  206.                 throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE,
  207.                         trueAnomaly, e, -vMax, vMax);
  208.             }
  209.         }

  210.         this.partialPV = null;

  211.     }

  212.     /** Creates a new instance with cached position angle same as value inputted.
  213.      * @param a  semi-major axis (m), negative for hyperbolic orbits
  214.      * @param e eccentricity (positive or equal to 0)
  215.      * @param i inclination (rad)
  216.      * @param pa perigee argument (ω, rad)
  217.      * @param raan right ascension of ascending node (Ω, rad)
  218.      * @param anomaly mean, eccentric or true anomaly (rad)
  219.      * @param aDot  semi-major axis derivative (m/s)
  220.      * @param eDot eccentricity derivative
  221.      * @param iDot inclination derivative (rad/s)
  222.      * @param paDot perigee argument derivative (rad/s)
  223.      * @param raanDot right ascension of ascending node derivative (rad/s)
  224.      * @param anomalyDot mean, eccentric or true anomaly derivative (rad/s)
  225.      * @param type type of anomaly
  226.      * @param frame the frame in which the parameters are defined
  227.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  228.      * @param date date of the orbital parameters
  229.      * @param mu central attraction coefficient (m³/s²)
  230.      * @exception IllegalArgumentException if frame is not a {@link
  231.      * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits,
  232.      * or v is out of range for hyperbolic orbits
  233.      * @since 9.0
  234.      */
  235.     public KeplerianOrbit(final double a, final double e, final double i,
  236.                           final double pa, final double raan, final double anomaly,
  237.                           final double aDot, final double eDot, final double iDot,
  238.                           final double paDot, final double raanDot, final double anomalyDot,
  239.                           final PositionAngleType type,
  240.                           final Frame frame, final AbsoluteDate date, final double mu)
  241.             throws IllegalArgumentException {
  242.         this(a, e, i, pa, raan, anomaly, aDot, eDot, iDot, paDot, raanDot, anomalyDot, type, type,
  243.                 frame, date, mu);
  244.     }

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

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

  284.         // compute inclination
  285.         final Vector3D momentum = pvCoordinates.getMomentum();
  286.         final double m2 = momentum.getNormSq();
  287.         i = Vector3D.angle(momentum, Vector3D.PLUS_K);

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

  290.         // preliminary computations for parameters depending on orbit shape (elliptic or hyperbolic)
  291.         final Vector3D pvP     = pvCoordinates.getPosition();
  292.         final Vector3D pvV     = pvCoordinates.getVelocity();
  293.         final Vector3D pvA     = pvCoordinates.getAcceleration();
  294.         final double   r2      = pvP.getNormSq();
  295.         final double   r       = FastMath.sqrt(r2);
  296.         final double   V2      = pvV.getNormSq();
  297.         final double   rV2OnMu = r * V2 / mu;

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

  301.         // compute cached anomaly
  302.         if (isElliptical()) {
  303.             // elliptic or circular orbit
  304.             final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA);
  305.             final double eCE = rV2OnMu - 1;
  306.             e = FastMath.sqrt(eSE * eSE + eCE * eCE);
  307.             this.cachedPositionAngleType = PositionAngleType.ECCENTRIC;
  308.             cachedAnomaly = FastMath.atan2(eSE, eCE);
  309.         } else {
  310.             // hyperbolic orbit
  311.             final double eSH = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(-muA);
  312.             final double eCH = rV2OnMu - 1;
  313.             e = FastMath.sqrt(1 - m2 / muA);
  314.             this.cachedPositionAngleType = PositionAngleType.TRUE;
  315.             cachedAnomaly = KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, FastMath.log((eCH + eSH) / (eCH - eSH)) / 2);
  316.         }

  317.         // Checking eccentricity range
  318.         checkParameterRangeInclusive(ECCENTRICITY, e, 0.0, Double.POSITIVE_INFINITY);

  319.         // compute perigee argument
  320.         final Vector3D node = new Vector3D(raan, 0.0);
  321.         final double px = Vector3D.dotProduct(pvP, node);
  322.         final double py = Vector3D.dotProduct(pvP, Vector3D.crossProduct(momentum, node)) / FastMath.sqrt(m2);
  323.         pa = FastMath.atan2(py, px) - getTrueAnomaly();

  324.         partialPV = pvCoordinates;

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

  327.             final double[][] jacobian = new double[6][6];
  328.             getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian);

  329.             final Vector3D keplerianAcceleration    = new Vector3D(-mu / (r * r2), pvP);
  330.             final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  331.             final double   aX                       = nonKeplerianAcceleration.getX();
  332.             final double   aY                       = nonKeplerianAcceleration.getY();
  333.             final double   aZ                       = nonKeplerianAcceleration.getZ();
  334.             aDot    = jacobian[0][3] * aX + jacobian[0][4] * aY + jacobian[0][5] * aZ;
  335.             eDot    = jacobian[1][3] * aX + jacobian[1][4] * aY + jacobian[1][5] * aZ;
  336.             iDot    = jacobian[2][3] * aX + jacobian[2][4] * aY + jacobian[2][5] * aZ;
  337.             paDot   = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ;
  338.             raanDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ;

  339.             // in order to compute cached anomaly derivative, we must compute
  340.             // mean anomaly derivative including Keplerian motion and convert to required anomaly
  341.             final double MDot = getKeplerianMeanMotion() +
  342.                     jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ;
  343.             final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  344.             final UnivariateDerivative1 MUD = new UnivariateDerivative1(getMeanAnomaly(), MDot);
  345.             if (cachedPositionAngleType == PositionAngleType.ECCENTRIC) {
  346.                 final UnivariateDerivative1 EUD = (a < 0) ?
  347.                         FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, MUD) :
  348.                         FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, MUD);
  349.                 cachedAnomalyDot = EUD.getFirstDerivative();
  350.             } else { // TRUE
  351.                 final UnivariateDerivative1 vUD = (a < 0) ?
  352.                         FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) :
  353.                         FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD);
  354.                 cachedAnomalyDot = vUD.getFirstDerivative();
  355.             }

  356.         } else {
  357.             // acceleration is either almost zero or NaN,
  358.             // we assume acceleration was not known
  359.             aDot    = 0.;
  360.             eDot    = 0.;
  361.             iDot    = 0.;
  362.             paDot   = 0.;
  363.             raanDot = 0.;
  364.             cachedAnomalyDot = computeKeplerianAnomalyDot(cachedPositionAngleType, a, e, mu, cachedAnomaly, cachedPositionAngleType);
  365.         }

  366.     }

  367.     /** Constructor from Cartesian parameters.
  368.      *
  369.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  370.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  371.      * use {@code mu} and the position to compute the acceleration, including
  372.      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
  373.      *
  374.      * @param pvCoordinates the PVCoordinates of the satellite
  375.      * @param frame the frame in which are defined the {@link PVCoordinates}
  376.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  377.      * @param date date of the orbital parameters
  378.      * @param mu central attraction coefficient (m³/s²)
  379.      * @exception IllegalArgumentException if frame is not a {@link
  380.      * Frame#isPseudoInertial pseudo-inertial frame}
  381.      */
  382.     public KeplerianOrbit(final PVCoordinates pvCoordinates,
  383.                           final Frame frame, final AbsoluteDate date, final double mu)
  384.             throws IllegalArgumentException {
  385.         this(new TimeStampedPVCoordinates(date, pvCoordinates), frame, mu);
  386.     }

  387.     /** Constructor from any kind of orbital parameters.
  388.      * @param op orbital parameters to copy
  389.      */
  390.     public KeplerianOrbit(final Orbit op) {
  391.         this(op.getPVCoordinates(), op.getFrame(), op.getMu(), op.hasNonKeplerianAcceleration());
  392.     }

  393.     /** {@inheritDoc} */
  394.     @Override
  395.     public boolean hasNonKeplerianAcceleration() {
  396.         return aDot != 0. || eDot != 0. || paDot != 0. || iDot != 0. || raanDot != 0. ||
  397.                 FastMath.abs(cachedAnomalyDot - computeKeplerianAnomalyDot(cachedPositionAngleType, a, e, getMu(), cachedAnomaly, cachedPositionAngleType)) > TOLERANCE_POSITION_ANGLE_RATE;
  398.     }

  399.     /** {@inheritDoc} */
  400.     @Override
  401.     public OrbitType getType() {
  402.         return OrbitType.KEPLERIAN;
  403.     }

  404.     /** {@inheritDoc} */
  405.     @Override
  406.     public double getA() {
  407.         return a;
  408.     }

  409.     /** {@inheritDoc} */
  410.     @Override
  411.     public double getADot() {
  412.         return aDot;
  413.     }

  414.     /** {@inheritDoc} */
  415.     @Override
  416.     public double getE() {
  417.         return e;
  418.     }

  419.     /** {@inheritDoc} */
  420.     @Override
  421.     public double getEDot() {
  422.         return eDot;
  423.     }

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

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

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

  440.     /** Get the perigee argument derivative.
  441.      * <p>
  442.      * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
  443.      * </p>
  444.      * @return perigee argument derivative (rad/s)
  445.      * @since 9.0
  446.      */
  447.     public double getPerigeeArgumentDot() {
  448.         return paDot;
  449.     }

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

  456.     /** Get the right ascension of the ascending node derivative.
  457.      * <p>
  458.      * If the orbit was created without derivatives, the value returned is {@link Double#NaN}.
  459.      * </p>
  460.      * @return right ascension of the ascending node derivative (rad/s)
  461.      * @since 9.0
  462.      */
  463.     public double getRightAscensionOfAscendingNodeDot() {
  464.         return raanDot;
  465.     }

  466.     /** Get the true anomaly.
  467.      * @return true anomaly (rad)
  468.      */
  469.     public double getTrueAnomaly() {
  470.         switch (cachedPositionAngleType) {
  471.             case MEAN: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicMeanToTrue(e, cachedAnomaly) :
  472.                     KeplerianAnomalyUtility.ellipticMeanToTrue(e, cachedAnomaly);

  473.             case TRUE: return cachedAnomaly;

  474.             case ECCENTRIC: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, cachedAnomaly) :
  475.                     KeplerianAnomalyUtility.ellipticEccentricToTrue(e, cachedAnomaly);

  476.             default: throw new OrekitInternalError(null);
  477.         }
  478.     }

  479.     /** Get the true anomaly derivative.
  480.      * @return true anomaly derivative (rad/s)
  481.      */
  482.     public double getTrueAnomalyDot() {
  483.         switch (cachedPositionAngleType) {
  484.             case MEAN:
  485.                 final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  486.                 final UnivariateDerivative1 MUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot);
  487.                 final UnivariateDerivative1 vUD = (a < 0) ?
  488.                         FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) :
  489.                         FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD);
  490.                 return vUD.getFirstDerivative();

  491.             case TRUE:
  492.                 return cachedAnomalyDot;

  493.             case ECCENTRIC:
  494.                 final UnivariateDerivative1 eUD2 = new UnivariateDerivative1(e, eDot);
  495.                 final UnivariateDerivative1 EUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot);
  496.                 final UnivariateDerivative1 vUD2 = (a < 0) ?
  497.                         FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD2, EUD) :
  498.                         FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD2, EUD);
  499.                 return vUD2.getFirstDerivative();

  500.             default:
  501.                 throw new OrekitInternalError(null);
  502.         }
  503.     }

  504.     /** Get the eccentric anomaly.
  505.      * @return eccentric anomaly (rad)
  506.      */
  507.     public double getEccentricAnomaly() {
  508.         switch (cachedPositionAngleType) {
  509.             case MEAN:
  510.                 return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, cachedAnomaly) :
  511.                     KeplerianAnomalyUtility.ellipticMeanToEccentric(e, cachedAnomaly);

  512.             case ECCENTRIC:
  513.                 return cachedAnomaly;

  514.             case TRUE:
  515.                 return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, cachedAnomaly) :
  516.                     KeplerianAnomalyUtility.ellipticTrueToEccentric(e, cachedAnomaly);

  517.             default:
  518.                 throw new OrekitInternalError(null);
  519.         }
  520.     }

  521.     /** Get the eccentric anomaly derivative.
  522.      * @return eccentric anomaly derivative (rad/s)
  523.      * @since 9.0
  524.      */
  525.     public double getEccentricAnomalyDot() {
  526.         switch (cachedPositionAngleType) {
  527.             case ECCENTRIC:
  528.                 return cachedAnomalyDot;

  529.             case TRUE:
  530.                 final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  531.                 final UnivariateDerivative1 vUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot);
  532.                 final UnivariateDerivative1 EUD = (a < 0) ?
  533.                         FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) :
  534.                         FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD);
  535.                 return EUD.getFirstDerivative();

  536.             case MEAN:
  537.                 final UnivariateDerivative1 eUD2 = new UnivariateDerivative1(e, eDot);
  538.                 final UnivariateDerivative1 MUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot);
  539.                 final UnivariateDerivative1 EUD2 = (a < 0) ?
  540.                         FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD2, MUD) :
  541.                         FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD2, MUD);
  542.                 return EUD2.getFirstDerivative();

  543.             default:
  544.                 throw new OrekitInternalError(null);
  545.         }
  546.     }

  547.     /** Get the mean anomaly.
  548.      * @return mean anomaly (rad)
  549.      */
  550.     public double getMeanAnomaly() {
  551.         switch (cachedPositionAngleType) {
  552.             case ECCENTRIC: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicEccentricToMean(e, cachedAnomaly) :
  553.                     KeplerianAnomalyUtility.ellipticEccentricToMean(e, cachedAnomaly);

  554.             case MEAN: return cachedAnomaly;

  555.             case TRUE: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicTrueToMean(e, cachedAnomaly) :
  556.                     KeplerianAnomalyUtility.ellipticTrueToMean(e, cachedAnomaly);

  557.             default: throw new OrekitInternalError(null);
  558.         }
  559.     }

  560.     /** Get the mean anomaly derivative.
  561.      * @return mean anomaly derivative (rad/s)
  562.      * @since 9.0
  563.      */
  564.     public double getMeanAnomalyDot() {
  565.         switch (cachedPositionAngleType) {
  566.             case MEAN:
  567.                 return cachedAnomalyDot;

  568.             case ECCENTRIC:
  569.                 final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  570.                 final UnivariateDerivative1 EUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot);
  571.                 final UnivariateDerivative1 MUD = (a < 0) ?
  572.                         FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, EUD) :
  573.                         FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, EUD);
  574.                 return MUD.getFirstDerivative();

  575.             case TRUE:
  576.                 final UnivariateDerivative1 eUD2 = new UnivariateDerivative1(e, eDot);
  577.                 final UnivariateDerivative1 vUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot);
  578.                 final UnivariateDerivative1 MUD2 = (a < 0) ?
  579.                         FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD2, vUD) :
  580.                         FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD2, vUD);
  581.                 return MUD2.getFirstDerivative();

  582.             default:
  583.                 throw new OrekitInternalError(null);
  584.         }
  585.     }

  586.     /** Get the anomaly.
  587.      * @param type type of the angle
  588.      * @return anomaly (rad)
  589.      */
  590.     public double getAnomaly(final PositionAngleType type) {
  591.         return (type == PositionAngleType.MEAN) ? getMeanAnomaly() :
  592.                 ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomaly() :
  593.                         getTrueAnomaly());
  594.     }

  595.     /** Get the anomaly derivative.
  596.      * @param type type of the angle
  597.      * @return anomaly derivative (rad/s)
  598.      * @since 9.0
  599.      */
  600.     public double getAnomalyDot(final PositionAngleType type) {
  601.         return (type == PositionAngleType.MEAN) ? getMeanAnomalyDot() :
  602.                 ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomalyDot() :
  603.                         getTrueAnomalyDot());
  604.     }

  605.     /** {@inheritDoc} */
  606.     @Override
  607.     public double getEquinoctialEx() {
  608.         return e * FastMath.cos(pa + raan);
  609.     }

  610.     /** {@inheritDoc} */
  611.     @Override
  612.     public double getEquinoctialExDot() {
  613.         if (!hasNonKeplerianAcceleration()) {
  614.             return 0.;
  615.         }
  616.         final double paPraan = pa + raan;
  617.         final SinCos sc      = FastMath.sinCos(paPraan);
  618.         return eDot * sc.cos() - e * sc.sin() * (paDot + raanDot);
  619.     }

  620.     /** {@inheritDoc} */
  621.     @Override
  622.     public double getEquinoctialEy() {
  623.         return e * FastMath.sin(pa + raan);
  624.     }

  625.     /** {@inheritDoc} */
  626.     @Override
  627.     public double getEquinoctialEyDot() {
  628.         if (!hasNonKeplerianAcceleration()) {
  629.             return 0.;
  630.         }
  631.         final double paPraan = pa + raan;
  632.         final SinCos sc      = FastMath.sinCos(paPraan);
  633.         return eDot * sc.sin() + e * sc.cos() * (paDot + raanDot);
  634.     }

  635.     /** {@inheritDoc} */
  636.     @Override
  637.     public double getHx() {
  638.         // Check for equatorial retrograde orbit
  639.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  640.             return Double.NaN;
  641.         }
  642.         return FastMath.cos(raan) * FastMath.tan(0.5 * i);
  643.     }

  644.     /** {@inheritDoc} */
  645.     @Override
  646.     public double getHxDot() {
  647.         // Check for equatorial retrograde orbit
  648.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  649.             return Double.NaN;
  650.         }
  651.         if (!hasNonKeplerianAcceleration()) {
  652.             return 0.;
  653.         }
  654.         final SinCos sc      = FastMath.sinCos(raan);
  655.         final double tan     = FastMath.tan(0.5 * i);
  656.         return 0.5 * (1 + tan * tan) * sc.cos() * iDot - tan * sc.sin() * raanDot;
  657.     }

  658.     /** {@inheritDoc} */
  659.     @Override
  660.     public double getHy() {
  661.         // Check for equatorial retrograde orbit
  662.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  663.             return Double.NaN;
  664.         }
  665.         return FastMath.sin(raan) * FastMath.tan(0.5 * i);
  666.     }

  667.     /** {@inheritDoc} */
  668.     @Override
  669.     public double getHyDot() {
  670.         // Check for equatorial retrograde orbit
  671.         if (FastMath.abs(i - FastMath.PI) < 1.0e-10) {
  672.             return Double.NaN;
  673.         }
  674.         if (!hasNonKeplerianAcceleration()) {
  675.             return 0.;
  676.         }
  677.         final SinCos sc      = FastMath.sinCos(raan);
  678.         final double tan     = FastMath.tan(0.5 * i);
  679.         return 0.5 * (1 + tan * tan) * sc.sin() * iDot + tan * sc.cos() * raanDot;
  680.     }

  681.     /** {@inheritDoc} */
  682.     @Override
  683.     public double getLv() {
  684.         return pa + raan + getTrueAnomaly();
  685.     }

  686.     /** {@inheritDoc} */
  687.     @Override
  688.     public double getLvDot() {
  689.         return paDot + raanDot + getTrueAnomalyDot();
  690.     }

  691.     /** {@inheritDoc} */
  692.     @Override
  693.     public double getLE() {
  694.         return pa + raan + getEccentricAnomaly();
  695.     }

  696.     /** {@inheritDoc} */
  697.     @Override
  698.     public double getLEDot() {
  699.         return paDot + raanDot + getEccentricAnomalyDot();
  700.     }

  701.     /** {@inheritDoc} */
  702.     @Override
  703.     public double getLM() {
  704.         return pa + raan + getMeanAnomaly();
  705.     }

  706.     /** {@inheritDoc} */
  707.     @Override
  708.     public double getLMDot() {
  709.         return paDot + raanDot + getMeanAnomalyDot();
  710.     }

  711.     /** Initialize cached anomaly with rate.
  712.      * @param anomaly input anomaly
  713.      * @param anomalyDot rate of input anomaly
  714.      * @param inputType position angle type passed as input
  715.      * @return anomaly to cache with rate
  716.      * @since 12.1
  717.      */
  718.     private UnivariateDerivative1 initializeCachedAnomaly(final double anomaly, final double anomalyDot,
  719.                                                           final PositionAngleType inputType) {
  720.         if (cachedPositionAngleType == inputType) {
  721.             return new UnivariateDerivative1(anomaly, anomalyDot);

  722.         } else {
  723.             final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot);
  724.             final UnivariateDerivative1 anomalyUD = new UnivariateDerivative1(anomaly, anomalyDot);

  725.             if (a < 0) {
  726.                 switch (cachedPositionAngleType) {
  727.                     case MEAN:
  728.                         if (inputType == PositionAngleType.ECCENTRIC) {
  729.                             return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, anomalyUD);
  730.                         } else {
  731.                             return FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, anomalyUD);
  732.                         }

  733.                     case ECCENTRIC:
  734.                         if (inputType == PositionAngleType.MEAN) {
  735.                             return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, anomalyUD);
  736.                         } else {
  737.                             return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, anomalyUD);
  738.                         }

  739.                     case TRUE:
  740.                         if (inputType == PositionAngleType.MEAN) {
  741.                             return FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD);
  742.                         } else {
  743.                             return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD);
  744.                         }

  745.                     default:
  746.                         break;
  747.                 }

  748.             } else {
  749.                 switch (cachedPositionAngleType) {
  750.                     case MEAN:
  751.                         if (inputType == PositionAngleType.ECCENTRIC) {
  752.                             return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, anomalyUD);
  753.                         } else {
  754.                             return FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, anomalyUD);
  755.                         }

  756.                     case ECCENTRIC:
  757.                         if (inputType == PositionAngleType.MEAN) {
  758.                             return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, anomalyUD);
  759.                         } else {
  760.                             return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, anomalyUD);
  761.                         }

  762.                     case TRUE:
  763.                         if (inputType == PositionAngleType.MEAN) {
  764.                             return FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD);
  765.                         } else {
  766.                             return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD);
  767.                         }

  768.                     default:
  769.                         break;
  770.                 }

  771.             }
  772.             throw new OrekitInternalError(null);
  773.         }

  774.     }

  775.     /** Compute reference axes.
  776.      * @return reference axes
  777.      * @since 12.0
  778.      */
  779.     private Vector3D[] referenceAxes() {
  780.         // preliminary variables
  781.         final SinCos scRaan  = FastMath.sinCos(raan);
  782.         final SinCos scPa    = FastMath.sinCos(pa);
  783.         final SinCos scI     = FastMath.sinCos(i);
  784.         final double cosRaan = scRaan.cos();
  785.         final double sinRaan = scRaan.sin();
  786.         final double cosPa   = scPa.cos();
  787.         final double sinPa   = scPa.sin();
  788.         final double cosI    = scI.cos();
  789.         final double sinI    = scI.sin();

  790.         final double crcp    = cosRaan * cosPa;
  791.         final double crsp    = cosRaan * sinPa;
  792.         final double srcp    = sinRaan * cosPa;
  793.         final double srsp    = sinRaan * sinPa;

  794.         // reference axes defining the orbital plane
  795.         return new Vector3D[] {
  796.             new Vector3D( crcp - cosI * srsp,  srcp + cosI * crsp, sinI * sinPa),
  797.             new Vector3D(-crsp - cosI * srcp, -srsp + cosI * crcp, sinI * cosPa)
  798.         };

  799.     }

  800.     /** Compute position and velocity but not acceleration.
  801.      */
  802.     private void computePVWithoutA() {

  803.         if (partialPV != null) {
  804.             // already computed
  805.             return;
  806.         }

  807.         final Vector3D[] axes = referenceAxes();

  808.         if (isElliptical()) {

  809.             // elliptical case

  810.             // elliptic eccentric anomaly
  811.             final double uME2   = (1 - e) * (1 + e);
  812.             final double s1Me2  = FastMath.sqrt(uME2);
  813.             final SinCos scE    = FastMath.sinCos(getEccentricAnomaly());
  814.             final double cosE   = scE.cos();
  815.             final double sinE   = scE.sin();

  816.             // coordinates of position and velocity in the orbital plane
  817.             final double x      = a * (cosE - e);
  818.             final double y      = a * sinE * s1Me2;
  819.             final double factor = FastMath.sqrt(getMu() / a) / (1 - e * cosE);
  820.             final double xDot   = -sinE * factor;
  821.             final double yDot   =  cosE * s1Me2 * factor;

  822.             final Vector3D position = new Vector3D(x, axes[0], y, axes[1]);
  823.             final Vector3D velocity = new Vector3D(xDot, axes[0], yDot, axes[1]);
  824.             partialPV = new PVCoordinates(position, velocity);

  825.         } else {

  826.             // hyperbolic case

  827.             // compute position and velocity factors
  828.             final SinCos scV       = FastMath.sinCos(getTrueAnomaly());
  829.             final double sinV      = scV.sin();
  830.             final double cosV      = scV.cos();
  831.             final double f         = a * (1 - e * e);
  832.             final double posFactor = f / (1 + e * cosV);
  833.             final double velFactor = FastMath.sqrt(getMu() / f);

  834.             final double   x            =  posFactor * cosV;
  835.             final double   y            =  posFactor * sinV;
  836.             final double   xDot         = -velFactor * sinV;
  837.             final double   yDot         =  velFactor * (e + cosV);

  838.             final Vector3D position = new Vector3D(x, axes[0], y, axes[1]);
  839.             final Vector3D velocity = new Vector3D(xDot, axes[0], yDot, axes[1]);
  840.             partialPV = new PVCoordinates(position, velocity);

  841.         }

  842.     }

  843.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  844.      * @return non-Keplerian part of the acceleration
  845.      */
  846.     private Vector3D nonKeplerianAcceleration() {

  847.         final double[][] dCdP = new double[6][6];
  848.         getJacobianWrtParameters(PositionAngleType.MEAN, dCdP);

  849.         final double nonKeplerianMeanMotion = getMeanAnomalyDot() - getKeplerianMeanMotion();
  850.         final double nonKeplerianAx = dCdP[3][0] * aDot    + dCdP[3][1] * eDot    + dCdP[3][2] * iDot    +
  851.                 dCdP[3][3] * paDot   + dCdP[3][4] * raanDot + dCdP[3][5] * nonKeplerianMeanMotion;
  852.         final double nonKeplerianAy = dCdP[4][0] * aDot    + dCdP[4][1] * eDot    + dCdP[4][2] * iDot    +
  853.                 dCdP[4][3] * paDot   + dCdP[4][4] * raanDot + dCdP[4][5] * nonKeplerianMeanMotion;
  854.         final double nonKeplerianAz = dCdP[5][0] * aDot    + dCdP[5][1] * eDot    + dCdP[5][2] * iDot    +
  855.                 dCdP[5][3] * paDot   + dCdP[5][4] * raanDot + dCdP[5][5] * nonKeplerianMeanMotion;

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

  857.     }

  858.     /** {@inheritDoc} */
  859.     @Override
  860.     protected Vector3D initPosition() {

  861.         final Vector3D[] axes = referenceAxes();

  862.         if (isElliptical()) {

  863.             // elliptical case

  864.             // elliptic eccentric anomaly
  865.             final double uME2   = (1 - e) * (1 + e);
  866.             final double s1Me2  = FastMath.sqrt(uME2);
  867.             final SinCos scE    = FastMath.sinCos(getEccentricAnomaly());
  868.             final double cosE   = scE.cos();
  869.             final double sinE   = scE.sin();

  870.             return new Vector3D(a * (cosE - e), axes[0], a * sinE * s1Me2, axes[1]);

  871.         } else {

  872.             // hyperbolic case

  873.             // compute position and velocity factors
  874.             final SinCos scV       = FastMath.sinCos(getTrueAnomaly());
  875.             final double sinV      = scV.sin();
  876.             final double cosV      = scV.cos();
  877.             final double f         = a * (1 - e * e);
  878.             final double posFactor = f / (1 + e * cosV);

  879.             return new Vector3D(posFactor * cosV, axes[0], posFactor * sinV, axes[1]);

  880.         }

  881.     }

  882.     /** {@inheritDoc} */
  883.     @Override
  884.     protected TimeStampedPVCoordinates initPVCoordinates() {

  885.         // position and velocity
  886.         computePVWithoutA();

  887.         // acceleration
  888.         final double r2 = partialPV.getPosition().getNormSq();
  889.         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
  890.         final Vector3D acceleration = hasNonKeplerianAcceleration() ?
  891.                 keplerianAcceleration.add(nonKeplerianAcceleration()) :
  892.                 keplerianAcceleration;

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

  894.     }

  895.     /** {@inheritDoc} */
  896.     @Override
  897.     public KeplerianOrbit inFrame(final Frame inertialFrame) {
  898.         final PVCoordinates pvCoordinates;
  899.         if (hasNonKeplerianAcceleration()) {
  900.             pvCoordinates = getPVCoordinates(inertialFrame);
  901.         } else {
  902.             final KinematicTransform transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
  903.             pvCoordinates = transform.transformOnlyPV(getPVCoordinates());
  904.         }
  905.         final KeplerianOrbit keplerianOrbit = new KeplerianOrbit(pvCoordinates, inertialFrame, getDate(), getMu());
  906.         if (keplerianOrbit.getCachedPositionAngleType() == getCachedPositionAngleType()) {
  907.             return keplerianOrbit;
  908.         } else {
  909.             return keplerianOrbit.withCachedPositionAngleType(getCachedPositionAngleType());
  910.         }
  911.     }

  912.     /** {@inheritDoc} */
  913.     @Override
  914.     public KeplerianOrbit withCachedPositionAngleType(final PositionAngleType positionAngleType) {
  915.         return new KeplerianOrbit(a, e, i, pa, raan, getAnomaly(positionAngleType), aDot, eDot, iDot, paDot, raanDot,
  916.                 getAnomalyDot(positionAngleType), positionAngleType, getFrame(), getDate(), getMu());
  917.     }

  918.     /** {@inheritDoc} */
  919.     @Override
  920.     public KeplerianOrbit shiftedBy(final double dt) {
  921.         return shiftedBy(new TimeOffset(dt));
  922.     }

  923.     /** {@inheritDoc} */
  924.     @Override
  925.     public KeplerianOrbit shiftedBy(final TimeOffset dt) {

  926.         final double dtS = dt.toDouble();

  927.         // use Keplerian-only motion
  928.         final KeplerianOrbit keplerianShifted = new KeplerianOrbit(a, e, i, pa, raan,
  929.                 getMeanAnomaly() + getKeplerianMeanMotion() * dtS, PositionAngleType.MEAN,
  930.                 cachedPositionAngleType, getFrame(), getDate().shiftedBy(dt), getMu());

  931.         if (hasNonKeplerianAcceleration()) {

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

  934.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  935.             keplerianShifted.computePVWithoutA();
  936.             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
  937.                     0.5 * dtS * dtS, nonKeplerianAcceleration);
  938.             final double   fixedR2 = fixedP.getNormSq();
  939.             final double   fixedR  = FastMath.sqrt(fixedR2);
  940.             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
  941.                     dtS, nonKeplerianAcceleration);
  942.             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
  943.                     1, nonKeplerianAcceleration);

  944.             // build a new orbit, taking non-Keplerian acceleration into account
  945.             return new KeplerianOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
  946.                     fixedP, fixedV, fixedA),
  947.                     keplerianShifted.getFrame(), keplerianShifted.getMu());

  948.         } else {
  949.             // Keplerian-only motion is all we can do
  950.             return keplerianShifted;
  951.         }

  952.     }

  953.     /** {@inheritDoc} */
  954.     @Override
  955.     protected double[][] computeJacobianMeanWrtCartesian() {
  956.         if (isElliptical()) {
  957.             return computeJacobianMeanWrtCartesianElliptical();
  958.         } else {
  959.             return computeJacobianMeanWrtCartesianHyperbolic();
  960.         }
  961.     }

  962.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  963.      * <p>
  964.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  965.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  966.      * yDot for j=4, zDot for j=5).
  967.      * </p>
  968.      * @return 6x6 Jacobian matrix
  969.      */
  970.     private double[][] computeJacobianMeanWrtCartesianElliptical() {

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

  972.         // compute various intermediate parameters
  973.         computePVWithoutA();
  974.         final Vector3D position = partialPV.getPosition();
  975.         final Vector3D velocity = partialPV.getVelocity();
  976.         final Vector3D momentum = partialPV.getMomentum();
  977.         final double v2         = velocity.getNormSq();
  978.         final double r2         = position.getNormSq();
  979.         final double r          = FastMath.sqrt(r2);
  980.         final double r3         = r * r2;

  981.         final double px         = position.getX();
  982.         final double py         = position.getY();
  983.         final double pz         = position.getZ();
  984.         final double vx         = velocity.getX();
  985.         final double vy         = velocity.getY();
  986.         final double vz         = velocity.getZ();
  987.         final double mx         = momentum.getX();
  988.         final double my         = momentum.getY();
  989.         final double mz         = momentum.getZ();

  990.         final double mu         = getMu();
  991.         final double sqrtMuA    = FastMath.sqrt(a * mu);
  992.         final double sqrtAoMu   = FastMath.sqrt(a / mu);
  993.         final double a2         = a * a;
  994.         final double twoA       = 2 * a;
  995.         final double rOnA       = r / a;

  996.         final double oMe2       = 1 - e * e;
  997.         final double epsilon    = FastMath.sqrt(oMe2);
  998.         final double sqrtRec    = 1 / epsilon;

  999.         final SinCos scI        = FastMath.sinCos(i);
  1000.         final SinCos scPA       = FastMath.sinCos(pa);
  1001.         final double cosI       = scI.cos();
  1002.         final double sinI       = scI.sin();
  1003.         final double cosPA      = scPA.cos();
  1004.         final double sinPA      = scPA.sin();

  1005.         final double pv         = Vector3D.dotProduct(position, velocity);
  1006.         final double cosE       = (a - r) / (a * e);
  1007.         final double sinE       = pv / (e * sqrtMuA);

  1008.         // da
  1009.         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
  1010.         final Vector3D vectorARDot = velocity.scalarMultiply(2 * a2 / mu);
  1011.         fillHalfRow(1, vectorAR,    jacobian[0], 0);
  1012.         fillHalfRow(1, vectorARDot, jacobian[0], 3);

  1013.         // de
  1014.         final double factorER3 = pv / twoA;
  1015.         final Vector3D vectorER   = new Vector3D(cosE * v2 / (r * mu), position,
  1016.                 sinE / sqrtMuA, velocity,
  1017.                 -factorER3 * sinE / sqrtMuA, vectorAR);
  1018.         final Vector3D vectorERDot = new Vector3D(sinE / sqrtMuA, position,
  1019.                 cosE * 2 * r / mu, velocity,
  1020.                 -factorER3 * sinE / sqrtMuA, vectorARDot);
  1021.         fillHalfRow(1, vectorER,    jacobian[1], 0);
  1022.         fillHalfRow(1, vectorERDot, jacobian[1], 3);

  1023.         // dE / dr (Eccentric anomaly)
  1024.         final double coefE = cosE / (e * sqrtMuA);
  1025.         final Vector3D  vectorEAnR =
  1026.                 new Vector3D(-sinE * v2 / (e * r * mu), position, coefE, velocity,
  1027.                         -factorER3 * coefE, vectorAR);

  1028.         // dE / drDot
  1029.         final Vector3D  vectorEAnRDot =
  1030.                 new Vector3D(-sinE * 2 * r / (e * mu), velocity, coefE, position,
  1031.                         -factorER3 * coefE, vectorARDot);

  1032.         // precomputing some more factors
  1033.         final double s1 = -sinE * pz / r - cosE * vz * sqrtAoMu;
  1034.         final double s2 = -cosE * pz / r3;
  1035.         final double s3 = -sinE * vz / (2 * sqrtMuA);
  1036.         final double t1 = sqrtRec * (cosE * pz / r - sinE * vz * sqrtAoMu);
  1037.         final double t2 = sqrtRec * (-sinE * pz / r3);
  1038.         final double t3 = sqrtRec * (cosE - e) * vz / (2 * sqrtMuA);
  1039.         final double t4 = sqrtRec * (e * sinI * cosPA * sqrtRec - vz * sqrtAoMu);
  1040.         final Vector3D s = new Vector3D(cosE / r, Vector3D.PLUS_K,
  1041.                 s1,       vectorEAnR,
  1042.                 s2,       position,
  1043.                 s3,       vectorAR);
  1044.         final Vector3D sDot = new Vector3D(-sinE * sqrtAoMu, Vector3D.PLUS_K,
  1045.                 s1,               vectorEAnRDot,
  1046.                 s3,               vectorARDot);
  1047.         final Vector3D t =
  1048.                 new Vector3D(sqrtRec * sinE / r, Vector3D.PLUS_K).add(new Vector3D(t1, vectorEAnR,
  1049.                         t2, position,
  1050.                         t3, vectorAR,
  1051.                         t4, vectorER));
  1052.         final Vector3D tDot = new Vector3D(sqrtRec * (cosE - e) * sqrtAoMu, Vector3D.PLUS_K,
  1053.                 t1,                              vectorEAnRDot,
  1054.                 t3,                              vectorARDot,
  1055.                 t4,                              vectorERDot);

  1056.         // di
  1057.         final double factorI1 = -sinI * sqrtRec / sqrtMuA;
  1058.         final double i1 =  factorI1;
  1059.         final double i2 = -factorI1 * mz / twoA;
  1060.         final double i3 =  factorI1 * mz * e / oMe2;
  1061.         final double i4 = cosI * sinPA;
  1062.         final double i5 = cosI * cosPA;
  1063.         fillHalfRow(i1, new Vector3D(vy, -vx, 0), i2, vectorAR, i3, vectorER, i4, s, i5, t,
  1064.                 jacobian[2], 0);
  1065.         fillHalfRow(i1, new Vector3D(-py, px, 0), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot,
  1066.                 jacobian[2], 3);

  1067.         // dpa
  1068.         fillHalfRow(cosPA / sinI, s,    -sinPA / sinI, t,    jacobian[3], 0);
  1069.         fillHalfRow(cosPA / sinI, sDot, -sinPA / sinI, tDot, jacobian[3], 3);

  1070.         // dRaan
  1071.         final double factorRaanR = 1 / (mu * a * oMe2 * sinI * sinI);
  1072.         fillHalfRow(-factorRaanR * my, new Vector3D(  0, vz, -vy),
  1073.                 factorRaanR * mx, new Vector3D(-vz,  0,  vx),
  1074.                 jacobian[4], 0);
  1075.         fillHalfRow(-factorRaanR * my, new Vector3D( 0, -pz,  py),
  1076.                 factorRaanR * mx, new Vector3D(pz,   0, -px),
  1077.                 jacobian[4], 3);

  1078.         // dM
  1079.         fillHalfRow(rOnA, vectorEAnR,    -sinE, vectorER,    jacobian[5], 0);
  1080.         fillHalfRow(rOnA, vectorEAnRDot, -sinE, vectorERDot, jacobian[5], 3);

  1081.         return jacobian;

  1082.     }

  1083.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1084.      * <p>
  1085.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1086.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1087.      * yDot for j=4, zDot for j=5).
  1088.      * </p>
  1089.      * @return 6x6 Jacobian matrix
  1090.      */
  1091.     private double[][] computeJacobianMeanWrtCartesianHyperbolic() {

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

  1093.         // compute various intermediate parameters
  1094.         computePVWithoutA();
  1095.         final Vector3D position = partialPV.getPosition();
  1096.         final Vector3D velocity = partialPV.getVelocity();
  1097.         final Vector3D momentum = partialPV.getMomentum();
  1098.         final double r2         = position.getNormSq();
  1099.         final double r          = FastMath.sqrt(r2);
  1100.         final double r3         = r * r2;

  1101.         final double x          = position.getX();
  1102.         final double y          = position.getY();
  1103.         final double z          = position.getZ();
  1104.         final double vx         = velocity.getX();
  1105.         final double vy         = velocity.getY();
  1106.         final double vz         = velocity.getZ();
  1107.         final double mx         = momentum.getX();
  1108.         final double my         = momentum.getY();
  1109.         final double mz         = momentum.getZ();

  1110.         final double mu         = getMu();
  1111.         final double absA       = -a;
  1112.         final double sqrtMuA    = FastMath.sqrt(absA * mu);
  1113.         final double a2         = a * a;
  1114.         final double rOa        = r / absA;

  1115.         final SinCos scI        = FastMath.sinCos(i);
  1116.         final double cosI       = scI.cos();
  1117.         final double sinI       = scI.sin();

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

  1119.         // da
  1120.         final Vector3D vectorAR = new Vector3D(-2 * a2 / r3, position);
  1121.         final Vector3D vectorARDot = velocity.scalarMultiply(-2 * a2 / mu);
  1122.         fillHalfRow(-1, vectorAR,    jacobian[0], 0);
  1123.         fillHalfRow(-1, vectorARDot, jacobian[0], 3);

  1124.         // differentials of the momentum
  1125.         final double m      = momentum.getNorm();
  1126.         final double oOm    = 1 / m;
  1127.         final Vector3D dcXP = new Vector3D(  0,  vz, -vy);
  1128.         final Vector3D dcYP = new Vector3D(-vz,   0,  vx);
  1129.         final Vector3D dcZP = new Vector3D( vy, -vx,   0);
  1130.         final Vector3D dcXV = new Vector3D(  0,  -z,   y);
  1131.         final Vector3D dcYV = new Vector3D(  z,   0,  -x);
  1132.         final Vector3D dcZV = new Vector3D( -y,   x,   0);
  1133.         final Vector3D dCP  = new Vector3D(mx * oOm, dcXP, my * oOm, dcYP, mz * oOm, dcZP);
  1134.         final Vector3D dCV  = new Vector3D(mx * oOm, dcXV, my * oOm, dcYV, mz * oOm, dcZV);

  1135.         // dp
  1136.         final double mOMu   = m / mu;
  1137.         final Vector3D dpP  = new Vector3D(2 * mOMu, dCP);
  1138.         final Vector3D dpV  = new Vector3D(2 * mOMu, dCV);

  1139.         // de
  1140.         final double p      = m * mOMu;
  1141.         final double moO2ae = 1 / (2 * absA * e);
  1142.         final double m2OaMu = -p / absA;
  1143.         fillHalfRow(moO2ae, dpP, m2OaMu * moO2ae, vectorAR,    jacobian[1], 0);
  1144.         fillHalfRow(moO2ae, dpV, m2OaMu * moO2ae, vectorARDot, jacobian[1], 3);

  1145.         // di
  1146.         final double cI1 = 1 / (m * sinI);
  1147.         final double cI2 = cosI * cI1;
  1148.         fillHalfRow(cI2, dCP, -cI1, dcZP, jacobian[2], 0);
  1149.         fillHalfRow(cI2, dCV, -cI1, dcZV, jacobian[2], 3);

  1150.         // dPA
  1151.         final double cP1     =  y * oOm;
  1152.         final double cP2     = -x * oOm;
  1153.         final double cP3     = -(mx * cP1 + my * cP2);
  1154.         final double cP4     = cP3 * oOm;
  1155.         final double cP5     = -1 / (r2 * sinI * sinI);
  1156.         final double cP6     = z  * cP5;
  1157.         final double cP7     = cP3 * cP5;
  1158.         final Vector3D dacP  = new Vector3D(cP1, dcXP, cP2, dcYP, cP4, dCP, oOm, new Vector3D(-my, mx, 0));
  1159.         final Vector3D dacV  = new Vector3D(cP1, dcXV, cP2, dcYV, cP4, dCV);
  1160.         final Vector3D dpoP  = new Vector3D(cP6, dacP, cP7, Vector3D.PLUS_K);
  1161.         final Vector3D dpoV  = new Vector3D(cP6, dacV);

  1162.         final double re2     = r2 * e * e;
  1163.         final double recOre2 = (p - r) / re2;
  1164.         final double resOre2 = (pv * mOMu) / re2;
  1165.         final Vector3D dreP  = new Vector3D(mOMu, velocity, pv / mu, dCP);
  1166.         final Vector3D dreV  = new Vector3D(mOMu, position, pv / mu, dCV);
  1167.         final Vector3D davP  = new Vector3D(-resOre2, dpP, recOre2, dreP, resOre2 / r, position);
  1168.         final Vector3D davV  = new Vector3D(-resOre2, dpV, recOre2, dreV);
  1169.         fillHalfRow(1, dpoP, -1, davP, jacobian[3], 0);
  1170.         fillHalfRow(1, dpoV, -1, davV, jacobian[3], 3);

  1171.         // dRAAN
  1172.         final double cO0 = cI1 * cI1;
  1173.         final double cO1 =  mx * cO0;
  1174.         final double cO2 = -my * cO0;
  1175.         fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0);
  1176.         fillHalfRow(cO1, dcYV, cO2, dcXV, jacobian[4], 3);

  1177.         // dM
  1178.         final double s2a    = pv / (2 * absA);
  1179.         final double oObux  = 1 / FastMath.sqrt(m * m + mu * absA);
  1180.         final double scasbu = pv * oObux;
  1181.         final Vector3D dauP = new Vector3D(1 / sqrtMuA, velocity, -s2a / sqrtMuA, vectorAR);
  1182.         final Vector3D dauV = new Vector3D(1 / sqrtMuA, position, -s2a / sqrtMuA, vectorARDot);
  1183.         final Vector3D dbuP = new Vector3D(oObux * mu / 2, vectorAR,    m * oObux, dCP);
  1184.         final Vector3D dbuV = new Vector3D(oObux * mu / 2, vectorARDot, m * oObux, dCV);
  1185.         final Vector3D dcuP = new Vector3D(oObux, velocity, -scasbu * oObux, dbuP);
  1186.         final Vector3D dcuV = new Vector3D(oObux, position, -scasbu * oObux, dbuV);
  1187.         fillHalfRow(1, dauP, -e / (1 + rOa), dcuP, jacobian[5], 0);
  1188.         fillHalfRow(1, dauV, -e / (1 + rOa), dcuV, jacobian[5], 3);

  1189.         return jacobian;

  1190.     }

  1191.     /** {@inheritDoc} */
  1192.     @Override
  1193.     protected double[][] computeJacobianEccentricWrtCartesian() {
  1194.         if (isElliptical()) {
  1195.             return computeJacobianEccentricWrtCartesianElliptical();
  1196.         } else {
  1197.             return computeJacobianEccentricWrtCartesianHyperbolic();
  1198.         }
  1199.     }

  1200.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1201.      * <p>
  1202.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1203.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1204.      * yDot for j=4, zDot for j=5).
  1205.      * </p>
  1206.      * @return 6x6 Jacobian matrix
  1207.      */
  1208.     private double[][] computeJacobianEccentricWrtCartesianElliptical() {

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

  1211.         // Differentiating the Kepler equation M = E - e sin E leads to:
  1212.         // dM = (1 - e cos E) dE - sin E de
  1213.         // which is inverted and rewritten as:
  1214.         // dE = a/r dM + sin E a/r de
  1215.         final SinCos scE              = FastMath.sinCos(getEccentricAnomaly());
  1216.         final double aOr              = 1 / (1 - e * scE.cos());

  1217.         // update anomaly row
  1218.         final double[] eRow           = jacobian[1];
  1219.         final double[] anomalyRow     = jacobian[5];
  1220.         for (int j = 0; j < anomalyRow.length; ++j) {
  1221.             anomalyRow[j] = aOr * (anomalyRow[j] + scE.sin() * eRow[j]);
  1222.         }

  1223.         return jacobian;

  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[][] computeJacobianEccentricWrtCartesianHyperbolic() {

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

  1236.         // Differentiating the Kepler equation M = e sinh H - H leads to:
  1237.         // dM = (e cosh H - 1) dH + sinh H de
  1238.         // which is inverted and rewritten as:
  1239.         // dH = 1 / (e cosh H - 1) dM - sinh H / (e cosh H - 1) de
  1240.         final double H      = getEccentricAnomaly();
  1241.         final double coshH  = FastMath.cosh(H);
  1242.         final double sinhH  = FastMath.sinh(H);
  1243.         final double absaOr = 1 / (e * coshH - 1);

  1244.         // update anomaly row
  1245.         final double[] eRow       = jacobian[1];
  1246.         final double[] anomalyRow = jacobian[5];
  1247.         for (int j = 0; j < anomalyRow.length; ++j) {
  1248.             anomalyRow[j] = absaOr * (anomalyRow[j] - sinhH * eRow[j]);
  1249.         }

  1250.         return jacobian;

  1251.     }

  1252.     /** {@inheritDoc} */
  1253.     @Override
  1254.     protected double[][] computeJacobianTrueWrtCartesian() {
  1255.         if (isElliptical()) {
  1256.             return computeJacobianTrueWrtCartesianElliptical();
  1257.         } else {
  1258.             return computeJacobianTrueWrtCartesianHyperbolic();
  1259.         }
  1260.     }

  1261.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1262.      * <p>
  1263.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1264.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1265.      * yDot for j=4, zDot for j=5).
  1266.      * </p>
  1267.      * @return 6x6 Jacobian matrix
  1268.      */
  1269.     private double[][] computeJacobianTrueWrtCartesianElliptical() {

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

  1272.         // Differentiating the eccentric anomaly equation sin E = sqrt(1-e^2) sin v / (1 + e cos v)
  1273.         // and using cos E = (e + cos v) / (1 + e cos v) to get rid of cos E leads to:
  1274.         // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de
  1275.         // which is inverted and rewritten as:
  1276.         // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de
  1277.         final double e2           = e * e;
  1278.         final double oMe2         = 1 - e2;
  1279.         final double epsilon      = FastMath.sqrt(oMe2);
  1280.         final SinCos scE          = FastMath.sinCos(getEccentricAnomaly());
  1281.         final double aOr          = 1 / (1 - e * scE.cos());
  1282.         final double aFactor      = epsilon * aOr;
  1283.         final double eFactor      = scE.sin() * aOr / epsilon;

  1284.         // update anomaly row
  1285.         final double[] eRow       = jacobian[1];
  1286.         final double[] anomalyRow = jacobian[5];
  1287.         for (int j = 0; j < anomalyRow.length; ++j) {
  1288.             anomalyRow[j] = aFactor * anomalyRow[j] + eFactor * eRow[j];
  1289.         }

  1290.         return jacobian;

  1291.     }

  1292.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  1293.      * <p>
  1294.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  1295.      * respect to Cartesian coordinate j (x for j=0, y for j=1, z for j=2, xDot for j=3,
  1296.      * yDot for j=4, zDot for j=5).
  1297.      * </p>
  1298.      * @return 6x6 Jacobian matrix
  1299.      */
  1300.     private double[][] computeJacobianTrueWrtCartesianHyperbolic() {

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

  1303.         // Differentiating the eccentric anomaly equation sinh H = sqrt(e^2-1) sin v / (1 + e cos v)
  1304.         // and using cosh H = (e + cos v) / (1 + e cos v) to get rid of cosh H leads to:
  1305.         // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de
  1306.         // which is inverted and rewritten as:
  1307.         // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de
  1308.         final double e2       = e * e;
  1309.         final double e2Mo     = e2 - 1;
  1310.         final double epsilon  = FastMath.sqrt(e2Mo);
  1311.         final double H        = getEccentricAnomaly();
  1312.         final double coshH    = FastMath.cosh(H);
  1313.         final double sinhH    = FastMath.sinh(H);
  1314.         final double aOr      = 1 / (e * coshH - 1);
  1315.         final double aFactor  = epsilon * aOr;
  1316.         final double eFactor  = sinhH * aOr / epsilon;

  1317.         // update anomaly row
  1318.         final double[] eRow           = jacobian[1];
  1319.         final double[] anomalyRow     = jacobian[5];
  1320.         for (int j = 0; j < anomalyRow.length; ++j) {
  1321.             anomalyRow[j] = aFactor * anomalyRow[j] - eFactor * eRow[j];
  1322.         }

  1323.         return jacobian;

  1324.     }

  1325.     /** {@inheritDoc} */
  1326.     @Override
  1327.     public void addKeplerContribution(final PositionAngleType type, final double gm,
  1328.                                       final double[] pDot) {
  1329.         pDot[5] += computeKeplerianAnomalyDot(type, a, e, gm, cachedAnomaly, cachedPositionAngleType);
  1330.     }

  1331.     /**
  1332.      * Compute rate of argument of latitude.
  1333.      * @param type position angle type of rate
  1334.      * @param a semi major axis
  1335.      * @param e eccentricity
  1336.      * @param mu mu
  1337.      * @param anomaly anomaly
  1338.      * @param cachedType position angle type of passed anomaly
  1339.      * @return first-order time derivative for anomaly
  1340.      * @since 12.2
  1341.      */
  1342.     private static double computeKeplerianAnomalyDot(final PositionAngleType type, final double a, final double e,
  1343.                                                      final double mu, final double anomaly, final PositionAngleType cachedType) {
  1344.         final double absA = FastMath.abs(a);
  1345.         final double n    = FastMath.sqrt(mu / absA) / absA;
  1346.         if (type == PositionAngleType.MEAN) {
  1347.             return n;
  1348.         }
  1349.         final double oMe2 = FastMath.abs(1 - e * e);
  1350.         final double ksi = 1 + e * FastMath.cos(KeplerianAnomalyUtility.convertAnomaly(cachedType, anomaly, e, PositionAngleType.TRUE));
  1351.         if (type == PositionAngleType.ECCENTRIC) {
  1352.             return n * ksi / oMe2;
  1353.         } else { // TRUE
  1354.             return n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
  1355.         }
  1356.     }

  1357.     /**  Returns a string representation of this Keplerian parameters object.
  1358.      * @return a string representation of this object
  1359.      */
  1360.     public String toString() {
  1361.         return new StringBuilder().append("Keplerian parameters: ").append('{').
  1362.                 append("a: ").append(a).
  1363.                 append("; e: ").append(e).
  1364.                 append("; i: ").append(FastMath.toDegrees(i)).
  1365.                 append("; pa: ").append(FastMath.toDegrees(pa)).
  1366.                 append("; raan: ").append(FastMath.toDegrees(raan)).
  1367.                 append("; v: ").append(FastMath.toDegrees(getTrueAnomaly())).
  1368.                 append(";}").toString();
  1369.     }

  1370.     /** {@inheritDoc} */
  1371.     @Override
  1372.     public PositionAngleType getCachedPositionAngleType() {
  1373.         return cachedPositionAngleType;
  1374.     }

  1375.     /** {@inheritDoc} */
  1376.     @Override
  1377.     public boolean hasNonKeplerianRates() {
  1378.         return hasNonKeplerianAcceleration();
  1379.     }

  1380.     /** {@inheritDoc} */
  1381.     @Override
  1382.     public KeplerianOrbit withKeplerianRates() {
  1383.         final PositionAngleType positionAngleType = getCachedPositionAngleType();
  1384.         return new KeplerianOrbit(a, e, i, pa, raan, cachedAnomaly, positionAngleType, positionAngleType,
  1385.                 getFrame(), getDate(), getMu());
  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. }