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