diff --git a/src/Devices/AtcBombardier.cs b/src/Devices/AtcBombardier.cs
index 34aa334..46795d8 100644
--- a/src/Devices/AtcBombardier.cs
+++ b/src/Devices/AtcBombardier.cs
@@ -63,6 +63,40 @@ namespace OpenbveFcmbTrainPlugin
/// A counter to keep track of YARD mode idle time, in seconds.
private double YardIdleCounter;
+ /// The maximum speed in YARD mode.
+ private double YardMaximumSpeed = 20;
+
+ /// Represents an ATC movement permission order.
+ private class MovementPermission
+ {
+ internal Speed LimitBase { get; private set; }
+ internal Speed LimitNoTraction { get; private set; }
+ internal Speed LimitService { get; private set; }
+ internal Speed LimitEmergency { get; private set; }
+ internal Speed TargetLimit { get; private set; }
+ internal double TargetLocation { get; private set; }
+
+ internal MovementPermission(Speed limit, Speed target, double targetLocation)
+ {
+ LimitBase = limit;
+ LimitNoTraction = new Speed(limit.MetersPerSecond + 3 / 3.6);
+ LimitService = new Speed(limit.MetersPerSecond + 6 / 3.6);
+ LimitEmergency = new Speed(limit.MetersPerSecond + 9 / 3.6);
+ TargetLimit = target;
+ TargetLocation = targetLocation;
+ }
+ }
+
+ /// The current permission regarding speed and travel distance.
+ private MovementPermission CurrentPermission;
+
+ /// The ideal power notch for the AI.
+ private int AiPowerNotch;
+
+ /// The ideal brake notch for the AI.
+ private int AiBrakeNotch;
+
+
/// Is called when the device state should be updated.
/// The current train.
/// The current route.
@@ -76,70 +110,63 @@ namespace OpenbveFcmbTrainPlugin
DeviceState = DeviceStates.Initializing;
}
+ // Update ATC movement permission
+ UpdatePermission();
+
double speed = train.State.Speed.KilometersPerHour;
bool stopped = speed < 0.05;
- // Speed limit calculation
- // By default, use YARD mode speed limit
- double limit = 25;
- double powerLimit = limit - 2;
- double releaseLimit = limit - 5;
- double emergencyLimit = limit + 4;
-
// Speed limit enforcement
if (DeviceState == DeviceStates.YARD || DeviceState == DeviceStates.ATP || DeviceState == DeviceStates.ATO)
{
- if (AtcControlState == AtcControlStates.Released && speed >= powerLimit)
+ if (AtcControlState == AtcControlStates.Released && speed > CurrentPermission.LimitNoTraction.KilometersPerHour)
{
// Cut power on power limit
AtcControlState = AtcControlStates.NoPower;
}
- if (AtcControlState == AtcControlStates.NoPower && speed < powerLimit)
+ if (AtcControlState == AtcControlStates.NoPower && speed < CurrentPermission.LimitNoTraction.KilometersPerHour)
{
// Stop cutting power below power limit
AtcControlState = AtcControlStates.Released;
}
- if (speed > limit && speed < emergencyLimit)
+ if (speed > CurrentPermission.LimitService.KilometersPerHour && speed < CurrentPermission.LimitEmergency.KilometersPerHour)
{
// Apply service brake on limit
AtcControlState = AtcControlStates.BrakeServiceNoRelease;
}
- if (speed > emergencyLimit)
+ if (speed > CurrentPermission.LimitEmergency.KilometersPerHour)
{
// Unselect driving mode to apply emergency brake if overspeeding
DeviceState = DeviceStates.Initialized;
}
// Allow brake release under defined threshold
- if (AtcControlState == AtcControlStates.BrakeServiceNoRelease && speed < releaseLimit)
+ if (AtcControlState == AtcControlStates.BrakeServiceNoRelease && speed < CurrentPermission.LimitBase.KilometersPerHour)
{
AtcControlState = AtcControlStates.BrakeService;
}
}
// Brake application
- if (DeviceState != DeviceStates.Override)
+ switch (AtcControlState)
{
- switch (AtcControlState)
- {
- case AtcControlStates.Released:
- RequestedBrakeNotch = -1;
- RequestedPowerNotch = -1;
- break;
- case AtcControlStates.NoPower:
- RequestedBrakeNotch = -1;
- RequestedPowerNotch = 0;
- break;
- case AtcControlStates.BrakeService:
- case AtcControlStates.BrakeServiceNoRelease:
- RequestedBrakeNotch = train.ServiceBrakeNotch;
- RequestedPowerNotch = 0;
- break;
- case AtcControlStates.BrakeEmergency:
- RequestedBrakeNotch = train.Specs.BrakeNotches + 1;
- RequestedPowerNotch = 0;
- break;
- }
+ case AtcControlStates.Released:
+ RequestedBrakeNotch = -1;
+ RequestedPowerNotch = -1;
+ break;
+ case AtcControlStates.NoPower:
+ RequestedBrakeNotch = -1;
+ RequestedPowerNotch = 0;
+ break;
+ case AtcControlStates.BrakeService:
+ case AtcControlStates.BrakeServiceNoRelease:
+ RequestedBrakeNotch = train.ServiceBrakeNotch;
+ RequestedPowerNotch = 0;
+ break;
+ case AtcControlStates.BrakeEmergency:
+ RequestedBrakeNotch = train.Specs.BrakeNotches + 1;
+ RequestedPowerNotch = 0;
+ break;
}
switch (DeviceState)
@@ -149,8 +176,7 @@ namespace OpenbveFcmbTrainPlugin
train.ContinuousProtection = false;
train.VigilanceOverride = false;
// Release control of the brakes and power
- RequestedBrakeNotch = -1;
- RequestedPowerNotch = -1;
+ AtcControlState = AtcControlStates.Released;
break;
// ATC device is initializing
case DeviceStates.Initializing:
@@ -173,6 +199,8 @@ namespace OpenbveFcmbTrainPlugin
train.VigilanceOverride = false;
// Apply service/emergency brake while no driving mode is selected
AtcControlState = stopped ? AtcControlStates.BrakeServiceNoRelease : AtcControlStates.BrakeEmergency;
+ // ATC movement permission
+ CurrentPermission = new MovementPermission(new Speed(0), new Speed(0), 0);
break;
// ATC device is in YARD (M+25) driving mode
case DeviceStates.YARD:
@@ -182,36 +210,18 @@ namespace OpenbveFcmbTrainPlugin
if (stopped)
{
YardIdleCounter += elapsedTime.Seconds;
+ // If above idle time, apply the service brake
+ AtcControlState = YardIdleCounter >= YardIdleTime ? AtcControlStates.BrakeService : AtcControlStates.Released;
}
else
{
YardIdleCounter = 0;
}
- // If above idle time, apply the service brake
- if (YardIdleCounter >= YardIdleTime)
- {
- AtcControlState = AtcControlStates.BrakeService;
- }
- else
- {
- AtcControlState = AtcControlStates.Released;
- }
// Apply brake if any door opens
if (train.DoorState != DoorStates.None)
{
RequestedBrakeNotch = stopped ? train.Specs.BrakeNotches + 1 : train.ServiceBrakeNotch;
}
- // Speed limitations
- if (speed > 23)
- {
- // Cut power over 23 km/h
- RequestedPowerNotch = 0;
- if (speed > 25)
- {
- // Apply service brake over 25 km/h
- AtcControlState = AtcControlStates.BrakeServiceNoRelease;
- }
- }
break;
// ATC device is in ATP (M+ATP) driving mode
case DeviceStates.ATP:
@@ -239,17 +249,19 @@ namespace OpenbveFcmbTrainPlugin
switch (AtcControlState)
{
case AtcControlStates.Released:
- case AtcControlStates.BrakeEmergency:
+ case AtcControlStates.NoPower:
train.Panel[32] = 0;
break;
case AtcControlStates.BrakeServiceNoRelease:
case AtcControlStates.BrakeFull:
- train.Panel[32] = 1;
+ case AtcControlStates.BrakeEmergency:
+ train.Panel[32] = DeviceState != DeviceStates.Initializing ? 1 : 0;
break;
case AtcControlStates.BrakeService:
train.Panel[32] = blink ? 1 : 0;
break;
}
+ train.Panel[34] = (int)CurrentPermission.LimitBase.KilometersPerHour * 1000;
}
@@ -259,6 +271,9 @@ namespace OpenbveFcmbTrainPlugin
/// The current train.
internal override void KeyChange(VirtualKeys key, bool pressed, Train train)
{
+ double speed = train.State.Speed.KilometersPerHour;
+ bool stopped = speed < 0.05;
+
if (pressed)
{
switch (key)
@@ -267,7 +282,7 @@ namespace OpenbveFcmbTrainPlugin
// YARD (M+25) mode selection button
if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key])
{
- if (DeviceState == DeviceStates.Initialized && train.State.Speed.KilometersPerHour < 0.05)
+ if (DeviceState == DeviceStates.Initialized && stopped)
{
DeviceState = DeviceStates.YARD;
}
@@ -314,14 +329,14 @@ namespace OpenbveFcmbTrainPlugin
case DeviceStates.Initialized:
case DeviceStates.YARD:
// Override device if possible
- if (train.State.Speed.KilometersPerHour < 0.05 && train.PhysicalHandles.PowerNotch == 0)
+ if (stopped && train.PhysicalHandles.PowerNotch == 0)
{
DeviceState = DeviceStates.Override;
}
break;
case DeviceStates.Override:
// Disable override if possible
- if (train.State.Speed.KilometersPerHour < 0.05 && train.PhysicalHandles.PowerNotch == 0)
+ if (stopped && train.PhysicalHandles.PowerNotch == 0)
{
DeviceState = DeviceStates.Initializing;
}
@@ -346,6 +361,13 @@ namespace OpenbveFcmbTrainPlugin
/// The current route.
internal override void PerformAI(AIData data, Train train, Route route)
{
+ // Set AI notches to what the AI is doing right now
+ AiBrakeNotch = data.Handles.BrakeNotch;
+ AiPowerNotch = data.Handles.PowerNotch;
+
+ double speed = train.State.Speed.KilometersPerHour;
+ bool stopped = speed < 0.05;
+
switch (DeviceState)
{
case DeviceStates.Initializing:
@@ -357,14 +379,92 @@ namespace OpenbveFcmbTrainPlugin
data.Handles.BrakeNotch = train.Specs.BrakeNotches;
data.Response = AIResponse.Short;
// If the train is stopped, select YARD (M+25) mode
- if (train.State.Speed.KilometersPerHour < 0.05)
+ if (stopped)
{
- data.Response = AIResponse.Long;
KeyChange(VirtualKeys.I, true, train);
data.Response = AIResponse.Short;
KeyChange(VirtualKeys.I, false, train);
}
break;
+ case DeviceStates.YARD:
+ case DeviceStates.ATP:
+ // If the ATC brakes can be released, press the ATC brake release button
+ if (AtcControlState == AtcControlStates.BrakeService)
+ {
+ KeyChange(VirtualKeys.B1, true, train);
+ data.Response = AIResponse.Medium;
+ KeyChange(VirtualKeys.B1, false, train);
+ }
+ // Calculate ideal notch for AI when approaching the speed limit
+ if (speed > CurrentPermission.LimitBase.KilometersPerHour - 2 && train.Acceleration > 0.1)
+ {
+ if (data.Handles.PowerNotch > 0)
+ {
+ AiPowerNotch -= 1;
+ }
+ else if (train.Specs.HasHoldBrake)
+ {
+ data.Handles.HoldBrake = true;
+ }
+ else
+ {
+ AiBrakeNotch += 1;
+ }
+ }
+ else if (speed < CurrentPermission.LimitBase.KilometersPerHour - 4 && train.Acceleration < 0.25)
+ {
+ if (data.Handles.BrakeNotch > 0)
+ {
+ AiBrakeNotch -= 1;
+ }
+ else
+ {
+ AiPowerNotch += 1;
+ }
+ }
+ // If still far from next station, take control of AI handles
+ if (speed / (route.CurrentStation.StopPosition - train.State.Location) < 0.2)
+ {
+ if (speed > CurrentPermission.LimitBase.KilometersPerHour - 5)
+ {
+ data.Handles.PowerNotch = AiPowerNotch;
+ data.Handles.BrakeNotch = AiBrakeNotch;
+ data.Response = AIResponse.Medium;
+ }
+ }
+ break;
+ }
+ }
+
+ /// Updates the data for the current and next station.
+ internal void UpdatePermission()
+ {
+ Speed zero = new Speed(0);
+ Speed yard = new Speed(YardMaximumSpeed / 3.6);
+
+ switch (DeviceState)
+ {
+ case DeviceStates.Override:
+ case DeviceStates.Initializing:
+ case DeviceStates.Initialized:
+ if (CurrentPermission == null || CurrentPermission.LimitBase != zero || CurrentPermission.TargetLimit != zero)
+ {
+ CurrentPermission = new MovementPermission(zero, zero, 0);
+ }
+ break;
+ case DeviceStates.YARD:
+ if (CurrentPermission == null || CurrentPermission.LimitBase != yard || CurrentPermission.TargetLimit != yard)
+ {
+ CurrentPermission = new MovementPermission(yard, yard, 0);
+ }
+ break;
+ case DeviceStates.ATP:
+ case DeviceStates.ATO:
+ if (CurrentPermission == null || CurrentPermission.LimitBase != zero || CurrentPermission.TargetLimit != zero)
+ {
+ CurrentPermission = new MovementPermission(zero, zero, 0);
+ }
+ break;
}
}
diff --git a/src/Train/Train.cs b/src/Train/Train.cs
index 68cec90..4f44b82 100644
--- a/src/Train/Train.cs
+++ b/src/Train/Train.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
@@ -47,6 +48,15 @@ namespace OpenbveFcmbTrainPlugin
/// The service brake notch for ATC.
internal int ServiceBrakeNotch { get; private set; }
+ /// The current acceleration of the train.
+ internal double Acceleration { get; private set; }
+
+ /// The time when acceleration was calculated.
+ private Time AccelerationTime = new Time(0);
+
+ /// The speed when acceleration was calculated.
+ private Speed AccelerationSpeed = new Speed(0);
+
/// Creates a new train with the device configuration provided.
/// The array of panel variables. 200)
+ {
+ Acceleration = Math.Round((State.Speed.MetersPerSecond - AccelerationSpeed.MetersPerSecond) / (data.TotalTime.Seconds - AccelerationTime.Seconds), 4);
+ AccelerationTime = data.TotalTime;
+ AccelerationSpeed = State.Speed;
+ }
+
// Retrieve data from all devices
foreach (Device dev in Devices)
{