@@ -2203,8 +2203,6 @@ public override void Update(float elapsedClockSeconds)
22032203 UpdateAuxiliaries(elapsedClockSeconds, absSpeedMpS);
22042204 UpdateSuperHeat();
22052205
2206- TractiveForceN = 0; // reset tractiveforceN in preparation to calculating a new value
2207- MotiveForceN = 0;
22082206 CylinderSteamUsageLBpS = 0;
22092207 CylCockSteamUsageLBpS = 0;
22102208 MeanEffectivePressurePSI = 0;
@@ -2310,41 +2308,11 @@ public override void Update(float elapsedClockSeconds)
23102308 tractiveforcethrottle = throttle;
23112309 }
23122310
2313- UpdateTractiveForce(elapsedClockSeconds, tractiveforcethrottle, 0, 0, i);
2314-
2315- TractiveForceN += SteamEngines[i].TractiveForceN;
2316-
2317- MotiveForceN += SteamEngines[i].AttachedAxle.CompensatedAxleForceN;
2318- }
2311+ UpdateSteamTractiveForce(elapsedClockSeconds, tractiveforcethrottle, 0, 0, i);
23192312
2320- // Find the maximum TE for debug i.e. @ start and full throttle
2321- if (absSpeedMpS < 1.0)
2322- {
2323- if (Math.Abs(TractiveForceN) > absStartTractiveEffortN && Math.Abs(TractiveForceN) < MaxForceN)
2324- {
2325- absStartTractiveEffortN = Math.Abs(TractiveForceN); // update to new maximum TE
2326- }
23272313 }
23282314
2329- DisplayTractiveForceN = TractiveForceN;
2330-
2331- MotiveForceSmoothN.Update(elapsedClockSeconds, MotiveForceN);
2332- MotiveForceSmoothedN = MotiveForceSmoothN.SmoothedValue;
2333- if (float.IsNaN(MotiveForceN))
2334- MotiveForceN = 0;
2335-
2336- DrawBarPullLbsF = N.ToLbf(Math.Abs(MotiveForceSmoothedN) - LocoTenderFrictionForceN); // Locomotive drawbar pull is equal to motive force of locomotive (+ tender) - friction forces of locomotive (+ tender)
2337- DrawBarPullLbsF = MathHelper.Clamp(DrawBarPullLbsF, 0, DrawBarPullLbsF); // clamp value so it doesn't go negative
2338-
2339- DrawbarHorsePowerHP = (DrawBarPullLbsF * MpS.ToMpH(absSpeedMpS)) / 375.0f; // TE in this instance is a maximum, and not at the wheel???
2340- DrawbarHorsePowerHP = MathHelper.Clamp(DrawbarHorsePowerHP, 0, DrawbarHorsePowerHP); // clamp value so it doesn't go negative
2341-
2342-
2343- // Calculate IHP
2344- // IHP = (MEP x CylStroke(ft) x cylArea(sq in) x No Strokes (/min)) / 33000) - this is per cylinder
2345-
2346- IndicatedHorsePowerHP = (N.ToLbf(MotiveForceSmoothedN) * pS.TopH(Me.ToMi(absSpeedMpS))) / 375.0f;
2347- IndicatedHorsePowerHP = MathHelper.Clamp(IndicatedHorsePowerHP, 0, IndicatedHorsePowerHP);
2315+ UpdateTractiveForce(elapsedClockSeconds, ThrottlePercent / 100f, AbsSpeedMpS, AbsWheelSpeedMpS);
23482316
23492317 #endregion
23502318
@@ -5130,21 +5098,20 @@ private void UpdateCylinders(float elapsedClockSeconds, float throttle, float cu
51305098 SteamReleasePressure_AtmPSI = SteamEngines[numberofengine].Pressure_c_AtmPSI; // for steam and smoke effects
51315099
51325100 }
5133- protected override void UpdateTractiveForce(float elapsedClockSeconds, float locomotivethrottle, float AbsSpeedMpS, float AbsWheelSpeedMpS, int numberofengine)
5134- {
5135- // Pass force and power information to MSTSLocomotive file by overriding corresponding method there
51365101
5137- // Set Max Power equal to max IHP
5138- MaxPowerW = W.FromHp(SteamEngines[numberofengine].MaxIndicatedHorsePowerHP);
51395102
5140- // Set maximum force for the locomotive
5141- MaxForceN = N.FromLbf(SteamEngines[numberofengine].MaxTractiveEffortLbf * CylinderEfficiencyRate);
5142-
5143- // Set Max Velocity of locomotive
5144- MaxSpeedMpS = Me.FromMi(pS.FrompH(MaxLocoSpeedMpH)); // Note this is not the true max velocity of the locomotive, but the speed at which max HP is reached
5103+ /// <summary>
5104+ /// Calculate the tractive forces for each steam engine
5105+ /// </summary>
5106+ private void UpdateSteamTractiveForce(float elapsedClockSeconds, float locomotivethrottle, float AbsSpeedMpS, float AbsWheelSpeedMpS, int numberofengine)
5107+ {
51455108
51465109 #region - Steam Adhesion Model Input for Steam Locomotives
51475110
5111+ // Caculate the current piston speed - purely for display purposes at the moment
5112+ // Piston Speed (Ft p Min) = (Stroke length x 2) x (Ft in Mile x Train Speed (mph) / ( Circum of Drv Wheel x 60))
5113+ SteamEngines[numberofengine].PistonSpeedFtpMin = Me.ToFt(pS.TopM(SteamEngines[numberofengine].CylindersStrokeM * 2.0f * DrvWheelRevRpS)) * SteamGearRatio;
5114+
51485115 // Based upon information presented on pg 276 of "Locomotive Operation - A Technical and Practical Analysis" by G. R. Henderson -
51495116 // https://archive.org/details/locomotiveoperat00hend/page/276/mode/2up
51505117 // At its simplest slip occurs when the wheel tangential force exceeds the static frictional force
@@ -5459,11 +5426,7 @@ protected override void UpdateTractiveForce(float elapsedClockSeconds, float loc
54595426 else // Adjust tractive force if "simple" friction is used, or is a geared steam locomotive
54605427 {
54615428 // This section updates the force calculations and maintains them at the current values.
5462-
5463- // Caculate the current piston speed - purely for display purposes at the moment
5464- // Piston Speed (Ft p Min) = (Stroke length x 2) x (Ft in Mile x Train Speed (mph) / ( Circum of Drv Wheel x 60))
5465- PistonSpeedFtpMin = Me.ToFt(pS.TopM(SteamEngines[numberofengine].CylindersStrokeM * 2.0f * DrvWheelRevRpS)) * SteamGearRatio;
5466-
5429+
54675430 if (SteamEngineType == SteamEngineTypes.Compound)
54685431 {
54695432 // Calculate tractive effort if set for compounding - tractive effort in each cylinder will need to be calculated
@@ -5482,21 +5445,20 @@ protected override void UpdateTractiveForce(float elapsedClockSeconds, float loc
54825445 // Calculate IHP
54835446 // IHP = (MEP x Speed (mph)) / 375.0) - this is per cylinder
54845447
5485- HPIndicatedHorsePowerHP = (HPTractiveEffortLbsF * pS.TopH(Me.ToMi(absSpeedMpS))) / 375.0f;
5486- LPIndicatedHorsePowerHP = (LPTractiveEffortLbsF * pS.TopH(Me.ToMi(absSpeedMpS))) / 375.0f;
5448+ SteamEngines[numberofengine]. HPIndicatedHorsePowerHP = (HPTractiveEffortLbsF * pS.TopH(Me.ToMi(absSpeedMpS))) / 375.0f;
5449+ SteamEngines[numberofengine]. LPIndicatedHorsePowerHP = (LPTractiveEffortLbsF * pS.TopH(Me.ToMi(absSpeedMpS))) / 375.0f;
54875450
54885451 float WheelRevs = pS.TopM(DrvWheelRevRpS);
5489- IndicatedHorsePowerHP += HPIndicatedHorsePowerHP + LPIndicatedHorsePowerHP;
5490- IndicatedHorsePowerHP = MathHelper.Clamp(IndicatedHorsePowerHP, 0, IndicatedHorsePowerHP);
5452+
54915453 }
54925454 else // if simple or geared locomotive calculate tractive effort
54935455 {
54945456
54955457 // If the steam piston is exceeding the maximum design piston rate then decrease efficiency of mep
5496- if (SteamEngineType == SteamEngineTypes.Geared && PistonSpeedFtpMin > MaxSteamGearPistonRateFtpM)
5458+ if (SteamEngineType == SteamEngineTypes.Geared && SteamEngines[numberofengine]. PistonSpeedFtpMin > MaxSteamGearPistonRateFtpM)
54975459 {
54985460 // use straight line curve to decay mep to zero by 2 x piston speed
5499- float pistonforcedecay = 1.0f - (1.0f / MaxSteamGearPistonRateFtpM) * (PistonSpeedFtpMin - MaxSteamGearPistonRateFtpM);
5461+ float pistonforcedecay = 1.0f - (1.0f / MaxSteamGearPistonRateFtpM) * (SteamEngines[numberofengine]. PistonSpeedFtpMin - MaxSteamGearPistonRateFtpM);
55005462 pistonforcedecay = MathHelper.Clamp(pistonforcedecay, 0.0f, 1.0f); // Clamp decay within bounds
55015463
55025464 SteamEngines[numberofengine].MeanEffectivePressurePSI *= pistonforcedecay; // Decrease mep once piston critical speed is exceeded
@@ -5575,22 +5537,88 @@ protected override void UpdateTractiveForce(float elapsedClockSeconds, float loc
55755537 IsCritTELimit = false; // Reset flag if limiting TE
55765538 }
55775539
5578- ApplyDirectionToTractiveForce(ref SteamEngines[numberofengine].TractiveForceN);
5540+ SteamEngines[numberofengine].AttachedAxle.DriveForceN = SteamEngines[numberofengine].TractiveForceN;
5541+ }
5542+
5543+ /// <summary>
5544+ /// Update the tractive force for the complete steam locomotive
5545+ /// </summary>
5546+ protected override void UpdateTractiveForce(float elapsedClockSeconds, float locomotivethrottle, float AbsSpeedMpS, float AbsWheelSpeedMpS)
5547+ {
5548+ TractiveForceN = 0; // reset tractiveforceN in preparation to calculating a new value
5549+ MotiveForceN = 0;
5550+ IndicatedHorsePowerHP = 0;
5551+ PistonSpeedFtpMin = 0;
5552+
5553+ // Update tractive effort across all steam engines
5554+ for (int i = 0; i < SteamEngines.Count; i++)
5555+ {
5556+ TractiveForceN += SteamEngines[i].TractiveForceN;
5557+
5558+ MotiveForceN += SteamEngines[i].AttachedAxle.CompensatedAxleForceN;
5559+
5560+ // Set Max Power equal to max IHP
5561+ MaxPowerW = W.FromHp(SteamEngines[i].MaxIndicatedHorsePowerHP);
5562+
5563+ // Set maximum force for the locomotive
5564+ MaxForceN = N.FromLbf(SteamEngines[i].MaxTractiveEffortLbf * CylinderEfficiencyRate);
5565+
5566+ if (SteamEngineType == SteamEngineTypes.Compound)
5567+ {
5568+ IndicatedHorsePowerHP += HPIndicatedHorsePowerHP + LPIndicatedHorsePowerHP;
5569+ IndicatedHorsePowerHP = MathHelper.Clamp(IndicatedHorsePowerHP, 0, IndicatedHorsePowerHP);
5570+ }
5571+ else
5572+ {
5573+ IndicatedHorsePowerHP += SteamEngines[i].IndicatedHorsePowerHP;
5574+ }
5575+
5576+ //TODO - identify the maximum value for display??
5577+ PistonSpeedFtpMin = SteamEngines[0].PistonSpeedFtpMin;
5578+
5579+ }
5580+
5581+ ApplyDirectionToTractiveForce(ref TractiveForceN);
5582+
5583+ // Find the maximum TE for debug i.e. @ start and full throttle
5584+ if (absSpeedMpS < 1.0)
5585+ {
5586+ if (Math.Abs(TractiveForceN) > absStartTractiveEffortN && Math.Abs(TractiveForceN) < MaxForceN)
5587+ {
5588+ absStartTractiveEffortN = Math.Abs(TractiveForceN); // update to new maximum TE
5589+ }
5590+ }
5591+
5592+ DisplayTractiveForceN = TractiveForceN;
5593+
5594+ MotiveForceSmoothN.Update(elapsedClockSeconds, MotiveForceN);
5595+ MotiveForceSmoothedN = MotiveForceSmoothN.SmoothedValue;
5596+ if (float.IsNaN(MotiveForceN))
5597+ MotiveForceN = 0;
5598+
5599+ DrawBarPullLbsF = N.ToLbf(Math.Abs(MotiveForceSmoothedN) - LocoTenderFrictionForceN); // Locomotive drawbar pull is equal to motive force of locomotive (+ tender) - friction forces of locomotive (+ tender)
5600+ DrawBarPullLbsF = MathHelper.Clamp(DrawBarPullLbsF, 0, DrawBarPullLbsF); // clamp value so it doesn't go negative
5601+
5602+ DrawbarHorsePowerHP = (DrawBarPullLbsF * MpS.ToMpH(absSpeedMpS)) / 375.0f; // TE in this instance is a maximum, and not at the wheel???
5603+ DrawbarHorsePowerHP = MathHelper.Clamp(DrawbarHorsePowerHP, 0, DrawbarHorsePowerHP); // clamp value so it doesn't go negative
5604+
5605+ // Set Max Velocity of locomotive
5606+ MaxSpeedMpS = Me.FromMi(pS.FrompH(MaxLocoSpeedMpH)); // Note this is not the true max velocity of the locomotive, but the speed at which max HP is reached
5607+
5608+
55795609
55805610 // Set tractive force to zero if throttle is closed
55815611 if (locomotivethrottle < 0.001)
55825612 {
5583- SteamEngines[numberofengine]. TractiveForceN = 0;
5613+ TractiveForceN = 0;
55845614 }
55855615
5586-
5587- SteamEngines[numberofengine].AttachedAxle.DriveForceN = SteamEngines[numberofengine].TractiveForceN;
55885616 }
55895617
5590- /// <summary>
5591- /// Normalise crank angle so that it is a value between 0 and 360 starting at the real crank angle difference
5592- /// </summary>
5593- private float NormalisedCrankAngle(int cylinderNumber)
5618+ /// <summary>
5619+ /// Normalise crank angle so that it is a value between 0 and 360 starting at the real crank angle difference
5620+ /// </summary>
5621+ private float NormalisedCrankAngle(int cylinderNumber)
55945622 {
55955623 float normalisedCrankAngleRad = (float)MathHelper.WrapAngle((float)LocomotiveAxles[0].AxlePositionRad + WheelCrankAngleDiffRad[cylinderNumber]);
55965624
0 commit comments