AbstractAnalyticalPropagator.java

  1. /* Copyright 2002-2016 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. import java.io.NotSerializableException;
  19. import java.io.Serializable;
  20. import java.util.ArrayList;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.Comparator;
  24. import java.util.Iterator;
  25. import java.util.List;

  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.OrekitInternalError;
  33. import org.orekit.errors.PropagationException;
  34. import org.orekit.frames.Frame;
  35. import org.orekit.orbits.Orbit;
  36. import org.orekit.propagation.AbstractPropagator;
  37. import org.orekit.propagation.AdditionalStateProvider;
  38. import org.orekit.propagation.BoundedPropagator;
  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.sampling.OrekitStepInterpolator;
  43. import org.orekit.time.AbsoluteDate;
  44. import org.orekit.utils.PVCoordinatesProvider;
  45. import org.orekit.utils.TimeStampedPVCoordinates;

  46. /** Common handling of {@link org.orekit.propagation.Propagator} methods for analytical propagators.
  47.  * <p>
  48.  * This abstract class allows to provide easily the full set of {@link
  49.  * org.orekit.propagation.Propagator Propagator} methods, including all propagation
  50.  * modes support and discrete events support for any simple propagation method. Only
  51.  * two methods must be implemented by derived classes: {@link #propagateOrbit(AbsoluteDate)}
  52.  * and {@link #getMass(AbsoluteDate)}. The first method should perform straightforward
  53.  * propagation starting from some internally stored initial state up to the specified target date.
  54.  * </p>
  55.  * @author Luc Maisonobe
  56.  */
  57. public abstract class AbstractAnalyticalPropagator extends AbstractPropagator {

  58.     /** Internal steps interpolator. */
  59.     private final BasicStepInterpolator interpolator;

  60.     /** Provider for attitude computation. */
  61.     private PVCoordinatesProvider pvProvider;

  62.     /** Start date of last propagation. */
  63.     private AbsoluteDate lastPropagationStart;

  64.     /** End date of last propagation. */
  65.     private AbsoluteDate lastPropagationEnd;

  66.     /** Initialization indicator of events states. */
  67.     private boolean statesInitialized;

  68.     /** Indicator for last step. */
  69.     private boolean isLastStep;

  70.     /** Event steps. */
  71.     private final Collection<EventState<?>> eventsStates;

  72.     /** Build a new instance.
  73.      * @param attitudeProvider provider for attitude computation
  74.      */
  75.     protected AbstractAnalyticalPropagator(final AttitudeProvider attitudeProvider) {
  76.         setAttitudeProvider(attitudeProvider);
  77.         interpolator             = new BasicStepInterpolator();
  78.         pvProvider               = new LocalPVProvider();
  79.         lastPropagationStart     = AbsoluteDate.PAST_INFINITY;
  80.         lastPropagationEnd       = AbsoluteDate.FUTURE_INFINITY;
  81.         statesInitialized        = false;
  82.         eventsStates             = new ArrayList<EventState<?>>();
  83.     }

  84.     /** {@inheritDoc} */
  85.     public BoundedPropagator getGeneratedEphemeris() {
  86.         return new BoundedPropagatorView(lastPropagationStart, lastPropagationEnd);
  87.     }

  88.     /** {@inheritDoc} */
  89.     public <T extends EventDetector> void addEventDetector(final T detector) {
  90.         eventsStates.add(new EventState<T>(detector));
  91.     }

  92.     /** {@inheritDoc} */
  93.     public Collection<EventDetector> getEventsDetectors() {
  94.         final List<EventDetector> list = new ArrayList<EventDetector>();
  95.         for (final EventState<?> state : eventsStates) {
  96.             list.add(state.getEventDetector());
  97.         }
  98.         return Collections.unmodifiableCollection(list);
  99.     }

  100.     /** {@inheritDoc} */
  101.     public void clearEventsDetectors() {
  102.         eventsStates.clear();
  103.     }

  104.     /** {@inheritDoc} */
  105.     public SpacecraftState propagate(final AbsoluteDate start, final AbsoluteDate target)
  106.         throws PropagationException {
  107.         try {

  108.             lastPropagationStart = start;

  109.             final double dt      = target.durationFrom(start);
  110.             final double epsilon = FastMath.ulp(dt);
  111.             interpolator.storeDate(start);
  112.             SpacecraftState state = interpolator.getInterpolatedState();

  113.             // evaluate step size
  114.             final double stepSize;
  115.             if (getMode() == MASTER_MODE) {
  116.                 if (Double.isNaN(getFixedStepSize())) {
  117.                     stepSize = FastMath.copySign(state.getKeplerianPeriod() / 100, dt);
  118.                 } else {
  119.                     stepSize = FastMath.copySign(getFixedStepSize(), dt);
  120.                 }
  121.             } else {
  122.                 stepSize = dt;
  123.             }

  124.             // initialize event detectors
  125.             for (final EventState<?> es : eventsStates) {
  126.                 es.init(state, target);
  127.             }

  128.             // initialize step handler
  129.             if (getStepHandler() != null) {
  130.                 getStepHandler().init(state, target);
  131.             }

  132.             // iterate over the propagation range
  133.             statesInitialized = false;
  134.             isLastStep = false;
  135.             do {

  136.                 // go ahead one step size
  137.                 interpolator.shift();
  138.                 final AbsoluteDate t = interpolator.getCurrentDate().shiftedBy(stepSize);
  139.                 if ((dt == 0) || ((dt > 0) ^ (t.compareTo(target) <= 0))) {
  140.                     // current step exceeds target
  141.                     interpolator.storeDate(target);
  142.                 } else {
  143.                     // current step is within range
  144.                     interpolator.storeDate(t);
  145.                 }

  146.                 // accept the step, trigger events and step handlers
  147.                 state = acceptStep(target, epsilon);

  148.             } while (!isLastStep);

  149.             // return the last computed state
  150.             lastPropagationEnd = state.getDate();
  151.             setStartDate(state.getDate());
  152.             return state;

  153.         } catch (PropagationException pe) {
  154.             throw pe;
  155.         } catch (OrekitException oe) {
  156.             throw PropagationException.unwrap(oe);
  157.         } catch (TooManyEvaluationsException tmee) {
  158.             throw PropagationException.unwrap(tmee);
  159.         } catch (NoBracketingException nbe) {
  160.             throw PropagationException.unwrap(nbe);
  161.         }
  162.     }

  163.     /** Accept a step, triggering events and step handlers.
  164.      * @param target final propagation time
  165.      * @param epsilon threshold for end date detection
  166.      * @return state at the end of the step
  167.      * @exception OrekitException if the switching function cannot be evaluated
  168.      * @exception TooManyEvaluationsException if an event cannot be located
  169.      * @exception NoBracketingException if bracketing cannot be performed
  170.      */
  171.     protected SpacecraftState acceptStep(final AbsoluteDate target, final double epsilon)
  172.         throws OrekitException, TooManyEvaluationsException, NoBracketingException {

  173.         AbsoluteDate previousT = interpolator.getGlobalPreviousDate();
  174.         AbsoluteDate currentT  = interpolator.getGlobalCurrentDate();

  175.         // initialize the events states if needed
  176.         if (!statesInitialized) {

  177.             if (!eventsStates.isEmpty()) {
  178.                 // initialize the events states
  179.                 final AbsoluteDate t0 = interpolator.getPreviousDate();
  180.                 interpolator.setInterpolatedDate(t0);
  181.                 final SpacecraftState y = interpolator.getInterpolatedState();
  182.                 for (final EventState<?> state : eventsStates) {
  183.                     state.reinitializeBegin(y, interpolator.isForward());
  184.                 }
  185.             }

  186.             statesInitialized = true;

  187.         }

  188.         // search for next events that may occur during the step
  189.         final List<EventState<?>> occurringEvents = new ArrayList<EventState<?>>();
  190.         for (final EventState<?> state : eventsStates) {
  191.             if (state.evaluateStep(interpolator)) {
  192.                 // the event occurs during the current step
  193.                 occurringEvents.add(state);
  194.             }
  195.         }

  196.         // chronological or reverse chronological sorter, according to propagation direction
  197.         final int orderingSign = interpolator.isForward() ? +1 : -1;
  198.         final Comparator<EventState<?>> sorter = new Comparator<EventState<?>>() {

  199.             /** {@inheritDoc} */
  200.             public int compare(final EventState<?> es0, final EventState<?> es1) {
  201.                 return orderingSign * es0.getEventTime().compareTo(es1.getEventTime());
  202.             }

  203.         };

  204.         while (!occurringEvents.isEmpty()) {

  205.             // handle the chronologically first event
  206.             Collections.sort(occurringEvents, sorter);
  207.             final Iterator<EventState<?>> iterator = occurringEvents.iterator();
  208.             final EventState<?> currentEvent = iterator.next();
  209.             iterator.remove();

  210.             // restrict the interpolator to the first part of the step, up to the event
  211.             final AbsoluteDate eventT = currentEvent.getEventTime();
  212.             interpolator.setSoftPreviousDate(previousT);
  213.             interpolator.setSoftCurrentDate(eventT);

  214.             // trigger the event
  215.             interpolator.setInterpolatedDate(eventT);
  216.             final SpacecraftState eventY = interpolator.getInterpolatedState();
  217.             currentEvent.stepAccepted(eventY);
  218.             isLastStep = currentEvent.stop();

  219.             // handle the first part of the step, up to the event
  220.             if (getStepHandler() != null) {
  221.                 getStepHandler().handleStep(interpolator, isLastStep);
  222.             }

  223.             if (isLastStep) {
  224.                 // the event asked to stop integration
  225.                 return eventY;
  226.             }

  227.             final SpacecraftState resetState = currentEvent.reset(eventY);
  228.             if (resetState != null) {
  229.                 resetIntermediateState(resetState, interpolator.isForward());
  230.                 return resetState;
  231.             }

  232.             // prepare handling of the remaining part of the step
  233.             previousT = eventT;
  234.             interpolator.setSoftPreviousDate(eventT);
  235.             interpolator.setSoftCurrentDate(currentT);

  236.             // check if the same event occurs again in the remaining part of the step
  237.             if (currentEvent.evaluateStep(interpolator)) {
  238.                 // the event occurs during the current step
  239.                 occurringEvents.add(currentEvent);
  240.             }

  241.         }

  242.         final double remaining = target.durationFrom(currentT);
  243.         if (interpolator.isForward()) {
  244.             isLastStep = remaining <  epsilon;
  245.         } else {
  246.             isLastStep = remaining > -epsilon;
  247.         }
  248.         if (isLastStep) {
  249.             currentT = target;
  250.         }

  251.         interpolator.setInterpolatedDate(currentT);
  252.         final SpacecraftState currentY = interpolator.getInterpolatedState();
  253.         for (final EventState<?> state : eventsStates) {
  254.             state.stepAccepted(currentY);
  255.             isLastStep = isLastStep || state.stop();
  256.         }

  257.         // handle the remaining part of the step, after all events if any
  258.         if (getStepHandler() != null) {
  259.             getStepHandler().handleStep(interpolator, isLastStep);
  260.         }

  261.         return currentY;

  262.     }

  263.     /** Get the mass.
  264.      * @param date target date for the orbit
  265.      * @return mass mass
  266.      * @exception PropagationException if some parameters are out of bounds
  267.      */
  268.     protected abstract double getMass(final AbsoluteDate date)
  269.         throws PropagationException;

  270.     /** Get PV coordinates provider.
  271.      * @return PV coordinates provider
  272.      */
  273.     public PVCoordinatesProvider getPvProvider() {
  274.         return pvProvider;
  275.     }

  276.     /** {@inheritDoc} */
  277.     public void resetInitialState(final SpacecraftState state)
  278.         throws PropagationException {
  279.         super.resetInitialState(state);
  280.         interpolator.globalCurrentDate = state.getDate();
  281.         interpolator.softCurrentDate   = interpolator.globalCurrentDate;
  282.     }

  283.     /** Reset an intermediate state.
  284.      * @param state new intermediate state to consider
  285.      * @param forward if true, the intermediate state is valid for
  286.      * propagations after itself
  287.      * @exception PropagationException if initial state cannot be reset
  288.      */
  289.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward)
  290.         throws PropagationException {
  291.         interpolator.globalCurrentDate = state.getDate();
  292.         interpolator.softCurrentDate   = interpolator.globalCurrentDate;
  293.     }

  294.     /** Extrapolate an orbit up to a specific target date.
  295.      * @param date target date for the orbit
  296.      * @return extrapolated parameters
  297.      * @exception PropagationException if some parameters are out of bounds
  298.      */
  299.     protected abstract Orbit propagateOrbit(final AbsoluteDate date)
  300.         throws PropagationException;

  301.     /** Propagate an orbit without any fancy features.
  302.      * <p>This method is similar in spirit to the {@link #propagate} method,
  303.      * except that it does <strong>not</strong> call any handler during
  304.      * propagation, nor any discrete events, not additional states. It always
  305.      * stop exactly at the specified date.</p>
  306.      * @param date target date for propagation
  307.      * @return state at specified date
  308.      * @exception PropagationException if propagation cannot reach specified date
  309.      */
  310.     protected SpacecraftState basicPropagate(final AbsoluteDate date) throws PropagationException {
  311.         try {

  312.             // evaluate orbit
  313.             final Orbit orbit = propagateOrbit(date);

  314.             // evaluate attitude
  315.             final Attitude attitude =
  316.                 getAttitudeProvider().getAttitude(pvProvider, date, orbit.getFrame());

  317.             // build raw state
  318.             return new SpacecraftState(orbit, attitude, getMass(date));

  319.         } catch (OrekitException oe) {
  320.             throw new PropagationException(oe);
  321.         }
  322.     }

  323.     /** Internal PVCoordinatesProvider for attitude computation. */
  324.     private class LocalPVProvider implements PVCoordinatesProvider {

  325.         /** {@inheritDoc} */
  326.         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
  327.             throws OrekitException {
  328.             return propagateOrbit(date).getPVCoordinates(frame);
  329.         }

  330.     }

  331.     /** {@link BoundedPropagator} view of the instance. */
  332.     private class BoundedPropagatorView
  333.         extends AbstractAnalyticalPropagator
  334.         implements BoundedPropagator, Serializable {

  335.         /** Serializable UID. */
  336.         private static final long serialVersionUID = 20151117L;

  337.         /** Min date. */
  338.         private final AbsoluteDate minDate;

  339.         /** Max date. */
  340.         private final AbsoluteDate maxDate;

  341.         /** Simple constructor.
  342.          * @param startDate start date of the propagation
  343.          * @param endDate end date of the propagation
  344.          */
  345.         BoundedPropagatorView(final AbsoluteDate startDate, final AbsoluteDate endDate) {
  346.             super(AbstractAnalyticalPropagator.this.getAttitudeProvider());
  347.             if (startDate.compareTo(endDate) <= 0) {
  348.                 minDate = startDate;
  349.                 maxDate = endDate;
  350.             } else {
  351.                 minDate = endDate;
  352.                 maxDate = startDate;
  353.             }

  354.             try {
  355.                 // copy the same additional state providers as the original propagator
  356.                 for (AdditionalStateProvider provider : AbstractAnalyticalPropagator.this.getAdditionalStateProviders()) {
  357.                     addAdditionalStateProvider(provider);
  358.                 }
  359.             } catch (OrekitException oe) {
  360.                 // as the providers are already compatible with each other,
  361.                 // this should never happen
  362.                 throw new OrekitInternalError(null);
  363.             }

  364.         }

  365.         /** {@inheritDoc} */
  366.         public AbsoluteDate getMinDate() {
  367.             return minDate;
  368.         }

  369.         /** {@inheritDoc} */
  370.         public AbsoluteDate getMaxDate() {
  371.             return maxDate;
  372.         }

  373.         /** {@inheritDoc} */
  374.         protected Orbit propagateOrbit(final AbsoluteDate target)
  375.             throws PropagationException {
  376.             return AbstractAnalyticalPropagator.this.propagateOrbit(target);
  377.         }

  378.         /** {@inheritDoc} */
  379.         public double getMass(final AbsoluteDate date) throws PropagationException {
  380.             return AbstractAnalyticalPropagator.this.getMass(date);
  381.         }

  382.         /** {@inheritDoc} */
  383.         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
  384.             throws OrekitException {
  385.             return propagate(date).getPVCoordinates(frame);
  386.         }

  387.         /** {@inheritDoc} */
  388.         public void resetInitialState(final SpacecraftState state) throws PropagationException {
  389.             AbstractAnalyticalPropagator.this.resetInitialState(state);
  390.         }

  391.         /** {@inheritDoc} */
  392.         protected void resetIntermediateState(final SpacecraftState state, final boolean forward)
  393.             throws PropagationException {
  394.             AbstractAnalyticalPropagator.this.resetIntermediateState(state, forward);
  395.         }

  396.         /** {@inheritDoc} */
  397.         public SpacecraftState getInitialState() throws PropagationException {
  398.             return AbstractAnalyticalPropagator.this.getInitialState();
  399.         }

  400.         /** {@inheritDoc} */
  401.         public Frame getFrame() {
  402.             return AbstractAnalyticalPropagator.this.getFrame();
  403.         }

  404.         /** Replace the instance with a data transfer object for serialization.
  405.          * @return data transfer object that will be serialized
  406.          * @exception NotSerializableException if attitude provider or additional
  407.          * state provider is not serializable
  408.          */
  409.         private Object writeReplace() throws NotSerializableException {
  410.             return new DataTransferObject(minDate, maxDate, AbstractAnalyticalPropagator.this);
  411.         }

  412.     }

  413.     /** Internal class used only for serialization. */
  414.     private static class DataTransferObject implements Serializable {

  415.         /** Serializable UID. */
  416.         private static final long serialVersionUID = 20151117L;

  417.         /** Min date. */
  418.         private final AbsoluteDate minDate;

  419.         /** Max date. */
  420.         private final AbsoluteDate maxDate;

  421.         /** Underlying propagator. */
  422.         private final AbstractAnalyticalPropagator propagator;

  423.         /** Simple constructor.
  424.          * @param minDate min date
  425.          * @param maxDate max date
  426.          * @param propagator underlying propagator
  427.          */
  428.         DataTransferObject(final AbsoluteDate minDate, final AbsoluteDate maxDate,
  429.                            final AbstractAnalyticalPropagator propagator) {
  430.             this.minDate    = minDate;
  431.             this.maxDate    = maxDate;
  432.             this.propagator = propagator;
  433.         }

  434.         /** Replace the deserialized data transfer object with an {@link BoundedPropagatorView}.
  435.          * @return replacement {@link BoundedPropagatorView}
  436.          */
  437.         private Object readResolve() {
  438.             propagator.lastPropagationStart = minDate;
  439.             propagator.lastPropagationEnd   = maxDate;
  440.             return propagator.getGeneratedEphemeris();
  441.         }

  442.     }

  443.     /** Internal class for local propagation. */
  444.     private class BasicStepInterpolator implements OrekitStepInterpolator {

  445.         /** Global previous date. */
  446.         private AbsoluteDate globalPreviousDate;

  447.         /** Global current date. */
  448.         private AbsoluteDate globalCurrentDate;

  449.         /** Soft previous date. */
  450.         private AbsoluteDate softPreviousDate;

  451.         /** Soft current date. */
  452.         private AbsoluteDate softCurrentDate;

  453.         /** Interpolated state. */
  454.         private SpacecraftState interpolatedState;

  455.         /** Forward propagation indicator. */
  456.         private boolean forward;

  457.         /** Build a new instance from a basic propagator.
  458.          */
  459.         BasicStepInterpolator() {
  460.             globalPreviousDate = AbsoluteDate.PAST_INFINITY;
  461.             globalCurrentDate  = AbsoluteDate.PAST_INFINITY;
  462.             softPreviousDate   = AbsoluteDate.PAST_INFINITY;
  463.             softCurrentDate    = AbsoluteDate.PAST_INFINITY;
  464.         }

  465.         /** Restrict step range to a limited part of the global step.
  466.          * <p>
  467.          * This method can be used to restrict a step and make it appear
  468.          * as if the original step was smaller. Calling this method
  469.          * <em>only</em> changes the value returned by {@link #getPreviousDate()},
  470.          * it does not change any other property
  471.          * </p>
  472.          * @param softPreviousDate start of the restricted step
  473.          */
  474.         public void setSoftPreviousDate(final AbsoluteDate softPreviousDate) {
  475.             this.softPreviousDate = softPreviousDate;
  476.         }

  477.         /** Restrict step range to a limited part of the global step.
  478.          * <p>
  479.          * This method can be used to restrict a step and make it appear
  480.          * as if the original step was smaller. Calling this method
  481.          * <em>only</em> changes the value returned by {@link #getCurrentDate()},
  482.          * it does not change any other property
  483.          * </p>
  484.          * @param softCurrentDate end of the restricted step
  485.          */
  486.         public void setSoftCurrentDate(final AbsoluteDate softCurrentDate) {
  487.             this.softCurrentDate  = softCurrentDate;
  488.         }

  489.         /**
  490.          * Get the previous global grid point time.
  491.          * @return previous global grid point time
  492.          */
  493.         public AbsoluteDate getGlobalPreviousDate() {
  494.             return globalPreviousDate;
  495.         }

  496.         /**
  497.          * Get the current global grid point time.
  498.          * @return current global grid point time
  499.          */
  500.         public AbsoluteDate getGlobalCurrentDate() {
  501.             return globalCurrentDate;
  502.         }

  503.         /** {@inheritDoc} */
  504.         public AbsoluteDate getCurrentDate() {
  505.             return softCurrentDate;
  506.         }

  507.         /** {@inheritDoc} */
  508.         public AbsoluteDate getInterpolatedDate() {
  509.             return interpolatedState.getDate();
  510.         }

  511.         /** {@inheritDoc} */
  512.         public SpacecraftState getInterpolatedState() throws OrekitException {
  513.             return interpolatedState;
  514.         }

  515.         /** {@inheritDoc} */
  516.         public AbsoluteDate getPreviousDate() {
  517.             return softPreviousDate;
  518.         }

  519.         /** {@inheritDoc} */
  520.         public boolean isForward() {
  521.             return forward;
  522.         }

  523.         /** {@inheritDoc} */
  524.         public void setInterpolatedDate(final AbsoluteDate date) throws PropagationException {

  525.             // compute the basic spacecraft state
  526.             final SpacecraftState basicState = basicPropagate(date);

  527.             // add the additional states
  528.             interpolatedState = updateAdditionalStates(basicState);

  529.         }

  530.         /** Shift one step forward.
  531.          * Copy the current date into the previous date, hence preparing the
  532.          * interpolator for future calls to {@link #storeDate storeDate}
  533.          */
  534.         public void shift() {
  535.             globalPreviousDate = globalCurrentDate;
  536.             softPreviousDate   = globalPreviousDate;
  537.             softCurrentDate    = globalCurrentDate;
  538.         }

  539.         /** Store the current step date.
  540.          * @param date current date
  541.          * @exception PropagationException if the state cannot be propagated at specified date
  542.          */
  543.         public void storeDate(final AbsoluteDate date)
  544.             throws PropagationException {
  545.             globalCurrentDate = date;
  546.             softCurrentDate   = globalCurrentDate;
  547.             forward           = globalCurrentDate.compareTo(globalPreviousDate) >= 0;
  548.             setInterpolatedDate(globalCurrentDate);
  549.         }

  550.     }

  551. }