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&eacute;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 }