49 #include "FGFDMExec.h"
50 #include "FGPropulsion.h"
51 #include "models/FGMassBalance.h"
52 #include "models/propulsion/FGRocket.h"
53 #include "models/propulsion/FGTurbine.h"
54 #include "models/propulsion/FGPiston.h"
55 #include "models/propulsion/FGElectric.h"
56 #include "models/propulsion/FGTurboProp.h"
57 #include "models/propulsion/FGTank.h"
58 #include "input_output/FGModelLoader.h"
64 extern short debug_lvl;
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];
105 bool FGPropulsion::InitModel(
void)
109 if (!FGModel::InitModel())
return false;
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();
154 case FGTank::ttOXIDIZER:
155 TotalOxidizerQuantity += Tanks[i]->GetContents();
162 if (refuel.node() && refuel) DoRefuel( in.TotalDeltaT );
163 if (dump.node() && dump) DumpFuel( in.TotalDeltaT );
180 void 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)) {
200 for (
unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
201 unsigned int TankId = engine->GetSourceTank(i);
202 FGTank* Tank = Tanks[TankId];
203 unsigned int TankPriority = Tank->GetPriority();
204 if (TankPriority != 0) {
210 FeedListFuel.push_back(TankId);
213 case FGTank::ttOXIDIZER:
219 if (TanksWithFuel == 0) CurrentFuelTankPriority++;
222 bool FuelStarved = Starved;
226 if (engine->GetType() == FGEngine::etRocket) {
227 while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
228 for (
unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
229 unsigned int TankId = engine->GetSourceTank(i);
230 FGTank* Tank = Tanks[TankId];
231 unsigned int TankPriority = Tank->GetPriority();
232 if (TankPriority != 0) {
233 switch(Tank->GetType()) {
237 case FGTank::ttOXIDIZER:
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);
266 if (engine->GetType() == FGEngine::etRocket) {
267 double OxidizerToBurn = engine->CalcOxidizerNeed();
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;
284 bool TrimMode = FDMExec->GetTrimStatus();
287 vForces.InitMatrix();
288 vMoments.InitMatrix();
291 FDMExec->SetTrimStatus(
true);
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();
318 FDMExec->SetTrimStatus(TrimMode);
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));
376 if (Tanks.back()->GetType() == FGTank::ttFUEL) {
377 FuelDensity = Tanks[numFuelTanks]->GetDensity();
380 else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
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();
403 Engines.push_back(
new FGPiston(FDMExec, element, numEngines, in));
404 }
else if (engine_element->
FindElement(
"turbine_engine")) {
405 HaveTurbineEngine =
true;
406 if (!IsBound) bind();
408 Engines.push_back(
new FGTurbine(FDMExec, element, numEngines, in));
409 }
else if (engine_element->
FindElement(
"turboprop_engine")) {
410 HaveTurboPropEngine =
true;
411 if (!IsBound) bind();
413 Engines.push_back(
new FGTurboProp(FDMExec, element, numEngines, in));
414 }
else if (engine_element->
FindElement(
"rocket_engine")) {
415 HaveRocketEngine =
true;
416 if (!IsBound) bind();
418 Engines.push_back(
new FGRocket(FDMExec, element, numEngines, in));
419 }
else if (engine_element->
FindElement(
"electric_engine")) {
420 HaveElectricEngine =
true;
421 if (!IsBound) bind();
423 Engines.push_back(
new FGElectric(FDMExec, element, numEngines, in));
425 cerr << engine_element->
ReadFrom() <<
" Unknown engine type" << endl;
428 }
catch (std::string& str) {
429 cerr << endl <<
fgred << str <<
reset << endl;
438 CalculateTankInertias();
446 for (i=0; i<Engines.size(); i++) {
447 Engines[i]->SetFuelDensity(FuelDensity);
451 PostLoad(el, FDMExec);
458 SGPath FGPropulsion::FindFullPathName(
const SGPath& path)
const
460 if (!ReadingEngine)
return FGModel::FindFullPathName(path);
464 if (!name.isNull())
return name;
471 string FGPropulsion::GetPropulsionStrings(
const string& delimiter)
const
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;
487 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter <<
"Oxidizer Tank " << i;
490 PropulsionStrings += buf.str();
493 return PropulsionStrings;
498 string FGPropulsion::GetPropulsionValues(
const string& delimiter)
const
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;
525 string FGPropulsion::GetPropulsionTankReport()
528 stringstream outstream;
530 CalculateTankInertias();
532 for (
unsigned int i=0; i<numTanks; i++)
534 FGTank* tank = Tanks[i];
536 if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
537 tankname =
"Solid Fuel";
538 }
else if (tank->GetType() == FGTank::ttFUEL) {
540 }
else if (tank->GetType() == FGTank::ttOXIDIZER) {
541 tankname =
"Oxidizer";
543 tankname =
"(Unknown tank type)";
545 outstream <<
highint << left << setw(4) << i << setw(30) << tankname <<
normint
546 << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
547 << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
548 << setw(12) << tank->GetIxx() << setw(12) << tank->GetIyy()
549 << setw(12) << tank->GetIzz() << endl;
551 return outstream.str();
556 const FGColumnVector3& FGPropulsion::GetTanksMoment(
void)
558 vXYZtank_arm.InitMatrix();
559 for (
unsigned int i=0; i<Tanks.size(); i++) {
560 vXYZtank_arm += Tanks[i]->GetXYZ() * Tanks[i]->GetContents();
567 double FGPropulsion::GetTanksWeight(
void)
const
571 for (
unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
578 const FGMatrix33& FGPropulsion::CalculateTankInertias(
void)
580 size_t size = Tanks.size();
582 if (size == 0)
return tankJ;
586 for (
unsigned int i=0; i<size; i++) {
589 tankJ(1,1) += Tanks[i]->GetIxx();
590 tankJ(2,2) += Tanks[i]->GetIyy();
591 tankJ(3,3) += Tanks[i]->GetIzz();
599 void FGPropulsion::SetMagnetos(
int setting)
601 if (ActiveEngine < 0) {
602 for (
unsigned i=0; i<Engines.size(); i++) {
606 if (Engines[i]->GetType() == FGEngine::etPiston)
607 ((FGPiston*)Engines[i])->SetMagnetos(setting);
610 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
616 void FGPropulsion::SetStarter(
int 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);
635 int FGPropulsion::GetStarter(
void)
const
637 if (ActiveEngine < 0) {
640 for (
unsigned i=0; i<Engines.size(); i++)
641 starter &= Engines[i]->GetStarter();
643 return starter ? 1 : 0;
645 return Engines[ActiveEngine]->GetStarter() ? 1: 0;
650 void FGPropulsion::SetCutoff(
int setting)
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()) {
657 case FGEngine::etTurbine:
658 ((FGTurbine*)Engines[i])->SetCutoff(bsetting);
660 case FGEngine::etTurboprop:
661 ((FGTurboProp*)Engines[i])->SetCutoff(bsetting);
668 switch (Engines[ActiveEngine]->GetType()) {
669 case FGEngine::etTurbine:
670 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(bsetting);
672 case FGEngine::etTurboprop:
673 ((FGTurboProp*)Engines[ActiveEngine])->SetCutoff(bsetting);
683 int FGPropulsion::GetCutoff(
void)
const
685 if (ActiveEngine < 0) {
688 for (
unsigned i=0; i<Engines.size(); i++) {
689 switch (Engines[i]->GetType()) {
690 case FGEngine::etTurbine:
691 cutoff &= ((FGTurbine*)Engines[i])->GetCutoff();
693 case FGEngine::etTurboprop:
694 cutoff &= ((FGTurboProp*)Engines[i])->GetCutoff();
701 return cutoff ? 1 : 0;
703 switch (Engines[ActiveEngine]->GetType()) {
704 case FGEngine::etTurbine:
705 return ((FGTurbine*)Engines[ActiveEngine])->GetCutoff() ? 1 : 0;
706 case FGEngine::etTurboprop:
707 return ((FGTurboProp*)Engines[ActiveEngine])->GetCutoff() ? 1 : 0;
718 void FGPropulsion::SetActiveEngine(
int engine)
720 if (engine >= (
int)Engines.size() || engine < 0)
723 ActiveEngine = engine;
728 double FGPropulsion::Transfer(
int source,
int target,
double amount)
730 double shortage, overage;
735 shortage = Tanks[source]->Drain(amount);
740 overage = Tanks[target]->Fill(amount - shortage);
747 void FGPropulsion::DoRefuel(
double time_slice)
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)
762 Transfer(-1, i, fillrate/TanksNotFull);
769 void FGPropulsion::DumpFuel(
double time_slice)
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()) {
784 Transfer(i, -1, dump_rate_per_tank);
791 void FGPropulsion::SetFuelFreeze(
bool f)
794 for (
unsigned int i=0; i<numEngines; i++) {
795 Engines[i]->SetFuelFreeze(f);
801 void FGPropulsion::bind(
void)
809 if (HaveTurbineEngine || HaveTurboPropEngine) {
810 PropertyManager->
Tie(
"propulsion/starter_cmd",
this, &FGPropulsion::GetStarter, &FGPropulsion::SetStarter);
811 PropertyManager->
Tie(
"propulsion/cutoff_cmd",
this, &FGPropulsion::GetCutoff, &FGPropulsion::SetCutoff);
814 if (HavePistonEngine) {
815 PropertyManager->
Tie(
"propulsion/starter_cmd",
this, &FGPropulsion::GetStarter, &FGPropulsion::SetStarter);
816 PropertyManager->
Tie(
"propulsion/magneto_cmd",
this, (iPMF)0, &FGPropulsion::SetMagnetos);
819 PropertyManager->
Tie(
"propulsion/active_engine",
this, (iPMF)&FGPropulsion::GetActiveEngine,
820 &FGPropulsion::SetActiveEngine);
821 PropertyManager->
Tie(
"forces/fbx-prop-lbs",
this, eX, (PMF)&FGPropulsion::GetForces);
822 PropertyManager->
Tie(
"forces/fby-prop-lbs",
this, eY, (PMF)&FGPropulsion::GetForces);
823 PropertyManager->
Tie(
"forces/fbz-prop-lbs",
this, eZ, (PMF)&FGPropulsion::GetForces);
824 PropertyManager->
Tie(
"moments/l-prop-lbsft",
this, eX, (PMF)&FGPropulsion::GetMoments);
825 PropertyManager->
Tie(
"moments/m-prop-lbsft",
this, eY, (PMF)&FGPropulsion::GetMoments);
826 PropertyManager->
Tie(
"moments/n-prop-lbsft",
this, eZ, (PMF)&FGPropulsion::GetMoments);
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");
831 PropertyManager->
Tie(
"propulsion/fuel_freeze",
this, (bPMF)
nullptr, &FGPropulsion::SetFuelFreeze);
853 void FGPropulsion::Debug(
int from)
855 if (debug_lvl <= 0)
return;
859 cout << endl <<
" Propulsion:" << endl;
862 if (debug_lvl & 2 ) {
863 if (from == 0) cout <<
"Instantiated: FGPropulsion" << endl;
864 if (from == 1) cout <<
"Destroyed: FGPropulsion" << endl;
866 if (debug_lvl & 4 ) {
868 if (debug_lvl & 8 ) {
870 if (debug_lvl & 16) {
872 if (debug_lvl & 64) {