-
Notifications
You must be signed in to change notification settings - Fork 81
/
MSTSSteamLocomotive.cs
8659 lines (7517 loc) · 531 KB
/
MSTSSteamLocomotive.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// COPYRIGHT 2009, 2010, 2011, 2012, 2013, 2014, 2015 by the Open Rails project.
//
// This file is part of Open Rails.
//
// Open Rails is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Open Rails is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
// Burn debugging is off by default - uncomment the #define to turn on - provides visibility of burn related parameters for AI Fireman on extended HUD.
//#define DEBUG_LOCO_BURN_AI
// Compound Indicator Pressures are off by default - uncomment the #define to turn on - provides visibility of calculations for MEP.
//#define DEBUG_LOCO_STEAM_COMPOUND_HP_MEP
// Compound Indicator Pressures are off by default - uncomment the #define to turn on - provides visibility of calculations for MEP.
//#define DEBUG_LOCO_STEAM_COMPOUND_LP_MEP
// Burn debugging is off by default - uncomment the #define to turn on - provides visibility of calculations for MEP.
//#define DEBUG_LOCO_STEAM_MEP
// Steam usage debugging is off by default - uncomment the #define to turn on - provides visibility of steam usage related parameters on extended HUD.
//#define DEBUG_LOCO_STEAM_USAGE
// Debug for Auxiliary Tender
//#define DEBUG_AUXTENDER
// Debug for Steam Effects
//#define DEBUG_STEAM_EFFECTS
// Debug for Steam Slip
//#define DEBUG_STEAM_SLIP
// Debug for Steam Slip HUD
//#define DEBUG_STEAM_SLIP_HUD
// Debug for Sound Variables
//#define DEBUG_STEAM_SOUND_VARIABLES
// Debug for Steam Performance Data @ 5mph increments
//#define DEBUG_STEAM_PERFORMANCE
// Debug for Steam Cylinder Events
//#define DEBUG_STEAM_CYLINDER_EVENTS
/* STEAM LOCOMOTIVE CLASSES
*
* The Locomotive is represented by two classes:
* MSTSDieselLocomotiveSimulator - defines the behaviour, ie physics, motion, power generated etc
* MSTSDieselLocomotiveViewer - defines the appearance in a 3D viewer. The viewer doesn't
* get attached to the car until it comes into viewing range.
*
* Both these classes derive from corresponding classes for a basic locomotive
* LocomotiveSimulator - provides for movement, basic controls etc
* LocomotiveViewer - provides basic animation for running gear, wipers, etc
*
*/
using Microsoft.Xna.Framework;
using Orts.Common;
using Orts.Formats.Msts;
using Orts.Parsers.Msts;
using Orts.Simulation.Physics;
using Orts.Simulation.RollingStocks.SubSystems.Controllers;
using Orts.Simulation.RollingStocks.SubSystems.Brakes.MSTS;
using ORTS.Common;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using Event = Orts.Common.Event;
using Orts.Simulation.RollingStocks.SubSystems.PowerSupplies;
using Orts.Simulation;
using Orts.Simulation.Simulation.RollingStocks.SubSystems.PowerSupplies;
using Orts.Simulation.RollingStocks.SubSystems.PowerTransmissions;
namespace Orts.Simulation.RollingStocks
{
///////////////////////////////////////////////////
/// SIMULATION BEHAVIOUR
///////////////////////////////////////////////////
/// <summary>
/// Adds physics and control for a steam locomotive
/// </summary>
public class MSTSSteamLocomotive : MSTSLocomotive
{
//Configure a default cutoff controller
//If none is specified, this will be used, otherwise those values will be overwritten
public MSTSNotchController CutoffController = new MSTSNotchController(-0.9f, 0.9f, 0.1f);
public MSTSNotchController Injector1Controller = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController Injector2Controller = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController BlowerController = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController DamperController = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController FiringRateController = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController FireboxDoorController = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController FuelController = new MSTSNotchController(0, 1, 0.01f); // Could be coal, wood, oil or even peat !
public MSTSNotchController SmallEjectorController = new MSTSNotchController(0, 1, 0.1f);
public MSTSNotchController LargeEjectorController = new MSTSNotchController(0, 1, 0.1f);
float DebugTimerS;
public bool Injector1IsOn;
bool Injector1SoundIsOn = false;
public bool Injector2IsOn;
bool Injector2SoundIsOn = false;
public bool CylinderCocksAreOpen;
public bool BlowdownValveOpen;
public bool CylinderCompoundOn; // Flag to indicate whether compound locomotive is in compound or simple mode of operation - simple = true (ie bypass valve is open)
bool FiringIsManual;
bool BlowerIsOn = false;
bool BoilerIsPriming = false;
bool WaterIsExhausted = false;
bool CoalIsExhausted = false;
bool FireIsExhausted = false;
bool FuelBoost = false;
bool FuelBoostReset = false;
bool StokerIsMechanical = false;
bool HotStart; // Determine whether locomotive is started in hot or cold state - selectable option in Options TAB
bool FullBoilerHeat = false; // Boiler heat has exceeded max possible heat in boiler (max operating steam pressure)
bool FullMaxPressBoilerHeat = false; // Boiler heat has exceed the max total possible heat in boiler (max safety valve pressure)
bool ShovelAnyway = false; // Predicts when the AI fireman should be increasing the fire burn rate despite the heat in the boiler
public bool SteamBoosterLatchOn = false;
public bool SteamBoosterIdle = false;
public bool SteamBoosterAirOpen = false;
public bool SteamBoosterRunMode = false;
public bool SteamBoosterIdleMode = false;
public bool BoosterGearsEngaged = false;
public bool SteamBoosterLatchedLocked = false;
public float SteamBoosterPressurePSI;
float BoosterGearEngageTimeS;
float BoosterIdleHeatingTimerS;
float BoosterIdleHeatingTimePeriodS = 120; // This is the time period that the Booster needs to be idled to heat it up
bool BoosterIdleHeatingTimerReset = false;
float BoosterGearEngageTimePeriodS;
float BoosterGearSyncTimePeriodS = 6; // This is the time period that the gears take to mesh, once the throttle is opened.
public float HuDBoosterSteamConsumptionLbpS;
public float BoosterSteamConsumptionLbpS;
float BoosterIdleChokeSizeIn;
float BoosterPressureFactor = 0;
float BoosterMaxIdleChokeSizeIn = 0.625f;
/// <summary>
/// Grate limit of locomotive exceedeed?
/// </summary>
public bool IsGrateLimit { get; protected set; } = false;
bool HasSuperheater = false; // Flag to indicate whether locomotive is superheated steam type
bool IsSuperSet = false; // Flag to indicate whether superheating is reducing cylinder condenstation
bool IsSaturated = false; // Flag to indicate locomotive is saturated steam type
bool safety2IsOn = false; // Safety valve #2 is on and opertaing
bool safety3IsOn = false; // Safety valve #3 is on and opertaing
bool safety4IsOn = false; // Safety valve #4 is on and opertaing
bool IsFixGeared = false;
bool IsSelectGeared = false;
bool IsCritTELimit = false; // Flag to advise if critical TE is exceeded
bool ISBoilerLimited = false; // Flag to indicate that Boiler is limiting factor with the locomotive power
bool SetFireOn = false; // Flag to set the AI fire to on for starting of locomotive
bool SetFireOff = false; // Flag to set the AI fire to off for locomotive when approaching a stop
bool SetFireReset = false; // Flag if AI fire has been reset, ie no overrides in place
bool AIFireOverride = false; // Flag to show ai fire has has been overriden
bool InjectorLockedOut = false; // Flag to lock injectors from changing within a fixed period of time
// Aux Tender Parameters
public bool AuxTenderMoveFlag = false; // Flag to indicate whether train has moved
bool SteamIsAuxTenderCoupled = false;
float TenderWaterPercent; // Percentage of water in tender
public float WaterConsumptionLbpS;
public float CurrentAuxTenderWaterMassKG;
public float CurrentAuxTenderWaterVolumeUKG;
public float CurrentLocoTenderWaterVolumeUKG;
float PrevCombinedTenderWaterVolumeUKG;
float PreviousTenderWaterVolumeUKG;
public float MaxLocoTenderWaterMassKG = 1; // Maximum read from Eng file - this value must be non-zero, if not defined in ENG file, can cause NaN errors
// Tender
public bool HasTenderCoupled = true;
float BlowdownSteamUsageLBpS;
float BlowdownValveSizeDiaIn2;
string SteamLocoType; // Type of steam locomotive type
float PulseTracker;
int NextPulse = 1;
// state variables
SmoothedData BoilerHeatSmoothBTU = new SmoothedData(240); // total heat in water and steam in boiler - lb/s * SteamHeat(BTU/lb)
float BoilerHeatSmoothedBTU;
float PreviousBoilerHeatSmoothedBTU;
float BoilerHeatBTU; // total heat in water and steam in boiler - lb/s * SteamHeat(BTU/lb)
float MaxBoilerHeatBTU; // Boiler heat at max rated output and pressure, etc
float MaxBoilerSafetyPressHeatBTU; // Boiler heat at boiler pressure for operation of safety valves
float MaxBoilerHeatSafetyPressurePSI; // Boiler Pressure for calculating max boiler pressure, includes safety valve pressure
float BoilerStartkW; // calculate starting boilerkW
float MaxBoilerHeatInBTUpS = 0.1f; // Remember the BoilerHeat value equivalent to Max Boiler Heat
float baseStartTempK; // Starting water temp
float StartBoilerHeatBTU;
public float BoilerMassLB; // current total mass of water and steam in boiler (changes as boiler usage changes)
float BoilerKW; // power of boiler
float MaxBoilerKW; // power of boiler at full performance
float MaxBoilerOutputHP; // Horsepower output of boiler
float BoilerSteamHeatBTUpLB; // Steam Heat based on current boiler pressure
float BoilerWaterHeatBTUpLB; // Water Heat based on current boiler pressure
float BoilerSteamDensityLBpFT3; // Steam Density based on current boiler pressure
float BoilerWaterDensityLBpFT3; // Water Density based on current boiler pressure
float BoilerWaterTempK;
public float FuelBurnRateSmoothedKGpS;
float FuelFeedRateKGpS;
float DesiredChange; // Amount of change to increase fire mass, clamped to range 0.0 - 1.0
public float CylinderSteamUsageLBpS;
public float NewCylinderSteamUsageLBpS;
public float BlowerSteamUsageLBpS;
public float EvaporationLBpS; // steam generation rate
public float FireMassKG; // Mass of coal currently on grate area
public float FireRatio; // Ratio of actual firemass to ideal firemass
float MaxFiringRateLbpH; // Max coal burnt when steam evaporation (production) rate is maximum
float TempFireHeatLossPercent;
float FireHeatLossPercent; // Percentage loss of heat due to too much or too little air for combustion
float FlueTempK = 775; // Initial FlueTemp (best @ 475)
float MaxFlueTempK; // FlueTemp at full boiler performance
public bool SafetyIsOn;
public readonly SmoothedData SmokeColor = new SmoothedData(2);
// eng file configuration parameters
float BoilerVolumeFT3; // total space in boiler that can hold water and steam
public int MSTSNumCylinders = 2; // Number of Cylinders
public float MSTSCylinderStrokeM; // High pressure cylinders
public float MSTSCylinderDiameterM; // High pressure cylinders
public int MSTSLPNumCylinders = 2; // Number of LP Cylinders
public float MSTSLPCylinderStrokeM; // Low pressure cylinders
public float MSTSLPCylinderDiameterM; // Low pressure cylinders
float CompoundCylinderRatio; // Compound locomotive - ratio of low pressure to high pressure cylinder
float MaxBoilerOutputLBpH; // maximum boiler steam generation rate
float IdealFireMassKG; // Target fire mass
float MaxFireMassKG; // Max possible fire mass
float MaxFiringRateKGpS; // Max rate at which fireman or stoker can can feed coal into fire
/// <summary>
/// Max combustion rate of the grate; once this is reached, no more steam is produced.
/// </summary>
public float GrateLimitLBpFt2 { get; protected set; } = 150.0f;
float MaxFuelBurnGrateKGpS; // Maximum rate of fuel burnt depending upon grate limit
/// <summary>
/// Grate combustion rate, i.e. how many lbs coal burnt per sq ft grate area.
/// </summary>
public float GrateCombustionRateLBpFt2 { get; protected set; }
float ORTSMaxFiringRateKGpS; // OR equivalent of above
float DisplayMaxFiringRateKGpS; // Display value of MaxFiringRate
public float SafetyValveUsageLBpS;
float SafetyValveBoilerHeatOutBTUpS; // Heat removed by blowing of safety valves.
float BoilerHeatOutSVAIBTUpS;
float SafetyValveDropPSI = 4.0f; // Pressure drop before Safety valve turns off, normally around 4 psi - First safety valve normally operates between MaxBoilerPressure, and MaxBoilerPressure - 4, ie Max Boiler = 200, cutoff = 196.
float EvaporationAreaM2;
float SuperheatAreaM2 = 0.0f; // Heating area of superheater
float SuperheatKFactor = 15000.0f; // Factor used to calculate superheat temperature - guesstimate
float MaxSuperheatRefTempF; // Maximum Superheat temperature in deg Fahrenheit, based upon the heating area.
float SuperheatTempRatio; // A ratio used to calculate the superheat temp - based on the ratio of superheat (using heat area) to "known" curve.
public float CurrentSuperheatTempF; // current value of superheating based upon boiler steam output
float SuperheatVolumeRatio; // Approximate ratio of Superheated steam to saturated steam at same pressure
float SuperheatCutoffPressureFactor; // Factor to adjust cutoff pressure for superheat locomotives, defaults to 55.0, user defineable
float FuelCalorificKJpKG = 33400;
float ManBlowerMultiplier = 20.0f; // Blower Multipler for Manual firing
float ShovelMassKG = 6;
float FiringSteamUsageRateLBpS; // rate if excessive usage
float FullBoilerHeatRatio = 1.0f; // Boiler heat ratio, if boiler heat exceeds, normal boiler pressure boiler heat
float MaxBoilerHeatRatio = 1.0f; // Max Boiler heat ratio, if boiler heat exceeds, safety boiler pressure boiler heat
float AIFiremanBurnFactor = 1.0f; // Factor by which to adjust burning (hence heat rate), combination of PressureRatio * BoilerHeatRatio * MaxBoilerHeatRatio
float AIFiremanBurnFactorExceed = 1.0f; // Factor by which to adjust burning (hence heat rate) for excessive shoveling, combination of PressureRatio * BoilerHeatRatio * MaxBoilerHeatRatio
float HeatRatio = 0.001f; // Ratio to control burn rate - based on ratio of heat in vs heat out
float PressureRatio = 0.001f; // Ratio to control burn rate - based upon boiler pressure
float BurnRateRawKGpS; // Raw combustion (burn) rate
float MaxCombustionRateKgpS;
SmoothedData FuelRateStoker = new SmoothedData(15); // Stoker is more responsive and only takes x seconds to fully react to changing needs.
SmoothedData FuelRate = new SmoothedData(45); // Automatic fireman takes x seconds to fully react to changing needs.
SmoothedData BurnRateSmoothKGpS = new SmoothedData(150); // Changes in BurnRate take x seconds to fully react to changing needs - models increase and decrease in heat.
float FuelRateSmoothed = 0.0f; // Smoothed Fuel Rate
public Orts.Simulation.Simulation.RollingStocks.SubSystems.PowerSupplies.SteamEngines SteamEngines;
// steam performance reporting
public float SteamPerformanceTimeS = 0.0f; // Records the time since starting movement
public float CumulativeWaterConsumptionLbs = 0.0f;
public float CumulativeCylinderSteamConsumptionLbs = 0.0f;
float CummulativeTotalSteamConsumptionLbs = 0.0f;
public static float DbfEvalCumulativeWaterConsumptionLbs;//DebriefEval
int LocoIndex;
public float LocoTenderFrictionForceN; // Combined friction of locomotive and tender
public float TotalFrictionForceN;
public float TrainLoadKg;
public float LocomotiveCouplerForceN;
// precomputed values
float CylinderSweptVolumeFT3pFT; // Volume of steam Cylinder
float LPCylinderSweptVolumeFT3pFT; // Volume of LP steam Cylinder
float CylinderCondensationFactor; // Cylinder compensation factor for condensation in cylinder due to cutoff
float BlowerSteamUsageFactor;
float InjectorLockOutResetTimeS = 15.0f; // Time to reset the injector lock out time - time to prevent change of injectors
float InjectorLockOutTimeS = 0.0f; // Current lock out time - reset after Reset Time exceeded
float InjectorFlowRateLBpS; // Current injector flow rate - based upon current boiler pressure
float MaxInjectorFlowRateLBpS = 0.0f; // Maximum possible injector flow rate - based upon maximum boiler pressure
Interpolator BackPressureIHPtoPSI; // back pressure in cylinders given usage
Interpolator CylinderSteamDensityPSItoLBpFT3; // steam density in cylinders given pressure (could be super heated)
Interpolator WaterDensityPSItoLBpFT3; // water density given pressure
Interpolator WaterHeatPSItoBTUpLB; // total heat in water given pressure
Interpolator HeatToPressureBTUpLBtoPSI; // pressure given total heat in water (inverse of WaterHeat)
Interpolator PressureToTemperaturePSItoF;
Interpolator InjDelWaterTempMinPressureFtoPSI; // Injector Delivery Water Temp - Minimum Capacity
Interpolator InjDelWaterTempMaxPressureFtoPSI; // Injector Delivery Water Temp - Maximum Capacity
Interpolator InjWaterFedSteamPressureFtoPSI; // Injector Water Lbs of water per lb steam used
Interpolator InjCapMinFactorX; // Injector Water Table to determin min capacity - max/min
Interpolator Injector09FlowratePSItoUKGpM; // Flowrate of 09mm injector in gpm based on boiler pressure
Interpolator Injector10FlowratePSItoUKGpM; // Flowrate of 10mm injector in gpm based on boiler pressure
Interpolator Injector11FlowratePSItoUKGpM; // Flowrate of 11mm injector in gpm based on boiler pressure
Interpolator Injector13FlowratePSItoUKGpM; // Flowrate of 13mm injector in gpm based on boiler pressure
Interpolator Injector14FlowratePSItoUKGpM; // Flowrate of 14mm injector in gpm based on boiler pressure
Interpolator Injector15FlowratePSItoUKGpM; // Flowrate of 15mm injector in gpm based on boiler pressure
Interpolator SpecificHeatKtoKJpKGpK; // table for specific heat capacity of water at temp of water
Interpolator SaturationPressureKtoPSI; // Saturated pressure of steam (psi) @ water temperature (K)
Interpolator BoilerEfficiencyGrateAreaLBpFT2toX; // Table to determine boiler efficiency based upon lbs of coal per sq ft of Grate Area
Interpolator BoilerEfficiency; // boiler efficiency given steam usage
Interpolator WaterTempFtoPSI; // Table to convert water temp to pressure
Interpolator CylinderCondensationFractionX; // Table to find the cylinder condensation fraction per cutoff for the cylinder - saturated steam
Interpolator SuperheatTempLimitXtoDegF; // Table to find Super heat temp required to prevent cylinder condensation - Ref Elseco Superheater manual
Interpolator SuperheatTempLbpHtoDegF; // Table to find Super heat temp per lbs of steam to cylinder - from BTC Test Results for Std 8
Interpolator InitialPressureDropRatioRpMtoX; // Allowance for wire-drawing - ie drop in initial pressure (cutoff) as speed increases
Interpolator SaturatedSpeedFactorSpeedDropFtpMintoX; // Allowance for drop in TE for a saturated locomotive due to piston speed limitations
Interpolator SuperheatedSpeedFactorSpeedDropFtpMintoX; // Allowance for drop in TE for a superheated locomotive due to piston speed limitations
Interpolator NewBurnRateSteamToCoalLbspH; // Combustion rate of steam generated per hour to Dry Coal per hour
Interpolator2D CutoffInitialPressureDropRatioUpper; // Upper limit of the pressure drop from initial pressure to cut-off pressure
Interpolator2D CutoffInitialPressureDropRatioLower; // Lower limit of the pressure drop from initial pressure to cut-off pressure
Interpolator CylinderExhausttoCutoff; // Fraction of cylinder travel to exhaust
Interpolator CylinderCompressiontoCutoff; // Fraction of cylinder travel to Compression
Interpolator CylinderAdmissiontoCutoff; // Fraction of cylinder travel to Admission
// Heat Radiation Parameters
float KcInsulation; // Insulated section of Boiler - Coefficient of thermal conductivity - BBTU / sq.ft. / hr / l in / °F.
float KcUninsulation = 1.67f; // Uninsulated section of Boiler (Steel only) - Coefficient of thermal conductivity - BBTU / sq.ft. / hr / l in / °F.
float BoilerSurfaceAreaFt2;
float FractionBoilerAreaInsulated;
float BoilerHeatRadiationLossBTU; // Heat loss of boiler (hourly value)
#region Additional steam properties
const float SpecificHeatCoalKJpKGpK = 1.26f; // specific heat of coal - kJ/kg/K
const float SteamVaporSpecVolumeAt100DegC1BarM3pKG = 1.696f;
float WaterHeatBTUpFT3; // Water heat in btu/ft3
bool FusiblePlugIsBlown = false; // Fusible plug blown, due to lack of water in the boiler
bool LocoIsOilBurner = false; // Used to identify if loco is oil burner
float GrateAreaM2; // Grate Area in SqM
float IdealFireDepthIN = 7.0f; // Assume standard coal coverage of grate = 7 inches.
float FuelDensityKGpM3 = 864.5f; // Anthracite Coal : 50 - 58 (lb/ft3), 800 - 929 (kg/m3)
float DamperFactorManual = 1.0f; // factor to control draft through fire when locomotive is running in Manual mode
public float WaterLBpUKG = 10.0f; // lbs of water in 1 gal (uk)
public float MaxTenderCoalMassKG = 1; // Maximum read from Eng File - - this value must be non-zero, if not defined in ENG file, can cause NaN errors
public float TenderCoalMassKG // Decreased by firing and increased by refilling
{
get { return FuelController.CurrentValue * MaxTenderCoalMassKG; }
set { FuelController.CurrentValue = value / MaxTenderCoalMassKG; }
}
float DamperBurnEffect; // Effect of the Damper control Used in manual firing)
float Injector1Fraction = 0.0f; // Fraction (0-1) of injector 1 flow from Fireman controller or AI
float Injector2Fraction = 0.0f; // Fraction (0-1) of injector of injector 2 flow from Fireman controller or AI
float SafetyValveStartPSI = 0.1f; // Set safety valve to just over max pressure - allows for safety valve not to operate in AI firing
float InjectorBoilerInputLB = 0.0f; // Input into boiler from injectors
const float WaterDensityAt100DegC1BarKGpM3 = 954.8f;
// Steam Ejector
float TempEjectorSmallSteamConsumptionLbpS;
float TempEjectorLargeSteamConsumptionLbpS;
float EjectorTotalSteamConsumptionLbpS;
public float VacuumPumpOutputFt3pM;
// Air Compressor Characteristics - assume 9.5in x 10in Compressor operating at 120 strokes per min.
float CompCylDiaIN = 9.5f;
float CompCylStrokeIN = 10.0f;
float CompStrokespM = 120.0f;
float CompSteamUsageLBpS = 0.0f;
const float BTUpHtoKJpS = 0.000293071f; // Convert BTU/s to Kj/s
float BoilerHeatTransferCoeffWpM2K = 45.0f; // Heat Transfer of locomotive boiler 45 Wm2K
float TotalSteamUsageLBpS; // Running total for complete current steam usage
float GeneratorSteamUsageLBpS = 1.0f; // Generator Steam Usage
float RadiationSteamLossLBpS = 2.5f; // Steam loss due to radiation losses
float BlowerBurnEffect; // Effect of Blower on burning rate
float FlueTempDiffK; // Current difference in flue temp at current firing and steam usage rates.
float FireHeatTxfKW; // Current heat generated by the locomotive fire
float HeatMaterialThicknessFactor = 1.0f; // Material thickness for convection heat transfer
float TheoreticalMaxSteamOutputLBpS; // Max boiler output based upon Output = EvapArea x 15 ( lbs steam per evap area)
// Water model - locomotive boilers require water level to be maintained above the firebox crown sheet
// This model is a crude representation of a water gauge based on a generic boiler and 8" water gauge
// Based on a scaled drawing following water fraction levels have been used - crown sheet = 0.7, min water level = 0.73, max water level = 0.89
float WaterFraction; // fraction of boiler volume occupied by water
float WaterMinLevel = 0.7f; // min level before we blow the fusible plug
float WaterMinLevelSafe = 0.75f; // min level which you would normally want for safety
float WaterMaxLevel = 0.91f; // max level above which we start priming
float WaterMaxLevelSafe = 0.90f; // max level below which we stop priming
float WaterGlassMaxLevel = 0.89f; // max height of water gauge as a fraction of boiler level
float WaterGlassMinLevel = 0.73f; // min height of water gauge as a fraction of boiler level
float WaterGlassLengthIN = 8.0f; // nominal length of water gauge
float WaterGlassLevelIN; // Water glass level in inches
float waterGlassPercent; // Water glass level in percent
float MEPFactor = 0.7f; // Factor to determine the MEP
float GrateAreaDesignFactor = 500.0f; // Design factor for determining Grate Area
float EvapAreaDesignFactor = 10.0f; // Design factor for determining Evaporation Area
float SpecificHeatWaterKJpKGpC; // Specific Heat Capacity of water in boiler (from Interpolator table) kJ/kG per deg C
float WaterTempInK; // Input to water Temp Integrator.
float WaterTempNewK; // Boiler Water Temp (Kelvin) - for testing purposes
float BkW_Diff; // Net Energy into boiler after steam loads taken.
float WaterVolL; // Actual volume of water in bolier (litres)
float BoilerHeatOutBTUpS = 0.0f;// heat out of boiler in BTU
/// <summary>
/// Heat into boiler in BTU
/// </summary>
public float BoilerHeatInBTUpS { get; protected set; } = 0.0f;
float BoilerHeatExcess; // Vlaue of excess boiler heat
float InjCylEquivSizeIN; // Calculate the equivalent cylinder size for purpose of sizing the injector.
float InjectorSize; // size of injector installed on boiler
// Values from previous iteration to use in UpdateFiring() and show in HUD
public float PreviousBoilerHeatOutBTUpS { get; protected set; } = 0.0f;
public float PreviousTotalSteamUsageLBpS { get; protected set; }
float Injector1WaterDelTempF = 65f; // Injector 1 water delivery temperature - F
float Injector2WaterDelTempF = 65f; // Injector 1 water delivery temperature - F
float Injector1TempFraction; // Find the fraction above the min temp of water delivery
float Injector2TempFraction; // Find the fraction above the min temp of water delivery
float Injector1WaterTempPressurePSI; // Pressure equivalent of water delivery temp
float Injector2WaterTempPressurePSI; // Pressure equivalent of water delivery temp
float MaxInject1SteamUsedLbpS; // Max steam injected into boiler when injector operating at full value - Injector 1
float MaxInject2SteamUsedLbpS; // Max steam injected into boiler when injector operating at full value - Injector 2
float ActInject1SteamUsedLbpS; // Act steam injected into boiler when injector operating at current value - Injector 1
float ActInject2SteamUsedLbpS; // Act steam injected into boiler when injector operating at current value - Injector 2
float Inject1SteamHeatLossBTU; // heat loss due to steam usage from boiler for injector operation - Injector 1
float Inject2SteamHeatLossBTU; // heat loss due to steam usage from boiler for injector operation - Injector 2
float Inject1WaterHeatLossBTU; // heat loss due to water injected into the boiler for injector operation - Injector 1
float Inject2WaterHeatLossBTU; // heat loss due to water injected into the boiler for injector operation - Injector 1
// Derating factors for motive force
float BoilerPrimingDeratingFactor = 0.1f; // Factor if boiler is priming
float OneAtmospherePSI = 14.696f; // Atmospheric Pressure
float SuperheaterFactor = 1.0f; // Currently 2 values respected: 0.0 for no superheat (default), > 1.0 for typical superheat
public float SuperheaterSteamUsageFactor = 1.0f; // Below 1.0, reduces steam usage due to superheater
float Stoker = 0.0f; // Currently 2 values respected: 0.0 for no mechanical stoker (default), = 1.0 for typical mechanical stoker
float StokerMaxUsage = 0.01f; // Max steam usage of stoker - 1% of max boiler output
float StokerMinUsage = 0.005f; // Min Steam usage - just to keep motor ticking over - 0.5% of max boiler output
float StokerSteamUsageLBpS; // Current steam usage of stoker
float MaxTheoreticalFiringRateKgpS; // Max firing rate that fireman can sustain for short periods
float FuelBoostOnTimerS = 0.01f; // Timer to allow fuel boosting for a short while
float FuelBoostResetTimerS = 0.01f; // Timer to rest fuel boosting for a while
float TimeFuelBoostOnS = 300.0f; // Time to allow fuel boosting to go on for
float TimeFuelBoostResetS = 1200.0f;// Time to wait before next fuel boost
float throttle;
float SpeedEquivMpS = 27.0f; // Equvalent speed of 60mph in mps (27m/s) - used for damper control
// Cylinder related parameters
public float CutoffPressureDropRatio; // Ratio of Cutoff Pressure to Initial Pressure
public float LPCutoffPressureDropRatio; // Ratio of Cutoff Pressure to Initial Pressure - LP Cylinder
float CylinderCocksPressureAtmPSI; // Pressure in cylinder (impacted by cylinder cocks).
float CylinderCocksPressurePSI;
float SteamChestPressurePSI; // Pressure in steam chest - input to cylinder
float CylinderWork_ab_InLbs; // Work done during steam admission into cylinder
float CylinderExhaustOpenFactor; // Point on cylinder stroke when exhaust valve opens.
float CylinderCompressionCloseFactor; // Point on cylinder stroke when compression valve closes - assumed reciporical of exhaust opens.
float CylinderAdmissionOpenFactor = 0.05f; // Point on cylinder stroke when pre-admission valve opens
float CylinderWork_bc_InLbs; // Work done during expansion stage of cylinder
float CylinderWork_cd_InLbs; // Work done during release stage of cylinder
float CylinderWork_ef_InLbs; // Work done during compression stage of cylinder
float CylinderWork_fa_InLbs; // Work done during PreAdmission stage of cylinder
float CylinderWork_de_InLbs; // Work done during Exhaust stage of cylinder
// Values for logging and displaying Steam pressure
public float LogInitialPressurePSI;
public float LogCutoffPressurePSI;
public float LogBackPressurePSI;
public float LogReleasePressurePSI;
public float LogSteamChestPressurePSI;
// Values for Steam Cylinder events
// Commented out as never used
//float ValveTravel = 10.8268f;
//float ValveLead = 0.275591f;
//float ValveExhLap = 0.708661f;
//float ValveSteamLap;
//double ValveAdvanceAngleDeg;
public float LogLPInitialPressurePSI;
public float LogLPCutoffPressurePSI;
public float LogLPBackPressurePSI;
public float LogLPReleasePressurePSI;
public float LogLPSteamChestPressurePSI;
public bool LogIsCompoundLoco = false;
float LogPreCompressionPressurePSI;
float LogPreAdmissionPressurePSI;
// Compound Cylinder Information - HP Cylinder - Compound Operation
float HPCompPressure_d_AtmPSI; // Pressure in HP cylinder when steam release valve opens
float HPCompPressure_e_AtmPSI; // Pressure in HP cylinder when steam release valve opens, and steam moves into steam passages which connect HP & LP together
float HPCompPressure_h_AtmPSI; // Pressure when exhaust valve closes, and compression commences
float HPCompPressure_k_AtmPSI; // Pre-Admission pressure prior to exhaust valve closing
float HPCompPressure_u_AtmPSI; // Admission pressure
float HPCompMeanPressure_gh_AtmPSI; // Back pressure on HP cylinder
public float HPCylinderMEPPSI; // Mean effective Pressure of HP Cylinder
float HPCylinderClearancePC = 0.19f; // Assume cylinder clearance of 19% of the piston displacement for HP cylinder
float CompoundRecieverVolumePCHP = 0.3f; // Volume of receiver or passages between HP and LP cylinder as a fraction of the HP cylinder volume.
float HPCylinderVolumeFactor = 1.0f; // Represents the full volume of the HP steam cylinder
float LPCylinderVolumeFactor = 1.0f; // Represents the full volume of the LP steam cylinder
float HPIndicatedHorsePowerHP;
float LPIndicatedHorsePowerHP;
float SteamReleasePressure_AtmPSI; // Pressure in LP cylinder when steam release valve opens
public float LPCylinderMEPPSI; // Mean effective pressure of LP Cylinder - required for logger
float LPCylinderClearancePC = 0.066f; // Assume cylinder clearance of 6.6% of the piston displacement for LP cylinder
// Simple locomotive cylinder information
public float MeanEffectivePressurePSI; // Mean effective pressure
float RatioOfExpansion_bc; // Ratio of expansion
float CylinderClearancePC = 0.09f; // Assume cylinder clearance of 8% of the piston displacement for saturated locomotives and 9% for superheated locomotive - default to saturated locomotive value
float CylinderPortOpeningFactor; // Model the size of the steam port opening in the cylinder - set to 0.085 as default, if no ENG file value added
float CylinderPortOpeningUpper = 0.12f; // Set upper limit for Cylinder port opening
float CylinderPortOpeningLower = 0.05f; // Set lower limit for Cylinder port opening
float CylinderPistonShaftFt3; // Volume taken up by the cylinder piston shaft
float CylinderPistonShaftDiaIn = 3.5f; // Assume cylinder piston shaft to be 3.5 inches
float CylinderPistonAreaFt2; // Area of the piston in the cylinder (& HP Cylinder in case of Compound locomotive)
float LPCylinderPistonAreaFt2; // Area of the piston in the LP cylinder
float CylinderAdmissionSteamWeightLbs; // Weight of steam remaining in cylinder at "admission" (when admission valve opens)
float CylinderReleaseSteamWeightLbs; // Weight of steam in cylinder at "release" (exhaust valve opens)
float CylinderReleaseSteamVolumeFt3; // Volume of cylinder at steam release in cylinder
float CylinderAdmissionSteamVolumeFt3; // Volume in cylinder at start of steam compression
float RawCylinderSteamWeightLbs; //
float RawCalculatedCylinderSteamUsageLBpS; // Steam usage before superheat or cylinder condensation compensation
float CalculatedCylinderSteamUsageLBpS; // Steam usage calculated from steam indicator diagram
const int CylStrokesPerCycle = 2; // each cylinder does 2 strokes for every wheel rotation, within each stroke
float CylinderEfficiencyRate = 1.0f; // Factor to vary the output power of the cylinder without changing steam usage - used as a player customisation factor.
public float CylCockSteamUsageLBpS = 0.0f; // Cylinder Cock Steam Usage if locomotive moving
public float CylCockSteamUsageDisplayLBpS = 0.0f; // Cylinder Cock Steam Usage for display and effects
float CylCockDiaIN = 0.5f; // Steam Cylinder Cock orifice size
float CylCockPressReduceFactor; // Factor to reduce cylinder pressure by if cocks open
float CylCockBoilerHeatOutBTUpS; // Amount of heat taken out by use of cylinder cocks
float DrvWheelRevRpS; // number of revolutions of the drive wheel per minute based upon speed.
float PistonSpeedFtpMin; // Piston speed of locomotive
float Cylinder1CrankAngleRad;
float Cylinder2CrankAngleRad;
float Cylinder3CrankAngleRad;
float Cylinder4CrankAngleRad;
static float[] WheelCrankAngleDiffRad = new float[]
{
0.0f, MathHelper.Pi/2, 0.0f, 0.0f // default 2 cylinder locomotive
};
public float IndicatedHorsePowerHP; // Indicated Horse Power (IHP), theoretical power of the locomotive, it doesn't take into account the losses due to friction, etc. Typically output HP will be 70 - 90% of the IHP
public float DrawbarHorsePowerHP; // Drawbar Horse Power (DHP), maximum power available at the wheels.
public float DrawBarPullLbsF; // Drawbar pull in lbf
float BoilerEvapRateLbspFt2; // Sets the evaporation rate for the boiler is used to multiple boiler evaporation area by - used as a player customisation factor.
float MaxSpeedFactor; // Max Speed factor - factor @ critical piston speed to reduce TE due to speed increase - American locomotive company
float DisplaySpeedFactor; // Value displayed in HUD
public float MaxTractiveEffortLbf; // Maximum theoritical tractive effort for locomotive
float DisplayTractiveForceN; // Value of Tractive effort to display in HUD
public float DisplayMaxTractiveEffortLbf; // HuD display value of maximum theoritical tractive effort for locomotive
float MaxCriticalSpeedTractiveEffortLbf; // Maximum power value @ critical speed of piston
float DisplayCriticalSpeedTractiveEffortLbf; // Display power value @ speed of piston
float absStartTractiveEffortN = 0.0f; // Record starting tractive effort
float TractiveEffortLbsF; // Current sim calculated tractive effort
float TractiveEffortFactor = 0.85f; // factor for calculating Theoretical Tractive Effort for non-geared locomotives
float GearedTractiveEffortFactor = 0.7f; // factor for calculating Theoretical Tractive Effort for geared locomotives
float NeutralGearedDavisAN; // Davis A value adjusted for neutral gearing
const float DavisMechanicalResistanceFactor = 20.0f;
float GearedRetainedDavisAN; // Remembers the Davis A value
float MaxLocoSpeedMpH; // Speed of loco when max performance reached
float DisplayMaxLocoSpeedMpH; // Display value of speed of loco when max performance reached
float MaxPistonSpeedFtpM; // Piston speed @ max performance for the locomotive
float MaxIndicatedHorsePowerHP; // IHP @ max performance for the locomotive
float DisplayMaxIndicatedHorsePowerHP; // Display value for HUD of IHP @ max performance for the geared locomotive
float RetainedGearedMaxMaxIndicatedHorsePowerHP; // Retrains maximum IHP value for steam locomotives.
float absSpeedMpS;
float CombFrictionN; // Temporary parameter to store combined friction values of locomotive and tender
float CombGravityN; // Temporary parameter to store combined Gravity values of locomotive and tender
float CombTunnelN; // Temporary parameter to store combined Tunnel values of locomotive and tender
float CombCurveN; // Temporary parameter to store combined Curve values of locomotive and tender
float CombWindN; // Temporary parameter to store combined Curve values of locomotive and tender
float cutoff;
float NumSafetyValves; // Number of safety valves fitted to locomotive - typically 1 to 4
float SafetyValveSizeIn; // Size of the safety value - all will be the same size.
float SafetyValveSizeDiaIn2; // Area of the safety valve - impacts steam discharge rate - is the space when the valve lifts
float MaxSafetyValveDischargeLbspS; // Steam discharge rate of all safety valves combined.
float SafetyValveUsage1LBpS; // Usage rate for safety valve #1
float SafetyValveUsage2LBpS; // Usage rate for safety valve #2
float SafetyValveUsage3LBpS; // Usage rate for safety valve #3
float SafetyValveUsage4LBpS; // Usage rate for safety valve #4
float MaxSteamGearPistonRateFtpM; // Max piston rate for a geared locomotive, such as a Shay
float SteamGearRatio; // Gear ratio for a geared locomotive, such as a Shay
float SteamGearRatioLow; // Gear ratio for a geared locomotive, such as a Shay
float SteamGearRatioHigh; // Gear ratio for a two speed geared locomotive, such as a Climax
float LowMaxGearedSpeedMpS; // Max speed of the geared locomotive - Low Gear
float HighMaxGearedSpeedMpS; // Max speed of the geared locomotive - High Gear
float MotiveForceGearRatio; // mulitplication factor to be used in calculating motive force etc, when a geared locomotive.
float SteamGearPosition = 0.0f; // Position of Gears if set
// Rotative Force and adhesion
float ReciprocatingWeightLb = 580.0f; // Weight of reciprocating parts of the rod driving gears
float ConnectingRodWeightLb = 600.0f; // Weignt of connecting rod
float ConnectingRodBalanceWeightLb = 300.0f; // Balance weight for connecting rods
float ExcessBalanceFactor = 400.0f; // Factor to be included in excess balance formula
float CrankRadiusFt = 1.08f; // Assume crank and rod lengths to give a 1:10 ratio - a reasonable av for steam locomotives?
float ConnectRodLengthFt = 10.8f;
float RodCoGFt = 4.32f; // 0.4 from crank end of rod
#endregion
#region Variables for visual effects (steam, smoke)
public readonly SmoothedData StackSteamVelocityMpS = new SmoothedData(2);
public float StackSteamVolumeM3pS;
public float StackParticleDurationS;
public float StackCount;
public float Cylinders1SteamVelocityMpS;
public float Cylinders1SteamVolumeM3pS;
public float Cylinders2SteamVelocityMpS;
public float Cylinders2SteamVolumeM3pS;
public float SafetyValvesSteamVelocityMpS;
public float SafetyValvesSteamVolumeM3pS;
public float Cylinders11SteamVolumeM3pS;
public float Cylinders12SteamVolumeM3pS;
public float Cylinders21SteamVolumeM3pS;
public float Cylinders22SteamVolumeM3pS;
public float Cylinders31SteamVolumeM3pS;
public float Cylinders32SteamVolumeM3pS;
public float Cylinders41SteamVolumeM3pS;
public float Cylinders42SteamVolumeM3pS;
public float CylinderSteamExhaustSteamVelocityMpS;
public float CylinderSteamExhaust1SteamVolumeM3pS;
public float CylinderSteamExhaust2SteamVolumeM3pS;
public float CylinderSteamExhaust3SteamVolumeM3pS;
public float CylinderSteamExhaust4SteamVolumeM3pS;
bool CylinderSteamExhaust1On = false;
bool CylinderSteamExhaust2On = false;
bool CylinderSteamExhaust3On = false;
bool CylinderSteamExhaust4On = false;
bool BoosterCylinderSteamExhaustOn = false;
public float BoosterCylinderSteamExhaust01SteamVelocityMpS;
public float BoosterCylinderSteamExhaust01SteamVolumeM3pS;
public float BoosterCylinderSteamExhaust02SteamVelocityMpS;
public float BoosterCylinderSteamExhaust02SteamVolumeM3pS;
float BoosterCylinderSteamExhaustTimerS = 0.0f;
bool BoosterCylinderSteamExhaust01On = false;
bool BoosterCylinderSteamExhaust02On = false;
float BoosterCylinderCockTimerS = 0.0f;
bool BoosterCylinderCock11On = true;
bool BoosterCylinderCock12On = false;
bool BoosterCylinderCock21On = true;
bool BoosterCylinderCock22On = false;
public float BoosterCylinderCock11SteamVelocityMpS;
public float BoosterCylinderCock12SteamVelocityMpS;
public float BoosterCylinderCock21SteamVelocityMpS;
public float BoosterCylinderCock22SteamVelocityMpS;
public float BoosterCylinderCockSteam11VolumeMpS;
public float BoosterCylinderCockSteam12VolumeMpS;
public float BoosterCylinderCockSteam21VolumeMpS;
public float BoosterCylinderCockSteam22VolumeMpS;
public float BoosterCylinderCockParticleDurationS;
bool BoosterCylinderCocksOn = false;
public float BlowdownSteamVolumeM3pS;
public float BlowdownSteamVelocityMpS;
public float BlowdownParticleDurationS = 3.0f;
public float DrainpipeSteamVolumeM3pS;
public float DrainpipeSteamVelocityMpS;
public float Injector1SteamVolumeM3pS;
public float Injector1SteamVelocityMpS;
public float Injector2SteamVolumeM3pS;
public float Injector2SteamVelocityMpS;
public float SmallEjectorSteamVolumeM3pS;
public float SmallEjectorSteamVelocityMpS;
public float SmallEjectorParticleDurationS = 3.0f;
public float LargeEjectorSteamVolumeM3pS;
public float LargeEjectorSteamVelocityMpS;
public float LargeEjectorParticleDurationS = 3.0f;
public float CompressorSteamVolumeM3pS;
public float CompressorSteamVelocityMpS;
public float GeneratorSteamVolumeM3pS;
public float GeneratorSteamVelocityMpS;
public float WhistleSteamVolumeM3pS;
public float WhistleSteamVelocityMpS;
float CylinderCockTimerS = 0.0f;
float CylinderCockOpenTimeS = 0.0f;
bool CylinderCock1On = true;
bool CylinderCock2On = false;
bool CylinderCock11On = true;
bool CylinderCock12On = false;
bool CylinderCock21On = true;
bool CylinderCock22On = false;
bool CylinderCock31On = true;
bool CylinderCock32On = false;
bool CylinderCock41On = true;
bool CylinderCock42On = false;
public bool Cylinder2SteamEffects = false;
public bool CylinderAdvancedSteamEffects = false;
public bool CylinderAdvancedSteamExhaustEffects = false;
public bool GeneratorSteamEffects = false;
public float CompressorParticleDurationS = 3.0f;
public float Cylinder1ParticleDurationS = 3.0f;
public float Cylinder2ParticleDurationS = 3.0f;
public float CylinderSteamExhaustParticleDurationS = 3.0f;
public float WhistleParticleDurationS = 3.0f;
public float SafetyValvesParticleDurationS = 3.0f;
public float DrainpipeParticleDurationS = 3.0f;
public float Injector1ParticleDurationS = 3.0f;
public float Injector2ParticleDurationS = 3.0f;
public float GeneratorParticleDurationS = 3.0f;
#endregion
public MSTSSteamLocomotive(Simulator simulator, string wagFile)
: base(simulator, wagFile)
{
SteamEngines = new SteamEngines(this);
PowerSupply = new SteamPowerSupply(this);
RefillTenderWithCoal();
RefillTenderWithWater();
}
/// <summary>
/// Sets the coal level to maximum.
/// </summary>
public void RefillTenderWithCoal()
{
FuelController.CurrentValue = 1.0f;
}
/// <summary>
/// Sets the water level to maximum.
/// </summary>
public void RefillTenderWithWater()
{
WaterController.CurrentValue = 1.0f;
}
/// <summary>
/// Adjusts the fuel controller to initial coal mass.
/// </summary>
public void InitializeTenderWithCoal()
{
FuelController.CurrentValue = TenderCoalMassKG / MaxTenderCoalMassKG;
}
/// <summary>
/// Adjusts the water controller to initial water volume.
/// </summary>
public void InitializeTenderWithWater()
{
WaterController.CurrentValue = CombinedTenderWaterVolumeUKG / MaxTotalCombinedWaterVolumeUKG;
}
private bool ZeroError(float v, string name)
{
if (v > 0)
return false;
if (Simulator.Settings.VerboseConfigurationMessages)
{
Trace.TraceWarning("Steam engine value {1} must be defined and greater than zero in {0}", WagFilePath, name);
}
return true;
}
/// <summary>
/// Parse the wag file parameters required for the simulator and viewer classes
/// </summary>
public override void Parse(string lowercasetoken, STFReader stf)
{
switch (lowercasetoken)
{
case "engine(ortswheelcrankangledifference":
stf.MustMatch("(");
Cylinder1CrankAngleRad = stf.ReadFloat(STFReader.UNITS.Angle, 0.0f);
Cylinder2CrankAngleRad = stf.ReadFloat(STFReader.UNITS.Angle, 0.0f);
Cylinder3CrankAngleRad = stf.ReadFloat(STFReader.UNITS.Angle, 0.0f);
Cylinder4CrankAngleRad = stf.ReadFloat(STFReader.UNITS.Angle, 0.0f);
stf.SkipRestOfBlock();
break;
case "engine(ortssteamengines":
SteamEngines.Parse(lowercasetoken, stf);
break;
case "engine(numcylinders":
MSTSNumCylinders = stf.ReadIntBlock(null); break; // retained for legacy purposes
case "engine(numberofcylinders":
MSTSNumCylinders = stf.ReadIntBlock(null); break;
case "engine(cylinderstroke": MSTSCylinderStrokeM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
case "engine(cylinderdiameter": MSTSCylinderDiameterM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
case "engine(lpnumcylinders": MSTSLPNumCylinders = stf.ReadIntBlock(null); break;
case "engine(lpcylinderstroke": MSTSLPCylinderStrokeM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
case "engine(lpcylinderdiameter": MSTSLPCylinderDiameterM = stf.ReadFloatBlock(STFReader.UNITS.Distance, null); break;
case "engine(ortscylinderportopening": CylinderPortOpeningFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(boilervolume": BoilerVolumeFT3 = stf.ReadFloatBlock(STFReader.UNITS.VolumeDefaultFT3, null); break;
case "engine(maxboilerpressure": MaxBoilerPressurePSI = stf.ReadFloatBlock(STFReader.UNITS.PressureDefaultPSI, null); break;
case "engine(ortsmaxsuperheattemperature": MaxSuperheatRefTempF = stf.ReadFloatBlock(STFReader.UNITS.Temperature, null); break;
case "engine(ortsmaxindicatedhorsepower":
MaxIndicatedHorsePowerHP = stf.ReadFloatBlock(STFReader.UNITS.Power, null);
MaxIndicatedHorsePowerHP = W.ToHp(MaxIndicatedHorsePowerHP); // Convert input to HP for use internally in this module
break;
case "engine(vacuumbrakeslargeejectorusagerate": EjectorLargeSteamConsumptionLbpS = pS.FrompH(stf.ReadFloatBlock(STFReader.UNITS.MassRateDefaultLBpH, null)); break;
case "engine(vacuumbrakessmallejectorusagerate": EjectorSmallSteamConsumptionLbpS = pS.FrompH(stf.ReadFloatBlock(STFReader.UNITS.MassRateDefaultLBpH, null)); break;
case "engine(ortssuperheatcutoffpressurefactor": SuperheatCutoffPressureFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(shovelcoalmass": ShovelMassKG = stf.ReadFloatBlock(STFReader.UNITS.Mass, null); break;
case "engine(maxtendercoalmass": MaxTenderCoalMassKG = stf.ReadFloatBlock(STFReader.UNITS.Mass, null); break;
case "engine(maxtenderwatermass": MaxLocoTenderWaterMassKG = stf.ReadFloatBlock(STFReader.UNITS.Mass, null); break;
case "engine(steamfiremanmaxpossiblefiringrate": MaxFiringRateKGpS = stf.ReadFloatBlock(STFReader.UNITS.MassRateDefaultLBpH, null) / 2.2046f / 3600; break;
case "engine(steamfiremanismechanicalstoker": Stoker = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortssteamfiremanmaxpossiblefiringrate": ORTSMaxFiringRateKGpS = stf.ReadFloatBlock(STFReader.UNITS.MassRateDefaultLBpH, null) / 2.2046f / 3600; break;
case "engine(enginecontrollers(cutoff": CutoffController.Parse(stf); break;
case "engine(enginecontrollers(ortssmallejector": SmallEjectorController.Parse(stf); SmallEjectorControllerFitted = true; break;
case "engine(enginecontrollers(ortslargeejector": LargeEjectorController.Parse(stf); LargeEjectorControllerFitted = true; break;
case "engine(enginecontrollers(injector1water": Injector1Controller.Parse(stf); break;
case "engine(enginecontrollers(injector2water": Injector2Controller.Parse(stf); break;
case "engine(enginecontrollers(blower": BlowerController.Parse(stf); break;
case "engine(enginecontrollers(dampersfront": DamperController.Parse(stf); break;
case "engine(enginecontrollers(shovel": FiringRateController.Parse(stf); break;
case "engine(enginecontrollers(firedoor": FireboxDoorController.Parse(stf); break;
case "engine(effects(steamspecialeffects": ParseEffects(lowercasetoken, stf); break;
case "engine(ortsgratearea": GrateAreaM2 = stf.ReadFloatBlock(STFReader.UNITS.AreaDefaultFT2, null); break;
case "engine(superheater": SuperheaterFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(istenderrequired": IsTenderRequired = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortsevaporationarea": EvaporationAreaM2 = stf.ReadFloatBlock(STFReader.UNITS.AreaDefaultFT2, null); break;
case "engine(ortsboilersurfacearea": BoilerSurfaceAreaFt2 = stf.ReadFloatBlock(STFReader.UNITS.AreaDefaultFT2, null); break;
case "engine(ortsfractionboilerinsulated": FractionBoilerAreaInsulated = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortsheatcoefficientinsulation": KcInsulation = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortssuperheatarea": SuperheatAreaM2 = stf.ReadFloatBlock(STFReader.UNITS.AreaDefaultFT2, null); break;
case "engine(ortsfuelcalorific": FuelCalorificKJpKG = stf.ReadFloatBlock(STFReader.UNITS.EnergyDensity, null); break;
case "engine(ortsboilerevaporationrate": BoilerEvapRateLbspFt2 = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortscylinderefficiencyrate": CylinderEfficiencyRate = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortscylinderinitialpressuredrop": InitialPressureDropRatioRpMtoX = new Interpolator(stf); break;
case "engine(ortscylinderbackpressure": BackPressureIHPtoPSI = new Interpolator(stf); break;
case "engine(ortsburnrate": NewBurnRateSteamToCoalLbspH = new Interpolator(stf); break;
case "engine(ortsboilerefficiency": BoilerEfficiencyGrateAreaLBpFT2toX = new Interpolator(stf); break;
case "engine(ortscylindereventexhaust": CylinderExhausttoCutoff = new Interpolator(stf); break;
case "engine(ortscylindereventcompression": CylinderCompressiontoCutoff = new Interpolator(stf); break;
case "engine(ortscylindereventadmission": CylinderAdmissiontoCutoff = new Interpolator(stf); break;
case "engine(ortssteamgearratio":
stf.MustMatch("(");
SteamGearRatioLow = stf.ReadFloat(STFReader.UNITS.None, null);
SteamGearRatioHigh = stf.ReadFloat(STFReader.UNITS.None, null);
stf.SkipRestOfBlock();
break;
case "engine(ortssteammaxgearpistonrate": MaxSteamGearPistonRateFtpM = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortsgearedtractiveeffortfactor": GearedTractiveEffortFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortstractiveeffortfactor": TractiveEffortFactor = stf.ReadFloatBlock(STFReader.UNITS.None, null); break;
case "engine(ortssteamlocomotivetype":
stf.MustMatch("(");
var steamengineType = stf.ReadString();
try
{
SteamEngineType = (SteamEngineTypes)Enum.Parse(typeof(SteamEngineTypes), steamengineType);
}
catch
{
STFException.TraceWarning(stf, "Assumed unknown engine type " + steamengineType);
}
break;
case "engine(ortssteamboilertype":
stf.MustMatch("(");
string typeString1 = stf.ReadString();
IsSaturated = String.Compare(typeString1, "Saturated") == 0;
HasSuperheater = String.Compare(typeString1, "Superheated") == 0;
break;
case "engine(ortssteamgeartype":
stf.MustMatch("(");
string typeString2 = stf.ReadString();
IsFixGeared = String.Compare(typeString2, "Fixed") == 0;
IsSelectGeared = String.Compare(typeString2, "Select") == 0;
break;
case "engine(ortsbattery(mode":
case "engine(ortsbattery(delay":
case "engine(ortsbattery(defaulton":
case "engine(ortsmasterkey(mode":
case "engine(ortsmasterkey(delayoff":
case "engine(ortsmasterkey(headlightcontrol":
LocomotivePowerSupply.Parse(lowercasetoken, stf);
break;
default: base.Parse(lowercasetoken, stf); break;
}
}
/// <summary>
/// This initializer is called when we are making a new copy of a car already
/// loaded in memory. We use this one to speed up loading by eliminating the
/// need to parse the wag file multiple times.
/// NOTE: you must initialize all the same variables as you parsed above
/// </summary>
public override void Copy(MSTSWagon copy)
{
base.Copy(copy); // each derived level initializes its own variables
MSTSSteamLocomotive locoCopy = (MSTSSteamLocomotive)copy;
MSTSNumCylinders = locoCopy.MSTSNumCylinders;
Cylinder1CrankAngleRad = locoCopy.Cylinder1CrankAngleRad;
Cylinder2CrankAngleRad = locoCopy.Cylinder2CrankAngleRad;
Cylinder3CrankAngleRad = locoCopy.Cylinder3CrankAngleRad;
Cylinder4CrankAngleRad = locoCopy.Cylinder4CrankAngleRad;
MSTSCylinderStrokeM = locoCopy.MSTSCylinderStrokeM;
MSTSCylinderDiameterM = locoCopy.MSTSCylinderDiameterM;
MSTSLPNumCylinders = locoCopy.MSTSLPNumCylinders;
MSTSLPCylinderStrokeM = locoCopy.MSTSLPCylinderStrokeM;
MSTSLPCylinderDiameterM = locoCopy.MSTSLPCylinderDiameterM;
CylinderExhaustOpenFactor = locoCopy.CylinderExhaustOpenFactor;
CylinderPortOpeningFactor = locoCopy.CylinderPortOpeningFactor;
BoilerVolumeFT3 = locoCopy.BoilerVolumeFT3;
MaxBoilerPressurePSI = locoCopy.MaxBoilerPressurePSI;
MaxSuperheatRefTempF = locoCopy.MaxSuperheatRefTempF;
MaxIndicatedHorsePowerHP = locoCopy.MaxIndicatedHorsePowerHP;
SuperheatCutoffPressureFactor = locoCopy.SuperheatCutoffPressureFactor;
EjectorSmallSteamConsumptionLbpS = locoCopy.EjectorSmallSteamConsumptionLbpS;
EjectorLargeSteamConsumptionLbpS = locoCopy.EjectorLargeSteamConsumptionLbpS;
ShovelMassKG = locoCopy.ShovelMassKG;
GearedTractiveEffortFactor = locoCopy.GearedTractiveEffortFactor;
TractiveEffortFactor = locoCopy.TractiveEffortFactor;
MaxTenderCoalMassKG = locoCopy.MaxTenderCoalMassKG;
MaxLocoTenderWaterMassKG = locoCopy.MaxLocoTenderWaterMassKG;
MaxFiringRateKGpS = locoCopy.MaxFiringRateKGpS;
Stoker = locoCopy.Stoker;
ORTSMaxFiringRateKGpS = locoCopy.ORTSMaxFiringRateKGpS;
CutoffController = (MSTSNotchController)locoCopy.CutoffController.Clone();
Injector1Controller = (MSTSNotchController)locoCopy.Injector1Controller.Clone();
Injector2Controller = (MSTSNotchController)locoCopy.Injector2Controller.Clone();
BlowerController = (MSTSNotchController)locoCopy.BlowerController.Clone();
DamperController = (MSTSNotchController)locoCopy.DamperController.Clone();
FiringRateController = (MSTSNotchController)locoCopy.FiringRateController.Clone();
FireboxDoorController = (MSTSNotchController)locoCopy.FireboxDoorController.Clone();
SmallEjectorController = (MSTSNotchController)locoCopy.SmallEjectorController.Clone();
LargeEjectorController = (MSTSNotchController)locoCopy.LargeEjectorController.Clone();
GrateAreaM2 = locoCopy.GrateAreaM2;
SuperheaterFactor = locoCopy.SuperheaterFactor;
EvaporationAreaM2 = locoCopy.EvaporationAreaM2;
BoilerSurfaceAreaFt2 = locoCopy.BoilerSurfaceAreaFt2;
FractionBoilerAreaInsulated = locoCopy.FractionBoilerAreaInsulated;
KcInsulation = locoCopy.KcInsulation;
SuperheatAreaM2 = locoCopy.SuperheatAreaM2;
FuelCalorificKJpKG = locoCopy.FuelCalorificKJpKG;
BoilerEvapRateLbspFt2 = locoCopy.BoilerEvapRateLbspFt2;
CylinderEfficiencyRate = locoCopy.CylinderEfficiencyRate;
InitialPressureDropRatioRpMtoX = new Interpolator(locoCopy.InitialPressureDropRatioRpMtoX);
BackPressureIHPtoPSI = new Interpolator(locoCopy.BackPressureIHPtoPSI);
NewBurnRateSteamToCoalLbspH = new Interpolator(locoCopy.NewBurnRateSteamToCoalLbspH);
BoilerEfficiency = locoCopy.BoilerEfficiency;
SteamGearRatioLow = locoCopy.SteamGearRatioLow;
SteamGearRatioHigh = locoCopy.SteamGearRatioHigh;
MaxSteamGearPistonRateFtpM = locoCopy.MaxSteamGearPistonRateFtpM;
SteamEngineType = locoCopy.SteamEngineType;
IsSaturated = locoCopy.IsSaturated;
IsTenderRequired = locoCopy.IsTenderRequired;
HasSuperheater = locoCopy.HasSuperheater;
IsFixGeared = locoCopy.IsFixGeared;
IsSelectGeared = locoCopy.IsSelectGeared;
LargeEjectorControllerFitted = locoCopy.LargeEjectorControllerFitted;
CylinderExhausttoCutoff = locoCopy.CylinderExhausttoCutoff;