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.analytical;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.List;
24  import java.util.PriorityQueue;
25  import java.util.Queue;
26  
27  import org.hipparchus.exception.MathRuntimeException;
28  import org.hipparchus.ode.events.Action;
29  import org.orekit.attitudes.Attitude;
30  import org.orekit.attitudes.AttitudeProvider;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitInternalError;
33  import org.orekit.frames.Frame;
34  import org.orekit.orbits.Orbit;
35  import org.orekit.propagation.AbstractPropagator;
36  import org.orekit.propagation.AdditionalStateProvider;
37  import org.orekit.propagation.BoundedPropagator;
38  import org.orekit.propagation.EphemerisGenerator;
39  import org.orekit.propagation.SpacecraftState;
40  import org.orekit.propagation.events.EventDetector;
41  import org.orekit.propagation.events.EventState;
42  import org.orekit.propagation.events.EventState.EventOccurrence;
43  import org.orekit.propagation.sampling.OrekitStepInterpolator;
44  import org.orekit.time.AbsoluteDate;
45  import org.orekit.utils.PVCoordinatesProvider;
46  import org.orekit.utils.TimeStampedPVCoordinates;
47  
48  /** Common handling of {@link org.orekit.propagation.Propagator} methods for analytical propagators.
49   * <p>
50   * This abstract class allows to provide easily the full set of {@link
51   * org.orekit.propagation.Propagator Propagator} methods, including all propagation
52   * modes support and discrete events support for any simple propagation method. Only
53   * two methods must be implemented by derived classes: {@link #propagateOrbit(AbsoluteDate)}
54   * and {@link #getMass(AbsoluteDate)}. The first method should perform straightforward
55   * propagation starting from some internally stored initial state up to the specified target date.
56   * </p>
57   * @author Luc Maisonobe
58   */
59  public abstract class AbstractAnalyticalPropagator extends AbstractPropagator {
60  
61      /** Provider for attitude computation. */
62      private PVCoordinatesProvider pvProvider;
63  
64      /** Start date of last propagation. */
65      private AbsoluteDate lastPropagationStart;
66  
67      /** End date of last propagation. */
68      private AbsoluteDate lastPropagationEnd;
69  
70      /** Initialization indicator of events states. */
71      private boolean statesInitialized;
72  
73      /** Indicator for last step. */
74      private boolean isLastStep;
75  
76      /** Event steps. */
77      private final Collection<EventState<?>> eventsStates;
78  
79      /** Build a new instance.
80       * @param attitudeProvider provider for attitude computation
81       */
82      protected AbstractAnalyticalPropagator(final AttitudeProvider attitudeProvider) {
83          setAttitudeProvider(attitudeProvider);
84          pvProvider           = new LocalPVProvider();
85          lastPropagationStart = AbsoluteDate.PAST_INFINITY;
86          lastPropagationEnd   = AbsoluteDate.FUTURE_INFINITY;
87          statesInitialized    = false;
88          eventsStates         = new ArrayList<>();
89      }
90  
91      /** {@inheritDoc} */
92      @Override
93      public EphemerisGenerator getEphemerisGenerator() {
94          return () -> new BoundedPropagatorView(lastPropagationStart, lastPropagationEnd);
95      }
96  
97      /** {@inheritDoc} */
98      public <T extends EventDetector> void addEventDetector(final T detector) {
99          eventsStates.add(new EventState<>(detector));
100     }
101 
102     /** {@inheritDoc} */
103     public Collection<EventDetector> getEventsDetectors() {
104         final List<EventDetector> list = new ArrayList<>();
105         for (final EventState<?> state : eventsStates) {
106             list.add(state.getEventDetector());
107         }
108         return Collections.unmodifiableCollection(list);
109     }
110 
111     /** {@inheritDoc} */
112     public void clearEventsDetectors() {
113         eventsStates.clear();
114     }
115 
116     /** {@inheritDoc} */
117     public SpacecraftState propagate(final AbsoluteDate start, final AbsoluteDate target) {
118         try {
119 
120             initializePropagation();
121 
122             lastPropagationStart = start;
123 
124             final boolean isForward = target.compareTo(start) >= 0;
125             SpacecraftState state   = updateAdditionalStates(basicPropagate(start));
126 
127             // initialize event detectors
128             for (final EventState<?> es : eventsStates) {
129                 es.init(state, target);
130             }
131 
132             // initialize step handlers
133             getMultiplexer().init(state, target);
134 
135             // iterate over the propagation range, need loop due to reset events
136             statesInitialized = false;
137             isLastStep = false;
138             do {
139 
140                 // attempt to advance to the target date
141                 final SpacecraftState previous = state;
142                 final SpacecraftState current = updateAdditionalStates(basicPropagate(target));
143                 final OrekitStepInterpolator interpolator =
144                         new BasicStepInterpolator(isForward, previous, current);
145 
146                 // accept the step, trigger events and step handlers
147                 state = acceptStep(interpolator, target);
148 
149                 // Update the potential changes in the spacecraft state due to the events
150                 // especially the potential attitude transition
151                 state = updateAdditionalStates(basicPropagate(state.getDate()));
152 
153             } while (!isLastStep);
154 
155             // return the last computed state
156             lastPropagationEnd = state.getDate();
157             setStartDate(state.getDate());
158             return state;
159 
160         } catch (MathRuntimeException mrte) {
161             throw OrekitException.unwrap(mrte);
162         }
163     }
164 
165     /** Accept a step, triggering events and step handlers.
166      * @param interpolator interpolator for the current step
167      * @param target final propagation time
168      * @return state at the end of the step
169      * @exception MathRuntimeException if an event cannot be located
170      */
171     protected SpacecraftState acceptStep(final OrekitStepInterpolator interpolator,
172                                          final AbsoluteDate target)
173         throws MathRuntimeException {
174 
175         SpacecraftState        previous   = interpolator.getPreviousState();
176         final SpacecraftState  current    = interpolator.getCurrentState();
177         OrekitStepInterpolator restricted = interpolator;
178 
179 
180         // initialize the events states if needed
181         if (!statesInitialized) {
182 
183             if (!eventsStates.isEmpty()) {
184                 // initialize the events states
185                 for (final EventState<?> state : eventsStates) {
186                     state.reinitializeBegin(interpolator);
187                 }
188             }
189 
190             statesInitialized = true;
191 
192         }
193 
194         // search for next events that may occur during the step
195         final int orderingSign = interpolator.isForward() ? +1 : -1;
196         final Queue<EventState<?>> occurringEvents = new PriorityQueue<>(new Comparator<EventState<?>>() {
197             /** {@inheritDoc} */
198             @Override
199             public int compare(final EventState<?> es0, final EventState<?> es1) {
200                 return orderingSign * es0.getEventDate().compareTo(es1.getEventDate());
201             }
202         });
203 
204         boolean doneWithStep = false;
205         resetEvents:
206         do {
207 
208             // Evaluate all event detectors for events
209             occurringEvents.clear();
210             for (final EventState<?> state : eventsStates) {
211                 if (state.evaluateStep(interpolator)) {
212                     // the event occurs during the current step
213                     occurringEvents.add(state);
214                 }
215             }
216 
217             do {
218 
219                 eventLoop:
220                 while (!occurringEvents.isEmpty()) {
221 
222                     // handle the chronologically first event
223                     final EventState<?> currentEvent = occurringEvents.poll();
224 
225                     // get state at event time
226                     SpacecraftState eventState = restricted.getInterpolatedState(currentEvent.getEventDate());
227 
228                     // restrict the interpolator to the first part of the step, up to the event
229                     restricted = restricted.restrictStep(previous, eventState);
230 
231                     // try to advance all event states to current time
232                     for (final EventState<?> state : eventsStates) {
233                         if (state != currentEvent && state.tryAdvance(eventState, interpolator)) {
234                             // we need to handle another event first
235                             // remove event we just updated to prevent heap corruption
236                             occurringEvents.remove(state);
237                             // add it back to update its position in the heap
238                             occurringEvents.add(state);
239                             // re-queue the event we were processing
240                             occurringEvents.add(currentEvent);
241                             continue eventLoop;
242                         }
243                     }
244                     // all event detectors agree we can advance to the current event time
245 
246                     // handle the first part of the step, up to the event
247                     getMultiplexer().handleStep(restricted);
248 
249                     // acknowledge event occurrence
250                     final EventOccurrence occurrence = currentEvent.doEvent(eventState);
251                     final Action action = occurrence.getAction();
252                     isLastStep = action == Action.STOP;
253 
254                     if (isLastStep) {
255 
256                         // ensure the event is after the root if it is returned STOP
257                         // this lets the user integrate to a STOP event and then restart
258                         // integration from the same time.
259                         final SpacecraftState savedState = eventState;
260                         eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
261                         restricted = restricted.restrictStep(savedState, eventState);
262 
263                         // handle the almost zero size last part of the final step, at event time
264                         getMultiplexer().handleStep(restricted);
265                         getMultiplexer().finish(restricted.getCurrentState());
266 
267                     }
268 
269                     if (isLastStep) {
270                         // the event asked to stop integration
271                         return eventState;
272                     }
273 
274                     if (action == Action.RESET_DERIVATIVES || action == Action.RESET_STATE) {
275                         // some event handler has triggered changes that
276                         // invalidate the derivatives, we need to recompute them
277                         final SpacecraftState resetState = occurrence.getNewState();
278                         resetIntermediateState(resetState, interpolator.isForward());
279                         return resetState;
280                     }
281                     // at this point action == Action.CONTINUE or Action.RESET_EVENTS
282 
283                     // prepare handling of the remaining part of the step
284                     previous = eventState;
285                     restricted = new BasicStepInterpolator(restricted.isForward(), eventState, current);
286 
287                     if (action == Action.RESET_EVENTS) {
288                         continue resetEvents;
289                     }
290 
291                     // at this point action == Action.CONTINUE
292                     // check if the same event occurs again in the remaining part of the step
293                     if (currentEvent.evaluateStep(restricted)) {
294                         // the event occurs during the current step
295                         occurringEvents.add(currentEvent);
296                     }
297 
298                 }
299 
300                 // last part of the step, after the last event. Advance all detectors to
301                 // the end of the step. Should only detect a new event here if an event
302                 // modified the g function of another detector. Detecting such events here
303                 // is unreliable and RESET_EVENTS should be used instead. Might as well
304                 // re-check here because we have to loop through all the detectors anyway
305                 // and the alternative is to throw an exception.
306                 for (final EventState<?> state : eventsStates) {
307                     if (state.tryAdvance(current, interpolator)) {
308                         occurringEvents.add(state);
309                     }
310                 }
311 
312             } while (!occurringEvents.isEmpty());
313 
314             doneWithStep = true;
315         } while (!doneWithStep);
316 
317         isLastStep = target.equals(current.getDate());
318 
319         // handle the remaining part of the step, after all events if any
320         getMultiplexer().handleStep(restricted);
321         if (isLastStep) {
322             getMultiplexer().finish(restricted.getCurrentState());
323         }
324 
325         return current;
326 
327     }
328 
329     /** Get the mass.
330      * @param date target date for the orbit
331      * @return mass mass
332      */
333     protected abstract double getMass(AbsoluteDate date);
334 
335     /** Get PV coordinates provider.
336      * @return PV coordinates provider
337      */
338     public PVCoordinatesProvider getPvProvider() {
339         return pvProvider;
340     }
341 
342     /** Reset an intermediate state.
343      * @param state new intermediate state to consider
344      * @param forward if true, the intermediate state is valid for
345      * propagations after itself
346      */
347     protected abstract void resetIntermediateState(SpacecraftState state, boolean forward);
348 
349     /** Extrapolate an orbit up to a specific target date.
350      * @param date target date for the orbit
351      * @return extrapolated parameters
352      */
353     protected abstract Orbit propagateOrbit(AbsoluteDate date);
354 
355     /** Propagate an orbit without any fancy features.
356      * <p>This method is similar in spirit to the {@link #propagate} method,
357      * except that it does <strong>not</strong> call any handler during
358      * propagation, nor any discrete events, not additional states. It always
359      * stop exactly at the specified date.</p>
360      * @param date target date for propagation
361      * @return state at specified date
362      */
363     protected SpacecraftState basicPropagate(final AbsoluteDate date) {
364         try {
365 
366             // evaluate orbit
367             final Orbit orbit = propagateOrbit(date);
368 
369             // evaluate attitude
370             final Attitude attitude =
371                 getAttitudeProvider().getAttitude(pvProvider, date, orbit.getFrame());
372 
373             // build raw state
374             return new SpacecraftState(orbit, attitude, getMass(date));
375 
376         } catch (OrekitException oe) {
377             throw new OrekitException(oe);
378         }
379     }
380 
381     /** Internal PVCoordinatesProvider for attitude computation. */
382     private class LocalPVProvider implements PVCoordinatesProvider {
383 
384         /** {@inheritDoc} */
385         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
386             return propagateOrbit(date).getPVCoordinates(frame);
387         }
388 
389     }
390 
391     /** {@link BoundedPropagator} view of the instance. */
392     private class BoundedPropagatorView extends AbstractAnalyticalPropagator implements BoundedPropagator {
393 
394         /** Min date. */
395         private final AbsoluteDate minDate;
396 
397         /** Max date. */
398         private final AbsoluteDate maxDate;
399 
400         /** Simple constructor.
401          * @param startDate start date of the propagation
402          * @param endDate end date of the propagation
403          */
404         BoundedPropagatorView(final AbsoluteDate startDate, final AbsoluteDate endDate) {
405             super(AbstractAnalyticalPropagator.this.getAttitudeProvider());
406             super.resetInitialState(AbstractAnalyticalPropagator.this.getInitialState());
407             if (startDate.compareTo(endDate) <= 0) {
408                 minDate = startDate;
409                 maxDate = endDate;
410             } else {
411                 minDate = endDate;
412                 maxDate = startDate;
413             }
414 
415             try {
416                 // copy the same additional state providers as the original propagator
417                 for (AdditionalStateProvider provider : AbstractAnalyticalPropagator.this.getAdditionalStateProviders()) {
418                     addAdditionalStateProvider(provider);
419                 }
420             } catch (OrekitException oe) {
421                 // as the providers are already compatible with each other,
422                 // this should never happen
423                 throw new OrekitInternalError(null);
424             }
425 
426         }
427 
428         /** {@inheritDoc} */
429         public AbsoluteDate getMinDate() {
430             return minDate;
431         }
432 
433         /** {@inheritDoc} */
434         public AbsoluteDate getMaxDate() {
435             return maxDate;
436         }
437 
438         /** {@inheritDoc} */
439         protected Orbit propagateOrbit(final AbsoluteDate target) {
440             return AbstractAnalyticalPropagator.this.propagateOrbit(target);
441         }
442 
443         /** {@inheritDoc} */
444         public double getMass(final AbsoluteDate date) {
445             return AbstractAnalyticalPropagator.this.getMass(date);
446         }
447 
448         /** {@inheritDoc} */
449         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
450             return propagate(date).getPVCoordinates(frame);
451         }
452 
453         /** {@inheritDoc} */
454         public void resetInitialState(final SpacecraftState state) {
455             super.resetInitialState(state);
456             AbstractAnalyticalPropagator.this.resetInitialState(state);
457         }
458 
459         /** {@inheritDoc} */
460         protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
461             AbstractAnalyticalPropagator.this.resetIntermediateState(state, forward);
462         }
463 
464         /** {@inheritDoc} */
465         public SpacecraftState getInitialState() {
466             return AbstractAnalyticalPropagator.this.getInitialState();
467         }
468 
469         /** {@inheritDoc} */
470         public Frame getFrame() {
471             return AbstractAnalyticalPropagator.this.getFrame();
472         }
473 
474     }
475 
476     /** Internal class for local propagation. */
477     private class BasicStepInterpolator implements OrekitStepInterpolator {
478 
479         /** Previous state. */
480         private final SpacecraftState previousState;
481 
482         /** Current state. */
483         private final SpacecraftState currentState;
484 
485         /** Forward propagation indicator. */
486         private final boolean forward;
487 
488         /** Simple constructor.
489          * @param isForward integration direction indicator
490          * @param previousState start of the step
491          * @param currentState end of the step
492          */
493         BasicStepInterpolator(final boolean isForward,
494                               final SpacecraftState previousState,
495                               final SpacecraftState currentState) {
496             this.forward         = isForward;
497             this.previousState   = previousState;
498             this.currentState    = currentState;
499         }
500 
501         /** {@inheritDoc} */
502         @Override
503         public SpacecraftState getPreviousState() {
504             return previousState;
505         }
506 
507         /** {@inheritDoc} */
508         @Override
509         public boolean isPreviousStateInterpolated() {
510             // no difference in analytical propagators
511             return false;
512         }
513 
514         /** {@inheritDoc} */
515         @Override
516         public SpacecraftState getCurrentState() {
517             return currentState;
518         }
519 
520         /** {@inheritDoc} */
521         @Override
522         public boolean isCurrentStateInterpolated() {
523             // no difference in analytical propagators
524             return false;
525         }
526 
527         /** {@inheritDoc} */
528         @Override
529         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
530 
531             // compute the basic spacecraft state
532             final SpacecraftState basicState = basicPropagate(date);
533 
534             // add the additional states
535             return updateAdditionalStates(basicState);
536 
537         }
538 
539         /** {@inheritDoc} */
540         @Override
541         public boolean isForward() {
542             return forward;
543         }
544 
545         /** {@inheritDoc} */
546         @Override
547         public BasicStepInterpolator restrictStep(final SpacecraftState newPreviousState,
548                                                   final SpacecraftState newCurrentState) {
549             return new BasicStepInterpolator(forward, newPreviousState, newCurrentState);
550         }
551 
552     }
553 
554 }