FieldSpacecraftStateInterpolator.java

  1. /* Copyright 2002-2025 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.FieldDataDictionary;
  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.Comparator;
  50. import java.util.HashMap;
  51. import java.util.List;
  52. import java.util.Map;
  53. import java.util.Optional;
  54. import java.util.stream.Collectors;

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

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

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

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

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

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

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

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

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

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

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

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

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

  244.     /**
  245.      * Constructor.
  246.      * <p>
  247.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  248.      * error otherwise.
  249.      *
  250.      * @param interpolationPoints number of interpolation points
  251.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  252.      * @param outputFrame output frame
  253.      * @param orbitInterpolator orbit interpolator
  254.      * @param absPVAInterpolator absolute position-velocity-acceleration
  255.      * @param massInterpolator mass interpolator
  256.      * @param attitudeInterpolator attitude interpolator
  257.      * @param additionalStateInterpolator additional state interpolator
  258.      */
  259.     public FieldSpacecraftStateInterpolator(final int interpolationPoints,
  260.                                             final double extrapolationThreshold,
  261.                                             final Frame outputFrame,
  262.                                             final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolator,
  263.                                             final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolator,
  264.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> massInterpolator,
  265.                                             final FieldTimeInterpolator<FieldAttitude<KK>, KK> attitudeInterpolator,
  266.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> additionalStateInterpolator) {
  267.         super(interpolationPoints, extrapolationThreshold);
  268.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  269.         this.outputFrame                 = outputFrame;
  270.         this.orbitInterpolator           = orbitInterpolator;
  271.         this.absPVAInterpolator          = absPVAInterpolator;
  272.         this.massInterpolator            = massInterpolator;
  273.         this.attitudeInterpolator        = attitudeInterpolator;
  274.         this.additionalStateInterpolator = additionalStateInterpolator;
  275.     }

  276.     /**
  277.      * {@inheritDoc}
  278.      * <p>
  279.      * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample
  280.      * instances must therefore have at least the same additional states as this neighbor instance. They may have more
  281.      * additional states, but the extra ones will be ignored.
  282.      * <p>
  283.      * All the sample instances <em>must</em> be based on similar trajectory data, i.e. they must either all be based on
  284.      * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an
  285.      * {@link OrekitIllegalArgumentException}.
  286.      *
  287.      * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute
  288.      * position-velocity-acceleration coordinates
  289.      * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
  290.      * definition type
  291.      */
  292.     @Override
  293.     public FieldSpacecraftState<KK> interpolate(final FieldAbsoluteDate<KK> interpolationDate,
  294.                                                 final Collection<FieldSpacecraftState<KK>> sample) {

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

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

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

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

  304.             // Check interpolator and sample consistency
  305.             SpacecraftStateInterpolator.checkSampleAndInterpolatorConsistency(nonFieldSampleList,
  306.                                                                               orbitInterpolator != null,
  307.                                                                               absPVAInterpolator != null);
  308.         }

  309.         return super.interpolate(interpolationDate, sample);
  310.     }

  311.     /** {@inheritDoc} */
  312.     @Override
  313.     public List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> getSubInterpolators() {

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

  316.         addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
  317.         addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
  318.         addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
  319.         addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
  320.         addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);

  321.         return subInterpolators;
  322.     }

  323.     /**
  324.      * {@inheritDoc}
  325.      */
  326.     @Override
  327.     protected FieldSpacecraftState<KK> interpolate(final InterpolationData interpolationData) {

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

  330.         // Get first state definition
  331.         final FieldSpacecraftState<KK> earliestState   = interpolationData.getNeighborList().get(0);
  332.         final boolean                  areOrbitDefined = earliestState.isOrbitDefined();

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

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

  336.         final List<FieldDataDictionary<KK>.Entry> additionalEntries = earliestState.getAdditionalDataValues().getData();
  337.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, Object>>> additionalSample =
  338.                 createAdditionalDataSample(additionalEntries);

  339.         final List<FieldArrayDictionary<KK>.Entry> additionalDotEntries =
  340.                 earliestState.getAdditionalStatesDerivatives().getData();
  341.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalDotSample =
  342.                 createAdditionalStateSample(additionalDotEntries);

  343.         // Fill interpolators with samples
  344.         final List<FieldSpacecraftState<KK>>       samples      = interpolationData.getNeighborList();
  345.         final List<FieldOrbit<KK>>                 orbitSample  = new ArrayList<>();
  346.         final List<FieldAbsolutePVCoordinates<KK>> absPVASample = new ArrayList<>();
  347.         for (FieldSpacecraftState<KK> state : samples) {
  348.             final FieldAbsoluteDate<KK> currentDate = state.getDate();

  349.             // Add orbit sample if state is defined with an orbit
  350.             if (state.isOrbitDefined()) {
  351.                 orbitSample.add(state.getOrbit());
  352.             }
  353.             // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
  354.             else {
  355.                 absPVASample.add(state.getAbsPVA());
  356.             }

  357.             // Add mass sample
  358.             if (massInterpolator != null) {
  359.                 masses.add(new TimeStampedField<>(state.getMass(), state.getDate()));
  360.             }

  361.             // Add attitude sample if it is interpolated
  362.             if (attitudeInterpolator != null) {
  363.                 attitudes.add(state.getAttitude());
  364.             }

  365.             if (additionalStateInterpolator != null) {

  366.                 // Add all additional state values if they are interpolated
  367.                 for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, Object>>> entry : additionalSample.entrySet()) {
  368.                     entry.getValue().add(new Pair<>(currentDate, state.getAdditionalData(entry.getKey())));
  369.                 }

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

  375.         }

  376.         // Interpolate mass
  377.         final KK one = interpolationData.getOne();
  378.         final KK interpolatedMass;
  379.         if (massInterpolator != null) {
  380.             interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
  381.         }
  382.         else {
  383.             interpolatedMass = one.newInstance(SpacecraftState.DEFAULT_MASS);
  384.         }

  385.         // Interpolate additional states and derivatives
  386.         final FieldDataDictionary<KK> interpolatedAdditional;
  387.         final FieldArrayDictionary<KK> interpolatedAdditionalDot;
  388.         if (additionalStateInterpolator != null) {
  389.             interpolatedAdditional    = interpolateAdditionalState(interpolationDate, additionalSample).orElse(null);
  390.             interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample).map(FieldDataDictionary::toFieldArrayDictionary).orElse(null);
  391.         }
  392.         else {
  393.             interpolatedAdditional    = null;
  394.             interpolatedAdditionalDot = null;
  395.         }

  396.         // Interpolate orbit
  397.         if (areOrbitDefined && orbitInterpolator != null) {
  398.             final FieldOrbit<KK> interpolatedOrbit =
  399.                     orbitInterpolator.interpolate(interpolationDate, orbitSample);

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

  402.             return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass,
  403.                                               interpolatedAdditional, interpolatedAdditionalDot);
  404.         }
  405.         // Interpolate absolute position-velocity-acceleration
  406.         else if (!areOrbitDefined && absPVAInterpolator != null) {

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

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

  411.             return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass,
  412.                                               interpolatedAdditional, interpolatedAdditionalDot);
  413.         }
  414.         // Should never happen
  415.         else {
  416.             throw new OrekitInternalError(null);
  417.         }

  418.     }

  419.     /** Get output frame.
  420.      * @return output frame
  421.      */
  422.     public Frame getOutputFrame() {
  423.         return outputFrame;
  424.     }

  425.     /** Get orbit interpolator.
  426.      * @return optional orbit interpolator
  427.      *
  428.      * @see Optional
  429.      */
  430.     public Optional<FieldTimeInterpolator<FieldOrbit<KK>, KK>> getOrbitInterpolator() {
  431.         return Optional.ofNullable(orbitInterpolator);
  432.     }

  433.     /** Get absolute position-velocity-acceleration interpolator.
  434.      * @return optional absolute position-velocity-acceleration interpolator
  435.      *
  436.      * @see Optional
  437.      */
  438.     public Optional<FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK>> getAbsPVAInterpolator() {
  439.         return Optional.ofNullable(absPVAInterpolator);
  440.     }

  441.     /** Get mass interpolator.
  442.      * @return optional mass interpolator
  443.      *
  444.      * @see Optional
  445.      */
  446.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getMassInterpolator() {
  447.         return Optional.ofNullable(massInterpolator);
  448.     }

  449.     /** Get attitude interpolator.
  450.      * @return optional attitude interpolator
  451.      *
  452.      * @see Optional
  453.      */
  454.     public Optional<FieldTimeInterpolator<FieldAttitude<KK>, KK>> getAttitudeInterpolator() {
  455.         return Optional.ofNullable(attitudeInterpolator);
  456.     }

  457.     /** Get additional state interpolator.
  458.      * @return optional additional state interpolator
  459.      *
  460.      * @see Optional
  461.      */
  462.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getAdditionalStateInterpolator() {
  463.         return Optional.ofNullable(additionalStateInterpolator);
  464.     }

  465.     /**
  466.      * Check that at least one interpolator is defined.
  467.      *
  468.      * @param orbitInterpolatorToCheck orbit interpolator
  469.      * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
  470.      */
  471.     private void checkAtLeastOneInterpolator(final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolatorToCheck,
  472.                                              final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolatorToCheck) {
  473.         if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
  474.             throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
  475.         }
  476.     }

  477.     /**
  478.      * Create empty samples for given additional entries.
  479.      *
  480.      * @param additionalEntries tabulated additional entries
  481.      *
  482.      * @return empty samples for given additional entries
  483.      */
  484.     private Map<String, List<Pair<FieldAbsoluteDate<KK>, Object>>> createAdditionalDataSample(
  485.             final List<FieldDataDictionary<KK>.Entry> additionalEntries) {
  486.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, Object>>> additionalSamples =
  487.                 new HashMap<>(additionalEntries.size());

  488.         for (final FieldDataDictionary<KK>.Entry entry : additionalEntries) {
  489.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  490.         }

  491.         return additionalSamples;
  492.     }

  493.     /**
  494.      * Create empty samples for given additional entries.
  495.      *
  496.      * @param additionalEntries tabulated additional entries
  497.      *
  498.      * @return empty samples for given additional entries
  499.      */
  500.     private Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> createAdditionalStateSample(
  501.             final List<FieldArrayDictionary<KK>.Entry> additionalEntries) {
  502.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples =
  503.                 new HashMap<>(additionalEntries.size());

  504.         for (final FieldArrayDictionary<KK>.Entry entry : additionalEntries) {
  505.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  506.         }

  507.         return additionalSamples;
  508.     }

  509.     /**
  510.      * Interpolate additional state values.
  511.      *
  512.      * @param interpolationDate interpolation date
  513.      * @param additionalSamples additional state samples
  514.      * @param <O> type of the data
  515.      * @return interpolated additional state values
  516.      */
  517.     private <O> Optional<FieldDataDictionary<KK>> interpolateAdditionalState(final FieldAbsoluteDate<KK> interpolationDate,
  518.                                                                 final Map<String, List<Pair<FieldAbsoluteDate<KK>, O>>> additionalSamples) {
  519.         final Field<KK> field = interpolationDate.getField();
  520.         final Optional<FieldDataDictionary<KK>> interpolatedAdditional;

  521.         if (additionalSamples.isEmpty()) {
  522.             interpolatedAdditional = Optional.empty();
  523.         }
  524.         else {
  525.             interpolatedAdditional = Optional.of(new FieldDataDictionary<>(field, additionalSamples.size()));
  526.             for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, O>>> entry : additionalSamples.entrySet()) {

  527.                 // Get current entry
  528.                 final List<Pair<FieldAbsoluteDate<KK>, O>> currentAdditionalSamples = entry.getValue();
  529.                 final O currentInterpolatedAdditional;
  530.                 if (currentAdditionalSamples.get(0).getValue() instanceof CalculusFieldElement[]) {
  531.                     //noinspection unchecked
  532.                     currentInterpolatedAdditional = (O) interpolateAdditionalSamples(field, interpolationDate, currentAdditionalSamples);
  533.                 } else {
  534.                     currentInterpolatedAdditional = currentAdditionalSamples.stream()
  535.                             .filter(pair -> pair.getKey().isAfter(interpolationDate))
  536.                             .min(Comparator.comparing(Pair::getKey))
  537.                             .get()
  538.                             .getValue();
  539.                 }
  540.                 interpolatedAdditional.get().put(entry.getKey(), currentInterpolatedAdditional);
  541.             }
  542.         }
  543.         return interpolatedAdditional;
  544.     }

  545.     /**
  546.      * Interpolates additional samples.
  547.      * @param field field of the elements
  548.      * @param interpolationDate interpolation date
  549.      * @param currentAdditionalSamples additional state samples
  550.      * @param <O> type of the data
  551.      * @return interpolated additional samples
  552.      */
  553.     @SuppressWarnings("unchecked")
  554.     private <O> KK[] interpolateAdditionalSamples(final Field<KK> field, final FieldAbsoluteDate<KK> interpolationDate, final List<Pair<FieldAbsoluteDate<KK>, O>> currentAdditionalSamples) {

  555.         // Extract number of values for this specific entry
  556.         final int nbOfValues = ((KK[]) currentAdditionalSamples.get(0).getValue()).length;

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

  560.             // Create final index for lambda expression use
  561.             final int currentIndex = i;

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

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

  566.             // Interpolate
  567.             currentInterpolatedAdditional[i] = additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
  568.         }
  569.         return currentInterpolatedAdditional;
  570.     }

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