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 << fgred << " Unrecognized engine type: " << underon << engType
230 << underoff << " found in config file." << fgdef << endl;
234 AC_cfg->GetNextConfigLine();
235 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
237 if (token == "XLOC") { *AC_cfg >> xLoc; }
238 else if (token == "YLOC") { *AC_cfg >> yLoc; }
239 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
240 else if (token == "PITCH") { *AC_cfg >> Pitch;}
241 else if (token == "YAW") { *AC_cfg >> Yaw;}
242 else if (token == "FEED") {
244 Engines[numEngines]->AddFeedTank(Feed);
245 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
246 } else cerr << "Unknown identifier: " << token << " in engine file: "
247 << engineFileName << endl;
251 cout << " X = " << xLoc << endl;
252 cout << " Y = " << yLoc << endl;
253 cout << " Z = " << zLoc << endl;
254 cout << " Pitch = " << Pitch << endl;
255 cout << " Yaw = " << Yaw << endl;
258 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
259 Engines[numEngines]->SetEngineNumber(numEngines);
264 cerr << fgred << "\n Could not read engine config file: " << underon <<
265 fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
269 } else if (token == "AC_TANK") { // ============== READING TANKS
271 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
272 Tanks.push_back(new FGTank(AC_cfg));
273 switch(Tanks[numTanks]->GetType()) {
275 numSelectedFuelTanks++;
278 case FGTank::ttOXIDIZER:
279 numSelectedOxiTanks++;
286 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
288 thrusterFileName = AC_cfg->GetValue("FILE");
290 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
291 fullpath + thrusterFileName + ".xml" << endl;
292 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
294 if (Thruster_cfg.IsOpen()) {
295 Thruster_cfg.GetNextConfigLine();
296 thrType = Thruster_cfg.GetValue();
298 if (thrType == "FG_PROPELLER") {
299 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
300 } else if (thrType == "FG_NOZZLE") {
301 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
304 AC_cfg->GetNextConfigLine();
305 while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
307 if (token == "XLOC") *AC_cfg >> xLoc;
308 else if (token == "YLOC") *AC_cfg >> yLoc;
309 else if (token == "ZLOC") *AC_cfg >> zLoc;
310 else if (token == "PITCH") *AC_cfg >> Pitch;
311 else if (token == "YAW") *AC_cfg >> Yaw;
312 else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
313 else if (token == "SENSE") *AC_cfg >> Sense;
314 else cerr << "Unknown identifier: " << token << " in engine file: "
315 << engineFileName << endl;
318 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
319 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
320 if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
321 ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
322 cout << " P-Factor: " << P_Factor << endl;
323 ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
324 cout << " Sense: " << Sense << endl;
326 Thrusters[numThrusters]->SetdeltaT(dt*rate);
331 cerr << "Could not read thruster config file: " << fullpath
332 + thrusterFileName + ".xml" << endl;
337 AC_cfg->GetNextConfigLine();
340 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
345 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 string FGPropulsion::GetPropulsionStrings(void)
349 string PropulsionStrings = "";
350 bool firstime = true;
353 for (unsigned int i=0;i<Engines.size();i++) {
354 if (firstime) firstime = false;
355 else PropulsionStrings += ", ";
357 sprintf(buffer, "%d", i);
359 switch(Engines[i]->GetType()) {
360 case FGEngine::etPiston:
361 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
363 case FGEngine::etRocket:
364 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
366 case FGEngine::etTurboJet:
367 case FGEngine::etTurboProp:
368 case FGEngine::etTurboShaft:
371 PropulsionStrings += "INVALID ENGINE TYPE";
375 PropulsionStrings += ", ";
377 switch(Thrusters[i]->GetType()) {
378 case FGThruster::ttNozzle:
379 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
381 case FGThruster::ttRotor:
383 case FGThruster::ttPropeller:
384 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
385 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
386 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
389 PropulsionStrings += "INVALID THRUSTER TYPE";
394 return PropulsionStrings;
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399 string FGPropulsion::GetPropulsionValues(void)
402 string PropulsionValues = "";
403 bool firstime = true;
405 for (unsigned int i=0;i<Engines.size();i++) {
406 if (firstime) firstime = false;
407 else PropulsionValues += ", ";
409 switch(Engines[i]->GetType()) {
410 case FGEngine::etPiston:
411 PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
413 case FGEngine::etRocket:
414 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
416 case FGEngine::etTurboJet:
417 case FGEngine::etTurboProp:
418 case FGEngine::etTurboShaft:
422 PropulsionValues += ", ";
424 switch(Thrusters[i]->GetType()) {
425 case FGThruster::ttNozzle:
426 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
428 case FGThruster::ttRotor:
430 case FGThruster::ttPropeller:
431 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
432 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
433 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
438 return PropulsionValues;
441 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443 FGColumnVector3& FGPropulsion::GetTanksCG(void)
445 iTank = Tanks.begin();
446 vXYZtank.InitMatrix();
447 while (iTank < Tanks.end()) {
448 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
449 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
450 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
456 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458 double FGPropulsion::GetTanksWeight(void)
462 iTank = Tanks.begin();
463 while (iTank < Tanks.end()) {
464 Tw += (*iTank)->GetContents();
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
475 iTank = Tanks.begin();
476 while (iTank < Tanks.end()) {
477 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
483 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
488 iTank = Tanks.begin();
489 while (iTank < Tanks.end()) {
490 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
496 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
501 iTank = Tanks.begin();
502 while (iTank < Tanks.end()) {
503 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
509 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
514 iTank = Tanks.begin();
515 while (iTank < Tanks.end()) {
516 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
522 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
527 iTank = Tanks.begin();
528 while (iTank != Tanks.end()) {
529 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
535 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
537 void FGPropulsion::Debug(void)
539 //TODO: Add your source code here