INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <iomanip>
+
+#include "FGFDMExec.h"
#include "FGPropulsion.h"
-#include "models/FGFCS.h"
#include "models/FGMassBalance.h"
-#include "models/propulsion/FGThruster.h"
#include "models/propulsion/FGRocket.h"
#include "models/propulsion/FGTurbine.h"
#include "models/propulsion/FGPiston.h"
#include "input_output/FGPropertyManager.h"
#include "input_output/FGXMLParse.h"
#include "math/FGColumnVector3.h"
-#include <iostream>
-#include <sstream>
-#include <cstdlib>
using namespace std;
namespace JSBSim {
-static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.41 2010/10/15 11:32:41 jberndt Exp $";
+static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.62 2012/07/19 03:50:19 jberndt Exp $";
static const char *IdHdr = ID_PROPULSION;
extern short debug_lvl;
tankJ.InitMatrix();
refuel = dump = false;
DumpRate = 0.0;
- fuel_freeze = false;
+ FuelFreeze = false;
TotalFuelQuantity = 0.0;
IsBound =
HavePistonEngine =
bool FGPropulsion::InitModel(void)
{
- if (!FGModel::InitModel()) return false;
+ bool result = true;
for (unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
switch (Engines[i]->GetType()) {
case FGEngine::etPiston:
((FGPiston*)Engines[i])->ResetToIC();
- if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
+ try {
+ if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
+ } catch (string str) {
+ cerr << str << endl;
+ result = false;
+ }
break;
case FGEngine::etTurbine:
((FGTurbine*)Engines[i])->ResetToIC();
- if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
+ try {
+ if (HasInitializedEngines && (InitializedEngines & i)) InitRunning(i);
+ } catch (string str) {
+ cerr << str << endl;
+ result = false;
+ }
break;
default:
break;
}
}
- return true;
+ return result;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-bool FGPropulsion::Run(void)
+bool FGPropulsion::Run(bool Holding)
{
unsigned int i;
- if (FGModel::Run()) return true;
- if (FDMExec->Holding()) return false;
+ if (FGModel::Run(Holding)) return true;
+ if (Holding) return false;
RunPreFunctions();
- double dt = FDMExec->GetDeltaT();
-
vForces.InitMatrix();
vMoments.InitMatrix();
for (i=0; i<numEngines; i++) {
Engines[i]->Calculate();
+ ConsumeFuel(Engines[i]);
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
}
TotalFuelQuantity = 0.0;
for (i=0; i<numTanks; i++) {
- Tanks[i]->Calculate( dt * rate );
+ Tanks[i]->Calculate( in.TotalDeltaT, in.TAT_c);
if (Tanks[i]->GetType() == FGTank::ttFUEL) {
TotalFuelQuantity += Tanks[i]->GetContents();
}
}
- if (refuel) DoRefuel( dt * rate );
- if (dump) DumpFuel( dt * rate );
+ if (refuel) DoRefuel( in.TotalDeltaT );
+ if (dump) DumpFuel( in.TotalDeltaT );
RunPostFunctions();
return false;
}
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+//
+// The engine can tell us how much fuel it needs, but it is up to the propulsion
+// subsystem manager class FGPropulsion to manage fuel flow amongst tanks. Engines
+// May burn fuel from more than one tank at a time, and may burn from one tank
+// before another - that is, may burn from one tank until the tank is depleted,
+// then burn from the next highest priority tank. This can be accompished
+// by defining a fuel management system, but this way of specifying priorities
+// is more automatic from a user perspective.
+
+void FGPropulsion::ConsumeFuel(FGEngine* engine)
+{
+ if (FuelFreeze) return;
+ if (FDMExec->GetTrimStatus()) return;
+
+ unsigned int TanksWithFuel=0, CurrentFuelTankPriority=1;
+ unsigned int TanksWithOxidizer=0, CurrentOxidizerTankPriority=1;
+ vector <int> FeedListFuel, FeedListOxi;
+ bool Starved = true; // Initially set Starved to true. Set to false in code below.
+ bool hasOxTanks = false;
+
+ // For this engine,
+ // 1) Count how many fuel tanks with the current priority level have fuel
+ // 2) If there none, then try next lower priority (higher number) - that is,
+ // increment CurrentPriority.
+ // 3) Build the feed list.
+ // 4) Do the same for oxidizer tanks, if needed.
+
+ // Process fuel tanks, if any
+ while ((TanksWithFuel == 0) && (CurrentFuelTankPriority <= numTanks)) {
+ for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
+ unsigned int TankId = engine->GetSourceTank(i);
+ FGTank* Tank = Tanks[TankId];
+ unsigned int TankPriority = Tank->GetPriority();
+ if (TankPriority != 0) {
+ switch(Tank->GetType()) {
+ case FGTank::ttFUEL:
+ if ((Tank->GetContents() > 0.0) && Tank->GetSelected() && (TankPriority == CurrentFuelTankPriority)) {
+ TanksWithFuel++;
+ Starved = false;
+ FeedListFuel.push_back(TankId);
+ }
+ break;
+ case FGTank::ttOXIDIZER:
+ // Skip this here (done below)
+ break;
+ }
+ }
+ }
+ if (TanksWithFuel == 0) CurrentFuelTankPriority++; // No tanks at this priority, try next priority
+ }
+
+ bool FuelStarved = Starved;
+ Starved = true;
+
+ // Process Oxidizer tanks, if any
+ if (engine->GetType() == FGEngine::etRocket) {
+ while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
+ for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
+ unsigned int TankId = engine->GetSourceTank(i);
+ FGTank* Tank = Tanks[TankId];
+ unsigned int TankPriority = Tank->GetPriority();
+ if (TankPriority != 0) {
+ switch(Tank->GetType()) {
+ case FGTank::ttFUEL:
+ // Skip this here (done above)
+ break;
+ case FGTank::ttOXIDIZER:
+ hasOxTanks = true;
+ if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
+ TanksWithOxidizer++;
+ if (TanksWithFuel > 0) Starved = false;
+ FeedListOxi.push_back(TankId);
+ }
+ break;
+ }
+ }
+ }
+ if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
+ }
+ }
+
+ bool OxiStarved = Starved;
+
+ engine->SetStarved(FuelStarved || (hasOxTanks && OxiStarved)); // Tanks can be refilled, so be sure to reset engine Starved flag here.
+
+ // No fuel or fuel/oxidizer found at any priority!
+// if (Starved) return;
+ if (FuelStarved || (hasOxTanks && OxiStarved)) return;
+
+ double FuelToBurn = engine->CalcFuelNeed(); // How much fuel does this engine need?
+ double FuelNeededPerTank = FuelToBurn / TanksWithFuel; // Determine fuel needed per tank.
+ for (unsigned int i=0; i<FeedListFuel.size(); i++) {
+ Tanks[FeedListFuel[i]]->Drain(FuelNeededPerTank);
+ }
+
+ if (engine->GetType() == FGEngine::etRocket) {
+ double OxidizerToBurn = engine->CalcOxidizerNeed(); // How much fuel does this engine need?
+ double OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer; // Determine fuel needed per tank.
+ for (unsigned int i=0; i<FeedListOxi.size(); i++) {
+ Tanks[FeedListOxi[i]]->Drain(OxidizerNeededPerTank);
+ }
+ }
+
+}
+
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGPropulsion::GetSteadyState(void)
double currentThrust = 0, lastThrust = -1;
int steady_count = 0, j = 0;
bool steady = false;
+ bool TrimMode = FDMExec->GetTrimStatus();
vForces.InitMatrix();
vMoments.InitMatrix();
- if (!FGModel::Run()) {
+ if (!FGModel::Run(false)) {
+ FDMExec->SetTrimStatus(true);
+
for (unsigned int i=0; i<numEngines; i++) {
-// cout << " Finding steady state for engine " << i << endl;
- Engines[i]->SetTrimMode(true);
steady=false;
steady_count=0;
j=0;
while (!steady && j < 6000) {
Engines[i]->Calculate();
lastThrust = currentThrust;
- currentThrust = Engines[i]->GetThruster()->GetThrust();
+ currentThrust = Engines[i]->GetThrust();
if (fabs(lastThrust-currentThrust) < 0.0001) {
steady_count++;
if (steady_count > 120) {
steady=true;
-// cout << " Steady state found at thrust: " << currentThrust << " lbs." << endl;
}
} else {
steady_count=0;
}
j++;
}
-// if (j >= 6000) {
-// cout << " Could not find a steady state for this engine." << endl;
-// }
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
- Engines[i]->SetTrimMode(false);
}
+ FDMExec->SetTrimStatus(TrimMode);
+
return false;
} else {
return true;
void FGPropulsion::InitRunning(int n)
{
- if (n > 0) { // A specific engine is supposed to be initialized
+ if (n >= 0) { // A specific engine is supposed to be initialized
if (n >= (int)GetNumEngines() ) {
- cerr << "Tried to initialize a non-existent engine!" << endl;
- throw;
+ throw(string("Tried to initialize a non-existent engine!"));
}
- FCS->SetThrottleCmd(n,1);
- FCS->SetMixtureCmd(n,1);
+
+ in.ThrottleCmd[n] = in.ThrottlePos[n] = 1; // Set the throttle command and position
+ in.MixtureCmd[n] = in.MixturePos[n] = 1; // Set the mixture command and position
+
GetEngine(n)->InitRunning();
GetSteadyState();
} else if (n < 0) { // -1 refers to "All Engines"
for (unsigned int i=0; i<GetNumEngines(); i++) {
- FCS->SetThrottleCmd(i,1);
- FCS->SetMixtureCmd(i,1);
+ in.ThrottleCmd[i] = in.ThrottlePos[i] = 1; // Set the throttle command and position
+ in.MixtureCmd[i] = in.MixturePos[i] = 1; // Set the mixture command and position
GetEngine(i)->InitRunning();
}
+
GetSteadyState();
InitializedEngines = -1;
HasInitializedEngines = true;
-
- } else if (n == 0) { // No engines are to be initialized
- // Do nothing
}
}
bool FGPropulsion::Load(Element* el)
{
string type, engine_filename;
- bool ThrottleAdded = false;
Debug(2);
}
engine_filename = FindEngineFullPathname(engine_filename);
+ if (engine_filename.empty()) {
+ // error message already printed by FindEngineFullPathname()
+ return false;
+ }
+
document = LoadXMLDocument(engine_filename);
document->SetParent(engine_element);
if (type == "piston_engine") {
HavePistonEngine = true;
if (!IsBound) bind();
- Engines.push_back(new FGPiston(FDMExec, document, numEngines));
+ Engines.push_back(new FGPiston(FDMExec, document, numEngines, in));
} else if (type == "turbine_engine") {
HaveTurbineEngine = true;
if (!IsBound) bind();
- Engines.push_back(new FGTurbine(FDMExec, document, numEngines));
+ Engines.push_back(new FGTurbine(FDMExec, document, numEngines, in));
} else if (type == "turboprop_engine") {
HaveTurboPropEngine = true;
if (!IsBound) bind();
- Engines.push_back(new FGTurboProp(FDMExec, document, numEngines));
+ Engines.push_back(new FGTurboProp(FDMExec, document, numEngines, in));
} else if (type == "rocket_engine") {
HaveRocketEngine = true;
if (!IsBound) bind();
- Engines.push_back(new FGRocket(FDMExec, document, numEngines));
+ Engines.push_back(new FGRocket(FDMExec, document, numEngines, in));
} else if (type == "electric_engine") {
HaveElectricEngine = true;
if (!IsBound) bind();
- Engines.push_back(new FGElectric(FDMExec, document, numEngines));
+ Engines.push_back(new FGElectric(FDMExec, document, numEngines, in));
} else {
cerr << "Unknown engine type: " << type << endl;
exit(-5);
return false;
}
- FCS->AddThrottle();
- ThrottleAdded = true;
-
numEngines++;
engine_element = el->FindNextElement("engine");
}
CalculateTankInertias();
- if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
// Process fuel dump rate
if (el->FindElement("dump-rate"))
fullpath = enginePath + separator;
localpath = aircraftPath + separator + "Engines" + separator;
- engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
+ engine_file.open(string(localpath + engine_filename + ".xml").c_str());
if ( !engine_file.is_open()) {
- engine_file.open(string(localpath + engine_filename + ".xml").c_str());
+ engine_file.open(string(fullpath + engine_filename + ".xml").c_str());
if ( !engine_file.is_open()) {
cerr << " Could not open engine file: " << engine_filename << " in path "
<< fullpath << " or " << localpath << endl;
return string("");
} else {
- return string(localpath + engine_filename + ".xml");
+ return string(fullpath + engine_filename + ".xml");
}
}
- return string(fullpath + engine_filename + ".xml");
+ return string(localpath + engine_filename + ".xml");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fullpath = enginePath + separator;
localpath = aircraftPath + separator + "Engines" + separator;
- engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
+ engine_file->open(string(localpath + engine_filename + ".xml").c_str());
if ( !engine_file->is_open()) {
- engine_file->open(string(localpath + engine_filename + ".xml").c_str());
+ engine_file->open(string(fullpath + engine_filename + ".xml").c_str());
if ( !engine_file->is_open()) {
cerr << " Could not open engine file: " << engine_filename << " in path "
- << fullpath << " or " << localpath << endl;
+ << localpath << " or " << fullpath << endl;
}
}
return engine_file;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-string FGPropulsion::GetPropulsionStrings(const string& delimiter)
+string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
{
unsigned int i;
else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
}
+ PropulsionStrings += buf.str();
+ buf.str("");
+
return PropulsionStrings;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-string FGPropulsion::GetPropulsionValues(const string& delimiter)
+string FGPropulsion::GetPropulsionValues(const string& delimiter) const
{
unsigned int i;
buf << Tanks[i]->GetContents();
}
+ PropulsionValues += buf.str();
+ buf.str("");
+
return PropulsionValues;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGColumnVector3& FGPropulsion::GetTanksMoment(void)
+string FGPropulsion::GetPropulsionTankReport()
+{
+ string out="";
+ stringstream outstream;
+ for (unsigned int i=0; i<numTanks; i++)
+ {
+ FGTank* tank = Tanks[i];
+ string tankname="";
+ if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
+ tankname = "Solid Fuel";
+ } else if (tank->GetType() == FGTank::ttFUEL) {
+ tankname = "Fuel";
+ } else if (tank->GetType() == FGTank::ttOXIDIZER) {
+ tankname = "Oxidizer";
+ } else {
+ tankname = "(Unknown tank type)";
+ }
+ outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
+ << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
+ << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
+ << setw(12) << "*" << setw(12) << "*"
+ << setw(12) << "*" << endl;
+ }
+ return outstream.str();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+const FGColumnVector3& FGPropulsion::GetTanksMoment(void)
{
vXYZtank_arm.InitMatrix();
for (unsigned int i=0; i<Tanks.size(); i++) {
- vXYZtank_arm(eX) += Tanks[i]->GetXYZ(eX) * Tanks[i]->GetContents();
- vXYZtank_arm(eY) += Tanks[i]->GetXYZ(eY) * Tanks[i]->GetContents();
- vXYZtank_arm(eZ) += Tanks[i]->GetXYZ(eZ) * Tanks[i]->GetContents();
+ vXYZtank_arm += Tanks[i]->GetXYZ() * Tanks[i]->GetContents();
}
return vXYZtank_arm;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-double FGPropulsion::GetTanksWeight(void)
+double FGPropulsion::GetTanksWeight(void) const
{
double Tw = 0.0;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-FGMatrix33& FGPropulsion::CalculateTankInertias(void)
+const FGMatrix33& FGPropulsion::CalculateTankInertias(void)
{
unsigned int size;
tankJ = FGMatrix33();
for (unsigned int i=0; i<size; i++) {
- tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
- Tanks[i]->GetXYZ() );
+ FGColumnVector3 vTankStructVec = in.vXYZcg - Tanks[i]->GetXYZ();
+
+ tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
+ vTankStructVec);
tankJ(1,1) += Tanks[i]->GetIxx();
tankJ(2,2) += Tanks[i]->GetIyy();
tankJ(3,3) += Tanks[i]->GetIzz();
void FGPropulsion::SetFuelFreeze(bool f)
{
- fuel_freeze = f;
+ FuelFreeze = f;
for (unsigned int i=0; i<numEngines; i++) {
Engines[i]->SetFuelFreeze(f);
}
typedef int (FGPropulsion::*iPMF)(void) const;
IsBound = true;
- PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, true);
+ PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
if (HaveTurbineEngine) {
- PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true);
- PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, true);
+ PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
+ PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, false);
}
if (HavePistonEngine) {
- PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true);
- PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, true);
+ PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
+ PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
}
PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,