From aeabbf4fb2555d58d767529fc06390f137c97700 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Tue, 4 Aug 2015 14:19:43 +0200
Subject: [PATCH] include method to calculate deceleration distance for
 lookahead (breaking)

---
 .../Data/AccelerationCurve.cs                 | 88 ++++++++++++++++---
 .../AccelerationCurveTest.cs                  | 39 ++++++++
 2 files changed, 117 insertions(+), 10 deletions(-)

diff --git a/VectoCore/Models/SimulationComponent/Data/AccelerationCurve.cs b/VectoCore/Models/SimulationComponent/Data/AccelerationCurve.cs
index 43e1c4d478..24024dc4f7 100644
--- a/VectoCore/Models/SimulationComponent/Data/AccelerationCurve.cs
+++ b/VectoCore/Models/SimulationComponent/Data/AccelerationCurve.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Data;
 using System.IO;
 using System.Linq;
@@ -47,6 +48,20 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 		}
 
 		public AccelerationEntry Lookup(MeterPerSecond key)
+		{
+			var index = FindIndex(key);
+
+			return new AccelerationEntry {
+				Acceleration =
+					VectoMath.Interpolate(_entries[index - 1].Key, _entries[index].Key, _entries[index - 1].Value.Acceleration,
+						_entries[index].Value.Acceleration, key),
+				Deceleration =
+					VectoMath.Interpolate(_entries[index - 1].Key, _entries[index].Key, _entries[index - 1].Value.Deceleration,
+						_entries[index].Value.Deceleration, key)
+			};
+		}
+
+		protected int FindIndex(MeterPerSecond key)
 		{
 			var index = 1;
 			if (key < _entries[0].Key) {
@@ -58,15 +73,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 					index = (key > _entries[0].Key) ? _entries.Count - 1 : 1;
 				}
 			}
-			
-			return new AccelerationEntry {
-				Acceleration =
-					VectoMath.Interpolate(_entries[index - 1].Key, _entries[index].Key, _entries[index - 1].Value.Acceleration,
-						_entries[index].Value.Acceleration, key),
-				Deceleration =
-					VectoMath.Interpolate(_entries[index - 1].Key, _entries[index].Key, _entries[index - 1].Value.Deceleration,
-						_entries[index].Value.Deceleration, key)
-			};
+			return index;
 		}
 
 		public class AccelerationEntry
@@ -74,5 +81,66 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 			public MeterPerSquareSecond Acceleration { get; set; }
 			public MeterPerSquareSecond Deceleration { get; set; }
 		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="v1">current speed of the vehicle</param>
+		/// <param name="v2">desired speed of the vehicle at the end of acceleration/deceleration phase</param>
+		/// <returns>distance required to accelerate/decelerate the vehicle from v1 to v2 according to the acceleration curve</returns>
+		public Meter ComputeAccelerationDistance(MeterPerSecond v1, MeterPerSecond v2)
+		{
+			var index1 = FindIndex(v1);
+			var index2 = FindIndex(v2);
+
+			var distance = 0.SI<Meter>();
+			for (var i = index2; i <= index1; i++) {
+				distance += ComputeAccelerationSegmentDistance(i, v1, v2);
+			}
+			return distance;
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="i">segment of the acceleration curve to use [(i-1) ... i]</param>
+		/// <param name="v1">current speed of the vehicle</param>
+		/// <param name="v2">desired speed of the vehicle at the end of acceleration/deceleration phase</param>
+		/// <returns>distance required to accelerate/decelerate the vehicle from v1 to v2 according to the acceleration curve</returns>
+		private Meter ComputeAccelerationSegmentDistance(int i, MeterPerSecond v1, MeterPerSecond v2)
+		{
+			var leftEntry = _entries[i - 1]; // entry with lower velocity
+			var rightEntry = _entries[i]; // entry with higher velocity
+
+			v2 = VectoMath.Max(v2, leftEntry.Key); // min. velocity within current segment
+			v1 = VectoMath.Min(v1, rightEntry.Key); // max. velocity within current segment
+
+			if (leftEntry.Value.Deceleration.IsEqual(rightEntry.Value.Deceleration)) {
+				// v(t) = a * t + v1  => t = (v2 - v1) / a
+				// s(t) = a/2 * t^2 + v1 * t + s0  {s0 == 0}  => s(t)
+				var acceleration = v2 > v1 ? leftEntry.Value.Acceleration : leftEntry.Value.Deceleration;
+				return ((v2 - v1) * (v2 - v1) / 2.0 / acceleration + v1 * (v2 - v1) / acceleration).Cast<Meter>();
+			}
+
+			// a(v) = k * v + d
+			// dv/dt = a(v) = d * v + d  ==> v(t) = sgn(k * v1 + d) * exp(-k * c) / k * exp(t * k) - d / k 
+			// v(0) = v1  => c = - ln(|v1 * k + d|) / k
+			// v(t) = (v1 + d / k) * exp(t * k) - d / k   => t = 1 / k * ln((v2 * k + d) / (v1 * k + d))
+			// s(t) = m / k* exp(t * k) + b * t + c'   {m = v1 + d / k, b = -d / k}
+
+			var k = (leftEntry.Value.Deceleration - rightEntry.Value.Deceleration) / (leftEntry.Key - rightEntry.Key);
+			var d = leftEntry.Value.Deceleration - k * leftEntry.Key;
+			if (v2 > v1) {
+				k = (leftEntry.Value.Acceleration - rightEntry.Value.Acceleration) / (leftEntry.Key - rightEntry.Key);
+				d = leftEntry.Value.Acceleration - k * leftEntry.Key;
+			}
+			var m = v1 + d / k;
+			var b = -d / k;
+			var c = 0.SI<Meter>() - m / k;
+			var t = (Math.Log((v2 * k + d).Value() / (v1 * k + d).Value()) / k.Value()).SI<Second>();
+			// TODO @@@quam: remove .SI<> when bug #VECTO-111 is fixed...
+
+			return m / k * Math.Exp((k * t).Value()) + b * t + c;
+		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCoreTest/Models/SimulationComponentData/AccelerationCurveTest.cs b/VectoCoreTest/Models/SimulationComponentData/AccelerationCurveTest.cs
index 8b4a72ee2c..508f6bbf0d 100644
--- a/VectoCoreTest/Models/SimulationComponentData/AccelerationCurveTest.cs
+++ b/VectoCoreTest/Models/SimulationComponentData/AccelerationCurveTest.cs
@@ -79,5 +79,44 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
 			// EXTRAPOLATE 
 			EqualAcceleration(130, 0.16, -0.103);
 		}
+
+		[TestMethod]
+		public void ComputeAccelerationDistanceTest()
+		{
+			Data = AccelerationCurveData.ReadFromFile(@"TestData\Components\Truck.vacc");
+
+			// in this part the deceleration is constant
+
+			var result = Data.ComputeAccelerationDistance(25.KMPHtoMeterPerSecond(), 0.KMPHtoMeterPerSecond());
+			Assert.AreEqual(24.11265432099, result.Value(), Tolerance);
+
+			result = Data.ComputeAccelerationDistance(25.KMPHtoMeterPerSecond(), 15.KMPHtoMeterPerSecond());
+			Assert.AreEqual(15.43209876543, result.Value(), Tolerance);
+
+			result = Data.ComputeAccelerationDistance(50.KMPHtoMeterPerSecond(), 0.KMPHtoMeterPerSecond());
+			Assert.AreEqual(96.45061728395, result.Value(), Tolerance);
+
+			result = Data.ComputeAccelerationDistance(50.KMPHtoMeterPerSecond(), 15.KMPHtoMeterPerSecond());
+			Assert.AreEqual(87.77006172840, result.Value(), Tolerance);
+
+			result = Data.ComputeAccelerationDistance(100.KMPHtoMeterPerSecond(), 60.KMPHtoMeterPerSecond());
+			Assert.AreEqual(493.82716049383, result.Value(), Tolerance);
+
+			// decelerate in the non-constant part only
+
+			result = Data.ComputeAccelerationDistance(60.KMPHtoMeterPerSecond(), 50.KMPHtoMeterPerSecond());
+			Assert.AreEqual(59.44491148, result.Value(), Tolerance);
+
+			result = Data.ComputeAccelerationDistance(59.KMPHtoMeterPerSecond(), 55.KMPHtoMeterPerSecond());
+			Assert.AreEqual(27.33155090, result.Value(), Tolerance);
+
+			// decelerate across multiple areas of acceleration curve
+
+			result = Data.ComputeAccelerationDistance(60.KMPHtoMeterPerSecond(), 0.KMPHtoMeterPerSecond());
+			Assert.AreEqual(59.44491148 + 96.45061728395, result.Value(), Tolerance);
+
+			result = Data.ComputeAccelerationDistance(100.KMPHtoMeterPerSecond(), 0.KMPHtoMeterPerSecond());
+			Assert.AreEqual(59.44491148 + 96.45061728395 + 493.82716049383, result.Value(), Tolerance);
+		}
 	}
 }
\ No newline at end of file
-- 
GitLab