@@ -41,6 +41,7 @@ public class AirSinglePipe : MSTSBrakeSystem
4141 protected float ControlResPressurePSI = 64 ;
4242 protected float FullServPressurePSI = 50 ;
4343 protected float MaxCylPressurePSI = 64 ;
44+ protected float MaxTripleValveCylPressurePSI ;
4445 protected float AuxCylVolumeRatio = 2.5f ;
4546 protected float AuxBrakeLineVolumeRatio ;
4647 protected float EmergResVolumeM3 = 0.07f ;
@@ -53,6 +54,10 @@ public class AirSinglePipe : MSTSBrakeSystem
5354 protected float EmergencyValveActuationRatePSIpS = 0 ;
5455 protected float EmergResChargingRatePSIpS = 1.684f ;
5556 protected float EmergAuxVolumeRatio = 1.4f ;
57+ protected bool RelayValveFitted = false ;
58+ public float RelayValveRatio { get ; protected set ; } = 1 ;
59+ protected float RelayValveApplicationRatePSIpS = 50 ;
60+ protected float RelayValveReleaseRatePSIpS = 50 ;
5661 protected string DebugType = string . Empty ;
5762 protected string RetainerDebugState = string . Empty ;
5863 protected bool MRPAuxResCharging ;
@@ -121,6 +126,11 @@ public override void InitializeFromCopy(BrakeSystem copy)
121126 TwoPipes = thiscopy . TwoPipes ;
122127 MRPAuxResCharging = thiscopy . MRPAuxResCharging ;
123128 HoldingValve = thiscopy . HoldingValve ;
129+ RelayValveFitted = thiscopy . RelayValveFitted ;
130+ RelayValveRatio = thiscopy . RelayValveRatio ;
131+ RelayValveApplicationRatePSIpS = thiscopy . RelayValveApplicationRatePSIpS ;
132+ RelayValveReleaseRatePSIpS = thiscopy . RelayValveReleaseRatePSIpS ;
133+ MaxTripleValveCylPressurePSI = thiscopy . MaxTripleValveCylPressurePSI ;
124134 }
125135
126136 // Get the brake BC & BP for EOT conditions
@@ -233,6 +243,21 @@ public override void Parse(string lowercasetoken, STFReader stf)
233243 case "wagon(ortsbrakeinsensitivity" : BrakeInsensitivityPSIpS = stf . ReadFloatBlock ( STFReader . UNITS . PressureRateDefaultPSIpS , 0.07f ) ; break ;
234244 case "wagon(ortsemergencyvalveactuationrate" : EmergencyValveActuationRatePSIpS = stf . ReadFloatBlock ( STFReader . UNITS . PressureRateDefaultPSIpS , 15f ) ; break ;
235245 case "wagon(ortsmainrespipeauxrescharging" : MRPAuxResCharging = this is AirTwinPipe && stf . ReadBoolBlock ( true ) ; break ;
246+ case "wagon(ortsbrakerelayvalveratio" :
247+ RelayValveRatio = stf . ReadFloatBlock ( STFReader . UNITS . None , null ) ;
248+ if ( RelayValveRatio != 0 )
249+ {
250+ RelayValveFitted = true ;
251+ }
252+ else
253+ {
254+ RelayValveRatio = 1 ;
255+ RelayValveFitted = false ;
256+ }
257+ break ;
258+ case "wagon(ortsbrakerelayvalveapplicationrate" : RelayValveApplicationRatePSIpS = stf . ReadFloatBlock ( STFReader . UNITS . PressureRateDefaultPSIpS , null ) ; break ;
259+ case "wagon(ortsbrakerelayvalvereleaserate" : RelayValveReleaseRatePSIpS = stf . ReadFloatBlock ( STFReader . UNITS . PressureRateDefaultPSIpS , null ) ; break ;
260+ case "wagon(ortsmaxtriplevalvecylinderpressure" : MaxTripleValveCylPressurePSI = stf . ReadFloatBlock ( STFReader . UNITS . PressureDefaultPSI , null ) ; break ;
236261 }
237262 }
238263
@@ -298,13 +323,16 @@ public override void Initialize(bool handbrakeOn, float maxPressurePSI, float fu
298323 if ( Car . Simulator . Settings . SimpleControlPhysics && EmergResVolumeM3 > 2.0 )
299324 EmergResVolumeM3 = 0.7f ;
300325
326+ if ( MaxTripleValveCylPressurePSI == 0 ) MaxTripleValveCylPressurePSI = MaxCylPressurePSI / RelayValveRatio ;
327+
301328 BrakeLine1PressurePSI = Car . Train . EqualReservoirPressurePSIorInHg ;
302329 BrakeLine2PressurePSI = Car . Train . BrakeLine2PressurePSI ;
303330 BrakeLine3PressurePSI = 0 ;
304331 if ( maxPressurePSI > 0 )
305332 ControlResPressurePSI = maxPressurePSI ;
306333 FullServPressurePSI = fullServPressurePSI ;
307- CylPressurePSI = AutoCylPressurePSI = immediateRelease ? 0 : Math . Min ( ( maxPressurePSI - BrakeLine1PressurePSI ) * AuxCylVolumeRatio , MaxCylPressurePSI ) ;
334+ AutoCylPressurePSI = immediateRelease ? 0 : Math . Min ( ( maxPressurePSI - BrakeLine1PressurePSI ) * AuxCylVolumeRatio , MaxCylPressurePSI ) ;
335+ CylPressurePSI = AutoCylPressurePSI * RelayValveRatio ;
308336 AuxResPressurePSI = Math . Max ( TwoPipes ? maxPressurePSI : maxPressurePSI - AutoCylPressurePSI / AuxCylVolumeRatio , BrakeLine1PressurePSI ) ;
309337 if ( ( Car as MSTSWagon ) . EmergencyReservoirPresent )
310338 EmergResPressurePSI = Math . Max ( AuxResPressurePSI , maxPressurePSI ) ;
@@ -324,6 +352,8 @@ public override void Initialize(bool handbrakeOn, float maxPressurePSI, float fu
324352 AuxBrakeLineVolumeRatio = 3.1f ;
325353
326354 CylVolumeM3 = EmergResVolumeM3 / EmergAuxVolumeRatio / AuxCylVolumeRatio ;
355+
356+ RelayValveFitted |= ( loco != null && ( loco . DynamicBrakeAutoBailOff || loco . DynamicBrakePartialBailOff ) ) || ( Car as MSTSWagon ) . BrakeValve == MSTSWagon . BrakeValveType . DistributingValve ;
327357 }
328358
329359 /// <summary>
@@ -420,8 +450,8 @@ public override void Update(float elapsedClockSeconds)
420450 dp = ( AuxResPressurePSI - AutoCylPressurePSI ) * AuxCylVolumeRatio / ( 1 + AuxCylVolumeRatio ) ;
421451 if ( ( ( Car as MSTSWagon ) . BrakeValve == MSTSWagon . BrakeValveType . Distributor ) && TripleValveState != ValveState . Emergency && dp > threshold - AutoCylPressurePSI )
422452 dp = threshold - AutoCylPressurePSI ;
423- if ( AutoCylPressurePSI + dp > MaxCylPressurePSI )
424- dp = MaxCylPressurePSI - AutoCylPressurePSI ;
453+ if ( AutoCylPressurePSI + dp > MaxTripleValveCylPressurePSI )
454+ dp = MaxTripleValveCylPressurePSI - AutoCylPressurePSI ;
425455 if ( BrakeLine1PressurePSI > AuxResPressurePSI - dp / AuxCylVolumeRatio && ! BleedOffValveOpen )
426456 dp = ( AuxResPressurePSI - BrakeLine1PressurePSI ) * AuxCylVolumeRatio ;
427457 if ( dp < 0 )
@@ -527,11 +557,12 @@ public override void Update(float elapsedClockSeconds)
527557 if ( AutoCylPressurePSI < 0 )
528558 AutoCylPressurePSI = 0 ;
529559
530- if ( Car is MSTSLocomotive && ( Car as MSTSWagon ) . BrakeValve == MSTSWagon . BrakeValveType . DistributingValve )
560+ float demandedPressurePSI = 0 ;
561+ var loco = Car as MSTSLocomotive ;
562+ if ( loco != null && ( Car as MSTSWagon ) . BrakeValve == MSTSWagon . BrakeValveType . DistributingValve )
531563 {
532564 // For distributing valves, we use AutoCylPressurePSI as "Application Chamber/Pipe" pressure
533565 // CylPressurePSI is the actual pressure applied to cylinders
534- var loco = Car as MSTSLocomotive ;
535566 var engineBrakeStatus = loco . EngineBrakeController . Notches [ loco . EngineBrakeController . CurrentNotch ] . Type ;
536567 var trainBrakeStatus = loco . TrainBrakeController . Notches [ loco . TrainBrakeController . CurrentNotch ] . Type ;
537568 // BailOff
@@ -544,8 +575,8 @@ public override void Update(float elapsedClockSeconds)
544575 if ( trainBrakeStatus == ControllerState . Emergency )
545576 {
546577 float dp = elapsedClockSeconds * MaxApplicationRatePSIpS ;
547- if ( dp > MaxCylPressurePSI - AutoCylPressurePSI )
548- dp = MaxCylPressurePSI - AutoCylPressurePSI ;
578+ if ( dp > MaxCylPressurePSI / RelayValveRatio - AutoCylPressurePSI )
579+ dp = MaxCylPressurePSI / RelayValveRatio - AutoCylPressurePSI ;
549580 AutoCylPressurePSI += dp ;
550581 }
551582 // Release pipe open
@@ -558,15 +589,13 @@ public override void Update(float elapsedClockSeconds)
558589 loco . Train . BrakeLine3PressurePSI = AutoCylPressurePSI ;
559590
560591 // Equalization between application chamber and brake cylinders
561- // TODO: Drain air from main reservoir
562- CylPressurePSI = AutoCylPressurePSI ;
592+ demandedPressurePSI = AutoCylPressurePSI ;
563593 }
564594 else
565595 {
566- if ( Car is MSTSLocomotive loco && loco . EngineType != TrainCar . EngineTypes . Control ) // TODO - Control cars ned to be linked to power suppy requirements.
596+ demandedPressurePSI = Math . Max ( AutoCylPressurePSI , BrakeLine3PressurePSI ) ;
597+ if ( loco != null && loco . EngineType != TrainCar . EngineTypes . Control ) // TODO - Control cars ned to be linked to power suppy requirements.
567598 {
568- float demandedPressurePSI = Math . Max ( AutoCylPressurePSI , BrakeLine3PressurePSI ) ;
569- // if (Car is MSTSLocomotive loco && loco.LocomotivePowerSupply.MainPowerSupplyOn)
570599 if ( loco . LocomotivePowerSupply . MainPowerSupplyOn )
571600 {
572601 if ( loco . Train . LeadLocomotiveIndex >= 0 )
@@ -600,6 +629,7 @@ public override void Update(float elapsedClockSeconds)
600629 {
601630 demandedPressurePSI = CylPressurePSI ;
602631 }
632+ demandedPressurePSI /= RelayValveRatio ;
603633 if ( demandedPressurePSI < BrakeLine3PressurePSI )
604634 demandedPressurePSI = BrakeLine3PressurePSI ;
605635 }
@@ -621,34 +651,35 @@ public override void Update(float elapsedClockSeconds)
621651 }
622652 }
623653 }
624- // TODO: this first clause is intended for locomotives fitted with some sort of proportional valve
625- // i.e. the triple valve is not directly attached to the physical brake cylinder
626- // This allows e.g. blending, variable load, or higher pressures than provided by triple valves
627- if ( loco . DynamicBrakeAutoBailOff || loco . DynamicBrakeAutoBailOff )
628- {
629- if ( demandedPressurePSI > CylPressurePSI )
630- {
631- float dp = elapsedClockSeconds * loco . EngineBrakeApplyRatePSIpS ;
632- if ( dp > demandedPressurePSI - CylPressurePSI )
633- dp = demandedPressurePSI - CylPressurePSI ;
634- /* TODO: Proportional valves need air from the main reservoir
635- if (BrakeLine2PressurePSI - dp * AuxBrakeLineVolumeRatio / AuxCylVolumeRatio < CylPressurePSI + dp)
636- dp = (BrakeLine2PressurePSI - CylPressurePSI) / (1 + AuxBrakeLineVolumeRatio / AuxCylVolumeRatio);
637- BrakeLine2PressurePSI -= dp * AuxBrakeLineVolumeRatio / AuxCylVolumeRatio;*/
638- CylPressurePSI += dp ;
639- }
640- else if ( demandedPressurePSI < CylPressurePSI ) CylPressurePSI = Math . Max ( demandedPressurePSI , CylPressurePSI - elapsedClockSeconds * loco . EngineBrakeReleaseRatePSIpS ) ;
641- }
642- else // Rest of cases
654+ }
655+ }
656+ if ( RelayValveFitted )
657+ {
658+ demandedPressurePSI *= RelayValveRatio ;
659+ if ( demandedPressurePSI > CylPressurePSI )
660+ {
661+ float dp = elapsedClockSeconds * RelayValveApplicationRatePSIpS ;
662+ if ( dp > demandedPressurePSI - CylPressurePSI )
663+ dp = demandedPressurePSI - CylPressurePSI ;
664+ if ( TwoPipes )
643665 {
644- CylPressurePSI = Math . Max ( AutoCylPressurePSI , BrakeLine3PressurePSI ) ;
666+ if ( BrakeLine2PressurePSI - dp * AuxBrakeLineVolumeRatio / AuxCylVolumeRatio < CylPressurePSI + dp )
667+ dp = ( BrakeLine2PressurePSI - CylPressurePSI ) / ( 1 + AuxBrakeLineVolumeRatio / AuxCylVolumeRatio ) ;
668+ BrakeLine2PressurePSI -= dp * AuxBrakeLineVolumeRatio / AuxCylVolumeRatio ;
645669 }
670+ if ( MaxCylPressurePSI < CylPressurePSI + dp )
671+ dp = MaxCylPressurePSI - CylPressurePSI ;
672+ CylPressurePSI += dp ;
646673 }
647- else
674+ else if ( demandedPressurePSI < CylPressurePSI )
648675 {
649- CylPressurePSI = Math . Max ( AutoCylPressurePSI , BrakeLine3PressurePSI ) ;
676+ CylPressurePSI = Math . Max ( Math . Max ( demandedPressurePSI , CylPressurePSI - elapsedClockSeconds * RelayValveReleaseRatePSIpS ) , 0 ) ;
650677 }
651678 }
679+ else
680+ {
681+ CylPressurePSI = demandedPressurePSI ;
682+ }
652683
653684 // During braking wheelslide control is effected throughout the train by additional equipment on each vehicle. In the piping to each pair of brake cylinders are fitted electrically operated
654685 // dump valves. When axle rotations which are sensed electrically, differ by a predetermined speed the dump valves are operated releasing brake cylinder pressure to both axles of the affected
@@ -670,7 +701,6 @@ public override void Update(float elapsedClockSeconds)
670701 {
671702 Car . WheelBrakeSlideProtectionActive = true ;
672703 AutoCylPressurePSI -= elapsedClockSeconds * MaxReleaseRatePSIpS ;
673- CylPressurePSI = AutoCylPressurePSI ;
674704 Car . WheelBrakeSlideProtectionTimerS -= elapsedClockSeconds ;
675705
676706 // Lockout WSP dump valve if it is open for greater then 7 seconds continuously
0 commit comments