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     /** Names of additional equations.
103      * @since 11.2
104      */
105     private final String[] equations;
106 
107     /** Dimensions of additional equations.
108      * @since 11.2
109      */
110     private final int[] dimensions;
111 
112     /** Creates a new instance of IntegratedEphemeris.
113      * @param startDate Start date of the integration (can be minDate or maxDate)
114      * @param minDate first date of the range
115      * @param maxDate last date of the range
116      * @param mapper mapper between raw double components and spacecraft state
117      * @param type type of orbit to output (mean or osculating)
118      * @param model underlying raw mathematical model
119      * @param unmanaged unmanaged additional states that must be simply copied
120      * @param providers providers for pre-integrated states
121      * @param equations names of additional equations
122      * @deprecated as of 11.1.2, replaced by {@link #IntegratedEphemeris(AbsoluteDate,
123      * AbsoluteDate, AbsoluteDate, StateMapper, PropagationType, DenseOutputModel,
124      * DoubleArrayDictionary, List, String[], int[])}
125      */
126     @Deprecated
127     public IntegratedEphemeris(final AbsoluteDate startDate,
128                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
129                                final StateMapper mapper, final PropagationType type,
130                                final DenseOutputModel model,
131                                final Map<String, double[]> unmanaged,
132                                final List<AdditionalStateProvider> providers,
133                                final String[] equations) {
134         this(startDate, minDate, maxDate, mapper, type, model,
135              new DoubleArrayDictionary(unmanaged), providers, equations);
136     }
137 
138     /** Creates a new instance of IntegratedEphemeris.
139      * @param startDate Start date of the integration (can be minDate or maxDate)
140      * @param minDate first date of the range
141      * @param maxDate last date of the range
142      * @param mapper mapper between raw double components and spacecraft state
143      * @param type type of orbit to output (mean or osculating)
144      * @param model underlying raw mathematical model
145      * @param unmanaged unmanaged additional states that must be simply copied
146      * @param providers providers for pre-integrated states
147      * @param equations names of additional equations
148      * @since 11.1
149      * @deprecated as of 11.1.2, replaced by {@link #IntegratedEphemeris(AbsoluteDate,
150      * AbsoluteDate, AbsoluteDate, StateMapper, PropagationType, DenseOutputModel,
151      * DoubleArrayDictionary, List, String[], int[])}
152      */
153     @Deprecated
154     public IntegratedEphemeris(final AbsoluteDate startDate,
155                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
156                                final StateMapper mapper, final PropagationType type,
157                                final DenseOutputModel model,
158                                final DoubleArrayDictionary unmanaged,
159                                final List<AdditionalStateProvider> providers,
160                                final String[] equations) {
161         this(startDate, minDate, maxDate, mapper, type, model,
162              unmanaged, providers, equations,
163              remainingDimensions(model, unmanaged, providers, equations));
164     }
165 
166     /** Creates a new instance of IntegratedEphemeris.
167      * @param startDate Start date of the integration (can be minDate or maxDate)
168      * @param minDate first date of the range
169      * @param maxDate last date of the range
170      * @param mapper mapper between raw double components and spacecraft state
171      * @param type type of orbit to output (mean or osculating)
172      * @param model underlying raw mathematical model
173      * @param unmanaged unmanaged additional states that must be simply copied
174      * @param providers providers for pre-integrated states
175      * @param equations names of additional equations
176      * @param dimensions dimensions of additional equations
177      * @since 11.1.2
178      */
179     public IntegratedEphemeris(final AbsoluteDate startDate,
180                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
181                                final StateMapper mapper, final PropagationType type,
182                                final DenseOutputModel model,
183                                final DoubleArrayDictionary unmanaged,
184                                final List<AdditionalStateProvider> providers,
185                                final String[] equations, final int[] dimensions) {
186 
187         super(mapper.getAttitudeProvider());
188 
189         this.startDate = startDate;
190         this.minDate   = minDate;
191         this.maxDate   = maxDate;
192         this.mapper    = mapper;
193         this.type      = type;
194         this.model     = model;
195         this.unmanaged = unmanaged;
196 
197         // set up the pre-integrated providers
198         for (final AdditionalStateProvider provider : providers) {
199             addAdditionalStateProvider(provider);
200         }
201 
202         this.equations  = equations.clone();
203         this.dimensions = dimensions.clone();
204 
205     }
206 
207     /** Compute remaining dimensions for additional equations.
208      * @param model underlying raw mathematical model
209      * @param unmanaged unmanaged additional states that must be simply copied
210      * @param providers providers for pre-integrated states
211      * @param equations names of additional equations
212      * @return dimensions of additional equations
213      * @deprecated as of 11.1.2 this method is temporary and should be removed
214      * when the calling constructors are removed
215      * @since 11.1.2
216      */
217     @Deprecated
218     private static int[] remainingDimensions(final DenseOutputModel model,
219                                              final DoubleArrayDictionary unmanaged,
220                                              final List<AdditionalStateProvider> providers,
221                                              final String[] equations) {
222         final ODEStateAndDerivative osd = model.getInterpolatedState(model.getInitialTime());
223         if (equations.length != osd.getNumberOfSecondaryStates()) {
224             throw new OrekitInternalError(null);
225         }
226         final int[] dimensions = new int[equations.length];
227         for (int i = 0; i < dimensions.length; ++i) {
228             dimensions[i] = osd.getSecondaryStateDimension(i + 1);
229         }
230         return dimensions;
231     }
232 
233     /** Interpolate the model at some date.
234      * @param date desired interpolation date
235      * @return state interpolated at date
236      */
237     private ODEStateAndDerivative getInterpolatedState(final AbsoluteDate date) {
238 
239         // compare using double precision instead of AbsoluteDate.compareTo(...)
240         // because time is expressed as a double when searching for events
241         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
242             // date is outside of supported range
243             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
244                     date, minDate, maxDate, minDate.durationFrom(date));
245         }
246         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
247             // date is outside of supported range
248             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
249                     date, minDate, maxDate, date.durationFrom(maxDate));
250         }
251 
252         return model.getInterpolatedState(date.durationFrom(startDate));
253 
254     }
255 
256     /** {@inheritDoc} */
257     @Override
258     protected SpacecraftState basicPropagate(final AbsoluteDate date) {
259         final ODEStateAndDerivative os = getInterpolatedState(date);
260         SpacecraftState state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
261                                                        os.getPrimaryState(), os.getPrimaryDerivative(),
262                                                        type);
263         for (DoubleArrayDictionary.Entry initial : unmanaged.getData()) {
264             state = state.addAdditionalState(initial.getKey(), initial.getValue());
265         }
266         return state;
267     }
268 
269     /** {@inheritDoc} */
270     protected Orbit propagateOrbit(final AbsoluteDate date) {
271         return basicPropagate(date).getOrbit();
272     }
273 
274     /** {@inheritDoc} */
275     protected double getMass(final AbsoluteDate date) {
276         return basicPropagate(date).getMass();
277     }
278 
279     /** {@inheritDoc} */
280     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
281         return propagate(date).getPVCoordinates(frame);
282     }
283 
284     /** Get the first date of the range.
285      * @return the first date of the range
286      */
287     public AbsoluteDate getMinDate() {
288         return minDate;
289     }
290 
291     /** Get the last date of the range.
292      * @return the last date of the range
293      */
294     public AbsoluteDate getMaxDate() {
295         return maxDate;
296     }
297 
298     @Override
299     public Frame getFrame() {
300         return this.mapper.getFrame();
301     }
302 
303     /** {@inheritDoc} */
304     public void resetInitialState(final SpacecraftState state) {
305         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
306     }
307 
308     /** {@inheritDoc} */
309     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
310         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
311     }
312 
313     /** {@inheritDoc} */
314     @Override
315     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
316         super.setAttitudeProvider(attitudeProvider);
317         if (mapper != null) {
318             // At the construction, the mapper is not set yet
319             // However, if the attitude provider is changed afterwards, it must be changed in the mapper too
320             mapper.setAttitudeProvider(attitudeProvider);
321         }
322     }
323 
324     /** {@inheritDoc} */
325     public SpacecraftState getInitialState() {
326         return updateAdditionalStates(basicPropagate(getMinDate()));
327     }
328 
329     /** {@inheritDoc} */
330     @Override
331     protected SpacecraftState updateAdditionalStates(final SpacecraftState original) {
332 
333         SpacecraftState updated = super.updateAdditionalStates(original);
334 
335         if (equations.length > 0) {
336             final ODEStateAndDerivative osd                = getInterpolatedState(updated.getDate());
337             final double[]              combinedState      = osd.getSecondaryState(1);
338             final double[]              combinedDerivative = osd.getSecondaryDerivative(1);
339             int index = 0;
340             for (int i = 0; i < equations.length; ++i) {
341                 final double[] state      = Arrays.copyOfRange(combinedState,      index, index + dimensions[i]);
342                 final double[] derivative = Arrays.copyOfRange(combinedDerivative, index, index + dimensions[i]);
343                 updated = updated.
344                           addAdditionalState(equations[i], state).
345                           addAdditionalStateDerivative(equations[i], derivative);
346                 index += dimensions[i];
347             }
348         }
349 
350         return updated;
351 
352     }
353 
354 }