@@ -283,23 +283,105 @@ public void Restore(BinaryReader inf)
283283 AxleList [ i ] . Restore ( inf ) ;
284284 }
285285 }
286+
287+ /// <summary>
288+ /// switch between Polach and Pacha adhesion calculation
289+ /// </summary>
290+ public static bool UsePolachAdhesion = false ; // "static" so there's only one value in the program.
291+ public bool PreviousUsePolachAdhesion = false ; // Keep a note for each Axles so that we can tell if it changed.
292+
286293 /// <summary>
287294 /// Updates each axle on the list
288295 /// </summary>
289- /// <param name="elapsedClockSeconds ">Time span within the simulation cycle</param>
290- public void Update ( float elapsedClockSeconds )
296+ /// <param name="elapsedSeconds ">Time span within the simulation cycle</param>
297+ public void Update ( float elapsedSeconds )
291298 {
299+ UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( this , elapsedSeconds , Car . Simulator . GameTime ) ;
292300 foreach ( var axle in AxleList )
293301 {
294- axle . Update ( elapsedClockSeconds ) ;
302+ if ( UsePolachAdhesion != PreviousUsePolachAdhesion ) // There's been a transition
303+ {
304+ axle . AxleSpeedMpS = axle . TrainSpeedMpS ; // So the transition doesn't cause a wheelslip
305+ }
306+ axle . Update ( elapsedSeconds ) ;
295307 }
308+ PreviousUsePolachAdhesion = UsePolachAdhesion ;
296309 }
297310 public List < Axle > . Enumerator GetEnumerator ( )
298311 {
299312 return AxleList . GetEnumerator ( ) ;
300313 }
314+
315+ static class AdhesionPrecision // "static" so all "Axles" share the same level of precision
316+ {
317+ enum AdhesionPrecisionLevel
318+ {
319+ /// <summary>
320+ /// Initial level uses Polach algorithm
321+ /// </summary>
322+ High = 0 ,
323+ /// <summary>
324+ /// Low-performance PCs use Pacha's algorithm
325+ /// </summary>
326+ Low = 1 ,
327+ /// <summary>
328+ /// After frequent transitions, low-performance PCs are locked to Pacha's algorithm
329+ /// </summary>
330+ LowLocked = 2
301331 }
302332
333+ // Adjustable limits
334+ const float LowerLimitS = 0.025f ; // timespan 0.025 = 40 fps screen rate, low timeSpan and high FPS
335+ const float UpperLimitS = 0.033f ; // timespan 0.033 = 30 fps screen rate, high timeSpan and low FPS
336+ const double IntervalBetweenDowngradesLimitS = 5 * 60 ; // Locks in low precision if < 5 mins between downgrades
337+
338+ static AdhesionPrecisionLevel PrecisionLevel = AdhesionPrecisionLevel . High ;
339+ static double TimeOfLatestDowngrade = 0 - IntervalBetweenDowngradesLimitS ; // Starts at -5 mins
340+
341+ // Tested by dropping the framerate below 30 fps interactively. Did this by opening and closing the HelpWindow after inserting
342+ // Threading.Thread.Sleep(40);
343+ // into HelpWindow.PrepareFrame() temporarily.
344+ public static bool IsPrecisionHigh ( Axles axles , float elapsedSeconds , double gameTime )
345+ {
346+ // Switches between Polach (high precision) adhesion model and Pacha (low precision) adhesion model depending upon the PC performance
347+ switch ( PrecisionLevel )
348+ {
349+ case AdhesionPrecisionLevel . High :
350+ if ( elapsedSeconds > UpperLimitS )
351+ {
352+ var screenFrameRate = 1 / elapsedSeconds ;
353+ var timeSincePreviousDowngradeS = gameTime - TimeOfLatestDowngrade ;
354+ if ( timeSincePreviousDowngradeS < IntervalBetweenDowngradesLimitS )
355+ {
356+ 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 } )") ;
357+ PrecisionLevel = AdhesionPrecisionLevel . LowLocked ;
358+ }
359+ else
360+ {
361+ TimeOfLatestDowngrade = gameTime ;
362+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision after low frame rate { screenFrameRate : F1} below limit { 1 / UpperLimitS : F0} ") ;
363+ PrecisionLevel = AdhesionPrecisionLevel . Low ;
364+ }
365+ }
366+ break ;
367+
368+ case AdhesionPrecisionLevel . Low :
369+ if ( elapsedSeconds > 0 // When debugging step by step, elapsedSeconds == 0, so test for that
370+ && elapsedSeconds < LowerLimitS )
371+ {
372+ PrecisionLevel = AdhesionPrecisionLevel . High ;
373+ var ScreenFrameRate = 1 / elapsedSeconds ;
374+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to high precision after high frame rate { ScreenFrameRate : F1} above limit { 1 / LowerLimitS : F0} ") ;
375+ }
376+ break ;
377+
378+ case AdhesionPrecisionLevel . LowLocked :
379+ break ;
380+ }
381+ return ( PrecisionLevel == AdhesionPrecisionLevel . High ) ;
382+ }
383+ }
384+ }
303385
304386
305387 /// <summary>
@@ -433,12 +515,6 @@ public float InertiaKgm2
433515 /// </summary>
434516 float forceToAccelerationFactor ;
435517
436- /// <summary>
437- /// switch between Polach and Pacha adhesion calculation
438- /// </summary>
439- public static bool UsePolachAdhesion = false ; // "static" so it's shared by all axles of the Player's loco
440- public double GameTime ; // Set by MSTSLocomotive and used by AdhesionPrecision.IsPrecisionHigh
441-
442518 /// <summary>
443519 /// Pre-calculation of slip characteristics at 0 slip speed
444520 /// </summary>
@@ -561,7 +637,7 @@ public float TransmissionEfficiency
561637 /// <summary>
562638 /// Axle speed value, in metric meters per second
563639 /// </summary>
564- public double AxleSpeedMpS { get ; private set ; }
640+ public double AxleSpeedMpS { get ; set ; }
565641
566642 /// <summary>
567643 /// Axle angular position in radians
@@ -845,7 +921,7 @@ public void Save(BinaryWriter outf)
845921 double slipSpeedMpS = axleSpeedMpS - TrainSpeedMpS ;
846922 double axleOutForceN = 0 ;
847923
848- if ( UsePolachAdhesion )
924+ if ( Axles . UsePolachAdhesion )
849925 {
850926 axleOutForceN = Math . Sign ( slipSpeedMpS ) * AxleWeightN * SlipCharacteristicsPolach ( slipSpeedMpS ) ;
851927 }
@@ -891,7 +967,7 @@ void Integrate(float elapsedClockSeconds)
891967 if ( elapsedClockSeconds <= 0 ) return ;
892968 double prevSpeedMpS = AxleSpeedMpS ;
893969
894- if ( UsePolachAdhesion )
970+ if ( Axles . UsePolachAdhesion )
895971 {
896972
897973 float upperSubStepLimit = 100 ;
@@ -980,7 +1056,7 @@ void Integrate(float elapsedClockSeconds)
9801056 {
9811057 var k1 = GetAxleMotionVariation ( AxleSpeedMpS , dt ) ;
9821058
983- if ( i == 0 && ! UsePolachAdhesion )
1059+ if ( i == 0 && ! Axles . UsePolachAdhesion )
9841060 {
9851061 if ( k1 . Item1 * dt > Math . Max ( ( Math . Abs ( SlipSpeedMpS ) - 1 ) * 10 , 1 ) / 100 )
9861062 {
@@ -1023,8 +1099,7 @@ void Integrate(float elapsedClockSeconds)
10231099 /// <param name="elapsedSeconds"></param>
10241100 public virtual void Update ( float elapsedSeconds )
10251101 {
1026- UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( elapsedSeconds , GameTime ) ;
1027- if ( UsePolachAdhesion )
1102+ if ( Axles . UsePolachAdhesion )
10281103 {
10291104 forceToAccelerationFactor = WheelRadiusM * WheelRadiusM / totalInertiaKgm2 ;
10301105
@@ -1128,79 +1203,6 @@ public virtual void Update(float elapsedSeconds)
11281203 }
11291204 }
11301205
1131- static class AdhesionPrecision // "static" so all "Axle"s share the same level of precision
1132- {
1133- enum AdhesionPrecisionLevel
1134- {
1135- /// <summary>
1136- /// Initial level uses Polach algorithm
1137- /// </summary>
1138- High = 0 ,
1139- /// <summary>
1140- /// Low-performance PCs use Pacha's algorithm
1141- /// </summary>
1142- Low = 1 ,
1143- /// <summary>
1144- /// After frequent transitions, low-performance PCs are locked to Pacha's algorithm
1145- /// </summary>
1146- LowLocked = 2
1147- }
1148-
1149- // Adjustable limits
1150- const float LowerLimitS = 0.025f ; // timespan 0.025 = 40 fps screen rate, low timeSpan and high FPS
1151- const float UpperLimitS = 0.033f ; // timespan 0.033 = 30 fps screen rate, high timeSpan and low FPS
1152- const double IntervalBetweenDowngradesLimitS = 5 * 60 ; // Locks in low precision if < 5 mins between downgrades
1153-
1154- static AdhesionPrecisionLevel PrecisionLevel = AdhesionPrecisionLevel . High ;
1155- static double TimeOfLatestDowngrade = 0 - IntervalBetweenDowngradesLimitS ; // Starts at -5 mins
1156-
1157- // Tested by varying the framerate interactively. Did this by opening and closing the HelpWindow after inserting
1158- // Threading.Thread.Sleep(40);
1159- // into HelpWindow.PrepareFrame() temporarily.
1160- public static bool IsPrecisionHigh ( float elapsedSeconds , double gameTime )
1161- {
1162- // Switches between Polach (high precision) adhesion model and Pacha (low precision) adhesion model depending upon the PC performance
1163- switch ( PrecisionLevel )
1164- {
1165- case AdhesionPrecisionLevel . High :
1166- if ( elapsedSeconds > UpperLimitS )
1167- {
1168- var screenFrameRate = 1 / elapsedSeconds ;
1169- var timeSincePreviousDowngradeS = gameTime - TimeOfLatestDowngrade ;
1170- if ( timeSincePreviousDowngradeS < IntervalBetweenDowngradesLimitS )
1171- {
1172- 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 } )") ;
1173- PrecisionLevel = AdhesionPrecisionLevel . LowLocked ;
1174- }
1175- else
1176- {
1177- TimeOfLatestDowngrade = gameTime ;
1178-
1179- Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision after low frame rate { screenFrameRate : F1} below limit { 1 / UpperLimitS : F0} ") ;
1180- PrecisionLevel = AdhesionPrecisionLevel . Low ;
1181-
1182- }
1183- }
1184- break ;
1185-
1186- case AdhesionPrecisionLevel . Low :
1187- if ( elapsedSeconds > 0 // When debugging step by step, elapsedSeconds == 0, so test for that
1188- && elapsedSeconds < LowerLimitS )
1189- {
1190- PrecisionLevel = AdhesionPrecisionLevel . High ;
1191- var ScreenFrameRate = 1 / elapsedSeconds ;
1192- Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to high precision after high frame rate { ScreenFrameRate : F1} above limit { 1 / LowerLimitS : F0} ") ;
1193- }
1194- break ;
1195-
1196- case AdhesionPrecisionLevel . LowLocked :
1197- break ;
1198-
1199- }
1200- return ( PrecisionLevel == AdhesionPrecisionLevel . High ) ;
1201- }
1202- }
1203-
12041206 class PolachCalculator
12051207 {
12061208 Axle Axle ;
0 commit comments