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 extern short debug_lvl;
62 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
67 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
69 Name = "FGPropulsion";
70 numSelectedFuelTanks = numSelectedOxiTanks = 0;
71 numTanks = numEngines = numThrusters = 0;
72 numOxiTanks = numFuelTanks = 0;
73 Forces = new FGColumnVector(3);
74 Moments = new FGColumnVector(3);
76 if (debug_lvl & 2) cout << "Instantiated: " << Name << endl;
79 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 FGPropulsion::~FGPropulsion()
83 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
85 if (Forces) delete Forces;
86 if (Moments) delete Moments;
87 if (debug_lvl & 2) cout << "Destroyed: FGPropulsion" << endl;
90 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 bool FGPropulsion::Run(void) {
97 Moments->InitMatrix();
99 if (!FGModel::Run()) {
100 for (unsigned int i=0; i<numEngines; i++) {
101 Thrusters[i]->SetdeltaT(dt*rate);
102 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
103 Thrusters[i]->Calculate(PowerAvailable);
104 *Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
105 *Moments += Thrusters[i]->GetMoments(); // sum body frame moments
114 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116 bool FGPropulsion::GetSteadyState(void) {
117 float PowerAvailable;
118 float currentThrust = 0, lastThrust=-1;
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);
128 while (pow(currentThrust - lastThrust, 2.0) > currentThrust*0.00010) {
129 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
130 lastThrust = currentThrust;
131 currentThrust = Thrusters[i]->Calculate(PowerAvailable);
133 *Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
134 *Moments += Thrusters[i]->GetMoments(); // sum body frame moments
135 Engines[i]->SetTrimMode(false);
144 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
148 string token, fullpath;
149 string engineFileName, engType;
150 string thrusterFileName, thrType;
152 string enginePath = FDMExec->GetEnginePath();
153 float xLoc, yLoc, zLoc, Pitch, Yaw;
155 bool ThrottleAdded = false;
158 fullpath = enginePath + "/";
160 fullpath = enginePath + ";";
163 AC_cfg->GetNextConfigLine();
165 while ((token = AC_cfg->GetValue()) != "/PROPULSION") {
167 if (token == "AC_ENGINE") { // ============ READING ENGINES
169 engineFileName = AC_cfg->GetValue("FILE");
171 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
172 + engineFileName + ".xml"<< endl;
173 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
175 if (Eng_cfg.IsOpen()) {
176 Eng_cfg.GetNextConfigLine();
177 engType = Eng_cfg.GetValue();
180 ThrottleAdded = true;
182 if (engType == "FG_ROCKET") {
183 Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
184 } else if (engType == "FG_PISTON") {
185 Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
186 } else if (engType == "FG_TURBOJET") {
187 Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
188 } else if (engType == "FG_TURBOSHAFT") {
189 Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
190 } else if (engType == "FG_TURBOPROP") {
191 Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
193 cerr << " Unrecognized engine type: " << engType << " found in config file.\n";
196 AC_cfg->GetNextConfigLine();
197 while ((token = AC_cfg->GetValue()) != "/AC_ENGINE") {
199 if (token == "XLOC") { *AC_cfg >> xLoc; }
200 else if (token == "YLOC") { *AC_cfg >> yLoc; }
201 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
202 else if (token == "PITCH") { *AC_cfg >> Pitch;}
203 else if (token == "YAW") { *AC_cfg >> Yaw;}
204 else if (token == "FEED") {
206 Engines[numEngines]->AddFeedTank(Feed);
207 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
208 } else cerr << "Unknown identifier: " << token << " in engine file: "
209 << engineFileName << endl;
213 cout << " X = " << xLoc << endl;
214 cout << " Y = " << yLoc << endl;
215 cout << " Z = " << zLoc << endl;
216 cout << " Pitch = " << Pitch << endl;
217 cout << " Yaw = " << Yaw << endl;
220 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
225 cerr << "Could not read engine config file: " << fullpath
226 + engineFileName + ".xml" << endl;
230 } else if (token == "AC_TANK") { // ============== READING TANKS
232 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
233 Tanks.push_back(new FGTank(AC_cfg));
234 switch(Tanks[numTanks]->GetType()) {
236 numSelectedFuelTanks++;
239 case FGTank::ttOXIDIZER:
240 numSelectedOxiTanks++;
247 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
249 thrusterFileName = AC_cfg->GetValue("FILE");
251 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
252 fullpath + thrusterFileName + ".xml" << endl;
253 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
255 if (Thruster_cfg.IsOpen()) {
256 Thruster_cfg.GetNextConfigLine();
257 thrType = Thruster_cfg.GetValue();
259 if (thrType == "FG_PROPELLER") {
260 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
261 } else if (thrType == "FG_NOZZLE") {
262 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
265 AC_cfg->GetNextConfigLine();
266 while ((token = AC_cfg->GetValue()) != "/AC_THRUSTER") {
268 if (token == "XLOC") *AC_cfg >> xLoc;
269 else if (token == "YLOC") *AC_cfg >> yLoc;
270 else if (token == "ZLOC") *AC_cfg >> zLoc;
271 else if (token == "PITCH") *AC_cfg >> Pitch;
272 else if (token == "YAW") *AC_cfg >> Yaw;
273 else cerr << "Unknown identifier: " << token << " in engine file: "
274 << engineFileName << endl;
277 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
278 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
279 Thrusters[numThrusters]->SetdeltaT(dt*rate);
284 cerr << "Could not read thruster config file: " << fullpath
285 + thrusterFileName + ".xml" << endl;
290 AC_cfg->GetNextConfigLine();
293 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
298 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300 string FGPropulsion::GetPropulsionStrings(void)
302 string PropulsionStrings = "";
303 bool firstime = true;
305 for (unsigned int i=0;i<Engines.size();i++) {
307 PropulsionStrings += ", ";
311 switch(Engines[i]->GetType()) {
312 case FGEngine::etPiston:
313 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
315 case FGEngine::etRocket:
316 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
318 case FGEngine::etTurboJet:
319 case FGEngine::etTurboProp:
320 case FGEngine::etTurboShaft:
323 PropulsionStrings += "INVALID ENGINE TYPE";
327 PropulsionStrings += ", ";
329 switch(Thrusters[i]->GetType()) {
330 case FGThruster::ttNozzle:
331 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
333 case FGThruster::ttRotor:
335 case FGThruster::ttPropeller:
336 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
337 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
338 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
341 PropulsionStrings += "INVALID THRUSTER TYPE";
346 return PropulsionStrings;
349 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
351 string FGPropulsion::GetPropulsionValues(void)
354 string PropulsionValues = "";
355 bool firstime = true;
357 for (unsigned int i=0;i<Engines.size();i++) {
359 PropulsionValues += ", ";
363 switch(Engines[i]->GetType()) {
364 case FGEngine::etPiston:
365 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetPowerAvailable(), 10, buff)));
367 case FGEngine::etRocket:
368 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
370 case FGEngine::etTurboJet:
371 case FGEngine::etTurboProp:
372 case FGEngine::etTurboShaft:
376 PropulsionValues += ", ";
378 switch(Thrusters[i]->GetType()) {
379 case FGThruster::ttNozzle:
380 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
382 case FGThruster::ttRotor:
384 case FGThruster::ttPropeller:
385 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
386 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
387 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
392 return PropulsionValues;
395 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397 FGColumnVector& FGPropulsion::GetTanksCG(void)
399 iTank = Tanks.begin();
400 vXYZtank.InitMatrix();
401 while (iTank < Tanks.end()) {
402 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
403 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
404 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
410 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 float FGPropulsion::GetTanksWeight(void)
416 iTank = Tanks.begin();
417 while (iTank < Tanks.end()) {
418 Tw += (*iTank)->GetContents();
424 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
426 float FGPropulsion::GetTanksIxx(const FGColumnVector& vXYZcg)
429 iTank = Tanks.begin();
430 while (iTank < Tanks.end()) {
431 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*GRAVITY);
437 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439 float FGPropulsion::GetTanksIyy(const FGColumnVector& vXYZcg)
442 iTank = Tanks.begin();
443 while (iTank < Tanks.end()) {
444 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
450 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452 float FGPropulsion::GetTanksIzz(const FGColumnVector& vXYZcg)
455 iTank = Tanks.begin();
456 while (iTank < Tanks.end()) {
457 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
463 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 float FGPropulsion::GetTanksIxz(const FGColumnVector& vXYZcg)
468 iTank = Tanks.begin();
469 while (iTank < Tanks.end()) {
470 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
476 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478 float FGPropulsion::GetTanksIxy(const FGColumnVector& vXYZcg)
481 iTank = Tanks.begin();
482 while (iTank != Tanks.end()) {
483 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
489 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 void FGPropulsion::Debug(void)
493 //TODO: Add your source code here