From 71cf0250880979b98e820cdd5e35b8d53c0528be Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Wed, 28 Jun 2017 08:29:55 +0200
Subject: [PATCH] remove duplicate code

---
 .../Impl/DistanceBasedDrivingCycle.cs         | 1044 +++++++++--------
 1 file changed, 523 insertions(+), 521 deletions(-)

diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs
index 207f17c590..dbae9245a3 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs
@@ -29,525 +29,527 @@
 *   Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
 */
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using TUGraz.VectoCommon.Exceptions;
-using TUGraz.VectoCommon.Models;
-using TUGraz.VectoCommon.Utils;
-using TUGraz.VectoCore.Configuration;
-using TUGraz.VectoCore.Models.Connector.Ports;
-using TUGraz.VectoCore.Models.Connector.Ports.Impl;
-using TUGraz.VectoCore.Models.Simulation;
-using TUGraz.VectoCore.Models.Simulation.Data;
-using TUGraz.VectoCore.Models.SimulationComponent.Data;
-using TUGraz.VectoCore.OutputData;
-using TUGraz.VectoCore.Utils;
-
-namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
-{
-	/// <summary>
-	///     Class representing one Distance Based Driving Cycle
-	/// </summary>
-	public sealed class DistanceBasedDrivingCycle : StatefulProviderComponent
-		<DistanceBasedDrivingCycle.DrivingCycleState, ISimulationOutPort, IDrivingCycleInPort, IDrivingCycleOutPort>,
-		IDrivingCycle, ISimulationOutPort, IDrivingCycleInPort, IDisposable
-	{
-		private const double LookaheadTimeSafetyMargin = 1.5;
-		internal readonly IDrivingCycleData Data;
-		internal readonly DrivingCycleEnumerator CycleIntervalIterator;
-		private bool _intervalProlonged;
-		internal IdleControllerSwitcher IdleController;
-
-		private DrivingCycleData.DrivingCycleEntry Left
-		{
-			get { return CycleIntervalIterator.LeftSample; }
-		}
-
-		private DrivingCycleData.DrivingCycleEntry Right
-		{
-			get { return CycleIntervalIterator.RightSample; }
-		}
-
-		public DistanceBasedDrivingCycle(IVehicleContainer container, IDrivingCycleData cycle) : base(container)
-		{
-			Data = cycle;
-			CycleIntervalIterator = new DrivingCycleEnumerator(Data);
-			CycleStartDistance = Data.Entries.Count > 0 ? Data.Entries.First().Distance : 0.SI<Meter>();
-
-			var first = Data.Entries.First();
-			PreviousState = new DrivingCycleState {
-				AbsTime = 0.SI<Second>(),
-				WaitTime = 0.SI<Second>(),
-				Distance = first.Distance,
-				Altitude = first.Altitude,
-			};
-			CurrentState = PreviousState.Clone();
-		}
-
-		public IResponse Initialize()
-		{
-			if (Left.VehicleTargetSpeed.IsEqual(0)) {
-				var retVal = NextComponent.Initialize(DataBus.StartSpeed,
-					Left.RoadGradient, DataBus.StartAcceleration);
-				if (!(retVal is ResponseSuccess)) {
-					throw new UnexpectedResponseException("DistanceBasedDrivingCycle.Initialize: Couldn't find start gear.", retVal);
-				}
-			}
-
-			return NextComponent.Initialize(Left.VehicleTargetSpeed,
-				Left.RoadGradient);
-		}
-
-		public IResponse Request(Second absTime, Second dt)
-		{
-			throw new NotImplementedException("Distance Based Driving Cycle does not support time requests.");
-		}
-
-		public IResponse Request(Second absTime, Meter ds)
-		{
-			if (Left.Distance.IsEqual(PreviousState.Distance.Value())) {
-				// we are exactly on an entry in the cycle.
-				var stopTime = Left.PTOActive && IdleController != null
-					? Left.StoppingTime + IdleController.Duration
-					: Left.StoppingTime;
-
-				if (stopTime.IsGreater(0) && PreviousState.WaitTime.IsSmaller(stopTime)) {
-					// stop for certain time unless we've already waited long enough ...
-
-					// we are stopping: ensure that velocity is 0.
-					if (!Left.VehicleTargetSpeed.IsEqual(0)) {
-						Log.Warn("Stopping Time requested in cycle but target-velocity not zero. distance: {0}, target speed: {1}",
-							Left.StoppingTime, Left.VehicleTargetSpeed);
-						throw new VectoSimulationException("Stopping Time only allowed when target speed is zero!");
-					}
-					var dt = GetStopTimeInterval();
-					if (dt == null) {
-						CurrentState.WaitPhase++;
-						dt = GetStopTimeInterval();
-					}
-					CurrentState.Response = DriveTimeInterval(absTime, dt);
-					return CurrentState.Response;
-				}
-			}
-			if (CycleIntervalIterator.LastEntry && PreviousState.Distance.IsEqual(Right.Distance)) {
-				CurrentState.Response = new ResponseCycleFinished();
-				return CurrentState.Response;
-			}
-
-			var nextSpeedChange = GetSpeedChangeWithinSimulationInterval(ds);
-			if (nextSpeedChange == null || ds.IsSmallerOrEqual(nextSpeedChange - PreviousState.Distance)) {
-				if (nextSpeedChange == null || DataBus.VehicleSpeed.IsEqual(0.SI<MeterPerSecond>())) {
-					CurrentState.Response = DriveDistance(absTime, ds);
-					return CurrentState.Response;
-				}
-				var remainingDistance = nextSpeedChange - PreviousState.Distance - ds;
-				var estimatedRemainingTime = remainingDistance / DataBus.VehicleSpeed;
-				if (_intervalProlonged || remainingDistance.IsEqual(0.SI<Meter>()) ||
-					estimatedRemainingTime.IsGreater(Constants.SimulationSettings.LowerBoundTimeInterval)) {
-					CurrentState.Response = DriveDistance(absTime, ds);
-					return CurrentState.Response;
-				}
-				Log.Debug("Extending distance by {0} to next sample point. ds: {1} new ds: {2}", remainingDistance, ds,
-					nextSpeedChange - PreviousState.Distance);
-				_intervalProlonged = true;
-				CurrentState.Response = new ResponseDrivingCycleDistanceExceeded {
-					Source = this,
-					MaxDistance = nextSpeedChange - PreviousState.Distance
-				};
-				return CurrentState.Response;
-			}
-			// only drive until next sample point in cycle with speed change
-			Log.Debug("Limiting distance to next sample point {0}",
-				Right.Distance - PreviousState.Distance);
-			CurrentState.Response = new ResponseDrivingCycleDistanceExceeded {
-				Source = this,
-				MaxDistance = nextSpeedChange - PreviousState.Distance
-			};
-			return CurrentState.Response;
-		}
-
-		private Second GetStopTimeInterval()
-		{
-			if (!Left.PTOActive || IdleController == null) {
-				return Left.StoppingTime.IsGreater(3 * Constants.SimulationSettings.TargetTimeInterval)
-					? GetStopTimeIntervalThreePhases()
-					: Left.StoppingTime;
-			}
-			if (Left.StoppingTime.IsGreater(6 * Constants.SimulationSettings.TargetTimeInterval)) {
-				// 7 phases
-				return GetStopTimeIntervalSevenPhasesPTO();
-			}
-			if (Left.StoppingTime.IsGreater(0)) {
-				// 3 phases
-				return GetStopTimeIntervalThreePhasesPTO();
-			}
-			// we have a pto cycle without stopping time.
-			IdleController.ActivatePTO();
-			return IdleController.GetNextCycleTime();
-		}
-
-		private Second GetStopTimeIntervalThreePhases()
-		{
-			switch (CurrentState.WaitPhase) {
-				case 1:
-				case 3:
-					CurrentState.WaitPhase++;
-					return Constants.SimulationSettings.TargetTimeInterval;
-				case 2:
-					CurrentState.WaitPhase++;
-					return Left.StoppingTime - 2 * Constants.SimulationSettings.TargetTimeInterval;
-			}
-			return null;
-		}
-
-		private Second GetStopTimeIntervalThreePhasesPTO()
-		{
-			switch (CurrentState.WaitPhase) {
-				case 1:
-				case 3:
-					CurrentState.WaitPhase++;
-					IdleController.ActivateIdle();
-					PTOActive = false;
-					return Left.StoppingTime / 2;
-				case 2:
-					PTOActive = true;
-					IdleController.ActivatePTO();
-					return IdleController.GetNextCycleTime();
-			}
-			return null;
-		}
-
-		private Second GetStopTimeIntervalSevenPhasesPTO()
-		{
-			switch (CurrentState.WaitPhase) {
-				case 1:
-				case 5:
-					CurrentState.WaitPhase++;
-					IdleController.ActivateIdle();
-					PTOActive = false;
-					return Constants.SimulationSettings.TargetTimeInterval;
-				case 3:
-				case 7:
-					CurrentState.WaitPhase++;
-					return Constants.SimulationSettings.TargetTimeInterval;
-				case 2:
-				case 6:
-					CurrentState.WaitPhase++;
-					return Left.StoppingTime / 2 - 2 * Constants.SimulationSettings.TargetTimeInterval;
-				case 4:
-					PTOActive = true;
-					IdleController.ActivatePTO();
-					return IdleController.GetNextCycleTime();
-			}
-			return null;
-		}
-
-		private IResponse DriveTimeInterval(Second absTime, Second dt)
-		{
-			CurrentState.AbsTime = absTime;
-			CurrentState.WaitTime = PreviousState.WaitTime + dt;
-			CurrentState.Gradient = ComputeGradient(0.SI<Meter>());
-			CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed;
-
-			return NextComponent.Request(absTime, dt, Left.VehicleTargetSpeed, CurrentState.Gradient);
-		}
-
-		private IResponse DriveDistance(Second absTime, Meter ds)
-		{
-			var nextSpeedChanges = LookAhead(Constants.SimulationSettings.BrakeNextTargetDistance);
-			if (nextSpeedChanges.Count > 0 && !CurrentState.RequestToNextSamplePointDone) {
-				CurrentState.RequestToNextSamplePointDone = true;
-				Log.Debug("current distance is close to the next speed change: {0}",
-					nextSpeedChanges.First().Distance - PreviousState.Distance);
-				return new ResponseDrivingCycleDistanceExceeded {
-					Source = this,
-					MaxDistance = Constants.SimulationSettings.BrakeNextTargetDistance
-				};
-			}
-
-			CurrentState.WaitPhase = 0;
-			//CurrentState.Distance = PreviousState.Distance + ds;
-			CurrentState.SimulationDistance = ds;
-			CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed;
-			CurrentState.Gradient = ComputeGradient(ds);
-
-			var retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient);
-			retVal.Switch()
-				.Case<ResponseFailTimeInterval>(
-					r => {
-						retVal = NextComponent.Request(absTime, r.DeltaT, 0.SI<MeterPerSecond>(), CurrentState.Gradient);
-						retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient);
-					});
-			CurrentState.AbsTime = absTime;
-			if (retVal is ResponseSuccess) {
-				CurrentState.Distance = PreviousState.Distance + retVal.SimulationDistance;
-			}
-			return retVal;
-		}
-
-		protected override void DoWriteModalResults(IModalDataContainer container)
-		{
-			container[ModalResultField.dist] = CurrentState.Distance; // (CurrentState.Distance + PreviousState.Distance) / 2.0;
-			container[ModalResultField.simulationDistance] = CurrentState.SimulationDistance;
-			container[ModalResultField.v_targ] = CurrentState.VehicleTargetSpeed;
-			container[ModalResultField.grad] = (Math.Tan(CurrentState.Gradient.Value()) * 100).SI<Scalar>();
-			container[ModalResultField.altitude] = CurrentState.Altitude;
-
-			if (IdleController != null) {
-				IdleController.CommitSimulationStep(container);
-			}
-		}
-
-		protected override void DoCommitSimulationStep()
-		{
-			if (!(CurrentState.Response is ResponseSuccess)) {
-				throw new VectoSimulationException("Previous request did not succeed!");
-			}
-
-			PreviousState = CurrentState;
-			CurrentState = CurrentState.Clone();
-			_intervalProlonged = false;
-
-			var stopTime = Left.PTOActive && IdleController != null
-				? Left.StoppingTime + IdleController.Duration
-				: Left.StoppingTime;
-
-			if (!stopTime.IsEqual(0) && stopTime.IsEqual(PreviousState.WaitTime)) {
-				// we needed to stop at the current interval in the cycle and have already waited enough time, move on..
-				if (IdleController != null) {
-					IdleController.ActivateIdle();
-				}
-				CycleIntervalIterator.MoveNext();
-			}
-
-			stopTime = Left.PTOActive && IdleController != null ? Left.StoppingTime + IdleController.Duration : Left.StoppingTime;
-
-			// separately test for equality and greater than to have tolerance for equality comparison
-			if (stopTime.IsEqual(0)) {
-				while (stopTime.IsEqual(0) && CurrentState.Distance.IsGreaterOrEqual(Right.Distance) &&
-						!CycleIntervalIterator.LastEntry) {
-					// we have reached the end of the current interval in the cycle, move on...
-					CycleIntervalIterator.MoveNext();
-
-					stopTime = Left.PTOActive && IdleController != null
-						? Left.StoppingTime + IdleController.Duration
-						: Left.StoppingTime;
-				}
-			} else {
-				if (stopTime.IsEqual(PreviousState.WaitTime)) {
-					// we needed to stop at the current interval in the cycle and have already waited enough time, move on..
-					if (IdleController != null) {
-						IdleController.ActivateIdle();
-					}
-					CycleIntervalIterator.MoveNext();
-				}
-			}
-		}
-
-		private Radian ComputeGradient(Meter ds)
-		{
-			var cycleIterator = CycleIntervalIterator.Clone();
-			while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && !cycleIterator.LastEntry) {
-				cycleIterator.MoveNext();
-			}
-			var leftSamplePoint = cycleIterator.LeftSample;
-			var rightSamplePoint = cycleIterator.RightSample;
-
-			if (leftSamplePoint.Distance.IsEqual(rightSamplePoint.Distance)) {
-				return leftSamplePoint.RoadGradient;
-			}
-			if (ds.IsEqual(0.SI<Meter>())) {
-				return leftSamplePoint.RoadGradient;
-			}
-			CurrentState.Altitude = VectoMath.Interpolate(leftSamplePoint.Distance, rightSamplePoint.Distance,
-				leftSamplePoint.Altitude, rightSamplePoint.Altitude, PreviousState.Distance + ds);
-
-			var gradient = VectoMath.InclinationToAngle(((CurrentState.Altitude - PreviousState.Altitude) /
-														ds).Value());
-			//return 0.SI<Radian>();
-			return gradient;
-		}
-
-		private Meter GetSpeedChangeWithinSimulationInterval(Meter ds)
-		{
-			var leftSamplePoint = Left;
-			var cycleIterator = CycleIntervalIterator.Clone();
-
-			do {
-				if (!leftSamplePoint.VehicleTargetSpeed.IsEqual(cycleIterator.RightSample.VehicleTargetSpeed)) {
-					return cycleIterator.RightSample.Distance;
-				}
-			} while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && cycleIterator.MoveNext());
-			if (cycleIterator.LastEntry) {
-				return cycleIterator.RightSample.Distance;
-			}
-			return null;
-		}
-
-		/// <summary>
-		/// Progress of the distance in the driving cycle.
-		/// </summary>
-		public double Progress
-		{
-			get {
-				return Data.Entries.Count > 0
-					? (CurrentState.Distance.Value() - Data.Entries.First().Distance.Value()) /
-					(Data.Entries.Last().Distance.Value() - Data.Entries.First().Distance.Value())
-					: 0;
-			}
-		}
-
-		public Meter CycleStartDistance { get; internal set; }
-
-		public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Meter lookaheadDistance)
-		{
-			var retVal = new List<DrivingCycleData.DrivingCycleEntry>();
-
-			var cycleIterator = CycleIntervalIterator.Clone();
-			var velocity = cycleIterator.LeftSample.VehicleTargetSpeed;
-
-			do {
-				if (cycleIterator.RightSample.VehicleTargetSpeed.IsEqual(velocity)) {
-					continue;
-				}
-				var lookaheadEntry = retVal.Find(x => x.Distance == cycleIterator.RightSample.Distance);
-				if (lookaheadEntry != null) {
-					// an entry may occur twice when vehicle stops (one entry with v=0 and the other with drive on after stop)
-					// only use the one with min. speed
-					if (cycleIterator.RightSample.VehicleTargetSpeed < lookaheadEntry.VehicleTargetSpeed) {
-						retVal.Remove(lookaheadEntry);
-						retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here?
-					}
-				} else {
-					retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here?
-				}
-				velocity = cycleIterator.RightSample.VehicleTargetSpeed;
-				if (velocity.IsEqual(0.KMPHtoMeterPerSecond())) {
-					// do not look beyond vehicle stop
-					break;
-				}
-			} while (cycleIterator.MoveNext() && cycleIterator.RightSample.Distance < PreviousState.Distance + lookaheadDistance);
-			if (retVal.Count > 0) {
-				retVal = retVal.Where(x => x.Distance <= PreviousState.Distance + lookaheadDistance).ToList();
-				retVal.Sort((x, y) => x.Distance.CompareTo(y.Distance));
-			}
-			return retVal;
-		}
-
-		public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Second time)
-		{
-			return LookAhead(LookaheadTimeSafetyMargin * DataBus.VehicleSpeed * time);
-		}
-
-		public void FinishSimulation()
-		{
-			Data.Finish();
-		}
-
-		public CycleData CycleData
-		{
-			get {
-				return new CycleData {
-					AbsTime = CurrentState.AbsTime,
-					AbsDistance = CurrentState.Distance,
-					LeftSample = Left,
-					RightSample = CycleIntervalIterator.RightSample
-				};
-			}
-		}
-
-		public bool PTOActive { get; private set; }
-
-		public DrivingCycleData.DrivingCycleEntry CycleLookAhead(Meter distance)
-		{
-			var absDistance = CurrentState.Distance + distance;
-			var myIterator = CycleIntervalIterator.Clone();
-
-			if (absDistance > Data.Entries.Last().Distance) {
-				return ExtrapolateCycleEntry(absDistance, Data.Entries.Last());
-			}
-			while (myIterator.RightSample.Distance < absDistance) {
-				myIterator.MoveNext();
-			}
-
-			return InterpolateCycleEntry(absDistance, myIterator.RightSample);
-		}
-
-		private DrivingCycleData.DrivingCycleEntry InterpolateCycleEntry(Meter absDistance,
-			DrivingCycleData.DrivingCycleEntry lookahead)
-		{
-			var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) {
-				Distance = absDistance,
-				Altitude = VectoMath.Interpolate(CurrentState.Distance, lookahead.Distance, CurrentState.Altitude,
-					lookahead.Altitude, absDistance)
-			};
-
-			retVal.RoadGradient =
-				((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>();
-
-			return retVal;
-		}
-
-		private DrivingCycleData.DrivingCycleEntry ExtrapolateCycleEntry(Meter absDistance,
-			DrivingCycleData.DrivingCycleEntry lookahead)
-		{
-			var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) {
-				Distance = absDistance,
-				Altitude = lookahead.Altitude + lookahead.RoadGradient * (absDistance - lookahead.Distance),
-			};
-
-			retVal.RoadGradient =
-				((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>();
-
-			return retVal;
-		}
-
-		public Meter Altitude
-		{
-			get { return PreviousState.Altitude; }
-		}
-
-		public sealed class DrivingCycleState
-		{
-			public DrivingCycleState Clone()
-			{
-				return new DrivingCycleState {
-					AbsTime = AbsTime,
-					Distance = Distance,
-					VehicleTargetSpeed = VehicleTargetSpeed,
-					Altitude = Altitude,
-					WaitPhase = WaitPhase,
-					// WaitTime is not cloned on purpose!
-					WaitTime = 0.SI<Second>(),
-					Response = null
-				};
-			}
-
-			public Second AbsTime;
-
-			public Meter Distance;
-
-			public Second WaitTime;
-
-			public uint WaitPhase;
-
-			public MeterPerSecond VehicleTargetSpeed;
-
-			public Meter Altitude;
-
-			public Radian Gradient;
-
-			public IResponse Response;
-
-			public bool RequestToNextSamplePointDone;
-
-			public Meter SimulationDistance;
-		}
-
-		public void Dispose()
-		{
-			CycleIntervalIterator.Dispose();
-		}
-	}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using TUGraz.VectoCommon.Exceptions;
+using TUGraz.VectoCommon.Models;
+using TUGraz.VectoCommon.Utils;
+using TUGraz.VectoCore.Configuration;
+using TUGraz.VectoCore.Models.Connector.Ports;
+using TUGraz.VectoCore.Models.Connector.Ports.Impl;
+using TUGraz.VectoCore.Models.Simulation;
+using TUGraz.VectoCore.Models.Simulation.Data;
+using TUGraz.VectoCore.Models.SimulationComponent.Data;
+using TUGraz.VectoCore.OutputData;
+using TUGraz.VectoCore.Utils;
+
+namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
+{
+	/// <summary>
+	///     Class representing one Distance Based Driving Cycle
+	/// </summary>
+	public sealed class DistanceBasedDrivingCycle : StatefulProviderComponent
+		<DistanceBasedDrivingCycle.DrivingCycleState, ISimulationOutPort, IDrivingCycleInPort, IDrivingCycleOutPort>,
+		IDrivingCycle, ISimulationOutPort, IDrivingCycleInPort, IDisposable
+	{
+		private const double LookaheadTimeSafetyMargin = 1.5;
+		internal readonly IDrivingCycleData Data;
+		internal readonly DrivingCycleEnumerator CycleIntervalIterator;
+		private bool _intervalProlonged;
+		internal IdleControllerSwitcher IdleController;
+
+		private DrivingCycleData.DrivingCycleEntry Left
+		{
+			get { return CycleIntervalIterator.LeftSample; }
+		}
+
+		private DrivingCycleData.DrivingCycleEntry Right
+		{
+			get { return CycleIntervalIterator.RightSample; }
+		}
+
+		public DistanceBasedDrivingCycle(IVehicleContainer container, IDrivingCycleData cycle) : base(container)
+		{
+			Data = cycle;
+			CycleIntervalIterator = new DrivingCycleEnumerator(Data);
+			CycleStartDistance = Data.Entries.Count > 0 ? Data.Entries.First().Distance : 0.SI<Meter>();
+
+			var first = Data.Entries.First();
+			PreviousState = new DrivingCycleState {
+				AbsTime = 0.SI<Second>(),
+				WaitTime = 0.SI<Second>(),
+				Distance = first.Distance,
+				Altitude = first.Altitude,
+			};
+			CurrentState = PreviousState.Clone();
+		}
+
+		public IResponse Initialize()
+		{
+			if (Left.VehicleTargetSpeed.IsEqual(0)) {
+				var retVal = NextComponent.Initialize(DataBus.StartSpeed,
+					Left.RoadGradient, DataBus.StartAcceleration);
+				if (!(retVal is ResponseSuccess)) {
+					throw new UnexpectedResponseException("DistanceBasedDrivingCycle.Initialize: Couldn't find start gear.", retVal);
+				}
+			}
+
+			return NextComponent.Initialize(Left.VehicleTargetSpeed,
+				Left.RoadGradient);
+		}
+
+		public IResponse Request(Second absTime, Second dt)
+		{
+			throw new NotImplementedException("Distance Based Driving Cycle does not support time requests.");
+		}
+
+		public IResponse Request(Second absTime, Meter ds)
+		{
+			if (Left.Distance.IsEqual(PreviousState.Distance.Value())) {
+				// we are exactly on an entry in the cycle.
+				var stopTime = Left.PTOActive && IdleController != null
+					? Left.StoppingTime + IdleController.Duration
+					: Left.StoppingTime;
+
+				if (stopTime.IsGreater(0) && PreviousState.WaitTime.IsSmaller(stopTime)) {
+					// stop for certain time unless we've already waited long enough ...
+
+					// we are stopping: ensure that velocity is 0.
+					if (!Left.VehicleTargetSpeed.IsEqual(0)) {
+						Log.Warn("Stopping Time requested in cycle but target-velocity not zero. distance: {0}, target speed: {1}",
+							Left.StoppingTime, Left.VehicleTargetSpeed);
+						throw new VectoSimulationException("Stopping Time only allowed when target speed is zero!");
+					}
+					var dt = GetStopTimeInterval();
+					if (dt == null) {
+						CurrentState.WaitPhase++;
+						dt = GetStopTimeInterval();
+					}
+					CurrentState.Response = DriveTimeInterval(absTime, dt);
+					return CurrentState.Response;
+				}
+			}
+			if (CycleIntervalIterator.LastEntry && PreviousState.Distance.IsEqual(Right.Distance)) {
+				CurrentState.Response = new ResponseCycleFinished();
+				return CurrentState.Response;
+			}
+
+			var nextSpeedChange = GetSpeedChangeWithinSimulationInterval(ds);
+			if (nextSpeedChange == null || ds.IsSmallerOrEqual(nextSpeedChange - PreviousState.Distance)) {
+				if (nextSpeedChange == null || DataBus.VehicleSpeed.IsEqual(0.SI<MeterPerSecond>())) {
+					CurrentState.Response = DriveDistance(absTime, ds);
+					return CurrentState.Response;
+				}
+				var remainingDistance = nextSpeedChange - PreviousState.Distance - ds;
+				var estimatedRemainingTime = remainingDistance / DataBus.VehicleSpeed;
+				if (_intervalProlonged || remainingDistance.IsEqual(0.SI<Meter>()) ||
+					estimatedRemainingTime.IsGreater(Constants.SimulationSettings.LowerBoundTimeInterval)) {
+					CurrentState.Response = DriveDistance(absTime, ds);
+					return CurrentState.Response;
+				}
+				Log.Debug("Extending distance by {0} to next sample point. ds: {1} new ds: {2}", remainingDistance, ds,
+					nextSpeedChange - PreviousState.Distance);
+				_intervalProlonged = true;
+				CurrentState.Response = new ResponseDrivingCycleDistanceExceeded {
+					Source = this,
+					MaxDistance = nextSpeedChange - PreviousState.Distance
+				};
+				return CurrentState.Response;
+			}
+			// only drive until next sample point in cycle with speed change
+			Log.Debug("Limiting distance to next sample point {0}",
+				Right.Distance - PreviousState.Distance);
+			CurrentState.Response = new ResponseDrivingCycleDistanceExceeded {
+				Source = this,
+				MaxDistance = nextSpeedChange - PreviousState.Distance
+			};
+			return CurrentState.Response;
+		}
+
+		private Second GetStopTimeInterval()
+		{
+			if (!Left.PTOActive || IdleController == null) {
+				return Left.StoppingTime.IsGreater(3 * Constants.SimulationSettings.TargetTimeInterval)
+					? GetStopTimeIntervalThreePhases()
+					: Left.StoppingTime;
+			}
+			if (Left.StoppingTime.IsGreater(6 * Constants.SimulationSettings.TargetTimeInterval)) {
+				// 7 phases
+				return GetStopTimeIntervalSevenPhasesPTO();
+			}
+			if (Left.StoppingTime.IsGreater(0)) {
+				// 3 phases
+				return GetStopTimeIntervalThreePhasesPTO();
+			}
+			// we have a pto cycle without stopping time.
+			IdleController.ActivatePTO();
+			return IdleController.GetNextCycleTime();
+		}
+
+		private Second GetStopTimeIntervalThreePhases()
+		{
+			switch (CurrentState.WaitPhase) {
+				case 1:
+				case 3:
+					CurrentState.WaitPhase++;
+					return Constants.SimulationSettings.TargetTimeInterval;
+				case 2:
+					CurrentState.WaitPhase++;
+					return Left.StoppingTime - 2 * Constants.SimulationSettings.TargetTimeInterval;
+			}
+			return null;
+		}
+
+		private Second GetStopTimeIntervalThreePhasesPTO()
+		{
+			switch (CurrentState.WaitPhase) {
+				case 1:
+				case 3:
+					CurrentState.WaitPhase++;
+					IdleController.ActivateIdle();
+					PTOActive = false;
+					return Left.StoppingTime / 2;
+				case 2:
+					PTOActive = true;
+					IdleController.ActivatePTO();
+					return IdleController.GetNextCycleTime();
+			}
+			return null;
+		}
+
+		private Second GetStopTimeIntervalSevenPhasesPTO()
+		{
+			switch (CurrentState.WaitPhase) {
+				case 1:
+				case 5:
+					CurrentState.WaitPhase++;
+					IdleController.ActivateIdle();
+					PTOActive = false;
+					return Constants.SimulationSettings.TargetTimeInterval;
+				case 3:
+				case 7:
+					CurrentState.WaitPhase++;
+					return Constants.SimulationSettings.TargetTimeInterval;
+				case 2:
+				case 6:
+					CurrentState.WaitPhase++;
+					return Left.StoppingTime / 2 - 2 * Constants.SimulationSettings.TargetTimeInterval;
+				case 4:
+					PTOActive = true;
+					IdleController.ActivatePTO();
+					return IdleController.GetNextCycleTime();
+			}
+			return null;
+		}
+
+		private IResponse DriveTimeInterval(Second absTime, Second dt)
+		{
+			CurrentState.AbsTime = absTime;
+			CurrentState.WaitTime = PreviousState.WaitTime + dt;
+			CurrentState.Gradient = ComputeGradient(0.SI<Meter>());
+			CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed;
+
+			return NextComponent.Request(absTime, dt, Left.VehicleTargetSpeed, CurrentState.Gradient);
+		}
+
+		private IResponse DriveDistance(Second absTime, Meter ds)
+		{
+			var nextSpeedChanges = LookAhead(Constants.SimulationSettings.BrakeNextTargetDistance);
+			if (nextSpeedChanges.Count > 0 && !CurrentState.RequestToNextSamplePointDone) {
+				CurrentState.RequestToNextSamplePointDone = true;
+				Log.Debug("current distance is close to the next speed change: {0}",
+					nextSpeedChanges.First().Distance - PreviousState.Distance);
+				return new ResponseDrivingCycleDistanceExceeded {
+					Source = this,
+					MaxDistance = Constants.SimulationSettings.BrakeNextTargetDistance
+				};
+			}
+
+			CurrentState.WaitPhase = 0;
+			//CurrentState.Distance = PreviousState.Distance + ds;
+			CurrentState.SimulationDistance = ds;
+			CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed;
+			CurrentState.Gradient = ComputeGradient(ds);
+
+			var retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient);
+			retVal.Switch()
+				.Case<ResponseFailTimeInterval>(
+					r => {
+						retVal = NextComponent.Request(absTime, r.DeltaT, 0.SI<MeterPerSecond>(), CurrentState.Gradient);
+						retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient);
+					});
+			CurrentState.AbsTime = absTime;
+			if (retVal is ResponseSuccess) {
+				CurrentState.Distance = PreviousState.Distance + retVal.SimulationDistance;
+			}
+			return retVal;
+		}
+
+		protected override void DoWriteModalResults(IModalDataContainer container)
+		{
+			container[ModalResultField.dist] = CurrentState.Distance; // (CurrentState.Distance + PreviousState.Distance) / 2.0;
+			container[ModalResultField.simulationDistance] = CurrentState.SimulationDistance;
+			container[ModalResultField.v_targ] = CurrentState.VehicleTargetSpeed;
+			container[ModalResultField.grad] = (Math.Tan(CurrentState.Gradient.Value()) * 100).SI<Scalar>();
+			container[ModalResultField.altitude] = CurrentState.Altitude;
+
+			if (IdleController != null) {
+				IdleController.CommitSimulationStep(container);
+			}
+		}
+
+		protected override void DoCommitSimulationStep()
+		{
+			if (!(CurrentState.Response is ResponseSuccess)) {
+				throw new VectoSimulationException("Previous request did not succeed!");
+			}
+
+			PreviousState = CurrentState;
+			CurrentState = CurrentState.Clone();
+			_intervalProlonged = false;
+
+			//var stopTime = Left.PTOActive && IdleController != null
+			//	? Left.StoppingTime + IdleController.Duration
+			//	: Left.StoppingTime;
+
+			//if (!stopTime.IsEqual(0) && stopTime.IsEqual(PreviousState.WaitTime)) {
+			//	// we needed to stop at the current interval in the cycle and have already waited enough time, move on..
+			//	if (IdleController != null) {
+			//		IdleController.ActivateIdle();
+			//	}
+			//	CycleIntervalIterator.MoveNext();
+			//}
+
+			var stopTime = Left.PTOActive && IdleController != null
+				? Left.StoppingTime + IdleController.Duration
+				: Left.StoppingTime;
+
+			// separately test for equality and greater than to have tolerance for equality comparison
+			if (stopTime.IsEqual(0)) {
+				while (stopTime.IsEqual(0) && CurrentState.Distance.IsGreaterOrEqual(Right.Distance) &&
+						!CycleIntervalIterator.LastEntry) {
+					// we have reached the end of the current interval in the cycle, move on...
+					CycleIntervalIterator.MoveNext();
+
+					stopTime = Left.PTOActive && IdleController != null
+						? Left.StoppingTime + IdleController.Duration
+						: Left.StoppingTime;
+				}
+			} else {
+				if (stopTime.IsEqual(PreviousState.WaitTime)) {
+					// we needed to stop at the current interval in the cycle and have already waited enough time, move on..
+					if (IdleController != null) {
+						IdleController.ActivateIdle();
+					}
+					CycleIntervalIterator.MoveNext();
+				}
+			}
+		}
+
+		private Radian ComputeGradient(Meter ds)
+		{
+			var cycleIterator = CycleIntervalIterator.Clone();
+			while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && !cycleIterator.LastEntry) {
+				cycleIterator.MoveNext();
+			}
+			var leftSamplePoint = cycleIterator.LeftSample;
+			var rightSamplePoint = cycleIterator.RightSample;
+
+			if (leftSamplePoint.Distance.IsEqual(rightSamplePoint.Distance)) {
+				return leftSamplePoint.RoadGradient;
+			}
+			if (ds.IsEqual(0.SI<Meter>())) {
+				return leftSamplePoint.RoadGradient;
+			}
+			CurrentState.Altitude = VectoMath.Interpolate(leftSamplePoint.Distance, rightSamplePoint.Distance,
+				leftSamplePoint.Altitude, rightSamplePoint.Altitude, PreviousState.Distance + ds);
+
+			var gradient = VectoMath.InclinationToAngle(((CurrentState.Altitude - PreviousState.Altitude) /
+														ds).Value());
+			//return 0.SI<Radian>();
+			return gradient;
+		}
+
+		private Meter GetSpeedChangeWithinSimulationInterval(Meter ds)
+		{
+			var leftSamplePoint = Left;
+			var cycleIterator = CycleIntervalIterator.Clone();
+
+			do {
+				if (!leftSamplePoint.VehicleTargetSpeed.IsEqual(cycleIterator.RightSample.VehicleTargetSpeed)) {
+					return cycleIterator.RightSample.Distance;
+				}
+			} while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && cycleIterator.MoveNext());
+			if (cycleIterator.LastEntry) {
+				return cycleIterator.RightSample.Distance;
+			}
+			return null;
+		}
+
+		/// <summary>
+		/// Progress of the distance in the driving cycle.
+		/// </summary>
+		public double Progress
+		{
+			get {
+				return Data.Entries.Count > 0
+					? (CurrentState.Distance.Value() - Data.Entries.First().Distance.Value()) /
+					(Data.Entries.Last().Distance.Value() - Data.Entries.First().Distance.Value())
+					: 0;
+			}
+		}
+
+		public Meter CycleStartDistance { get; internal set; }
+
+		public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Meter lookaheadDistance)
+		{
+			var retVal = new List<DrivingCycleData.DrivingCycleEntry>();
+
+			var cycleIterator = CycleIntervalIterator.Clone();
+			var velocity = cycleIterator.LeftSample.VehicleTargetSpeed;
+
+			do {
+				if (cycleIterator.RightSample.VehicleTargetSpeed.IsEqual(velocity)) {
+					continue;
+				}
+				var lookaheadEntry = retVal.Find(x => x.Distance == cycleIterator.RightSample.Distance);
+				if (lookaheadEntry != null) {
+					// an entry may occur twice when vehicle stops (one entry with v=0 and the other with drive on after stop)
+					// only use the one with min. speed
+					if (cycleIterator.RightSample.VehicleTargetSpeed < lookaheadEntry.VehicleTargetSpeed) {
+						retVal.Remove(lookaheadEntry);
+						retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here?
+					}
+				} else {
+					retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here?
+				}
+				velocity = cycleIterator.RightSample.VehicleTargetSpeed;
+				if (velocity.IsEqual(0.KMPHtoMeterPerSecond())) {
+					// do not look beyond vehicle stop
+					break;
+				}
+			} while (cycleIterator.MoveNext() && cycleIterator.RightSample.Distance < PreviousState.Distance + lookaheadDistance);
+			if (retVal.Count > 0) {
+				retVal = retVal.Where(x => x.Distance <= PreviousState.Distance + lookaheadDistance).ToList();
+				retVal.Sort((x, y) => x.Distance.CompareTo(y.Distance));
+			}
+			return retVal;
+		}
+
+		public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Second time)
+		{
+			return LookAhead(LookaheadTimeSafetyMargin * DataBus.VehicleSpeed * time);
+		}
+
+		public void FinishSimulation()
+		{
+			Data.Finish();
+		}
+
+		public CycleData CycleData
+		{
+			get {
+				return new CycleData {
+					AbsTime = CurrentState.AbsTime,
+					AbsDistance = CurrentState.Distance,
+					LeftSample = Left,
+					RightSample = CycleIntervalIterator.RightSample
+				};
+			}
+		}
+
+		public bool PTOActive { get; private set; }
+
+		public DrivingCycleData.DrivingCycleEntry CycleLookAhead(Meter distance)
+		{
+			var absDistance = CurrentState.Distance + distance;
+			var myIterator = CycleIntervalIterator.Clone();
+
+			if (absDistance > Data.Entries.Last().Distance) {
+				return ExtrapolateCycleEntry(absDistance, Data.Entries.Last());
+			}
+			while (myIterator.RightSample.Distance < absDistance) {
+				myIterator.MoveNext();
+			}
+
+			return InterpolateCycleEntry(absDistance, myIterator.RightSample);
+		}
+
+		private DrivingCycleData.DrivingCycleEntry InterpolateCycleEntry(Meter absDistance,
+			DrivingCycleData.DrivingCycleEntry lookahead)
+		{
+			var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) {
+				Distance = absDistance,
+				Altitude = VectoMath.Interpolate(CurrentState.Distance, lookahead.Distance, CurrentState.Altitude,
+					lookahead.Altitude, absDistance)
+			};
+
+			retVal.RoadGradient =
+				((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>();
+
+			return retVal;
+		}
+
+		private DrivingCycleData.DrivingCycleEntry ExtrapolateCycleEntry(Meter absDistance,
+			DrivingCycleData.DrivingCycleEntry lookahead)
+		{
+			var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) {
+				Distance = absDistance,
+				Altitude = lookahead.Altitude + lookahead.RoadGradient * (absDistance - lookahead.Distance),
+			};
+
+			retVal.RoadGradient =
+				((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>();
+
+			return retVal;
+		}
+
+		public Meter Altitude
+		{
+			get { return PreviousState.Altitude; }
+		}
+
+		public sealed class DrivingCycleState
+		{
+			public DrivingCycleState Clone()
+			{
+				return new DrivingCycleState {
+					AbsTime = AbsTime,
+					Distance = Distance,
+					VehicleTargetSpeed = VehicleTargetSpeed,
+					Altitude = Altitude,
+					WaitPhase = WaitPhase,
+					// WaitTime is not cloned on purpose!
+					WaitTime = 0.SI<Second>(),
+					Response = null
+				};
+			}
+
+			public Second AbsTime;
+
+			public Meter Distance;
+
+			public Second WaitTime;
+
+			public uint WaitPhase;
+
+			public MeterPerSecond VehicleTargetSpeed;
+
+			public Meter Altitude;
+
+			public Radian Gradient;
+
+			public IResponse Response;
+
+			public bool RequestToNextSamplePointDone;
+
+			public Meter SimulationDistance;
+		}
+
+		public void Dispose()
+		{
+			CycleIntervalIterator.Dispose();
+		}
+	}
 }
\ No newline at end of file
-- 
GitLab