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/FGModelLoader.h" 62 #include "math/FGColumnVector3.h" 68 IDENT(IdSrc,
"$Id: FGPropulsion.cpp,v 1.88 2017/02/25 14:23:19 bcoconni Exp $");
69 IDENT(IdHdr,ID_PROPULSION);
71 extern short debug_lvl;
80 Name =
"FGPropulsion";
82 numSelectedFuelTanks = numSelectedOxiTanks = 0;
83 numTanks = numEngines = 0;
84 numOxiTanks = numFuelTanks = 0;
95 HaveElectricEngine =
false;
104 for (
unsigned int i=0; i<Engines.size(); i++)
delete Engines[i];
106 for (
unsigned int i=0; i<Tanks.size(); i++)
delete Tanks[i];
113 bool FGPropulsion::InitModel(
void)
117 if (!FGModel::InitModel())
return false;
119 vForces.InitMatrix();
120 vMoments.InitMatrix();
122 for (
unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
123 TotalFuelQuantity = 0.0;
124 TotalOxidizerQuantity = 0.0;
125 refuel = dump =
false;
127 for (
unsigned int i=0; i<numEngines; i++)
128 Engines[i]->ResetToIC();
140 if (Holding)
return false;
144 vForces.InitMatrix();
145 vMoments.InitMatrix();
147 for (i=0; i<numEngines; i++) {
148 Engines[i]->Calculate();
149 ConsumeFuel(Engines[i]);
150 vForces += Engines[i]->GetBodyForces();
151 vMoments += Engines[i]->GetMoments();
154 TotalFuelQuantity = 0.0;
155 TotalOxidizerQuantity = 0.0;
156 for (i=0; i<numTanks; i++) {
157 Tanks[i]->Calculate( in.TotalDeltaT, in.TAT_c);
158 switch (Tanks[i]->GetType()) {
160 TotalFuelQuantity += Tanks[i]->GetContents();
162 case FGTank::ttOXIDIZER:
163 TotalOxidizerQuantity += Tanks[i]->GetContents();
170 if (refuel.node() && refuel) DoRefuel( in.TotalDeltaT );
171 if (dump.node() && dump) DumpFuel( in.TotalDeltaT );
188 void FGPropulsion::ConsumeFuel(
FGEngine* engine)
190 if (FuelFreeze)
return;
191 if (FDMExec->GetTrimStatus())
return;
193 unsigned int TanksWithFuel=0, CurrentFuelTankPriority=1;
194 unsigned int TanksWithOxidizer=0, CurrentOxidizerTankPriority=1;
195 vector <int> FeedListFuel, FeedListOxi;
197 bool hasOxTanks =
false;
207 while ((TanksWithFuel == 0) && (CurrentFuelTankPriority <= numTanks)) {
208 for (
unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
209 unsigned int TankId = engine->GetSourceTank(i);
210 FGTank* Tank = Tanks[TankId];
211 unsigned int TankPriority = Tank->GetPriority();
212 if (TankPriority != 0) {
218 FeedListFuel.push_back(TankId);
221 case FGTank::ttOXIDIZER:
227 if (TanksWithFuel == 0) CurrentFuelTankPriority++;
230 bool FuelStarved = Starved;
234 if (engine->GetType() == FGEngine::etRocket) {
235 while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
236 for (
unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
237 unsigned int TankId = engine->GetSourceTank(i);
238 FGTank* Tank = Tanks[TankId];
239 unsigned int TankPriority = Tank->GetPriority();
240 if (TankPriority != 0) {
245 case FGTank::ttOXIDIZER:
249 if (TanksWithFuel > 0) Starved =
false;
250 FeedListOxi.push_back(TankId);
256 if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++;
260 bool OxiStarved = Starved;
262 engine->SetStarved(FuelStarved || (hasOxTanks && OxiStarved));
266 if (FuelStarved || (hasOxTanks && OxiStarved))
return;
269 double FuelNeededPerTank = FuelToBurn / TanksWithFuel;
270 for (
unsigned int i=0; i<FeedListFuel.size(); i++) {
271 Tanks[FeedListFuel[i]]->Drain(FuelNeededPerTank);
274 if (engine->GetType() == FGEngine::etRocket) {
275 double OxidizerToBurn = engine->CalcOxidizerNeed();
276 double OxidizerNeededPerTank = 0;
277 if (TanksWithOxidizer > 0) OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer;
278 for (
unsigned int i=0; i<FeedListOxi.size(); i++) {
279 Tanks[FeedListOxi[i]]->Drain(OxidizerNeededPerTank);
289 double currentThrust = 0, lastThrust = -1;
290 int steady_count = 0, j = 0;
292 bool TrimMode = FDMExec->GetTrimStatus();
295 vForces.InitMatrix();
296 vMoments.InitMatrix();
299 FDMExec->SetTrimStatus(
true);
302 in.TotalDeltaT = 0.5;
304 for (
unsigned int i=0; i<numEngines; i++) {
308 while (!steady && j < 6000) {
309 Engines[i]->Calculate();
310 lastThrust = currentThrust;
311 currentThrust = Engines[i]->GetThrust();
312 if (fabs(lastThrust-currentThrust) < 0.0001) {
314 if (steady_count > 120) {
322 vForces += Engines[i]->GetBodyForces();
323 vMoments += Engines[i]->GetMoments();
326 FDMExec->SetTrimStatus(TrimMode);
327 in.TotalDeltaT = TimeStep;
342 throw(
string(
"Tried to initialize a non-existent engine!"));
345 in.ThrottleCmd[n] = in.ThrottlePos[n] = 1;
346 in.MixtureCmd[n] = in.MixturePos[n] = 1;
354 in.ThrottleCmd[i] = in.ThrottlePos[i] = 1;
355 in.MixtureCmd[i] = in.MixturePos[i] = 1;
370 ReadingEngine =
false;
371 double FuelDensity = 6.0;
382 while (tank_element) {
383 Tanks.push_back(
new FGTank(FDMExec, tank_element, numTanks));
384 if (Tanks.back()->GetType() == FGTank::ttFUEL) {
385 FuelDensity = Tanks[numFuelTanks]->GetDensity();
388 else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
389 else {cerr <<
"Unknown tank type specified." << endl;
return false;}
393 numSelectedFuelTanks = numFuelTanks;
394 numSelectedOxiTanks = numOxiTanks;
396 ReadingEngine =
true;
398 while (engine_element) {
399 if (!ModelLoader.Open(engine_element))
return false;
404 if (!thruster_element || !ModelLoader.Open(thruster_element))
405 throw(
"No thruster definition supplied with engine definition.");
407 if (engine_element->
FindElement(
"piston_engine")) {
408 HavePistonEngine =
true;
409 if (!IsBound) bind();
411 Engines.push_back(
new FGPiston(FDMExec, element, numEngines, in));
412 }
else if (engine_element->
FindElement(
"turbine_engine")) {
413 HaveTurbineEngine =
true;
414 if (!IsBound) bind();
416 Engines.push_back(
new FGTurbine(FDMExec, element, numEngines, in));
417 }
else if (engine_element->
FindElement(
"turboprop_engine")) {
418 HaveTurboPropEngine =
true;
419 if (!IsBound) bind();
421 Engines.push_back(
new FGTurboProp(FDMExec, element, numEngines, in));
422 }
else if (engine_element->
FindElement(
"rocket_engine")) {
423 HaveRocketEngine =
true;
424 if (!IsBound) bind();
426 Engines.push_back(
new FGRocket(FDMExec, element, numEngines, in));
427 }
else if (engine_element->
FindElement(
"electric_engine")) {
428 HaveElectricEngine =
true;
429 if (!IsBound) bind();
431 Engines.push_back(
new FGElectric(FDMExec, element, numEngines, in));
433 cerr << engine_element->
ReadFrom() <<
" Unknown engine type" << endl;
436 }
catch (std::string& str) {
437 cerr << endl <<
fgred << str <<
reset << endl;
446 CalculateTankInertias();
454 for (i=0; i<Engines.size(); i++) {
455 Engines[i]->SetFuelDensity(FuelDensity);
459 PostLoad(el, PropertyManager);
466 SGPath FGPropulsion::FindFullPathName(
const SGPath& path)
const 468 if (!ReadingEngine)
return FGModel::FindFullPathName(path);
472 if (!name.isNull())
return name;
479 string FGPropulsion::GetPropulsionStrings(
const string& delimiter)
const 483 string PropulsionStrings =
"";
484 bool firstime =
true;
487 for (i=0; i<Engines.size(); i++) {
488 if (firstime) firstime =
false;
489 else PropulsionStrings += delimiter;
491 PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
493 for (i=0; i<Tanks.size(); i++) {
494 if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter <<
"Fuel Tank " << i;
495 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter <<
"Oxidizer Tank " << i;
498 PropulsionStrings += buf.str();
501 return PropulsionStrings;
506 string FGPropulsion::GetPropulsionValues(
const string& delimiter)
const 510 string PropulsionValues =
"";
511 bool firstime =
true;
514 for (i=0; i<Engines.size(); i++) {
515 if (firstime) firstime =
false;
516 else PropulsionValues += delimiter;
518 PropulsionValues += Engines[i]->GetEngineValues(delimiter);
520 for (i=0; i<Tanks.size(); i++) {
522 buf << Tanks[i]->GetContents();
525 PropulsionValues += buf.str();
528 return PropulsionValues;
533 string FGPropulsion::GetPropulsionTankReport()
536 stringstream outstream;
538 CalculateTankInertias();
540 for (
unsigned int i=0; i<numTanks; i++)
544 if (tank->
GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
545 tankname =
"Solid Fuel";
546 }
else if (tank->
GetType() == FGTank::ttFUEL) {
548 }
else if (tank->
GetType() == FGTank::ttOXIDIZER) {
549 tankname =
"Oxidizer";
551 tankname =
"(Unknown tank type)";
553 outstream <<
highint << left << setw(4) << i << setw(30) << tankname <<
normint 554 << right << setw(10) << tank->
GetContents() << setw(8) << tank->GetXYZ(eX)
555 << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
556 << setw(12) << tank->GetIxx() << setw(12) << tank->GetIyy()
557 << setw(12) << tank->GetIzz() << endl;
559 return outstream.str();
566 vXYZtank_arm.InitMatrix();
567 for (
unsigned int i=0; i<Tanks.size(); i++) {
568 vXYZtank_arm += Tanks[i]->GetXYZ() * Tanks[i]->GetContents();
575 double FGPropulsion::GetTanksWeight(
void)
const 579 for (
unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
586 const FGMatrix33& FGPropulsion::CalculateTankInertias(
void)
588 size_t size = Tanks.size();
590 if (size == 0)
return tankJ;
594 for (
unsigned int i=0; i<size; i++) {
597 tankJ(1,1) += Tanks[i]->GetIxx();
598 tankJ(2,2) += Tanks[i]->GetIyy();
599 tankJ(3,3) += Tanks[i]->GetIzz();
607 void FGPropulsion::SetMagnetos(
int setting)
609 if (ActiveEngine < 0) {
610 for (
unsigned i=0; i<Engines.size(); i++) {
614 if (Engines[i]->GetType() == FGEngine::etPiston)
615 ((
FGPiston*)Engines[i])->SetMagnetos(setting);
618 ((
FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
624 void FGPropulsion::SetStarter(
int setting)
626 if (ActiveEngine < 0) {
627 for (
unsigned i=0; i<Engines.size(); i++) {
629 Engines[i]->SetStarter(
false);
631 Engines[i]->SetStarter(
true);
635 Engines[ActiveEngine]->SetStarter(
false);
637 Engines[ActiveEngine]->SetStarter(
true);
643 void FGPropulsion::SetCutoff(
int setting)
645 bool bsetting = setting == 0 ? false :
true;
647 if (ActiveEngine < 0) {
648 for (
unsigned i=0; i<Engines.size(); i++) {
649 switch (Engines[i]->GetType()) {
650 case FGEngine::etTurbine:
651 ((
FGTurbine*)Engines[i])->SetCutoff(bsetting);
653 case FGEngine::etTurboprop:
661 switch (Engines[ActiveEngine]->GetType()) {
662 case FGEngine::etTurbine:
663 ((
FGTurbine*)Engines[ActiveEngine])->SetCutoff(bsetting);
665 case FGEngine::etTurboprop:
666 ((
FGTurboProp*)Engines[ActiveEngine])->SetCutoff(bsetting);
676 void FGPropulsion::SetActiveEngine(
int engine)
678 if (engine >= (
int)Engines.size() || engine < 0)
681 ActiveEngine = engine;
686 double FGPropulsion::Transfer(
int source,
int target,
double amount)
688 double shortage, overage;
693 shortage = Tanks[source]->Drain(amount);
698 overage = Tanks[target]->Fill(amount - shortage);
705 void FGPropulsion::DoRefuel(
double time_slice)
709 double fillrate = RefuelRate / 60.0 * time_slice;
710 int TanksNotFull = 0;
712 for (i=0; i<numTanks; i++) {
713 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
718 for (i=0; i<numTanks; i++) {
719 if (Tanks[i]->GetPctFull() < 99.99)
720 Transfer(-1, i, fillrate/TanksNotFull);
727 void FGPropulsion::DumpFuel(
double time_slice)
730 int TanksDumping = 0;
732 for (i=0; i<numTanks; i++) {
733 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
736 if (TanksDumping == 0)
return;
738 double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
740 for (i=0; i<numTanks; i++) {
741 if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
742 Transfer(i, -1, dump_rate_per_tank);
749 void FGPropulsion::SetFuelFreeze(
bool f)
752 for (
unsigned int i=0; i<numEngines; i++) {
753 Engines[i]->SetFuelFreeze(f);
759 void FGPropulsion::bind(
void)
766 if (HaveTurbineEngine || HaveTurboPropEngine) {
767 PropertyManager->
Tie(
"propulsion/starter_cmd",
this, (iPMF)0, &FGPropulsion::SetStarter,
false);
768 PropertyManager->
Tie(
"propulsion/cutoff_cmd",
this, (iPMF)0, &FGPropulsion::SetCutoff,
false);
771 if (HavePistonEngine) {
772 PropertyManager->
Tie(
"propulsion/starter_cmd",
this, (iPMF)0, &FGPropulsion::SetStarter,
false);
773 PropertyManager->
Tie(
"propulsion/magneto_cmd",
this, (iPMF)0, &FGPropulsion::SetMagnetos,
false);
776 PropertyManager->
Tie(
"propulsion/active_engine",
this, (iPMF)&FGPropulsion::GetActiveEngine,
777 &FGPropulsion::SetActiveEngine,
true);
778 PropertyManager->
Tie(
"forces/fbx-prop-lbs",
this, eX, (PMF)&FGPropulsion::GetForces);
779 PropertyManager->
Tie(
"forces/fby-prop-lbs",
this, eY, (PMF)&FGPropulsion::GetForces);
780 PropertyManager->
Tie(
"forces/fbz-prop-lbs",
this, eZ, (PMF)&FGPropulsion::GetForces);
781 PropertyManager->
Tie(
"moments/l-prop-lbsft",
this, eX, (PMF)&FGPropulsion::GetMoments);
782 PropertyManager->
Tie(
"moments/m-prop-lbsft",
this, eY, (PMF)&FGPropulsion::GetMoments);
783 PropertyManager->
Tie(
"moments/n-prop-lbsft",
this, eZ, (PMF)&FGPropulsion::GetMoments);
784 TotalFuelQuantity = PropertyManager->CreatePropertyObject<
double>(
"propulsion/total-fuel-lbs");
785 TotalOxidizerQuantity = PropertyManager->CreatePropertyObject<
double>(
"propulsion/total-oxidizer-lbs");
786 refuel = PropertyManager->CreatePropertyObject<
bool>(
"propulsion/refuel");
787 dump = PropertyManager->CreatePropertyObject<
bool>(
"propulsion/fuel_dump");
809 void FGPropulsion::Debug(
int from)
811 if (debug_lvl <= 0)
return;
815 cout << endl <<
" Propulsion:" << endl;
818 if (debug_lvl & 2 ) {
819 if (from == 0) cout <<
"Instantiated: FGPropulsion" << endl;
820 if (from == 1) cout <<
"Destroyed: FGPropulsion" << endl;
822 if (debug_lvl & 4 ) {
824 if (debug_lvl & 8 ) {
826 if (debug_lvl & 16) {
828 if (debug_lvl & 64) {
830 cout << IdSrc << endl;
831 cout << IdHdr << endl;
bool GetSelected(void) const
If the tank is set to supply fuel, this function returns true.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
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. ...
const SGPath & GetFullAircraftPath(void)
Retrieves the full aircraft path name.
static char reset[5]
resets text properties
This class models a turbine engine.
FGEngine * GetEngine(unsigned int index) const
Retrieves an engine object pointer from the list of engines.
Element * FindElement(const std::string &el="")
Searches for a specified element.
Models a generic rocket engine.
static char normint[6]
normal intensity text
virtual double CalcFuelNeed(void)
The fuel need is calculated based on power levels and flow rate for that power level.
~FGPropulsion()
Destructor.
bool Load(Element *el)
Loads the propulsion system (engine[s] and tank[s]).
static char fgred[6]
red text
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
void Tie(const std::string &name, bool *pointer, bool useDefault=true)
Tie a property to an external bool variable.
Propulsion management class.
Base class for all scheduled JSBSim models.
double GetContents(void) const
Gets the contents of the tank.
int GetType(void) const
Retrieves the type of tank: Fuel or Oxidizer.
FGMatrix33 GetPointmassInertia(double mass_sl, const FGColumnVector3 &r) const
Computes the inertia contribution of a pointmass.
This class implements a 3 element column vector.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
bool GetSteadyState(void)
Loops the engines until thrust output steady (used for trimming)
Base class for all engines.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
static char highint[5]
highlights text
void InitRunning(int n)
Sets up the engines as running.
Models a Supercharged Piston engine.
Handles matrix math operations.
void InitMatrix(void)
Initialize the matrix.
double GetDeltaT(void) const
Returns the simulation delta T.
Models an electric motor.
Encapsulates the JSBSim simulation executive.
virtual bool Load(Element *el)
Loads this model.
const SGPath & GetEnginePath(void)
Retrieves the engine path.
bool Run(bool Holding)
Executes the propulsion model.
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.
unsigned int GetNumEngines(void) const
Retrieves the number of engines defined for the aircraft.