Ephemeris.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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 java.io.Serializable;
  19. import java.util.List;
  20. import java.util.Set;
  21. import java.util.stream.Collectors;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.util.FastMath;
  25. import org.orekit.attitudes.Attitude;
  26. import org.orekit.attitudes.AttitudeProvider;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.errors.OrekitMessages;
  30. import org.orekit.frames.Frame;
  31. import org.orekit.orbits.Orbit;
  32. import org.orekit.propagation.BoundedPropagator;
  33. import org.orekit.propagation.SpacecraftState;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.utils.ImmutableTimeStampedCache;
  36. import org.orekit.utils.PVCoordinatesProvider;
  37. import org.orekit.utils.TimeStampedPVCoordinates;

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

  47.     /** Default extrapolation time threshold: 1ms.
  48.      * @since 9.0
  49.      **/
  50.     public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3;

  51.     /** Serializable UID. */
  52.     private static final long serialVersionUID = 20170606L;

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

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

  57.     /** The extrapolation threshold beyond which the propagation will fail. **/
  58.     private final double extrapolationThreshold;

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

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

  63.     /** Local PV Provider used for computing attitude. **/
  64.     private LocalPVProvider pvProvider;

  65.     /** Thread-safe cache. */
  66.     private final transient ImmutableTimeStampedCache<SpacecraftState> cache;

  67.     /** Constructor with tabulated states.
  68.      * <p>
  69.      * This constructor allows extrapolating outside of the states time span
  70.      * by up to the 1ms {@link #DEFAULT_EXTRAPOLATION_THRESHOLD_SEC default
  71.      * extrapolation threshold}.
  72.      * </p>
  73.      * @param states tabulates states
  74.      * @param interpolationPoints number of points to use in interpolation
  75.      * @exception OrekitException if some states have incompatible additional states
  76.      * @exception MathIllegalArgumentException if the number of states is smaller than
  77.      * the number of points to use in interpolation
  78.      * @see #Ephemeris(List, int, double)
  79.      */
  80.     public Ephemeris(final List<SpacecraftState> states, final int interpolationPoints)
  81.         throws OrekitException, MathIllegalArgumentException {
  82.         this(states, interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC);
  83.     }

  84.     /** Constructor with tabulated states.
  85.      * @param states tabulates states
  86.      * @param interpolationPoints number of points to use in interpolation
  87.      * @param extrapolationThreshold the largest time difference in seconds between
  88.      * the start or stop boundary of the ephemeris bounds to be doing extrapolation
  89.      * @exception OrekitException if some states have incompatible additional states
  90.      * @exception MathIllegalArgumentException if the number of states is smaller than
  91.      * the number of points to use in interpolation
  92.      * @since 9.0
  93.      */
  94.     public Ephemeris(final List<SpacecraftState> states, final int interpolationPoints,
  95.                      final double extrapolationThreshold)
  96.         throws OrekitException, MathIllegalArgumentException {

  97.         super(DEFAULT_LAW);

  98.         if (states.size() < interpolationPoints) {
  99.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INSUFFICIENT_DIMENSION,
  100.                                                    states.size(), interpolationPoints);
  101.         }

  102.         final SpacecraftState s0 = states.get(0);
  103.         minDate = s0.getDate();
  104.         maxDate = states.get(states.size() - 1).getDate();
  105.         frame = s0.getFrame();

  106.         final Set<String> names0 = s0.getAdditionalStates().keySet();
  107.         additional = names0.toArray(new String[names0.size()]);

  108.         // check all states handle the same additional states
  109.         for (final SpacecraftState state : states) {
  110.             s0.ensureCompatibleAdditionalStates(state);
  111.         }

  112.         pvProvider = new LocalPVProvider();

  113.         // user needs to explicitly set attitude provider if they want to use one
  114.         setAttitudeProvider(null);

  115.         // set up cache
  116.         cache = new ImmutableTimeStampedCache<SpacecraftState>(interpolationPoints, states);

  117.         this.extrapolationThreshold = extrapolationThreshold;
  118.     }

  119.     /** Get the first date of the range.
  120.      * @return the first date of the range
  121.      */
  122.     public AbsoluteDate getMinDate() {
  123.         return minDate;
  124.     }

  125.     /** Get the last date of the range.
  126.      * @return the last date of the range
  127.      */
  128.     public AbsoluteDate getMaxDate() {
  129.         return maxDate;
  130.     }

  131.     /** Get the maximum timespan outside of the stored ephemeris that is allowed
  132.      * for extrapolation.
  133.      * @return the extrapolation threshold in seconds
  134.      */
  135.     public double getExtrapolationThreshold() {
  136.         return extrapolationThreshold;
  137.     }

  138.     @Override
  139.     public Frame getFrame() {
  140.         return frame;
  141.     }

  142.     @Override
  143.     /** {@inheritDoc} */
  144.     public SpacecraftState basicPropagate(final AbsoluteDate date) throws OrekitException {
  145.         final SpacecraftState evaluatedState;

  146.         final AbsoluteDate central;
  147.         if (date.compareTo(minDate) < 0 && FastMath.abs(date.durationFrom(minDate)) <= extrapolationThreshold) {
  148.             // avoid TimeStampedCacheException as we are still within the tolerance before minDate
  149.             central = minDate;
  150.         } else if (date.compareTo(maxDate) > 0 && FastMath.abs(date.durationFrom(maxDate)) <= extrapolationThreshold) {
  151.             // avoid TimeStampedCacheException as we are still within the tolerance after maxDate
  152.             central = maxDate;
  153.         } else {
  154.             central = date;
  155.         }
  156.         final List<SpacecraftState> neighbors = cache.getNeighbors(central).collect(Collectors.toList());
  157.         evaluatedState = neighbors.get(0).interpolate(date, neighbors);

  158.         final AttitudeProvider attitudeProvider = getAttitudeProvider();

  159.         if (attitudeProvider == null) {
  160.             return evaluatedState;
  161.         } else {
  162.             pvProvider.setCurrentState(evaluatedState);
  163.             final Attitude calculatedAttitude = attitudeProvider.getAttitude(pvProvider, date,
  164.                                                                              evaluatedState.getFrame());
  165.             return new SpacecraftState(evaluatedState.getOrbit(), calculatedAttitude, evaluatedState.getMass());
  166.         }
  167.     }

  168.     /** {@inheritDoc} */
  169.     protected Orbit propagateOrbit(final AbsoluteDate date) throws OrekitException {
  170.         return basicPropagate(date).getOrbit();
  171.     }

  172.     /** {@inheritDoc} */
  173.     protected double getMass(final AbsoluteDate date) throws OrekitException {
  174.         return basicPropagate(date).getMass();
  175.     }

  176.     /** {@inheritDoc} */
  177.     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f)
  178.         throws OrekitException {
  179.         return propagate(date).getPVCoordinates(f);
  180.     }

  181.     /** Try (and fail) to reset the initial state.
  182.      * <p>
  183.      * This method always throws an exception, as ephemerides cannot be reset.
  184.      * </p>
  185.      * @param state new initial state to consider
  186.      * @exception OrekitException always thrown as ephemerides cannot be reset
  187.      */
  188.     public void resetInitialState(final SpacecraftState state)
  189.         throws OrekitException {
  190.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  191.     }

  192.     /** {@inheritDoc} */
  193.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward)
  194.         throws OrekitException {
  195.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  196.     }

  197.     /** {@inheritDoc} */
  198.     public SpacecraftState getInitialState() throws OrekitException {
  199.         return basicPropagate(getMinDate());
  200.     }

  201.     /** {@inheritDoc} */
  202.     @Override
  203.     public boolean isAdditionalStateManaged(final String name) {

  204.         // the additional state may be managed by a specific provider in the base class
  205.         if (super.isAdditionalStateManaged(name)) {
  206.             return true;
  207.         }

  208.         // the additional state may be managed in the states sample
  209.         for (final String a : additional) {
  210.             if (a.equals(name)) {
  211.                 return true;
  212.             }
  213.         }

  214.         return false;

  215.     }

  216.     /** {@inheritDoc} */
  217.     @Override
  218.     public String[] getManagedAdditionalStates() {
  219.         final String[] upperManaged = super.getManagedAdditionalStates();
  220.         final String[] managed = new String[upperManaged.length + additional.length];
  221.         System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length);
  222.         System.arraycopy(additional, 0, managed, upperManaged.length, additional.length);
  223.         return managed;
  224.     }

  225.     /** Replace the instance with a data transfer object for serialization.
  226.      * <p>
  227.      * This intermediate class serializes only the data needed for generation,
  228.      * but does <em>not</em> serializes the cache itself (in fact the cache is
  229.      * not serializable).
  230.      * </p>
  231.      * @return data transfer object that will be serialized
  232.      */
  233.     private Object writeReplace() {
  234.         return new DataTransferObject(cache.getAll(), cache.getNeighborsSize(), extrapolationThreshold);
  235.     }

  236.     /** Internal class used only for serialization. */
  237.     private static class DataTransferObject implements Serializable {

  238.         /** Serializable UID. */
  239.         private static final long serialVersionUID = 20170606L;

  240.         /** Tabulates states. */
  241.         private final List<SpacecraftState> states;

  242.         /** Number of points to use in interpolation. */
  243.         private final int interpolationPoints;

  244.         /** The extrapolation threshold beyond which the propagation will fail. **/
  245.         private final double extrapolationThreshold;

  246.         /** Simple constructor.
  247.          * @param states tabulates states
  248.          * @param interpolationPoints number of points to use in interpolation
  249.          * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  250.          */
  251.         private DataTransferObject(final List<SpacecraftState> states, final int interpolationPoints,
  252.                                    final double extrapolationThreshold) {
  253.             this.states                 = states;
  254.             this.interpolationPoints    = interpolationPoints;
  255.             this.extrapolationThreshold = extrapolationThreshold;
  256.         }

  257.         /** Replace the deserialized data transfer object with a
  258.          * {@link Ephemeris}.
  259.          * @return replacement {@link Ephemeris}
  260.          */
  261.         private Object readResolve() {
  262.             try {
  263.                 // build a new provider, with an empty cache
  264.                 return new Ephemeris(states, interpolationPoints, extrapolationThreshold);
  265.             } catch (OrekitException oe) {
  266.                 // this should never happen
  267.                 throw new OrekitInternalError(oe);
  268.             }
  269.         }

  270.     }

  271.     /** Internal PVCoordinatesProvider for attitude computation. */
  272.     private static class LocalPVProvider implements PVCoordinatesProvider, Serializable {

  273.         /** Serializable UID. */
  274.         private static final long serialVersionUID = 20160115L;

  275.         /** Current state. */
  276.         private SpacecraftState currentState;

  277.         /** Get the current state.
  278.          * @return current state
  279.          */
  280.         public SpacecraftState getCurrentState() {
  281.             return currentState;
  282.         }

  283.         /** Set the current state.
  284.          * @param state state to set
  285.          */
  286.         public void setCurrentState(final SpacecraftState state) {
  287.             this.currentState = state;
  288.         }

  289.         /** {@inheritDoc} */
  290.         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f)
  291.             throws OrekitException {
  292.             final double dt = getCurrentState().getDate().durationFrom(date);
  293.             final double closeEnoughTimeInSec = 1e-9;

  294.             if (FastMath.abs(dt) > closeEnoughTimeInSec) {
  295.                 throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, FastMath.abs(dt), 0.0,
  296.                                           closeEnoughTimeInSec);
  297.             }

  298.             return getCurrentState().getPVCoordinates(f);

  299.         }

  300.     }

  301. }