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 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()) != "/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()) != "/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);
261 cerr << "Could not read engine config file: " << fullpath
262 + engineFileName + ".xml" << endl;
266 } else if (token == "AC_TANK") { // ============== READING TANKS
268 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
269 Tanks.push_back(new FGTank(AC_cfg));
270 switch(Tanks[numTanks]->GetType()) {
272 numSelectedFuelTanks++;
275 case FGTank::ttOXIDIZER:
276 numSelectedOxiTanks++;
283 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
285 thrusterFileName = AC_cfg->GetValue("FILE");
287 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
288 fullpath + thrusterFileName + ".xml" << endl;
289 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
291 if (Thruster_cfg.IsOpen()) {
292 Thruster_cfg.GetNextConfigLine();
293 thrType = Thruster_cfg.GetValue();
295 if (thrType == "FG_PROPELLER") {
296 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
297 } else if (thrType == "FG_NOZZLE") {
298 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
301 AC_cfg->GetNextConfigLine();
302 while ((token = AC_cfg->GetValue()) != "/AC_THRUSTER") {
304 if (token == "XLOC") *AC_cfg >> xLoc;
305 else if (token == "YLOC") *AC_cfg >> yLoc;
306 else if (token == "ZLOC") *AC_cfg >> zLoc;
307 else if (token == "PITCH") *AC_cfg >> Pitch;
308 else if (token == "YAW") *AC_cfg >> Yaw;
309 else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
310 else if (token == "SENSE") *AC_cfg >> Sense;
311 else cerr << "Unknown identifier: " << token << " in engine file: "
312 << engineFileName << endl;
315 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
316 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
317 if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
318 ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
319 cout << " P-Factor: " << P_Factor << endl;
320 ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
321 cout << " Sense: " << Sense << endl;
323 Thrusters[numThrusters]->SetdeltaT(dt*rate);
328 cerr << "Could not read thruster config file: " << fullpath
329 + thrusterFileName + ".xml" << endl;
334 AC_cfg->GetNextConfigLine();
337 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
342 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344 string FGPropulsion::GetPropulsionStrings(void)
346 string PropulsionStrings = "";
347 bool firstime = true;
349 for (unsigned int i=0;i<Engines.size();i++) {
351 PropulsionStrings += ", ";
355 switch(Engines[i]->GetType()) {
356 case FGEngine::etPiston:
357 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
359 case FGEngine::etRocket:
360 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
362 case FGEngine::etTurboJet:
363 case FGEngine::etTurboProp:
364 case FGEngine::etTurboShaft:
367 PropulsionStrings += "INVALID ENGINE TYPE";
371 PropulsionStrings += ", ";
373 switch(Thrusters[i]->GetType()) {
374 case FGThruster::ttNozzle:
375 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
377 case FGThruster::ttRotor:
379 case FGThruster::ttPropeller:
380 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
381 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
382 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
385 PropulsionStrings += "INVALID THRUSTER TYPE";
390 return PropulsionStrings;
393 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395 string FGPropulsion::GetPropulsionValues(void)
398 string PropulsionValues = "";
399 bool firstime = true;
401 for (unsigned int i=0;i<Engines.size();i++) {
403 PropulsionValues += ", ";
407 switch(Engines[i]->GetType()) {
408 case FGEngine::etPiston:
409 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetPowerAvailable(), 10, buff)));
411 case FGEngine::etRocket:
412 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
414 case FGEngine::etTurboJet:
415 case FGEngine::etTurboProp:
416 case FGEngine::etTurboShaft:
420 PropulsionValues += ", ";
422 switch(Thrusters[i]->GetType()) {
423 case FGThruster::ttNozzle:
424 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
426 case FGThruster::ttRotor:
428 case FGThruster::ttPropeller:
429 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
430 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
431 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
436 return PropulsionValues;
439 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441 FGColumnVector3& FGPropulsion::GetTanksCG(void)
443 iTank = Tanks.begin();
444 vXYZtank.InitMatrix();
445 while (iTank < Tanks.end()) {
446 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
447 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
448 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
454 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456 float FGPropulsion::GetTanksWeight(void)
460 iTank = Tanks.begin();
461 while (iTank < Tanks.end()) {
462 Tw += (*iTank)->GetContents();
468 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470 float FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
473 iTank = Tanks.begin();
474 while (iTank < Tanks.end()) {
475 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483 float FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
486 iTank = Tanks.begin();
487 while (iTank < Tanks.end()) {
488 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
494 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 float FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
499 iTank = Tanks.begin();
500 while (iTank < Tanks.end()) {
501 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
507 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509 float FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
512 iTank = Tanks.begin();
513 while (iTank < Tanks.end()) {
514 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
520 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522 float FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
525 iTank = Tanks.begin();
526 while (iTank != Tanks.end()) {
527 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
533 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535 void FGPropulsion::Debug(void)
537 //TODO: Add your source code here