1   /* Copyright 2002-2015 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.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.commons.math3.exception.NoBracketingException;
27  import org.apache.commons.math3.exception.TooManyEvaluationsException;
28  import org.apache.commons.math3.util.FastMath;
29  import org.orekit.attitudes.Attitude;
30  import org.orekit.attitudes.AttitudeProvider;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.PropagationException;
33  import org.orekit.frames.Frame;
34  import org.orekit.orbits.Orbit;
35  import org.orekit.propagation.AbstractPropagator;
36  import org.orekit.propagation.BoundedPropagator;
37  import org.orekit.propagation.SpacecraftState;
38  import org.orekit.propagation.events.EventDetector;
39  import org.orekit.propagation.events.EventState;
40  import org.orekit.propagation.sampling.OrekitStepInterpolator;
41  import org.orekit.time.AbsoluteDate;
42  import org.orekit.utils.PVCoordinatesProvider;
43  import org.orekit.utils.TimeStampedPVCoordinates;
44  
45  /** Common handling of {@link org.orekit.propagation.Propagator} methods for analytical propagators.
46   * <p>
47   * This abstract class allows to provide easily the full set of {@link
48   * org.orekit.propagation.Propagator Propagator} methods, including all propagation
49   * modes support and discrete events support for any simple propagation method. Only
50   * two methods must be implemented by derived classes: {@link #propagateOrbit(AbsoluteDate)}
51   * and {@link #getMass(AbsoluteDate)}. The first method should perform straightforward
52   * propagation starting from some internally stored initial state up to the specified target date.
53   * </p>
54   * @author Luc Maisonobe
55   */
56  public abstract class AbstractAnalyticalPropagator extends AbstractPropagator {
57  
58      /** Internal steps interpolator. */
59      private final BasicStepInterpolator interpolator;
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          interpolator             = new BasicStepInterpolator();
85          pvProvider               = new LocalPVProvider();
86          lastPropagationStart     = AbsoluteDate.PAST_INFINITY;
87          lastPropagationEnd       = AbsoluteDate.FUTURE_INFINITY;
88          statesInitialized        = false;
89          eventsStates             = new ArrayList<EventState<?>>();
90      }
91  
92      /** {@inheritDoc} */
93      public BoundedPropagator getGeneratedEphemeris() {
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<T>(detector));
100     }
101 
102     /** {@inheritDoc} */
103     public Collection<EventDetector> getEventsDetectors() {
104         final List<EventDetector> list = new ArrayList<EventDetector>();
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         throws PropagationException {
119         try {
120 
121             lastPropagationStart = start;
122 
123             final double dt      = target.durationFrom(start);
124             final double epsilon = FastMath.ulp(dt);
125             interpolator.storeDate(start);
126             SpacecraftState state = interpolator.getInterpolatedState();
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                 interpolator.shift();
157                 final AbsoluteDate t = interpolator.getCurrentDate().shiftedBy(stepSize);
158                 if ((dt == 0) || ((dt > 0) ^ (t.compareTo(target) <= 0))) {
159                     // current step exceeds target
160                     interpolator.storeDate(target);
161                 } else {
162                     // current step is within range
163                     interpolator.storeDate(t);
164                 }
165 
166                 // accept the step, trigger events and step handlers
167                 state = acceptStep(target, epsilon);
168 
169             } while (!isLastStep);
170 
171             // return the last computed state
172             lastPropagationEnd = state.getDate();
173             setStartDate(state.getDate());
174             return state;
175 
176         } catch (PropagationException pe) {
177             throw pe;
178         } catch (OrekitException oe) {
179             throw PropagationException.unwrap(oe);
180         } catch (TooManyEvaluationsException tmee) {
181             throw PropagationException.unwrap(tmee);
182         } catch (NoBracketingException nbe) {
183             throw PropagationException.unwrap(nbe);
184         }
185     }
186 
187     /** Accept a step, triggering events and step handlers.
188      * @param target final propagation time
189      * @param epsilon threshold for end date detection
190      * @return state at the end of the step
191      * @exception OrekitException if the switching function cannot be evaluated
192      * @exception TooManyEvaluationsException if an event cannot be located
193      * @exception NoBracketingException if bracketing cannot be performed
194      */
195     protected SpacecraftState acceptStep(final AbsoluteDate target, final double epsilon)
196         throws OrekitException, TooManyEvaluationsException, NoBracketingException {
197 
198         AbsoluteDate previousT = interpolator.getGlobalPreviousDate();
199         AbsoluteDate currentT  = interpolator.getGlobalCurrentDate();
200 
201         // initialize the events states if needed
202         if (!statesInitialized) {
203 
204             if (!eventsStates.isEmpty()) {
205                 // initialize the events states
206                 final AbsoluteDate t0 = interpolator.getPreviousDate();
207                 interpolator.setInterpolatedDate(t0);
208                 final SpacecraftState y = interpolator.getInterpolatedState();
209                 for (final EventState<?> state : eventsStates) {
210                     state.reinitializeBegin(y, interpolator.isForward());
211                 }
212             }
213 
214             statesInitialized = true;
215 
216         }
217 
218         // search for next events that may occur during the step
219         final List<EventState<?>> occurringEvents = new ArrayList<EventState<?>>();
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         // chronological or reverse chronological sorter, according to propagation direction
228         final int orderingSign = interpolator.isForward() ? +1 : -1;
229         final Comparator<EventState<?>> sorter = new Comparator<EventState<?>>() {
230 
231             /** {@inheritDoc} */
232             public int compare(final EventState<?> es0, final EventState<?> es1) {
233                 return orderingSign * es0.getEventTime().compareTo(es1.getEventTime());
234             }
235 
236         };
237 
238         while (!occurringEvents.isEmpty()) {
239 
240             // handle the chronologically first event
241             Collections.sort(occurringEvents, sorter);
242             final Iterator<EventState<?>> iterator = occurringEvents.iterator();
243             final EventState<?> currentEvent = iterator.next();
244             iterator.remove();
245 
246             // restrict the interpolator to the first part of the step, up to the event
247             final AbsoluteDate eventT = currentEvent.getEventTime();
248             interpolator.setSoftPreviousDate(previousT);
249             interpolator.setSoftCurrentDate(eventT);
250 
251             // trigger the event
252             interpolator.setInterpolatedDate(eventT);
253             final SpacecraftState eventY = interpolator.getInterpolatedState();
254             currentEvent.stepAccepted(eventY);
255             isLastStep = currentEvent.stop();
256 
257             // handle the first part of the step, up to the event
258             if (getStepHandler() != null) {
259                 getStepHandler().handleStep(interpolator, isLastStep);
260             }
261 
262             if (isLastStep) {
263                 // the event asked to stop integration
264                 return eventY;
265             }
266 
267             final SpacecraftState resetState = currentEvent.reset(eventY);
268             if (resetState != null) {
269                 resetInitialState(resetState);
270                 return resetState;
271             }
272 
273             // prepare handling of the remaining part of the step
274             previousT = eventT;
275             interpolator.setSoftPreviousDate(eventT);
276             interpolator.setSoftCurrentDate(currentT);
277 
278             // check if the same event occurs again in the remaining part of the step
279             if (currentEvent.evaluateStep(interpolator)) {
280                 // the event occurs during the current step
281                 occurringEvents.add(currentEvent);
282             }
283 
284         }
285 
286         final double remaining = target.durationFrom(currentT);
287         if (interpolator.isForward()) {
288             isLastStep = remaining <  epsilon;
289         } else {
290             isLastStep = remaining > -epsilon;
291         }
292         if (isLastStep) {
293             currentT = target;
294         }
295 
296         interpolator.setInterpolatedDate(currentT);
297         final SpacecraftState currentY = interpolator.getInterpolatedState();
298         for (final EventState<?> state : eventsStates) {
299             state.stepAccepted(currentY);
300             isLastStep = isLastStep || state.stop();
301         }
302 
303         // handle the remaining part of the step, after all events if any
304         if (getStepHandler() != null) {
305             getStepHandler().handleStep(interpolator, isLastStep);
306         }
307 
308         return currentY;
309 
310     }
311 
312     /** Get the mass.
313      * @param date target date for the orbit
314      * @return mass mass
315      * @exception PropagationException if some parameters are out of bounds
316      */
317     protected abstract double getMass(final AbsoluteDate date)
318         throws PropagationException;
319 
320     /** Get PV coordinates provider.
321      * @return PV coordinates provider
322      */
323     public PVCoordinatesProvider getPvProvider() {
324         return pvProvider;
325     }
326 
327     /** Extrapolate an orbit up to a specific target date.
328      * @param date target date for the orbit
329      * @return extrapolated parameters
330      * @exception PropagationException if some parameters are out of bounds
331      */
332     protected abstract Orbit propagateOrbit(final AbsoluteDate date)
333         throws PropagationException;
334 
335     /** Propagate an orbit without any fancy features.
336      * <p>This method is similar in spirit to the {@link #propagate} method,
337      * except that it does <strong>not</strong> call any handler during
338      * propagation, nor any discrete events, not additional states. It always
339      * stop exactly at the specified date.</p>
340      * @param date target date for propagation
341      * @return state at specified date
342      * @exception PropagationException if propagation cannot reach specified date
343      */
344     protected SpacecraftState basicPropagate(final AbsoluteDate date) throws PropagationException {
345         try {
346 
347             // evaluate orbit
348             final Orbit orbit = propagateOrbit(date);
349 
350             // evaluate attitude
351             final Attitude attitude =
352                 getAttitudeProvider().getAttitude(pvProvider, date, orbit.getFrame());
353 
354             // build raw state
355             return new SpacecraftState(orbit, attitude, getMass(date));
356 
357         } catch (OrekitException oe) {
358             throw new PropagationException(oe);
359         }
360     }
361 
362     /** Internal PVCoordinatesProvider for attitude computation. */
363     private class LocalPVProvider implements PVCoordinatesProvider {
364 
365         /** {@inheritDoc} */
366         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
367             throws OrekitException {
368             return propagateOrbit(date).getPVCoordinates(frame);
369         }
370 
371     }
372 
373     /** {@link BoundedPropagator} view of the instance. */
374     private class BoundedPropagatorView extends AbstractAnalyticalPropagator implements BoundedPropagator {
375 
376         /** Min date. */
377         private final AbsoluteDate minDate;
378 
379         /** Max date. */
380         private final AbsoluteDate maxDate;
381 
382         /** Simple constructor.
383          * @param startDate start date of the propagation
384          * @param endDate end date of the propagation
385          */
386         public BoundedPropagatorView(final AbsoluteDate startDate, final AbsoluteDate endDate) {
387             super(AbstractAnalyticalPropagator.this.getAttitudeProvider());
388             if (startDate.compareTo(endDate) <= 0) {
389                 minDate = startDate;
390                 maxDate = endDate;
391             } else {
392                 minDate = endDate;
393                 maxDate = startDate;
394             }
395         }
396 
397         /** {@inheritDoc} */
398         public AbsoluteDate getMinDate() {
399             return minDate;
400         }
401 
402         /** {@inheritDoc} */
403         public AbsoluteDate getMaxDate() {
404             return maxDate;
405         }
406 
407         /** {@inheritDoc} */
408         protected Orbit propagateOrbit(final AbsoluteDate target)
409             throws PropagationException {
410             return AbstractAnalyticalPropagator.this.propagateOrbit(target);
411         }
412 
413         /** {@inheritDoc} */
414         public double getMass(final AbsoluteDate date) throws PropagationException {
415             return AbstractAnalyticalPropagator.this.getMass(date);
416         }
417 
418         /** {@inheritDoc} */
419         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
420             throws OrekitException {
421             return propagate(date).getPVCoordinates(frame);
422         }
423 
424         /** {@inheritDoc} */
425         public void resetInitialState(final SpacecraftState state) throws PropagationException {
426             AbstractAnalyticalPropagator.this.resetInitialState(state);
427         }
428 
429         /** {@inheritDoc} */
430         public SpacecraftState getInitialState() throws PropagationException {
431             return AbstractAnalyticalPropagator.this.getInitialState();
432         }
433 
434     }
435 
436     /** Internal class for local propagation. */
437     private class BasicStepInterpolator implements OrekitStepInterpolator {
438 
439         /** Global previous date. */
440         private AbsoluteDate globalPreviousDate;
441 
442         /** Global current date. */
443         private AbsoluteDate globalCurrentDate;
444 
445         /** Soft previous date. */
446         private AbsoluteDate softPreviousDate;
447 
448         /** Soft current date. */
449         private AbsoluteDate softCurrentDate;
450 
451         /** Interpolated state. */
452         private SpacecraftState interpolatedState;
453 
454         /** Forward propagation indicator. */
455         private boolean forward;
456 
457         /** Build a new instance from a basic propagator.
458          */
459         public BasicStepInterpolator() {
460             globalPreviousDate = AbsoluteDate.PAST_INFINITY;
461             globalCurrentDate  = AbsoluteDate.PAST_INFINITY;
462             softPreviousDate   = AbsoluteDate.PAST_INFINITY;
463             softCurrentDate    = AbsoluteDate.PAST_INFINITY;
464         }
465 
466         /** Restrict step range to a limited part of the global step.
467          * <p>
468          * This method can be used to restrict a step and make it appear
469          * as if the original step was smaller. Calling this method
470          * <em>only</em> changes the value returned by {@link #getPreviousDate()},
471          * it does not change any other property
472          * </p>
473          * @param softPreviousDate start of the restricted step
474          */
475         public void setSoftPreviousDate(final AbsoluteDate softPreviousDate) {
476             this.softPreviousDate = softPreviousDate;
477         }
478 
479         /** Restrict step range to a limited part of the global step.
480          * <p>
481          * This method can be used to restrict a step and make it appear
482          * as if the original step was smaller. Calling this method
483          * <em>only</em> changes the value returned by {@link #getCurrentDate()},
484          * it does not change any other property
485          * </p>
486          * @param softCurrentDate end of the restricted step
487          */
488         public void setSoftCurrentDate(final AbsoluteDate softCurrentDate) {
489             this.softCurrentDate  = softCurrentDate;
490         }
491 
492         /**
493          * Get the previous global grid point time.
494          * @return previous global grid point time
495          */
496         public AbsoluteDate getGlobalPreviousDate() {
497             return globalPreviousDate;
498         }
499 
500         /**
501          * Get the current global grid point time.
502          * @return current global grid point time
503          */
504         public AbsoluteDate getGlobalCurrentDate() {
505             return globalCurrentDate;
506         }
507 
508         /** {@inheritDoc} */
509         public AbsoluteDate getCurrentDate() {
510             return softCurrentDate;
511         }
512 
513         /** {@inheritDoc} */
514         public AbsoluteDate getInterpolatedDate() {
515             return interpolatedState.getDate();
516         }
517 
518         /** {@inheritDoc} */
519         public SpacecraftState getInterpolatedState() throws OrekitException {
520             return interpolatedState;
521         }
522 
523         /** {@inheritDoc} */
524         public AbsoluteDate getPreviousDate() {
525             return softPreviousDate;
526         }
527 
528         /** {@inheritDoc} */
529         public boolean isForward() {
530             return forward;
531         }
532 
533         /** {@inheritDoc} */
534         public void setInterpolatedDate(final AbsoluteDate date) throws PropagationException {
535 
536             // compute the basic spacecraft state
537             final SpacecraftState basicState = basicPropagate(date);
538 
539             // add the additional states
540             interpolatedState = updateAdditionalStates(basicState);
541 
542         }
543 
544         /** Shift one step forward.
545          * Copy the current date into the previous date, hence preparing the
546          * interpolator for future calls to {@link #storeDate storeDate}
547          */
548         public void shift() {
549             globalPreviousDate = globalCurrentDate;
550             softPreviousDate   = globalPreviousDate;
551             softCurrentDate    = globalCurrentDate;
552         }
553 
554         /** Store the current step date.
555          * @param date current date
556          * @exception PropagationException if the state cannot be propagated at specified date
557          */
558         public void storeDate(final AbsoluteDate date)
559             throws PropagationException {
560             globalCurrentDate = date;
561             softCurrentDate   = globalCurrentDate;
562             forward           = globalCurrentDate.compareTo(globalPreviousDate) >= 0;
563             setInterpolatedDate(globalCurrentDate);
564         }
565 
566     }
567 
568 }