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.62 2012/07/19 03:50:19 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 bool FuelStarved = Starved;
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()) {
253 // Skip this here (done above)
255 case FGTank::ttOXIDIZER:
257 if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
259 if (TanksWithFuel > 0) Starved = false;
260 FeedListOxi.push_back(TankId);
266 if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
270 bool OxiStarved = Starved;
272 engine->SetStarved(FuelStarved || (hasOxTanks && OxiStarved)); // Tanks can be refilled, so be sure to reset engine Starved flag here.
274 // No fuel or fuel/oxidizer found at any priority!
275 // if (Starved) return;
276 if (FuelStarved || (hasOxTanks && OxiStarved)) return;
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);
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);
294 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
296 bool FGPropulsion::GetSteadyState(void)
298 double currentThrust = 0, lastThrust = -1;
299 int steady_count = 0, j = 0;
301 bool TrimMode = FDMExec->GetTrimStatus();
303 vForces.InitMatrix();
304 vMoments.InitMatrix();
306 if (!FGModel::Run(false)) {
307 FDMExec->SetTrimStatus(true);
309 for (unsigned int i=0; i<numEngines; i++) {
313 while (!steady && j < 6000) {
314 Engines[i]->Calculate();
315 lastThrust = currentThrust;
316 currentThrust = Engines[i]->GetThrust();
317 if (fabs(lastThrust-currentThrust) < 0.0001) {
319 if (steady_count > 120) {
327 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
328 vMoments += Engines[i]->GetMoments(); // sum body frame moments
331 FDMExec->SetTrimStatus(TrimMode);
339 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341 void FGPropulsion::InitRunning(int n)
343 if (n >= 0) { // A specific engine is supposed to be initialized
345 if (n >= (int)GetNumEngines() ) {
346 throw(string("Tried to initialize a non-existent engine!"));
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
352 GetEngine(n)->InitRunning();
355 InitializedEngines = 1 << n;
356 HasInitializedEngines = true;
358 } else if (n < 0) { // -1 refers to "All Engines"
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();
367 InitializedEngines = -1;
368 HasInitializedEngines = true;
372 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374 bool FGPropulsion::Load(Element* el)
376 string type, engine_filename;
380 FGModel::Load(el); // Perform base class Load.
382 // Process tank definitions first to establish the number of fuel tanks
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;}
391 tank_element = el->FindNextElement("tank");
393 numSelectedFuelTanks = numFuelTanks;
394 numSelectedOxiTanks = numOxiTanks;
396 Element* engine_element = el->FindElement("engine");
397 while (engine_element) {
398 engine_filename = engine_element->GetAttributeValue("file");
400 if (engine_filename.empty()) {
401 cerr << "Engine definition did not supply an engine file." << endl;
405 engine_filename = FindEngineFullPathname(engine_filename);
406 if (engine_filename.empty()) {
407 // error message already printed by FindEngineFullPathname()
411 document = LoadXMLDocument(engine_filename);
412 document->SetParent(engine_element);
414 type = document->GetName();
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));
437 cerr << "Unknown engine type: " << type << endl;
440 } catch (std::string str) {
441 cerr << endl << fgred << str << reset << endl;
447 engine_element = el->FindNextElement("engine");
451 CalculateTankInertias();
453 // Process fuel dump rate
454 if (el->FindElement("dump-rate"))
455 DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
457 PostLoad(el, PropertyManager);
462 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464 string FGPropulsion::FindEngineFullPathname(const string& engine_filename)
466 string fullpath, localpath;
467 string enginePath = FDMExec->GetEnginePath();
468 string aircraftPath = FDMExec->GetFullAircraftPath();
469 ifstream engine_file;
471 string separator = "/";
473 fullpath = enginePath + separator;
474 localpath = aircraftPath + separator + "Engines" + separator;
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;
484 return string(fullpath + engine_filename + ".xml");
487 return string(localpath + engine_filename + ".xml");
490 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492 ifstream* FGPropulsion::FindEngineFile(const string& engine_filename)
494 string fullpath, localpath;
495 string enginePath = FDMExec->GetEnginePath();
496 string aircraftPath = FDMExec->GetFullAircraftPath();
497 ifstream* engine_file = new ifstream();
499 string separator = "/";
501 fullpath = enginePath + separator;
502 localpath = aircraftPath + separator + "Engines" + separator;
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;
515 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
521 string PropulsionStrings = "";
522 bool firstime = true;
525 for (i=0; i<Engines.size(); i++) {
526 if (firstime) firstime = false;
527 else PropulsionStrings += delimiter;
529 PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
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;
536 PropulsionStrings += buf.str();
539 return PropulsionStrings;
542 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
544 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
548 string PropulsionValues = "";
549 bool firstime = true;
552 for (i=0; i<Engines.size(); i++) {
553 if (firstime) firstime = false;
554 else PropulsionValues += delimiter;
556 PropulsionValues += Engines[i]->GetEngineValues(delimiter);
558 for (i=0; i<Tanks.size(); i++) {
560 buf << Tanks[i]->GetContents();
563 PropulsionValues += buf.str();
566 return PropulsionValues;
569 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571 string FGPropulsion::GetPropulsionTankReport()
574 stringstream outstream;
575 for (unsigned int i=0; i<numTanks; i++)
577 FGTank* tank = Tanks[i];
579 if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
580 tankname = "Solid Fuel";
581 } else if (tank->GetType() == FGTank::ttFUEL) {
583 } else if (tank->GetType() == FGTank::ttOXIDIZER) {
584 tankname = "Oxidizer";
586 tankname = "(Unknown tank type)";
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;
594 return outstream.str();
597 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
599 const FGColumnVector3& FGPropulsion::GetTanksMoment(void)
601 vXYZtank_arm.InitMatrix();
602 for (unsigned int i=0; i<Tanks.size(); i++) {
603 vXYZtank_arm += Tanks[i]->GetXYZ() * Tanks[i]->GetContents();
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610 double FGPropulsion::GetTanksWeight(void) const
614 for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
619 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
621 const FGMatrix33& FGPropulsion::CalculateTankInertias(void)
626 if (size == 0) return tankJ;
628 tankJ = FGMatrix33();
630 for (unsigned int i=0; i<size; i++) {
631 FGColumnVector3 vTankStructVec = in.vXYZcg - Tanks[i]->GetXYZ();
633 tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
635 tankJ(1,1) += Tanks[i]->GetIxx();
636 tankJ(2,2) += Tanks[i]->GetIyy();
637 tankJ(3,3) += Tanks[i]->GetIzz();
643 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645 void FGPropulsion::SetMagnetos(int setting)
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);
655 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
659 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 void FGPropulsion::SetStarter(int setting)
663 if (ActiveEngine < 0) {
664 for (unsigned i=0; i<Engines.size(); i++) {
666 Engines[i]->SetStarter(false);
668 Engines[i]->SetStarter(true);
672 Engines[ActiveEngine]->SetStarter(false);
674 Engines[ActiveEngine]->SetStarter(true);
678 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
680 void FGPropulsion::SetCutoff(int setting)
682 if (ActiveEngine < 0) {
683 for (unsigned i=0; i<Engines.size(); i++) {
685 ((FGTurbine*)Engines[i])->SetCutoff(false);
687 ((FGTurbine*)Engines[i])->SetCutoff(true);
691 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
693 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
697 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699 void FGPropulsion::SetActiveEngine(int engine)
701 if (engine >= (int)Engines.size() || engine < 0)
704 ActiveEngine = engine;
707 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
709 double FGPropulsion::Transfer(int source, int target, double amount)
711 double shortage, overage;
716 shortage = Tanks[source]->Drain(amount);
721 overage = Tanks[target]->Fill(amount - shortage);
726 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 void FGPropulsion::DoRefuel(double time_slice)
732 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
733 int TanksNotFull = 0;
735 for (i=0; i<numTanks; i++) {
736 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
740 for (i=0; i<numTanks; i++) {
741 if (Tanks[i]->GetPctFull() < 99.99)
742 Transfer(-1, i, fillrate/TanksNotFull);
747 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
749 void FGPropulsion::DumpFuel(double time_slice)
752 int TanksDumping = 0;
754 for (i=0; i<numTanks; i++) {
755 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
758 if (TanksDumping == 0) return;
760 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
762 for (i=0; i<numTanks; i++) {
763 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
764 Transfer(i, -1, dump_rate_per_tank);
769 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771 void FGPropulsion::SetFuelFreeze(bool f)
774 for (unsigned int i=0; i<numEngines; i++) {
775 Engines[i]->SetFuelFreeze(f);
779 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781 void FGPropulsion::bind(void)
783 typedef double (FGPropulsion::*PMF)(int) const;
784 typedef int (FGPropulsion::*iPMF)(void) const;
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);
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);
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);
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
828 // 1: This value explicity requests the normal JSBSim
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
839 void FGPropulsion::Debug(int from)
841 if (debug_lvl <= 0) return;
843 if (debug_lvl & 1) { // Standard console startup message output
844 if (from == 2) { // Loader
845 cout << endl << " Propulsion:" << endl;
848 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
849 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
850 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
852 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
854 if (debug_lvl & 8 ) { // Runtime state variables
856 if (debug_lvl & 16) { // Sanity checking
858 if (debug_lvl & 64) {
859 if (from == 0) { // Constructor
860 cout << IdSrc << endl;
861 cout << IdHdr << endl;