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.Collections;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.ode.FieldDenseOutputModel;
25  import org.hipparchus.ode.FieldODEStateAndDerivative;
26  import org.orekit.errors.OrekitException;
27  import org.orekit.errors.OrekitMessages;
28  import org.orekit.frames.Frame;
29  import org.orekit.orbits.FieldOrbit;
30  import org.orekit.propagation.FieldAdditionalStateProvider;
31  import org.orekit.propagation.FieldBoundedPropagator;
32  import org.orekit.propagation.FieldSpacecraftState;
33  import org.orekit.propagation.PropagationType;
34  import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator;
35  import org.orekit.time.FieldAbsoluteDate;
36  import org.orekit.utils.FieldArrayDictionary;
37  import org.orekit.utils.ParameterDriver;
38  import org.orekit.utils.TimeStampedFieldPVCoordinates;
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.FieldPropagator#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 FieldIntegratedEphemeris <T extends CalculusFieldElement<T>>
71      extends FieldAbstractAnalyticalPropagator<T> implements FieldBoundedPropagator<T> {
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 FieldStateMapper<T> 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 FieldAbsoluteDate<T> startDate;
89  
90      /** First date of the range. */
91      private final FieldAbsoluteDate<T> minDate;
92  
93      /** Last date of the range. */
94      private final FieldAbsoluteDate<T> maxDate;
95  
96      /** Underlying raw mathematical model. */
97      private FieldDenseOutputModel<T> model;
98  
99      /** Unmanaged additional states that must be simply copied. */
100     private final FieldArrayDictionary<T> 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, replaced by {@link #FieldIntegratedEphemeris(FieldAbsoluteDate,
113      * FieldAbsoluteDate, FieldAbsoluteDate, FieldStateMapper, PropagationType,
114      * FieldDenseOutputModel, FieldArrayDictionary, List, String[])}
115      */
116     @Deprecated
117     public FieldIntegratedEphemeris(final FieldAbsoluteDate<T> startDate,
118                                final FieldAbsoluteDate<T> minDate, final FieldAbsoluteDate<T> maxDate,
119                                final FieldStateMapper<T> mapper, final PropagationType type,
120                                final FieldDenseOutputModel<T> model,
121                                final Map<String, T[]> unmanaged,
122                                final List<org.orekit.propagation.FieldAdditionalStateProvider<T>> providers,
123                                final String[] equations) {
124         this(startDate, minDate, maxDate, mapper, type, model,
125              new FieldArrayDictionary<>(startDate.getField(), unmanaged),
126              providers, equations);
127     }
128 
129     /** Creates a new instance of IntegratedEphemeris.
130      * @param startDate Start date of the integration (can be minDate or maxDate)
131      * @param minDate first date of the range
132      * @param maxDate last date of the range
133      * @param mapper mapper between raw double components and spacecraft state
134      * @param type type of orbit to output (mean or osculating)
135      * @param model underlying raw mathematical model
136      * @param unmanaged unmanaged additional states that must be simply copied
137      * @param providers generators for pre-integrated states
138      * @param equations names of additional equations
139      * @since 11.1
140      */
141     public FieldIntegratedEphemeris(final FieldAbsoluteDate<T> startDate,
142                                     final FieldAbsoluteDate<T> minDate, final FieldAbsoluteDate<T> maxDate,
143                                     final FieldStateMapper<T> mapper, final PropagationType type,
144                                     final FieldDenseOutputModel<T> model,
145                                     final FieldArrayDictionary<T> unmanaged,
146                                     final List<FieldAdditionalStateProvider<T>> providers,
147                                     final String[] equations) {
148 
149         super(startDate.getField(), mapper.getAttitudeProvider());
150 
151         this.startDate = startDate;
152         this.minDate   = minDate;
153         this.maxDate   = maxDate;
154         this.mapper    = mapper;
155         this.type      = type;
156         this.model     = model;
157         this.unmanaged = unmanaged;
158 
159         // set up the pre-integrated providers
160         for (final FieldAdditionalStateProvider<T> provider : providers) {
161             addAdditionalStateProvider(provider);
162         }
163 
164         // set up providers to map the final elements of the model array to additional states
165         for (int i = 0; i < equations.length; ++i) {
166             addAdditionalStateProvider(new LocalGenerator(equations[i], i));
167         }
168 
169     }
170 
171     /** Interpolate the model at some date.
172      * @param date desired interpolation date
173      * @return state interpolated at date
174           * of supported range
175      */
176     private FieldODEStateAndDerivative<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
177 
178         // compare using double precision instead of FieldAbsoluteDate<T>.compareTo(...)
179         // because time is expressed as a double when searching for events
180         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
181             // date is outside of supported range
182             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
183                     date, minDate, maxDate, minDate.durationFrom(date).getReal());
184         }
185         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
186             // date is outside of supported range
187             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
188                     date, minDate, maxDate, date.durationFrom(maxDate).getReal());
189         }
190 
191         return model.getInterpolatedState(date.durationFrom(startDate));
192 
193     }
194 
195     /** {@inheritDoc} */
196     @Override
197     protected FieldSpacecraftState<T> basicPropagate(final FieldAbsoluteDate<T> date) {
198         final FieldODEStateAndDerivative<T> os = getInterpolatedState(date);
199         FieldSpacecraftState<T> state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
200                                                                os.getPrimaryState(), os.getPrimaryDerivative(),
201                                                                type);
202         for (FieldArrayDictionary<T>.Entry initial : unmanaged.getData()) {
203             state = state.addAdditionalState(initial.getKey(), initial.getValue());
204         }
205         return state;
206     }
207 
208     /** {@inheritDoc} */
209     @Override
210     protected FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date, final T[] parameters) {
211         return basicPropagate(date).getOrbit();
212     }
213 
214     /** {@inheritDoc} */
215     @Override
216     protected T getMass(final FieldAbsoluteDate<T> date) {
217         return basicPropagate(date).getMass();
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date, final Frame frame) {
223         return propagate(date).getPVCoordinates(frame);
224     }
225 
226     /** Get the first date of the range.
227      * @return the first date of the range
228      */
229     @Override
230     public FieldAbsoluteDate<T> getMinDate() {
231         return minDate;
232     }
233 
234     /** Get the last date of the range.
235      * @return the last date of the range
236      */
237     @Override
238     public FieldAbsoluteDate<T> getMaxDate() {
239         return maxDate;
240     }
241 
242     @Override
243     public Frame getFrame() {
244         return this.mapper.getFrame();
245     }
246 
247     /** {@inheritDoc} */
248     @Override
249     public void resetInitialState(final FieldSpacecraftState<T> state) {
250         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
251     }
252 
253     /** {@inheritDoc} */
254     @Override
255     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
256         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
257     }
258 
259     /** {@inheritDoc} */
260     @Override
261     public FieldSpacecraftState<T> getInitialState() {
262         return updateAdditionalStates(basicPropagate(getMinDate()));
263     }
264 
265     /** Local generator for additional state data. */
266     private class LocalGenerator implements FieldAdditionalStateProvider<T> {
267 
268         /** Name of the additional state. */
269         private final String name;
270 
271         /** Index of the additional state. */
272         private final int index;
273 
274         /** Simple constructor.
275          * @param name name of the additional state
276          * @param index index of the additional state
277          */
278         LocalGenerator(final String name, final int index) {
279             this.name  = name;
280             this.index = index;
281         }
282 
283         /** {@inheritDoc} */
284         @Override
285         public String getName() {
286             return name;
287         }
288 
289         /** {@inheritDoc} */
290         @Override
291         public T[] getAdditionalState(final FieldSpacecraftState<T> state) {
292 
293             // extract the part of the interpolated array corresponding to the additional state
294             return getInterpolatedState(state.getDate()).getSecondaryState(index + 1);
295 
296         }
297 
298     }
299 
300     /** {@inheritDoc} */
301     @Override
302     protected List<ParameterDriver> getParametersDrivers() {
303         // Integrated Ephemeris propagation model does not have parameter drivers.
304         return Collections.emptyList();
305     }
306 
307 }