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