]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGPropulsion.cpp
Merge branch 'next' of git@gitorious.org:fg/flightgear into next
[flightgear.git] / src / FDM / JSBSim / models / FGPropulsion.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGPropulsion.cpp
4  Author:       Jon S. Berndt
5  Date started: 08/20/00
6  Purpose:      Encapsulates the set of engines and tanks associated
7                with this aircraft
8
9  ------------- Copyright (C) 2000  Jon S. Berndt (jon@jsbsim.org) -------------
10
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
14  version.
15
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
19  details.
20
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.
24
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.
27
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:
33
34 1) The appropriate engine type instance is created
35 2) At least one tank object is created, and is linked to an engine.
36
37 At Run time each engines Calculate() method is called.
38
39 HISTORY
40 --------------------------------------------------------------------------------
41 08/20/00   JSB   Created
42
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 INCLUDES
45 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
46
47 #include "FGPropulsion.h"
48 #include "models/FGFCS.h"
49 #include "models/FGMassBalance.h"
50 #include "models/propulsion/FGThruster.h"
51 #include "models/propulsion/FGRocket.h"
52 #include "models/propulsion/FGTurbine.h"
53 #include "models/propulsion/FGPiston.h"
54 #include "models/propulsion/FGElectric.h"
55 #include "models/propulsion/FGTurboProp.h"
56 #include "models/propulsion/FGTank.h"
57 #include "input_output/FGPropertyManager.h"
58 #include "input_output/FGXMLParse.h"
59 #include "math/FGColumnVector3.h"
60 #include <iostream>
61 #include <sstream>
62 #include <cstdlib>
63
64 using namespace std;
65
66 namespace JSBSim {
67
68 static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.39 2010/02/25 05:21:36 jberndt Exp $";
69 static const char *IdHdr = ID_PROPULSION;
70
71 extern short debug_lvl;
72
73
74 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 CLASS IMPLEMENTATION
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
77
78 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
79 {
80   Name = "FGPropulsion";
81
82   InitializedEngines = 0;
83   numSelectedFuelTanks = numSelectedOxiTanks = 0;
84   numTanks = numEngines = 0;
85   numOxiTanks = numFuelTanks = 0;
86   ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
87   tankJ.InitMatrix();
88   refuel = dump = false;
89   DumpRate = 0.0;
90   fuel_freeze = false;
91   TotalFuelQuantity = 0.0;
92   IsBound =
93   HavePistonEngine =
94   HaveTurbineEngine =
95   HaveRocketEngine =
96   HaveTurboPropEngine =
97   HaveElectricEngine = false;
98   HasInitializedEngines = false;
99
100   Debug(0);
101 }
102
103 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104
105 FGPropulsion::~FGPropulsion()
106 {
107   for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
108   Engines.clear();
109   for (unsigned int i=0; i<Tanks.size(); i++) delete Tanks[i];
110   Tanks.clear();
111   Debug(1);
112 }
113
114 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115
116 bool FGPropulsion::InitModel(void)
117 {
118   if (!FGModel::InitModel()) return false;
119
120   for (unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
121
122   for (unsigned int i=0; i<numEngines; i++) {
123     switch (Engines[i]->GetType()) {
124       case FGEngine::etPiston:
125         ((FGPiston*)Engines[i])->ResetToIC();
126         if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
127         break;
128       case FGEngine::etTurbine:
129         ((FGTurbine*)Engines[i])->ResetToIC();
130         if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
131         break;
132       default:
133         break;
134     }
135   }
136
137   return true;
138 }
139
140 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141
142 bool FGPropulsion::Run(void)
143 {
144   unsigned int i;
145
146   if (FGModel::Run()) return true;
147   if (FDMExec->Holding()) return false;
148
149   RunPreFunctions();
150
151   double dt = FDMExec->GetDeltaT();
152
153   vForces.InitMatrix();
154   vMoments.InitMatrix();
155
156   for (i=0; i<numEngines; i++) {
157     Engines[i]->Calculate();
158     vForces  += Engines[i]->GetBodyForces();  // sum body frame forces
159     vMoments += Engines[i]->GetMoments();     // sum body frame moments
160   }
161
162   TotalFuelQuantity = 0.0;
163   for (i=0; i<numTanks; i++) {
164     Tanks[i]->Calculate( dt * rate );
165     if (Tanks[i]->GetType() == FGTank::ttFUEL) {
166       TotalFuelQuantity += Tanks[i]->GetContents();
167     }
168   }
169
170   if (refuel) DoRefuel( dt * rate );
171   if (dump) DumpFuel( dt * rate );
172
173   RunPostFunctions();
174
175   return false;
176 }
177
178 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179
180 bool FGPropulsion::GetSteadyState(void)
181 {
182   double currentThrust = 0, lastThrust = -1;
183   int steady_count = 0, j = 0;
184   bool steady = false;
185
186   vForces.InitMatrix();
187   vMoments.InitMatrix();
188
189   if (!FGModel::Run()) {
190     for (unsigned int i=0; i<numEngines; i++) {
191 //      cout << "  Finding steady state for engine " << i << endl;
192       Engines[i]->SetTrimMode(true);
193       steady=false;
194       steady_count=0;
195       j=0;
196       while (!steady && j < 6000) {
197         Engines[i]->Calculate();
198         lastThrust = currentThrust;
199         currentThrust = Engines[i]->GetThruster()->GetThrust();
200         if (fabs(lastThrust-currentThrust) < 0.0001) {
201           steady_count++;
202           if (steady_count > 120) {
203             steady=true;
204 //            cout << "    Steady state found at thrust: " << currentThrust << " lbs." << endl;
205           }
206         } else {
207           steady_count=0;
208         }
209         j++;
210       }
211 //      if (j >= 6000) {
212 //        cout << "    Could not find a steady state for this engine." << endl;
213 //      }
214       vForces  += Engines[i]->GetBodyForces();  // sum body frame forces
215       vMoments += Engines[i]->GetMoments();     // sum body frame moments
216       Engines[i]->SetTrimMode(false);
217     }
218
219     return false;
220   } else {
221     return true;
222   }
223 }
224
225 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226
227 void FGPropulsion::InitRunning(int n)
228 {
229   if (n > 0) { // A specific engine is supposed to be initialized
230
231     if (n >= (int)GetNumEngines() ) {
232       cerr << "Tried to initialize a non-existent engine!" << endl;
233       throw;
234     }
235     FCS->SetThrottleCmd(n,1);
236     FCS->SetMixtureCmd(n,1);
237     GetEngine(n)->InitRunning();
238     GetSteadyState();
239
240     InitializedEngines = 1 << n;
241     HasInitializedEngines = true;
242
243   } else if (n < 0) { // -1 refers to "All Engines"
244
245     for (unsigned int i=0; i<GetNumEngines(); i++) {
246       FCS->SetThrottleCmd(i,1);
247       FCS->SetMixtureCmd(i,1);
248       GetEngine(i)->InitRunning();
249     }
250     GetSteadyState();
251     InitializedEngines = -1;
252     HasInitializedEngines = true;
253
254   } else if (n == 0) { // No engines are to be initialized
255     // Do nothing
256   }
257 }
258
259 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
260
261 bool FGPropulsion::Load(Element* el)
262 {
263   string type, engine_filename;
264   bool ThrottleAdded = false;
265
266   Debug(2);
267
268   FGModel::Load(el); // Perform base class Load.
269
270   // Process tank definitions first to establish the number of fuel tanks
271
272   Element* tank_element = el->FindElement("tank");
273   while (tank_element) {
274     Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
275     if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
276     else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
277     else {cerr << "Unknown tank type specified." << endl; return false;}
278     numTanks++;
279     tank_element = el->FindNextElement("tank");
280   }
281   numSelectedFuelTanks = numFuelTanks;
282   numSelectedOxiTanks  = numOxiTanks;
283
284   Element* engine_element = el->FindElement("engine");
285   while (engine_element) {
286     engine_filename = engine_element->GetAttributeValue("file");
287
288     if (engine_filename.empty()) {
289       cerr << "Engine definition did not supply an engine file." << endl;
290       return false;
291     }
292
293     engine_filename = FindEngineFullPathname(engine_filename);
294     document = LoadXMLDocument(engine_filename);
295     document->SetParent(engine_element);
296
297     type = document->GetName();
298     if (type == "piston_engine") {
299       HavePistonEngine = true;
300       if (!IsBound) bind();
301       Engines.push_back(new FGPiston(FDMExec, document, numEngines));
302     } else if (type == "turbine_engine") {
303       HaveTurbineEngine = true;
304       if (!IsBound) bind();
305       Engines.push_back(new FGTurbine(FDMExec, document, numEngines));
306     } else if (type == "turboprop_engine") {
307       HaveTurboPropEngine = true;
308       if (!IsBound) bind();
309       Engines.push_back(new FGTurboProp(FDMExec, document, numEngines));
310     } else if (type == "rocket_engine") {
311       HaveRocketEngine = true;
312       if (!IsBound) bind();
313       Engines.push_back(new FGRocket(FDMExec, document, numEngines));
314     } else if (type == "electric_engine") {
315       HaveElectricEngine = true;
316       if (!IsBound) bind();
317       Engines.push_back(new FGElectric(FDMExec, document, numEngines));
318     } else {
319       cerr << "Unknown engine type: " << type << endl;
320       exit(-5);
321     }
322
323     FCS->AddThrottle();
324     ThrottleAdded = true;
325
326     numEngines++;
327
328     engine_element = el->FindNextElement("engine");
329     ResetParser();
330   }
331
332   CalculateTankInertias();
333   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
334
335   // Process fuel dump rate
336   if (el->FindElement("dump-rate"))
337     DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
338
339   FGModel::PostLoad(el);
340
341   return true;
342 }
343
344 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345
346 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
347 {
348   string fullpath, localpath;
349   string enginePath = FDMExec->GetEnginePath();
350   string aircraftPath = FDMExec->GetFullAircraftPath();
351   ifstream engine_file;
352
353   string separator = "/";
354
355   fullpath = enginePath + separator;
356   localpath = aircraftPath + separator + "Engines" + separator;
357
358   engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
359   if ( !engine_file.is_open()) {
360     engine_file.open(string(localpath + engine_filename + ".xml").c_str());
361       if ( !engine_file.is_open()) {
362         cerr << " Could not open engine file: " << engine_filename << " in path "
363              << fullpath << " or " << localpath << endl;
364         return string("");
365       } else {
366         return string(localpath + engine_filename + ".xml");
367       }
368   }
369   return string(fullpath + engine_filename + ".xml");
370 }
371
372 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
373
374 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
375 {
376   string fullpath, localpath;
377   string enginePath = FDMExec->GetEnginePath();
378   string aircraftPath = FDMExec->GetFullAircraftPath();
379   ifstream* engine_file = new ifstream();
380
381   string separator = "/";
382
383   fullpath = enginePath + separator;
384   localpath = aircraftPath + separator + "Engines" + separator;
385
386   engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
387   if ( !engine_file->is_open()) {
388     engine_file->open(string(localpath + engine_filename + ".xml").c_str());
389       if ( !engine_file->is_open()) {
390         cerr << " Could not open engine file: " << engine_filename << " in path "
391              << fullpath << " or " << localpath << endl;
392       }
393   }
394   return engine_file;
395 }
396
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398
399 string FGPropulsion::GetPropulsionStrings(const string& delimiter)
400 {
401   unsigned int i;
402
403   string PropulsionStrings = "";
404   bool firstime = true;
405   stringstream buf;
406
407   for (i=0; i<Engines.size(); i++) {
408     if (firstime)  firstime = false;
409     else           PropulsionStrings += delimiter;
410
411     PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
412   }
413   for (i=0; i<Tanks.size(); i++) {
414     if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
415     else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
416   }
417
418   return PropulsionStrings;
419 }
420
421 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422
423 string FGPropulsion::GetPropulsionValues(const string& delimiter)
424 {
425   unsigned int i;
426
427   string PropulsionValues = "";
428   bool firstime = true;
429   stringstream buf;
430
431   for (i=0; i<Engines.size(); i++) {
432     if (firstime)  firstime = false;
433     else           PropulsionValues += delimiter;
434
435     PropulsionValues += Engines[i]->GetEngineValues(delimiter);
436   }
437   for (i=0; i<Tanks.size(); i++) {
438     buf << delimiter;
439     buf << Tanks[i]->GetContents();
440   }
441
442   return PropulsionValues;
443 }
444
445 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
446
447 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
448 {
449   vXYZtank_arm.InitMatrix();
450   for (unsigned int i=0; i<Tanks.size(); i++) {
451     vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
452     vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
453     vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
454   }
455   return vXYZtank_arm;
456 }
457
458 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459
460 double FGPropulsion::GetTanksWeight(void)
461 {
462   double Tw = 0.0;
463
464   for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
465
466   return Tw;
467 }
468
469 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470
471 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
472 {
473   unsigned int size;
474
475   size = Tanks.size();
476   if (size == 0) return tankJ;
477
478   tankJ = FGMatrix33();
479
480   for (unsigned int i=0; i<size; i++) {
481     tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
482                                                Tanks[i]->GetXYZ() );
483     tankJ(1,1) += Tanks[i]->GetIxx();
484     tankJ(2,2) += Tanks[i]->GetIyy();
485     tankJ(3,3) += Tanks[i]->GetIzz();
486   }
487
488   return tankJ;
489 }
490
491 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492
493 void FGPropulsion::SetMagnetos(int setting)
494 {
495   if (ActiveEngine < 0) {
496     for (unsigned i=0; i<Engines.size(); i++) {
497       // ToDo: first need to make sure the engine Type is really appropriate:
498       //   do a check to see if it is of type Piston. This should be done for
499       //   all of this kind of possibly across-the-board settings.
500       ((FGPiston*)Engines[i])->SetMagnetos(setting);
501     }
502   } else {
503     ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
504   }
505 }
506
507 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508
509 void FGPropulsion::SetStarter(int setting)
510 {
511   if (ActiveEngine < 0) {
512     for (unsigned i=0; i<Engines.size(); i++) {
513       if (setting == 0)
514         Engines[i]->SetStarter(false);
515       else
516         Engines[i]->SetStarter(true);
517     }
518   } else {
519     if (setting == 0)
520       Engines[ActiveEngine]->SetStarter(false);
521     else
522       Engines[ActiveEngine]->SetStarter(true);
523   }
524 }
525
526 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527
528 void FGPropulsion::SetCutoff(int setting)
529 {
530   if (ActiveEngine < 0) {
531     for (unsigned i=0; i<Engines.size(); i++) {
532       if (setting == 0)
533         ((FGTurbine*)Engines[i])->SetCutoff(false);
534       else
535         ((FGTurbine*)Engines[i])->SetCutoff(true);
536     }
537   } else {
538     if (setting == 0)
539       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
540     else
541       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
542   }
543 }
544
545 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546
547 void FGPropulsion::SetActiveEngine(int engine)
548 {
549   if (engine >= (int)Engines.size() || engine < 0)
550     ActiveEngine = -1;
551   else
552     ActiveEngine = engine;
553 }
554
555 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556
557 double FGPropulsion::Transfer(int source, int target, double amount)
558 {
559  double shortage, overage;
560
561   if (source == -1) {
562      shortage = 0.0;
563   } else {
564      shortage = Tanks[source]->Drain(amount);
565   }
566   if (target == -1) {
567      overage = 0.0;
568   } else {
569      overage = Tanks[target]->Fill(amount - shortage);
570   }
571   return overage;
572 }
573
574 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575
576 void FGPropulsion::DoRefuel(double time_slice)
577 {
578   unsigned int i;
579
580   double fillrate = 100 * time_slice;   // 100 lbs/sec = 6000 lbs/min
581   int TanksNotFull = 0;
582
583   for (i=0; i<numTanks; i++) {
584     if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
585   }
586
587   if (TanksNotFull) {
588     for (i=0; i<numTanks; i++) {
589       if (Tanks[i]->GetPctFull() < 99.99)
590           Transfer(-1, i, fillrate/TanksNotFull);
591     }
592   }
593 }
594
595 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596
597 void FGPropulsion::DumpFuel(double time_slice)
598 {
599   unsigned int i;
600   int TanksDumping = 0;
601
602   for (i=0; i<numTanks; i++) {
603     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
604   }
605
606   if (TanksDumping == 0) return;
607
608   double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
609
610   for (i=0; i<numTanks; i++) {
611     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
612       Transfer(i, -1, dump_rate_per_tank);
613     }
614   }
615 }
616
617 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618
619 void FGPropulsion::SetFuelFreeze(bool f)
620 {
621   fuel_freeze = f;
622   for (unsigned int i=0; i<numEngines; i++) {
623     Engines[i]->SetFuelFreeze(f);
624   }
625 }
626
627 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
628
629 void FGPropulsion::bind(void)
630 {
631   typedef double (FGPropulsion::*PMF)(int) const;
632   typedef int (FGPropulsion::*iPMF)(void) const;
633
634   IsBound = true;
635   PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, true);
636   if (HaveTurbineEngine) {
637     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  true);
638     PropertyManager->Tie("propulsion/cutoff_cmd", this,  (iPMF)0, &FGPropulsion::SetCutoff,   true);
639   }
640
641   if (HavePistonEngine) {
642     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  true);
643     PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, true);
644   }
645
646   PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
647                         &FGPropulsion::SetActiveEngine, true);
648   PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
649   PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
650                         &FGPropulsion::SetRefuel, true);
651   PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
652                         &FGPropulsion::SetFuelDump, true);
653   PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
654                        (PMF)&FGPropulsion::GetForces);
655   PropertyManager->Tie("forces/fby-prop-lbs", this,2,
656                        (PMF)&FGPropulsion::GetForces);
657   PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
658                        (PMF)&FGPropulsion::GetForces);
659   PropertyManager->Tie("moments/l-prop-lbsft", this,1,
660                        (PMF)&FGPropulsion::GetMoments);
661   PropertyManager->Tie("moments/m-prop-lbsft", this,2,
662                        (PMF)&FGPropulsion::GetMoments);
663   PropertyManager->Tie("moments/n-prop-lbsft", this,3,
664                        (PMF)&FGPropulsion::GetMoments);
665
666 }
667
668 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 //    The bitmasked value choices are as follows:
670 //    unset: In this case (the default) JSBSim would only print
671 //       out the normally expected messages, essentially echoing
672 //       the config files as they are read. If the environment
673 //       variable is not set, debug_lvl is set to 1 internally
674 //    0: This requests JSBSim not to output any messages
675 //       whatsoever.
676 //    1: This value explicity requests the normal JSBSim
677 //       startup messages
678 //    2: This value asks for a message to be printed out when
679 //       a class is instantiated
680 //    4: When this value is set, a message is displayed when a
681 //       FGModel object executes its Run() method
682 //    8: When this value is set, various runtime state variables
683 //       are printed out periodically
684 //    16: When set various parameters are sanity checked and
685 //       a message is printed out when they go out of bounds
686
687 void FGPropulsion::Debug(int from)
688 {
689   if (debug_lvl <= 0) return;
690
691   if (debug_lvl & 1) { // Standard console startup message output
692     if (from == 2) { // Loader
693       cout << endl << "  Propulsion:" << endl;
694     }
695   }
696   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
697     if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
698     if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
699   }
700   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
701   }
702   if (debug_lvl & 8 ) { // Runtime state variables
703   }
704   if (debug_lvl & 16) { // Sanity checking
705   }
706   if (debug_lvl & 64) {
707     if (from == 0) { // Constructor
708       cout << IdSrc << endl;
709       cout << IdHdr << endl;
710     }
711   }
712 }
713 }