FieldCartesianOrbit.java

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


  18. import java.util.Arrays;
  19. import java.util.HashMap;
  20. import java.util.Map;
  21. import java.util.stream.Stream;

  22. import org.hipparchus.Field;
  23. import org.hipparchus.RealFieldElement;
  24. import org.hipparchus.analysis.differentiation.FDSFactory;
  25. import org.hipparchus.analysis.differentiation.FieldDerivativeStructure;
  26. import org.hipparchus.exception.LocalizedCoreFormats;
  27. import org.hipparchus.exception.MathIllegalStateException;
  28. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  29. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  30. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  31. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  32. import org.hipparchus.util.FastMath;
  33. import org.hipparchus.util.MathArrays;
  34. import org.orekit.errors.OrekitMessages;
  35. import org.orekit.frames.Frame;
  36. import org.orekit.time.AbsoluteDate;
  37. import org.orekit.time.FieldAbsoluteDate;
  38. import org.orekit.utils.CartesianDerivativesFilter;
  39. import org.orekit.utils.FieldPVCoordinates;
  40. import org.orekit.utils.PVCoordinates;
  41. import org.orekit.utils.TimeStampedFieldPVCoordinates;


  42. /** This class holds Cartesian orbital parameters.

  43.  * <p>
  44.  * The parameters used internally are the Cartesian coordinates:
  45.  *   <ul>
  46.  *     <li>x</li>
  47.  *     <li>y</li>
  48.  *     <li>z</li>
  49.  *     <li>xDot</li>
  50.  *     <li>yDot</li>
  51.  *     <li>zDot</li>
  52.  *   </ul>
  53.  * contained in {@link PVCoordinates}.

  54.  * <p>
  55.  * Note that the implementation of this class delegates all non-Cartesian related
  56.  * computations ({@link #getA()}, {@link #getEquinoctialEx()}, ...) to an underlying
  57.  * instance of the {@link EquinoctialOrbit} class. This implies that using this class
  58.  * only for analytical computations which are always based on non-Cartesian
  59.  * parameters is perfectly possible but somewhat sub-optimal.
  60.  * </p>
  61.  * <p>
  62.  * The instance <code>CartesianOrbit</code> is guaranteed to be immutable.
  63.  * </p>
  64.  * @see    Orbit
  65.  * @see    KeplerianOrbit
  66.  * @see    CircularOrbit
  67.  * @see    EquinoctialOrbit
  68.  * @author Luc Maisonobe
  69.  * @author Guylaine Prat
  70.  * @author Fabien Maussion
  71.  * @author V&eacute;ronique Pommier-Maurussane
  72.  * @since 9.0
  73.  */
  74. public class FieldCartesianOrbit<T extends RealFieldElement<T>> extends FieldOrbit<T> {

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

  78.     /** Indicator for non-Keplerian acceleration. */
  79.     private final transient boolean hasNonKeplerianAcceleration;

  80.     /** Underlying equinoctial orbit to which high-level methods are delegated. */
  81.     private transient FieldEquinoctialOrbit<T> equinoctial;

  82.     /** Field used by this class.*/
  83.     private final Field<T> field;

  84.     /** Zero. (could be usefull)*/
  85.     private final T zero;

  86.     /** One. (could be useful)*/
  87.     private final T one;

  88.     /** Constructor from Cartesian parameters.
  89.      *
  90.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  91.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  92.      * use {@code mu} and the position to compute the acceleration, including
  93.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  94.      *
  95.      * @param pvaCoordinates the position, velocity and acceleration of the satellite.
  96.      * @param frame the frame in which the {@link PVCoordinates} are defined
  97.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  98.      * @param mu central attraction coefficient (m³/s²)
  99.      * @exception IllegalArgumentException if frame is not a {@link
  100.      * Frame#isPseudoInertial pseudo-inertial frame}
  101.      */
  102.     public FieldCartesianOrbit(final TimeStampedFieldPVCoordinates<T> pvaCoordinates,
  103.                                final Frame frame, final T mu)
  104.         throws IllegalArgumentException {
  105.         super(pvaCoordinates, frame, mu);
  106.         hasNonKeplerianAcceleration = hasNonKeplerianAcceleration(pvaCoordinates, mu);
  107.         equinoctial = null;
  108.         field = pvaCoordinates.getPosition().getX().getField();
  109.         zero = field.getZero();
  110.         one = field.getOne();

  111.         if (!FACTORIES.containsKey(field)) {
  112.             FACTORIES.put(field, new FDSFactory<>(field, 1, 1));
  113.         }

  114.     }

  115.     /** Constructor from Cartesian parameters.
  116.      *
  117.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  118.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  119.      * use {@code mu} and the position to compute the acceleration, including
  120.      * {@link #shiftedBy(RealFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  121.      *
  122.      * @param pvaCoordinates the position and velocity of the satellite.
  123.      * @param frame the frame in which the {@link PVCoordinates} are defined
  124.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  125.      * @param date date of the orbital parameters
  126.      * @param mu central attraction coefficient (m³/s²)
  127.      * @exception IllegalArgumentException if frame is not a {@link
  128.      * Frame#isPseudoInertial pseudo-inertial frame}
  129.      */
  130.     public FieldCartesianOrbit(final FieldPVCoordinates<T> pvaCoordinates, final Frame frame,
  131.                                final FieldAbsoluteDate<T> date, final T mu)
  132.         throws IllegalArgumentException {
  133.         this(new TimeStampedFieldPVCoordinates<>(date, pvaCoordinates), frame, mu);
  134.     }

  135.     /** Constructor from any kind of orbital parameters.
  136.      * @param op orbital parameters to copy
  137.      */
  138.     public FieldCartesianOrbit(final FieldOrbit<T> op) {
  139.         super(op.getPVCoordinates(), op.getFrame(), op.getMu());
  140.         hasNonKeplerianAcceleration = op.hasDerivatives();
  141.         if (op instanceof FieldEquinoctialOrbit) {
  142.             equinoctial = (FieldEquinoctialOrbit<T>) op;
  143.         } else if (op instanceof FieldCartesianOrbit) {
  144.             equinoctial = ((FieldCartesianOrbit<T>) op).equinoctial;
  145.         } else {
  146.             equinoctial = null;
  147.         }
  148.         field = op.getA().getField();
  149.         zero = field.getZero();
  150.         one = field.getOne();

  151.         if (!FACTORIES.containsKey(field)) {
  152.             FACTORIES.put(field, new FDSFactory<>(field, 1, 1));
  153.         }

  154.     }

  155.     /** {@inheritDoc} */
  156.     public OrbitType getType() {
  157.         return OrbitType.CARTESIAN;
  158.     }

  159.     /** Lazy evaluation of equinoctial parameters. */
  160.     private void initEquinoctial() {
  161.         if (equinoctial == null) {
  162.             if (hasDerivatives()) {
  163.                 // getPVCoordinates includes accelerations that will be interpreted as derivatives
  164.                 equinoctial = new FieldEquinoctialOrbit<>(getPVCoordinates(), getFrame(), getDate(), getMu());
  165.             } else {
  166.                 // get rid of Keplerian acceleration so we don't assume
  167.                 // we have derivatives when in fact we don't have them
  168.                 equinoctial = new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(getPVCoordinates().getPosition(),
  169.                                                                                    getPVCoordinates().getVelocity()),
  170.                                                           getFrame(), getDate(), getMu());
  171.             }
  172.         }
  173.     }

  174.     /** Get position with derivatives.
  175.      * @return position with derivatives
  176.      */
  177.     private FieldVector3D<FieldDerivativeStructure<T>> getPositionDS() {
  178.         final FieldVector3D<T> p = getPVCoordinates().getPosition();
  179.         final FieldVector3D<T> v = getPVCoordinates().getVelocity();
  180.         @SuppressWarnings("unchecked")
  181.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(field);
  182.         return new FieldVector3D<>(factory.build(p.getX(), v.getX()),
  183.                                    factory.build(p.getY(), v.getY()),
  184.                                    factory.build(p.getZ(), v.getZ()));
  185.     }

  186.     /** Get velocity with derivatives.
  187.      * @return velocity with derivatives
  188.      */
  189.     private FieldVector3D<FieldDerivativeStructure<T>> getVelocityDS() {
  190.         final FieldVector3D<T> v = getPVCoordinates().getVelocity();
  191.         final FieldVector3D<T> a = getPVCoordinates().getAcceleration();
  192.         @SuppressWarnings("unchecked")
  193.         final FDSFactory<T> factory = (FDSFactory<T>) FACTORIES.get(field);
  194.         return new FieldVector3D<>(factory.build(v.getX(), a.getX()),
  195.                                    factory.build(v.getY(), a.getY()),
  196.                                    factory.build(v.getZ(), a.getZ()));
  197.     }

  198.     /** {@inheritDoc} */
  199.     public T getA() {
  200.         // lazy evaluation of semi-major axis
  201.         final T r  = getPVCoordinates().getPosition().getNorm();
  202.         final T V2 = getPVCoordinates().getVelocity().getNormSq();
  203.         return r.divide(r.negate().multiply(V2).divide(getMu()).add(2));
  204.     }

  205.     /** {@inheritDoc} */
  206.     public T getADot() {
  207.         if (hasDerivatives()) {
  208.             final FieldDerivativeStructure<T> r  = getPositionDS().getNorm();
  209.             final FieldDerivativeStructure<T> V2 = getVelocityDS().getNormSq();
  210.             final FieldDerivativeStructure<T> a  = r.divide(r.multiply(V2).divide(getMu()).subtract(2).negate());
  211.             return a.getPartialDerivative(1);
  212.         } else {
  213.             return null;
  214.         }
  215.     }

  216.     /** {@inheritDoc} */
  217.     public T getE() {
  218.         final T muA = getA().multiply(getMu());
  219.         if (muA.getReal() > 0) {
  220.             // elliptic or circular orbit
  221.             final FieldVector3D<T> pvP   = getPVCoordinates().getPosition();
  222.             final FieldVector3D<T> pvV   = getPVCoordinates().getVelocity();
  223.             final T rV2OnMu = pvP.getNorm().multiply(pvV.getNormSq()).divide(getMu());
  224.             final T eSE     = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt());
  225.             final T eCE     = rV2OnMu.subtract(1);
  226.             return (eCE.multiply(eCE).add(eSE.multiply(eSE))).sqrt();
  227.         } else {
  228.             // hyperbolic orbit
  229.             final FieldVector3D<T> pvM = getPVCoordinates().getMomentum();
  230.             return pvM.getNormSq().divide(muA).negate().add(1).sqrt();
  231.         }
  232.     }

  233.     /** {@inheritDoc} */
  234.     public T getEDot() {
  235.         if (hasDerivatives()) {
  236.             final FieldVector3D<FieldDerivativeStructure<T>> pvP   = getPositionDS();
  237.             final FieldVector3D<FieldDerivativeStructure<T>> pvV   = getVelocityDS();
  238.             final FieldDerivativeStructure<T> r       = getPositionDS().getNorm();
  239.             final FieldDerivativeStructure<T> V2      = getVelocityDS().getNormSq();
  240.             final FieldDerivativeStructure<T> rV2OnMu = r.multiply(V2).divide(getMu());
  241.             final FieldDerivativeStructure<T> a       = r.divide(rV2OnMu.negate().add(2));
  242.             final FieldDerivativeStructure<T> eSE     = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(getMu()).sqrt());
  243.             final FieldDerivativeStructure<T> eCE     = rV2OnMu.subtract(1);
  244.             final FieldDerivativeStructure<T> e       = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt();
  245.             return e.getPartialDerivative(1);
  246.         } else {
  247.             return null;
  248.         }
  249.     }

  250.     /** {@inheritDoc} */
  251.     public T getI() {
  252.         return FieldVector3D.angle(new FieldVector3D<>(zero, zero, one), getPVCoordinates().getMomentum());
  253.     }

  254.     /** {@inheritDoc} */
  255.     public T getIDot() {
  256.         if (hasDerivatives()) {
  257.             final FieldVector3D<FieldDerivativeStructure<T>> momentum =
  258.                             FieldVector3D.crossProduct(getPositionDS(), getVelocityDS());
  259.             final FieldDerivativeStructure<T> i = FieldVector3D.angle(Vector3D.PLUS_K, momentum);
  260.             return i.getPartialDerivative(1);
  261.         } else {
  262.             return null;
  263.         }
  264.     }

  265.     /** {@inheritDoc} */
  266.     public T getEquinoctialEx() {
  267.         initEquinoctial();
  268.         return equinoctial.getEquinoctialEx();
  269.     }

  270.     /** {@inheritDoc} */
  271.     public T getEquinoctialExDot() {
  272.         initEquinoctial();
  273.         return equinoctial.getEquinoctialExDot();
  274.     }

  275.     /** {@inheritDoc} */
  276.     public T getEquinoctialEy() {
  277.         initEquinoctial();
  278.         return equinoctial.getEquinoctialEy();
  279.     }

  280.     /** {@inheritDoc} */
  281.     public T getEquinoctialEyDot() {
  282.         initEquinoctial();
  283.         return equinoctial.getEquinoctialEyDot();
  284.     }

  285.     /** {@inheritDoc} */
  286.     public T getHx() {
  287.         final FieldVector3D<T> w = getPVCoordinates().getMomentum().normalize();
  288.         // Check for equatorial retrograde orbit
  289.         final double x = w.getX().getReal();
  290.         final double y = w.getY().getReal();
  291.         final double z = w.getZ().getReal();
  292.         if (((x * x + y * y) == 0) && z < 0) {
  293.             return zero.add(Double.NaN);
  294.         }
  295.         return w.getY().negate().divide(w.getZ().add(1));
  296.     }

  297.     /** {@inheritDoc} */
  298.     public T getHxDot() {
  299.         if (hasDerivatives()) {
  300.             final FieldVector3D<FieldDerivativeStructure<T>> w =
  301.                             FieldVector3D.crossProduct(getPositionDS(), getVelocityDS()).normalize();
  302.             // Check for equatorial retrograde orbit
  303.             final double x = w.getX().getValue().getReal();
  304.             final double y = w.getY().getValue().getReal();
  305.             final double z = w.getZ().getValue().getReal();
  306.             if (((x * x + y * y) == 0) && z < 0) {
  307.                 return zero.add(Double.NaN);
  308.             }
  309.             final FieldDerivativeStructure<T> hx = w.getY().negate().divide(w.getZ().add(1));
  310.             return hx.getPartialDerivative(1);
  311.         } else {
  312.             return null;
  313.         }
  314.     }

  315.     /** {@inheritDoc} */
  316.     public T getHy() {
  317.         final FieldVector3D<T> w = getPVCoordinates().getMomentum().normalize();
  318.         // Check for equatorial retrograde orbit
  319.         final double x = w.getX().getReal();
  320.         final double y = w.getY().getReal();
  321.         final double z = w.getZ().getReal();
  322.         if (((x * x + y * y) == 0) && z < 0) {
  323.             return zero.add(Double.NaN);
  324.         }
  325.         return  w.getX().divide(w.getZ().add(1));
  326.     }

  327.     /** {@inheritDoc} */
  328.     public T getHyDot() {
  329.         if (hasDerivatives()) {
  330.             final FieldVector3D<FieldDerivativeStructure<T>> w =
  331.                             FieldVector3D.crossProduct(getPositionDS(), getVelocityDS()).normalize();
  332.             // Check for equatorial retrograde orbit
  333.             final double x = w.getX().getValue().getReal();
  334.             final double y = w.getY().getValue().getReal();
  335.             final double z = w.getZ().getValue().getReal();
  336.             if (((x * x + y * y) == 0) && z < 0) {
  337.                 return zero.add(Double.NaN);
  338.             }
  339.             final FieldDerivativeStructure<T> hy = w.getX().divide(w.getZ().add(1));
  340.             return hy.getPartialDerivative(1);
  341.         } else {
  342.             return null;
  343.         }
  344.     }

  345.     /** {@inheritDoc} */
  346.     public T getLv() {
  347.         initEquinoctial();
  348.         return equinoctial.getLv();
  349.     }

  350.     /** {@inheritDoc} */
  351.     public T getLvDot() {
  352.         initEquinoctial();
  353.         return equinoctial.getLvDot();
  354.     }

  355.     /** {@inheritDoc} */
  356.     public T getLE() {
  357.         initEquinoctial();
  358.         return equinoctial.getLE();
  359.     }

  360.     /** {@inheritDoc} */
  361.     public T getLEDot() {
  362.         initEquinoctial();
  363.         return equinoctial.getLEDot();
  364.     }

  365.     /** {@inheritDoc} */
  366.     public T getLM() {
  367.         initEquinoctial();
  368.         return equinoctial.getLM();
  369.     }

  370.     /** {@inheritDoc} */
  371.     public T getLMDot() {
  372.         initEquinoctial();
  373.         return equinoctial.getLMDot();
  374.     }

  375.     /** {@inheritDoc} */
  376.     public boolean hasDerivatives() {
  377.         return hasNonKeplerianAcceleration;
  378.     }

  379.     /** {@inheritDoc} */
  380.     protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {
  381.         // nothing to do here, as the canonical elements are already the Cartesian ones
  382.         return getPVCoordinates();
  383.     }

  384.     /** {@inheritDoc} */
  385.     public FieldCartesianOrbit<T> shiftedBy(final double dt) {
  386.         return shiftedBy(getDate().getField().getZero().add(dt));
  387.     }

  388.     /** {@inheritDoc} */
  389.     public FieldCartesianOrbit<T> shiftedBy(final T dt) {
  390.         final FieldPVCoordinates<T> shiftedPV = (getA().getReal() < 0) ? shiftPVHyperbolic(dt) : shiftPVElliptic(dt);
  391.         return new FieldCartesianOrbit<>(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
  392.     }

  393.     /** {@inheritDoc}
  394.      * <p>
  395.      * The interpolated instance is created by polynomial Hermite interpolation
  396.      * ensuring velocity remains the exact derivative of position.
  397.      * </p>
  398.      * <p>
  399.      * As this implementation of interpolation is polynomial, it should be used only
  400.      * with small samples (about 10-20 points) in order to avoid <a
  401.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  402.      * and numerical problems (including NaN appearing).
  403.      * </p>
  404.      * <p>
  405.      * If orbit interpolation on large samples is needed, using the {@link
  406.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  407.      * low-level interpolation. The Ephemeris class automatically handles selection of
  408.      * a neighboring sub-sample with a predefined number of point from a large global sample
  409.      * in a thread-safe way.
  410.      * </p>
  411.      */
  412.     public FieldCartesianOrbit<T> interpolate(final FieldAbsoluteDate<T> date, final Stream<FieldOrbit<T>> sample) {
  413.         final TimeStampedFieldPVCoordinates<T> interpolated =
  414.                 TimeStampedFieldPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_PVA,
  415.                                                           sample.map(orbit -> orbit.getPVCoordinates()));
  416.         return new FieldCartesianOrbit<>(interpolated, getFrame(), date, getMu());
  417.     }

  418.     /** Compute shifted position and velocity in elliptic case.
  419.      * @param dt time shift
  420.      * @return shifted position and velocity
  421.      */
  422.     private FieldPVCoordinates<T> shiftPVElliptic(final T dt) {

  423.         // preliminary computation
  424.         final FieldVector3D<T> pvP   = getPVCoordinates().getPosition();
  425.         final FieldVector3D<T> pvV   = getPVCoordinates().getVelocity();
  426.         final T r2      = pvP.getNormSq();
  427.         final T r       = r2.sqrt();
  428.         final T rV2OnMu = r.multiply(pvV.getNormSq()).divide(getMu());
  429.         final T a       = r.divide(rV2OnMu.negate().add(2));
  430.         final T eSE     = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(getMu()).sqrt());
  431.         final T eCE     = rV2OnMu.subtract(1);
  432.         final T e2      = eCE.multiply(eCE).add(eSE.multiply(eSE));

  433.         // we can use any arbitrary reference 2D frame in the orbital plane
  434.         // in order to simplify some equations below, we use the current position as the u axis
  435.         final FieldVector3D<T> u     = pvP.normalize();
  436.         final FieldVector3D<T> v     = FieldVector3D.crossProduct(FieldVector3D.crossProduct(pvP, pvV), u).normalize();

  437.         // the following equations rely on the specific choice of u explained above,
  438.         // some coefficients that vanish to 0 in this case have already been removed here
  439.         final T ex      = eCE.subtract(e2).multiply(a).divide(r);
  440.         final T s       = e2.negate().add(1).sqrt();
  441.         final T ey      = s.negate().multiply(eSE).multiply(a).divide(r);
  442.         final T beta    = s.add(1).reciprocal();
  443.         final T thetaE0 = ey.add(eSE.multiply(beta).multiply(ex)).atan2(r.divide(a).add(ex).subtract(eSE.multiply(beta).multiply(ey)));
  444.         final T thetaM0 = thetaE0.subtract(ex.multiply(thetaE0.sin())).add(ey.multiply(thetaE0.cos()));

  445.         // compute in-plane shifted eccentric argument
  446.         final T sqrtMmuOA = a.reciprocal().multiply(getMu()).sqrt();
  447.         final T thetaM1   = thetaM0.add(sqrtMmuOA.divide(a).multiply(dt));
  448.         final T thetaE1   = meanToEccentric(thetaM1, ex, ey);
  449.         final T cTE       = thetaE1.cos();
  450.         final T sTE       = thetaE1.sin();

  451.         // compute shifted in-plane Cartesian coordinates
  452.         final T exey   = ex.multiply(ey);
  453.         final T exCeyS = ex.multiply(cTE).add(ey.multiply(sTE));
  454.         final T x      = a.multiply(beta.multiply(ey).multiply(ey).negate().add(1).multiply(cTE).add(beta.multiply(exey).multiply(sTE)).subtract(ex));
  455.         final T y      = a.multiply(beta.multiply(ex).multiply(ex).negate().add(1).multiply(sTE).add(beta.multiply(exey).multiply(cTE)).subtract(ey));
  456.         final T factor = sqrtMmuOA.divide(exCeyS.negate().add(1));
  457.         final T xDot   = factor.multiply(beta.multiply(ey).multiply(exCeyS).subtract(sTE));
  458.         final T yDot   = factor.multiply(cTE.subtract(beta.multiply(ex).multiply(exCeyS)));

  459.         final FieldVector3D<T> shiftedP = new FieldVector3D<>(x, u, y, v);
  460.         final FieldVector3D<T> shiftedV = new FieldVector3D<>(xDot, u, yDot, v);
  461.         if (hasNonKeplerianAcceleration) {

  462.             // extract non-Keplerian part of the initial acceleration
  463.             final FieldVector3D<T> nonKeplerianAcceleration = new FieldVector3D<>(one, getPVCoordinates().getAcceleration(),
  464.                                                                                   r.multiply(r2).reciprocal().multiply(getMu()), pvP);

  465.             // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
  466.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(one, shiftedP,
  467.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  468.             final T                fixedR2 = fixedP.getNormSq();
  469.             final T                fixedR  = fixedR2.sqrt();
  470.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(one, shiftedV,
  471.                                                                  dt, nonKeplerianAcceleration);
  472.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP,
  473.                                                                  one, nonKeplerianAcceleration);

  474.             return new FieldPVCoordinates<>(fixedP, fixedV, fixedA);

  475.         } else {
  476.             // don't include acceleration,
  477.             // so the shifted orbit is not considered to have derivatives
  478.             return new FieldPVCoordinates<>(shiftedP, shiftedV);
  479.         }

  480.     }

  481.     /** Compute shifted position and velocity in hyperbolic case.
  482.      * @param dt time shift
  483.      * @return shifted position and velocity
  484.      */
  485.     private FieldPVCoordinates<T> shiftPVHyperbolic(final T dt) {

  486.         final FieldPVCoordinates<T> pv = getPVCoordinates();
  487.         final FieldVector3D<T> pvP   = pv.getPosition();
  488.         final FieldVector3D<T> pvV   = pv.getVelocity();
  489.         final FieldVector3D<T> pvM   = pv.getMomentum();
  490.         final T r2      = pvP.getNormSq();
  491.         final T r       = r2.sqrt();
  492.         final T rV2OnMu = r.multiply(pvV.getNormSq()).divide(getMu());
  493.         final T a       = getA();
  494.         final T muA     = a.multiply(getMu());
  495.         final T e       = one.subtract(FieldVector3D.dotProduct(pvM, pvM).divide(muA)).sqrt();
  496.         final T sqrt    = e.add(1).divide(e.subtract(1)).sqrt();

  497.         // compute mean anomaly
  498.         final T eSH     = FieldVector3D.dotProduct(pvP, pvV).divide(muA.negate().sqrt());
  499.         final T eCH     = rV2OnMu.subtract(1);
  500.         final T H0      = eCH.add(eSH).divide(eCH.subtract(eSH)).log().divide(2);
  501.         final T M0      = e.multiply(H0.sinh()).subtract(H0);

  502.         // find canonical 2D frame with p pointing to perigee
  503.         final T v0      = sqrt.multiply(H0.divide(2).tanh()).atan().multiply(2);
  504.         final FieldVector3D<T> p     = new FieldRotation<>(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize();
  505.         final FieldVector3D<T> q     = FieldVector3D.crossProduct(pvM, p).normalize();

  506.         // compute shifted eccentric anomaly
  507.         final T M1      = M0.add(getKeplerianMeanMotion().multiply(dt));
  508.         final T H1      = meanToHyperbolicEccentric(M1, e);

  509.         // compute shifted in-plane Cartesian coordinates
  510.         final T cH     = H1.cosh();
  511.         final T sH     = H1.sinh();
  512.         final T sE2m1  = e.subtract(1).multiply(e.add(1)).sqrt();

  513.         // coordinates of position and velocity in the orbital plane
  514.         final T x      = a.multiply(cH.subtract(e));
  515.         final T y      = a.negate().multiply(sE2m1).multiply(sH);
  516.         final T factor = getMu().divide(a.negate()).sqrt().divide(e.multiply(cH).subtract(1));
  517.         final T xDot   = factor.negate().multiply(sH);
  518.         final T yDot   =  factor.multiply(sE2m1).multiply(cH);

  519.         final FieldVector3D<T> shiftedP = new FieldVector3D<>(x, p, y, q);
  520.         final FieldVector3D<T> shiftedV = new FieldVector3D<>(xDot, p, yDot, q);
  521.         if (hasNonKeplerianAcceleration) {

  522.             // extract non-Keplerian part of the initial acceleration
  523.             final FieldVector3D<T> nonKeplerianAcceleration = new FieldVector3D<>(one, getPVCoordinates().getAcceleration(),
  524.                                                                                   r.multiply(r2).reciprocal().multiply(getMu()), pvP);

  525.             // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
  526.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(one, shiftedP,
  527.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  528.             final T                fixedR2 = fixedP.getNormSq();
  529.             final T                fixedR  = fixedR2.sqrt();
  530.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(one, shiftedV,
  531.                                                                  dt, nonKeplerianAcceleration);
  532.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP,
  533.                                                                  one, nonKeplerianAcceleration);

  534.             return new FieldPVCoordinates<>(fixedP, fixedV, fixedA);

  535.         } else {
  536.             // don't include acceleration,
  537.             // so the shifted orbit is not considered to have derivatives
  538.             return new FieldPVCoordinates<>(shiftedP, shiftedV);
  539.         }

  540.     }

  541.     /** Computes the eccentric in-plane argument from the mean in-plane argument.
  542.      * @param thetaM = mean in-plane argument (rad)
  543.      * @param ex first component of eccentricity vector
  544.      * @param ey second component of eccentricity vector
  545.      * @return the eccentric in-plane argument.
  546.      */
  547.     private T meanToEccentric(final T thetaM, final T ex, final T ey) {
  548.         // Generalization of Kepler equation to in-plane parameters
  549.         // with thetaE = eta + E and
  550.         //      thetaM = eta + M = thetaE - ex.sin(thetaE) + ey.cos(thetaE)
  551.         // and eta being counted from an arbitrary reference in the orbital plane
  552.         T thetaE        = thetaM;
  553.         T thetaEMthetaM = zero;
  554.         int    iter          = 0;
  555.         do {
  556.             final T cosThetaE = thetaE.cos();
  557.             final T sinThetaE = thetaE.sin();

  558.             final T f2 = ex.multiply(sinThetaE).subtract(ey.multiply(cosThetaE));
  559.             final T f1 = one.subtract(ex.multiply(cosThetaE)).subtract(ey.multiply(sinThetaE));
  560.             final T f0 = thetaEMthetaM.subtract(f2);

  561.             final T f12 = f1.multiply(2.0);
  562.             final T shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2)));

  563.             thetaEMthetaM = thetaEMthetaM.subtract(shift);
  564.             thetaE         = thetaM.add(thetaEMthetaM);

  565.             if (FastMath.abs(shift.getReal()) <= 1.0e-12) {
  566.                 return thetaE;
  567.             }

  568.         } while (++iter < 50);

  569.         throw new MathIllegalStateException(LocalizedCoreFormats.CONVERGENCE_FAILED);

  570.     }

  571.     /** Computes the hyperbolic eccentric anomaly from the mean anomaly.
  572.      * <p>
  573.      * The algorithm used here for solving hyperbolic Kepler equation is
  574.      * Danby's iterative method (3rd order) with Vallado's initial guess.
  575.      * </p>
  576.      * @param M mean anomaly (rad)
  577.      * @param ecc eccentricity
  578.      * @return the hyperbolic eccentric anomaly
  579.      */
  580.     private T meanToHyperbolicEccentric(final T M, final T ecc) {

  581.         // Resolution of hyperbolic Kepler equation for Keplerian parameters

  582.         // Initial guess
  583.         T H;
  584.         if (ecc.getReal() < 1.6) {
  585.             if ((-FastMath.PI < M.getReal() && M.getReal() < 0.) || M.getReal() > FastMath.PI) {
  586.                 H = M.subtract(ecc);
  587.             } else {
  588.                 H = M.add(ecc);
  589.             }
  590.         } else {
  591.             if (ecc.getReal() < 3.6 && FastMath.abs(M.getReal()) > FastMath.PI) {
  592.                 H = M.subtract(ecc.copySign(M));
  593.             } else {
  594.                 H = M.divide(ecc.subtract(1.));
  595.             }
  596.         }

  597.         // Iterative computation
  598.         int iter = 0;
  599.         do {
  600.             final T f3  = ecc.multiply(H.cosh());
  601.             final T f2  = ecc.multiply(H.sinh());
  602.             final T f1  = f3.subtract(1.);
  603.             final T f0  = f2.subtract(H).subtract(M);
  604.             final T f12 = f1.multiply(2);
  605.             final T d   = f0.divide(f12);
  606.             final T fdf = f1.subtract(d.multiply(f2));
  607.             final T ds  = f0.divide(fdf);

  608.             final T shift = f0.divide(fdf.add(ds.multiply(ds).multiply(f3.divide(6.))));

  609.             H = H.subtract(shift);

  610.             if (FastMath.abs(shift.getReal()) <= 1.0e-12) {
  611.                 return H;
  612.             }

  613.         } while (++iter < 50);

  614.         throw new MathIllegalStateException(OrekitMessages.UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY,
  615.                                             iter);
  616.     }

  617.     /** Create a 6x6 identity matrix.
  618.      * @return 6x6 identity matrix
  619.      */
  620.     private T[][] create6x6Identity() {
  621.         // this is the fastest way to set the 6x6 identity matrix
  622.         final T[][] identity = MathArrays.buildArray(field, 6, 6);
  623.         for (int i = 0; i < 6; i++) {
  624.             Arrays.fill(identity[i], zero);
  625.             identity[i][i] = one;
  626.         }
  627.         return identity;
  628.     }

  629.     @Override
  630.     protected T[][] computeJacobianMeanWrtCartesian() {
  631.         return create6x6Identity();
  632.     }

  633.     @Override
  634.     protected T[][] computeJacobianEccentricWrtCartesian() {
  635.         return create6x6Identity();
  636.     }

  637.     @Override
  638.     protected T[][] computeJacobianTrueWrtCartesian() {
  639.         return create6x6Identity();
  640.     }

  641.     /** {@inheritDoc} */
  642.     public void addKeplerContribution(final PositionAngle type, final T gm,
  643.                                       final T[] pDot) {

  644.         final FieldPVCoordinates<T> pv = getPVCoordinates();

  645.         // position derivative is velocity
  646.         final FieldVector3D<T> velocity = pv.getVelocity();
  647.         pDot[0] = pDot[0].add(velocity.getX());
  648.         pDot[1] = pDot[1].add(velocity.getY());
  649.         pDot[2] = pDot[2].add(velocity.getZ());

  650.         // velocity derivative is Newtonian acceleration
  651.         final FieldVector3D<T> position = pv.getPosition();
  652.         final T r2         = position.getNormSq();
  653.         final T coeff      = r2.multiply(r2.sqrt()).reciprocal().negate().multiply(gm);
  654.         pDot[3] = pDot[3].add(coeff.multiply(position.getX()));
  655.         pDot[4] = pDot[4].add(coeff.multiply(position.getY()));
  656.         pDot[5] = pDot[5].add(coeff.multiply(position.getZ()));

  657.     }

  658.     /**  Returns a string representation of this Orbit object.
  659.      * @return a string representation of this object
  660.      */
  661.     public String toString() {
  662.         // use only the six defining elements, like the other Orbit.toString() methods
  663.         final String comma = ", ";
  664.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  665.         final Vector3D p = pv.getPosition();
  666.         final Vector3D v = pv.getVelocity();
  667.         return "Cartesian parameters: {P(" +
  668.                 p.getX() + comma +
  669.                 p.getY() + comma +
  670.                 p.getZ() + "), V(" +
  671.                 v.getX() + comma +
  672.                 v.getY() + comma +
  673.                 v.getZ() + ")}";
  674.     }

  675.     @Override
  676.     public CartesianOrbit toOrbit() {
  677.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  678.         final AbsoluteDate date = getPVCoordinates().getDate().toAbsoluteDate();
  679.         if (hasDerivatives()) {
  680.             // getPVCoordinates includes accelerations that will be interpreted as derivatives
  681.             return new CartesianOrbit(pv, getFrame(), date, getMu().getReal());
  682.         } else {
  683.             // get rid of Keplerian acceleration so we don't assume
  684.             // we have derivatives when in fact we don't have them
  685.             return new CartesianOrbit(new PVCoordinates(pv.getPosition(), pv.getVelocity()),
  686.                                       getFrame(), date, getMu().getReal());
  687.         }
  688.     }

  689. }