View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.util;
2   
3   import java.io.Serializable;
4   
5   import org.djunits.unit.DurationUnit;
6   import org.djunits.unit.LengthUnit;
7   import org.djunits.value.vdouble.scalar.Acceleration;
8   import org.djunits.value.vdouble.scalar.Duration;
9   import org.djunits.value.vdouble.scalar.Length;
10  import org.djunits.value.vdouble.scalar.Speed;
11  import org.opentrafficsim.base.parameters.ParameterException;
12  import org.opentrafficsim.base.parameters.Parameters;
13  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
14  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
15  
16  /**
17   * Utility class that stores duration and end-speed for a given anticipated movement.
18   * <p>
19   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
21   * </p>
22   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
23   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
24   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
25   * @param duration Duration; duration of movement
26   * @param endSpeed Speed; end speed of movement
27   */
28  public final record AnticipationInfo(Duration duration, Speed endSpeed) implements Serializable
29  {
30  
31      /** */
32      private static final long serialVersionUID = 20160811L;
33  
34      /**
35       * Returns info of the anticipation assuming constant acceleration.
36       * @param distance Length; distance to cover
37       * @param initialSpeed Speed; initial speed
38       * @param acceleration Acceleration; (assumed) acceleration
39       * @return duration to cover given distance with given initial speed and acceleration
40       */
41      public static AnticipationInfo anticipateMovement(final Length distance, final Speed initialSpeed,
42              final Acceleration acceleration)
43      {
44          return anticipateMovementSpeedLimited(distance, initialSpeed, acceleration, Speed.POSITIVE_INFINITY);
45      }
46  
47      /**
48       * Returns info of the anticipation assuming constant acceleration, without exceeding maximum speed.
49       * @param distance Length; distance to cover
50       * @param initialSpeed Speed; initial speed
51       * @param acceleration Acceleration; (assumed) acceleration
52       * @param maxSpeed Speed; maximum speed
53       * @return duration to cover given distance with given initial speed and acceleration, without exceeding maximum speed
54       */
55      public static AnticipationInfo anticipateMovementSpeedLimited(final Length distance, final Speed initialSpeed,
56              final Acceleration acceleration, final Speed maxSpeed)
57      {
58          if (distance.lt0())
59          {
60              return new AnticipationInfo(Duration.ZERO, initialSpeed);
61          }
62          // solve constant speed movement
63          if (acceleration.eq(Acceleration.ZERO))
64          {
65              if (initialSpeed.gt0())
66              {
67                  return new AnticipationInfo(distance.divide(initialSpeed), initialSpeed);
68              }
69              // stand-still, so infinite
70              return new AnticipationInfo(new Duration(Double.POSITIVE_INFINITY, DurationUnit.SI), Speed.ZERO);
71          }
72          // solve parabolic movement
73          double tmp = initialSpeed.si * initialSpeed.si + 2.0 * acceleration.si * distance.si;
74          if (tmp < 0)
75          {
76              // will never cover distance due to deceleration
77              return new AnticipationInfo(new Duration(Double.POSITIVE_INFINITY, DurationUnit.SI), Speed.ZERO);
78          }
79          // parabolic solution
80          Duration d = new Duration((Math.sqrt(tmp) - initialSpeed.si) / acceleration.si, DurationUnit.SI);
81          // check max speed
82          Speed endSpeed = initialSpeed.plus(acceleration.times(d));
83          if (endSpeed.le(maxSpeed))
84          {
85              return new AnticipationInfo(d, endSpeed);
86          }
87          // maximum speed exceeded, calculate in two steps
88          Duration d1 = maxSpeed.minus(initialSpeed).divide(acceleration);
89          Length x2 = new Length(distance.si - initialSpeed.si * d1.si - .5 * acceleration.si * d1.si * d1.si, LengthUnit.SI);
90          return new AnticipationInfo(d1.plus(x2.divide(maxSpeed)), maxSpeed);
91      }
92  
93      /**
94       * Returns info of the anticipation using free acceleration from car-following model.
95       * @param distance Length; distance to cover
96       * @param initialSpeed Speed; initial speed
97       * @param parameters Parameters; parameters of the anticipated GTU
98       * @param carFollowingModel CarFollowingModel; car-following model of the anticipated GTU
99       * @param speedLimitInfo SpeedLimitInfo; speed limit info of the anticipated GTU
100      * @param timeStep Duration; time step to use
101      * @return info regarding anticipation of movement
102      * @throws ParameterException if parameter is not defined
103      */
104     public static AnticipationInfo anticipateMovementFreeAcceleration(final Length distance, final Speed initialSpeed,
105             final Parameters parameters, final CarFollowingModel carFollowingModel, final SpeedLimitInfo speedLimitInfo,
106             final Duration timeStep) throws ParameterException
107     {
108         if (distance.lt0())
109         {
110             return new AnticipationInfo(Duration.ZERO, initialSpeed);
111         }
112         Duration out = Duration.ZERO;
113         if (distance.lt0())
114         {
115             return new AnticipationInfo(out, initialSpeed);
116         }
117         Length xCumul = Length.ZERO;
118         Speed speed = initialSpeed;
119         while (xCumul.lt(distance))
120         {
121             Acceleration a = CarFollowingUtil.freeAcceleration(carFollowingModel, parameters, speed, speedLimitInfo);
122             Length add = new Length(speed.si * timeStep.si + .5 * a.si * timeStep.si * timeStep.si, LengthUnit.SI);
123             Length remain = distance.minus(xCumul);
124             if (add.lt(remain))
125             {
126                 xCumul = xCumul.plus(add);
127                 speed = speed.plus(a.times(timeStep));
128                 out = out.plus(timeStep);
129             }
130             else
131             {
132                 Duration timeInStep;
133                 double tmp = Math.sqrt(2 * a.si * remain.si + speed.si * speed.si) - speed.si;
134                 if (tmp < 0.000001)
135                 {
136                     // (near) constant speed
137                     timeInStep = remain.divide(speed);
138                 }
139                 else
140                 {
141                     timeInStep = new Duration(tmp / a.si, DurationUnit.SI);
142                     speed = speed.plus(a.times(timeInStep));
143                 }
144                 out = out.plus(timeInStep);
145                 return new AnticipationInfo(out, speed);
146             }
147         }
148         // should not happen
149         throw new RuntimeException("Distance for anticipation of conflict movement is surpassed.");
150     }
151 
152 }