FieldOrbit.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.orbits;



  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  21. import org.hipparchus.linear.FieldDecompositionSolver;
  22. import org.hipparchus.linear.FieldLUDecomposition;
  23. import org.hipparchus.linear.FieldMatrix;
  24. import org.hipparchus.linear.MatrixUtils;
  25. import org.hipparchus.util.MathArrays;
  26. import org.orekit.errors.OrekitIllegalArgumentException;
  27. import org.orekit.errors.OrekitInternalError;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.frames.FieldStaticTransform;
  30. import org.orekit.frames.FieldTransform;
  31. import org.orekit.frames.Frame;
  32. import org.orekit.time.FieldAbsoluteDate;
  33. import org.orekit.time.FieldTimeShiftable;
  34. import org.orekit.time.FieldTimeStamped;
  35. import org.orekit.utils.FieldPVCoordinates;
  36. import org.orekit.utils.FieldPVCoordinatesProvider;
  37. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  38. import org.orekit.utils.TimeStampedPVCoordinates;

  39. /**
  40.  * This class handles orbital parameters.

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

  65.     /** Frame in which are defined the orbital parameters. */
  66.     private final Frame frame;

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

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

  71.     /** Field-valued one.
  72.      * @since 12.0
  73.      * */
  74.     private final T one;

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

  79.     /** Field used by this class.
  80.      * @since 12.0
  81.      */
  82.     private final Field<T> field;

  83.     /** Computed position.
  84.      * @since 12.0
  85.      */
  86.     private transient FieldVector3D<T> position;

  87.     /** Computed PVCoordinates. */
  88.     private transient TimeStampedFieldPVCoordinates<T> pvCoordinates;

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

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

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

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

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

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

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

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

  163.     /** Check if Cartesian coordinates include non-Keplerian acceleration.
  164.      * @param pva Cartesian coordinates
  165.      * @param mu central attraction coefficient
  166.      * @param <T> type of the field elements
  167.      * @return true if Cartesian coordinates include non-Keplerian acceleration
  168.      */
  169.     protected static <T extends CalculusFieldElement<T>> boolean hasNonKeplerianAcceleration(final FieldPVCoordinates<T> pva, final T mu) {

  170.         final FieldVector3D<T> a = pva.getAcceleration();
  171.         if (a == null) {
  172.             return false;
  173.         }

  174.         final FieldVector3D<T> p = pva.getPosition();
  175.         final T r2 = p.getNormSq();
  176.         final T r  = r2.sqrt();
  177.         final FieldVector3D<T> keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), p);
  178.         if (a.getNorm().getReal() > 1.0e-9 * keplerianAcceleration.getNorm().getReal()) {
  179.             // we have a relevant acceleration, we can compute derivatives
  180.             return true;
  181.         } else {
  182.             // the provided acceleration is either too small to be reliable (probably even 0), or NaN
  183.             return false;
  184.         }

  185.     }

  186.     /** Returns true if and only if the orbit is elliptical i.e. has a non-negative semi-major axis.
  187.      * @return true if getA() is strictly greater than 0
  188.      * @since 12.0
  189.      */
  190.     public boolean isElliptical() {
  191.         return getA().getReal() > 0.;
  192.     }

  193.     /** Get the orbit type.
  194.      * @return orbit type
  195.      */
  196.     public abstract OrbitType getType();

  197.     /** Ensure the defining frame is a pseudo-inertial frame.
  198.      * @param frame frame to check
  199.      * @exception IllegalArgumentException if frame is not a {@link
  200.      * Frame#isPseudoInertial pseudo-inertial frame}
  201.      */
  202.     private static void ensurePseudoInertialFrame(final Frame frame)
  203.         throws IllegalArgumentException {
  204.         if (!frame.isPseudoInertial()) {
  205.             throw new OrekitIllegalArgumentException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
  206.                                                      frame.getName());
  207.         }
  208.     }

  209.     /** Get the frame in which the orbital parameters are defined.
  210.      * @return frame in which the orbital parameters are defined
  211.      */
  212.     public Frame getFrame() {
  213.         return frame;
  214.     }

  215.     /**Transforms the FieldOrbit instance into an Orbit instance.
  216.      * @return Orbit instance with same properties
  217.      */
  218.     public abstract Orbit toOrbit();

  219.     /** Get the semi-major axis.
  220.      * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
  221.      * @return semi-major axis (m)
  222.      */
  223.     public abstract T getA();

  224.     /** Get the semi-major axis derivative.
  225.      * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
  226.      * <p>
  227.      * If the orbit was created without derivatives, the value returned is null.
  228.      * </p>
  229.      * @return semi-major axis  derivative (m/s)
  230.      */
  231.     public abstract T getADot();

  232.     /** Get the first component of the equinoctial eccentricity vector.
  233.      * @return first component of the equinoctial eccentricity vector
  234.      */
  235.     public abstract T getEquinoctialEx();

  236.     /** Get the first component of the equinoctial eccentricity vector.
  237.      * <p>
  238.      * If the orbit was created without derivatives, the value returned is null.
  239.      * </p>
  240.      * @return first component of the equinoctial eccentricity vector
  241.      */
  242.     public abstract T getEquinoctialExDot();

  243.     /** Get the second component of the equinoctial eccentricity vector.
  244.      * @return second component of the equinoctial eccentricity vector
  245.      */
  246.     public abstract T getEquinoctialEy();

  247.     /** Get the second component of the equinoctial eccentricity vector.
  248.      * <p>
  249.      * If the orbit was created without derivatives, the value returned is null.
  250.      * </p>
  251.      * @return second component of the equinoctial eccentricity vector
  252.      */
  253.     public abstract T getEquinoctialEyDot();

  254.     /** Get the first component of the inclination vector.
  255.      * @return first component of the inclination vector
  256.      */
  257.     public abstract T getHx();

  258.     /** Get the first component of the inclination vector derivative.
  259.      * <p>
  260.      * If the orbit was created without derivatives, the value returned is null.
  261.      * </p>
  262.      * @return first component of the inclination vector derivative
  263.      */
  264.     public abstract T getHxDot();

  265.     /** Get the second component of the inclination vector.
  266.      * @return second component of the inclination vector
  267.      */
  268.     public abstract T getHy();

  269.     /** Get the second component of the inclination vector derivative.
  270.      * @return second component of the inclination vector derivative
  271.      */
  272.     public abstract T getHyDot();

  273.     /** Get the eccentric longitude argument.
  274.      * @return E + ω + Ω eccentric longitude argument (rad)
  275.      */
  276.     public abstract T getLE();

  277.     /** Get the eccentric longitude argument derivative.
  278.      * <p>
  279.      * If the orbit was created without derivatives, the value returned is null.
  280.      * </p>
  281.      * @return d(E + ω + Ω)/dt eccentric longitude argument derivative (rad/s)
  282.      */
  283.     public abstract T getLEDot();

  284.     /** Get the true longitude argument.
  285.      * @return v + ω + Ω true longitude argument (rad)
  286.      */
  287.     public abstract T getLv();

  288.     /** Get the true longitude argument derivative.
  289.      * <p>
  290.      * If the orbit was created without derivatives, the value returned is null.
  291.      * </p>
  292.      * @return d(v + ω + Ω)/dt true longitude argument derivative (rad/s)
  293.      */
  294.     public abstract T getLvDot();

  295.     /** Get the mean longitude argument.
  296.      * @return M + ω + Ω mean longitude argument (rad)
  297.      */
  298.     public abstract T getLM();

  299.     /** Get the mean longitude argument derivative.
  300.      * <p>
  301.      * If the orbit was created without derivatives, the value returned is null.
  302.      * </p>
  303.      * @return d(M + ω + Ω)/dt mean longitude argument derivative (rad/s)
  304.      */
  305.     public abstract T getLMDot();

  306.     // Additional orbital elements

  307.     /** Get the eccentricity.
  308.      * @return eccentricity
  309.      */
  310.     public abstract T getE();

  311.     /** Get the eccentricity derivative.
  312.      * <p>
  313.      * If the orbit was created without derivatives, the value returned is null.
  314.      * </p>
  315.      * @return eccentricity derivative
  316.      */
  317.     public abstract T getEDot();

  318.     /** Get the inclination.
  319.      * <p>
  320.      * If the orbit was created without derivatives, the value returned is null.
  321.      * </p>
  322.      * @return inclination (rad)
  323.      */
  324.     public abstract T getI();

  325.     /** Get the inclination derivative.
  326.      * @return inclination derivative (rad/s)
  327.      */
  328.     public abstract T getIDot();

  329.     /** Check if orbit includes derivatives.
  330.      * @return true if orbit includes derivatives
  331.      * @see #getADot()
  332.      * @see #getEquinoctialExDot()
  333.      * @see #getEquinoctialEyDot()
  334.      * @see #getHxDot()
  335.      * @see #getHyDot()
  336.      * @see #getLEDot()
  337.      * @see #getLvDot()
  338.      * @see #getLMDot()
  339.      * @see #getEDot()
  340.      * @see #getIDot()
  341.      * @since 9.0
  342.      */
  343.     public abstract boolean hasDerivatives();

  344.     /** Get the central attraction coefficient used for position and velocity conversions (m³/s²).
  345.      * @return central attraction coefficient used for position and velocity conversions (m³/s²)
  346.      */
  347.     public T getMu() {
  348.         return mu;
  349.     }

  350.     /** Get the Keplerian period.
  351.      * <p>The Keplerian period is computed directly from semi major axis
  352.      * and central acceleration constant.</p>
  353.      * @return Keplerian period in seconds, or positive infinity for hyperbolic orbits
  354.      */
  355.     public T getKeplerianPeriod() {
  356.         final T a = getA();
  357.         return isElliptical() ?
  358.                a.multiply(a.getPi().multiply(2.0)).multiply(a.divide(mu).sqrt()) :
  359.                zero.add(Double.POSITIVE_INFINITY);
  360.     }

  361.     /** Get the Keplerian mean motion.
  362.      * <p>The Keplerian mean motion is computed directly from semi major axis
  363.      * and central acceleration constant.</p>
  364.      * @return Keplerian mean motion in radians per second
  365.      */
  366.     public T getKeplerianMeanMotion() {
  367.         final T absA = getA().abs();
  368.         return absA.reciprocal().multiply(mu).sqrt().divide(absA);
  369.     }

  370.     /** Get the derivative of the mean anomaly with respect to the semi major axis.
  371.      * @return derivative of the mean anomaly with respect to the semi major axis
  372.      */
  373.     public T getMeanAnomalyDotWrtA() {
  374.         return getKeplerianMeanMotion().divide(getA()).multiply(-1.5);
  375.     }

  376.     /** Get the date of orbital parameters.
  377.      * @return date of the orbital parameters
  378.      */
  379.     public FieldAbsoluteDate<T> getDate() {
  380.         return date;
  381.     }

  382.     /** Get the {@link TimeStampedPVCoordinates} in a specified frame.
  383.      * @param outputFrame frame in which the position/velocity coordinates shall be computed
  384.      * @return FieldPVCoordinates in the specified output frame
  385.           * @see #getPVCoordinates()
  386.      */
  387.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final Frame outputFrame) {
  388.         if (pvCoordinates == null) {
  389.             pvCoordinates = initPVCoordinates();
  390.         }

  391.         // If output frame requested is the same as definition frame,
  392.         // PV coordinates are returned directly
  393.         if (outputFrame == frame) {
  394.             return pvCoordinates;
  395.         }

  396.         // Else, PV coordinates are transformed to output frame
  397.         final FieldTransform<T> t = frame.getTransformTo(outputFrame, date);
  398.         return t.transformPVCoordinates(pvCoordinates);
  399.     }

  400.     /** {@inheritDoc} */
  401.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> otherDate, final Frame otherFrame) {
  402.         return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame);
  403.     }

  404.     /** Get the position in a specified frame.
  405.      * @param outputFrame frame in which the position coordinates shall be computed
  406.      * @return position in the specified output frame
  407.      * @see #getPosition()
  408.      * @since 12.0
  409.      */
  410.     public FieldVector3D<T> getPosition(final Frame outputFrame) {
  411.         if (position == null) {
  412.             position = initPosition();
  413.         }

  414.         // If output frame requested is the same as definition frame,
  415.         // Position vector is returned directly
  416.         if (outputFrame == frame) {
  417.             return position;
  418.         }

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

  422.     }

  423.     /** Get the position in definition frame.
  424.      * @return position in the definition frame
  425.      * @see #getPVCoordinates()
  426.      * @since 12.0
  427.      */
  428.     public FieldVector3D<T> getPosition() {
  429.         if (position == null) {
  430.             position = initPosition();
  431.         }
  432.         return position;
  433.     }

  434.     /** Get the {@link TimeStampedPVCoordinates} in definition frame.
  435.      * @return FieldPVCoordinates in the definition frame
  436.      * @see #getPVCoordinates(Frame)
  437.      */
  438.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates() {
  439.         if (pvCoordinates == null) {
  440.             pvCoordinates = initPVCoordinates();
  441.             position      = pvCoordinates.getPosition();
  442.         }
  443.         return pvCoordinates;
  444.     }

  445.     /** Getter for Field-valued one.
  446.      * @return one
  447.      * */
  448.     protected T getOne() {
  449.         return one;
  450.     }

  451.     /** Getter for Field-valued zero.
  452.      * @return zero
  453.      * */
  454.     protected T getZero() {
  455.         return zero;
  456.     }

  457.     /** Getter for Field.
  458.      * @return CalculusField
  459.      * */
  460.     protected Field<T> getField() {
  461.         return field;
  462.     }

  463.     /** Compute the position coordinates from the canonical parameters.
  464.      * @return computed position coordinates
  465.      * @since 12.0
  466.      */
  467.     protected abstract FieldVector3D<T> initPosition();

  468.     /** Compute the position/velocity coordinates from the canonical parameters.
  469.      * @return computed position/velocity coordinates
  470.      */
  471.     protected abstract TimeStampedFieldPVCoordinates<T> initPVCoordinates();

  472.     /** Get a time-shifted orbit.
  473.      * <p>
  474.      * The orbit can be slightly shifted to close dates. This shift is based on
  475.      * a simple Keplerian model. It is <em>not</em> intended as a replacement
  476.      * for proper orbit and attitude propagation but should be sufficient for
  477.      * small time shifts or coarse accuracy.
  478.      * </p>
  479.      * @param dt time shift in seconds
  480.      * @return a new orbit, shifted with respect to the instance (which is immutable)
  481.      */
  482.     public abstract FieldOrbit<T> shiftedBy(T dt);

  483.     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
  484.      * <p>
  485.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  486.      * respect to Cartesian coordinate j. This means each row corresponds to one orbital parameter
  487.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  488.      * </p>
  489.      * @param type type of the position angle to use
  490.      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
  491.      * is larger than 6x6, only the 6x6 upper left corner will be modified
  492.      */
  493.     public void getJacobianWrtCartesian(final PositionAngleType type, final T[][] jacobian) {

  494.         final T[][] cachedJacobian;
  495.         synchronized (this) {
  496.             switch (type) {
  497.                 case MEAN :
  498.                     if (jacobianMeanWrtCartesian == null) {
  499.                         // first call, we need to compute the Jacobian and cache it
  500.                         jacobianMeanWrtCartesian = computeJacobianMeanWrtCartesian();
  501.                     }
  502.                     cachedJacobian = jacobianMeanWrtCartesian;
  503.                     break;
  504.                 case ECCENTRIC :
  505.                     if (jacobianEccentricWrtCartesian == null) {
  506.                         // first call, we need to compute the Jacobian and cache it
  507.                         jacobianEccentricWrtCartesian = computeJacobianEccentricWrtCartesian();
  508.                     }
  509.                     cachedJacobian = jacobianEccentricWrtCartesian;
  510.                     break;
  511.                 case TRUE :
  512.                     if (jacobianTrueWrtCartesian == null) {
  513.                         // first call, we need to compute the Jacobian and cache it
  514.                         jacobianTrueWrtCartesian = computeJacobianTrueWrtCartesian();
  515.                     }
  516.                     cachedJacobian = jacobianTrueWrtCartesian;
  517.                     break;
  518.                 default :
  519.                     throw new OrekitInternalError(null);
  520.             }
  521.         }

  522.         // fill the user provided array
  523.         for (int i = 0; i < cachedJacobian.length; ++i) {
  524.             System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
  525.         }

  526.     }

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

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

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

  570.     }

  571.     /** Create an inverse Jacobian.
  572.      * @param type type of the position angle to use
  573.      * @return inverse Jacobian
  574.      */
  575.     private T[][] createInverseJacobian(final PositionAngleType type) {

  576.         // get the direct Jacobian
  577.         final T[][] directJacobian = MathArrays.buildArray(getA().getField(), 6, 6);
  578.         getJacobianWrtCartesian(type, directJacobian);

  579.         // invert the direct Jacobian
  580.         final FieldMatrix<T> matrix = MatrixUtils.createFieldMatrix(directJacobian);
  581.         final FieldDecompositionSolver<T> solver = new FieldLUDecomposition<>(matrix).getSolver();
  582.         return solver.getInverse().getData();

  583.     }

  584.     /** Compute the Jacobian of the orbital parameters with mean angle with respect to the Cartesian parameters.
  585.      * <p>
  586.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  587.      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
  588.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  589.      * </p>
  590.      * @return 6x6 Jacobian matrix
  591.      * @see #computeJacobianEccentricWrtCartesian()
  592.      * @see #computeJacobianTrueWrtCartesian()
  593.      */
  594.     protected abstract T[][] computeJacobianMeanWrtCartesian();

  595.     /** Compute the Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian parameters.
  596.      * <p>
  597.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  598.      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
  599.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  600.      * </p>
  601.      * @return 6x6 Jacobian matrix
  602.      * @see #computeJacobianMeanWrtCartesian()
  603.      * @see #computeJacobianTrueWrtCartesian()
  604.      */
  605.     protected abstract T[][] computeJacobianEccentricWrtCartesian();

  606.     /** Compute the Jacobian of the orbital parameters with true angle with respect to the Cartesian parameters.
  607.      * <p>
  608.      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
  609.      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
  610.      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
  611.      * </p>
  612.      * @return 6x6 Jacobian matrix
  613.      * @see #computeJacobianMeanWrtCartesian()
  614.      * @see #computeJacobianEccentricWrtCartesian()
  615.      */
  616.     protected abstract T[][] computeJacobianTrueWrtCartesian();

  617.     /** Add the contribution of the Keplerian motion to parameters derivatives
  618.      * <p>
  619.      * This method is used by integration-based propagators to evaluate the part of Keplerian
  620.      * motion to evolution of the orbital state.
  621.      * </p>
  622.      * @param type type of the position angle in the state
  623.      * @param gm attraction coefficient to use
  624.      * @param pDot array containing orbital state derivatives to update (the Keplerian
  625.      * part must be <em>added</em> to the array components, as the array may already
  626.      * contain some non-zero elements corresponding to non-Keplerian parts)
  627.      */
  628.     public abstract void addKeplerContribution(PositionAngleType type, T gm, T[] pDot);

  629.         /** Fill a Jacobian half row with a single vector.
  630.      * @param a coefficient of the vector
  631.      * @param v vector
  632.      * @param row Jacobian matrix row
  633.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  634.      */

  635.     protected void fillHalfRow(final T a, final FieldVector3D<T> v, final T[] row, final int j) {
  636.         row[j]     = a.multiply(v.getX());
  637.         row[j + 1] = a.multiply(v.getY());
  638.         row[j + 2] = a.multiply(v.getZ());
  639.     }

  640.     /** Fill a Jacobian half row with a linear combination of vectors.
  641.      * @param a1 coefficient of the first vector
  642.      * @param v1 first vector
  643.      * @param a2 coefficient of the second vector
  644.      * @param v2 second vector
  645.      * @param row Jacobian matrix row
  646.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  647.      */
  648.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  649.                                       final T[] row, final int j) {
  650.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX());
  651.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY());
  652.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ());

  653.     }

  654.     /** Fill a Jacobian half row with a linear combination of vectors.
  655.      * @param a1 coefficient of the first vector
  656.      * @param v1 first vector
  657.      * @param a2 coefficient of the second vector
  658.      * @param v2 second vector
  659.      * @param a3 coefficient of the third vector
  660.      * @param v3 third vector
  661.      * @param row Jacobian matrix row
  662.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  663.      */
  664.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1,
  665.                                final T a2, final FieldVector3D<T> v2,
  666.                                final T a3, final FieldVector3D<T> v3,
  667.                                       final T[] row, final int j) {
  668.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX());
  669.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY());
  670.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ());

  671.     }

  672.     /** Fill a Jacobian half row with a linear combination of vectors.
  673.      * @param a1 coefficient of the first vector
  674.      * @param v1 first vector
  675.      * @param a2 coefficient of the second vector
  676.      * @param v2 second vector
  677.      * @param a3 coefficient of the third vector
  678.      * @param v3 third vector
  679.      * @param a4 coefficient of the fourth vector
  680.      * @param v4 fourth vector
  681.      * @param row Jacobian matrix row
  682.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  683.      */
  684.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  685.                                       final T a3, final FieldVector3D<T> v3, final T a4, final FieldVector3D<T> v4,
  686.                                       final T[] row, final int j) {
  687.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX());
  688.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY());
  689.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ());

  690.     }

  691.     /** Fill a Jacobian half row with a linear combination of vectors.
  692.      * @param a1 coefficient of the first vector
  693.      * @param v1 first vector
  694.      * @param a2 coefficient of the second vector
  695.      * @param v2 second vector
  696.      * @param a3 coefficient of the third vector
  697.      * @param v3 third vector
  698.      * @param a4 coefficient of the fourth vector
  699.      * @param v4 fourth vector
  700.      * @param a5 coefficient of the fifth vector
  701.      * @param v5 fifth vector
  702.      * @param row Jacobian matrix row
  703.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  704.      */
  705.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  706.                                       final T a3, final FieldVector3D<T> v3, final T a4, final FieldVector3D<T> v4,
  707.                                       final T a5, final FieldVector3D<T> v5,
  708.                                       final T[] row, final int j) {
  709.         row[j]     = a1.linearCombination(a1, v1.getX(), a2, v2.getX(), a3, v3.getX(), a4, v4.getX()).add(a5.multiply(v5.getX()));
  710.         row[j + 1] = a1.linearCombination(a1, v1.getY(), a2, v2.getY(), a3, v3.getY(), a4, v4.getY()).add(a5.multiply(v5.getY()));
  711.         row[j + 2] = a1.linearCombination(a1, v1.getZ(), a2, v2.getZ(), a3, v3.getZ(), a4, v4.getZ()).add(a5.multiply(v5.getZ()));

  712.     }

  713.     /** Fill a Jacobian half row with a linear combination of vectors.
  714.      * @param a1 coefficient of the first vector
  715.      * @param v1 first vector
  716.      * @param a2 coefficient of the second vector
  717.      * @param v2 second vector
  718.      * @param a3 coefficient of the third vector
  719.      * @param v3 third vector
  720.      * @param a4 coefficient of the fourth vector
  721.      * @param v4 fourth vector
  722.      * @param a5 coefficient of the fifth vector
  723.      * @param v5 fifth vector
  724.      * @param a6 coefficient of the sixth vector
  725.      * @param v6 sixth vector
  726.      * @param row Jacobian matrix row
  727.      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
  728.      */
  729.     protected void fillHalfRow(final T a1, final FieldVector3D<T> v1, final T a2, final FieldVector3D<T> v2,
  730.                                       final T a3, final FieldVector3D<T> v3, final T a4, final FieldVector3D<T> v4,
  731.                                       final T a5, final FieldVector3D<T> v5, final T a6, final FieldVector3D<T> v6,
  732.                                       final T[] row, final int j) {
  733.         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()));
  734.         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()));
  735.         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()));

  736.     }


  737. }