EquinoctialOrbit.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.OrekitIllegalArgumentException;
  31. import org.orekit.errors.OrekitInternalError;
  32. import org.orekit.errors.OrekitMessages;
  33. import org.orekit.frames.Frame;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.utils.PVCoordinates;
  36. import org.orekit.utils.TimeStampedPVCoordinates;


  37. /**
  38.  * This class handles equinoctial orbital parameters, which can support both
  39.  * circular and equatorial orbits.
  40.  * <p>
  41.  * The parameters used internally are the equinoctial elements which can be
  42.  * related to Keplerian elements as follows:
  43.  *   <pre>
  44.  *     a
  45.  *     ex = e cos(ω + Ω)
  46.  *     ey = e sin(ω + Ω)
  47.  *     hx = tan(i/2) cos(Ω)
  48.  *     hy = tan(i/2) sin(Ω)
  49.  *     lv = v + ω + Ω
  50.  *   </pre>
  51.  * where ω stands for the Perigee Argument and Ω stands for the
  52.  * Right Ascension of the Ascending Node.
  53.  *
  54.  * <p>
  55.  * The conversion equations from and to Keplerian elements given above hold only
  56.  * when both sides are unambiguously defined, i.e. when orbit is neither equatorial
  57.  * nor circular. When orbit is either equatorial or circular, the equinoctial
  58.  * parameters are still unambiguously defined whereas some Keplerian elements
  59.  * (more precisely ω and Ω) become ambiguous. For this reason, equinoctial
  60.  * parameters are the recommended way to represent orbits.
  61.  * </p>
  62.  * <p>
  63.  * The instance <code>EquinoctialOrbit</code> is guaranteed to be immutable.
  64.  * </p>
  65.  * @see    Orbit
  66.  * @see    KeplerianOrbit
  67.  * @see    CircularOrbit
  68.  * @see    CartesianOrbit
  69.  * @author Mathieu Rom&eacute;ro
  70.  * @author Luc Maisonobe
  71.  * @author Guylaine Prat
  72.  * @author Fabien Maussion
  73.  * @author V&eacute;ronique Pommier-Maurussane
  74.  */
  75. public class EquinoctialOrbit extends Orbit {

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

  78.     /** Semi-major axis (m). */
  79.     private final double a;

  80.     /** First component of the eccentricity vector. */
  81.     private final double ex;

  82.     /** Second component of the eccentricity vector. */
  83.     private final double ey;

  84.     /** First component of the inclination vector. */
  85.     private final double hx;

  86.     /** Second component of the inclination vector. */
  87.     private final double hy;

  88.     /** True longitude argument (rad). */
  89.     private final double lv;

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

  92.     /** First component of the eccentricity vector derivative. */
  93.     private final double exDot;

  94.     /** Second component of the eccentricity vector derivative. */
  95.     private final double eyDot;

  96.     /** First component of the inclination vector derivative. */
  97.     private final double hxDot;

  98.     /** Second component of the inclination vector derivative. */
  99.     private final double hyDot;

  100.     /** True longitude argument derivative (rad/s). */
  101.     private final double lvDot;

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

  104.     /** Creates a new instance.
  105.      * @param a  semi-major axis (m)
  106.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  107.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  108.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  109.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  110.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  111.      * @param type type of longitude argument
  112.      * @param frame the frame in which the parameters are defined
  113.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  114.      * @param date date of the orbital parameters
  115.      * @param mu central attraction coefficient (m³/s²)
  116.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  117.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  118.      */
  119.     public EquinoctialOrbit(final double a, final double ex, final double ey,
  120.                             final double hx, final double hy, final double l,
  121.                             final PositionAngle type,
  122.                             final Frame frame, final AbsoluteDate date, final double mu)
  123.         throws IllegalArgumentException {
  124.         this(a, ex, ey, hx, hy, l,
  125.              Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN,
  126.              type, frame, date, mu);
  127.     }

  128.     /** Creates a new instance.
  129.      * @param a  semi-major axis (m)
  130.      * @param ex e cos(ω + Ω), first component of eccentricity vector
  131.      * @param ey e sin(ω + Ω), second component of eccentricity vector
  132.      * @param hx tan(i/2) cos(Ω), first component of inclination vector
  133.      * @param hy tan(i/2) sin(Ω), second component of inclination vector
  134.      * @param l  (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad)
  135.      * @param aDot  semi-major axis derivative (m/s)
  136.      * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative
  137.      * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative
  138.      * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative
  139.      * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative
  140.      * @param lDot  d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument  derivative (rad/s)
  141.      * @param type type of longitude argument
  142.      * @param frame the frame in which the parameters are defined
  143.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  144.      * @param date date of the orbital parameters
  145.      * @param mu central attraction coefficient (m³/s²)
  146.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  147.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  148.      */
  149.     public EquinoctialOrbit(final double a, final double ex, final double ey,
  150.                             final double hx, final double hy, final double l,
  151.                             final double aDot, final double exDot, final double eyDot,
  152.                             final double hxDot, final double hyDot, final double lDot,
  153.                             final PositionAngle type,
  154.                             final Frame frame, final AbsoluteDate date, final double mu)
  155.         throws IllegalArgumentException {
  156.         super(frame, date, mu);
  157.         if (ex * ex + ey * ey >= 1.0) {
  158.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  159.                                                      getClass().getName());
  160.         }
  161.         this.a     = a;
  162.         this.aDot  = aDot;
  163.         this.ex    = ex;
  164.         this.exDot = exDot;
  165.         this.ey    = ey;
  166.         this.eyDot = eyDot;
  167.         this.hx    = hx;
  168.         this.hxDot = hxDot;
  169.         this.hy    = hy;
  170.         this.hyDot = hyDot;

  171.         if (hasDerivatives()) {
  172.             final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
  173.             final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
  174.             final UnivariateDerivative1 lUD  = new UnivariateDerivative1(l,  lDot);
  175.             final UnivariateDerivative1 lvUD;
  176.             switch (type) {
  177.                 case MEAN :
  178.                     lvUD = FieldEquinoctialOrbit.eccentricToTrue(FieldEquinoctialOrbit.meanToEccentric(lUD, exUD, eyUD), exUD, eyUD);
  179.                     break;
  180.                 case ECCENTRIC :
  181.                     lvUD = FieldEquinoctialOrbit.eccentricToTrue(lUD, exUD, eyUD);
  182.                     break;
  183.                 case TRUE :
  184.                     lvUD = lUD;
  185.                     break;
  186.                 default : // this should never happen
  187.                     throw new OrekitInternalError(null);
  188.             }
  189.             this.lv    = lvUD.getValue();
  190.             this.lvDot = lvUD.getDerivative(1);
  191.         } else {
  192.             switch (type) {
  193.                 case MEAN :
  194.                     this.lv = eccentricToTrue(meanToEccentric(l, ex, ey), ex, ey);
  195.                     break;
  196.                 case ECCENTRIC :
  197.                     this.lv = eccentricToTrue(l, ex, ey);
  198.                     break;
  199.                 case TRUE :
  200.                     this.lv = l;
  201.                     break;
  202.                 default : // this should never happen
  203.                     throw new OrekitInternalError(null);
  204.             }
  205.             this.lvDot = Double.NaN;
  206.         }

  207.         this.partialPV = null;

  208.     }

  209.     /** Constructor from Cartesian parameters.
  210.      *
  211.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  212.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  213.      * use {@code mu} and the position to compute the acceleration, including
  214.      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
  215.      *
  216.      * @param pvCoordinates the position, velocity and acceleration
  217.      * @param frame the frame in which are defined the {@link PVCoordinates}
  218.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  219.      * @param mu central attraction coefficient (m³/s²)
  220.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  221.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  222.      */
  223.     public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates,
  224.                             final Frame frame, final double mu)
  225.         throws IllegalArgumentException {
  226.         super(pvCoordinates, frame, mu);

  227.         //  compute semi-major axis
  228.         final Vector3D pvP   = pvCoordinates.getPosition();
  229.         final Vector3D pvV   = pvCoordinates.getVelocity();
  230.         final Vector3D pvA   = pvCoordinates.getAcceleration();
  231.         final double r2      = pvP.getNormSq();
  232.         final double r       = FastMath.sqrt(r2);
  233.         final double V2      = pvV.getNormSq();
  234.         final double rV2OnMu = r * V2 / mu;

  235.         if (rV2OnMu > 2) {
  236.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  237.                                                      getClass().getName());
  238.         }

  239.         // compute inclination vector
  240.         final Vector3D w = pvCoordinates.getMomentum().normalize();
  241.         final double d = 1.0 / (1 + w.getZ());
  242.         hx = -d * w.getY();
  243.         hy =  d * w.getX();

  244.         // compute true longitude argument
  245.         final double cLv = (pvP.getX() - d * pvP.getZ() * w.getX()) / r;
  246.         final double sLv = (pvP.getY() - d * pvP.getZ() * w.getY()) / r;
  247.         lv = FastMath.atan2(sLv, cLv);


  248.         // compute semi-major axis
  249.         a = r / (2 - rV2OnMu);

  250.         // compute eccentricity vector
  251.         final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(mu * a);
  252.         final double eCE = rV2OnMu - 1;
  253.         final double e2  = eCE * eCE + eSE * eSE;
  254.         final double f   = eCE - e2;
  255.         final double g   = FastMath.sqrt(1 - e2) * eSE;
  256.         ex = a * (f * cLv + g * sLv) / r;
  257.         ey = a * (f * sLv - g * cLv) / r;

  258.         partialPV = pvCoordinates;

  259.         if (hasNonKeplerianAcceleration(pvCoordinates, mu)) {
  260.             // we have a relevant acceleration, we can compute derivatives

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

  263.             final Vector3D keplerianAcceleration    = new Vector3D(-mu / (r * r2), pvP);
  264.             final Vector3D nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  265.             final double   aX                       = nonKeplerianAcceleration.getX();
  266.             final double   aY                       = nonKeplerianAcceleration.getY();
  267.             final double   aZ                       = nonKeplerianAcceleration.getZ();
  268.             aDot  = jacobian[0][3] * aX + jacobian[0][4] * aY + jacobian[0][5] * aZ;
  269.             exDot = jacobian[1][3] * aX + jacobian[1][4] * aY + jacobian[1][5] * aZ;
  270.             eyDot = jacobian[2][3] * aX + jacobian[2][4] * aY + jacobian[2][5] * aZ;
  271.             hxDot = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ;
  272.             hyDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ;

  273.             // in order to compute true anomaly derivative, we must compute
  274.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  275.             final double lMDot = getKeplerianMeanMotion() +
  276.                                  jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ;
  277.             final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
  278.             final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
  279.             final UnivariateDerivative1 lMUD = new UnivariateDerivative1(getLM(), lMDot);
  280.             final UnivariateDerivative1 lvUD = FieldEquinoctialOrbit.eccentricToTrue(FieldEquinoctialOrbit.meanToEccentric(lMUD, exUD, eyUD), exUD, eyUD);
  281.             lvDot = lvUD.getDerivative(1);

  282.         } else {
  283.             // acceleration is either almost zero or NaN,
  284.             // we assume acceleration was not known
  285.             // we don't set up derivatives
  286.             aDot  = Double.NaN;
  287.             exDot = Double.NaN;
  288.             eyDot = Double.NaN;
  289.             hxDot = Double.NaN;
  290.             hyDot = Double.NaN;
  291.             lvDot = Double.NaN;
  292.         }

  293.     }

  294.     /** Constructor from Cartesian parameters.
  295.      *
  296.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  297.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  298.      * use {@code mu} and the position to compute the acceleration, including
  299.      * {@link #shiftedBy(double)} and {@link #getPVCoordinates(AbsoluteDate, Frame)}.
  300.      *
  301.      * @param pvCoordinates the position end velocity
  302.      * @param frame the frame in which are defined the {@link PVCoordinates}
  303.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  304.      * @param date date of the orbital parameters
  305.      * @param mu central attraction coefficient (m³/s²)
  306.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  307.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  308.      */
  309.     public EquinoctialOrbit(final PVCoordinates pvCoordinates, final Frame frame,
  310.                             final AbsoluteDate date, final double mu)
  311.         throws IllegalArgumentException {
  312.         this(new TimeStampedPVCoordinates(date, pvCoordinates), frame, mu);
  313.     }

  314.     /** Constructor from any kind of orbital parameters.
  315.      * @param op orbital parameters to copy
  316.      */
  317.     public EquinoctialOrbit(final Orbit op) {
  318.         super(op.getFrame(), op.getDate(), op.getMu());
  319.         a         = op.getA();
  320.         aDot      = op.getADot();
  321.         ex        = op.getEquinoctialEx();
  322.         exDot     = op.getEquinoctialExDot();
  323.         ey        = op.getEquinoctialEy();
  324.         eyDot     = op.getEquinoctialEyDot();
  325.         hx        = op.getHx();
  326.         hxDot     = op.getHxDot();
  327.         hy        = op.getHy();
  328.         hyDot     = op.getHyDot();
  329.         lv        = op.getLv();
  330.         lvDot     = op.getLvDot();
  331.         partialPV = null;
  332.     }

  333.     /** {@inheritDoc} */
  334.     public OrbitType getType() {
  335.         return OrbitType.EQUINOCTIAL;
  336.     }

  337.     /** {@inheritDoc} */
  338.     public double getA() {
  339.         return a;
  340.     }

  341.     /** {@inheritDoc} */
  342.     public double getADot() {
  343.         return aDot;
  344.     }

  345.     /** {@inheritDoc} */
  346.     public double getEquinoctialEx() {
  347.         return ex;
  348.     }

  349.     /** {@inheritDoc} */
  350.     public double getEquinoctialExDot() {
  351.         return exDot;
  352.     }

  353.     /** {@inheritDoc} */
  354.     public double getEquinoctialEy() {
  355.         return ey;
  356.     }

  357.     /** {@inheritDoc} */
  358.     public double getEquinoctialEyDot() {
  359.         return eyDot;
  360.     }

  361.     /** {@inheritDoc} */
  362.     public double getHx() {
  363.         return hx;
  364.     }

  365.     /** {@inheritDoc} */
  366.     public double getHxDot() {
  367.         return hxDot;
  368.     }

  369.     /** {@inheritDoc} */
  370.     public double getHy() {
  371.         return hy;
  372.     }

  373.     /** {@inheritDoc} */
  374.     public double getHyDot() {
  375.         return hyDot;
  376.     }

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

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

  385.     /** {@inheritDoc} */
  386.     public double getLE() {
  387.         return trueToEccentric(lv, ex, ey);
  388.     }

  389.     /** {@inheritDoc} */
  390.     public double getLEDot() {
  391.         final UnivariateDerivative1 lVUD = new UnivariateDerivative1(lv, lvDot);
  392.         final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
  393.         final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
  394.         final UnivariateDerivative1 lEUD = FieldEquinoctialOrbit.trueToEccentric(lVUD, exUD, eyUD);
  395.         return lEUD.getDerivative(1);
  396.     }

  397.     /** {@inheritDoc} */
  398.     public double getLM() {
  399.         return eccentricToMean(trueToEccentric(lv, ex, ey), ex, ey);
  400.     }

  401.     /** {@inheritDoc} */
  402.     public double getLMDot() {
  403.         final UnivariateDerivative1 lVUD = new UnivariateDerivative1(lv, lvDot);
  404.         final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot);
  405.         final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot);
  406.         final UnivariateDerivative1 lMUD = FieldEquinoctialOrbit.eccentricToMean(FieldEquinoctialOrbit.trueToEccentric(lVUD, exUD, eyUD), exUD, eyUD);
  407.         return lMUD.getDerivative(1);
  408.     }

  409.     /** Get the longitude argument.
  410.      * @param type type of the angle
  411.      * @return longitude argument (rad)
  412.      */
  413.     public double getL(final PositionAngle type) {
  414.         return (type == PositionAngle.MEAN) ? getLM() :
  415.                                               ((type == PositionAngle.ECCENTRIC) ? getLE() :
  416.                                                                                    getLv());
  417.     }

  418.     /** Get the longitude argument derivative.
  419.      * @param type type of the angle
  420.      * @return longitude argument derivative (rad/s)
  421.      */
  422.     public double getLDot(final PositionAngle type) {
  423.         return (type == PositionAngle.MEAN) ? getLMDot() :
  424.                                               ((type == PositionAngle.ECCENTRIC) ? getLEDot() :
  425.                                                                                    getLvDot());
  426.     }

  427.     /** Computes the true longitude argument from the eccentric longitude argument.
  428.      * @param lE = E + ω + Ω eccentric longitude argument (rad)
  429.      * @param ex first component of the eccentricity vector
  430.      * @param ey second component of the eccentricity vector
  431.      * @return the true longitude argument
  432.      */
  433.     public static double eccentricToTrue(final double lE, final double ex, final double ey) {
  434.         final double epsilon = FastMath.sqrt(1 - ex * ex - ey * ey);
  435.         final SinCos scLE    = FastMath.sinCos(lE);
  436.         final double num     = ex * scLE.sin() - ey * scLE.cos();
  437.         final double den     = epsilon + 1 - ex * scLE.cos() - ey * scLE.sin();
  438.         return lE + 2 * FastMath.atan(num / den);
  439.     }

  440.     /** Computes the eccentric longitude argument from the true longitude argument.
  441.      * @param lv = v + ω + Ω true longitude argument (rad)
  442.      * @param ex first component of the eccentricity vector
  443.      * @param ey second component of the eccentricity vector
  444.      * @return the eccentric longitude argument
  445.      */
  446.     public static double trueToEccentric(final double lv, final double ex, final double ey) {
  447.         final double epsilon = FastMath.sqrt(1 - ex * ex - ey * ey);
  448.         final SinCos scLv    = FastMath.sinCos(lv);
  449.         final double num     = ey * scLv.cos() - ex * scLv.sin();
  450.         final double den     = epsilon + 1 + ex * scLv.cos() + ey * scLv.sin();
  451.         return lv + 2 * FastMath.atan(num / den);
  452.     }

  453.     /** Computes the eccentric longitude argument from the mean longitude argument.
  454.      * @param lM = M + ω + Ω mean longitude argument (rad)
  455.      * @param ex first component of the eccentricity vector
  456.      * @param ey second component of the eccentricity vector
  457.      * @return the eccentric longitude argument
  458.      */
  459.     public static double meanToEccentric(final double lM, final double ex, final double ey) {
  460.         // Generalization of Kepler equation to equinoctial parameters
  461.         // with lE = PA + RAAN + E and
  462.         //      lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE)
  463.         double lE = lM;
  464.         double shift = 0.0;
  465.         double lEmlM = 0.0;
  466.         SinCos scLE  = FastMath.sinCos(lE);
  467.         int iter = 0;
  468.         do {
  469.             final double f2 = ex * scLE.sin() - ey * scLE.cos();
  470.             final double f1 = 1.0 - ex * scLE.cos() - ey * scLE.sin();
  471.             final double f0 = lEmlM - f2;

  472.             final double f12 = 2.0 * f1;
  473.             shift = f0 * f12 / (f1 * f12 - f0 * f2);

  474.             lEmlM -= shift;
  475.             lE     = lM + lEmlM;
  476.             scLE   = FastMath.sinCos(lE);

  477.         } while (++iter < 50 && FastMath.abs(shift) > 1.0e-12);

  478.         return lE;

  479.     }

  480.     /** Computes the mean longitude argument from the eccentric longitude argument.
  481.      * @param lE = E + ω + Ω mean longitude argument (rad)
  482.      * @param ex first component of the eccentricity vector
  483.      * @param ey second component of the eccentricity vector
  484.      * @return the mean longitude argument
  485.      */
  486.     public static double eccentricToMean(final double lE, final double ex, final double ey) {
  487.         final SinCos scLE = FastMath.sinCos(lE);
  488.         return lE - ex * scLE.sin() + ey * scLE.cos();
  489.     }

  490.     /** {@inheritDoc} */
  491.     public double getE() {
  492.         return FastMath.sqrt(ex * ex + ey * ey);
  493.     }

  494.     /** {@inheritDoc} */
  495.     public double getEDot() {
  496.         return (ex * exDot + ey * eyDot) / FastMath.sqrt(ex * ex + ey * ey);
  497.     }

  498.     /** {@inheritDoc} */
  499.     public double getI() {
  500.         return 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy));
  501.     }

  502.     /** {@inheritDoc} */
  503.     public double getIDot() {
  504.         final double h2 = hx * hx + hy * hy;
  505.         final double h  = FastMath.sqrt(h2);
  506.         return 2 * (hx * hxDot + hy * hyDot) / (h * (1 + h2));
  507.     }

  508.     /** Compute position and velocity but not acceleration.
  509.      */
  510.     private void computePVWithoutA() {

  511.         if (partialPV != null) {
  512.             // already computed
  513.             return;
  514.         }

  515.         // get equinoctial parameters
  516.         final double lE = getLE();

  517.         // inclination-related intermediate parameters
  518.         final double hx2   = hx * hx;
  519.         final double hy2   = hy * hy;
  520.         final double factH = 1. / (1 + hx2 + hy2);

  521.         // reference axes defining the orbital plane
  522.         final double ux = (1 + hx2 - hy2) * factH;
  523.         final double uy =  2 * hx * hy * factH;
  524.         final double uz = -2 * hy * factH;

  525.         final double vx = uy;
  526.         final double vy = (1 - hx2 + hy2) * factH;
  527.         final double vz =  2 * hx * factH;

  528.         // eccentricity-related intermediate parameters
  529.         final double exey = ex * ey;
  530.         final double ex2  = ex * ex;
  531.         final double ey2  = ey * ey;
  532.         final double e2   = ex2 + ey2;
  533.         final double eta  = 1 + FastMath.sqrt(1 - e2);
  534.         final double beta = 1. / eta;

  535.         // eccentric longitude argument
  536.         final SinCos scLe   = FastMath.sinCos(lE);
  537.         final double cLe    = scLe.cos();
  538.         final double sLe    = scLe.sin();
  539.         final double exCeyS = ex * cLe + ey * sLe;

  540.         // coordinates of position and velocity in the orbital plane
  541.         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - ex);
  542.         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - ey);

  543.         final double factor = FastMath.sqrt(getMu() / a) / (1 - exCeyS);
  544.         final double xdot   = factor * (-sLe + beta * ey * exCeyS);
  545.         final double ydot   = factor * ( cLe - beta * ex * exCeyS);

  546.         final Vector3D position =
  547.                         new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
  548.         final Vector3D velocity =
  549.                         new Vector3D(xdot * ux + ydot * vx, xdot * uy + ydot * vy, xdot * uz + ydot * vz);
  550.         partialPV = new PVCoordinates(position, velocity);

  551.     }

  552.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  553.      * <p>
  554.      * This method should be called only when {@link #hasDerivatives()} returns true.
  555.      * </p>
  556.      * @return non-Keplerian part of the acceleration
  557.      */
  558.     private Vector3D nonKeplerianAcceleration() {

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

  561.         final double nonKeplerianMeanMotion = getLMDot() - getKeplerianMeanMotion();
  562.         final double nonKeplerianAx = dCdP[3][0] * aDot  + dCdP[3][1] * exDot + dCdP[3][2] * eyDot +
  563.                                       dCdP[3][3] * hxDot + dCdP[3][4] * hyDot + dCdP[3][5] * nonKeplerianMeanMotion;
  564.         final double nonKeplerianAy = dCdP[4][0] * aDot  + dCdP[4][1] * exDot + dCdP[4][2] * eyDot +
  565.                                       dCdP[4][3] * hxDot + dCdP[4][4] * hyDot + dCdP[4][5] * nonKeplerianMeanMotion;
  566.         final double nonKeplerianAz = dCdP[5][0] * aDot  + dCdP[5][1] * exDot + dCdP[5][2] * eyDot +
  567.                                       dCdP[5][3] * hxDot + dCdP[5][4] * hyDot + dCdP[5][5] * nonKeplerianMeanMotion;

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

  569.     }

  570.     /** {@inheritDoc} */
  571.     protected TimeStampedPVCoordinates initPVCoordinates() {

  572.         // position and velocity
  573.         computePVWithoutA();

  574.         // acceleration
  575.         final double r2 = partialPV.getPosition().getNormSq();
  576.         final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition());
  577.         final Vector3D acceleration = hasDerivatives() ?
  578.                                       keplerianAcceleration.add(nonKeplerianAcceleration()) :
  579.                                       keplerianAcceleration;

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

  581.     }

  582.     /** {@inheritDoc} */
  583.     public EquinoctialOrbit shiftedBy(final double dt) {

  584.         // use Keplerian-only motion
  585.         final EquinoctialOrbit keplerianShifted = new EquinoctialOrbit(a, ex, ey, hx, hy,
  586.                                                                        getLM() + getKeplerianMeanMotion() * dt,
  587.                                                                        PositionAngle.MEAN, getFrame(),
  588.                                                                        getDate().shiftedBy(dt), getMu());

  589.         if (hasDerivatives()) {

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

  592.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  593.             keplerianShifted.computePVWithoutA();
  594.             final Vector3D fixedP   = new Vector3D(1, keplerianShifted.partialPV.getPosition(),
  595.                                                    0.5 * dt * dt, nonKeplerianAcceleration);
  596.             final double   fixedR2 = fixedP.getNormSq();
  597.             final double   fixedR  = FastMath.sqrt(fixedR2);
  598.             final Vector3D fixedV  = new Vector3D(1, keplerianShifted.partialPV.getVelocity(),
  599.                                                   dt, nonKeplerianAcceleration);
  600.             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(),
  601.                                                   1, nonKeplerianAcceleration);

  602.             // build a new orbit, taking non-Keplerian acceleration into account
  603.             return new EquinoctialOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(),
  604.                                                                      fixedP, fixedV, fixedA),
  605.                                         keplerianShifted.getFrame(), keplerianShifted.getMu());

  606.         } else {
  607.             // Keplerian-only motion is all we can do
  608.             return keplerianShifted;
  609.         }

  610.     }

  611.     /** {@inheritDoc}
  612.      * <p>
  613.      * The interpolated instance is created by polynomial Hermite interpolation
  614.      * on equinoctial elements, without derivatives (which means the interpolation
  615.      * falls back to Lagrange interpolation only).
  616.      * </p>
  617.      * <p>
  618.      * As this implementation of interpolation is polynomial, it should be used only
  619.      * with small samples (about 10-20 points) in order to avoid <a
  620.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  621.      * and numerical problems (including NaN appearing).
  622.      * </p>
  623.      * <p>
  624.      * If orbit interpolation on large samples is needed, using the {@link
  625.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  626.      * low-level interpolation. The Ephemeris class automatically handles selection of
  627.      * a neighboring sub-sample with a predefined number of point from a large global sample
  628.      * in a thread-safe way.
  629.      * </p>
  630.      */
  631.     public EquinoctialOrbit interpolate(final AbsoluteDate date, final Stream<Orbit> sample) {

  632.         // first pass to check if derivatives are available throughout the sample
  633.         final List<Orbit> list = sample.collect(Collectors.toList());
  634.         boolean useDerivatives = true;
  635.         for (final Orbit orbit : list) {
  636.             useDerivatives = useDerivatives && orbit.hasDerivatives();
  637.         }

  638.         // set up an interpolator
  639.         final HermiteInterpolator interpolator = new HermiteInterpolator();

  640.         // second pass to feed interpolator
  641.         AbsoluteDate previousDate = null;
  642.         double       previousLm   = Double.NaN;
  643.         for (final Orbit orbit : list) {
  644.             final EquinoctialOrbit equi = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit);
  645.             final double continuousLm;
  646.             if (previousDate == null) {
  647.                 continuousLm = equi.getLM();
  648.             } else {
  649.                 final double dt       = equi.getDate().durationFrom(previousDate);
  650.                 final double keplerLm = previousLm + equi.getKeplerianMeanMotion() * dt;
  651.                 continuousLm = MathUtils.normalizeAngle(equi.getLM(), keplerLm);
  652.             }
  653.             previousDate = equi.getDate();
  654.             previousLm   = continuousLm;
  655.             if (useDerivatives) {
  656.                 interpolator.addSamplePoint(equi.getDate().durationFrom(date),
  657.                                             new double[] {
  658.                                                 equi.getA(),
  659.                                                 equi.getEquinoctialEx(),
  660.                                                 equi.getEquinoctialEy(),
  661.                                                 equi.getHx(),
  662.                                                 equi.getHy(),
  663.                                                 continuousLm
  664.                                             },
  665.                                             new double[] {
  666.                                                 equi.getADot(),
  667.                                                 equi.getEquinoctialExDot(),
  668.                                                 equi.getEquinoctialEyDot(),
  669.                                                 equi.getHxDot(),
  670.                                                 equi.getHyDot(),
  671.                                                 equi.getLMDot()
  672.                                             });
  673.             } else {
  674.                 interpolator.addSamplePoint(equi.getDate().durationFrom(date),
  675.                                             new double[] {
  676.                                                 equi.getA(),
  677.                                                 equi.getEquinoctialEx(),
  678.                                                 equi.getEquinoctialEy(),
  679.                                                 equi.getHx(),
  680.                                                 equi.getHy(),
  681.                                                 continuousLm
  682.                                             });
  683.             }
  684.         }

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

  687.         // build a new interpolated instance
  688.         return new EquinoctialOrbit(interpolated[0][0], interpolated[0][1], interpolated[0][2],
  689.                                     interpolated[0][3], interpolated[0][4], interpolated[0][5],
  690.                                     interpolated[1][0], interpolated[1][1], interpolated[1][2],
  691.                                     interpolated[1][3], interpolated[1][4], interpolated[1][5],
  692.                                     PositionAngle.MEAN, getFrame(), date, getMu());

  693.     }

  694.     /** {@inheritDoc} */
  695.     protected double[][] computeJacobianMeanWrtCartesian() {

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

  697.         // compute various intermediate parameters
  698.         computePVWithoutA();
  699.         final Vector3D position = partialPV.getPosition();
  700.         final Vector3D velocity = partialPV.getVelocity();
  701.         final double r2         = position.getNormSq();
  702.         final double r          = FastMath.sqrt(r2);
  703.         final double r3         = r * r2;

  704.         final double mu         = getMu();
  705.         final double sqrtMuA    = FastMath.sqrt(a * mu);
  706.         final double a2         = a * a;

  707.         final double e2         = ex * ex + ey * ey;
  708.         final double oMe2       = 1 - e2;
  709.         final double epsilon    = FastMath.sqrt(oMe2);
  710.         final double beta       = 1 / (1 + epsilon);
  711.         final double ratio      = epsilon * beta;

  712.         final double hx2        = hx * hx;
  713.         final double hy2        = hy * hy;
  714.         final double hxhy       = hx * hy;

  715.         // precomputing equinoctial frame unit vectors (f, g, w)
  716.         final Vector3D f  = new Vector3D(1 - hy2 + hx2, 2 * hxhy, -2 * hy).normalize();
  717.         final Vector3D g  = new Vector3D(2 * hxhy, 1 + hy2 - hx2, 2 * hx).normalize();
  718.         final Vector3D w  = Vector3D.crossProduct(position, velocity).normalize();

  719.         // coordinates of the spacecraft in the equinoctial frame
  720.         final double x    = Vector3D.dotProduct(position, f);
  721.         final double y    = Vector3D.dotProduct(position, g);
  722.         final double xDot = Vector3D.dotProduct(velocity, f);
  723.         final double yDot = Vector3D.dotProduct(velocity, g);

  724.         // drDot / dEx = dXDot / dEx * f + dYDot / dEx * g
  725.         final double c1 = a / (sqrtMuA * epsilon);
  726.         final double c2 = a * sqrtMuA * beta / r3;
  727.         final double c3 = sqrtMuA / (r3 * epsilon);
  728.         final Vector3D drDotSdEx = new Vector3D( c1 * xDot * yDot - c2 * ey * x - c3 * x * y, f,
  729.                                                 -c1 * xDot * xDot - c2 * ey * y + c3 * x * x, g);

  730.         // drDot / dEy = dXDot / dEy * f + dYDot / dEy * g
  731.         final Vector3D drDotSdEy = new Vector3D( c1 * yDot * yDot + c2 * ex * x - c3 * y * y, f,
  732.                                                 -c1 * xDot * yDot + c2 * ex * y + c3 * x * y, g);

  733.         // da
  734.         final Vector3D vectorAR = new Vector3D(2 * a2 / r3, position);
  735.         final Vector3D vectorARDot = new Vector3D(2 * a2 / mu, velocity);
  736.         fillHalfRow(1, vectorAR,    jacobian[0], 0);
  737.         fillHalfRow(1, vectorARDot, jacobian[0], 3);

  738.         // dEx
  739.         final double d1 = -a * ratio / r3;
  740.         final double d2 = (hy * xDot - hx * yDot) / (sqrtMuA * epsilon);
  741.         final double d3 = (hx * y - hy * x) / sqrtMuA;
  742.         final Vector3D vectorExRDot =
  743.             new Vector3D((2 * x * yDot - xDot * y) / mu, g, -y * yDot / mu, f, -ey * d3 / epsilon, w);
  744.         fillHalfRow(ex * d1, position, -ey * d2, w, epsilon / sqrtMuA, drDotSdEy, jacobian[1], 0);
  745.         fillHalfRow(1, vectorExRDot, jacobian[1], 3);

  746.         // dEy
  747.         final Vector3D vectorEyRDot =
  748.             new Vector3D((2 * xDot * y - x * yDot) / mu, f, -x * xDot / mu, g, ex * d3 / epsilon, w);
  749.         fillHalfRow(ey * d1, position, ex * d2, w, -epsilon / sqrtMuA, drDotSdEx, jacobian[2], 0);
  750.         fillHalfRow(1, vectorEyRDot, jacobian[2], 3);

  751.         // dHx
  752.         final double h = (1 + hx2 + hy2) / (2 * sqrtMuA * epsilon);
  753.         fillHalfRow(-h * xDot, w, jacobian[3], 0);
  754.         fillHalfRow( h * x,    w, jacobian[3], 3);

  755.         // dHy
  756.         fillHalfRow(-h * yDot, w, jacobian[4], 0);
  757.         fillHalfRow( h * y,    w, jacobian[4], 3);

  758.         // dLambdaM
  759.         final double l = -ratio / sqrtMuA;
  760.         fillHalfRow(-1 / sqrtMuA, velocity, d2, w, l * ex, drDotSdEx, l * ey, drDotSdEy, jacobian[5], 0);
  761.         fillHalfRow(-2 / sqrtMuA, position, ex * beta, vectorEyRDot, -ey * beta, vectorExRDot, d3, w, jacobian[5], 3);

  762.         return jacobian;

  763.     }

  764.     /** {@inheritDoc} */
  765.     protected double[][] computeJacobianEccentricWrtCartesian() {

  766.         // start by computing the Jacobian with mean angle
  767.         final double[][] jacobian = computeJacobianMeanWrtCartesian();

  768.         // Differentiating the Kepler equation lM = lE - ex sin lE + ey cos lE leads to:
  769.         // dlM = (1 - ex cos lE - ey sin lE) dE - sin lE dex + cos lE dey
  770.         // which is inverted and rewritten as:
  771.         // dlE = a/r dlM + sin lE a/r dex - cos lE a/r dey
  772.         final SinCos scLe  = FastMath.sinCos(getLE());
  773.         final double cosLe = scLe.cos();
  774.         final double sinLe = scLe.sin();
  775.         final double aOr   = 1 / (1 - ex * cosLe - ey * sinLe);

  776.         // update longitude row
  777.         final double[] rowEx = jacobian[1];
  778.         final double[] rowEy = jacobian[2];
  779.         final double[] rowL  = jacobian[5];
  780.         for (int j = 0; j < 6; ++j) {
  781.             rowL[j] = aOr * (rowL[j] + sinLe * rowEx[j] - cosLe * rowEy[j]);
  782.         }

  783.         return jacobian;

  784.     }

  785.     /** {@inheritDoc} */
  786.     protected double[][] computeJacobianTrueWrtCartesian() {

  787.         // start by computing the Jacobian with eccentric angle
  788.         final double[][] jacobian = computeJacobianEccentricWrtCartesian();

  789.         // Differentiating the eccentric longitude equation
  790.         // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE]
  791.         // leads to
  792.         // cT (dlV - dlE) = cE dlE + cX dex + cY dey
  793.         // with
  794.         // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2
  795.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE
  796.         // cE = (ex cos lE + ey sin lE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  797.         // cX =  sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  798.         // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2)
  799.         // which can be solved to find the differential of the true longitude
  800.         // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX
  801.         final SinCos scLe      = FastMath.sinCos(getLE());
  802.         final double cosLe     = scLe.cos();
  803.         final double sinLe     = scLe.sin();
  804.         final double eSinE     = ex * sinLe - ey * cosLe;
  805.         final double ecosE     = ex * cosLe + ey * sinLe;
  806.         final double e2        = ex * ex + ey * ey;
  807.         final double epsilon   = FastMath.sqrt(1 - e2);
  808.         final double onePeps   = 1 + epsilon;
  809.         final double d         = onePeps - ecosE;
  810.         final double cT        = (d * d + eSinE * eSinE) / 2;
  811.         final double cE        = ecosE * onePeps - e2;
  812.         final double cX        = ex * eSinE / epsilon - ey + sinLe * onePeps;
  813.         final double cY        = ey * eSinE / epsilon + ex - cosLe * onePeps;
  814.         final double factorLe  = (cT + cE) / cT;
  815.         final double factorEx  = cX / cT;
  816.         final double factorEy  = cY / cT;

  817.         // update longitude row
  818.         final double[] rowEx = jacobian[1];
  819.         final double[] rowEy = jacobian[2];
  820.         final double[] rowL = jacobian[5];
  821.         for (int j = 0; j < 6; ++j) {
  822.             rowL[j] = factorLe * rowL[j] + factorEx * rowEx[j] + factorEy * rowEy[j];
  823.         }

  824.         return jacobian;

  825.     }

  826.     /** {@inheritDoc} */
  827.     public void addKeplerContribution(final PositionAngle type, final double gm,
  828.                                       final double[] pDot) {
  829.         final double oMe2;
  830.         final double ksi;
  831.         final double n  = FastMath.sqrt(gm / a) / a;
  832.         final SinCos sc = FastMath.sinCos(lv);
  833.         switch (type) {
  834.             case MEAN :
  835.                 pDot[5] += n;
  836.                 break;
  837.             case ECCENTRIC :
  838.                 oMe2 = 1 - ex * ex - ey * ey;
  839.                 ksi  = 1 + ex * sc.cos() + ey * sc.sin();
  840.                 pDot[5] += n * ksi / oMe2;
  841.                 break;
  842.             case TRUE :
  843.                 oMe2 = 1 - ex * ex - ey * ey;
  844.                 ksi  = 1 + ex * sc.cos() + ey * sc.sin();
  845.                 pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
  846.                 break;
  847.             default :
  848.                 throw new OrekitInternalError(null);
  849.         }
  850.     }

  851.     /**  Returns a string representation of this equinoctial parameters object.
  852.      * @return a string representation of this object
  853.      */
  854.     public String toString() {
  855.         return new StringBuilder().append("equinoctial parameters: ").append('{').
  856.                                   append("a: ").append(a).
  857.                                   append("; ex: ").append(ex).append("; ey: ").append(ey).
  858.                                   append("; hx: ").append(hx).append("; hy: ").append(hy).
  859.                                   append("; lv: ").append(FastMath.toDegrees(lv)).
  860.                                   append(";}").toString();
  861.     }

  862.     /** Replace the instance with a data transfer object for serialization.
  863.      * @return data transfer object that will be serialized
  864.      */
  865.     @DefaultDataContext
  866.     private Object writeReplace() {
  867.         return new DTO(this);
  868.     }

  869.     /** Internal class used only for serialization. */
  870.     @DefaultDataContext
  871.     private static class DTO implements Serializable {

  872.         /** Serializable UID. */
  873.         private static final long serialVersionUID = 20170414L;

  874.         /** Double values. */
  875.         private double[] d;

  876.         /** Frame in which are defined the orbital parameters. */
  877.         private final Frame frame;

  878.         /** Simple constructor.
  879.          * @param orbit instance to serialize
  880.          */
  881.         private DTO(final EquinoctialOrbit orbit) {

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

  883.             // decompose date
  884.             final AbsoluteDate j2000Epoch =
  885.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  886.             final double epoch  = FastMath.floor(pv.getDate().durationFrom(j2000Epoch));
  887.             final double offset = pv.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));

  888.             if (orbit.hasDerivatives()) {
  889.                 // we have derivatives
  890.                 this.d = new double[] {
  891.                     epoch, offset, orbit.getMu(),
  892.                     orbit.a, orbit.ex, orbit.ey,
  893.                     orbit.hx, orbit.hy, orbit.lv,
  894.                     orbit.aDot, orbit.exDot, orbit.eyDot,
  895.                     orbit.hxDot, orbit.hyDot, orbit.lvDot
  896.                 };
  897.             } else {
  898.                 // we don't have derivatives
  899.                 this.d = new double[] {
  900.                     epoch, offset, orbit.getMu(),
  901.                     orbit.a, orbit.ex, orbit.ey,
  902.                     orbit.hx, orbit.hy, orbit.lv
  903.                 };
  904.             }

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

  906.         }

  907.         /** Replace the deserialized data transfer object with a {@link EquinoctialOrbit}.
  908.          * @return replacement {@link EquinoctialOrbit}
  909.          */
  910.         private Object readResolve() {
  911.             final AbsoluteDate j2000Epoch =
  912.                     DataContext.getDefault().getTimeScales().getJ2000Epoch();
  913.             if (d.length >= 15) {
  914.                 // we have derivatives
  915.                 return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8],
  916.                                             d[ 9], d[10], d[11], d[12], d[13], d[14],
  917.                                             PositionAngle.TRUE,
  918.                                             frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  919.                                             d[2]);
  920.             } else {
  921.                 // we don't have derivatives
  922.                 return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], PositionAngle.TRUE,
  923.                                             frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
  924.                                             d[2]);
  925.             }
  926.         }

  927.     }

  928. }