Ephemeris.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.analytical;

  18. import org.hipparchus.exception.LocalizedCoreFormats;
  19. import org.hipparchus.exception.MathIllegalArgumentException;
  20. import org.hipparchus.linear.RealMatrix;
  21. import org.orekit.attitudes.Attitude;
  22. import org.orekit.attitudes.AttitudeProvider;
  23. import org.orekit.attitudes.FrameAlignedProvider;
  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.OrekitIllegalArgumentException;
  26. import org.orekit.errors.OrekitIllegalStateException;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.orbits.Orbit;
  30. import org.orekit.propagation.AbstractMatricesHarvester;
  31. import org.orekit.propagation.BoundedPropagator;
  32. import org.orekit.propagation.SpacecraftState;
  33. import org.orekit.propagation.SpacecraftStateInterpolator;
  34. import org.orekit.propagation.StateCovariance;
  35. import org.orekit.time.AbsoluteDate;
  36. import org.orekit.time.AbstractTimeInterpolator;
  37. import org.orekit.time.TimeInterpolator;
  38. import org.orekit.time.TimeStampedPair;
  39. import org.orekit.utils.DoubleArrayDictionary;
  40. import org.orekit.utils.ImmutableTimeStampedCache;

  41. import java.util.ArrayList;
  42. import java.util.List;
  43. import java.util.Optional;

  44. /**
  45.  * This class is designed to accept and handle tabulated orbital entries. Tabulated entries are classified and then
  46.  * extrapolated in way to obtain continuous output, with accuracy and computation methods configured by the user.
  47.  *
  48.  * @author Fabien Maussion
  49.  * @author Véronique Pommier-Maurussane
  50.  * @author Luc Maisonobe
  51.  * @author Vincent Cucchietti
  52.  */
  53. public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPropagator {

  54.     /** First date in range. */
  55.     private final AbsoluteDate minDate;

  56.     /** Last date in range. */
  57.     private final AbsoluteDate maxDate;

  58.     /** Reference frame. */
  59.     private final Frame frame;

  60.     /** Names of the additional states. */
  61.     private final String[] additional;

  62.     /** List of spacecraft states. */
  63.     private final transient ImmutableTimeStampedCache<SpacecraftState> statesCache;

  64.     /** List of covariances. **/
  65.     private final transient ImmutableTimeStampedCache<StateCovariance> covariancesCache;

  66.     /** Spacecraft state interpolator. */
  67.     private final transient TimeInterpolator<SpacecraftState> stateInterpolator;

  68.     /** State covariance interpolator. */
  69.     private final transient TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator;

  70.     /** Flag defining if states are defined using an orbit or an absolute position-velocity-acceleration. */
  71.     private final transient boolean statesAreOrbitDefined;

  72.     /**
  73.      * Legacy constructor with tabulated states and default Hermite interpolation.
  74.      * <p>
  75.      * As this implementation of interpolation is polynomial, it should be used only with small samples (about 10-20 points)
  76.      * in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a> and numerical
  77.      * problems (including NaN appearing).
  78.      *
  79.      * @param states list of spacecraft states
  80.      * @param interpolationPoints number of points to use in interpolation
  81.      *
  82.      * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
  83.      *                                        interpolation
  84.      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
  85.      *                                        position-velocity-acceleration)
  86.      * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator)
  87.      * @see SpacecraftStateInterpolator
  88.      */
  89.     public Ephemeris(final List<SpacecraftState> states, final int interpolationPoints)
  90.             throws MathIllegalArgumentException {
  91.         // If states is empty an exception will be thrown in the other constructor
  92.         this(states, new SpacecraftStateInterpolator(interpolationPoints,
  93.                                                      states.get(0).getFrame(),
  94.                                                      states.get(0).getFrame()),
  95.              new ArrayList<>(), null);
  96.     }

  97.     /**
  98.      * Constructor with tabulated states.
  99.      *
  100.      * @param states list of spacecraft states
  101.      * @param stateInterpolator spacecraft state interpolator
  102.      *
  103.      * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
  104.      *                                        interpolation
  105.      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
  106.      *                                        position-velocity-acceleration)
  107.      * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator)
  108.      */
  109.     public Ephemeris(final List<SpacecraftState> states, final TimeInterpolator<SpacecraftState> stateInterpolator)
  110.             throws MathIllegalArgumentException {
  111.         this(states, stateInterpolator, new ArrayList<>(), null);
  112.     }

  113.     /**
  114.      * Constructor with tabulated states.
  115.      *
  116.      * @param states list of spacecraft states
  117.      * @param stateInterpolator spacecraft state interpolator
  118.      * @param attitudeProvider attitude law to use, null by default
  119.      *
  120.      * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
  121.      *                                        interpolation
  122.      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
  123.      *                                        position-velocity-acceleration)
  124.      * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator)
  125.      */
  126.     public Ephemeris(final List<SpacecraftState> states, final TimeInterpolator<SpacecraftState> stateInterpolator,
  127.                      final AttitudeProvider attitudeProvider)
  128.             throws MathIllegalArgumentException {
  129.         this(states, stateInterpolator, new ArrayList<>(), null, attitudeProvider);
  130.     }

  131.     /**
  132.      * Constructor with tabulated states and associated covariances.
  133.      *
  134.      * @param states list of spacecraft states
  135.      * @param stateInterpolator spacecraft state interpolator
  136.      * @param covariances tabulated covariances associated to tabulated states ephemeris bounds to be doing extrapolation
  137.      * @param covarianceInterpolator covariance interpolator
  138.      *
  139.      * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
  140.      *                                        interpolation
  141.      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
  142.      *                                        position-velocity-acceleration)
  143.      * @throws OrekitIllegalArgumentException if number of states is different from the number of covariances
  144.      * @throws OrekitIllegalStateException    if dates between states and associated covariances are different
  145.      * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator, AttitudeProvider)
  146.      * @since 9.0
  147.      */
  148.     public Ephemeris(final List<SpacecraftState> states,
  149.                      final TimeInterpolator<SpacecraftState> stateInterpolator,
  150.                      final List<StateCovariance> covariances,
  151.                      final TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator)
  152.             throws MathIllegalArgumentException {
  153.         this(states, stateInterpolator, covariances, covarianceInterpolator,
  154.              // if states is empty an exception will be thrown in the other constructor
  155.              states.isEmpty() ? null : FrameAlignedProvider.of(states.get(0).getFrame()));
  156.     }

  157.     /**
  158.      * Constructor with tabulated states and associated covariances.
  159.      * <p>
  160.      * The user is expected to explicitly define an attitude provider if they want to use one. Otherwise, it is null by
  161.      * default
  162.      *
  163.      * @param states list of spacecraft states
  164.      * @param stateInterpolator spacecraft state interpolator
  165.      * @param covariances tabulated covariances associated to tabulated states
  166.      * @param covarianceInterpolator covariance interpolator
  167.      * @param attitudeProvider attitude law to use, null by default
  168.      *
  169.      * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
  170.      *                                        interpolation
  171.      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
  172.      *                                        position-velocity-acceleration)
  173.      * @throws OrekitIllegalArgumentException if number of states is different from the number of covariances
  174.      * @throws OrekitIllegalStateException    if dates between states and associated covariances are different
  175.      * @since 10.1
  176.      */
  177.     public Ephemeris(final List<SpacecraftState> states,
  178.                      final TimeInterpolator<SpacecraftState> stateInterpolator,
  179.                      final List<StateCovariance> covariances,
  180.                      final TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator,
  181.                      final AttitudeProvider attitudeProvider)
  182.             throws MathIllegalArgumentException {

  183.         super(attitudeProvider);

  184.         // Check input consistency
  185.         checkInputConsistency(states, stateInterpolator, covariances, covarianceInterpolator);

  186.         // Initialize variables
  187.         final SpacecraftState s0 = states.get(0);
  188.         minDate = s0.getDate();
  189.         maxDate = states.get(states.size() - 1).getDate();
  190.         frame   = s0.getFrame();

  191.         final List<DoubleArrayDictionary.Entry> as = s0.getAdditionalStatesValues().getData();
  192.         additional = new String[as.size()];
  193.         for (int i = 0; i < additional.length; ++i) {
  194.             additional[i] = as.get(i).getKey();
  195.         }

  196.         this.statesCache       = new ImmutableTimeStampedCache<>(stateInterpolator.getNbInterpolationPoints(), states);
  197.         this.stateInterpolator = stateInterpolator;

  198.         this.covarianceInterpolator = covarianceInterpolator;
  199.         if (covarianceInterpolator != null) {
  200.             this.covariancesCache = new ImmutableTimeStampedCache<>(covarianceInterpolator.getNbInterpolationPoints(),
  201.                                                                     covariances);
  202.         } else {
  203.             this.covariancesCache = null;
  204.         }
  205.         this.statesAreOrbitDefined = s0.isOrbitDefined();

  206.         // Initialize initial state
  207.         super.resetInitialState(getInitialState());
  208.     }

  209.     /**
  210.      * Check input consistency between states, covariances and their associated interpolators.
  211.      *
  212.      * @param states spacecraft states sample
  213.      * @param stateInterpolator spacecraft state interpolator
  214.      * @param covariances covariances sample
  215.      * @param covarianceInterpolator covariance interpolator
  216.      */
  217.     public static void checkInputConsistency(final List<SpacecraftState> states,
  218.                                              final TimeInterpolator<SpacecraftState> stateInterpolator,
  219.                                              final List<StateCovariance> covariances,
  220.                                              final TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator) {
  221.         // Checks to perform is states are provided
  222.         if (!states.isEmpty()) {
  223.             // Check given that given states definition are consistent
  224.             // (all defined by either orbits or absolute position-velocity-acceleration coordinates)
  225.             SpacecraftStateInterpolator.checkStatesDefinitionsConsistency(states);

  226.             // Check that every interpolator used in the state interpolator are compatible with the sample size
  227.             AbstractTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(stateInterpolator, states.size());

  228.             // Additional checks if covariances are provided
  229.             if (!covariances.isEmpty()) {
  230.                 // Check that every interpolator used in the state covariance interpolator are compatible with the sample size
  231.                 AbstractTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(covarianceInterpolator,
  232.                                                                                       covariances.size());
  233.                 // Check states and covariances consistency
  234.                 checkStatesAndCovariancesConsistency(states, covariances);
  235.             }
  236.         }
  237.         else {
  238.             throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0);
  239.         }
  240.     }

  241.     /**
  242.      * Check that given states and covariances are consistent.
  243.      *
  244.      * @param states tabulates states to check
  245.      * @param covariances tabulated covariances associated to tabulated states to check
  246.      */
  247.     public static void checkStatesAndCovariancesConsistency(final List<SpacecraftState> states,
  248.                                                             final List<StateCovariance> covariances) {
  249.         final int nbStates = states.size();

  250.         // Check that we have an equal number of states and covariances
  251.         if (nbStates != covariances.size()) {
  252.             throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  253.                                                      states.size(),
  254.                                                      covariances.size());
  255.         }

  256.         // Check that states and covariance are defined at the same date
  257.         for (int i = 0; i < nbStates; i++) {
  258.             if (!states.get(i).getDate().isCloseTo(covariances.get(i).getDate(),
  259.                                                    TimeStampedPair.DEFAULT_DATE_EQUALITY_THRESHOLD)) {
  260.                 throw new OrekitIllegalStateException(OrekitMessages.STATE_AND_COVARIANCE_DATES_MISMATCH,
  261.                                                       states.get(i).getDate(), covariances.get(i).getDate());
  262.             }
  263.         }
  264.     }

  265.     /**
  266.      * Get the first date of the range.
  267.      *
  268.      * @return the first date of the range
  269.      */
  270.     public AbsoluteDate getMinDate() {
  271.         return minDate;
  272.     }

  273.     /**
  274.      * Get the last date of the range.
  275.      *
  276.      * @return the last date of the range
  277.      */
  278.     public AbsoluteDate getMaxDate() {
  279.         return maxDate;
  280.     }

  281.     /** {@inheritDoc} */
  282.     @Override
  283.     public Frame getFrame() {
  284.         return frame;
  285.     }

  286.     /**
  287.      * Get the covariance at given date.
  288.      * <p>
  289.      * BEWARE : If this instance has been created without sample of covariances and/or with spacecraft states defined with
  290.      * absolute position-velocity-acceleration, it will return an empty {@link Optional}.
  291.      *
  292.      * @param date date at which the covariance is desired
  293.      *
  294.      * @return covariance at given date
  295.      *
  296.      * @see Optional
  297.      */
  298.     public Optional<StateCovariance> getCovariance(final AbsoluteDate date) {
  299.         if (covarianceInterpolator != null && covariancesCache != null && statesAreOrbitDefined) {

  300.             // Build list of time stamped pair of orbits and their associated covariances
  301.             final List<TimeStampedPair<Orbit, StateCovariance>> sample = buildOrbitAndCovarianceSample();

  302.             // Interpolate
  303.             final TimeStampedPair<Orbit, StateCovariance> interpolatedOrbitAndCovariance =
  304.                     covarianceInterpolator.interpolate(date, sample);

  305.             return Optional.of(interpolatedOrbitAndCovariance.getSecond());
  306.         }
  307.         else {
  308.             return Optional.empty();
  309.         }

  310.     }

  311.     /** @return sample of orbits and their associated covariances */
  312.     private List<TimeStampedPair<Orbit, StateCovariance>> buildOrbitAndCovarianceSample() {
  313.         final List<TimeStampedPair<Orbit, StateCovariance>> sample      = new ArrayList<>();
  314.         final List<SpacecraftState>                         states      = statesCache.getAll();
  315.         final List<StateCovariance>                         covariances = covariancesCache.getAll();
  316.         for (int i = 0; i < states.size(); i++) {
  317.             sample.add(new TimeStampedPair<>(states.get(i).getOrbit(), covariances.get(i)));
  318.         }

  319.         return sample;
  320.     }

  321.     /** {@inheritDoc} */
  322.     @Override
  323.     public SpacecraftState basicPropagate(final AbsoluteDate date) {

  324.         final AbsoluteDate centralDate =
  325.                 AbstractTimeInterpolator.getCentralDate(date, statesCache, stateInterpolator.getExtrapolationThreshold());
  326.         final SpacecraftState  evaluatedState   = stateInterpolator.interpolate(date, statesCache.getNeighbors(centralDate));
  327.         final AttitudeProvider attitudeProvider = getAttitudeProvider();
  328.         if (attitudeProvider == null) {
  329.             return evaluatedState;
  330.         }
  331.         else {
  332.             final Attitude calculatedAttitude;
  333.             // Verify if orbit is defined
  334.             if (evaluatedState.isOrbitDefined()) {
  335.                 calculatedAttitude =
  336.                         attitudeProvider.getAttitude(evaluatedState.getOrbit(), date, evaluatedState.getFrame());
  337.                 return new SpacecraftState(evaluatedState.getOrbit(), calculatedAttitude, evaluatedState.getMass(),
  338.                                            evaluatedState.getAdditionalStatesValues(),
  339.                                            evaluatedState.getAdditionalStatesDerivatives());
  340.             }
  341.             else {
  342.                 calculatedAttitude =
  343.                         attitudeProvider.getAttitude(evaluatedState.getAbsPVA(), date, evaluatedState.getFrame());
  344.                 return new SpacecraftState(evaluatedState.getAbsPVA(), calculatedAttitude, evaluatedState.getMass(),
  345.                                            evaluatedState.getAdditionalStatesValues(),
  346.                                            evaluatedState.getAdditionalStatesDerivatives());
  347.             }

  348.         }
  349.     }

  350.     /** {@inheritDoc} */
  351.     protected Orbit propagateOrbit(final AbsoluteDate date) {
  352.         return basicPropagate(date).getOrbit();
  353.     }

  354.     /** {@inheritDoc} */
  355.     protected double getMass(final AbsoluteDate date) {
  356.         return basicPropagate(date).getMass();
  357.     }

  358.     /**
  359.      * Try (and fail) to reset the initial state.
  360.      * <p>
  361.      * This method always throws an exception, as ephemerides cannot be reset.
  362.      * </p>
  363.      *
  364.      * @param state new initial state to consider
  365.      */
  366.     public void resetInitialState(final SpacecraftState state) {
  367.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  368.     }

  369.     /** {@inheritDoc} */
  370.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
  371.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  372.     }

  373.     /** {@inheritDoc} */
  374.     public SpacecraftState getInitialState() {
  375.         return basicPropagate(getMinDate());
  376.     }

  377.     /** {@inheritDoc} */
  378.     @Override
  379.     public boolean isAdditionalStateManaged(final String name) {

  380.         // the additional state may be managed by a specific provider in the base class
  381.         if (super.isAdditionalStateManaged(name)) {
  382.             return true;
  383.         }

  384.         // the additional state may be managed in the states sample
  385.         for (final String a : additional) {
  386.             if (a.equals(name)) {
  387.                 return true;
  388.             }
  389.         }

  390.         return false;

  391.     }

  392.     /** {@inheritDoc} */
  393.     @Override
  394.     public String[] getManagedAdditionalStates() {
  395.         final String[] upperManaged = super.getManagedAdditionalStates();
  396.         final String[] managed      = new String[upperManaged.length + additional.length];
  397.         System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length);
  398.         System.arraycopy(additional, 0, managed, upperManaged.length, additional.length);
  399.         return managed;
  400.     }

  401.     /** {@inheritDoc} */
  402.     @Override
  403.     protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
  404.                                                         final DoubleArrayDictionary initialJacobianColumns) {
  405.         // In order to not throw an Orekit exception during ephemeris based orbit determination
  406.         // The default behavior of the method is overridden to return a null parameter
  407.         return null;
  408.     }

  409.     /** Get state interpolator.
  410.      * @return state interpolator
  411.      */
  412.     public TimeInterpolator<SpacecraftState> getStateInterpolator() {
  413.         return stateInterpolator;
  414.     }

  415.     /** Get covariance interpolator.
  416.      * @return optional covariance interpolator
  417.      * @see Optional
  418.      */
  419.     public Optional<TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>>> getCovarianceInterpolator() {
  420.         return Optional.ofNullable(covarianceInterpolator);
  421.     }

  422. }