Segment.java
package org.opentrafficsim.core.gtu.plan.operational;
import java.io.Serializable;
import org.djunits.unit.DurationUnit;
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.djutils.exceptions.Throw;
import org.opentrafficsim.core.math.Solver;
/**
* The segment of an operational plan contains a part of the speed profile of a movement in which some of the variables
* determining movement (speed, acceleration) are constant.
* <p>
* Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. <br>
* 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 isStandStill boolean; whether this is a stand still segment
* @param startSpeed Speed; start speed.
* @param duration Duration; the duration of the acceleration for this segment.
* @param acceleration Acceleration; acceleration of this segment.
*/
public record Segment(boolean isStandStill, Speed startSpeed, Duration duration, Acceleration acceleration)
implements Serializable
{
/** */
private static final long serialVersionUID = 20230421L;
/**
* Constructor.
* @param startSpeed Speed; start speed.
* @param duration Duration; the duration of the acceleration for this segment.
* @param acceleration Acceleration; acceleration of this segment.
*/
public Segment(final Speed startSpeed, final Duration duration, final Acceleration acceleration)
{
this(false, startSpeed, duration, acceleration);
}
/**
* Constructor for stand-still segment.
* @param duration Duration; duration.
*/
private Segment(final Duration duration)
{
this(true, Speed.ZERO, duration, Acceleration.ZERO);
}
/**
* Returns the speed at the end of the segment.
* @return Speed; speed at the end of the segment.
*/
public Speed endSpeed()
{
return speed(this.duration);
}
/**
* Returns the total distance traveled during the segment.
* @return Length; total distance traveled during the segment.
*/
public Length totalDistance()
{
return distance(this.duration);
}
/**
* Returns the speed at the given duration relative to the start of the segment.
* @param duration Duration; duration since start time of segment.
* @return Speed; speed at the given duration relative to the start of the segment.
*/
public Speed speed(final Duration duration)
{
Throw.when(duration.lt0(), IllegalArgumentException.class, "Duration must be positive.");
Throw.when(duration.gt(this.duration), IllegalArgumentException.class, "Duration is beyond duration of segment.");
if (this.isStandStill)
{
return Speed.ZERO;
}
return Speed.instantiateSI(this.startSpeed.si + duration.si * this.acceleration.si);
}
/**
* Return the distance traveled at the given duration relative to the start of the segment.
* @param duration Duration; duration since start time of segment.
* @return Length; distance traveled at the given duration relative to the start of the segment.
*/
public Length distance(final Duration duration)
{
Throw.when(duration.lt0(), IllegalArgumentException.class, "Duration must be positive.");
Throw.when(duration.gt(this.duration), IllegalArgumentException.class, "Duration is beyond duration of segment.");
if (this.isStandStill)
{
return Length.ZERO;
}
return Length.instantiateSI(duration.si * this.startSpeed.si + .5 * this.acceleration.si * duration.si * duration.si);
}
/**
* Returns the duration within the segment it takes to travel the distance from the start of the segment.
* @param distance Length; distance from the start of the segment.
* @return Duration; duration within the segment it takes to travel the distance from the start of the segment.
*/
public Duration durationAtDistance(final Length distance)
{
Throw.when(distance.lt0(), IllegalArgumentException.class, "Distance must be positive.");
double[] solutions = Solver.solve(this.acceleration.si / 2, this.startSpeed.si, -distance.si);
// Find the solution that occurs within our duration (there should be only one).
for (double solution : solutions)
{
if (solution >= 0 && solution <= this.duration.si)
{
return new Duration(solution, DurationUnit.SI);
}
}
return this.duration; // probably a rounding error
}
/**
* Creates a stand-still segment.
* @param duration Duration; duration.
* @return Segment; segment with zero speed and acceleration.
*/
public static Segment standStill(final Duration duration)
{
return new Segment(duration);
}
}