FieldCartesianOrbit.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 java.util.Arrays;

  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.Field;
  21. import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
  22. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  25. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.FieldSinCos;
  28. import org.hipparchus.util.MathArrays;
  29. import org.orekit.frames.Frame;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.time.FieldAbsoluteDate;
  32. import org.orekit.utils.FieldPVCoordinates;
  33. import org.orekit.utils.PVCoordinates;
  34. import org.orekit.utils.TimeStampedFieldPVCoordinates;


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

  36.  * <p>
  37.  * The parameters used internally are the Cartesian coordinates:
  38.  *   <ul>
  39.  *     <li>x</li>
  40.  *     <li>y</li>
  41.  *     <li>z</li>
  42.  *     <li>xDot</li>
  43.  *     <li>yDot</li>
  44.  *     <li>zDot</li>
  45.  *   </ul>
  46.  * contained in {@link PVCoordinates}.

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

  70.     /** Indicator for non-Keplerian acceleration. */
  71.     private final transient boolean hasNonKeplerianAcceleration;

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

  74.     /** Constructor from Cartesian parameters.
  75.      *
  76.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  77.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  78.      * use {@code mu} and the position to compute the acceleration, including
  79.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  80.      *
  81.      * @param pvaCoordinates the position, velocity and acceleration of the satellite.
  82.      * @param frame the frame in which the {@link PVCoordinates} are defined
  83.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  84.      * @param mu central attraction coefficient (m³/s²)
  85.      * @exception IllegalArgumentException if frame is not a {@link
  86.      * Frame#isPseudoInertial pseudo-inertial frame}
  87.      */
  88.     public FieldCartesianOrbit(final TimeStampedFieldPVCoordinates<T> pvaCoordinates,
  89.                                final Frame frame, final T mu)
  90.         throws IllegalArgumentException {
  91.         super(pvaCoordinates, frame, mu);
  92.         hasNonKeplerianAcceleration = hasNonKeplerianAcceleration(pvaCoordinates, mu);
  93.         equinoctial = null;
  94.     }

  95.     /** Constructor from Cartesian parameters.
  96.      *
  97.      * <p> The acceleration provided in {@code pvCoordinates} is accessible using
  98.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  99.      * use {@code mu} and the position to compute the acceleration, including
  100.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  101.      *
  102.      * @param pvaCoordinates the position and velocity of the satellite.
  103.      * @param frame the frame in which the {@link PVCoordinates} are defined
  104.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  105.      * @param date date of the orbital parameters
  106.      * @param mu central attraction coefficient (m³/s²)
  107.      * @exception IllegalArgumentException if frame is not a {@link
  108.      * Frame#isPseudoInertial pseudo-inertial frame}
  109.      */
  110.     public FieldCartesianOrbit(final FieldPVCoordinates<T> pvaCoordinates, final Frame frame,
  111.                                final FieldAbsoluteDate<T> date, final T mu)
  112.         throws IllegalArgumentException {
  113.         this(new TimeStampedFieldPVCoordinates<>(date, pvaCoordinates), frame, mu);
  114.     }

  115.     /** Constructor from any kind of orbital parameters.
  116.      * @param op orbital parameters to copy
  117.      */
  118.     public FieldCartesianOrbit(final FieldOrbit<T> op) {
  119.         super(op.getPVCoordinates(), op.getFrame(), op.getMu());
  120.         hasNonKeplerianAcceleration = op.hasDerivatives();
  121.         if (op instanceof FieldEquinoctialOrbit) {
  122.             equinoctial = (FieldEquinoctialOrbit<T>) op;
  123.         } else if (op instanceof FieldCartesianOrbit) {
  124.             equinoctial = ((FieldCartesianOrbit<T>) op).equinoctial;
  125.         } else {
  126.             equinoctial = null;
  127.         }
  128.     }

  129.     /** Constructor from Field and CartesianOrbit.
  130.      * <p>Build a FieldCartesianOrbit from non-Field CartesianOrbit.</p>
  131.      * @param field CalculusField to base object on
  132.      * @param op non-field orbit with only "constant" terms
  133.      * @since 12.0
  134.      */
  135.     public FieldCartesianOrbit(final Field<T> field, final CartesianOrbit op) {
  136.         super(new TimeStampedFieldPVCoordinates<>(field, op.getPVCoordinates()), op.getFrame(),
  137.                 field.getZero().add(op.getMu()));
  138.         hasNonKeplerianAcceleration = op.hasDerivatives();
  139.         if (op.isElliptical()) {
  140.             equinoctial = new FieldEquinoctialOrbit<>(field, new EquinoctialOrbit(op));
  141.         } else {
  142.             equinoctial = null;
  143.         }
  144.     }

  145.     /** Constructor from Field and Orbit.
  146.      * <p>Build a FieldCartesianOrbit from any non-Field Orbit.</p>
  147.      * @param field CalculusField to base object on
  148.      * @param op non-field orbit with only "constant" terms
  149.      * @since 12.0
  150.      */
  151.     public FieldCartesianOrbit(final Field<T> field, final Orbit op) {
  152.         this(field, new CartesianOrbit(op));
  153.     }

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

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

  173.     /** Get the position/velocity with derivatives.
  174.      * @return position/velocity with derivatives
  175.      * @since 10.2
  176.      */
  177.     private FieldPVCoordinates<FieldUnivariateDerivative2<T>> getPVDerivatives() {
  178.         // PVA coordinates
  179.         final FieldPVCoordinates<T> pva = getPVCoordinates();
  180.         final FieldVector3D<T>      p   = pva.getPosition();
  181.         final FieldVector3D<T>      v   = pva.getVelocity();
  182.         final FieldVector3D<T>      a   = pva.getAcceleration();
  183.         // Field coordinates
  184.         final FieldVector3D<FieldUnivariateDerivative2<T>> pG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(p.getX(), v.getX(), a.getX()),
  185.                                                              new FieldUnivariateDerivative2<>(p.getY(), v.getY(), a.getY()),
  186.                                                              new FieldUnivariateDerivative2<>(p.getZ(), v.getZ(), a.getZ()));
  187.         final FieldVector3D<FieldUnivariateDerivative2<T>> vG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(v.getX(), a.getX(), getZero()),
  188.                                                              new FieldUnivariateDerivative2<>(v.getY(), a.getY(), getZero()),
  189.                                                              new FieldUnivariateDerivative2<>(v.getZ(), a.getZ(), getZero()));
  190.         return new FieldPVCoordinates<>(pG, vG);
  191.     }

  192.     /** {@inheritDoc} */
  193.     public T getA() {
  194.         // lazy evaluation of semi-major axis
  195.         final FieldPVCoordinates<T> pva = getPVCoordinates();
  196.         final T r  = pva.getPosition().getNorm();
  197.         final T V2 = pva.getVelocity().getNormSq();
  198.         return r.divide(r.negate().multiply(V2).divide(getMu()).add(2));
  199.     }

  200.     /** {@inheritDoc} */
  201.     public T getADot() {
  202.         if (hasDerivatives()) {
  203.             final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
  204.             final FieldUnivariateDerivative2<T> r  = pv.getPosition().getNorm();
  205.             final FieldUnivariateDerivative2<T> V2 = pv.getVelocity().getNormSq();
  206.             final FieldUnivariateDerivative2<T> a  = r.divide(r.multiply(V2).divide(getMu()).subtract(2).negate());
  207.             return a.getDerivative(1);
  208.         } else {
  209.             return null;
  210.         }
  211.     }

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

  230.     /** {@inheritDoc} */
  231.     public T getEDot() {
  232.         if (hasDerivatives()) {
  233.             final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
  234.             final FieldUnivariateDerivative2<T> r       = pv.getPosition().getNorm();
  235.             final FieldUnivariateDerivative2<T> V2      = pv.getVelocity().getNormSq();
  236.             final FieldUnivariateDerivative2<T> rV2OnMu = r.multiply(V2).divide(getMu());
  237.             final FieldUnivariateDerivative2<T> a       = r.divide(rV2OnMu.negate().add(2));
  238.             final FieldUnivariateDerivative2<T> eSE     = FieldVector3D.dotProduct(pv.getPosition(), pv.getVelocity()).divide(a.multiply(getMu()).sqrt());
  239.             final FieldUnivariateDerivative2<T> eCE     = rV2OnMu.subtract(1);
  240.             final FieldUnivariateDerivative2<T> e       = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt();
  241.             return e.getDerivative(1);
  242.         } else {
  243.             return null;
  244.         }
  245.     }

  246.     /** {@inheritDoc} */
  247.     public T getI() {
  248.         return FieldVector3D.angle(new FieldVector3D<>(getZero(), getZero(), getOne()), getPVCoordinates().getMomentum());
  249.     }

  250.     /** {@inheritDoc} */
  251.     public T getIDot() {
  252.         if (hasDerivatives()) {
  253.             final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
  254.             final FieldVector3D<FieldUnivariateDerivative2<T>> momentum =
  255.                             FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity());
  256.             final FieldUnivariateDerivative2<T> i = FieldVector3D.angle(Vector3D.PLUS_K, momentum);
  257.             return i.getDerivative(1);
  258.         } else {
  259.             return null;
  260.         }
  261.     }

  262.     /** {@inheritDoc} */
  263.     public T getEquinoctialEx() {
  264.         initEquinoctial();
  265.         return equinoctial.getEquinoctialEx();
  266.     }

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

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

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

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

  294.     /** {@inheritDoc} */
  295.     public T getHxDot() {
  296.         if (hasDerivatives()) {
  297.             final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
  298.             final FieldVector3D<FieldUnivariateDerivative2<T>> w =
  299.                             FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity()).normalize();
  300.             // Check for equatorial retrograde orbit
  301.             final double x = w.getX().getValue().getReal();
  302.             final double y = w.getY().getValue().getReal();
  303.             final double z = w.getZ().getValue().getReal();
  304.             if ((x * x + y * y) == 0 && z < 0) {
  305.                 return getZero().add(Double.NaN);
  306.             }
  307.             final FieldUnivariateDerivative2<T> hx = w.getY().negate().divide(w.getZ().add(1));
  308.             return hx.getDerivative(1);
  309.         } else {
  310.             return null;
  311.         }
  312.     }

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

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

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

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

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

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

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

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

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

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

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

  388.     /** {@inheritDoc} */
  389.     public FieldCartesianOrbit<T> shiftedBy(final double dt) {
  390.         return shiftedBy(getZero().add(dt));
  391.     }

  392.     /** {@inheritDoc} */
  393.     public FieldCartesianOrbit<T> shiftedBy(final T dt) {
  394.         final FieldPVCoordinates<T> shiftedPV = isElliptical() ? shiftPVElliptic(dt) : shiftPVHyperbolic(dt);
  395.         return new FieldCartesianOrbit<>(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
  396.     }

  397.     /** Compute shifted position and velocity in elliptic case.
  398.      * @param dt time shift
  399.      * @return shifted position and velocity
  400.      */
  401.     private FieldPVCoordinates<T> shiftPVElliptic(final T dt) {

  402.         // preliminary computation0
  403.         final FieldPVCoordinates<T> pva = getPVCoordinates();
  404.         final FieldVector3D<T>      pvP = pva.getPosition();
  405.         final FieldVector3D<T>      pvV = pva.getVelocity();
  406.         final FieldVector3D<T>      pvM = pva.getMomentum();
  407.         final T r2                      = pvP.getNormSq();
  408.         final T r                       = r2.sqrt();
  409.         final T rV2OnMu                 = r.multiply(pvV.getNormSq()).divide(getMu());
  410.         final T a                       = r.divide(rV2OnMu.negate().add(2));
  411.         final T muA                     = getMu().multiply(a);

  412.         // compute mean anomaly
  413.         final T eSE              = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt());
  414.         final T eCE              = rV2OnMu.subtract(1);
  415.         final T E0               = FastMath.atan2(eSE, eCE);
  416.         final T M0               = E0.subtract(eSE);

  417.         final T e                       = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt();
  418.         final T sqrt                    = e.add(1).divide(e.negate().add(1)).sqrt();

  419.         // find canonical 2D frame with p pointing to perigee
  420.         final T v0               = sqrt.multiply(FastMath.tan(E0.divide(2))).atan().multiply(2);
  421.         final FieldVector3D<T> p = new FieldRotation<>(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize();
  422.         final FieldVector3D<T> q = FieldVector3D.crossProduct(pvM, p).normalize();

  423.         // compute shifted eccentric anomaly
  424.         final T M1               = M0.add(getKeplerianMeanMotion().multiply(dt));
  425.         final T E1               = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, M1);

  426.         // compute shifted in-plane Cartesian coordinates
  427.         final FieldSinCos<T> scE = FastMath.sinCos(E1);
  428.         final T               cE = scE.cos();
  429.         final T               sE = scE.sin();
  430.         final T            sE2m1 = e.negate().add(1).multiply(e.add(1)).sqrt();

  431.         // coordinates of position and velocity in the orbital plane
  432.         final T x        = a.multiply(cE.subtract(e));
  433.         final T y        = a.multiply(sE2m1).multiply(sE);
  434.         final T factor   = a.reciprocal().multiply(getMu()).sqrt().divide(e.multiply(cE).negate().add(1));
  435.         final T xDot     = factor.multiply(sE).negate();
  436.         final T yDot     = factor.multiply(sE2m1).multiply(cE);

  437.         final FieldVector3D<T> shiftedP = new FieldVector3D<>(x, p, y, q);
  438.         final FieldVector3D<T> shiftedV = new FieldVector3D<>(xDot, p, yDot, q);
  439.         if (hasNonKeplerianAcceleration) {

  440.             // extract non-Keplerian part of the initial acceleration
  441.             final FieldVector3D<T> nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(),
  442.                                                                                   r.multiply(r2).reciprocal().multiply(getMu()), pvP);

  443.             // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
  444.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), shiftedP,
  445.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  446.             final T                fixedR2 = fixedP.getNormSq();
  447.             final T                fixedR  = fixedR2.sqrt();
  448.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), shiftedV,
  449.                                                                  dt, nonKeplerianAcceleration);
  450.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP,
  451.                                                                  getOne(), nonKeplerianAcceleration);

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

  453.         } else {
  454.             // don't include acceleration,
  455.             // so the shifted orbit is not considered to have derivatives
  456.             return new FieldPVCoordinates<>(shiftedP, shiftedV);
  457.         }

  458.     }

  459.     /** Compute shifted position and velocity in hyperbolic case.
  460.      * @param dt time shift
  461.      * @return shifted position and velocity
  462.      */
  463.     private FieldPVCoordinates<T> shiftPVHyperbolic(final T dt) {

  464.         final FieldPVCoordinates<T> pv = getPVCoordinates();
  465.         final FieldVector3D<T> pvP   = pv.getPosition();
  466.         final FieldVector3D<T> pvV   = pv.getVelocity();
  467.         final FieldVector3D<T> pvM   = pv.getMomentum();
  468.         final T r2      = pvP.getNormSq();
  469.         final T r       = r2.sqrt();
  470.         final T rV2OnMu = r.multiply(pvV.getNormSq()).divide(getMu());
  471.         final T a       = getA();
  472.         final T muA     = a.multiply(getMu());
  473.         final T e       = getOne().subtract(FieldVector3D.dotProduct(pvM, pvM).divide(muA)).sqrt();
  474.         final T sqrt    = e.add(1).divide(e.subtract(1)).sqrt();

  475.         // compute mean anomaly
  476.         final T eSH     = FieldVector3D.dotProduct(pvP, pvV).divide(muA.negate().sqrt());
  477.         final T eCH     = rV2OnMu.subtract(1);
  478.         final T H0      = eCH.add(eSH).divide(eCH.subtract(eSH)).log().divide(2);
  479.         final T M0      = e.multiply(H0.sinh()).subtract(H0);

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

  484.         // compute shifted eccentric anomaly
  485.         final T M1      = M0.add(getKeplerianMeanMotion().multiply(dt));
  486.         final T H1      = FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M1);

  487.         // compute shifted in-plane Cartesian coordinates
  488.         final T cH     = H1.cosh();
  489.         final T sH     = H1.sinh();
  490.         final T sE2m1  = e.subtract(1).multiply(e.add(1)).sqrt();

  491.         // coordinates of position and velocity in the orbital plane
  492.         final T x      = a.multiply(cH.subtract(e));
  493.         final T y      = a.negate().multiply(sE2m1).multiply(sH);
  494.         final T factor = getMu().divide(a.negate()).sqrt().divide(e.multiply(cH).subtract(1));
  495.         final T xDot   = factor.negate().multiply(sH);
  496.         final T yDot   =  factor.multiply(sE2m1).multiply(cH);

  497.         final FieldVector3D<T> shiftedP = new FieldVector3D<>(x, p, y, q);
  498.         final FieldVector3D<T> shiftedV = new FieldVector3D<>(xDot, p, yDot, q);
  499.         if (hasNonKeplerianAcceleration) {

  500.             // extract non-Keplerian part of the initial acceleration
  501.             final FieldVector3D<T> nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(),
  502.                                                                                   r.multiply(r2).reciprocal().multiply(getMu()), pvP);

  503.             // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
  504.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), shiftedP,
  505.                                                                   dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration);
  506.             final T                fixedR2 = fixedP.getNormSq();
  507.             final T                fixedR  = fixedR2.sqrt();
  508.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), shiftedV,
  509.                                                                  dt, nonKeplerianAcceleration);
  510.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP,
  511.                                                                  getOne(), nonKeplerianAcceleration);

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

  513.         } else {
  514.             // don't include acceleration,
  515.             // so the shifted orbit is not considered to have derivatives
  516.             return new FieldPVCoordinates<>(shiftedP, shiftedV);
  517.         }

  518.     }

  519.     /** Create a 6x6 identity matrix.
  520.      * @return 6x6 identity matrix
  521.      */
  522.     private T[][] create6x6Identity() {
  523.         // this is the fastest way to set the 6x6 identity matrix
  524.         final T[][] identity = MathArrays.buildArray(getField(), 6, 6);
  525.         for (int i = 0; i < 6; i++) {
  526.             Arrays.fill(identity[i], getZero());
  527.             identity[i][i] = getOne();
  528.         }
  529.         return identity;
  530.     }

  531.     @Override
  532.     protected T[][] computeJacobianMeanWrtCartesian() {
  533.         return create6x6Identity();
  534.     }

  535.     @Override
  536.     protected T[][] computeJacobianEccentricWrtCartesian() {
  537.         return create6x6Identity();
  538.     }

  539.     @Override
  540.     protected T[][] computeJacobianTrueWrtCartesian() {
  541.         return create6x6Identity();
  542.     }

  543.     /** {@inheritDoc} */
  544.     public void addKeplerContribution(final PositionAngleType type, final T gm,
  545.                                       final T[] pDot) {

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

  547.         // position derivative is velocity
  548.         final FieldVector3D<T> velocity = pv.getVelocity();
  549.         pDot[0] = pDot[0].add(velocity.getX());
  550.         pDot[1] = pDot[1].add(velocity.getY());
  551.         pDot[2] = pDot[2].add(velocity.getZ());

  552.         // velocity derivative is Newtonian acceleration
  553.         final FieldVector3D<T> position = pv.getPosition();
  554.         final T r2         = position.getNormSq();
  555.         final T coeff      = r2.multiply(r2.sqrt()).reciprocal().negate().multiply(gm);
  556.         pDot[3] = pDot[3].add(coeff.multiply(position.getX()));
  557.         pDot[4] = pDot[4].add(coeff.multiply(position.getY()));
  558.         pDot[5] = pDot[5].add(coeff.multiply(position.getZ()));

  559.     }

  560.     /**  Returns a string representation of this Orbit object.
  561.      * @return a string representation of this object
  562.      */
  563.     public String toString() {
  564.         // use only the six defining elements, like the other Orbit.toString() methods
  565.         final String comma = ", ";
  566.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  567.         final Vector3D p = pv.getPosition();
  568.         final Vector3D v = pv.getVelocity();
  569.         return "Cartesian parameters: {P(" +
  570.                 p.getX() + comma +
  571.                 p.getY() + comma +
  572.                 p.getZ() + "), V(" +
  573.                 v.getX() + comma +
  574.                 v.getY() + comma +
  575.                 v.getZ() + ")}";
  576.     }

  577.     @Override
  578.     public CartesianOrbit toOrbit() {
  579.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  580.         final AbsoluteDate date = getPVCoordinates().getDate().toAbsoluteDate();
  581.         if (hasDerivatives()) {
  582.             // getPVCoordinates includes accelerations that will be interpreted as derivatives
  583.             return new CartesianOrbit(pv, getFrame(), date, getMu().getReal());
  584.         } else {
  585.             // get rid of Keplerian acceleration so we don't assume
  586.             // we have derivatives when in fact we don't have them
  587.             return new CartesianOrbit(new PVCoordinates(pv.getPosition(), pv.getVelocity()),
  588.                                       getFrame(), date, getMu().getReal());
  589.         }
  590.     }

  591. }