]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGPropulsion.cpp
Merge branch 'next' into attenuation
[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 <iostream>
48 #include <sstream>
49 #include <cstdlib>
50 #include <iomanip>
51
52 #include "FGFDMExec.h"
53 #include "FGPropulsion.h"
54 #include "models/FGMassBalance.h"
55 #include "models/propulsion/FGRocket.h"
56 #include "models/propulsion/FGTurbine.h"
57 #include "models/propulsion/FGPiston.h"
58 #include "models/propulsion/FGElectric.h"
59 #include "models/propulsion/FGTurboProp.h"
60 #include "models/propulsion/FGTank.h"
61 #include "input_output/FGPropertyManager.h"
62 #include "input_output/FGXMLParse.h"
63 #include "math/FGColumnVector3.h"
64
65 using namespace std;
66
67 namespace JSBSim {
68
69 static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.52 2011/10/31 14:54:41 bcoconni Exp $";
70 static const char *IdHdr = ID_PROPULSION;
71
72 extern short debug_lvl;
73
74
75 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 CLASS IMPLEMENTATION
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
78
79 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
80 {
81   Name = "FGPropulsion";
82
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 ...
88   tankJ.InitMatrix();
89   refuel = dump = false;
90   DumpRate = 0.0;
91   FuelFreeze = false;
92   TotalFuelQuantity = 0.0;
93   IsBound =
94   HavePistonEngine =
95   HaveTurbineEngine =
96   HaveRocketEngine =
97   HaveTurboPropEngine =
98   HaveElectricEngine = false;
99   HasInitializedEngines = false;
100
101   Debug(0);
102 }
103
104 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105
106 FGPropulsion::~FGPropulsion()
107 {
108   for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
109   Engines.clear();
110   for (unsigned int i=0; i<Tanks.size(); i++) delete Tanks[i];
111   Tanks.clear();
112   Debug(1);
113 }
114
115 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116
117 bool FGPropulsion::InitModel(void)
118 {
119   bool result = true;
120
121   for (unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
122
123   for (unsigned int i=0; i<numEngines; i++) {
124     switch (Engines[i]->GetType()) {
125       case FGEngine::etPiston:
126         ((FGPiston*)Engines[i])->ResetToIC();
127         try {
128           if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
129         } catch (string str) {
130           cerr << str << endl;
131           result = false;
132         }
133         break;
134       case FGEngine::etTurbine:
135         ((FGTurbine*)Engines[i])->ResetToIC();
136         try {
137           if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
138         } catch (string str) {
139           cerr << str << endl;
140           result = false;
141         }
142         break;
143       default:
144         break;
145     }
146   }
147
148   return result;
149 }
150
151 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152
153 bool FGPropulsion::Run(bool Holding)
154 {
155   unsigned int i;
156
157   if (FGModel::Run(Holding)) return true;
158   if (Holding) return false;
159
160   RunPreFunctions();
161
162   vForces.InitMatrix();
163   vMoments.InitMatrix();
164
165   for (i=0; i<numEngines; i++) {
166     Engines[i]->Calculate();
167     ConsumeFuel(Engines[i]);
168     vForces  += Engines[i]->GetBodyForces();  // sum body frame forces
169     vMoments += Engines[i]->GetMoments();     // sum body frame moments
170   }
171
172   TotalFuelQuantity = 0.0;
173   for (i=0; i<numTanks; i++) {
174     Tanks[i]->Calculate( in.TotalDeltaT, in.TAT_c);
175     if (Tanks[i]->GetType() == FGTank::ttFUEL) {
176       TotalFuelQuantity += Tanks[i]->GetContents();
177     }
178   }
179
180   if (refuel) DoRefuel( in.TotalDeltaT );
181   if (dump) DumpFuel( in.TotalDeltaT );
182
183   RunPostFunctions();
184
185   return false;
186 }
187
188 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189 //
190 // The engine can tell us how much fuel it needs, but it is up to the propulsion
191 // subsystem manager class FGPropulsion to manage fuel flow amongst tanks. Engines
192 // May burn fuel from more than one tank at a time, and may burn from one tank
193 // before another - that is, may burn from one tank until the tank is depleted,
194 // then burn from the next highest priority tank. This can be accompished
195 // by defining a fuel management system, but this way of specifying priorities
196 // is more automatic from a user perspective.
197
198 void FGPropulsion::ConsumeFuel(FGEngine* engine)
199 {
200   if (FuelFreeze) return;
201   if (FDMExec->GetTrimStatus()) return;
202
203   unsigned int TanksWithFuel=0, CurrentFuelTankPriority=1;
204   unsigned int TanksWithOxidizer=0, CurrentOxidizerTankPriority=1;
205   vector <int> FeedListFuel, FeedListOxi;
206   bool Starved = true; // Initially set Starved to true. Set to false in code below.
207 //  bool hasOxTanks = false;
208
209   // For this engine,
210   // 1) Count how many fuel tanks with the current priority level have fuel
211   // 2) If there none, then try next lower priority (higher number) - that is,
212   //    increment CurrentPriority.
213   // 3) Build the feed list.
214   // 4) Do the same for oxidizer tanks, if needed.
215
216   // Process fuel tanks, if any
217   while ((TanksWithFuel == 0) && (CurrentFuelTankPriority <= numTanks)) {
218     for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
219       unsigned int TankId = engine->GetSourceTank(i);
220       FGTank* Tank = Tanks[TankId];
221       unsigned int TankPriority = Tank->GetPriority();
222       if (TankPriority != 0) {
223         switch(Tank->GetType()) {
224         case FGTank::ttFUEL:
225           if ((Tank->GetContents() > 0.0) && Tank->GetSelected() && (TankPriority == CurrentFuelTankPriority)) {
226             TanksWithFuel++;
227             Starved = false;
228             FeedListFuel.push_back(TankId);
229           } 
230           break;
231         case FGTank::ttOXIDIZER:
232           // Skip this here (done below)
233           break;
234         }
235       }
236     }
237     if (TanksWithFuel == 0) CurrentFuelTankPriority++; // No tanks at this priority, try next priority
238   }
239
240   // Process Oxidizer tanks, if any
241   if (engine->GetType() == FGEngine::etRocket) {
242     while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
243       for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
244         unsigned int TankId = engine->GetSourceTank(i);
245         FGTank* Tank = Tanks[TankId];
246         unsigned int TankPriority = Tank->GetPriority();
247         if (TankPriority != 0) {
248           switch(Tank->GetType()) {
249           case FGTank::ttFUEL:
250             // Skip this here (done above)
251             break;
252           case FGTank::ttOXIDIZER:
253 //            hasOxTanks = true;
254             if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
255               TanksWithOxidizer++;
256               if (TanksWithFuel > 0) Starved = false;
257               FeedListOxi.push_back(TankId);
258             }
259             break;
260           }
261         }
262       }
263       if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
264     }
265   }
266
267   engine->SetStarved(Starved); // Tanks can be refilled, so be sure to reset engine Starved flag here.
268
269   // No fuel or fuel/oxidizer found at any priority!
270   if (Starved) return;
271
272   double FuelToBurn = engine->CalcFuelNeed();            // How much fuel does this engine need?
273   double FuelNeededPerTank = FuelToBurn / TanksWithFuel; // Determine fuel needed per tank.  
274   for (unsigned int i=0; i<FeedListFuel.size(); i++) {
275     Tanks[FeedListFuel[i]]->Drain(FuelNeededPerTank); 
276   }
277
278   if (engine->GetType() == FGEngine::etRocket) {
279     double OxidizerToBurn = engine->CalcOxidizerNeed();                // How much fuel does this engine need?
280     double OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer; // Determine fuel needed per tank.  
281     for (unsigned int i=0; i<FeedListOxi.size(); i++) {
282       Tanks[FeedListOxi[i]]->Drain(OxidizerNeededPerTank); 
283     }
284   }
285
286 }
287
288 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
289
290 bool FGPropulsion::GetSteadyState(void)
291 {
292   double currentThrust = 0, lastThrust = -1;
293   int steady_count = 0, j = 0;
294   bool steady = false;
295   bool TrimMode = FDMExec->GetTrimStatus();
296
297   vForces.InitMatrix();
298   vMoments.InitMatrix();
299
300   if (!FGModel::Run(false)) {
301     FDMExec->SetTrimStatus(true);
302
303     for (unsigned int i=0; i<numEngines; i++) {
304       steady=false;
305       steady_count=0;
306       j=0;
307       while (!steady && j < 6000) {
308         Engines[i]->Calculate();
309         lastThrust = currentThrust;
310         currentThrust = Engines[i]->GetThrust();
311         if (fabs(lastThrust-currentThrust) < 0.0001) {
312           steady_count++;
313           if (steady_count > 120) {
314             steady=true;
315           }
316         } else {
317           steady_count=0;
318         }
319         j++;
320       }
321       vForces  += Engines[i]->GetBodyForces();  // sum body frame forces
322       vMoments += Engines[i]->GetMoments();     // sum body frame moments
323     }
324
325     FDMExec->SetTrimStatus(TrimMode);
326
327     return false;
328   } else {
329     return true;
330   }
331 }
332
333 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334
335 void FGPropulsion::InitRunning(int n)
336 {
337   if (n >= 0) { // A specific engine is supposed to be initialized
338
339     if (n >= (int)GetNumEngines() ) {
340       throw(string("Tried to initialize a non-existent engine!"));
341     }
342
343     in.ThrottleCmd[n] = in.ThrottlePos[n] = 1; // Set the throttle command and position
344     in.MixtureCmd[n] = in.MixturePos[n] = 1;   // Set the mixture command and position
345
346     GetEngine(n)->InitRunning();
347     GetSteadyState();
348
349     InitializedEngines = 1 << n;
350     HasInitializedEngines = true;
351
352   } else if (n < 0) { // -1 refers to "All Engines"
353
354     for (unsigned int i=0; i<GetNumEngines(); i++) {
355       in.ThrottleCmd[i] = in.ThrottlePos[i] = 1; // Set the throttle command and position
356       in.MixtureCmd[i] = in.MixturePos[i] = 1;   // Set the mixture command and position
357       GetEngine(i)->InitRunning();
358     }
359
360     GetSteadyState();
361     InitializedEngines = -1;
362     HasInitializedEngines = true;
363   }
364 }
365
366 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367
368 bool FGPropulsion::Load(Element* el)
369 {
370   string type, engine_filename;
371
372   Debug(2);
373
374   FGModel::Load(el); // Perform base class Load.
375
376   // Process tank definitions first to establish the number of fuel tanks
377
378   Element* tank_element = el->FindElement("tank");
379   while (tank_element) {
380     Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
381     if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
382     else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
383     else {cerr << "Unknown tank type specified." << endl; return false;}
384     numTanks++;
385     tank_element = el->FindNextElement("tank");
386   }
387   numSelectedFuelTanks = numFuelTanks;
388   numSelectedOxiTanks  = numOxiTanks;
389
390   Element* engine_element = el->FindElement("engine");
391   while (engine_element) {
392     engine_filename = engine_element->GetAttributeValue("file");
393
394     if (engine_filename.empty()) {
395       cerr << "Engine definition did not supply an engine file." << endl;
396       return false;
397     }
398
399     engine_filename = FindEngineFullPathname(engine_filename);
400     if (engine_filename.empty()) {
401       // error message already printed by FindEngineFullPathname()
402       return false;
403     }
404
405     document = LoadXMLDocument(engine_filename);
406     document->SetParent(engine_element);
407
408     type = document->GetName();
409     try {
410       if (type == "piston_engine") {
411         HavePistonEngine = true;
412         if (!IsBound) bind();
413         Engines.push_back(new FGPiston(FDMExec, document, numEngines, in));
414       } else if (type == "turbine_engine") {
415         HaveTurbineEngine = true;
416         if (!IsBound) bind();
417         Engines.push_back(new FGTurbine(FDMExec, document, numEngines, in));
418       } else if (type == "turboprop_engine") {
419         HaveTurboPropEngine = true;
420         if (!IsBound) bind();
421         Engines.push_back(new FGTurboProp(FDMExec, document, numEngines, in));
422       } else if (type == "rocket_engine") {
423         HaveRocketEngine = true;
424         if (!IsBound) bind();
425         Engines.push_back(new FGRocket(FDMExec, document, numEngines, in));
426       } else if (type == "electric_engine") {
427         HaveElectricEngine = true;
428         if (!IsBound) bind();
429         Engines.push_back(new FGElectric(FDMExec, document, numEngines, in));
430       } else {
431         cerr << "Unknown engine type: " << type << endl;
432         exit(-5);
433       }
434     } catch (std::string str) {
435       cerr << endl << fgred << str << reset << endl;
436       return false;
437     }
438
439     numEngines++;
440
441     engine_element = el->FindNextElement("engine");
442     ResetParser();
443   }
444
445   CalculateTankInertias();
446
447   // Process fuel dump rate
448   if (el->FindElement("dump-rate"))
449     DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
450
451   PostLoad(el, PropertyManager);
452
453   return true;
454 }
455
456 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457
458 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
459 {
460   string fullpath, localpath;
461   string enginePath = FDMExec->GetEnginePath();
462   string aircraftPath = FDMExec->GetFullAircraftPath();
463   ifstream engine_file;
464
465   string separator = "/";
466
467   fullpath = enginePath + separator;
468   localpath = aircraftPath + separator + "Engines" + separator;
469
470   engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
471   if ( !engine_file.is_open()) {
472     engine_file.open(string(localpath + engine_filename + ".xml").c_str());
473       if ( !engine_file.is_open()) {
474         cerr << " Could not open engine file: " << engine_filename << " in path "
475              << fullpath << " or " << localpath << endl;
476         return string("");
477       } else {
478         return string(localpath + engine_filename + ".xml");
479       }
480   }
481   return string(fullpath + engine_filename + ".xml");
482 }
483
484 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485
486 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
487 {
488   string fullpath, localpath;
489   string enginePath = FDMExec->GetEnginePath();
490   string aircraftPath = FDMExec->GetFullAircraftPath();
491   ifstream* engine_file = new ifstream();
492
493   string separator = "/";
494
495   fullpath = enginePath + separator;
496   localpath = aircraftPath + separator + "Engines" + separator;
497
498   engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
499   if ( !engine_file->is_open()) {
500     engine_file->open(string(localpath + engine_filename + ".xml").c_str());
501       if ( !engine_file->is_open()) {
502         cerr << " Could not open engine file: " << engine_filename << " in path "
503              << fullpath << " or " << localpath << endl;
504       }
505   }
506   return engine_file;
507 }
508
509 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510
511 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
512 {
513   unsigned int i;
514
515   string PropulsionStrings = "";
516   bool firstime = true;
517   stringstream buf;
518
519   for (i=0; i<Engines.size(); i++) {
520     if (firstime)  firstime = false;
521     else           PropulsionStrings += delimiter;
522
523     PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
524   }
525   for (i=0; i<Tanks.size(); i++) {
526     if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
527     else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
528   }
529
530   return PropulsionStrings;
531 }
532
533 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534
535 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
536 {
537   unsigned int i;
538
539   string PropulsionValues = "";
540   bool firstime = true;
541   stringstream buf;
542
543   for (i=0; i<Engines.size(); i++) {
544     if (firstime)  firstime = false;
545     else           PropulsionValues += delimiter;
546
547     PropulsionValues += Engines[i]->GetEngineValues(delimiter);
548   }
549   for (i=0; i<Tanks.size(); i++) {
550     buf << delimiter;
551     buf << Tanks[i]->GetContents();
552   }
553
554   return PropulsionValues;
555 }
556
557 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558
559 string FGPropulsion::GetPropulsionTankReport()
560 {
561   string out="";
562   stringstream outstream;
563   for (unsigned int i=0; i<numTanks; i++)
564   {
565     FGTank* tank = Tanks[i];
566     string tankname="";
567     if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
568       tankname = "Solid Fuel";
569     } else if (tank->GetType() == FGTank::ttFUEL) {
570       tankname = "Fuel";
571     } else if (tank->GetType() == FGTank::ttOXIDIZER) {
572       tankname = "Oxidizer";
573     } else {
574       tankname = "(Unknown tank type)";
575     }
576     outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
577       << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
578          << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
579          << setw(12) << "*" << setw(12) << "*"
580          << setw(12) << "*" << endl;
581   }
582   return outstream.str();
583 }
584
585 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
586
587 const FGColumnVector3& FGPropulsion::GetTanksMoment(void)
588 {
589   vXYZtank_arm.InitMatrix();
590   for (unsigned int i=0; i<Tanks.size(); i++) {
591     vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
592     vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
593     vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
594   }
595   return vXYZtank_arm;
596 }
597
598 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
599
600 double FGPropulsion::GetTanksWeight(void) const
601 {
602   double Tw = 0.0;
603
604   for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
605
606   return Tw;
607 }
608
609 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610
611 const FGMatrix33& FGPropulsion::CalculateTankInertias(void)
612 {
613   unsigned int size;
614
615   size = Tanks.size();
616   if (size == 0) return tankJ;
617
618   tankJ = FGMatrix33();
619
620   for (unsigned int i=0; i<size; i++) {
621     tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
622                                                Tanks[i]->GetXYZ() );
623     tankJ(1,1) += Tanks[i]->GetIxx();
624     tankJ(2,2) += Tanks[i]->GetIyy();
625     tankJ(3,3) += Tanks[i]->GetIzz();
626   }
627
628   return tankJ;
629 }
630
631 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632
633 void FGPropulsion::SetMagnetos(int setting)
634 {
635   if (ActiveEngine < 0) {
636     for (unsigned i=0; i<Engines.size(); i++) {
637       // ToDo: first need to make sure the engine Type is really appropriate:
638       //   do a check to see if it is of type Piston. This should be done for
639       //   all of this kind of possibly across-the-board settings.
640       ((FGPiston*)Engines[i])->SetMagnetos(setting);
641     }
642   } else {
643     ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
644   }
645 }
646
647 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648
649 void FGPropulsion::SetStarter(int setting)
650 {
651   if (ActiveEngine < 0) {
652     for (unsigned i=0; i<Engines.size(); i++) {
653       if (setting == 0)
654         Engines[i]->SetStarter(false);
655       else
656         Engines[i]->SetStarter(true);
657     }
658   } else {
659     if (setting == 0)
660       Engines[ActiveEngine]->SetStarter(false);
661     else
662       Engines[ActiveEngine]->SetStarter(true);
663   }
664 }
665
666 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667
668 void FGPropulsion::SetCutoff(int setting)
669 {
670   if (ActiveEngine < 0) {
671     for (unsigned i=0; i<Engines.size(); i++) {
672       if (setting == 0)
673         ((FGTurbine*)Engines[i])->SetCutoff(false);
674       else
675         ((FGTurbine*)Engines[i])->SetCutoff(true);
676     }
677   } else {
678     if (setting == 0)
679       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
680     else
681       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
682   }
683 }
684
685 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
686
687 void FGPropulsion::SetActiveEngine(int engine)
688 {
689   if (engine >= (int)Engines.size() || engine < 0)
690     ActiveEngine = -1;
691   else
692     ActiveEngine = engine;
693 }
694
695 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696
697 double FGPropulsion::Transfer(int source, int target, double amount)
698 {
699  double shortage, overage;
700
701   if (source == -1) {
702      shortage = 0.0;
703   } else {
704      shortage = Tanks[source]->Drain(amount);
705   }
706   if (target == -1) {
707      overage = 0.0;
708   } else {
709      overage = Tanks[target]->Fill(amount - shortage);
710   }
711   return overage;
712 }
713
714 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
715
716 void FGPropulsion::DoRefuel(double time_slice)
717 {
718   unsigned int i;
719
720   double fillrate = 100 * time_slice;   // 100 lbs/sec = 6000 lbs/min
721   int TanksNotFull = 0;
722
723   for (i=0; i<numTanks; i++) {
724     if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
725   }
726
727   if (TanksNotFull) {
728     for (i=0; i<numTanks; i++) {
729       if (Tanks[i]->GetPctFull() < 99.99)
730           Transfer(-1, i, fillrate/TanksNotFull);
731     }
732   }
733 }
734
735 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736
737 void FGPropulsion::DumpFuel(double time_slice)
738 {
739   unsigned int i;
740   int TanksDumping = 0;
741
742   for (i=0; i<numTanks; i++) {
743     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
744   }
745
746   if (TanksDumping == 0) return;
747
748   double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
749
750   for (i=0; i<numTanks; i++) {
751     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
752       Transfer(i, -1, dump_rate_per_tank);
753     }
754   }
755 }
756
757 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758
759 void FGPropulsion::SetFuelFreeze(bool f)
760 {
761   FuelFreeze = f;
762   for (unsigned int i=0; i<numEngines; i++) {
763     Engines[i]->SetFuelFreeze(f);
764   }
765 }
766
767 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
768
769 void FGPropulsion::bind(void)
770 {
771   typedef double (FGPropulsion::*PMF)(int) const;
772   typedef int (FGPropulsion::*iPMF)(void) const;
773
774   IsBound = true;
775   PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
776   if (HaveTurbineEngine) {
777     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
778     PropertyManager->Tie("propulsion/cutoff_cmd", this,  (iPMF)0, &FGPropulsion::SetCutoff,   false);
779   }
780
781   if (HavePistonEngine) {
782     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
783     PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
784   }
785
786   PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
787                         &FGPropulsion::SetActiveEngine, true);
788   PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
789   PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
790                         &FGPropulsion::SetRefuel, true);
791   PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
792                         &FGPropulsion::SetFuelDump, true);
793   PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
794                        (PMF)&FGPropulsion::GetForces);
795   PropertyManager->Tie("forces/fby-prop-lbs", this,2,
796                        (PMF)&FGPropulsion::GetForces);
797   PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
798                        (PMF)&FGPropulsion::GetForces);
799   PropertyManager->Tie("moments/l-prop-lbsft", this,1,
800                        (PMF)&FGPropulsion::GetMoments);
801   PropertyManager->Tie("moments/m-prop-lbsft", this,2,
802                        (PMF)&FGPropulsion::GetMoments);
803   PropertyManager->Tie("moments/n-prop-lbsft", this,3,
804                        (PMF)&FGPropulsion::GetMoments);
805
806 }
807
808 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809 //    The bitmasked value choices are as follows:
810 //    unset: In this case (the default) JSBSim would only print
811 //       out the normally expected messages, essentially echoing
812 //       the config files as they are read. If the environment
813 //       variable is not set, debug_lvl is set to 1 internally
814 //    0: This requests JSBSim not to output any messages
815 //       whatsoever.
816 //    1: This value explicity requests the normal JSBSim
817 //       startup messages
818 //    2: This value asks for a message to be printed out when
819 //       a class is instantiated
820 //    4: When this value is set, a message is displayed when a
821 //       FGModel object executes its Run() method
822 //    8: When this value is set, various runtime state variables
823 //       are printed out periodically
824 //    16: When set various parameters are sanity checked and
825 //       a message is printed out when they go out of bounds
826
827 void FGPropulsion::Debug(int from)
828 {
829   if (debug_lvl <= 0) return;
830
831   if (debug_lvl & 1) { // Standard console startup message output
832     if (from == 2) { // Loader
833       cout << endl << "  Propulsion:" << endl;
834     }
835   }
836   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
837     if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
838     if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
839   }
840   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
841   }
842   if (debug_lvl & 8 ) { // Runtime state variables
843   }
844   if (debug_lvl & 16) { // Sanity checking
845   }
846   if (debug_lvl & 64) {
847     if (from == 0) { // Constructor
848       cout << IdSrc << endl;
849       cout << IdHdr << endl;
850     }
851   }
852 }
853 }