FieldCartesianOrbit.java

  1. /* Copyright 2002-2025 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.FieldVector3D;
  23. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  24. import org.hipparchus.util.MathArrays;
  25. import org.orekit.frames.FieldKinematicTransform;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.time.AbsoluteDate;
  28. import org.orekit.time.FieldAbsoluteDate;
  29. import org.orekit.utils.FieldPVCoordinates;
  30. import org.orekit.utils.PVCoordinates;
  31. import org.orekit.utils.TimeStampedFieldPVCoordinates;


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

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

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

  67.     /** Indicator for non-Keplerian acceleration. */
  68.     private final transient boolean hasNonKeplerianAcceleration;

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

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

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

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

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

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

  151.     /** {@inheritDoc} */
  152.     public OrbitType getType() {
  153.         return OrbitType.CARTESIAN;
  154.     }

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

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

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

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

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

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

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

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

  259.     /** {@inheritDoc} */
  260.     public T getEquinoctialEx() {
  261.         initEquinoctial();
  262.         return equinoctial.getEquinoctialEx();
  263.     }

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

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

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

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

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

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

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

  341.     /** {@inheritDoc} */
  342.     public T getLv() {
  343.         initEquinoctial();
  344.         return equinoctial.getLv();
  345.     }

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

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

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

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

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

  371.     /** {@inheritDoc} */
  372.     @Override
  373.     public boolean hasNonKeplerianAcceleration() {
  374.         return hasNonKeplerianAcceleration;
  375.     }

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

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

  386.     /** {@inheritDoc} */
  387.     @Override
  388.     public FieldCartesianOrbit<T> inFrame(final Frame inertialFrame) {
  389.         if (hasNonKeplerianAcceleration()) {
  390.             return new FieldCartesianOrbit<>(getPVCoordinates(inertialFrame), inertialFrame, getMu());
  391.         } else {
  392.             final FieldKinematicTransform<T> transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
  393.             return new FieldCartesianOrbit<>(transform.transformOnlyPV(getPVCoordinates()), inertialFrame, getDate(), getMu());
  394.         }
  395.     }

  396.     /** {@inheritDoc} */
  397.     public FieldCartesianOrbit<T> shiftedBy(final double dt) {
  398.         return shiftedBy(getZero().newInstance(dt));
  399.     }

  400.     /** {@inheritDoc} */
  401.     public FieldCartesianOrbit<T> shiftedBy(final T dt) {
  402.         final FieldPVCoordinates<T> shiftedPV = shiftPV(dt);
  403.         return new FieldCartesianOrbit<>(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
  404.     }

  405.     /** Compute shifted position and velocity.
  406.      * @param dt time shift
  407.      * @return shifted position and velocity
  408.      */
  409.     private FieldPVCoordinates<T> shiftPV(final T dt) {
  410.         final FieldPVCoordinates<T> pvCoordinates = getPVCoordinates();
  411.         final FieldPVCoordinates<T> shiftedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt,
  412.                 pvCoordinates.getPosition(), pvCoordinates.getVelocity(), getMu());

  413.         if (hasNonKeplerianAcceleration) {

  414.             final FieldVector3D<T> pvP = getPosition();
  415.             final T r2 = pvP.getNormSq();
  416.             final T r = r2.sqrt();
  417.             // extract non-Keplerian part of the initial acceleration
  418.             final FieldVector3D<T> nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(),
  419.                                                                                   r.multiply(r2).reciprocal().multiply(getMu()), pvP);

  420.             // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
  421.             final FieldVector3D<T> shiftedP = shiftedPV.getPosition();
  422.             final FieldVector3D<T> shiftedV = shiftedPV.getVelocity();
  423.             final FieldVector3D<T> fixedP   = new FieldVector3D<>(getOne(), shiftedP,
  424.                                                                   dt.square().multiply(0.5), nonKeplerianAcceleration);
  425.             final T                fixedR2 = fixedP.getNormSq();
  426.             final T                fixedR  = fixedR2.sqrt();
  427.             final FieldVector3D<T> fixedV  = new FieldVector3D<>(getOne(), shiftedV,
  428.                                                                  dt, nonKeplerianAcceleration);
  429.             final FieldVector3D<T> fixedA  = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP,
  430.                                                                  getOne(), nonKeplerianAcceleration);

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

  432.         } else {
  433.             // don't include acceleration,
  434.             // so the shifted orbit is not considered to have derivatives
  435.             return shiftedPV;
  436.         }
  437.     }

  438.     /** Create a 6x6 identity matrix.
  439.      * @return 6x6 identity matrix
  440.      */
  441.     private T[][] create6x6Identity() {
  442.         // this is the fastest way to set the 6x6 identity matrix
  443.         final T[][] identity = MathArrays.buildArray(getField(), 6, 6);
  444.         for (int i = 0; i < 6; i++) {
  445.             Arrays.fill(identity[i], getZero());
  446.             identity[i][i] = getOne();
  447.         }
  448.         return identity;
  449.     }

  450.     @Override
  451.     protected T[][] computeJacobianMeanWrtCartesian() {
  452.         return create6x6Identity();
  453.     }

  454.     @Override
  455.     protected T[][] computeJacobianEccentricWrtCartesian() {
  456.         return create6x6Identity();
  457.     }

  458.     @Override
  459.     protected T[][] computeJacobianTrueWrtCartesian() {
  460.         return create6x6Identity();
  461.     }

  462.     /** {@inheritDoc} */
  463.     public void addKeplerContribution(final PositionAngleType type, final T gm,
  464.                                       final T[] pDot) {

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

  466.         // position derivative is velocity
  467.         final FieldVector3D<T> velocity = pv.getVelocity();
  468.         pDot[0] = pDot[0].add(velocity.getX());
  469.         pDot[1] = pDot[1].add(velocity.getY());
  470.         pDot[2] = pDot[2].add(velocity.getZ());

  471.         // velocity derivative is Newtonian acceleration
  472.         final FieldVector3D<T> position = pv.getPosition();
  473.         final T r2         = position.getNormSq();
  474.         final T coeff      = r2.multiply(r2.sqrt()).reciprocal().negate().multiply(gm);
  475.         pDot[3] = pDot[3].add(coeff.multiply(position.getX()));
  476.         pDot[4] = pDot[4].add(coeff.multiply(position.getY()));
  477.         pDot[5] = pDot[5].add(coeff.multiply(position.getZ()));

  478.     }

  479.     /**  Returns a string representation of this Orbit object.
  480.      * @return a string representation of this object
  481.      */
  482.     public String toString() {
  483.         // use only the six defining elements, like the other Orbit.toString() methods
  484.         final String comma = ", ";
  485.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  486.         final Vector3D p = pv.getPosition();
  487.         final Vector3D v = pv.getVelocity();
  488.         return "Cartesian parameters: {P(" +
  489.                 p.getX() + comma +
  490.                 p.getY() + comma +
  491.                 p.getZ() + "), V(" +
  492.                 v.getX() + comma +
  493.                 v.getY() + comma +
  494.                 v.getZ() + ")}";
  495.     }

  496.     @Override
  497.     public CartesianOrbit toOrbit() {
  498.         final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
  499.         final AbsoluteDate date = getPVCoordinates().getDate().toAbsoluteDate();
  500.         if (hasNonKeplerianAcceleration()) {
  501.             // getPVCoordinates includes accelerations that will be interpreted as derivatives
  502.             return new CartesianOrbit(pv, getFrame(), date, getMu().getReal());
  503.         } else {
  504.             // get rid of Keplerian acceleration so we don't assume
  505.             // we have derivatives when in fact we don't have them
  506.             return new CartesianOrbit(new PVCoordinates(pv.getPosition(), pv.getVelocity()),
  507.                                       getFrame(), date, getMu().getReal());
  508.         }
  509.     }

  510. }