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éro
67 * @author Luc Maisonobe
68 * @author Vé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 }