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 /** 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 }