Skip to content

Commit 4b4afe3

Browse files
committed
Avoids wheelslip when the precision level is changed.
Also moved IsPrecisionHigh() from Axle class to Axles.
1 parent a325039 commit 4b4afe3

File tree

2 files changed

+92
-90
lines changed
  • Source
    • Orts.Simulation/Simulation/RollingStocks/SubSystems/PowerTransmissions
    • RunActivity/Viewer3D/Popups

2 files changed

+92
-90
lines changed

Source/Orts.Simulation/Simulation/RollingStocks/SubSystems/PowerTransmissions/Axle.cs

Lines changed: 91 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

Source/RunActivity/Viewer3D/Popups/HUDWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ void TextPageForceInfo(TableData table)
10801080
if (mstsLocomotive.AdvancedAdhesionModel)
10811081
{
10821082
var text = Viewer.Catalog.GetString("(Advanced adhesion model)");
1083-
if (Axle.UsePolachAdhesion == false) text += "???";
1083+
if (Axles.UsePolachAdhesion == false) text += "???";
10841084
TableAddLine(table, text);
10851085
int row0 = table.CurrentRow;
10861086
TableSetCell(table, table.CurrentRow++, table.CurrentLabelColumn, Viewer.Catalog.GetString("Wheel slip (Thres)"));

0 commit comments

Comments
 (0)