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&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 = 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 }