1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 Date started: 03/11/2003
6 Purpose: This module models a turbine engine.
8 ------------- Copyright (C) 2003 David Culp (davidculp2@comcast.net) ---------
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
20 You should have received a copy of the GNU Lesser General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 Place - Suite 330, Boston, MA 02111-1307, USA.
24 Further information about the GNU Lesser General Public License can also be found on
25 the world wide web at http://www.gnu.org.
27 FUNCTIONAL DESCRIPTION
28 --------------------------------------------------------------------------------
30 This class descends from the FGEngine class and models a turbine engine based
31 on parameters given in the engine config file for this class
34 --------------------------------------------------------------------------------
35 03/11/2003 DPC Created
36 09/08/2003 DPC Changed Calculate() and added engine phases
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
45 #include "FGTurbine.h"
49 static const char *IdSrc = "$Id$";
50 static const char *IdHdr = ID_TURBINE;
52 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
57 FGTurbine::FGTurbine(FGFDMExec* exec, Element *el, int engine_number)
58 : FGEngine(exec, el, engine_number)
62 MilThrust = MaxThrust = 10000.0;
67 MaxN1 = MaxN2 = 100.0;
68 Augmented = AugMethod = Injected = 0;
69 BypassRatio = BleedDemand = 0.0;
70 IdleThrustLookup = MilThrustLookup = MaxThrustLookup = InjectionLookup = 0;
78 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 FGTurbine::~FGTurbine()
82 delete IdleThrustLookup;
83 delete MilThrustLookup;
84 delete MaxThrustLookup;
85 delete InjectionLookup;
89 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 void FGTurbine::ResetToIC(void)
95 ThrottlePos = AugmentCmd = 0.0;
96 InletPosition = NozzlePosition = 1.0;
97 Stalled = Seized = Overtemp = Fire = Augmentation = Injection = Reversed = false;
101 OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
104 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105 // The main purpose of Calculate() is to determine what phase the engine should
106 // be in, then call the corresponding function.
108 double FGTurbine::Calculate(void)
110 TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556;
111 dt = State->Getdt() * Propulsion->GetRate();
112 ThrottlePos = FCS->GetThrottlePos(EngineNumber);
113 if (ThrottlePos > 1.0) {
114 AugmentCmd = ThrottlePos - 1.0;
115 ThrottlePos -= AugmentCmd;
120 // When trimming is finished check if user wants engine OFF or RUNNING
121 if ((phase == tpTrim) && (dt > 0)) {
122 if (Running && !Starved) {
124 N2 = IdleN2 + ThrottlePos * N2_factor;
125 N1 = IdleN1 + ThrottlePos * N1_factor;
126 OilTemp_degK = 366.0;
136 if (!Running && Cutoff && Starter) {
137 if (phase == tpOff) phase = tpSpinUp;
139 if (!Running && !Cutoff && (N2 > 15.0)) phase = tpStart;
140 if (Cutoff && (phase != tpSpinUp)) phase = tpOff;
141 if (dt == 0) phase = tpTrim;
142 if (Starved) phase = tpOff;
143 if (Stalled) phase = tpStall;
144 if (Seized) phase = tpSeize;
147 case tpOff: Thrust = Off(); break;
148 case tpRun: Thrust = Run(); break;
149 case tpSpinUp: Thrust = SpinUp(); break;
150 case tpStart: Thrust = Start(); break;
151 case tpStall: Thrust = Stall(); break;
152 case tpSeize: Thrust = Seize(); break;
153 case tpTrim: Thrust = Trim(); break;
154 default: Thrust = Off();
157 Thrust = Thruster->Calculate(Thrust); // allow thruster to modify thrust (i.e. reversing)
162 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
164 double FGTurbine::Off(void)
166 double qbar = Auxiliary->Getqbar();
168 FuelFlow_pph = Seek(&FuelFlow_pph, 0, 1000.0, 10000.0);
169 N1 = Seek(&N1, qbar/10.0, N1/2.0, N1/2.0);
170 N2 = Seek(&N2, qbar/15.0, N2/2.0, N2/2.0);
171 EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
172 OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
173 OilPressure_psi = N2 * 0.62;
174 NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
175 EPR = Seek(&EPR, 1.0, 0.2, 0.2);
176 Augmentation = false;
181 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183 double FGTurbine::Run()
185 double idlethrust, milthrust, thrust;
186 double N2norm; // 0.0 = idle N2, 1.0 = maximum N2
188 idlethrust = MilThrust * IdleThrustLookup->GetValue();
189 milthrust = (MilThrust - idlethrust) * MilThrustLookup->GetValue();
194 N2 = Seek(&N2, IdleN2 + ThrottlePos * N2_factor, delay, delay * 3.0);
195 N1 = Seek(&N1, IdleN1 + ThrottlePos * N1_factor, delay, delay * 2.4);
196 N2norm = (N2 - IdleN2) / N2_factor;
197 thrust = idlethrust + (milthrust * N2norm * N2norm);
198 EGT_degC = TAT + 363.1 + ThrottlePos * 357.1;
199 OilPressure_psi = N2 * 0.62;
200 OilTemp_degK = Seek(&OilTemp_degK, 366.0, 1.2, 0.1);
203 correctedTSFC = TSFC * (0.84 + (1-N2norm)*(1-N2norm));
204 FuelFlow_pph = Seek(&FuelFlow_pph, thrust * correctedTSFC, 1000.0, 100000);
205 if (FuelFlow_pph < IdleFF) FuelFlow_pph = IdleFF;
206 NozzlePosition = Seek(&NozzlePosition, 1.0 - N2norm, 0.8, 0.8);
207 thrust = thrust * (1.0 - BleedDemand);
208 EPR = 1.0 + thrust/MilThrust;
211 if (AugMethod == 1) {
212 if ((ThrottlePos > 0.99) && (N2 > 97.0)) {Augmentation = true;}
213 else {Augmentation = false;}
216 if ((Augmented == 1) && Augmentation && (AugMethod < 2)) {
217 thrust = MaxThrustLookup->GetValue() * MaxThrust;
218 FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
219 NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
222 if (AugMethod == 2) {
223 if (AugmentCmd > 0.0) {
225 double tdiff = (MaxThrust * MaxThrustLookup->GetValue()) - thrust;
226 thrust += (tdiff * AugmentCmd);
227 FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
228 NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
230 Augmentation = false;
234 if ((Injected == 1) && Injection) {
235 InjectionTimer += dt;
236 if (InjectionTimer < InjectionTime) {
237 thrust = thrust * InjectionLookup->GetValue();
244 if (Cutoff) phase = tpOff;
245 if (Starved) phase = tpOff;
250 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252 double FGTurbine::SpinUp(void)
256 N2 = Seek(&N2, 25.18, 3.0, N2/2.0);
257 N1 = Seek(&N1, 5.21, 1.0, N1/2.0);
258 EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
259 OilPressure_psi = N2 * 0.62;
260 OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
262 NozzlePosition = 1.0;
266 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
268 double FGTurbine::Start(void)
270 if ((N2 > 15.0) && !Starved) { // minimum 15% N2 needed for start
271 Cranking = true; // provided for sound effects signal
273 N2 = Seek(&N2, IdleN2, 2.0, N2/2.0);
274 N1 = Seek(&N1, IdleN1, 1.4, N1/2.0);
275 EGT_degC = Seek(&EGT_degC, TAT + 363.1, 21.3, 7.3);
276 FuelFlow_pph = Seek(&FuelFlow_pph, IdleFF, 103.7, 103.7);
277 OilPressure_psi = N2 * 0.62;
287 else { // no start if N2 < 15%
295 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297 double FGTurbine::Stall(void)
299 double qbar = Auxiliary->Getqbar();
300 EGT_degC = TAT + 903.14;
301 FuelFlow_pph = IdleFF;
302 N1 = Seek(&N1, qbar/10.0, 0, N1/10.0);
303 N2 = Seek(&N2, qbar/15.0, 0, N2/10.0);
305 if (ThrottlePos < 0.01) phase = tpRun; // clear the stall with throttle
310 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312 double FGTurbine::Seize(void)
314 double qbar = Auxiliary->Getqbar();
316 N1 = Seek(&N1, qbar/20.0, 0, N1/15.0);
317 FuelFlow_pph = IdleFF;
319 OilPressure_psi = 0.0;
320 OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0, 0.2);
325 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 double FGTurbine::Trim()
329 double idlethrust, milthrust, thrust, tdiff, N2norm;
330 idlethrust = MilThrust * IdleThrustLookup->GetValue();
331 milthrust = (MilThrust - idlethrust) * MilThrustLookup->GetValue();
332 N2 = IdleN2 + ThrottlePos * N2_factor;
333 N2norm = (N2 - IdleN2) / N2_factor;
334 thrust = (idlethrust + (milthrust * N2norm * N2norm))
335 * (1.0 - BleedDemand);
337 if (AugMethod == 1) {
338 if ((ThrottlePos > 0.99) && (N2 > 97.0)) {Augmentation = true;}
339 else {Augmentation = false;}
342 if ((Augmented == 1) && Augmentation && (AugMethod < 2)) {
343 thrust = MaxThrust * MaxThrustLookup->GetValue();
346 if (AugMethod == 2) {
347 if (AugmentCmd > 0.0) {
348 tdiff = (MaxThrust * MaxThrustLookup->GetValue()) - thrust;
349 thrust += (tdiff * AugmentCmd);
353 if ((Injected == 1) && Injection) {
354 thrust = thrust * InjectionLookup->GetValue();
360 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362 double FGTurbine::CalcFuelNeed(void)
364 double dT = State->Getdt() * Propulsion->GetRate();
365 FuelFlowRate = FuelFlow_pph / 3600.0; // Calculates flow in lbs/sec from lbs/hr
366 FuelExpended = FuelFlowRate * dT; // Calculates fuel expended in this time step
370 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372 double FGTurbine::GetPowerAvailable(void) {
373 if( ThrottlePos <= 0.77 )
374 return 64.94*ThrottlePos;
376 return 217.38*ThrottlePos - 117.38;
379 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381 double FGTurbine::Seek(double *var, double target, double accel, double decel) {
385 if (v < target) v = target;
386 } else if (v < target) {
388 if (v > target) v = target;
393 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395 bool FGTurbine::Load(FGFDMExec* exec, Element *el)
397 char property_prefix[80];
398 snprintf(property_prefix, 80, "propulsion/engine[%u]/", EngineNumber);
400 if (el->FindElement("milthrust"))
401 MilThrust = el->FindElementValueAsNumberConvertTo("milthrust","LBS");
402 if (el->FindElement("maxthrust"))
403 MaxThrust = el->FindElementValueAsNumberConvertTo("maxthrust","LBS");
404 if (el->FindElement("bypassratio"))
405 BypassRatio = el->FindElementValueAsNumber("bypassratio");
406 if (el->FindElement("bleed"))
407 BleedDemand = el->FindElementValueAsNumber("bleed");
408 if (el->FindElement("tsfc"))
409 TSFC = el->FindElementValueAsNumber("tsfc");
410 if (el->FindElement("atsfc"))
411 ATSFC = el->FindElementValueAsNumber("atsfc");
412 if (el->FindElement("idlen1"))
413 IdleN1 = el->FindElementValueAsNumber("idlen1");
414 if (el->FindElement("idlen2"))
415 IdleN2 = el->FindElementValueAsNumber("idlen2");
416 if (el->FindElement("maxn1"))
417 MaxN1 = el->FindElementValueAsNumber("maxn1");
418 if (el->FindElement("maxn2"))
419 MaxN2 = el->FindElementValueAsNumber("maxn2");
420 if (el->FindElement("augmented"))
421 Augmented = (int)el->FindElementValueAsNumber("augmented");
422 if (el->FindElement("augmethod"))
423 AugMethod = (int)el->FindElementValueAsNumber("augmethod");
424 if (el->FindElement("injected"))
425 Injected = (int)el->FindElementValueAsNumber("injected");
426 if (el->FindElement("injection-time"))
427 InjectionTime = el->FindElementValueAsNumber("injection-time");
429 Element *function_element;
431 FGPropertyManager* PropertyManager = exec->GetPropertyManager();
434 function_element = el->FindNextElement("function");
435 if (!function_element) break;
436 name = function_element->GetAttributeValue("name");
437 if (name == "IdleThrust") {
438 IdleThrustLookup = new FGFunction(PropertyManager, function_element, property_prefix);
439 } else if (name == "MilThrust") {
440 MilThrustLookup = new FGFunction(PropertyManager, function_element, property_prefix);
441 } else if (name == "AugThrust") {
442 MaxThrustLookup = new FGFunction(PropertyManager, function_element, property_prefix);
443 } else if (name == "Injection") {
444 InjectionLookup = new FGFunction(PropertyManager, function_element, property_prefix);
446 cerr << "Unknown function type: " << name << " in turbine definition." <<
451 // Pre-calculations and initializations
453 delay = 60.0 / (BypassRatio + 3.0);
454 N1_factor = MaxN1 - IdleN1;
455 N2_factor = MaxN2 - IdleN2;
456 OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
457 IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate
463 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 string FGTurbine::GetEngineLabels(string delimeter)
467 std::ostringstream buf;
469 buf << Name << "_N1[" << EngineNumber << "]" << delimeter
470 << Name << "_N2[" << EngineNumber << "]" << delimeter
471 << Thruster->GetThrusterLabels(EngineNumber, delimeter);
476 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478 string FGTurbine::GetEngineValues(string delimeter)
480 std::ostringstream buf;
482 buf << N1 << delimeter
484 << Thruster->GetThrusterValues(EngineNumber, delimeter);
489 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 void FGTurbine::bindmodel()
493 char property_name[80];
495 snprintf(property_name, 80, "propulsion/engine[%u]/n1", EngineNumber);
496 PropertyManager->Tie( property_name, &N1);
497 snprintf(property_name, 80, "propulsion/engine[%u]/n2", EngineNumber);
498 PropertyManager->Tie( property_name, &N2);
499 snprintf(property_name, 80, "propulsion/engine[%u]/injection_cmd", EngineNumber);
500 PropertyManager->Tie( property_name, (FGTurbine*)this,
501 &FGTurbine::GetInjection, &FGTurbine::SetInjection);
504 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 int FGTurbine::InitRunning(void) {
507 State->SuspendIntegration();
512 State->ResumeIntegration();
516 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 // The bitmasked value choices are as follows:
518 // unset: In this case (the default) JSBSim would only print
519 // out the normally expected messages, essentially echoing
520 // the config files as they are read. If the environment
521 // variable is not set, debug_lvl is set to 1 internally
522 // 0: This requests JSBSim not to output any messages
524 // 1: This value explicity requests the normal JSBSim
526 // 2: This value asks for a message to be printed out when
527 // a class is instantiated
528 // 4: When this value is set, a message is displayed when a
529 // FGModel object executes its Run() method
530 // 8: When this value is set, various runtime state variables
531 // are printed out periodically
532 // 16: When set various parameters are sanity checked and
533 // a message is printed out when they go out of bounds
535 void FGTurbine::Debug(int from)
537 if (debug_lvl <= 0) return;
539 if (debug_lvl & 1) { // Standard console startup message output
540 if (from == 0) { // Constructor
543 if (from == 2) { // called from Load()
544 cout << "\n Engine Name: " << Name << endl;
545 cout << " MilThrust: " << MilThrust << endl;
546 cout << " MaxThrust: " << MaxThrust << endl;
547 cout << " BypassRatio: " << BypassRatio << endl;
548 cout << " TSFC: " << TSFC << endl;
549 cout << " ATSFC: " << ATSFC << endl;
550 cout << " IdleN1: " << IdleN1 << endl;
551 cout << " IdleN2: " << IdleN2 << endl;
552 cout << " MaxN1: " << MaxN1 << endl;
553 cout << " MaxN2: " << MaxN2 << endl;
554 cout << " Augmented: " << Augmented << endl;
555 cout << " AugMethod: " << AugMethod << endl;
556 cout << " Injected: " << Injected << endl;
557 cout << " MinThrottle: " << MinThrottle << endl;
562 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
563 if (from == 0) cout << "Instantiated: FGTurbine" << endl;
564 if (from == 1) cout << "Destroyed: FGTurbine" << endl;
566 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
568 if (debug_lvl & 8 ) { // Runtime state variables
570 if (debug_lvl & 16) { // Sanity checking
572 if (debug_lvl & 64) {
573 if (from == 0) { // Constructor
574 cout << IdSrc << endl;
575 cout << IdHdr << endl;