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&eacute;ro
72   * @author Luc Maisonobe
73   * @author V&eacute;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 }