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;
119 Forces->InitMatrix();
120 Moments->InitMatrix();
122 if (!FGModel::Run()) {
123 for (unsigned int i=0; i<numEngines; i++) {
124 Engines[i]->SetTrimMode(true);
125 Thrusters[i]->SetdeltaT(dt*rate);
126 while (pow(currentThrust - lastThrust, 2.0) > currentThrust*0.00010) {
127 PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
128 lastThrust = currentThrust;
129 currentThrust = Thrusters[i]->Calculate(PowerAvailable);
131 *Forces += Thrusters[i]->GetBodyForces(); // sum body frame forces
132 *Moments += Thrusters[i]->GetMoments(); // sum body frame moments
133 Engines[i]->SetTrimMode(false);
142 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
146 string token, fullpath;
147 string engineFileName, engType;
148 string thrusterFileName, thrType;
150 string enginePath = FDMExec->GetEnginePath();
151 float xLoc, yLoc, zLoc, Pitch, Yaw;
153 bool ThrottleAdded = false;
156 fullpath = enginePath + "/";
158 fullpath = enginePath + ";";
161 AC_cfg->GetNextConfigLine();
163 while ((token = AC_cfg->GetValue()) != "/PROPULSION") {
165 if (token == "AC_ENGINE") { // ============ READING ENGINES
167 engineFileName = AC_cfg->GetValue("FILE");
169 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
170 + engineFileName + ".xml"<< endl;
171 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
173 if (Eng_cfg.IsOpen()) {
174 Eng_cfg.GetNextConfigLine();
175 engType = Eng_cfg.GetValue();
178 ThrottleAdded = true;
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();
291 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
296 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
298 string FGPropulsion::GetPropulsionStrings(void)
300 string PropulsionStrings = "";
301 bool firstime = true;
303 for (unsigned int i=0;i<Engines.size();i++) {
305 PropulsionStrings += ", ";
309 switch(Engines[i]->GetType()) {
310 case FGEngine::etPiston:
311 PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
313 case FGEngine::etRocket:
314 PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
316 case FGEngine::etTurboJet:
317 case FGEngine::etTurboProp:
318 case FGEngine::etTurboShaft:
321 PropulsionStrings += "INVALID ENGINE TYPE";
325 PropulsionStrings += ", ";
327 switch(Thrusters[i]->GetType()) {
328 case FGThruster::ttNozzle:
329 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
331 case FGThruster::ttRotor:
333 case FGThruster::ttPropeller:
334 PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
335 PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
336 PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
339 PropulsionStrings += "INVALID THRUSTER TYPE";
344 return PropulsionStrings;
347 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349 string FGPropulsion::GetPropulsionValues(void)
352 string PropulsionValues = "";
353 bool firstime = true;
355 for (unsigned int i=0;i<Engines.size();i++) {
357 PropulsionValues += ", ";
361 switch(Engines[i]->GetType()) {
362 case FGEngine::etPiston:
363 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetPowerAvailable(), 10, buff)));
365 case FGEngine::etRocket:
366 PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
368 case FGEngine::etTurboJet:
369 case FGEngine::etTurboProp:
370 case FGEngine::etTurboShaft:
374 PropulsionValues += ", ";
376 switch(Thrusters[i]->GetType()) {
377 case FGThruster::ttNozzle:
378 PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
380 case FGThruster::ttRotor:
382 case FGThruster::ttPropeller:
383 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
384 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
385 PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
390 return PropulsionValues;
393 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395 FGColumnVector3& FGPropulsion::GetTanksCG(void)
397 iTank = Tanks.begin();
398 vXYZtank.InitMatrix();
399 while (iTank < Tanks.end()) {
400 vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
401 vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
402 vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
408 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410 float FGPropulsion::GetTanksWeight(void)
414 iTank = Tanks.begin();
415 while (iTank < Tanks.end()) {
416 Tw += (*iTank)->GetContents();
422 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
424 float FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
427 iTank = Tanks.begin();
428 while (iTank < Tanks.end()) {
429 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*GRAVITY);
435 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
437 float FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
440 iTank = Tanks.begin();
441 while (iTank < Tanks.end()) {
442 I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
448 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
450 float FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
453 iTank = Tanks.begin();
454 while (iTank < Tanks.end()) {
455 I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
461 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 float FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
466 iTank = Tanks.begin();
467 while (iTank < Tanks.end()) {
468 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*GRAVITY);
474 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
476 float FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
479 iTank = Tanks.begin();
480 while (iTank != Tanks.end()) {
481 I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*GRAVITY);
487 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489 void FGPropulsion::Debug(void)
491 //TODO: Add your source code here