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);
128 while (!steady && j < 6000) {
129 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
130 lastThrust = currentThrust;
131 currentThrust = Thrusters[i]->Calculate(PowerAvailable);
132 if(fabs(lastThrust-currentThrust) < 0.0001) {
134 if(steady_count > 120) { steady=true; }
140 Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
141 Moments += Thrusters[i]->GetMoments(); // sum body frame moments
142 Engines[i]->SetTrimMode(false);
151 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 bool FGPropulsion::ICEngineStart(void)
156 double PowerAvailable;
161 Moments.InitMatrix();
163 for (unsigned int i=0; i<numEngines; i++) {
164 Engines[i]->SetTrimMode(true);
165 Thrusters[i]->SetdeltaT(dt*rate);
167 while (!Engines[i]->GetRunning() && j < 2000) {
168 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
169 Thrusters[i]->Calculate(PowerAvailable);
172 Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
173 Moments += Thrusters[i]->GetMoments(); // sum body frame moments
174 Engines[i]->SetTrimMode(false);
179 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
183 string token, fullpath;
184 string engineFileName, engType;
185 string thrusterFileName, thrType;
187 string enginePath = FDMExec->GetEnginePath();
188 double xLoc, yLoc, zLoc, Pitch, Yaw;
189 double P_Factor = 0, Sense = 0.0;
191 bool ThrottleAdded = false;
194 fullpath = enginePath + "/";
196 fullpath = enginePath + ";";
199 AC_cfg->GetNextConfigLine();
201 while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
203 if (token == "AC_ENGINE") { // ============ READING ENGINES
205 engineFileName = AC_cfg->GetValue("FILE");
207 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
208 + engineFileName + ".xml"<< endl;
209 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
211 if (Eng_cfg.IsOpen()) {
212 Eng_cfg.GetNextConfigLine();
213 engType = Eng_cfg.GetValue();
216 ThrottleAdded = true;
218 if (engType == "FG_ROCKET") {
219 Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
220 } else if (engType == "FG_PISTON") {
221 Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
222 } else if (engType == "FG_TURBOJET") {
223 Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
224 } else if (engType == "FG_TURBOSHAFT") {
225 Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
226 } else if (engType == "FG_TURBOPROP") {
227 Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
229 cerr << " Unrecognized engine type: " << engType << " found in config file.\n";
232 AC_cfg->GetNextConfigLine();
233 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
235 if (token == "XLOC") { *AC_cfg >> xLoc; }
236 else if (token == "YLOC") { *AC_cfg >> yLoc; }
237 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
238 else if (token == "PITCH") { *AC_cfg >> Pitch;}
239 else if (token == "YAW") { *AC_cfg >> Yaw;}
240 else if (token == "FEED") {
242 Engines[numEngines]->AddFeedTank(Feed);
243 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
244 } else cerr << "Unknown identifier: " << token << " in engine file: "
245 << engineFileName << endl;
249 cout << " X = " << xLoc << endl;
250 cout << " Y = " << yLoc << endl;
251 cout << " Z = " << zLoc << endl;
252 cout << " Pitch = " << Pitch << endl;
253 cout << " Yaw = " << Yaw << endl;
256 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
257 Engines[numEngines]->SetEngineNumber(numEngines);
262 cerr << "Could not read engine config file: " << fullpath
263 + engineFileName + ".xml" << endl;
267 } else if (token == "AC_TANK") { // ============== READING TANKS
269 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
270 Tanks.push_back(new FGTank(AC_cfg));
271 switch(Tanks[numTanks]->GetType()) {
273 numSelectedFuelTanks++;
276 case FGTank::ttOXIDIZER:
277 numSelectedOxiTanks++;
284 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
286 thrusterFileName = AC_cfg->GetValue("FILE");
288 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
289 fullpath + thrusterFileName + ".xml" << endl;
290 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
292 if (Thruster_cfg.IsOpen()) {
293 Thruster_cfg.GetNextConfigLine();
294 thrType = Thruster_cfg.GetValue();
296 if (thrType == "FG_PROPELLER") {
297 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
298 } else if (thrType == "FG_NOZZLE") {
299 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
302 AC_cfg->GetNextConfigLine();
303 while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
305 if (token == "XLOC") *AC_cfg >> xLoc;
306 else if (token == "YLOC") *AC_cfg >> yLoc;
307 else if (token == "ZLOC") *AC_cfg >> zLoc;
308 else if (token == "PITCH") *AC_cfg >> Pitch;
309 else if (token == "YAW") *AC_cfg >> Yaw;
310 else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
311 else if (token == "SENSE") *AC_cfg >> Sense;
312 else cerr << "Unknown identifier: " << token << " in engine file: "
313 << engineFileName << endl;
316 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
317 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
318 if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
319 ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
320 cout << " P-Factor: " << P_Factor << endl;
321 ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
322 cout << " Sense: " << Sense << endl;
324 Thrusters[numThrusters]->SetdeltaT(dt*rate);
329 cerr << "Could not read thruster config file: " << fullpath
330 + thrusterFileName + ".xml" << endl;
335 AC_cfg->GetNextConfigLine();
338 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
343 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345 string FGPropulsion::GetPropulsionStrings(void)
347 string PropulsionStrings = "";
348 bool firstime = true;
350 for (unsigned int i=0;i<Engines.size();i++) {
352 PropulsionStrings += ", ";
356 switch(Engines[i]->GetType()) {
357 case FGEngine::etPiston:
358 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
360 case FGEngine::etRocket:
361 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
363 case FGEngine::etTurboJet:
364 case FGEngine::etTurboProp:
365 case FGEngine::etTurboShaft:
368 PropulsionStrings += "INVALID ENGINE TYPE";
372 PropulsionStrings += ", ";
374 switch(Thrusters[i]->GetType()) {
375 case FGThruster::ttNozzle:
376 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
378 case FGThruster::ttRotor:
380 case FGThruster::ttPropeller:
381 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
382 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
383 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
386 PropulsionStrings += "INVALID THRUSTER TYPE";
391 return PropulsionStrings;
394 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396 string FGPropulsion::GetPropulsionValues(void)
399 string PropulsionValues = "";
400 bool firstime = true;
402 for (unsigned int i=0;i<Engines.size();i++) {
404 PropulsionValues += ", ";
408 switch(Engines[i]->GetType()) {
409 case FGEngine::etPiston:
410 PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
412 case FGEngine::etRocket:
413 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
415 case FGEngine::etTurboJet:
416 case FGEngine::etTurboProp:
417 case FGEngine::etTurboShaft:
421 PropulsionValues += ", ";
423 switch(Thrusters[i]->GetType()) {
424 case FGThruster::ttNozzle:
425 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
427 case FGThruster::ttRotor:
429 case FGThruster::ttPropeller:
430 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
431 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
432 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
437 return PropulsionValues;
440 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442 FGColumnVector3& FGPropulsion::GetTanksCG(void)
444 iTank = Tanks.begin();
445 vXYZtank.InitMatrix();
446 while (iTank < Tanks.end()) {
447 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
448 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
449 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
455 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457 double FGPropulsion::GetTanksWeight(void)
461 iTank = Tanks.begin();
462 while (iTank < Tanks.end()) {
463 Tw += (*iTank)->GetContents();
469 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
474 iTank = Tanks.begin();
475 while (iTank < Tanks.end()) {
476 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
482 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
487 iTank = Tanks.begin();
488 while (iTank < Tanks.end()) {
489 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
495 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
500 iTank = Tanks.begin();
501 while (iTank < Tanks.end()) {
502 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
508 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
513 iTank = Tanks.begin();
514 while (iTank < Tanks.end()) {
515 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
521 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
523 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
526 iTank = Tanks.begin();
527 while (iTank != Tanks.end()) {
528 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
534 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
536 void FGPropulsion::Debug(void)
538 //TODO: Add your source code here