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.51 2011/09/11 11:36:04 bcoconni 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;
374 FGModel::Load(el); // Perform base class Load.
376 // Process tank definitions first to establish the number of fuel tanks
378 Element* tank_element = el->FindElement("tank");
379 while (tank_element) {
380 Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
381 if (Tanks.back()->GetType() == FGTank::ttFUEL) numFuelTanks++;
382 else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
383 else {cerr << "Unknown tank type specified." << endl; return false;}
385 tank_element = el->FindNextElement("tank");
387 numSelectedFuelTanks = numFuelTanks;
388 numSelectedOxiTanks = numOxiTanks;
390 Element* engine_element = el->FindElement("engine");
391 while (engine_element) {
392 engine_filename = engine_element->GetAttributeValue("file");
394 if (engine_filename.empty()) {
395 cerr << "Engine definition did not supply an engine file." << endl;
399 engine_filename = FindEngineFullPathname(engine_filename);
400 if (engine_filename.empty()) {
401 // error message already printed by FindEngineFullPathname()
405 document = LoadXMLDocument(engine_filename);
406 document->SetParent(engine_element);
408 type = document->GetName();
410 if (type == "piston_engine") {
411 HavePistonEngine = true;
412 if (!IsBound) bind();
413 Engines.push_back(new FGPiston(FDMExec, document, numEngines, in));
414 } else if (type == "turbine_engine") {
415 HaveTurbineEngine = true;
416 if (!IsBound) bind();
417 Engines.push_back(new FGTurbine(FDMExec, document, numEngines, in));
418 } else if (type == "turboprop_engine") {
419 HaveTurboPropEngine = true;
420 if (!IsBound) bind();
421 Engines.push_back(new FGTurboProp(FDMExec, document, numEngines, in));
422 } else if (type == "rocket_engine") {
423 HaveRocketEngine = true;
424 if (!IsBound) bind();
425 Engines.push_back(new FGRocket(FDMExec, document, numEngines, in));
426 } else if (type == "electric_engine") {
427 HaveElectricEngine = true;
428 if (!IsBound) bind();
429 Engines.push_back(new FGElectric(FDMExec, document, numEngines, in));
431 cerr << "Unknown engine type: " << type << endl;
434 } catch (std::string str) {
435 cerr << endl << fgred << str << reset << endl;
441 engine_element = el->FindNextElement("engine");
445 CalculateTankInertias();
447 // Process fuel dump rate
448 if (el->FindElement("dump-rate"))
449 DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
451 PostLoad(el, PropertyManager);
456 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
460 string fullpath, localpath;
461 string enginePath = FDMExec->GetEnginePath();
462 string aircraftPath = FDMExec->GetFullAircraftPath();
463 ifstream engine_file;
465 string separator = "/";
467 fullpath = enginePath + separator;
468 localpath = aircraftPath + separator + "Engines" + separator;
470 engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
471 if ( !engine_file.is_open()) {
472 engine_file.open(string(localpath + engine_filename + ".xml").c_str());
473 if ( !engine_file.is_open()) {
474 cerr << " Could not open engine file: " << engine_filename << " in path "
475 << fullpath << " or " << localpath << endl;
478 return string(localpath + engine_filename + ".xml");
481 return string(fullpath + engine_filename + ".xml");
484 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
488 string fullpath, localpath;
489 string enginePath = FDMExec->GetEnginePath();
490 string aircraftPath = FDMExec->GetFullAircraftPath();
491 ifstream* engine_file = new ifstream();
493 string separator = "/";
495 fullpath = enginePath + separator;
496 localpath = aircraftPath + separator + "Engines" + separator;
498 engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
499 if ( !engine_file->is_open()) {
500 engine_file->open(string(localpath + engine_filename + ".xml").c_str());
501 if ( !engine_file->is_open()) {
502 cerr << " Could not open engine file: " << engine_filename << " in path "
503 << fullpath << " or " << localpath << endl;
509 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
515 string PropulsionStrings = "";
516 bool firstime = true;
519 for (i=0; i<Engines.size(); i++) {
520 if (firstime) firstime = false;
521 else PropulsionStrings += delimiter;
523 PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
525 for (i=0; i<Tanks.size(); i++) {
526 if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
527 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
530 return PropulsionStrings;
533 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
539 string PropulsionValues = "";
540 bool firstime = true;
543 for (i=0; i<Engines.size(); i++) {
544 if (firstime) firstime = false;
545 else PropulsionValues += delimiter;
547 PropulsionValues += Engines[i]->GetEngineValues(delimiter);
549 for (i=0; i<Tanks.size(); i++) {
551 buf << Tanks[i]->GetContents();
554 return PropulsionValues;
557 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559 string FGPropulsion::GetPropulsionTankReport()
562 stringstream outstream;
563 for (unsigned int i=0; i<numTanks; i++)
565 FGTank* tank = Tanks[i];
567 if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
568 tankname = "Solid Fuel";
569 } else if (tank->GetType() == FGTank::ttFUEL) {
571 } else if (tank->GetType() == FGTank::ttOXIDIZER) {
572 tankname = "Oxidizer";
574 tankname = "(Unknown tank type)";
576 outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
577 << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
578 << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
579 << setw(12) << "*" << setw(12) << "*"
580 << setw(12) << "*" << endl;
582 return outstream.str();
585 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
587 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
589 vXYZtank_arm.InitMatrix();
590 for (unsigned int i=0; i<Tanks.size(); i++) {
591 vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
592 vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
593 vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
598 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
600 double FGPropulsion::GetTanksWeight(void)
604 for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
609 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
616 if (size == 0) return tankJ;
618 tankJ = FGMatrix33();
620 for (unsigned int i=0; i<size; i++) {
621 tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
622 Tanks[i]->GetXYZ() );
623 tankJ(1,1) += Tanks[i]->GetIxx();
624 tankJ(2,2) += Tanks[i]->GetIyy();
625 tankJ(3,3) += Tanks[i]->GetIzz();
631 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 void FGPropulsion::SetMagnetos(int setting)
635 if (ActiveEngine < 0) {
636 for (unsigned i=0; i<Engines.size(); i++) {
637 // ToDo: first need to make sure the engine Type is really appropriate:
638 // do a check to see if it is of type Piston. This should be done for
639 // all of this kind of possibly across-the-board settings.
640 ((FGPiston*)Engines[i])->SetMagnetos(setting);
643 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
647 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649 void FGPropulsion::SetStarter(int setting)
651 if (ActiveEngine < 0) {
652 for (unsigned i=0; i<Engines.size(); i++) {
654 Engines[i]->SetStarter(false);
656 Engines[i]->SetStarter(true);
660 Engines[ActiveEngine]->SetStarter(false);
662 Engines[ActiveEngine]->SetStarter(true);
666 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 void FGPropulsion::SetCutoff(int setting)
670 if (ActiveEngine < 0) {
671 for (unsigned i=0; i<Engines.size(); i++) {
673 ((FGTurbine*)Engines[i])->SetCutoff(false);
675 ((FGTurbine*)Engines[i])->SetCutoff(true);
679 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
681 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
685 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687 void FGPropulsion::SetActiveEngine(int engine)
689 if (engine >= (int)Engines.size() || engine < 0)
692 ActiveEngine = engine;
695 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697 double FGPropulsion::Transfer(int source, int target, double amount)
699 double shortage, overage;
704 shortage = Tanks[source]->Drain(amount);
709 overage = Tanks[target]->Fill(amount - shortage);
714 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716 void FGPropulsion::DoRefuel(double time_slice)
720 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
721 int TanksNotFull = 0;
723 for (i=0; i<numTanks; i++) {
724 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
728 for (i=0; i<numTanks; i++) {
729 if (Tanks[i]->GetPctFull() < 99.99)
730 Transfer(-1, i, fillrate/TanksNotFull);
735 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
737 void FGPropulsion::DumpFuel(double time_slice)
740 int TanksDumping = 0;
742 for (i=0; i<numTanks; i++) {
743 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
746 if (TanksDumping == 0) return;
748 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
750 for (i=0; i<numTanks; i++) {
751 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
752 Transfer(i, -1, dump_rate_per_tank);
757 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759 void FGPropulsion::SetFuelFreeze(bool f)
762 for (unsigned int i=0; i<numEngines; i++) {
763 Engines[i]->SetFuelFreeze(f);
767 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769 void FGPropulsion::bind(void)
771 typedef double (FGPropulsion::*PMF)(int) const;
772 typedef int (FGPropulsion::*iPMF)(void) const;
775 PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
776 if (HaveTurbineEngine) {
777 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
778 PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, false);
781 if (HavePistonEngine) {
782 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
783 PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
786 PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
787 &FGPropulsion::SetActiveEngine, true);
788 PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
789 PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
790 &FGPropulsion::SetRefuel, true);
791 PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
792 &FGPropulsion::SetFuelDump, true);
793 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
794 (PMF)&FGPropulsion::GetForces);
795 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
796 (PMF)&FGPropulsion::GetForces);
797 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
798 (PMF)&FGPropulsion::GetForces);
799 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
800 (PMF)&FGPropulsion::GetMoments);
801 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
802 (PMF)&FGPropulsion::GetMoments);
803 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
804 (PMF)&FGPropulsion::GetMoments);
808 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809 // The bitmasked value choices are as follows:
810 // unset: In this case (the default) JSBSim would only print
811 // out the normally expected messages, essentially echoing
812 // the config files as they are read. If the environment
813 // variable is not set, debug_lvl is set to 1 internally
814 // 0: This requests JSBSim not to output any messages
816 // 1: This value explicity requests the normal JSBSim
818 // 2: This value asks for a message to be printed out when
819 // a class is instantiated
820 // 4: When this value is set, a message is displayed when a
821 // FGModel object executes its Run() method
822 // 8: When this value is set, various runtime state variables
823 // are printed out periodically
824 // 16: When set various parameters are sanity checked and
825 // a message is printed out when they go out of bounds
827 void FGPropulsion::Debug(int from)
829 if (debug_lvl <= 0) return;
831 if (debug_lvl & 1) { // Standard console startup message output
832 if (from == 2) { // Loader
833 cout << endl << " Propulsion:" << endl;
836 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
837 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
838 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
840 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
842 if (debug_lvl & 8 ) { // Runtime state variables
844 if (debug_lvl & 16) { // Sanity checking
846 if (debug_lvl & 64) {
847 if (from == 0) { // Constructor
848 cout << IdSrc << endl;
849 cout << IdHdr << endl;