FieldCartesianOrbit.java

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


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

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

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

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

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

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

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

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

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

  115.     }

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

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

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

  155.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  481.     }

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

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

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

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

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

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

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

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

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

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

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

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

  541.     }

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

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

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

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

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

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

  570.         throw new MathIllegalStateException(LocalizedCoreFormats.CONVERGENCE_FAILED);

  571.     }

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

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

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

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

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

  610.             H = H.subtract(shift);

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

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

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

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

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

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

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

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

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

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

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

  658.     }

  659.     /**  Returns a string representation of this Orbit object.
  660.      * @return a string representation of this object
  661.      */
  662.     public String toString() {
  663.         return "Cartesian parameters: " + getPVCoordinates().toString();
  664.     }

  665.     @Override
  666.     public CartesianOrbit toOrbit() {
  667.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  668.         final AbsoluteDate date = getPVCoordinates().getDate().toAbsoluteDate();
  669.         if (hasDerivatives()) {
  670.             // getPVCoordinates includes accelerations that will be interpreted as derivatives
  671.             return new CartesianOrbit(pv, getFrame(), date, getMu());
  672.         } else {
  673.             // get rid of Keplerian acceleration so we don't assume
  674.             // we have derivatives when in fact we don't have them
  675.             return new CartesianOrbit(new PVCoordinates(pv.getPosition(), pv.getVelocity()),
  676.                                       getFrame(), date, getMu());
  677.         }
  678.     }

  679. }