@@ -295,23 +295,105 @@ public void Restore(BinaryReader inf)
295
295
AxleList [ i ] . Restore ( inf ) ;
296
296
}
297
297
}
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
+
298
305
/// <summary>
299
306
/// Updates each axle on the list
300
307
/// </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 )
303
310
{
311
+ UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( this , elapsedSeconds , Car . Simulator . GameTime ) ;
304
312
foreach ( var axle in AxleList )
305
313
{
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 ) ;
307
319
}
320
+ PreviousUsePolachAdhesion = UsePolachAdhesion ;
308
321
}
309
322
public List < Axle > . Enumerator GetEnumerator ( )
310
323
{
311
324
return AxleList . GetEnumerator ( ) ;
312
325
}
313
- }
314
326
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
+ }
315
397
316
398
317
399
/// <summary>
@@ -446,11 +528,6 @@ public float InertiaKgm2
446
528
/// </summary>
447
529
float forceToAccelerationFactor ;
448
530
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
-
454
531
/// <summary>
455
532
/// Pre-calculation of slip characteristics at 0 slip speed
456
533
/// </summary>
@@ -578,7 +655,7 @@ public float TransmissionEfficiency
578
655
/// <summary>
579
656
/// Axle speed value, in metric meters per second
580
657
/// </summary>
581
- public double AxleSpeedMpS { get ; private set ; }
658
+ public double AxleSpeedMpS { get ; set ; }
582
659
583
660
/// <summary>
584
661
/// Axle angular position in radians
@@ -860,7 +937,7 @@ public void Save(BinaryWriter outf)
860
937
double slipSpeedMpS = axleSpeedMpS - TrainSpeedMpS ;
861
938
double axleOutForceN = 0 ;
862
939
863
- if ( UsePolachAdhesion )
940
+ if ( Axles . UsePolachAdhesion )
864
941
{
865
942
axleOutForceN = Math . Sign ( slipSpeedMpS ) * AxleWeightN * SlipCharacteristicsPolach ( slipSpeedMpS ) ;
866
943
}
@@ -906,7 +983,7 @@ void Integrate(float elapsedClockSeconds)
906
983
if ( elapsedClockSeconds <= 0 ) return ;
907
984
double prevSpeedMpS = AxleSpeedMpS ;
908
985
909
- if ( UsePolachAdhesion )
986
+ if ( Axles . UsePolachAdhesion )
910
987
{
911
988
912
989
float upperSubStepLimit = 100 ;
@@ -995,7 +1072,7 @@ void Integrate(float elapsedClockSeconds)
995
1072
{
996
1073
var k1 = GetAxleMotionVariation ( AxleSpeedMpS , dt ) ;
997
1074
998
- if ( i == 0 && ! UsePolachAdhesion )
1075
+ if ( i == 0 && ! Axles . UsePolachAdhesion )
999
1076
{
1000
1077
if ( k1 . Item1 * dt > Math . Max ( ( Math . Abs ( SlipSpeedMpS ) - 1 ) * 10 , 1 ) / 100 )
1001
1078
{
@@ -1038,8 +1115,7 @@ void Integrate(float elapsedClockSeconds)
1038
1115
/// <param name="elapsedSeconds"></param>
1039
1116
public virtual void Update ( float elapsedSeconds )
1040
1117
{
1041
- UsePolachAdhesion = AdhesionPrecision . IsPrecisionHigh ( elapsedSeconds ) ;
1042
- if ( UsePolachAdhesion )
1118
+ if ( Axles . UsePolachAdhesion )
1043
1119
{
1044
1120
forceToAccelerationFactor = WheelRadiusM * WheelRadiusM / totalInertiaKgm2 ;
1045
1121
@@ -1143,55 +1219,6 @@ public virtual void Update(float elapsedSeconds)
1143
1219
}
1144
1220
}
1145
1221
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
-
1195
1222
class PolachCalculator
1196
1223
{
1197
1224
Axle Axle ;
0 commit comments