OperationalPlan.java

  1. package org.opentrafficsim.core.gtu.plan.operational;

  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. import java.util.List;

  6. import org.djunits.unit.AccelerationUnit;
  7. import org.djunits.unit.LengthUnit;
  8. import org.djunits.unit.SpeedUnit;
  9. import org.djunits.unit.TimeUnit;
  10. import org.djunits.value.vdouble.scalar.Acceleration;
  11. import org.djunits.value.vdouble.scalar.Duration;
  12. import org.djunits.value.vdouble.scalar.Length;
  13. import org.djunits.value.vdouble.scalar.Speed;
  14. import org.djunits.value.vdouble.scalar.Time;
  15. import org.opentrafficsim.core.geometry.OTSGeometryException;
  16. import org.opentrafficsim.core.geometry.OTSLine3D;
  17. import org.opentrafficsim.core.geometry.OTSPoint3D;
  18. import org.opentrafficsim.core.gtu.GTU;
  19. import org.opentrafficsim.core.math.Solver;

  20. import nl.tudelft.simulation.language.d3.DirectedPoint;

  21. /**
  22.  * An Operational plan describes a path through the world with a speed profile that a GTU intends to follow. The OperationalPlan
  23.  * can be updated or replaced at any time (including before it has been totally executed), for which a tactical planner is
  24.  * responsible. The operational plan is implemented using segments of the movement (time, location, speed, acceleration) that
  25.  * the GTU will use to plan its location and movement. Within an OperationalPlan the GTU cannot reverse direction along the path
  26.  * of movement. This ensures that the timeAtDistance method will never have to select among several valid solutions.
  27.  * <p>
  28.  * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  29.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  30.  * </p>
  31.  * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  32.  * initial version Nov 14, 2015 <br>
  33.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  34.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  35.  */
  36. public class OperationalPlan implements Serializable
  37. {
  38.     /** */
  39.     private static final long serialVersionUID = 20151114L;

  40.     /** The path to follow from a certain time till a certain time. */
  41.     private final OTSLine3D path;

  42.     /** The absolute start time when we start executing the path. */
  43.     private final Time startTime;

  44.     /** The GTU speed when we start executing the path. */
  45.     private final Speed startSpeed;

  46.     /** The segments that make up the path with an acceleration, constant speed or deceleration profile. */
  47.     private final List<OperationalPlan.Segment> operationalPlanSegmentList;

  48.     /** The duration of executing the entire operational plan. */
  49.     private final Duration totalDuration;

  50.     /** The length of the entire operational plan. */
  51.     private final Length totalLength;

  52.     /** The speed at the end of the operational plan. */
  53.     private final Speed endSpeed;

  54.     /** Is this operational plan a wait plan? */
  55.     private final boolean waitPlan;

  56.     /** GTU for debugging purposes. */
  57.     private final GTU gtu;

  58.     /**
  59.      * An array of relative start times of each segment, expressed in the SI unit, where the last time is the overall ending
  60.      * time of the operational plan.
  61.      */
  62.     private final double[] segmentStartTimesRelSI;

  63.     /**
  64.      * The maximum difference in the length of the path and the calculated driven distance implied by the segment list. The same
  65.      * constant is also used as a maximum between speeds of segments that should align in terms of speed.
  66.      */
  67.     static final double MAX_DELTA_SI = 1E-6;

  68.     /** The drifting speed. Speeds under this value will be cropped to zero. */
  69.     public static final double DRIFTING_SPEED_SI = 1E-3;

  70.     /**
  71.      * Construct an operational plan.
  72.      * @param gtu the GTU for debugging purposes
  73.      * @param path the path to follow from a certain time till a certain time. The path should have <i>at least</i> the length
  74.      * @param startTime the absolute start time when we start executing the path
  75.      * @param startSpeed the GTU speed when we start executing the path
  76.      * @param operationalPlanSegmentList the segments that make up the path with an acceleration, constant speed or deceleration
  77.      *            profile
  78.      * @throws OperationalPlanException when the path is too short for the operation
  79.      */
  80.     public OperationalPlan(final GTU gtu, final OTSLine3D path, final Time startTime, final Speed startSpeed,
  81.             final List<Segment> operationalPlanSegmentList) throws OperationalPlanException
  82.     {
  83.         this.waitPlan = false;
  84.         this.gtu = gtu;
  85.         this.startTime = startTime;
  86.         this.startSpeed = startSpeed;
  87.         this.operationalPlanSegmentList = operationalPlanSegmentList;
  88.         this.segmentStartTimesRelSI = new double[this.operationalPlanSegmentList.size() + 1];

  89.         // check the driven distance of the segments
  90.         Speed v0 = this.startSpeed;
  91.         double distanceSI = 0.0;
  92.         double durationSI = 0.0;
  93.         for (int i = 0; i < this.operationalPlanSegmentList.size(); i++)
  94.         {
  95.             Segment segment = this.operationalPlanSegmentList.get(i);
  96.             if (Math.abs(v0.si) < DRIFTING_SPEED_SI && segment.accelerationSI(0.0) == 0.0)
  97.             {
  98.                 v0 = Speed.ZERO;
  99.             }
  100.             segment.setV0(v0);
  101.             this.segmentStartTimesRelSI[i] = durationSI;
  102.             distanceSI += segment.distanceSI();
  103.             v0 = segment.endSpeed();
  104.             durationSI += segment.getDuration().si;
  105.         }
  106.         this.segmentStartTimesRelSI[this.segmentStartTimesRelSI.length - 1] = durationSI;
  107.         try
  108.         {
  109.             this.path = path.extract(0.0, Math.min(distanceSI, path.getLengthSI()));
  110.         }
  111.         catch (OTSGeometryException exception)
  112.         {
  113.             throw new OperationalPlanException(exception);
  114.         }
  115.         this.totalDuration = new Duration(durationSI, TimeUnit.SI);
  116.         this.totalLength = new Length(distanceSI, LengthUnit.SI);
  117.         this.endSpeed = v0;

  118.         double pathDistanceDeviation = Math.abs(this.totalLength.si - this.path.getLengthSI()) / this.totalLength.si;
  119.         if (pathDistanceDeviation < -0.01 || pathDistanceDeviation > 0.01)
  120.         {
  121.             System.err.println("path length and driven distance deviate more than 1% for operationalPlan: " + this);
  122.         }
  123.     }

  124.     /**
  125.      * Build a plan where the GTU will wait for a certain time.
  126.      * @param gtu the GTU for debugging purposes
  127.      * @param waitPoint the point at which the GTU will wait
  128.      * @param startTime the current time or a time in the future when the plan should start
  129.      * @param duration the waiting time
  130.      * @throws OperationalPlanException when construction of a waiting path fails
  131.      */
  132.     public OperationalPlan(final GTU gtu, final DirectedPoint waitPoint, final Time startTime, final Duration duration)
  133.             throws OperationalPlanException
  134.     {
  135.         this.waitPlan = true;
  136.         this.gtu = gtu;
  137.         this.startTime = startTime;
  138.         this.startSpeed = Speed.ZERO;
  139.         this.endSpeed = Speed.ZERO;
  140.         this.totalDuration = duration;
  141.         this.totalLength = Length.ZERO;

  142.         // make a path
  143.         OTSPoint3D p2 = new OTSPoint3D(waitPoint.x + Math.cos(waitPoint.getRotZ()), waitPoint.y + Math.sin(waitPoint.getRotZ()),
  144.                 waitPoint.z);
  145.         try
  146.         {
  147.             this.path = new OTSLine3D(new OTSPoint3D(waitPoint), p2);
  148.         }
  149.         catch (OTSGeometryException exception)
  150.         {
  151.             throw new OperationalPlanException(exception);
  152.         }

  153.         this.operationalPlanSegmentList = new ArrayList<>();
  154.         Segment segment = new SpeedSegment(duration);
  155.         segment.setV0(Speed.ZERO);
  156.         this.operationalPlanSegmentList.add(segment);
  157.         this.segmentStartTimesRelSI = new double[2];
  158.         this.segmentStartTimesRelSI[0] = 0.0;
  159.         this.segmentStartTimesRelSI[1] = duration.si;
  160.     }

  161.     /**
  162.      * Return the path that will be traveled. If the plan is a wait plan, the start point of the path is good; the end point of
  163.      * the path is bogus (should only be used to determine the orientation of the GTU).
  164.      * @return the path
  165.      */
  166.     public final OTSLine3D getPath()
  167.     {
  168.         return this.path;
  169.     }

  170.     /**
  171.      * Return the (absolute) start time of the operational plan.
  172.      * @return startTime
  173.      */
  174.     public final Time getStartTime()
  175.     {
  176.         return this.startTime;
  177.     }

  178.     /**
  179.      * Return the start speed of the entire plan.
  180.      * @return startSpeed
  181.      */
  182.     public final Speed getStartSpeed()
  183.     {
  184.         return this.startSpeed;
  185.     }

  186.     /**
  187.      * @return the end speed when completing the entire plan.
  188.      */
  189.     public final Speed getEndSpeed()
  190.     {
  191.         return this.endSpeed;
  192.     }

  193.     /**
  194.      * Return the segments (parts with constant speed, acceleration or deceleration) of the operational plan. <br>
  195.      * The caller MUST NOT MODIFY the returned object.
  196.      * @return operationalPlanSegmentList
  197.      */
  198.     public final List<OperationalPlan.Segment> getOperationalPlanSegmentList()
  199.     {
  200.         return this.operationalPlanSegmentList;
  201.     }

  202.     /**
  203.      * Return the time it will take to complete the entire operational plan.
  204.      * @return the time it will take to complete the entire operational plan
  205.      */
  206.     public final Duration getTotalDuration()
  207.     {
  208.         return this.totalDuration;
  209.     }

  210.     /**
  211.      * Return the distance the entire operational plan will cover.
  212.      * @return the distance of the entire operational plan
  213.      */
  214.     public final Length getTotalLength()
  215.     {
  216.         return this.totalLength;
  217.     }

  218.     /**
  219.      * Return the time it will take to complete the entire operational plan.
  220.      * @return the time it will take to complete the entire operational plan
  221.      */
  222.     public final Time getEndTime()
  223.     {
  224.         return this.startTime.plus(this.totalDuration);
  225.     }

  226.     /**
  227.      * Provide the end location of this operational plan as a DirectedPoint.
  228.      * @return the end location
  229.      */
  230.     public final DirectedPoint getEndLocation()
  231.     {
  232.         try
  233.         {
  234.             if (this.waitPlan)
  235.             {
  236.                 return this.path.getLocationFraction(0.0); // no move...
  237.             }
  238.             else
  239.             {
  240.                 return this.path.getLocationFraction(1.0);
  241.             }
  242.         }
  243.         catch (OTSGeometryException exception)
  244.         {
  245.             // should not happen -- only for fractions less than 0.0 or larger than 1.0.
  246.             throw new RuntimeException(exception);
  247.         }
  248.     }

  249.     /**
  250.      * Store a Segment and the progress within that segment in one Object.
  251.      * <p>
  252.      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. <br>
  253.      * All rights reserved. <br>
  254.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  255.      * <p>
  256.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Dec 16, 2015 <br>
  257.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  258.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  259.      */
  260.     private class SegmentProgress implements Serializable
  261.     {
  262.         /** */
  263.         private static final long serialVersionUID = 20160000L;

  264.         /** Active Segment. */
  265.         private final Segment segment;

  266.         /** Start time of the Segment. */
  267.         private final Time segmentStartTime;

  268.         /** Position on the path of the plan. */
  269.         private final Length segmentStartPosition;

  270.         /**
  271.          * Construct a new SegmentProgress object.
  272.          * @param segment Segment; the Segment
  273.          * @param segmentStartTime Time; the start time of the Segment
  274.          * @param segmentStartPosition Length; the position of the start of the segment on the path of the OperationalPlan
  275.          */
  276.         SegmentProgress(final Segment segment, final Time segmentStartTime, final Length segmentStartPosition)
  277.         {
  278.             this.segment = segment;
  279.             this.segmentStartTime = segmentStartTime;
  280.             this.segmentStartPosition = segmentStartPosition;
  281.         }

  282.         /**
  283.          * Retrieve the Segment.
  284.          * @return Segment
  285.          */
  286.         public final Segment getSegment()
  287.         {
  288.             return this.segment;
  289.         }

  290.         /**
  291.          * Retrieve the start time of the Segment.
  292.          * @return Time; the start time of the Segment
  293.          */
  294.         public final Time getSegmentStartTime()
  295.         {
  296.             return this.segmentStartTime;
  297.         }

  298.         /**
  299.          * Retrieve the fractionalPosition at the start of the Segment.
  300.          * @return double; the fractional position at the start of the Segment
  301.          */
  302.         public final Length getSegmentStartPosition()
  303.         {
  304.             return this.segmentStartPosition;
  305.         }

  306.         /** {@inheritDoc} */
  307.         @Override
  308.         public final String toString()
  309.         {
  310.             return String.format("SegmentProgress segment=%s startpos.rel=%s starttime.abs=%s", this.segment,
  311.                     this.segmentStartPosition, this.segmentStartTime);
  312.         }
  313.     }

  314.     /**
  315.      * Find the Segment and the progress within that Segment at a specified time.
  316.      * @param time Time; the time
  317.      * @return SegmentProgress; the Segment and progress within that segment, or null when no Segment applies to the specified
  318.      *         time
  319.      * @throws OperationalPlanException when SegmentProgress cannot be determined
  320.      */
  321.     private SegmentProgress getSegmentProgress(final Time time) throws OperationalPlanException
  322.     {
  323.         if (time.lt(this.startTime))
  324.         {
  325.             throw new OperationalPlanException(
  326.                     this.gtu + ", t = " + time + "SegmentProgress cannot be determined for time before startTime "
  327.                             + getStartTime() + " of this OperationalPlan");
  328.         }
  329.         double cumulativeDistance = 0;
  330.         for (int i = 0; i < this.segmentStartTimesRelSI.length - 1; i++)
  331.         {
  332.             if (this.startTime.si + this.segmentStartTimesRelSI[i + 1] >= time.si)
  333.             {
  334.                 return new SegmentProgress(this.operationalPlanSegmentList.get(i),
  335.                         new Time(this.startTime.si + this.segmentStartTimesRelSI[i], TimeUnit.SI),
  336.                         new Length(cumulativeDistance, LengthUnit.SI));
  337.             }
  338.         }
  339.         throw new OperationalPlanException(this.gtu + ", t = " + time
  340.                 + " SegmentProgress cannot be determined for time after endTime " + getEndTime() + " of this OperationalPlan");
  341.     }

  342.     /**
  343.      * Return the time when the GTU will reach the given distance.
  344.      * @param distance the distance to calculate the time for
  345.      * @return the time it will take to have traveled the given distance
  346.      */
  347.     public final Time timeAtDistance(final Length distance)
  348.     {
  349.         double remainingDistanceSI = distance.si;
  350.         double timeAtStartOfSegment = this.startTime.si;
  351.         for (Segment segment : this.operationalPlanSegmentList)
  352.         {
  353.             double distanceOfSegment = segment.distanceSI();
  354.             if (distanceOfSegment > remainingDistanceSI)
  355.             {
  356.                 return new Time(
  357.                         timeAtStartOfSegment + segment.timeAtDistance(new Length(remainingDistanceSI, LengthUnit.SI)).si,
  358.                         TimeUnit.SI);
  359.             }
  360.             remainingDistanceSI -= distanceOfSegment;
  361.             timeAtStartOfSegment += segment.getDurationSI();
  362.         }
  363.         return new Time(Double.NaN, TimeUnit.SI);
  364.     }

  365.     /**
  366.      * Calculate the location at the given time.
  367.      * @param time the absolute time to look for a location
  368.      * @return the location at the given time.
  369.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  370.      */
  371.     public final DirectedPoint getLocation(final Time time) throws OperationalPlanException
  372.     {
  373.         SegmentProgress sp = getSegmentProgress(time);
  374.         Segment segment = sp.getSegment();
  375.         Duration deltaT = time.minus(sp.getSegmentStartTime());
  376.         double distanceTraveledInSegment = segment.distanceSI(deltaT.si);
  377.         double startDistance = sp.getSegmentStartPosition().si;
  378.         double fraction = (startDistance + distanceTraveledInSegment) / this.path.getLengthSI();
  379.         DirectedPoint p = new DirectedPoint();
  380.         try
  381.         {
  382.             p = this.path.getLocationFraction(fraction, 0.01);
  383.         }
  384.         catch (OTSGeometryException exception)
  385.         {
  386.             System.err.println("OperationalPlan.getLocation(): " + exception.getMessage());
  387.             p = this.path.getLocationFractionExtended(fraction);
  388.         }
  389.         p.setZ(p.getZ() + 0.001);
  390.         return p;
  391.     }

  392.     /**
  393.      * Calculate the speed of the GTU after the given duration since the start of the plan.
  394.      * @param time the relative time to look for a location
  395.      * @return the location after the given duration since the start of the plan.
  396.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  397.      */
  398.     public final Speed getSpeed(final Duration time) throws OperationalPlanException
  399.     {
  400.         return getSpeed(time.plus(this.startTime));
  401.     }

  402.     /**
  403.      * Calculate the speed of the GTU at the given time.
  404.      * @param time the absolute time to look for a location
  405.      * @return the location at the given time.
  406.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  407.      */
  408.     public final Speed getSpeed(final Time time) throws OperationalPlanException
  409.     {
  410.         SegmentProgress sp = getSegmentProgress(time);
  411.         return new Speed(sp.getSegment().speedSI(time.minus(sp.getSegmentStartTime()).si), SpeedUnit.SI);
  412.     }

  413.     /**
  414.      * Calculate the acceleration of the GTU after the given duration since the start of the plan.
  415.      * @param time the relative time to look for a location
  416.      * @return the location after the given duration since the start of the plan.
  417.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  418.      */
  419.     public final Acceleration getAcceleration(final Duration time) throws OperationalPlanException
  420.     {
  421.         return getAcceleration(time.plus(this.startTime));
  422.     }

  423.     /**
  424.      * Calculate the acceleration of the GTU at the given time.
  425.      * @param time the absolute time to look for a location
  426.      * @return the location at the given time.
  427.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  428.      */
  429.     public final Acceleration getAcceleration(final Time time) throws OperationalPlanException
  430.     {
  431.         SegmentProgress sp = getSegmentProgress(time);
  432.         return new Acceleration(sp.getSegment().accelerationSI(time.minus(sp.getSegmentStartTime()).si), AccelerationUnit.SI);
  433.     }

  434.     /**
  435.      * Calculate the location after the given duration since the start of the plan.
  436.      * @param time the relative time to look for a location
  437.      * @return the location after the given duration since the start of the plan.
  438.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  439.      */
  440.     public final DirectedPoint getLocation(final Duration time) throws OperationalPlanException
  441.     {
  442.         double distanceSI = getTraveledDistanceSI(time);
  443.         return this.path.getLocationExtendedSI(distanceSI);
  444.     }

  445.     /**
  446.      * Calculate the distance traveled as part of this plan after the given duration since the start of the plan. This method
  447.      * returns the traveled distance as a double in SI units.
  448.      * @param duration the relative time to calculate the traveled distance
  449.      * @return the distance traveled as part of this plan after the given duration since the start of the plan.
  450.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  451.      */
  452.     public final double getTraveledDistanceSI(final Duration duration) throws OperationalPlanException
  453.     {
  454.         Time absTime = duration.plus(this.startTime);
  455.         SegmentProgress sp = getSegmentProgress(absTime);
  456.         return sp.getSegment().distanceSI(absTime.minus(sp.getSegmentStartTime()).si);
  457.     }

  458.     /**
  459.      * Calculate the distance traveled as part of this plan after the given duration since the start of the plan.
  460.      * @param duration the relative time to calculate the traveled distance
  461.      * @return the distance traveled as part of this plan after the given duration since the start of the plan.
  462.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  463.      */
  464.     public final Length getTraveledDistance(final Duration duration) throws OperationalPlanException
  465.     {
  466.         return new Length(getTraveledDistanceSI(duration), LengthUnit.SI);
  467.     }

  468.     /**
  469.      * Calculate the distance traveled as part of this plan at the given absolute time. This method returns the traveled
  470.      * distance as a double in SI units.
  471.      * @param time the absolute time to calculate the traveled distance for as part of this plan
  472.      * @return the distance traveled as part of this plan at the given time
  473.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  474.      */
  475.     public final double getTraveledDistanceSI(final Time time) throws OperationalPlanException
  476.     {
  477.         return getTraveledDistanceSI(time.minus(this.startTime));
  478.     }

  479.     /**
  480.      * Calculate the distance traveled as part of this plan at the given absolute time.
  481.      * @param time the absolute time to calculate the traveled distance for as part of this plan
  482.      * @return the distance traveled as part of this plan at the given time
  483.      * @throws OperationalPlanException when the time is after the validity of the operational plan
  484.      */
  485.     public final Length getTraveledDistance(final Time time) throws OperationalPlanException
  486.     {
  487.         return new Length(getTraveledDistanceSI(time.minus(this.startTime)), LengthUnit.SI);
  488.     }

  489.     /** {@inheritDoc} */
  490.     @SuppressWarnings("checkstyle:designforextension")
  491.     @Override
  492.     public int hashCode()
  493.     {
  494.         final int prime = 31;
  495.         int result = 1;
  496.         result = prime * result + ((this.operationalPlanSegmentList == null) ? 0 : this.operationalPlanSegmentList.hashCode());
  497.         result = prime * result + ((this.path == null) ? 0 : this.path.hashCode());
  498.         result = prime * result + ((this.startSpeed == null) ? 0 : this.startSpeed.hashCode());
  499.         result = prime * result + ((this.startTime == null) ? 0 : this.startTime.hashCode());
  500.         return result;
  501.     }

  502.     /** {@inheritDoc} */
  503.     @SuppressWarnings({ "checkstyle:needbraces", "checkstyle:designforextension" })
  504.     @Override
  505.     public boolean equals(final Object obj)
  506.     {
  507.         if (this == obj)
  508.             return true;
  509.         if (obj == null)
  510.             return false;
  511.         if (getClass() != obj.getClass())
  512.             return false;
  513.         OperationalPlan other = (OperationalPlan) obj;
  514.         if (this.operationalPlanSegmentList == null)
  515.         {
  516.             if (other.operationalPlanSegmentList != null)
  517.                 return false;
  518.         }
  519.         else if (!this.operationalPlanSegmentList.equals(other.operationalPlanSegmentList))
  520.             return false;
  521.         if (this.path == null)
  522.         {
  523.             if (other.path != null)
  524.                 return false;
  525.         }
  526.         else if (!this.path.equals(other.path))
  527.             return false;
  528.         if (this.startSpeed == null)
  529.         {
  530.             if (other.startSpeed != null)
  531.                 return false;
  532.         }
  533.         else if (!this.startSpeed.equals(other.startSpeed))
  534.             return false;
  535.         if (this.startTime == null)
  536.         {
  537.             if (other.startTime != null)
  538.                 return false;
  539.         }
  540.         else if (!this.startTime.equals(other.startTime))
  541.             return false;
  542.         return true;
  543.     }

  544.     /** {@inheritDoc} */
  545.     @SuppressWarnings("checkstyle:designforextension")
  546.     @Override
  547.     public String toString()
  548.     {
  549.         return "OperationalPlan [path=" + this.path + ", startTime=" + this.startTime + ", startSpeed=" + this.startSpeed
  550.                 + ", operationalPlanSegmentList=" + this.operationalPlanSegmentList + ", totalDuration=" + this.totalDuration
  551.                 + ", segmentStartTimesSI=" + Arrays.toString(this.segmentStartTimesRelSI) + ", endSpeed = " + this.endSpeed
  552.                 + "]";
  553.     }

  554.     /****************************************************************************************************************/
  555.     /******************************************** SEGMENT DEFINITIONS ***********************************************/
  556.     /****************************************************************************************************************/

  557.     /**
  558.      * The segment of an operational plan contains a part of the speed profile of a movement in which some of the variables
  559.      * determining movement (speed, acceleration) are constant.
  560.      * <p>
  561.      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. <br>
  562.      * All rights reserved. <br>
  563.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  564.      * </p>
  565.      * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  566.      * initial version Nov 14, 2015 <br>
  567.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  568.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  569.      */
  570.     public abstract static class Segment implements Serializable
  571.     {
  572.         /** */
  573.         private static final long serialVersionUID = 20151114L;

  574.         /** The duration of the acceleration or speed for this segment. */
  575.         @SuppressWarnings("checkstyle:visibilitymodifier")
  576.         protected final Duration duration;

  577.         /** The initial speed for this segment. */
  578.         @SuppressWarnings("checkstyle:visibilitymodifier")
  579.         protected Speed v0;

  580.         /**
  581.          * @param duration the duration of the acceleration or speed for this segment
  582.          */
  583.         public Segment(final Duration duration)
  584.         {
  585.             super();
  586.             this.duration = duration;
  587.         }

  588.         /**
  589.          * @param v0 the initial speed of this segment; called from the Operational Plan constructor.
  590.          */
  591.         final void setV0(final Speed v0)
  592.         {
  593.             this.v0 = v0;
  594.         }

  595.         /**
  596.          * @return duration the duration of the acceleration or speed for this segment
  597.          */
  598.         public final Duration getDuration()
  599.         {
  600.             return this.duration;
  601.         }

  602.         /**
  603.          * @return duration the duration of the acceleration or speed for this segment
  604.          */
  605.         public final double getDurationSI()
  606.         {
  607.             return this.duration.si;
  608.         }

  609.         /**
  610.          * Calculate the distance covered by a GTU in this segment.
  611.          * @return distance covered
  612.          */
  613.         final double distanceSI()
  614.         {
  615.             return distanceSI(getDuration().si);
  616.         }

  617.         /**
  618.          * Calculate the distance covered by a GTU in this segment after relative time t.
  619.          * @param t the relative time since starting this segment for which to calculate the distance covered
  620.          * @return distance covered
  621.          */
  622.         abstract double distanceSI(double t);

  623.         /**
  624.          * Calculate the speed of a GTU in this segment after relative time t.
  625.          * @param t the relative time since starting this segment for which to calculate the speed
  626.          * @return speed at relative time t
  627.          */
  628.         abstract double speedSI(double t);

  629.         /**
  630.          * Calculate the acceleration of a GTU in this segment after relative time t.
  631.          * @param t the relative time since starting this segment for which to calculate the acceleration
  632.          * @return acceleration at relative time t
  633.          */
  634.         abstract double accelerationSI(double t);

  635.         /**
  636.          * Calculate the end speed for this segment.
  637.          * @return speed at end of the segment
  638.          */
  639.         abstract Speed endSpeed();

  640.         /**
  641.          * Calculate the time it takes for the GTU to travel from the start of this Segment to the specified distance within
  642.          * this Segment.
  643.          * @param distance the distance for which the travel time has to be calculated
  644.          * @return the time at distance
  645.          */
  646.         abstract Duration timeAtDistance(final Length distance);

  647.         /** {@inheritDoc} */
  648.         @SuppressWarnings("checkstyle:designforextension")
  649.         @Override
  650.         public int hashCode()
  651.         {
  652.             final int prime = 31;
  653.             int result = 1;
  654.             result = prime * result + ((this.duration == null) ? 0 : this.duration.hashCode());
  655.             result = prime * result + ((this.v0 == null) ? 0 : this.v0.hashCode());
  656.             return result;
  657.         }

  658.         /** {@inheritDoc} */
  659.         @SuppressWarnings({ "checkstyle:needbraces", "checkstyle:designforextension" })
  660.         @Override
  661.         public boolean equals(final Object obj)
  662.         {
  663.             if (this == obj)
  664.                 return true;
  665.             if (obj == null)
  666.                 return false;
  667.             if (getClass() != obj.getClass())
  668.                 return false;
  669.             Segment other = (Segment) obj;
  670.             if (this.duration == null)
  671.             {
  672.                 if (other.duration != null)
  673.                     return false;
  674.             }
  675.             else if (!this.duration.equals(other.duration))
  676.                 return false;
  677.             if (this.v0 == null)
  678.             {
  679.                 if (other.v0 != null)
  680.                     return false;
  681.             }
  682.             else if (!this.v0.equals(other.v0))
  683.                 return false;
  684.             return true;
  685.         }
  686.     }

  687.     /**
  688.      * The segment of an operational plan in which the acceleration is constant.
  689.      * <p>
  690.      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. <br>
  691.      * All rights reserved. <br>
  692.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  693.      * </p>
  694.      * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  695.      * initial version Nov 14, 2015 <br>
  696.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  697.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  698.      */
  699.     public static class AccelerationSegment extends Segment
  700.     {
  701.         /** */
  702.         private static final long serialVersionUID = 20151114L;

  703.         /** The acceleration for the given duration. */
  704.         private final Acceleration acceleration;

  705.         /**
  706.          * @param duration the duration of the constant acceleration for this segment
  707.          * @param acceleration the acceleration for the given duration
  708.          */
  709.         public AccelerationSegment(final Duration duration, final Acceleration acceleration)
  710.         {
  711.             super(duration);
  712.             this.acceleration = acceleration;
  713.         }

  714.         /** {@inheritDoc} */
  715.         @Override
  716.         final double distanceSI(final double t)
  717.         {
  718.             return this.v0.si * t + 0.5 * this.acceleration.si * t * t;
  719.         }

  720.         /** {@inheritDoc} */
  721.         @Override
  722.         final double accelerationSI(final double t)
  723.         {
  724.             return this.acceleration.si;
  725.         }

  726.         /** {@inheritDoc} */
  727.         @Override
  728.         final double speedSI(final double t)
  729.         {
  730.             return this.v0.si + this.acceleration.si * t;
  731.         }

  732.         /** {@inheritDoc} */
  733.         @Override
  734.         final Speed endSpeed()
  735.         {
  736.             return this.v0.plus(this.acceleration.multiplyBy(getDuration()));
  737.         }

  738.         /** {@inheritDoc} */
  739.         @Override
  740.         public final int hashCode()
  741.         {
  742.             final int prime = 31;
  743.             int result = super.hashCode();
  744.             result = prime * result + ((this.acceleration == null) ? 0 : this.acceleration.hashCode());
  745.             return result;
  746.         }

  747.         /** {@inheritDoc} */
  748.         @Override
  749.         @SuppressWarnings("checkstyle:needbraces")
  750.         public final boolean equals(final Object obj)
  751.         {
  752.             if (this == obj)
  753.                 return true;
  754.             if (!super.equals(obj))
  755.                 return false;
  756.             if (getClass() != obj.getClass())
  757.                 return false;
  758.             AccelerationSegment other = (AccelerationSegment) obj;
  759.             if (this.acceleration == null)
  760.             {
  761.                 if (other.acceleration != null)
  762.                     return false;
  763.             }
  764.             else if (!this.acceleration.equals(other.acceleration))
  765.                 return false;
  766.             return true;
  767.         }

  768.         /** {@inheritDoc} */
  769.         @Override
  770.         public final Duration timeAtDistance(final Length distance)
  771.         {
  772.             double[] solutions = Solver.solve(this.acceleration.si / 2, this.v0.si, -distance.si);
  773.             // Find the solution that occurs within our duration (there should be only one).
  774.             for (double solution : solutions)
  775.             {
  776.                 if (solution >= 0 && solution <= this.duration.si)
  777.                 {
  778.                     return new Duration(solution, TimeUnit.SI);
  779.                 }
  780.             }
  781.             System.err.println("AccelerationSegment " + this + " timeAtDistance( " + distance + ") failed");
  782.             return null; // No valid solution
  783.         }

  784.         /** {@inheritDoc} */
  785.         @Override
  786.         public final String toString()
  787.         {
  788.             return "AccelerationSegment [t=" + this.duration + ", v0=" + this.v0 + ", a=" + this.acceleration + "]";
  789.         }

  790.     }

  791.     /**
  792.      * The segment of an operational plan in which the speed is constant.
  793.      * <p>
  794.      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. <br>
  795.      * All rights reserved. <br>
  796.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  797.      * </p>
  798.      * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  799.      * initial version Nov 14, 2015 <br>
  800.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  801.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  802.      */
  803.     public static class SpeedSegment extends Segment
  804.     {
  805.         /** */
  806.         private static final long serialVersionUID = 20151114L;

  807.         /**
  808.          * @param duration the duration of the constant speed for this segment
  809.          */
  810.         public SpeedSegment(final Duration duration)
  811.         {
  812.             super(duration);
  813.         }

  814.         /** {@inheritDoc} */
  815.         @Override
  816.         final double distanceSI(final double t)
  817.         {
  818.             return this.v0.si * t;
  819.         }

  820.         /** {@inheritDoc} */
  821.         @Override
  822.         final double accelerationSI(final double t)
  823.         {
  824.             return 0.0;
  825.         }

  826.         /** {@inheritDoc} */
  827.         @Override
  828.         final double speedSI(final double t)
  829.         {
  830.             return this.v0.si;
  831.         }

  832.         /** {@inheritDoc} */
  833.         @Override
  834.         final Speed endSpeed()
  835.         {
  836.             return this.v0;
  837.         }

  838.         /**
  839.          * @return speed
  840.          */
  841.         public final Speed getSpeed()
  842.         {
  843.             return this.v0;
  844.         }

  845.         /** {@inheritDoc} */
  846.         @Override
  847.         public final Duration timeAtDistance(final Length distance)
  848.         {
  849.             double[] solution = Solver.solve(this.v0.si, -distance.si);
  850.             if (solution.length > 0 && solution[0] >= 0 && solution[0] <= getDurationSI())
  851.             {
  852.                 return new Duration(solution[0], TimeUnit.SI);
  853.             }
  854.             System.err.println("SpeedSegment " + this + " timeAtDistance( " + distance + ") failed");
  855.             return null; // No valid solution
  856.         }

  857.         /** {@inheritDoc} */
  858.         @Override
  859.         public final String toString()
  860.         {
  861.             return "SpeedSegment [t=" + this.duration + ", v0=" + this.v0 + "]";
  862.         }

  863.     }

  864. }