@@ -295,23 +295,105 @@ public void Restore(BinaryReader inf)
295295 AxleList [ i ] . Restore ( inf ) ;
296296 }
297297 }
298+
299+ /// <summary>
300+ /// switch between Polach and Pacha adhesion calculation
301+ /// </summary>
302+ public static bool UsePolachAdhesion = false ; // "static" so there's only one value in the program.
303+ public bool PreviousUsePolachAdhesion = false ; // Keep a note for each Axles so that we can tell if it changed.
304+
298305 /// <summary>
299306 /// Updates each axle on the list
300307 /// </summary>
301- /// <param name="elapsedClockSeconds ">Time span within the simulation cycle</param>
302- public void Update ( float elapsedClockSeconds )
308+ /// <param name="elapsedSeconds ">Time span within the simulation cycle</param>
309+ public void Update ( float elapsedSeconds )
303310 {
311+ UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( this , elapsedSeconds , Car . Simulator . GameTime ) ;
304312 foreach ( var axle in AxleList )
305313 {
306- axle . Update ( elapsedClockSeconds ) ;
314+ if ( UsePolachAdhesion != PreviousUsePolachAdhesion ) // There's been a transition
315+ {
316+ axle . AxleSpeedMpS = axle . TrainSpeedMpS ; // So the transition doesn't cause a wheelslip
317+ }
318+ axle . Update ( elapsedSeconds ) ;
307319 }
320+ PreviousUsePolachAdhesion = UsePolachAdhesion ;
308321 }
309322 public List < Axle > . Enumerator GetEnumerator ( )
310323 {
311324 return AxleList . GetEnumerator ( ) ;
312325 }
313- }
314326
327+ static class AdhesionPrecision // "static" so all "Axles" share the same level of precision
328+ {
329+ enum AdhesionPrecisionLevel
330+ {
331+ /// <summary>
332+ /// Initial level uses Polach algorithm
333+ /// </summary>
334+ High = 0 ,
335+ /// <summary>
336+ /// Low-performance PCs use Pacha's algorithm
337+ /// </summary>
338+ Low = 1 ,
339+ /// <summary>
340+ /// After frequent transitions, low-performance PCs are locked to Pacha's algorithm
341+ /// </summary>
342+ LowLocked = 2
343+ }
344+
345+ // Adjustable limits
346+ const float LowerLimitS = 0.025f ; // timespan 0.025 = 40 fps screen rate, low timeSpan and high FPS
347+ const float UpperLimitS = 0.033f ; // timespan 0.033 = 30 fps screen rate, high timeSpan and low FPS
348+ const double IntervalBetweenDowngradesLimitS = 5 * 60 ; // Locks in low precision if < 5 mins between downgrades
349+
350+ static AdhesionPrecisionLevel PrecisionLevel = AdhesionPrecisionLevel . High ;
351+ static double TimeOfLatestDowngrade = 0 - IntervalBetweenDowngradesLimitS ; // Starts at -5 mins
352+
353+ // Tested by dropping the framerate below 30 fps interactively. Did this by opening and closing the HelpWindow after inserting
354+ // Threading.Thread.Sleep(40);
355+ // into HelpWindow.PrepareFrame() temporarily.
356+ public static bool IsPrecisionHigh ( Axles axles , float elapsedSeconds , double gameTime )
357+ {
358+ // Switches between Polach (high precision) adhesion model and Pacha (low precision) adhesion model depending upon the PC performance
359+ switch ( PrecisionLevel )
360+ {
361+ case AdhesionPrecisionLevel . High :
362+ if ( elapsedSeconds > UpperLimitS )
363+ {
364+ var screenFrameRate = 1 / elapsedSeconds ;
365+ var timeSincePreviousDowngradeS = gameTime - TimeOfLatestDowngrade ;
366+ if ( timeSincePreviousDowngradeS < IntervalBetweenDowngradesLimitS )
367+ {
368+ 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 } )") ;
369+ PrecisionLevel = AdhesionPrecisionLevel . LowLocked ;
370+ }
371+ else
372+ {
373+ TimeOfLatestDowngrade = gameTime ;
374+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to low precision after low frame rate { screenFrameRate : F1} below limit { 1 / UpperLimitS : F0} ") ;
375+ PrecisionLevel = AdhesionPrecisionLevel . Low ;
376+ }
377+ }
378+ break ;
379+
380+ case AdhesionPrecisionLevel . Low :
381+ if ( elapsedSeconds > 0 // When debugging step by step, elapsedSeconds == 0, so test for that
382+ && elapsedSeconds < LowerLimitS )
383+ {
384+ PrecisionLevel = AdhesionPrecisionLevel . High ;
385+ var ScreenFrameRate = 1 / elapsedSeconds ;
386+ Trace . TraceInformation ( $ "At { gameTime : F0} secs, advanced adhesion model switched to high precision after high frame rate { ScreenFrameRate : F1} above limit { 1 / LowerLimitS : F0} ") ;
387+ }
388+ break ;
389+
390+ case AdhesionPrecisionLevel . LowLocked :
391+ break ;
392+ }
393+ return ( PrecisionLevel == AdhesionPrecisionLevel . High ) ;
394+ }
395+ }
396+ }
315397
316398
317399 /// <summary>
@@ -446,11 +528,6 @@ public float InertiaKgm2
446528 /// </summary>
447529 float forceToAccelerationFactor ;
448530
449- /// <summary>
450- /// switch between Polach and Pacha adhesion calculation
451- /// </summary>
452- public static bool UsePolachAdhesion = false ; // "static" so it's shared by all axles of the Player's loco
453-
454531 /// <summary>
455532 /// Pre-calculation of slip characteristics at 0 slip speed
456533 /// </summary>
@@ -578,7 +655,7 @@ public float TransmissionEfficiency
578655 /// <summary>
579656 /// Axle speed value, in metric meters per second
580657 /// </summary>
581- public double AxleSpeedMpS { get ; private set ; }
658+ public double AxleSpeedMpS { get ; set ; }
582659
583660 /// <summary>
584661 /// Axle angular position in radians
@@ -860,7 +937,7 @@ public void Save(BinaryWriter outf)
860937 double slipSpeedMpS = axleSpeedMpS - TrainSpeedMpS ;
861938 double axleOutForceN = 0 ;
862939
863- if ( UsePolachAdhesion )
940+ if ( Axles . UsePolachAdhesion )
864941 {
865942 axleOutForceN = Math . Sign ( slipSpeedMpS ) * AxleWeightN * SlipCharacteristicsPolach ( slipSpeedMpS ) ;
866943 }
@@ -906,7 +983,7 @@ void Integrate(float elapsedClockSeconds)
906983 if ( elapsedClockSeconds <= 0 ) return ;
907984 double prevSpeedMpS = AxleSpeedMpS ;
908985
909- if ( UsePolachAdhesion )
986+ if ( Axles . UsePolachAdhesion )
910987 {
911988
912989 float upperSubStepLimit = 100 ;
@@ -995,7 +1072,7 @@ void Integrate(float elapsedClockSeconds)
9951072 {
9961073 var k1 = GetAxleMotionVariation ( AxleSpeedMpS , dt ) ;
9971074
998- if ( i == 0 && ! UsePolachAdhesion )
1075+ if ( i == 0 && ! Axles . UsePolachAdhesion )
9991076 {
10001077 if ( k1 . Item1 * dt > Math . Max ( ( Math . Abs ( SlipSpeedMpS ) - 1 ) * 10 , 1 ) / 100 )
10011078 {
@@ -1038,8 +1115,7 @@ void Integrate(float elapsedClockSeconds)
10381115 /// <param name="elapsedSeconds"></param>
10391116 public virtual void Update ( float elapsedSeconds )
10401117 {
1041- UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( elapsedSeconds ) ;
1042- if ( UsePolachAdhesion )
1118+ if ( Axles . UsePolachAdhesion )
10431119 {
10441120 forceToAccelerationFactor = WheelRadiusM * WheelRadiusM / totalInertiaKgm2 ;
10451121
@@ -1143,55 +1219,6 @@ public virtual void Update(float elapsedSeconds)
11431219 }
11441220 }
11451221
1146- static class AdhesionPrecision // "static" so all "Axle"s share the same level of precision
1147- {
1148- enum AdhesionPrecisionLevel
1149- {
1150- /// <summary>
1151- /// Initial level uses Polach algorithm
1152- /// </summary>
1153- High ,
1154- /// <summary>
1155- /// Low-performance PCs use Pacha's algorithm
1156- /// </summary>
1157- Low
1158- }
1159-
1160- static AdhesionPrecisionLevel PrecisionLevel = AdhesionPrecisionLevel . High ;
1161- static double TimeOfLatestDowngrade = 0 ;
1162-
1163- // Adjustable limits
1164- const float UpperLimitS = 0.033f ; // timespan 0.033 = 30 fps screen rate, high timeSpan and low FPS
1165-
1166- // Tested by varying the framerate interactively. Did this by opening and closing the HelpWindow after inserting
1167- // Threading.Thread.Sleep(40);
1168- // into HelpWindow.PrepareFrame() temporarily.
1169- public static bool IsPrecisionHigh ( float elapsedSeconds )
1170- {
1171- if ( elapsedSeconds > 0 ) // Ignore period with elapsedSeconds == 0 until user starts game.
1172- {
1173- // Switches between Polach (high precision) adhesion model and Pacha (low precision) adhesion model depending upon the PC performance
1174- switch ( PrecisionLevel )
1175- {
1176- case AdhesionPrecisionLevel . High :
1177- if ( elapsedSeconds > UpperLimitS )
1178- {
1179- var screenFrameRate = 1 / elapsedSeconds ;
1180- {
1181- Trace . TraceInformation ( $ "Advanced adhesion model switched to low precision permanently after low frame rate { screenFrameRate : F1} below limit { 1 / UpperLimitS : F0} ") ;
1182- PrecisionLevel = AdhesionPrecisionLevel . Low ;
1183- }
1184- }
1185- break ;
1186-
1187- case AdhesionPrecisionLevel . Low :
1188- break ;
1189- }
1190- }
1191- return ( PrecisionLevel == AdhesionPrecisionLevel . High ) ;
1192- }
1193- }
1194-
11951222 class PolachCalculator
11961223 {
11971224 Axle Axle ;
0 commit comments