FieldOrbit.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.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.analysis.differentiation.Derivative;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.linear.FieldDecompositionSolver;
  23. import org.hipparchus.linear.FieldLUDecomposition;
  24. import org.hipparchus.linear.FieldMatrix;
  25. import org.hipparchus.linear.FieldVector;
  26. import org.hipparchus.linear.MatrixUtils;
  27. import org.hipparchus.util.MathArrays;
  28. import org.orekit.errors.OrekitIllegalArgumentException;
  29. import org.orekit.errors.OrekitInternalError;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.frames.FieldStaticTransform;
  32. import org.orekit.frames.FieldTransform;
  33. import org.orekit.frames.Frame;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.time.FieldTimeShiftable;
  36. import org.orekit.time.FieldTimeStamped;
  37. import org.orekit.utils.FieldPVCoordinates;
  38. import org.orekit.utils.FieldPVCoordinatesProvider;
  39. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  40. import org.orekit.utils.TimeStampedPVCoordinates;

  41. /**
  42.  * This class handles orbital parameters.

  43.  * <p>
  44.  * For user convenience, both the Cartesian and the equinoctial elements
  45.  * are provided by this class, regardless of the canonical representation
  46.  * implemented in the derived class (which may be classical Keplerian
  47.  * elements for example).
  48.  * </p>
  49.  * <p>
  50.  * The parameters are defined in a frame specified by the user. It is important
  51.  * to make sure this frame is consistent: it probably is inertial and centered
  52.  * on the central body. This information is used for example by some
  53.  * force models.
  54.  * </p>
  55.  * <p>
  56.  * Instance of this class are guaranteed to be immutable.
  57.  * </p>
  58.  * @author Luc Maisonobe
  59.  * @author Guylaine Prat
  60.  * @author Fabien Maussion
  61.  * @author V&eacute;ronique Pommier-Maurussane
  62.  * @since 9.0
  63.  * @see Orbit
  64.  * @param <T> type of the field elements
  65.  */
  66. public abstract class FieldOrbit<T extends CalculusFieldElement<T>>
  67.     implements FieldPVCoordinatesProvider<T>, FieldTimeStamped<T>, FieldTimeShiftable<FieldOrbit<T>, T> {

  68.     /** Absolute tolerance when checking if the rate of the position angle is Keplerian or not. */
  69.     protected static final double TOLERANCE_POSITION_ANGLE_RATE = 1e-15;

  70.     /** Frame in which are defined the orbital parameters. */
  71.     private final Frame frame;

  72.     /** Date of the orbital parameters. */
  73.     private final FieldAbsoluteDate<T> date;

  74.     /** Value of mu used to compute position and velocity (m³/s²). */
  75.     private final T mu;

  76.     /** Field-valued one.
  77.      * @since 12.0
  78.      * */
  79.     private final T one;

  80.     /** Field-valued zero.
  81.      * @since 12.0
  82.      * */
  83.     private final T zero;

  84.     /** Field used by this class.
  85.      * @since 12.0
  86.      */
  87.     private final Field<T> field;

  88.     /** Computed position.
  89.      * @since 12.0
  90.      */
  91.     private FieldVector3D<T> position;

  92.     /** Computed PVCoordinates. */
  93.     private TimeStampedFieldPVCoordinates<T> pvCoordinates;

  94.     /** Jacobian of the orbital parameters with mean angle with respect to the Cartesian coordinates. */
  95.     private T[][] jacobianMeanWrtCartesian;

  96.     /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with mean angle. */
  97.     private T[][] jacobianWrtParametersMean;

  98.     /** Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian coordinates. */
  99.     private T[][] jacobianEccentricWrtCartesian;

  100.     /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with eccentric angle. */
  101.     private T[][] jacobianWrtParametersEccentric;

  102.     /** Jacobian of the orbital parameters with true angle with respect to the Cartesian coordinates. */
  103.     private T[][] jacobianTrueWrtCartesian;

  104.     /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with true angle. */
  105.     private T[][] jacobianWrtParametersTrue;

  106.     /** Default constructor.
  107.      * Build a new instance with arbitrary default elements.
  108.      * @param frame the frame in which the parameters are defined
  109.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  110.      * @param date date of the orbital parameters
  111.      * @param mu central attraction coefficient (m^3/s^2)
  112.      * @exception IllegalArgumentException if frame is not a {@link
  113.      * Frame#isPseudoInertial pseudo-inertial frame}
  114.      */
  115.     protected FieldOrbit(final Frame frame, final FieldAbsoluteDate<T> date, final T mu)
  116.         throws IllegalArgumentException {
  117.         ensurePseudoInertialFrame(frame);
  118.         this.field = mu.getField();
  119.         this.zero = this.field.getZero();
  120.         this.one = this.field.getOne();
  121.         this.date                      = date;
  122.         this.mu                        = mu;
  123.         this.pvCoordinates             = null;
  124.         this.frame                     = frame;
  125.         jacobianMeanWrtCartesian       = null;
  126.         jacobianWrtParametersMean      = null;
  127.         jacobianEccentricWrtCartesian  = null;
  128.         jacobianWrtParametersEccentric = null;
  129.         jacobianTrueWrtCartesian       = null;
  130.         jacobianWrtParametersTrue      = null;
  131.     }

  132.     /** Set the orbit from Cartesian parameters.
  133.      *
  134.      * <p> The acceleration provided in {@code FieldPVCoordinates} is accessible using
  135.      * {@link #getPVCoordinates()} and {@link #getPVCoordinates(Frame)}. All other methods
  136.      * use {@code mu} and the position to compute the acceleration, including
  137.      * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}.
  138.      *
  139.      * @param fieldPVCoordinates the position and velocity in the inertial frame
  140.      * @param frame the frame in which the {@link TimeStampedPVCoordinates} are defined
  141.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  142.      * @param mu central attraction coefficient (m^3/s^2)
  143.      * @exception IllegalArgumentException if frame is not a {@link
  144.      * Frame#isPseudoInertial pseudo-inertial frame}
  145.      */
  146.     protected FieldOrbit(final TimeStampedFieldPVCoordinates<T> fieldPVCoordinates, final Frame frame, final T mu)
  147.         throws IllegalArgumentException {
  148.         ensurePseudoInertialFrame(frame);
  149.         this.field = mu.getField();
  150.         this.zero = this.field.getZero();
  151.         this.one = this.field.getOne();
  152.         this.date = fieldPVCoordinates.getDate();
  153.         this.mu = mu;
  154.         if (fieldPVCoordinates.getAcceleration().getNormSq().getReal() == 0.0) {
  155.             // the acceleration was not provided,
  156.             // compute it from Newtonian attraction
  157.             final T r2 = fieldPVCoordinates.getPosition().getNormSq();
  158.             final T r3 = r2.multiply(r2.sqrt());
  159.             this.pvCoordinates = new TimeStampedFieldPVCoordinates<>(fieldPVCoordinates.getDate(),
  160.                                                                      fieldPVCoordinates.getPosition(),
  161.                                                                      fieldPVCoordinates.getVelocity(),
  162.                                                                      new FieldVector3D<>(r3.reciprocal().multiply(mu.negate()), fieldPVCoordinates.getPosition()));
  163.         } else {
  164.             this.pvCoordinates = fieldPVCoordinates;
  165.         }
  166.         this.frame = frame;
  167.     }

  168.     /** Compute non-Keplerian part of the acceleration from first time derivatives.
  169.      * @return non-Keplerian part of the acceleration
  170.      * @since 13.1
  171.      */
  172.     protected FieldVector3D<T> nonKeplerianAcceleration() {

  173.         final T[][] dPdC = MathArrays.buildArray(getField(), 6, 6);
  174.         final PositionAngleType positionAngleType = PositionAngleType.MEAN;
  175.         getJacobianWrtCartesian(positionAngleType, dPdC);
  176.         final FieldMatrix<T> subMatrix = MatrixUtils.createFieldMatrix(dPdC);

  177.         final FieldDecompositionSolver<T> solver = getDecompositionSolver(subMatrix);

  178.         final T[] derivatives = dPdC[0].clone();
  179.         getType().mapOrbitToArray(this, positionAngleType, derivatives.clone(), derivatives);
  180.         derivatives[5] = derivatives[5].subtract(getKeplerianMeanMotion());

  181.         final FieldVector<T> solution = solver.solve(MatrixUtils.createFieldVector(derivatives));
  182.         // solution is vector-acceleration vector
  183.         return new FieldVector3D<>(solution.getEntry(3), solution.getEntry(4), solution.getEntry(5));

  184.     }

  185.     /** Check if Cartesian coordinates include non-Keplerian acceleration.
  186.      * @param pva Cartesian coordinates
  187.      * @param mu central attraction coefficient
  188.      * @param <T> type of the field elements
  189.      * @return true if Cartesian coordinates include non-Keplerian acceleration
  190.      */
  191.     protected static <T extends CalculusFieldElement<T>> boolean hasNonKeplerianAcceleration(final FieldPVCoordinates<T> pva, final T mu) {

  192.         if (mu.getField().getZero() instanceof Derivative<?>) {
  193.             return Orbit.hasNonKeplerianAcceleration(pva.toPVCoordinates(), mu.getReal()); // for performance

  194.         } else {
  195.             final FieldVector3D<T> a = pva.getAcceleration();
  196.             if (a == null) {
  197.                 return false;
  198.             }

  199.             final FieldVector3D<T> p = pva.getPosition();
  200.             final T r2 = p.getNormSq();

  201.             // Check if acceleration is relatively close to 0 compared to the Keplerian acceleration
  202.             final double tolerance = mu.getReal() * 1e-9;
  203.             final FieldVector3D<T> aTimesR2 = a.scalarMultiply(r2);
  204.             if (aTimesR2.getNorm().getReal() < tolerance) {
  205.                 return false;
  206.             }

  207.             if ((aTimesR2.add(p.normalize().scalarMultiply(mu))).getNorm().getReal() > tolerance) {
  208.                 // we have a relevant acceleration, we can compute derivatives
  209.                 return true;
  210.             } else {
  211.                 // the provided acceleration is either too small to be reliable (probably even 0), or NaN
  212.                 return false;
  213.             }
  214.         }

  215.     }

  216.     /** Returns true if and only if the orbit is elliptical i.e. has a non-negative semi-major axis.
  217.      * @return true if getA() is strictly greater than 0
  218.      * @since 12.0
  219.      */
  220.     public boolean isElliptical() {
  221.         return getA().getReal() > 0.;
  222.     }

  223.     /** Get the orbit type.
  224.      * @return orbit type
  225.      */
  226.     public abstract OrbitType getType();

  227.     /** Ensure the defining frame is a pseudo-inertial frame.
  228.      * @param frame frame to check
  229.      * @exception IllegalArgumentException if frame is not a {@link
  230.      * Frame#isPseudoInertial pseudo-inertial frame}
  231.      */
  232.     private static void ensurePseudoInertialFrame(final Frame frame)
  233.         throws IllegalArgumentException {
  234.         if (!frame.isPseudoInertial()) {
  235.             throw new OrekitIllegalArgumentException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
  236.                                                      frame.getName());
  237.         }
  238.     }

  239.     /** Get the frame in which the orbital parameters are defined.
  240.      * @return frame in which the orbital parameters are defined
  241.      */
  242.     public Frame getFrame() {
  243.         return frame;
  244.     }

  245.     /**Transforms the FieldOrbit instance into an Orbit instance.
  246.      * @return Orbit instance with same properties
  247.      */
  248.     public abstract Orbit toOrbit();

  249.     /** Get the semi-major axis.
  250.      * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
  251.      * @return semi-major axis (m)
  252.      */
  253.     public abstract T getA();

  254.     /** Get the semi-major axis derivative.
  255.      * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
  256.      * <p>
  257.      * If the orbit was created without derivatives, the value returned is null.
  258.      * </p>
  259.      * @return semi-major axis  derivative (m/s)
  260.      */
  261.     public abstract T getADot();

  262.     /** Get the first component of the equinoctial eccentricity vector.
  263.      * @return first component of the equinoctial eccentricity vector
  264.      */
  265.     public abstract T getEquinoctialEx();

  266.     /** Get the first component of the equinoctial eccentricity vector derivative.
  267.      * <p>
  268.      * If the orbit was created without derivatives, the value returned is null.
  269.      * </p>
  270.      * @return first component of the equinoctial eccentricity vector derivative
  271.      */
  272.     public abstract T getEquinoctialExDot();

  273.     /** Get the second component of the equinoctial eccentricity vector.
  274.      * @return second component of the equinoctial eccentricity vector
  275.      */
  276.     public abstract T getEquinoctialEy();

  277.     /** Get the second component of the equinoctial eccentricity vector derivative.
  278.      * <p>
  279.      * If the orbit was created without derivatives, the value returned is null.
  280.      * </p>
  281.      * @return second component of the equinoctial eccentricity vector derivative
  282.      */
  283.     public abstract T getEquinoctialEyDot();

  284.     /** Get the first component of the inclination vector.
  285.      * @return first component of the inclination vector
  286.      */
  287.     public abstract T getHx();

  288.     /** Get the first component of the inclination vector derivative.
  289.      * <p>
  290.      * If the orbit was created without derivatives, the value returned is null.
  291.      * </p>
  292.      * @return first component of the inclination vector derivative
  293.      */
  294.     public abstract T getHxDot();

  295.     /** Get the second component of the inclination vector.
  296.      * @return second component of the inclination vector
  297.      */
  298.     public abstract T getHy();

  299.     /** Get the second component of the inclination vector derivative.
  300.      * @return second component of the inclination vector derivative
  301.      */
  302.     public abstract T getHyDot();

  303.     /** Get the eccentric longitude argument.
  304.      * @return E + ω + Ω eccentric longitude argument (rad)
  305.      */
  306.     public abstract T getLE();

  307.     /** Get the eccentric longitude argument derivative.
  308.      * <p>
  309.      * If the orbit was created without derivatives, the value returned is null.
  310.      * </p>
  311.      * @return d(E + ω + Ω)/dt eccentric longitude argument derivative (rad/s)
  312.      */
  313.     public abstract T getLEDot();

  314.     /** Get the true longitude argument.
  315.      * @return v + ω + Ω true longitude argument (rad)
  316.      */
  317.     public abstract T getLv();

  318.     /** Get the true longitude argument derivative.
  319.      * <p>
  320.      * If the orbit was created without derivatives, the value returned is null.
  321.      * </p>
  322.      * @return d(v + ω + Ω)/dt true longitude argument derivative (rad/s)
  323.      */
  324.     public abstract T getLvDot();

  325.     /** Get the mean longitude argument.
  326.      * @return M + ω + Ω mean longitude argument (rad)
  327.      */
  328.     public abstract T getLM();

  329.     /** Get the mean longitude argument derivative.
  330.      * <p>
  331.      * If the orbit was created without derivatives, the value returned is null.
  332.      * </p>
  333.      * @return d(M + ω + Ω)/dt mean longitude argument derivative (rad/s)
  334.      */
  335.     public abstract T getLMDot();

  336.     // Additional orbital elements

  337.     /** Get the eccentricity.
  338.      * @return eccentricity
  339.      */
  340.     public abstract T getE();

  341.     /** Get the eccentricity derivative.
  342.      * <p>
  343.      * If the orbit was created without derivatives, the value returned is null.
  344.      * </p>
  345.      * @return eccentricity derivative
  346.      */
  347.     public abstract T getEDot();

  348.     /** Get the inclination.
  349.      * <p>
  350.      * If the orbit was created without derivatives, the value returned is null.
  351.      * </p>
  352.      * @return inclination (rad)
  353.      */
  354.     public abstract T getI();

  355.     /** Get the inclination derivative.
  356.      * @return inclination derivative (rad/s)
  357.      */
  358.     public abstract T getIDot();

  359.     /** Check if orbit includes non-Keplerian rates.
  360.      * @return true if orbit includes non-Keplerian derivatives
  361.      * @see #getADot()
  362.      * @see #getEquinoctialExDot()
  363.      * @see #getEquinoctialEyDot()
  364.      * @see #getHxDot()
  365.      * @see #getHyDot()
  366.      * @see #getLEDot()
  367.      * @see #getLvDot()
  368.      * @see #getLMDot()
  369.      * @see #getEDot()
  370.      * @see #getIDot()
  371.      * @since 13.0
  372.      */
  373.     public boolean hasNonKeplerianAcceleration() {
  374.         return hasNonKeplerianAcceleration(getPVCoordinates(), getMu());
  375.     }

  376.     /** Get the central attraction coefficient used for position and velocity conversions (m³/s²).
  377.      * @return central attraction coefficient used for position and velocity conversions (m³/s²)
  378.      */
  379.     public T getMu() {
  380.         return mu;
  381.     }

  382.     /** Get the Keplerian period.
  383.      * <p>The Keplerian period is computed directly from semi major axis
  384.      * and central acceleration constant.</p>
  385.      * @return Keplerian period in seconds, or positive infinity for hyperbolic orbits
  386.      */
  387.     public T getKeplerianPeriod() {
  388.         final T a = getA();
  389.         return isElliptical() ?
  390.                a.multiply(a.getPi().multiply(2.0)).multiply(a.divide(mu).sqrt()) :
  391.                zero.add(Double.POSITIVE_INFINITY);
  392.     }

  393.     /** Get the Keplerian mean motion.
  394.      * <p>The Keplerian mean motion is computed directly from semi major axis
  395.      * and central acceleration constant.</p>
  396.      * @return Keplerian mean motion in radians per second
  397.      */
  398.     public T getKeplerianMeanMotion() {
  399.         final T absA = getA().abs();
  400.         return absA.reciprocal().multiply(mu).sqrt().divide(absA);
  401.     }

  402.     /** Get the derivative of the mean anomaly with respect to the semi major axis.
  403.      * @return derivative of the mean anomaly with respect to the semi major axis
  404.      */
  405.     public T getMeanAnomalyDotWrtA() {
  406.         return getKeplerianMeanMotion().divide(getA()).multiply(-1.5);
  407.     }

  408.     /** Get the date of orbital parameters.
  409.      * @return date of the orbital parameters
  410.      */
  411.     public FieldAbsoluteDate<T> getDate() {
  412.         return date;
  413.     }

  414.     /** Get the {@link TimeStampedPVCoordinates} in a specified frame.
  415.      * @param outputFrame frame in which the position/velocity coordinates shall be computed
  416.      * @return FieldPVCoordinates in the specified output frame
  417.           * @see #getPVCoordinates()
  418.      */
  419.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final Frame outputFrame) {
  420.         if (pvCoordinates == null) {
  421.             pvCoordinates = initPVCoordinates();
  422.         }

  423.         // If output frame requested is the same as definition frame,
  424.         // PV coordinates are returned directly
  425.         if (outputFrame == frame) {
  426.             return pvCoordinates;
  427.         }

  428.         // Else, PV coordinates are transformed to output frame
  429.         final FieldTransform<T> t = frame.getTransformTo(outputFrame, date);
  430.         return t.transformPVCoordinates(pvCoordinates);
  431.     }

  432.     /** {@inheritDoc} */
  433.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> otherDate, final Frame otherFrame) {
  434.         return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame);
  435.     }

  436.     /** {@inheritDoc} */
  437.     @Override
  438.     public FieldVector3D<T> getPosition(final FieldAbsoluteDate<T> otherDate, final Frame otherFrame) {
  439.         return shiftedBy(otherDate.durationFrom(getDate())).getPosition(otherFrame);
  440.     }

  441.     /** Get the position in a specified frame.
  442.      * @param outputFrame frame in which the position coordinates shall be computed
  443.      * @return position in the specified output frame
  444.      * @see #getPosition()
  445.      * @since 12.0
  446.      */
  447.     public FieldVector3D<T> getPosition(final Frame outputFrame) {
  448.         if (position == null) {
  449.             position = initPosition();
  450.         }

  451.         // If output frame requested is the same as definition frame,
  452.         // Position vector is returned directly
  453.         if (outputFrame == frame) {
  454.             return position;
  455.         }

  456.         // Else, position vector is transformed to output frame
  457.         final FieldStaticTransform<T> t = frame.getStaticTransformTo(outputFrame, date);
  458.         return t.transformPosition(position);

  459.     }

  460.     /** Get the position in definition frame.
  461.      * @return position in the definition frame
  462.      * @see #getPVCoordinates()
  463.      * @since 12.0
  464.      */
  465.     public FieldVector3D<T> getPosition() {
  466.         if (position == null) {
  467.             position = initPosition();
  468.         }
  469.         return position;
  470.     }

  471.     /** Get the {@link TimeStampedPVCoordinates} in definition frame.
  472.      * @return FieldPVCoordinates in the definition frame
  473.      * @see #getPVCoordinates(Frame)
  474.      */
  475.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates() {
  476.         if (pvCoordinates == null) {
  477.             pvCoordinates = initPVCoordinates();
  478.             position      = pvCoordinates.getPosition();
  479.         }
  480.         return pvCoordinates;
  481.     }

  482.     /** Getter for Field-valued one.
  483.      * @return one
  484.      * */
  485.     protected T getOne() {
  486.         return one;
  487.     }

  488.     /** Getter for Field-valued zero.
  489.      * @return zero
  490.      * */
  491.     protected T getZero() {
  492.         return zero;
  493.     }

  494.     /** Getter for Field.
  495.      * @return CalculusField
  496.      * */
  497.     protected Field<T> getField() {
  498.         return field;
  499.     }

  500.     /** Compute the position coordinates from the canonical parameters.
  501.      * @return computed position coordinates
  502.      * @since 12.0
  503.      */
  504.     protected abstract FieldVector3D<T> initPosition();

  505.     /** Compute the position/velocity coordinates from the canonical parameters.
  506.      * @return computed position/velocity coordinates
  507.      */
  508.     protected abstract TimeStampedFieldPVCoordinates<T> initPVCoordinates();

  509.     /**
  510.      * Create a new object representing the same physical orbital state, but attached to a different reference frame.
  511.      * If the new frame is not inertial, an exception will be thrown.
  512.      *
  513.      * @param inertialFrame reference frame of output orbit
  514.      * @return orbit with different frame
  515.      * @since 13.0
  516.      */
  517.     public abstract FieldOrbit<T> inFrame(Frame inertialFrame);

  518.     /** Get a time-shifted orbit.
  519.      * <p>
  520.      * The orbit can be slightly shifted to close dates. This shift is based on
  521.      * a simple Keplerian model. It is <em>not</em> intended as a replacement
  522.      * for proper orbit and attitude propagation but should be sufficient for
  523.      * small time shifts or coarse accuracy.
  524.      * </p>
  525.      * @param dt time shift in seconds
  526.      * @return a new orbit, shifted with respect to the instance (which is immutable)
  527.      */
  528.     public abstract FieldOrbit<T> shiftedBy(T dt);

  529.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  530.      * <p>
  531.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  532.      * respect to Cartesian coordinate j. This means each row corresponds to one orbital parameter
  533.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  534.      * </p>
  535.      * @param type type of the position angle to use
  536.      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
  537.      * is larger than 6x6, only the 6x6 upper left corner will be modified
  538.      */
  539.     public void getJacobianWrtCartesian(final PositionAngleType type, final T[][] jacobian) {

  540.         final T[][] cachedJacobian;
  541.         synchronized (this) {
  542.             switch (type) {
  543.                 case MEAN :
  544.                     if (jacobianMeanWrtCartesian == null) {
  545.                         // first call, we need to compute the Jacobian and cache it
  546.                         jacobianMeanWrtCartesian = computeJacobianMeanWrtCartesian();
  547.                     }
  548.                     cachedJacobian = jacobianMeanWrtCartesian;
  549.                     break;
  550.                 case ECCENTRIC :
  551.                     if (jacobianEccentricWrtCartesian == null) {
  552.                         // first call, we need to compute the Jacobian and cache it
  553.                         jacobianEccentricWrtCartesian = computeJacobianEccentricWrtCartesian();
  554.                     }
  555.                     cachedJacobian = jacobianEccentricWrtCartesian;
  556.                     break;
  557.                 case TRUE :
  558.                     if (jacobianTrueWrtCartesian == null) {
  559.                         // first call, we need to compute the Jacobian and cache it
  560.                         jacobianTrueWrtCartesian = computeJacobianTrueWrtCartesian();
  561.                     }
  562.                     cachedJacobian = jacobianTrueWrtCartesian;
  563.                     break;
  564.                 default :
  565.                     throw new OrekitInternalError(null);
  566.             }
  567.         }

  568.         // fill the user provided array
  569.         for (int i = 0; i < cachedJacobian.length; ++i) {
  570.             System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
  571.         }

  572.     }

  573.     /** Compute the Jacobian of the Cartesian parameters with respect to the orbital parameters.
  574.      * <p>
  575.      * Element {@code jacobian[i][j]} is the derivative of Cartesian coordinate i of the orbit with
  576.      * respect to orbital parameter j. This means each row corresponds to one Cartesian coordinate
  577.      * x, y, z, xdot, ydot, zdot whereas columns 0 to 5 correspond to the orbital parameters.
  578.      * </p>
  579.      * @param type type of the position angle to use
  580.      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
  581.      * is larger than 6x6, only the 6x6 upper left corner will be modified
  582.      */
  583.     public void getJacobianWrtParameters(final PositionAngleType type, final T[][] jacobian) {

  584.         final T[][] cachedJacobian;
  585.         synchronized (this) {
  586.             switch (type) {
  587.                 case MEAN :
  588.                     if (jacobianWrtParametersMean == null) {
  589.                         // first call, we need to compute the Jacobian and cache it
  590.                         jacobianWrtParametersMean = createInverseJacobian(type);
  591.                     }
  592.                     cachedJacobian = jacobianWrtParametersMean;
  593.                     break;
  594.                 case ECCENTRIC :
  595.                     if (jacobianWrtParametersEccentric == null) {
  596.                         // first call, we need to compute the Jacobian and cache it
  597.                         jacobianWrtParametersEccentric = createInverseJacobian(type);
  598.                     }
  599.                     cachedJacobian = jacobianWrtParametersEccentric;
  600.                     break;
  601.                 case TRUE :
  602.                     if (jacobianWrtParametersTrue == null) {
  603.                         // first call, we need to compute the Jacobian and cache it
  604.                         jacobianWrtParametersTrue = createInverseJacobian(type);
  605.                     }
  606.                     cachedJacobian = jacobianWrtParametersTrue;
  607.                     break;
  608.                 default :
  609.                     throw new OrekitInternalError(null);
  610.             }
  611.         }

  612.         // fill the user-provided array
  613.         for (int i = 0; i < cachedJacobian.length; ++i) {
  614.             System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
  615.         }

  616.     }

  617.     /** Create an inverse Jacobian.
  618.      * @param type type of the position angle to use
  619.      * @return inverse Jacobian
  620.      */
  621.     private T[][] createInverseJacobian(final PositionAngleType type) {

  622.         // get the direct Jacobian
  623.         final T[][] directJacobian = MathArrays.buildArray(getA().getField(), 6, 6);
  624.         getJacobianWrtCartesian(type, directJacobian);

  625.         // invert the direct Jacobian
  626.         final FieldMatrix<T> matrix = MatrixUtils.createFieldMatrix(directJacobian);
  627.         final FieldDecompositionSolver<T> solver = getDecompositionSolver(matrix);
  628.         return solver.getInverse().getData();

  629.     }

  630.     /**
  631.      * Method to build a matrix decomposition solver.
  632.      * @param matrix matrix
  633.      * @return solver
  634.      * @since 13.1
  635.      */
  636.     protected FieldDecompositionSolver<T> getDecompositionSolver(final FieldMatrix<T> matrix) {
  637.         return new FieldLUDecomposition<>(matrix).getSolver();
  638.     }

  639.     /** Compute the Jacobian of the orbital parameters with mean angle with respect to the Cartesian parameters.
  640.      * <p>
  641.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  642.      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
  643.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  644.      * </p>
  645.      * @return 6x6 Jacobian matrix
  646.      * @see #computeJacobianEccentricWrtCartesian()
  647.      * @see #computeJacobianTrueWrtCartesian()
  648.      */
  649.     protected abstract T[][] computeJacobianMeanWrtCartesian();

  650.     /** Compute the Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian parameters.
  651.      * <p>
  652.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  653.      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
  654.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  655.      * </p>
  656.      * @return 6x6 Jacobian matrix
  657.      * @see #computeJacobianMeanWrtCartesian()
  658.      * @see #computeJacobianTrueWrtCartesian()
  659.      */
  660.     protected abstract T[][] computeJacobianEccentricWrtCartesian();

  661.     /** Compute the Jacobian of the orbital parameters with true angle with respect to the Cartesian parameters.
  662.      * <p>
  663.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  664.      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
  665.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  666.      * </p>
  667.      * @return 6x6 Jacobian matrix
  668.      * @see #computeJacobianMeanWrtCartesian()
  669.      * @see #computeJacobianEccentricWrtCartesian()
  670.      */
  671.     protected abstract T[][] computeJacobianTrueWrtCartesian();

  672.     /** Add the contribution of the Keplerian motion to parameters derivatives
  673.      * <p>
  674.      * This method is used by integration-based propagators to evaluate the part of Keplerian
  675.      * motion to evolution of the orbital state.
  676.      * </p>
  677.      * @param type type of the position angle in the state
  678.      * @param gm attraction coefficient to use
  679.      * @param pDot array containing orbital state derivatives to update (the Keplerian
  680.      * part must be <em>added</em> to the array components, as the array may already
  681.      * contain some non-zero elements corresponding to non-Keplerian parts)
  682.      */
  683.     public abstract void addKeplerContribution(PositionAngleType type, T gm, T[] pDot);

  684.         /** Fill a Jacobian half row with a single vector.
  685.      * @param a coefficient of the vector
  686.      * @param v vector
  687.      * @param row Jacobian matrix row
  688.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  689.      */

  690.     protected void fillHalfRow(final T a, final FieldVector3D<T> v, final T[] row, final int j) {
  691.         row[j]     = a.multiply(v.getX());
  692.         row[j + 1] = a.multiply(v.getY());
  693.         row[j + 2] = a.multiply(v.getZ());
  694.     }

  695.     /** Fill a Jacobian half row with a linear combination of vectors.
  696.      * @param a1 coefficient of the first vector
  697.      * @param v1 first vector
  698.      * @param a2 coefficient of the second vector
  699.      * @param v2 second vector
  700.      * @param row Jacobian matrix row
  701.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  702.      */
  703.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  704.                                       final T[] row, final int j) {
  705.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX());
  706.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY());
  707.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ());

  708.     }

  709.     /** Fill a Jacobian half row with a linear combination of vectors.
  710.      * @param a1 coefficient of the first vector
  711.      * @param v1 first vector
  712.      * @param a2 coefficient of the second vector
  713.      * @param v2 second vector
  714.      * @param a3 coefficient of the third vector
  715.      * @param v3 third vector
  716.      * @param row Jacobian matrix row
  717.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  718.      */
  719.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1,
  720.                                final T a2, final FieldVector3D<T> v2,
  721.                                final T a3, final FieldVector3D<T> v3,
  722.                                       final T[] row, final int j) {
  723.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX());
  724.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY());
  725.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ());

  726.     }

  727.     /** Fill a Jacobian half row with a linear combination of vectors.
  728.      * @param a1 coefficient of the first vector
  729.      * @param v1 first vector
  730.      * @param a2 coefficient of the second vector
  731.      * @param v2 second vector
  732.      * @param a3 coefficient of the third vector
  733.      * @param v3 third vector
  734.      * @param a4 coefficient of the fourth vector
  735.      * @param v4 fourth vector
  736.      * @param row Jacobian matrix row
  737.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  738.      */
  739.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  740.                                       final T a3, final FieldVector3D<T> v3, final T a4, final FieldVector3D<T> v4,
  741.                                       final T[] row, final int j) {
  742.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX());
  743.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY());
  744.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ());

  745.     }

  746.     /** Fill a Jacobian half row with a linear combination of vectors.
  747.      * @param a1 coefficient of the first vector
  748.      * @param v1 first vector
  749.      * @param a2 coefficient of the second vector
  750.      * @param v2 second vector
  751.      * @param a3 coefficient of the third vector
  752.      * @param v3 third vector
  753.      * @param a4 coefficient of the fourth vector
  754.      * @param v4 fourth vector
  755.      * @param a5 coefficient of the fifth vector
  756.      * @param v5 fifth vector
  757.      * @param row Jacobian matrix row
  758.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  759.      */
  760.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  761.                                       final T a3, final FieldVector3D<T> v3, final T a4, final FieldVector3D<T> v4,
  762.                                       final T a5, final FieldVector3D<T> v5,
  763.                                       final T[] row, final int j) {
  764.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX()).add(a5.multiply(v5.getX()));
  765.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY()).add(a5.multiply(v5.getY()));
  766.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ()).add(a5.multiply(v5.getZ()));

  767.     }

  768.     /** Fill a Jacobian half row with a linear combination of vectors.
  769.      * @param a1 coefficient of the first vector
  770.      * @param v1 first vector
  771.      * @param a2 coefficient of the second vector
  772.      * @param v2 second vector
  773.      * @param a3 coefficient of the third vector
  774.      * @param v3 third vector
  775.      * @param a4 coefficient of the fourth vector
  776.      * @param v4 fourth vector
  777.      * @param a5 coefficient of the fifth vector
  778.      * @param v5 fifth vector
  779.      * @param a6 coefficient of the sixth vector
  780.      * @param v6 sixth vector
  781.      * @param row Jacobian matrix row
  782.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  783.      */
  784.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  785.                                       final T a3, final FieldVector3D<T> v3, final T a4, final FieldVector3D<T> v4,
  786.                                       final T a5, final FieldVector3D<T> v5, final T a6, final FieldVector3D<T> v6,
  787.                                       final T[] row, final int j) {
  788.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX()).add(a1.linearCombination(a5, v5.getX(), a6, v6.getX()));
  789.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY()).add(a1.linearCombination(a5, v5.getY(), a6, v6.getY()));
  790.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ()).add(a1.linearCombination(a5, v5.getZ(), a6, v6.getZ()));

  791.     }


  792. }