1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGPropulsion.cpp
6 Purpose: Encapsulates the set of engines and tanks associated
9 ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) -------------
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU Lesser General Public License as published by the Free Software
13 Foundation; either version 2 of the License, or (at your option) any later
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
21 You should have received a copy of the GNU Lesser General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place - Suite 330, Boston, MA 02111-1307, USA.
25 Further information about the GNU Lesser General Public License can also be found on
26 the world wide web at http://www.gnu.org.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 The Propulsion class is the container for the entire propulsion system, which is
31 comprised of engines and tanks. Once the Propulsion class gets the config file,
32 it reads in information which is specific to a type of engine. Then:
34 1) The appropriate engine type instance is created
35 2) At least one tank object is created, and is linked to an engine.
37 At Run time each engines Calculate() method is called.
40 --------------------------------------------------------------------------------
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
47 #include "FGPropulsion.h"
49 #include "models/FGFCS.h"
50 #include "models/FGMassBalance.h"
51 #include "models/propulsion/FGThruster.h"
52 #include "models/propulsion/FGRocket.h"
53 #include "models/propulsion/FGTurbine.h"
54 #include "models/propulsion/FGPiston.h"
55 #include "models/propulsion/FGElectric.h"
56 #include "models/propulsion/FGTurboProp.h"
57 #include "models/propulsion/FGTank.h"
58 #include "input_output/FGPropertyManager.h"
59 #include "input_output/FGXMLParse.h"
60 #include "math/FGColumnVector3.h"
69 static const char *IdSrc = "$Id$";
70 static const char *IdHdr = ID_PROPULSION;
72 extern short debug_lvl;
75 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
79 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
81 Name = "FGPropulsion";
83 InitializedEngines = 0;
84 numSelectedFuelTanks = numSelectedOxiTanks = 0;
85 numTanks = numEngines = 0;
86 numOxiTanks = numFuelTanks = 0;
87 ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
89 refuel = dump = false;
92 TotalFuelQuantity = 0.0;
98 HaveElectricEngine = false;
99 HasInitializedEngines = false;
104 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 FGPropulsion::~FGPropulsion()
108 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
110 for (unsigned int i=0; i<Tanks.size(); i++) delete Tanks[i];
115 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 bool FGPropulsion::InitModel(void)
119 if (!FGModel::InitModel()) return false;
121 for (unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
123 for (unsigned int i=0; i<numEngines; i++) {
124 switch (Engines[i]->GetType()) {
125 case FGEngine::etPiston:
126 ((FGPiston*)Engines[i])->ResetToIC();
127 if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
129 case FGEngine::etTurbine:
130 ((FGTurbine*)Engines[i])->ResetToIC();
131 if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
141 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143 bool FGPropulsion::Run(void)
147 if (FGModel::Run()) return true;
148 if (FDMExec->Holding()) return false;
150 double dt = State->Getdt();
152 vForces.InitMatrix();
153 vMoments.InitMatrix();
155 for (i=0; i<numEngines; i++) {
156 Engines[i]->Calculate();
157 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
158 vMoments += Engines[i]->GetMoments(); // sum body frame moments
161 TotalFuelQuantity = 0.0;
162 for (i=0; i<numTanks; i++) {
163 Tanks[i]->Calculate( dt * rate );
164 if (Tanks[i]->GetType() == FGTank::ttFUEL) {
165 TotalFuelQuantity += Tanks[i]->GetContents();
169 if (refuel) DoRefuel( dt * rate );
170 if (dump) DumpFuel( dt * rate );
175 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
177 bool FGPropulsion::GetSteadyState(void)
179 double currentThrust = 0, lastThrust = -1;
180 int steady_count = 0, j = 0;
183 vForces.InitMatrix();
184 vMoments.InitMatrix();
186 if (!FGModel::Run()) {
187 for (unsigned int i=0; i<numEngines; i++) {
188 // cout << " Finding steady state for engine " << i << endl;
189 Engines[i]->SetTrimMode(true);
193 while (!steady && j < 6000) {
194 Engines[i]->Calculate();
195 lastThrust = currentThrust;
196 currentThrust = Engines[i]->GetThruster()->GetThrust();
197 if (fabs(lastThrust-currentThrust) < 0.0001) {
199 if (steady_count > 120) {
201 // cout << " Steady state found at thrust: " << currentThrust << " lbs." << endl;
209 // cout << " Could not find a steady state for this engine." << endl;
211 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
212 vMoments += Engines[i]->GetMoments(); // sum body frame moments
213 Engines[i]->SetTrimMode(false);
222 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224 void FGPropulsion::InitRunning(int n)
226 if (n > 0) { // A specific engine is supposed to be initialized
228 if (n >= (int)GetNumEngines() ) {
229 cerr << "Tried to initialize a non-existent engine!" << endl;
232 FCS->SetThrottleCmd(n,1);
233 FCS->SetMixtureCmd(n,1);
234 GetEngine(n)->InitRunning();
237 InitializedEngines = 1 << n;
238 HasInitializedEngines = true;
240 } else if (n < 0) { // -1 refers to "All Engines"
242 for (unsigned int i=0; i<GetNumEngines(); i++) {
243 FCS->SetThrottleCmd(i,1);
244 FCS->SetMixtureCmd(i,1);
245 GetEngine(i)->InitRunning();
248 InitializedEngines = -1;
249 HasInitializedEngines = true;
251 } else if (n == 0) { // No engines are to be initialized
256 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258 bool FGPropulsion::Load(Element* el)
260 string type, engine_filename;
261 bool ThrottleAdded = false;
265 FGModel::Load(el); // Perform base class Load.
267 // Process tank definitions first to establish the number of fuel tanks
269 Element* tank_element = el->FindElement("tank");
270 while (tank_element) {
271 Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
272 if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
273 else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
274 else {cerr << "Unknown tank type specified." << endl; return false;}
276 tank_element = el->FindNextElement("tank");
278 numSelectedFuelTanks = numFuelTanks;
279 numSelectedOxiTanks = numOxiTanks;
281 Element* engine_element = el->FindElement("engine");
282 while (engine_element) {
283 engine_filename = engine_element->GetAttributeValue("file");
285 if (engine_filename.empty()) {
286 cerr << "Engine definition did not supply an engine file." << endl;
290 engine_filename = FindEngineFullPathname(engine_filename);
291 document = LoadXMLDocument(engine_filename);
292 document->SetParent(engine_element);
294 type = document->GetName();
295 if (type == "piston_engine") {
296 HavePistonEngine = true;
297 if (!IsBound) bind();
298 Engines.push_back(new FGPiston(FDMExec, document, numEngines));
299 } else if (type == "turbine_engine") {
300 HaveTurbineEngine = true;
301 if (!IsBound) bind();
302 Engines.push_back(new FGTurbine(FDMExec, document, numEngines));
303 } else if (type == "turboprop_engine") {
304 HaveTurboPropEngine = true;
305 if (!IsBound) bind();
306 Engines.push_back(new FGTurboProp(FDMExec, document, numEngines));
307 } else if (type == "rocket_engine") {
308 HaveRocketEngine = true;
309 if (!IsBound) bind();
310 Engines.push_back(new FGRocket(FDMExec, document, numEngines));
311 } else if (type == "electric_engine") {
312 HaveElectricEngine = true;
313 if (!IsBound) bind();
314 Engines.push_back(new FGElectric(FDMExec, document, numEngines));
316 cerr << "Unknown engine type: " << type << endl;
321 ThrottleAdded = true;
325 engine_element = el->FindNextElement("engine");
329 CalculateTankInertias();
330 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
332 // Process fuel dump rate
333 if (el->FindElement("dump-rate"))
334 DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
340 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
344 string fullpath, localpath;
345 string enginePath = FDMExec->GetEnginePath();
346 string aircraftPath = FDMExec->GetFullAircraftPath();
347 ifstream engine_file;
349 string separator = "/";
351 fullpath = enginePath + separator;
352 localpath = aircraftPath + separator + "Engines" + separator;
354 engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
355 if ( !engine_file.is_open()) {
356 engine_file.open(string(localpath + engine_filename + ".xml").c_str());
357 if ( !engine_file.is_open()) {
358 cerr << " Could not open engine file: " << engine_filename << " in path "
359 << fullpath << " or " << localpath << endl;
362 return string(localpath + engine_filename + ".xml");
365 return string(fullpath + engine_filename + ".xml");
368 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
372 string fullpath, localpath;
373 string enginePath = FDMExec->GetEnginePath();
374 string aircraftPath = FDMExec->GetFullAircraftPath();
375 ifstream* engine_file = new ifstream();
377 string separator = "/";
379 fullpath = enginePath + separator;
380 localpath = aircraftPath + separator + "Engines" + separator;
382 engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
383 if ( !engine_file->is_open()) {
384 engine_file->open(string(localpath + engine_filename + ".xml").c_str());
385 if ( !engine_file->is_open()) {
386 cerr << " Could not open engine file: " << engine_filename << " in path "
387 << fullpath << " or " << localpath << endl;
393 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395 string FGPropulsion::GetPropulsionStrings(const string& delimiter)
399 string PropulsionStrings = "";
400 bool firstime = true;
403 for (i=0; i<Engines.size(); i++) {
404 if (firstime) firstime = false;
405 else PropulsionStrings += delimiter;
407 PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
409 for (i=0; i<Tanks.size(); i++) {
410 if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
411 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
414 return PropulsionStrings;
417 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419 string FGPropulsion::GetPropulsionValues(const string& delimiter)
423 string PropulsionValues = "";
424 bool firstime = true;
427 for (i=0; i<Engines.size(); i++) {
428 if (firstime) firstime = false;
429 else PropulsionValues += delimiter;
431 PropulsionValues += Engines[i]->GetEngineValues(delimiter);
433 for (i=0; i<Tanks.size(); i++) {
435 buf << Tanks[i]->GetContents();
438 return PropulsionValues;
441 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
445 vXYZtank_arm.InitMatrix();
446 for (unsigned int i=0; i<Tanks.size(); i++) {
447 vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
448 vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
449 vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
454 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456 double FGPropulsion::GetTanksWeight(void)
460 for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
465 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
472 if (size == 0) return tankJ;
474 tankJ = FGMatrix33();
476 for (unsigned int i=0; i<size; i++) {
477 tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
478 Tanks[i]->GetXYZ() );
479 tankJ(1,1) += Tanks[i]->GetIxx();
480 tankJ(2,2) += Tanks[i]->GetIyy();
481 tankJ(3,3) += Tanks[i]->GetIzz();
487 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489 void FGPropulsion::SetMagnetos(int setting)
491 if (ActiveEngine < 0) {
492 for (unsigned i=0; i<Engines.size(); i++) {
493 // ToDo: first need to make sure the engine Type is really appropriate:
494 // do a check to see if it is of type Piston. This should be done for
495 // all of this kind of possibly across-the-board settings.
496 ((FGPiston*)Engines[i])->SetMagnetos(setting);
499 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
503 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505 void FGPropulsion::SetStarter(int setting)
507 if (ActiveEngine < 0) {
508 for (unsigned i=0; i<Engines.size(); i++) {
510 Engines[i]->SetStarter(false);
512 Engines[i]->SetStarter(true);
516 Engines[ActiveEngine]->SetStarter(false);
518 Engines[ActiveEngine]->SetStarter(true);
522 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524 void FGPropulsion::SetCutoff(int setting)
526 if (ActiveEngine < 0) {
527 for (unsigned i=0; i<Engines.size(); i++) {
529 ((FGTurbine*)Engines[i])->SetCutoff(false);
531 ((FGTurbine*)Engines[i])->SetCutoff(true);
535 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
537 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
541 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
543 void FGPropulsion::SetActiveEngine(int engine)
545 if (engine >= (int)Engines.size() || engine < 0)
548 ActiveEngine = engine;
551 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553 double FGPropulsion::Transfer(int source, int target, double amount)
555 double shortage, overage;
560 shortage = Tanks[source]->Drain(amount);
565 overage = Tanks[target]->Fill(amount - shortage);
570 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572 void FGPropulsion::DoRefuel(double time_slice)
576 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
577 int TanksNotFull = 0;
579 for (i=0; i<numTanks; i++) {
580 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
584 for (i=0; i<numTanks; i++) {
585 if (Tanks[i]->GetPctFull() < 99.99)
586 Transfer(-1, i, fillrate/TanksNotFull);
591 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
593 void FGPropulsion::DumpFuel(double time_slice)
596 int TanksDumping = 0;
598 for (i=0; i<numTanks; i++) {
599 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
602 if (TanksDumping == 0) return;
604 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
606 for (i=0; i<numTanks; i++) {
607 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
608 Transfer(i, -1, dump_rate_per_tank);
613 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
615 void FGPropulsion::SetFuelFreeze(bool f)
618 for (unsigned int i=0; i<numEngines; i++) {
619 Engines[i]->SetFuelFreeze(f);
623 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625 void FGPropulsion::bind(void)
627 typedef double (FGPropulsion::*PMF)(int) const;
628 typedef int (FGPropulsion::*iPMF)(void) const;
631 PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, true);
632 if (HaveTurbineEngine) {
633 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true);
634 PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, true);
637 if (HavePistonEngine) {
638 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true);
639 PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, true);
642 PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
643 &FGPropulsion::SetActiveEngine, true);
644 PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
645 PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
646 &FGPropulsion::SetRefuel, true);
647 PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
648 &FGPropulsion::SetFuelDump, true);
649 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
650 (PMF)&FGPropulsion::GetForces);
651 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
652 (PMF)&FGPropulsion::GetForces);
653 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
654 (PMF)&FGPropulsion::GetForces);
655 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
656 (PMF)&FGPropulsion::GetMoments);
657 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
658 (PMF)&FGPropulsion::GetMoments);
659 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
660 (PMF)&FGPropulsion::GetMoments);
664 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665 // The bitmasked value choices are as follows:
666 // unset: In this case (the default) JSBSim would only print
667 // out the normally expected messages, essentially echoing
668 // the config files as they are read. If the environment
669 // variable is not set, debug_lvl is set to 1 internally
670 // 0: This requests JSBSim not to output any messages
672 // 1: This value explicity requests the normal JSBSim
674 // 2: This value asks for a message to be printed out when
675 // a class is instantiated
676 // 4: When this value is set, a message is displayed when a
677 // FGModel object executes its Run() method
678 // 8: When this value is set, various runtime state variables
679 // are printed out periodically
680 // 16: When set various parameters are sanity checked and
681 // a message is printed out when they go out of bounds
683 void FGPropulsion::Debug(int from)
685 if (debug_lvl <= 0) return;
687 if (debug_lvl & 1) { // Standard console startup message output
688 if (from == 2) { // Loader
689 cout << endl << " Propulsion:" << endl;
692 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
693 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
694 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
696 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
698 if (debug_lvl & 8 ) { // Runtime state variables
700 if (debug_lvl & 16) { // Sanity checking
702 if (debug_lvl & 64) {
703 if (from == 0) { // Constructor
704 cout << IdSrc << endl;
705 cout << IdHdr << endl;