Skip to content

Commit 8228f35

Browse files
authored
Merge pull request #1094 from SteelFill/blended_brake_tweaks
Blended Braking Compatibility and Effectiveness Improvements
2 parents 07be044 + b9b4843 commit 8228f35

File tree

3 files changed

+82
-33
lines changed

3 files changed

+82
-33
lines changed

Source/Documentation/Manual/physics.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,6 +3343,8 @@ the following parameters will adjust the behaviour of air brakes:
33433343
.. index::
33443344
single: DynamicBrakeHasAutoBailOff
33453345
single: ORTSDynamicBrakesHasPartialBailOff
3346+
single: ORTSDynamicBlendingRetainedPressure
3347+
single: ORTSDynamicBlendingMinimumSpeed
33463348
single: ORTSTrainDynamicBlendingTable
33473349
single: ORTSDynamicBrakeReplacementWithEngineBrake
33483350
single: ORTSDynamicBrakeReplacementWithEngineBrakeAtSpeed
@@ -3353,6 +3355,13 @@ the following parameters will adjust the behaviour of air brakes:
33533355
air brakes are released while dynamic brakes satisfy the train brake demand.
33543356
If dynamic braking is not sufficient, air brakes will be partially applied
33553357
so the combination air+dynamic provides the required brake demand.
3358+
- ``Engine(ORTSDynamicBlendingRetainedPressure`` -- Sets the brake cylinder
3359+
pressure which, when used in combination with ORTSDynamicBrakesHasPartialBailOff,
3360+
will remain applied regardless of the blended dynamic brake force. This
3361+
pressure is also the minimum pressure at which the blended braking system will activate.
3362+
- ``Engine(ORTSDynamicBlendingMinimumSpeed`` -- Below the specified speed
3363+
(default units mph, default value 5 mph / 8 kph), local dynamic brake blending
3364+
will be disabled, allowing locomotive brakes to hold the train while stopped.
33563365

33573366
Sometimes the train brake controller is capable to apply the dynamic
33583367
brakes for the whole consist, usually as a first step before air brakes

Source/Orts.Simulation/Simulation/RollingStocks/MSTSLocomotive.cs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ public float OdometerM
420420
public double? DynamicBrakeCommandStartTime;
421421
protected bool DynamicBrakeBlendingOverride; // true when DB lever >0% should always override the blending. When false, the bigger command is applied.
422422
protected bool DynamicBrakeBlendingForceMatch = true; // if true, dynamic brake blending tries to achieve the same braking force as the airbrake would have.
423+
public float DynamicBrakeBlendingRetainedPressurePSI { get; private set; } = -1.0f; // the amount of pressure that will always be retained in the brake cylinders during blended braking
424+
public float DynamicBrakeBlendingMinSpeedMpS { get; private set; } = -1.0f; // below this speed, blended braking is disabled
423425
protected bool DynamicBrakeControllerSetupLock; // if true if dynamic brake lever will lock until dynamic brake is available
424426

425427
public float DynamicBrakeBlendingPercent { get; protected set; } = -1;
@@ -1097,6 +1099,7 @@ public override void Parse(string lowercasetoken, STFReader stf)
10971099
case "engine(ortsdynamicbrakeshaspartialbailoff": DynamicBrakePartialBailOff = stf.ReadBoolBlock(false); break;
10981100
case "engine(ortsdynamicbrakereplacementwithenginebrake": DynamicBrakeEngineBrakeReplacement = stf.ReadBoolBlock(false); break;
10991101
case "engine(ortsdynamicbrakereplacementwithenginebrakeatspeed": DynamicBrakeEngineBrakeReplacementSpeed = stf.ReadFloatBlock(STFReader.UNITS.SpeedDefaultMPH, null); break;
1102+
case "engine(ortsdynamicblendingminimumspeed": DynamicBrakeBlendingMinSpeedMpS = stf.ReadFloatBlock(STFReader.UNITS.SpeedDefaultMPH, null); break;
11001103
case "engine(dynamicbrakesdelaytimebeforeengaging": DynamicBrakeDelayS = stf.ReadFloatBlock(STFReader.UNITS.Time, null); break;
11011104
case "engine(dynamicbrakesresistorcurrentlimit": DynamicBrakeMaxCurrentA = stf.ReadFloatBlock(STFReader.UNITS.Current, null); break;
11021105
case "engine(numwheels": MSTSLocoNumDrvWheels = stf.ReadFloatBlock(STFReader.UNITS.None, 4.0f); if (MSTSLocoNumDrvWheels < 1) STFException.TraceWarning(stf, "Engine:NumWheels is less than 1, parts of the simulation may not function correctly"); break;
@@ -1177,6 +1180,7 @@ public override void Parse(string lowercasetoken, STFReader stf)
11771180
break;
11781181
case "engine(ortsdynamicblendingoverride": DynamicBrakeBlendingOverride = stf.ReadBoolBlock(false); break;
11791182
case "engine(ortsdynamicblendingforcematch": DynamicBrakeBlendingForceMatch = stf.ReadBoolBlock(false); break;
1183+
case "engine(ortsdynamicblendingretainedpressure": DynamicBrakeBlendingRetainedPressurePSI = stf.ReadFloatBlock(STFReader.UNITS.PressureDefaultPSI, null); break;
11801184
case "engine(vacuumbrakeshasvacuumpump": VacuumPumpFitted = stf.ReadBoolBlock(false); break;
11811185
case "engine(enginecontrollers(ortssteamheat": SteamHeatController.Parse(stf); break;
11821186
case "engine(name": stf.MustMatch("("); LocomotiveName = stf.ReadString(); break;
@@ -1263,6 +1267,7 @@ public override void Copy(MSTSWagon copy)
12631267
DynamicBrakePartialBailOff = locoCopy.DynamicBrakePartialBailOff;
12641268
DynamicBrakeEngineBrakeReplacement = locoCopy.DynamicBrakeEngineBrakeReplacement;
12651269
DynamicBrakeEngineBrakeReplacementSpeed = locoCopy.DynamicBrakeEngineBrakeReplacementSpeed;
1270+
DynamicBrakeBlendingMinSpeedMpS = locoCopy.DynamicBrakeBlendingMinSpeedMpS;
12661271
DynamicBrakeMaxCurrentA = locoCopy.DynamicBrakeMaxCurrentA;
12671272
DynamicBrakeSpeed1MpS = locoCopy.DynamicBrakeSpeed1MpS;
12681273
DynamicBrakeSpeed2MpS = locoCopy.DynamicBrakeSpeed2MpS;
@@ -1334,6 +1339,7 @@ public override void Copy(MSTSWagon copy)
13341339
DynamicBrakeCommandStartTime = locoCopy.DynamicBrakeCommandStartTime;
13351340
DynamicBrakeBlendingOverride = locoCopy.DynamicBrakeBlendingOverride;
13361341
DynamicBrakeBlendingForceMatch = locoCopy.DynamicBrakeBlendingForceMatch;
1342+
DynamicBrakeBlendingRetainedPressurePSI = locoCopy.DynamicBrakeBlendingRetainedPressurePSI;
13371343
DynamicBrakeControllerSetupLock = locoCopy.DynamicBrakeControllerSetupLock;
13381344

13391345
MainPressureUnit = locoCopy.MainPressureUnit;
@@ -1889,6 +1895,21 @@ public override void Initialize()
18891895
DynamicBrakeEngineBrakeReplacementSpeed = DynamicBrakeSpeed2MpS;
18901896
}
18911897

1898+
// Define blending minimum speed if it was left undefined (use MSTS minimum dynamic brake speed)
1899+
if (DynamicBrakeBlendingMinSpeedMpS < 0)
1900+
{
1901+
DynamicBrakeBlendingMinSpeedMpS = DynamicBrakeSpeed1MpS;
1902+
}
1903+
1904+
// Define blending retained pressure if it was left undefined
1905+
if (DynamicBrakeBlendingRetainedPressurePSI < 0)
1906+
{
1907+
if (BrakeSystem is AirSinglePipe airSystem)
1908+
DynamicBrakeBlendingRetainedPressurePSI = 2.0f * airSystem.BrakeCylinderSpringPressurePSI;
1909+
else
1910+
DynamicBrakeBlendingRetainedPressurePSI = 0.0f;
1911+
}
1912+
18921913
// Initialise track sanding parameters
18931914
if (MaxTrackSandBoxCapacityM3 == 0)
18941915
{
@@ -2022,24 +2043,43 @@ protected void CorrectBrakingParams()
20222043
public void DynamicBrakeBlending(float elapsedClockSeconds)
20232044
{
20242045
// Local blending
2025-
if (Math.Abs(SpeedMpS) > DynamicBrakeSpeed1MpS && airPipeSystem != null && airPipeSystem.AutoCylPressurePSI > 0.1f
2046+
if (airPipeSystem == null)
2047+
{
2048+
DynamicBrakeBlendingPercent = -1;
2049+
return;
2050+
}
2051+
float autoDemandedPressurePSI = airPipeSystem.AutoCylPressurePSI * airPipeSystem.RelayValveRatio;
2052+
2053+
if (Math.Abs(SpeedMpS) > DynamicBrakeBlendingMinSpeedMpS && autoDemandedPressurePSI > DynamicBrakeBlendingRetainedPressurePSI
20262054
&& ThrottlePercent == 0 && !(DynamicBrakeController != null && DynamicBrakeBlendingOverride && DynamicBrakeController.SavedValue > 0))
20272055
{
2028-
float maxCylPressurePSI = airPipeSystem.GetMaxCylPressurePSI();
2029-
float target = airPipeSystem.AutoCylPressurePSI * airPipeSystem.RelayValveRatio / maxCylPressurePSI;
2056+
float target = (autoDemandedPressurePSI - DynamicBrakeBlendingRetainedPressurePSI) /
2057+
(airPipeSystem.ReferencePressurePSI - DynamicBrakeBlendingRetainedPressurePSI);
20302058

20312059
if (DynamicBrakeBlendingForceMatch)
20322060
{
2033-
float diff = target * FrictionBrakeBlendingMaxForceN - DynamicBrakeForceN;
2034-
float threshold = 100;
2035-
if (diff > threshold && DynamicBrakePercent < 100)
2036-
DynamicBrakeBlendingPercent = Math.Min(DynamicBrakePercent + 100 * elapsedClockSeconds, 100);
2037-
else if (diff < -threshold && DynamicBrakePercent > 1)
2038-
DynamicBrakeBlendingPercent = Math.Max(DynamicBrakePercent - 100 * elapsedClockSeconds, 1);
2061+
if (DynamicBrake)
2062+
{
2063+
float diff = target * FrictionBrakeBlendingMaxForceN - DynamicBrakeForceN;
2064+
float normDiff = diff / MaxDynamicBrakeForceN;
2065+
2066+
if (Math.Abs(diff) > 100.0f)
2067+
{
2068+
// Limit rate of change to reduce overshoots
2069+
if (diff > 0 && DynamicBrakeForceRampUpNpS > 0)
2070+
normDiff = Math.Min(normDiff, DynamicBrakeForceRampUpNpS / MaxDynamicBrakeForceN);
2071+
if (diff < 0 && DynamicBrakeForceRampDownNpS > 0)
2072+
normDiff = Math.Max(normDiff, -DynamicBrakeForceRampDownNpS / MaxDynamicBrakeForceN);
2073+
2074+
DynamicBrakeBlendingPercent = MathHelper.Clamp(DynamicBrakePercent + 100.0f * normDiff * elapsedClockSeconds, 1f, 100f);
2075+
}
2076+
}
2077+
else // Don't increase dynamic brake setting until set-up has completed
2078+
DynamicBrakeBlendingPercent = 1.0f;
20392079
}
20402080
else
20412081
{
2042-
DynamicBrakeBlendingPercent = target * 100;
2082+
DynamicBrakeBlendingPercent = MathHelper.Clamp(target * 100, 1f, 100f);
20432083
}
20442084
}
20452085
else

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/Brakes/MSTS/AirSinglePipe.cs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public enum BrakeValveType
8181
protected float ControlResPressurePSI = 64;
8282
protected float FullServPressurePSI = 50;
8383
protected float MaxCylPressurePSI;
84-
protected float ReferencePressurePSI;
84+
public float ReferencePressurePSI { get; protected set; }
8585
protected float MaxTripleValveCylPressurePSI;
8686
protected float AuxResVolumeM3;
8787
protected float AuxCylVolumeRatio;
@@ -143,7 +143,7 @@ public enum BrakeValveType
143143
protected float AcceleratedApplicationLimitPSIpS = 5.0f;
144144
protected float InitialApplicationThresholdPSI;
145145
protected float TripleValveSensitivityPSI;
146-
protected float BrakeCylinderSpringPressurePSI;
146+
public float BrakeCylinderSpringPressurePSI { get; protected set; }
147147
protected float ServiceMaxCylPressurePSI;
148148
protected float ServiceApplicationRatePSIpS;
149149
protected float TwoStageLowPressurePSI;
@@ -1721,25 +1721,21 @@ public override void Update(float elapsedClockSeconds)
17211721
if (loco.Train.DetermineDPLeadLocomotive(loco) is MSTSLocomotive lead && (lead.BailOff || (lead.EngineBrakeController != null && lead.EngineBrakeController.TrainBrakeControllerState == ControllerState.BailOff)))
17221722
{
17231723
if (BrakeValve == BrakeValveType.Distributor)
1724-
{
17251724
ControlResPressurePSI = 0;
17261725

1727-
if (loco.AttachedTender?.BrakeSystem is AirSinglePipe tenderBrakes)
1728-
tenderBrakes.ControlResPressurePSI = 0;
1729-
}
1730-
else
1726+
float dp = Math.Max(MaxReleaseRatePSIpS, loco.EngineBrakeReleaseRatePSIpS) * elapsedClockSeconds;
1727+
AutoCylPressurePSI -= dp;
1728+
if (AutoCylPressurePSI < 0)
1729+
AutoCylPressurePSI = 0;
1730+
1731+
if (loco.AttachedTender?.BrakeSystem is AirSinglePipe tenderBrakes)
17311732
{
1732-
float dp = Math.Max(MaxReleaseRatePSIpS, loco.EngineBrakeReleaseRatePSIpS) * elapsedClockSeconds;
1733-
AutoCylPressurePSI -= dp;
1734-
if (AutoCylPressurePSI < 0)
1735-
AutoCylPressurePSI = 0;
1733+
if (tenderBrakes.BrakeValve == BrakeValveType.Distributor)
1734+
tenderBrakes.ControlResPressurePSI = 0;
17361735

1737-
if (loco.AttachedTender?.BrakeSystem is AirSinglePipe tenderBrakes)
1738-
{
1739-
tenderBrakes.AutoCylPressurePSI -= dp;
1740-
if (tenderBrakes.AutoCylPressurePSI < 0)
1741-
tenderBrakes.AutoCylPressurePSI = 0;
1742-
}
1736+
tenderBrakes.AutoCylPressurePSI -= dp;
1737+
if (tenderBrakes.AutoCylPressurePSI < 0)
1738+
tenderBrakes.AutoCylPressurePSI = 0;
17431739
}
17441740
}
17451741
}
@@ -1750,19 +1746,23 @@ public override void Update(float elapsedClockSeconds)
17501746
{
17511747
if (loco.DynamicBrakePartialBailOff)
17521748
{
1753-
var requiredBrakeForceN = MathHelper.Max((AutoCylPressurePSI * RelayValveRatio - BrakeCylinderSpringPressurePSI)
1754-
/ (ReferencePressurePSI - BrakeCylinderSpringPressurePSI), 0) * Car.FrictionBrakeBlendingMaxForceN;
1755-
var localBrakeForceN = loco.DynamicBrakeForceN + MathHelper.Max((CylPressurePSI - BrakeCylinderSpringPressurePSI)
1756-
/ (ReferencePressurePSI - BrakeCylinderSpringPressurePSI), 0) * Car.FrictionBrakeBlendingMaxForceN;
1749+
var requiredBrakeForceN = (AutoCylPressurePSI * RelayValveRatio - loco.DynamicBrakeBlendingRetainedPressurePSI)
1750+
/ (ReferencePressurePSI - loco.DynamicBrakeBlendingRetainedPressurePSI) * Car.FrictionBrakeBlendingMaxForceN;
1751+
var localBrakeForceN = loco.DynamicBrakeForceN + (CylPressurePSI - loco.DynamicBrakeBlendingRetainedPressurePSI)
1752+
/ (ReferencePressurePSI - loco.DynamicBrakeBlendingRetainedPressurePSI) * Car.FrictionBrakeBlendingMaxForceN;
17571753
if (localBrakeForceN > requiredBrakeForceN - 0.15f * Car.FrictionBrakeBlendingMaxForceN)
17581754
{
1759-
demandedPressurePSI = Math.Min(Math.Max((requiredBrakeForceN - loco.DynamicBrakeForceN) / Car.FrictionBrakeBlendingMaxForceN * ReferencePressurePSI
1760-
+ BrakeCylinderSpringPressurePSI, 0), MaxCylPressurePSI);
1755+
demandedPressurePSI = MathHelper.Clamp((requiredBrakeForceN - loco.DynamicBrakeForceN) / Car.FrictionBrakeBlendingMaxForceN *
1756+
(ReferencePressurePSI - loco.DynamicBrakeBlendingRetainedPressurePSI) + loco.DynamicBrakeBlendingRetainedPressurePSI,
1757+
loco.DynamicBrakeBlendingRetainedPressurePSI, MaxCylPressurePSI);
17611758
if (demandedPressurePSI > CylPressurePSI && demandedPressurePSI < CylPressurePSI + 4) // Allow some margin for unnecessary air brake application
17621759
{
17631760
demandedPressurePSI = CylPressurePSI;
17641761
}
17651762
demandedPressurePSI /= RelayValveRatio;
1763+
1764+
if (demandedPressurePSI > AutoCylPressurePSI)
1765+
demandedPressurePSI = AutoCylPressurePSI;
17661766
}
17671767
}
17681768
else if (loco.DynamicBrakeAutoBailOff)

0 commit comments

Comments
 (0)