1 /* Copyright 2002-2024 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.Arrays;
20 import java.util.List;
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.DoubleArrayDictionary;
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 DoubleArrayDictionary unmanaged;
98
99 /** Names of additional equations.
100 * @since 11.2
101 */
102 private final String[] equations;
103
104 /** Dimensions of additional equations.
105 * @since 11.2
106 */
107 private final int[] dimensions;
108
109 /** Creates a new instance of IntegratedEphemeris.
110 * @param startDate Start date of the integration (can be minDate or maxDate)
111 * @param minDate first date of the range
112 * @param maxDate last date of the range
113 * @param mapper mapper between raw double components and spacecraft state
114 * @param type type of orbit to output (mean or osculating)
115 * @param model underlying raw mathematical model
116 * @param unmanaged unmanaged additional states that must be simply copied
117 * @param providers providers for pre-integrated states
118 * @param equations names of additional equations
119 * @param dimensions dimensions of additional equations
120 * @since 11.1.2
121 */
122 public IntegratedEphemeris(final AbsoluteDate startDate,
123 final AbsoluteDate minDate, final AbsoluteDate maxDate,
124 final StateMapper mapper, final PropagationType type,
125 final DenseOutputModel model,
126 final DoubleArrayDictionary unmanaged,
127 final List<AdditionalStateProvider> providers,
128 final String[] equations, final int[] dimensions) {
129
130 super(mapper.getAttitudeProvider());
131
132 this.startDate = startDate;
133 this.minDate = minDate;
134 this.maxDate = maxDate;
135 this.mapper = mapper;
136 this.type = type;
137 this.model = model;
138 this.unmanaged = unmanaged;
139
140 // set up the pre-integrated providers
141 for (final AdditionalStateProvider provider : providers) {
142 addAdditionalStateProvider(provider);
143 }
144
145 this.equations = equations.clone();
146 this.dimensions = dimensions.clone();
147
148 // set up initial state
149 super.resetInitialState(getInitialState());
150
151 }
152
153 /** Interpolate the model at some date.
154 * @param date desired interpolation date
155 * @return state interpolated at date
156 */
157 private ODEStateAndDerivative getInterpolatedState(final AbsoluteDate date) {
158
159 // compare using double precision instead of AbsoluteDate.compareTo(...)
160 // because time is expressed as a double when searching for events
161 if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
162 // date is outside of supported range
163 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
164 date, minDate, maxDate, minDate.durationFrom(date));
165 }
166 if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
167 // date is outside of supported range
168 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
169 date, minDate, maxDate, date.durationFrom(maxDate));
170 }
171
172 return model.getInterpolatedState(date.durationFrom(startDate));
173
174 }
175
176 /** {@inheritDoc} */
177 @Override
178 protected SpacecraftState basicPropagate(final AbsoluteDate date) {
179 final ODEStateAndDerivative os = getInterpolatedState(date);
180 SpacecraftState state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
181 os.getPrimaryState(), os.getPrimaryDerivative(),
182 type);
183 for (DoubleArrayDictionary.Entry initial : unmanaged.getData()) {
184 state = state.addAdditionalState(initial.getKey(), initial.getValue());
185 }
186 return state;
187 }
188
189 /** {@inheritDoc} */
190 protected Orbit propagateOrbit(final AbsoluteDate date) {
191 return basicPropagate(date).getOrbit();
192 }
193
194 /** {@inheritDoc} */
195 protected double getMass(final AbsoluteDate date) {
196 return basicPropagate(date).getMass();
197 }
198
199 /** Get the first date of the range.
200 * @return the first date of the range
201 */
202 public AbsoluteDate getMinDate() {
203 return minDate;
204 }
205
206 /** Get the last date of the range.
207 * @return the last date of the range
208 */
209 public AbsoluteDate getMaxDate() {
210 return maxDate;
211 }
212
213 @Override
214 public Frame getFrame() {
215 return this.mapper.getFrame();
216 }
217
218 /** {@inheritDoc} */
219 public void resetInitialState(final SpacecraftState state) {
220 throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
221 }
222
223 /** {@inheritDoc} */
224 protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
225 throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
226 }
227
228 /** {@inheritDoc} */
229 @Override
230 public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
231 super.setAttitudeProvider(attitudeProvider);
232 if (mapper != null) {
233 // At the construction, the mapper is not set yet
234 // However, if the attitude provider is changed afterwards, it must be changed in the mapper too
235 mapper.setAttitudeProvider(attitudeProvider);
236 }
237 }
238
239 /** {@inheritDoc} */
240 public SpacecraftState getInitialState() {
241 return updateAdditionalStates(basicPropagate(getMinDate()));
242 }
243
244 /** {@inheritDoc} */
245 @Override
246 protected SpacecraftState updateAdditionalStates(final SpacecraftState original) {
247
248 SpacecraftState updated = super.updateAdditionalStates(original);
249
250 if (equations.length > 0) {
251 final ODEStateAndDerivative osd = getInterpolatedState(updated.getDate());
252 final double[] combinedState = osd.getSecondaryState(1);
253 final double[] combinedDerivative = osd.getSecondaryDerivative(1);
254 int index = 0;
255 for (int i = 0; i < equations.length; ++i) {
256 final double[] state = Arrays.copyOfRange(combinedState, index, index + dimensions[i]);
257 final double[] derivative = Arrays.copyOfRange(combinedDerivative, index, index + dimensions[i]);
258 updated = updated.
259 addAdditionalState(equations[i], state).
260 addAdditionalStateDerivative(equations[i], derivative);
261 index += dimensions[i];
262 }
263 }
264
265 return updated;
266
267 }
268
269 }