CartesianOrbit.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 org.hipparchus.analysis.differentiation.UnivariateDerivative2;
  19. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.linear.MatrixUtils;
  22. import org.hipparchus.util.FastMath;
  23. import org.orekit.frames.Frame;
  24. import org.orekit.frames.KinematicTransform;
  25. import org.orekit.time.AbsoluteDate;
  26. import org.orekit.time.TimeOffset;
  27. import org.orekit.utils.FieldPVCoordinates;
  28. import org.orekit.utils.PVCoordinates;
  29. import org.orekit.utils.TimeStampedPVCoordinates;


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

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

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

  64.     /** 6x6 identity matrix. */
  65.     private static final double[][] SIX_BY_SIX_IDENTITY = MatrixUtils.createRealIdentityMatrix(6).getData();

  66.     /** Indicator for non-Keplerian derivatives. */
  67.     private final transient boolean hasNonKeplerianAcceleration;

  68.     /** Underlying equinoctial orbit to which high-level methods are delegated. */
  69.     private transient EquinoctialOrbit equinoctial;

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

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

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

  125.     /** {@inheritDoc} */
  126.     public OrbitType getType() {
  127.         return OrbitType.CARTESIAN;
  128.     }

  129.     /** Lazy evaluation of equinoctial parameters. */
  130.     private void initEquinoctial() {
  131.         if (equinoctial == null) {
  132.             if (hasNonKeplerianAcceleration()) {
  133.                 // getPVCoordinates includes accelerations that will be interpreted as derivatives
  134.                 equinoctial = new EquinoctialOrbit(getPVCoordinates(), getFrame(), getDate(), getMu());
  135.             } else {
  136.                 // get rid of Keplerian acceleration so we don't assume
  137.                 // we have derivatives when in fact we don't have them
  138.                 equinoctial = new EquinoctialOrbit(new PVCoordinates(getPosition(),
  139.                                                                      getPVCoordinates().getVelocity()),
  140.                                                    getFrame(), getDate(), getMu());
  141.             }
  142.         }
  143.     }

  144.     /** Get the position/velocity with derivatives.
  145.      * @return position/velocity with derivatives
  146.      * @since 10.2
  147.      */
  148.     private FieldPVCoordinates<UnivariateDerivative2> getPVDerivatives() {
  149.         // PVA coordinates
  150.         final PVCoordinates pva = getPVCoordinates();
  151.         final Vector3D      p   = pva.getPosition();
  152.         final Vector3D      v   = pva.getVelocity();
  153.         final Vector3D      a   = pva.getAcceleration();
  154.         // Field coordinates
  155.         final FieldVector3D<UnivariateDerivative2> pG = new FieldVector3D<>(new UnivariateDerivative2(p.getX(), v.getX(), a.getX()),
  156.                                                                new UnivariateDerivative2(p.getY(), v.getY(), a.getY()),
  157.                                                                new UnivariateDerivative2(p.getZ(), v.getZ(), a.getZ()));
  158.         final FieldVector3D<UnivariateDerivative2> vG = new FieldVector3D<>(new UnivariateDerivative2(v.getX(), a.getX(), 0.0),
  159.                                                                new UnivariateDerivative2(v.getY(), a.getY(), 0.0),
  160.                                                                new UnivariateDerivative2(v.getZ(), a.getZ(), 0.0));
  161.         return new FieldPVCoordinates<>(pG, vG);
  162.     }

  163.     /** {@inheritDoc} */
  164.     public double getA() {
  165.         final double r  = getPosition().getNorm();
  166.         final double V2 = getPVCoordinates().getVelocity().getNormSq();
  167.         return r / (2 - r * V2 / getMu());
  168.     }

  169.     /** {@inheritDoc} */
  170.     public double getADot() {
  171.         if (hasNonKeplerianAcceleration) {
  172.             final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
  173.             final UnivariateDerivative2 r  = pv.getPosition().getNorm();
  174.             final UnivariateDerivative2 V2 = pv.getVelocity().getNormSq();
  175.             final UnivariateDerivative2 a  = r.divide(r.multiply(V2).divide(getMu()).subtract(2).negate());
  176.             return a.getDerivative(1);
  177.         } else {
  178.             return 0.;
  179.         }
  180.     }

  181.     /** {@inheritDoc} */
  182.     public double getE() {
  183.         final double muA = getMu() * getA();
  184.         if (isElliptical()) {
  185.             // elliptic or circular orbit
  186.             final Vector3D pvP   = getPosition();
  187.             final Vector3D pvV   = getPVCoordinates().getVelocity();
  188.             final double rV2OnMu = pvP.getNorm() * pvV.getNormSq() / getMu();
  189.             final double eSE     = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA);
  190.             final double eCE     = rV2OnMu - 1;
  191.             return FastMath.sqrt(eCE * eCE + eSE * eSE);
  192.         } else {
  193.             // hyperbolic orbit
  194.             final Vector3D pvM = getPVCoordinates().getMomentum();
  195.             return FastMath.sqrt(1 - pvM.getNormSq() / muA);
  196.         }
  197.     }

  198.     /** {@inheritDoc} */
  199.     public double getEDot() {
  200.         if (hasNonKeplerianAcceleration) {
  201.             final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
  202.             final FieldVector3D<UnivariateDerivative2> pvP   = pv.getPosition();
  203.             final FieldVector3D<UnivariateDerivative2> pvV   = pv.getVelocity();
  204.             final UnivariateDerivative2 r       = pvP.getNorm();
  205.             final UnivariateDerivative2 V2      = pvV.getNormSq();
  206.             final UnivariateDerivative2 rV2OnMu = r.multiply(V2).divide(getMu());
  207.             final UnivariateDerivative2 a       = r.divide(rV2OnMu.negate().add(2));
  208.             final UnivariateDerivative2 eSE     = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(getMu()).sqrt());
  209.             final UnivariateDerivative2 eCE     = rV2OnMu.subtract(1);
  210.             final UnivariateDerivative2 e       = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt();
  211.             return e.getDerivative(1);
  212.         } else {
  213.             return 0.;
  214.         }
  215.     }

  216.     /** {@inheritDoc} */
  217.     public double getI() {
  218.         return Vector3D.angle(Vector3D.PLUS_K, getPVCoordinates().getMomentum());
  219.     }

  220.     /** {@inheritDoc} */
  221.     public double getIDot() {
  222.         if (hasNonKeplerianAcceleration) {
  223.             final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
  224.             final FieldVector3D<UnivariateDerivative2> momentum =
  225.                             FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity());
  226.             final UnivariateDerivative2 i = FieldVector3D.angle(Vector3D.PLUS_K, momentum);
  227.             return i.getDerivative(1);
  228.         } else {
  229.             return 0.;
  230.         }
  231.     }

  232.     /** {@inheritDoc} */
  233.     public double getEquinoctialEx() {
  234.         initEquinoctial();
  235.         return equinoctial.getEquinoctialEx();
  236.     }

  237.     /** {@inheritDoc} */
  238.     public double getEquinoctialExDot() {
  239.         initEquinoctial();
  240.         return equinoctial.getEquinoctialExDot();
  241.     }

  242.     /** {@inheritDoc} */
  243.     public double getEquinoctialEy() {
  244.         initEquinoctial();
  245.         return equinoctial.getEquinoctialEy();
  246.     }

  247.     /** {@inheritDoc} */
  248.     public double getEquinoctialEyDot() {
  249.         initEquinoctial();
  250.         return equinoctial.getEquinoctialEyDot();
  251.     }

  252.     /** {@inheritDoc} */
  253.     public double getHx() {
  254.         final Vector3D w = getPVCoordinates().getMomentum().normalize();
  255.         // Check for equatorial retrograde orbit
  256.         if ((w.getX() * w.getX() + w.getY() * w.getY()) == 0 && w.getZ() < 0) {
  257.             return Double.NaN;
  258.         }
  259.         return -w.getY() / (1 + w.getZ());
  260.     }

  261.     /** {@inheritDoc} */
  262.     public double getHxDot() {
  263.         if (hasNonKeplerianAcceleration) {
  264.             final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
  265.             final FieldVector3D<UnivariateDerivative2> w =
  266.                             FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity()).normalize();
  267.             // Check for equatorial retrograde orbit
  268.             final double x = w.getX().getValue();
  269.             final double y = w.getY().getValue();
  270.             final double z = w.getZ().getValue();
  271.             if ((x * x + y * y) == 0 && z < 0) {
  272.                 return Double.NaN;
  273.             }
  274.             final UnivariateDerivative2 hx = w.getY().negate().divide(w.getZ().add(1));
  275.             return hx.getDerivative(1);
  276.         } else {
  277.             return 0.;
  278.         }
  279.     }

  280.     /** {@inheritDoc} */
  281.     public double getHy() {
  282.         final Vector3D w = getPVCoordinates().getMomentum().normalize();
  283.         // Check for equatorial retrograde orbit
  284.         if ((w.getX() * w.getX() + w.getY() * w.getY()) == 0 && w.getZ() < 0) {
  285.             return Double.NaN;
  286.         }
  287.         return  w.getX() / (1 + w.getZ());
  288.     }

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

  308.     /** {@inheritDoc} */
  309.     public double getLv() {
  310.         initEquinoctial();
  311.         return equinoctial.getLv();
  312.     }

  313.     /** {@inheritDoc} */
  314.     public double getLvDot() {
  315.         initEquinoctial();
  316.         return equinoctial.getLvDot();
  317.     }

  318.     /** {@inheritDoc} */
  319.     public double getLE() {
  320.         initEquinoctial();
  321.         return equinoctial.getLE();
  322.     }

  323.     /** {@inheritDoc} */
  324.     public double getLEDot() {
  325.         initEquinoctial();
  326.         return equinoctial.getLEDot();
  327.     }

  328.     /** {@inheritDoc} */
  329.     public double getLM() {
  330.         initEquinoctial();
  331.         return equinoctial.getLM();
  332.     }

  333.     /** {@inheritDoc} */
  334.     public double getLMDot() {
  335.         initEquinoctial();
  336.         return equinoctial.getLMDot();
  337.     }

  338.     /** {@inheritDoc} */
  339.     @Override
  340.     public boolean hasNonKeplerianAcceleration() {
  341.         return hasNonKeplerianAcceleration;
  342.     }

  343.     /** {@inheritDoc} */
  344.     protected Vector3D initPosition() {
  345.         // nothing to do here, as the canonical elements are already the Cartesian ones
  346.         return getPVCoordinates().getPosition();
  347.     }

  348.     /** {@inheritDoc} */
  349.     protected TimeStampedPVCoordinates initPVCoordinates() {
  350.         // nothing to do here, as the canonical elements are already the Cartesian ones
  351.         return getPVCoordinates();
  352.     }

  353.     /** {@inheritDoc} */
  354.     @Override
  355.     public CartesianOrbit inFrame(final Frame inertialFrame) {
  356.         if (hasNonKeplerianAcceleration()) {
  357.             return new CartesianOrbit(getPVCoordinates(inertialFrame), inertialFrame, getMu());
  358.         } else {
  359.             final KinematicTransform transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
  360.             return new CartesianOrbit(transform.transformOnlyPV(getPVCoordinates()), inertialFrame, getDate(), getMu());
  361.         }
  362.     }

  363.     /** {@inheritDoc} */
  364.     public CartesianOrbit shiftedBy(final double dt) {
  365.         final PVCoordinates shiftedPV = shiftPV(dt);
  366.         return new CartesianOrbit(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
  367.     }

  368.     /** {@inheritDoc} */
  369.     public CartesianOrbit shiftedBy(final TimeOffset dt) {
  370.         final PVCoordinates shiftedPV = shiftPV(dt.toDouble());
  371.         return new CartesianOrbit(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
  372.     }

  373.     /** Compute shifted position and velocity.
  374.      * @param dt time shift
  375.      * @return shifted position and velocity
  376.      */
  377.     private PVCoordinates shiftPV(final double dt) {

  378.         final Vector3D pvP = getPosition();
  379.         final PVCoordinates shiftedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, pvP,
  380.             getPVCoordinates().getVelocity(), getMu());

  381.         if (hasNonKeplerianAcceleration) {

  382.             // extract non-Keplerian part of the initial acceleration
  383.             final double r2 = pvP.getNormSq();
  384.             final double r = FastMath.sqrt(r2);
  385.             final Vector3D nonKeplerianAcceleration = new Vector3D(1, getPVCoordinates().getAcceleration(),
  386.                                                                    getMu() / (r2 * r), pvP);

  387.             // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
  388.             final Vector3D shiftedP = shiftedPV.getPosition();
  389.             final Vector3D shiftedV = shiftedPV.getVelocity();
  390.             final Vector3D fixedP   = new Vector3D(1, shiftedP,
  391.                                                    0.5 * dt * dt, nonKeplerianAcceleration);
  392.             final double   fixedR2 = fixedP.getNormSq();
  393.             final double   fixedR  = FastMath.sqrt(fixedR2);
  394.             final Vector3D fixedV  = new Vector3D(1, shiftedV,
  395.                                                   dt, nonKeplerianAcceleration);
  396.             final Vector3D fixedA  = new Vector3D(-getMu() / (fixedR2 * fixedR), shiftedP,
  397.                                                   1, nonKeplerianAcceleration);

  398.             return new PVCoordinates(fixedP, fixedV, fixedA);

  399.         } else {
  400.             // don't include acceleration,
  401.             // so the shifted orbit is not considered to have derivatives
  402.             return shiftedPV;
  403.         }

  404.     }

  405.     @Override
  406.     protected double[][] computeJacobianMeanWrtCartesian() {
  407.         return SIX_BY_SIX_IDENTITY;
  408.     }

  409.     @Override
  410.     protected double[][] computeJacobianEccentricWrtCartesian() {
  411.         return SIX_BY_SIX_IDENTITY;
  412.     }

  413.     @Override
  414.     protected double[][] computeJacobianTrueWrtCartesian() {
  415.         return SIX_BY_SIX_IDENTITY;
  416.     }

  417.     /** {@inheritDoc} */
  418.     public void addKeplerContribution(final PositionAngleType type, final double gm,
  419.                                       final double[] pDot) {

  420.         final PVCoordinates pv = getPVCoordinates();

  421.         // position derivative is velocity
  422.         final Vector3D velocity = pv.getVelocity();
  423.         pDot[0] += velocity.getX();
  424.         pDot[1] += velocity.getY();
  425.         pDot[2] += velocity.getZ();

  426.         // velocity derivative is Newtonian acceleration
  427.         final Vector3D position = pv.getPosition();
  428.         final double r2         = position.getNormSq();
  429.         final double coeff      = -gm / (r2 * FastMath.sqrt(r2));
  430.         pDot[3] += coeff * position.getX();
  431.         pDot[4] += coeff * position.getY();
  432.         pDot[5] += coeff * position.getZ();

  433.     }

  434.     /**  Returns a string representation of this Orbit object.
  435.      * @return a string representation of this object
  436.      */
  437.     public String toString() {
  438.         // use only the six defining elements, like the other Orbit.toString() methods
  439.         final String comma = ", ";
  440.         final PVCoordinates pv = getPVCoordinates();
  441.         final Vector3D p = pv.getPosition();
  442.         final Vector3D v = pv.getVelocity();
  443.         return "Cartesian parameters: {P(" +
  444.                 p.getX() + comma +
  445.                 p.getY() + comma +
  446.                 p.getZ() + "), V(" +
  447.                 v.getX() + comma +
  448.                 v.getY() + comma +
  449.                 v.getZ() + ")}";
  450.     }

  451. }