FieldCartesianOrbit.java
- /* Copyright 2002-2025 CS GROUP
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.orbits;
- import java.util.Arrays;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.Field;
- import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
- import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
- import org.hipparchus.geometry.euclidean.threed.Vector3D;
- import org.hipparchus.util.MathArrays;
- import org.orekit.frames.FieldKinematicTransform;
- import org.orekit.frames.Frame;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.time.FieldAbsoluteDate;
- import org.orekit.utils.FieldPVCoordinates;
- import org.orekit.utils.PVCoordinates;
- import org.orekit.utils.TimeStampedFieldPVCoordinates;
- /** This class holds Cartesian orbital parameters.
- * <p>
- * The parameters used internally are the Cartesian coordinates:
- * <ul>
- * <li>x</li>
- * <li>y</li>
- * <li>z</li>
- * <li>xDot</li>
- * <li>yDot</li>
- * <li>zDot</li>
- * </ul>
- * contained in {@link PVCoordinates}.
- * <p>
- * Note that the implementation of this class delegates all non-Cartesian related
- * computations ({@link #getA()}, {@link #getEquinoctialEx()}, ...) to an underlying
- * instance of the {@link EquinoctialOrbit} class. This implies that using this class
- * only for analytical computations which are always based on non-Cartesian
- * parameters is perfectly possible but somewhat sub-optimal.
- * </p>
- * <p>
- * The instance <code>CartesianOrbit</code> is guaranteed to be immutable.
- * </p>
- * @see Orbit
- * @see KeplerianOrbit
- * @see CircularOrbit
- * @see EquinoctialOrbit
- * @author Luc Maisonobe
- * @author Guylaine Prat
- * @author Fabien Maussion
- * @author Véronique Pommier-Maurussane
- * @author Andrew Goetz
- * @since 9.0
- * @param <T> type of the field elements
- */
- public class FieldCartesianOrbit<T extends CalculusFieldElement<T>> extends FieldOrbit<T> {
- /** Indicator for non-Keplerian acceleration. */
- private final transient boolean hasNonKeplerianAcceleration;
- /** Underlying equinoctial orbit to which high-level methods are delegated. */
- private transient FieldEquinoctialOrbit<T> equinoctial;
- /** Constructor from Cartesian parameters.
- *
- * <p> The acceleration provided in {@code pvCoordinates} is accessible using
- * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
- * use {@code mu} and the position to compute the acceleration, including
- * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
- *
- * @param pvaCoordinates the position, velocity and acceleration of the satellite.
- * @param frame the frame in which the {@link PVCoordinates} are defined
- * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
- * @param mu central attraction coefficient (m³/s²)
- * @exception IllegalArgumentException if frame is not a {@link
- * Frame#isPseudoInertial pseudo-inertial frame}
- */
- public FieldCartesianOrbit(final TimeStampedFieldPVCoordinates<T> pvaCoordinates,
- final Frame frame, final T mu)
- throws IllegalArgumentException {
- super(pvaCoordinates, frame, mu);
- hasNonKeplerianAcceleration = hasNonKeplerianAcceleration(pvaCoordinates, mu);
- equinoctial = null;
- }
- /** Constructor from Cartesian parameters.
- *
- * <p> The acceleration provided in {@code pvCoordinates} is accessible using
- * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
- * use {@code mu} and the position to compute the acceleration, including
- * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
- *
- * @param pvaCoordinates the position and velocity of the satellite.
- * @param frame the frame in which the {@link PVCoordinates} are defined
- * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
- * @param date date of the orbital parameters
- * @param mu central attraction coefficient (m³/s²)
- * @exception IllegalArgumentException if frame is not a {@link
- * Frame#isPseudoInertial pseudo-inertial frame}
- */
- public FieldCartesianOrbit(final FieldPVCoordinates<T> pvaCoordinates, final Frame frame,
- final FieldAbsoluteDate<T> date, final T mu)
- throws IllegalArgumentException {
- this(new TimeStampedFieldPVCoordinates<>(date, pvaCoordinates), frame, mu);
- }
- /** Constructor from any kind of orbital parameters.
- * @param op orbital parameters to copy
- */
- public FieldCartesianOrbit(final FieldOrbit<T> op) {
- super(op.getPVCoordinates(), op.getFrame(), op.getMu());
- hasNonKeplerianAcceleration = op.hasNonKeplerianAcceleration();
- if (op instanceof FieldEquinoctialOrbit) {
- equinoctial = (FieldEquinoctialOrbit<T>) op;
- } else if (op instanceof FieldCartesianOrbit) {
- equinoctial = ((FieldCartesianOrbit<T>) op).equinoctial;
- } else {
- equinoctial = null;
- }
- }
- /** Constructor from Field and CartesianOrbit.
- * <p>Build a FieldCartesianOrbit from non-Field CartesianOrbit.</p>
- * @param field CalculusField to base object on
- * @param op non-field orbit with only "constant" terms
- * @since 12.0
- */
- public FieldCartesianOrbit(final Field<T> field, final CartesianOrbit op) {
- super(new TimeStampedFieldPVCoordinates<>(field, op.getPVCoordinates()), op.getFrame(),
- field.getZero().newInstance(op.getMu()));
- hasNonKeplerianAcceleration = op.hasNonKeplerianAcceleration();
- if (op.isElliptical()) {
- equinoctial = new FieldEquinoctialOrbit<>(field, new EquinoctialOrbit(op));
- } else {
- equinoctial = null;
- }
- }
- /** Constructor from Field and Orbit.
- * <p>Build a FieldCartesianOrbit from any non-Field Orbit.</p>
- * @param field CalculusField to base object on
- * @param op non-field orbit with only "constant" terms
- * @since 12.0
- */
- public FieldCartesianOrbit(final Field<T> field, final Orbit op) {
- this(field, new CartesianOrbit(op));
- }
- /** {@inheritDoc} */
- public OrbitType getType() {
- return OrbitType.CARTESIAN;
- }
- /** Lazy evaluation of equinoctial parameters. */
- private void initEquinoctial() {
- if (equinoctial == null) {
- if (hasNonKeplerianAcceleration()) {
- // getPVCoordinates includes accelerations that will be interpreted as derivatives
- equinoctial = new FieldEquinoctialOrbit<>(getPVCoordinates(), getFrame(), getDate(), getMu());
- } else {
- // get rid of Keplerian acceleration so we don't assume
- // we have derivatives when in fact we don't have them
- final FieldPVCoordinates<T> pva = getPVCoordinates();
- equinoctial = new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(pva.getPosition(), pva.getVelocity()),
- getFrame(), getDate(), getMu());
- }
- }
- }
- /** Get the position/velocity with derivatives.
- * @return position/velocity with derivatives
- * @since 10.2
- */
- private FieldPVCoordinates<FieldUnivariateDerivative2<T>> getPVDerivatives() {
- // PVA coordinates
- final FieldPVCoordinates<T> pva = getPVCoordinates();
- final FieldVector3D<T> p = pva.getPosition();
- final FieldVector3D<T> v = pva.getVelocity();
- final FieldVector3D<T> a = pva.getAcceleration();
- // Field coordinates
- final FieldVector3D<FieldUnivariateDerivative2<T>> pG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(p.getX(), v.getX(), a.getX()),
- new FieldUnivariateDerivative2<>(p.getY(), v.getY(), a.getY()),
- new FieldUnivariateDerivative2<>(p.getZ(), v.getZ(), a.getZ()));
- final FieldVector3D<FieldUnivariateDerivative2<T>> vG = new FieldVector3D<>(new FieldUnivariateDerivative2<>(v.getX(), a.getX(), getZero()),
- new FieldUnivariateDerivative2<>(v.getY(), a.getY(), getZero()),
- new FieldUnivariateDerivative2<>(v.getZ(), a.getZ(), getZero()));
- return new FieldPVCoordinates<>(pG, vG);
- }
- /** {@inheritDoc} */
- public T getA() {
- // lazy evaluation of semi-major axis
- final FieldPVCoordinates<T> pva = getPVCoordinates();
- final T r = pva.getPosition().getNorm();
- final T V2 = pva.getVelocity().getNormSq();
- return r.divide(r.negate().multiply(V2).divide(getMu()).add(2));
- }
- /** {@inheritDoc} */
- public T getADot() {
- if (hasNonKeplerianAcceleration()) {
- final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
- final FieldUnivariateDerivative2<T> r = pv.getPosition().getNorm();
- final FieldUnivariateDerivative2<T> V2 = pv.getVelocity().getNormSq();
- final FieldUnivariateDerivative2<T> a = r.divide(r.multiply(V2).divide(getMu()).subtract(2).negate());
- return a.getDerivative(1);
- } else {
- return getZero();
- }
- }
- /** {@inheritDoc} */
- public T getE() {
- final T muA = getA().multiply(getMu());
- if (isElliptical()) {
- // elliptic or circular orbit
- final FieldPVCoordinates<T> pva = getPVCoordinates();
- final FieldVector3D<T> pvP = pva.getPosition();
- final FieldVector3D<T> pvV = pva.getVelocity();
- final T rV2OnMu = pvP.getNorm().multiply(pvV.getNormSq()).divide(getMu());
- final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt());
- final T eCE = rV2OnMu.subtract(1);
- return (eCE.square().add(eSE.square())).sqrt();
- } else {
- // hyperbolic orbit
- final FieldVector3D<T> pvM = getPVCoordinates().getMomentum();
- return pvM.getNormSq().divide(muA).negate().add(1).sqrt();
- }
- }
- /** {@inheritDoc} */
- public T getEDot() {
- if (hasNonKeplerianAcceleration()) {
- final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
- final FieldUnivariateDerivative2<T> r = pv.getPosition().getNorm();
- final FieldUnivariateDerivative2<T> V2 = pv.getVelocity().getNormSq();
- final FieldUnivariateDerivative2<T> rV2OnMu = r.multiply(V2).divide(getMu());
- final FieldUnivariateDerivative2<T> a = r.divide(rV2OnMu.negate().add(2));
- final FieldUnivariateDerivative2<T> eSE = FieldVector3D.dotProduct(pv.getPosition(), pv.getVelocity()).divide(a.multiply(getMu()).sqrt());
- final FieldUnivariateDerivative2<T> eCE = rV2OnMu.subtract(1);
- final FieldUnivariateDerivative2<T> e = eCE.square().add(eSE.square()).sqrt();
- return e.getDerivative(1);
- } else {
- return getZero();
- }
- }
- /** {@inheritDoc} */
- public T getI() {
- return FieldVector3D.angle(new FieldVector3D<>(getZero(), getZero(), getOne()), getPVCoordinates().getMomentum());
- }
- /** {@inheritDoc} */
- public T getIDot() {
- if (hasNonKeplerianAcceleration()) {
- final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
- final FieldVector3D<FieldUnivariateDerivative2<T>> momentum =
- FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity());
- final FieldUnivariateDerivative2<T> i = FieldVector3D.angle(Vector3D.PLUS_K, momentum);
- return i.getDerivative(1);
- } else {
- return getZero();
- }
- }
- /** {@inheritDoc} */
- public T getEquinoctialEx() {
- initEquinoctial();
- return equinoctial.getEquinoctialEx();
- }
- /** {@inheritDoc} */
- public T getEquinoctialExDot() {
- initEquinoctial();
- return equinoctial.getEquinoctialExDot();
- }
- /** {@inheritDoc} */
- public T getEquinoctialEy() {
- initEquinoctial();
- return equinoctial.getEquinoctialEy();
- }
- /** {@inheritDoc} */
- public T getEquinoctialEyDot() {
- initEquinoctial();
- return equinoctial.getEquinoctialEyDot();
- }
- /** {@inheritDoc} */
- public T getHx() {
- final FieldVector3D<T> w = getPVCoordinates().getMomentum().normalize();
- // Check for equatorial retrograde orbit
- final double x = w.getX().getReal();
- final double y = w.getY().getReal();
- final double z = w.getZ().getReal();
- if ((x * x + y * y) == 0 && z < 0) {
- return getZero().add(Double.NaN);
- }
- return w.getY().negate().divide(w.getZ().add(1));
- }
- /** {@inheritDoc} */
- public T getHxDot() {
- if (hasNonKeplerianAcceleration()) {
- final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
- final FieldVector3D<FieldUnivariateDerivative2<T>> w =
- FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity()).normalize();
- // Check for equatorial retrograde orbit
- final double x = w.getX().getValue().getReal();
- final double y = w.getY().getValue().getReal();
- final double z = w.getZ().getValue().getReal();
- if ((x * x + y * y) == 0 && z < 0) {
- return getZero().add(Double.NaN);
- }
- final FieldUnivariateDerivative2<T> hx = w.getY().negate().divide(w.getZ().add(1));
- return hx.getDerivative(1);
- } else {
- return getZero();
- }
- }
- /** {@inheritDoc} */
- public T getHy() {
- final FieldVector3D<T> w = getPVCoordinates().getMomentum().normalize();
- // Check for equatorial retrograde orbit
- final double x = w.getX().getReal();
- final double y = w.getY().getReal();
- final double z = w.getZ().getReal();
- if ((x * x + y * y) == 0 && z < 0) {
- return getZero().add(Double.NaN);
- }
- return w.getX().divide(w.getZ().add(1));
- }
- /** {@inheritDoc} */
- public T getHyDot() {
- if (hasNonKeplerianAcceleration()) {
- final FieldPVCoordinates<FieldUnivariateDerivative2<T>> pv = getPVDerivatives();
- final FieldVector3D<FieldUnivariateDerivative2<T>> w =
- FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity()).normalize();
- // Check for equatorial retrograde orbit
- final double x = w.getX().getValue().getReal();
- final double y = w.getY().getValue().getReal();
- final double z = w.getZ().getValue().getReal();
- if ((x * x + y * y) == 0 && z < 0) {
- return getZero().add(Double.NaN);
- }
- final FieldUnivariateDerivative2<T> hy = w.getX().divide(w.getZ().add(1));
- return hy.getDerivative(1);
- } else {
- return getZero();
- }
- }
- /** {@inheritDoc} */
- public T getLv() {
- initEquinoctial();
- return equinoctial.getLv();
- }
- /** {@inheritDoc} */
- public T getLvDot() {
- initEquinoctial();
- return equinoctial.getLvDot();
- }
- /** {@inheritDoc} */
- public T getLE() {
- initEquinoctial();
- return equinoctial.getLE();
- }
- /** {@inheritDoc} */
- public T getLEDot() {
- initEquinoctial();
- return equinoctial.getLEDot();
- }
- /** {@inheritDoc} */
- public T getLM() {
- initEquinoctial();
- return equinoctial.getLM();
- }
- /** {@inheritDoc} */
- public T getLMDot() {
- initEquinoctial();
- return equinoctial.getLMDot();
- }
- /** {@inheritDoc} */
- @Override
- public boolean hasNonKeplerianAcceleration() {
- return hasNonKeplerianAcceleration;
- }
- /** {@inheritDoc} */
- protected FieldVector3D<T> initPosition() {
- // nothing to do here, as the canonical elements are already the Cartesian ones
- return getPVCoordinates().getPosition();
- }
- /** {@inheritDoc} */
- protected TimeStampedFieldPVCoordinates<T> initPVCoordinates() {
- // nothing to do here, as the canonical elements are already the Cartesian ones
- return getPVCoordinates();
- }
- /** {@inheritDoc} */
- @Override
- public FieldCartesianOrbit<T> inFrame(final Frame inertialFrame) {
- if (hasNonKeplerianAcceleration()) {
- return new FieldCartesianOrbit<>(getPVCoordinates(inertialFrame), inertialFrame, getMu());
- } else {
- final FieldKinematicTransform<T> transform = getFrame().getKinematicTransformTo(inertialFrame, getDate());
- return new FieldCartesianOrbit<>(transform.transformOnlyPV(getPVCoordinates()), inertialFrame, getDate(), getMu());
- }
- }
- /** {@inheritDoc} */
- public FieldCartesianOrbit<T> shiftedBy(final double dt) {
- return shiftedBy(getZero().newInstance(dt));
- }
- /** {@inheritDoc} */
- public FieldCartesianOrbit<T> shiftedBy(final T dt) {
- final FieldPVCoordinates<T> shiftedPV = shiftPV(dt);
- return new FieldCartesianOrbit<>(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
- }
- /** Compute shifted position and velocity.
- * @param dt time shift
- * @return shifted position and velocity
- */
- private FieldPVCoordinates<T> shiftPV(final T dt) {
- final FieldPVCoordinates<T> pvCoordinates = getPVCoordinates();
- final FieldPVCoordinates<T> shiftedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt,
- pvCoordinates.getPosition(), pvCoordinates.getVelocity(), getMu());
- if (hasNonKeplerianAcceleration) {
- final FieldVector3D<T> pvP = getPosition();
- final T r2 = pvP.getNormSq();
- final T r = r2.sqrt();
- // extract non-Keplerian part of the initial acceleration
- final FieldVector3D<T> nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(),
- r.multiply(r2).reciprocal().multiply(getMu()), pvP);
- // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion
- final FieldVector3D<T> shiftedP = shiftedPV.getPosition();
- final FieldVector3D<T> shiftedV = shiftedPV.getVelocity();
- final FieldVector3D<T> fixedP = new FieldVector3D<>(getOne(), shiftedP,
- dt.square().multiply(0.5), nonKeplerianAcceleration);
- final T fixedR2 = fixedP.getNormSq();
- final T fixedR = fixedR2.sqrt();
- final FieldVector3D<T> fixedV = new FieldVector3D<>(getOne(), shiftedV,
- dt, nonKeplerianAcceleration);
- final FieldVector3D<T> fixedA = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP,
- getOne(), nonKeplerianAcceleration);
- return new FieldPVCoordinates<>(fixedP, fixedV, fixedA);
- } else {
- // don't include acceleration,
- // so the shifted orbit is not considered to have derivatives
- return shiftedPV;
- }
- }
- /** Create a 6x6 identity matrix.
- * @return 6x6 identity matrix
- */
- private T[][] create6x6Identity() {
- // this is the fastest way to set the 6x6 identity matrix
- final T[][] identity = MathArrays.buildArray(getField(), 6, 6);
- for (int i = 0; i < 6; i++) {
- Arrays.fill(identity[i], getZero());
- identity[i][i] = getOne();
- }
- return identity;
- }
- @Override
- protected T[][] computeJacobianMeanWrtCartesian() {
- return create6x6Identity();
- }
- @Override
- protected T[][] computeJacobianEccentricWrtCartesian() {
- return create6x6Identity();
- }
- @Override
- protected T[][] computeJacobianTrueWrtCartesian() {
- return create6x6Identity();
- }
- /** {@inheritDoc} */
- public void addKeplerContribution(final PositionAngleType type, final T gm,
- final T[] pDot) {
- final FieldPVCoordinates<T> pv = getPVCoordinates();
- // position derivative is velocity
- final FieldVector3D<T> velocity = pv.getVelocity();
- pDot[0] = pDot[0].add(velocity.getX());
- pDot[1] = pDot[1].add(velocity.getY());
- pDot[2] = pDot[2].add(velocity.getZ());
- // velocity derivative is Newtonian acceleration
- final FieldVector3D<T> position = pv.getPosition();
- final T r2 = position.getNormSq();
- final T coeff = r2.multiply(r2.sqrt()).reciprocal().negate().multiply(gm);
- pDot[3] = pDot[3].add(coeff.multiply(position.getX()));
- pDot[4] = pDot[4].add(coeff.multiply(position.getY()));
- pDot[5] = pDot[5].add(coeff.multiply(position.getZ()));
- }
- /** Returns a string representation of this Orbit object.
- * @return a string representation of this object
- */
- public String toString() {
- // use only the six defining elements, like the other Orbit.toString() methods
- final String comma = ", ";
- final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
- final Vector3D p = pv.getPosition();
- final Vector3D v = pv.getVelocity();
- return "Cartesian parameters: {P(" +
- p.getX() + comma +
- p.getY() + comma +
- p.getZ() + "), V(" +
- v.getX() + comma +
- v.getY() + comma +
- v.getZ() + ")}";
- }
- @Override
- public CartesianOrbit toOrbit() {
- final PVCoordinates pv = getPVCoordinates().toPVCoordinates();
- final AbsoluteDate date = getPVCoordinates().getDate().toAbsoluteDate();
- if (hasNonKeplerianAcceleration()) {
- // getPVCoordinates includes accelerations that will be interpreted as derivatives
- return new CartesianOrbit(pv, getFrame(), date, getMu().getReal());
- } else {
- // get rid of Keplerian acceleration so we don't assume
- // we have derivatives when in fact we don't have them
- return new CartesianOrbit(new PVCoordinates(pv.getPosition(), pv.getVelocity()),
- getFrame(), date, getMu().getReal());
- }
- }
- }