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