SpacecraftStateInterpolator.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.util.Pair;
  19. import org.orekit.attitudes.Attitude;
  20. import org.orekit.attitudes.AttitudeInterpolator;
  21. import org.orekit.attitudes.AttitudeProvider;
  22. import org.orekit.attitudes.FrameAlignedProvider;
  23. import org.orekit.errors.OrekitIllegalArgumentException;
  24. import org.orekit.errors.OrekitInternalError;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.orbits.Orbit;
  28. import org.orekit.orbits.OrbitHermiteInterpolator;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.AbstractTimeInterpolator;
  31. import org.orekit.time.TimeInterpolator;
  32. import org.orekit.time.TimeStamped;
  33. import org.orekit.time.TimeStampedDouble;
  34. import org.orekit.time.TimeStampedDoubleHermiteInterpolator;
  35. import org.orekit.utils.AbsolutePVCoordinates;
  36. import org.orekit.utils.AbsolutePVCoordinatesHermiteInterpolator;
  37. import org.orekit.utils.AngularDerivativesFilter;
  38. import org.orekit.utils.CartesianDerivativesFilter;
  39. import org.orekit.utils.DoubleArrayDictionary;
  40. import org.orekit.utils.PVCoordinatesProvider;
  41. import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator;

  42. import java.util.ArrayList;
  43. import java.util.Collection;
  44. import java.util.HashMap;
  45. import java.util.List;
  46. import java.util.Map;
  47. import java.util.Optional;

  48. /**
  49.  * Generic class for spacecraft state interpolator.
  50.  * <p>
  51.  * The user can specify what interpolator to use for each attribute of the spacecraft state. However, at least one
  52.  * interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other interpolators can be
  53.  * left to null if the user do not want to interpolate these values.
  54.  *
  55.  * @author Luc Maisonobe
  56.  * @author Vincent Cucchietti
  57.  * @see SpacecraftState
  58.  */
  59. public class SpacecraftStateInterpolator extends AbstractTimeInterpolator<SpacecraftState> {

  60.     /**
  61.      * Output frame.
  62.      * <p><b>Must be inertial</b> if interpolating spacecraft states defined by orbit</p>
  63.      */
  64.     private final Frame outputFrame;

  65.     /** Orbit interpolator. */
  66.     private final TimeInterpolator<Orbit> orbitInterpolator;

  67.     /** Absolute position-velocity-acceleration interpolator. */
  68.     private final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolator;

  69.     /** Mass interpolator. */
  70.     private final TimeInterpolator<TimeStampedDouble> massInterpolator;

  71.     /** Attitude interpolator. */
  72.     private final TimeInterpolator<Attitude> attitudeInterpolator;

  73.     /** Additional state interpolator. */
  74.     private final TimeInterpolator<TimeStampedDouble> additionalStateInterpolator;

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

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

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

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

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

  213.     /**
  214.      * Constructor with:
  215.      * <ul>
  216.      *     <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
  217.      *     <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
  218.      * </ul>
  219.      * <p>
  220.      * At least one interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other
  221.      * interpolators can be left to null if the user do not want to interpolate these values.
  222.      * <p>
  223.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  224.      * error otherwise.
  225.      * <p>
  226.      * <b>BEWARE:</b> it is up to the user to check the consistency of input interpolators.
  227.      *
  228.      * @param outputFrame output frame (inertial if the user is planning to use the orbit interpolator)
  229.      * @param orbitInterpolator orbit interpolator (can be null if absPVAInterpolator is defined)
  230.      * @param absPVAInterpolator absolute position-velocity-acceleration (can be null if orbitInterpolator is defined)
  231.      * @param massInterpolator mass interpolator (can be null)
  232.      * @param attitudeInterpolator attitude interpolator (can be null)
  233.      * @param additionalStateInterpolator additional state interpolator (can be null)
  234.      *
  235.      * @see AbstractTimeInterpolator
  236.      */
  237.     public SpacecraftStateInterpolator(final Frame outputFrame, final TimeInterpolator<Orbit> orbitInterpolator,
  238.                                        final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolator,
  239.                                        final TimeInterpolator<TimeStampedDouble> massInterpolator,
  240.                                        final TimeInterpolator<Attitude> attitudeInterpolator,
  241.                                        final TimeInterpolator<TimeStampedDouble> additionalStateInterpolator) {
  242.         super(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC);
  243.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  244.         this.outputFrame                 = outputFrame;
  245.         this.orbitInterpolator           = orbitInterpolator;
  246.         this.absPVAInterpolator          = absPVAInterpolator;
  247.         this.massInterpolator            = massInterpolator;
  248.         this.attitudeInterpolator        = attitudeInterpolator;
  249.         this.additionalStateInterpolator = additionalStateInterpolator;
  250.     }

  251.     /**
  252.      * Constructor.
  253.      * <p>
  254.      * At least one interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other
  255.      * interpolators can be left to null if the user do not want to interpolate these values.
  256.      * <p>
  257.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  258.      * error otherwise.
  259.      * <p>
  260.      * <b>BEWARE:</b> it is up to the user to check the consistency of input interpolators.
  261.      *
  262.      * @param interpolationPoints number of interpolation points
  263.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  264.      * @param outputFrame output frame (inertial if the user is planning to use the orbit interpolator)
  265.      * @param orbitInterpolator orbit interpolator (can be null if absPVAInterpolator is defined)
  266.      * @param absPVAInterpolator absolute position-velocity-acceleration (can be null if orbitInterpolator is defined)
  267.      * @param massInterpolator mass interpolator (can be null)
  268.      * @param attitudeInterpolator attitude interpolator (can be null)
  269.      * @param additionalStateInterpolator additional state interpolator (can be null)
  270.      *
  271.      * @since 12.0.1
  272.      */
  273.     public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold,
  274.                                        final Frame outputFrame, final TimeInterpolator<Orbit> orbitInterpolator,
  275.                                        final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolator,
  276.                                        final TimeInterpolator<TimeStampedDouble> massInterpolator,
  277.                                        final TimeInterpolator<Attitude> attitudeInterpolator,
  278.                                        final TimeInterpolator<TimeStampedDouble> additionalStateInterpolator) {
  279.         super(interpolationPoints, extrapolationThreshold);
  280.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  281.         this.outputFrame                 = outputFrame;
  282.         this.orbitInterpolator           = orbitInterpolator;
  283.         this.absPVAInterpolator          = absPVAInterpolator;
  284.         this.massInterpolator            = massInterpolator;
  285.         this.attitudeInterpolator        = attitudeInterpolator;
  286.         this.additionalStateInterpolator = additionalStateInterpolator;
  287.     }

  288.     /**
  289.      * Check that an interpolator exist for given sample state definition.
  290.      *
  291.      * @param sample sample (non empty)
  292.      * @param orbitInterpolatorIsPresent flag defining if an orbit interpolator has been defined for this instance
  293.      * @param absPVInterpolatorIsPresent flag defining if an absolute position-velocity-acceleration interpolator has been
  294.      * defined for this instance
  295.      *
  296.      * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
  297.      * definition type
  298.      */
  299.     public static void checkSampleAndInterpolatorConsistency(final List<SpacecraftState> sample,
  300.                                                              final boolean orbitInterpolatorIsPresent,
  301.                                                              final boolean absPVInterpolatorIsPresent) {
  302.         // Get first state definition
  303.         final SpacecraftState earliestState = sample.get(0);

  304.         if (earliestState.isOrbitDefined() && !orbitInterpolatorIsPresent ||
  305.                 !earliestState.isOrbitDefined() && !absPVInterpolatorIsPresent) {
  306.             throw new OrekitIllegalArgumentException(OrekitMessages.WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION);
  307.         }
  308.     }

  309.     /**
  310.      * Check that all state are either orbit defined or based on absolute position-velocity-acceleration.
  311.      *
  312.      * @param states spacecraft state sample
  313.      */
  314.     public static void checkStatesDefinitionsConsistency(final List<SpacecraftState> states) {
  315.         // Check all states handle the same additional states and are defined the same way (orbit or absolute PVA)
  316.         final SpacecraftState s0               = states.get(0);
  317.         final boolean         s0IsOrbitDefined = s0.isOrbitDefined();
  318.         for (final SpacecraftState state : states) {
  319.             s0.ensureCompatibleAdditionalStates(state);
  320.             if (s0IsOrbitDefined != state.isOrbitDefined()) {
  321.                 throw new OrekitIllegalArgumentException(OrekitMessages.DIFFERENT_STATE_DEFINITION);
  322.             }
  323.         }
  324.     }

  325.     /**
  326.      * {@inheritDoc}
  327.      * <p>
  328.      * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample
  329.      * instances must therefore have at least the same additional states as this neighbor instance. They may have more
  330.      * additional states, but the extra ones will be ignored.
  331.      * <p>
  332.      * All the sample instances <em>must</em> be based on similar trajectory data, i.e. they must either all be based on
  333.      * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an
  334.      * {@link OrekitIllegalArgumentException}.
  335.      *
  336.      * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute
  337.      * position-velocity-acceleration coordinates
  338.      * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
  339.      * definition type
  340.      */
  341.     @Override
  342.     public SpacecraftState interpolate(final AbsoluteDate interpolationDate, final Collection<SpacecraftState> sample) {

  343.         final List<SpacecraftState> sampleList = new ArrayList<>(sample);

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

  346.             // Check given that given states definition are consistent
  347.             // (all defined by either orbits or absolute position-velocity-acceleration coordinates)
  348.             checkStatesDefinitionsConsistency(sampleList);

  349.             // Check interpolator and sample consistency
  350.             checkSampleAndInterpolatorConsistency(sampleList, orbitInterpolator != null, absPVAInterpolator != null);
  351.         }

  352.         return super.interpolate(interpolationDate, sample);
  353.     }

  354.     /** {@inheritDoc} */
  355.     @Override
  356.     public List<TimeInterpolator<? extends TimeStamped>> getSubInterpolators() {

  357.         // Add all sub interpolators that are defined
  358.         final List<TimeInterpolator<? extends TimeStamped>> subInterpolators = new ArrayList<>();

  359.         addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
  360.         addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
  361.         addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
  362.         addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
  363.         addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);

  364.         return subInterpolators;

  365.     }

  366.     /**
  367.      * {@inheritDoc}
  368.      */
  369.     @Override
  370.     protected SpacecraftState interpolate(final InterpolationData interpolationData) {

  371.         // Get first state definition
  372.         final SpacecraftState earliestState   = interpolationData.getNeighborList().get(0);
  373.         final boolean         areOrbitDefined = earliestState.isOrbitDefined();

  374.         // Prepare samples
  375.         final List<Attitude> attitudes = new ArrayList<>();

  376.         final List<TimeStampedDouble> masses = new ArrayList<>();

  377.         final List<DoubleArrayDictionary.Entry> additionalEntries = earliestState.getAdditionalStatesValues().getData();
  378.         final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalSample =
  379.                 createAdditionalStateSample(additionalEntries);

  380.         final List<DoubleArrayDictionary.Entry> additionalDotEntries =
  381.                 earliestState.getAdditionalStatesDerivatives().getData();
  382.         final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalDotSample =
  383.                 createAdditionalStateSample(additionalDotEntries);

  384.         // Fill interpolators with samples
  385.         final List<SpacecraftState>       samples      = interpolationData.getCachedSamples().getAll();
  386.         final List<Orbit>                 orbitSample  = new ArrayList<>();
  387.         final List<AbsolutePVCoordinates> absPVASample = new ArrayList<>();
  388.         for (SpacecraftState state : samples) {
  389.             final AbsoluteDate currentDate = state.getDate();

  390.             // Add orbit sample if state is defined with an orbit
  391.             if (state.isOrbitDefined()) {
  392.                 orbitSample.add(state.getOrbit());
  393.             }
  394.             // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
  395.             else {
  396.                 absPVASample.add(state.getAbsPVA());
  397.             }

  398.             // Add mass sample
  399.             if (massInterpolator != null) {
  400.                 masses.add(new TimeStampedDouble(state.getMass(), state.getDate()));
  401.             }

  402.             // Add attitude sample if it is interpolated
  403.             if (attitudeInterpolator != null) {
  404.                 attitudes.add(state.getAttitude());
  405.             }

  406.             if (additionalStateInterpolator != null) {

  407.                 // Add all additional state values if they are interpolated
  408.                 for (final Map.Entry<String, List<Pair<AbsoluteDate, double[]>>> entry : additionalSample.entrySet()) {
  409.                     entry.getValue().add(new Pair<>(currentDate, state.getAdditionalState(entry.getKey())));
  410.                 }

  411.                 // Add all additional state derivative values if they are interpolated
  412.                 for (final Map.Entry<String, List<Pair<AbsoluteDate, double[]>>> entry : additionalDotSample.entrySet()) {
  413.                     entry.getValue().add(new Pair<>(currentDate, state.getAdditionalStateDerivative(entry.getKey())));
  414.                 }
  415.             }
  416.         }

  417.         // Interpolate mass
  418.         final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate();
  419.         final double       interpolatedMass;
  420.         if (massInterpolator != null) {
  421.             interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
  422.         } else {
  423.             interpolatedMass = SpacecraftState.DEFAULT_MASS;
  424.         }

  425.         // Interpolate additional states and derivatives
  426.         final DoubleArrayDictionary interpolatedAdditional;
  427.         final DoubleArrayDictionary interpolatedAdditionalDot;
  428.         if (additionalStateInterpolator != null) {
  429.             interpolatedAdditional    = interpolateAdditionalState(interpolationDate, additionalSample);
  430.             interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample);
  431.         } else {
  432.             interpolatedAdditional    = null;
  433.             interpolatedAdditionalDot = null;
  434.         }

  435.         // Interpolate orbit
  436.         if (areOrbitDefined && orbitInterpolator != null) {
  437.             final Orbit interpolatedOrbit = orbitInterpolator.interpolate(interpolationDate, orbitSample);

  438.             final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedOrbit);

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

  444.             final AbsolutePVCoordinates interpolatedAbsPva = absPVAInterpolator.interpolate(interpolationDate, absPVASample);

  445.             final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedAbsPva);

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

  453.     }

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

  462.     /**
  463.      * Get orbit interpolator.
  464.      *
  465.      * @return optional orbit interpolator
  466.      *
  467.      * @see Optional
  468.      */
  469.     public Optional<TimeInterpolator<Orbit>> getOrbitInterpolator() {
  470.         return Optional.ofNullable(orbitInterpolator);
  471.     }

  472.     /**
  473.      * Get absolute position-velocity-acceleration interpolator.
  474.      *
  475.      * @return optional absolute position-velocity-acceleration interpolator
  476.      *
  477.      * @see Optional
  478.      */
  479.     public Optional<TimeInterpolator<AbsolutePVCoordinates>> getAbsPVAInterpolator() {
  480.         return Optional.ofNullable(absPVAInterpolator);
  481.     }

  482.     /**
  483.      * Get mass interpolator.
  484.      *
  485.      * @return optional mass interpolator
  486.      *
  487.      * @see Optional
  488.      */
  489.     public Optional<TimeInterpolator<TimeStampedDouble>> getMassInterpolator() {
  490.         return Optional.ofNullable(massInterpolator);
  491.     }

  492.     /**
  493.      * Get attitude interpolator.
  494.      *
  495.      * @return optional attitude interpolator
  496.      *
  497.      * @see Optional
  498.      */
  499.     public Optional<TimeInterpolator<Attitude>> getAttitudeInterpolator() {
  500.         return Optional.ofNullable(attitudeInterpolator);
  501.     }

  502.     /**
  503.      * Get additional state interpolator.
  504.      *
  505.      * @return optional additional state interpolator
  506.      *
  507.      * @see Optional
  508.      */
  509.     public Optional<TimeInterpolator<TimeStampedDouble>> getAdditionalStateInterpolator() {
  510.         return Optional.ofNullable(additionalStateInterpolator);
  511.     }

  512.     /**
  513.      * Check that at least one interpolator is defined.
  514.      *
  515.      * @param orbitInterpolatorToCheck orbit interpolator
  516.      * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
  517.      */
  518.     private void checkAtLeastOneInterpolator(final TimeInterpolator<Orbit> orbitInterpolatorToCheck,
  519.                                              final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolatorToCheck) {
  520.         if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
  521.             throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
  522.         }
  523.     }

  524.     /**
  525.      * Create empty samples for given additional entries.
  526.      *
  527.      * @param additionalEntries tabulated additional entries
  528.      *
  529.      * @return empty samples for given additional entries
  530.      */
  531.     private Map<String, List<Pair<AbsoluteDate, double[]>>> createAdditionalStateSample(
  532.             final List<DoubleArrayDictionary.Entry> additionalEntries) {
  533.         final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalSamples = new HashMap<>(additionalEntries.size());

  534.         for (final DoubleArrayDictionary.Entry entry : additionalEntries) {
  535.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  536.         }

  537.         return additionalSamples;
  538.     }

  539.     /**
  540.      * Interpolate additional state values.
  541.      *
  542.      * @param interpolationDate interpolation date
  543.      * @param additionalSamples additional state samples
  544.      *
  545.      * @return interpolated additional state values
  546.      */
  547.     private DoubleArrayDictionary interpolateAdditionalState(final AbsoluteDate interpolationDate,
  548.                                                              final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalSamples) {
  549.         final DoubleArrayDictionary interpolatedAdditional;

  550.         if (additionalSamples.isEmpty()) {
  551.             interpolatedAdditional = null;
  552.         } else {
  553.             interpolatedAdditional = new DoubleArrayDictionary(additionalSamples.size());
  554.             for (final Map.Entry<String, List<Pair<AbsoluteDate, double[]>>> entry : additionalSamples.entrySet()) {

  555.                 // Get current entry
  556.                 final List<Pair<AbsoluteDate, double[]>> currentAdditionalSamples = entry.getValue();

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

  559.                 // For each value of current additional state entry
  560.                 final double[] currentInterpolatedAdditional = new double[nbOfValues];
  561.                 for (int i = 0; i < nbOfValues; i++) {

  562.                     // Create final index for lambda expression use
  563.                     final int currentIndex = i;

  564.                     // Create sample for specific value of current additional state values
  565.                     final List<TimeStampedDouble> currentValueSample = new ArrayList<>();

  566.                     currentAdditionalSamples.forEach(currentSamples -> currentValueSample.add(
  567.                             new TimeStampedDouble(currentSamples.getValue()[currentIndex], currentSamples.getFirst())));

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

  572.                 interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional);
  573.             }
  574.         }
  575.         return interpolatedAdditional;
  576.     }

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