using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace MultiWheelC.TrajectoryPlanning.EMPlanner;
/// One discrete lateral iterate expressed against exact reference-S stations.
public sealed class LateralCandidate
{
public LateralCandidate(IReadOnlyList referenceStations, IReadOnlyList l, IReadOnlyList dl,
IReadOnlyList ddl, IReadOnlyList dddl)
{
ReferenceStations = CopyStations(referenceStations);
int stationCount = ReferenceStations.Count;
L = CopyValues(l, stationCount, nameof(l));
DL = CopyValues(dl, stationCount, nameof(dl));
DDL = CopyValues(ddl, stationCount, nameof(ddl));
DDDL = CopyValues(dddl, stationCount - 1, nameof(dddl));
}
public IReadOnlyList ReferenceStations { get; }
public IReadOnlyList L { get; }
public IReadOnlyList DL { get; }
public IReadOnlyList DDL { get; }
public IReadOnlyList DDDL { get; }
public static LateralCandidate Integrate(IReadOnlyList referenceStations, double initialL, double initialDL,
double initialDDL, IReadOnlyList dddl)
{
IReadOnlyList stations = CopyStations(referenceStations);
if (!IsFinite(initialL) || !IsFinite(initialDL) || !IsFinite(initialDDL))
throw new ArgumentOutOfRangeException(nameof(initialL));
IReadOnlyList copiedJerk = CopyValues(dddl, stations.Count - 1, nameof(dddl));
var l = new double[stations.Count];
var dl = new double[stations.Count];
var ddl = new double[stations.Count];
l[0] = initialL;
dl[0] = initialDL;
ddl[0] = initialDDL;
for (int index = 0; index < copiedJerk.Count; index++)
{
double ds = stations[index + 1] - stations[index];
double jerk = copiedJerk[index];
ddl[index + 1] = ddl[index] + ds * jerk;
dl[index + 1] = dl[index] + ds * ddl[index] + 0.5d * ds * ds * jerk;
l[index + 1] = l[index] + ds * dl[index] + 0.5d * ds * ds * ddl[index] +
ds * ds * ds * jerk / 6d;
}
return new LateralCandidate(stations, l, dl, ddl, copiedJerk);
}
public bool SatisfiesExactDiscreteDynamics(double tolerance)
{
if (!IsFinite(tolerance) || tolerance < 0d)
throw new ArgumentOutOfRangeException(nameof(tolerance));
for (int index = 0; index < DDDL.Count; index++)
{
double ds = ReferenceStations[index + 1] - ReferenceStations[index];
double jerk = DDDL[index];
if (Math.Abs(DDL[index + 1] - (DDL[index] + ds * jerk)) > tolerance ||
Math.Abs(DL[index + 1] - (DL[index] + ds * DDL[index] + 0.5d * ds * ds * jerk)) > tolerance ||
Math.Abs(L[index + 1] - (L[index] + ds * DL[index] + 0.5d * ds * ds * DDL[index] +
ds * ds * ds * jerk / 6d)) > tolerance)
{
return false;
}
}
return true;
}
private static IReadOnlyList CopyStations(IReadOnlyList values)
{
if (values == null || values.Count < 2)
throw new ArgumentException("At least two reference-S stations are required.", nameof(values));
var copy = new List(values.Count);
double previous = double.NegativeInfinity;
for (int index = 0; index < values.Count; index++)
{
double value = values[index];
if (!IsFinite(value) || value <= previous)
throw new ArgumentException("Reference-S stations must be finite and strictly increasing.", nameof(values));
copy.Add(value);
previous = value;
}
return new ReadOnlyCollection(copy);
}
private static IReadOnlyList CopyValues(IReadOnlyList values, int expectedCount, string parameterName)
{
if (values == null || values.Count != expectedCount)
throw new ArgumentException("Lateral value count does not match the station layout.", parameterName);
var copy = new List(values.Count);
for (int index = 0; index < values.Count; index++)
{
if (!IsFinite(values[index]))
throw new ArgumentOutOfRangeException(parameterName);
copy.Add(values[index]);
}
return new ReadOnlyCollection(copy);
}
private static bool IsFinite(double value)
{
return !double.IsNaN(value) && !double.IsInfinity(value);
}
}