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éro
64 * @author Luc Maisonobe
65 * @author Vé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 }