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;
71 N1_spinup = 1.0; N2_spinup = 3.0;
79 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 FGTurbine::~FGTurbine()
83 delete IdleThrustLookup;
84 delete MilThrustLookup;
85 delete MaxThrustLookup;
86 delete InjectionLookup;
90 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 void FGTurbine::ResetToIC(void)
96 ThrottlePos = AugmentCmd = 0.0;
97 InletPosition = NozzlePosition = 1.0;
98 Stalled = Seized = Overtemp = Fire = Augmentation = Injection = Reversed = false;
102 OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
105 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 // The main purpose of Calculate() is to determine what phase the engine should
107 // be in, then call the corresponding function.
109 double FGTurbine::Calculate(void)
113 TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556;
114 dt = State->Getdt() * Propulsion->GetRate();
115 ThrottlePos = FCS->GetThrottlePos(EngineNumber);
116 if (ThrottlePos > 1.0) {
117 AugmentCmd = ThrottlePos - 1.0;
118 ThrottlePos -= AugmentCmd;
123 // When trimming is finished check if user wants engine OFF or RUNNING
124 if ((phase == tpTrim) && (dt > 0)) {
125 if (Running && !Starved) {
127 N2 = IdleN2 + ThrottlePos * N2_factor;
128 N1 = IdleN1 + ThrottlePos * N1_factor;
129 OilTemp_degK = 366.0;
139 if (!Running && Cutoff && Starter) {
140 if (phase == tpOff) phase = tpSpinUp;
142 if (!Running && !Cutoff && (N2 > 15.0)) phase = tpStart;
143 if (Cutoff && (phase != tpSpinUp)) phase = tpOff;
144 if (dt == 0) phase = tpTrim;
145 if (Starved) phase = tpOff;
146 if (Stalled) phase = tpStall;
147 if (Seized) phase = tpSeize;
150 case tpOff: thrust = Off(); break;
151 case tpRun: thrust = Run(); break;
152 case tpSpinUp: thrust = SpinUp(); break;
153 case tpStart: thrust = Start(); break;
154 case tpStall: thrust = Stall(); break;
155 case tpSeize: thrust = Seize(); break;
156 case tpTrim: thrust = Trim(); break;
157 default: thrust = Off();
160 thrust = Thruster->Calculate(thrust); // allow thruster to modify thrust (i.e. reversing)
165 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 double FGTurbine::Off(void)
169 double qbar = Auxiliary->Getqbar();
171 FuelFlow_pph = Seek(&FuelFlow_pph, 0, 1000.0, 10000.0);
172 N1 = Seek(&N1, qbar/10.0, N1/2.0, N1/2.0);
173 N2 = Seek(&N2, qbar/15.0, N2/2.0, N2/2.0);
174 EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
175 OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
176 OilPressure_psi = N2 * 0.62;
177 NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
178 EPR = Seek(&EPR, 1.0, 0.2, 0.2);
179 Augmentation = false;
184 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 double FGTurbine::Run()
188 double idlethrust, milthrust, thrust;
189 double N2norm; // 0.0 = idle N2, 1.0 = maximum N2
191 idlethrust = MilThrust * IdleThrustLookup->GetValue();
192 milthrust = (MilThrust - idlethrust) * MilThrustLookup->GetValue();
197 N2 = Seek(&N2, IdleN2 + ThrottlePos * N2_factor, delay, delay * 3.0);
198 N1 = Seek(&N1, IdleN1 + ThrottlePos * N1_factor, delay, delay * 2.4);
199 N2norm = (N2 - IdleN2) / N2_factor;
200 thrust = idlethrust + (milthrust * N2norm * N2norm);
201 EGT_degC = TAT + 363.1 + ThrottlePos * 357.1;
202 OilPressure_psi = N2 * 0.62;
203 OilTemp_degK = Seek(&OilTemp_degK, 366.0, 1.2, 0.1);
206 correctedTSFC = TSFC * (0.84 + (1-N2norm)*(1-N2norm));
207 FuelFlow_pph = Seek(&FuelFlow_pph, thrust * correctedTSFC, 1000.0, 100000);
208 if (FuelFlow_pph < IdleFF) FuelFlow_pph = IdleFF;
209 NozzlePosition = Seek(&NozzlePosition, 1.0 - N2norm, 0.8, 0.8);
210 thrust = thrust * (1.0 - BleedDemand);
211 EPR = 1.0 + thrust/MilThrust;
214 if (AugMethod == 1) {
215 if ((ThrottlePos > 0.99) && (N2 > 97.0)) {Augmentation = true;}
216 else {Augmentation = false;}
219 if ((Augmented == 1) && Augmentation && (AugMethod < 2)) {
220 thrust = MaxThrustLookup->GetValue() * MaxThrust;
221 FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
222 NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
225 if (AugMethod == 2) {
226 if (AugmentCmd > 0.0) {
228 double tdiff = (MaxThrust * MaxThrustLookup->GetValue()) - thrust;
229 thrust += (tdiff * AugmentCmd);
230 FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
231 NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
233 Augmentation = false;
237 if ((Injected == 1) && Injection) {
238 InjectionTimer += dt;
239 if (InjectionTimer < InjectionTime) {
240 thrust = thrust * InjectionLookup->GetValue();
247 if (Cutoff) phase = tpOff;
248 if (Starved) phase = tpOff;
253 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 double FGTurbine::SpinUp(void)
259 N2 = Seek(&N2, 25.18, N2_spinup, N2/2.0);
260 N1 = Seek(&N1, 5.21, N1_spinup, N1/2.0);
261 EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
262 OilPressure_psi = N2 * 0.62;
263 OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
265 NozzlePosition = 1.0;
269 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271 double FGTurbine::Start(void)
273 if ((N2 > 15.0) && !Starved) { // minimum 15% N2 needed for start
274 Cranking = true; // provided for sound effects signal
276 N2 = Seek(&N2, IdleN2, 2.0, N2/2.0);
277 N1 = Seek(&N1, IdleN1, 1.4, N1/2.0);
278 EGT_degC = Seek(&EGT_degC, TAT + 363.1, 21.3, 7.3);
279 FuelFlow_pph = Seek(&FuelFlow_pph, IdleFF, 103.7, 103.7);
280 OilPressure_psi = N2 * 0.62;
290 else { // no start if N2 < 15%
298 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300 double FGTurbine::Stall(void)
302 double qbar = Auxiliary->Getqbar();
303 EGT_degC = TAT + 903.14;
304 FuelFlow_pph = IdleFF;
305 N1 = Seek(&N1, qbar/10.0, 0, N1/10.0);
306 N2 = Seek(&N2, qbar/15.0, 0, N2/10.0);
308 if (ThrottlePos < 0.01) phase = tpRun; // clear the stall with throttle
313 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315 double FGTurbine::Seize(void)
317 double qbar = Auxiliary->Getqbar();
319 N1 = Seek(&N1, qbar/20.0, 0, N1/15.0);
320 FuelFlow_pph = IdleFF;
322 OilPressure_psi = 0.0;
323 OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0, 0.2);
328 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330 double FGTurbine::Trim()
332 double idlethrust, milthrust, thrust, tdiff, N2norm;
333 idlethrust = MilThrust * IdleThrustLookup->GetValue();
334 milthrust = (MilThrust - idlethrust) * MilThrustLookup->GetValue();
335 N2 = IdleN2 + ThrottlePos * N2_factor;
336 N2norm = (N2 - IdleN2) / N2_factor;
337 thrust = (idlethrust + (milthrust * N2norm * N2norm))
338 * (1.0 - BleedDemand);
340 if (AugMethod == 1) {
341 if ((ThrottlePos > 0.99) && (N2 > 97.0)) {Augmentation = true;}
342 else {Augmentation = false;}
345 if ((Augmented == 1) && Augmentation && (AugMethod < 2)) {
346 thrust = MaxThrust * MaxThrustLookup->GetValue();
349 if (AugMethod == 2) {
350 if (AugmentCmd > 0.0) {
351 tdiff = (MaxThrust * MaxThrustLookup->GetValue()) - thrust;
352 thrust += (tdiff * AugmentCmd);
356 if ((Injected == 1) && Injection) {
357 thrust = thrust * InjectionLookup->GetValue();
363 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
365 double FGTurbine::CalcFuelNeed(void)
367 double dT = State->Getdt() * Propulsion->GetRate();
368 FuelFlowRate = FuelFlow_pph / 3600.0; // Calculates flow in lbs/sec from lbs/hr
369 FuelExpended = FuelFlowRate * dT; // Calculates fuel expended in this time step
373 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 double FGTurbine::GetPowerAvailable(void) {
376 if( ThrottlePos <= 0.77 )
377 return 64.94*ThrottlePos;
379 return 217.38*ThrottlePos - 117.38;
382 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
384 double FGTurbine::Seek(double *var, double target, double accel, double decel) {
388 if (v < target) v = target;
389 } else if (v < target) {
391 if (v > target) v = target;
396 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 bool FGTurbine::Load(FGFDMExec* exec, Element *el)
400 string property_name, property_prefix;
401 property_prefix = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
403 if (el->FindElement("milthrust"))
404 MilThrust = el->FindElementValueAsNumberConvertTo("milthrust","LBS");
405 if (el->FindElement("maxthrust"))
406 MaxThrust = el->FindElementValueAsNumberConvertTo("maxthrust","LBS");
407 if (el->FindElement("bypassratio"))
408 BypassRatio = el->FindElementValueAsNumber("bypassratio");
409 if (el->FindElement("bleed"))
410 BleedDemand = el->FindElementValueAsNumber("bleed");
411 if (el->FindElement("tsfc"))
412 TSFC = el->FindElementValueAsNumber("tsfc");
413 if (el->FindElement("atsfc"))
414 ATSFC = el->FindElementValueAsNumber("atsfc");
415 if (el->FindElement("idlen1"))
416 IdleN1 = el->FindElementValueAsNumber("idlen1");
417 if (el->FindElement("idlen2"))
418 IdleN2 = el->FindElementValueAsNumber("idlen2");
419 if (el->FindElement("maxn1"))
420 MaxN1 = el->FindElementValueAsNumber("maxn1");
421 if (el->FindElement("maxn2"))
422 MaxN2 = el->FindElementValueAsNumber("maxn2");
423 if (el->FindElement("n1spinup"))
424 N1_spinup = el->FindElementValueAsNumber("n1spinup");
425 if (el->FindElement("n2spinup"))
426 N2_spinup = el->FindElementValueAsNumber("n2spinup");
427 if (el->FindElement("augmented"))
428 Augmented = (int)el->FindElementValueAsNumber("augmented");
429 if (el->FindElement("augmethod"))
430 AugMethod = (int)el->FindElementValueAsNumber("augmethod");
431 if (el->FindElement("injected"))
432 Injected = (int)el->FindElementValueAsNumber("injected");
433 if (el->FindElement("injection-time"))
434 InjectionTime = el->FindElementValueAsNumber("injection-time");
436 Element *function_element;
438 FGPropertyManager* PropertyManager = exec->GetPropertyManager();
441 function_element = el->FindNextElement("function");
442 if (!function_element) break;
443 name = function_element->GetAttributeValue("name");
444 if (name == "IdleThrust") {
445 IdleThrustLookup = new FGFunction(PropertyManager, function_element, property_prefix);
446 } else if (name == "MilThrust") {
447 MilThrustLookup = new FGFunction(PropertyManager, function_element, property_prefix);
448 } else if (name == "AugThrust") {
449 MaxThrustLookup = new FGFunction(PropertyManager, function_element, property_prefix);
450 } else if (name == "Injection") {
451 InjectionLookup = new FGFunction(PropertyManager, function_element, property_prefix);
453 cerr << "Unknown function type: " << name << " in turbine definition." <<
458 // Pre-calculations and initializations
460 delay = 60.0 / (BypassRatio + 3.0);
461 N1_factor = MaxN1 - IdleN1;
462 N2_factor = MaxN2 - IdleN2;
463 OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
464 IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 string FGTurbine::GetEngineLabels(string delimeter)
474 std::ostringstream buf;
476 buf << Name << "_N1[" << EngineNumber << "]" << delimeter
477 << Name << "_N2[" << EngineNumber << "]" << delimeter
478 << Thruster->GetThrusterLabels(EngineNumber, delimeter);
483 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 string FGTurbine::GetEngineValues(string delimeter)
487 std::ostringstream buf;
489 buf << N1 << delimeter
491 << Thruster->GetThrusterValues(EngineNumber, delimeter);
496 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 void FGTurbine::bindmodel()
500 string property_name, base_property_name;
501 base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
502 property_name = base_property_name + "/n1";
503 PropertyManager->Tie( property_name.c_str(), &N1);
504 property_name = base_property_name + "/n2";
505 PropertyManager->Tie( property_name.c_str(), &N2);
506 property_name = base_property_name + "/injection_cmd";
507 PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this,
508 &FGTurbine::GetInjection, &FGTurbine::SetInjection);
511 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 int FGTurbine::InitRunning(void) {
514 State->SuspendIntegration();
519 State->ResumeIntegration();
523 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524 // The bitmasked value choices are as follows:
525 // unset: In this case (the default) JSBSim would only print
526 // out the normally expected messages, essentially echoing
527 // the config files as they are read. If the environment
528 // variable is not set, debug_lvl is set to 1 internally
529 // 0: This requests JSBSim not to output any messages
531 // 1: This value explicity requests the normal JSBSim
533 // 2: This value asks for a message to be printed out when
534 // a class is instantiated
535 // 4: When this value is set, a message is displayed when a
536 // FGModel object executes its Run() method
537 // 8: When this value is set, various runtime state variables
538 // are printed out periodically
539 // 16: When set various parameters are sanity checked and
540 // a message is printed out when they go out of bounds
542 void FGTurbine::Debug(int from)
544 if (debug_lvl <= 0) return;
546 if (debug_lvl & 1) { // Standard console startup message output
547 if (from == 0) { // Constructor
550 if (from == 2) { // called from Load()
551 cout << "\n Engine Name: " << Name << endl;
552 cout << " MilThrust: " << MilThrust << endl;
553 cout << " MaxThrust: " << MaxThrust << endl;
554 cout << " BypassRatio: " << BypassRatio << endl;
555 cout << " TSFC: " << TSFC << endl;
556 cout << " ATSFC: " << ATSFC << endl;
557 cout << " IdleN1: " << IdleN1 << endl;
558 cout << " IdleN2: " << IdleN2 << endl;
559 cout << " MaxN1: " << MaxN1 << endl;
560 cout << " MaxN2: " << MaxN2 << endl;
561 cout << " Augmented: " << Augmented << endl;
562 cout << " AugMethod: " << AugMethod << endl;
563 cout << " Injected: " << Injected << endl;
564 cout << " MinThrottle: " << MinThrottle << endl;
569 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
570 if (from == 0) cout << "Instantiated: FGTurbine" << endl;
571 if (from == 1) cout << "Destroyed: FGTurbine" << endl;
573 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
575 if (debug_lvl & 8 ) { // Runtime state variables
577 if (debug_lvl & 16) { // Sanity checking
579 if (debug_lvl & 64) {
580 if (from == 0) { // Constructor
581 cout << IdSrc << endl;
582 cout << IdHdr << endl;