FieldCircularOrbit.java

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

  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.stream.Collectors;
  22. import java.util.stream.Stream;

  23. import org.hipparchus.Field;
  24. import org.hipparchus.RealFieldElement;
  25. import org.hipparchus.analysis.differentiation.FDSFactory;
  26. import org.hipparchus.analysis.differentiation.FieldDerivativeStructure;
  27. import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
  28. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  29. import org.hipparchus.util.FastMath;
  30. import org.hipparchus.util.MathArrays;
  31. import org.orekit.errors.OrekitIllegalArgumentException;
  32. import org.orekit.errors.OrekitInternalError;
  33. import org.orekit.errors.OrekitMessages;
  34. import org.orekit.frames.Frame;
  35. import org.orekit.time.FieldAbsoluteDate;
  36. import org.orekit.utils.FieldPVCoordinates;
  37. import org.orekit.utils.TimeStampedFieldPVCoordinates;


  38. /**
  39.  * This class handles circular orbital parameters.

  40.  * <p>
  41.  * The parameters used internally are the circular elements which can be
  42.  * related to Keplerian elements as follows:
  43.  *   <ul>
  44.  *     <li>a</li>
  45.  *     <li>e<sub>x</sub> = e cos(ω)</li>
  46.  *     <li>e<sub>y</sub> = e sin(ω)</li>
  47.  *     <li>i</li>
  48.  *     <li>Ω</li>
  49.  *     <li>α<sub>v</sub> = v + ω</li>
  50.  *   </ul>
  51.  * where Ω stands for the Right Ascension of the Ascending Node and
  52.  * α<sub>v</sub> stands for the true latitude argument
  53.  * </p>
  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 circular (but not equatorial), the circular
  58.  * parameters are still unambiguously defined whereas some Keplerian elements
  59.  * (more precisely ω and Ω) become ambiguous. When orbit is equatorial,
  60.  * neither the Keplerian nor the circular parameters can be defined unambiguously.
  61.  * {@link EquinoctialOrbit equinoctial orbits} is the recommended way to represent
  62.  * orbits.
  63.  * </p>
  64.  * <p>
  65.  * The instance <code>CircularOrbit</code> is guaranteed to be immutable.
  66.  * </p>
  67.  * @see    Orbit
  68.  * @see    KeplerianOrbit
  69.  * @see    CartesianOrbit
  70.  * @see    EquinoctialOrbit
  71.  * @author Luc Maisonobe
  72.  * @author Fabien Maussion
  73.  * @author V&eacute;ronique Pommier-Maurussane
  74.  * @since 9.0
  75.  */

  76. public  class FieldCircularOrbit<T extends RealFieldElement<T>>
  77.     extends FieldOrbit<T> {

  78.     /** Factory for first time derivatives. */
  79.     private static final Map<Field<? extends RealFieldElement<?>>, FDSFactory<? extends RealFieldElement<?>>> FACTORIES =
  80.                     new HashMap<>();

  81.     /** Semi-major axis (m). */
  82.     private final T a;

  83.     /** First component of the circular eccentricity vector. */
  84.     private final T ex;

  85.     /** Second component of the circular eccentricity vector. */
  86.     private final T ey;

  87.     /** Inclination (rad). */
  88.     private final T i;

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

  91.     /** True latitude argument (rad). */
  92.     private final T alphaV;

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

  95.     /** First component of the circular eccentricity vector derivative. */
  96.     private final T exDot;

  97.     /** Second component of the circular eccentricity vector derivative. */
  98.     private final T eyDot;

  99.     /** Inclination derivative (rad/s). */
  100.     private final T iDot;

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

  103.     /** True latitude argument derivative (rad/s). */
  104.     private final T alphaVDot;

  105.     /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */
  106.     private FieldPVCoordinates<T> partialPV;

  107.     /** one. */
  108.     private final T one;

  109.     /** zero. */
  110.     private final T zero;

  111.     /** Creates a new instance.
  112.      * @param a  semi-major axis (m)
  113.      * @param ex e cos(ω), first component of circular eccentricity vector
  114.      * @param ey e sin(ω), second component of circular eccentricity vector
  115.      * @param i inclination (rad)
  116.      * @param raan right ascension of ascending node (Ω, rad)
  117.      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
  118.      * @param type type of latitude argument
  119.      * @param frame the frame in which are defined the parameters
  120.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  121.      * @param date date of the orbital parameters
  122.      * @param mu central attraction coefficient (m³/s²)
  123.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  124.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  125.      */
  126.     public FieldCircularOrbit(final T a, final T ex, final T ey,
  127.                               final T i, final T raan,
  128.                               final T alpha, final PositionAngle type,
  129.                               final Frame frame, final FieldAbsoluteDate<T> date, final double mu)
  130.         throws IllegalArgumentException {
  131.         this(a, ex, ey, i, raan, alpha,
  132.              null, null, null, null, null, null,
  133.              type, frame, date, mu);
  134.     }

  135.     /** Creates a new instance.
  136.      * @param a  semi-major axis (m)
  137.      * @param ex e cos(ω), first component of circular eccentricity vector
  138.      * @param ey e sin(ω), second component of circular eccentricity vector
  139.      * @param i inclination (rad)
  140.      * @param raan right ascension of ascending node (Ω, rad)
  141.      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
  142.      * @param aDot  semi-major axis derivative (m/s)
  143.      * @param exDot d(e cos(ω))/dt, first component of circular eccentricity vector derivative
  144.      * @param eyDot d(e sin(ω))/dt, second component of circular eccentricity vector derivative
  145.      * @param iDot inclination  derivative(rad/s)
  146.      * @param raanDot right ascension of ascending node derivative (rad/s)
  147.      * @param alphaDot  d(an + ω), mean, eccentric or true latitude argument derivative (rad/s)
  148.      * @param type type of latitude argument
  149.      * @param frame the frame in which are defined the parameters
  150.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  151.      * @param date date of the orbital parameters
  152.      * @param mu central attraction coefficient (m³/s²)
  153.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  154.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  155.      */
  156.     public FieldCircularOrbit(final T a, final T ex, final T ey,
  157.                               final T i, final T raan, final T alpha,
  158.                               final T aDot, final T exDot, final T eyDot,
  159.                               final T iDot, final T raanDot, final T alphaDot,
  160.                               final PositionAngle type,
  161.                               final Frame frame, final FieldAbsoluteDate<T> date, final double mu)
  162.         throws IllegalArgumentException {
  163.         super(frame, date, mu);
  164.         if (ex.getReal() * ex.getReal() + ey.getReal() * ey.getReal() >= 1.0) {
  165.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  166.                                                      getClass().getName());
  167.         }

  168.         if (!FACTORIES.containsKey(a.getField())) {
  169.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  170.         }

  171.         this.a       =  a;
  172.         this.aDot    =  aDot;
  173.         this.ex      = ex;
  174.         this.exDot   = exDot;
  175.         this.ey      = ey;
  176.         this.eyDot   = eyDot;
  177.         this.i       = i;
  178.         this.iDot    = iDot;
  179.         this.raan    = raan;
  180.         this.raanDot = raanDot;

  181.         one = a.getField().getOne();
  182.         zero = a.getField().getZero();

  183.         if (hasDerivatives()) {
  184.             @SuppressWarnings("unchecked")
  185.             final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  186.             final FieldDerivativeStructure<T> exDS    = factory.build(ex,    exDot);
  187.             final FieldDerivativeStructure<T> eyDS    = factory.build(ey,    eyDot);
  188.             final FieldDerivativeStructure<T> alphaDS = factory.build(alpha, alphaDot);
  189.             final FieldDerivativeStructure<T> alphavDS;
  190.             switch (type) {
  191.                 case MEAN :
  192.                     alphavDS = eccentricToTrue(meanToEccentric(alphaDS, exDS, eyDS), exDS, eyDS);
  193.                     break;
  194.                 case ECCENTRIC :
  195.                     alphavDS = eccentricToTrue(alphaDS, exDS, eyDS);
  196.                     break;
  197.                 case TRUE :
  198.                     alphavDS = alphaDS;
  199.                     break;
  200.                 default :
  201.                     throw new OrekitInternalError(null);
  202.             }
  203.             this.alphaV    = alphavDS.getValue();
  204.             this.alphaVDot = alphavDS.getPartialDerivative(1);
  205.         } else {
  206.             switch (type) {
  207.                 case MEAN :
  208.                     this.alphaV = eccentricToTrue(meanToEccentric(alpha, ex, ey), ex, ey);
  209.                     break;
  210.                 case ECCENTRIC :
  211.                     this.alphaV = eccentricToTrue(alpha, ex, ey);
  212.                     break;
  213.                 case TRUE :
  214.                     this.alphaV = alpha;
  215.                     break;
  216.                 default :
  217.                     throw new OrekitInternalError(null);
  218.             }
  219.             this.alphaVDot = null;
  220.         }

  221.         this.partialPV = null;

  222.     }

  223.     /** Constructor from Cartesian parameters.
  224.      *
  225.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  226.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  227.      * use {@code mu} and the position to compute the acceleration, including
  228.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  229.      *
  230.      * @param pvCoordinates the {@link FieldPVCoordinates} in inertial frame
  231.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  232.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  233.      * @param mu central attraction coefficient (m³/s²)
  234.      * @exception IllegalArgumentException if frame is not a {@link
  235.      * Frame#isPseudoInertial pseudo-inertial frame}
  236.      */
  237.     public FieldCircularOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
  238.                               final Frame frame, final double mu)
  239.         throws IllegalArgumentException {
  240.         super(pvCoordinates, frame, mu);

  241.         // compute semi-major axis
  242.         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
  243.         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
  244.         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
  245.         final T r2 = pvP.getNormSq();
  246.         final T r  = r2.sqrt();
  247.         final T V2 = pvV.getNormSq();
  248.         final T rV2OnMu = r.multiply(V2).divide(mu);

  249.         zero = r.getField().getZero();
  250.         one = r.getField().getOne();

  251.         if (rV2OnMu.getReal() > 2) {
  252.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  253.                                                      getClass().getName());
  254.         }

  255.         a = r.divide(rV2OnMu.negate().add(2));

  256.         // compute inclination
  257.         final FieldVector3D<T> momentum = pvCoordinates.getMomentum();
  258.         final FieldVector3D<T> plusK = FieldVector3D.getPlusK(r.getField());
  259.         i = FieldVector3D.angle(momentum, plusK);

  260.         // compute right ascension of ascending node
  261.         final FieldVector3D<T> node  = FieldVector3D.crossProduct(plusK, momentum);
  262.         raan = node.getY().atan2(node.getX());

  263.         // 2D-coordinates in the canonical frame
  264.         final T cosRaan = raan.cos();
  265.         final T sinRaan = raan.sin();
  266.         final T cosI    = i.cos();
  267.         final T sinI    = i.sin();
  268.         final T xP      = pvP.getX();
  269.         final T yP      = pvP.getY();
  270.         final T zP      = pvP.getZ();
  271.         final T x2      = (xP.multiply(cosRaan).add(yP .multiply(sinRaan))).divide(a);
  272.         final T y2      = (yP.multiply(cosRaan).subtract(xP.multiply(sinRaan))).multiply(cosI).add(zP.multiply(sinI)).divide(a);

  273.         // compute eccentricity vector
  274.         final T eSE    = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
  275.         final T eCE    = rV2OnMu.subtract(1);
  276.         final T e2     = eCE.multiply(eCE).add(eSE.multiply(eSE));
  277.         final T f      = eCE.subtract(e2);
  278.         final T g      = eSE.multiply(e2.negate().add(1).sqrt());
  279.         final T aOnR   = a.divide(r);
  280.         final T a2OnR2 = aOnR.multiply(aOnR);
  281.         ex = a2OnR2.multiply(f.multiply(x2).add(g.multiply(y2)));
  282.         ey = a2OnR2.multiply(f.multiply(y2).subtract(g.multiply(x2)));

  283.         // compute latitude argument
  284.         final T beta = (ex.multiply(ex).add(ey.multiply(ey)).negate().add(1)).sqrt().add(1).reciprocal();
  285.         alphaV = eccentricToTrue(y2.add(ey).add(eSE.multiply(beta).multiply(ex)).atan2(x2.add(ex).subtract(eSE.multiply(beta).multiply(ey))),
  286.                                  ex, ey);

  287.         partialPV = pvCoordinates;

  288.         if (!FACTORIES.containsKey(a.getField())) {
  289.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  290.         }

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

  293.             final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6);
  294.             getJacobianWrtCartesian(PositionAngle.MEAN, jacobian);

  295.             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(-mu), pvP);
  296.             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  297.             final T   aX                       = nonKeplerianAcceleration.getX();
  298.             final T   aY                       = nonKeplerianAcceleration.getY();
  299.             final T   aZ                       = nonKeplerianAcceleration.getZ();
  300.             aDot    = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
  301.             exDot   = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
  302.             eyDot   = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
  303.             iDot    = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
  304.             raanDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));

  305.             // in order to compute true anomaly derivative, we must compute
  306.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  307.             final T alphaMDot = getKeplerianMeanMotion().
  308.                                 add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  309.             @SuppressWarnings("unchecked")
  310.             final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  311.             final FieldDerivativeStructure<T> exDS     = factory.build(ex, exDot);
  312.             final FieldDerivativeStructure<T> eyDS     = factory.build(ey, eyDot);
  313.             final FieldDerivativeStructure<T> alphaMDS = factory.build(getAlphaM(), alphaMDot);
  314.             final FieldDerivativeStructure<T> alphavDS = eccentricToTrue(meanToEccentric(alphaMDS, exDS, eyDS), exDS, eyDS);
  315.             alphaVDot = alphavDS.getPartialDerivative(1);

  316.         } else {
  317.             // acceleration is either almost zero or NaN,
  318.             // we assume acceleration was not known
  319.             // we don't set up derivatives
  320.             aDot      = null;
  321.             exDot     = null;
  322.             eyDot     = null;
  323.             iDot      = null;
  324.             raanDot   = null;
  325.             alphaVDot = null;
  326.         }

  327.     }

  328.     /** Constructor from Cartesian parameters.
  329.      *
  330.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  331.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  332.      * use {@code mu} and the position to compute the acceleration, including
  333.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  334.      *
  335.      * @param PVCoordinates the {@link FieldPVCoordinates} in inertial frame
  336.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  337.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  338.      * @param date date of the orbital parameters
  339.      * @param mu central attraction coefficient (m³/s²)
  340.      * @exception IllegalArgumentException if frame is not a {@link
  341.      * Frame#isPseudoInertial pseudo-inertial frame}
  342.      */
  343.     public FieldCircularOrbit(final FieldPVCoordinates<T> PVCoordinates, final Frame frame,
  344.                               final FieldAbsoluteDate<T> date, final double mu)
  345.         throws IllegalArgumentException {
  346.         this(new TimeStampedFieldPVCoordinates<>(date, PVCoordinates), frame, mu);
  347.     }

  348.     /** Constructor from any kind of orbital parameters.
  349.      * @param op orbital parameters to copy
  350.      */
  351.     public FieldCircularOrbit(final FieldOrbit<T> op) {
  352.         super(op.getFrame(), op.getDate(), op.getMu());
  353.         a    = op.getA();
  354.         i    = op.getI();
  355.         final T hx = op.getHx();
  356.         final T hy = op.getHy();
  357.         final T h2 = hx.multiply(hx).add(hy.multiply(hy));
  358.         final T h  = h2.sqrt();
  359.         raan = hy.atan2(hx);
  360.         final T cosRaan = h.getReal() == 0 ? raan.cos() : hx.divide(h);
  361.         final T sinRaan = h.getReal() == 0 ? raan.sin() : hy.divide(h);
  362.         final T equiEx = op.getEquinoctialEx();
  363.         final T equiEy = op.getEquinoctialEy();
  364.         ex   = equiEx.multiply(cosRaan).add(equiEy.multiply(sinRaan));
  365.         ey   = equiEy.multiply(cosRaan).subtract(equiEx.multiply(sinRaan));
  366.         this.alphaV = op.getLv().subtract(raan);

  367.         if (!FACTORIES.containsKey(a.getField())) {
  368.             FACTORIES.put(a.getField(), new FDSFactory<>(a.getField(), 1, 1));
  369.         }

  370.         if (op.hasDerivatives()) {
  371.             aDot      = op.getADot();
  372.             final T      hxDot = op.getHxDot();
  373.             final T      hyDot = op.getHyDot();
  374.             iDot      = cosRaan.multiply(hxDot).add(sinRaan.multiply(hyDot)).multiply(2).divide(h2.add(1));
  375.             raanDot   = hx.multiply(hyDot).subtract(hy.multiply(hxDot)).divide(h2);
  376.             final T equiExDot = op.getEquinoctialExDot();
  377.             final T equiEyDot = op.getEquinoctialEyDot();
  378.             exDot     = equiExDot.add(equiEy.multiply(raanDot)).multiply(cosRaan).
  379.                         add(equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(sinRaan));
  380.             eyDot     = equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(cosRaan).
  381.                         subtract(equiExDot.add(equiEy.multiply(raanDot)).multiply(sinRaan));
  382.             alphaVDot = op.getLvDot().subtract(raanDot);
  383.         } else {
  384.             aDot      = null;
  385.             exDot     = null;
  386.             eyDot     = null;
  387.             iDot      = null;
  388.             raanDot   = null;
  389.             alphaVDot = null;
  390.         }

  391.         partialPV = null;

  392.         one = a.getField().getOne();
  393.         zero = a.getField().getZero();

  394.     }

  395.     /** {@inheritDoc} */
  396.     public OrbitType getType() {
  397.         return OrbitType.CIRCULAR;
  398.     }

  399.     /** {@inheritDoc} */
  400.     public T getA() {
  401.         return a;
  402.     }

  403.     /** {@inheritDoc} */
  404.     public T getADot() {
  405.         return aDot;
  406.     }

  407.     /** {@inheritDoc} */
  408.     public T getEquinoctialEx() {
  409.         return ex.multiply(raan.cos()).subtract(ey.multiply(raan.sin()));
  410.     }

  411.     /** {@inheritDoc} */
  412.     public T getEquinoctialExDot() {

  413.         if (!hasDerivatives()) {
  414.             return null;
  415.         }

  416.         final T cosRaan = raan.cos();
  417.         final T sinRaan = raan.sin();
  418.         return exDot.subtract(ey.multiply(raanDot)).multiply(cosRaan).
  419.                subtract(eyDot.add(ex.multiply(raanDot)).multiply(sinRaan));

  420.     }

  421.     /** {@inheritDoc} */
  422.     public T getEquinoctialEy() {
  423.         return ey.multiply(raan.cos()).add(ex.multiply(raan.sin()));
  424.     }

  425.     /** {@inheritDoc} */
  426.     public T getEquinoctialEyDot() {

  427.         if (!hasDerivatives()) {
  428.             return null;
  429.         }

  430.         final T cosRaan = raan.cos();
  431.         final T sinRaan = raan.sin();
  432.         return eyDot.add(ex.multiply(raanDot)).multiply(cosRaan).
  433.                add(exDot.subtract(ey.multiply(raanDot)).multiply(sinRaan));

  434.     }

  435.     /** Get the first component of the circular eccentricity vector.
  436.      * @return ex = e cos(ω), first component of the circular eccentricity vector
  437.      */
  438.     public T getCircularEx() {
  439.         return ex;
  440.     }

  441.     /** Get the first component of the circular eccentricity vector derivative.
  442.      * @return d(ex)/dt = d(e cos(ω))/dt, first component of the circular eccentricity vector derivative
  443.      */
  444.     public T getCircularExDot() {
  445.         return exDot;
  446.     }

  447.     /** Get the second component of the circular eccentricity vector.
  448.      * @return ey = e sin(ω), second component of the circular eccentricity vector
  449.      */
  450.     public T getCircularEy() {
  451.         return ey;
  452.     }

  453.     /** Get the second component of the circular eccentricity vector derivative.
  454.      * @return d(ey)/dt = d(e sin(ω))/dt, second component of the circular eccentricity vector derivative
  455.      */
  456.     public T getCircularEyDot() {
  457.         return eyDot;
  458.     }

  459.     /** {@inheritDoc} */
  460.     public T getHx() {
  461.         // Check for equatorial retrograde orbit
  462.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  463.             return zero.add(Double.NaN);
  464.         }
  465.         return  raan.cos().multiply(i.divide(2).tan());
  466.     }

  467.     /** {@inheritDoc} */
  468.     public T getHxDot() {

  469.         if (!hasDerivatives()) {
  470.             return null;
  471.         }

  472.         // Check for equatorial retrograde orbit
  473.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  474.             return zero.add(Double.NaN);
  475.         }

  476.         final T cosRaan = raan.cos();
  477.         final T sinRaan = raan.sin();
  478.         final T tan     = i.multiply(0.5).tan();
  479.         return cosRaan.multiply(0.5).multiply(tan.multiply(tan).add(1)).multiply(iDot).
  480.                subtract(sinRaan.multiply(tan).multiply(raanDot));

  481.     }

  482.     /** {@inheritDoc} */
  483.     public T getHy() {
  484.         // Check for equatorial retrograde orbit
  485.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  486.             return zero.add(Double.NaN);
  487.         }
  488.         return raan.sin().multiply(i.divide(2).tan());
  489.     }

  490.     /** {@inheritDoc} */
  491.     public T getHyDot() {

  492.         if (!hasDerivatives()) {
  493.             return null;
  494.         }

  495.         // Check for equatorial retrograde orbit
  496.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  497.             return zero.add(Double.NaN);
  498.         }

  499.         final T cosRaan = raan.cos();
  500.         final T sinRaan = raan.sin();
  501.         final T tan     = i.multiply(0.5).tan();
  502.         return sinRaan.multiply(0.5).multiply(tan.multiply(tan).add(1)).multiply(iDot).
  503.                add(cosRaan.multiply(tan).multiply(raanDot));

  504.     }

  505.     /** Get the true latitude argument.
  506.      * @return v + ω true latitude argument (rad)
  507.      */
  508.     public T getAlphaV() {
  509.         return alphaV;
  510.     }

  511.     /** Get the true latitude argument derivative.
  512.      * @return d(v + ω)/dt true latitude argument derivative (rad/s)
  513.      */
  514.     public T getAlphaVDot() {
  515.         return alphaVDot;
  516.     }

  517.     /** Get the eccentric latitude argument.
  518.      * @return E + ω eccentric latitude argument (rad)
  519.      */
  520.     public T getAlphaE() {
  521.         return trueToEccentric(alphaV, ex, ey);
  522.     }

  523.     /** Get the eccentric latitude argument derivative.
  524.      * @return d(E + ω)/dt eccentric latitude argument derivative (rad/s)
  525.      */
  526.     public T getAlphaEDot() {

  527.         if (!hasDerivatives()) {
  528.             return null;
  529.         }

  530.         @SuppressWarnings("unchecked")
  531.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  532.         final FieldDerivativeStructure<T> alphaVDS = factory.build(alphaV, alphaVDot);
  533.         final FieldDerivativeStructure<T> exDS     = factory.build(ex, exDot);
  534.         final FieldDerivativeStructure<T> eyDS     = factory.build(ey, eyDot);
  535.         final FieldDerivativeStructure<T> alphaEDS = trueToEccentric(alphaVDS, exDS, eyDS);
  536.         return alphaEDS.getPartialDerivative(1);

  537.     }

  538.     /** Get the mean latitude argument.
  539.      * @return M + ω mean latitude argument (rad)
  540.      */
  541.     public T getAlphaM() {
  542.         return eccentricToMean(trueToEccentric(alphaV, ex, ey), ex, ey);
  543.     }

  544.     /** Get the mean latitude argument derivative.
  545.      * @return d(M + ω)/dt mean latitude argument derivative (rad/s)
  546.      */
  547.     public T getAlphaMDot() {

  548.         if (!hasDerivatives()) {
  549.             return null;
  550.         }

  551.         @SuppressWarnings("unchecked")
  552.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(a.getField());
  553.         final FieldDerivativeStructure<T> alphaVDS = factory.build(alphaV, alphaVDot);
  554.         final FieldDerivativeStructure<T> exDS     = factory.build(ex, exDot);
  555.         final FieldDerivativeStructure<T> eyDS     = factory.build(ey, eyDot);
  556.         final FieldDerivativeStructure<T> alphaMDS = eccentricToMean(trueToEccentric(alphaVDS, exDS, eyDS), exDS, eyDS);
  557.         return alphaMDS.getPartialDerivative(1);

  558.     }

  559.     /** Get the latitude argument.
  560.      * @param type type of the angle
  561.      * @return latitude argument (rad)
  562.      */
  563.     public T getAlpha(final PositionAngle type) {
  564.         return (type == PositionAngle.MEAN) ? getAlphaM() :
  565.                                               ((type == PositionAngle.ECCENTRIC) ? getAlphaE() :
  566.                                                                                    getAlphaV());
  567.     }

  568.     /** Get the latitude argument derivative.
  569.      * @param type type of the angle
  570.      * @return latitude argument derivative (rad/s)
  571.      */
  572.     public T getAlphaDot(final PositionAngle type) {
  573.         return (type == PositionAngle.MEAN) ? getAlphaMDot() :
  574.                                               ((type == PositionAngle.ECCENTRIC) ? getAlphaEDot() :
  575.                                                                                    getAlphaVDot());
  576.     }

  577.     /** Computes the true latitude argument from the eccentric latitude argument.
  578.      * @param alphaE = E + ω eccentric latitude argument (rad)
  579.      * @param ex e cos(ω), first component of circular eccentricity vector
  580.      * @param ey e sin(ω), second component of circular eccentricity vector
  581.      * @param <T> Type of the field elements
  582.      * @return the true latitude argument.
  583.      */
  584.     public static <T extends RealFieldElement<T>> T eccentricToTrue(final T alphaE, final T ex, final T ey) {
  585.         final T epsilon   = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt();
  586.         final T cosAlphaE = alphaE.cos();
  587.         final T sinAlphaE = alphaE.sin();
  588.         return alphaE.add(ex.multiply(sinAlphaE).subtract(ey.multiply(cosAlphaE)).divide(
  589.                                       epsilon.add(1).subtract(ex.multiply(cosAlphaE)).subtract(
  590.                                       ey.multiply(sinAlphaE))).atan().multiply(2));
  591.     }

  592.     /** Computes the eccentric latitude argument from the true latitude argument.
  593.      * @param alphaV = v + ω true latitude argument (rad)
  594.      * @param ex e cos(ω), first component of circular eccentricity vector
  595.      * @param ey e sin(ω), second component of circular eccentricity vector
  596.      * @param <T> Type of the field elements
  597.      * @return the eccentric latitude argument.
  598.      */
  599.     public static <T extends RealFieldElement<T>> T trueToEccentric(final T alphaV, final T ex, final T ey) {
  600.         final T epsilon   = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt();
  601.         final T cosAlphaV = alphaV.cos();
  602.         final T sinAlphaV = alphaV.sin();
  603.         return alphaV.add(ey.multiply(cosAlphaV).subtract(ex.multiply(sinAlphaV)).divide
  604.                                       (epsilon.add(1).add(ex.multiply(cosAlphaV).add(ey.multiply(sinAlphaV)))).atan().multiply(2));
  605.     }

  606.     /** Computes the eccentric latitude argument from the mean latitude argument.
  607.      * @param alphaM = M + ω  mean latitude argument (rad)
  608.      * @param ex e cos(ω), first component of circular eccentricity vector
  609.      * @param ey e sin(ω), second component of circular eccentricity vector
  610.      * @param <T> Type of the field elements
  611.      * @return the eccentric latitude argument.
  612.      */
  613.     public static <T extends RealFieldElement<T>> T meanToEccentric(final T alphaM, final T ex, final T ey) {
  614.         // Generalization of Kepler equation to circular parameters
  615.         // with alphaE = PA + E and
  616.         //      alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE)

  617.         T alphaE        = alphaM;
  618.         T shift         = alphaM.getField().getZero();
  619.         T alphaEMalphaM = alphaM.getField().getZero();
  620.         T cosAlphaE     = alphaE.cos();
  621.         T sinAlphaE     = alphaE.sin();
  622.         int    iter     = 0;
  623.         do {
  624.             final T f2 = ex.multiply(sinAlphaE).subtract(ey.multiply(cosAlphaE));
  625.             final T f1 = ex.negate().multiply(cosAlphaE).subtract(ey.multiply(sinAlphaE)).add(1);
  626.             final T f0 = alphaEMalphaM.subtract(f2);

  627.             final T f12 = f1.multiply(2);
  628.             shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2)));

  629.             alphaEMalphaM  = alphaEMalphaM.subtract(shift);
  630.             alphaE         = alphaM.add(alphaEMalphaM);
  631.             cosAlphaE      = alphaE.cos();
  632.             sinAlphaE      = alphaE.sin();
  633.         } while ((++iter < 50) && (FastMath.abs(shift.getReal()) > 1.0e-12));
  634.         return alphaE;

  635.     }

  636.     /** Computes the mean latitude argument from the eccentric latitude argument.
  637.      * @param alphaE = E + ω  eccentric latitude argument (rad)
  638.      * @param ex e cos(ω), first component of circular eccentricity vector
  639.      * @param ey e sin(ω), second component of circular eccentricity vector
  640.      * @param <T> Type of the field elements
  641.      * @return the mean latitude argument.
  642.      */
  643.     public static <T extends RealFieldElement<T>> T eccentricToMean(final T alphaE, final T ex, final T ey) {
  644.         return alphaE.subtract(ex.multiply(alphaE.sin()).subtract(ey.multiply(alphaE.cos())));
  645.     }

  646.     /** Compute position from circular parameters.
  647.      * @param a  semi-major axis (m)
  648.      * @param ex e cos(ω), first component of circular eccentricity vector
  649.      * @param ey e sin(ω), second component of circular eccentricity vector
  650.      * @param i inclination (rad)
  651.      * @param raan right ascension of ascending node (Ω, rad)
  652.      * @param alphaV  v + ω true latitude argument (rad)
  653.      * @param mu central attraction coefficient (m³/s²)
  654.      * @param <T> type of the fiels elements
  655.      * @return position vector
  656.      */
  657.     public static <T extends RealFieldElement<T>> FieldVector3D<T> circularToPosition(final T a, final T ex, final T ey,
  658.                                                                                       final T i, final T raan, final T alphaV,
  659.                                                                                       final double mu) {

  660.         final T zero = a.getField().getZero();

  661.         // get equinoctial parameters
  662.         final T equEx = ex.multiply(raan.cos()).subtract(ey.multiply(raan.sin()));
  663.         final T equEy = ey.multiply(raan.cos()).add(ex.multiply(raan.sin()));
  664.         final T hx;
  665.         final T hy;
  666.         if (FastMath.abs(i.getReal() - FastMath.PI) < 1.0e-10) {
  667.             hx = zero.add(Double.NaN);
  668.             hy = zero.add(Double.NaN);
  669.         } else {
  670.             final T tan = i.divide(2).tan();
  671.             hx = raan.cos().multiply(tan);
  672.             hy = raan.sin().multiply(tan);
  673.         }
  674.         final T lE = trueToEccentric(alphaV, ex, ey).add(raan);

  675.         // inclination-related intermediate parameters
  676.         final T hx2   = hx.multiply(hx);
  677.         final T hy2   = hy.multiply(hy);
  678.         final T factH = (hx2.add(1).add(hy2)).reciprocal();

  679.         // reference axes defining the orbital plane
  680.         final T ux = (hx2.add(1).subtract(hy2)).multiply(factH);
  681.         final T uy =  hx.multiply(2).multiply(hy).multiply(factH);
  682.         final T uz = hy.multiply(-2).multiply(factH);

  683.         final T vx = uy;
  684.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  685.         final T vz =  hx.multiply(factH).multiply(2);

  686.         // eccentricity-related intermediate parameters
  687.         final T exey = equEx.multiply(equEy);
  688.         final T ex2  = equEx.multiply(equEx);
  689.         final T ey2  = equEy.multiply(equEy);
  690.         final T e2   = ex2.add(ey2);
  691.         final T eta  = e2.negate().add(1).sqrt().add(1);
  692.         final T beta = eta.reciprocal();

  693.         // eccentric latitude argument
  694.         final T cLe    = lE.cos();
  695.         final T sLe    = lE.sin();

  696.         // coordinates of position and velocity in the orbital plane
  697.         final T x      = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx));
  698.         final T y      = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy));

  699.         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  700.                                    x.multiply(uy).add(y.multiply(vy)),
  701.                                    x.multiply(uz).add(y.multiply(vz)));

  702.     }

  703.     /** {@inheritDoc} */
  704.     public T getE() {
  705.         return ex.multiply(ex).add(ey.multiply(ey)).sqrt();
  706.     }

  707.     /** {@inheritDoc} */
  708.     public T getEDot() {

  709.         if (!hasDerivatives()) {
  710.             return null;
  711.         }

  712.         return ex.multiply(exDot).add(ey.multiply(eyDot)).divide(getE());

  713.     }

  714.     /** {@inheritDoc} */
  715.     public T getI() {
  716.         return i;
  717.     }

  718.     /** {@inheritDoc} */
  719.     public T getIDot() {
  720.         return iDot;
  721.     }

  722.     /** Get the right ascension of the ascending node.
  723.      * @return right ascension of the ascending node (rad)
  724.      */
  725.     public T getRightAscensionOfAscendingNode() {
  726.         return raan;
  727.     }

  728.     /** Get the right ascension of the ascending node derivative.
  729.      * @return right ascension of the ascending node derivative (rad/s)
  730.      */
  731.     public T getRightAscensionOfAscendingNodeDot() {
  732.         return raanDot;
  733.     }

  734.     /** {@inheritDoc} */
  735.     public T getLv() {
  736.         return alphaV.add(raan);
  737.     }

  738.     /** {@inheritDoc} */
  739.     public T getLvDot() {
  740.         return hasDerivatives() ? alphaVDot.add(raanDot) : null;
  741.     }

  742.     /** {@inheritDoc} */
  743.     public T getLE() {
  744.         return getAlphaE().add(raan);
  745.     }

  746.     /** {@inheritDoc} */
  747.     public T getLEDot() {
  748.         return hasDerivatives() ? getAlphaEDot().add(raanDot) : null;
  749.     }

  750.     /** {@inheritDoc} */
  751.     public T getLM() {
  752.         return getAlphaM().add(raan);
  753.     }

  754.     /** {@inheritDoc} */
  755.     public T getLMDot() {
  756.         return hasDerivatives() ? getAlphaMDot().add(raanDot) : null;
  757.     }

  758.     /** {@inheritDoc} */
  759.     @Override
  760.     public boolean hasDerivatives() {
  761.         return aDot != null;
  762.     }

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

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

  770.         // get equinoctial parameters
  771.         final T equEx = getEquinoctialEx();
  772.         final T equEy = getEquinoctialEy();
  773.         final T hx = getHx();
  774.         final T hy = getHy();
  775.         final T lE = getLE();
  776.         // inclination-related intermediate parameters
  777.         final T hx2   = hx.multiply(hx);
  778.         final T hy2   = hy.multiply(hy);
  779.         final T factH = (hx2.add(1).add(hy2)).reciprocal();

  780.         // reference axes defining the orbital plane
  781.         final T ux = (hx2.add(1).subtract(hy2)).multiply(factH);
  782.         final T uy =  hx.multiply(2).multiply(hy).multiply(factH);
  783.         final T uz = hy.multiply(-2).multiply(factH);

  784.         final T vx = uy;
  785.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  786.         final T vz =  hx.multiply(factH).multiply(2);

  787.         // eccentricity-related intermediate parameters
  788.         final T exey = equEx.multiply(equEy);
  789.         final T ex2  = equEx.multiply(equEx);
  790.         final T ey2  = equEy.multiply(equEy);
  791.         final T e2   = ex2.add(ey2);
  792.         final T eta  = e2.negate().add(1).sqrt().add(1);
  793.         final T beta = eta.reciprocal();

  794.         // eccentric latitude argument
  795.         final T cLe    = lE.cos();
  796.         final T sLe    = lE.sin();
  797.         final T exCeyS = equEx.multiply(cLe).add(equEy.multiply(sLe));
  798.         // coordinates of position and velocity in the orbital plane
  799.         final T x      = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx));
  800.         final T y      = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy));

  801.         final T factor = one.add(getMu()).divide(a).sqrt().divide(exCeyS.negate().add(1));
  802.         final T xdot   = factor.multiply( beta.multiply(equEy).multiply(exCeyS).subtract(sLe ));
  803.         final T ydot   = factor.multiply( cLe.subtract(beta.multiply(equEx).multiply(exCeyS)));

  804.         final FieldVector3D<T> position = new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  805.                                                               x.multiply(uy).add(y.multiply(vy)),
  806.                                                               x.multiply(uz).add(y.multiply(vz)));
  807.         final FieldVector3D<T> velocity = new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)),
  808.                                                               xdot.multiply(uy).add(ydot.multiply(vy)),
  809.                                                               xdot.multiply(uz).add(ydot.multiply(vz)));

  810.         partialPV = new FieldPVCoordinates<>(position, velocity);

  811.     }

  812.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  813.      * <p>
  814.      * This method should be called only when {@link #hasDerivatives()} returns true.
  815.      * </p>
  816.      * @return non-Keplerian part of the acceleration
  817.      */
  818.     private FieldVector3D<T> nonKeplerianAcceleration() {

  819.         final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6);
  820.         getJacobianWrtParameters(PositionAngle.MEAN, dCdP);

  821.         final T nonKeplerianMeanMotion = getAlphaMDot().subtract(getKeplerianMeanMotion());
  822.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  823.                                  add(dCdP[3][1].multiply(exDot)).
  824.                                  add(dCdP[3][2].multiply(eyDot)).
  825.                                  add(dCdP[3][3].multiply(iDot)).
  826.                                  add(dCdP[3][4].multiply(raanDot)).
  827.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  828.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  829.                                  add(dCdP[4][1].multiply(exDot)).
  830.                                  add(dCdP[4][2].multiply(eyDot)).
  831.                                  add(dCdP[4][3].multiply(iDot)).
  832.                                  add(dCdP[4][4].multiply(raanDot)).
  833.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  834.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  835.                                  add(dCdP[5][1].multiply(exDot)).
  836.                                  add(dCdP[5][2].multiply(eyDot)).
  837.                                  add(dCdP[5][3].multiply(iDot)).
  838.                                  add(dCdP[5][4].multiply(raanDot)).
  839.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

  840.         return new FieldVector3D<>(nonKeplerianAx, nonKeplerianAy, nonKeplerianAz);

  841.     }

  842.     /** {@inheritDoc} */
  843.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  844.         // position and velocity
  845.         computePVWithoutA();

  846.         // acceleration
  847.         final T r2 = partialPV.getPosition().getNormSq();
  848.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(-getMu()),
  849.                                                                            partialPV.getPosition());
  850.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  851.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  852.                                               keplerianAcceleration;

  853.         return new TimeStampedFieldPVCoordinates<>(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration);

  854.     }

  855.     /** {@inheritDoc} */
  856.     public FieldCircularOrbit<T> shiftedBy(final double dt) {
  857.         return shiftedBy(getDate().getField().getZero().add(dt));
  858.     }

  859.     /** {@inheritDoc} */
  860.     public FieldCircularOrbit<T> shiftedBy(final T dt) {

  861.         // use Keplerian-only motion
  862.         final FieldCircularOrbit<T> keplerianShifted = new FieldCircularOrbit<>(a, ex, ey, i, raan,
  863.                                                                                 getAlphaM().add(getKeplerianMeanMotion().multiply(dt)),
  864.                                                                                 PositionAngle.MEAN, getFrame(),
  865.                                                                                 getDate().shiftedBy(dt), getMu());

  866.         if (hasDerivatives()) {

  867.             // extract non-Keplerian acceleration from first time derivatives
  868.             final FieldVector3D<T> nonKeplerianAcceleration = nonKeplerianAcceleration();

  869.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  870.             keplerianShifted.computePVWithoutA();
  871.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(one, keplerianShifted.partialPV.getPosition(),
  872.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  873.             final T   fixedR2 = fixedP.getNormSq();
  874.             final T   fixedR  = fixedR2.sqrt();
  875.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(one, keplerianShifted.partialPV.getVelocity(),
  876.                                                                  dt, nonKeplerianAcceleration);
  877.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(-getMu()),
  878.                                                                  keplerianShifted.partialPV.getPosition(),
  879.                                                                  one, nonKeplerianAcceleration);

  880.             // build a new orbit, taking non-Keplerian acceleration into account
  881.             return new FieldCircularOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  882.                                                                                 fixedP, fixedV, fixedA),
  883.                                             keplerianShifted.getFrame(), keplerianShifted.getMu());

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

  888.     }

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

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

  916.         // set up an interpolator
  917.         final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();

  918.         // second pass to feed interpolator
  919.         FieldAbsoluteDate<T> previousDate   = null;
  920.         T                    previousRAAN   = zero.add(Double.NaN);
  921.         T                    previousAlphaM = zero.add(Double.NaN);
  922.         for (final FieldOrbit<T> orbit : list) {
  923.             final FieldCircularOrbit<T> circ = (FieldCircularOrbit<T>) OrbitType.CIRCULAR.convertType(orbit);
  924.             final T continuousRAAN;
  925.             final T continuousAlphaM;
  926.             if (previousDate == null) {
  927.                 continuousRAAN   = circ.getRightAscensionOfAscendingNode();
  928.                 continuousAlphaM = circ.getAlphaM();
  929.             } else {
  930.                 final T dt       = circ.getDate().durationFrom(previousDate);
  931.                 final T keplerAM = previousAlphaM .add(circ.getKeplerianMeanMotion().multiply(dt));
  932.                 continuousRAAN   = normalizeAngle(circ.getRightAscensionOfAscendingNode(), previousRAAN);
  933.                 continuousAlphaM = normalizeAngle(circ.getAlphaM(), keplerAM);
  934.             }
  935.             previousDate   = circ.getDate();
  936.             previousRAAN   = continuousRAAN;
  937.             previousAlphaM = continuousAlphaM;
  938.             final T[] toAdd = MathArrays.buildArray(one.getField(), 6);
  939.             toAdd[0] = circ.getA();
  940.             toAdd[1] = circ.getCircularEx();
  941.             toAdd[2] = circ.getCircularEy();
  942.             toAdd[3] = circ.getI();
  943.             toAdd[4] = continuousRAAN;
  944.             toAdd[5] = continuousAlphaM;
  945.             if (useDerivatives) {
  946.                 final T[] toAddDot = MathArrays.buildArray(one.getField(), 6);
  947.                 toAddDot[0] = circ.getADot();
  948.                 toAddDot[1] = circ.getCircularExDot();
  949.                 toAddDot[2] = circ.getCircularEyDot();
  950.                 toAddDot[3] = circ.getIDot();
  951.                 toAddDot[4] = circ.getRightAscensionOfAscendingNodeDot();
  952.                 toAddDot[5] = circ.getAlphaMDot();
  953.                 interpolator.addSamplePoint(circ.getDate().durationFrom(date),
  954.                                             toAdd, toAddDot);
  955.             } else {
  956.                 interpolator.addSamplePoint(circ.getDate().durationFrom(date),
  957.                                             toAdd);
  958.             }
  959.         }

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

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

  968.     }

  969.     /** {@inheritDoc} */
  970.     protected T[][] computeJacobianMeanWrtCartesian() {

  971.         final T[][] jacobian = MathArrays.buildArray(one.getField(), 6, 6);

  972.         // compute various intermediate parameters
  973.         computePVWithoutA();
  974.         final FieldVector3D<T> position = partialPV.getPosition();
  975.         final FieldVector3D<T> velocity = partialPV.getVelocity();

  976.         final T x          = position.getX();
  977.         final T y          = position.getY();
  978.         final T z          = position.getZ();
  979.         final T vx         = velocity.getX();
  980.         final T vy         = velocity.getY();
  981.         final T vz         = velocity.getZ();
  982.         final T pv         = FieldVector3D.dotProduct(position, velocity);
  983.         final T r2         = position.getNormSq();
  984.         final T r          = r2.sqrt();
  985.         final T v2         = velocity.getNormSq();

  986.         final double mu         = getMu();
  987.         final T oOsqrtMuA  = one.divide(a.multiply(mu).sqrt());
  988.         final T rOa        = r.divide(a);
  989.         final T aOr        = a.divide(r);
  990.         final T aOr2       = a.divide(r2);
  991.         final T a2         = a.multiply(a);

  992.         final T ex2        = ex.multiply(ex);
  993.         final T ey2        = ey.multiply(ey);
  994.         final T e2         = ex2.add(ey2);
  995.         final T epsilon    = e2.negate().add(1.0).sqrt();
  996.         final T beta       = epsilon.add(1).reciprocal();

  997.         final T eCosE      = rOa.negate().add(1);
  998.         final T eSinE      = pv.multiply(oOsqrtMuA);

  999.         final T cosI       = i.cos();
  1000.         final T sinI       = i.sin();
  1001.         final T cosRaan    = raan.cos();
  1002.         final T sinRaan    = raan.sin();

  1003.         // da
  1004.         fillHalfRow(aOr.multiply(2.0).multiply(aOr2), position, jacobian[0], 0);
  1005.         fillHalfRow(a2.multiply(2.0 / mu), velocity, jacobian[0], 3);

  1006.         // differentials of the normalized momentum
  1007.         final FieldVector3D<T> danP = new FieldVector3D<>(v2, position, pv.negate(), velocity);
  1008.         final FieldVector3D<T> danV = new FieldVector3D<>(r2, velocity, pv.negate(), position);
  1009.         final T recip   = partialPV.getMomentum().getNorm().reciprocal();
  1010.         final T recip2  = recip.multiply(recip);
  1011.         final T recip2N = recip2.negate();
  1012.         final FieldVector3D<T> dwXP = new FieldVector3D<>(recip,
  1013.                                                           new FieldVector3D<>(zero, vz, vy.negate()),
  1014.                                                           recip2N.multiply(sinRaan).multiply(sinI),
  1015.                                                           danP);
  1016.         final FieldVector3D<T> dwYP = new FieldVector3D<>(recip,
  1017.                                                           new FieldVector3D<>(vz.negate(), zero, vx),
  1018.                                                           recip2.multiply(cosRaan).multiply(sinI),
  1019.                                                           danP);
  1020.         final FieldVector3D<T> dwZP = new FieldVector3D<>(recip,
  1021.                                                           new FieldVector3D<>(vy, vx.negate(), zero),
  1022.                                                           recip2N.multiply(cosI),
  1023.                                                           danP);
  1024.         final FieldVector3D<T> dwXV = new FieldVector3D<>(recip,
  1025.                                                           new FieldVector3D<>(zero, z.negate(), y),
  1026.                                                           recip2N.multiply(sinRaan).multiply(sinI),
  1027.                                                           danV);
  1028.         final FieldVector3D<T> dwYV = new FieldVector3D<>(recip,
  1029.                                                           new FieldVector3D<>(z, zero, x.negate()),
  1030.                                                           recip2.multiply(cosRaan).multiply(sinI),
  1031.                                                           danV);
  1032.         final FieldVector3D<T> dwZV = new FieldVector3D<>(recip,
  1033.                                                           new FieldVector3D<>(y.negate(), x, zero),
  1034.                                                           recip2N.multiply(cosI),
  1035.                                                           danV);

  1036.         // di
  1037.         fillHalfRow(sinRaan.multiply(cosI), dwXP, cosRaan.negate().multiply(cosI), dwYP, sinI.negate(), dwZP, jacobian[3], 0);
  1038.         fillHalfRow(sinRaan.multiply(cosI), dwXV, cosRaan.negate().multiply(cosI), dwYV, sinI.negate(), dwZV, jacobian[3], 3);

  1039.         // dRaan
  1040.         fillHalfRow(sinRaan.divide(sinI), dwYP, cosRaan.divide(sinI), dwXP, jacobian[4], 0);
  1041.         fillHalfRow(sinRaan.divide(sinI), dwYV, cosRaan.divide(sinI), dwXV, jacobian[4], 3);

  1042.         // orbital frame: (p, q, w) p along ascending node, w along momentum
  1043.         // the coordinates of the spacecraft in this frame are: (u, v, 0)
  1044.         final T u     =  x.multiply(cosRaan).add(y.multiply(sinRaan));
  1045.         final T cv    =  x.negate().multiply(sinRaan).add(y.multiply(cosRaan));
  1046.         final T v     = cv.multiply(cosI).add(z.multiply(sinI));

  1047.         // du
  1048.         final FieldVector3D<T> duP = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXP,
  1049.                                                          cv.multiply(sinRaan).divide(sinI), dwYP,
  1050.                                                          one, new FieldVector3D<>(cosRaan, sinRaan, zero));
  1051.         final FieldVector3D<T> duV = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXV,
  1052.                                                          cv.multiply(sinRaan).divide(sinI), dwYV);

  1053.         // dv
  1054.         final FieldVector3D<T> dvP = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXP,
  1055.                                                          u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYP,
  1056.                                                          cv, dwZP,
  1057.                                                          one, new FieldVector3D<>(sinRaan.negate().multiply(cosI), cosRaan.multiply(cosI), sinI));
  1058.         final FieldVector3D<T> dvV = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXV,
  1059.                                                          u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYV,
  1060.                                                          cv, dwZV);

  1061.         final FieldVector3D<T> dc1P = new FieldVector3D<>(aOr2.multiply(eSinE.multiply(eSinE).multiply(2).add(1).subtract(eCosE)).divide(r2), position,
  1062.                                                           aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), velocity);
  1063.         final FieldVector3D<T> dc1V = new FieldVector3D<>(aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), position,
  1064.                                                           zero.add(2).divide(mu), velocity);
  1065.         final FieldVector3D<T> dc2P = new FieldVector3D<>(aOr2.multiply(eSinE).multiply(eSinE.multiply(eSinE).subtract(e2.negate().add(1))).divide(r2.multiply(epsilon)), position,
  1066.                                                           aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), velocity);
  1067.         final FieldVector3D<T> dc2V = new FieldVector3D<>(aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), position,
  1068.                                                           eSinE.divide(epsilon.multiply(mu)), velocity);

  1069.         final T cof1   = aOr2.multiply(eCosE.subtract(e2));
  1070.         final T cof2   = aOr2.multiply(epsilon).multiply(eSinE);
  1071.         final FieldVector3D<T> dexP = new FieldVector3D<>(u, dc1P,  v, dc2P, cof1, duP,  cof2, dvP);
  1072.         final FieldVector3D<T> dexV = new FieldVector3D<>(u, dc1V,  v, dc2V, cof1, duV,  cof2, dvV);
  1073.         final FieldVector3D<T> deyP = new FieldVector3D<>(v, dc1P, u.negate(), dc2P, cof1, dvP, cof2.negate(), duP);
  1074.         final FieldVector3D<T> deyV = new FieldVector3D<>(v, dc1V, u.negate(), dc2V, cof1, dvV, cof2.negate(), duV);
  1075.         fillHalfRow(one, dexP, jacobian[1], 0);
  1076.         fillHalfRow(one, dexV, jacobian[1], 3);
  1077.         fillHalfRow(one, deyP, jacobian[2], 0);
  1078.         fillHalfRow(one, deyV, jacobian[2], 3);

  1079.         final T cle = u.divide(a).add(ex).subtract(eSinE.multiply(beta).multiply(ey));
  1080.         final T sle = v.divide(a).add(ey).add(eSinE.multiply(beta).multiply(ex));
  1081.         final T m1  = beta.multiply(eCosE);
  1082.         final T m2  = m1.multiply(eCosE).negate().add(1);
  1083.         final T m3  = (u.multiply(ey).subtract(v.multiply(ex))).add(eSinE.multiply(beta).multiply(u.multiply(ex).add(v.multiply(ey))));
  1084.         final T m4  = sle.negate().add(cle.multiply(eSinE).multiply(beta));
  1085.         final T m5  = cle.add(sle.multiply(eSinE).multiply(beta));
  1086.         final T kk = m3.multiply(2).divide(r).add(aOr.multiply(eSinE)).add(m1.multiply(eSinE).multiply(m1.add(1).subtract(aOr.add(1).multiply(m2))).divide(epsilon)).divide(r2);
  1087.         final T jj = (m1.multiply(m2).divide(epsilon).subtract(1)).multiply(oOsqrtMuA);
  1088.         fillHalfRow(kk, position,
  1089.                     jj, velocity,
  1090.                     m4, dexP,
  1091.                     m5, deyP,
  1092.                     sle.negate().divide(a), duP,
  1093.                     cle.divide(a), dvP,
  1094.                     jacobian[5], 0);
  1095.         final T ll = (m1.multiply(m2).divide(epsilon ).subtract(1)).multiply(oOsqrtMuA);
  1096.         final T mm = m3.multiply(2).add(eSinE.multiply(a)).add(m1.multiply(eSinE).multiply(r).multiply(eCosE.multiply(beta).multiply(2).subtract(aOr.multiply(m2))).divide(epsilon)).divide(mu);

  1097.         fillHalfRow(ll, position,
  1098.                     mm, velocity,
  1099.                     m4, dexV,
  1100.                     m5, deyV,
  1101.                     sle.negate().divide(a), duV,
  1102.                     cle.divide(a), dvV,
  1103.                     jacobian[5], 3);
  1104.         return jacobian;

  1105.     }

  1106.     /** {@inheritDoc} */
  1107.     protected T[][] computeJacobianEccentricWrtCartesian() {

  1108.         // start by computing the Jacobian with mean angle
  1109.         final T[][] jacobian = computeJacobianMeanWrtCartesian();

  1110.         // Differentiating the Kepler equation aM = aE - ex sin aE + ey cos aE leads to:
  1111.         // daM = (1 - ex cos aE - ey sin aE) dE - sin aE dex + cos aE dey
  1112.         // which is inverted and rewritten as:
  1113.         // daE = a/r daM + sin aE a/r dex - cos aE a/r dey
  1114.         final T alphaE = getAlphaE();
  1115.         final T cosAe  = alphaE.cos();
  1116.         final T sinAe  = alphaE.sin();
  1117.         final T aOr    = one.divide(one.subtract(ex.multiply(cosAe)).subtract(ey.multiply(sinAe)));

  1118.         // update longitude row
  1119.         final T[] rowEx = jacobian[1];
  1120.         final T[] rowEy = jacobian[2];
  1121.         final T[] rowL  = jacobian[5];
  1122.         for (int j = 0; j < 6; ++j) {
  1123.          // rowL[j] = aOr * (      rowL[j] +   sinAe *        rowEx[j]   -         cosAe *        rowEy[j]);
  1124.             rowL[j] = aOr.multiply(rowL[j].add(sinAe.multiply(rowEx[j])).subtract( cosAe.multiply(rowEy[j])));
  1125.         }
  1126.         jacobian[5] = rowL;
  1127.         return jacobian;

  1128.     }
  1129.     /** {@inheritDoc} */
  1130.     protected T[][] computeJacobianTrueWrtCartesian() {

  1131.         // start by computing the Jacobian with eccentric angle
  1132.         final T[][] jacobian = computeJacobianEccentricWrtCartesian();
  1133.         // Differentiating the eccentric latitude equation
  1134.         // tan((aV - aE)/2) = [ex sin aE - ey cos aE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos aE - ey sin aE]
  1135.         // leads to
  1136.         // cT (daV - daE) = cE daE + cX dex + cY dey
  1137.         // with
  1138.         // cT = [d^2 + (ex sin aE - ey cos aE)^2] / 2
  1139.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos aE - ey sin aE
  1140.         // cE = (ex cos aE + ey sin aE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  1141.         // cX =  sin aE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
  1142.         // cY = -cos aE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
  1143.         // which can be solved to find the differential of the true latitude
  1144.         // daV = (cT + cE) / cT daE + cX / cT deX + cY / cT deX
  1145.         final T alphaE    = getAlphaE();
  1146.         final T cosAe     = alphaE.cos();
  1147.         final T sinAe     = alphaE.sin();
  1148.         final T eSinE     = ex.multiply(sinAe).subtract(ey.multiply(cosAe));
  1149.         final T ecosE     = ex.multiply(cosAe).add(ey.multiply(sinAe));
  1150.         final T e2        = ex.multiply(ex).add(ey.multiply(ey));
  1151.         final T epsilon   = (one.subtract(e2)).sqrt();
  1152.         final T onePeps   = one.add(epsilon);
  1153.         final T d         = onePeps.subtract(ecosE);
  1154.         final T cT        = (d.multiply(d).add(eSinE.multiply(eSinE))).divide(2);
  1155.         final T cE        = ecosE.multiply(onePeps).subtract(e2);
  1156.         final T cX        = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinAe.multiply(onePeps));
  1157.         final T cY        = ey.multiply(eSinE).divide(epsilon).add(ex).subtract(cosAe.multiply(onePeps));
  1158.         final T factorLe  = (cT.add(cE)).divide(cT);
  1159.         final T factorEx  = cX.divide(cT);
  1160.         final T factorEy  = cY.divide(cT);

  1161.         // update latitude row
  1162.         final T[] rowEx = jacobian[1];
  1163.         final T[] rowEy = jacobian[2];
  1164.         final T[] rowA = jacobian[5];
  1165.         for (int j = 0; j < 6; ++j) {
  1166.             rowA[j] = factorLe.multiply(rowA[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  1167.         }
  1168.         return jacobian;

  1169.     }

  1170.     /** {@inheritDoc} */
  1171.     public void addKeplerContribution(final PositionAngle type, final double gm,
  1172.                                       final T[] pDot) {
  1173.         final T oMe2;
  1174.         final T ksi;
  1175.         final T n = a.reciprocal().multiply(gm).sqrt().divide(a);
  1176.         switch (type) {
  1177.             case MEAN :
  1178.                 pDot[5] = pDot[5].add(n);
  1179.                 break;
  1180.             case ECCENTRIC :
  1181.                 oMe2  = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  1182.                 ksi   = one.add(ex.multiply(alphaV.cos())).add(ey.multiply(alphaV.sin()));
  1183.                 pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2));
  1184.                 break;
  1185.             case TRUE :
  1186.                 oMe2  = one.subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  1187.                 ksi   = one.add(ex.multiply(alphaV.cos())).add(ey.multiply(alphaV.sin()));
  1188.                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
  1189.                 break;
  1190.             default :
  1191.                 throw new OrekitInternalError(null);
  1192.         }
  1193.     }

  1194.     /**  Returns a string representation of this Orbit object.
  1195.      * @return a string representation of this object
  1196.      */
  1197.     public String toString() {
  1198.         return new StringBuffer().append("circular parameters: ").append('{').
  1199.                                   append("a: ").append(a.getReal()).
  1200.                                   append(", ex: ").append(ex.getReal()).append(", ey: ").append(ey.getReal()).
  1201.                                   append(", i: ").append(FastMath.toDegrees(i.getReal())).
  1202.                                   append(", raan: ").append(FastMath.toDegrees(raan.getReal())).
  1203.                                   append(", alphaV: ").append(FastMath.toDegrees(alphaV.getReal())).
  1204.                                   append(";}").toString();
  1205.     }



  1206.     /**
  1207.      * Normalize an angle in a 2&pi; wide interval around a center value.
  1208.      * <p>This method has three main uses:</p>
  1209.      * <ul>
  1210.      *   <li>normalize an angle between 0 and 2&pi;:<br/>
  1211.      *       {@code a = MathUtils.normalizeAngle(a, FastMath.PI);}</li>
  1212.      *   <li>normalize an angle between -&pi; and +&pi;<br/>
  1213.      *       {@code a = MathUtils.normalizeAngle(a, 0.0);}</li>
  1214.      *   <li>compute the angle between two defining angular positions:<br>
  1215.      *       {@code angle = MathUtils.normalizeAngle(end, start) - start;}</li>
  1216.      * </ul>
  1217.      * <p>Note that due to numerical accuracy and since &pi; cannot be represented
  1218.      * exactly, the result interval is <em>closed</em>, it cannot be half-closed
  1219.      * as would be more satisfactory in a purely mathematical view.</p>
  1220.      * @param a angle to normalize
  1221.      * @param center center of the desired 2&pi; interval for the result
  1222.      * @param <T> the type of the field elements
  1223.      * @return a-2k&pi; with integer k and center-&pi; &lt;= a-2k&pi; &lt;= center+&pi;
  1224.      */
  1225.     public static <T extends RealFieldElement<T>> T normalizeAngle(final T a, final T center) {
  1226.         return a.subtract(2 * FastMath.PI * FastMath.floor((a.getReal() + FastMath.PI - center.getReal()) / (2 * FastMath.PI)));
  1227.     }

  1228.     @Override
  1229.     public CircularOrbit toOrbit() {
  1230.         if (hasDerivatives()) {
  1231.             return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1232.                                      i.getReal(), raan.getReal(), alphaV.getReal(),
  1233.                                      aDot.getReal(), exDot.getReal(), eyDot.getReal(),
  1234.                                      iDot.getReal(), raanDot.getReal(), alphaVDot.getReal(),
  1235.                                      PositionAngle.TRUE, getFrame(),
  1236.                                      getDate().toAbsoluteDate(), getMu());
  1237.         } else {
  1238.             return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1239.                                      i.getReal(), raan.getReal(), alphaV.getReal(),
  1240.                                      PositionAngle.TRUE, getFrame(),
  1241.                                      getDate().toAbsoluteDate(), getMu());
  1242.         }
  1243.     }


  1244. }