FieldSpacecraftState.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.propagation;


  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.exception.LocalizedCoreFormats;
  21. import org.hipparchus.exception.MathIllegalArgumentException;
  22. import org.hipparchus.exception.MathIllegalStateException;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.util.FastMath;
  25. import org.orekit.attitudes.FieldAttitude;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitIllegalArgumentException;
  28. import org.orekit.errors.OrekitIllegalStateException;
  29. import org.orekit.errors.OrekitMessages;
  30. import org.orekit.frames.FieldStaticTransform;
  31. import org.orekit.frames.FieldTransform;
  32. import org.orekit.frames.Frame;
  33. import org.orekit.orbits.FieldOrbit;
  34. import org.orekit.orbits.Orbit;
  35. import org.orekit.time.FieldAbsoluteDate;
  36. import org.orekit.time.FieldTimeShiftable;
  37. import org.orekit.time.FieldTimeStamped;
  38. import org.orekit.utils.DoubleArrayDictionary;
  39. import org.orekit.utils.FieldArrayDictionary;
  40. import org.orekit.utils.FieldAbsolutePVCoordinates;
  41. import org.orekit.utils.FieldPVCoordinates;
  42. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  43. import org.orekit.utils.TimeStampedPVCoordinates;

  44. /** This class is the representation of a complete state holding orbit, attitude
  45.  * and mass information at a given date, meant primarily for propagation.
  46.  *
  47.  * <p>It contains an {@link FieldOrbit}, or a {@link FieldAbsolutePVCoordinates} if there
  48.  * is no definite central body, plus the current mass and attitude at the intrinsic
  49.  * {@link FieldAbsoluteDate}. Quantities are guaranteed to be consistent in terms
  50.  * of date and reference frame. The spacecraft state may also contain additional
  51.  * states, which are simply named double arrays which can hold any user-defined
  52.  * data.
  53.  * </p>
  54.  * <p>
  55.  * The state can be slightly shifted to close dates. This actual shift varies
  56.  * between {@link FieldOrbit} and {@link FieldAbsolutePVCoordinates}.
  57.  * For attitude it is a linear extrapolation taking the spin rate into account
  58.  * and no mass change. It is <em>not</em> intended as a replacement for proper
  59.  * orbit and attitude propagation but should be sufficient for either small
  60.  * time shifts or coarse accuracy.
  61.  * </p>
  62.  * <p>
  63.  * The instance {@code FieldSpacecraftState} is guaranteed to be immutable.
  64.  * </p>
  65.  * @see org.orekit.propagation.numerical.NumericalPropagator
  66.  * @see SpacecraftState
  67.  * @author Fabien Maussion
  68.  * @author V&eacute;ronique Pommier-Maurussane
  69.  * @author Luc Maisonobe
  70.  * @author Vincent Mouraux
  71.  * @param <T> type of the field elements
  72.  */
  73. public class FieldSpacecraftState <T extends CalculusFieldElement<T>>
  74.     implements FieldTimeStamped<T>, FieldTimeShiftable<FieldSpacecraftState<T>, T> {

  75.     /** Default mass. */
  76.     private static final double DEFAULT_MASS = 1000.0;

  77.     /**
  78.      * tolerance on date comparison in {@link #checkConsistency(FieldOrbit, FieldAttitude)}. 100 ns
  79.      * corresponds to sub-mm accuracy at LEO orbital velocities.
  80.      */
  81.     private static final double DATE_INCONSISTENCY_THRESHOLD = 100e-9;

  82.     /** Orbital state. */
  83.     private final FieldOrbit<T> orbit;

  84.     /** Trajectory state, when it is not an orbit. */
  85.     private final FieldAbsolutePVCoordinates<T> absPva;

  86.     /** FieldAttitude<T>. */
  87.     private final FieldAttitude<T> attitude;

  88.     /** Current mass (kg). */
  89.     private final T mass;

  90.     /** Additional states. */
  91.     private final FieldArrayDictionary<T> additional;

  92.     /** Additional states derivatives.
  93.      * @since 11.1
  94.      */
  95.     private final FieldArrayDictionary<T> additionalDot;

  96.     /** Build a spacecraft state from orbit only.
  97.      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
  98.      * @param orbit the orbit
  99.      */
  100.     public FieldSpacecraftState(final FieldOrbit<T> orbit) {
  101.         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
  102.                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  103.              orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  104.     }

  105.     /** Build a spacecraft state from orbit and attitude.
  106.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  107.      * @param orbit the orbit
  108.      * @param attitude attitude
  109.      * @exception IllegalArgumentException if orbit and attitude dates
  110.      * or frames are not equal
  111.      */
  112.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude)
  113.         throws IllegalArgumentException {
  114.         this(orbit, attitude, orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  115.     }

  116.     /** Create a new instance from orbit and mass.
  117.      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
  118.      * @param orbit the orbit
  119.      * @param mass the mass (kg)
  120.      */
  121.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass) {
  122.         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
  123.                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  124.              mass, (FieldArrayDictionary<T>) null);
  125.     }

  126.     /** Build a spacecraft state from orbit, attitude and mass.
  127.      * @param orbit the orbit
  128.      * @param attitude attitude
  129.      * @param mass the mass (kg)
  130.      * @exception IllegalArgumentException if orbit and attitude dates
  131.      * or frames are not equal
  132.      */
  133.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final T mass)
  134.         throws IllegalArgumentException {
  135.         this(orbit, attitude, mass, (FieldArrayDictionary<T>) null);
  136.     }

  137.     /** Build a spacecraft state from orbit and additional states.
  138.      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
  139.      * @param orbit the orbit
  140.      * @param additional additional states
  141.      * @since 11.1
  142.      */
  143.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldArrayDictionary<T> additional) {
  144.         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
  145.                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  146.              orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), additional);
  147.     }

  148.     /** Build a spacecraft state from orbit attitude and additional states.
  149.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  150.      * @param orbit the orbit
  151.      * @param attitude attitude
  152.      * @param additional additional states
  153.      * @exception IllegalArgumentException if orbit and attitude dates
  154.      * or frames are not equal
  155.      * @since 11.1
  156.      */
  157.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final FieldArrayDictionary<T> additional)
  158.         throws IllegalArgumentException {
  159.         this(orbit, attitude, orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), additional);
  160.     }

  161.     /** Create a new instance from orbit, mass and additional states.
  162.      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
  163.      * @param orbit the orbit
  164.      * @param mass the mass (kg)
  165.      * @param additional additional states
  166.      * @since 11.1
  167.      */
  168.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass, final FieldArrayDictionary<T> additional) {
  169.         this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame())
  170.                         .getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  171.              mass, additional);
  172.     }

  173.     /** Build a spacecraft state from orbit, attitude, mass and additional states.
  174.      * @param orbit the orbit
  175.      * @param attitude attitude
  176.      * @param mass the mass (kg)
  177.      * @param additional additional states (may be null if no additional states are available)
  178.      * @since 11.1
  179.      */
  180.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude,
  181.                                 final T mass, final FieldArrayDictionary<T> additional) {
  182.         this(orbit, attitude, mass, additional, null);
  183.     }

  184.     /** Build a spacecraft state from orbit, attitude, mass, additional states and derivatives.
  185.      * @param orbit the orbit
  186.      * @param attitude attitude
  187.      * @param mass the mass (kg)
  188.      * @param additional additional states (may be null if no additional states are available)
  189.      * @param additionalDot additional states derivatives(may be null if no additional states derivative sare available)
  190.      * @exception IllegalArgumentException if orbit and attitude dates
  191.      * or frames are not equal
  192.      * @since 11.1
  193.      */
  194.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final T mass,
  195.                                 final FieldArrayDictionary<T> additional,
  196.                                 final FieldArrayDictionary<T> additionalDot)
  197.         throws IllegalArgumentException {
  198.         checkConsistency(orbit, attitude);
  199.         this.orbit      = orbit;
  200.         this.attitude   = attitude;
  201.         this.mass       = mass;
  202.         this.absPva     = null;

  203.         if (additional == null) {
  204.             this.additional = new FieldArrayDictionary<>(orbit.getDate().getField());
  205.         } else {
  206.             this.additional = new FieldArrayDictionary<>(additional);
  207.         }

  208.         if (additionalDot == null) {
  209.             this.additionalDot = new FieldArrayDictionary<>(orbit.getDate().getField());
  210.         } else {

  211.             this.additionalDot = new FieldArrayDictionary<>(additionalDot);
  212.         }

  213.     }

  214.     /** Convert a {@link FieldSpacecraftState}.
  215.      * @param field field to which the elements belong
  216.      * @param state state to convert
  217.      */
  218.     public FieldSpacecraftState(final Field<T> field, final SpacecraftState state) {

  219.         if (state.isOrbitDefined()) {

  220.             final Orbit nonFieldOrbit = state.getOrbit();
  221.             this.orbit    = nonFieldOrbit.getType().convertToFieldOrbit(field, nonFieldOrbit);
  222.             this.absPva   = null;

  223.         } else {
  224.             final TimeStampedPVCoordinates tspva = state.getPVCoordinates();
  225.             final FieldVector3D<T> position = new FieldVector3D<>(field, tspva.getPosition());
  226.             final FieldVector3D<T> velocity = new FieldVector3D<>(field, tspva.getVelocity());
  227.             final FieldVector3D<T> acceleration = new FieldVector3D<>(field, tspva.getAcceleration());
  228.             final FieldPVCoordinates<T> pva = new FieldPVCoordinates<>(position, velocity, acceleration);
  229.             final FieldAbsoluteDate<T> dateF = new FieldAbsoluteDate<>(field, state.getDate());
  230.             this.orbit  = null;
  231.             this.absPva = new FieldAbsolutePVCoordinates<>(state.getFrame(), dateF, pva);
  232.         }

  233.         this.attitude = new FieldAttitude<>(field, state.getAttitude());
  234.         this.mass     = field.getZero().newInstance(state.getMass());

  235.         final DoubleArrayDictionary additionalD = state.getAdditionalStatesValues();
  236.         if (additionalD.size() == 0) {
  237.             this.additional = new FieldArrayDictionary<>(field);
  238.         } else {
  239.             this.additional = new FieldArrayDictionary<>(field, additionalD.size());
  240.             for (final DoubleArrayDictionary.Entry entry : additionalD.getData()) {
  241.                 this.additional.put(entry.getKey(), entry.getValue());
  242.             }
  243.         }
  244.         final DoubleArrayDictionary additionalDotD = state.getAdditionalStatesDerivatives();
  245.         if (additionalDotD.size() == 0) {
  246.             this.additionalDot = new FieldArrayDictionary<>(field);
  247.         } else {
  248.             this.additionalDot = new FieldArrayDictionary<>(field, additionalDotD.size());
  249.             for (final DoubleArrayDictionary.Entry entry : additionalDotD.getData()) {
  250.                 this.additionalDot.put(entry.getKey(), entry.getValue());
  251.             }
  252.         }

  253.     }

  254.     /** Build a spacecraft state from orbit only.
  255.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  256.      * @param absPva position-velocity-acceleration
  257.      */
  258.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva) {
  259.         this(absPva,
  260.              SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()).
  261.                      getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  262.              absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  263.     }

  264.     /** Build a spacecraft state from orbit and attitude.
  265.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  266.      * @param absPva position-velocity-acceleration
  267.      * @param attitude attitude
  268.      * @exception IllegalArgumentException if orbit and attitude dates
  269.      * or frames are not equal
  270.      */
  271.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
  272.         throws IllegalArgumentException {
  273.         this(absPva, attitude, absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  274.     }

  275.     /** Create a new instance from orbit and mass.
  276.      * <p>Attitude law is set to an unspecified default attitude.</p>
  277.      * @param absPva position-velocity-acceleration
  278.      * @param mass the mass (kg)
  279.      */
  280.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass) {
  281.         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
  282.                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  283.              mass, (FieldArrayDictionary<T>) null);
  284.     }

  285.     /** Build a spacecraft state from orbit, attitude and mass.
  286.      * @param absPva position-velocity-acceleration
  287.      * @param attitude attitude
  288.      * @param mass the mass (kg)
  289.      * @exception IllegalArgumentException if orbit and attitude dates
  290.      * or frames are not equal
  291.      */
  292.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass)
  293.         throws IllegalArgumentException {
  294.         this(absPva, attitude, mass, (FieldArrayDictionary<T>) null);
  295.     }

  296.     /** Build a spacecraft state from orbit only.
  297.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  298.      * @param absPva position-velocity-acceleration
  299.      * @param additional additional states
  300.      * @since 11.1
  301.      */
  302.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldArrayDictionary<T> additional) {
  303.         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
  304.                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  305.              absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), additional);
  306.     }

  307.     /** Build a spacecraft state from orbit and attitude.
  308.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  309.      * @param absPva position-velocity-acceleration
  310.      * @param attitude attitude
  311.      * @param additional additional states
  312.      * @exception IllegalArgumentException if orbit and attitude dates
  313.      * or frames are not equal
  314.      * @since 11.1
  315.      */
  316.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  317.                                 final FieldArrayDictionary<T> additional)
  318.         throws IllegalArgumentException {
  319.         this(absPva, attitude, absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), additional);
  320.     }

  321.     /** Create a new instance from orbit and mass.
  322.      * <p>Attitude law is set to an unspecified default attitude.</p>
  323.      * @param absPva position-velocity-acceleration
  324.      * @param mass the mass (kg)
  325.      * @param additional additional states
  326.      * @since 11.1
  327.      */
  328.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass, final FieldArrayDictionary<T> additional) {
  329.         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
  330.                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  331.              mass, additional);
  332.     }

  333.     /** Build a spacecraft state from orbit, attitude and mass.
  334.      * @param absPva position-velocity-acceleration
  335.      * @param attitude attitude
  336.      * @param mass the mass (kg)
  337.      * @param additional additional states (may be null if no additional states are available)
  338.      * @since 11.1
  339.      */
  340.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  341.                            final T mass, final FieldArrayDictionary<T> additional) {
  342.         this(absPva, attitude, mass, additional, null);
  343.     }

  344.     /** Build a spacecraft state from orbit, attitude and mass.
  345.      * @param absPva position-velocity-acceleration
  346.      * @param attitude attitude
  347.      * @param mass the mass (kg)
  348.      * @param additional additional states (may be null if no additional states are available)
  349.      * @param additionalDot additional states derivatives(may be null if no additional states derivatives are available)
  350.      * @exception IllegalArgumentException if orbit and attitude dates
  351.      * or frames are not equal
  352.      * @since 11.1
  353.      */
  354.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass,
  355.                                 final FieldArrayDictionary<T> additional, final FieldArrayDictionary<T> additionalDot)
  356.         throws IllegalArgumentException {
  357.         checkConsistency(absPva, attitude);
  358.         this.orbit      = null;
  359.         this.absPva     = absPva;
  360.         this.attitude   = attitude;
  361.         this.mass       = mass;
  362.         if (additional == null) {
  363.             this.additional = new FieldArrayDictionary<>(absPva.getDate().getField());
  364.         } else {
  365.             this.additional = new FieldArrayDictionary<>(additional);
  366.         }
  367.         if (additionalDot == null) {
  368.             this.additionalDot = new FieldArrayDictionary<>(absPva.getDate().getField());
  369.         } else {
  370.             this.additionalDot = new FieldArrayDictionary<>(additionalDot);
  371.         }
  372.     }

  373.     /** Add an additional state.
  374.      * <p>
  375.      * {@link FieldSpacecraftState SpacecraftState} instances are immutable,
  376.      * so this method does <em>not</em> change the instance, but rather
  377.      * creates a new instance, which has the same orbit, attitude, mass
  378.      * and additional states as the original instance, except it also
  379.      * has the specified state. If the original instance already had an
  380.      * additional state with the same name, it will be overridden. If it
  381.      * did not have any additional state with that name, the new instance
  382.      * will have one more additional state than the original instance.
  383.      * </p>
  384.      * @param name name of the additional state
  385.      * @param value value of the additional state
  386.      * @return a new instance, with the additional state added
  387.      * @see #hasAdditionalState(String)
  388.      * @see #getAdditionalState(String)
  389.      * @see #getAdditionalStatesValues()
  390.      */
  391.     @SafeVarargs
  392.     public final FieldSpacecraftState<T> addAdditionalState(final String name, final T... value) {
  393.         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additional);
  394.         newDict.put(name, value.clone());
  395.         if (isOrbitDefined()) {
  396.             return new FieldSpacecraftState<>(orbit, attitude, mass, newDict, additionalDot);
  397.         } else {
  398.             return new FieldSpacecraftState<>(absPva, attitude, mass, newDict, additionalDot);
  399.         }
  400.     }

  401.     /** Add an additional state derivative.
  402.     * {@link FieldSpacecraftState FieldSpacecraftState} instances are immutable,
  403.      * so this method does <em>not</em> change the instance, but rather
  404.      * creates a new instance, which has the same components as the original
  405.      * instance, except it also has the specified state derivative. If the
  406.      * original instance already had an additional state derivative with the
  407.      * same name, it will be overridden. If it did not have any additional
  408.      * state derivative with that name, the new instance will have one more
  409.      * additional state derivative than the original instance.
  410.      * @param name name of the additional state derivative
  411.      * @param value value of the additional state derivative
  412.      * @return a new instance, with the additional state derivative added
  413.      * @see #hasAdditionalStateDerivative(String)
  414.      * @see #getAdditionalStateDerivative(String)
  415.      * @see #getAdditionalStatesDerivatives()
  416.      */
  417.     @SafeVarargs
  418.     public final FieldSpacecraftState<T> addAdditionalStateDerivative(final String name, final T... value) {
  419.         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additionalDot);
  420.         newDict.put(name, value.clone());
  421.         if (isOrbitDefined()) {
  422.             return new FieldSpacecraftState<>(orbit, attitude, mass, additional, newDict);
  423.         } else {
  424.             return new FieldSpacecraftState<>(absPva, attitude, mass, additional, newDict);
  425.         }
  426.     }

  427.     /** Check orbit and attitude dates are equal.
  428.      * @param orbitN the orbit
  429.      * @param attitudeN attitude
  430.      * @exception IllegalArgumentException if orbit and attitude dates
  431.      * are not equal
  432.      */
  433.     private void checkConsistency(final FieldOrbit<T> orbitN, final FieldAttitude<T> attitudeN)
  434.         throws IllegalArgumentException {
  435.         if (orbitN.getDate().durationFrom(attitudeN.getDate()).abs().getReal() >
  436.             DATE_INCONSISTENCY_THRESHOLD) {

  437.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
  438.                                                      orbitN.getDate(), attitudeN.getDate());
  439.         }

  440.         if (orbitN.getFrame() != attitudeN.getReferenceFrame()) {
  441.             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
  442.                                                      orbitN.getFrame().getName(),
  443.                                                      attitudeN.getReferenceFrame().getName());
  444.         }
  445.     }

  446.     /** Check if the state contains an orbit part.
  447.      * <p>
  448.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  449.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}.
  450.      * </p>
  451.      * @return true if state contains an orbit (in which case {@link #getOrbit()}
  452.      * will not throw an exception), or false if the state contains an
  453.      * absolut position-velocity-acceleration (in which case {@link #getAbsPVA()}
  454.      * will not throw an exception)
  455.      */
  456.     public boolean isOrbitDefined() {
  457.         return orbit != null;
  458.     }

  459.     /**
  460.      * Check FieldAbsolutePVCoordinates and attitude dates are equal.
  461.      * @param absPva   position-velocity-acceleration
  462.      * @param attitude attitude
  463.      * @param <T>      the type of the field elements
  464.      * @exception IllegalArgumentException if orbit and attitude dates are not equal
  465.      */
  466.     private static <T extends CalculusFieldElement<T>> void checkConsistency(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
  467.         throws IllegalArgumentException {
  468.         if (FastMath.abs(absPva.getDate().durationFrom(attitude.getDate())).getReal() >
  469.             DATE_INCONSISTENCY_THRESHOLD) {
  470.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
  471.                                                      absPva.getDate(), attitude.getDate());
  472.         }
  473.         if (absPva.getFrame() != attitude.getReferenceFrame()) {
  474.             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
  475.                                                      absPva.getFrame().getName(),
  476.                                                      attitude.getReferenceFrame().getName());
  477.         }
  478.     }

  479.     /** Get a time-shifted state.
  480.      * <p>
  481.      * The state can be slightly shifted to close dates. This shift is based on
  482.      * a simple Keplerian model for orbit, a linear extrapolation for attitude
  483.      * taking the spin rate into account and neither mass nor additional states
  484.      * changes. It is <em>not</em> intended as a replacement for proper orbit
  485.      * and attitude propagation but should be sufficient for small time shifts
  486.      * or coarse accuracy.
  487.      * </p>
  488.      * <p>
  489.      * As a rough order of magnitude, the following table shows the extrapolation
  490.      * errors obtained between this simple shift method and an {@link
  491.      * org.orekit.propagation.numerical.FieldNumericalPropagator numerical
  492.      * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field,
  493.      * Sun and Moon third bodies attractions, drag and solar radiation pressure.
  494.      * Beware that these results will be different for other orbits.
  495.      * </p>
  496.      * <table border="1">
  497.      * <caption>Extrapolation Error</caption>
  498.      * <tr style="background-color: #ccccff;"><th>interpolation time (s)</th>
  499.      * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr>
  500.      * <tr><td style="background-color: #eeeeff; padding:5px"> 60</td><td>  18</td><td> 1.1</td></tr>
  501.      * <tr><td style="background-color: #eeeeff; padding:5px">120</td><td>  72</td><td> 9.1</td></tr>
  502.      * <tr><td style="background-color: #eeeeff; padding:5px">300</td><td> 447</td><td> 140</td></tr>
  503.      * <tr><td style="background-color: #eeeeff; padding:5px">600</td><td>1601</td><td>1067</td></tr>
  504.      * <tr><td style="background-color: #eeeeff; padding:5px">900</td><td>3141</td><td>3307</td></tr>
  505.      * </table>
  506.      * @param dt time shift in seconds
  507.      * @return a new state, shifted with respect to the instance (which is immutable)
  508.      * except for the mass which stay unchanged
  509.      */
  510.     @Override
  511.     public FieldSpacecraftState<T> shiftedBy(final double dt) {
  512.         if (isOrbitDefined()) {
  513.             return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
  514.                                               mass, shiftAdditional(dt), additionalDot);
  515.         } else {
  516.             return new FieldSpacecraftState<>(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
  517.                                               mass, shiftAdditional(dt), additionalDot);
  518.         }
  519.     }

  520.     /** Get a time-shifted state.
  521.      * <p>
  522.      * The state can be slightly shifted to close dates. This shift is based on
  523.      * a simple Keplerian model for orbit, a linear extrapolation for attitude
  524.      * taking the spin rate into account and neither mass nor additional states
  525.      * changes. It is <em>not</em> intended as a replacement for proper orbit
  526.      * and attitude propagation but should be sufficient for small time shifts
  527.      * or coarse accuracy.
  528.      * </p>
  529.      * <p>
  530.      * As a rough order of magnitude, the following table shows the extrapolation
  531.      * errors obtained between this simple shift method and an {@link
  532.      * org.orekit.propagation.numerical.FieldNumericalPropagator numerical
  533.      * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field,
  534.      * Sun and Moon third bodies attractions, drag and solar radiation pressure.
  535.      * Beware that these results will be different for other orbits.
  536.      * </p>
  537.      * <table border="1">
  538.      * <caption>Extrapolation Error</caption>
  539.      * <tr style="background-color: #ccccff;"><th>interpolation time (s)</th>
  540.      * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr>
  541.      * <tr><td style="background-color: #eeeeff; padding:5px"> 60</td><td>  18</td><td> 1.1</td></tr>
  542.      * <tr><td style="background-color: #eeeeff; padding:5px">120</td><td>  72</td><td> 9.1</td></tr>
  543.      * <tr><td style="background-color: #eeeeff; padding:5px">300</td><td> 447</td><td> 140</td></tr>
  544.      * <tr><td style="background-color: #eeeeff; padding:5px">600</td><td>1601</td><td>1067</td></tr>
  545.      * <tr><td style="background-color: #eeeeff; padding:5px">900</td><td>3141</td><td>3307</td></tr>
  546.      * </table>
  547.      * @param dt time shift in seconds
  548.      * @return a new state, shifted with respect to the instance (which is immutable)
  549.      * except for the mass which stay unchanged
  550.      */
  551.     @Override
  552.     public FieldSpacecraftState<T> shiftedBy(final T dt) {
  553.         if (isOrbitDefined()) {
  554.             return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
  555.                                               mass, shiftAdditional(dt), additionalDot);
  556.         } else {
  557.             return new FieldSpacecraftState<>(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
  558.                                               mass, shiftAdditional(dt), additionalDot);
  559.         }
  560.     }

  561.     /** Shift additional states.
  562.      * @param dt time shift in seconds
  563.      * @return shifted additional states
  564.      * @since 11.1.1
  565.      */
  566.     private FieldArrayDictionary<T> shiftAdditional(final double dt) {

  567.         // fast handling when there are no derivatives at all
  568.         if (additionalDot.size() == 0) {
  569.             return additional;
  570.         }

  571.         // there are derivatives, we need to take them into account in the additional state
  572.         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<>(additional);
  573.         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
  574.             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
  575.             if (entry != null) {
  576.                 entry.scaledIncrement(dt, dotEntry);
  577.             }
  578.         }

  579.         return shifted;

  580.     }

  581.     /** Shift additional states.
  582.      * @param dt time shift in seconds
  583.      * @return shifted additional states
  584.      * @since 11.1.1
  585.      */
  586.     private FieldArrayDictionary<T> shiftAdditional(final T dt) {

  587.         // fast handling when there are no derivatives at all
  588.         if (additionalDot.size() == 0) {
  589.             return additional;
  590.         }

  591.         // there are derivatives, we need to take them into account in the additional state
  592.         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<>(additional);
  593.         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
  594.             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
  595.             if (entry != null) {
  596.                 entry.scaledIncrement(dt, dotEntry);
  597.             }
  598.         }

  599.         return shifted;

  600.     }

  601.     /** Get the absolute position-velocity-acceleration.
  602.      * <p>
  603.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  604.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
  605.      * one is present can be checked using {@link #isOrbitDefined()}.
  606.      * </p>
  607.      * @return absolute position-velocity-acceleration
  608.      * @exception OrekitIllegalStateException if position-velocity-acceleration is null,
  609.      * which mean the state rather contains an {@link FieldOrbit}
  610.      * @see #isOrbitDefined()
  611.      * @see #getOrbit()
  612.      */
  613.     public FieldAbsolutePVCoordinates<T> getAbsPVA() throws OrekitIllegalStateException {
  614.         if (isOrbitDefined()) {
  615.             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ABSOLUTE_PVCOORDINATES);
  616.         }
  617.         return absPva;
  618.     }

  619.     /** Get the current orbit.
  620.      * <p>
  621.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  622.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
  623.      * one is present can be checked using {@link #isOrbitDefined()}.
  624.      * </p>
  625.      * @return the orbit
  626.      * @exception OrekitIllegalStateException if orbit is null,
  627.      * which means the state rather contains an {@link FieldAbsolutePVCoordinates absolute
  628.      * position-velocity-acceleration}
  629.      * @see #isOrbitDefined()
  630.      * @see #getAbsPVA()
  631.      */
  632.     public FieldOrbit<T> getOrbit() throws OrekitIllegalStateException {
  633.         if (orbit == null) {
  634.             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ORBIT);
  635.         }
  636.         return orbit;
  637.     }

  638.     /** {@inheritDoc} */
  639.     @Override
  640.     public FieldAbsoluteDate<T> getDate() {
  641.         return isOrbitDefined() ? orbit.getDate() : absPva.getDate();
  642.     }

  643.     /** Get the defining frame.
  644.      * @return the frame in which state is defined
  645.      */
  646.     public Frame getFrame() {
  647.         return isOrbitDefined() ? orbit.getFrame() : absPva.getFrame();
  648.     }


  649.     /** Check if an additional state is available.
  650.      * @param name name of the additional state
  651.      * @return true if the additional state is available
  652.      * @see #addAdditionalState(String, CalculusFieldElement...)
  653.      * @see #getAdditionalState(String)
  654.      * @see #getAdditionalStatesValues()
  655.      */
  656.     public boolean hasAdditionalState(final String name) {
  657.         return additional.getEntry(name) != null;
  658.     }

  659.     /** Check if an additional state derivative is available.
  660.      * @param name name of the additional state derivative
  661.      * @return true if the additional state derivative is available
  662.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  663.      * @see #getAdditionalStateDerivative(String)
  664.      * @see #getAdditionalStatesDerivatives()
  665.      */
  666.     public boolean hasAdditionalStateDerivative(final String name) {
  667.         return additionalDot.getEntry(name) != null;
  668.     }

  669.     /** Check if two instances have the same set of additional states available.
  670.      * <p>
  671.      * Only the names and dimensions of the additional states are compared,
  672.      * not their values.
  673.      * </p>
  674.      * @param state state to compare to instance
  675.      * @exception MathIllegalArgumentException if an additional state does not have
  676.      * the same dimension in both states
  677.      */
  678.     public void ensureCompatibleAdditionalStates(final FieldSpacecraftState<T> state)
  679.         throws MathIllegalArgumentException {

  680.         // check instance additional states is a subset of the other one
  681.         for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
  682.             final T[] other = state.additional.get(entry.getKey());
  683.             if (other == null) {
  684.                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  685.                                           entry.getKey());
  686.             }
  687.             if (other.length != entry.getValue().length) {
  688.                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  689.                                                     other.length, entry.getValue().length);
  690.             }
  691.         }

  692.         // check instance additional states derivatives is a subset of the other one
  693.         for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
  694.             final T[] other = state.additionalDot.get(entry.getKey());
  695.             if (other == null) {
  696.                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  697.                                           entry.getKey());
  698.             }
  699.             if (other.length != entry.getValue().length) {
  700.                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  701.                                                     other.length, entry.getValue().length);
  702.             }
  703.         }

  704.         if (state.additional.size() > additional.size()) {
  705.             // the other state has more additional states
  706.             for (final FieldArrayDictionary<T>.Entry entry : state.additional.getData()) {
  707.                 if (additional.getEntry(entry.getKey()) == null) {
  708.                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  709.                                               entry.getKey());
  710.                 }
  711.             }
  712.         }

  713.         if (state.additionalDot.size() > additionalDot.size()) {
  714.             // the other state has more additional states
  715.             for (final FieldArrayDictionary<T>.Entry entry : state.additionalDot.getData()) {
  716.                 if (additionalDot.getEntry(entry.getKey()) == null) {
  717.                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  718.                                               entry.getKey());
  719.                 }
  720.             }
  721.         }

  722.     }

  723.     /** Get an additional state.
  724.      * @param name name of the additional state
  725.      * @return value of the additional state
  726.           * @see #addAdditionalState(String, CalculusFieldElement...)
  727.      * @see #hasAdditionalState(String)
  728.      * @see #getAdditionalStatesValues()
  729.      */
  730.     public T[] getAdditionalState(final String name) {
  731.         final FieldArrayDictionary<T>.Entry entry = additional.getEntry(name);
  732.         if (entry == null) {
  733.             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
  734.         }
  735.         return entry.getValue();
  736.     }

  737.     /** Get an additional state derivative.
  738.      * @param name name of the additional state derivative
  739.      * @return value of the additional state derivative
  740.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  741.      * @see #hasAdditionalStateDerivative(String)
  742.      * @see #getAdditionalStatesDerivatives()
  743.      * @since 11.1
  744.      */
  745.     public T[] getAdditionalStateDerivative(final String name) {
  746.         final FieldArrayDictionary<T>.Entry entry = additionalDot.getEntry(name);
  747.         if (entry == null) {
  748.             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
  749.         }
  750.         return entry.getValue();
  751.     }

  752.     /** Get an unmodifiable map of additional states.
  753.      * @return unmodifiable map of additional states
  754.      * @see #addAdditionalState(String, CalculusFieldElement...)
  755.      * @see #hasAdditionalState(String)
  756.      * @see #getAdditionalState(String)
  757.      * @since 11.1
  758.      */
  759.     public FieldArrayDictionary<T> getAdditionalStatesValues() {
  760.         return additional.unmodifiableView();
  761.     }

  762.     /** Get an unmodifiable map of additional states derivatives.
  763.      * @return unmodifiable map of additional states derivatives
  764.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  765.      * @see #hasAdditionalStateDerivative(String)
  766.      * @see #getAdditionalStateDerivative(String)
  767.     * @since 11.1
  768.       */
  769.     public FieldArrayDictionary<T> getAdditionalStatesDerivatives() {
  770.         return additionalDot.unmodifiableView();
  771.     }

  772.     /** Compute the transform from state defining frame to spacecraft frame.
  773.      * <p>The spacecraft frame origin is at the point defined by the orbit,
  774.      * and its orientation is defined by the attitude.</p>
  775.      * @return transform from specified frame to current spacecraft frame
  776.      */
  777.     public FieldTransform<T> toTransform() {
  778.         final TimeStampedFieldPVCoordinates<T> pv = getPVCoordinates();
  779.         return new FieldTransform<>(pv.getDate(),
  780.                                     new FieldTransform<>(pv.getDate(), pv.negate()),
  781.                                     new FieldTransform<>(pv.getDate(), attitude.getOrientation()));
  782.     }

  783.     /** Compute the static transform from state defining frame to spacecraft frame.
  784.      * @return static transform from specified frame to current spacecraft frame
  785.      * @see #toTransform()
  786.      * @since 12.0
  787.      */
  788.     public FieldStaticTransform<T> toStaticTransform() {
  789.         return FieldStaticTransform.of(getDate(), getPosition().negate(), attitude.getRotation());
  790.     }

  791.     /** Get the central attraction coefficient.
  792.      * @return mu central attraction coefficient (m^3/s^2), or {code Double.NaN} if the
  793.      * state contains an absolute position-velocity-acceleration rather than an orbit
  794.      */
  795.     public T getMu() {
  796.         return isOrbitDefined() ? orbit.getMu() : absPva.getDate().getField().getZero().add(Double.NaN);
  797.     }

  798.     /** Get the Keplerian period.
  799.      * <p>The Keplerian period is computed directly from semi major axis
  800.      * and central acceleration constant.</p>
  801.      * @return Keplerian period in seconds, or {code Double.NaN} if the
  802.      * state contains an absolute position-velocity-acceleration rather
  803.      * than an orbit
  804.      */
  805.     public T getKeplerianPeriod() {
  806.         return isOrbitDefined() ? orbit.getKeplerianPeriod() : absPva.getDate().getField().getZero().add(Double.NaN);
  807.     }

  808.     /** Get the Keplerian mean motion.
  809.      * <p>The Keplerian mean motion is computed directly from semi major axis
  810.      * and central acceleration constant.</p>
  811.      * @return Keplerian mean motion in radians per second, or {code Double.NaN} if the
  812.      * state contains an absolute position-velocity-acceleration rather
  813.      * than an orbit
  814.      */
  815.     public T getKeplerianMeanMotion() {
  816.         return isOrbitDefined() ? orbit.getKeplerianMeanMotion() : absPva.getDate().getField().getZero().add(Double.NaN);
  817.     }

  818.     /** Get the semi-major axis.
  819.      * @return semi-major axis (m), or {code Double.NaN} if the
  820.      * state contains an absolute position-velocity-acceleration rather
  821.      * than an orbit
  822.      */
  823.     public T getA() {
  824.         return isOrbitDefined() ? orbit.getA() : absPva.getDate().getField().getZero().add(Double.NaN);
  825.     }

  826.     /** Get the first component of the eccentricity vector (as per equinoctial parameters).
  827.      * @return e cos(ω + Ω), first component of eccentricity vector, or {code Double.NaN} if the
  828.      * state contains an absolute position-velocity-acceleration rather
  829.      * than an orbit
  830.      * @see #getE()
  831.      */
  832.     public T getEquinoctialEx() {
  833.         return isOrbitDefined() ? orbit.getEquinoctialEx() : absPva.getDate().getField().getZero().add(Double.NaN);
  834.     }

  835.     /** Get the second component of the eccentricity vector (as per equinoctial parameters).
  836.      * @return e sin(ω + Ω), second component of the eccentricity vector, or {code Double.NaN} if the
  837.      * state contains an absolute position-velocity-acceleration rather
  838.      * than an orbit
  839.      * @see #getE()
  840.      */
  841.     public T getEquinoctialEy() {
  842.         return isOrbitDefined() ? orbit.getEquinoctialEy() : absPva.getDate().getField().getZero().add(Double.NaN);
  843.     }

  844.     /** Get the first component of the inclination vector (as per equinoctial parameters).
  845.      * @return tan(i/2) cos(Ω), first component of the inclination vector, or {code Double.NaN} if the
  846.      * state contains an absolute position-velocity-acceleration rather
  847.      * than an orbit
  848.      * @see #getI()
  849.      */
  850.     public T getHx() {
  851.         return isOrbitDefined() ? orbit.getHx() : absPva.getDate().getField().getZero().add(Double.NaN);
  852.     }

  853.     /** Get the second component of the inclination vector (as per equinoctial parameters).
  854.      * @return tan(i/2) sin(Ω), second component of the inclination vector, or {code Double.NaN} if the
  855.      * state contains an absolute position-velocity-acceleration rather
  856.      * than an orbit
  857.      * @see #getI()
  858.      */
  859.     public T getHy() {
  860.         return isOrbitDefined() ? orbit.getHy() : absPva.getDate().getField().getZero().add(Double.NaN);
  861.     }

  862.     /** Get the true latitude argument (as per equinoctial parameters).
  863.      * @return v + ω + Ω true longitude argument (rad), or {code Double.NaN} if the
  864.      * state contains an absolute position-velocity-acceleration rather
  865.      * than an orbit
  866.      * @see #getLE()
  867.      * @see #getLM()
  868.      */
  869.     public T getLv() {
  870.         return isOrbitDefined() ? orbit.getLv() : absPva.getDate().getField().getZero().add(Double.NaN);
  871.     }

  872.     /** Get the eccentric latitude argument (as per equinoctial parameters).
  873.      * @return E + ω + Ω eccentric longitude argument (rad), or {code Double.NaN} if the
  874.      * state contains an absolute position-velocity-acceleration rather
  875.      * than an orbit
  876.      * @see #getLv()
  877.      * @see #getLM()
  878.      */
  879.     public T getLE() {
  880.         return isOrbitDefined() ? orbit.getLE() : absPva.getDate().getField().getZero().add(Double.NaN);
  881.     }

  882.     /** Get the mean longitude argument (as per equinoctial parameters).
  883.      * @return M + ω + Ω mean latitude argument (rad), or {code Double.NaN} if the
  884.      * state contains an absolute position-velocity-acceleration rather
  885.      * than an orbit
  886.      * @see #getLv()
  887.      * @see #getLE()
  888.      */
  889.     public T getLM() {
  890.         return isOrbitDefined() ? orbit.getLM() : absPva.getDate().getField().getZero().add(Double.NaN);
  891.     }

  892.     // Additional orbital elements

  893.     /** Get the eccentricity.
  894.      * @return eccentricity, or {code Double.NaN} if the
  895.      * state contains an absolute position-velocity-acceleration rather
  896.      * than an orbit
  897.      * @see #getEquinoctialEx()
  898.      * @see #getEquinoctialEy()
  899.      */
  900.     public T getE() {
  901.         return isOrbitDefined() ? orbit.getE() : absPva.getDate().getField().getZero().add(Double.NaN);
  902.     }

  903.     /** Get the inclination.
  904.      * @return inclination (rad)
  905.      * @see #getHx()
  906.      * @see #getHy()
  907.      */
  908.     public T getI() {
  909.         return isOrbitDefined() ? orbit.getI() : absPva.getDate().getField().getZero().add(Double.NaN);
  910.     }

  911.     /** Get the position in orbit definition frame.
  912.      * @return position in orbit definition frame
  913.      * @since 12.0
  914.      */
  915.     public FieldVector3D<T> getPosition() {
  916.         return isOrbitDefined() ? orbit.getPosition() : absPva.getPosition();
  917.     }

  918.     /** Get the {@link TimeStampedFieldPVCoordinates} in orbit definition frame.
  919.      * <p>
  920.      * Compute the position and velocity of the satellite. This method caches its
  921.      * results, and recompute them only when the method is called with a new value
  922.      * for mu. The result is provided as a reference to the internally cached
  923.      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
  924.      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
  925.      * </p>
  926.      * @return pvCoordinates in orbit definition frame
  927.      */
  928.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates() {
  929.         return isOrbitDefined() ? orbit.getPVCoordinates() : absPva.getPVCoordinates();
  930.     }

  931.     /** Get the position in given output frame.
  932.      * @param outputFrame frame in which position should be defined
  933.      * @return position in given output frame
  934.      * @since 12.0
  935.      * @see #getPVCoordinates(Frame)
  936.      */
  937.     public FieldVector3D<T> getPosition(final Frame outputFrame) {
  938.         return isOrbitDefined() ? orbit.getPosition(outputFrame) : absPva.getPosition(outputFrame);
  939.     }

  940.     /** Get the {@link TimeStampedFieldPVCoordinates} in given output frame.
  941.      * <p>
  942.      * Compute the position and velocity of the satellite. This method caches its
  943.      * results, and recompute them only when the method is called with a new value
  944.      * for mu. The result is provided as a reference to the internally cached
  945.      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
  946.      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
  947.      * </p>
  948.      * @param outputFrame frame in which coordinates should be defined
  949.      * @return pvCoordinates in orbit definition frame
  950.      */
  951.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final Frame outputFrame) {
  952.         return isOrbitDefined() ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame);
  953.     }

  954.     /** Get the attitude.
  955.      * @return the attitude.
  956.      */
  957.     public FieldAttitude<T> getAttitude() {
  958.         return attitude;
  959.     }

  960.     /** Gets the current mass.
  961.      * @return the mass (kg)
  962.      */
  963.     public T getMass() {
  964.         return mass;
  965.     }

  966.     /**To convert a FieldSpacecraftState instance into a SpacecraftState instance.
  967.      *
  968.      * @return SpacecraftState instance with the same properties
  969.      */
  970.     public SpacecraftState toSpacecraftState() {
  971.         final DoubleArrayDictionary dictionary;
  972.         if (additional.size() == 0) {
  973.             dictionary = new DoubleArrayDictionary();
  974.         } else {
  975.             dictionary = new DoubleArrayDictionary(additional.size());
  976.             for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
  977.                 final double[] array = new double[entry.getValue().length];
  978.                 for (int k = 0; k < array.length; ++k) {
  979.                     array[k] = entry.getValue()[k].getReal();
  980.                 }
  981.                 dictionary.put(entry.getKey(), array);
  982.             }
  983.         }
  984.         final DoubleArrayDictionary dictionaryDot;
  985.         if (additionalDot.size() == 0) {
  986.             dictionaryDot = new DoubleArrayDictionary();
  987.         } else {
  988.             dictionaryDot = new DoubleArrayDictionary(additionalDot.size());
  989.             for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
  990.                 final double[] array = new double[entry.getValue().length];
  991.                 for (int k = 0; k < array.length; ++k) {
  992.                     array[k] = entry.getValue()[k].getReal();
  993.                 }
  994.                 dictionaryDot.put(entry.getKey(), array);
  995.             }
  996.         }
  997.         if (isOrbitDefined()) {
  998.             return new SpacecraftState(orbit.toOrbit(), attitude.toAttitude(),
  999.                                        mass.getReal(), dictionary, dictionaryDot);
  1000.         } else {
  1001.             return new SpacecraftState(absPva.toAbsolutePVCoordinates(),
  1002.                                        attitude.toAttitude(), mass.getReal(),
  1003.                                        dictionary, dictionaryDot);
  1004.         }
  1005.     }

  1006.     @Override
  1007.     public String toString() {
  1008.         return "FieldSpacecraftState{" +
  1009.                 "orbit=" + orbit +
  1010.                 ", attitude=" + attitude +
  1011.                 ", mass=" + mass +
  1012.                 ", additional=" + additional +
  1013.                 ", additionalDot=" + additionalDot +
  1014.                 '}';
  1015.     }

  1016. }