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.OrekitException;
  27. import org.orekit.errors.OrekitIllegalArgumentException;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.errors.OrekitMessages;
  30. import org.orekit.frames.Frame;
  31. import org.orekit.orbits.FieldOrbit;
  32. import org.orekit.orbits.FieldOrbitHermiteInterpolator;
  33. import org.orekit.time.AbstractFieldTimeInterpolator;
  34. import org.orekit.time.AbstractTimeInterpolator;
  35. import org.orekit.time.FieldAbsoluteDate;
  36. import org.orekit.time.FieldTimeInterpolator;
  37. import org.orekit.time.FieldTimeStamped;
  38. import org.orekit.time.TimeStampedField;
  39. import org.orekit.time.TimeStampedFieldHermiteInterpolator;
  40. import org.orekit.utils.AngularDerivativesFilter;
  41. import org.orekit.utils.CartesianDerivativesFilter;
  42. import org.orekit.utils.FieldAbsolutePVCoordinates;
  43. import org.orekit.utils.FieldAbsolutePVCoordinatesHermiteInterpolator;
  44. import org.orekit.utils.FieldArrayDictionary;
  45. import org.orekit.utils.FieldPVCoordinatesProvider;
  46. import org.orekit.utils.TimeStampedFieldAngularCoordinatesHermiteInterpolator;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  291.         return subInterpolators;
  292.     }

  293.     /**
  294.      * @return fail to return number of interpolation points used by this interpolator.
  295.      *
  296.      * @throws OrekitException because multiple interpolator are defined so the number of interpolation points used may
  297.      * differ.
  298.      */
  299.     @Override
  300.     public int getNbInterpolationPoints() {
  301.         throw new OrekitException(OrekitMessages.MULTIPLE_INTERPOLATOR_USED);
  302.     }

  303.     /**
  304.      * {@inheritDoc}
  305.      */
  306.     @Override
  307.     protected FieldSpacecraftState<KK> interpolate(final InterpolationData interpolationData) {

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

  310.         // Get first state definition
  311.         final FieldSpacecraftState<KK> earliestState   = interpolationData.getNeighborList().get(0);
  312.         final boolean                  areOrbitDefined = earliestState.isOrbitDefined();

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

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

  316.         final List<FieldArrayDictionary<KK>.Entry> additionalEntries =
  317.                 earliestState.getAdditionalStatesValues().getData();
  318.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSample =
  319.                 createAdditionalStateSample(additionalEntries);

  320.         final List<FieldArrayDictionary<KK>.Entry> additionalDotEntries =
  321.                 earliestState.getAdditionalStatesDerivatives().getData();
  322.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalDotSample =
  323.                 createAdditionalStateSample(additionalDotEntries);

  324.         // Fill interpolators with samples
  325.         final List<FieldSpacecraftState<KK>>       samples      = interpolationData.getCachedSamples().getAll();
  326.         final List<FieldOrbit<KK>>                 orbitSample  = new ArrayList<>();
  327.         final List<FieldAbsolutePVCoordinates<KK>> absPVASample = new ArrayList<>();
  328.         for (FieldSpacecraftState<KK> state : samples) {
  329.             final FieldAbsoluteDate<KK> currentDate = state.getDate();

  330.             // Add orbit sample if state is defined with an orbit
  331.             if (state.isOrbitDefined()) {
  332.                 orbitSample.add(state.getOrbit());
  333.             }
  334.             // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
  335.             else {
  336.                 absPVASample.add(state.getAbsPVA());
  337.             }

  338.             // Add mass sample
  339.             if (massInterpolator != null) {
  340.                 masses.add(new TimeStampedField<>(state.getMass(), state.getDate()));
  341.             }

  342.             // Add attitude sample if it is interpolated
  343.             if (attitudeInterpolator != null) {
  344.                 attitudes.add(state.getAttitude());
  345.             }

  346.             if (additionalStateInterpolator != null) {

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

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

  356.         }

  357.         // Interpolate mass
  358.         final KK one = interpolationData.getOne();
  359.         final KK interpolatedMass;
  360.         if (massInterpolator != null) {
  361.             interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
  362.         }
  363.         else {
  364.             interpolatedMass = one.multiply(SpacecraftState.DEFAULT_MASS);
  365.         }

  366.         // Interpolate additional states and derivatives
  367.         final FieldArrayDictionary<KK> interpolatedAdditional;
  368.         final FieldArrayDictionary<KK> interpolatedAdditionalDot;
  369.         if (additionalStateInterpolator != null) {
  370.             interpolatedAdditional    = interpolateAdditionalState(interpolationDate, additionalSample);
  371.             interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample);
  372.         }
  373.         else {
  374.             interpolatedAdditional    = null;
  375.             interpolatedAdditionalDot = null;
  376.         }

  377.         // Interpolate orbit
  378.         if (areOrbitDefined && orbitInterpolator != null) {
  379.             final FieldOrbit<KK> interpolatedOrbit =
  380.                     orbitInterpolator.interpolate(interpolationDate, orbitSample);

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

  383.             return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass,
  384.                                               interpolatedAdditional, interpolatedAdditionalDot);
  385.         }
  386.         // Interpolate absolute position-velocity-acceleration
  387.         else if (!areOrbitDefined && absPVAInterpolator != null) {

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

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

  392.             return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass,
  393.                                               interpolatedAdditional, interpolatedAdditionalDot);
  394.         }
  395.         // Should never happen
  396.         else {
  397.             throw new OrekitInternalError(null);
  398.         }

  399.     }

  400.     /** Get output frame.
  401.      * @return output frame
  402.      */
  403.     public Frame getOutputFrame() {
  404.         return outputFrame;
  405.     }

  406.     /** Get orbit interpolator.
  407.      * @return optional orbit interpolator
  408.      *
  409.      * @see Optional
  410.      */
  411.     public Optional<FieldTimeInterpolator<FieldOrbit<KK>, KK>> getOrbitInterpolator() {
  412.         return Optional.ofNullable(orbitInterpolator);
  413.     }

  414.     /** Get absolute position-velocity-acceleration interpolator.
  415.      * @return optional absolute position-velocity-acceleration interpolator
  416.      *
  417.      * @see Optional
  418.      */
  419.     public Optional<FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK>> getAbsPVAInterpolator() {
  420.         return Optional.ofNullable(absPVAInterpolator);
  421.     }

  422.     /** Get mass interpolator.
  423.      * @return optional mass interpolator
  424.      *
  425.      * @see Optional
  426.      */
  427.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getMassInterpolator() {
  428.         return Optional.ofNullable(massInterpolator);
  429.     }

  430.     /** Get attitude interpolator.
  431.      * @return optional attitude interpolator
  432.      *
  433.      * @see Optional
  434.      */
  435.     public Optional<FieldTimeInterpolator<FieldAttitude<KK>, KK>> getAttitudeInterpolator() {
  436.         return Optional.ofNullable(attitudeInterpolator);
  437.     }

  438.     /** Get additional state interpolator.
  439.      * @return optional additional state interpolator
  440.      *
  441.      * @see Optional
  442.      */
  443.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getAdditionalStateInterpolator() {
  444.         return Optional.ofNullable(additionalStateInterpolator);
  445.     }

  446.     /**
  447.      * Check that at least one interpolator is defined.
  448.      *
  449.      * @param orbitInterpolatorToCheck orbit interpolator
  450.      * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
  451.      */
  452.     private void checkAtLeastOneInterpolator(final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolatorToCheck,
  453.                                              final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolatorToCheck) {
  454.         if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
  455.             throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
  456.         }
  457.     }

  458.     /**
  459.      * Create empty samples for given additional entries.
  460.      *
  461.      * @param additionalEntries tabulated additional entries
  462.      *
  463.      * @return empty samples for given additional entries
  464.      */
  465.     private Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> createAdditionalStateSample(
  466.             final List<FieldArrayDictionary<KK>.Entry> additionalEntries) {
  467.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples =
  468.                 new HashMap<>(additionalEntries.size());

  469.         for (final FieldArrayDictionary<KK>.Entry entry : additionalEntries) {
  470.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  471.         }

  472.         return additionalSamples;
  473.     }

  474.     /**
  475.      * Interpolate additional state values.
  476.      *
  477.      * @param interpolationDate interpolation date
  478.      * @param additionalSamples additional state samples
  479.      *
  480.      * @return interpolated additional state values
  481.      */
  482.     private FieldArrayDictionary<KK> interpolateAdditionalState(final FieldAbsoluteDate<KK> interpolationDate,
  483.                                                                 final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples) {
  484.         final Field<KK> field = interpolationDate.getField();
  485.         final FieldArrayDictionary<KK> interpolatedAdditional;

  486.         if (additionalSamples.isEmpty()) {
  487.             interpolatedAdditional = null;
  488.         }
  489.         else {
  490.             interpolatedAdditional = new FieldArrayDictionary<>(field, additionalSamples.size());
  491.             for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> entry : additionalSamples.entrySet()) {

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

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

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

  499.                     // Create final index for lambda expression use
  500.                     final int currentIndex = i;

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

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

  505.                     // Interpolate
  506.                     currentInterpolatedAdditional[i] =
  507.                             additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
  508.                 }

  509.                 interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional);
  510.             }
  511.         }
  512.         return interpolatedAdditional;
  513.     }

  514.     /**
  515.      * Interpolate attitude.
  516.      * <p>
  517.      * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame.
  518.      *
  519.      * @param interpolationDate interpolation date
  520.      * @param attitudes attitudes sample
  521.      * @param pvProvider position-velocity-acceleration coordinates provider
  522.      *
  523.      * @return interpolated attitude if attitude interpolator is present, default attitude otherwise
  524.      */
  525.     private FieldAttitude<KK> interpolateAttitude(final FieldAbsoluteDate<KK> interpolationDate,
  526.                                                   final List<FieldAttitude<KK>> attitudes,
  527.                                                   final FieldPVCoordinatesProvider<KK> pvProvider) {
  528.         if (attitudes.isEmpty()) {
  529.             final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame);
  530.             return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame);
  531.         }
  532.         else {
  533.             return attitudeInterpolator.interpolate(interpolationDate, attitudes);
  534.         }
  535.     }
  536. }