IntegratedEphemeris.java

  1. /* Copyright 2002-2025 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.integration;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.ode.DenseOutputModel;
  21. import org.hipparchus.ode.ODEStateAndDerivative;
  22. import org.orekit.attitudes.AttitudeProvider;
  23. import org.orekit.attitudes.AttitudeProviderModifier;
  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.orbits.Orbit;
  28. import org.orekit.propagation.AdditionalDataProvider;
  29. import org.orekit.propagation.BoundedPropagator;
  30. import org.orekit.propagation.PropagationType;
  31. import org.orekit.propagation.SpacecraftState;
  32. import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
  33. import org.orekit.propagation.events.EventDetector;
  34. import org.orekit.propagation.events.FieldEventDetector;
  35. import org.orekit.time.AbsoluteDate;
  36. import org.orekit.utils.DataDictionary;

  37. import java.util.Arrays;
  38. import java.util.List;
  39. import java.util.stream.Stream;

  40. /** This class stores sequentially generated orbital parameters for
  41.  * later retrieval.
  42.  *
  43.  * <p>
  44.  * Instances of this class are built automatically when the {@link
  45.  * org.orekit.propagation.Propagator#getEphemerisGenerator()
  46.  * getEphemerisGenerator} method has been called. They are created when propagation is over.
  47.  * Random access to any intermediate state of the orbit throughout the propagation range is
  48.  * possible afterwards through this object.
  49.  * </p>
  50.  * <p>
  51.  * A typical use case is for numerically integrated orbits, which can be used by
  52.  * algorithms that need to wander around according to their own algorithm without
  53.  * cumbersome tight links with the integrator.
  54.  * </p>
  55.  * <p>
  56.  * As this class implements the {@link org.orekit.propagation.Propagator Propagator}
  57.  * interface, it can itself be used in batch mode to build another instance of the
  58.  * same type. This is however not recommended since it would be a waste of resources.
  59.  * </p>
  60.  * <p>
  61.  * Note that this class stores all intermediate states along with interpolation
  62.  * models, so it may be memory intensive.
  63.  * </p>
  64.  *
  65.  * @see org.orekit.propagation.numerical.NumericalPropagator
  66.  * @author Mathieu Rom&eacute;ro
  67.  * @author Luc Maisonobe
  68.  * @author V&eacute;ronique Pommier-Maurussane
  69.  */
  70. public class IntegratedEphemeris
  71.     extends AbstractAnalyticalPropagator implements BoundedPropagator {

  72.     /** Event detection requires evaluating the state slightly before / past an event. */
  73.     private static final double EXTRAPOLATION_TOLERANCE = 1.0;

  74.     /** Mapper between raw double components and spacecraft state. */
  75.     private final StateMapper mapper;

  76.     /** Type of orbit to output (mean or osculating).
  77.      * <p>
  78.      * This is used only in the case of semianalitical propagators where there is a clear separation between
  79.      * mean and short periodic elements. It is ignored by the Numerical propagator.
  80.      * </p>
  81.      */
  82.     private final PropagationType type;

  83.     /** Start date of the integration (can be min or max). */
  84.     private final AbsoluteDate startDate;

  85.     /** First date of the range. */
  86.     private final AbsoluteDate minDate;

  87.     /** Last date of the range. */
  88.     private final AbsoluteDate maxDate;

  89.     /** Underlying raw mathematical model. */
  90.     private final DenseOutputModel model;

  91.     /** Unmanaged additional states that must be simply copied. */
  92.     private final DataDictionary unmanaged;

  93.     /** Names of additional equations.
  94.      * @since 11.2
  95.      */
  96.     private final String[] equations;

  97.     /** Dimensions of additional equations.
  98.      * @since 11.2
  99.      */
  100.     private final int[] dimensions;

  101.     /** Creates a new instance of IntegratedEphemeris.
  102.      * @param startDate Start date of the integration (can be minDate or maxDate)
  103.      * @param minDate first date of the range
  104.      * @param maxDate last date of the range
  105.      * @param mapper mapper between raw double components and spacecraft state
  106.      * @param attitudeProvider attitude provider
  107.      * @param type type of orbit to output (mean or osculating)
  108.      * @param model underlying raw mathematical model
  109.      * @param unmanaged unmanaged additional states that must be simply copied
  110.      * @param providers providers for pre-integrated states
  111.      * @param equations names of additional equations
  112.      * @param dimensions dimensions of additional equations
  113.      * @since 13.0
  114.      */
  115.     public IntegratedEphemeris(final AbsoluteDate startDate,
  116.                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
  117.                                final StateMapper mapper, final AttitudeProvider attitudeProvider,
  118.                                final PropagationType type, final DenseOutputModel model,
  119.                                final DataDictionary unmanaged,
  120.                                final List<AdditionalDataProvider<?>> providers,
  121.                                final String[] equations, final int[] dimensions) {

  122.         super(attitudeProvider);

  123.         this.startDate = startDate;
  124.         this.minDate   = minDate;
  125.         this.maxDate   = maxDate;
  126.         this.mapper    = mapper;
  127.         this.type      = type;
  128.         this.model     = model;
  129.         this.unmanaged = unmanaged;

  130.         // set up the pre-integrated providers
  131.         for (final AdditionalDataProvider<?> provider : providers) {
  132.             addAdditionalDataProvider(provider);
  133.         }

  134.         this.equations  = equations.clone();
  135.         this.dimensions = dimensions.clone();

  136.         // set up initial state
  137.         super.resetInitialState(getInitialState());

  138.         // remove event detectors in attitude provider
  139.         setAttitudeProvider(new AttitudeProviderModifier() {
  140.             @Override
  141.             public AttitudeProvider getUnderlyingAttitudeProvider() {
  142.                 return attitudeProvider;
  143.             }

  144.             @Override
  145.             public Stream<EventDetector> getEventDetectors() {
  146.                 return Stream.of();
  147.             }

  148.             @Override
  149.             public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
  150.                 return Stream.of();
  151.             }
  152.         });
  153.     }

  154.     /** Interpolate the model at some date.
  155.      * @param date desired interpolation date
  156.      * @return state interpolated at date
  157.      */
  158.     private ODEStateAndDerivative getInterpolatedState(final AbsoluteDate date) {

  159.         // compare using double precision instead of AbsoluteDate.compareTo(...)
  160.         // because time is expressed as a double when searching for events
  161.         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
  162.             // date is outside of supported range
  163.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
  164.                     date, minDate, maxDate, minDate.durationFrom(date));
  165.         }
  166.         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
  167.             // date is outside of supported range
  168.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
  169.                     date, minDate, maxDate, date.durationFrom(maxDate));
  170.         }

  171.         return model.getInterpolatedState(date.durationFrom(startDate));

  172.     }

  173.     /** {@inheritDoc} */
  174.     @Override
  175.     public SpacecraftState basicPropagate(final AbsoluteDate date) {
  176.         final ODEStateAndDerivative os = getInterpolatedState(date);
  177.         SpacecraftState state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
  178.                                                        os.getPrimaryState(), os.getPrimaryDerivative(),
  179.                                                        type);
  180.         for (DataDictionary.Entry initial : unmanaged.getData()) {
  181.             state = state.addAdditionalData(initial.getKey(), initial.getValue());
  182.         }
  183.         return state;
  184.     }

  185.     /** {@inheritDoc} */
  186.     public Orbit propagateOrbit(final AbsoluteDate date) {
  187.         return basicPropagate(date).getOrbit();
  188.     }

  189.     /** {@inheritDoc} */
  190.     protected double getMass(final AbsoluteDate date) {
  191.         return basicPropagate(date).getMass();
  192.     }

  193.     /** Get the first date of the range.
  194.      * @return the first date of the range
  195.      */
  196.     public AbsoluteDate getMinDate() {
  197.         return minDate;
  198.     }

  199.     /** Get the last date of the range.
  200.      * @return the last date of the range
  201.      */
  202.     public AbsoluteDate getMaxDate() {
  203.         return maxDate;
  204.     }

  205.     @Override
  206.     public Frame getFrame() {
  207.         return this.mapper.getFrame();
  208.     }

  209.     /** {@inheritDoc} */
  210.     @Override
  211.     public void resetInitialState(final SpacecraftState state) {
  212.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  213.     }

  214.     /** {@inheritDoc} */
  215.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
  216.         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
  217.     }

  218.     /** {@inheritDoc} */
  219.     @Override
  220.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  221.         super.setAttitudeProvider(attitudeProvider);
  222.         if (mapper != null) {
  223.             // At the construction, the mapper is not set yet
  224.             // However, if the attitude provider is changed afterwards, it must be changed in the mapper too
  225.             mapper.setAttitudeProvider(attitudeProvider);
  226.         }
  227.     }

  228.     /** {@inheritDoc} */
  229.     @Override
  230.     public SpacecraftState getInitialState() {
  231.         return updateAdditionalData(basicPropagate(getMinDate()));
  232.     }

  233.     /** {@inheritDoc} */
  234.     @Override
  235.     public SpacecraftState updateAdditionalData(final SpacecraftState original) {

  236.         SpacecraftState updated = super.updateAdditionalData(original);

  237.         if (equations.length > 0) {
  238.             final ODEStateAndDerivative osd                = getInterpolatedState(updated.getDate());
  239.             final double[]              combinedState      = osd.getSecondaryState(1);
  240.             final double[]              combinedDerivative = osd.getSecondaryDerivative(1);
  241.             int index = 0;
  242.             for (int i = 0; i < equations.length; ++i) {
  243.                 final double[] state      = Arrays.copyOfRange(combinedState,      index, index + dimensions[i]);
  244.                 final double[] derivative = Arrays.copyOfRange(combinedDerivative, index, index + dimensions[i]);
  245.                 updated = updated.
  246.                           addAdditionalData(equations[i], state).
  247.                           addAdditionalStateDerivative(equations[i], derivative);
  248.                 index += dimensions[i];
  249.             }
  250.         }

  251.         return updated;

  252.     }

  253. }