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.61 2012/04/14 18:10:44 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 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)
623 const FGMatrix33 Ts2b(-inchtoft, 0., 0., 0., inchtoft, 0., 0., 0., -inchtoft);
627 if (size == 0) return tankJ;
629 tankJ = FGMatrix33();
631 for (unsigned int i=0; i<size; i++) {
632 FGColumnVector3 vTankBodyVec = Ts2b * (in.vXYZcg - Tanks[i]->GetXYZ());
634 tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
636 tankJ(1,1) += Tanks[i]->GetIxx();
637 tankJ(2,2) += Tanks[i]->GetIyy();
638 tankJ(3,3) += Tanks[i]->GetIzz();
644 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646 void FGPropulsion::SetMagnetos(int setting)
648 if (ActiveEngine < 0) {
649 for (unsigned i=0; i<Engines.size(); i++) {
650 // ToDo: first need to make sure the engine Type is really appropriate:
651 // do a check to see if it is of type Piston. This should be done for
652 // all of this kind of possibly across-the-board settings.
653 ((FGPiston*)Engines[i])->SetMagnetos(setting);
656 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
660 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 void FGPropulsion::SetStarter(int setting)
664 if (ActiveEngine < 0) {
665 for (unsigned i=0; i<Engines.size(); i++) {
667 Engines[i]->SetStarter(false);
669 Engines[i]->SetStarter(true);
673 Engines[ActiveEngine]->SetStarter(false);
675 Engines[ActiveEngine]->SetStarter(true);
679 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
681 void FGPropulsion::SetCutoff(int setting)
683 if (ActiveEngine < 0) {
684 for (unsigned i=0; i<Engines.size(); i++) {
686 ((FGTurbine*)Engines[i])->SetCutoff(false);
688 ((FGTurbine*)Engines[i])->SetCutoff(true);
692 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
694 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
698 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700 void FGPropulsion::SetActiveEngine(int engine)
702 if (engine >= (int)Engines.size() || engine < 0)
705 ActiveEngine = engine;
708 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 double FGPropulsion::Transfer(int source, int target, double amount)
712 double shortage, overage;
717 shortage = Tanks[source]->Drain(amount);
722 overage = Tanks[target]->Fill(amount - shortage);
727 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729 void FGPropulsion::DoRefuel(double time_slice)
733 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
734 int TanksNotFull = 0;
736 for (i=0; i<numTanks; i++) {
737 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
741 for (i=0; i<numTanks; i++) {
742 if (Tanks[i]->GetPctFull() < 99.99)
743 Transfer(-1, i, fillrate/TanksNotFull);
748 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750 void FGPropulsion::DumpFuel(double time_slice)
753 int TanksDumping = 0;
755 for (i=0; i<numTanks; i++) {
756 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
759 if (TanksDumping == 0) return;
761 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
763 for (i=0; i<numTanks; i++) {
764 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
765 Transfer(i, -1, dump_rate_per_tank);
770 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772 void FGPropulsion::SetFuelFreeze(bool f)
775 for (unsigned int i=0; i<numEngines; i++) {
776 Engines[i]->SetFuelFreeze(f);
780 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
782 void FGPropulsion::bind(void)
784 typedef double (FGPropulsion::*PMF)(int) const;
785 typedef int (FGPropulsion::*iPMF)(void) const;
788 PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
789 if (HaveTurbineEngine) {
790 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
791 PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, false);
794 if (HavePistonEngine) {
795 PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
796 PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
799 PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
800 &FGPropulsion::SetActiveEngine, true);
801 PropertyManager->Tie("propulsion/total-fuel-lbs", this, &FGPropulsion::GetTotalFuelQuantity);
802 PropertyManager->Tie("propulsion/refuel", this, &FGPropulsion::GetRefuel,
803 &FGPropulsion::SetRefuel, true);
804 PropertyManager->Tie("propulsion/fuel_dump", this, &FGPropulsion::GetFuelDump,
805 &FGPropulsion::SetFuelDump, true);
806 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
807 (PMF)&FGPropulsion::GetForces);
808 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
809 (PMF)&FGPropulsion::GetForces);
810 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
811 (PMF)&FGPropulsion::GetForces);
812 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
813 (PMF)&FGPropulsion::GetMoments);
814 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
815 (PMF)&FGPropulsion::GetMoments);
816 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
817 (PMF)&FGPropulsion::GetMoments);
821 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 // The bitmasked value choices are as follows:
823 // unset: In this case (the default) JSBSim would only print
824 // out the normally expected messages, essentially echoing
825 // the config files as they are read. If the environment
826 // variable is not set, debug_lvl is set to 1 internally
827 // 0: This requests JSBSim not to output any messages
829 // 1: This value explicity requests the normal JSBSim
831 // 2: This value asks for a message to be printed out when
832 // a class is instantiated
833 // 4: When this value is set, a message is displayed when a
834 // FGModel object executes its Run() method
835 // 8: When this value is set, various runtime state variables
836 // are printed out periodically
837 // 16: When set various parameters are sanity checked and
838 // a message is printed out when they go out of bounds
840 void FGPropulsion::Debug(int from)
842 if (debug_lvl <= 0) return;
844 if (debug_lvl & 1) { // Standard console startup message output
845 if (from == 2) { // Loader
846 cout << endl << " Propulsion:" << endl;
849 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
850 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
851 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
853 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
855 if (debug_lvl & 8 ) { // Runtime state variables
857 if (debug_lvl & 16) { // Sanity checking
859 if (debug_lvl & 64) {
860 if (from == 0) { // Constructor
861 cout << IdSrc << endl;
862 cout << IdHdr << endl;