@@ -91,6 +91,9 @@ public class AirSinglePipe : MSTSBrakeSystem
9191 protected float prevBrakePipePressurePSI = 0f ;
9292 protected float prevBrakePipePressurePSI_sound = 0f ;
9393
94+ protected float BrakePipeChangePSIpS ;
95+ protected SmoothedData SmoothedBrakePipeChangePSIpS ;
96+
9497
9598 /// <summary>
9699 /// EP brake holding valve. Needs to be closed (Lap) in case of brake application or holding.
@@ -114,6 +117,8 @@ public AirSinglePipe(TrainCar car)
114117 BrakePipeVolumeM3 = ( 0.032f * 0.032f * ( float ) Math . PI / 4f ) * Math . Max ( 5.0f , ( 1 + car . CarLengthM ) ) ; // Using DN32 (1-1/4") pipe
115118 DebugType = "1P" ;
116119
120+ SmoothedBrakePipeChangePSIpS = new SmoothedData ( 0.25f ) ;
121+
117122 // Force graduated releasable brakes. Workaround for MSTS with bugs preventing to set eng/wag files correctly for this.
118123 if ( Car . Simulator . Settings . GraduatedRelease ) ( Car as MSTSWagon ) . BrakeValve = MSTSWagon . BrakeValveType . Distributor ;
119124
@@ -205,7 +210,7 @@ public override string[] GetDebugStatus(Dictionary<BrakeSystemComponent, Pressur
205210 string . Empty , // Spacer because the state above needs 2 columns.
206211 ( Car as MSTSWagon ) . HandBrakePresent ? string . Format ( "{0:F0}%" , HandbrakePercent ) : string . Empty ,
207212 FrontBrakeHoseConnected ? "I" : "T" ,
208- string . Format ( "A{0} B{1}" , AngleCockAOpen ? "+" : "-" , AngleCockBOpen ? "+" : "- ") ,
213+ string . Format ( "A{0} B{1}" , AngleCockAOpenAmount >= 1 ? "+" : AngleCockAOpenAmount <= 0 ? "-" : "/" , AngleCockBOpenAmount >= 1 ? "+" : AngleCockBOpenAmount <= 0 ? "-" : "/ ") ,
209214 BleedOffValveOpen ? Simulator . Catalog . GetString ( "Open" ) : string . Empty ,
210215 } ;
211216
@@ -329,7 +334,9 @@ public override void Save(BinaryWriter outf)
329334 outf . Write ( ( int ) TripleValveState ) ;
330335 outf . Write ( FrontBrakeHoseConnected ) ;
331336 outf . Write ( AngleCockAOpen ) ;
337+ outf . Write ( AngleCockAOpenAmount ) ;
332338 outf . Write ( AngleCockBOpen ) ;
339+ outf . Write ( AngleCockBOpenAmount ) ;
333340 outf . Write ( BleedOffValveOpen ) ;
334341 outf . Write ( ( int ) HoldingValve ) ;
335342 outf . Write ( UniformChargingActive ) ;
@@ -352,7 +359,9 @@ public override void Restore(BinaryReader inf)
352359 TripleValveState = ( ValveState ) inf . ReadInt32 ( ) ;
353360 FrontBrakeHoseConnected = inf . ReadBoolean ( ) ;
354361 AngleCockAOpen = inf . ReadBoolean ( ) ;
362+ AngleCockAOpenAmount = inf . ReadSingle ( ) ;
355363 AngleCockBOpen = inf . ReadBoolean ( ) ;
364+ AngleCockBOpenAmount = inf . ReadSingle ( ) ;
356365 BleedOffValveOpen = inf . ReadBoolean ( ) ;
357366 HoldingValve = ( ValveState ) inf . ReadInt32 ( ) ;
358367 UniformChargingActive = inf . ReadBoolean ( ) ;
@@ -380,6 +389,8 @@ public override void Initialize(bool handbrakeOn, float maxPressurePSI, float fu
380389 {
381390 loco . MainResPressurePSI = loco . MaxMainResPressurePSI ;
382391 }
392+
393+ SmoothedBrakePipeChangePSIpS . ForceSmoothValue ( 0 ) ;
383394 }
384395
385396 public override void Initialize ( )
@@ -455,12 +466,15 @@ public void UpdateTripleValveState(float elapsedClockSeconds)
455466 var prevState = TripleValveState ;
456467 var valveType = ( Car as MSTSWagon ) . BrakeValve ;
457468 bool disableGradient = ! ( Car . Train . LeadLocomotive is MSTSLocomotive ) && Car . Train . TrainType != Orts . Simulation . Physics . Train . TRAINTYPE . STATIC ;
469+ // Small workaround to allow trains to more reliably go into emergency after uncoupling
470+ bool emergencyTripped = ( Car . Train . TrainType == Orts . Simulation . Physics . Train . TRAINTYPE . STATIC ) ?
471+ BrakeLine1PressurePSI <= EmergResPressurePSI * AuxCylVolumeRatio / ( AuxCylVolumeRatio + 1 ) : Math . Max ( - SmoothedBrakePipeChangePSIpS . SmoothedValue , 0 ) > EmergencyValveActuationRatePSIpS ;
458472
459473 if ( valveType == MSTSWagon . BrakeValveType . Distributor )
460474 {
461475 float applicationPSI = ControlResPressurePSI - BrakeLine1PressurePSI ;
462476 float targetPressurePSI = applicationPSI * AuxCylVolumeRatio ;
463- if ( ! disableGradient && EmergencyValveActuationRatePSIpS > 0 && ( prevBrakePipePressurePSI - BrakeLine1PressurePSI ) > Math . Max ( elapsedClockSeconds , 0.0001f ) * EmergencyValveActuationRatePSIpS )
477+ if ( ! disableGradient && EmergencyValveActuationRatePSIpS > 0 && emergencyTripped )
464478 {
465479 if ( prevState == ValveState . Release ) // If valve transitions from release to emergency, quick service activates
466480 {
@@ -497,7 +511,7 @@ public void UpdateTripleValveState(float elapsedClockSeconds)
497511 }
498512 else if ( valveType == MSTSWagon . BrakeValveType . TripleValve || valveType == MSTSWagon . BrakeValveType . DistributingValve )
499513 {
500- if ( ! disableGradient && EmergencyValveActuationRatePSIpS > 0 && ( prevBrakePipePressurePSI - BrakeLine1PressurePSI ) > Math . Max ( elapsedClockSeconds , 0.0001f ) * EmergencyValveActuationRatePSIpS )
514+ if ( ! disableGradient && EmergencyValveActuationRatePSIpS > 0 && emergencyTripped )
501515 {
502516 if ( prevState == ValveState . Release ) // If valve transitions from release to emergency, quick service activates
503517 {
@@ -547,11 +561,53 @@ public void UpdateTripleValveState(float elapsedClockSeconds)
547561 else EmergencyDumpStartTime = null ;
548562 }
549563
564+ public void UpdateAngleCockState ( bool AngleCockOpen , ref float AngleCockOpenAmount , ref float ? AngleCockOpenTime )
565+ {
566+ float currentTime = ( float ) this . Car . Simulator . GameTime ;
567+
568+ if ( AngleCockOpen && AngleCockOpenAmount < 1.0f )
569+ {
570+ if ( AngleCockOpenTime == null )
571+ {
572+ AngleCockOpenTime = currentTime ;
573+ }
574+ else if ( currentTime - AngleCockOpenTime > AngleCockOpeningTime )
575+ {
576+ // Finish opening anglecock at a faster rate once time has elapsed
577+ AngleCockOpenAmount = ( currentTime - ( ( float ) AngleCockOpenTime + AngleCockOpeningTime ) ) / 5 + 0.3f ;
578+
579+ if ( AngleCockOpenAmount >= 1.0f )
580+ {
581+ AngleCockOpenAmount = 1.0f ;
582+ AngleCockOpenTime = null ;
583+ }
584+ }
585+ else
586+ {
587+ // Gradually open anglecock toward 30% over 30 seconds
588+ AngleCockOpenAmount = 0.3f * ( currentTime - ( float ) AngleCockOpenTime ) / AngleCockOpeningTime ;
589+ }
590+ }
591+ else if ( ! AngleCockOpen && AngleCockOpenAmount > 0.0f )
592+ {
593+ AngleCockOpenAmount = 0.0f ;
594+ AngleCockOpenTime = null ;
595+ }
596+ }
597+
550598 public override void Update ( float elapsedClockSeconds )
551599 {
552600 var valveType = ( Car as MSTSWagon ) . BrakeValve ;
553601 float threshold = valveType == MSTSWagon . BrakeValveType . Distributor ? Math . Max ( ( ControlResPressurePSI - BrakeLine1PressurePSI ) * AuxCylVolumeRatio , 0 ) : 0 ;
554602
603+ BrakePipeChangePSIpS = ( BrakeLine1PressurePSI - prevBrakePipePressurePSI ) / Math . Max ( elapsedClockSeconds , 0.0001f ) ;
604+ SmoothedBrakePipeChangePSIpS . Update ( Math . Max ( elapsedClockSeconds , 0.0001f ) , BrakePipeChangePSIpS ) ;
605+
606+ // Update anglecock opening. Anglecocks set to gradually open over 30 seconds, but close instantly.
607+ // Gradual opening prevents undesired emergency applications
608+ UpdateAngleCockState ( AngleCockAOpen , ref AngleCockAOpenAmount , ref AngleCockAOpenTime ) ;
609+ UpdateAngleCockState ( AngleCockBOpen , ref AngleCockBOpenAmount , ref AngleCockBOpenTime ) ;
610+
555611 if ( BleedOffValveOpen )
556612 {
557613 if ( valveType == MSTSWagon . BrakeValveType . Distributor )
@@ -591,7 +647,6 @@ public override void Update(float elapsedClockSeconds)
591647 {
592648 float dp = 0 ;
593649 float dpPipe = 0 ;
594- float brakePipeChange = Math . Max ( prevBrakePipePressurePSI - BrakeLine1PressurePSI , 0 ) ;
595650 if ( QuickServiceActive ) // Quick service: Brake pipe pressure is locally reduced to speed up initial reduction
596651 {
597652 if ( QuickServiceVentRatePSIpS > 0 )
@@ -608,7 +663,8 @@ public override void Update(float elapsedClockSeconds)
608663 {
609664 if ( AcceleratedApplicationFactor > 0 ) // Accelerated application: Air is vented from the brake pipe to speed up service applications
610665 {
611- dpPipe = Math . Min ( brakePipeChange * AcceleratedApplicationFactor , elapsedClockSeconds * AcceleratedApplicationLimitPSIpS ) ; // Amount of air vented is proportional to pressure reduction from external sources
666+ // Amount of air vented is proportional to pressure reduction from external sources
667+ dpPipe = MathHelper . Clamp ( - SmoothedBrakePipeChangePSIpS . SmoothedValue * AcceleratedApplicationFactor , 0 , AcceleratedApplicationLimitPSIpS ) * elapsedClockSeconds ;
612668 }
613669 dp = elapsedClockSeconds * MaxApplicationRatePSIpS ;
614670 }
@@ -1116,7 +1172,8 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11161172 int nSteps = ( int ) ( elapsedClockSeconds / brakePipeTimeFactorS + 1 ) ;
11171173 float trainPipeTimeVariationS = elapsedClockSeconds / nSteps ;
11181174 float trainPipeLeakLossPSI = lead == null ? 0.0f : ( trainPipeTimeVariationS * lead . TrainBrakePipeLeakPSIorInHgpS ) ;
1119- float serviceTimeFactor = lead != null ? lead . TrainBrakeController != null && lead . TrainBrakeController . EmergencyBraking ? lead . BrakeEmergencyTimeFactorPSIpS : lead . BrakeServiceTimeFactorPSIpS : 0 ;
1175+ float serviceTimeFactor = lead != null && lead . TrainBrakeController != null ? lead . BrakeServiceTimeFactorPSIpS : 0.001f ;
1176+ float emergencyTimeFactor = lead != null && lead . TrainBrakeController != null ? lead . BrakeEmergencyTimeFactorPSIpS : 0.001f ;
11201177 for ( int i = 0 ; i < nSteps ; i ++ )
11211178 {
11221179 if ( lead != null )
@@ -1127,7 +1184,17 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
11271184 lead . BrakeSystem . BrakeLine1PressurePSI -= trainPipeLeakLossPSI ;
11281185 }
11291186
1130- if ( lead . TrainBrakeController . TrainBrakeControllerState != ControllerState . Neutral )
1187+ // Emergency brake - vent brake pipe to 0 psi regardless of equalizing res pressure
1188+ if ( lead . TrainBrakeController . EmergencyBraking )
1189+ {
1190+ float emergencyVariationFactor = Math . Min ( trainPipeTimeVariationS / emergencyTimeFactor , 0.95f ) ;
1191+ float pressureDiffPSI = emergencyVariationFactor * lead . BrakeSystem . BrakeLine1PressurePSI ;
1192+
1193+ if ( lead . BrakeSystem . BrakeLine1PressurePSI - pressureDiffPSI < 0 )
1194+ pressureDiffPSI = lead . BrakeSystem . BrakeLine1PressurePSI ;
1195+ lead . BrakeSystem . BrakeLine1PressurePSI -= pressureDiffPSI ;
1196+ }
1197+ else if ( lead . TrainBrakeController . TrainBrakeControllerState != ControllerState . Neutral )
11311198 {
11321199 // Charge train brake pipe - adjust main reservoir pressure, and lead brake pressure line to maintain brake pipe equal to equalising resevoir pressure - release brakes
11331200 if ( lead . BrakeSystem . BrakeLine1PressurePSI < train . EqualReservoirPressurePSIorInHg )
@@ -1203,6 +1270,13 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
12031270 // The sign in the equation determines the direction of air flow.
12041271 float trainPipePressureDiffPropagationPSI = pressureDiffPSI * Math . Min ( trainPipeTimeVariationS / brakePipeTimeFactorS , 1 ) ;
12051272
1273+ // Flow is restricted when either anglecock is not opened fully
1274+ if ( car . BrakeSystem . AngleCockAOpenAmount < 1 || prevCar . BrakeSystem . AngleCockBOpenAmount < 1 )
1275+ {
1276+ trainPipePressureDiffPropagationPSI *= MathHelper . Min ( ( float ) Math . Pow ( car . BrakeSystem . AngleCockAOpenAmount * prevCar . BrakeSystem . AngleCockBOpenAmount , 2 ) , 1.0f ) ;
1277+
1278+ }
1279+
12061280 // Air flows from high pressure to low pressure, until pressure is equal in both cars.
12071281 // Brake pipe volumes of both cars are taken into account, so pressure increase/decrease is proportional to relative volumes.
12081282 // If TrainPipePressureDiffPropagationPSI equals to p1-p0 the equalization is achieved in one step.
@@ -1221,11 +1295,25 @@ protected static void PropagateBrakeLinePressures(float elapsedClockSeconds, Tra
12211295 // Empty the brake pipe if the brake hose is not connected and angle cocks are open
12221296 if ( ! car . BrakeSystem . FrontBrakeHoseConnected && car . BrakeSystem . AngleCockAOpen )
12231297 {
1224- car . BrakeSystem . BrakeLine1PressurePSI = Math . Max ( car . BrakeSystem . BrakeLine1PressurePSI * ( 1 - trainPipeTimeVariationS / brakePipeTimeFactorS ) , 0 ) ;
1298+ float dp = car . BrakeSystem . BrakeLine1PressurePSI * trainPipeTimeVariationS / brakePipeTimeFactorS ;
1299+
1300+ if ( car . BrakeSystem . AngleCockAOpenAmount < 1 )
1301+ dp *= MathHelper . Clamp ( ( float ) Math . Pow ( car . BrakeSystem . AngleCockAOpenAmount , 2 ) , 0.0f , 1.0f ) ;
1302+
1303+ if ( car . BrakeSystem . BrakeLine1PressurePSI - dp < 0 )
1304+ dp = car . BrakeSystem . BrakeLine1PressurePSI ;
1305+ car . BrakeSystem . BrakeLine1PressurePSI -= dp ;
12251306 }
12261307 if ( ( nextCar == null || ! nextCar . BrakeSystem . FrontBrakeHoseConnected ) && car . BrakeSystem . AngleCockBOpen )
12271308 {
1228- car . BrakeSystem . BrakeLine1PressurePSI = Math . Max ( car . BrakeSystem . BrakeLine1PressurePSI * ( 1 - trainPipeTimeVariationS / brakePipeTimeFactorS ) , 0 ) ;
1309+ float dp = car . BrakeSystem . BrakeLine1PressurePSI * trainPipeTimeVariationS / brakePipeTimeFactorS ;
1310+
1311+ if ( car . BrakeSystem . AngleCockBOpenAmount < 1 )
1312+ dp *= MathHelper . Clamp ( ( float ) Math . Pow ( car . BrakeSystem . AngleCockBOpenAmount , 2 ) , 0.0f , 1.0f ) ;
1313+
1314+ if ( car . BrakeSystem . BrakeLine1PressurePSI - dp < 0 )
1315+ dp = car . BrakeSystem . BrakeLine1PressurePSI ;
1316+ car . BrakeSystem . BrakeLine1PressurePSI -= dp ;
12291317 }
12301318 }
12311319#if DEBUG_TRAIN_PIPE_LEAK
0 commit comments