1 /* Copyright 2002-2013 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.PVCoordinates;
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 = 20130613L;
80
81 /** Mapper between raw double components and spacecraft state. */
82 private final StateMapper mapper;
83
84 /** Start date of the integration (can be min or max). */
85 private final AbsoluteDate startDate;
86
87 /** First date of the range. */
88 private final AbsoluteDate minDate;
89
90 /** Last date of the range. */
91 private final AbsoluteDate maxDate;
92
93 /** Underlying raw mathematical model. */
94 private ContinuousOutputModel model;
95
96 /** Unmanaged additional states that must be simply copied. */
97 private final Map<String, double[]> unmanaged;
98
99 /** Creates a new instance of IntegratedEphemeris.
100 * @param startDate Start date of the integration (can be minDate or maxDate)
101 * @param minDate first date of the range
102 * @param maxDate last date of the range
103 * @param mapper mapper between raw double components and spacecraft state
104 * @param model underlying raw mathematical model
105 * @param unmanaged unmanaged additional states that must be simply copied
106 * @param providers providers for pre-integrated states
107 * @param equations names of additional equations
108 * @exception OrekitException if several providers have the same name
109 */
110 public IntegratedEphemeris(final AbsoluteDate startDate,
111 final AbsoluteDate minDate, final AbsoluteDate maxDate,
112 final StateMapper mapper, final ContinuousOutputModel model,
113 final Map<String, double[]> unmanaged,
114 final List<AdditionalStateProvider> providers,
115 final String[] equations)
116 throws OrekitException {
117
118 super(mapper.getAttitudeProvider());
119
120 this.startDate = startDate;
121 this.minDate = minDate;
122 this.maxDate = maxDate;
123 this.mapper = mapper;
124 this.model = model;
125 this.unmanaged = unmanaged;
126
127 // set up the pre-integrated providers
128 for (final AdditionalStateProvider provider : providers) {
129 addAdditionalStateProvider(provider);
130 }
131
132 // set up providers to map the final elements of the model array to additional states
133 for (int i = 0; i < equations.length; ++i) {
134 addAdditionalStateProvider(new LocalProvider(equations[i], i));
135 }
136
137 }
138
139 /** Set up the model at some interpolation date.
140 * @param date desired interpolation date
141 * @exception PropagationException if specified date is outside
142 * of supported range
143 */
144 private void setInterpolationDate(final AbsoluteDate date)
145 throws PropagationException {
146
147 if (date.equals(startDate.shiftedBy(model.getInterpolatedTime()))) {
148 // the current model date is already the desired one
149 return;
150 }
151
152 if ((date.compareTo(minDate) < 0) || (date.compareTo(maxDate) > 0)) {
153 // date is outside of supported range
154 throw new PropagationException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE,
155 date, minDate, maxDate);
156 }
157
158 // reset interpolation model to the desired date
159 model.setInterpolatedTime(date.durationFrom(startDate));
160
161 }
162
163 /** {@inheritDoc} */
164 @Override
165 protected SpacecraftState basicPropagate(final AbsoluteDate date)
166 throws PropagationException {
167 try {
168 setInterpolationDate(date);
169 SpacecraftState state = mapper.mapArrayToState(model.getInterpolatedTime(),
170 model.getInterpolatedState());
171 for (Map.Entry<String, double[]> initial : unmanaged.entrySet()) {
172 state = state.addAdditionalState(initial.getKey(), initial.getValue());
173 }
174 return state;
175 } catch (OrekitExceptionWrapper oew) {
176 if (oew.getException() instanceof PropagationException) {
177 throw (PropagationException) oew.getException();
178 } else {
179 throw new PropagationException(oew.getException());
180 }
181 } catch (OrekitException oe) {
182 if (oe instanceof PropagationException) {
183 throw (PropagationException) oe;
184 } else {
185 throw new PropagationException(oe);
186 }
187 }
188 }
189
190 /** {@inheritDoc} */
191 protected Orbit propagateOrbit(final AbsoluteDate date)
192 throws PropagationException {
193 return basicPropagate(date).getOrbit();
194 }
195
196 /** {@inheritDoc} */
197 protected double getMass(final AbsoluteDate date) throws PropagationException {
198 return basicPropagate(date).getMass();
199 }
200
201 /** {@inheritDoc} */
202 public PVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
203 throws OrekitException {
204 return propagate(date).getPVCoordinates(frame);
205 }
206
207 /** Get the first date of the range.
208 * @return the first date of the range
209 */
210 public AbsoluteDate getMinDate() {
211 return minDate;
212 }
213
214 /** Get the last date of the range.
215 * @return the last date of the range
216 */
217 public AbsoluteDate getMaxDate() {
218 return maxDate;
219 }
220
221 @Override
222 public Frame getFrame() {
223 return this.mapper.getFrame();
224 }
225
226 /** {@inheritDoc} */
227 public void resetInitialState(final SpacecraftState state)
228 throws PropagationException {
229 throw new PropagationException(OrekitMessages.NON_RESETABLE_STATE);
230 }
231
232 /** {@inheritDoc} */
233 public SpacecraftState getInitialState() throws PropagationException {
234 return updateAdditionalStates(basicPropagate(getMinDate()));
235 }
236
237 /** Replace the instance with a data transfer object for serialization.
238 * @return data transfer object that will be serialized
239 * @exception NotSerializableException if the state mapper cannot be serialized (typically for DSST propagator)
240 */
241 private Object writeReplace() throws NotSerializableException {
242
243 // unmanaged additional states
244 final String[] unmanagedNames = new String[unmanaged.size()];
245 final double[][] unmanagedValues = new double[unmanaged.size()][];
246 int i = 0;
247 for (Map.Entry<String, double[]> entry : unmanaged.entrySet()) {
248 unmanagedNames[i] = entry.getKey();
249 unmanagedValues[i] = entry.getValue();
250 ++i;
251 }
252
253 // managed states providers
254 final List<AdditionalStateProvider> serializableProviders = new ArrayList<AdditionalStateProvider>();
255 final List<String> equationNames = new ArrayList<String>();
256 for (final AdditionalStateProvider provider : getAdditionalStateProviders()) {
257 if (provider instanceof LocalProvider) {
258 equationNames.add(((LocalProvider) provider).getName());
259 } else if (provider instanceof Serializable) {
260 serializableProviders.add(provider);
261 }
262 }
263
264 return new DataTransferObject(startDate, minDate, maxDate, mapper, model,
265 unmanagedNames, unmanagedValues,
266 serializableProviders.toArray(new AdditionalStateProvider[serializableProviders.size()]),
267 equationNames.toArray(new String[equationNames.size()]));
268
269 }
270
271 /** Local provider for additional state data. */
272 private class LocalProvider implements AdditionalStateProvider {
273
274 /** Name of the additional state. */
275 private final String name;
276
277 /** Index of the additional state. */
278 private final int index;
279
280 /** Simple constructor.
281 * @param name name of the additional state
282 * @param index index of the additional state
283 */
284 public LocalProvider(final String name, final int index) {
285 this.name = name;
286 this.index = index;
287 }
288
289 /** {@inheritDoc} */
290 public String getName() {
291 return name;
292 }
293
294 /** {@inheritDoc} */
295 public double[] getAdditionalState(final SpacecraftState state)
296 throws PropagationException {
297
298 // set the model date
299 setInterpolationDate(state.getDate());
300
301 // extract the part of the interpolated array corresponding to the additional state
302 return model.getInterpolatedSecondaryState(index);
303
304 }
305
306 }
307
308 /** Internal class used only for serialization. */
309 private static class DataTransferObject implements Serializable {
310
311 /** Serializable UID. */
312 private static final long serialVersionUID = 20130621L;
313
314 /** Mapper between raw double components and spacecraft state. */
315 private final StateMapper mapper;
316
317 /** Start date of the integration (can be min or max). */
318 private final AbsoluteDate startDate;
319
320 /** First date of the range. */
321 private final AbsoluteDate minDate;
322
323 /** Last date of the range. */
324 private final AbsoluteDate maxDate;
325
326 /** Underlying raw mathematical model. */
327 private final ContinuousOutputModel model;
328
329 /** Names of unmanaged additional states that must be simply copied. */
330 private final String[] unmanagedNames;
331
332 /** Values of unmanaged additional states that must be simply copied. */
333 private final double[][] unmanagedValues;
334
335 /** Names of additional equations. */
336 private final String[] equations;
337
338 /** Providers for pre-integrated states. */
339 private final AdditionalStateProvider[] providers;
340
341 /** Simple constructor.
342 * @param startDate Start date of the integration (can be minDate or maxDate)
343 * @param minDate first date of the range
344 * @param maxDate last date of the range
345 * @param mapper mapper between raw double components and spacecraft state
346 * @param model underlying raw mathematical model
347 * @param unmanagedNames names of unmanaged additional states that must be simply copied
348 * @param unmanagedValues values of unmanaged additional states that must be simply copied
349 * @param providers providers for pre-integrated states
350 * @param equations names of additional equations
351 */
352 public DataTransferObject(final AbsoluteDate startDate,
353 final AbsoluteDate minDate, final AbsoluteDate maxDate,
354 final StateMapper mapper, final ContinuousOutputModel model,
355 final String[] unmanagedNames, final double[][] unmanagedValues,
356 final AdditionalStateProvider[] providers,
357 final String[] equations) {
358 this.startDate = startDate;
359 this.minDate = minDate;
360 this.maxDate = maxDate;
361 this.mapper = mapper;
362 this.model = model;
363 this.unmanagedNames = unmanagedNames;
364 this.unmanagedValues = unmanagedValues;
365 this.providers = providers;
366 this.equations = equations;
367 }
368
369 /** Replace the deserialized data transfer object with a {@link IntegratedEphemeris}.
370 * @return replacement {@link IntegratedEphemeris}
371 */
372 private Object readResolve() {
373 try {
374 final Map<String, double[]> unmanaged = new HashMap<String, double[]>(unmanagedNames.length);
375 for (int i = 0; i < unmanagedNames.length; ++i) {
376 unmanaged.put(unmanagedNames[i], unmanagedValues[i]);
377 }
378 return new IntegratedEphemeris(startDate, minDate, maxDate, mapper, model,
379 unmanaged, Arrays.asList(providers), equations);
380 } catch (OrekitException oe) {
381 throw OrekitException.createInternalError(oe);
382 }
383 }
384
385 }
386
387 }