Skip to content

Commit a78797f

Browse files
committed
Update derailment functionality
1 parent 1ac9e37 commit a78797f

File tree

3 files changed

+120
-4
lines changed

3 files changed

+120
-4
lines changed

Source/Orts.Simulation/Simulation/RollingStocks/MSTSWagon.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,28 @@ public virtual void LoadFromWagFile(string wagFilePath)
500500
}
501501
}
502502

503+
// Should always be at least one bogie on rolling stock. If is zero then NaN error occurs.
504+
if (WagonNumBogies == 0)
505+
{
506+
WagonNumBogies = 1;
507+
}
508+
509+
// Set wheel flange parameters to default values.
510+
if (MaximumWheelFlangeAngleRad == 0)
511+
{
512+
MaximumWheelFlangeAngleRad = 1.09956f; // Default = 63.36 deg.
513+
}
514+
else
515+
{
516+
const float convertDegtoRad = 0.01745329252f;
517+
MaximumWheelFlangeAngleRad = convertDegtoRad * MaximumWheelFlangeAngleRad; // Assume that input has been in degrees - TO ADD - appropriate input parameters
518+
}
519+
520+
if (WheelFlangeLengthM == 0)
521+
{
522+
WheelFlangeLengthM = 0.026194f; // Height = 1.031in
523+
}
524+
503525
// Initialise steam heat parameters
504526
if (TrainHeatBoilerWaterUsageGalukpH == null) // If no table entered in WAG file, then use the default table
505527
{
@@ -1015,6 +1037,8 @@ public virtual void Parse(string lowercasetoken, STFReader stf)
10151037
case "wagon(ortslengthairhose": CarAirHoseLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
10161038
case "wagon(ortshorizontallengthairhose": CarAirHoseHorizontalLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
10171039
case "wagon(ortslengthcouplerface": CarCouplerFaceLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
1040+
case "wagon(ortswheelflangelength": WheelFlangeLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
1041+
case "wagon(ortsmaximumwheelflangeangle": MaximumWheelFlangeAngleRad = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
10181042
case "wagon(ortstrackgauge":
10191043
stf.MustMatch("(");
10201044
TrackGaugeM = stf.ReadFloat(STFReader.UNITS.Distance, null);
@@ -1472,6 +1496,8 @@ public virtual void Copy(MSTSWagon copy)
14721496
CarCouplerFaceLengthM = copy.CarCouplerFaceLengthM;
14731497
CarAirHoseLengthM = copy.CarAirHoseLengthM;
14741498
CarAirHoseHorizontalLengthM = copy.CarAirHoseHorizontalLengthM;
1499+
MaximumWheelFlangeAngleRad = copy.MaximumWheelFlangeAngleRad;
1500+
WheelFlangeLengthM = copy.WheelFlangeLengthM;
14751501
AuxTenderWaterMassKG = copy.AuxTenderWaterMassKG;
14761502
TenderWagonMaxCoalMassKG = copy.TenderWagonMaxCoalMassKG;
14771503
TenderWagonMaxWaterMassKG = copy.TenderWagonMaxWaterMassKG;
@@ -1710,6 +1736,11 @@ public override void Save(BinaryWriter outf)
17101736

17111737
outf.Write(WheelBrakeSlideProtectionActive);
17121738
outf.Write(WheelBrakeSlideProtectionTimerS);
1739+
outf.Write(AngleOfAttackRad);
1740+
outf.Write(DerailClimbDistanceM);
1741+
outf.Write(DerailPossible);
1742+
outf.Write(DerailExpected);
1743+
outf.Write(DerailElapsedTimeS);
17131744

17141745
base.Save(outf);
17151746
}
@@ -1757,6 +1788,11 @@ public override void Restore(BinaryReader inf)
17571788

17581789
WheelBrakeSlideProtectionActive = inf.ReadBoolean();
17591790
WheelBrakeSlideProtectionTimerS = inf.ReadInt32();
1791+
AngleOfAttackRad = inf.ReadSingle();
1792+
DerailClimbDistanceM = inf.ReadSingle();
1793+
DerailPossible = inf.ReadBoolean();
1794+
DerailExpected = inf.ReadBoolean();
1795+
DerailElapsedTimeS = inf.ReadSingle();
17601796

17611797
base.Restore(inf);
17621798
}

Source/Orts.Simulation/Simulation/RollingStocks/TrainCar.cs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,14 @@ public static Interpolator SteamHeatBoilerFuelUsageGalukpH()
196196
public float CarBodyLengthM;
197197
public float CarCouplerFaceLengthM;
198198
public float DerailmentCoefficient;
199+
public float NadalDerailmentCoefficient;
200+
public float MaximumWheelFlangeAngleRad;
201+
public float WheelFlangeLengthM;
202+
public float AngleOfAttackRad;
203+
public float DerailClimbDistanceM;
204+
public bool DerailPossible = false;
205+
public bool DerailExpected = false;
206+
public float DerailElapsedTimeS;
199207

200208
public float MaxHandbrakeForceN;
201209
public float MaxBrakeForceN = 89e3f;
@@ -852,7 +860,7 @@ public virtual void Update(float elapsedClockSeconds)
852860
UpdateCurveForce(elapsedClockSeconds);
853861
UpdateTunnelForce();
854862
UpdateBrakeSlideCalculation();
855-
UpdateTrainDerailmentRisk();
863+
UpdateTrainDerailmentRisk(elapsedClockSeconds);
856864

857865
// acceleration
858866
if (elapsedClockSeconds > 0.0f)
@@ -1173,9 +1181,24 @@ public virtual void UpdateTunnelForce()
11731181
///
11741182
/// This section calculates the coupler angle behind the current car (ie the rear coupler on this car and the front coupler on the following car. The coupler angle will be used for
11751183
/// coupler automation as well as calculating Lateral forces on the car.
1184+
///
1185+
/// In addition Chapter 2 - Flange Climb Derailment Criteria of the TRB’s Transit Cooperative Research Program (TCRP) Report 71, examines flange climb derailment criteria for transit
1186+
/// vehicles that include lateral-to-vertical ratio limits and a corresponding flange-climb-distance limit. The report also includes guidance to transit agencies on wheel and rail
1187+
/// maintenance practices.
1188+
///
1189+
/// Some of the concepts described in this publication have also been used to calculate the derailment likelihood.
1190+
///
1191+
/// https://www.nap.edu/read/13841/chapter/4
1192+
///
1193+
/// It should be noted that car derailment is a very complex process that is impacted by many diferent factors, including the track structure and train conditions. To model all of
1194+
/// these factors is not practical so only some of the key factors are considered. For eaxmple, wheel wear may determine whether a particular car will derial or not. So the same
1195+
/// type of car can either derail or not under similar circumstances.
1196+
///
1197+
/// Hence these calculations provide a "generic" approach to determining whether a car will derial or not.
1198+
///
11761199
/// </summary>
11771200

1178-
public void UpdateTrainDerailmentRisk()
1201+
public void UpdateTrainDerailmentRisk(float elapsedClockSeconds)
11791202
{
11801203
// Calculate coupler angle when travelling around curve
11811204
// To achieve an accurate coupler angle calculation the following length need to be calculated. These values can be included in the ENG/WAG file for greatest accuracy, or alternatively OR will
@@ -1438,14 +1461,71 @@ public void UpdateTrainDerailmentRisk()
14381461
DerailmentCoefficient *= 2.0f;
14391462
}
14401463

1464+
var wagonAdhesion = Train.WagonCoefficientFriction;
1465+
1466+
// Calculate Nadal derailment coefficient limit
1467+
NadalDerailmentCoefficient = ((float) Math.Tan(MaximumWheelFlangeAngleRad) - wagonAdhesion) / (1f + wagonAdhesion * (float) Math.Tan(MaximumWheelFlangeAngleRad));
1468+
1469+
// Calculate Angle of Attack - AOA = sin-1(2 * bogie wheel base / curve radius)
1470+
AngleOfAttackRad = (float)Math.Asin(2 * RigidWheelBaseM / CurrentCurveRadius);
1471+
var angleofAttackmRad = AngleOfAttackRad * 1000f; // Convert to micro radians
1472+
1473+
// Calculate the derail climb distance - uses the general form equation 2.4 from the above publication
1474+
var parameterA_1 = ((100 / (-1.9128f * MathHelper.ToDegrees(MaximumWheelFlangeAngleRad) + 146.56f)) + 3.1f) * Me.ToIn(WheelFlangeLengthM);
1475+
1476+
var parameterA_2 = (1.0f / (-0.0092f * Math.Pow(MathHelper.ToDegrees(MaximumWheelFlangeAngleRad), 2) + 1.2125f * MathHelper.ToDegrees(MaximumWheelFlangeAngleRad) - 39.031f)) + 1.23f;
1477+
1478+
var parameterA = parameterA_1 + parameterA_2;
1479+
1480+
var parameterB_1 = ((10f / (-21.157f * Me.ToIn(WheelFlangeLengthM) + 2.1052f)) + 0.05f) * MathHelper.ToDegrees(MaximumWheelFlangeAngleRad);
1481+
1482+
var parameterB_2 = (10 / (0.2688f * Me.ToIn(WheelFlangeLengthM) - 0.0266f)) - 5f;
1483+
1484+
var parameterB = parameterB_1 + parameterB_2;
1485+
1486+
DerailClimbDistanceM = Me.FromFt( (float)((parameterA * parameterB * Me.ToIn(WheelFlangeLengthM)) / ((angleofAttackmRad + (parameterB * Me.ToIn(WheelFlangeLengthM))))) );
1487+
1488+
// calculate the time taken to travel the derail climb distance
1489+
var derailTimeS = AbsSpeedMpS / DerailClimbDistanceM;
1490+
1491+
// Set indication that a derail may occur
1492+
if (DerailmentCoefficient > NadalDerailmentCoefficient)
1493+
{
1494+
DerailPossible = true;
1495+
}
1496+
else
1497+
{
1498+
DerailPossible = false;
1499+
}
1500+
1501+
// If derail climb time exceeded, then derail happens
1502+
if (DerailPossible && DerailElapsedTimeS > derailTimeS)
1503+
{
1504+
DerailExpected = true;
1505+
Simulator.Confirmer.Message(ConfirmLevel.Warning, Simulator.Catalog.GetStringFmt("Car {0} has derailed on the curve.", CarID));
1506+
1507+
}
1508+
else if (DerailPossible)
1509+
{
1510+
DerailElapsedTimeS += elapsedClockSeconds;
1511+
}
1512+
else
1513+
{
1514+
DerailElapsedTimeS = 0; // Reset timer if derail is not possible
1515+
}
14411516
}
14421517
else
14431518
{
14441519
TotalWagonLateralDerailForceN = 0;
14451520
TotalWagonVerticalDerailForceN = 0;
14461521
DerailmentCoefficient = 0;
1522+
DerailExpected = false;
1523+
DerailPossible = false;
1524+
DerailElapsedTimeS = 0;
14471525
}
14481526

1527+
1528+
14491529
if (TotalWagonLateralDerailForceN > TotalWagonVerticalDerailForceN)
14501530
{
14511531
BuffForceExceeded = true;
@@ -1690,7 +1770,7 @@ public virtual void UpdateCurveSpeedLimit()
16901770
}
16911771
else
16921772
{
1693-
Simulator.Confirmer.Message(ConfirmLevel.Warning, Simulator.Catalog.GetStringFmt("You are travelling too fast for this curve. Slow down, your passengers in car {0} are feeling uncomfortable. The recommended speed for this curve is {1}", CarID, FormatStrings.FormatSpeedDisplay(MaxSafeCurveSpeedMps, IsMetric))); ;
1773+
Simulator.Confirmer.Message(ConfirmLevel.Warning, Simulator.Catalog.GetStringFmt("You are travelling too fast for this curve. Slow down, your passengers in car {0} are feeling uncomfortable. The recommended speed for this curve is {1}", CarID, FormatStrings.FormatSpeedDisplay(MaxSafeCurveSpeedMps, IsMetric)));
16941774
}
16951775

16961776
if (dbfmaxsafecurvespeedmps != MaxSafeCurveSpeedMps)//Debrief eval

Source/RunActivity/Viewer3D/Popups/HUDWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ void TextPageForceInfo(TableData table)
11391139
TableSetCell(table, 16, car.HUDBrakeSkid ? Viewer.Catalog.GetString("Yes") : Viewer.Catalog.GetString("No"));
11401140
TableSetCell(table, 17, "{0} {1}", FormatStrings.FormatTemperature(car.WheelBearingTemperatureDegC, car.IsMetric, false), car.DisplayWheelBearingTemperatureStatus);
11411141
TableSetCell(table, 18, car.Flipped ? Viewer.Catalog.GetString("Flipped") : "");
1142-
TableSetCell(table, 19, "{0:F2}{1}", car.DerailmentCoefficient, car.DerailmentCoefficient > 1 ? "!!!" : car.DerailmentCoefficient < 1 && car.DerailmentCoefficient > 0.66 ? "???" : "");
1142+
TableSetCell(table, 19, "{0:F2}{1}", car.DerailmentCoefficient, car.DerailExpected ? "!!!" : car.DerailPossible ? "???" : "");
11431143
TableAddLine(table);
11441144

11451145
}

0 commit comments

Comments
 (0)