FieldSpacecraftState.java

  1. /* Copyright 2002-2022 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 java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.stream.Stream;

  23. import org.hipparchus.CalculusFieldElement;
  24. import org.hipparchus.Field;
  25. import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
  26. import org.hipparchus.exception.LocalizedCoreFormats;
  27. import org.hipparchus.exception.MathIllegalArgumentException;
  28. import org.hipparchus.exception.MathIllegalStateException;
  29. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  30. import org.hipparchus.util.FastMath;
  31. import org.hipparchus.util.MathArrays;
  32. import org.orekit.attitudes.FieldAttitude;
  33. import org.orekit.attitudes.LofOffset;
  34. import org.orekit.errors.OrekitException;
  35. import org.orekit.errors.OrekitIllegalArgumentException;
  36. import org.orekit.errors.OrekitIllegalStateException;
  37. import org.orekit.errors.OrekitMessages;
  38. import org.orekit.frames.FieldTransform;
  39. import org.orekit.frames.Frame;
  40. import org.orekit.frames.LOFType;
  41. import org.orekit.orbits.FieldOrbit;
  42. import org.orekit.orbits.PositionAngle;
  43. import org.orekit.time.FieldAbsoluteDate;
  44. import org.orekit.time.FieldTimeInterpolable;
  45. import org.orekit.time.FieldTimeShiftable;
  46. import org.orekit.time.FieldTimeStamped;
  47. import org.orekit.utils.DoubleArrayDictionary;
  48. import org.orekit.utils.FieldAbsolutePVCoordinates;
  49. import org.orekit.utils.FieldArrayDictionary;
  50. import org.orekit.utils.FieldPVCoordinates;
  51. import org.orekit.utils.TimeStampedFieldPVCoordinates;


  52. /** This class is the representation of a complete state holding orbit, attitude
  53.  * and mass information at a given date.
  54.  *
  55.  * <p>It contains an {@link FieldOrbit orbital state} at a current
  56.  * {@link FieldAbsoluteDate} both handled by an {@link FieldOrbit}, plus the current
  57.  * mass and attitude. FieldOrbit and state are guaranteed to be consistent in terms
  58.  * of date and reference frame. The spacecraft state may also contain additional
  59.  * states, which are simply named double arrays which can hold any user-defined
  60.  * data.
  61.  * </p>
  62.  * <p>
  63.  * The state can be slightly shifted to close dates. This shift is based on
  64.  * a simple Keplerian model for orbit, a linear extrapolation for attitude
  65.  * taking the spin rate into account and no mass change. It is <em>not</em>
  66.  * intended as a replacement for proper orbit and attitude propagation but
  67.  * should be sufficient for either small time shifts or coarse accuracy.
  68.  * </p>
  69.  * <p>
  70.  * The instance {@code FieldSpacecraftState} is guaranteed to be immutable.
  71.  * </p>
  72.  * @see org.orekit.propagation.numerical.NumericalPropagator
  73.  * @author Fabien Maussion
  74.  * @author V&eacute;ronique Pommier-Maurussane
  75.  * @author Luc Maisonobe
  76.  * @author Vincent Mouraux
  77.  */
  78. public class FieldSpacecraftState <T extends CalculusFieldElement<T>>
  79.     implements FieldTimeStamped<T>, FieldTimeShiftable<FieldSpacecraftState<T>, T>, FieldTimeInterpolable<FieldSpacecraftState<T>, T> {

  80.     /** Default mass. */
  81.     private static final double DEFAULT_MASS = 1000.0;

  82.     /**
  83.      * tolerance on date comparison in {@link #checkConsistency(FieldOrbit<T>, FieldAttitude<T>)}. 100 ns
  84.      * corresponds to sub-mm accuracy at LEO orbital velocities.
  85.      */
  86.     private static final double DATE_INCONSISTENCY_THRESHOLD = 100e-9;

  87.     /** Orbital state. */
  88.     private final FieldOrbit<T> orbit;

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

  91.     /** FieldAttitude<T>. */
  92.     private final FieldAttitude<T> attitude;

  93.     /** Current mass (kg). */
  94.     private final T mass;

  95.     /** Additional states. */
  96.     private final FieldArrayDictionary<T> additional;

  97.     /** Additional states derivatives.
  98.      * @since 11.1
  99.      */
  100.     private final FieldArrayDictionary<T> additionalDot;

  101.     /** Build a spacecraft state from orbit only.
  102.      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
  103.      * @param orbit the orbit
  104.      */
  105.     public FieldSpacecraftState(final FieldOrbit<T> orbit) {
  106.         this(orbit,
  107.              new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  108.              orbit.getA().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  109.     }

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

  121.     /** Create a new instance from orbit and mass.
  122.      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
  123.      * @param orbit the orbit
  124.      * @param mass the mass (kg)
  125.      */
  126.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass) {
  127.         this(orbit,
  128.              new LofOffset(orbit.getFrame(),
  129.                            LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  130.              mass, (FieldArrayDictionary<T>) null);
  131.     }

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

  143.     /** Build a spacecraft state from orbit and additional states.
  144.      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
  145.      * @param orbit the orbit
  146.      * @param additional additional states
  147.      * @deprecated as of 11.1, replacezd by {@link #FieldSpacecraftState(FieldOrbit, FieldArrayDictionary)}
  148.      */
  149.     @Deprecated
  150.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final Map<String, T[]> additional) {
  151.         this(orbit, new FieldArrayDictionary<>(orbit.getDate().getField(), additional));
  152.     }

  153.     /** Build a spacecraft state from orbit and additional states.
  154.      * <p>FieldAttitude and mass are set to unspecified non-null arbitrary values.</p>
  155.      * @param orbit the orbit
  156.      * @param additional additional states
  157.      * @since 11.1
  158.      */
  159.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldArrayDictionary<T> additional) {
  160.         this(orbit,
  161.              new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  162.              orbit.getA().getField().getZero().add(DEFAULT_MASS), additional);
  163.     }

  164.     /** Build a spacecraft state from orbit attitude and additional states.
  165.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  166.      * @param orbit the orbit
  167.      * @param attitude attitude
  168.      * @param additional additional states
  169.      * @exception IllegalArgumentException if orbit and attitude dates
  170.      * or frames are not equal
  171.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldOrbit, FieldAttitude, FieldArrayDictionary)}
  172.      */
  173.     @Deprecated
  174.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final Map<String, T[]> additional)
  175.         throws IllegalArgumentException {
  176.         this(orbit, attitude, new FieldArrayDictionary<>(orbit.getDate().getField(), additional));
  177.     }

  178.     /** Build a spacecraft state from orbit attitude and additional states.
  179.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  180.      * @param orbit the orbit
  181.      * @param attitude attitude
  182.      * @param additional additional states
  183.      * @exception IllegalArgumentException if orbit and attitude dates
  184.      * or frames are not equal
  185.      * @since 11.1
  186.      */
  187.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final FieldArrayDictionary<T> additional)
  188.         throws IllegalArgumentException {
  189.         this(orbit, attitude, orbit.getA().getField().getZero().add(DEFAULT_MASS), additional);
  190.     }

  191.     /** Create a new instance from orbit, mass and additional states.
  192.      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
  193.      * @param orbit the orbit
  194.      * @param mass the mass (kg)
  195.      * @param additional additional states
  196.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldOrbit, CalculusFieldElement, FieldArrayDictionary)}
  197.      */
  198.     @Deprecated
  199.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass, final Map<String, T[]> additional) {
  200.         this(orbit, mass, new FieldArrayDictionary<>(orbit.getDate().getField(), additional));
  201.     }

  202.     /** Create a new instance from orbit, mass and additional states.
  203.      * <p>FieldAttitude law is set to an unspecified default attitude.</p>
  204.      * @param orbit the orbit
  205.      * @param mass the mass (kg)
  206.      * @param additional additional states
  207.      * @since 11.1
  208.      */
  209.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final T mass, final FieldArrayDictionary<T> additional) {
  210.         this(orbit,
  211.              new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
  212.              mass, additional);
  213.     }

  214.     /** Build a spacecraft state from orbit, attitude, mass and additional states.
  215.      * @param orbit the orbit
  216.      * @param attitude attitude
  217.      * @param mass the mass (kg)
  218.      * @param additional additional states (may be null if no additional states are available)
  219.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldOrbit, FieldAttitude, CalculusFieldElement, FieldArrayDictionary)}
  220.      */
  221.     @Deprecated
  222.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude,
  223.                                 final T mass, final Map<String, T[]> additional) {
  224.         this(orbit, attitude, mass, new FieldArrayDictionary<>(orbit.getDate().getField(), additional));
  225.     }

  226.     /** Build a spacecraft state from orbit, attitude, mass and additional states.
  227.      * @param orbit the orbit
  228.      * @param attitude attitude
  229.      * @param mass the mass (kg)
  230.      * @param additional additional states (may be null if no additional states are available)
  231.      * @since 11.1
  232.      */
  233.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude,
  234.                                 final T mass, final FieldArrayDictionary<T> additional) {
  235.         this(orbit, attitude, mass, additional, null);
  236.     }

  237.     /** Build a spacecraft state from orbit, attitude, mass, additional states and derivatives.
  238.      * @param orbit the orbit
  239.      * @param attitude attitude
  240.      * @param mass the mass (kg)
  241.      * @param additional additional states (may be null if no additional states are available)
  242.      * @param additionalDot additional states derivatives(may be null if no additional states derivative sare available)
  243.      * @exception IllegalArgumentException if orbit and attitude dates
  244.      * or frames are not equal
  245.      * @since 11.1
  246.      */
  247.     public FieldSpacecraftState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final T mass,
  248.                                 final FieldArrayDictionary<T> additional,
  249.                                 final FieldArrayDictionary<T> additionalDot)
  250.         throws IllegalArgumentException {
  251.         checkConsistency(orbit, attitude);
  252.         this.orbit      = orbit;
  253.         this.attitude   = attitude;
  254.         this.mass       = mass;
  255.         this.absPva     = null;

  256.         if (additional == null) {
  257.             this.additional = new FieldArrayDictionary<>(orbit.getDate().getField());
  258.         } else {
  259.             this.additional = new FieldArrayDictionary<>(additional);
  260.         }

  261.         if (additionalDot == null) {
  262.             this.additionalDot = new FieldArrayDictionary<>(orbit.getDate().getField());
  263.         } else {

  264.             this.additionalDot = new FieldArrayDictionary<>(additionalDot);
  265.         }

  266.     }

  267.     /** Convert a {@link FieldSpacecraftState}.
  268.      * @param field field to which the elements belong
  269.      * @param state state to convert
  270.      */
  271.     public FieldSpacecraftState(final Field<T> field, final SpacecraftState state) {

  272.         if (state.isOrbitDefined()) {
  273.             final double[] stateD    = new double[6];
  274.             final double[] stateDotD = state.getOrbit().hasDerivatives() ? new double[6] : null;
  275.             state.getOrbit().getType().mapOrbitToArray(state.getOrbit(), PositionAngle.TRUE, stateD, stateDotD);
  276.             final T[] stateF    = MathArrays.buildArray(field, 6);
  277.             for (int i = 0; i < stateD.length; ++i) {
  278.                 stateF[i]    = field.getZero().add(stateD[i]);
  279.             }
  280.             final T[] stateDotF;
  281.             if (stateDotD == null) {
  282.                 stateDotF = null;
  283.             } else {
  284.                 stateDotF = MathArrays.buildArray(field, 6);
  285.                 for (int i = 0; i < stateDotD.length; ++i) {
  286.                     stateDotF[i] = field.getZero().add(stateDotD[i]);
  287.                 }
  288.             }

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

  290.             this.orbit    = state.getOrbit().getType().mapArrayToOrbit(stateF, stateDotF,
  291.                                                                        PositionAngle.TRUE,
  292.                                                                        dateF, field.getZero().add(state.getMu()), state.getFrame());
  293.             this.absPva   = null;

  294.         } else {
  295.             final T zero = field.getZero();
  296.             final T x = zero.add(state.getPVCoordinates().getPosition().getX());
  297.             final T y = zero.add(state.getPVCoordinates().getPosition().getY());
  298.             final T z = zero.add(state.getPVCoordinates().getPosition().getZ());
  299.             final T vx = zero.add(state.getPVCoordinates().getVelocity().getX());
  300.             final T vy = zero.add(state.getPVCoordinates().getVelocity().getY());
  301.             final T vz = zero.add(state.getPVCoordinates().getVelocity().getZ());
  302.             final T ax = zero.add(state.getPVCoordinates().getAcceleration().getX());
  303.             final T ay = zero.add(state.getPVCoordinates().getAcceleration().getY());
  304.             final T az = zero.add(state.getPVCoordinates().getAcceleration().getZ());
  305.             final FieldPVCoordinates<T> pva_f = new FieldPVCoordinates<>(new FieldVector3D<>(x, y, z),
  306.                                                                          new FieldVector3D<>(vx, vy, vz),
  307.                                                                          new FieldVector3D<>(ax, ay, az));
  308.             final FieldAbsoluteDate<T> dateF = new FieldAbsoluteDate<>(field, state.getDate());
  309.             this.orbit  = null;
  310.             this.absPva = new FieldAbsolutePVCoordinates<>(state.getFrame(), dateF, pva_f);
  311.         }

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

  314.         final DoubleArrayDictionary additionalD = state.getAdditionalStatesValues();
  315.         if (additionalD.size() == 0) {
  316.             this.additional = new FieldArrayDictionary<>(field);
  317.         } else {
  318.             this.additional = new FieldArrayDictionary<>(field, additionalD.size());
  319.             for (final DoubleArrayDictionary.Entry entry : additionalD.getData()) {
  320.                 this.additional.put(entry.getKey(), entry.getValue());
  321.             }
  322.         }
  323.         final DoubleArrayDictionary additionalDotD = state.getAdditionalStatesDerivatives();
  324.         if (additionalDotD.size() == 0) {
  325.             this.additionalDot = new FieldArrayDictionary<>(field);
  326.         } else {
  327.             this.additionalDot = new FieldArrayDictionary<>(field, additionalDotD.size());
  328.             for (final DoubleArrayDictionary.Entry entry : additionalDotD.getData()) {
  329.                 this.additionalDot.put(entry.getKey(), entry.getValue());
  330.             }
  331.         }

  332.     }

  333.     /** Build a spacecraft state from orbit only.
  334.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  335.      * @param absPva position-velocity-acceleration
  336.      */
  337.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva) {
  338.         this(absPva,
  339.              new LofOffset(absPva.getFrame(),
  340.                            LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  341.              absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  342.     }

  343.     /** Build a spacecraft state from orbit and attitude.
  344.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  345.      * @param absPva position-velocity-acceleration
  346.      * @param attitude attitude
  347.      * @exception IllegalArgumentException if orbit and attitude dates
  348.      * or frames are not equal
  349.      */
  350.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
  351.         throws IllegalArgumentException {
  352.         this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary<T>) null);
  353.     }

  354.     /** Create a new instance from orbit and mass.
  355.      * <p>Attitude law is set to an unspecified default attitude.</p>
  356.      * @param absPva position-velocity-acceleration
  357.      * @param mass the mass (kg)
  358.      */
  359.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass) {
  360.         this(absPva,
  361.              new LofOffset(absPva.getFrame(),
  362.                            LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  363.              mass, (FieldArrayDictionary<T>) null);
  364.     }

  365.     /** Build a spacecraft state from orbit, attitude and mass.
  366.      * @param absPva position-velocity-acceleration
  367.      * @param attitude attitude
  368.      * @param mass the mass (kg)
  369.      * @exception IllegalArgumentException if orbit and attitude dates
  370.      * or frames are not equal
  371.      */
  372.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass)
  373.         throws IllegalArgumentException {
  374.         this(absPva, attitude, mass, (FieldArrayDictionary<T>) null);
  375.     }

  376.     /** Build a spacecraft state from orbit only.
  377.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  378.      * @param absPva position-velocity-acceleration
  379.      * @param additional additional states
  380.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, FieldArrayDictionary)}
  381.      */
  382.     @Deprecated
  383.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final Map<String, T[]> additional) {
  384.         this(absPva, new FieldArrayDictionary<>(absPva.getDate().getField(), additional));
  385.     }

  386.     /** Build a spacecraft state from orbit only.
  387.      * <p>Attitude and mass are set to unspecified non-null arbitrary values.</p>
  388.      * @param absPva position-velocity-acceleration
  389.      * @param additional additional states
  390.      * @since 11.1
  391.      */
  392.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldArrayDictionary<T> additional) {
  393.         this(absPva,
  394.              new LofOffset(absPva.getFrame(),
  395.                            LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  396.              absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional);
  397.     }

  398.     /** Build a spacecraft state from orbit and attitude.
  399.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  400.      * @param absPva position-velocity-acceleration
  401.      * @param attitude attitude
  402.      * @param additional additional states
  403.      * @exception IllegalArgumentException if orbit and attitude dates
  404.      * or frames are not equal
  405.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, FieldAttitude, FieldArrayDictionary)}
  406.      */
  407.     @Deprecated
  408.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  409.                                 final Map<String, T[]> additional)
  410.         throws IllegalArgumentException {
  411.         this(absPva, attitude, new FieldArrayDictionary<>(absPva.getDate().getField(), additional));
  412.     }

  413.     /** Build a spacecraft state from orbit and attitude.
  414.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  415.      * @param absPva position-velocity-acceleration
  416.      * @param attitude attitude
  417.      * @param additional additional states
  418.      * @exception IllegalArgumentException if orbit and attitude dates
  419.      * or frames are not equal
  420.      * @since 11.1
  421.      */
  422.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  423.                                 final FieldArrayDictionary<T> additional)
  424.         throws IllegalArgumentException {
  425.         this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional);
  426.     }

  427.     /** Create a new instance from orbit and mass.
  428.      * <p>Attitude law is set to an unspecified default attitude.</p>
  429.      * @param absPva position-velocity-acceleration
  430.      * @param mass the mass (kg)
  431.      * @param additional additional states
  432.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, CalculusFieldElement, FieldArrayDictionary)}
  433.      */
  434.     @Deprecated
  435.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass, final Map<String, T[]> additional) {
  436.         this(absPva, mass, new FieldArrayDictionary<>(absPva.getDate().getField(), additional));
  437.     }

  438.     /** Create a new instance from orbit and mass.
  439.      * <p>Attitude law is set to an unspecified default attitude.</p>
  440.      * @param absPva position-velocity-acceleration
  441.      * @param mass the mass (kg)
  442.      * @param additional additional states
  443.      * @since 11.1
  444.      */
  445.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final T mass, final FieldArrayDictionary<T> additional) {
  446.         this(absPva,
  447.              new LofOffset(absPva.getFrame(),
  448.                            LOFType.LVLH_CCSDS).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
  449.              mass, additional);
  450.     }

  451.     /** Build a spacecraft state from orbit, attitude and mass.
  452.      * @param absPva position-velocity-acceleration
  453.      * @param attitude attitude
  454.      * @param mass the mass (kg)
  455.      * @param additional additional states (may be null if no additional states are available)
  456.      * @deprecated as of 11.1, replaced by {@link #FieldSpacecraftState(FieldAbsolutePVCoordinates, FieldAttitude, CalculusFieldElement, FieldArrayDictionary)}
  457.      */
  458.     @Deprecated
  459.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  460.                            final T mass, final Map<String, T[]> additional) {
  461.         this(absPva, attitude, mass, new FieldArrayDictionary<>(absPva.getDate().getField(), additional));
  462.     }

  463.     /** Build a spacecraft state from orbit, attitude and mass.
  464.      * @param absPva position-velocity-acceleration
  465.      * @param attitude attitude
  466.      * @param mass the mass (kg)
  467.      * @param additional additional states (may be null if no additional states are available)
  468.      * @since 11.1
  469.      */
  470.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude,
  471.                            final T mass, final FieldArrayDictionary<T> additional) {
  472.         this(absPva, attitude, mass, additional, null);
  473.     }

  474.     /** Build a spacecraft state from orbit, attitude and mass.
  475.      * @param absPva position-velocity-acceleration
  476.      * @param attitude attitude
  477.      * @param mass the mass (kg)
  478.      * @param additional additional states (may be null if no additional states are available)
  479.      * @param additionalDot additional states derivatives(may be null if no additional states derivatives are available)
  480.      * @exception IllegalArgumentException if orbit and attitude dates
  481.      * or frames are not equal
  482.      * @since 11.1
  483.      */
  484.     public FieldSpacecraftState(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude, final T mass,
  485.                                 final FieldArrayDictionary<T> additional, final FieldArrayDictionary<T> additionalDot)
  486.         throws IllegalArgumentException {
  487.         checkConsistency(absPva, attitude);
  488.         this.orbit      = null;
  489.         this.absPva     = absPva;
  490.         this.attitude   = attitude;
  491.         this.mass       = mass;
  492.         if (additional == null) {
  493.             this.additional = new FieldArrayDictionary<T>(absPva.getDate().getField());
  494.         } else {
  495.             this.additional = new FieldArrayDictionary<T>(additional);
  496.         }
  497.         if (additionalDot == null) {
  498.             this.additionalDot = new FieldArrayDictionary<T>(absPva.getDate().getField());
  499.         } else {
  500.             this.additionalDot = new FieldArrayDictionary<T>(additionalDot);
  501.         }
  502.     }

  503.     /** Add an additional state.
  504.      * <p>
  505.      * {@link FieldSpacecraftState SpacecraftState} instances are immutable,
  506.      * so this method does <em>not</em> change the instance, but rather
  507.      * creates a new instance, which has the same orbit, attitude, mass
  508.      * and additional states as the original instance, except it also
  509.      * has the specified state. If the original instance already had an
  510.      * additional state with the same name, it will be overridden. If it
  511.      * did not have any additional state with that name, the new instance
  512.      * will have one more additional state than the original instance.
  513.      * </p>
  514.      * @param name name of the additional state
  515.      * @param value value of the additional state
  516.      * @return a new instance, with the additional state added
  517.      * @see #hasAdditionalState(String)
  518.      * @see #getAdditionalState(String)
  519.      * @see #getAdditionalStates()
  520.      */
  521.     @SafeVarargs
  522.     public final FieldSpacecraftState<T> addAdditionalState(final String name, final T... value) {
  523.         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additional);
  524.         newDict.put(name, value.clone());
  525.         if (absPva == null) {
  526.             return new FieldSpacecraftState<>(orbit, attitude, mass, newDict, additionalDot);
  527.         } else {
  528.             return new FieldSpacecraftState<>(absPva, attitude, mass, newDict, additionalDot);
  529.         }
  530.     }

  531.     /** Add an additional state derivative.
  532.     * {@link FieldSpacecraftState FieldSpacecraftState} instances are immutable,
  533.      * so this method does <em>not</em> change the instance, but rather
  534.      * creates a new instance, which has the same components as the original
  535.      * instance, except it also has the specified state derivative. If the
  536.      * original instance already had an additional state derivative with the
  537.      * same name, it will be overridden. If it did not have any additional
  538.      * state derivative with that name, the new instance will have one more
  539.      * additional state derivative than the original instance.
  540.      * @param name name of the additional state derivative
  541.      * @param value value of the additional state derivative
  542.      * @return a new instance, with the additional state derivative added
  543.      * @see #hasAdditionalStateDerivative(String)
  544.      * @see #getAdditionalStateDerivative(String)
  545.      * @see #getAdditionalStatesDerivatives()
  546.      */
  547.     @SafeVarargs
  548.     public final FieldSpacecraftState<T> addAdditionalStateDerivative(final String name, final T... value) {
  549.         final FieldArrayDictionary<T> newDict = new FieldArrayDictionary<>(additionalDot);
  550.         newDict.put(name, value.clone());
  551.         if (absPva == null) {
  552.             return new FieldSpacecraftState<>(orbit, attitude, mass, additional, newDict);
  553.         } else {
  554.             return new FieldSpacecraftState<>(absPva, attitude, mass, additional, newDict);
  555.         }
  556.     }

  557.     /** Check orbit and attitude dates are equal.
  558.      * @param orbitN the orbit
  559.      * @param attitudeN attitude
  560.      * @exception IllegalArgumentException if orbit and attitude dates
  561.      * are not equal
  562.      */
  563.     private void checkConsistency(final FieldOrbit<T> orbitN, final FieldAttitude<T> attitudeN)
  564.         throws IllegalArgumentException {
  565.         if (orbitN.getDate().durationFrom(attitudeN.getDate()).abs().getReal() >
  566.             DATE_INCONSISTENCY_THRESHOLD) {

  567.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
  568.                                                      orbitN.getDate(), attitudeN.getDate());
  569.         }

  570.         if (orbitN.getFrame() != attitudeN.getReferenceFrame()) {
  571.             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
  572.                                                      orbitN.getFrame().getName(),
  573.                                                      attitudeN.getReferenceFrame().getName());
  574.         }
  575.     }

  576.     /** Check if the state contains an orbit part.
  577.      * <p>
  578.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  579.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}.
  580.      * </p>
  581.      * @return true if state contains an orbit (in which case {@link #getOrbit()}
  582.      * will not throw an exception), or false if the state contains an
  583.      * absolut position-velocity-acceleration (in which case {@link #getAbsPVA()}
  584.      * will not throw an exception)
  585.      */
  586.     public boolean isOrbitDefined() {
  587.         return orbit != null;
  588.     }

  589.     /**
  590.      * Check FieldAbsolutePVCoordinates and attitude dates are equal.
  591.      * @param absPva   position-velocity-acceleration
  592.      * @param attitude attitude
  593.      * @param <T>      the type of the field elements
  594.      * @exception IllegalArgumentException if orbit and attitude dates are not equal
  595.      */
  596.     private static <T extends CalculusFieldElement<T>> void checkConsistency(final FieldAbsolutePVCoordinates<T> absPva, final FieldAttitude<T> attitude)
  597.         throws IllegalArgumentException {
  598.         if (FastMath.abs(absPva.getDate().durationFrom(attitude.getDate())).getReal() >
  599.             DATE_INCONSISTENCY_THRESHOLD) {
  600.             throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
  601.                                                      absPva.getDate(), attitude.getDate());
  602.         }
  603.         if (absPva.getFrame() != attitude.getReferenceFrame()) {
  604.             throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
  605.                                                      absPva.getFrame().getName(),
  606.                                                      attitude.getReferenceFrame().getName());
  607.         }
  608.     }

  609.     /** Get a time-shifted state.
  610.      * <p>
  611.      * The state can be slightly shifted to close dates. This shift is based on
  612.      * a simple Keplerian model for orbit, a linear extrapolation for attitude
  613.      * taking the spin rate into account and neither mass nor additional states
  614.      * changes. It is <em>not</em> intended as a replacement for proper orbit
  615.      * and attitude propagation but should be sufficient for small time shifts
  616.      * or coarse accuracy.
  617.      * </p>
  618.      * <p>
  619.      * As a rough order of magnitude, the following table shows the extrapolation
  620.      * errors obtained between this simple shift method and an {@link
  621.      * org.orekit.propagation.numerical.FieldNumericalPropagator numerical
  622.      * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field,
  623.      * Sun and Moon third bodies attractions, drag and solar radiation pressure.
  624.      * Beware that these results will be different for other orbits.
  625.      * </p>
  626.      * <table border="1">
  627.      * <caption>Extrapolation Error</caption>
  628.      * <tr style="background-color: #ccccff;"><th>interpolation time (s)</th>
  629.      * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr>
  630.      * <tr><td style="background-color: #eeeeff; padding:5px"> 60</td><td>  18</td><td> 1.1</td></tr>
  631.      * <tr><td style="background-color: #eeeeff; padding:5px">120</td><td>  72</td><td> 9.1</td></tr>
  632.      * <tr><td style="background-color: #eeeeff; padding:5px">300</td><td> 447</td><td> 140</td></tr>
  633.      * <tr><td style="background-color: #eeeeff; padding:5px">600</td><td>1601</td><td>1067</td></tr>
  634.      * <tr><td style="background-color: #eeeeff; padding:5px">900</td><td>3141</td><td>3307</td></tr>
  635.      * </table>
  636.      * @param dt time shift in seconds
  637.      * @return a new state, shifted with respect to the instance (which is immutable)
  638.      * except for the mass which stay unchanged
  639.      */
  640.     public FieldSpacecraftState<T> shiftedBy(final double dt) {
  641.         if (absPva == null) {
  642.             return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
  643.                                               mass, shiftAdditional(dt), additionalDot);
  644.         } else {
  645.             return new FieldSpacecraftState<>(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
  646.                                               mass, shiftAdditional(dt), additionalDot);
  647.         }
  648.     }

  649.     /** Get a time-shifted state.
  650.      * <p>
  651.      * The state can be slightly shifted to close dates. This shift is based on
  652.      * a simple Keplerian model for orbit, a linear extrapolation for attitude
  653.      * taking the spin rate into account and neither mass nor additional states
  654.      * changes. It is <em>not</em> intended as a replacement for proper orbit
  655.      * and attitude propagation but should be sufficient for small time shifts
  656.      * or coarse accuracy.
  657.      * </p>
  658.      * <p>
  659.      * As a rough order of magnitude, the following table shows the extrapolation
  660.      * errors obtained between this simple shift method and an {@link
  661.      * org.orekit.propagation.numerical.FieldNumericalPropagator numerical
  662.      * propagator} for a low Earth Sun Synchronous Orbit, with a 20x20 gravity field,
  663.      * Sun and Moon third bodies attractions, drag and solar radiation pressure.
  664.      * Beware that these results will be different for other orbits.
  665.      * </p>
  666.      * <table border="1">
  667.      * <caption>Extrapolation Error</caption>
  668.      * <tr style="background-color: #ccccff;"><th>interpolation time (s)</th>
  669.      * <th>position error without derivatives (m)</th><th>position error with derivatives (m)</th></tr>
  670.      * <tr><td style="background-color: #eeeeff; padding:5px"> 60</td><td>  18</td><td> 1.1</td></tr>
  671.      * <tr><td style="background-color: #eeeeff; padding:5px">120</td><td>  72</td><td> 9.1</td></tr>
  672.      * <tr><td style="background-color: #eeeeff; padding:5px">300</td><td> 447</td><td> 140</td></tr>
  673.      * <tr><td style="background-color: #eeeeff; padding:5px">600</td><td>1601</td><td>1067</td></tr>
  674.      * <tr><td style="background-color: #eeeeff; padding:5px">900</td><td>3141</td><td>3307</td></tr>
  675.      * </table>
  676.      * @param dt time shift in seconds
  677.      * @return a new state, shifted with respect to the instance (which is immutable)
  678.      * except for the mass which stay unchanged
  679.      */
  680.     public FieldSpacecraftState<T> shiftedBy(final T dt) {
  681.         if (absPva == null) {
  682.             return new FieldSpacecraftState<>(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
  683.                                               mass, shiftAdditional(dt), additionalDot);
  684.         } else {
  685.             return new FieldSpacecraftState<>(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
  686.                                               mass, shiftAdditional(dt), additionalDot);
  687.         }
  688.     }

  689.     /** Shift additional states.
  690.      * @param dt time shift in seconds
  691.      * @return shifted additional states
  692.      * @since 11.1.1
  693.      */
  694.     private FieldArrayDictionary<T> shiftAdditional(final double dt) {

  695.         // fast handling when there are no derivatives at all
  696.         if (additionalDot.size() == 0) {
  697.             return additional;
  698.         }

  699.         // there are derivatives, we need to take them into account in the additional state
  700.         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<T>(additional);
  701.         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
  702.             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
  703.             if (entry != null) {
  704.                 entry.scaledIncrement(dt, dotEntry);
  705.             }
  706.         }

  707.         return shifted;

  708.     }

  709.     /** Shift additional states.
  710.      * @param dt time shift in seconds
  711.      * @return shifted additional states
  712.      * @since 11.1.1
  713.      */
  714.     private FieldArrayDictionary<T> shiftAdditional(final T dt) {

  715.         // fast handling when there are no derivatives at all
  716.         if (additionalDot.size() == 0) {
  717.             return additional;
  718.         }

  719.         // there are derivatives, we need to take them into account in the additional state
  720.         final FieldArrayDictionary<T> shifted = new FieldArrayDictionary<T>(additional);
  721.         for (final FieldArrayDictionary<T>.Entry dotEntry : additionalDot.getData()) {
  722.             final FieldArrayDictionary<T>.Entry entry = shifted.getEntry(dotEntry.getKey());
  723.             if (entry != null) {
  724.                 entry.scaledIncrement(dt, dotEntry);
  725.             }
  726.         }

  727.         return shifted;

  728.     }

  729.     /** {@inheritDoc}
  730.      * <p>
  731.      * The additional states that are interpolated are the ones already present
  732.      * in the instance. The sample instances must therefore have at least the same
  733.      * additional states has the instance. They may have more additional states,
  734.      * but the extra ones will be ignored.
  735.      * </p>
  736.      * <p>
  737.      * The instance and all the sample instances <em>must</em> be based on similar
  738.      * trajectory data, i.e. they must either all be based on orbits or all be based
  739.      * on absolute position-velocity-acceleration. Any inconsistency will trigger
  740.      * an {@link OrekitIllegalStateException}.
  741.      * </p>
  742.      * <p>
  743.      * As this implementation of interpolation is polynomial, it should be used only
  744.      * with small samples (about 10-20 points) in order to avoid <a
  745.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  746.      * and numerical problems (including NaN appearing).
  747.      * </p>
  748.      * @exception OrekitIllegalStateException if some instances are not based on
  749.      * similar trajectory data
  750.      */
  751.     public FieldSpacecraftState<T> interpolate(final FieldAbsoluteDate<T> date,
  752.                                                final Stream<FieldSpacecraftState<T>> sample) {

  753.         // prepare interpolators
  754.         final List<FieldOrbit<T>> orbits;
  755.         final List<FieldAbsolutePVCoordinates<T>> absPvas;
  756.         if (isOrbitDefined()) {
  757.             orbits  = new ArrayList<FieldOrbit<T>>();
  758.             absPvas = null;
  759.         } else {
  760.             orbits  = null;
  761.             absPvas = new ArrayList<FieldAbsolutePVCoordinates<T>>();
  762.         }
  763.         final List<FieldAttitude<T>> attitudes = new ArrayList<>();
  764.         final FieldHermiteInterpolator<T> massInterpolator = new FieldHermiteInterpolator<>();
  765.         final List<FieldArrayDictionary<T>.Entry> addionalEntries = additional.getData();
  766.         final Map<String, FieldHermiteInterpolator<T>> additionalInterpolators =
  767.                 new HashMap<String, FieldHermiteInterpolator<T>>(additional.size());
  768.         for (final FieldArrayDictionary<T>.Entry entry : addionalEntries) {
  769.             additionalInterpolators.put(entry.getKey(), new FieldHermiteInterpolator<>());
  770.         }
  771.         final List<FieldArrayDictionary<T>.Entry> addionalDotEntries = additionalDot.getData();
  772.         final Map<String, FieldHermiteInterpolator<T>> additionalDotInterpolators =
  773.                 new HashMap<String, FieldHermiteInterpolator<T>>(additionalDot.size());
  774.         for (final FieldArrayDictionary<T>.Entry entry : addionalDotEntries) {
  775.             additionalDotInterpolators.put(entry.getKey(), new FieldHermiteInterpolator<>());
  776.         }

  777.         // extract sample data
  778.         sample.forEach(state -> {
  779.             final T deltaT = state.getDate().durationFrom(date);
  780.             if (isOrbitDefined()) {
  781.                 orbits.add(state.getOrbit());
  782.             } else {
  783.                 absPvas.add(state.getAbsPVA());
  784.             }
  785.             attitudes.add(state.getAttitude());
  786.             final T[] mm = MathArrays.buildArray(date.getField(), 1);
  787.             mm[0] = state.getMass();
  788.             massInterpolator.addSamplePoint(deltaT,
  789.                                             mm);
  790.             for (final Map.Entry<String, FieldHermiteInterpolator<T>> entry : additionalInterpolators.entrySet()) {
  791.                 entry.getValue().addSamplePoint(deltaT, state.getAdditionalState(entry.getKey()));
  792.             }
  793.             for (final Map.Entry<String, FieldHermiteInterpolator<T>> entry : additionalDotInterpolators.entrySet()) {
  794.                 entry.getValue().addSamplePoint(deltaT, state.getAdditionalStateDerivative(entry.getKey()));
  795.             }
  796.         });

  797.         // perform interpolations
  798.         final FieldOrbit<T> interpolatedOrbit;
  799.         final FieldAbsolutePVCoordinates<T> interpolatedAbsPva;
  800.         if (isOrbitDefined()) {
  801.             interpolatedOrbit  = orbit.interpolate(date, orbits);
  802.             interpolatedAbsPva = null;
  803.         } else {
  804.             interpolatedOrbit  = null;
  805.             interpolatedAbsPva = absPva.interpolate(date, absPvas);
  806.         }
  807.         final FieldAttitude<T> interpolatedAttitude = attitude.interpolate(date, attitudes);
  808.         final T interpolatedMass       = massInterpolator.value(date.getField().getZero())[0];
  809.         final FieldArrayDictionary<T> interpolatedAdditional;
  810.         if (additionalInterpolators.isEmpty()) {
  811.             interpolatedAdditional = null;
  812.         } else {
  813.             interpolatedAdditional = new FieldArrayDictionary<>(date.getField(), additionalInterpolators.size());
  814.             for (final Map.Entry<String, FieldHermiteInterpolator<T>> entry : additionalInterpolators.entrySet()) {
  815.                 interpolatedAdditional.put(entry.getKey(), entry.getValue().value(date.getField().getZero()));
  816.             }
  817.         }
  818.         final FieldArrayDictionary<T> interpolatedAdditionalDot;
  819.         if (additionalDotInterpolators.isEmpty()) {
  820.             interpolatedAdditionalDot = null;
  821.         } else {
  822.             interpolatedAdditionalDot = new FieldArrayDictionary<>(date.getField(), additionalDotInterpolators.size());
  823.             for (final Map.Entry<String, FieldHermiteInterpolator<T>> entry : additionalDotInterpolators.entrySet()) {
  824.                 interpolatedAdditionalDot.put(entry.getKey(), entry.getValue().value(date.getField().getZero()));
  825.             }
  826.         }

  827.         // create the complete interpolated state
  828.         if (isOrbitDefined()) {
  829.             return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass,
  830.                                               interpolatedAdditional, interpolatedAdditionalDot);
  831.         } else {
  832.             return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass,
  833.                                               interpolatedAdditional, interpolatedAdditionalDot);
  834.         }

  835.     }

  836.     /** Get the absolute position-velocity-acceleration.
  837.      * <p>
  838.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  839.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
  840.      * one is present can be checked using {@link #isOrbitDefined()}.
  841.      * </p>
  842.      * @return absolute position-velocity-acceleration
  843.      * @exception OrekitIllegalStateException if position-velocity-acceleration is null,
  844.      * which mean the state rather contains an {@link FieldOrbit}
  845.      * @see #isOrbitDefined()
  846.      * @see #getOrbit()
  847.      */
  848.     public FieldAbsolutePVCoordinates<T> getAbsPVA() throws OrekitIllegalStateException {
  849.         if (absPva == null) {
  850.             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ABSOLUTE_PVCOORDINATES);
  851.         }
  852.         return absPva;
  853.     }

  854.     /** Get the current orbit.
  855.      * <p>
  856.      * A state contains either an {@link FieldAbsolutePVCoordinates absolute
  857.      * position-velocity-acceleration} or an {@link FieldOrbit orbit}. Which
  858.      * one is present can be checked using {@link #isOrbitDefined()}.
  859.      * </p>
  860.      * @return the orbit
  861.      * @exception OrekitIllegalStateException if orbit is null,
  862.      * which means the state rather contains an {@link FieldAbsolutePVCoordinates absolute
  863.      * position-velocity-acceleration}
  864.      * @see #isOrbitDefined()
  865.      * @see #getAbsPVA()
  866.      */
  867.     public FieldOrbit<T> getOrbit() throws OrekitIllegalStateException {
  868.         if (orbit == null) {
  869.             throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ORBIT);
  870.         }
  871.         return orbit;
  872.     }

  873.     /** Get the date.
  874.      * @return date
  875.      */
  876.     public FieldAbsoluteDate<T> getDate() {
  877.         return (absPva == null) ? orbit.getDate() : absPva.getDate();
  878.     }

  879.     /** Get the defining frame.
  880.      * @return the frame in which state is defined
  881.      */
  882.     public Frame getFrame() {
  883.         return (absPva == null) ? orbit.getFrame() : absPva.getFrame();
  884.     }


  885.     /** Check if an additional state is available.
  886.      * @param name name of the additional state
  887.      * @return true if the additional state is available
  888.      * @see #addAdditionalState(String, CalculusFieldElement...)
  889.      * @see #getAdditionalState(String)
  890.      * @see #getAdditionalStates()
  891.      */
  892.     public boolean hasAdditionalState(final String name) {
  893.         return additional.getEntry(name) != null;
  894.     }

  895.     /** Check if an additional state derivative is available.
  896.      * @param name name of the additional state derivative
  897.      * @return true if the additional state derivative is available
  898.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  899.      * @see #getAdditionalStateDerivative(String)
  900.      * @see #getAdditionalStatesDerivatives()
  901.      */
  902.     public boolean hasAdditionalStateDerivative(final String name) {
  903.         return additionalDot.getEntry(name) != null;
  904.     }

  905.     /** Check if two instances have the same set of additional states available.
  906.      * <p>
  907.      * Only the names and dimensions of the additional states are compared,
  908.      * not their values.
  909.      * </p>
  910.      * @param state state to compare to instance
  911.      * @exception MathIllegalArgumentException if an additional state does not have
  912.      * the same dimension in both states
  913.      */
  914.     public void ensureCompatibleAdditionalStates(final FieldSpacecraftState<T> state)
  915.         throws MathIllegalArgumentException {

  916.         // check instance additional states is a subset of the other one
  917.         for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
  918.             final T[] other = state.additional.get(entry.getKey());
  919.             if (other == null) {
  920.                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  921.                                           entry.getKey());
  922.             }
  923.             if (other.length != entry.getValue().length) {
  924.                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  925.                                                     other.length, entry.getValue().length);
  926.             }
  927.         }

  928.         // check instance additional states derivatives is a subset of the other one
  929.         for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
  930.             final T[] other = state.additionalDot.get(entry.getKey());
  931.             if (other == null) {
  932.                 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  933.                                           entry.getKey());
  934.             }
  935.             if (other.length != entry.getValue().length) {
  936.                 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  937.                                                     other.length, entry.getValue().length);
  938.             }
  939.         }

  940.         if (state.additional.size() > additional.size()) {
  941.             // the other state has more additional states
  942.             for (final FieldArrayDictionary<T>.Entry entry : state.additional.getData()) {
  943.                 if (additional.getEntry(entry.getKey()) == null) {
  944.                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  945.                                               entry.getKey());
  946.                 }
  947.             }
  948.         }

  949.         if (state.additionalDot.size() > additionalDot.size()) {
  950.             // the other state has more additional states
  951.             for (final FieldArrayDictionary<T>.Entry entry : state.additionalDot.getData()) {
  952.                 if (additionalDot.getEntry(entry.getKey()) == null) {
  953.                     throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
  954.                                               entry.getKey());
  955.                 }
  956.             }
  957.         }

  958.     }

  959.     /** Get an additional state.
  960.      * @param name name of the additional state
  961.      * @return value of the additional state
  962.           * @see #addAdditionalState(String, CalculusFieldElement...)
  963.      * @see #hasAdditionalState(String)
  964.      * @see #getAdditionalStates()
  965.      */
  966.     public T[] getAdditionalState(final String name) {
  967.         final FieldArrayDictionary<T>.Entry entry = additional.getEntry(name);
  968.         if (entry == null) {
  969.             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
  970.         }
  971.         return entry.getValue();
  972.     }

  973.     /** Get an additional state derivative.
  974.      * @param name name of the additional state derivative
  975.      * @return value of the additional state derivative
  976.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  977.      * @see #hasAdditionalStateDerivative(String)
  978.      * @see #getAdditionalStatesDerivatives()
  979.      * @since 11.1
  980.      */
  981.     public T[] getAdditionalStateDerivative(final String name) {
  982.         final FieldArrayDictionary<T>.Entry entry = additionalDot.getEntry(name);
  983.         if (entry == null) {
  984.             throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
  985.         }
  986.         return entry.getValue();
  987.     }

  988.     /** Get an unmodifiable map of additional states.
  989.      * @return unmodifiable map of additional states
  990.      * @see #addAdditionalState(String, CalculusFieldElement...)
  991.      * @see #hasAdditionalState(String)
  992.      * @see #getAdditionalState(String)
  993.      * @deprecated as of 11.1, replaced by {@link #getAdditionalStatesValues()}
  994.      */
  995.     @Deprecated
  996.     public Map<String, T[]> getAdditionalStates() {
  997.         return getAdditionalStatesValues().toMap();
  998.     }

  999.     /** Get an unmodifiable map of additional states.
  1000.      * @return unmodifiable map of additional states
  1001.      * @see #addAdditionalState(String, CalculusFieldElement...)
  1002.      * @see #hasAdditionalState(String)
  1003.      * @see #getAdditionalState(String)
  1004.      * @since 11.1
  1005.      */
  1006.     public FieldArrayDictionary<T> getAdditionalStatesValues() {
  1007.         return additional.unmodifiableView();
  1008.     }

  1009.     /** Get an unmodifiable map of additional states derivatives.
  1010.      * @return unmodifiable map of additional states derivatives
  1011.      * @see #addAdditionalStateDerivative(String, CalculusFieldElement...)
  1012.      * @see #hasAdditionalStateDerivative(String)
  1013.      * @see #getAdditionalStateDerivative(String)
  1014.     * @since 11.1
  1015.       */
  1016.     public FieldArrayDictionary<T> getAdditionalStatesDerivatives() {
  1017.         return additionalDot.unmodifiableView();
  1018.     }

  1019.     /** Compute the transform from state defining frame to spacecraft frame.
  1020.      * <p>The spacecraft frame origin is at the point defined by the orbit,
  1021.      * and its orientation is defined by the attitude.</p>
  1022.      * @return transform from specified frame to current spacecraft frame
  1023.      */
  1024.     public FieldTransform<T> toTransform() {
  1025.         final TimeStampedFieldPVCoordinates<T> pv = getPVCoordinates();
  1026.         return new FieldTransform<>(pv.getDate(),
  1027.                                     new FieldTransform<>(pv.getDate(), pv.negate()),
  1028.                                     new FieldTransform<>(pv.getDate(), attitude.getOrientation()));
  1029.     }

  1030.     /** Get the central attraction coefficient.
  1031.      * @return mu central attraction coefficient (m^3/s^2), or {code Double.NaN} if the
  1032.      * state is contains an absolute position-velocity-acceleration rather than an orbit
  1033.      */
  1034.     public T getMu() {
  1035.         return (absPva == null) ? orbit.getMu() : absPva.getDate().getField().getZero().add(Double.NaN);
  1036.     }

  1037.     /** Get the Keplerian period.
  1038.      * <p>The Keplerian period is computed directly from semi major axis
  1039.      * and central acceleration constant.</p>
  1040.      * @return keplerian period in seconds, or {code Double.NaN} if the
  1041.      * state is contains an absolute position-velocity-acceleration rather
  1042.      * than an orbit
  1043.      */
  1044.     public T getKeplerianPeriod() {
  1045.         return (absPva == null) ? orbit.getKeplerianPeriod() : absPva.getDate().getField().getZero().add(Double.NaN);
  1046.     }

  1047.     /** Get the Keplerian mean motion.
  1048.      * <p>The Keplerian mean motion is computed directly from semi major axis
  1049.      * and central acceleration constant.</p>
  1050.      * @return keplerian mean motion in radians per second, or {code Double.NaN} if the
  1051.      * state is contains an absolute position-velocity-acceleration rather
  1052.      * than an orbit
  1053.      */
  1054.     public T getKeplerianMeanMotion() {
  1055.         return (absPva == null) ? orbit.getKeplerianMeanMotion() : absPva.getDate().getField().getZero().add(Double.NaN);
  1056.     }

  1057.     /** Get the semi-major axis.
  1058.      * @return semi-major axis (m), or {code Double.NaN} if the
  1059.      * state is contains an absolute position-velocity-acceleration rather
  1060.      * than an orbit
  1061.      */
  1062.     public T getA() {
  1063.         return (absPva == null) ? orbit.getA() : absPva.getDate().getField().getZero().add(Double.NaN);
  1064.     }

  1065.     /** Get the first component of the eccentricity vector (as per equinoctial parameters).
  1066.      * @return e cos(ω + Ω), first component of eccentricity vector, or {code Double.NaN} if the
  1067.      * state is contains an absolute position-velocity-acceleration rather
  1068.      * than an orbit
  1069.      * @see #getE()
  1070.      */
  1071.     public T getEquinoctialEx() {
  1072.         return (absPva == null) ? orbit.getEquinoctialEx() : absPva.getDate().getField().getZero().add(Double.NaN);
  1073.     }

  1074.     /** Get the second component of the eccentricity vector (as per equinoctial parameters).
  1075.      * @return e sin(ω + Ω), second component of the eccentricity vector, or {code Double.NaN} if the
  1076.      * state is contains an absolute position-velocity-acceleration rather
  1077.      * than an orbit
  1078.      * @see #getE()
  1079.      */
  1080.     public T getEquinoctialEy() {
  1081.         return (absPva == null) ? orbit.getEquinoctialEy() : absPva.getDate().getField().getZero().add(Double.NaN);
  1082.     }

  1083.     /** Get the first component of the inclination vector (as per equinoctial parameters).
  1084.      * @return tan(i/2) cos(Ω), first component of the inclination vector, or {code Double.NaN} if the
  1085.      * state is contains an absolute position-velocity-acceleration rather
  1086.      * than an orbit
  1087.      * @see #getI()
  1088.      */
  1089.     public T getHx() {
  1090.         return (absPva == null) ? orbit.getHx() : absPva.getDate().getField().getZero().add(Double.NaN);
  1091.     }

  1092.     /** Get the second component of the inclination vector (as per equinoctial parameters).
  1093.      * @return tan(i/2) sin(Ω), second component of the inclination vector, or {code Double.NaN} if the
  1094.      * state is contains an absolute position-velocity-acceleration rather
  1095.      * than an orbit
  1096.      * @see #getI()
  1097.      */
  1098.     public T getHy() {
  1099.         return (absPva == null) ? orbit.getHy() : absPva.getDate().getField().getZero().add(Double.NaN);
  1100.     }

  1101.     /** Get the true latitude argument (as per equinoctial parameters).
  1102.      * @return v + ω + Ω true longitude argument (rad), or {code Double.NaN} if the
  1103.      * state is contains an absolute position-velocity-acceleration rather
  1104.      * than an orbit
  1105.      * @see #getLE()
  1106.      * @see #getLM()
  1107.      */
  1108.     public T getLv() {
  1109.         return (absPva == null) ? orbit.getLv() : absPva.getDate().getField().getZero().add(Double.NaN);
  1110.     }

  1111.     /** Get the eccentric latitude argument (as per equinoctial parameters).
  1112.      * @return E + ω + Ω eccentric longitude argument (rad), or {code Double.NaN} if the
  1113.      * state is contains an absolute position-velocity-acceleration rather
  1114.      * than an orbit
  1115.      * @see #getLv()
  1116.      * @see #getLM()
  1117.      */
  1118.     public T getLE() {
  1119.         return (absPva == null) ? orbit.getLE() : absPva.getDate().getField().getZero().add(Double.NaN);
  1120.     }

  1121.     /** Get the mean longitude argument (as per equinoctial parameters).
  1122.      * @return M + ω + Ω mean latitude argument (rad), or {code Double.NaN} if the
  1123.      * state is contains an absolute position-velocity-acceleration rather
  1124.      * than an orbit
  1125.      * @see #getLv()
  1126.      * @see #getLE()
  1127.      */
  1128.     public T getLM() {
  1129.         return (absPva == null) ? orbit.getLM() : absPva.getDate().getField().getZero().add(Double.NaN);
  1130.     }

  1131.     // Additional orbital elements

  1132.     /** Get the eccentricity.
  1133.      * @return eccentricity, or {code Double.NaN} if the
  1134.      * state is contains an absolute position-velocity-acceleration rather
  1135.      * than an orbit
  1136.      * @see #getEquinoctialEx()
  1137.      * @see #getEquinoctialEy()
  1138.      */
  1139.     public T getE() {
  1140.         return (absPva == null) ? orbit.getE() : absPva.getDate().getField().getZero().add(Double.NaN);
  1141.     }

  1142.     /** Get the inclination.
  1143.      * @return inclination (rad)
  1144.      * @see #getHx()
  1145.      * @see #getHy()
  1146.      */
  1147.     public T getI() {
  1148.         return (absPva == null) ? orbit.getI() : absPva.getDate().getField().getZero().add(Double.NaN);
  1149.     }

  1150.     /** Get the {@link TimeStampedFieldPVCoordinates} in orbit definition frame.
  1151.      * <p>
  1152.      * Compute the position and velocity of the satellite. This method caches its
  1153.      * results, and recompute them only when the method is called with a new value
  1154.      * for mu. The result is provided as a reference to the internally cached
  1155.      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
  1156.      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
  1157.      * </p>
  1158.      * @return pvCoordinates in orbit definition frame
  1159.      */
  1160.     public TimeStampedFieldPVCoordinates<T> getPVCoordinates() {
  1161.         return (absPva == null) ? orbit.getPVCoordinates() : absPva.getPVCoordinates();
  1162.     }

  1163.     /** Get the {@link TimeStampedFieldPVCoordinates} in given output frame.
  1164.      * <p>
  1165.      * Compute the position and velocity of the satellite. This method caches its
  1166.      * results, and recompute them only when the method is called with a new value
  1167.      * for mu. The result is provided as a reference to the internally cached
  1168.      * {@link TimeStampedFieldPVCoordinates}, so the caller is responsible to copy it in a separate
  1169.      * {@link TimeStampedFieldPVCoordinates} if it needs to keep the value for a while.
  1170.      * </p>
  1171.      * @param outputFrame frame in which coordinates should be defined
  1172.      * @return pvCoordinates in orbit definition frame
  1173.      */
  1174.     public TimeStampedFieldPVCoordinates<T>getPVCoordinates(final Frame outputFrame) {
  1175.         return (absPva == null) ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame);
  1176.     }

  1177.     /** Get the attitude.
  1178.      * @return the attitude.
  1179.      */
  1180.     public FieldAttitude<T> getAttitude() {
  1181.         return attitude;
  1182.     }

  1183.     /** Gets the current mass.
  1184.      * @return the mass (kg)
  1185.      */
  1186.     public T getMass() {
  1187.         return mass;
  1188.     }

  1189.     /**To convert a FieldSpacecraftState instance into a SpacecraftState instance.
  1190.      *
  1191.      * @return SpacecraftState instance with the same properties
  1192.      */
  1193.     public SpacecraftState toSpacecraftState() {
  1194.         final DoubleArrayDictionary dictionary;
  1195.         if (additional.size() == 0) {
  1196.             dictionary = new DoubleArrayDictionary();
  1197.         } else {
  1198.             dictionary = new DoubleArrayDictionary(additional.size());
  1199.             for (final FieldArrayDictionary<T>.Entry entry : additional.getData()) {
  1200.                 final double[] array = new double[entry.getValue().length];
  1201.                 for (int k = 0; k < array.length; ++k) {
  1202.                     array[k] = entry.getValue()[k].getReal();
  1203.                 }
  1204.                 dictionary.put(entry.getKey(), array);
  1205.             }
  1206.         }
  1207.         final DoubleArrayDictionary dictionaryDot;
  1208.         if (additionalDot.size() == 0) {
  1209.             dictionaryDot = new DoubleArrayDictionary();
  1210.         } else {
  1211.             dictionaryDot = new DoubleArrayDictionary(additionalDot.size());
  1212.             for (final FieldArrayDictionary<T>.Entry entry : additionalDot.getData()) {
  1213.                 final double[] array = new double[entry.getValue().length];
  1214.                 for (int k = 0; k < array.length; ++k) {
  1215.                     array[k] = entry.getValue()[k].getReal();
  1216.                 }
  1217.                 dictionaryDot.put(entry.getKey(), array);
  1218.             }
  1219.         }
  1220.         if (isOrbitDefined()) {
  1221.             return new SpacecraftState(orbit.toOrbit(), attitude.toAttitude(),
  1222.                                        mass.getReal(), dictionary, dictionaryDot);
  1223.         } else {
  1224.             return new SpacecraftState(absPva.toAbsolutePVCoordinates(),
  1225.                                        attitude.toAttitude(), mass.getReal(),
  1226.                                        dictionary, dictionaryDot);
  1227.         }
  1228.     }

  1229.     @Override
  1230.     public String toString() {
  1231.         return "FieldSpacecraftState{" +
  1232.                 "orbit=" + orbit +
  1233.                 ", attitude=" + attitude +
  1234.                 ", mass=" + mass +
  1235.                 ", additional=" + additional +
  1236.                 ", additionalDot=" + additionalDot +
  1237.                 '}';
  1238.     }

  1239. }