FieldSpacecraftStateInterpolator.java

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

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.util.MathArrays;
  21. import org.hipparchus.util.Pair;
  22. import org.orekit.attitudes.AttitudeProvider;
  23. import org.orekit.attitudes.FieldAttitude;
  24. import org.orekit.attitudes.FieldAttitudeInterpolator;
  25. import org.orekit.attitudes.FrameAlignedProvider;
  26. import org.orekit.errors.OrekitIllegalArgumentException;
  27. import org.orekit.errors.OrekitInternalError;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.frames.Frame;
  30. import org.orekit.orbits.FieldOrbit;
  31. import org.orekit.orbits.FieldOrbitHermiteInterpolator;
  32. import org.orekit.time.AbstractFieldTimeInterpolator;
  33. import org.orekit.time.AbstractTimeInterpolator;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.time.FieldTimeInterpolator;
  36. import org.orekit.time.FieldTimeStamped;
  37. import org.orekit.time.TimeStampedField;
  38. import org.orekit.time.TimeStampedFieldHermiteInterpolator;
  39. import org.orekit.utils.AngularDerivativesFilter;
  40. import org.orekit.utils.CartesianDerivativesFilter;
  41. import org.orekit.utils.FieldAbsolutePVCoordinates;
  42. import org.orekit.utils.FieldAbsolutePVCoordinatesHermiteInterpolator;
  43. import org.orekit.utils.FieldArrayDictionary;
  44. import org.orekit.utils.FieldPVCoordinatesProvider;
  45. import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator;

  46. import java.util.ArrayList;
  47. import java.util.Collection;
  48. import java.util.HashMap;
  49. import java.util.List;
  50. import java.util.Map;
  51. import java.util.Optional;
  52. import java.util.stream.Collectors;

  53. /**
  54.  * Generic class for spacecraft state interpolator.
  55.  * <p>
  56.  * The user can specify what interpolator to use for each attribute of the spacecraft state.  However, at least one
  57.  * interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other interpolators can be
  58.  * left to null if the user do not want to interpolate these values.
  59.  *
  60.  * @param <KK> type of the field element
  61.  *
  62.  * @author Luc Maisonobe
  63.  * @author Vincent Cucchietti
  64.  * @see SpacecraftState
  65.  */
  66. public class FieldSpacecraftStateInterpolator<KK extends CalculusFieldElement<KK>>
  67.         extends AbstractFieldTimeInterpolator<FieldSpacecraftState<KK>, KK> {

  68.     /**
  69.      * Output frame.
  70.      * <p><b>Must be inertial</b> if interpolating spacecraft states defined by orbit</p>
  71.      */
  72.     private final Frame outputFrame;

  73.     /** Orbit interpolator. */
  74.     private final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolator;

  75.     /** Absolute position-velocity-acceleration interpolator. */
  76.     private final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolator;

  77.     /** Mass interpolator. */
  78.     private final FieldTimeInterpolator<TimeStampedField<KK>, KK> massInterpolator;

  79.     /** Attitude interpolator. */
  80.     private final FieldTimeInterpolator<FieldAttitude<KK>, KK> attitudeInterpolator;

  81.     /** Additional state interpolator. */
  82.     private final FieldTimeInterpolator<TimeStampedField<KK>, KK> additionalStateInterpolator;

  83.     /**
  84.      * Simplest constructor to create a default Hermite interpolator for every spacecraft state field.
  85.      * <p>
  86.      * The interpolators will have the following configuration :
  87.      * <ul>
  88.      *     <li>Same frame for coordinates and attitude </li>
  89.      *     <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
  90.      *     <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
  91.      *     <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
  92.      *     <li>Use of angular and first time derivative for attitude interpolation</li>
  93.      * </ul>
  94.      * <p>
  95.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  96.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  97.      * phenomenon</a> and numerical problems (including NaN appearing).
  98.      * <p>
  99.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  100.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  101.      *
  102.      * @param outputFrame output frame
  103.      */
  104.     public FieldSpacecraftStateInterpolator(final Frame outputFrame) {
  105.         this(DEFAULT_INTERPOLATION_POINTS, outputFrame);
  106.     }

  107.     /**
  108.      * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
  109.      * <p>
  110.      * The interpolators will have the following configuration :
  111.      * <ul>
  112.      *     <li>Same frame for coordinates and attitude </li>
  113.      *     <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
  114.      *     <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
  115.      *     <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
  116.      *     <li>Use of angular and first time derivative for attitude interpolation</li>
  117.      * </ul>
  118.      * <p>
  119.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  120.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  121.      * phenomenon</a> and numerical problems (including NaN appearing).
  122.      * <p>
  123.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  124.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  125.      *
  126.      * @param interpolationPoints number of interpolation points
  127.      * @param outputFrame output frame
  128.      */
  129.     public FieldSpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame) {
  130.         this(interpolationPoints, outputFrame, outputFrame);
  131.     }

  132.     /**
  133.      * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
  134.      * <p>
  135.      * The interpolators will have the following configuration :
  136.      * <ul>
  137.      *     <li>Same frame for coordinates and attitude </li>
  138.      *     <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
  139.      *     <li>Use of angular and first time derivative for attitude interpolation</li>
  140.      * </ul>
  141.      * <p>
  142.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  143.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  144.      * phenomenon</a> and numerical problems (including NaN appearing).
  145.      * <p>
  146.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  147.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  148.      *
  149.      * @param interpolationPoints number of interpolation points
  150.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  151.      * @param outputFrame output frame
  152.      * @since 12.1
  153.      */
  154.     public FieldSpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, final Frame outputFrame) {
  155.         this(interpolationPoints, extrapolationThreshold, outputFrame, outputFrame);
  156.     }

  157.     /**
  158.      * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
  159.      * <p>
  160.      * The interpolators will have the following configuration :
  161.      * <ul>
  162.      *     <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
  163.      *     <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
  164.      *     <li>Use of angular and first time derivative for attitude interpolation</li>
  165.      * </ul>
  166.      * <p>
  167.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  168.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  169.      * phenomenon</a> and numerical problems (including NaN appearing).
  170.      * <p>
  171.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  172.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  173.      *
  174.      * @param interpolationPoints number of interpolation points
  175.      * @param outputFrame output frame
  176.      * @param attitudeReferenceFrame reference frame from which attitude is defined
  177.      */
  178.     public FieldSpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame,
  179.                                             final Frame attitudeReferenceFrame) {
  180.         this(interpolationPoints, AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC,
  181.              outputFrame, attitudeReferenceFrame);
  182.     }

  183.     /**
  184.      * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
  185.      * <p>
  186.      * The interpolators will have the following configuration :
  187.      * <ul>
  188.      *     <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
  189.      *     <li>Use of angular and first time derivative for attitude interpolation</li>
  190.      * </ul>
  191.      * <p>
  192.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  193.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  194.      * phenomenon</a> and numerical problems (including NaN appearing).
  195.      * <p>
  196.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  197.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  198.      *
  199.      * @param interpolationPoints number of interpolation points
  200.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  201.      * @param outputFrame output frame
  202.      * @param attitudeReferenceFrame reference frame from which attitude is defined
  203.      */
  204.     public FieldSpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold,
  205.                                             final Frame outputFrame, final Frame attitudeReferenceFrame) {
  206.         this(interpolationPoints, extrapolationThreshold, outputFrame, attitudeReferenceFrame,
  207.              CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR);
  208.     }

  209.     /**
  210.      * Constructor.
  211.      * <p>
  212.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  213.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  214.      * phenomenon</a> and numerical problems (including NaN appearing).
  215.      * <p>
  216.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  217.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  218.      *
  219.      * @param interpolationPoints number of interpolation points
  220.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  221.      * @param outputFrame output frame
  222.      * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation
  223.      * @param attitudeReferenceFrame reference frame from which attitude is defined
  224.      * @param angularFilter filter for derivatives from the sample to use in attitude interpolation
  225.      */
  226.     public FieldSpacecraftStateInterpolator(final int interpolationPoints,
  227.                                             final double extrapolationThreshold,
  228.                                             final Frame outputFrame,
  229.                                             final Frame attitudeReferenceFrame,
  230.                                             final CartesianDerivativesFilter pvaFilter,
  231.                                             final AngularDerivativesFilter angularFilter) {

  232.         this(interpolationPoints, extrapolationThreshold, outputFrame,
  233.              new FieldOrbitHermiteInterpolator<>(interpolationPoints, extrapolationThreshold, outputFrame, pvaFilter),
  234.              new FieldAbsolutePVCoordinatesHermiteInterpolator<>(interpolationPoints, extrapolationThreshold, outputFrame,
  235.                                                                  pvaFilter),
  236.              new TimeStampedFieldHermiteInterpolator<>(interpolationPoints, extrapolationThreshold),
  237.              new FieldAttitudeInterpolator<>(attitudeReferenceFrame,
  238.                                              new TimeStampedFieldAngularCoordinatesHermiteInterpolator<KK>(
  239.                                                      interpolationPoints, extrapolationThreshold, angularFilter)),
  240.              new TimeStampedFieldHermiteInterpolator<>(interpolationPoints, extrapolationThreshold));
  241.     }

  242.     /**
  243.      * Constructor.
  244.      * <p>
  245.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  246.      * error otherwise.
  247.      *
  248.      * @param outputFrame output frame
  249.      * @param orbitInterpolator orbit interpolator
  250.      * @param absPVAInterpolator absolute position-velocity-acceleration
  251.      * @param massInterpolator mass interpolator
  252.      * @param attitudeInterpolator attitude interpolator
  253.      * @param additionalStateInterpolator additional state interpolator
  254.      *
  255.      * @deprecated using this constructor may throw an exception if any given interpolator
  256.      * does not use {@link #DEFAULT_INTERPOLATION_POINTS} and {@link
  257.      * #DEFAULT_EXTRAPOLATION_THRESHOLD_SEC}. Use {@link
  258.      * #FieldSpacecraftStateInterpolator(int, double, Frame, FieldTimeInterpolator,
  259.      * FieldTimeInterpolator, FieldTimeInterpolator, FieldTimeInterpolator,
  260.      * FieldTimeInterpolator)} instead.
  261.      */
  262.     @Deprecated
  263.     public FieldSpacecraftStateInterpolator(final Frame outputFrame,
  264.                                             final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolator,
  265.                                             final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolator,
  266.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> massInterpolator,
  267.                                             final FieldTimeInterpolator<FieldAttitude<KK>, KK> attitudeInterpolator,
  268.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> additionalStateInterpolator) {
  269.         super(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC);
  270.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  271.         this.outputFrame                 = outputFrame;
  272.         this.orbitInterpolator           = orbitInterpolator;
  273.         this.absPVAInterpolator          = absPVAInterpolator;
  274.         this.massInterpolator            = massInterpolator;
  275.         this.attitudeInterpolator        = attitudeInterpolator;
  276.         this.additionalStateInterpolator = additionalStateInterpolator;
  277.     }

  278.     /**
  279.      * Constructor.
  280.      * <p>
  281.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  282.      * error otherwise.
  283.      *
  284.      * @param interpolationPoints number of interpolation points
  285.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  286.      * @param outputFrame output frame
  287.      * @param orbitInterpolator orbit interpolator
  288.      * @param absPVAInterpolator absolute position-velocity-acceleration
  289.      * @param massInterpolator mass interpolator
  290.      * @param attitudeInterpolator attitude interpolator
  291.      * @param additionalStateInterpolator additional state interpolator
  292.      */
  293.     public FieldSpacecraftStateInterpolator(final int interpolationPoints,
  294.                                             final double extrapolationThreshold,
  295.                                             final Frame outputFrame,
  296.                                             final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolator,
  297.                                             final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolator,
  298.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> massInterpolator,
  299.                                             final FieldTimeInterpolator<FieldAttitude<KK>, KK> attitudeInterpolator,
  300.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> additionalStateInterpolator) {
  301.         super(interpolationPoints, extrapolationThreshold);
  302.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  303.         this.outputFrame                 = outputFrame;
  304.         this.orbitInterpolator           = orbitInterpolator;
  305.         this.absPVAInterpolator          = absPVAInterpolator;
  306.         this.massInterpolator            = massInterpolator;
  307.         this.attitudeInterpolator        = attitudeInterpolator;
  308.         this.additionalStateInterpolator = additionalStateInterpolator;
  309.     }

  310.     /**
  311.      * {@inheritDoc}
  312.      * <p>
  313.      * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample
  314.      * instances must therefore have at least the same additional states as this neighbor instance. They may have more
  315.      * additional states, but the extra ones will be ignored.
  316.      * <p>
  317.      * All the sample instances <em>must</em> be based on similar trajectory data, i.e. they must either all be based on
  318.      * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an
  319.      * {@link OrekitIllegalArgumentException}.
  320.      *
  321.      * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute
  322.      * position-velocity-acceleration coordinates
  323.      * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
  324.      * definition type
  325.      */
  326.     @Override
  327.     public FieldSpacecraftState<KK> interpolate(final FieldAbsoluteDate<KK> interpolationDate,
  328.                                                 final Collection<FieldSpacecraftState<KK>> sample) {

  329.         final List<FieldSpacecraftState<KK>> sampleList = new ArrayList<>(sample);

  330.         // Convert to spacecraft state for consistency check
  331.         final List<SpacecraftState> nonFieldSampleList =
  332.                 sampleList.stream().map(FieldSpacecraftState::toSpacecraftState).collect(Collectors.toList());

  333.         // If sample is empty, an error will be thrown in super method
  334.         if (!sampleList.isEmpty()) {

  335.             // Check given that given states definition are consistent
  336.             // (all defined by either orbits or absolute position-velocity-acceleration coordinates)
  337.             SpacecraftStateInterpolator.checkStatesDefinitionsConsistency(nonFieldSampleList);

  338.             // Check interpolator and sample consistency
  339.             SpacecraftStateInterpolator.checkSampleAndInterpolatorConsistency(nonFieldSampleList,
  340.                                                                               orbitInterpolator != null,
  341.                                                                               absPVAInterpolator != null);
  342.         }

  343.         return super.interpolate(interpolationDate, sample);
  344.     }

  345.     /** {@inheritDoc} */
  346.     @Override
  347.     public List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> getSubInterpolators() {

  348.         // Add all sub interpolators that are defined
  349.         final List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> subInterpolators = new ArrayList<>();

  350.         addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
  351.         addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
  352.         addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
  353.         addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
  354.         addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);

  355.         return subInterpolators;
  356.     }

  357.     /**
  358.      * {@inheritDoc}
  359.      */
  360.     @Override
  361.     protected FieldSpacecraftState<KK> interpolate(final InterpolationData interpolationData) {

  362.         // Get interpolation date
  363.         final FieldAbsoluteDate<KK> interpolationDate = interpolationData.getInterpolationDate();

  364.         // Get first state definition
  365.         final FieldSpacecraftState<KK> earliestState   = interpolationData.getNeighborList().get(0);
  366.         final boolean                  areOrbitDefined = earliestState.isOrbitDefined();

  367.         // Prepare samples
  368.         final List<FieldAttitude<KK>> attitudes = new ArrayList<>();

  369.         final List<TimeStampedField<KK>> masses = new ArrayList<>();

  370.         final List<FieldArrayDictionary<KK>.Entry> additionalEntries =
  371.                 earliestState.getAdditionalStatesValues().getData();
  372.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSample =
  373.                 createAdditionalStateSample(additionalEntries);

  374.         final List<FieldArrayDictionary<KK>.Entry> additionalDotEntries =
  375.                 earliestState.getAdditionalStatesDerivatives().getData();
  376.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalDotSample =
  377.                 createAdditionalStateSample(additionalDotEntries);

  378.         // Fill interpolators with samples
  379.         final List<FieldSpacecraftState<KK>>       samples      = interpolationData.getNeighborList();
  380.         final List<FieldOrbit<KK>>                 orbitSample  = new ArrayList<>();
  381.         final List<FieldAbsolutePVCoordinates<KK>> absPVASample = new ArrayList<>();
  382.         for (FieldSpacecraftState<KK> state : samples) {
  383.             final FieldAbsoluteDate<KK> currentDate = state.getDate();

  384.             // Add orbit sample if state is defined with an orbit
  385.             if (state.isOrbitDefined()) {
  386.                 orbitSample.add(state.getOrbit());
  387.             }
  388.             // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
  389.             else {
  390.                 absPVASample.add(state.getAbsPVA());
  391.             }

  392.             // Add mass sample
  393.             if (massInterpolator != null) {
  394.                 masses.add(new TimeStampedField<>(state.getMass(), state.getDate()));
  395.             }

  396.             // Add attitude sample if it is interpolated
  397.             if (attitudeInterpolator != null) {
  398.                 attitudes.add(state.getAttitude());
  399.             }

  400.             if (additionalStateInterpolator != null) {

  401.                 // Add all additional state values if they are interpolated
  402.                 for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> entry : additionalSample.entrySet()) {
  403.                     entry.getValue().add(new Pair<>(currentDate, state.getAdditionalState(entry.getKey())));
  404.                 }

  405.                 // Add all additional state derivative values if they are interpolated
  406.                 for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> entry : additionalDotSample.entrySet()) {
  407.                     entry.getValue().add(new Pair<>(currentDate, state.getAdditionalStateDerivative(entry.getKey())));
  408.                 }
  409.             }

  410.         }

  411.         // Interpolate mass
  412.         final KK one = interpolationData.getOne();
  413.         final KK interpolatedMass;
  414.         if (massInterpolator != null) {
  415.             interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
  416.         }
  417.         else {
  418.             interpolatedMass = one.newInstance(SpacecraftState.DEFAULT_MASS);
  419.         }

  420.         // Interpolate additional states and derivatives
  421.         final FieldArrayDictionary<KK> interpolatedAdditional;
  422.         final FieldArrayDictionary<KK> interpolatedAdditionalDot;
  423.         if (additionalStateInterpolator != null) {
  424.             interpolatedAdditional    = interpolateAdditionalState(interpolationDate, additionalSample);
  425.             interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample);
  426.         }
  427.         else {
  428.             interpolatedAdditional    = null;
  429.             interpolatedAdditionalDot = null;
  430.         }

  431.         // Interpolate orbit
  432.         if (areOrbitDefined && orbitInterpolator != null) {
  433.             final FieldOrbit<KK> interpolatedOrbit =
  434.                     orbitInterpolator.interpolate(interpolationDate, orbitSample);

  435.             final FieldAttitude<KK> interpolatedAttitude =
  436.                     interpolateAttitude(interpolationDate, attitudes, interpolatedOrbit);

  437.             return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass,
  438.                                               interpolatedAdditional, interpolatedAdditionalDot);
  439.         }
  440.         // Interpolate absolute position-velocity-acceleration
  441.         else if (!areOrbitDefined && absPVAInterpolator != null) {

  442.             final FieldAbsolutePVCoordinates<KK> interpolatedAbsPva =
  443.                     absPVAInterpolator.interpolate(interpolationDate, absPVASample);

  444.             final FieldAttitude<KK> interpolatedAttitude =
  445.                     interpolateAttitude(interpolationDate, attitudes, interpolatedAbsPva);

  446.             return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass,
  447.                                               interpolatedAdditional, interpolatedAdditionalDot);
  448.         }
  449.         // Should never happen
  450.         else {
  451.             throw new OrekitInternalError(null);
  452.         }

  453.     }

  454.     /** Get output frame.
  455.      * @return output frame
  456.      */
  457.     public Frame getOutputFrame() {
  458.         return outputFrame;
  459.     }

  460.     /** Get orbit interpolator.
  461.      * @return optional orbit interpolator
  462.      *
  463.      * @see Optional
  464.      */
  465.     public Optional<FieldTimeInterpolator<FieldOrbit<KK>, KK>> getOrbitInterpolator() {
  466.         return Optional.ofNullable(orbitInterpolator);
  467.     }

  468.     /** Get absolute position-velocity-acceleration interpolator.
  469.      * @return optional absolute position-velocity-acceleration interpolator
  470.      *
  471.      * @see Optional
  472.      */
  473.     public Optional<FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK>> getAbsPVAInterpolator() {
  474.         return Optional.ofNullable(absPVAInterpolator);
  475.     }

  476.     /** Get mass interpolator.
  477.      * @return optional mass interpolator
  478.      *
  479.      * @see Optional
  480.      */
  481.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getMassInterpolator() {
  482.         return Optional.ofNullable(massInterpolator);
  483.     }

  484.     /** Get attitude interpolator.
  485.      * @return optional attitude interpolator
  486.      *
  487.      * @see Optional
  488.      */
  489.     public Optional<FieldTimeInterpolator<FieldAttitude<KK>, KK>> getAttitudeInterpolator() {
  490.         return Optional.ofNullable(attitudeInterpolator);
  491.     }

  492.     /** Get additional state interpolator.
  493.      * @return optional additional state interpolator
  494.      *
  495.      * @see Optional
  496.      */
  497.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getAdditionalStateInterpolator() {
  498.         return Optional.ofNullable(additionalStateInterpolator);
  499.     }

  500.     /**
  501.      * Check that at least one interpolator is defined.
  502.      *
  503.      * @param orbitInterpolatorToCheck orbit interpolator
  504.      * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
  505.      */
  506.     private void checkAtLeastOneInterpolator(final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolatorToCheck,
  507.                                              final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolatorToCheck) {
  508.         if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
  509.             throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
  510.         }
  511.     }

  512.     /**
  513.      * Create empty samples for given additional entries.
  514.      *
  515.      * @param additionalEntries tabulated additional entries
  516.      *
  517.      * @return empty samples for given additional entries
  518.      */
  519.     private Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> createAdditionalStateSample(
  520.             final List<FieldArrayDictionary<KK>.Entry> additionalEntries) {
  521.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples =
  522.                 new HashMap<>(additionalEntries.size());

  523.         for (final FieldArrayDictionary<KK>.Entry entry : additionalEntries) {
  524.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  525.         }

  526.         return additionalSamples;
  527.     }

  528.     /**
  529.      * Interpolate additional state values.
  530.      *
  531.      * @param interpolationDate interpolation date
  532.      * @param additionalSamples additional state samples
  533.      *
  534.      * @return interpolated additional state values
  535.      */
  536.     private FieldArrayDictionary<KK> interpolateAdditionalState(final FieldAbsoluteDate<KK> interpolationDate,
  537.                                                                 final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples) {
  538.         final Field<KK> field = interpolationDate.getField();
  539.         final FieldArrayDictionary<KK> interpolatedAdditional;

  540.         if (additionalSamples.isEmpty()) {
  541.             interpolatedAdditional = null;
  542.         }
  543.         else {
  544.             interpolatedAdditional = new FieldArrayDictionary<>(field, additionalSamples.size());
  545.             for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> entry : additionalSamples.entrySet()) {

  546.                 // Get current entry
  547.                 final List<Pair<FieldAbsoluteDate<KK>, KK[]>> currentAdditionalSamples = entry.getValue();

  548.                 // Extract number of values for this specific entry
  549.                 final int nbOfValues = currentAdditionalSamples.get(0).getValue().length;

  550.                 // For each value of current additional state entry
  551.                 final KK[] currentInterpolatedAdditional = MathArrays.buildArray(field, nbOfValues);
  552.                 for (int i = 0; i < nbOfValues; i++) {

  553.                     // Create final index for lambda expression use
  554.                     final int currentIndex = i;

  555.                     // Create sample for specific value of current additional state values
  556.                     final List<TimeStampedField<KK>> currentValueSample = new ArrayList<>();

  557.                     currentAdditionalSamples.forEach(currentSamples -> currentValueSample.add(
  558.                             new TimeStampedField<>(currentSamples.getValue()[currentIndex], currentSamples.getFirst())));

  559.                     // Interpolate
  560.                     currentInterpolatedAdditional[i] =
  561.                             additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
  562.                 }

  563.                 interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional);
  564.             }
  565.         }
  566.         return interpolatedAdditional;
  567.     }

  568.     /**
  569.      * Interpolate attitude.
  570.      * <p>
  571.      * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame.
  572.      *
  573.      * @param interpolationDate interpolation date
  574.      * @param attitudes attitudes sample
  575.      * @param pvProvider position-velocity-acceleration coordinates provider
  576.      *
  577.      * @return interpolated attitude if attitude interpolator is present, default attitude otherwise
  578.      */
  579.     private FieldAttitude<KK> interpolateAttitude(final FieldAbsoluteDate<KK> interpolationDate,
  580.                                                   final List<FieldAttitude<KK>> attitudes,
  581.                                                   final FieldPVCoordinatesProvider<KK> pvProvider) {
  582.         if (attitudes.isEmpty()) {
  583.             final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame);
  584.             return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame);
  585.         }
  586.         else {
  587.             return attitudeInterpolator.interpolate(interpolationDate, attitudes);
  588.         }
  589.     }
  590. }