]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGPropulsion.cpp
Fixed a bug where a structural to Body frame conversion was being doen twice for...
[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.62 2012/07/19 03:50:19 jberndt 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   unsigned int size;
624
625   size = Tanks.size();
626   if (size == 0) return tankJ;
627
628   tankJ = FGMatrix33();
629
630   for (unsigned int i=0; i<size; i++) {
631     FGColumnVector3 vTankStructVec = in.vXYZcg - Tanks[i]->GetXYZ();
632
633     tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
634                                                              vTankStructVec);
635     tankJ(1,1) += Tanks[i]->GetIxx();
636     tankJ(2,2) += Tanks[i]->GetIyy();
637     tankJ(3,3) += Tanks[i]->GetIzz();
638   }
639
640   return tankJ;
641 }
642
643 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
644
645 void FGPropulsion::SetMagnetos(int setting)
646 {
647   if (ActiveEngine < 0) {
648     for (unsigned i=0; i<Engines.size(); i++) {
649       // ToDo: first need to make sure the engine Type is really appropriate:
650       //   do a check to see if it is of type Piston. This should be done for
651       //   all of this kind of possibly across-the-board settings.
652       ((FGPiston*)Engines[i])->SetMagnetos(setting);
653     }
654   } else {
655     ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
656   }
657 }
658
659 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
660
661 void FGPropulsion::SetStarter(int setting)
662 {
663   if (ActiveEngine < 0) {
664     for (unsigned i=0; i<Engines.size(); i++) {
665       if (setting == 0)
666         Engines[i]->SetStarter(false);
667       else
668         Engines[i]->SetStarter(true);
669     }
670   } else {
671     if (setting == 0)
672       Engines[ActiveEngine]->SetStarter(false);
673     else
674       Engines[ActiveEngine]->SetStarter(true);
675   }
676 }
677
678 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
679
680 void FGPropulsion::SetCutoff(int setting)
681 {
682   if (ActiveEngine < 0) {
683     for (unsigned i=0; i<Engines.size(); i++) {
684       if (setting == 0)
685         ((FGTurbine*)Engines[i])->SetCutoff(false);
686       else
687         ((FGTurbine*)Engines[i])->SetCutoff(true);
688     }
689   } else {
690     if (setting == 0)
691       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
692     else
693       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
694   }
695 }
696
697 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698
699 void FGPropulsion::SetActiveEngine(int engine)
700 {
701   if (engine >= (int)Engines.size() || engine < 0)
702     ActiveEngine = -1;
703   else
704     ActiveEngine = engine;
705 }
706
707 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
708
709 double FGPropulsion::Transfer(int source, int target, double amount)
710 {
711  double shortage, overage;
712
713   if (source == -1) {
714      shortage = 0.0;
715   } else {
716      shortage = Tanks[source]->Drain(amount);
717   }
718   if (target == -1) {
719      overage = 0.0;
720   } else {
721      overage = Tanks[target]->Fill(amount - shortage);
722   }
723   return overage;
724 }
725
726 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727
728 void FGPropulsion::DoRefuel(double time_slice)
729 {
730   unsigned int i;
731
732   double fillrate = 100 * time_slice;   // 100 lbs/sec = 6000 lbs/min
733   int TanksNotFull = 0;
734
735   for (i=0; i<numTanks; i++) {
736     if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
737   }
738
739   if (TanksNotFull) {
740     for (i=0; i<numTanks; i++) {
741       if (Tanks[i]->GetPctFull() < 99.99)
742           Transfer(-1, i, fillrate/TanksNotFull);
743     }
744   }
745 }
746
747 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748
749 void FGPropulsion::DumpFuel(double time_slice)
750 {
751   unsigned int i;
752   int TanksDumping = 0;
753
754   for (i=0; i<numTanks; i++) {
755     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
756   }
757
758   if (TanksDumping == 0) return;
759
760   double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
761
762   for (i=0; i<numTanks; i++) {
763     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
764       Transfer(i, -1, dump_rate_per_tank);
765     }
766   }
767 }
768
769 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
770
771 void FGPropulsion::SetFuelFreeze(bool f)
772 {
773   FuelFreeze = f;
774   for (unsigned int i=0; i<numEngines; i++) {
775     Engines[i]->SetFuelFreeze(f);
776   }
777 }
778
779 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780
781 void FGPropulsion::bind(void)
782 {
783   typedef double (FGPropulsion::*PMF)(int) const;
784   typedef int (FGPropulsion::*iPMF)(void) const;
785
786   IsBound = true;
787   PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
788   if (HaveTurbineEngine) {
789     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
790     PropertyManager->Tie("propulsion/cutoff_cmd", this,  (iPMF)0, &FGPropulsion::SetCutoff,   false);
791   }
792
793   if (HavePistonEngine) {
794     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
795     PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
796   }
797
798   PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
799                         &FGPropulsion::SetActiveEngine, true);
800   PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
801   PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
802                         &FGPropulsion::SetRefuel, true);
803   PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
804                         &FGPropulsion::SetFuelDump, true);
805   PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
806                        (PMF)&FGPropulsion::GetForces);
807   PropertyManager->Tie("forces/fby-prop-lbs", this,2,
808                        (PMF)&FGPropulsion::GetForces);
809   PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
810                        (PMF)&FGPropulsion::GetForces);
811   PropertyManager->Tie("moments/l-prop-lbsft", this,1,
812                        (PMF)&FGPropulsion::GetMoments);
813   PropertyManager->Tie("moments/m-prop-lbsft", this,2,
814                        (PMF)&FGPropulsion::GetMoments);
815   PropertyManager->Tie("moments/n-prop-lbsft", this,3,
816                        (PMF)&FGPropulsion::GetMoments);
817
818 }
819
820 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 //    The bitmasked value choices are as follows:
822 //    unset: In this case (the default) JSBSim would only print
823 //       out the normally expected messages, essentially echoing
824 //       the config files as they are read. If the environment
825 //       variable is not set, debug_lvl is set to 1 internally
826 //    0: This requests JSBSim not to output any messages
827 //       whatsoever.
828 //    1: This value explicity requests the normal JSBSim
829 //       startup messages
830 //    2: This value asks for a message to be printed out when
831 //       a class is instantiated
832 //    4: When this value is set, a message is displayed when a
833 //       FGModel object executes its Run() method
834 //    8: When this value is set, various runtime state variables
835 //       are printed out periodically
836 //    16: When set various parameters are sanity checked and
837 //       a message is printed out when they go out of bounds
838
839 void FGPropulsion::Debug(int from)
840 {
841   if (debug_lvl <= 0) return;
842
843   if (debug_lvl & 1) { // Standard console startup message output
844     if (from == 2) { // Loader
845       cout << endl << "  Propulsion:" << endl;
846     }
847   }
848   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
849     if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
850     if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
851   }
852   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
853   }
854   if (debug_lvl & 8 ) { // Runtime state variables
855   }
856   if (debug_lvl & 16) { // Sanity checking
857   }
858   if (debug_lvl & 64) {
859     if (from == 0) { // Constructor
860       cout << IdSrc << endl;
861       cout << IdHdr << endl;
862     }
863   }
864 }
865 }