FieldSpacecraftState.java

  1. /* Copyright 2002-2023 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.hipparchus.util.MathArrays;
  26. import org.orekit.attitudes.FieldAttitude;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitIllegalArgumentException;
  29. import org.orekit.errors.OrekitIllegalStateException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.frames.FieldStaticTransform;
  32. import org.orekit.frames.FieldTransform;
  33. import org.orekit.frames.Frame;
  34. import org.orekit.orbits.FieldOrbit;
  35. import org.orekit.orbits.PositionAngleType;
  36. import org.orekit.time.FieldAbsoluteDate;
  37. import org.orekit.time.FieldTimeShiftable;
  38. import org.orekit.time.FieldTimeStamped;
  39. import org.orekit.utils.DoubleArrayDictionary;
  40. import org.orekit.utils.FieldArrayDictionary;
  41. import org.orekit.utils.FieldAbsolutePVCoordinates;
  42. import org.orekit.utils.FieldPVCoordinates;
  43. import org.orekit.utils.TimeStampedFieldPVCoordinates;
  44. import org.orekit.utils.TimeStampedPVCoordinates;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  214.     }

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

  220.         if (state.isOrbitDefined()) {
  221.             final double[] stateD    = new double[6];
  222.             final double[] stateDotD = state.getOrbit().hasDerivatives() ? new double[6] : null;
  223.             final PositionAngleType positionAngleType = PositionAngleType.TRUE;
  224.             state.getOrbit().getType().mapOrbitToArray(state.getOrbit(), positionAngleType, stateD, stateDotD);
  225.             final T[] stateF    = MathArrays.buildArray(field, 6);
  226.             for (int i = 0; i < stateD.length; ++i) {
  227.                 stateF[i]    = field.getZero().add(stateD[i]);
  228.             }
  229.             final T[] stateDotF;
  230.             if (stateDotD == null) {
  231.                 stateDotF = null;
  232.             } else {
  233.                 stateDotF = MathArrays.buildArray(field, 6);
  234.                 for (int i = 0; i < stateDotD.length; ++i) {
  235.                     stateDotF[i] = field.getZero().add(stateDotD[i]);
  236.                 }
  237.             }

  238.             final FieldAbsoluteDate<T> dateF = new FieldAbsoluteDate<>(field, state.getDate());

  239.             this.orbit    = state.getOrbit().getType().mapArrayToOrbit(stateF, stateDotF, positionAngleType, dateF,
  240.                                                                        field.getZero().add(state.getMu()), state.getFrame());
  241.             this.absPva   = null;

  242.         } else {
  243.             final TimeStampedPVCoordinates tspva = state.getPVCoordinates();
  244.             final FieldVector3D<T> position = new FieldVector3D<>(field, tspva.getPosition());
  245.             final FieldVector3D<T> velocity = new FieldVector3D<>(field, tspva.getVelocity());
  246.             final FieldVector3D<T> acceleration = new FieldVector3D<>(field, tspva.getAcceleration());
  247.             final FieldPVCoordinates<T> pva = new FieldPVCoordinates<>(position, velocity, acceleration);
  248.             final FieldAbsoluteDate<T> dateF = new FieldAbsoluteDate<>(field, state.getDate());
  249.             this.orbit  = null;
  250.             this.absPva = new FieldAbsolutePVCoordinates<>(state.getFrame(), dateF, pva);
  251.         }

  252.         this.attitude = new FieldAttitude<>(field, state.getAttitude());
  253.         this.mass     = field.getZero().add(state.getMass());

  254.         final DoubleArrayDictionary additionalD = state.getAdditionalStatesValues();
  255.         if (additionalD.size() == 0) {
  256.             this.additional = new FieldArrayDictionary<>(field);
  257.         } else {
  258.             this.additional = new FieldArrayDictionary<>(field, additionalD.size());
  259.             for (final DoubleArrayDictionary.Entry entry : additionalD.getData()) {
  260.                 this.additional.put(entry.getKey(), entry.getValue());
  261.             }
  262.         }
  263.         final DoubleArrayDictionary additionalDotD = state.getAdditionalStatesDerivatives();
  264.         if (additionalDotD.size() == 0) {
  265.             this.additionalDot = new FieldArrayDictionary<>(field);
  266.         } else {
  267.             this.additionalDot = new FieldArrayDictionary<>(field, additionalDotD.size());
  268.             for (final DoubleArrayDictionary.Entry entry : additionalDotD.getData()) {
  269.                 this.additionalDot.put(entry.getKey(), entry.getValue());
  270.             }
  271.         }

  272.     }

  273.     /** Build a spacecraft state from orbit only.
  274.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  275.      * @param absPva position-velocity-acceleration
  276.      */
  277.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva) {
  278.         this(absPva,
  279.              SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()).
  280.                      getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  281.              absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  282.     }

  283.     /** Build a spacecraft state from orbit and attitude.
  284.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  285.      * @param absPva position-velocity-acceleration
  286.      * @param attitude attitude
  287.      * @exception IllegalArgumentException if orbit and attitude dates
  288.      * or frames are not equal
  289.      */
  290.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
  291.         throws IllegalArgumentException {
  292.         this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  293.     }

  294.     /** Create a new instance from orbit and mass.
  295.      * <p>Attitude law is set to an unspecified default attitude.</p>
  296.      * @param absPva position-velocity-acceleration
  297.      * @param mass the mass (kg)
  298.      */
  299.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass) {
  300.         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
  301.                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  302.              mass, (FieldArrayDictionary<T>) null);
  303.     }

  304.     /** Build a spacecraft state from orbit, attitude and mass.
  305.      * @param absPva position-velocity-acceleration
  306.      * @param attitude attitude
  307.      * @param mass the mass (kg)
  308.      * @exception IllegalArgumentException if orbit and attitude dates
  309.      * or frames are not equal
  310.      */
  311.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass)
  312.         throws IllegalArgumentException {
  313.         this(absPva, attitude, mass, (FieldArrayDictionary<T>) null);
  314.     }

  315.     /** Build a spacecraft state from orbit only.
  316.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  317.      * @param absPva position-velocity-acceleration
  318.      * @param additional additional states
  319.      * @since 11.1
  320.      */
  321.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldArrayDictionary<T> additional) {
  322.         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
  323.                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  324.              absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional);
  325.     }

  326.     /** Build a spacecraft state from orbit and attitude.
  327.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  328.      * @param absPva position-velocity-acceleration
  329.      * @param attitude attitude
  330.      * @param additional additional states
  331.      * @exception IllegalArgumentException if orbit and attitude dates
  332.      * or frames are not equal
  333.      * @since 11.1
  334.      */
  335.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  336.                                 final FieldArrayDictionary<T> additional)
  337.         throws IllegalArgumentException {
  338.         this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional);
  339.     }

  340.     /** Create a new instance from orbit and mass.
  341.      * <p>Attitude law is set to an unspecified default attitude.</p>
  342.      * @param absPva position-velocity-acceleration
  343.      * @param mass the mass (kg)
  344.      * @param additional additional states
  345.      * @since 11.1
  346.      */
  347.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass, final FieldArrayDictionary<T> additional) {
  348.         this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame())
  349.                         .getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  350.              mass, additional);
  351.     }

  352.     /** Build a spacecraft state from orbit, attitude and mass.
  353.      * @param absPva position-velocity-acceleration
  354.      * @param attitude attitude
  355.      * @param mass the mass (kg)
  356.      * @param additional additional states (may be null if no additional states are available)
  357.      * @since 11.1
  358.      */
  359.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  360.                            final T mass, final FieldArrayDictionary<T> additional) {
  361.         this(absPva, attitude, mass, additional, null);
  362.     }

  363.     /** Build a spacecraft state from orbit, attitude and mass.
  364.      * @param absPva position-velocity-acceleration
  365.      * @param attitude attitude
  366.      * @param mass the mass (kg)
  367.      * @param additional additional states (may be null if no additional states are available)
  368.      * @param additionalDot additional states derivatives(may be null if no additional states derivatives are available)
  369.      * @exception IllegalArgumentException if orbit and attitude dates
  370.      * or frames are not equal
  371.      * @since 11.1
  372.      */
  373.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass,
  374.                                 final FieldArrayDictionary<T> additional, final FieldArrayDictionary<T> additionalDot)
  375.         throws IllegalArgumentException {
  376.         checkConsistency(absPva, attitude);
  377.         this.orbit      = null;
  378.         this.absPva     = absPva;
  379.         this.attitude   = attitude;
  380.         this.mass       = mass;
  381.         if (additional == null) {
  382.             this.additional = new FieldArrayDictionary<>(absPva.getDate().getField());
  383.         } else {
  384.             this.additional = new FieldArrayDictionary<>(additional);
  385.         }
  386.         if (additionalDot == null) {
  387.             this.additionalDot = new FieldArrayDictionary<>(absPva.getDate().getField());
  388.         } else {
  389.             this.additionalDot = new FieldArrayDictionary<>(additionalDot);
  390.         }
  391.     }

  392.     /** Add an additional state.
  393.      * <p>
  394.      * {@link FieldSpacecraftState SpacecraftState} instances are immutable,
  395.      * so this method does <em>not</em> change the instance, but rather
  396.      * creates a new instance, which has the same orbit, attitude, mass
  397.      * and additional states as the original instance, except it also
  398.      * has the specified state. If the original instance already had an
  399.      * additional state with the same name, it will be overridden. If it
  400.      * did not have any additional state with that name, the new instance
  401.      * will have one more additional state than the original instance.
  402.      * </p>
  403.      * @param name name of the additional state
  404.      * @param value value of the additional state
  405.      * @return a new instance, with the additional state added
  406.      * @see #hasAdditionalState(String)
  407.      * @see #getAdditionalState(String)
  408.      * @see #getAdditionalStatesValues()
  409.      */
  410.     @SafeVarargs
  411.     public final FieldSpacecraftState<T> addAdditionalState(final String name, final T... value) {
  412.         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additional);
  413.         newDict.put(name, value.clone());
  414.         if (isOrbitDefined()) {
  415.             return new FieldSpacecraftState<>(orbit, attitude, mass, newDict, additionalDot);
  416.         } else {
  417.             return new FieldSpacecraftState<>(absPva, attitude, mass, newDict, additionalDot);
  418.         }
  419.     }

  420.     /** Add an additional state derivative.
  421.     * {@link FieldSpacecraftState FieldSpacecraftState} instances are immutable,
  422.      * so this method does <em>not</em> change the instance, but rather
  423.      * creates a new instance, which has the same components as the original
  424.      * instance, except it also has the specified state derivative. If the
  425.      * original instance already had an additional state derivative with the
  426.      * same name, it will be overridden. If it did not have any additional
  427.      * state derivative with that name, the new instance will have one more
  428.      * additional state derivative than the original instance.
  429.      * @param name name of the additional state derivative
  430.      * @param value value of the additional state derivative
  431.      * @return a new instance, with the additional state derivative added
  432.      * @see #hasAdditionalStateDerivative(String)
  433.      * @see #getAdditionalStateDerivative(String)
  434.      * @see #getAdditionalStatesDerivatives()
  435.      */
  436.     @SafeVarargs
  437.     public final FieldSpacecraftState<T> addAdditionalStateDerivative(final String name, final T... value) {
  438.         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additionalDot);
  439.         newDict.put(name, value.clone());
  440.         if (isOrbitDefined()) {
  441.             return new FieldSpacecraftState<>(orbit, attitude, mass, additional, newDict);
  442.         } else {
  443.             return new FieldSpacecraftState<>(absPva, attitude, mass, additional, newDict);
  444.         }
  445.     }

  446.     /** Check orbit and attitude dates are equal.
  447.      * @param orbitN the orbit
  448.      * @param attitudeN attitude
  449.      * @exception IllegalArgumentException if orbit and attitude dates
  450.      * are not equal
  451.      */
  452.     private void checkConsistency(final FieldOrbit<T> orbitN, final FieldAttitude<T> attitudeN)
  453.         throws IllegalArgumentException {
  454.         if (orbitN.getDate().durationFrom(attitudeN.getDate()).abs().getReal() >
  455.             DATE_INCONSISTENCY_THRESHOLD) {

  456.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
  457.                                                      orbitN.getDate(), attitudeN.getDate());
  458.         }

  459.         if (orbitN.getFrame() != attitudeN.getReferenceFrame()) {
  460.             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
  461.                                                      orbitN.getFrame().getName(),
  462.                                                      attitudeN.getReferenceFrame().getName());
  463.         }
  464.     }

  465.     /** Check if the state contains an orbit part.
  466.      * <p>
  467.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  468.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}.
  469.      * </p>
  470.      * @return true if state contains an orbit (in which case {@link #getOrbit()}
  471.      * will not throw an exception), or false if the state contains an
  472.      * absolut position-velocity-acceleration (in which case {@link #getAbsPVA()}
  473.      * will not throw an exception)
  474.      */
  475.     public boolean isOrbitDefined() {
  476.         return orbit != null;
  477.     }

  478.     /**
  479.      * Check FieldAbsolutePVCoordinates and attitude dates are equal.
  480.      * @param absPva   position-velocity-acceleration
  481.      * @param attitude attitude
  482.      * @param <T>      the type of the field elements
  483.      * @exception IllegalArgumentException if orbit and attitude dates are not equal
  484.      */
  485.     private static <T extends CalculusFieldElement<T>> void checkConsistency(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
  486.         throws IllegalArgumentException {
  487.         if (FastMath.abs(absPva.getDate().durationFrom(attitude.getDate())).getReal() >
  488.             DATE_INCONSISTENCY_THRESHOLD) {
  489.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
  490.                                                      absPva.getDate(), attitude.getDate());
  491.         }
  492.         if (absPva.getFrame() != attitude.getReferenceFrame()) {
  493.             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
  494.                                                      absPva.getFrame().getName(),
  495.                                                      attitude.getReferenceFrame().getName());
  496.         }
  497.     }

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

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

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

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

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

  598.         return shifted;

  599.     }

  600.     /** Shift additional states.
  601.      * @param dt time shift in seconds
  602.      * @return shifted additional states
  603.      * @since 11.1.1
  604.      */
  605.     private FieldArrayDictionary<T> shiftAdditional(final T dt) {

  606.         // fast handling when there are no derivatives at all
  607.         if (additionalDot.size() == 0) {
  608.             return additional;
  609.         }

  610.         // there are derivatives, we need to take them into account in the additional state
  611.         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<>(additional);
  612.         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
  613.             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
  614.             if (entry != null) {
  615.                 entry.scaledIncrement(dt, dotEntry);
  616.             }
  617.         }

  618.         return shifted;

  619.     }

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

  638.     /** Get the current orbit.
  639.      * <p>
  640.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  641.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
  642.      * one is present can be checked using {@link #isOrbitDefined()}.
  643.      * </p>
  644.      * @return the orbit
  645.      * @exception OrekitIllegalStateException if orbit is null,
  646.      * which means the state rather contains an {@link FieldAbsolutePVCoordinates absolute
  647.      * position-velocity-acceleration}
  648.      * @see #isOrbitDefined()
  649.      * @see #getAbsPVA()
  650.      */
  651.     public FieldOrbit<T> getOrbit() throws OrekitIllegalStateException {
  652.         if (orbit == null) {
  653.             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ORBIT);
  654.         }
  655.         return orbit;
  656.     }

  657.     /** {@inheritDoc} */
  658.     @Override
  659.     public FieldAbsoluteDate<T> getDate() {
  660.         return isOrbitDefined() ? orbit.getDate() : absPva.getDate();
  661.     }

  662.     /** Get the defining frame.
  663.      * @return the frame in which state is defined
  664.      */
  665.     public Frame getFrame() {
  666.         return isOrbitDefined() ? orbit.getFrame() : absPva.getFrame();
  667.     }


  668.     /** Check if an additional state is available.
  669.      * @param name name of the additional state
  670.      * @return true if the additional state is available
  671.      * @see #addAdditionalState(String, CalculusFieldElement...)
  672.      * @see #getAdditionalState(String)
  673.      * @see #getAdditionalStatesValues()
  674.      */
  675.     public boolean hasAdditionalState(final String name) {
  676.         return additional.getEntry(name) != null;
  677.     }

  678.     /** Check if an additional state derivative is available.
  679.      * @param name name of the additional state derivative
  680.      * @return true if the additional state derivative is available
  681.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  682.      * @see #getAdditionalStateDerivative(String)
  683.      * @see #getAdditionalStatesDerivatives()
  684.      */
  685.     public boolean hasAdditionalStateDerivative(final String name) {
  686.         return additionalDot.getEntry(name) != null;
  687.     }

  688.     /** Check if two instances have the same set of additional states available.
  689.      * <p>
  690.      * Only the names and dimensions of the additional states are compared,
  691.      * not their values.
  692.      * </p>
  693.      * @param state state to compare to instance
  694.      * @exception MathIllegalArgumentException if an additional state does not have
  695.      * the same dimension in both states
  696.      */
  697.     public void ensureCompatibleAdditionalStates(final FieldSpacecraftState<T> state)
  698.         throws MathIllegalArgumentException {

  699.         // check instance additional states is a subset of the other one
  700.         for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
  701.             final T[] other = state.additional.get(entry.getKey());
  702.             if (other == null) {
  703.                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  704.                                           entry.getKey());
  705.             }
  706.             if (other.length != entry.getValue().length) {
  707.                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  708.                                                     other.length, entry.getValue().length);
  709.             }
  710.         }

  711.         // check instance additional states derivatives is a subset of the other one
  712.         for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
  713.             final T[] other = state.additionalDot.get(entry.getKey());
  714.             if (other == null) {
  715.                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  716.                                           entry.getKey());
  717.             }
  718.             if (other.length != entry.getValue().length) {
  719.                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  720.                                                     other.length, entry.getValue().length);
  721.             }
  722.         }

  723.         if (state.additional.size() > additional.size()) {
  724.             // the other state has more additional states
  725.             for (final FieldArrayDictionary<T>.Entry entry : state.additional.getData()) {
  726.                 if (additional.getEntry(entry.getKey()) == null) {
  727.                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  728.                                               entry.getKey());
  729.                 }
  730.             }
  731.         }

  732.         if (state.additionalDot.size() > additionalDot.size()) {
  733.             // the other state has more additional states
  734.             for (final FieldArrayDictionary<T>.Entry entry : state.additionalDot.getData()) {
  735.                 if (additionalDot.getEntry(entry.getKey()) == null) {
  736.                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  737.                                               entry.getKey());
  738.                 }
  739.             }
  740.         }

  741.     }

  742.     /** Get an additional state.
  743.      * @param name name of the additional state
  744.      * @return value of the additional state
  745.           * @see #addAdditionalState(String, CalculusFieldElement...)
  746.      * @see #hasAdditionalState(String)
  747.      * @see #getAdditionalStatesValues()
  748.      */
  749.     public T[] getAdditionalState(final String name) {
  750.         final FieldArrayDictionary<T>.Entry entry = additional.getEntry(name);
  751.         if (entry == null) {
  752.             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
  753.         }
  754.         return entry.getValue();
  755.     }

  756.     /** Get an additional state derivative.
  757.      * @param name name of the additional state derivative
  758.      * @return value of the additional state derivative
  759.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  760.      * @see #hasAdditionalStateDerivative(String)
  761.      * @see #getAdditionalStatesDerivatives()
  762.      * @since 11.1
  763.      */
  764.     public T[] getAdditionalStateDerivative(final String name) {
  765.         final FieldArrayDictionary<T>.Entry entry = additionalDot.getEntry(name);
  766.         if (entry == null) {
  767.             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
  768.         }
  769.         return entry.getValue();
  770.     }

  771.     /** Get an unmodifiable map of additional states.
  772.      * @return unmodifiable map of additional states
  773.      * @see #addAdditionalState(String, CalculusFieldElement...)
  774.      * @see #hasAdditionalState(String)
  775.      * @see #getAdditionalState(String)
  776.      * @since 11.1
  777.      */
  778.     public FieldArrayDictionary<T> getAdditionalStatesValues() {
  779.         return additional.unmodifiableView();
  780.     }

  781.     /** Get an unmodifiable map of additional states derivatives.
  782.      * @return unmodifiable map of additional states derivatives
  783.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  784.      * @see #hasAdditionalStateDerivative(String)
  785.      * @see #getAdditionalStateDerivative(String)
  786.     * @since 11.1
  787.       */
  788.     public FieldArrayDictionary<T> getAdditionalStatesDerivatives() {
  789.         return additionalDot.unmodifiableView();
  790.     }

  791.     /** Compute the transform from state defining frame to spacecraft frame.
  792.      * <p>The spacecraft frame origin is at the point defined by the orbit,
  793.      * and its orientation is defined by the attitude.</p>
  794.      * @return transform from specified frame to current spacecraft frame
  795.      */
  796.     public FieldTransform<T> toTransform() {
  797.         final TimeStampedFieldPVCoordinates<T> pv = getPVCoordinates();
  798.         return new FieldTransform<>(pv.getDate(),
  799.                                     new FieldTransform<>(pv.getDate(), pv.negate()),
  800.                                     new FieldTransform<>(pv.getDate(), attitude.getOrientation()));
  801.     }

  802.     /** Compute the static transform from state defining frame to spacecraft frame.
  803.      * @return static transform from specified frame to current spacecraft frame
  804.      * @see #toTransform()
  805.      * @since 12.0
  806.      */
  807.     public FieldStaticTransform<T> toStaticTransform() {
  808.         return FieldStaticTransform.of(getDate(), getPosition().negate(), attitude.getRotation());
  809.     }

  810.     /** Get the central attraction coefficient.
  811.      * @return mu central attraction coefficient (m^3/s^2), or {code Double.NaN} if the
  812.      * state contains an absolute position-velocity-acceleration rather than an orbit
  813.      */
  814.     public T getMu() {
  815.         return isOrbitDefined() ? orbit.getMu() : absPva.getDate().getField().getZero().add(Double.NaN);
  816.     }

  817.     /** Get the Keplerian period.
  818.      * <p>The Keplerian period is computed directly from semi major axis
  819.      * and central acceleration constant.</p>
  820.      * @return Keplerian period in seconds, or {code Double.NaN} if the
  821.      * state contains an absolute position-velocity-acceleration rather
  822.      * than an orbit
  823.      */
  824.     public T getKeplerianPeriod() {
  825.         return isOrbitDefined() ? orbit.getKeplerianPeriod() : absPva.getDate().getField().getZero().add(Double.NaN);
  826.     }

  827.     /** Get the Keplerian mean motion.
  828.      * <p>The Keplerian mean motion is computed directly from semi major axis
  829.      * and central acceleration constant.</p>
  830.      * @return Keplerian mean motion in radians per second, or {code Double.NaN} if the
  831.      * state contains an absolute position-velocity-acceleration rather
  832.      * than an orbit
  833.      */
  834.     public T getKeplerianMeanMotion() {
  835.         return isOrbitDefined() ? orbit.getKeplerianMeanMotion() : absPva.getDate().getField().getZero().add(Double.NaN);
  836.     }

  837.     /** Get the semi-major axis.
  838.      * @return semi-major axis (m), or {code Double.NaN} if the
  839.      * state contains an absolute position-velocity-acceleration rather
  840.      * than an orbit
  841.      */
  842.     public T getA() {
  843.         return isOrbitDefined() ? orbit.getA() : absPva.getDate().getField().getZero().add(Double.NaN);
  844.     }

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

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

  863.     /** Get the first component of the inclination vector (as per equinoctial parameters).
  864.      * @return tan(i/2) cos(Ω), first component of the inclination vector, or {code Double.NaN} if the
  865.      * state contains an absolute position-velocity-acceleration rather
  866.      * than an orbit
  867.      * @see #getI()
  868.      */
  869.     public T getHx() {
  870.         return isOrbitDefined() ? orbit.getHx() : absPva.getDate().getField().getZero().add(Double.NaN);
  871.     }

  872.     /** Get the second component of the inclination vector (as per equinoctial parameters).
  873.      * @return tan(i/2) sin(Ω), second component of the inclination vector, or {code Double.NaN} if the
  874.      * state contains an absolute position-velocity-acceleration rather
  875.      * than an orbit
  876.      * @see #getI()
  877.      */
  878.     public T getHy() {
  879.         return isOrbitDefined() ? orbit.getHy() : absPva.getDate().getField().getZero().add(Double.NaN);
  880.     }

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

  891.     /** Get the eccentric latitude argument (as per equinoctial parameters).
  892.      * @return E + ω + Ω eccentric longitude argument (rad), or {code Double.NaN} if the
  893.      * state contains an absolute position-velocity-acceleration rather
  894.      * than an orbit
  895.      * @see #getLv()
  896.      * @see #getLM()
  897.      */
  898.     public T getLE() {
  899.         return isOrbitDefined() ? orbit.getLE() : absPva.getDate().getField().getZero().add(Double.NaN);
  900.     }

  901.     /** Get the mean longitude argument (as per equinoctial parameters).
  902.      * @return M + ω + Ω mean latitude argument (rad), or {code Double.NaN} if the
  903.      * state contains an absolute position-velocity-acceleration rather
  904.      * than an orbit
  905.      * @see #getLv()
  906.      * @see #getLE()
  907.      */
  908.     public T getLM() {
  909.         return isOrbitDefined() ? orbit.getLM() : absPva.getDate().getField().getZero().add(Double.NaN);
  910.     }

  911.     // Additional orbital elements

  912.     /** Get the eccentricity.
  913.      * @return eccentricity, or {code Double.NaN} if the
  914.      * state contains an absolute position-velocity-acceleration rather
  915.      * than an orbit
  916.      * @see #getEquinoctialEx()
  917.      * @see #getEquinoctialEy()
  918.      */
  919.     public T getE() {
  920.         return isOrbitDefined() ? orbit.getE() : absPva.getDate().getField().getZero().add(Double.NaN);
  921.     }

  922.     /** Get the inclination.
  923.      * @return inclination (rad)
  924.      * @see #getHx()
  925.      * @see #getHy()
  926.      */
  927.     public T getI() {
  928.         return isOrbitDefined() ? orbit.getI() : absPva.getDate().getField().getZero().add(Double.NaN);
  929.     }

  930.     /** Get the position in orbit definition frame.
  931.      * @return position in orbit definition frame
  932.      * @since 12.0
  933.      */
  934.     public FieldVector3D<T> getPosition() {
  935.         return isOrbitDefined() ? orbit.getPosition() : absPva.getPosition();
  936.     }

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

  950.     /** Get the position in given output frame.
  951.      * @param outputFrame frame in which position should be defined
  952.      * @return position in given output frame
  953.      * @since 12.0
  954.      * @see #getPVCoordinates(Frame)
  955.      */
  956.     public FieldVector3D<T> getPosition(final Frame outputFrame) {
  957.         return isOrbitDefined() ? orbit.getPosition(outputFrame) : absPva.getPosition(outputFrame);
  958.     }

  959.     /** Get the {@link TimeStampedFieldPVCoordinates} in given output frame.
  960.      * <p>
  961.      * Compute the position and velocity of the satellite. This method caches its
  962.      * results, and recompute them only when the method is called with a new value
  963.      * for mu. The result is provided as a reference to the internally cached
  964.      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
  965.      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
  966.      * </p>
  967.      * @param outputFrame frame in which coordinates should be defined
  968.      * @return pvCoordinates in orbit definition frame
  969.      */
  970.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final Frame outputFrame) {
  971.         return isOrbitDefined() ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame);
  972.     }

  973.     /** Get the attitude.
  974.      * @return the attitude.
  975.      */
  976.     public FieldAttitude<T> getAttitude() {
  977.         return attitude;
  978.     }

  979.     /** Gets the current mass.
  980.      * @return the mass (kg)
  981.      */
  982.     public T getMass() {
  983.         return mass;
  984.     }

  985.     /**To convert a FieldSpacecraftState instance into a SpacecraftState instance.
  986.      *
  987.      * @return SpacecraftState instance with the same properties
  988.      */
  989.     public SpacecraftState toSpacecraftState() {
  990.         final DoubleArrayDictionary dictionary;
  991.         if (additional.size() == 0) {
  992.             dictionary = new DoubleArrayDictionary();
  993.         } else {
  994.             dictionary = new DoubleArrayDictionary(additional.size());
  995.             for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
  996.                 final double[] array = new double[entry.getValue().length];
  997.                 for (int k = 0; k < array.length; ++k) {
  998.                     array[k] = entry.getValue()[k].getReal();
  999.                 }
  1000.                 dictionary.put(entry.getKey(), array);
  1001.             }
  1002.         }
  1003.         final DoubleArrayDictionary dictionaryDot;
  1004.         if (additionalDot.size() == 0) {
  1005.             dictionaryDot = new DoubleArrayDictionary();
  1006.         } else {
  1007.             dictionaryDot = new DoubleArrayDictionary(additionalDot.size());
  1008.             for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
  1009.                 final double[] array = new double[entry.getValue().length];
  1010.                 for (int k = 0; k < array.length; ++k) {
  1011.                     array[k] = entry.getValue()[k].getReal();
  1012.                 }
  1013.                 dictionaryDot.put(entry.getKey(), array);
  1014.             }
  1015.         }
  1016.         if (isOrbitDefined()) {
  1017.             return new SpacecraftState(orbit.toOrbit(), attitude.toAttitude(),
  1018.                                        mass.getReal(), dictionary, dictionaryDot);
  1019.         } else {
  1020.             return new SpacecraftState(absPva.toAbsolutePVCoordinates(),
  1021.                                        attitude.toAttitude(), mass.getReal(),
  1022.                                        dictionary, dictionaryDot);
  1023.         }
  1024.     }

  1025.     @Override
  1026.     public String toString() {
  1027.         return "FieldSpacecraftState{" +
  1028.                 "orbit=" + orbit +
  1029.                 ", attitude=" + attitude +
  1030.                 ", mass=" + mass +
  1031.                 ", additional=" + additional +
  1032.                 ", additionalDot=" + additionalDot +
  1033.                 '}';
  1034.     }

  1035. }