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); } }