1   /* Copyright 2002-2022 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  
19  import java.util.Arrays;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.hipparchus.ode.DenseOutputModel;
24  import org.hipparchus.ode.ODEStateAndDerivative;
25  import org.orekit.attitudes.AttitudeProvider;
26  import org.orekit.errors.OrekitException;
27  import org.orekit.errors.OrekitInternalError;
28  import org.orekit.errors.OrekitMessages;
29  import org.orekit.frames.Frame;
30  import org.orekit.orbits.Orbit;
31  import org.orekit.propagation.AdditionalStateProvider;
32  import org.orekit.propagation.BoundedPropagator;
33  import org.orekit.propagation.PropagationType;
34  import org.orekit.propagation.SpacecraftState;
35  import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.utils.DoubleArrayDictionary;
38  import org.orekit.utils.TimeStampedPVCoordinates;
39  
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  
73      /** Event detection requires evaluating the state slightly before / past an event. */
74      private static final double EXTRAPOLATION_TOLERANCE = 1.0;
75  
76      /** Mapper between raw double components and spacecraft state. */
77      private final StateMapper mapper;
78  
79      /** Type of orbit to output (mean or osculating).
80       * <p>
81       * This is used only in the case of semianalitical propagators where there is a clear separation between
82       * mean and short periodic elements. It is ignored by the Numerical propagator.
83       * </p>
84       */
85      private PropagationType type;
86  
87      /** Start date of the integration (can be min or max). */
88      private final AbsoluteDate startDate;
89  
90      /** First date of the range. */
91      private final AbsoluteDate minDate;
92  
93      /** Last date of the range. */
94      private final AbsoluteDate maxDate;
95  
96      /** Underlying raw mathematical model. */
97      private DenseOutputModel model;
98  
99      /** Unmanaged additional states that must be simply copied. */
100     private final DoubleArrayDictionary unmanaged;
101 
102     /** Creates a new instance of IntegratedEphemeris.
103      * @param startDate Start date of the integration (can be minDate or maxDate)
104      * @param minDate first date of the range
105      * @param maxDate last date of the range
106      * @param mapper mapper between raw double components and spacecraft state
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      * @deprecated as of 11.1.2, replaced by {@link #IntegratedEphemeris(AbsoluteDate,
113      * AbsoluteDate, AbsoluteDate, StateMapper, PropagationType, DenseOutputModel,
114      * DoubleArrayDictionary, List, String[], int[])}
115      */
116     @Deprecated
117     public IntegratedEphemeris(final AbsoluteDate startDate,
118                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
119                                final StateMapper mapper, final PropagationType type,
120                                final DenseOutputModel model,
121                                final Map<String, double[]> unmanaged,
122                                final List<AdditionalStateProvider> providers,
123                                final String[] equations) {
124         this(startDate, minDate, maxDate, mapper, type, model,
125              new DoubleArrayDictionary(unmanaged), providers, equations);
126     }
127 
128     /** Creates a new instance of IntegratedEphemeris.
129      * @param startDate Start date of the integration (can be minDate or maxDate)
130      * @param minDate first date of the range
131      * @param maxDate last date of the range
132      * @param mapper mapper between raw double components and spacecraft state
133      * @param type type of orbit to output (mean or osculating)
134      * @param model underlying raw mathematical model
135      * @param unmanaged unmanaged additional states that must be simply copied
136      * @param providers providers for pre-integrated states
137      * @param equations names of additional equations
138      * @since 11.1
139      * @deprecated as of 11.1.2, replaced by {@link #IntegratedEphemeris(AbsoluteDate,
140      * AbsoluteDate, AbsoluteDate, StateMapper, PropagationType, DenseOutputModel,
141      * DoubleArrayDictionary, List, String[], int[])}
142      */
143     @Deprecated
144     public IntegratedEphemeris(final AbsoluteDate startDate,
145                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
146                                final StateMapper mapper, final PropagationType type,
147                                final DenseOutputModel model,
148                                final DoubleArrayDictionary unmanaged,
149                                final List<AdditionalStateProvider> providers,
150                                final String[] equations) {
151         this(startDate, minDate, maxDate, mapper, type, model,
152              unmanaged, providers, equations,
153              remainingDimensions(model, unmanaged, providers, equations));
154     }
155 
156     /** Creates a new instance of IntegratedEphemeris.
157      * @param startDate Start date of the integration (can be minDate or maxDate)
158      * @param minDate first date of the range
159      * @param maxDate last date of the range
160      * @param mapper mapper between raw double components and spacecraft state
161      * @param type type of orbit to output (mean or osculating)
162      * @param model underlying raw mathematical model
163      * @param unmanaged unmanaged additional states that must be simply copied
164      * @param providers providers for pre-integrated states
165      * @param equations names of additional equations
166      * @param dimensions dimensions of additional equations
167      * @since 11.1.2
168      */
169     public IntegratedEphemeris(final AbsoluteDate startDate,
170                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
171                                final StateMapper mapper, final PropagationType type,
172                                final DenseOutputModel model,
173                                final DoubleArrayDictionary unmanaged,
174                                final List<AdditionalStateProvider> providers,
175                                final String[] equations, final int[] dimensions) {
176 
177         super(mapper.getAttitudeProvider());
178 
179         this.startDate = startDate;
180         this.minDate   = minDate;
181         this.maxDate   = maxDate;
182         this.mapper    = mapper;
183         this.type      = type;
184         this.model     = model;
185         this.unmanaged = unmanaged;
186 
187         // set up the pre-integrated providers
188         for (final AdditionalStateProvider provider : providers) {
189             addAdditionalStateProvider(provider);
190         }
191 
192         // set up providers to map the final elements of the model array to additional states
193         for (int i = 0; i < equations.length; ++i) {
194             addAdditionalStateProvider(new LocalGenerator(equations[i], i, dimensions[i]));
195         }
196 
197     }
198 
199     /** Compute remaining dimensions for additional equations.
200      * @param model underlying raw mathematical model
201      * @param unmanaged unmanaged additional states that must be simply copied
202      * @param providers providers for pre-integrated states
203      * @param equations names of additional equations
204      * @return dimensions of additional equations
205      * @deprecated as of 11.1.2 this method is temporary and should be removed
206      * when the calling constructors are removed
207      * @since 11.1.2
208      */
209     @Deprecated
210     private static int[] remainingDimensions(final DenseOutputModel model,
211                                              final DoubleArrayDictionary unmanaged,
212                                              final List<AdditionalStateProvider> providers,
213                                              final String[] equations) {
214         final ODEStateAndDerivative osd = model.getInterpolatedState(model.getInitialTime());
215         if (equations.length != osd.getNumberOfSecondaryStates()) {
216             throw new OrekitInternalError(null);
217         }
218         final int[] dimensions = new int[equations.length];
219         for (int i = 0; i < dimensions.length; ++i) {
220             dimensions[i] = osd.getSecondaryStateDimension(i + 1);
221         }
222         return dimensions;
223     }
224 
225     /** Interpolate the model at some date.
226      * @param date desired interpolation date
227      * @return state interpolated at date
228      */
229     private ODEStateAndDerivative getInterpolatedState(final AbsoluteDate date) {
230 
231         // compare using double precision instead of AbsoluteDate.compareTo(...)
232         // because time is expressed as a double when searching for events
233         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
234             // date is outside of supported range
235             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
236                     date, minDate, maxDate, minDate.durationFrom(date));
237         }
238         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
239             // date is outside of supported range
240             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
241                     date, minDate, maxDate, date.durationFrom(maxDate));
242         }
243 
244         return model.getInterpolatedState(date.durationFrom(startDate));
245 
246     }
247 
248     /** {@inheritDoc} */
249     @Override
250     protected SpacecraftState basicPropagate(final AbsoluteDate date) {
251         final ODEStateAndDerivative os = getInterpolatedState(date);
252         SpacecraftState state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
253                                                        os.getPrimaryState(), os.getPrimaryDerivative(),
254                                                        type);
255         for (DoubleArrayDictionary.Entry initial : unmanaged.getData()) {
256             state = state.addAdditionalState(initial.getKey(), initial.getValue());
257         }
258         return state;
259     }
260 
261     /** {@inheritDoc} */
262     protected Orbit propagateOrbit(final AbsoluteDate date) {
263         return basicPropagate(date).getOrbit();
264     }
265 
266     /** {@inheritDoc} */
267     protected double getMass(final AbsoluteDate date) {
268         return basicPropagate(date).getMass();
269     }
270 
271     /** {@inheritDoc} */
272     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
273         return propagate(date).getPVCoordinates(frame);
274     }
275 
276     /** Get the first date of the range.
277      * @return the first date of the range
278      */
279     public AbsoluteDate getMinDate() {
280         return minDate;
281     }
282 
283     /** Get the last date of the range.
284      * @return the last date of the range
285      */
286     public AbsoluteDate getMaxDate() {
287         return maxDate;
288     }
289 
290     @Override
291     public Frame getFrame() {
292         return this.mapper.getFrame();
293     }
294 
295     /** {@inheritDoc} */
296     public void resetInitialState(final SpacecraftState state) {
297         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
298     }
299 
300     /** {@inheritDoc} */
301     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
302         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
303     }
304 
305     /** {@inheritDoc} */
306     @Override
307     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
308         super.setAttitudeProvider(attitudeProvider);
309         if (mapper != null) {
310             // At the construction, the mapper is not set yet
311             // However, if the attitude provider is changed afterwards, it must be changed in the mapper too
312             mapper.setAttitudeProvider(attitudeProvider);
313         }
314     }
315 
316     /** {@inheritDoc} */
317     public SpacecraftState getInitialState() {
318         return updateAdditionalStates(basicPropagate(getMinDate()));
319     }
320 
321     /** Local generator for additional state data. */
322     private class LocalGenerator implements AdditionalStateProvider {
323 
324         /** Name of the additional state. */
325         private final String name;
326 
327         /** Index of the additional state. */
328         private final int index;
329 
330         /** Dimension of the additional state. */
331         private final int dimension;
332 
333         /** Simple constructor.
334          * @param name name of the additional state
335          * @param index index of the additional state
336          * @param dimension dimension of the additional state
337          */
338         LocalGenerator(final String name, final int index, final int dimension) {
339             this.name      = name;
340             this.index     = index;
341             this.dimension = dimension;
342         }
343 
344         /** {@inheritDoc} */
345         public String getName() {
346             return name;
347         }
348 
349         /** {@inheritDoc} */
350         public double[] getAdditionalState(final SpacecraftState state) {
351 
352             // extract the part of the interpolated array corresponding to the additional state
353             final double[] combined = getInterpolatedState(state.getDate()).getSecondaryState(1);
354             return Arrays.copyOfRange(combined, index, index + dimension);
355 
356         }
357 
358     }
359 
360 }