]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGPropulsion.cpp
Sync with latest JSBSim changes.
[flightgear.git] / src / FDM / JSBSim / FGPropulsion.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGPropulsion.cpp
4  Author:       Jon S. Berndt
5  Date started: 08/20/00
6  Purpose:      Encapsulates the set of engines, tanks, and thrusters associated
7                with this aircraft
8
9  ------------- Copyright (C) 2000  Jon S. Berndt (jsb@hal-pc.org) -------------
10
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
14  version.
15
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
19  details.
20
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.
24
25  Further information about the GNU General Public License can also be found on
26  the world wide web at http://www.gnu.org.
27
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:
35
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.
39
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.
46
47 HISTORY
48 --------------------------------------------------------------------------------
49 08/20/00   JSB   Created
50
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 INCLUDES
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
54
55 #include "FGPropulsion.h"
56
57 static const char *IdSrc = "$Id$";
58 static const char *IdHdr = ID_PROPULSION;
59
60 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 CLASS IMPLEMENTATION
62 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
63
64
65 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec),
66                                               Forces(3),
67                                               Moments(3)
68 {
69   Name = "FGPropulsion";
70   numSelectedFuelTanks = numSelectedOxiTanks = 0;
71   numTanks = numEngines = numThrusters = 0;
72   numOxiTanks = numFuelTanks = 0;
73
74   if (debug_lvl & 2) cout << "Instantiated: " << Name << endl;
75 }
76
77 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78
79 FGPropulsion::~FGPropulsion()
80 {
81   for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
82   Engines.clear();
83   if (debug_lvl & 2) cout << "Destroyed:    FGPropulsion" << endl;
84 }
85
86 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87
88 bool FGPropulsion::Run(void)
89 {
90   double PowerAvailable;
91   dt = State->Getdt();
92
93   Forces.InitMatrix();
94   Moments.InitMatrix();
95
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
103     }
104     return false;
105   } else {
106     return true;
107   }
108 }
109
110 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111
112 bool FGPropulsion::GetSteadyState(void)
113 {
114   double PowerAvailable;
115   double currentThrust = 0, lastThrust=-1;
116   dt = State->Getdt();
117   int steady_count,j=0;
118   bool steady=false;
119
120   Forces.InitMatrix();
121   Moments.InitMatrix();
122
123   if (!FGModel::Run()) {
124     for (unsigned int i=0; i<numEngines; i++) {
125       Engines[i]->SetTrimMode(true);
126       Thrusters[i]->SetdeltaT(dt*rate);
127       steady=false;
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) {
133           steady_count++;
134           if(steady_count > 120) { steady=true; }
135         } else {
136           steady_count=0;
137         }
138         j++;    
139       }
140       Forces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
141       Moments += Thrusters[i]->GetMoments();     // sum body frame moments
142       Engines[i]->SetTrimMode(false);
143     }
144
145     return false;
146   } else {
147     return true;
148   }
149 }
150
151 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152
153
154 bool FGPropulsion::ICEngineStart(void)
155 {
156   double PowerAvailable;
157   int j;
158   dt = State->Getdt();
159
160   Forces.InitMatrix();
161   Moments.InitMatrix();
162     
163   for (unsigned int i=0; i<numEngines; i++) {
164     Engines[i]->SetTrimMode(true);
165     Thrusters[i]->SetdeltaT(dt*rate);
166     j=0;
167     while (!Engines[i]->GetRunning() && j < 2000) {
168       PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
169       Thrusters[i]->Calculate(PowerAvailable);
170       j++;    
171     }
172     Forces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
173     Moments += Thrusters[i]->GetMoments();     // sum body frame moments
174     Engines[i]->SetTrimMode(false);
175   }
176   return true;
177 }
178
179 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180
181 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
182 {
183   string token, fullpath;
184   string engineFileName, engType;
185   string thrusterFileName, thrType;
186   string parameter;
187   string enginePath = FDMExec->GetEnginePath();
188   double xLoc, yLoc, zLoc, Pitch, Yaw;
189   double P_Factor = 0, Sense = 0.0;
190   int Feed;
191   bool ThrottleAdded = false;
192
193 # ifndef macintosh
194       fullpath = enginePath + "/";
195 # else
196       fullpath = enginePath + ";";
197 # endif
198
199   AC_cfg->GetNextConfigLine();
200
201   while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
202
203     if (token == "AC_ENGINE") {                   // ============ READING ENGINES
204
205       engineFileName = AC_cfg->GetValue("FILE");
206
207       if (debug_lvl > 0) cout << "\n    Reading engine from file: " << fullpath
208                                                 + engineFileName + ".xml"<< endl;
209       FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
210
211       if (Eng_cfg.IsOpen()) {
212         Eng_cfg.GetNextConfigLine();
213         engType = Eng_cfg.GetValue();
214
215         FCS->AddThrottle();
216         ThrottleAdded = true;
217
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));
228         } else {
229           cerr << "    Unrecognized engine type: " << engType << " found in config file.\n";
230         }
231
232         AC_cfg->GetNextConfigLine();
233         while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
234           *AC_cfg >> token;
235           if      (token == "XLOC")  { *AC_cfg >> xLoc; }
236           else if (token == "YLOC")  { *AC_cfg >> yLoc; }
237           else if (token == "ZLOC")  { *AC_cfg >> zLoc; }
238           else if (token == "PITCH") { *AC_cfg >> Pitch;}
239           else if (token == "YAW")   { *AC_cfg >> Yaw;}
240           else if (token == "FEED")  {
241             *AC_cfg >> Feed;
242             Engines[numEngines]->AddFeedTank(Feed);
243             if (debug_lvl > 0) cout << "      Feed tank: " << Feed << endl;
244           } else cerr << "Unknown identifier: " << token << " in engine file: "
245                                                         << engineFileName << endl;
246         }
247
248         if (debug_lvl > 0)  {
249           cout << "      X = " << xLoc << endl;
250           cout << "      Y = " << yLoc << endl;
251           cout << "      Z = " << zLoc << endl;
252           cout << "      Pitch = " << Pitch << endl;
253           cout << "      Yaw = " << Yaw << endl;
254         }
255         
256         Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
257         Engines[numEngines]->SetEngineNumber(numEngines);
258         numEngines++;
259
260       } else {
261
262         cerr << "Could not read engine config file: " << fullpath
263                                                   + engineFileName + ".xml" << endl;
264         return false;
265       }
266
267     } else if (token == "AC_TANK") {              // ============== READING TANKS
268
269       if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
270       Tanks.push_back(new FGTank(AC_cfg));
271       switch(Tanks[numTanks]->GetType()) {
272       case FGTank::ttFUEL:
273         numSelectedFuelTanks++;
274         numFuelTanks++;
275         break;
276       case FGTank::ttOXIDIZER:
277         numSelectedOxiTanks++;
278         numOxiTanks++;
279         break;
280       }
281
282       numTanks++;
283
284     } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
285
286       thrusterFileName = AC_cfg->GetValue("FILE");
287
288       if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
289                                     fullpath + thrusterFileName + ".xml" << endl;
290       FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
291
292       if (Thruster_cfg.IsOpen()) {
293         Thruster_cfg.GetNextConfigLine();
294         thrType = Thruster_cfg.GetValue();
295
296         if (thrType == "FG_PROPELLER") {
297           Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
298         } else if (thrType == "FG_NOZZLE") {
299           Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
300         }
301
302         AC_cfg->GetNextConfigLine();
303         while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
304           *AC_cfg >> token;
305           if (token == "XLOC") *AC_cfg >> xLoc;
306           else if (token == "YLOC") *AC_cfg >> yLoc;
307           else if (token == "ZLOC") *AC_cfg >> zLoc;
308           else if (token == "PITCH") *AC_cfg >> Pitch;
309           else if (token == "YAW") *AC_cfg >> Yaw;
310           else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
311           else if (token == "SENSE")   *AC_cfg >> Sense;
312           else cerr << "Unknown identifier: " << token << " in engine file: "
313                                                         << engineFileName << endl;
314         }
315
316         Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
317         Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
318         if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
319           ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
320           cout << "      P-Factor: " << P_Factor << endl;
321           ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
322           cout << "      Sense: " << Sense <<  endl;
323         }
324         Thrusters[numThrusters]->SetdeltaT(dt*rate);
325
326         numThrusters++;
327
328       } else {
329         cerr << "Could not read thruster config file: " << fullpath
330                                                 + thrusterFileName + ".xml" << endl;
331         return false;
332       }
333
334     }
335     AC_cfg->GetNextConfigLine();
336   }
337
338   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
339
340   return true;
341 }
342
343 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344
345 string FGPropulsion::GetPropulsionStrings(void)
346 {
347   string PropulsionStrings = "";
348   bool firstime = true;
349
350   for (unsigned int i=0;i<Engines.size();i++) {
351     if (!firstime) {
352       PropulsionStrings += ", ";
353       firstime = false;
354     }
355
356     switch(Engines[i]->GetType()) {
357     case FGEngine::etPiston:
358       PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
359       break;
360     case FGEngine::etRocket:
361       PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
362       break;
363     case FGEngine::etTurboJet:
364     case FGEngine::etTurboProp:
365     case FGEngine::etTurboShaft:
366       break;
367     default:
368       PropulsionStrings += "INVALID ENGINE TYPE";
369       break;
370     }
371
372     PropulsionStrings += ", ";
373
374     switch(Thrusters[i]->GetType()) {
375     case FGThruster::ttNozzle:
376       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
377       break;
378     case FGThruster::ttRotor:
379       break;
380     case FGThruster::ttPropeller:
381       PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
382       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
383       PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
384       break;
385     default:
386       PropulsionStrings += "INVALID THRUSTER TYPE";
387       break;
388     }    
389   }
390
391   return PropulsionStrings;
392 }
393
394 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395
396 string FGPropulsion::GetPropulsionValues(void)
397 {
398   char buff[20];
399   string PropulsionValues = "";
400   bool firstime = true;
401
402   for (unsigned int i=0;i<Engines.size();i++) {
403     if (!firstime) {
404       PropulsionValues += ", ";
405       firstime = false;
406     }
407
408     switch(Engines[i]->GetType()) {
409     case FGEngine::etPiston:
410       PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
411       break;
412     case FGEngine::etRocket:
413       PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
414       break;
415     case FGEngine::etTurboJet:
416     case FGEngine::etTurboProp:
417     case FGEngine::etTurboShaft:
418       break;
419     }
420
421     PropulsionValues += ", ";
422
423     switch(Thrusters[i]->GetType()) {
424     case FGThruster::ttNozzle:
425       PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
426       break;
427     case FGThruster::ttRotor:
428       break;
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)));
433       break;
434     }
435   }
436
437   return PropulsionValues;
438 }
439
440 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441
442 FGColumnVector3& FGPropulsion::GetTanksCG(void)
443 {
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();
450     iTank++;
451   }
452   return vXYZtank;
453 }
454
455 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456
457 double FGPropulsion::GetTanksWeight(void)
458 {
459   double Tw = 0.0;
460
461   iTank = Tanks.begin();
462   while (iTank < Tanks.end()) {
463     Tw += (*iTank)->GetContents();
464     iTank++;
465   }
466   return Tw;
467 }
468
469 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470
471 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
472 {
473   double I = 0.0;
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());
477     iTank++;
478   }
479   return I;
480 }
481
482 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483
484 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
485 {
486   double I = 0.0;
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());
490     iTank++;
491   }
492   return I;
493 }
494
495 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496
497 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
498 {
499   double I = 0.0;
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());
503     iTank++;
504   }
505   return I;
506 }
507
508 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509
510 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
511 {
512   double I = 0.0;
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());
516     iTank++;
517   }
518   return I;
519 }
520
521 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522
523 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
524 {
525   double I = 0.0;
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());
529     iTank++;
530   }
531   return I;
532 }
533
534 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535
536 void FGPropulsion::Debug(void)
537 {
538     //TODO: Add your source code here
539 }
540