Skip to content

Commit

Permalink
Merge pull request #835 from peternewell/brake_shoe#1
Browse files Browse the repository at this point in the history
Correct Brake Shoe Force Calculation
  • Loading branch information
cjakeman committed Jun 18, 2023
2 parents baefb6b + e632edb commit a80e868
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 148 deletions.
28 changes: 28 additions & 0 deletions Source/Documentation/Manual/physics.rst
Expand Up @@ -2739,6 +2739,34 @@ These changes introduce an extra challenge to train braking, but provide a more

For example, in a lot of normal Westinghouse brake systems, a minimum pressure reduction was applied by moving the brake controller to the LAP position. Typically Westinghouse recommended values of between 7 and 10 psi.

Brake Shoe Force
----------------

As indicated above the ``MaxBrakeForce`` parameter in the WAG file is the actual force applied to the wheel after reduction by the friction coefficient, often railway companies will provide an Net Braking Ratio (NBR) value to
specify the amount of force to be applied to the brake shoe. This force is then reduced by the brake shoe CoF to determine the actual force applied to the wheel.

To facilitate the direct usage of the NBR value in the WAG file, the following parameters can be used. NB: When using these parameters the ``MaxBrakeForce`` parameter is not required.

Brake Shoe Force - This is the current change being implemented. The following changes and parameter are included.

``ORTSMaxBrakeShoeForce`` - the force applied to the brake shoe is the main braking force.

``ORTSBrakeShoeType`` - this defines a number of different brake shoe types and curves. To provide a more realistic representation of the braking force the default CoF curves are 2D, ie
they are impacted by both the speed and Brake Shoe Force. Typically ``ORTSBrakeShoeType`` will have one of the following keywords included -
``Cast_Iron`` - cast iron brake shoe, 2D as above, ``Hi_Friction_Composite`` - high friction composite shoe, 2D as above, ``User_Defined`` - is a user defined curve
using the ORTSBrakeShoeFriction parameter, 1D (ie, speed only, see above section for the parameter format).

``ORTSNumberCarBrakeShoes`` - to facilitate the operation of the default 2D curves above it is necessary to configure the number of brake shoes for each car.

Whilst OR will attempt to set some defaults if parameters are left out, the most realistic operation will be achieved by using all the relevant parameters.

The following two legacy arrangements can be used as an alternative to the above method,

- Legacy #1 - legacy arrangements using MaxBrakeForce on its own will remain unchanged. This in effect is an old MSTS file.

- Legacy #2 - where MaxBrakeForce and ORTSBrakeShoeFriction have been set, legacy operation will remain unchanged.


Train Brake Pipe Losses
-----------------------

Expand Down
4 changes: 2 additions & 2 deletions Source/Orts.Simulation/Simulation/Physics/Train.cs
Expand Up @@ -5465,7 +5465,7 @@ public void UpdateCarSpeeds(float elapsedTime)
if (car is MSTSLocomotive) locoBehind = false;
if (car.SpeedMpS > 0)
{
car.SpeedMpS += car.TotalForceN / car.MassKG * elapsedTime;
car.SpeedMpS += (car.TotalForceN / car.MassKG) * elapsedTime;
if (car.SpeedMpS < 0)
car.SpeedMpS = 0;
// If car is manual braked, air_piped car or vacuum_piped, and preceeding car is at stop, then set speed to zero.
Expand All @@ -5478,7 +5478,7 @@ public void UpdateCarSpeeds(float elapsedTime)
}
else if (car.SpeedMpS < 0)
{
car.SpeedMpS += car.TotalForceN / car.MassKG * elapsedTime;
car.SpeedMpS += (car.TotalForceN / car.MassKG) * elapsedTime;
if (car.SpeedMpS > 0)
car.SpeedMpS = 0;
// If car is manual braked, air_piped car or vacuum_piped, and preceeding car is at stop, then set speed to zero.
Expand Down
Expand Up @@ -1828,7 +1828,7 @@ public void DynamicBrakeBlending(float elapsedClockSeconds)
{
if (DynamicBrakeBlendingForceMatch)
{
float diff = target * MaxBrakeForceN - DynamicBrakeForceN;
float diff = target * FrictionBrakeBlendingMaxForceN - DynamicBrakeForceN;
float threshold = 100;
if (diff > threshold && DynamicBrakeIntervention < 1)
DynamicBrakeIntervention = Math.Min(DynamicBrakeIntervention + elapsedClockSeconds, 1);
Expand Down
Expand Up @@ -6388,6 +6388,10 @@ public override string GetDebugStatus()
FormatStrings.FormatEnergy(W.FromBTUpS(BoilerHeatBTU), IsMetric)
);

// calculate values for display, so that display value doesn't go negative
var superheatTempDisplayC = C.FromF(CurrentSuperheatTempF);
superheatTempDisplayC = MathHelper.Clamp(superheatTempDisplayC, 0.0f, C.FromF(MaxSuperheatRefTempF));

status.AppendFormat("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}\n",
Simulator.Catalog.GetString("Temp:"),
Simulator.Catalog.GetString("Flue"),
Expand All @@ -6397,7 +6401,8 @@ public override string GetDebugStatus()
Simulator.Catalog.GetString("MaxSupH"),
FormatStrings.FormatTemperature(C.FromF(MaxSuperheatRefTempF), IsMetric, false),
Simulator.Catalog.GetString("CurSupH"),
FormatStrings.FormatTemperature(C.FromF(CurrentSuperheatTempF), IsMetric, false));
FormatStrings.FormatTemperature(superheatTempDisplayC, IsMetric, false)
);

status.AppendFormat("\n\t\t === {0} === \t\t{1}/{2}\n",
Simulator.Catalog.GetString("Steam Usage"),
Expand Down Expand Up @@ -6838,8 +6843,8 @@ public override string GetDebugStatus()
);
}

if (Simulator.UseAdvancedAdhesion && !Simulator.Settings.SimpleControlPhysics && SteamEngineType != SteamEngineTypes.Geared)
// Only display slip monitor if advanced adhesion is set and simplecontrols/physics not set
if (Simulator.UseAdvancedAdhesion && !Simulator.Settings.SimpleControlPhysics && SteamEngineType != SteamEngineTypes.Geared)
// Only display slip monitor if advanced adhesion is set and simplecontrols/physics not set
{
status.AppendFormat("\n\t\t === {0} === \n", Simulator.Catalog.GetString("Slip Monitor"));
status.AppendFormat("{0}\t{1}\t{2:N0}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8:N2}\t{9}\t{10}\t{11:N2}\t{12}\t{13}\t{14:N1}\n",
Expand Down
130 changes: 81 additions & 49 deletions Source/Orts.Simulation/Simulation/RollingStocks/MSTSWagon.cs
Expand Up @@ -86,8 +86,7 @@ public class MSTSWagon : TrainCar

public bool GenericItem1;
public bool GenericItem2;

Interpolator BrakeShoeFrictionFactor; // Factor of friction for wagon brake shoes

const float WaterLBpUKG = 10.0f; // lbs of water in 1 gal (uk)
float TempMassDiffRatio;

Expand Down Expand Up @@ -600,8 +599,54 @@ public virtual void LoadFromWagFile(string wagFilePath)

// Initialise key wagon parameters
MassKG = InitialMassKG;

MaxHandbrakeForceN = InitialMaxHandbrakeForceN;
MaxBrakeForceN = InitialMaxBrakeForceN;

FrictionBrakeBlendingMaxForceN = InitialMaxBrakeForceN; // set the value of braking when blended with dynamic brakes

if (MaxBrakeShoeForceN != 0 && BrakeShoeType != BrakeShoeTypes.Unknown)
{
MaxBrakeForceN = MaxBrakeShoeForceN;
}
else
{
MaxBrakeForceN = InitialMaxBrakeForceN;

if (Simulator.Settings.VerboseConfigurationMessages)
{
Trace.TraceInformation("Unknown BrakeShoeType set to OR default (Cast Iron) with a MaxBrakeForce of {0}", FormatStrings.FormatForce(MaxBrakeForceN, IsMetric));
}
}

// Initialise number of brake shoes per wagon
if (NumberCarBrakeShoes == 0 && WagonType == WagonTypes.Engine)
{
var LocoTest = Simulator.PlayerLocomotive as MSTSLocomotive;

if (LocoTest != null && LocoTest.DriveWheelOnlyBrakes)
{
NumberCarBrakeShoes = LocoNumDrvAxles * 4; // Assume 4 brake shoes per axle on drive wheels only
}
else
{
NumberCarBrakeShoes = (LocoNumDrvAxles * 4) + (WagonNumAxles * 4); // Assume 4 brake shoes per axle on all wheels
}

if (Simulator.Settings.VerboseConfigurationMessages && (BrakeShoeType == BrakeShoeTypes.Cast_Iron || BrakeShoeType == BrakeShoeTypes.High_Friction_Composite))
{
Trace.TraceInformation("Number of Locomotive Brakeshoes set to default value of {0}", NumberCarBrakeShoes);
}
}
else if (NumberCarBrakeShoes == 0)
{
NumberCarBrakeShoes = WagonNumAxles * 4; // Assume 4 brake shoes per axle

if (Simulator.Settings.VerboseConfigurationMessages && (BrakeShoeType == BrakeShoeTypes.Cast_Iron || BrakeShoeType == BrakeShoeTypes.High_Friction_Composite))
{
Trace.TraceInformation("Number of Wagon Brakeshoes set to default value of {0}", NumberCarBrakeShoes);
}
}

CentreOfGravityM = InitialCentreOfGravityM;

if (FreightAnimations != null)
Expand Down Expand Up @@ -673,7 +718,11 @@ public virtual void LoadFromWagFile(string wagFilePath)
LoadEmptyWagonFrontalAreaM2 = WagonFrontalAreaM2;
}

if (FreightAnimations.EmptyMaxBrakeForceN > 0)
if (FreightAnimations.EmptyMaxBrakeShoeForceN > 0)
{
LoadEmptyMaxBrakeForceN = FreightAnimations.EmptyMaxBrakeShoeForceN;
}
else if (FreightAnimations.EmptyMaxBrakeForceN > 0)
{
LoadEmptyMaxBrakeForceN = FreightAnimations.EmptyMaxBrakeForceN;
}
Expand Down Expand Up @@ -749,8 +798,11 @@ public virtual void LoadFromWagFile(string wagFilePath)
LoadFullWagonFrontalAreaM2 = WagonFrontalAreaM2;
}


if (FreightAnimations.FullPhysicsStaticOne.FullStaticMaxBrakeForceN > 0)
if (FreightAnimations.FullPhysicsStaticOne.FullStaticMaxBrakeShoeForceN > 0)
{
LoadFullMaxBrakeForceN = FreightAnimations.FullPhysicsStaticOne.FullStaticMaxBrakeShoeForceN;
}
else if (FreightAnimations.FullPhysicsStaticOne.FullStaticMaxBrakeForceN > 0)
{
LoadFullMaxBrakeForceN = FreightAnimations.FullPhysicsStaticOne.FullStaticMaxBrakeForceN;
}
Expand Down Expand Up @@ -836,8 +888,11 @@ public virtual void LoadFromWagFile(string wagFilePath)
LoadFullWagonFrontalAreaM2 = WagonFrontalAreaM2;
}


if (FreightAnimations.FullPhysicsContinuousOne.FullMaxBrakeForceN > 0)
if (FreightAnimations.FullPhysicsContinuousOne.FullMaxBrakeShoeForceN > 0)
{
LoadFullMaxBrakeForceN = FreightAnimations.FullPhysicsContinuousOne.FullMaxBrakeShoeForceN;
}
else if (FreightAnimations.FullPhysicsContinuousOne.FullMaxBrakeForceN > 0)
{
LoadFullMaxBrakeForceN = FreightAnimations.FullPhysicsContinuousOne.FullMaxBrakeForceN;
}
Expand Down Expand Up @@ -957,7 +1012,6 @@ public virtual void LoadFromWagFile(string wagFilePath)
Trace.TraceInformation("Empty Values = Brake {0} Handbrake {1} DavisA {2} DavisB {3} DavisC {4} CoGY {5}", LoadEmptyMaxBrakeForceN, LoadEmptyMaxHandbrakeForceN, LoadEmptyORTSDavis_A, LoadEmptyORTSDavis_B, LoadEmptyORTSDavis_C, LoadEmptyCentreOfGravityM_Y);
Trace.TraceInformation("Full Values = Brake {0} Handbrake {1} DavisA {2} DavisB {3} DavisC {4} CoGY {5}", LoadFullMaxBrakeForceN, LoadFullMaxHandbrakeForceN, LoadFullORTSDavis_A, LoadFullORTSDavis_B, LoadFullORTSDavis_C, LoadFullCentreOfGravityM_Y);
#endif

}

// Determine whether or not to use the Davis friction model. Must come after freight animations are initialized.
Expand Down Expand Up @@ -1134,6 +1188,21 @@ public virtual void Parse(string lowercasetoken, STFReader stf)
case "wagon(ortsbrakeshoefriction": BrakeShoeFrictionFactor = new Interpolator(stf); break;
case "wagon(maxhandbrakeforce": InitialMaxHandbrakeForceN = stf.ReadFloatBlock(STFReader.UNITS.Force, null); break;
case "wagon(maxbrakeforce": InitialMaxBrakeForceN = stf.ReadFloatBlock(STFReader.UNITS.Force, null); break;
case "wagon(ortsmaxbrakeshoeforce": MaxBrakeShoeForceN = stf.ReadFloatBlock(STFReader.UNITS.Force, null); break;
case "wagon(ortsnumbercarbrakeshoes": NumberCarBrakeShoes = stf.ReadIntBlock(null); break;
case "wagon(ortsbrakeshoetype":
stf.MustMatch("(");
var brakeShoeType = stf.ReadString();
try
{
BrakeShoeType = (BrakeShoeTypes)Enum.Parse(typeof(BrakeShoeTypes), brakeShoeType);
}
catch
{
STFException.TraceWarning(stf, "Assumed unknown brake shoe type " + brakeShoeType);
}
break;

case "wagon(ortswheelbrakeslideprotection":
// stf.MustMatch("(");
var brakeslideprotection = stf.ReadFloatBlock(STFReader.UNITS.None, null);
Expand Down Expand Up @@ -1461,6 +1530,7 @@ public virtual void Copy(MSTSWagon copy)
HasPassengerCapacity = copy.HasPassengerCapacity;
WagonType = copy.WagonType;
WagonSpecialType = copy.WagonSpecialType;
BrakeShoeType = copy.BrakeShoeType;
FreightShapeFileName = copy.FreightShapeFileName;
FreightAnimMaxLevelM = copy.FreightAnimMaxLevelM;
FreightAnimMinLevelM = copy.FreightAnimMinLevelM;
Expand Down Expand Up @@ -1503,6 +1573,8 @@ public virtual void Copy(MSTSWagon copy)
InitialMaxBrakeForceN = copy.InitialMaxBrakeForceN;
InitialMaxHandbrakeForceN = copy.InitialMaxHandbrakeForceN;
MaxBrakeForceN = copy.MaxBrakeForceN;
MaxBrakeShoeForceN = copy.MaxBrakeShoeForceN;
NumberCarBrakeShoes = copy.NumberCarBrakeShoes;
MaxHandbrakeForceN = copy.MaxHandbrakeForceN;
WindowDeratingFactor = copy.WindowDeratingFactor;
DesiredCompartmentTempSetpointC = copy.DesiredCompartmentTempSetpointC;
Expand Down Expand Up @@ -3968,46 +4040,6 @@ public override float GetFilledFraction(uint pickupType)
if (FreightAnimations.LoadedOne != null) fraction = FreightAnimations.LoadedOne.LoadPerCent / 100;
return fraction;
}

/// <summary>
/// Returns the Brake shoe coefficient.
/// </summary>

public override float GetUserBrakeShoeFrictionFactor()
{
var frictionfraction = 0.0f;
if ( BrakeShoeFrictionFactor == null)
{
frictionfraction = 0.0f;
}
else
{
frictionfraction = BrakeShoeFrictionFactor[MpS.ToKpH(AbsSpeedMpS)];
}

return frictionfraction;
}

/// <summary>
/// Returns the Brake shoe coefficient at zero speed.
/// </summary>

public override float GetZeroUserBrakeShoeFrictionFactor()
{
var frictionfraction = 0.0f;
if (BrakeShoeFrictionFactor == null)
{
frictionfraction = 0.0f;
}
else
{
frictionfraction = BrakeShoeFrictionFactor[0.0f];
}

return frictionfraction;
}



/// <summary>
/// Starts a continuous increase in controlled value.
Expand Down

0 comments on commit a80e868

Please sign in to comment.