FieldSpacecraftStateInterpolator.java

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

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.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>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</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 outputFrame output frame
  151.      * @param attitudeReferenceFrame reference frame from which attitude is defined
  152.      */
  153.     public FieldSpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame,
  154.                                             final Frame attitudeReferenceFrame) {
  155.         this(interpolationPoints, AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC,
  156.              outputFrame, attitudeReferenceFrame);
  157.     }

  158.     /**
  159.      * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
  160.      * <p>
  161.      * The interpolators will have the following configuration :
  162.      * <ul>
  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 extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  176.      * @param outputFrame output frame
  177.      * @param attitudeReferenceFrame reference frame from which attitude is defined
  178.      */
  179.     public FieldSpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold,
  180.                                             final Frame outputFrame, final Frame attitudeReferenceFrame) {
  181.         this(interpolationPoints, extrapolationThreshold, outputFrame, attitudeReferenceFrame,
  182.              CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR);
  183.     }

  184.     /**
  185.      * Constructor.
  186.      * <p>
  187.      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
  188.      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
  189.      * phenomenon</a> and numerical problems (including NaN appearing).
  190.      * <p>
  191.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
  192.      * tabulated spacecraft states defined by orbit, will throw an error otherwise.
  193.      *
  194.      * @param interpolationPoints number of interpolation points
  195.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  196.      * @param outputFrame output frame
  197.      * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation
  198.      * @param attitudeReferenceFrame reference frame from which attitude is defined
  199.      * @param angularFilter filter for derivatives from the sample to use in attitude interpolation
  200.      */
  201.     public FieldSpacecraftStateInterpolator(final int interpolationPoints,
  202.                                             final double extrapolationThreshold,
  203.                                             final Frame outputFrame,
  204.                                             final Frame attitudeReferenceFrame,
  205.                                             final CartesianDerivativesFilter pvaFilter,
  206.                                             final AngularDerivativesFilter angularFilter) {

  207.         this(outputFrame,
  208.              new FieldOrbitHermiteInterpolator<>(interpolationPoints, extrapolationThreshold, outputFrame, pvaFilter),
  209.              new FieldAbsolutePVCoordinatesHermiteInterpolator<>(interpolationPoints, extrapolationThreshold, outputFrame,
  210.                                                                  pvaFilter),
  211.              new TimeStampedFieldHermiteInterpolator<>(interpolationPoints, extrapolationThreshold),
  212.              new FieldAttitudeInterpolator<>(attitudeReferenceFrame,
  213.                                              new TimeStampedFieldAngularCoordinatesHermiteInterpolator<KK>(
  214.                                                      interpolationPoints, extrapolationThreshold, angularFilter)),
  215.              new TimeStampedFieldHermiteInterpolator<>(interpolationPoints, extrapolationThreshold));
  216.     }

  217.     /**
  218.      * Constructor.
  219.      * <p>
  220.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  221.      * error otherwise.
  222.      *
  223.      * @param outputFrame output frame
  224.      * @param orbitInterpolator orbit interpolator
  225.      * @param absPVAInterpolator absolute position-velocity-acceleration
  226.      * @param massInterpolator mass interpolator
  227.      * @param attitudeInterpolator attitude interpolator
  228.      * @param additionalStateInterpolator additional state interpolator
  229.      */
  230.     public FieldSpacecraftStateInterpolator(final Frame outputFrame,
  231.                                             final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolator,
  232.                                             final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolator,
  233.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> massInterpolator,
  234.                                             final FieldTimeInterpolator<FieldAttitude<KK>, KK> attitudeInterpolator,
  235.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> additionalStateInterpolator) {
  236.         super(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC);
  237.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  238.         this.outputFrame                 = outputFrame;
  239.         this.orbitInterpolator           = orbitInterpolator;
  240.         this.absPVAInterpolator          = absPVAInterpolator;
  241.         this.massInterpolator            = massInterpolator;
  242.         this.attitudeInterpolator        = attitudeInterpolator;
  243.         this.additionalStateInterpolator = additionalStateInterpolator;
  244.     }

  245.     /**
  246.      * {@inheritDoc}
  247.      * <p>
  248.      * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample
  249.      * instances must therefore have at least the same additional states as this neighbor instance. They may have more
  250.      * additional states, but the extra ones will be ignored.
  251.      * <p>
  252.      * All the sample instances <em>must</em> be based on similar trajectory data, i.e. they must either all be based on
  253.      * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an
  254.      * {@link OrekitIllegalArgumentException}.
  255.      *
  256.      * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute
  257.      * position-velocity-acceleration coordinates
  258.      * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
  259.      * definition type
  260.      */
  261.     @Override
  262.     public FieldSpacecraftState<KK> interpolate(final FieldAbsoluteDate<KK> interpolationDate,
  263.                                                 final Collection<FieldSpacecraftState<KK>> sample) {

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

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

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

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

  273.             // Check interpolator and sample consistency
  274.             SpacecraftStateInterpolator.checkSampleAndInterpolatorConsistency(nonFieldSampleList,
  275.                                                                               orbitInterpolator != null,
  276.                                                                               absPVAInterpolator != null);
  277.         }

  278.         return super.interpolate(interpolationDate, sample);
  279.     }

  280.     /** {@inheritDoc} */
  281.     @Override
  282.     public List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> getSubInterpolators() {

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

  285.         addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
  286.         addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
  287.         addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
  288.         addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
  289.         addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);

  290.         return subInterpolators;
  291.     }

  292.     /**
  293.      * {@inheritDoc}
  294.      */
  295.     @Override
  296.     protected FieldSpacecraftState<KK> interpolate(final InterpolationData interpolationData) {

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

  299.         // Get first state definition
  300.         final FieldSpacecraftState<KK> earliestState   = interpolationData.getNeighborList().get(0);
  301.         final boolean                  areOrbitDefined = earliestState.isOrbitDefined();

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

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

  305.         final List<FieldArrayDictionary<KK>.Entry> additionalEntries =
  306.                 earliestState.getAdditionalStatesValues().getData();
  307.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSample =
  308.                 createAdditionalStateSample(additionalEntries);

  309.         final List<FieldArrayDictionary<KK>.Entry> additionalDotEntries =
  310.                 earliestState.getAdditionalStatesDerivatives().getData();
  311.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalDotSample =
  312.                 createAdditionalStateSample(additionalDotEntries);

  313.         // Fill interpolators with samples
  314.         final List<FieldSpacecraftState<KK>>       samples      = interpolationData.getCachedSamples().getAll();
  315.         final List<FieldOrbit<KK>>                 orbitSample  = new ArrayList<>();
  316.         final List<FieldAbsolutePVCoordinates<KK>> absPVASample = new ArrayList<>();
  317.         for (FieldSpacecraftState<KK> state : samples) {
  318.             final FieldAbsoluteDate<KK> currentDate = state.getDate();

  319.             // Add orbit sample if state is defined with an orbit
  320.             if (state.isOrbitDefined()) {
  321.                 orbitSample.add(state.getOrbit());
  322.             }
  323.             // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
  324.             else {
  325.                 absPVASample.add(state.getAbsPVA());
  326.             }

  327.             // Add mass sample
  328.             if (massInterpolator != null) {
  329.                 masses.add(new TimeStampedField<>(state.getMass(), state.getDate()));
  330.             }

  331.             // Add attitude sample if it is interpolated
  332.             if (attitudeInterpolator != null) {
  333.                 attitudes.add(state.getAttitude());
  334.             }

  335.             if (additionalStateInterpolator != null) {

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

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

  345.         }

  346.         // Interpolate mass
  347.         final KK one = interpolationData.getOne();
  348.         final KK interpolatedMass;
  349.         if (massInterpolator != null) {
  350.             interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
  351.         }
  352.         else {
  353.             interpolatedMass = one.multiply(SpacecraftState.DEFAULT_MASS);
  354.         }

  355.         // Interpolate additional states and derivatives
  356.         final FieldArrayDictionary<KK> interpolatedAdditional;
  357.         final FieldArrayDictionary<KK> interpolatedAdditionalDot;
  358.         if (additionalStateInterpolator != null) {
  359.             interpolatedAdditional    = interpolateAdditionalState(interpolationDate, additionalSample);
  360.             interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample);
  361.         }
  362.         else {
  363.             interpolatedAdditional    = null;
  364.             interpolatedAdditionalDot = null;
  365.         }

  366.         // Interpolate orbit
  367.         if (areOrbitDefined && orbitInterpolator != null) {
  368.             final FieldOrbit<KK> interpolatedOrbit =
  369.                     orbitInterpolator.interpolate(interpolationDate, orbitSample);

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

  372.             return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass,
  373.                                               interpolatedAdditional, interpolatedAdditionalDot);
  374.         }
  375.         // Interpolate absolute position-velocity-acceleration
  376.         else if (!areOrbitDefined && absPVAInterpolator != null) {

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

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

  381.             return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass,
  382.                                               interpolatedAdditional, interpolatedAdditionalDot);
  383.         }
  384.         // Should never happen
  385.         else {
  386.             throw new OrekitInternalError(null);
  387.         }

  388.     }

  389.     /** Get output frame.
  390.      * @return output frame
  391.      */
  392.     public Frame getOutputFrame() {
  393.         return outputFrame;
  394.     }

  395.     /** Get orbit interpolator.
  396.      * @return optional orbit interpolator
  397.      *
  398.      * @see Optional
  399.      */
  400.     public Optional<FieldTimeInterpolator<FieldOrbit<KK>, KK>> getOrbitInterpolator() {
  401.         return Optional.ofNullable(orbitInterpolator);
  402.     }

  403.     /** Get absolute position-velocity-acceleration interpolator.
  404.      * @return optional absolute position-velocity-acceleration interpolator
  405.      *
  406.      * @see Optional
  407.      */
  408.     public Optional<FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK>> getAbsPVAInterpolator() {
  409.         return Optional.ofNullable(absPVAInterpolator);
  410.     }

  411.     /** Get mass interpolator.
  412.      * @return optional mass interpolator
  413.      *
  414.      * @see Optional
  415.      */
  416.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getMassInterpolator() {
  417.         return Optional.ofNullable(massInterpolator);
  418.     }

  419.     /** Get attitude interpolator.
  420.      * @return optional attitude interpolator
  421.      *
  422.      * @see Optional
  423.      */
  424.     public Optional<FieldTimeInterpolator<FieldAttitude<KK>, KK>> getAttitudeInterpolator() {
  425.         return Optional.ofNullable(attitudeInterpolator);
  426.     }

  427.     /** Get additional state interpolator.
  428.      * @return optional additional state interpolator
  429.      *
  430.      * @see Optional
  431.      */
  432.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getAdditionalStateInterpolator() {
  433.         return Optional.ofNullable(additionalStateInterpolator);
  434.     }

  435.     /**
  436.      * Check that at least one interpolator is defined.
  437.      *
  438.      * @param orbitInterpolatorToCheck orbit interpolator
  439.      * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
  440.      */
  441.     private void checkAtLeastOneInterpolator(final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolatorToCheck,
  442.                                              final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolatorToCheck) {
  443.         if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
  444.             throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
  445.         }
  446.     }

  447.     /**
  448.      * Create empty samples for given additional entries.
  449.      *
  450.      * @param additionalEntries tabulated additional entries
  451.      *
  452.      * @return empty samples for given additional entries
  453.      */
  454.     private Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> createAdditionalStateSample(
  455.             final List<FieldArrayDictionary<KK>.Entry> additionalEntries) {
  456.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples =
  457.                 new HashMap<>(additionalEntries.size());

  458.         for (final FieldArrayDictionary<KK>.Entry entry : additionalEntries) {
  459.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  460.         }

  461.         return additionalSamples;
  462.     }

  463.     /**
  464.      * Interpolate additional state values.
  465.      *
  466.      * @param interpolationDate interpolation date
  467.      * @param additionalSamples additional state samples
  468.      *
  469.      * @return interpolated additional state values
  470.      */
  471.     private FieldArrayDictionary<KK> interpolateAdditionalState(final FieldAbsoluteDate<KK> interpolationDate,
  472.                                                                 final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples) {
  473.         final Field<KK> field = interpolationDate.getField();
  474.         final FieldArrayDictionary<KK> interpolatedAdditional;

  475.         if (additionalSamples.isEmpty()) {
  476.             interpolatedAdditional = null;
  477.         }
  478.         else {
  479.             interpolatedAdditional = new FieldArrayDictionary<>(field, additionalSamples.size());
  480.             for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> entry : additionalSamples.entrySet()) {

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

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

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

  488.                     // Create final index for lambda expression use
  489.                     final int currentIndex = i;

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

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

  494.                     // Interpolate
  495.                     currentInterpolatedAdditional[i] =
  496.                             additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
  497.                 }

  498.                 interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional);
  499.             }
  500.         }
  501.         return interpolatedAdditional;
  502.     }

  503.     /**
  504.      * Interpolate attitude.
  505.      * <p>
  506.      * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame.
  507.      *
  508.      * @param interpolationDate interpolation date
  509.      * @param attitudes attitudes sample
  510.      * @param pvProvider position-velocity-acceleration coordinates provider
  511.      *
  512.      * @return interpolated attitude if attitude interpolator is present, default attitude otherwise
  513.      */
  514.     private FieldAttitude<KK> interpolateAttitude(final FieldAbsoluteDate<KK> interpolationDate,
  515.                                                   final List<FieldAttitude<KK>> attitudes,
  516.                                                   final FieldPVCoordinatesProvider<KK> pvProvider) {
  517.         if (attitudes.isEmpty()) {
  518.             final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame);
  519.             return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame);
  520.         }
  521.         else {
  522.             return attitudeInterpolator.interpolate(interpolationDate, attitudes);
  523.         }
  524.     }
  525. }