@@ -281,23 +281,105 @@ public void Restore(BinaryReader inf)
281281 AxleList [ i ] . Restore ( inf ) ;
282282 }
283283 }
284+
285+ /// <summary>
286+ /// switch between Polach and Pacha adhesion calculation
287+ /// </summary>
288+ public static bool UsePolachAdhesion = false ; // "static" so there's only one value in the program.
289+ public bool PreviousUsePolachAdhesion = false ; // Keep a note for each Axles so that we can tell if it changed.
290+
284291 /// <summary>
285292 /// Updates each axle on the list
286293 /// </summary>
287- /// <param name="elapsedClockSeconds ">Time span within the simulation cycle</param>
288- public void Update ( float elapsedClockSeconds )
294+ /// <param name="elapsedSeconds ">Time span within the simulation cycle</param>
295+ public void Update ( float elapsedSeconds )
289296 {
297+ UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( this , elapsedSeconds , Car . Simulator . GameTime ) ;
290298 foreach ( var axle in AxleList )
291299 {
292- axle . Update ( elapsedClockSeconds ) ;
300+ if ( UsePolachAdhesion != PreviousUsePolachAdhesion ) // There's been a transition
301+ {
302+ axle . AxleSpeedMpS = axle . TrainSpeedMpS ; // So the transition doesn't cause a wheelslip
303+ }
304+ axle . Update ( elapsedSeconds ) ;
293305 }
306+ PreviousUsePolachAdhesion = UsePolachAdhesion ;
294307 }
295308 public List < Axle > . Enumerator GetEnumerator ( )
296309 {
297310 return AxleList . GetEnumerator ( ) ;
298311 }
299- }
300312
313+ static class AdhesionPrecision // "static" so all "Axles" share the same level of precision
314+ {
315+ enum AdhesionPrecisionLevel
316+ {
317+ /// <summary>
318+ /// Initial level uses Polach algorithm
319+ /// </summary>
320+ High = 0 ,
321+ /// <summary>
322+ /// Low-performance PCs use Pacha's algorithm
323+ /// </summary>
324+ Low = 1 ,
325+ /// <summary>
326+ /// After frequent transitions, low-performance PCs are locked to Pacha's algorithm
327+ /// </summary>
328+ LowLocked = 2
329+ }
330+
331+ // Adjustable limits
332+ const float LowerLimitS = 0.025f ; // timespan 0.025 = 40 fps screen rate, low timeSpan and high FPS
333+ const float UpperLimitS = 0.033f ; // timespan 0.033 = 30 fps screen rate, high timeSpan and low FPS
334+ const double IntervalBetweenDowngradesLimitS = 5 * 60 ; // Locks in low precision if < 5 mins between downgrades
335+
336+ static AdhesionPrecisionLevel PrecisionLevel = AdhesionPrecisionLevel . High ;
337+ static double TimeOfLatestDowngrade = 0 - IntervalBetweenDowngradesLimitS ; // Starts at -5 mins
338+
339+ // Tested by dropping the framerate below 30 fps interactively. Did this by opening and closing the HelpWindow after inserting
340+ // Threading.Thread.Sleep(40);
341+ // into HelpWindow.PrepareFrame() temporarily.
342+ public static bool IsPrecisionHigh ( Axles axles , float elapsedSeconds , double gameTime )
343+ {
344+ // Switches between Polach (high precision) adhesion model and Pacha (low precision) adhesion model depending upon the PC performance
345+ switch ( PrecisionLevel )
346+ {
347+ case AdhesionPrecisionLevel . High :
348+ if ( elapsedSeconds > UpperLimitS )
349+ {
350+ var screenFrameRate = 1 / elapsedSeconds ;
351+ var timeSincePreviousDowngradeS = gameTime - TimeOfLatestDowngrade ;
352+ if ( timeSincePreviousDowngradeS < IntervalBetweenDowngradesLimitS )
353+ {
354+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision permanently after { timeSincePreviousDowngradeS : F0} secs since previous switch (less than limit of { IntervalBetweenDowngradesLimitS } )") ;
355+ PrecisionLevel = AdhesionPrecisionLevel . LowLocked ;
356+ }
357+ else
358+ {
359+ TimeOfLatestDowngrade = gameTime ;
360+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision after low frame rate { screenFrameRate : F1} below limit { 1 / UpperLimitS : F0} ") ;
361+ PrecisionLevel = AdhesionPrecisionLevel . Low ;
362+ }
363+ }
364+ break ;
365+
366+ case AdhesionPrecisionLevel . Low :
367+ if ( elapsedSeconds > 0 // When debugging step by step, elapsedSeconds == 0, so test for that
368+ && elapsedSeconds < LowerLimitS )
369+ {
370+ PrecisionLevel = AdhesionPrecisionLevel . High ;
371+ var ScreenFrameRate = 1 / elapsedSeconds ;
372+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to high precision after high frame rate { ScreenFrameRate : F1} above limit { 1 / LowerLimitS : F0} ") ;
373+ }
374+ break ;
375+
376+ case AdhesionPrecisionLevel . LowLocked :
377+ break ;
378+ }
379+ return ( PrecisionLevel == AdhesionPrecisionLevel . High ) ;
380+ }
381+ }
382+ }
301383
302384
303385 /// <summary>
@@ -428,12 +510,6 @@ public float InertiaKgm2
428510 /// </summary>
429511 float forceToAccelerationFactor ;
430512
431- /// <summary>
432- /// switch between Polach and Pacha adhesion calculation
433- /// </summary>
434- public static bool UsePolachAdhesion = false ; // "static" so it's shared by all axles of the Player's loco
435- public double GameTime ; // Set by MSTSLocomotive and used by AdhesionPrecision.IsPrecisionHigh
436-
437513 /// <summary>
438514 /// Pre-calculation of slip characteristics at 0 slip speed
439515 /// </summary>
@@ -546,7 +622,7 @@ public float TransmissionEfficiency
546622 /// <summary>
547623 /// Axle speed value, in metric meters per second
548624 /// </summary>
549- public double AxleSpeedMpS { get ; private set ; }
625+ public double AxleSpeedMpS { get ; set ; }
550626
551627 /// <summary>
552628 /// Axle angular position in radians
@@ -824,7 +900,7 @@ public void Save(BinaryWriter outf)
824900 double slipSpeedMpS = axleSpeedMpS - TrainSpeedMpS ;
825901 double axleOutForceN = 0 ;
826902
827- if ( UsePolachAdhesion )
903+ if ( Axles . UsePolachAdhesion )
828904 {
829905 axleOutForceN = Math . Sign ( slipSpeedMpS ) * AxleWeightN * SlipCharacteristicsPolach ( slipSpeedMpS ) ;
830906 }
@@ -870,7 +946,7 @@ void Integrate(float elapsedClockSeconds)
870946 if ( elapsedClockSeconds <= 0 ) return ;
871947 double prevSpeedMpS = AxleSpeedMpS ;
872948
873- if ( UsePolachAdhesion )
949+ if ( Axles . UsePolachAdhesion )
874950 {
875951
876952 float upperSubStepLimit = 100 ;
@@ -959,7 +1035,7 @@ void Integrate(float elapsedClockSeconds)
9591035 {
9601036 var k1 = GetAxleMotionVariation ( AxleSpeedMpS , dt ) ;
9611037
962- if ( i == 0 && ! UsePolachAdhesion )
1038+ if ( i == 0 && ! Axles . UsePolachAdhesion )
9631039 {
9641040 if ( k1 . Item1 * dt > Math . Max ( ( Math . Abs ( SlipSpeedMpS ) - 1 ) * 10 , 1 ) / 100 )
9651041 {
@@ -1002,8 +1078,7 @@ void Integrate(float elapsedClockSeconds)
10021078 /// <param name="elapsedSeconds"></param>
10031079 public virtual void Update ( float elapsedSeconds )
10041080 {
1005- UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( elapsedSeconds , GameTime ) ;
1006- if ( UsePolachAdhesion )
1081+ if ( Axles . UsePolachAdhesion )
10071082 {
10081083 forceToAccelerationFactor = WheelRadiusM * WheelRadiusM / totalInertiaKgm2 ;
10091084
@@ -1098,79 +1173,6 @@ public virtual void Update(float elapsedSeconds)
10981173 }
10991174 }
11001175
1101- static class AdhesionPrecision // "static" so all "Axle"s share the same level of precision
1102- {
1103- enum AdhesionPrecisionLevel
1104- {
1105- /// <summary>
1106- /// Initial level uses Polach algorithm
1107- /// </summary>
1108- High = 0 ,
1109- /// <summary>
1110- /// Low-performance PCs use Pacha's algorithm
1111- /// </summary>
1112- Low = 1 ,
1113- /// <summary>
1114- /// After frequent transitions, low-performance PCs are locked to Pacha's algorithm
1115- /// </summary>
1116- LowLocked = 2
1117- }
1118-
1119- // Adjustable limits
1120- const float LowerLimitS = 0.025f ; // timespan 0.025 = 40 fps screen rate, low timeSpan and high FPS
1121- const float UpperLimitS = 0.033f ; // timespan 0.033 = 30 fps screen rate, high timeSpan and low FPS
1122- const double IntervalBetweenDowngradesLimitS = 5 * 60 ; // Locks in low precision if < 5 mins between downgrades
1123-
1124- static AdhesionPrecisionLevel PrecisionLevel = AdhesionPrecisionLevel . High ;
1125- static double TimeOfLatestDowngrade = 0 - IntervalBetweenDowngradesLimitS ; // Starts at -5 mins
1126-
1127- // Tested by varying the framerate interactively. Did this by opening and closing the HelpWindow after inserting
1128- // Threading.Thread.Sleep(40);
1129- // into HelpWindow.PrepareFrame() temporarily.
1130- public static bool IsPrecisionHigh ( float elapsedSeconds , double gameTime )
1131- {
1132- // Switches between Polach (high precision) adhesion model and Pacha (low precision) adhesion model depending upon the PC performance
1133- switch ( PrecisionLevel )
1134- {
1135- case AdhesionPrecisionLevel . High :
1136- if ( elapsedSeconds > UpperLimitS )
1137- {
1138- var screenFrameRate = 1 / elapsedSeconds ;
1139- var timeSincePreviousDowngradeS = gameTime - TimeOfLatestDowngrade ;
1140- if ( timeSincePreviousDowngradeS < IntervalBetweenDowngradesLimitS )
1141- {
1142- Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision permanently after { timeSincePreviousDowngradeS : F0} secs since previous switch (less than limit of { IntervalBetweenDowngradesLimitS } )") ;
1143- PrecisionLevel = AdhesionPrecisionLevel . LowLocked ;
1144- }
1145- else
1146- {
1147- TimeOfLatestDowngrade = gameTime ;
1148-
1149- Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision after low frame rate { screenFrameRate : F1} below limit { 1 / UpperLimitS : F0} ") ;
1150- PrecisionLevel = AdhesionPrecisionLevel . Low ;
1151-
1152- }
1153- }
1154- break ;
1155-
1156- case AdhesionPrecisionLevel . Low :
1157- if ( elapsedSeconds > 0 // When debugging step by step, elapsedSeconds == 0, so test for that
1158- && elapsedSeconds < LowerLimitS )
1159- {
1160- PrecisionLevel = AdhesionPrecisionLevel . High ;
1161- var ScreenFrameRate = 1 / elapsedSeconds ;
1162- Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to high precision after high frame rate { ScreenFrameRate : F1} above limit { 1 / LowerLimitS : F0} ") ;
1163- }
1164- break ;
1165-
1166- case AdhesionPrecisionLevel . LowLocked :
1167- break ;
1168-
1169- }
1170- return ( PrecisionLevel == AdhesionPrecisionLevel . High ) ;
1171- }
1172- }
1173-
11741176 class PolachCalculator
11751177 {
11761178 Axle Axle ;
0 commit comments