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;
152 double dt = State->Getdt();
154 vForces.InitMatrix();
155 vMoments.InitMatrix();
157 for (i=0; i<numEngines; i++) {
158 Engines[i]->Calculate();
159 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
160 vMoments += Engines[i]->GetMoments(); // sum body frame moments
163 TotalFuelQuantity = 0.0;
164 for (i=0; i<numTanks; i++) {
165 Tanks[i]->Calculate( dt * rate );
166 if (Tanks[i]->GetType() == FGTank::ttFUEL) {
167 TotalFuelQuantity += Tanks[i]->GetContents();
171 if (refuel) DoRefuel( dt * rate );
172 if (dump) DumpFuel( dt * rate );
179 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 bool FGPropulsion::GetSteadyState(void)
183 double currentThrust = 0, lastThrust = -1;
184 int steady_count = 0, j = 0;
187 vForces.InitMatrix();
188 vMoments.InitMatrix();
190 if (!FGModel::Run()) {
191 for (unsigned int i=0; i<numEngines; i++) {
192 // cout << " Finding steady state for engine " << i << endl;
193 Engines[i]->SetTrimMode(true);
197 while (!steady && j < 6000) {
198 Engines[i]->Calculate();
199 lastThrust = currentThrust;
200 currentThrust = Engines[i]->GetThruster()->GetThrust();
201 if (fabs(lastThrust-currentThrust) < 0.0001) {
203 if (steady_count > 120) {
205 // cout << " Steady state found at thrust: " << currentThrust << " lbs." << endl;
213 // cout << " Could not find a steady state for this engine." << endl;
215 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
216 vMoments += Engines[i]->GetMoments(); // sum body frame moments
217 Engines[i]->SetTrimMode(false);
226 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228 void FGPropulsion::InitRunning(int n)
230 if (n > 0) { // A specific engine is supposed to be initialized
232 if (n >= (int)GetNumEngines() ) {
233 cerr << "Tried to initialize a non-existent engine!" << endl;
236 FCS->SetThrottleCmd(n,1);
237 FCS->SetMixtureCmd(n,1);
238 GetEngine(n)->InitRunning();
241 InitializedEngines = 1 << n;
242 HasInitializedEngines = true;
244 } else if (n < 0) { // -1 refers to "All Engines"
246 for (unsigned int i=0; i<GetNumEngines(); i++) {
247 FCS->SetThrottleCmd(i,1);
248 FCS->SetMixtureCmd(i,1);
249 GetEngine(i)->InitRunning();
252 InitializedEngines = -1;
253 HasInitializedEngines = true;
255 } else if (n == 0) { // No engines are to be initialized
260 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
262 bool FGPropulsion::Load(Element* el)
264 string type, engine_filename;
265 bool ThrottleAdded = false;
269 FGModel::Load(el); // Perform base class Load.
271 // Process tank definitions first to establish the number of fuel tanks
273 Element* tank_element = el->FindElement("tank");
274 while (tank_element) {
275 Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
276 if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
277 else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
278 else {cerr << "Unknown tank type specified." << endl; return false;}
280 tank_element = el->FindNextElement("tank");
282 numSelectedFuelTanks = numFuelTanks;
283 numSelectedOxiTanks = numOxiTanks;
285 Element* engine_element = el->FindElement("engine");
286 while (engine_element) {
287 engine_filename = engine_element->GetAttributeValue("file");
289 if (engine_filename.empty()) {
290 cerr << "Engine definition did not supply an engine file." << endl;
294 engine_filename = FindEngineFullPathname(engine_filename);
295 document = LoadXMLDocument(engine_filename);
296 document->SetParent(engine_element);
298 type = document->GetName();
299 if (type == "piston_engine") {
300 HavePistonEngine = true;
301 if (!IsBound) bind();
302 Engines.push_back(new FGPiston(FDMExec, document, numEngines));
303 } else if (type == "turbine_engine") {
304 HaveTurbineEngine = true;
305 if (!IsBound) bind();
306 Engines.push_back(new FGTurbine(FDMExec, document, numEngines));
307 } else if (type == "turboprop_engine") {
308 HaveTurboPropEngine = true;
309 if (!IsBound) bind();
310 Engines.push_back(new FGTurboProp(FDMExec, document, numEngines));
311 } else if (type == "rocket_engine") {
312 HaveRocketEngine = true;
313 if (!IsBound) bind();
314 Engines.push_back(new FGRocket(FDMExec, document, numEngines));
315 } else if (type == "electric_engine") {
316 HaveElectricEngine = true;
317 if (!IsBound) bind();
318 Engines.push_back(new FGElectric(FDMExec, document, numEngines));
320 cerr << "Unknown engine type: " << type << endl;
325 ThrottleAdded = true;
329 engine_element = el->FindNextElement("engine");
333 CalculateTankInertias();
334 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
336 // Process fuel dump rate
337 if (el->FindElement("dump-rate"))
338 DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
340 FGModel::PostLoad(el);
345 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
349 string fullpath, localpath;
350 string enginePath = FDMExec->GetEnginePath();
351 string aircraftPath = FDMExec->GetFullAircraftPath();
352 ifstream engine_file;
354 string separator = "/";
356 fullpath = enginePath + separator;
357 localpath = aircraftPath + separator + "Engines" + separator;
359 engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
360 if ( !engine_file.is_open()) {
361 engine_file.open(string(localpath + engine_filename + ".xml").c_str());
362 if ( !engine_file.is_open()) {
363 cerr << " Could not open engine file: " << engine_filename << " in path "
364 << fullpath << " or " << localpath << endl;
367 return string(localpath + engine_filename + ".xml");
370 return string(fullpath + engine_filename + ".xml");
373 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
377 string fullpath, localpath;
378 string enginePath = FDMExec->GetEnginePath();
379 string aircraftPath = FDMExec->GetFullAircraftPath();
380 ifstream* engine_file = new ifstream();
382 string separator = "/";
384 fullpath = enginePath + separator;
385 localpath = aircraftPath + separator + "Engines" + separator;
387 engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
388 if ( !engine_file->is_open()) {
389 engine_file->open(string(localpath + engine_filename + ".xml").c_str());
390 if ( !engine_file->is_open()) {
391 cerr << " Could not open engine file: " << engine_filename << " in path "
392 << fullpath << " or " << localpath << endl;
398 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400 string FGPropulsion::GetPropulsionStrings(const string& delimiter)
404 string PropulsionStrings = "";
405 bool firstime = true;
408 for (i=0; i<Engines.size(); i++) {
409 if (firstime) firstime = false;
410 else PropulsionStrings += delimiter;
412 PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
414 for (i=0; i<Tanks.size(); i++) {
415 if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
416 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
419 return PropulsionStrings;
422 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
424 string FGPropulsion::GetPropulsionValues(const string& delimiter)
428 string PropulsionValues = "";
429 bool firstime = true;
432 for (i=0; i<Engines.size(); i++) {
433 if (firstime) firstime = false;
434 else PropulsionValues += delimiter;
436 PropulsionValues += Engines[i]->GetEngineValues(delimiter);
438 for (i=0; i<Tanks.size(); i++) {
440 buf << Tanks[i]->GetContents();
443 return PropulsionValues;
446 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
450 vXYZtank_arm.InitMatrix();
451 for (unsigned int i=0; i<Tanks.size(); i++) {
452 vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
453 vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
454 vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
459 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 double FGPropulsion::GetTanksWeight(void)
465 for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
477 if (size == 0) return tankJ;
479 tankJ = FGMatrix33();
481 for (unsigned int i=0; i<size; i++) {
482 tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
483 Tanks[i]->GetXYZ() );
484 tankJ(1,1) += Tanks[i]->GetIxx();
485 tankJ(2,2) += Tanks[i]->GetIyy();
486 tankJ(3,3) += Tanks[i]->GetIzz();
492 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494 void FGPropulsion::SetMagnetos(int setting)
496 if (ActiveEngine < 0) {
497 for (unsigned i=0; i<Engines.size(); i++) {
498 // ToDo: first need to make sure the engine Type is really appropriate:
499 // do a check to see if it is of type Piston. This should be done for
500 // all of this kind of possibly across-the-board settings.
501 ((FGPiston*)Engines[i])->SetMagnetos(setting);
504 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
508 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 void FGPropulsion::SetStarter(int setting)
512 if (ActiveEngine < 0) {
513 for (unsigned i=0; i<Engines.size(); i++) {
515 Engines[i]->SetStarter(false);
517 Engines[i]->SetStarter(true);
521 Engines[ActiveEngine]->SetStarter(false);
523 Engines[ActiveEngine]->SetStarter(true);
527 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529 void FGPropulsion::SetCutoff(int setting)
531 if (ActiveEngine < 0) {
532 for (unsigned i=0; i<Engines.size(); i++) {
534 ((FGTurbine*)Engines[i])->SetCutoff(false);
536 ((FGTurbine*)Engines[i])->SetCutoff(true);
540 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
542 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
546 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548 void FGPropulsion::SetActiveEngine(int engine)
550 if (engine >= (int)Engines.size() || engine < 0)
553 ActiveEngine = engine;
556 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558 double FGPropulsion::Transfer(int source, int target, double amount)
560 double shortage, overage;
565 shortage = Tanks[source]->Drain(amount);
570 overage = Tanks[target]->Fill(amount - shortage);
575 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577 void FGPropulsion::DoRefuel(double time_slice)
581 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
582 int TanksNotFull = 0;
584 for (i=0; i<numTanks; i++) {
585 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
589 for (i=0; i<numTanks; i++) {
590 if (Tanks[i]->GetPctFull() < 99.99)
591 Transfer(-1, i, fillrate/TanksNotFull);
596 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
598 void FGPropulsion::DumpFuel(double time_slice)
601 int TanksDumping = 0;
603 for (i=0; i<numTanks; i++) {
604 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
607 if (TanksDumping == 0) return;
609 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
611 for (i=0; i<numTanks; i++) {
612 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
613 Transfer(i, -1, dump_rate_per_tank);
618 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620 void FGPropulsion::SetFuelFreeze(bool f)
623 for (unsigned int i=0; i<numEngines; i++) {
624 Engines[i]->SetFuelFreeze(f);
628 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
630 void FGPropulsion::bind(void)
632 typedef double (FGPropulsion::*PMF)(int) const;
633 typedef int (FGPropulsion::*iPMF)(void) const;
636 PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, true);
637 if (HaveTurbineEngine) {
638 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true);
639 PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, true);
642 if (HavePistonEngine) {
643 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true);
644 PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, true);
647 PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
648 &FGPropulsion::SetActiveEngine, true);
649 PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
650 PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
651 &FGPropulsion::SetRefuel, true);
652 PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
653 &FGPropulsion::SetFuelDump, true);
654 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
655 (PMF)&FGPropulsion::GetForces);
656 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
657 (PMF)&FGPropulsion::GetForces);
658 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
659 (PMF)&FGPropulsion::GetForces);
660 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
661 (PMF)&FGPropulsion::GetMoments);
662 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
663 (PMF)&FGPropulsion::GetMoments);
664 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
665 (PMF)&FGPropulsion::GetMoments);
669 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 // The bitmasked value choices are as follows:
671 // unset: In this case (the default) JSBSim would only print
672 // out the normally expected messages, essentially echoing
673 // the config files as they are read. If the environment
674 // variable is not set, debug_lvl is set to 1 internally
675 // 0: This requests JSBSim not to output any messages
677 // 1: This value explicity requests the normal JSBSim
679 // 2: This value asks for a message to be printed out when
680 // a class is instantiated
681 // 4: When this value is set, a message is displayed when a
682 // FGModel object executes its Run() method
683 // 8: When this value is set, various runtime state variables
684 // are printed out periodically
685 // 16: When set various parameters are sanity checked and
686 // a message is printed out when they go out of bounds
688 void FGPropulsion::Debug(int from)
690 if (debug_lvl <= 0) return;
692 if (debug_lvl & 1) { // Standard console startup message output
693 if (from == 2) { // Loader
694 cout << endl << " Propulsion:" << endl;
697 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
698 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
699 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
701 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
703 if (debug_lvl & 8 ) { // Runtime state variables
705 if (debug_lvl & 16) { // Sanity checking
707 if (debug_lvl & 64) {
708 if (from == 0) { // Constructor
709 cout << IdSrc << endl;
710 cout << IdHdr << endl;