Skip to content

Commit 14d79c7

Browse files
committed
Initial addition of air flow meters
1 parent fc86323 commit 14d79c7

File tree

11 files changed

+168
-45
lines changed

11 files changed

+168
-45
lines changed

Source/Documentation/Manual/cabs.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,37 @@ An example of implementation of the above controls in a .cvf file follows::
679679
)
680680

681681

682+
Air Flow Meter
683+
--------------
684+
685+
.. index::
686+
single: ORTS_AIR_FLOW_METER
687+
688+
This cabview control is used on some locomotives, particularly in North America, to show the
689+
volumetric flow rate of air moving from the main res to the brake pipe during release/recharge.
690+
Such an indication can be used to determine when brake pipe charging is complete,
691+
measure the amount of brake pipe leakage, and so on.
692+
The control will only function on locomotives with air brakes.
693+
694+
Here is an example implementation of ORTS_AIR_FLOW_METER as an analog dial::
695+
696+
697+
Dial (
698+
Type ( ORTS_AIR_FLOW_METER DIAL )
699+
Position ( 258 271 1 32 )
700+
Graphic ( "white_needle.ace" )
701+
Style ( NEEDLE )
702+
ScaleRange ( 0 150 )
703+
ScalePos ( 295 65 )
704+
Units ( CUBIC_FT_MIN )
705+
Pivot ( 24 )
706+
DirIncrease ( 0 )
707+
)
708+
709+
Applicable user-defined units are CUBIC_FT_MIN and LITRES_S. Cubic meters per second will be
710+
used if no units are specified.
711+
712+
682713
Animated 2D Wipers
683714
------------------
684715

Source/Documentation/Manual/driving.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,9 @@ The following information is displayed in the basic display:
764764
Train brake HUD line has two Brake Reservoir pressure numbers: the first is
765765
the Equalization Reservoir (EQ) and the second is the Brake Cylinder (BC)
766766
pressure. The two BP numbers report the brake pressure in the lead engine
767-
and in the last car of the train. The unit of measure used for brake
767+
and in the last car of the train. Additionally, the brake flow is shown,
768+
which measures the rate of air flowing into the brake pipe during release
769+
and recharge. The unit of measure used for brake
768770
pressure is defined by the option :ref:`Pressure unit <options-pressure>`.
769771
- Engine Brake = percentage of independent engine brake. Not fully
770772
releasing the engine brake will affect train brake pressures.

Source/ORTS.Common/Conversions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ public static class FormatStrings
549549
public static string psi = Catalog.GetString("psi");
550550
public static string inhg = Catalog.GetString("inHg");
551551
public static string kgfpcm2 = Catalog.GetString("kgf/cm²");
552+
public static string lps = Catalog.GetString("L/s");
553+
public static string cfm = Catalog.GetString("cfm");
552554
public static string kg = Catalog.GetString("kg");
553555
public static string t = Catalog.GetString("t");
554556
public static string tonUK = Catalog.GetString("t-uk");
@@ -814,6 +816,12 @@ public static string FormatPressure(float pressure, PressureUnit inputUnit, Pres
814816
return String.Format(CultureInfo.CurrentCulture, format, pressureOut);
815817
}
816818

819+
public static string FormatAirFlow(float flowM3pS, bool isMetric)
820+
{
821+
var flow = isMetric ? flowM3pS * 1000.0f : flowM3pS * 35.3147f * 60.0f;
822+
return String.Format(CultureInfo.CurrentCulture, "{0:F0} {1}", flow, isMetric ? lps : cfm);
823+
}
824+
817825
/// <summary>
818826
/// Converts duration in floating-point seconds to whole hours, minutes and seconds (rounded down).
819827
/// </summary>

Source/Orts.Formats.Msts/CabViewFile.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ public enum CABViewControlTypes
194194
ORTS_BAILOFF,
195195
ORTS_QUICKRELEASE,
196196
ORTS_OVERCHARGE,
197+
ORTS_AIR_FLOW_METER,
197198
ORTS_BATTERY_SWITCH_COMMAND_SWITCH,
198199
ORTS_BATTERY_SWITCH_COMMAND_BUTTON_CLOSE,
199200
ORTS_BATTERY_SWITCH_COMMAND_BUTTON_OPEN,
@@ -330,7 +331,10 @@ public enum CABViewControlUnits
330331
METRES,
331332
MILES,
332333
FEET,
333-
YARDS
334+
YARDS,
335+
336+
CUBIC_FT_MIN,
337+
LITRES_S
334338
}
335339

336340
public enum DiscreteStates

Source/Orts.Simulation/Simulation/RollingStocks/MSTSDieselLocomotive.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,11 +1175,11 @@ public string GetDpuStatus(bool dataDpu, CABViewControlUnits loadUnits = CABView
11751175
status.AppendFormat((data < 0 ? "???" : " ") + "\t");
11761176

11771177
// BP
1178-
var brakeInfoValue = brakeValue(Simulator.Catalog.GetString("BP"), Simulator.Catalog.GetString("EOT"));
1178+
var brakeInfoValue = brakeValue(Simulator.Catalog.GetString("BP"), Simulator.Catalog.GetString("Flow"));
1179+
status.AppendFormat("{0:F0}\t", brakeInfoValue);
1180+
// Air flow meter
1181+
brakeInfoValue = brakeValue(Simulator.Catalog.GetString("Flow"), Simulator.Catalog.GetString("EOT"));
11791182
status.AppendFormat("{0:F0}\t", brakeInfoValue);
1180-
1181-
// Flow.
1182-
// TODO:The BP air flow that feeds the brake tube is not yet modeled in Open Rails.
11831183

11841184
// Remote
11851185
if (dataDpu)
@@ -1277,6 +1277,7 @@ private static void SetDPULabels(bool dpuFull, int numberOfEngines)
12771277
labels.AppendFormat("{0}\t", Simulator.Catalog.GetString("Throttle"));
12781278
labels.AppendFormat("{0}\t", Simulator.Catalog.GetString("Load"));
12791279
labels.AppendFormat("{0}\t", Simulator.Catalog.GetString("BP"));
1280+
labels.AppendFormat("{0}\t", Simulator.Catalog.GetString("Flow"));
12801281
if (!dpuFull)
12811282
{
12821283
labels.AppendFormat("{0}", Simulator.Catalog.GetString("Remote"));

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public enum SoundState
131131
public float MaxSpeedMpS = 1e3f;
132132
public float UnloadingSpeedMpS;
133133
public float MainResPressurePSI = 130;
134+
public float BrakePipeFlowM3pS;
134135
public float MaximumMainReservoirPipePressurePSI;
135136
public bool CompressorIsOn;
136137
public bool CompressorIsMechanical = false;
@@ -146,6 +147,9 @@ public enum SoundState
146147
public bool OnLineCabRadio;
147148
public string OnLineCabRadioURL;
148149

150+
public float FilteredBrakePipeFlowM3pS;
151+
public IIRFilter AFMFilter;
152+
149153
// Water trough filling
150154
public bool HasWaterScoop = false; // indicates whether loco + tender have a water scoop or not
151155
public float ScoopMaxPickupSpeedMpS = 200.0f; // Maximum scoop pickup speed - used in steam locomotive viewer
@@ -417,7 +421,7 @@ public float OdometerM
417421
protected const float DefaultMainResVolume = 0.78f; // Value to be inserted if .eng parameters are corrected
418422
protected const float DefaultMaxMainResPressure = 140; // Max value to be inserted if .eng parameters are corrected
419423

420-
public List<CabView> CabViewList = new List<CabView>();
424+
public List<CabView> CabViewList = new List<CabView>();
421425
public CabView3D CabView3D;
422426

423427
public MSTSNotchController SteamHeatController = new MSTSNotchController(0, 1, 0.1f);
@@ -482,6 +486,7 @@ public MSTSLocomotive(Simulator simulator, string wagPath)
482486
LocomotiveAxles.Add(new Axle());
483487
CurrentFilter = new IIRFilter(IIRFilter.FilterTypes.Butterworth, 1, IIRFilter.HzToRad(0.5f), 0.001f);
484488
AdhesionFilter = new IIRFilter(IIRFilter.FilterTypes.Butterworth, 1, IIRFilter.HzToRad(1f), 0.001f);
489+
AFMFilter = new IIRFilter(IIRFilter.FilterTypes.Butterworth, 1, IIRFilter.HzToRad(0.1f), 1.0f);
485490

486491
TrainBrakeController = new ScriptedBrakeController(this);
487492
EngineBrakeController = new ScriptedBrakeController(this);
@@ -5411,6 +5416,25 @@ public virtual float GetDataOf(CabViewControl cvc)
54115416
data = (TrainBrakeController == null || !TrainBrakeController.OverchargeButtonPressed) ? 0 : 1;
54125417
break;
54135418
}
5419+
case CABViewControlTypes.ORTS_AIR_FLOW_METER:
5420+
{
5421+
switch (cvc.Units)
5422+
{
5423+
case CABViewControlUnits.CUBIC_FT_MIN:
5424+
data = this.FilteredBrakePipeFlowM3pS * 35.3147f * 60.0f;
5425+
break;
5426+
5427+
case CABViewControlUnits.LITRES_S:
5428+
data = this.FilteredBrakePipeFlowM3pS * 1000.0f;
5429+
break;
5430+
5431+
default:
5432+
data = this.FilteredBrakePipeFlowM3pS;
5433+
break;
5434+
5435+
}
5436+
break;
5437+
}
54145438
case CABViewControlTypes.FRICTION_BRAKING:
54155439
{
54165440
data = (BrakeSystem == null) ? 0.0f : BrakeSystem.GetCylPressurePSI();

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace Orts.Simulation.RollingStocks.SubSystems.Brakes.MSTS
3333
public class AirSinglePipe : MSTSBrakeSystem
3434
{
3535
protected TrainCar Car;
36+
readonly static float OneAtmospherePSI = 14.696f;
3637
protected float HandbrakePercent;
3738
protected float CylPressurePSI = 64;
3839
public float AutoCylPressurePSI { get; protected set; } = 64;
@@ -180,9 +181,11 @@ public override string GetStatus(Dictionary<BrakeSystemComponent, PressureUnit>
180181
// Get Brake information for train
181182
public override string GetFullStatus(BrakeSystem lastCarBrakeSystem, Dictionary<BrakeSystemComponent, PressureUnit> units)
182183
{
184+
var loco = Car as MSTSLocomotive;
183185
var s = $" {Simulator.Catalog.GetString("EQ")} {FormatStrings.FormatPressure(Car.Train.EqualReservoirPressurePSIorInHg, PressureUnit.PSI, units[BrakeSystemComponent.EqualizingReservoir], true)}"
184186
+ $" {Simulator.Catalog.GetString("BC")} {FormatStrings.FormatPressure(Car.Train.HUDWagonBrakeCylinderPSI, PressureUnit.PSI, units[BrakeSystemComponent.BrakeCylinder], true)}"
185-
+ $" {Simulator.Catalog.GetString("BP")} {FormatStrings.FormatPressure(BrakeLine1PressurePSI, PressureUnit.PSI, units[BrakeSystemComponent.BrakePipe], true)}";
187+
+ $" {Simulator.Catalog.GetString("BP")} {FormatStrings.FormatPressure(BrakeLine1PressurePSI, PressureUnit.PSI, units[BrakeSystemComponent.BrakePipe], true)}"
188+
+ $" {Simulator.Catalog.GetString("Flow")} {FormatStrings.FormatAirFlow(loco.FilteredBrakePipeFlowM3pS, loco.IsMetric)}";
186189
if (lastCarBrakeSystem != null && lastCarBrakeSystem != this)
187190
s += $" {Simulator.Catalog.GetString("EOT")} {lastCarBrakeSystem.GetStatus(units)}";
188191
if (HandbrakePercent > 0)
@@ -1094,13 +1097,17 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
10941097
var lead = trainCar as MSTSLocomotive;
10951098
train.FindLeadLocomotives(out int first, out int last);
10961099

1100+
float tempBrakePipeFlow = 0.0f; // Flow calculation will assume 0 flow unless calculated otherwise
1101+
10971102
// Propagate brake line (1) data if pressure gradient disabled
10981103
if (lead != null && lead.BrakePipeChargingRatePSIorInHgpS >= 1000)
10991104
{ // pressure gradient disabled
11001105
if (lead.BrakeSystem.BrakeLine1PressurePSI < train.EqualReservoirPressurePSIorInHg)
11011106
{
11021107
var dp1 = train.EqualReservoirPressurePSIorInHg - lead.BrakeSystem.BrakeLine1PressurePSI;
11031108
lead.MainResPressurePSI -= dp1 * lead.BrakeSystem.BrakePipeVolumeM3 / lead.MainResVolumeM3;
1109+
1110+
tempBrakePipeFlow = (dp1 * lead.BrakeSystem.BrakePipeVolumeM3) / (OneAtmospherePSI * elapsedClockSeconds); // Instantaneous flow rate from MR to BP
11041111
}
11051112
foreach (TrainCar car in train.Cars)
11061113
{
@@ -1109,6 +1116,9 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11091116
if (car.BrakeSystem.TwoPipes)
11101117
car.BrakeSystem.BrakeLine2PressurePSI = Math.Min(lead.MainResPressurePSI, lead.MaximumMainReservoirPipePressurePSI);
11111118
}
1119+
1120+
lead.BrakePipeFlowM3pS = tempBrakePipeFlow;
1121+
lead.FilteredBrakePipeFlowM3pS = lead.AFMFilter.Filter(lead.BrakePipeFlowM3pS, elapsedClockSeconds); // Actual flow rate displayed by air flow meter
11121122
}
11131123
else
11141124
{ // approximate pressure gradient in train pipe line1
@@ -1121,6 +1131,8 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11211131
{
11221132
if (lead != null)
11231133
{
1134+
tempBrakePipeFlow = 0.0f;
1135+
11241136
// Allow for leaking train air brakepipe
11251137
if (lead.BrakeSystem.BrakeLine1PressurePSI - trainPipeLeakLossPSI > 0 && lead.TrainBrakePipeLeakPSIorInHgpS != 0) // if train brake pipe has pressure in it, ensure result will not be negative if loss is subtracted
11261138
{
@@ -1140,8 +1152,10 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11401152
}
11411153
float PressureDiffEqualToPipePSI = trainPipeTimeVariationS * chargingRatePSIpS; // default condition - if EQ Res is higher then Brake Pipe Pressure
11421154

1143-
if (train.EqualReservoirPressurePSIorInHg < lead.BrakeSystem.BrakeLine1PressurePSI + 1)
1144-
PressureDiffEqualToPipePSI *= MathHelper.Clamp(train.EqualReservoirPressurePSIorInHg - lead.BrakeSystem.BrakeLine1PressurePSI, 0.1f, 1.0f); // Reduce recharge rate if near EQ pressure to prevent air pulsing
1155+
if (train.EqualReservoirPressurePSIorInHg - lead.BrakeSystem.BrakeLine1PressurePSI < 5.0f) // Reduce recharge rate if near EQ to simulate feed valve behavior
1156+
PressureDiffEqualToPipePSI *= Math.Min((float)Math.Sqrt((train.EqualReservoirPressurePSIorInHg - lead.BrakeSystem.BrakeLine1PressurePSI) / 5.0f), 1.0f);
1157+
if (lead.MainResPressurePSI - train.EqualReservoirPressurePSIorInHg < 15.0f) // Reduce recharge rate if near MR pressure as per reality
1158+
PressureDiffEqualToPipePSI *= Math.Min((lead.MainResPressurePSI - train.EqualReservoirPressurePSIorInHg) / 15.0f, 1.0f);
11451159

11461160
if (lead.BrakeSystem.BrakeLine1PressurePSI + PressureDiffEqualToPipePSI > train.EqualReservoirPressurePSIorInHg)
11471161
PressureDiffEqualToPipePSI = train.EqualReservoirPressurePSIorInHg - lead.BrakeSystem.BrakeLine1PressurePSI;
@@ -1157,6 +1171,8 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11571171
{
11581172
lead.BrakeSystem.BrakeLine1PressurePSI += PressureDiffEqualToPipePSI;
11591173
lead.MainResPressurePSI -= PressureDiffEqualToPipePSI * lead.BrakeSystem.BrakePipeVolumeM3 / lead.MainResVolumeM3;
1174+
1175+
tempBrakePipeFlow = (PressureDiffEqualToPipePSI * lead.BrakeSystem.BrakePipeVolumeM3) / (OneAtmospherePSI * trainPipeTimeVariationS); // Instantaneous flow rate from MR to BP
11601176
}
11611177
}
11621178
// reduce pressure in lead brake line if brake pipe pressure is above equalising pressure - apply brakes
@@ -1165,14 +1181,18 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11651181
float serviceVariationFactor = Math.Min(trainPipeTimeVariationS / serviceTimeFactor, 0.95f);
11661182
float pressureDiffPSI = serviceVariationFactor * lead.BrakeSystem.BrakeLine1PressurePSI;
11671183

1168-
if (train.EqualReservoirPressurePSIorInHg > lead.BrakeSystem.BrakeLine1PressurePSI - 1)
1169-
pressureDiffPSI *= MathHelper.Clamp(lead.BrakeSystem.BrakeLine1PressurePSI - train.EqualReservoirPressurePSIorInHg, 0.1f, 1.0f); // Reduce exhausting rate if near EQ pressure to prevent air pulsing
1184+
if (train.EqualReservoirPressurePSIorInHg > lead.BrakeSystem.BrakeLine1PressurePSI - 5.0f) // Reduce exhausting rate if near EQ pressure to simulate feed valve
1185+
pressureDiffPSI *= Math.Min((float)Math.Sqrt((lead.BrakeSystem.BrakeLine1PressurePSI - train.EqualReservoirPressurePSIorInHg) / 5.0f), 1.0f);
11701186
if (lead.BrakeSystem.BrakeLine1PressurePSI - pressureDiffPSI < train.EqualReservoirPressurePSIorInHg)
11711187
pressureDiffPSI = lead.BrakeSystem.BrakeLine1PressurePSI - train.EqualReservoirPressurePSIorInHg;
11721188
lead.BrakeSystem.BrakeLine1PressurePSI -= pressureDiffPSI;
11731189
}
11741190
}
11751191

1192+
// Finish updating air flow meter
1193+
lead.BrakePipeFlowM3pS = tempBrakePipeFlow;
1194+
lead.FilteredBrakePipeFlowM3pS = lead.AFMFilter.Filter(lead.BrakePipeFlowM3pS, trainPipeTimeVariationS); // Actual flow rate displayed by air flow meter
1195+
11761196
train.LeadPipePressurePSI = lead.BrakeSystem.BrakeLine1PressurePSI; // Keep a record of current train pipe pressure in lead locomotive
11771197
}
11781198

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/Controllers/MSTSBrakeController.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
1717

1818
using System;
19+
using Microsoft.Xna.Framework;
1920
using ORTS.Scripting.Api;
2021

2122
namespace Orts.Simulation.RollingStocks.SubSystems.Controllers
@@ -409,23 +410,29 @@ public override ControllerState GetState()
409410
}
410411
}
411412

412-
static void IncreasePressure(ref float pressurePSI, float targetPSI, float ratePSIpS, float elapsedSeconds)
413+
static void IncreasePressure(ref float pressureBar, float targetBar, float rateBarpS, float elapsedSeconds)
413414
{
414-
if (pressurePSI < targetPSI)
415+
if (pressureBar < targetBar)
415416
{
416-
pressurePSI += ratePSIpS * elapsedSeconds;
417-
if (pressurePSI > targetPSI)
418-
pressurePSI = targetPSI;
417+
float dp = rateBarpS * elapsedSeconds;
418+
if (targetBar - pressureBar < 0.5f) // Slow down increase when nearing target to simulate chokes in brake valve
419+
dp *= Math.Min((targetBar - pressureBar) / 0.5f + 0.1f, 1.0f);
420+
pressureBar += dp;
421+
if (pressureBar > targetBar)
422+
pressureBar = targetBar;
419423
}
420424
}
421425

422-
static void DecreasePressure(ref float pressurePSI, float targetPSI, float ratePSIpS, float elapsedSeconds)
426+
static void DecreasePressure(ref float pressureBar, float targetBar, float rateBarpS, float elapsedSeconds)
423427
{
424-
if (pressurePSI > targetPSI)
428+
if (pressureBar > targetBar)
425429
{
426-
pressurePSI -= ratePSIpS * elapsedSeconds;
427-
if (pressurePSI < targetPSI)
428-
pressurePSI = targetPSI;
430+
float dp = rateBarpS * elapsedSeconds;
431+
if (pressureBar - targetBar < 0.5f) // Slow down decrease when nearing target to simulate chokes in brake valve
432+
dp *= Math.Min((pressureBar - targetBar) / 0.5f + 0.1f, 1.0f);
433+
pressureBar -= dp;
434+
if (pressureBar < targetBar)
435+
pressureBar = targetBar;
429436
}
430437
}
431438
}

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/PowerSupplies/DieselEngine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ public string GetDPStatus()
469469
result.AppendFormat("\t{0}", FormatStrings.FormatPower(eng.CurrentDieselOutputPowerW, Locomotive.IsMetric, false, false));
470470
result.AppendFormat("\t{0:F1}%", eng.LoadPercent);
471471
result.AppendFormat("\t{0:F0} {1}", eng.RealRPM, FormatStrings.rpm);
472-
result.AppendFormat("\t{0}/{1}", FormatStrings.FormatFuelVolume(pS.TopH(eng.DieselFlowLps), Locomotive.IsMetric, Locomotive.IsUK), FormatStrings.h);
472+
result.AppendFormat("\t{0}", FormatStrings.FormatAirFlow(Locomotive.FilteredBrakePipeFlowM3pS, Locomotive.IsMetric));
473473
result.AppendFormat("\t{0}", FormatStrings.FormatTemperature(eng.DieselTemperatureDeg, Locomotive.IsMetric, false));
474474
result.AppendFormat("\t{0}", FormatStrings.FormatPressure(eng.DieselOilPressurePSI, PressureUnit.PSI, Locomotive.MainPressureUnit, true));
475475

0 commit comments

Comments
 (0)