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)
67 Name = "FGPropulsion";
68 numSelectedFuelTanks = numSelectedOxiTanks = 0;
69 numTanks = numEngines = numThrusters = 0;
70 numOxiTanks = numFuelTanks = 0;
71 Forces = new FGColumnVector3(3);
72 Moments = new FGColumnVector3(3);
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 (Forces) delete Forces;
84 if (Moments) delete Moments;
85 if (debug_lvl & 2) cout << "Destroyed: FGPropulsion" << endl;
88 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 bool FGPropulsion::Run(void) {
95 Moments->InitMatrix();
97 if (!FGModel::Run()) {
98 for (unsigned int i=0; i<numEngines; i++) {
99 Thrusters[i]->SetdeltaT(dt*rate);
100 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
101 Thrusters[i]->Calculate(PowerAvailable);
102 *Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
103 *Moments += Thrusters[i]->GetMoments(); // sum body frame moments
112 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 bool FGPropulsion::GetSteadyState(void) {
115 float PowerAvailable;
116 float currentThrust = 0, lastThrust=-1;
118 int steady_count,j=0;
121 Forces->InitMatrix();
122 Moments->InitMatrix();
124 if (!FGModel::Run()) {
125 for (unsigned int i=0; i<numEngines; i++) {
126 Engines[i]->SetTrimMode(true);
127 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) {
156 float PowerAvailable;
160 Forces->InitMatrix();
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 float xLoc, yLoc, zLoc, Pitch, Yaw;
190 bool ThrottleAdded = false;
193 fullpath = enginePath + "/";
195 fullpath = enginePath + ";";
198 AC_cfg->GetNextConfigLine();
200 while ((token = AC_cfg->GetValue()) != "/PROPULSION") {
202 if (token == "AC_ENGINE") { // ============ READING ENGINES
204 engineFileName = AC_cfg->GetValue("FILE");
206 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
207 + engineFileName + ".xml"<< endl;
208 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
210 if (Eng_cfg.IsOpen()) {
211 Eng_cfg.GetNextConfigLine();
212 engType = Eng_cfg.GetValue();
215 ThrottleAdded = true;
217 if (engType == "FG_ROCKET") {
218 Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
219 } else if (engType == "FG_PISTON") {
220 Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
221 } else if (engType == "FG_TURBOJET") {
222 Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
223 } else if (engType == "FG_TURBOSHAFT") {
224 Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
225 } else if (engType == "FG_TURBOPROP") {
226 Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
228 cerr << " Unrecognized engine type: " << engType << " found in config file.\n";
231 AC_cfg->GetNextConfigLine();
232 while ((token = AC_cfg->GetValue()) != "/AC_ENGINE") {
234 if (token == "XLOC") { *AC_cfg >> xLoc; }
235 else if (token == "YLOC") { *AC_cfg >> yLoc; }
236 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
237 else if (token == "PITCH") { *AC_cfg >> Pitch;}
238 else if (token == "YAW") { *AC_cfg >> Yaw;}
239 else if (token == "FEED") {
241 Engines[numEngines]->AddFeedTank(Feed);
242 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
243 } else cerr << "Unknown identifier: " << token << " in engine file: "
244 << engineFileName << endl;
248 cout << " X = " << xLoc << endl;
249 cout << " Y = " << yLoc << endl;
250 cout << " Z = " << zLoc << endl;
251 cout << " Pitch = " << Pitch << endl;
252 cout << " Yaw = " << Yaw << endl;
255 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
260 cerr << "Could not read engine config file: " << fullpath
261 + engineFileName + ".xml" << endl;
265 } else if (token == "AC_TANK") { // ============== READING TANKS
267 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
268 Tanks.push_back(new FGTank(AC_cfg));
269 switch(Tanks[numTanks]->GetType()) {
271 numSelectedFuelTanks++;
274 case FGTank::ttOXIDIZER:
275 numSelectedOxiTanks++;
282 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
284 thrusterFileName = AC_cfg->GetValue("FILE");
286 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
287 fullpath + thrusterFileName + ".xml" << endl;
288 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
290 if (Thruster_cfg.IsOpen()) {
291 Thruster_cfg.GetNextConfigLine();
292 thrType = Thruster_cfg.GetValue();
294 if (thrType == "FG_PROPELLER") {
295 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
296 } else if (thrType == "FG_NOZZLE") {
297 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
300 AC_cfg->GetNextConfigLine();
301 while ((token = AC_cfg->GetValue()) != "/AC_THRUSTER") {
303 if (token == "XLOC") *AC_cfg >> xLoc;
304 else if (token == "YLOC") *AC_cfg >> yLoc;
305 else if (token == "ZLOC") *AC_cfg >> zLoc;
306 else if (token == "PITCH") *AC_cfg >> Pitch;
307 else if (token == "YAW") *AC_cfg >> Yaw;
308 else cerr << "Unknown identifier: " << token << " in engine file: "
309 << engineFileName << endl;
312 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
313 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
314 Thrusters[numThrusters]->SetdeltaT(dt*rate);
319 cerr << "Could not read thruster config file: " << fullpath
320 + thrusterFileName + ".xml" << endl;
325 AC_cfg->GetNextConfigLine();
328 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
333 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 string FGPropulsion::GetPropulsionStrings(void)
337 string PropulsionStrings = "";
338 bool firstime = true;
340 for (unsigned int i=0;i<Engines.size();i++) {
342 PropulsionStrings += ", ";
346 switch(Engines[i]->GetType()) {
347 case FGEngine::etPiston:
348 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
350 case FGEngine::etRocket:
351 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
353 case FGEngine::etTurboJet:
354 case FGEngine::etTurboProp:
355 case FGEngine::etTurboShaft:
358 PropulsionStrings += "INVALID ENGINE TYPE";
362 PropulsionStrings += ", ";
364 switch(Thrusters[i]->GetType()) {
365 case FGThruster::ttNozzle:
366 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
368 case FGThruster::ttRotor:
370 case FGThruster::ttPropeller:
371 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
372 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
373 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
376 PropulsionStrings += "INVALID THRUSTER TYPE";
381 return PropulsionStrings;
384 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
386 string FGPropulsion::GetPropulsionValues(void)
389 string PropulsionValues = "";
390 bool firstime = true;
392 for (unsigned int i=0;i<Engines.size();i++) {
394 PropulsionValues += ", ";
398 switch(Engines[i]->GetType()) {
399 case FGEngine::etPiston:
400 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetPowerAvailable(), 10, buff)));
402 case FGEngine::etRocket:
403 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
405 case FGEngine::etTurboJet:
406 case FGEngine::etTurboProp:
407 case FGEngine::etTurboShaft:
411 PropulsionValues += ", ";
413 switch(Thrusters[i]->GetType()) {
414 case FGThruster::ttNozzle:
415 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
417 case FGThruster::ttRotor:
419 case FGThruster::ttPropeller:
420 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
421 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
422 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
427 return PropulsionValues;
430 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
432 FGColumnVector3& FGPropulsion::GetTanksCG(void)
434 iTank = Tanks.begin();
435 vXYZtank.InitMatrix();
436 while (iTank < Tanks.end()) {
437 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
438 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
439 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
445 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447 float FGPropulsion::GetTanksWeight(void)
451 iTank = Tanks.begin();
452 while (iTank < Tanks.end()) {
453 Tw += (*iTank)->GetContents();
459 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 float FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
464 iTank = Tanks.begin();
465 while (iTank < Tanks.end()) {
466 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*GRAVITY);
472 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 float FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
477 iTank = Tanks.begin();
478 while (iTank < Tanks.end()) {
479 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
485 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 float FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
490 iTank = Tanks.begin();
491 while (iTank < Tanks.end()) {
492 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
498 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 float FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
503 iTank = Tanks.begin();
504 while (iTank < Tanks.end()) {
505 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
511 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 float FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
516 iTank = Tanks.begin();
517 while (iTank != Tanks.end()) {
518 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
524 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
526 void FGPropulsion::Debug(void)
528 //TODO: Add your source code here