]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGPropulsion.cpp
Merge branch 'next' of 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 <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.61 2012/04/14 18:10:44 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   bool FuelStarved = Starved;
241   Starved = true;
242
243   // Process Oxidizer tanks, if any
244   if (engine->GetType() == FGEngine::etRocket) {
245     while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
246       for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
247         unsigned int TankId = engine->GetSourceTank(i);
248         FGTank* Tank = Tanks[TankId];
249         unsigned int TankPriority = Tank->GetPriority();
250         if (TankPriority != 0) {
251           switch(Tank->GetType()) {
252           case FGTank::ttFUEL:
253             // Skip this here (done above)
254             break;
255           case FGTank::ttOXIDIZER:
256             hasOxTanks = true;
257             if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
258               TanksWithOxidizer++;
259               if (TanksWithFuel > 0) Starved = false;
260               FeedListOxi.push_back(TankId);
261             }
262             break;
263           }
264         }
265       }
266       if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
267     }
268   }
269
270   bool OxiStarved = Starved;
271
272   engine->SetStarved(FuelStarved || (hasOxTanks && OxiStarved)); // Tanks can be refilled, so be sure to reset engine Starved flag here.
273
274   // No fuel or fuel/oxidizer found at any priority!
275 //  if (Starved) return;
276   if (FuelStarved || (hasOxTanks && OxiStarved)) return;
277
278   double FuelToBurn = engine->CalcFuelNeed();            // How much fuel does this engine need?
279   double FuelNeededPerTank = FuelToBurn / TanksWithFuel; // Determine fuel needed per tank.  
280   for (unsigned int i=0; i<FeedListFuel.size(); i++) {
281     Tanks[FeedListFuel[i]]->Drain(FuelNeededPerTank); 
282   }
283
284   if (engine->GetType() == FGEngine::etRocket) {
285     double OxidizerToBurn = engine->CalcOxidizerNeed();                // How much fuel does this engine need?
286     double OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer; // Determine fuel needed per tank.  
287     for (unsigned int i=0; i<FeedListOxi.size(); i++) {
288       Tanks[FeedListOxi[i]]->Drain(OxidizerNeededPerTank); 
289     }
290   }
291
292 }
293
294 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
295
296 bool FGPropulsion::GetSteadyState(void)
297 {
298   double currentThrust = 0, lastThrust = -1;
299   int steady_count = 0, j = 0;
300   bool steady = false;
301   bool TrimMode = FDMExec->GetTrimStatus();
302
303   vForces.InitMatrix();
304   vMoments.InitMatrix();
305
306   if (!FGModel::Run(false)) {
307     FDMExec->SetTrimStatus(true);
308
309     for (unsigned int i=0; i<numEngines; i++) {
310       steady=false;
311       steady_count=0;
312       j=0;
313       while (!steady && j < 6000) {
314         Engines[i]->Calculate();
315         lastThrust = currentThrust;
316         currentThrust = Engines[i]->GetThrust();
317         if (fabs(lastThrust-currentThrust) < 0.0001) {
318           steady_count++;
319           if (steady_count > 120) {
320             steady=true;
321           }
322         } else {
323           steady_count=0;
324         }
325         j++;
326       }
327       vForces  += Engines[i]->GetBodyForces();  // sum body frame forces
328       vMoments += Engines[i]->GetMoments();     // sum body frame moments
329     }
330
331     FDMExec->SetTrimStatus(TrimMode);
332
333     return false;
334   } else {
335     return true;
336   }
337 }
338
339 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340
341 void FGPropulsion::InitRunning(int n)
342 {
343   if (n >= 0) { // A specific engine is supposed to be initialized
344
345     if (n >= (int)GetNumEngines() ) {
346       throw(string("Tried to initialize a non-existent engine!"));
347     }
348
349     in.ThrottleCmd[n] = in.ThrottlePos[n] = 1; // Set the throttle command and position
350     in.MixtureCmd[n] = in.MixturePos[n] = 1;   // Set the mixture command and position
351
352     GetEngine(n)->InitRunning();
353     GetSteadyState();
354
355     InitializedEngines = 1 << n;
356     HasInitializedEngines = true;
357
358   } else if (n < 0) { // -1 refers to "All Engines"
359
360     for (unsigned int i=0; i<GetNumEngines(); i++) {
361       in.ThrottleCmd[i] = in.ThrottlePos[i] = 1; // Set the throttle command and position
362       in.MixtureCmd[i] = in.MixturePos[i] = 1;   // Set the mixture command and position
363       GetEngine(i)->InitRunning();
364     }
365
366     GetSteadyState();
367     InitializedEngines = -1;
368     HasInitializedEngines = true;
369   }
370 }
371
372 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
373
374 bool FGPropulsion::Load(Element* el)
375 {
376   string type, engine_filename;
377
378   Debug(2);
379
380   FGModel::Load(el); // Perform base class Load.
381
382   // Process tank definitions first to establish the number of fuel tanks
383
384   Element* tank_element = el->FindElement("tank");
385   while (tank_element) {
386     Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
387     if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
388     else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
389     else {cerr << "Unknown tank type specified." << endl; return false;}
390     numTanks++;
391     tank_element = el->FindNextElement("tank");
392   }
393   numSelectedFuelTanks = numFuelTanks;
394   numSelectedOxiTanks  = numOxiTanks;
395
396   Element* engine_element = el->FindElement("engine");
397   while (engine_element) {
398     engine_filename = engine_element->GetAttributeValue("file");
399
400     if (engine_filename.empty()) {
401       cerr << "Engine definition did not supply an engine file." << endl;
402       return false;
403     }
404
405     engine_filename = FindEngineFullPathname(engine_filename);
406     if (engine_filename.empty()) {
407       // error message already printed by FindEngineFullPathname()
408       return false;
409     }
410
411     document = LoadXMLDocument(engine_filename);
412     document->SetParent(engine_element);
413
414     type = document->GetName();
415     try {
416       if (type == "piston_engine") {
417         HavePistonEngine = true;
418         if (!IsBound) bind();
419         Engines.push_back(new FGPiston(FDMExec, document, numEngines, in));
420       } else if (type == "turbine_engine") {
421         HaveTurbineEngine = true;
422         if (!IsBound) bind();
423         Engines.push_back(new FGTurbine(FDMExec, document, numEngines, in));
424       } else if (type == "turboprop_engine") {
425         HaveTurboPropEngine = true;
426         if (!IsBound) bind();
427         Engines.push_back(new FGTurboProp(FDMExec, document, numEngines, in));
428       } else if (type == "rocket_engine") {
429         HaveRocketEngine = true;
430         if (!IsBound) bind();
431         Engines.push_back(new FGRocket(FDMExec, document, numEngines, in));
432       } else if (type == "electric_engine") {
433         HaveElectricEngine = true;
434         if (!IsBound) bind();
435         Engines.push_back(new FGElectric(FDMExec, document, numEngines, in));
436       } else {
437         cerr << "Unknown engine type: " << type << endl;
438         exit(-5);
439       }
440     } catch (std::string str) {
441       cerr << endl << fgred << str << reset << endl;
442       return false;
443     }
444
445     numEngines++;
446
447     engine_element = el->FindNextElement("engine");
448     ResetParser();
449   }
450
451   CalculateTankInertias();
452
453   // Process fuel dump rate
454   if (el->FindElement("dump-rate"))
455     DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
456
457   PostLoad(el, PropertyManager);
458
459   return true;
460 }
461
462 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463
464 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
465 {
466   string fullpath, localpath;
467   string enginePath = FDMExec->GetEnginePath();
468   string aircraftPath = FDMExec->GetFullAircraftPath();
469   ifstream engine_file;
470
471   string separator = "/";
472
473   fullpath = enginePath + separator;
474   localpath = aircraftPath + separator + "Engines" + separator;
475
476   engine_file.open(string(localpath + engine_filename + ".xml").c_str());
477   if ( !engine_file.is_open()) {
478     engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
479       if ( !engine_file.is_open()) {
480         cerr << " Could not open engine file: " << engine_filename << " in path "
481              << fullpath << " or " << localpath << endl;
482         return string("");
483       } else {
484         return string(fullpath + engine_filename + ".xml");
485       }
486   }
487   return string(localpath + engine_filename + ".xml");
488 }
489
490 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491
492 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
493 {
494   string fullpath, localpath;
495   string enginePath = FDMExec->GetEnginePath();
496   string aircraftPath = FDMExec->GetFullAircraftPath();
497   ifstream* engine_file = new ifstream();
498
499   string separator = "/";
500
501   fullpath = enginePath + separator;
502   localpath = aircraftPath + separator + "Engines" + separator;
503
504   engine_file->open(string(localpath + engine_filename + ".xml").c_str());
505   if ( !engine_file->is_open()) {
506     engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
507       if ( !engine_file->is_open()) {
508         cerr << " Could not open engine file: " << engine_filename << " in path "
509              << localpath << " or " << fullpath << endl;
510       }
511   }
512   return engine_file;
513 }
514
515 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516
517 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
518 {
519   unsigned int i;
520
521   string PropulsionStrings = "";
522   bool firstime = true;
523   stringstream buf;
524
525   for (i=0; i<Engines.size(); i++) {
526     if (firstime)  firstime = false;
527     else           PropulsionStrings += delimiter;
528
529     PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
530   }
531   for (i=0; i<Tanks.size(); i++) {
532     if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
533     else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
534   }
535
536   PropulsionStrings += buf.str();
537   buf.str("");
538
539   return PropulsionStrings;
540 }
541
542 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
543
544 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
545 {
546   unsigned int i;
547
548   string PropulsionValues = "";
549   bool firstime = true;
550   stringstream buf;
551
552   for (i=0; i<Engines.size(); i++) {
553     if (firstime)  firstime = false;
554     else           PropulsionValues += delimiter;
555
556     PropulsionValues += Engines[i]->GetEngineValues(delimiter);
557   }
558   for (i=0; i<Tanks.size(); i++) {
559     buf << delimiter;
560     buf << Tanks[i]->GetContents();
561   }
562
563   PropulsionValues += buf.str();
564   buf.str("");
565
566   return PropulsionValues;
567 }
568
569 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570
571 string FGPropulsion::GetPropulsionTankReport()
572 {
573   string out="";
574   stringstream outstream;
575   for (unsigned int i=0; i<numTanks; i++)
576   {
577     FGTank* tank = Tanks[i];
578     string tankname="";
579     if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
580       tankname = "Solid Fuel";
581     } else if (tank->GetType() == FGTank::ttFUEL) {
582       tankname = "Fuel";
583     } else if (tank->GetType() == FGTank::ttOXIDIZER) {
584       tankname = "Oxidizer";
585     } else {
586       tankname = "(Unknown tank type)";
587     }
588     outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
589       << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
590          << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
591          << setw(12) << "*" << setw(12) << "*"
592          << setw(12) << "*" << endl;
593   }
594   return outstream.str();
595 }
596
597 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
598
599 const FGColumnVector3& FGPropulsion::GetTanksMoment(void)
600 {
601   vXYZtank_arm.InitMatrix();
602   for (unsigned int i=0; i<Tanks.size(); i++) {
603     vXYZtank_arm += Tanks[i]->GetXYZ() * Tanks[i]->GetContents();
604   }
605   return vXYZtank_arm;
606 }
607
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609
610 double FGPropulsion::GetTanksWeight(void) const
611 {
612   double Tw = 0.0;
613
614   for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
615
616   return Tw;
617 }
618
619 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620
621 const FGMatrix33& FGPropulsion::CalculateTankInertias(void)
622 {
623   const FGMatrix33 Ts2b(-inchtoft, 0., 0., 0., inchtoft, 0., 0., 0., -inchtoft);
624   unsigned int size;
625
626   size = Tanks.size();
627   if (size == 0) return tankJ;
628
629   tankJ = FGMatrix33();
630
631   for (unsigned int i=0; i<size; i++) {
632     FGColumnVector3 vTankBodyVec = Ts2b * (in.vXYZcg - Tanks[i]->GetXYZ());
633
634     tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
635                                                              vTankBodyVec);
636     tankJ(1,1) += Tanks[i]->GetIxx();
637     tankJ(2,2) += Tanks[i]->GetIyy();
638     tankJ(3,3) += Tanks[i]->GetIzz();
639   }
640
641   return tankJ;
642 }
643
644 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645
646 void FGPropulsion::SetMagnetos(int setting)
647 {
648   if (ActiveEngine < 0) {
649     for (unsigned i=0; i<Engines.size(); i++) {
650       // ToDo: first need to make sure the engine Type is really appropriate:
651       //   do a check to see if it is of type Piston. This should be done for
652       //   all of this kind of possibly across-the-board settings.
653       ((FGPiston*)Engines[i])->SetMagnetos(setting);
654     }
655   } else {
656     ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
657   }
658 }
659
660 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661
662 void FGPropulsion::SetStarter(int setting)
663 {
664   if (ActiveEngine < 0) {
665     for (unsigned i=0; i<Engines.size(); i++) {
666       if (setting == 0)
667         Engines[i]->SetStarter(false);
668       else
669         Engines[i]->SetStarter(true);
670     }
671   } else {
672     if (setting == 0)
673       Engines[ActiveEngine]->SetStarter(false);
674     else
675       Engines[ActiveEngine]->SetStarter(true);
676   }
677 }
678
679 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
680
681 void FGPropulsion::SetCutoff(int setting)
682 {
683   if (ActiveEngine < 0) {
684     for (unsigned i=0; i<Engines.size(); i++) {
685       if (setting == 0)
686         ((FGTurbine*)Engines[i])->SetCutoff(false);
687       else
688         ((FGTurbine*)Engines[i])->SetCutoff(true);
689     }
690   } else {
691     if (setting == 0)
692       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
693     else
694       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
695   }
696 }
697
698 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699
700 void FGPropulsion::SetActiveEngine(int engine)
701 {
702   if (engine >= (int)Engines.size() || engine < 0)
703     ActiveEngine = -1;
704   else
705     ActiveEngine = engine;
706 }
707
708 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
709
710 double FGPropulsion::Transfer(int source, int target, double amount)
711 {
712  double shortage, overage;
713
714   if (source == -1) {
715      shortage = 0.0;
716   } else {
717      shortage = Tanks[source]->Drain(amount);
718   }
719   if (target == -1) {
720      overage = 0.0;
721   } else {
722      overage = Tanks[target]->Fill(amount - shortage);
723   }
724   return overage;
725 }
726
727 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728
729 void FGPropulsion::DoRefuel(double time_slice)
730 {
731   unsigned int i;
732
733   double fillrate = 100 * time_slice;   // 100 lbs/sec = 6000 lbs/min
734   int TanksNotFull = 0;
735
736   for (i=0; i<numTanks; i++) {
737     if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
738   }
739
740   if (TanksNotFull) {
741     for (i=0; i<numTanks; i++) {
742       if (Tanks[i]->GetPctFull() < 99.99)
743           Transfer(-1, i, fillrate/TanksNotFull);
744     }
745   }
746 }
747
748 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
749
750 void FGPropulsion::DumpFuel(double time_slice)
751 {
752   unsigned int i;
753   int TanksDumping = 0;
754
755   for (i=0; i<numTanks; i++) {
756     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
757   }
758
759   if (TanksDumping == 0) return;
760
761   double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
762
763   for (i=0; i<numTanks; i++) {
764     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
765       Transfer(i, -1, dump_rate_per_tank);
766     }
767   }
768 }
769
770 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771
772 void FGPropulsion::SetFuelFreeze(bool f)
773 {
774   FuelFreeze = f;
775   for (unsigned int i=0; i<numEngines; i++) {
776     Engines[i]->SetFuelFreeze(f);
777   }
778 }
779
780 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781
782 void FGPropulsion::bind(void)
783 {
784   typedef double (FGPropulsion::*PMF)(int) const;
785   typedef int (FGPropulsion::*iPMF)(void) const;
786
787   IsBound = true;
788   PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
789   if (HaveTurbineEngine) {
790     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
791     PropertyManager->Tie("propulsion/cutoff_cmd", this,  (iPMF)0, &FGPropulsion::SetCutoff,   false);
792   }
793
794   if (HavePistonEngine) {
795     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
796     PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
797   }
798
799   PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
800                         &FGPropulsion::SetActiveEngine, true);
801   PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
802   PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
803                         &FGPropulsion::SetRefuel, true);
804   PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
805                         &FGPropulsion::SetFuelDump, true);
806   PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
807                        (PMF)&FGPropulsion::GetForces);
808   PropertyManager->Tie("forces/fby-prop-lbs", this,2,
809                        (PMF)&FGPropulsion::GetForces);
810   PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
811                        (PMF)&FGPropulsion::GetForces);
812   PropertyManager->Tie("moments/l-prop-lbsft", this,1,
813                        (PMF)&FGPropulsion::GetMoments);
814   PropertyManager->Tie("moments/m-prop-lbsft", this,2,
815                        (PMF)&FGPropulsion::GetMoments);
816   PropertyManager->Tie("moments/n-prop-lbsft", this,3,
817                        (PMF)&FGPropulsion::GetMoments);
818
819 }
820
821 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 //    The bitmasked value choices are as follows:
823 //    unset: In this case (the default) JSBSim would only print
824 //       out the normally expected messages, essentially echoing
825 //       the config files as they are read. If the environment
826 //       variable is not set, debug_lvl is set to 1 internally
827 //    0: This requests JSBSim not to output any messages
828 //       whatsoever.
829 //    1: This value explicity requests the normal JSBSim
830 //       startup messages
831 //    2: This value asks for a message to be printed out when
832 //       a class is instantiated
833 //    4: When this value is set, a message is displayed when a
834 //       FGModel object executes its Run() method
835 //    8: When this value is set, various runtime state variables
836 //       are printed out periodically
837 //    16: When set various parameters are sanity checked and
838 //       a message is printed out when they go out of bounds
839
840 void FGPropulsion::Debug(int from)
841 {
842   if (debug_lvl <= 0) return;
843
844   if (debug_lvl & 1) { // Standard console startup message output
845     if (from == 2) { // Loader
846       cout << endl << "  Propulsion:" << endl;
847     }
848   }
849   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
850     if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
851     if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
852   }
853   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
854   }
855   if (debug_lvl & 8 ) { // Runtime state variables
856   }
857   if (debug_lvl & 16) { // Sanity checking
858   }
859   if (debug_lvl & 64) {
860     if (from == 0) { // Constructor
861       cout << IdSrc << endl;
862       cout << IdHdr << endl;
863     }
864   }
865 }
866 }