AnticipationInfo.java
package org.opentrafficsim.road.gtu.lane.tactical.util;
import java.io.Serializable;
import org.djunits.unit.DurationUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.value.vdouble.scalar.Acceleration;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.base.parameters.Parameters;
import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
/**
* Utility class that stores duration and end-speed for a given anticipated movement.
* <p>
* Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
* @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
* @param duration Duration; duration of movement
* @param endSpeed Speed; end speed of movement
*/
public final record AnticipationInfo(Duration duration, Speed endSpeed) implements Serializable
{
/** */
private static final long serialVersionUID = 20160811L;
/**
* Returns info of the anticipation assuming constant acceleration.
* @param distance Length; distance to cover
* @param initialSpeed Speed; initial speed
* @param acceleration Acceleration; (assumed) acceleration
* @return duration to cover given distance with given initial speed and acceleration
*/
public static AnticipationInfo anticipateMovement(final Length distance, final Speed initialSpeed,
final Acceleration acceleration)
{
return anticipateMovementSpeedLimited(distance, initialSpeed, acceleration, Speed.POSITIVE_INFINITY);
}
/**
* Returns info of the anticipation assuming constant acceleration, without exceeding maximum speed.
* @param distance Length; distance to cover
* @param initialSpeed Speed; initial speed
* @param acceleration Acceleration; (assumed) acceleration
* @param maxSpeed Speed; maximum speed
* @return duration to cover given distance with given initial speed and acceleration, without exceeding maximum speed
*/
public static AnticipationInfo anticipateMovementSpeedLimited(final Length distance, final Speed initialSpeed,
final Acceleration acceleration, final Speed maxSpeed)
{
if (distance.lt0())
{
return new AnticipationInfo(Duration.ZERO, initialSpeed);
}
// solve constant speed movement
if (acceleration.eq(Acceleration.ZERO))
{
if (initialSpeed.gt0())
{
return new AnticipationInfo(distance.divide(initialSpeed), initialSpeed);
}
// stand-still, so infinite
return new AnticipationInfo(new Duration(Double.POSITIVE_INFINITY, DurationUnit.SI), Speed.ZERO);
}
// solve parabolic movement
double tmp = initialSpeed.si * initialSpeed.si + 2.0 * acceleration.si * distance.si;
if (tmp < 0)
{
// will never cover distance due to deceleration
return new AnticipationInfo(new Duration(Double.POSITIVE_INFINITY, DurationUnit.SI), Speed.ZERO);
}
// parabolic solution
Duration d = new Duration((Math.sqrt(tmp) - initialSpeed.si) / acceleration.si, DurationUnit.SI);
// check max speed
Speed endSpeed = initialSpeed.plus(acceleration.times(d));
if (endSpeed.le(maxSpeed))
{
return new AnticipationInfo(d, endSpeed);
}
// maximum speed exceeded, calculate in two steps
Duration d1 = maxSpeed.minus(initialSpeed).divide(acceleration);
Length x2 = new Length(distance.si - initialSpeed.si * d1.si - .5 * acceleration.si * d1.si * d1.si, LengthUnit.SI);
return new AnticipationInfo(d1.plus(x2.divide(maxSpeed)), maxSpeed);
}
/**
* Returns info of the anticipation using free acceleration from car-following model.
* @param distance Length; distance to cover
* @param initialSpeed Speed; initial speed
* @param parameters Parameters; parameters of the anticipated GTU
* @param carFollowingModel CarFollowingModel; car-following model of the anticipated GTU
* @param speedLimitInfo SpeedLimitInfo; speed limit info of the anticipated GTU
* @param timeStep Duration; time step to use
* @return info regarding anticipation of movement
* @throws ParameterException if parameter is not defined
*/
public static AnticipationInfo anticipateMovementFreeAcceleration(final Length distance, final Speed initialSpeed,
final Parameters parameters, final CarFollowingModel carFollowingModel, final SpeedLimitInfo speedLimitInfo,
final Duration timeStep) throws ParameterException
{
if (distance.lt0())
{
return new AnticipationInfo(Duration.ZERO, initialSpeed);
}
Duration out = Duration.ZERO;
if (distance.lt0())
{
return new AnticipationInfo(out, initialSpeed);
}
Length xCumul = Length.ZERO;
Speed speed = initialSpeed;
while (xCumul.lt(distance))
{
Acceleration a = CarFollowingUtil.freeAcceleration(carFollowingModel, parameters, speed, speedLimitInfo);
Length add = new Length(speed.si * timeStep.si + .5 * a.si * timeStep.si * timeStep.si, LengthUnit.SI);
Length remain = distance.minus(xCumul);
if (add.lt(remain))
{
xCumul = xCumul.plus(add);
speed = speed.plus(a.times(timeStep));
out = out.plus(timeStep);
}
else
{
Duration timeInStep;
double tmp = Math.sqrt(2 * a.si * remain.si + speed.si * speed.si) - speed.si;
if (tmp < 0.000001)
{
// (near) constant speed
timeInStep = remain.divide(speed);
}
else
{
timeInStep = new Duration(tmp / a.si, DurationUnit.SI);
speed = speed.plus(a.times(timeInStep));
}
out = out.plus(timeInStep);
return new AnticipationInfo(out, speed);
}
}
// should not happen
throw new RuntimeException("Distance for anticipation of conflict movement is surpassed.");
}
}