112 lines
5.2 KiB
C#
112 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using MultiWheelC.TrajectoryPlanning.CoarsePath;
|
|
|
|
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
|
|
|
|
/// <summary>Immutable longitudinal inputs derived only from an independently validated lateral PathS path.</summary>
|
|
public sealed class LongitudinalPlanningInput
|
|
{
|
|
private const double PathSTolerance = 1e-12d;
|
|
|
|
public LongitudinalPlanningInput(LateralPath path, TravelDirection direction, double initialProgressSpeedMetersPerSecond,
|
|
double initialAccelerationMetersPerSecondSquared, EmTerminalType terminalType, EmPlannerConfiguration configuration,
|
|
IReadOnlyList<double> previousPathS, IReadOnlyList<double> previousProgressSpeedMetersPerSecond)
|
|
{
|
|
if (path == null || !path.IsIndependentlyValidated || path.Points.Count < 2)
|
|
throw new ArgumentException("Longitudinal planning requires an independently validated lateral path with at least two points.",
|
|
nameof(path));
|
|
if (!Enum.IsDefined(typeof(TravelDirection), direction))
|
|
throw new ArgumentOutOfRangeException(nameof(direction));
|
|
if (!IsFinite(initialProgressSpeedMetersPerSecond) || initialProgressSpeedMetersPerSecond < 0d)
|
|
throw new ArgumentOutOfRangeException(nameof(initialProgressSpeedMetersPerSecond));
|
|
if (!IsFinite(initialAccelerationMetersPerSecondSquared))
|
|
throw new ArgumentOutOfRangeException(nameof(initialAccelerationMetersPerSecondSquared));
|
|
if (!Enum.IsDefined(typeof(EmTerminalType), terminalType))
|
|
throw new ArgumentOutOfRangeException(nameof(terminalType));
|
|
if (configuration == null)
|
|
throw new ArgumentNullException(nameof(configuration));
|
|
|
|
Path = CopyAndValidatePath(path);
|
|
Direction = direction;
|
|
InitialProgressSpeedMetersPerSecond = initialProgressSpeedMetersPerSecond;
|
|
InitialAccelerationMetersPerSecondSquared = initialAccelerationMetersPerSecondSquared;
|
|
TerminalType = terminalType;
|
|
Configuration = configuration.Copy();
|
|
PreviousPathS = CopyFiniteNonnegative(previousPathS, nameof(previousPathS));
|
|
PreviousProgressSpeedMetersPerSecond = CopyFiniteNonnegative(previousProgressSpeedMetersPerSecond,
|
|
nameof(previousProgressSpeedMetersPerSecond));
|
|
if (PreviousPathS.Count != PreviousProgressSpeedMetersPerSecond.Count)
|
|
throw new ArgumentException("Previous path-S and progress-speed samples must have matching counts.",
|
|
nameof(previousProgressSpeedMetersPerSecond));
|
|
}
|
|
|
|
public LateralPath Path { get; }
|
|
|
|
public TravelDirection Direction { get; }
|
|
|
|
public double InitialProgressSpeedMetersPerSecond { get; }
|
|
|
|
public double InitialAccelerationMetersPerSecondSquared { get; }
|
|
|
|
public EmTerminalType TerminalType { get; }
|
|
|
|
public EmPlannerConfiguration Configuration { get; }
|
|
|
|
public IReadOnlyList<double> PreviousPathS { get; }
|
|
|
|
public IReadOnlyList<double> PreviousProgressSpeedMetersPerSecond { get; }
|
|
|
|
public double TerminalPathS { get { return Path.Points[Path.Points.Count - 1].PathS; } }
|
|
|
|
public double DirectionMaximumSpeedMetersPerSecond
|
|
{
|
|
get
|
|
{
|
|
return Direction == TravelDirection.Forward
|
|
? Configuration.Longitudinal.MaximumForwardSpeedMetersPerSecond
|
|
: Configuration.Longitudinal.MaximumReverseSpeedMetersPerSecond;
|
|
}
|
|
}
|
|
|
|
private static LateralPath CopyAndValidatePath(LateralPath source)
|
|
{
|
|
var copy = new List<LateralPathPoint>(source.Points.Count);
|
|
double previousPathS = double.NegativeInfinity;
|
|
for (int index = 0; index < source.Points.Count; index++)
|
|
{
|
|
LateralPathPoint point = source.Points[index];
|
|
if (point == null || !IsFinite(point.PathS) || point.PathS <= previousPathS)
|
|
throw new ArgumentException("Lateral PathS must be finite and strictly increasing for ST planning.", nameof(source));
|
|
if (index == 0 && Math.Abs(point.PathS) > PathSTolerance)
|
|
throw new ArgumentException("The lateral PathS supplied to ST must begin at zero.", nameof(source));
|
|
copy.Add(new LateralPathPoint(point.ReferenceS, point.PathS, point.L, point.DL, point.DDL, point.DDDL,
|
|
point.X, point.Y, point.VehicleYaw, point.GeometricCurvature, point.VehicleCurvature,
|
|
point.VehicleCurvatureDerivative));
|
|
previousPathS = point.PathS;
|
|
}
|
|
return new LateralPath(copy, true);
|
|
}
|
|
|
|
private static IReadOnlyList<double> CopyFiniteNonnegative(IReadOnlyList<double> source, string parameterName)
|
|
{
|
|
var copy = new List<double>(source == null ? 0 : source.Count);
|
|
if (source != null)
|
|
{
|
|
for (int index = 0; index < source.Count; index++)
|
|
{
|
|
if (!IsFinite(source[index]) || source[index] < 0d)
|
|
throw new ArgumentOutOfRangeException(parameterName);
|
|
copy.Add(source[index]);
|
|
}
|
|
}
|
|
return new ReadOnlyCollection<double>(copy);
|
|
}
|
|
|
|
private static bool IsFinite(double value)
|
|
{
|
|
return !double.IsNaN(value) && !double.IsInfinity(value);
|
|
}
|
|
}
|