1 /* Copyright 2002-2026 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;
18
19 import java.util.Collection;
20 import java.util.List;
21
22 import org.hipparchus.geometry.euclidean.threed.Rotation;
23 import org.hipparchus.geometry.euclidean.threed.Vector3D;
24 import org.hipparchus.linear.RealMatrix;
25 import org.orekit.attitudes.AttitudeProvider;
26 import org.orekit.attitudes.FrameAlignedProvider;
27 import org.orekit.frames.Frame;
28 import org.orekit.frames.Frames;
29 import org.orekit.frames.KinematicTransform;
30 import org.orekit.orbits.OrbitParamsType;
31 import org.orekit.orbits.PositionAngleType;
32 import org.orekit.propagation.events.EventDetector;
33 import org.orekit.propagation.sampling.OrekitFixedStepHandler;
34 import org.orekit.propagation.sampling.OrekitStepHandler;
35 import org.orekit.propagation.sampling.StepHandlerMultiplexer;
36 import org.orekit.time.AbsoluteDate;
37 import org.orekit.utils.DoubleArrayDictionary;
38 import org.orekit.utils.PVCoordinatesProvider;
39 import org.orekit.utils.TimeStampedPVCoordinates;
40
41 /** This interface provides a way to propagate an orbit at any time.
42 *
43 * <p>This interface is the top-level abstraction for orbit propagation.
44 * It only allows propagation to a predefined date.
45 * It is implemented by analytical models which have no time limit,
46 * by orbit readers based on external data files, by numerical integrators
47 * using rich force models and by continuous models built after numerical
48 * integration has been completed and dense output data as been
49 * gathered.</p>
50 * <p>Note that one single propagator cannot be called from multiple threads.
51 * Its configuration can be changed as there is at least a {@link
52 * #resetInitialState(SpacecraftState)} method, and even propagators that do
53 * not support resetting state (like the {@link
54 * org.orekit.propagation.analytical.tle.TLEPropagator TLEPropagator} do
55 * cache some internal data during computation. However, as long as they
56 * are configured with independent building blocks (mainly event handlers
57 * and step handlers that may preserve some internal state), and as long
58 * as they are called from one thread only, they <em>can</em> be used in
59 * multi-threaded applications. Synchronizing several propagators to run in
60 * parallel is also possible using {@link PropagatorsParallelizer}.</p>
61 * @author Luc Maisonobe
62 * @author Véronique Pommier-Maurussane
63 *
64 */
65
66 public interface Propagator extends PVCoordinatesProvider {
67
68 /** Default mass. */
69 double DEFAULT_MASS = 1000.0;
70
71 /**
72 * Get a default law using the given frames.
73 *
74 * @param frames the set of frames to use.
75 * @return attitude law.
76 */
77 static AttitudeProvider getDefaultLaw(final Frames frames) {
78 return new FrameAlignedProvider(Rotation.IDENTITY, frames.getEME2000());
79 }
80
81 /** Get the multiplexer holding all step handlers.
82 * @return multiplexer holding all step handlers
83 * @since 11.0
84 */
85 StepHandlerMultiplexer getMultiplexer();
86
87 /** Remove all step handlers.
88 * <p>This convenience method is equivalent to call {@code getMultiplexer().clear()}</p>
89 * @see #getMultiplexer()
90 * @see StepHandlerMultiplexer#clear()
91 * @since 11.0
92 */
93 default void clearStepHandlers() {
94 getMultiplexer().clear();
95 }
96
97 /** Set a single handler for fixed stepsizes.
98 * <p>This convenience method is equivalent to call {@code getMultiplexer().clear()}
99 * followed by {@code getMultiplexer().add(h, handler)}</p>
100 * @param h fixed stepsize (s)
101 * @param handler handler called at the end of each finalized step
102 * @see #getMultiplexer()
103 * @see StepHandlerMultiplexer#add(double, OrekitFixedStepHandler)
104 * @since 11.0
105 */
106 default void setStepHandler(final double h, final OrekitFixedStepHandler handler) {
107 getMultiplexer().clear();
108 getMultiplexer().add(h, handler);
109 }
110
111 /** Set a single handler for variable stepsizes.
112 * <p>This convenience method is equivalent to call {@code getMultiplexer().clear()}
113 * followed by {@code getMultiplexer().add(handler)}</p>
114 * @param handler handler called at the end of each finalized step
115 * @see #getMultiplexer()
116 * @see StepHandlerMultiplexer#add(OrekitStepHandler)
117 * @since 11.0
118 */
119 default void setStepHandler(final OrekitStepHandler handler) {
120 getMultiplexer().clear();
121 getMultiplexer().add(handler);
122 }
123
124 /**
125 * Set up an ephemeris generator that will monitor the propagation for building
126 * an ephemeris from it once completed.
127 *
128 * <p>
129 * This generator can be used when the user needs fast random access to the orbit
130 * state at any time between the initial and target times. A typical example is the
131 * implementation of search and iterative algorithms that may navigate forward and
132 * backward inside the propagation range before finding their result even if the
133 * propagator used is integration-based and only goes from one initial time to one
134 * target time.
135 * </p>
136 * <p>
137 * Beware that when used with integration-based propagators, the generator will
138 * store <strong>all</strong> intermediate results. It is therefore memory intensive
139 * for long integration-based ranges and high precision/short time steps. When
140 * used with analytical propagators, the generator only stores start/stop time
141 * and a reference to the analytical propagator itself to call it back as needed,
142 * so it is less memory intensive.
143 * </p>
144 * <p>
145 * The returned ephemeris generator will be initially empty, it will be filled
146 * with propagation data when a subsequent call to either {@link #propagate(AbsoluteDate)
147 * propagate(target)} or {@link #propagate(AbsoluteDate, AbsoluteDate)
148 * propagate(start, target)} is called. The proper way to use this method is
149 * therefore to do:
150 * </p>
151 * <pre>
152 * EphemerisGenerator generator = propagator.getEphemerisGenerator();
153 * propagator.propagate(target);
154 * BoundedPropagator ephemeris = generator.getGeneratedEphemeris();
155 * </pre>
156 * @return ephemeris generator
157 */
158 EphemerisGenerator getEphemerisGenerator();
159
160 /** Get the base initial state without additional data.
161 * @return base initial state without additional data
162 * @see #getInitialState()
163 * @since 14.0
164 */
165 SpacecraftState getBaseInitialState();
166
167 /** Get the propagator initial state.
168 * <p>
169 * If additional data has been {@link #addAdditionalDataProvider(AdditionalDataProvider) added}
170 * to the propagator, the initial state returned here includes it.
171 * </p>
172 * @return initial state, ultimately with additional data
173 * @see #getBaseInitialState()
174 */
175 SpacecraftState getInitialState();
176
177 /** Reset the propagator initial state.
178 * @param state new initial state to consider (without additional data)
179 */
180 void resetInitialState(SpacecraftState state);
181
182 /** Add a set of user-specified data to be computed along with the orbit propagation.
183 * @param additionalDataProvider provider for additional data
184 */
185 void addAdditionalDataProvider(AdditionalDataProvider<?> additionalDataProvider);
186
187 /** Get an unmodifiable list of providers for additional data.
188 * @return providers for the additional data
189 */
190 List<AdditionalDataProvider<?>> getAdditionalDataProviders();
191
192 /** Check if an additional data is managed.
193 * <p>
194 * Managed data are the ones for which the propagators know how to compute
195 * its evolution. They correspond to additional data for which a
196 * {@link AdditionalDataProvider provider} has been registered by calling the
197 * {@link #addAdditionalDataProvider(AdditionalDataProvider) addAdditionalDataProvider} method.
198 * </p>
199 * <p>
200 * Additional data that are present in the {@link #getInitialState() initial state}
201 * but have no evolution method registered are <em>not</em> considered as managed data.
202 * These unmanaged additional data are not lost during propagation, though. Their
203 * value are piecewise constant between state resets that may change them if some
204 * event handler {@link
205 * org.orekit.propagation.events.handlers.EventHandler#resetState(EventDetector,
206 * SpacecraftState) resetState} method is called at an event occurrence and happens
207 * to change the unmanaged additional data.
208 * </p>
209 * @param name name of the additional data
210 * @return true if the additional data is managed
211 */
212 boolean isAdditionalDataManaged(String name);
213
214 /** Get all the names of all managed additional data.
215 * @return names of all managed additional data
216 */
217 String[] getManagedAdditionalData();
218
219 /** Add an event detector.
220 * @param detector event detector to add
221 * @see #clearEventsDetectors()
222 * @see #getEventDetectors()
223 * @param <T> class type for the generic version
224 */
225 <T extends EventDetector> void addEventDetector(T detector);
226
227 /** Get all the events detectors that have been added.
228 * @return an unmodifiable collection of the added detectors
229 * @see #addEventDetector(EventDetector)
230 * @see #clearEventsDetectors()
231 */
232 Collection<EventDetector> getEventDetectors();
233
234 /** Remove all events detectors.
235 * @see #addEventDetector(EventDetector)
236 * @see #getEventDetectors()
237 */
238 void clearEventsDetectors();
239
240 /** Get attitude provider.
241 * @return attitude provider
242 */
243 AttitudeProvider getAttitudeProvider();
244
245 /** Set attitude provider.
246 * @param attitudeProvider attitude provider
247 */
248 void setAttitudeProvider(AttitudeProvider attitudeProvider);
249
250 /** Get the frame in which the orbit is propagated.
251 * <p>
252 * The propagation frame is the definition frame of the initial
253 * state, so this method should be called after this state has
254 * been set, otherwise it may return null.
255 * </p>
256 * @return frame in which the orbit is propagated
257 * @see #resetInitialState(SpacecraftState)
258 */
259 Frame getFrame();
260
261 /** Set up computation of State Transition Matrix and Jacobians matrix with respect to parameters.
262 * <p>
263 * If this method is called, both State Transition Matrix and Jacobians with respect to the
264 * force models parameters that will be selected when propagation starts will be automatically
265 * computed, and the harvester will allow to retrieve them.
266 * </p>
267 * <p>
268 * The arguments for initial matrices <em>must</em> be compatible with the {@link OrbitParamsType
269 * orbit type} and {@link PositionAngleType position angle} that will be used by the propagator.
270 * </p>
271 * @param stmName State Transition Matrix state name
272 * @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
273 * if null (which is the most frequent case), assumed to be 6x6 identity
274 * @param initialJacobianColumns initial columns of the Jacobians matrix with respect to parameters,
275 * if null or if some selected parameters are missing from the dictionary, the corresponding
276 * initial column is assumed to be 0
277 * @return harvester to retrieve computed matrices during and after propagation
278 * @since 11.1
279 */
280 MatricesHarvester setupMatricesComputation(String stmName, RealMatrix initialStm,
281 DoubleArrayDictionary initialJacobianColumns);
282
283 /** Propagate towards a target date.
284 * <p>Simple propagators use only the target date as the specification for
285 * computing the propagated state. More feature rich propagators can consider
286 * other information and provide different operating modes or G-stop
287 * facilities to stop at pinpointed events occurrences. In these cases, the
288 * target date is only a hint, not a mandatory objective.</p>
289 * @param target target date towards which orbit state should be propagated
290 * @return propagated state
291 */
292 SpacecraftState propagate(AbsoluteDate target);
293
294 /** Propagate from a start date towards a target date.
295 * <p>Those propagators use a start date and a target date to
296 * compute the propagated state. For propagators using event detection mechanism,
297 * if the provided start date is different from the initial state date, a first,
298 * simple propagation is performed, without processing any event computation.
299 * Then complete propagation is performed from start date to target date.</p>
300 * @param start start date from which orbit state should be propagated
301 * @param target target date to which orbit state should be propagated
302 * @return propagated state
303 */
304 SpacecraftState propagate(AbsoluteDate start, AbsoluteDate target);
305
306 /** {@inheritDoc} */
307 @Override
308 default TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
309 return propagate(date).getPVCoordinates(frame);
310 }
311
312 /** {@inheritDoc} */
313 @Override
314 default Vector3D getVelocity(final AbsoluteDate date, final Frame frame) {
315 final SpacecraftState state = propagate(date);
316 final KinematicTransform transform = getFrame().getKinematicTransformTo(frame, date);
317 return transform.transformOnlyPV(state.getPVCoordinates()).getVelocity();
318 }
319
320 /** {@inheritDoc} */
321 @Override
322 default Vector3D getPosition(final AbsoluteDate date, final Frame frame) {
323 return propagate(date).getPosition(frame);
324 }
325
326 }