@@ -258,6 +258,10 @@ public class MSTSSteamLocomotive : MSTSLocomotive
258258 // eng file configuration parameters
259259
260260 float BoilerVolumeFT3; // total space in boiler that can hold water and steam
261+ float MSTSBoilerLengthM;
262+ float ORBoilerLengthM;
263+ float BoilerLengthM;
264+ public float GradientBoilerLevelPercent;
261265 public int MSTSNumCylinders = 2; // Number of Cylinders
262266 public float MSTSCylinderStrokeM; // High pressure cylinders
263267 public float MSTSCylinderDiameterM; // High pressure cylinders
@@ -455,7 +459,11 @@ public float TenderFuelMassKG // Decreased by firing and increased
455459 float WaterGlassMinLevel = 0.73f; // min height of water gauge as a fraction of boiler level
456460 float WaterGlassLengthIN = 8.0f; // nominal length of water gauge
457461 float WaterGlassLevelIN; // Water glass level in inches
462+ float ORSteamGaugeGlassHeightM;
463+ float MSTSSteamGaugeGlassHeightM;
464+ float WaterGlassLengthM;
458465 float waterGlassPercent; // Water glass level in percent
466+ bool WaterGlassLevelGradientEnabled = false;
459467 float MEPFactor = 0.7f; // Factor to determine the MEP
460468 float GrateAreaDesignFactor = 500.0f; // Design factor for determining Grate Area
461469 float EvapAreaDesignFactor = 10.0f; // Design factor for determining Evaporation Area
@@ -945,6 +953,10 @@ public override void Parse(string lowercasetoken, STFReader stf)
945953 case "engine(lpcylinderdiameter": MSTSLPCylinderDiameterM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
946954 case "engine(ortscylinderportopening": CylinderPortOpeningFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
947955 case "engine(boilervolume": BoilerVolumeFT3 = stf.ReadFloatBlock(STFReader.UNITS.VolumeDefaultFT3, null); break;
956+ case "engine(boilerlength": MSTSBoilerLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
957+ case "engine(ortsboilerlength": ORBoilerLengthM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
958+ case "engine(steamgaugeglassheight": MSTSSteamGaugeGlassHeightM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
959+ case "engine(ortswatergaugeglassheight": ORSteamGaugeGlassHeightM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
948960 case "engine(maxboilerpressure": MaxBoilerPressurePSI = stf.ReadFloatBlock(STFReader.UNITS.PressureDefaultPSI, null); break;
949961 case "engine(ortsmaxsuperheattemperature": MaxSuperheatRefTempF = stf.ReadFloatBlock(STFReader.UNITS.Temperature, null); break;
950962 case "engine(ortsmaxindicatedhorsepower":
@@ -1091,6 +1103,10 @@ public override void Copy(MSTSWagon copy)
10911103 CylinderExhaustOpenFactor = locoCopy.CylinderExhaustOpenFactor;
10921104 CylinderPortOpeningFactor = locoCopy.CylinderPortOpeningFactor;
10931105 BoilerVolumeFT3 = locoCopy.BoilerVolumeFT3;
1106+ MSTSBoilerLengthM = locoCopy.MSTSBoilerLengthM;
1107+ ORBoilerLengthM = locoCopy.ORBoilerLengthM;
1108+ MSTSSteamGaugeGlassHeightM = locoCopy.MSTSSteamGaugeGlassHeightM;
1109+ ORSteamGaugeGlassHeightM = locoCopy.ORSteamGaugeGlassHeightM;
10941110 MaxBoilerPressurePSI = locoCopy.MaxBoilerPressurePSI;
10951111 SteamLocomotiveFeedWaterType = locoCopy.SteamLocomotiveFeedWaterType;
10961112 MaxSuperheatRefTempF = locoCopy.MaxSuperheatRefTempF;
@@ -1461,6 +1477,57 @@ public override void Initialize()
14611477 BoilerEvapRateLbspFt2 = MathHelper.Clamp(BoilerEvapRateLbspFt2, 7.5f, 30.0f); // Clamp BoilerEvap Rate to between 7.5 & 30 - some modern locomotives can go as high as 30, but majority are around 15.
14621478 TheoreticalMaxSteamOutputLBpS = pS.FrompH(Me2.ToFt2(EvaporationAreaM2) * BoilerEvapRateLbspFt2); // set max boiler theoretical steam output
14631479
1480+ // Initialise Boiler parameters
1481+
1482+ // Boiler Length - always use OR entered value as first preference
1483+ BoilerLengthM = ORBoilerLengthM;
1484+
1485+ /*
1486+ // If OR value hasn't been set, then use MSTS value if present
1487+ if (BoilerLengthM == 0 && MSTSBoilerLengthM > 0)
1488+ {
1489+ if (MSTSBoilerLengthM > 0.4f * CarLengthM && MSTSBoilerLengthM < CarLengthM)
1490+ {
1491+ BoilerLengthM = MSTSBoilerLengthM;
1492+
1493+ if (Simulator.Settings.VerboseConfigurationMessages)
1494+ {
1495+ Trace.TraceInformation("Boiler Length set as per MSTS default = {0}", FormatStrings.FormatDistance(BoilerLengthM, IsMetric));
1496+ }
1497+ }
1498+ }
1499+ */
1500+
1501+ // Water Gauge Length - always use OR entered value as first preference
1502+ WaterGlassLengthM = ORSteamGaugeGlassHeightM;
1503+
1504+ /*
1505+ // If OR value hasn't been set, then use MSTS value if present
1506+ if (WaterGlassLengthM == 0 && MSTSSteamGaugeGlassHeightM > 0)
1507+ {
1508+ var defaultGlassLength = Me.FromIn(12);
1509+
1510+ if (MSTSSteamGaugeGlassHeightM < defaultGlassLength)
1511+ {
1512+ WaterGlassLengthM = MSTSSteamGaugeGlassHeightM;
1513+ WaterGlassLengthIN = Me.ToIn(WaterGlassLengthM);
1514+
1515+ if (Simulator.Settings.VerboseConfigurationMessages)
1516+ {
1517+ Trace.TraceInformation("Water Glass Length set as per MSTS default = {0}", FormatStrings.FormatDistance(WaterGlassLengthM, IsMetric));
1518+ }
1519+ }
1520+ }
1521+ */
1522+
1523+
1524+ if (WaterGlassLengthM > 0 && BoilerLengthM > 0 )
1525+ {
1526+ WaterGlassLevelGradientEnabled = true;
1527+ }
1528+
1529+ // Trace.TraceInformation("Boiler Water Level - MSTSLength {0} ORLength {1} MSTSGlass {2} OR Glass {3} Enabled {4}, WaterGlass {5} BoilerLength {6}", MSTSBoilerLengthM, ORBoilerLengthM, MSTSSteamGaugeGlassHeightM, ORSteamGaugeGlassHeightM, WaterGlassLevelGradientEnabled, WaterGlassLengthM, BoilerLengthM);
1530+
14641531 float BoilerVolumeCheck = Me2.ToFt2(EvaporationAreaM2) / BoilerVolumeFT3; //Calculate the Boiler Volume Check value.
14651532 if (BoilerVolumeCheck > 15) // If boiler volume is not in ENG file or less then a viable figure (ie high ratio figure), then set to a default value
14661533 {
@@ -7056,6 +7123,36 @@ private void UpdateWaterGauge()
70567123 Simulator.Confirmer.Message(ConfirmLevel.Information, Simulator.Catalog.GetString("Boiler no longer priming."));
70577124 BoilerIsPriming = false;
70587125 }
7126+
7127+ // Calculate water glass level when on gradient
7128+ if (WaterGlassLevelGradientEnabled)
7129+ {
7130+ var boilerangleRad = Math.Atan(CurrentElevationPercent / 100);
7131+ var waterVariationLevelM = (float)Math.Sin(boilerangleRad) * BoilerLengthM / 2.0f;
7132+
7133+ // Assume that reference glass height is 50% of glass
7134+ var maxWaterVariationIN = Me.ToIn(WaterGlassLengthM) / 2.0f;
7135+
7136+ float glasslevelIN = 0;
7137+ float glassLevelFraction = 0;
7138+
7139+ if (CurrentElevationPercent > 0) // Downslope - -ve water slope
7140+ {
7141+ glasslevelIN -= Me.ToIn(waterVariationLevelM) * -1.0f;
7142+ }
7143+ else // up slope - +ve water level
7144+ {
7145+ glasslevelIN += Me.ToIn(waterVariationLevelM) * -1.0f;
7146+ }
7147+
7148+ glassLevelFraction = glasslevelIN / maxWaterVariationIN;
7149+
7150+ GradientBoilerLevelPercent = glassLevelFraction * 100;
7151+
7152+ // Trace.TraceInformation("Gradient - Current {0} BoilAng {1} GlassIN {2} Glass% {3}", CurrentElevationPercent, boilerangleRad, glasslevelIN, GradientBoilerLevelPercent);
7153+ }
7154+
7155+
70597156 }
70607157
70617158 private void UpdateWaterInjection(float elapsedClockSeconds)
@@ -7828,7 +7925,15 @@ public override string GetStatus()
78287925 SteamGearRatio, SteamGearPosition == 0 ? Simulator.Catalog.GetParticularString("Gear", "N") : SteamGearPosition.ToString());
78297926 status.AppendFormat("{0}{2} = {1}/{3}{2}\n", Simulator.Catalog.GetString("Steam usage"), FormatStrings.FormatMass(pS.TopH(Kg.FromLb(PreviousTotalSteamUsageLBpS)), MainPressureUnit != PressureUnit.PSI), steamusagesafety, FormatStrings.h);
78307927 status.AppendFormat("{0}{2} = {1}{2}\n", Simulator.Catalog.GetString("Boiler pressure"), FormatStrings.FormatPressure(BoilerPressurePSI, PressureUnit.PSI, MainPressureUnit, true), boilerPressureSafety);
7831- status.AppendFormat("{0}{2} = {1:F0}% {3}{2}\n", Simulator.Catalog.GetString("Boiler water glass"), 100 * waterGlassPercent, boilerWaterSafety, FiringIsManual ? Simulator.Catalog.GetString("(safe range)") : "");
7928+
7929+ if (WaterGlassLevelGradientEnabled)
7930+ {
7931+ status.AppendFormat("{0}{2} = {1:F0}% {3}{2}\n", Simulator.Catalog.GetString("Boiler water glass"), GradientBoilerLevelPercent, boilerWaterSafety, FiringIsManual ? Simulator.Catalog.GetString("(safe range)") : "");
7932+ }
7933+ else
7934+ {
7935+ status.AppendFormat("{0}{2} = {1:F0}% {3}{2}\n", Simulator.Catalog.GetString("Boiler water glass"), 100 * waterGlassPercent, boilerWaterSafety, FiringIsManual ? Simulator.Catalog.GetString("(safe range)") : "");
7936+ }
78327937
78337938 if (FiringIsManual)
78347939 {
0 commit comments