FieldSpacecraftStateInterpolator.java

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  217.     /**
  218.      * Constructor.
  219.      * <p>
  220.      * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
  221.      * error otherwise.
  222.      *
  223.      * @param outputFrame output frame
  224.      * @param orbitInterpolator orbit interpolator
  225.      * @param absPVAInterpolator absolute position-velocity-acceleration
  226.      * @param massInterpolator mass interpolator
  227.      * @param attitudeInterpolator attitude interpolator
  228.      * @param additionalStateInterpolator additional state interpolator
  229.      *
  230.      * @deprecated using this constructor may throw an exception if any given interpolator
  231.      * does not use {@link #DEFAULT_INTERPOLATION_POINTS} and {@link
  232.      * #DEFAULT_EXTRAPOLATION_THRESHOLD_SEC}. Use {@link
  233.      * #FieldSpacecraftStateInterpolator(int, double, Frame, FieldTimeInterpolator,
  234.      * FieldTimeInterpolator, FieldTimeInterpolator, FieldTimeInterpolator,
  235.      * FieldTimeInterpolator)} instead.
  236.      */
  237.     @Deprecated
  238.     public FieldSpacecraftStateInterpolator(final Frame outputFrame,
  239.                                             final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolator,
  240.                                             final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolator,
  241.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> massInterpolator,
  242.                                             final FieldTimeInterpolator<FieldAttitude<KK>, KK> attitudeInterpolator,
  243.                                             final FieldTimeInterpolator<TimeStampedField<KK>, KK> additionalStateInterpolator) {
  244.         super(DEFAULT_INTERPOLATION_POINTS, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC);
  245.         checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
  246.         this.outputFrame                 = outputFrame;
  247.         this.orbitInterpolator           = orbitInterpolator;
  248.         this.absPVAInterpolator          = absPVAInterpolator;
  249.         this.massInterpolator            = massInterpolator;
  250.         this.attitudeInterpolator        = attitudeInterpolator;
  251.         this.additionalStateInterpolator = additionalStateInterpolator;
  252.     }

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

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

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

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

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

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

  313.             // Check interpolator and sample consistency
  314.             SpacecraftStateInterpolator.checkSampleAndInterpolatorConsistency(nonFieldSampleList,
  315.                                                                               orbitInterpolator != null,
  316.                                                                               absPVAInterpolator != null);
  317.         }

  318.         return super.interpolate(interpolationDate, sample);
  319.     }

  320.     /** {@inheritDoc} */
  321.     @Override
  322.     public List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> getSubInterpolators() {

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

  325.         addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
  326.         addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
  327.         addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
  328.         addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
  329.         addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);

  330.         return subInterpolators;
  331.     }

  332.     /**
  333.      * {@inheritDoc}
  334.      */
  335.     @Override
  336.     protected FieldSpacecraftState<KK> interpolate(final InterpolationData interpolationData) {

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

  339.         // Get first state definition
  340.         final FieldSpacecraftState<KK> earliestState   = interpolationData.getNeighborList().get(0);
  341.         final boolean                  areOrbitDefined = earliestState.isOrbitDefined();

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

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

  345.         final List<FieldArrayDictionary<KK>.Entry> additionalEntries =
  346.                 earliestState.getAdditionalStatesValues().getData();
  347.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSample =
  348.                 createAdditionalStateSample(additionalEntries);

  349.         final List<FieldArrayDictionary<KK>.Entry> additionalDotEntries =
  350.                 earliestState.getAdditionalStatesDerivatives().getData();
  351.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalDotSample =
  352.                 createAdditionalStateSample(additionalDotEntries);

  353.         // Fill interpolators with samples
  354.         final List<FieldSpacecraftState<KK>>       samples      = interpolationData.getNeighborList();
  355.         final List<FieldOrbit<KK>>                 orbitSample  = new ArrayList<>();
  356.         final List<FieldAbsolutePVCoordinates<KK>> absPVASample = new ArrayList<>();
  357.         for (FieldSpacecraftState<KK> state : samples) {
  358.             final FieldAbsoluteDate<KK> currentDate = state.getDate();

  359.             // Add orbit sample if state is defined with an orbit
  360.             if (state.isOrbitDefined()) {
  361.                 orbitSample.add(state.getOrbit());
  362.             }
  363.             // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
  364.             else {
  365.                 absPVASample.add(state.getAbsPVA());
  366.             }

  367.             // Add mass sample
  368.             if (massInterpolator != null) {
  369.                 masses.add(new TimeStampedField<>(state.getMass(), state.getDate()));
  370.             }

  371.             // Add attitude sample if it is interpolated
  372.             if (attitudeInterpolator != null) {
  373.                 attitudes.add(state.getAttitude());
  374.             }

  375.             if (additionalStateInterpolator != null) {

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

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

  385.         }

  386.         // Interpolate mass
  387.         final KK one = interpolationData.getOne();
  388.         final KK interpolatedMass;
  389.         if (massInterpolator != null) {
  390.             interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
  391.         }
  392.         else {
  393.             interpolatedMass = one.multiply(SpacecraftState.DEFAULT_MASS);
  394.         }

  395.         // Interpolate additional states and derivatives
  396.         final FieldArrayDictionary<KK> interpolatedAdditional;
  397.         final FieldArrayDictionary<KK> interpolatedAdditionalDot;
  398.         if (additionalStateInterpolator != null) {
  399.             interpolatedAdditional    = interpolateAdditionalState(interpolationDate, additionalSample);
  400.             interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample);
  401.         }
  402.         else {
  403.             interpolatedAdditional    = null;
  404.             interpolatedAdditionalDot = null;
  405.         }

  406.         // Interpolate orbit
  407.         if (areOrbitDefined && orbitInterpolator != null) {
  408.             final FieldOrbit<KK> interpolatedOrbit =
  409.                     orbitInterpolator.interpolate(interpolationDate, orbitSample);

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

  412.             return new FieldSpacecraftState<>(interpolatedOrbit, interpolatedAttitude, interpolatedMass,
  413.                                               interpolatedAdditional, interpolatedAdditionalDot);
  414.         }
  415.         // Interpolate absolute position-velocity-acceleration
  416.         else if (!areOrbitDefined && absPVAInterpolator != null) {

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

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

  421.             return new FieldSpacecraftState<>(interpolatedAbsPva, interpolatedAttitude, interpolatedMass,
  422.                                               interpolatedAdditional, interpolatedAdditionalDot);
  423.         }
  424.         // Should never happen
  425.         else {
  426.             throw new OrekitInternalError(null);
  427.         }

  428.     }

  429.     /** Get output frame.
  430.      * @return output frame
  431.      */
  432.     public Frame getOutputFrame() {
  433.         return outputFrame;
  434.     }

  435.     /** Get orbit interpolator.
  436.      * @return optional orbit interpolator
  437.      *
  438.      * @see Optional
  439.      */
  440.     public Optional<FieldTimeInterpolator<FieldOrbit<KK>, KK>> getOrbitInterpolator() {
  441.         return Optional.ofNullable(orbitInterpolator);
  442.     }

  443.     /** Get absolute position-velocity-acceleration interpolator.
  444.      * @return optional absolute position-velocity-acceleration interpolator
  445.      *
  446.      * @see Optional
  447.      */
  448.     public Optional<FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK>> getAbsPVAInterpolator() {
  449.         return Optional.ofNullable(absPVAInterpolator);
  450.     }

  451.     /** Get mass interpolator.
  452.      * @return optional mass interpolator
  453.      *
  454.      * @see Optional
  455.      */
  456.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getMassInterpolator() {
  457.         return Optional.ofNullable(massInterpolator);
  458.     }

  459.     /** Get attitude interpolator.
  460.      * @return optional attitude interpolator
  461.      *
  462.      * @see Optional
  463.      */
  464.     public Optional<FieldTimeInterpolator<FieldAttitude<KK>, KK>> getAttitudeInterpolator() {
  465.         return Optional.ofNullable(attitudeInterpolator);
  466.     }

  467.     /** Get additional state interpolator.
  468.      * @return optional additional state interpolator
  469.      *
  470.      * @see Optional
  471.      */
  472.     public Optional<FieldTimeInterpolator<TimeStampedField<KK>, KK>> getAdditionalStateInterpolator() {
  473.         return Optional.ofNullable(additionalStateInterpolator);
  474.     }

  475.     /**
  476.      * Check that at least one interpolator is defined.
  477.      *
  478.      * @param orbitInterpolatorToCheck orbit interpolator
  479.      * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
  480.      */
  481.     private void checkAtLeastOneInterpolator(final FieldTimeInterpolator<FieldOrbit<KK>, KK> orbitInterpolatorToCheck,
  482.                                              final FieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> absPVAInterpolatorToCheck) {
  483.         if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
  484.             throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
  485.         }
  486.     }

  487.     /**
  488.      * Create empty samples for given additional entries.
  489.      *
  490.      * @param additionalEntries tabulated additional entries
  491.      *
  492.      * @return empty samples for given additional entries
  493.      */
  494.     private Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> createAdditionalStateSample(
  495.             final List<FieldArrayDictionary<KK>.Entry> additionalEntries) {
  496.         final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples =
  497.                 new HashMap<>(additionalEntries.size());

  498.         for (final FieldArrayDictionary<KK>.Entry entry : additionalEntries) {
  499.             additionalSamples.put(entry.getKey(), new ArrayList<>());
  500.         }

  501.         return additionalSamples;
  502.     }

  503.     /**
  504.      * Interpolate additional state values.
  505.      *
  506.      * @param interpolationDate interpolation date
  507.      * @param additionalSamples additional state samples
  508.      *
  509.      * @return interpolated additional state values
  510.      */
  511.     private FieldArrayDictionary<KK> interpolateAdditionalState(final FieldAbsoluteDate<KK> interpolationDate,
  512.                                                                 final Map<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> additionalSamples) {
  513.         final Field<KK> field = interpolationDate.getField();
  514.         final FieldArrayDictionary<KK> interpolatedAdditional;

  515.         if (additionalSamples.isEmpty()) {
  516.             interpolatedAdditional = null;
  517.         }
  518.         else {
  519.             interpolatedAdditional = new FieldArrayDictionary<>(field, additionalSamples.size());
  520.             for (final Map.Entry<String, List<Pair<FieldAbsoluteDate<KK>, KK[]>>> entry : additionalSamples.entrySet()) {

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

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

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

  528.                     // Create final index for lambda expression use
  529.                     final int currentIndex = i;

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

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

  534.                     // Interpolate
  535.                     currentInterpolatedAdditional[i] =
  536.                             additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
  537.                 }

  538.                 interpolatedAdditional.put(entry.getKey(), currentInterpolatedAdditional);
  539.             }
  540.         }
  541.         return interpolatedAdditional;
  542.     }

  543.     /**
  544.      * Interpolate attitude.
  545.      * <p>
  546.      * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame.
  547.      *
  548.      * @param interpolationDate interpolation date
  549.      * @param attitudes attitudes sample
  550.      * @param pvProvider position-velocity-acceleration coordinates provider
  551.      *
  552.      * @return interpolated attitude if attitude interpolator is present, default attitude otherwise
  553.      */
  554.     private FieldAttitude<KK> interpolateAttitude(final FieldAbsoluteDate<KK> interpolationDate,
  555.                                                   final List<FieldAttitude<KK>> attitudes,
  556.                                                   final FieldPVCoordinatesProvider<KK> pvProvider) {
  557.         if (attitudes.isEmpty()) {
  558.             final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame);
  559.             return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame);
  560.         }
  561.         else {
  562.             return attitudeInterpolator.interpolate(interpolationDate, attitudes);
  563.         }
  564.     }
  565. }