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;
72 if (debug_lvl & 2) cout << "Instantiated: " << Name << endl;
75 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 FGPropulsion::~FGPropulsion()
79 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
81 if (debug_lvl & 2) cout << "Destroyed: FGPropulsion" << endl;
84 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 bool FGPropulsion::Run(void)
88 double PowerAvailable;
92 vMoments.InitMatrix();
94 if (!FGModel::Run()) {
95 for (unsigned int i=0; i<numEngines; i++) {
96 Thrusters[i]->SetdeltaT(dt*rate);
97 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
98 Thrusters[i]->Calculate(PowerAvailable);
99 vForces += Thrusters[i]->GetBodyForces(); // sum body frame forces
100 vMoments += Thrusters[i]->GetMoments(); // sum body frame moments
108 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110 bool FGPropulsion::GetSteadyState(void)
112 double PowerAvailable;
113 double currentThrust = 0, lastThrust=-1;
115 int steady_count,j=0;
118 vForces.InitMatrix();
119 vMoments.InitMatrix();
121 if (!FGModel::Run()) {
122 for (unsigned int i=0; i<numEngines; i++) {
123 Engines[i]->SetTrimMode(true);
124 Thrusters[i]->SetdeltaT(dt*rate);
127 while (!steady && j < 6000) {
128 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
129 lastThrust = currentThrust;
130 currentThrust = Thrusters[i]->Calculate(PowerAvailable);
131 if(fabs(lastThrust-currentThrust) < 0.0001) {
133 if(steady_count > 120) { steady=true; }
139 vForces += Thrusters[i]->GetBodyForces(); // sum body frame forces
140 vMoments += Thrusters[i]->GetMoments(); // sum body frame moments
141 Engines[i]->SetTrimMode(false);
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 bool FGPropulsion::ICEngineStart(void)
155 double PowerAvailable;
159 vForces.InitMatrix();
160 vMoments.InitMatrix();
162 for (unsigned int i=0; i<numEngines; i++) {
163 Engines[i]->SetTrimMode(true);
164 Thrusters[i]->SetdeltaT(dt*rate);
166 while (!Engines[i]->GetRunning() && j < 2000) {
167 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
168 Thrusters[i]->Calculate(PowerAvailable);
171 vForces += Thrusters[i]->GetBodyForces(); // sum body frame forces
172 vMoments += Thrusters[i]->GetMoments(); // sum body frame moments
173 Engines[i]->SetTrimMode(false);
178 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
182 string token, fullpath;
183 string engineFileName, engType;
184 string thrusterFileName, thrType;
186 string enginePath = FDMExec->GetEnginePath();
187 double xLoc, yLoc, zLoc, Pitch, Yaw;
188 double P_Factor = 0, Sense = 0.0;
190 bool ThrottleAdded = false;
193 fullpath = enginePath + "/";
195 fullpath = enginePath + ";";
198 AC_cfg->GetNextConfigLine();
200 while ((token = AC_cfg->GetValue()) != string("/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 << fgred << " Unrecognized engine type: " << underon << engType
229 << underoff << " found in config file." << fgdef << endl;
233 AC_cfg->GetNextConfigLine();
234 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
236 if (token == "XLOC") { *AC_cfg >> xLoc; }
237 else if (token == "YLOC") { *AC_cfg >> yLoc; }
238 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
239 else if (token == "PITCH") { *AC_cfg >> Pitch;}
240 else if (token == "YAW") { *AC_cfg >> Yaw;}
241 else if (token == "FEED") {
243 Engines[numEngines]->AddFeedTank(Feed);
244 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
245 } else cerr << "Unknown identifier: " << token << " in engine file: "
246 << engineFileName << endl;
250 cout << " X = " << xLoc << endl;
251 cout << " Y = " << yLoc << endl;
252 cout << " Z = " << zLoc << endl;
253 cout << " Pitch = " << Pitch << endl;
254 cout << " Yaw = " << Yaw << endl;
257 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
258 Engines[numEngines]->SetEngineNumber(numEngines);
263 cerr << fgred << "\n Could not read engine config file: " << underon <<
264 fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
268 } else if (token == "AC_TANK") { // ============== READING TANKS
270 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
271 Tanks.push_back(new FGTank(AC_cfg));
272 switch(Tanks[numTanks]->GetType()) {
274 numSelectedFuelTanks++;
277 case FGTank::ttOXIDIZER:
278 numSelectedOxiTanks++;
285 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
287 thrusterFileName = AC_cfg->GetValue("FILE");
289 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
290 fullpath + thrusterFileName + ".xml" << endl;
291 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
293 if (Thruster_cfg.IsOpen()) {
294 Thruster_cfg.GetNextConfigLine();
295 thrType = Thruster_cfg.GetValue();
297 if (thrType == "FG_PROPELLER") {
298 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
299 } else if (thrType == "FG_NOZZLE") {
300 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
303 AC_cfg->GetNextConfigLine();
304 while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
306 if (token == "XLOC") *AC_cfg >> xLoc;
307 else if (token == "YLOC") *AC_cfg >> yLoc;
308 else if (token == "ZLOC") *AC_cfg >> zLoc;
309 else if (token == "PITCH") *AC_cfg >> Pitch;
310 else if (token == "YAW") *AC_cfg >> Yaw;
311 else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
312 else if (token == "SENSE") *AC_cfg >> Sense;
313 else cerr << "Unknown identifier: " << token << " in engine file: "
314 << engineFileName << endl;
317 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
318 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
319 if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
320 ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
321 cout << " P-Factor: " << P_Factor << endl;
322 ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
323 cout << " Sense: " << Sense << endl;
325 Thrusters[numThrusters]->SetdeltaT(dt*rate);
326 Thrusters[numThrusters]->SetThrusterNumber(numThrusters);
330 cerr << "Could not read thruster config file: " << fullpath
331 + thrusterFileName + ".xml" << endl;
336 AC_cfg->GetNextConfigLine();
339 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
344 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346 string FGPropulsion::GetPropulsionStrings(void)
348 string PropulsionStrings = "";
349 bool firstime = true;
352 for (unsigned int i=0;i<Engines.size();i++) {
353 if (firstime) firstime = false;
354 else PropulsionStrings += ", ";
356 sprintf(buffer, "%d", i);
358 switch(Engines[i]->GetType()) {
359 case FGEngine::etPiston:
360 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
362 case FGEngine::etRocket:
363 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
365 case FGEngine::etTurboJet:
366 case FGEngine::etTurboProp:
367 case FGEngine::etTurboShaft:
370 PropulsionStrings += "INVALID ENGINE TYPE";
374 PropulsionStrings += ", ";
376 switch(Thrusters[i]->GetType()) {
377 case FGThruster::ttNozzle:
378 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
380 case FGThruster::ttRotor:
382 case FGThruster::ttPropeller:
383 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
384 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
385 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
388 PropulsionStrings += "INVALID THRUSTER TYPE";
393 return PropulsionStrings;
396 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 string FGPropulsion::GetPropulsionValues(void)
401 string PropulsionValues = "";
402 bool firstime = true;
404 for (unsigned int i=0;i<Engines.size();i++) {
405 if (firstime) firstime = false;
406 else 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