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