72 Name =
"FGPropulsion";
74 numSelectedFuelTanks = numSelectedOxiTanks = 0;
75 numTanks = numEngines = 0;
76 numOxiTanks = numFuelTanks = 0;
87 HaveElectricEngine =
false;
96 for (
unsigned int i=0;
i<Engines.size();
i++)
delete Engines[
i];
98 for (
unsigned int i=0;
i<Tanks.size();
i++)
delete Tanks[
i];
111 vForces.InitMatrix();
112 vMoments.InitMatrix();
114 for (
unsigned int i=0;
i<numTanks;
i++) Tanks[
i]->ResetToIC();
115 TotalFuelQuantity = 0.0;
116 TotalOxidizerQuantity = 0.0;
117 refuel = dump =
false;
119 for (
unsigned int i=0;
i<numEngines;
i++)
120 Engines[
i]->ResetToIC();
132 if (Holding)
return false;
136 vForces.InitMatrix();
137 vMoments.InitMatrix();
139 for (
i=0;
i<numEngines;
i++) {
140 Engines[
i]->Calculate();
141 ConsumeFuel(Engines[
i]);
142 vForces += Engines[
i]->GetBodyForces();
143 vMoments += Engines[
i]->GetMoments();
146 TotalFuelQuantity = 0.0;
147 TotalOxidizerQuantity = 0.0;
148 for (
i=0;
i<numTanks;
i++) {
149 Tanks[
i]->Calculate(
in.TotalDeltaT,
in.TAT_c);
150 switch (Tanks[
i]->GetType()) {
152 TotalFuelQuantity += Tanks[
i]->GetContents();
155 TotalOxidizerQuantity += Tanks[
i]->GetContents();
162 if (refuel.node() && refuel)
DoRefuel(
in.TotalDeltaT );
163 if (dump.node() && dump)
DumpFuel(
in.TotalDeltaT );
180void FGPropulsion::ConsumeFuel(
FGEngine* engine)
182 if (FuelFreeze)
return;
183 if (
FDMExec->GetTrimStatus())
return;
185 unsigned int TanksWithFuel=0, CurrentFuelTankPriority=1;
186 unsigned int TanksWithOxidizer=0, CurrentOxidizerTankPriority=1;
187 vector <int> FeedListFuel, FeedListOxi;
189 bool hasOxTanks =
false;
199 while ((TanksWithFuel == 0) && (CurrentFuelTankPriority <= numTanks)) {
202 FGTank* Tank = Tanks[TankId];
204 if (TankPriority != 0) {
210 FeedListFuel.push_back(TankId);
219 if (TanksWithFuel == 0) CurrentFuelTankPriority++;
222 bool FuelStarved = Starved;
227 while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
230 FGTank* Tank = Tanks[TankId];
232 if (TankPriority != 0) {
233 switch(Tank->GetType()) {
239 if (Tank->GetContents() > Tank->GetUnusable() && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
241 if (TanksWithFuel > 0) Starved =
false;
242 FeedListOxi.push_back(TankId);
248 if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++;
252 bool OxiStarved = Starved;
254 engine->
SetStarved(FuelStarved || (hasOxTanks && OxiStarved));
258 if (FuelStarved || (hasOxTanks && OxiStarved))
return;
261 double FuelNeededPerTank = FuelToBurn / TanksWithFuel;
262 for (
unsigned int i=0;
i<FeedListFuel.size();
i++) {
263 Tanks[FeedListFuel[
i]]->Drain(FuelNeededPerTank);
268 double OxidizerNeededPerTank = 0;
269 if (TanksWithOxidizer > 0) OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer;
270 for (
unsigned int i=0;
i<FeedListOxi.size();
i++) {
271 Tanks[FeedListOxi[
i]]->Drain(OxidizerNeededPerTank);
281 double currentThrust = 0, lastThrust = -1;
282 int steady_count = 0, j = 0;
285 double TimeStep =
FDMExec->GetDeltaT();
287 vForces.InitMatrix();
288 vMoments.InitMatrix();
294 in.TotalDeltaT = 0.5;
296 for (
unsigned int i=0;
i<numEngines;
i++) {
300 while (!steady && j < 6000) {
301 Engines[
i]->Calculate();
302 lastThrust = currentThrust;
303 currentThrust = Engines[
i]->GetThrust();
304 if (fabs(lastThrust-currentThrust) < 0.0001) {
306 if (steady_count > 120) {
314 vForces += Engines[
i]->GetBodyForces();
315 vMoments += Engines[
i]->GetMoments();
319 in.TotalDeltaT = TimeStep;
334 throw(
string(
"Tried to initialize a non-existent engine!"));
337 in.ThrottleCmd[n] =
in.ThrottlePos[n] = 1;
338 in.MixtureCmd[n] =
in.MixturePos[n] = 1;
346 in.ThrottleCmd[
i] =
in.ThrottlePos[
i] = 1;
347 in.MixtureCmd[
i] =
in.MixturePos[
i] = 1;
362 ReadingEngine =
false;
363 double FuelDensity = 6.0;
374 while (tank_element) {
375 Tanks.push_back(
new FGTank(
FDMExec, tank_element, numTanks));
377 FuelDensity = Tanks[numFuelTanks]->GetDensity();
381 else {cerr <<
"Unknown tank type specified." << endl;
return false;}
385 numSelectedFuelTanks = numFuelTanks;
386 numSelectedOxiTanks = numOxiTanks;
388 ReadingEngine =
true;
390 while (engine_element) {
391 if (!ModelLoader.
Open(engine_element))
return false;
396 if (!thruster_element || !ModelLoader.
Open(thruster_element))
397 throw(
"No thruster definition supplied with engine definition.");
399 if (engine_element->
FindElement(
"piston_engine")) {
400 HavePistonEngine =
true;
401 if (!IsBound) bind();
404 }
else if (engine_element->
FindElement(
"turbine_engine")) {
405 HaveTurbineEngine =
true;
406 if (!IsBound) bind();
409 }
else if (engine_element->
FindElement(
"turboprop_engine")) {
410 HaveTurboPropEngine =
true;
411 if (!IsBound) bind();
414 }
else if (engine_element->
FindElement(
"rocket_engine")) {
415 HaveRocketEngine =
true;
416 if (!IsBound) bind();
419 }
else if (engine_element->
FindElement(
"electric_engine")) {
420 HaveElectricEngine =
true;
421 if (!IsBound) bind();
425 cerr << engine_element->
ReadFrom() <<
" Unknown engine type" << endl;
428 }
catch (std::string& str) {
429 cerr << endl <<
fgred << str <<
reset << endl;
446 for (
i=0;
i<Engines.size();
i++) {
447 Engines[
i]->SetFuelDensity(FuelDensity);
475 string PropulsionStrings =
"";
476 bool firstime =
true;
479 for (
i=0;
i<Engines.size();
i++) {
480 if (firstime) firstime =
false;
481 else PropulsionStrings += delimiter;
483 PropulsionStrings += Engines[
i]->GetEngineLabels(delimiter);
485 for (
i=0;
i<Tanks.size();
i++) {
486 if (Tanks[
i]->GetType() ==
FGTank::ttFUEL) buf << delimiter <<
"Fuel Tank " <<
i;
490 PropulsionStrings += buf.str();
493 return PropulsionStrings;
502 string PropulsionValues =
"";
503 bool firstime =
true;
506 for (
i=0;
i<Engines.size();
i++) {
507 if (firstime) firstime =
false;
508 else PropulsionValues += delimiter;
510 PropulsionValues += Engines[
i]->GetEngineValues(delimiter);
512 for (
i=0;
i<Tanks.size();
i++) {
514 buf << Tanks[
i]->GetContents();
517 PropulsionValues += buf.str();
520 return PropulsionValues;
528 stringstream outstream;
532 for (
unsigned int i=0;
i<numTanks;
i++)
537 tankname =
"Solid Fuel";
541 tankname =
"Oxidizer";
543 tankname =
"(Unknown tank type)";
545 outstream <<
highint << left << setw(4) <<
i << setw(30) << tankname <<
normint
548 << setw(12) << tank->
GetIxx() << setw(12) << tank->
GetIyy()
549 << setw(12) << tank->
GetIzz() << endl;
551 return outstream.str();
559 for (
unsigned int i=0;
i<Tanks.size();
i++) {
560 vXYZtank_arm += Tanks[
i]->GetXYZ() * Tanks[
i]->GetContents();
571 for (
unsigned int i=0;
i<Tanks.size();
i++) Tw += Tanks[
i]->GetContents();
580 size_t size = Tanks.size();
582 if (size == 0)
return tankJ;
586 for (
unsigned int i=0;
i<size;
i++) {
587 tankJ +=
FDMExec->GetMassBalance()->GetPointmassInertia(
lbtoslug * Tanks[
i]->GetContents(),
589 tankJ(1,1) += Tanks[
i]->GetIxx();
590 tankJ(2,2) += Tanks[
i]->GetIyy();
591 tankJ(3,3) += Tanks[
i]->GetIzz();
601 if (ActiveEngine < 0) {
602 for (
unsigned i=0;
i<Engines.size();
i++) {
607 ((
FGPiston*)Engines[
i])->SetMagnetos(setting);
610 ((
FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
618 if (ActiveEngine < 0) {
619 for (
unsigned i=0;
i<Engines.size();
i++) {
621 Engines[
i]->SetStarter(
false);
623 Engines[
i]->SetStarter(
true);
627 Engines[ActiveEngine]->SetStarter(
false);
629 Engines[ActiveEngine]->SetStarter(
true);
637 if (ActiveEngine < 0) {
640 for (
unsigned i=0;
i<Engines.size();
i++)
643 return starter ? 1 : 0;
645 return Engines[ActiveEngine]->GetStarter() ? 1: 0;
652 bool bsetting = setting == 0 ? false :
true;
654 if (ActiveEngine < 0) {
655 for (
unsigned i=0;
i<Engines.size();
i++) {
656 switch (Engines[
i]->GetType()) {
658 ((
FGTurbine*)Engines[
i])->SetCutoff(bsetting);
668 switch (Engines[ActiveEngine]->GetType()) {
670 ((
FGTurbine*)Engines[ActiveEngine])->SetCutoff(bsetting);
673 ((
FGTurboProp*)Engines[ActiveEngine])->SetCutoff(bsetting);
685 if (ActiveEngine < 0) {
688 for (
unsigned i=0;
i<Engines.size();
i++) {
689 switch (Engines[
i]->GetType()) {
691 cutoff &= ((
FGTurbine*)Engines[
i])->GetCutoff();
701 return cutoff ? 1 : 0;
703 switch (Engines[ActiveEngine]->GetType()) {
705 return ((
FGTurbine*)Engines[ActiveEngine])->GetCutoff() ? 1 : 0;
707 return ((
FGTurboProp*)Engines[ActiveEngine])->GetCutoff() ? 1 : 0;
720 if (engine >= (
int)Engines.size() || engine < 0)
723 ActiveEngine = engine;
730 double shortage, overage;
735 shortage = Tanks[source]->Drain(amount);
740 overage = Tanks[target]->Fill(amount - shortage);
751 double fillrate = RefuelRate / 60.0 * time_slice;
752 int TanksNotFull = 0;
754 for (
i=0;
i<numTanks;
i++) {
755 if (Tanks[
i]->GetPctFull() < 99.99) ++TanksNotFull;
760 for (
i=0;
i<numTanks;
i++) {
761 if (Tanks[
i]->GetPctFull() < 99.99)
772 int TanksDumping = 0;
774 for (
i=0;
i<numTanks;
i++) {
775 if (Tanks[
i]->GetContents() > Tanks[
i]->GetStandpipe()) ++TanksDumping;
778 if (TanksDumping == 0)
return;
780 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
782 for (
i=0;
i<numTanks;
i++) {
783 if (Tanks[
i]->GetContents() > Tanks[
i]->GetStandpipe()) {
794 for (
unsigned int i=0;
i<numEngines;
i++) {
795 Engines[
i]->SetFuelFreeze(f);
801void FGPropulsion::bind(
void)
809 if (HaveTurbineEngine || HaveTurboPropEngine) {
814 if (HavePistonEngine) {
827 TotalFuelQuantity =
PropertyManager->CreatePropertyObject<
double>(
"propulsion/total-fuel-lbs");
828 TotalOxidizerQuantity =
PropertyManager->CreatePropertyObject<
double>(
"propulsion/total-oxidizer-lbs");
829 refuel =
PropertyManager->CreatePropertyObject<
bool>(
"propulsion/refuel");
830 dump =
PropertyManager->CreatePropertyObject<
bool>(
"propulsion/fuel_dump");
853void FGPropulsion::Debug(
int from)
859 cout << endl <<
" Propulsion:" << endl;
863 if (from == 0) cout <<
"Instantiated: FGPropulsion" << endl;
864 if (from == 1) cout <<
"Destroyed: FGPropulsion" << endl;
JSBSim::FGFDMExec * FDMExec
double FindElementValueAsNumberConvertTo(const std::string &el, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Element * FindElement(const std::string &el="")
Searches for a specified element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
This class implements a 3 element column vector.
Models an electric motor.
Base class for all engines.
EngineType GetType(void) const
unsigned int GetSourceTank(unsigned int i) const
virtual double CalcFuelNeed(void)
The fuel need is calculated based on power levels and flow rate for that power level.
size_t GetNumSourceTanks() const
virtual int InitRunning(void)
virtual double CalcOxidizerNeed(void)
virtual void SetStarved(bool tt)
static char highint[5]
highlights text
static char reset[5]
resets text properties
static char normint[6]
normal intensity text
static char fgred[6]
red text
static constexpr double lbtoslug
Handles matrix math operations.
void InitMatrix(void)
Initialize the matrix.
void RunPostFunctions(void)
void PostLoad(Element *el, FGFDMExec *fdmex, std::string prefix="")
void RunPreFunctions(void)
Element_ptr Open(Element *el)
FGPropertyManager * PropertyManager
bool InitModel(void) override
FGModel(FGFDMExec *)
Constructor.
virtual SGPath FindFullPathName(const SGPath &path) const
bool Upload(Element *el, bool preLoad)
Uploads this model in memory.
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
Models a Supercharged Piston engine.
void Tie(const std::string &name, T *pointer)
Tie a property to an external variable.
Propulsion management class.
struct FGEngine::Inputs in
const FGColumnVector3 & GetForces(void) const
~FGPropulsion() override
Destructor.
void SetFuelFreeze(bool f)
std::string GetPropulsionStrings(const std::string &delimiter) const
int GetCutoff(void) const
double GetTanksWeight(void) const
double Transfer(int source, int target, double amount)
void SetCutoff(int setting=0)
void DumpFuel(double time_slice)
unsigned int GetNumEngines(void) const
Retrieves the number of engines defined for the aircraft.
const FGColumnVector3 & GetMoments(void) const
void SetActiveEngine(int engine)
int GetActiveEngine(void) const
SGPath FindFullPathName(const SGPath &path) const override
bool Load(Element *el) override
Loads the propulsion system (engine[s] and tank[s]).
void SetStarter(int setting)
void DoRefuel(double time_slice)
const FGColumnVector3 & GetTanksMoment(void)
void InitRunning(int n)
Sets up the engines as running.
const FGMatrix33 & CalculateTankInertias(void)
bool Run(bool Holding) override
Executes the propulsion model.
bool InitModel(void) override
std::string GetPropulsionValues(const std::string &delimiter) const
void SetMagnetos(int setting)
std::string GetPropulsionTankReport()
FGPropulsion(FGFDMExec *)
Constructor.
bool GetSteadyState(void)
Loops the engines until thrust output steady (used for trimming)
FGEngine * GetEngine(unsigned int index) const
Retrieves an engine object pointer from the list of engines.
int GetStarter(void) const
Models a generic rocket engine.
bool GetSelected(void) const
If the tank is set to supply fuel, this function returns true.
double GetContents(void) const
Gets the contents of the tank.
double GetIyy(void) const
double GetIxx(void) const
int GetType(void) const
Retrieves the type of tank: Fuel or Oxidizer.
double GetIzz(void) const
FGColumnVector3 GetXYZ(void) const
int GetPriority(void) const
double GetUnusable(void) const
Returns the amount of unusable fuel in the tank.
GrainType GetGrainType(void) const
This class models a turbine engine.
SGPath CheckPathName(const SGPath &path, const SGPath &filename)