1 /* Copyright 2002-2015 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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.io.NotSerializableException;
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.apache.commons.math3.ode.ContinuousOutputModel;
28 import org.orekit.errors.OrekitException;
29 import org.orekit.errors.OrekitExceptionWrapper;
30 import org.orekit.errors.OrekitMessages;
31 import org.orekit.errors.PropagationException;
32 import org.orekit.frames.Frame;
33 import org.orekit.orbits.Orbit;
34 import org.orekit.propagation.AdditionalStateProvider;
35 import org.orekit.propagation.BoundedPropagator;
36 import org.orekit.propagation.SpacecraftState;
37 import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
38 import org.orekit.time.AbsoluteDate;
39 import org.orekit.utils.TimeStampedPVCoordinates;
40
41 /** This class stores sequentially generated orbital parameters for
42 * later retrieval.
43 *
44 * <p>
45 * Instances of this class are built and then must be fed with the results
46 * provided by {@link org.orekit.propagation.Propagator Propagator} objects
47 * configured in {@link org.orekit.propagation.Propagator#setEphemerisMode()
48 * ephemeris generation mode}. Once propagation is o, random access to any
49 * intermediate state of the orbit throughout the propagation range is possible.
50 * </p>
51 * <p>
52 * A typical use case is for numerically integrated orbits, which can be used by
53 * algorithms that need to wander around according to their own algorithm without
54 * cumbersome tight links with the integrator.
55 * </p>
56 * <p>
57 * Another use case is persistence, as this class is one of the few propagators
58 * to be serializable.
59 * </p>
60 * <p>
61 * As this class implements the {@link org.orekit.propagation.Propagator Propagator}
62 * interface, it can itself be used in batch mode to build another instance of the
63 * same type. This is however not recommended since it would be a waste of resources.
64 * </p>
65 * <p>
66 * Note that this class stores all intermediate states along with interpolation
67 * models, so it may be memory intensive.
68 * </p>
69 *
70 * @see org.orekit.propagation.numerical.NumericalPropagator
71 * @author Mathieu Roméro
72 * @author Luc Maisonobe
73 * @author Véronique Pommier-Maurussane
74 */
75 public class IntegratedEphemeris
76 extends AbstractAnalyticalPropagator implements BoundedPropagator, Serializable {
77
78 /** Serializable UID. */
79 private static final long serialVersionUID = 20140213L;
80
81 /** Mapper between raw double components and spacecraft state. */
82 private final StateMapper mapper;
83
84 /** Output only the mean orbit. <br/>
85 * <p>
86 * This is used only in the case of semianalitical propagators where there is a clear separation between
87 * mean and short periodic elements. It is ignored by the Numerical propagator.
88 * </p>
89 */
90 private boolean meanOrbit;
91
92 /** Start date of the integration (can be min or max). */
93 private final AbsoluteDate startDate;
94
95 /** First date of the range. */
96 private final AbsoluteDate minDate;
97
98 /** Last date of the range. */
99 private final AbsoluteDate maxDate;
100
101 /** Underlying raw mathematical model. */
102 private ContinuousOutputModel model;
103
104 /** Unmanaged additional states that must be simply copied. */
105 private final Map<String, double[]> unmanaged;
106
107 /** Creates a new instance of IntegratedEphemeris.
108 * @param startDate Start date of the integration (can be minDate or maxDate)
109 * @param minDate first date of the range
110 * @param maxDate last date of the range
111 * @param mapper mapper between raw double components and spacecraft state
112 * @param meanOrbit output only the mean orbit
113 * @param model underlying raw mathematical model
114 * @param unmanaged unmanaged additional states that must be simply copied
115 * @param providers providers for pre-integrated states
116 * @param equations names of additional equations
117 * @exception OrekitException if several providers have the same name
118 */
119 public IntegratedEphemeris(final AbsoluteDate startDate,
120 final AbsoluteDate minDate, final AbsoluteDate maxDate,
121 final StateMapper mapper, final boolean meanOrbit,
122 final ContinuousOutputModel model,
123 final Map<String, double[]> unmanaged,
124 final List<AdditionalStateProvider> providers,
125 final String[] equations)
126 throws OrekitException {
127
128 super(mapper.getAttitudeProvider());
129
130 this.startDate = startDate;
131 this.minDate = minDate;
132 this.maxDate = maxDate;
133 this.mapper = mapper;
134 this.meanOrbit = meanOrbit;
135 this.model = model;
136 this.unmanaged = unmanaged;
137
138 // set up the pre-integrated providers
139 for (final AdditionalStateProvider provider : providers) {
140 addAdditionalStateProvider(provider);
141 }
142
143 // set up providers to map the final elements of the model array to additional states
144 for (int i = 0; i < equations.length; ++i) {
145 addAdditionalStateProvider(new LocalProvider(equations[i], i));
146 }
147
148 }
149
150 /** Set up the model at some interpolation date.
151 * @param date desired interpolation date
152 * @exception PropagationException if specified date is outside
153 * of supported range
154 */
155 private void setInterpolationDate(final AbsoluteDate date)
156 throws PropagationException {
157
158 if (date.equals(startDate.shiftedBy(model.getInterpolatedTime()))) {
159 // the current model date is already the desired one
160 return;
161 }
162
163 if ((date.compareTo(minDate) < 0) || (date.compareTo(maxDate) > 0)) {
164 // date is outside of supported range
165 throw new PropagationException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE,
166 date, minDate, maxDate);
167 }
168
169 // reset interpolation model to the desired date
170 model.setInterpolatedTime(date.durationFrom(startDate));
171
172 }
173
174 /** {@inheritDoc} */
175 @Override
176 protected SpacecraftState basicPropagate(final AbsoluteDate date)
177 throws PropagationException {
178 try {
179 setInterpolationDate(date);
180 SpacecraftState state = mapper.mapArrayToState(model.getInterpolatedTime(),
181 model.getInterpolatedState(),
182 meanOrbit);
183 for (Map.Entry<String, double[]> initial : unmanaged.entrySet()) {
184 state = state.addAdditionalState(initial.getKey(), initial.getValue());
185 }
186 return state;
187 } catch (OrekitExceptionWrapper oew) {
188 if (oew.getException() instanceof PropagationException) {
189 throw (PropagationException) oew.getException();
190 } else {
191 throw new PropagationException(oew.getException());
192 }
193 } catch (OrekitException oe) {
194 if (oe instanceof PropagationException) {
195 throw (PropagationException) oe;
196 } else {
197 throw new PropagationException(oe);
198 }
199 }
200 }
201
202 /** {@inheritDoc} */
203 protected Orbit propagateOrbit(final AbsoluteDate date)
204 throws PropagationException {
205 return basicPropagate(date).getOrbit();
206 }
207
208 /** {@inheritDoc} */
209 protected double getMass(final AbsoluteDate date) throws PropagationException {
210 return basicPropagate(date).getMass();
211 }
212
213 /** {@inheritDoc} */
214 public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
215 throws OrekitException {
216 return propagate(date).getPVCoordinates(frame);
217 }
218
219 /** Get the first date of the range.
220 * @return the first date of the range
221 */
222 public AbsoluteDate getMinDate() {
223 return minDate;
224 }
225
226 /** Get the last date of the range.
227 * @return the last date of the range
228 */
229 public AbsoluteDate getMaxDate() {
230 return maxDate;
231 }
232
233 @Override
234 public Frame getFrame() {
235 return this.mapper.getFrame();
236 }
237
238 /** {@inheritDoc} */
239 public void resetInitialState(final SpacecraftState state)
240 throws PropagationException {
241 throw new PropagationException(OrekitMessages.NON_RESETABLE_STATE);
242 }
243
244 /** {@inheritDoc} */
245 public SpacecraftState getInitialState() throws PropagationException {
246 return updateAdditionalStates(basicPropagate(getMinDate()));
247 }
248
249 /** Replace the instance with a data transfer object for serialization.
250 * @return data transfer object that will be serialized
251 * @exception NotSerializableException if the state mapper cannot be serialized (typically for DSST propagator)
252 */
253 private Object writeReplace() throws NotSerializableException {
254
255 // unmanaged additional states
256 final String[] unmanagedNames = new String[unmanaged.size()];
257 final double[][] unmanagedValues = new double[unmanaged.size()][];
258 int i = 0;
259 for (Map.Entry<String, double[]> entry : unmanaged.entrySet()) {
260 unmanagedNames[i] = entry.getKey();
261 unmanagedValues[i] = entry.getValue();
262 ++i;
263 }
264
265 // managed states providers
266 final List<AdditionalStateProvider> serializableProviders = new ArrayList<AdditionalStateProvider>();
267 final List<String> equationNames = new ArrayList<String>();
268 for (final AdditionalStateProvider provider : getAdditionalStateProviders()) {
269 if (provider instanceof LocalProvider) {
270 equationNames.add(((LocalProvider) provider).getName());
271 } else if (provider instanceof Serializable) {
272 serializableProviders.add(provider);
273 }
274 }
275
276 return new DataTransferObject(startDate, minDate, maxDate, mapper, meanOrbit, model,
277 unmanagedNames, unmanagedValues,
278 serializableProviders.toArray(new AdditionalStateProvider[serializableProviders.size()]),
279 equationNames.toArray(new String[equationNames.size()]));
280
281 }
282
283 /** Local provider for additional state data. */
284 private class LocalProvider implements AdditionalStateProvider {
285
286 /** Name of the additional state. */
287 private final String name;
288
289 /** Index of the additional state. */
290 private final int index;
291
292 /** Simple constructor.
293 * @param name name of the additional state
294 * @param index index of the additional state
295 */
296 public LocalProvider(final String name, final int index) {
297 this.name = name;
298 this.index = index;
299 }
300
301 /** {@inheritDoc} */
302 public String getName() {
303 return name;
304 }
305
306 /** {@inheritDoc} */
307 public double[] getAdditionalState(final SpacecraftState state)
308 throws PropagationException {
309
310 // set the model date
311 setInterpolationDate(state.getDate());
312
313 // extract the part of the interpolated array corresponding to the additional state
314 return model.getInterpolatedSecondaryState(index);
315
316 }
317
318 }
319
320 /** Internal class used only for serialization. */
321 private static class DataTransferObject implements Serializable {
322
323 /** Serializable UID. */
324 private static final long serialVersionUID = 20140213L;
325
326 /** Mapper between raw double components and spacecraft state. */
327 private final StateMapper mapper;
328
329 /** Indicator for mean orbit output. */
330 private final boolean meanOrbit;
331
332 /** Start date of the integration (can be min or max). */
333 private final AbsoluteDate startDate;
334
335 /** First date of the range. */
336 private final AbsoluteDate minDate;
337
338 /** Last date of the range. */
339 private final AbsoluteDate maxDate;
340
341 /** Underlying raw mathematical model. */
342 private final ContinuousOutputModel model;
343
344 /** Names of unmanaged additional states that must be simply copied. */
345 private final String[] unmanagedNames;
346
347 /** Values of unmanaged additional states that must be simply copied. */
348 private final double[][] unmanagedValues;
349
350 /** Names of additional equations. */
351 private final String[] equations;
352
353 /** Providers for pre-integrated states. */
354 private final AdditionalStateProvider[] providers;
355
356 /** Simple constructor.
357 * @param startDate Start date of the integration (can be minDate or maxDate)
358 * @param minDate first date of the range
359 * @param maxDate last date of the range
360 * @param mapper mapper between raw double components and spacecraft state
361 * @param meanOrbit output only the mean orbit.
362 * @param model underlying raw mathematical model
363 * @param unmanagedNames names of unmanaged additional states that must be simply copied
364 * @param unmanagedValues values of unmanaged additional states that must be simply copied
365 * @param providers providers for pre-integrated states
366 * @param equations names of additional equations
367 */
368 public DataTransferObject(final AbsoluteDate startDate,
369 final AbsoluteDate minDate, final AbsoluteDate maxDate,
370 final StateMapper mapper, final boolean meanOrbit,
371 final ContinuousOutputModel model,
372 final String[] unmanagedNames, final double[][] unmanagedValues,
373 final AdditionalStateProvider[] providers,
374 final String[] equations) {
375 this.startDate = startDate;
376 this.minDate = minDate;
377 this.maxDate = maxDate;
378 this.mapper = mapper;
379 this.meanOrbit = meanOrbit;
380 this.model = model;
381 this.unmanagedNames = unmanagedNames;
382 this.unmanagedValues = unmanagedValues;
383 this.providers = providers;
384 this.equations = equations;
385 }
386
387 /** Replace the deserialized data transfer object with a {@link IntegratedEphemeris}.
388 * @return replacement {@link IntegratedEphemeris}
389 */
390 private Object readResolve() {
391 try {
392 final Map<String, double[]> unmanaged = new HashMap<String, double[]>(unmanagedNames.length);
393 for (int i = 0; i < unmanagedNames.length; ++i) {
394 unmanaged.put(unmanagedNames[i], unmanagedValues[i]);
395 }
396 return new IntegratedEphemeris(startDate, minDate, maxDate, mapper, meanOrbit, model,
397 unmanaged, Arrays.asList(providers), equations);
398 } catch (OrekitException oe) {
399 throw OrekitException.createInternalError(oe);
400 }
401 }
402
403 }
404
405 }