1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGPropulsion.cpp
6 Purpose: Encapsulates the set of engines, tanks, and thrusters associated
9 ------------- Copyright (C) 2000 Jon S. Berndt (jsb@hal-pc.org) -------------
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU 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 General Public License for more
21 You should have received a copy of the GNU 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 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, tanks, and "thrusters" (the device that transforms the
32 engine power into a force that acts on the aircraft, such as a nozzle or
33 propeller). Once the Propulsion class gets the config file, it reads in
34 information which is specific to a type of engine. Then:
36 1) The appropriate engine type instance is created
37 2) A thruster object is instantiated, and is linked to the engine
38 3) At least one tank object is created, and is linked to an engine.
40 At Run time each engines Calculate() method is called to return the excess power
41 generated during that iteration. The drag from the previous iteration is sub-
42 tracted to give the excess power available for thrust this pass. That quantity
43 is passed to the thrusters associated with a particular engine - perhaps with a
44 scaling mechanism (gearing?) to allow the engine to give its associated thrust-
45 ers specific distributed portions of the excess power.
48 --------------------------------------------------------------------------------
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
55 #include "FGPropulsion.h"
57 static const char *IdSrc = "$Id$";
58 static const char *IdHdr = ID_PROPULSION;
60 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
65 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec),
69 Name = "FGPropulsion";
70 numSelectedFuelTanks = numSelectedOxiTanks = 0;
71 numTanks = numEngines = numThrusters = 0;
72 numOxiTanks = numFuelTanks = 0;
74 if (debug_lvl & 2) cout << "Instantiated: " << Name << endl;
77 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 FGPropulsion::~FGPropulsion()
81 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
83 if (debug_lvl & 2) cout << "Destroyed: FGPropulsion" << endl;
86 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 bool FGPropulsion::Run(void)
90 double PowerAvailable;
96 if (!FGModel::Run()) {
97 for (unsigned int i=0; i<numEngines; i++) {
98 Thrusters[i]->SetdeltaT(dt*rate);
99 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
100 Thrusters[i]->Calculate(PowerAvailable);
101 Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
102 Moments += Thrusters[i]->GetMoments(); // sum body frame moments
110 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112 bool FGPropulsion::GetSteadyState(void)
114 double PowerAvailable;
115 double currentThrust = 0, lastThrust=-1;
117 int steady_count,j=0;
121 Moments.InitMatrix();
123 if (!FGModel::Run()) {
124 for (unsigned int i=0; i<numEngines; i++) {
125 Engines[i]->SetTrimMode(true);
126 Thrusters[i]->SetdeltaT(dt*rate);
129 while (!steady && j < 6000) {
130 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
131 lastThrust = currentThrust;
132 currentThrust = Thrusters[i]->Calculate(PowerAvailable);
133 if(fabs(lastThrust-currentThrust) < 0.0001) {
135 if(steady_count > 120) { steady=true; }
141 Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
142 Moments += Thrusters[i]->GetMoments(); // sum body frame moments
143 Engines[i]->SetTrimMode(false);
152 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155 bool FGPropulsion::ICEngineStart(void)
157 double PowerAvailable;
162 Moments.InitMatrix();
164 for (unsigned int i=0; i<numEngines; i++) {
165 Engines[i]->SetTrimMode(true);
166 Thrusters[i]->SetdeltaT(dt*rate);
168 while (!Engines[i]->GetRunning() && j < 2000) {
169 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
170 Thrusters[i]->Calculate(PowerAvailable);
173 Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
174 Moments += Thrusters[i]->GetMoments(); // sum body frame moments
175 Engines[i]->SetTrimMode(false);
180 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
184 string token, fullpath;
185 string engineFileName, engType;
186 string thrusterFileName, thrType;
188 string enginePath = FDMExec->GetEnginePath();
189 double xLoc, yLoc, zLoc, Pitch, Yaw;
190 double P_Factor = 0, Sense = 0.0;
192 bool ThrottleAdded = false;
195 fullpath = enginePath + "/";
197 fullpath = enginePath + ";";
200 AC_cfg->GetNextConfigLine();
202 while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
204 if (token == "AC_ENGINE") { // ============ READING ENGINES
206 engineFileName = AC_cfg->GetValue("FILE");
208 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
209 + engineFileName + ".xml"<< endl;
210 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
212 if (Eng_cfg.IsOpen()) {
213 Eng_cfg.GetNextConfigLine();
214 engType = Eng_cfg.GetValue();
217 ThrottleAdded = true;
219 if (engType == "FG_ROCKET") {
220 Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
221 } else if (engType == "FG_PISTON") {
222 Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
223 } else if (engType == "FG_TURBOJET") {
224 Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
225 } else if (engType == "FG_TURBOSHAFT") {
226 Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
227 } else if (engType == "FG_TURBOPROP") {
228 Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
230 cerr << fgred << " Unrecognized engine type: " << underon << engType
231 << underoff << " found in config file." << fgdef << endl;
235 AC_cfg->GetNextConfigLine();
236 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
238 if (token == "XLOC") { *AC_cfg >> xLoc; }
239 else if (token == "YLOC") { *AC_cfg >> yLoc; }
240 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
241 else if (token == "PITCH") { *AC_cfg >> Pitch;}
242 else if (token == "YAW") { *AC_cfg >> Yaw;}
243 else if (token == "FEED") {
245 Engines[numEngines]->AddFeedTank(Feed);
246 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
247 } else cerr << "Unknown identifier: " << token << " in engine file: "
248 << engineFileName << endl;
252 cout << " X = " << xLoc << endl;
253 cout << " Y = " << yLoc << endl;
254 cout << " Z = " << zLoc << endl;
255 cout << " Pitch = " << Pitch << endl;
256 cout << " Yaw = " << Yaw << endl;
259 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
260 Engines[numEngines]->SetEngineNumber(numEngines);
265 cerr << fgred << "\n Could not read engine config file: " << underon <<
266 fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
270 } else if (token == "AC_TANK") { // ============== READING TANKS
272 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
273 Tanks.push_back(new FGTank(AC_cfg));
274 switch(Tanks[numTanks]->GetType()) {
276 numSelectedFuelTanks++;
279 case FGTank::ttOXIDIZER:
280 numSelectedOxiTanks++;
287 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
289 thrusterFileName = AC_cfg->GetValue("FILE");
291 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
292 fullpath + thrusterFileName + ".xml" << endl;
293 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
295 if (Thruster_cfg.IsOpen()) {
296 Thruster_cfg.GetNextConfigLine();
297 thrType = Thruster_cfg.GetValue();
299 if (thrType == "FG_PROPELLER") {
300 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
301 } else if (thrType == "FG_NOZZLE") {
302 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
305 AC_cfg->GetNextConfigLine();
306 while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
308 if (token == "XLOC") *AC_cfg >> xLoc;
309 else if (token == "YLOC") *AC_cfg >> yLoc;
310 else if (token == "ZLOC") *AC_cfg >> zLoc;
311 else if (token == "PITCH") *AC_cfg >> Pitch;
312 else if (token == "YAW") *AC_cfg >> Yaw;
313 else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
314 else if (token == "SENSE") *AC_cfg >> Sense;
315 else cerr << "Unknown identifier: " << token << " in engine file: "
316 << engineFileName << endl;
319 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
320 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
321 if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
322 ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
323 cout << " P-Factor: " << P_Factor << endl;
324 ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
325 cout << " Sense: " << Sense << endl;
327 Thrusters[numThrusters]->SetdeltaT(dt*rate);
328 Thrusters[numThrusters]->SetThrusterNumber(numThrusters);
332 cerr << "Could not read thruster config file: " << fullpath
333 + thrusterFileName + ".xml" << endl;
338 AC_cfg->GetNextConfigLine();
341 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
346 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
348 string FGPropulsion::GetPropulsionStrings(void)
350 string PropulsionStrings = "";
351 bool firstime = true;
354 for (unsigned int i=0;i<Engines.size();i++) {
355 if (firstime) firstime = false;
356 else PropulsionStrings += ", ";
358 sprintf(buffer, "%d", i);
360 switch(Engines[i]->GetType()) {
361 case FGEngine::etPiston:
362 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
364 case FGEngine::etRocket:
365 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
367 case FGEngine::etTurboJet:
368 case FGEngine::etTurboProp:
369 case FGEngine::etTurboShaft:
372 PropulsionStrings += "INVALID ENGINE TYPE";
376 PropulsionStrings += ", ";
378 switch(Thrusters[i]->GetType()) {
379 case FGThruster::ttNozzle:
380 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
382 case FGThruster::ttRotor:
384 case FGThruster::ttPropeller:
385 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
386 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
387 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
390 PropulsionStrings += "INVALID THRUSTER TYPE";
395 return PropulsionStrings;
398 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400 string FGPropulsion::GetPropulsionValues(void)
403 string PropulsionValues = "";
404 bool firstime = true;
406 for (unsigned int i=0;i<Engines.size();i++) {
407 if (firstime) firstime = false;
408 else PropulsionValues += ", ";
410 switch(Engines[i]->GetType()) {
411 case FGEngine::etPiston:
412 PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
414 case FGEngine::etRocket:
415 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
417 case FGEngine::etTurboJet:
418 case FGEngine::etTurboProp:
419 case FGEngine::etTurboShaft:
423 PropulsionValues += ", ";
425 switch(Thrusters[i]->GetType()) {
426 case FGThruster::ttNozzle:
427 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
429 case FGThruster::ttRotor:
431 case FGThruster::ttPropeller:
432 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
433 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
434 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
439 return PropulsionValues;
442 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444 FGColumnVector3& FGPropulsion::GetTanksCG(void)
446 iTank = Tanks.begin();
447 vXYZtank.InitMatrix();
448 while (iTank < Tanks.end()) {
449 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
450 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
451 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
457 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 double FGPropulsion::GetTanksWeight(void)
463 iTank = Tanks.begin();
464 while (iTank < Tanks.end()) {
465 Tw += (*iTank)->GetContents();
471 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
476 iTank = Tanks.begin();
477 while (iTank < Tanks.end()) {
478 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
484 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
489 iTank = Tanks.begin();
490 while (iTank < Tanks.end()) {
491 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
497 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
499 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
502 iTank = Tanks.begin();
503 while (iTank < Tanks.end()) {
504 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
510 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
515 iTank = Tanks.begin();
516 while (iTank < Tanks.end()) {
517 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
523 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
525 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
528 iTank = Tanks.begin();
529 while (iTank != Tanks.end()) {
530 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
536 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
538 void FGPropulsion::Debug(void)
540 //TODO: Add your source code here