Ephemeris.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.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 java.util.ArrayList;
  41. import java.util.List;
  42. import java.util.Optional;

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

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

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

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

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

  61.     /** List of spacecraft states. */
  62.     private final transient List<SpacecraftState> states;

  63.     /** List of covariances. **/
  64.     private final transient List<StateCovariance> covariances;

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

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

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

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

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

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

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

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

  182.         super(attitudeProvider);

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

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

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

  195.         this.states            = states;
  196.         this.stateInterpolator = stateInterpolator;

  197.         this.covariances            = covariances;
  198.         this.covarianceInterpolator = covarianceInterpolator;

  199.         this.statesAreOrbitDefined = s0.isOrbitDefined();

  200.         // Initialize initial state
  201.         super.resetInitialState(getInitialState());
  202.     }

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

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

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

  235.     /**
  236.      * Check that given states and covariances are consistent.
  237.      *
  238.      * @param states tabulates states to check
  239.      * @param covariances tabulated covariances associated to tabulated states to check
  240.      */
  241.     public static void checkStatesAndCovariancesConsistency(final List<SpacecraftState> states,
  242.                                                             final List<StateCovariance> covariances) {
  243.         final int nbStates = states.size();

  244.         // Check that we have an equal number of states and covariances
  245.         if (nbStates != covariances.size()) {
  246.             throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  247.                                                      states.size(),
  248.                                                      covariances.size());
  249.         }

  250.         // Check that states and covariance are defined at the same date
  251.         for (int i = 0; i < nbStates; i++) {
  252.             if (!states.get(i).getDate().isCloseTo(covariances.get(i).getDate(),
  253.                                                    TimeStampedPair.DEFAULT_DATE_EQUALITY_THRESHOLD)) {
  254.                 throw new OrekitIllegalStateException(OrekitMessages.STATE_AND_COVARIANCE_DATES_MISMATCH,
  255.                                                       states.get(i).getDate(), covariances.get(i).getDate());
  256.             }
  257.         }
  258.     }

  259.     /**
  260.      * Get the first date of the range.
  261.      *
  262.      * @return the first date of the range
  263.      */
  264.     public AbsoluteDate getMinDate() {
  265.         return minDate;
  266.     }

  267.     /**
  268.      * Get the last date of the range.
  269.      *
  270.      * @return the last date of the range
  271.      */
  272.     public AbsoluteDate getMaxDate() {
  273.         return maxDate;
  274.     }

  275.     /** {@inheritDoc} */
  276.     @Override
  277.     public Frame getFrame() {
  278.         return frame;
  279.     }

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

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

  296.             // Interpolate
  297.             final TimeStampedPair<Orbit, StateCovariance> interpolatedOrbitAndCovariance =
  298.                     covarianceInterpolator.interpolate(date, sample);

  299.             return Optional.of(interpolatedOrbitAndCovariance.getSecond());
  300.         }
  301.         else {
  302.             return Optional.empty();
  303.         }

  304.     }

  305.     /** @return sample of orbits and their associated covariances */
  306.     private List<TimeStampedPair<Orbit, StateCovariance>> buildOrbitAndCovarianceSample() {
  307.         final List<TimeStampedPair<Orbit, StateCovariance>> sample = new ArrayList<>();

  308.         for (int i = 0; i < states.size(); i++) {
  309.             sample.add(new TimeStampedPair<>(states.get(i).getOrbit(), covariances.get(i)));
  310.         }

  311.         return sample;
  312.     }

  313.     /** {@inheritDoc} */
  314.     @Override
  315.     public SpacecraftState basicPropagate(final AbsoluteDate date) {

  316.         final SpacecraftState  evaluatedState   = stateInterpolator.interpolate(date, states);
  317.         final AttitudeProvider attitudeProvider = getAttitudeProvider();
  318.         if (attitudeProvider == null) {
  319.             return evaluatedState;
  320.         }
  321.         else {
  322.             final Attitude calculatedAttitude;
  323.             // Verify if orbit is defined
  324.             if (evaluatedState.isOrbitDefined()) {
  325.                 calculatedAttitude =
  326.                         attitudeProvider.getAttitude(evaluatedState.getOrbit(), date, evaluatedState.getFrame());
  327.                 return new SpacecraftState(evaluatedState.getOrbit(), calculatedAttitude, evaluatedState.getMass(),
  328.                                            evaluatedState.getAdditionalStatesValues(),
  329.                                            evaluatedState.getAdditionalStatesDerivatives());
  330.             }
  331.             else {
  332.                 calculatedAttitude =
  333.                         attitudeProvider.getAttitude(evaluatedState.getAbsPVA(), date, evaluatedState.getFrame());
  334.                 return new SpacecraftState(evaluatedState.getAbsPVA(), calculatedAttitude, evaluatedState.getMass(),
  335.                                            evaluatedState.getAdditionalStatesValues(),
  336.                                            evaluatedState.getAdditionalStatesDerivatives());
  337.             }

  338.         }
  339.     }

  340.     /** {@inheritDoc} */
  341.     protected Orbit propagateOrbit(final AbsoluteDate date) {
  342.         return basicPropagate(date).getOrbit();
  343.     }

  344.     /** {@inheritDoc} */
  345.     protected double getMass(final AbsoluteDate date) {
  346.         return basicPropagate(date).getMass();
  347.     }

  348.     /**
  349.      * Try (and fail) to reset the initial state.
  350.      * <p>
  351.      * This method always throws an exception, as ephemerides cannot be reset.
  352.      * </p>
  353.      *
  354.      * @param state new initial state to consider
  355.      */
  356.     public void resetInitialState(final SpacecraftState state) {
  357.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  358.     }

  359.     /** {@inheritDoc} */
  360.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
  361.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  362.     }

  363.     /** {@inheritDoc} */
  364.     public SpacecraftState getInitialState() {
  365.         return basicPropagate(getMinDate());
  366.     }

  367.     /** {@inheritDoc} */
  368.     @Override
  369.     public boolean isAdditionalStateManaged(final String name) {

  370.         // the additional state may be managed by a specific provider in the base class
  371.         if (super.isAdditionalStateManaged(name)) {
  372.             return true;
  373.         }

  374.         // the additional state may be managed in the states sample
  375.         for (final String a : additional) {
  376.             if (a.equals(name)) {
  377.                 return true;
  378.             }
  379.         }

  380.         return false;

  381.     }

  382.     /** {@inheritDoc} */
  383.     @Override
  384.     public String[] getManagedAdditionalStates() {
  385.         final String[] upperManaged = super.getManagedAdditionalStates();
  386.         final String[] managed      = new String[upperManaged.length + additional.length];
  387.         System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length);
  388.         System.arraycopy(additional, 0, managed, upperManaged.length, additional.length);
  389.         return managed;
  390.     }

  391.     /** {@inheritDoc} */
  392.     @Override
  393.     protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
  394.                                                         final DoubleArrayDictionary initialJacobianColumns) {
  395.         // In order to not throw an Orekit exception during ephemeris based orbit determination
  396.         // The default behavior of the method is overridden to return a null parameter
  397.         return null;
  398.     }

  399.     /** Get state interpolator.
  400.      * @return state interpolator
  401.      */
  402.     public TimeInterpolator<SpacecraftState> getStateInterpolator() {
  403.         return stateInterpolator;
  404.     }

  405.     /** Get covariance interpolator.
  406.      * @return optional covariance interpolator
  407.      * @see Optional
  408.      */
  409.     public Optional<TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>>> getCovarianceInterpolator() {
  410.         return Optional.ofNullable(covarianceInterpolator);
  411.     }

  412. }