FieldCircularOrbit.java

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

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.FieldSinCos;
  24. import org.hipparchus.util.MathArrays;
  25. import org.orekit.errors.OrekitIllegalArgumentException;
  26. import org.orekit.errors.OrekitInternalError;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.time.FieldAbsoluteDate;
  30. import org.orekit.utils.FieldPVCoordinates;
  31. import org.orekit.utils.TimeStampedFieldPVCoordinates;


  32. /**
  33.  * This class handles circular orbital parameters.

  34.  * <p>
  35.  * The parameters used internally are the circular elements which can be
  36.  * related to Keplerian elements as follows:
  37.  *   <ul>
  38.  *     <li>a</li>
  39.  *     <li>e<sub>x</sub> = e cos(ω)</li>
  40.  *     <li>e<sub>y</sub> = e sin(ω)</li>
  41.  *     <li>i</li>
  42.  *     <li>Ω</li>
  43.  *     <li>α<sub>v</sub> = v + ω</li>
  44.  *   </ul>
  45.  * where Ω stands for the Right Ascension of the Ascending Node and
  46.  * α<sub>v</sub> stands for the true latitude argument
  47.  * <p>
  48.  * The conversion equations from and to Keplerian elements given above hold only
  49.  * when both sides are unambiguously defined, i.e. when orbit is neither equatorial
  50.  * nor circular. When orbit is circular (but not equatorial), the circular
  51.  * parameters are still unambiguously defined whereas some Keplerian elements
  52.  * (more precisely ω and Ω) become ambiguous. When orbit is equatorial,
  53.  * neither the Keplerian nor the circular parameters can be defined unambiguously.
  54.  * {@link EquinoctialOrbit equinoctial orbits} is the recommended way to represent
  55.  * orbits.
  56.  * </p>
  57.  * <p>
  58.  * The instance <code>CircularOrbit</code> is guaranteed to be immutable.
  59.  * </p>
  60.  * @see    Orbit
  61.  * @see    KeplerianOrbit
  62.  * @see    CartesianOrbit
  63.  * @see    EquinoctialOrbit
  64.  * @author Luc Maisonobe
  65.  * @author Fabien Maussion
  66.  * @author V&eacute;ronique Pommier-Maurussane
  67.  * @since 9.0
  68.  * @param <T> type of the field elements
  69.  */

  70. public class FieldCircularOrbit<T extends CalculusFieldElement<T>> extends FieldOrbit<T> implements PositionAngleBased {

  71.     /** Semi-major axis (m). */
  72.     private final T a;

  73.     /** First component of the circular eccentricity vector. */
  74.     private final T ex;

  75.     /** Second component of the circular eccentricity vector. */
  76.     private final T ey;

  77.     /** Inclination (rad). */
  78.     private final T i;

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

  81.     /** True latitude argument (rad). */
  82.     private final T alphaV;

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

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

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

  89.     /** Inclination derivative (rad/s). */
  90.     private final T iDot;

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

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

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

  97.     /** Creates a new instance.
  98.      * @param a  semi-major axis (m)
  99.      * @param ex e cos(ω), first component of circular eccentricity vector
  100.      * @param ey e sin(ω), second component of circular eccentricity vector
  101.      * @param i inclination (rad)
  102.      * @param raan right ascension of ascending node (Ω, rad)
  103.      * @param alpha  an + ω, mean, eccentric or true latitude argument (rad)
  104.      * @param type type of latitude argument
  105.      * @param frame the frame in which are defined the parameters
  106.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  107.      * @param date date of the orbital parameters
  108.      * @param mu central attraction coefficient (m³/s²)
  109.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  110.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  111.      */
  112.     public FieldCircularOrbit(final T a, final T ex, final T ey,
  113.                               final T i, final T raan,
  114.                               final T alpha, final PositionAngleType type,
  115.                               final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  116.         throws IllegalArgumentException {
  117.         this(a, ex, ey, i, raan, alpha,
  118.              null, null, null, null, null, null,
  119.              type, frame, date, mu);
  120.     }

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

  154.         this.a       =  a;
  155.         this.aDot    =  aDot;
  156.         this.ex      = ex;
  157.         this.exDot   = exDot;
  158.         this.ey      = ey;
  159.         this.eyDot   = eyDot;
  160.         this.i       = i;
  161.         this.iDot    = iDot;
  162.         this.raan    = raan;
  163.         this.raanDot = raanDot;

  164.         if (hasDerivatives()) {
  165.             final FieldUnivariateDerivative1<T> exUD    = new FieldUnivariateDerivative1<>(ex,    exDot);
  166.             final FieldUnivariateDerivative1<T> eyUD    = new FieldUnivariateDerivative1<>(ey,    eyDot);
  167.             final FieldUnivariateDerivative1<T> alphaUD = new FieldUnivariateDerivative1<>(alpha, alphaDot);
  168.             final FieldUnivariateDerivative1<T> alphavUD;
  169.             switch (type) {
  170.                 case MEAN :
  171.                     alphavUD = eccentricToTrue(meanToEccentric(alphaUD, exUD, eyUD), exUD, eyUD);
  172.                     break;
  173.                 case ECCENTRIC :
  174.                     alphavUD = eccentricToTrue(alphaUD, exUD, eyUD);
  175.                     break;
  176.                 case TRUE :
  177.                     alphavUD = alphaUD;
  178.                     break;
  179.                 default :
  180.                     throw new OrekitInternalError(null);
  181.             }
  182.             this.alphaV    = alphavUD.getValue();
  183.             this.alphaVDot = alphavUD.getDerivative(1);
  184.         } else {
  185.             switch (type) {
  186.                 case MEAN :
  187.                     this.alphaV = eccentricToTrue(meanToEccentric(alpha, ex, ey), ex, ey);
  188.                     break;
  189.                 case ECCENTRIC :
  190.                     this.alphaV = eccentricToTrue(alpha, ex, ey);
  191.                     break;
  192.                 case TRUE :
  193.                     this.alphaV = alpha;
  194.                     break;
  195.                 default :
  196.                     throw new OrekitInternalError(null);
  197.             }
  198.             this.alphaVDot = null;
  199.         }

  200.         this.partialPV = null;

  201.     }

  202.     /** Constructor from Cartesian parameters.
  203.      *
  204.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  205.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  206.      * use {@code mu} and the position to compute the acceleration, including
  207.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  208.      *
  209.      * @param pvCoordinates the {@link FieldPVCoordinates} in inertial frame
  210.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  211.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  212.      * @param mu central attraction coefficient (m³/s²)
  213.      * @exception IllegalArgumentException if frame is not a {@link
  214.      * Frame#isPseudoInertial pseudo-inertial frame}
  215.      */
  216.     public FieldCircularOrbit(final TimeStampedFieldPVCoordinates<T> pvCoordinates,
  217.                               final Frame frame, final T mu)
  218.         throws IllegalArgumentException {
  219.         super(pvCoordinates, frame, mu);

  220.         // compute semi-major axis
  221.         final FieldVector3D<T> pvP = pvCoordinates.getPosition();
  222.         final FieldVector3D<T> pvV = pvCoordinates.getVelocity();
  223.         final FieldVector3D<T> pvA = pvCoordinates.getAcceleration();
  224.         final T r2 = pvP.getNormSq();
  225.         final T r  = r2.sqrt();
  226.         final T V2 = pvV.getNormSq();
  227.         final T rV2OnMu = r.multiply(V2).divide(mu);

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

  229.         if (!isElliptical()) {
  230.             throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS,
  231.                                                      getClass().getName());
  232.         }

  233.         // compute inclination
  234.         final FieldVector3D<T> momentum = pvCoordinates.getMomentum();
  235.         final FieldVector3D<T> plusK = FieldVector3D.getPlusK(r.getField());
  236.         i = FieldVector3D.angle(momentum, plusK);

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

  240.         // 2D-coordinates in the canonical frame
  241.         final FieldSinCos<T> scRaan = FastMath.sinCos(raan);
  242.         final FieldSinCos<T> scI    = FastMath.sinCos(i);
  243.         final T xP      = pvP.getX();
  244.         final T yP      = pvP.getY();
  245.         final T zP      = pvP.getZ();
  246.         final T x2      = (xP.multiply(scRaan.cos()).add(yP .multiply(scRaan.sin()))).divide(a);
  247.         final T y2      = (yP.multiply(scRaan.cos()).subtract(xP.multiply(scRaan.sin()))).multiply(scI.cos()).add(zP.multiply(scI.sin())).divide(a);

  248.         // compute eccentricity vector
  249.         final T eSE    = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt());
  250.         final T eCE    = rV2OnMu.subtract(1);
  251.         final T e2     = eCE.multiply(eCE).add(eSE.multiply(eSE));
  252.         final T f      = eCE.subtract(e2);
  253.         final T g      = eSE.multiply(e2.negate().add(1).sqrt());
  254.         final T aOnR   = a.divide(r);
  255.         final T a2OnR2 = aOnR.multiply(aOnR);
  256.         ex = a2OnR2.multiply(f.multiply(x2).add(g.multiply(y2)));
  257.         ey = a2OnR2.multiply(f.multiply(y2).subtract(g.multiply(x2)));

  258.         // compute latitude argument
  259.         final T beta = (ex.multiply(ex).add(ey.multiply(ey)).negate().add(1)).sqrt().add(1).reciprocal();
  260.         alphaV = eccentricToTrue(y2.add(ey).add(eSE.multiply(beta).multiply(ex)).atan2(x2.add(ex).subtract(eSE.multiply(beta).multiply(ey))),
  261.                                  ex, ey);

  262.         partialPV = pvCoordinates;

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

  265.             final T[][] jacobian = MathArrays.buildArray(a.getField(), 6, 6);
  266.             getJacobianWrtCartesian(PositionAngleType.MEAN, jacobian);

  267.             final FieldVector3D<T> keplerianAcceleration    = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), pvP);
  268.             final FieldVector3D<T> nonKeplerianAcceleration = pvA.subtract(keplerianAcceleration);
  269.             final T   aX                       = nonKeplerianAcceleration.getX();
  270.             final T   aY                       = nonKeplerianAcceleration.getY();
  271.             final T   aZ                       = nonKeplerianAcceleration.getZ();
  272.             aDot    = jacobian[0][3].multiply(aX).add(jacobian[0][4].multiply(aY)).add(jacobian[0][5].multiply(aZ));
  273.             exDot   = jacobian[1][3].multiply(aX).add(jacobian[1][4].multiply(aY)).add(jacobian[1][5].multiply(aZ));
  274.             eyDot   = jacobian[2][3].multiply(aX).add(jacobian[2][4].multiply(aY)).add(jacobian[2][5].multiply(aZ));
  275.             iDot    = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ));
  276.             raanDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ));

  277.             // in order to compute true anomaly derivative, we must compute
  278.             // mean anomaly derivative including Keplerian motion and convert to true anomaly
  279.             final T alphaMDot = getKeplerianMeanMotion().
  280.                                 add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ));
  281.             final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex, exDot);
  282.             final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey, eyDot);
  283.             final FieldUnivariateDerivative1<T> alphaMUD = new FieldUnivariateDerivative1<>(getAlphaM(), alphaMDot);
  284.             final FieldUnivariateDerivative1<T> alphavUD = eccentricToTrue(meanToEccentric(alphaMUD, exUD, eyUD), exUD, eyUD);
  285.             alphaVDot = alphavUD.getDerivative(1);

  286.         } else {
  287.             // acceleration is either almost zero or NaN,
  288.             // we assume acceleration was not known
  289.             // we don't set up derivatives
  290.             aDot      = null;
  291.             exDot     = null;
  292.             eyDot     = null;
  293.             iDot      = null;
  294.             raanDot   = null;
  295.             alphaVDot = null;
  296.         }

  297.     }

  298.     /** Constructor from Cartesian parameters.
  299.      *
  300.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  301.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  302.      * use {@code mu} and the position to compute the acceleration, including
  303.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  304.      *
  305.      * @param PVCoordinates the {@link FieldPVCoordinates} in inertial frame
  306.      * @param frame the frame in which are defined the {@link FieldPVCoordinates}
  307.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  308.      * @param date date of the orbital parameters
  309.      * @param mu central attraction coefficient (m³/s²)
  310.      * @exception IllegalArgumentException if frame is not a {@link
  311.      * Frame#isPseudoInertial pseudo-inertial frame}
  312.      */
  313.     public FieldCircularOrbit(final FieldPVCoordinates<T> PVCoordinates, final Frame frame,
  314.                               final FieldAbsoluteDate<T> date, final T mu)
  315.         throws IllegalArgumentException {
  316.         this(new TimeStampedFieldPVCoordinates<>(date, PVCoordinates), frame, mu);
  317.     }

  318.     /** Constructor from any kind of orbital parameters.
  319.      * @param op orbital parameters to copy
  320.      */
  321.     public FieldCircularOrbit(final FieldOrbit<T> op) {
  322.         super(op.getFrame(), op.getDate(), op.getMu());
  323.         a    = op.getA();
  324.         i    = op.getI();
  325.         final T hx = op.getHx();
  326.         final T hy = op.getHy();
  327.         final T h2 = hx.multiply(hx).add(hy.multiply(hy));
  328.         final T h  = h2.sqrt();
  329.         raan = hy.atan2(hx);
  330.         final FieldSinCos<T> scRaan = FastMath.sinCos(raan);
  331.         final T cosRaan = h.getReal() == 0 ? scRaan.cos() : hx.divide(h);
  332.         final T sinRaan = h.getReal() == 0 ? scRaan.sin() : hy.divide(h);
  333.         final T equiEx = op.getEquinoctialEx();
  334.         final T equiEy = op.getEquinoctialEy();
  335.         ex   = equiEx.multiply(cosRaan).add(equiEy.multiply(sinRaan));
  336.         ey   = equiEy.multiply(cosRaan).subtract(equiEx.multiply(sinRaan));
  337.         alphaV = op.getLv().subtract(raan);

  338.         if (op.hasDerivatives()) {
  339.             aDot      = op.getADot();
  340.             final T      hxDot = op.getHxDot();
  341.             final T      hyDot = op.getHyDot();
  342.             iDot      = cosRaan.multiply(hxDot).add(sinRaan.multiply(hyDot)).multiply(2).divide(h2.add(1));
  343.             raanDot   = hx.multiply(hyDot).subtract(hy.multiply(hxDot)).divide(h2);
  344.             final T equiExDot = op.getEquinoctialExDot();
  345.             final T equiEyDot = op.getEquinoctialEyDot();
  346.             exDot     = equiExDot.add(equiEy.multiply(raanDot)).multiply(cosRaan).
  347.                         add(equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(sinRaan));
  348.             eyDot     = equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(cosRaan).
  349.                         subtract(equiExDot.add(equiEy.multiply(raanDot)).multiply(sinRaan));
  350.             alphaVDot = op.getLvDot().subtract(raanDot);
  351.         } else {
  352.             aDot      = null;
  353.             exDot     = null;
  354.             eyDot     = null;
  355.             iDot      = null;
  356.             raanDot   = null;
  357.             alphaVDot = null;
  358.         }

  359.         partialPV = null;

  360.     }

  361.     /** Constructor from Field and CircularOrbit.
  362.      * <p>Build a FieldCircularOrbit from non-Field CircularOrbit.</p>
  363.      * @param field CalculusField to base object on
  364.      * @param op non-field orbit with only "constant" terms
  365.      * @since 12.0
  366.      */
  367.     public FieldCircularOrbit(final Field<T> field, final CircularOrbit op) {
  368.         super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu()));

  369.         a    = getZero().add(op.getA());
  370.         i    = getZero().add(op.getI());
  371.         raan = getZero().add(op.getRightAscensionOfAscendingNode());
  372.         ex   = getZero().add(op.getCircularEx());
  373.         ey   = getZero().add(op.getCircularEy());
  374.         alphaV = getZero().add(op.getAlphaV());

  375.         if (op.hasDerivatives()) {
  376.             aDot      = getZero().add(op.getADot());
  377.             iDot      = getZero().add(op.getIDot());
  378.             raanDot   = getZero().add(op.getRightAscensionOfAscendingNodeDot());
  379.             exDot     = getZero().add(op.getCircularExDot());
  380.             eyDot     = getZero().add(op.getCircularEyDot());
  381.             alphaVDot = getZero().add(op.getAlphaVDot());
  382.         } else {
  383.             aDot      = null;
  384.             exDot     = null;
  385.             eyDot     = null;
  386.             iDot      = null;
  387.             raanDot   = null;
  388.             alphaVDot = null;
  389.         }

  390.         partialPV = null;

  391.     }

  392.     /** Constructor from Field and Orbit.
  393.      * <p>Build a FieldCircularOrbit from any non-Field Orbit.</p>
  394.      * @param field CalculusField to base object on
  395.      * @param op non-field orbit with only "constant" terms
  396.      * @since 12.0
  397.      */
  398.     public FieldCircularOrbit(final Field<T> field, final Orbit op) {
  399.         this(field, new CircularOrbit(op));
  400.     }

  401.     /** {@inheritDoc} */
  402.     public OrbitType getType() {
  403.         return OrbitType.CIRCULAR;
  404.     }

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

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

  413.     /** {@inheritDoc} */
  414.     public T getEquinoctialEx() {
  415.         final FieldSinCos<T> sc = FastMath.sinCos(raan);
  416.         return ex.multiply(sc.cos()).subtract(ey.multiply(sc.sin()));
  417.     }

  418.     /** {@inheritDoc} */
  419.     public T getEquinoctialExDot() {

  420.         if (!hasDerivatives()) {
  421.             return null;
  422.         }

  423.         final FieldSinCos<T> sc = FastMath.sinCos(raan);
  424.         return exDot.subtract(ey.multiply(raanDot)).multiply(sc.cos()).
  425.                subtract(eyDot.add(ex.multiply(raanDot)).multiply(sc.sin()));

  426.     }

  427.     /** {@inheritDoc} */
  428.     public T getEquinoctialEy() {
  429.         final FieldSinCos<T> sc = FastMath.sinCos(raan);
  430.         return ey.multiply(sc.cos()).add(ex.multiply(sc.sin()));
  431.     }

  432.     /** {@inheritDoc} */
  433.     public T getEquinoctialEyDot() {

  434.         if (!hasDerivatives()) {
  435.             return null;
  436.         }

  437.         final FieldSinCos<T> sc = FastMath.sinCos(raan);
  438.         return eyDot.add(ex.multiply(raanDot)).multiply(sc.cos()).
  439.                add(exDot.subtract(ey.multiply(raanDot)).multiply(sc.sin()));

  440.     }

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

  447.     /** Get the first component of the circular eccentricity vector derivative.
  448.      * @return d(ex)/dt = d(e cos(ω))/dt, first component of the circular eccentricity vector derivative
  449.      */
  450.     public T getCircularExDot() {
  451.         return exDot;
  452.     }

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

  459.     /** Get the second component of the circular eccentricity vector derivative.
  460.      * @return d(ey)/dt = d(e sin(ω))/dt, second component of the circular eccentricity vector derivative
  461.      */
  462.     public T getCircularEyDot() {
  463.         return eyDot;
  464.     }

  465.     /** {@inheritDoc} */
  466.     public T getHx() {
  467.         // Check for equatorial retrograde orbit
  468.         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
  469.             return getZero().add(Double.NaN);
  470.         }
  471.         return raan.cos().multiply(i.divide(2).tan());
  472.     }

  473.     /** {@inheritDoc} */
  474.     public T getHxDot() {

  475.         if (!hasDerivatives()) {
  476.             return null;
  477.         }

  478.         // Check for equatorial retrograde orbit
  479.         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
  480.             return getZero().add(Double.NaN);
  481.         }

  482.         final FieldSinCos<T> sc = FastMath.sinCos(raan);
  483.         final T tan             = i.multiply(0.5).tan();
  484.         return sc.cos().multiply(0.5).multiply(tan.multiply(tan).add(1)).multiply(iDot).
  485.                subtract(sc.sin().multiply(tan).multiply(raanDot));

  486.     }

  487.     /** {@inheritDoc} */
  488.     public T getHy() {
  489.         // Check for equatorial retrograde orbit
  490.         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
  491.             return getZero().add(Double.NaN);
  492.         }
  493.         return raan.sin().multiply(i.divide(2).tan());
  494.     }

  495.     /** {@inheritDoc} */
  496.     public T getHyDot() {

  497.         if (!hasDerivatives()) {
  498.             return null;
  499.         }

  500.         // Check for equatorial retrograde orbit
  501.         if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) {
  502.             return getZero().add(Double.NaN);
  503.         }

  504.         final FieldSinCos<T> sc = FastMath.sinCos(raan);
  505.         final T tan     = i.multiply(0.5).tan();
  506.         return sc.sin().multiply(0.5).multiply(tan.multiply(tan).add(1)).multiply(iDot).
  507.                add(sc.cos().multiply(tan).multiply(raanDot));

  508.     }

  509.     /** Get the true latitude argument.
  510.      * @return v + ω true latitude argument (rad)
  511.      */
  512.     public T getAlphaV() {
  513.         return alphaV;
  514.     }

  515.     /** Get the true latitude argument derivative.
  516.      * @return d(v + ω)/dt true latitude argument derivative (rad/s)
  517.      */
  518.     public T getAlphaVDot() {
  519.         return alphaVDot;
  520.     }

  521.     /** Get the eccentric latitude argument.
  522.      * @return E + ω eccentric latitude argument (rad)
  523.      */
  524.     public T getAlphaE() {
  525.         return trueToEccentric(alphaV, ex, ey);
  526.     }

  527.     /** Get the eccentric latitude argument derivative.
  528.      * @return d(E + ω)/dt eccentric latitude argument derivative (rad/s)
  529.      */
  530.     public T getAlphaEDot() {

  531.         if (!hasDerivatives()) {
  532.             return null;
  533.         }

  534.         final FieldUnivariateDerivative1<T> alphaVUD = new FieldUnivariateDerivative1<>(alphaV, alphaVDot);
  535.         final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex, exDot);
  536.         final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey, eyDot);
  537.         final FieldUnivariateDerivative1<T> alphaEUD = trueToEccentric(alphaVUD, exUD, eyUD);
  538.         return alphaEUD.getDerivative(1);

  539.     }

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

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

  550.         if (!hasDerivatives()) {
  551.             return null;
  552.         }

  553.         final FieldUnivariateDerivative1<T> alphaVUD = new FieldUnivariateDerivative1<>(alphaV, alphaVDot);
  554.         final FieldUnivariateDerivative1<T> exUD     = new FieldUnivariateDerivative1<>(ex, exDot);
  555.         final FieldUnivariateDerivative1<T> eyUD     = new FieldUnivariateDerivative1<>(ey, eyDot);
  556.         final FieldUnivariateDerivative1<T> alphaMUD = eccentricToMean(trueToEccentric(alphaVUD, exUD, eyUD), exUD, eyUD);
  557.         return alphaMUD.getDerivative(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 PositionAngleType type) {
  564.         return (type == PositionAngleType.MEAN) ? getAlphaM() :
  565.                                               ((type == PositionAngleType.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 PositionAngleType type) {
  573.         return (type == PositionAngleType.MEAN) ? getAlphaMDot() :
  574.                                               ((type == PositionAngleType.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 CalculusFieldElement<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 FieldSinCos<T> scAlphaE = FastMath.sinCos(alphaE);
  587.         return alphaE.add(ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos())).divide(
  588.                                       epsilon.add(1).subtract(ex.multiply(scAlphaE.cos())).subtract(
  589.                                       ey.multiply(scAlphaE.sin()))).atan().multiply(2));
  590.     }

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

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

  615.         T alphaE                = alphaM;
  616.         T shift                 = alphaM.getField().getZero();
  617.         T alphaEMalphaM         = alphaM.getField().getZero();
  618.         FieldSinCos<T> scAlphaE = FastMath.sinCos(alphaE);
  619.         int    iter     = 0;
  620.         do {
  621.             final T f2 = ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos()));
  622.             final T f1 = ex.negate().multiply(scAlphaE.cos()).subtract(ey.multiply(scAlphaE.sin())).add(1);
  623.             final T f0 = alphaEMalphaM.subtract(f2);

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

  626.             alphaEMalphaM  = alphaEMalphaM.subtract(shift);
  627.             alphaE         = alphaM.add(alphaEMalphaM);
  628.             scAlphaE       = FastMath.sinCos(alphaE);
  629.         } while (++iter < 50 && FastMath.abs(shift.getReal()) > 1.0e-12);
  630.         return alphaE;

  631.     }

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

  643.     /** {@inheritDoc} */
  644.     public T getE() {
  645.         return ex.multiply(ex).add(ey.multiply(ey)).sqrt();
  646.     }

  647.     /** {@inheritDoc} */
  648.     public T getEDot() {

  649.         if (!hasDerivatives()) {
  650.             return null;
  651.         }

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

  653.     }

  654.     /** {@inheritDoc} */
  655.     public T getI() {
  656.         return i;
  657.     }

  658.     /** {@inheritDoc} */
  659.     public T getIDot() {
  660.         return iDot;
  661.     }

  662.     /** Get the right ascension of the ascending node.
  663.      * @return right ascension of the ascending node (rad)
  664.      */
  665.     public T getRightAscensionOfAscendingNode() {
  666.         return raan;
  667.     }

  668.     /** Get the right ascension of the ascending node derivative.
  669.      * @return right ascension of the ascending node derivative (rad/s)
  670.      */
  671.     public T getRightAscensionOfAscendingNodeDot() {
  672.         return raanDot;
  673.     }

  674.     /** {@inheritDoc} */
  675.     public T getLv() {
  676.         return alphaV.add(raan);
  677.     }

  678.     /** {@inheritDoc} */
  679.     public T getLvDot() {
  680.         return hasDerivatives() ? alphaVDot.add(raanDot) : null;
  681.     }

  682.     /** {@inheritDoc} */
  683.     public T getLE() {
  684.         return getAlphaE().add(raan);
  685.     }

  686.     /** {@inheritDoc} */
  687.     public T getLEDot() {
  688.         return hasDerivatives() ? getAlphaEDot().add(raanDot) : null;
  689.     }

  690.     /** {@inheritDoc} */
  691.     public T getLM() {
  692.         return getAlphaM().add(raan);
  693.     }

  694.     /** {@inheritDoc} */
  695.     public T getLMDot() {
  696.         return hasDerivatives() ? getAlphaMDot().add(raanDot) : null;
  697.     }

  698.     /** {@inheritDoc} */
  699.     @Override
  700.     public boolean hasDerivatives() {
  701.         return aDot != null;
  702.     }

  703.     /** Compute position and velocity but not acceleration.
  704.      */
  705.     private void computePVWithoutA() {

  706.         if (partialPV != null) {
  707.             // already computed
  708.             return;
  709.         }

  710.         // get equinoctial parameters
  711.         final T equEx = getEquinoctialEx();
  712.         final T equEy = getEquinoctialEy();
  713.         final T hx = getHx();
  714.         final T hy = getHy();
  715.         final T lE = getLE();
  716.         // inclination-related intermediate parameters
  717.         final T hx2   = hx.multiply(hx);
  718.         final T hy2   = hy.multiply(hy);
  719.         final T factH = (hx2.add(1).add(hy2)).reciprocal();

  720.         // reference axes defining the orbital plane
  721.         final T ux = (hx2.add(1).subtract(hy2)).multiply(factH);
  722.         final T uy =  hx.multiply(2).multiply(hy).multiply(factH);
  723.         final T uz = hy.multiply(-2).multiply(factH);

  724.         final T vx = uy;
  725.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  726.         final T vz =  hx.multiply(factH).multiply(2);

  727.         // eccentricity-related intermediate parameters
  728.         final T exey = equEx.multiply(equEy);
  729.         final T ex2  = equEx.multiply(equEx);
  730.         final T ey2  = equEy.multiply(equEy);
  731.         final T e2   = ex2.add(ey2);
  732.         final T eta  = e2.negate().add(1).sqrt().add(1);
  733.         final T beta = eta.reciprocal();

  734.         // eccentric latitude argument
  735.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  736.         final T cLe    = scLe.cos();
  737.         final T sLe    = scLe.sin();
  738.         final T exCeyS = equEx.multiply(cLe).add(equEy.multiply(sLe));
  739.         // coordinates of position and velocity in the orbital plane
  740.         final T x      = a.multiply(beta.negate().multiply(ey2).add(1).multiply(cLe).add(beta.multiply(exey).multiply(sLe)).subtract(equEx));
  741.         final T y      = a.multiply(beta.negate().multiply(ex2).add(1).multiply(sLe).add(beta.multiply(exey).multiply(cLe)).subtract(equEy));

  742.         final T factor = getOne().add(getMu()).divide(a).sqrt().divide(exCeyS.negate().add(1));
  743.         final T xdot   = factor.multiply( beta.multiply(equEy).multiply(exCeyS).subtract(sLe ));
  744.         final T ydot   = factor.multiply( cLe.subtract(beta.multiply(equEx).multiply(exCeyS)));

  745.         final FieldVector3D<T> position = new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  746.                                                               x.multiply(uy).add(y.multiply(vy)),
  747.                                                               x.multiply(uz).add(y.multiply(vz)));
  748.         final FieldVector3D<T> velocity = new FieldVector3D<>(xdot.multiply(ux).add(ydot.multiply(vx)),
  749.                                                               xdot.multiply(uy).add(ydot.multiply(vy)),
  750.                                                               xdot.multiply(uz).add(ydot.multiply(vz)));

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

  752.     }

  753.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  754.      * <p>
  755.      * This method should be called only when {@link #hasDerivatives()} returns true.
  756.      * </p>
  757.      * @return non-Keplerian part of the acceleration
  758.      */
  759.     private FieldVector3D<T> nonKeplerianAcceleration() {

  760.         final T[][] dCdP = MathArrays.buildArray(a.getField(), 6, 6);
  761.         getJacobianWrtParameters(PositionAngleType.MEAN, dCdP);

  762.         final T nonKeplerianMeanMotion = getAlphaMDot().subtract(getKeplerianMeanMotion());
  763.         final T nonKeplerianAx =     dCdP[3][0].multiply(aDot).
  764.                                  add(dCdP[3][1].multiply(exDot)).
  765.                                  add(dCdP[3][2].multiply(eyDot)).
  766.                                  add(dCdP[3][3].multiply(iDot)).
  767.                                  add(dCdP[3][4].multiply(raanDot)).
  768.                                  add(dCdP[3][5].multiply(nonKeplerianMeanMotion));
  769.         final T nonKeplerianAy =     dCdP[4][0].multiply(aDot).
  770.                                  add(dCdP[4][1].multiply(exDot)).
  771.                                  add(dCdP[4][2].multiply(eyDot)).
  772.                                  add(dCdP[4][3].multiply(iDot)).
  773.                                  add(dCdP[4][4].multiply(raanDot)).
  774.                                  add(dCdP[4][5].multiply(nonKeplerianMeanMotion));
  775.         final T nonKeplerianAz =     dCdP[5][0].multiply(aDot).
  776.                                  add(dCdP[5][1].multiply(exDot)).
  777.                                  add(dCdP[5][2].multiply(eyDot)).
  778.                                  add(dCdP[5][3].multiply(iDot)).
  779.                                  add(dCdP[5][4].multiply(raanDot)).
  780.                                  add(dCdP[5][5].multiply(nonKeplerianMeanMotion));

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

  782.     }

  783.     /** {@inheritDoc} */
  784.     protected FieldVector3D<T> initPosition() {
  785.         // get equinoctial parameters
  786.         final T equEx = getEquinoctialEx();
  787.         final T equEy = getEquinoctialEy();
  788.         final T hx = getHx();
  789.         final T hy = getHy();
  790.         final T lE = getLE();
  791.         // inclination-related intermediate parameters
  792.         final T hx2   = hx.multiply(hx);
  793.         final T hy2   = hy.multiply(hy);
  794.         final T factH = (hx2.add(1).add(hy2)).reciprocal();

  795.         // reference axes defining the orbital plane
  796.         final T ux = (hx2.add(1).subtract(hy2)).multiply(factH);
  797.         final T uy =  hx.multiply(2).multiply(hy).multiply(factH);
  798.         final T uz = hy.multiply(-2).multiply(factH);

  799.         final T vx = uy;
  800.         final T vy = (hy2.subtract(hx2).add(1)).multiply(factH);
  801.         final T vz =  hx.multiply(factH).multiply(2);

  802.         // eccentricity-related intermediate parameters
  803.         final T exey = equEx.multiply(equEy);
  804.         final T ex2  = equEx.multiply(equEx);
  805.         final T ey2  = equEy.multiply(equEy);
  806.         final T e2   = ex2.add(ey2);
  807.         final T eta  = e2.negate().add(1).sqrt().add(1);
  808.         final T beta = eta.reciprocal();

  809.         // eccentric latitude argument
  810.         final FieldSinCos<T> scLe = FastMath.sinCos(lE);
  811.         final T cLe    = scLe.cos();
  812.         final T sLe    = scLe.sin();

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

  816.         return new FieldVector3D<>(x.multiply(ux).add(y.multiply(vx)),
  817.                                    x.multiply(uy).add(y.multiply(vy)),
  818.                                    x.multiply(uz).add(y.multiply(vz)));

  819.     }

  820.     /** {@inheritDoc} */
  821.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {

  822.         // position and velocity
  823.         computePVWithoutA();

  824.         // acceleration
  825.         final T r2 = partialPV.getPosition().getNormSq();
  826.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r2.multiply(r2.sqrt()).reciprocal().multiply(getMu().negate()),
  827.                                                                            partialPV.getPosition());
  828.         final FieldVector3D<T> acceleration = hasDerivatives() ?
  829.                                               keplerianAcceleration.add(nonKeplerianAcceleration()) :
  830.                                               keplerianAcceleration;

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

  832.     }

  833.     /** {@inheritDoc} */
  834.     public FieldCircularOrbit<T> shiftedBy(final double dt) {
  835.         return shiftedBy(getZero().add(dt));
  836.     }

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

  839.         // use Keplerian-only motion
  840.         final FieldCircularOrbit<T> keplerianShifted = new FieldCircularOrbit<>(a, ex, ey, i, raan,
  841.                                                                                 getAlphaM().add(getKeplerianMeanMotion().multiply(dt)),
  842.                                                                                 PositionAngleType.MEAN, getFrame(),
  843.                                                                                 getDate().shiftedBy(dt), getMu());

  844.         if (hasDerivatives()) {

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

  847.             // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift
  848.             keplerianShifted.computePVWithoutA();
  849.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(),
  850.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  851.             final T   fixedR2 = fixedP.getNormSq();
  852.             final T   fixedR  = fixedR2.sqrt();
  853.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(),
  854.                                                                  dt, nonKeplerianAcceleration);
  855.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR2.multiply(fixedR).reciprocal().multiply(getMu().negate()),
  856.                                                                  keplerianShifted.partialPV.getPosition(),
  857.                                                                  getOne(), nonKeplerianAcceleration);

  858.             // build a new orbit, taking non-Keplerian acceleration into account
  859.             return new FieldCircularOrbit<>(new TimeStampedFieldPVCoordinates<>(keplerianShifted.getDate(),
  860.                                                                                 fixedP, fixedV, fixedA),
  861.                                             keplerianShifted.getFrame(), keplerianShifted.getMu());

  862.         } else {
  863.             // Keplerian-only motion is all we can do
  864.             return keplerianShifted;
  865.         }

  866.     }

  867.     /** {@inheritDoc} */
  868.     protected T[][] computeJacobianMeanWrtCartesian() {

  869.         final T[][] jacobian = MathArrays.buildArray(getOne().getField(), 6, 6);

  870.         // compute various intermediate parameters
  871.         computePVWithoutA();
  872.         final FieldVector3D<T> position = partialPV.getPosition();
  873.         final FieldVector3D<T> velocity = partialPV.getVelocity();

  874.         final T x          = position.getX();
  875.         final T y          = position.getY();
  876.         final T z          = position.getZ();
  877.         final T vx         = velocity.getX();
  878.         final T vy         = velocity.getY();
  879.         final T vz         = velocity.getZ();
  880.         final T pv         = FieldVector3D.dotProduct(position, velocity);
  881.         final T r2         = position.getNormSq();
  882.         final T r          = r2.sqrt();
  883.         final T v2         = velocity.getNormSq();

  884.         final T mu         = getMu();
  885.         final T oOsqrtMuA  = getOne().divide(a.multiply(mu).sqrt());
  886.         final T rOa        = r.divide(a);
  887.         final T aOr        = a.divide(r);
  888.         final T aOr2       = a.divide(r2);
  889.         final T a2         = a.multiply(a);

  890.         final T ex2        = ex.multiply(ex);
  891.         final T ey2        = ey.multiply(ey);
  892.         final T e2         = ex2.add(ey2);
  893.         final T epsilon    = e2.negate().add(1.0).sqrt();
  894.         final T beta       = epsilon.add(1).reciprocal();

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

  897.         final FieldSinCos<T> scI    = FastMath.sinCos(i);
  898.         final FieldSinCos<T> scRaan = FastMath.sinCos(raan);
  899.         final T cosI       = scI.cos();
  900.         final T sinI       = scI.sin();
  901.         final T cosRaan    = scRaan.cos();
  902.         final T sinRaan    = scRaan.sin();

  903.         // da
  904.         fillHalfRow(aOr.multiply(2.0).multiply(aOr2), position, jacobian[0], 0);
  905.         fillHalfRow(a2.multiply(mu.divide(2.).reciprocal()), velocity, jacobian[0], 3);

  906.         // differentials of the normalized momentum
  907.         final FieldVector3D<T> danP = new FieldVector3D<>(v2, position, pv.negate(), velocity);
  908.         final FieldVector3D<T> danV = new FieldVector3D<>(r2, velocity, pv.negate(), position);
  909.         final T recip   = partialPV.getMomentum().getNorm().reciprocal();
  910.         final T recip2  = recip.multiply(recip);
  911.         final T recip2N = recip2.negate();
  912.         final FieldVector3D<T> dwXP = new FieldVector3D<>(recip,
  913.                                                           new FieldVector3D<>(getZero(), vz, vy.negate()),
  914.                                                           recip2N.multiply(sinRaan).multiply(sinI),
  915.                                                           danP);
  916.         final FieldVector3D<T> dwYP = new FieldVector3D<>(recip,
  917.                                                           new FieldVector3D<>(vz.negate(), getZero(), vx),
  918.                                                           recip2.multiply(cosRaan).multiply(sinI),
  919.                                                           danP);
  920.         final FieldVector3D<T> dwZP = new FieldVector3D<>(recip,
  921.                                                           new FieldVector3D<>(vy, vx.negate(), getZero()),
  922.                                                           recip2N.multiply(cosI),
  923.                                                           danP);
  924.         final FieldVector3D<T> dwXV = new FieldVector3D<>(recip,
  925.                                                           new FieldVector3D<>(getZero(), z.negate(), y),
  926.                                                           recip2N.multiply(sinRaan).multiply(sinI),
  927.                                                           danV);
  928.         final FieldVector3D<T> dwYV = new FieldVector3D<>(recip,
  929.                                                           new FieldVector3D<>(z, getZero(), x.negate()),
  930.                                                           recip2.multiply(cosRaan).multiply(sinI),
  931.                                                           danV);
  932.         final FieldVector3D<T> dwZV = new FieldVector3D<>(recip,
  933.                                                           new FieldVector3D<>(y.negate(), x, getZero()),
  934.                                                           recip2N.multiply(cosI),
  935.                                                           danV);

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

  939.         // dRaan
  940.         fillHalfRow(sinRaan.divide(sinI), dwYP, cosRaan.divide(sinI), dwXP, jacobian[4], 0);
  941.         fillHalfRow(sinRaan.divide(sinI), dwYV, cosRaan.divide(sinI), dwXV, jacobian[4], 3);

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

  947.         // du
  948.         final FieldVector3D<T> duP = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXP,
  949.                                                          cv.multiply(sinRaan).divide(sinI), dwYP,
  950.                                                          getOne(), new FieldVector3D<>(cosRaan, sinRaan, getZero()));
  951.         final FieldVector3D<T> duV = new FieldVector3D<>(cv.multiply(cosRaan).divide(sinI), dwXV,
  952.                                                          cv.multiply(sinRaan).divide(sinI), dwYV);

  953.         // dv
  954.         final FieldVector3D<T> dvP = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXP,
  955.                                                          u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYP,
  956.                                                          cv, dwZP,
  957.                                                          getOne(), new FieldVector3D<>(sinRaan.negate().multiply(cosI), cosRaan.multiply(cosI), sinI));
  958.         final FieldVector3D<T> dvV = new FieldVector3D<>(u.negate().multiply(cosRaan).multiply(cosI).divide(sinI).add(sinRaan.multiply(z)), dwXV,
  959.                                                          u.negate().multiply(sinRaan).multiply(cosI).divide(sinI).subtract(cosRaan.multiply(z)), dwYV,
  960.                                                          cv, dwZV);

  961.         final FieldVector3D<T> dc1P = new FieldVector3D<>(aOr2.multiply(eSinE.multiply(eSinE).multiply(2).add(1).subtract(eCosE)).divide(r2), position,
  962.                                                           aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), velocity);
  963.         final FieldVector3D<T> dc1V = new FieldVector3D<>(aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), position,
  964.                                                           getZero().add(2).divide(mu), velocity);
  965.         final FieldVector3D<T> dc2P = new FieldVector3D<>(aOr2.multiply(eSinE).multiply(eSinE.multiply(eSinE).subtract(e2.negate().add(1))).divide(r2.multiply(epsilon)), position,
  966.                                                           aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), velocity);
  967.         final FieldVector3D<T> dc2V = new FieldVector3D<>(aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), position,
  968.                                                           eSinE.divide(epsilon.multiply(mu)), velocity);

  969.         final T cof1   = aOr2.multiply(eCosE.subtract(e2));
  970.         final T cof2   = aOr2.multiply(epsilon).multiply(eSinE);
  971.         final FieldVector3D<T> dexP = new FieldVector3D<>(u, dc1P,  v, dc2P, cof1, duP,  cof2, dvP);
  972.         final FieldVector3D<T> dexV = new FieldVector3D<>(u, dc1V,  v, dc2V, cof1, duV,  cof2, dvV);
  973.         final FieldVector3D<T> deyP = new FieldVector3D<>(v, dc1P, u.negate(), dc2P, cof1, dvP, cof2.negate(), duP);
  974.         final FieldVector3D<T> deyV = new FieldVector3D<>(v, dc1V, u.negate(), dc2V, cof1, dvV, cof2.negate(), duV);
  975.         fillHalfRow(getOne(), dexP, jacobian[1], 0);
  976.         fillHalfRow(getOne(), dexV, jacobian[1], 3);
  977.         fillHalfRow(getOne(), deyP, jacobian[2], 0);
  978.         fillHalfRow(getOne(), deyV, jacobian[2], 3);

  979.         final T cle = u.divide(a).add(ex).subtract(eSinE.multiply(beta).multiply(ey));
  980.         final T sle = v.divide(a).add(ey).add(eSinE.multiply(beta).multiply(ex));
  981.         final T m1  = beta.multiply(eCosE);
  982.         final T m2  = m1.multiply(eCosE).negate().add(1);
  983.         final T m3  = (u.multiply(ey).subtract(v.multiply(ex))).add(eSinE.multiply(beta).multiply(u.multiply(ex).add(v.multiply(ey))));
  984.         final T m4  = sle.negate().add(cle.multiply(eSinE).multiply(beta));
  985.         final T m5  = cle.add(sle.multiply(eSinE).multiply(beta));
  986.         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);
  987.         final T jj = (m1.multiply(m2).divide(epsilon).subtract(1)).multiply(oOsqrtMuA);
  988.         fillHalfRow(kk, position,
  989.                     jj, velocity,
  990.                     m4, dexP,
  991.                     m5, deyP,
  992.                     sle.negate().divide(a), duP,
  993.                     cle.divide(a), dvP,
  994.                     jacobian[5], 0);
  995.         final T ll = (m1.multiply(m2).divide(epsilon ).subtract(1)).multiply(oOsqrtMuA);
  996.         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);

  997.         fillHalfRow(ll, position,
  998.                     mm, velocity,
  999.                     m4, dexV,
  1000.                     m5, deyV,
  1001.                     sle.negate().divide(a), duV,
  1002.                     cle.divide(a), dvV,
  1003.                     jacobian[5], 3);
  1004.         return jacobian;

  1005.     }

  1006.     /** {@inheritDoc} */
  1007.     protected T[][] computeJacobianEccentricWrtCartesian() {

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

  1010.         // Differentiating the Kepler equation aM = aE - ex sin aE + ey cos aE leads to:
  1011.         // daM = (1 - ex cos aE - ey sin aE) dE - sin aE dex + cos aE dey
  1012.         // which is inverted and rewritten as:
  1013.         // daE = a/r daM + sin aE a/r dex - cos aE a/r dey
  1014.         final T alphaE            = getAlphaE();
  1015.         final FieldSinCos<T> scAe = FastMath.sinCos(alphaE);
  1016.         final T cosAe             = scAe.cos();
  1017.         final T sinAe             = scAe.sin();
  1018.         final T aOr               = getOne().divide(getOne().subtract(ex.multiply(cosAe)).subtract(ey.multiply(sinAe)));

  1019.         // update longitude row
  1020.         final T[] rowEx = jacobian[1];
  1021.         final T[] rowEy = jacobian[2];
  1022.         final T[] rowL  = jacobian[5];
  1023.         for (int j = 0; j < 6; ++j) {
  1024.          // rowL[j] = aOr * (      rowL[j] +   sinAe *        rowEx[j]   -         cosAe *        rowEy[j]);
  1025.             rowL[j] = aOr.multiply(rowL[j].add(sinAe.multiply(rowEx[j])).subtract( cosAe.multiply(rowEy[j])));
  1026.         }
  1027.         jacobian[5] = rowL;
  1028.         return jacobian;

  1029.     }
  1030.     /** {@inheritDoc} */
  1031.     protected T[][] computeJacobianTrueWrtCartesian() {

  1032.         // start by computing the Jacobian with eccentric angle
  1033.         final T[][] jacobian = computeJacobianEccentricWrtCartesian();
  1034.         // Differentiating the eccentric latitude equation
  1035.         // tan((aV - aE)/2) = [ex sin aE - ey cos aE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos aE - ey sin aE]
  1036.         // leads to
  1037.         // cT (daV - daE) = cE daE + cX dex + cY dey
  1038.         // with
  1039.         // cT = [d^2 + (ex sin aE - ey cos aE)^2] / 2
  1040.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos aE - ey sin aE
  1041.         // cE = (ex cos aE + ey sin aE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  1042.         // cX =  sin aE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
  1043.         // cY = -cos aE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
  1044.         // which can be solved to find the differential of the true latitude
  1045.         // daV = (cT + cE) / cT daE + cX / cT deX + cY / cT deX
  1046.         final T alphaE            = getAlphaE();
  1047.         final FieldSinCos<T> scAe = FastMath.sinCos(alphaE);
  1048.         final T cosAe             = scAe.cos();
  1049.         final T sinAe             = scAe.sin();
  1050.         final T eSinE             = ex.multiply(sinAe).subtract(ey.multiply(cosAe));
  1051.         final T ecosE             = ex.multiply(cosAe).add(ey.multiply(sinAe));
  1052.         final T e2                = ex.multiply(ex).add(ey.multiply(ey));
  1053.         final T epsilon           = (getOne().subtract(e2)).sqrt();
  1054.         final T onePeps           = getOne().add(epsilon);
  1055.         final T d                 = onePeps.subtract(ecosE);
  1056.         final T cT                = (d.multiply(d).add(eSinE.multiply(eSinE))).divide(2);
  1057.         final T cE                = ecosE.multiply(onePeps).subtract(e2);
  1058.         final T cX                = ex.multiply(eSinE).divide(epsilon).subtract(ey).add(sinAe.multiply(onePeps));
  1059.         final T cY                = ey.multiply(eSinE).divide(epsilon).add(ex).subtract(cosAe.multiply(onePeps));
  1060.         final T factorLe          = (cT.add(cE)).divide(cT);
  1061.         final T factorEx          = cX.divide(cT);
  1062.         final T factorEy          = cY.divide(cT);

  1063.         // update latitude row
  1064.         final T[] rowEx = jacobian[1];
  1065.         final T[] rowEy = jacobian[2];
  1066.         final T[] rowA = jacobian[5];
  1067.         for (int j = 0; j < 6; ++j) {
  1068.             rowA[j] = factorLe.multiply(rowA[j]).add(factorEx.multiply(rowEx[j])).add(factorEy.multiply(rowEy[j]));
  1069.         }
  1070.         return jacobian;

  1071.     }

  1072.     /** {@inheritDoc} */
  1073.     public void addKeplerContribution(final PositionAngleType type, final T gm,
  1074.                                       final T[] pDot) {
  1075.         final T oMe2;
  1076.         final T ksi;
  1077.         final T n = a.reciprocal().multiply(gm).sqrt().divide(a);
  1078.         final FieldSinCos<T> sc = FastMath.sinCos(alphaV);
  1079.         switch (type) {
  1080.             case MEAN :
  1081.                 pDot[5] = pDot[5].add(n);
  1082.                 break;
  1083.             case ECCENTRIC :
  1084.                 oMe2  = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  1085.                 ksi   = getOne().add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin()));
  1086.                 pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2));
  1087.                 break;
  1088.             case TRUE :
  1089.                 oMe2  = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey));
  1090.                 ksi   = getOne().add(ex.multiply(sc.cos())).add(ey.multiply(sc.sin()));
  1091.                 pDot[5] = pDot[5].add(n.multiply(ksi).multiply(ksi).divide(oMe2.multiply(oMe2.sqrt())));
  1092.                 break;
  1093.             default :
  1094.                 throw new OrekitInternalError(null);
  1095.         }
  1096.     }

  1097.     /**  Returns a string representation of this Orbit object.
  1098.      * @return a string representation of this object
  1099.      */
  1100.     public String toString() {
  1101.         return new StringBuilder().append("circular parameters: ").append('{').
  1102.                                   append("a: ").append(a.getReal()).
  1103.                                   append(", ex: ").append(ex.getReal()).append(", ey: ").append(ey.getReal()).
  1104.                                   append(", i: ").append(FastMath.toDegrees(i.getReal())).
  1105.                                   append(", raan: ").append(FastMath.toDegrees(raan.getReal())).
  1106.                                   append(", alphaV: ").append(FastMath.toDegrees(alphaV.getReal())).
  1107.                                   append(";}").toString();
  1108.     }

  1109.     /** {@inheritDoc} */
  1110.     @Override
  1111.     public PositionAngleType getCachedPositionAngleType() {
  1112.         return PositionAngleType.TRUE;
  1113.     }

  1114.     /** {@inheritDoc} */
  1115.     @Override
  1116.     public boolean hasRates() {
  1117.         return hasDerivatives();
  1118.     }

  1119.     /** {@inheritDoc} */
  1120.     @Override
  1121.     public FieldCircularOrbit<T> removeRates() {
  1122.         final PositionAngleType positionAngleType = getCachedPositionAngleType();
  1123.         return new FieldCircularOrbit<>(getA(), getCircularEx(), getCircularEy(),
  1124.                 getI(), getRightAscensionOfAscendingNode(), getAlpha(positionAngleType),
  1125.                 positionAngleType, getFrame(), getDate(), getMu());
  1126.     }

  1127.     /** {@inheritDoc} */
  1128.     @Override
  1129.     public CircularOrbit toOrbit() {
  1130.         if (hasDerivatives()) {
  1131.             return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1132.                                      i.getReal(), raan.getReal(), alphaV.getReal(),
  1133.                                      aDot.getReal(), exDot.getReal(), eyDot.getReal(),
  1134.                                      iDot.getReal(), raanDot.getReal(), alphaVDot.getReal(),
  1135.                                      PositionAngleType.TRUE, getFrame(),
  1136.                                      getDate().toAbsoluteDate(), getMu().getReal());
  1137.         } else {
  1138.             return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(),
  1139.                                      i.getReal(), raan.getReal(), alphaV.getReal(),
  1140.                                      PositionAngleType.TRUE, getFrame(),
  1141.                                      getDate().toAbsoluteDate(), getMu().getReal());
  1142.         }
  1143.     }


  1144. }