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::LoadPropulsion(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;
157 fullpath = enginePath + "/";
159 fullpath = enginePath + ";";
162 AC_cfg->GetNextConfigLine();
164 while ((token = AC_cfg->GetValue()) != "/PROPULSION") {
166 if (token == "AC_ENGINE") { // ============ READING ENGINES
168 engineFileName = AC_cfg->GetValue("FILE");
170 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
171 + engineFileName + ".xml"<< endl;
172 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
174 if (Eng_cfg.IsOpen()) {
175 Eng_cfg.GetNextConfigLine();
176 engType = Eng_cfg.GetValue();
180 if (engType == "FG_ROCKET") {
181 Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
182 } else if (engType == "FG_PISTON") {
183 Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
184 } else if (engType == "FG_TURBOJET") {
185 Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
186 } else if (engType == "FG_TURBOSHAFT") {
187 Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
188 } else if (engType == "FG_TURBOPROP") {
189 Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
191 cerr << " Unrecognized engine type: " << engType << " found in config file.\n";
194 AC_cfg->GetNextConfigLine();
195 while ((token = AC_cfg->GetValue()) != "/AC_ENGINE") {
197 if (token == "XLOC") { *AC_cfg >> xLoc; }
198 else if (token == "YLOC") { *AC_cfg >> yLoc; }
199 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
200 else if (token == "PITCH") { *AC_cfg >> Pitch;}
201 else if (token == "YAW") { *AC_cfg >> Yaw;}
202 else if (token == "FEED") {
204 Engines[numEngines]->AddFeedTank(Feed);
205 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
206 } else cerr << "Unknown identifier: " << token << " in engine file: "
207 << engineFileName << endl;
211 cout << " X = " << xLoc << endl;
212 cout << " Y = " << yLoc << endl;
213 cout << " Z = " << zLoc << endl;
214 cout << " Pitch = " << Pitch << endl;
215 cout << " Yaw = " << Yaw << endl;
218 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
223 cerr << "Could not read engine config file: " << fullpath
224 + engineFileName + ".xml" << endl;
228 } else if (token == "AC_TANK") { // ============== READING TANKS
230 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
231 Tanks.push_back(new FGTank(AC_cfg));
232 switch(Tanks[numTanks]->GetType()) {
234 numSelectedFuelTanks++;
237 case FGTank::ttOXIDIZER:
238 numSelectedOxiTanks++;
245 } else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
247 thrusterFileName = AC_cfg->GetValue("FILE");
249 if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
250 fullpath + thrusterFileName + ".xml" << endl;
251 FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
253 if (Thruster_cfg.IsOpen()) {
254 Thruster_cfg.GetNextConfigLine();
255 thrType = Thruster_cfg.GetValue();
257 if (thrType == "FG_PROPELLER") {
258 Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
259 } else if (thrType == "FG_NOZZLE") {
260 Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
263 AC_cfg->GetNextConfigLine();
264 while ((token = AC_cfg->GetValue()) != "/AC_THRUSTER") {
266 if (token == "XLOC") *AC_cfg >> xLoc;
267 else if (token == "YLOC") *AC_cfg >> yLoc;
268 else if (token == "ZLOC") *AC_cfg >> zLoc;
269 else if (token == "PITCH") *AC_cfg >> Pitch;
270 else if (token == "YAW") *AC_cfg >> Yaw;
271 else cerr << "Unknown identifier: " << token << " in engine file: "
272 << engineFileName << endl;
275 Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
276 Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
277 Thrusters[numThrusters]->SetdeltaT(dt*rate);
282 cerr << "Could not read thruster config file: " << fullpath
283 + thrusterFileName + ".xml" << endl;
288 AC_cfg->GetNextConfigLine();
294 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
296 string FGPropulsion::GetPropulsionStrings(void)
298 string PropulsionStrings = "";
299 bool firstime = true;
301 for (unsigned int i=0;i<Engines.size();i++) {
303 PropulsionStrings += ", ";
307 switch(Engines[i]->GetType()) {
308 case FGEngine::etPiston:
309 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
311 case FGEngine::etRocket:
312 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
314 case FGEngine::etTurboJet:
315 case FGEngine::etTurboProp:
316 case FGEngine::etTurboShaft:
319 PropulsionStrings += "INVALID ENGINE TYPE";
323 PropulsionStrings += ", ";
325 switch(Thrusters[i]->GetType()) {
326 case FGThruster::ttNozzle:
327 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
329 case FGThruster::ttRotor:
331 case FGThruster::ttPropeller:
332 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
333 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
334 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
337 PropulsionStrings += "INVALID THRUSTER TYPE";
342 return PropulsionStrings;
345 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 string FGPropulsion::GetPropulsionValues(void)
350 string PropulsionValues = "";
351 bool firstime = true;
353 for (unsigned int i=0;i<Engines.size();i++) {
355 PropulsionValues += ", ";
359 switch(Engines[i]->GetType()) {
360 case FGEngine::etPiston:
361 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetPowerAvailable(), 10, buff)));
363 case FGEngine::etRocket:
364 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
366 case FGEngine::etTurboJet:
367 case FGEngine::etTurboProp:
368 case FGEngine::etTurboShaft:
372 PropulsionValues += ", ";
374 switch(Thrusters[i]->GetType()) {
375 case FGThruster::ttNozzle:
376 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
378 case FGThruster::ttRotor:
380 case FGThruster::ttPropeller:
381 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
382 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
383 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
388 return PropulsionValues;
391 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393 FGColumnVector& FGPropulsion::GetTanksCG(void)
395 iTank = Tanks.begin();
396 vXYZtank.InitMatrix();
397 while (iTank < Tanks.end()) {
398 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
399 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
400 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
406 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408 float FGPropulsion::GetTanksWeight(void)
412 iTank = Tanks.begin();
413 while (iTank < Tanks.end()) {
414 Tw += (*iTank)->GetContents();
420 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422 float FGPropulsion::GetTanksIxx(const FGColumnVector& vXYZcg)
425 iTank = Tanks.begin();
426 while (iTank < Tanks.end()) {
427 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*GRAVITY);
433 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435 float FGPropulsion::GetTanksIyy(const FGColumnVector& vXYZcg)
438 iTank = Tanks.begin();
439 while (iTank < Tanks.end()) {
440 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
446 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 float FGPropulsion::GetTanksIzz(const FGColumnVector& vXYZcg)
451 iTank = Tanks.begin();
452 while (iTank < Tanks.end()) {
453 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
459 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 float FGPropulsion::GetTanksIxz(const FGColumnVector& vXYZcg)
464 iTank = Tanks.begin();
465 while (iTank < Tanks.end()) {
466 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
472 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 float FGPropulsion::GetTanksIxy(const FGColumnVector& vXYZcg)
477 iTank = Tanks.begin();
478 while (iTank != Tanks.end()) {
479 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
485 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 void FGPropulsion::Debug(void)
489 //TODO: Add your source code here