1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGPropulsion.cpp
6 Purpose: Encapsulates the set of engines and tanks associated
9 ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) -------------
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
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
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.
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.
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:
34 1) The appropriate engine type instance is created
35 2) At least one tank object is created, and is linked to an engine.
37 At Run time each engines Calculate() method is called.
40 --------------------------------------------------------------------------------
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
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"
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;
72 extern short debug_lvl;
75 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
79 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
81 Name = "FGPropulsion";
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 ...
89 refuel = dump = false;
92 TotalFuelQuantity = 0.0;
98 HaveElectricEngine = false;
99 HasInitializedEngines = false;
104 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 FGPropulsion::~FGPropulsion()
108 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
110 for (unsigned int i=0; i<Tanks.size(); i++) delete Tanks[i];
115 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 bool FGPropulsion::InitModel(void)
121 for (unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
123 for (unsigned int i=0; i<numEngines; i++) {
124 switch (Engines[i]->GetType()) {
125 case FGEngine::etPiston:
126 ((FGPiston*)Engines[i])->ResetToIC();
128 if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
129 } catch (string str) {
134 case FGEngine::etTurbine:
135 ((FGTurbine*)Engines[i])->ResetToIC();
137 if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
138 } catch (string str) {
151 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 bool FGPropulsion::Run(bool Holding)
157 if (FGModel::Run(Holding)) return true;
158 if (Holding) return false;
162 vForces.InitMatrix();
163 vMoments.InitMatrix();
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
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();
180 if (refuel) DoRefuel( in.TotalDeltaT );
181 if (dump) DumpFuel( in.TotalDeltaT );
188 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
198 void FGPropulsion::ConsumeFuel(FGEngine* engine)
200 if (FuelFreeze) return;
201 if (FDMExec->GetTrimStatus()) return;
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;
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.
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()) {
225 if ((Tank->GetContents() > 0.0) && Tank->GetSelected() && (TankPriority == CurrentFuelTankPriority)) {
228 FeedListFuel.push_back(TankId);
231 case FGTank::ttOXIDIZER:
232 // Skip this here (done below)
237 if (TanksWithFuel == 0) CurrentFuelTankPriority++; // No tanks at this priority, try next priority
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()) {
250 // Skip this here (done above)
252 case FGTank::ttOXIDIZER:
253 // hasOxTanks = true;
254 if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
256 if (TanksWithFuel > 0) Starved = false;
257 FeedListOxi.push_back(TankId);
263 if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
267 engine->SetStarved(Starved); // Tanks can be refilled, so be sure to reset engine Starved flag here.
269 // No fuel or fuel/oxidizer found at any priority!
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);
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);
288 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
290 bool FGPropulsion::GetSteadyState(void)
292 double currentThrust = 0, lastThrust = -1;
293 int steady_count = 0, j = 0;
295 bool TrimMode = FDMExec->GetTrimStatus();
297 vForces.InitMatrix();
298 vMoments.InitMatrix();
300 if (!FGModel::Run(false)) {
301 FDMExec->SetTrimStatus(true);
303 for (unsigned int i=0; i<numEngines; i++) {
307 while (!steady && j < 6000) {
308 Engines[i]->Calculate();
309 lastThrust = currentThrust;
310 currentThrust = Engines[i]->GetThrust();
311 if (fabs(lastThrust-currentThrust) < 0.0001) {
313 if (steady_count > 120) {
321 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
322 vMoments += Engines[i]->GetMoments(); // sum body frame moments
325 FDMExec->SetTrimStatus(TrimMode);
333 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 void FGPropulsion::InitRunning(int n)
337 if (n >= 0) { // A specific engine is supposed to be initialized
339 if (n >= (int)GetNumEngines() ) {
340 throw(string("Tried to initialize a non-existent engine!"));
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
346 GetEngine(n)->InitRunning();
349 InitializedEngines = 1 << n;
350 HasInitializedEngines = true;
352 } else if (n < 0) { // -1 refers to "All Engines"
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();
361 InitializedEngines = -1;
362 HasInitializedEngines = true;
366 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368 bool FGPropulsion::Load(Element* el)
370 string type, engine_filename;
371 bool ThrottleAdded = false;
375 FGModel::Load(el); // Perform base class Load.
377 // Process tank definitions first to establish the number of fuel tanks
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;}
386 tank_element = el->FindNextElement("tank");
388 numSelectedFuelTanks = numFuelTanks;
389 numSelectedOxiTanks = numOxiTanks;
391 Element* engine_element = el->FindElement("engine");
392 while (engine_element) {
393 engine_filename = engine_element->GetAttributeValue("file");
395 if (engine_filename.empty()) {
396 cerr << "Engine definition did not supply an engine file." << endl;
400 engine_filename = FindEngineFullPathname(engine_filename);
401 if (engine_filename.empty()) {
402 // error message already printed by FindEngineFullPathname()
406 document = LoadXMLDocument(engine_filename);
407 document->SetParent(engine_element);
409 type = document->GetName();
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));
432 cerr << "Unknown engine type: " << type << endl;
435 } catch (std::string str) {
436 cerr << endl << fgred << str << reset << endl;
442 engine_element = el->FindNextElement("engine");
446 CalculateTankInertias();
448 // Process fuel dump rate
449 if (el->FindElement("dump-rate"))
450 DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
452 PostLoad(el, PropertyManager);
457 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
461 string fullpath, localpath;
462 string enginePath = FDMExec->GetEnginePath();
463 string aircraftPath = FDMExec->GetFullAircraftPath();
464 ifstream engine_file;
466 string separator = "/";
468 fullpath = enginePath + separator;
469 localpath = aircraftPath + separator + "Engines" + separator;
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;
479 return string(localpath + engine_filename + ".xml");
482 return string(fullpath + engine_filename + ".xml");
485 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
489 string fullpath, localpath;
490 string enginePath = FDMExec->GetEnginePath();
491 string aircraftPath = FDMExec->GetFullAircraftPath();
492 ifstream* engine_file = new ifstream();
494 string separator = "/";
496 fullpath = enginePath + separator;
497 localpath = aircraftPath + separator + "Engines" + separator;
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;
510 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
516 string PropulsionStrings = "";
517 bool firstime = true;
520 for (i=0; i<Engines.size(); i++) {
521 if (firstime) firstime = false;
522 else PropulsionStrings += delimiter;
524 PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
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;
531 return PropulsionStrings;
534 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
536 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
540 string PropulsionValues = "";
541 bool firstime = true;
544 for (i=0; i<Engines.size(); i++) {
545 if (firstime) firstime = false;
546 else PropulsionValues += delimiter;
548 PropulsionValues += Engines[i]->GetEngineValues(delimiter);
550 for (i=0; i<Tanks.size(); i++) {
552 buf << Tanks[i]->GetContents();
555 return PropulsionValues;
558 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560 string FGPropulsion::GetPropulsionTankReport()
563 stringstream outstream;
564 for (unsigned int i=0; i<numTanks; i++)
566 FGTank* tank = Tanks[i];
568 if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
569 tankname = "Solid Fuel";
570 } else if (tank->GetType() == FGTank::ttFUEL) {
572 } else if (tank->GetType() == FGTank::ttOXIDIZER) {
573 tankname = "Oxidizer";
575 tankname = "(Unknown tank type)";
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;
583 return outstream.str();
586 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
588 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
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();
599 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601 double FGPropulsion::GetTanksWeight(void)
605 for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
610 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
617 if (size == 0) return tankJ;
619 tankJ = FGMatrix33();
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();
632 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634 void FGPropulsion::SetMagnetos(int setting)
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);
644 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
648 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650 void FGPropulsion::SetStarter(int setting)
652 if (ActiveEngine < 0) {
653 for (unsigned i=0; i<Engines.size(); i++) {
655 Engines[i]->SetStarter(false);
657 Engines[i]->SetStarter(true);
661 Engines[ActiveEngine]->SetStarter(false);
663 Engines[ActiveEngine]->SetStarter(true);
667 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 void FGPropulsion::SetCutoff(int setting)
671 if (ActiveEngine < 0) {
672 for (unsigned i=0; i<Engines.size(); i++) {
674 ((FGTurbine*)Engines[i])->SetCutoff(false);
676 ((FGTurbine*)Engines[i])->SetCutoff(true);
680 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
682 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
686 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
688 void FGPropulsion::SetActiveEngine(int engine)
690 if (engine >= (int)Engines.size() || engine < 0)
693 ActiveEngine = engine;
696 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698 double FGPropulsion::Transfer(int source, int target, double amount)
700 double shortage, overage;
705 shortage = Tanks[source]->Drain(amount);
710 overage = Tanks[target]->Fill(amount - shortage);
715 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
717 void FGPropulsion::DoRefuel(double time_slice)
721 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
722 int TanksNotFull = 0;
724 for (i=0; i<numTanks; i++) {
725 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
729 for (i=0; i<numTanks; i++) {
730 if (Tanks[i]->GetPctFull() < 99.99)
731 Transfer(-1, i, fillrate/TanksNotFull);
736 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
738 void FGPropulsion::DumpFuel(double time_slice)
741 int TanksDumping = 0;
743 for (i=0; i<numTanks; i++) {
744 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
747 if (TanksDumping == 0) return;
749 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
751 for (i=0; i<numTanks; i++) {
752 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
753 Transfer(i, -1, dump_rate_per_tank);
758 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
760 void FGPropulsion::SetFuelFreeze(bool f)
763 for (unsigned int i=0; i<numEngines; i++) {
764 Engines[i]->SetFuelFreeze(f);
768 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
770 void FGPropulsion::bind(void)
772 typedef double (FGPropulsion::*PMF)(int) const;
773 typedef int (FGPropulsion::*iPMF)(void) const;
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);
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);
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);
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
817 // 1: This value explicity requests the normal JSBSim
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
828 void FGPropulsion::Debug(int from)
830 if (debug_lvl <= 0) return;
832 if (debug_lvl & 1) { // Standard console startup message output
833 if (from == 2) { // Loader
834 cout << endl << " Propulsion:" << endl;
837 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
838 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
839 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
841 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
843 if (debug_lvl & 8 ) { // Runtime state variables
845 if (debug_lvl & 16) { // Sanity checking
847 if (debug_lvl & 64) {
848 if (from == 0) { // Constructor
849 cout << IdSrc << endl;
850 cout << IdHdr << endl;