]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGPropulsion.cpp
New version of JSBSim, a big rewrite.
[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.50 2011/08/03 03:21:06 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   // 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   bool ThrottleAdded = false;
372
373   Debug(2);
374
375   FGModel::Load(el); // Perform base class Load.
376
377   // Process tank definitions first to establish the number of fuel tanks
378
379   Element* tank_element = el->FindElement("tank");
380   while (tank_element) {
381     Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
382     if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
383     else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
384     else {cerr << "Unknown tank type specified." << endl; return false;}
385     numTanks++;
386     tank_element = el->FindNextElement("tank");
387   }
388   numSelectedFuelTanks = numFuelTanks;
389   numSelectedOxiTanks  = numOxiTanks;
390
391   Element* engine_element = el->FindElement("engine");
392   while (engine_element) {
393     engine_filename = engine_element->GetAttributeValue("file");
394
395     if (engine_filename.empty()) {
396       cerr << "Engine definition did not supply an engine file." << endl;
397       return false;
398     }
399
400     engine_filename = FindEngineFullPathname(engine_filename);
401     if (engine_filename.empty()) {
402       // error message already printed by FindEngineFullPathname()
403       return false;
404     }
405
406     document = LoadXMLDocument(engine_filename);
407     document->SetParent(engine_element);
408
409     type = document->GetName();
410     try {
411       if (type == "piston_engine") {
412         HavePistonEngine = true;
413         if (!IsBound) bind();
414         Engines.push_back(new FGPiston(FDMExec, document, numEngines, in));
415       } else if (type == "turbine_engine") {
416         HaveTurbineEngine = true;
417         if (!IsBound) bind();
418         Engines.push_back(new FGTurbine(FDMExec, document, numEngines, in));
419       } else if (type == "turboprop_engine") {
420         HaveTurboPropEngine = true;
421         if (!IsBound) bind();
422         Engines.push_back(new FGTurboProp(FDMExec, document, numEngines, in));
423       } else if (type == "rocket_engine") {
424         HaveRocketEngine = true;
425         if (!IsBound) bind();
426         Engines.push_back(new FGRocket(FDMExec, document, numEngines, in));
427       } else if (type == "electric_engine") {
428         HaveElectricEngine = true;
429         if (!IsBound) bind();
430         Engines.push_back(new FGElectric(FDMExec, document, numEngines, in));
431       } else {
432         cerr << "Unknown engine type: " << type << endl;
433         exit(-5);
434       }
435     } catch (std::string str) {
436       cerr << endl << fgred << str << reset << endl;
437       return false;
438     }
439
440     numEngines++;
441
442     engine_element = el->FindNextElement("engine");
443     ResetParser();
444   }
445
446   CalculateTankInertias();
447
448   // Process fuel dump rate
449   if (el->FindElement("dump-rate"))
450     DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
451
452   PostLoad(el, PropertyManager);
453
454   return true;
455 }
456
457 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458
459 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
460 {
461   string fullpath, localpath;
462   string enginePath = FDMExec->GetEnginePath();
463   string aircraftPath = FDMExec->GetFullAircraftPath();
464   ifstream engine_file;
465
466   string separator = "/";
467
468   fullpath = enginePath + separator;
469   localpath = aircraftPath + separator + "Engines" + separator;
470
471   engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
472   if ( !engine_file.is_open()) {
473     engine_file.open(string(localpath + engine_filename + ".xml").c_str());
474       if ( !engine_file.is_open()) {
475         cerr << " Could not open engine file: " << engine_filename << " in path "
476              << fullpath << " or " << localpath << endl;
477         return string("");
478       } else {
479         return string(localpath + engine_filename + ".xml");
480       }
481   }
482   return string(fullpath + engine_filename + ".xml");
483 }
484
485 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486
487 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
488 {
489   string fullpath, localpath;
490   string enginePath = FDMExec->GetEnginePath();
491   string aircraftPath = FDMExec->GetFullAircraftPath();
492   ifstream* engine_file = new ifstream();
493
494   string separator = "/";
495
496   fullpath = enginePath + separator;
497   localpath = aircraftPath + separator + "Engines" + separator;
498
499   engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
500   if ( !engine_file->is_open()) {
501     engine_file->open(string(localpath + engine_filename + ".xml").c_str());
502       if ( !engine_file->is_open()) {
503         cerr << " Could not open engine file: " << engine_filename << " in path "
504              << fullpath << " or " << localpath << endl;
505       }
506   }
507   return engine_file;
508 }
509
510 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511
512 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
513 {
514   unsigned int i;
515
516   string PropulsionStrings = "";
517   bool firstime = true;
518   stringstream buf;
519
520   for (i=0; i<Engines.size(); i++) {
521     if (firstime)  firstime = false;
522     else           PropulsionStrings += delimiter;
523
524     PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
525   }
526   for (i=0; i<Tanks.size(); i++) {
527     if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
528     else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
529   }
530
531   return PropulsionStrings;
532 }
533
534 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535
536 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
537 {
538   unsigned int i;
539
540   string PropulsionValues = "";
541   bool firstime = true;
542   stringstream buf;
543
544   for (i=0; i<Engines.size(); i++) {
545     if (firstime)  firstime = false;
546     else           PropulsionValues += delimiter;
547
548     PropulsionValues += Engines[i]->GetEngineValues(delimiter);
549   }
550   for (i=0; i<Tanks.size(); i++) {
551     buf << delimiter;
552     buf << Tanks[i]->GetContents();
553   }
554
555   return PropulsionValues;
556 }
557
558 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559
560 string FGPropulsion::GetPropulsionTankReport()
561 {
562   string out="";
563   stringstream outstream;
564   for (unsigned int i=0; i<numTanks; i++)
565   {
566     FGTank* tank = Tanks[i];
567     string tankname="";
568     if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
569       tankname = "Solid Fuel";
570     } else if (tank->GetType() == FGTank::ttFUEL) {
571       tankname = "Fuel";
572     } else if (tank->GetType() == FGTank::ttOXIDIZER) {
573       tankname = "Oxidizer";
574     } else {
575       tankname = "(Unknown tank type)";
576     }
577     outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
578       << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
579          << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
580          << setw(12) << "*" << setw(12) << "*"
581          << setw(12) << "*" << endl;
582   }
583   return outstream.str();
584 }
585
586 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
587
588 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
589 {
590   vXYZtank_arm.InitMatrix();
591   for (unsigned int i=0; i<Tanks.size(); i++) {
592     vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
593     vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
594     vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
595   }
596   return vXYZtank_arm;
597 }
598
599 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
600
601 double FGPropulsion::GetTanksWeight(void)
602 {
603   double Tw = 0.0;
604
605   for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
606
607   return Tw;
608 }
609
610 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611
612 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
613 {
614   unsigned int size;
615
616   size = Tanks.size();
617   if (size == 0) return tankJ;
618
619   tankJ = FGMatrix33();
620
621   for (unsigned int i=0; i<size; i++) {
622     tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
623                                                Tanks[i]->GetXYZ() );
624     tankJ(1,1) += Tanks[i]->GetIxx();
625     tankJ(2,2) += Tanks[i]->GetIyy();
626     tankJ(3,3) += Tanks[i]->GetIzz();
627   }
628
629   return tankJ;
630 }
631
632 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633
634 void FGPropulsion::SetMagnetos(int setting)
635 {
636   if (ActiveEngine < 0) {
637     for (unsigned i=0; i<Engines.size(); i++) {
638       // ToDo: first need to make sure the engine Type is really appropriate:
639       //   do a check to see if it is of type Piston. This should be done for
640       //   all of this kind of possibly across-the-board settings.
641       ((FGPiston*)Engines[i])->SetMagnetos(setting);
642     }
643   } else {
644     ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
645   }
646 }
647
648 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649
650 void FGPropulsion::SetStarter(int setting)
651 {
652   if (ActiveEngine < 0) {
653     for (unsigned i=0; i<Engines.size(); i++) {
654       if (setting == 0)
655         Engines[i]->SetStarter(false);
656       else
657         Engines[i]->SetStarter(true);
658     }
659   } else {
660     if (setting == 0)
661       Engines[ActiveEngine]->SetStarter(false);
662     else
663       Engines[ActiveEngine]->SetStarter(true);
664   }
665 }
666
667 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668
669 void FGPropulsion::SetCutoff(int setting)
670 {
671   if (ActiveEngine < 0) {
672     for (unsigned i=0; i<Engines.size(); i++) {
673       if (setting == 0)
674         ((FGTurbine*)Engines[i])->SetCutoff(false);
675       else
676         ((FGTurbine*)Engines[i])->SetCutoff(true);
677     }
678   } else {
679     if (setting == 0)
680       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
681     else
682       ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
683   }
684 }
685
686 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687
688 void FGPropulsion::SetActiveEngine(int engine)
689 {
690   if (engine >= (int)Engines.size() || engine < 0)
691     ActiveEngine = -1;
692   else
693     ActiveEngine = engine;
694 }
695
696 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697
698 double FGPropulsion::Transfer(int source, int target, double amount)
699 {
700  double shortage, overage;
701
702   if (source == -1) {
703      shortage = 0.0;
704   } else {
705      shortage = Tanks[source]->Drain(amount);
706   }
707   if (target == -1) {
708      overage = 0.0;
709   } else {
710      overage = Tanks[target]->Fill(amount - shortage);
711   }
712   return overage;
713 }
714
715 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716
717 void FGPropulsion::DoRefuel(double time_slice)
718 {
719   unsigned int i;
720
721   double fillrate = 100 * time_slice;   // 100 lbs/sec = 6000 lbs/min
722   int TanksNotFull = 0;
723
724   for (i=0; i<numTanks; i++) {
725     if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
726   }
727
728   if (TanksNotFull) {
729     for (i=0; i<numTanks; i++) {
730       if (Tanks[i]->GetPctFull() < 99.99)
731           Transfer(-1, i, fillrate/TanksNotFull);
732     }
733   }
734 }
735
736 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
737
738 void FGPropulsion::DumpFuel(double time_slice)
739 {
740   unsigned int i;
741   int TanksDumping = 0;
742
743   for (i=0; i<numTanks; i++) {
744     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
745   }
746
747   if (TanksDumping == 0) return;
748
749   double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
750
751   for (i=0; i<numTanks; i++) {
752     if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
753       Transfer(i, -1, dump_rate_per_tank);
754     }
755   }
756 }
757
758 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759
760 void FGPropulsion::SetFuelFreeze(bool f)
761 {
762   FuelFreeze = f;
763   for (unsigned int i=0; i<numEngines; i++) {
764     Engines[i]->SetFuelFreeze(f);
765   }
766 }
767
768 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769
770 void FGPropulsion::bind(void)
771 {
772   typedef double (FGPropulsion::*PMF)(int) const;
773   typedef int (FGPropulsion::*iPMF)(void) const;
774
775   IsBound = true;
776   PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
777   if (HaveTurbineEngine) {
778     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
779     PropertyManager->Tie("propulsion/cutoff_cmd", this,  (iPMF)0, &FGPropulsion::SetCutoff,   false);
780   }
781
782   if (HavePistonEngine) {
783     PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter,  false);
784     PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
785   }
786
787   PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
788                         &FGPropulsion::SetActiveEngine, true);
789   PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
790   PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
791                         &FGPropulsion::SetRefuel, true);
792   PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
793                         &FGPropulsion::SetFuelDump, true);
794   PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
795                        (PMF)&FGPropulsion::GetForces);
796   PropertyManager->Tie("forces/fby-prop-lbs", this,2,
797                        (PMF)&FGPropulsion::GetForces);
798   PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
799                        (PMF)&FGPropulsion::GetForces);
800   PropertyManager->Tie("moments/l-prop-lbsft", this,1,
801                        (PMF)&FGPropulsion::GetMoments);
802   PropertyManager->Tie("moments/m-prop-lbsft", this,2,
803                        (PMF)&FGPropulsion::GetMoments);
804   PropertyManager->Tie("moments/n-prop-lbsft", this,3,
805                        (PMF)&FGPropulsion::GetMoments);
806
807 }
808
809 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810 //    The bitmasked value choices are as follows:
811 //    unset: In this case (the default) JSBSim would only print
812 //       out the normally expected messages, essentially echoing
813 //       the config files as they are read. If the environment
814 //       variable is not set, debug_lvl is set to 1 internally
815 //    0: This requests JSBSim not to output any messages
816 //       whatsoever.
817 //    1: This value explicity requests the normal JSBSim
818 //       startup messages
819 //    2: This value asks for a message to be printed out when
820 //       a class is instantiated
821 //    4: When this value is set, a message is displayed when a
822 //       FGModel object executes its Run() method
823 //    8: When this value is set, various runtime state variables
824 //       are printed out periodically
825 //    16: When set various parameters are sanity checked and
826 //       a message is printed out when they go out of bounds
827
828 void FGPropulsion::Debug(int from)
829 {
830   if (debug_lvl <= 0) return;
831
832   if (debug_lvl & 1) { // Standard console startup message output
833     if (from == 2) { // Loader
834       cout << endl << "  Propulsion:" << endl;
835     }
836   }
837   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
838     if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
839     if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
840   }
841   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
842   }
843   if (debug_lvl & 8 ) { // Runtime state variables
844   }
845   if (debug_lvl & 16) { // Sanity checking
846   }
847   if (debug_lvl & 64) {
848     if (from == 0) { // Constructor
849       cout << IdSrc << endl;
850       cout << IdHdr << endl;
851     }
852   }
853 }
854 }