1   /* Copyright 2002-2021 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.List;
20  import java.util.Map;
21  
22  import org.hipparchus.ode.DenseOutputModel;
23  import org.hipparchus.ode.ODEStateAndDerivative;
24  import org.orekit.attitudes.AttitudeProvider;
25  import org.orekit.errors.OrekitException;
26  import org.orekit.errors.OrekitMessages;
27  import org.orekit.frames.Frame;
28  import org.orekit.orbits.Orbit;
29  import org.orekit.propagation.AdditionalStateProvider;
30  import org.orekit.propagation.BoundedPropagator;
31  import org.orekit.propagation.PropagationType;
32  import org.orekit.propagation.SpacecraftState;
33  import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.utils.TimeStampedPVCoordinates;
36  
37  /** This class stores sequentially generated orbital parameters for
38   * later retrieval.
39   *
40   * <p>
41   * Instances of this class are built automatically when the {@link
42   * org.orekit.propagation.Propagator#getEphemerisGenerator()
43   * getEphemerisGenerator} method has been called. They are created when propagation is over.
44   * Random access to any intermediate state of the orbit throughout the propagation range is
45   * possible afterwards through this object.
46   * </p>
47   * <p>
48   * A typical use case is for numerically integrated orbits, which can be used by
49   * algorithms that need to wander around according to their own algorithm without
50   * cumbersome tight links with the integrator.
51   * </p>
52   * <p>
53   * As this class implements the {@link org.orekit.propagation.Propagator Propagator}
54   * interface, it can itself be used in batch mode to build another instance of the
55   * same type. This is however not recommended since it would be a waste of resources.
56   * </p>
57   * <p>
58   * Note that this class stores all intermediate states along with interpolation
59   * models, so it may be memory intensive.
60   * </p>
61   *
62   * @see org.orekit.propagation.numerical.NumericalPropagator
63   * @author Mathieu Rom&eacute;ro
64   * @author Luc Maisonobe
65   * @author V&eacute;ronique Pommier-Maurussane
66   */
67  public class IntegratedEphemeris
68      extends AbstractAnalyticalPropagator implements BoundedPropagator {
69  
70      /** Event detection requires evaluating the state slightly before / past an event. */
71      private static final double EXTRAPOLATION_TOLERANCE = 1.0;
72  
73      /** Mapper between raw double components and spacecraft state. */
74      private final StateMapper mapper;
75  
76      /** Type of orbit to output (mean or osculating).
77       * <p>
78       * This is used only in the case of semianalitical propagators where there is a clear separation between
79       * mean and short periodic elements. It is ignored by the Numerical propagator.
80       * </p>
81       */
82      private PropagationType type;
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 DenseOutputModel 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 type type of orbit to output (mean or osculating)
105      * @param model underlying raw mathematical model
106      * @param unmanaged unmanaged additional states that must be simply copied
107      * @param providers providers for pre-integrated states
108      * @param equations names of additional equations
109      */
110     public IntegratedEphemeris(final AbsoluteDate startDate,
111                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
112                                final StateMapper mapper, final PropagationType type,
113                                final DenseOutputModel model,
114                                final Map<String, double[]> unmanaged,
115                                final List<AdditionalStateProvider> providers,
116                                final String[] equations) {
117 
118         super(mapper.getAttitudeProvider());
119 
120         this.startDate = startDate;
121         this.minDate   = minDate;
122         this.maxDate   = maxDate;
123         this.mapper    = mapper;
124         this.type      = type;
125         this.model     = model;
126         this.unmanaged = unmanaged;
127 
128         // set up the pre-integrated providers
129         for (final AdditionalStateProvider provider : providers) {
130             addAdditionalStateProvider(provider);
131         }
132 
133         // set up providers to map the final elements of the model array to additional states
134         for (int i = 0; i < equations.length; ++i) {
135             addAdditionalStateProvider(new LocalProvider(equations[i], i));
136         }
137 
138     }
139 
140     /** Interpolate the model at some date.
141      * @param date desired interpolation date
142      * @return state interpolated at date
143      */
144     private ODEStateAndDerivative getInterpolatedState(final AbsoluteDate date) {
145 
146         // compare using double precision instead of AbsoluteDate.compareTo(...)
147         // because time is expressed as a double when searching for events
148         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
149             // date is outside of supported range
150             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
151                     date, minDate, maxDate, minDate.durationFrom(date));
152         }
153         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
154             // date is outside of supported range
155             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
156                     date, minDate, maxDate, date.durationFrom(maxDate));
157         }
158 
159         return model.getInterpolatedState(date.durationFrom(startDate));
160 
161     }
162 
163     /** {@inheritDoc} */
164     @Override
165     protected SpacecraftState basicPropagate(final AbsoluteDate date) {
166         final ODEStateAndDerivative os = getInterpolatedState(date);
167         SpacecraftState state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
168                                                        os.getPrimaryState(), os.getPrimaryDerivative(),
169                                                        type);
170         for (Map.Entry<String, double[]> initial : unmanaged.entrySet()) {
171             state = state.addAdditionalState(initial.getKey(), initial.getValue());
172         }
173         return state;
174     }
175 
176     /** {@inheritDoc} */
177     protected Orbit propagateOrbit(final AbsoluteDate date) {
178         return basicPropagate(date).getOrbit();
179     }
180 
181     /** {@inheritDoc} */
182     protected double getMass(final AbsoluteDate date) {
183         return basicPropagate(date).getMass();
184     }
185 
186     /** {@inheritDoc} */
187     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
188         return propagate(date).getPVCoordinates(frame);
189     }
190 
191     /** Get the first date of the range.
192      * @return the first date of the range
193      */
194     public AbsoluteDate getMinDate() {
195         return minDate;
196     }
197 
198     /** Get the last date of the range.
199      * @return the last date of the range
200      */
201     public AbsoluteDate getMaxDate() {
202         return maxDate;
203     }
204 
205     @Override
206     public Frame getFrame() {
207         return this.mapper.getFrame();
208     }
209 
210     /** {@inheritDoc} */
211     public void resetInitialState(final SpacecraftState state) {
212         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
213     }
214 
215     /** {@inheritDoc} */
216     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
217         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
223         super.setAttitudeProvider(attitudeProvider);
224         if (mapper != null) {
225             // At the construction, the mapper is not set yet
226             // However, if the attitude provider is changed afterwards, it must be changed in the mapper too
227             mapper.setAttitudeProvider(attitudeProvider);
228         }
229     }
230 
231     /** {@inheritDoc} */
232     public SpacecraftState getInitialState() {
233         return updateAdditionalStates(basicPropagate(getMinDate()));
234     }
235 
236     /** Local provider for additional state data. */
237     private class LocalProvider implements AdditionalStateProvider {
238 
239         /** Name of the additional state. */
240         private final String name;
241 
242         /** Index of the additional state. */
243         private final int index;
244 
245         /** Simple constructor.
246          * @param name name of the additional state
247          * @param index index of the additional state
248          */
249         LocalProvider(final String name, final int index) {
250             this.name  = name;
251             this.index = index;
252         }
253 
254         /** {@inheritDoc} */
255         public String getName() {
256             return name;
257         }
258 
259         /** {@inheritDoc} */
260         public double[] getAdditionalState(final SpacecraftState state) {
261 
262             // extract the part of the interpolated array corresponding to the additional state
263             return getInterpolatedState(state.getDate()).getSecondaryState(index + 1);
264 
265         }
266 
267     }
268 
269 }