]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGPropulsion.cpp
Sync with latest JSBSim.
[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 << fgred << "    Unrecognized engine type: " << underon << engType
230                     << underoff << " found in config file." << fgdef << endl;
231           return false;
232         }
233
234         AC_cfg->GetNextConfigLine();
235         while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
236           *AC_cfg >> token;
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")  {
243             *AC_cfg >> 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;
248         }
249
250         if (debug_lvl > 0)  {
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;
256         }
257         
258         Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
259         Engines[numEngines]->SetEngineNumber(numEngines);
260         numEngines++;
261
262       } else {
263
264         cerr << fgred << "\n  Could not read engine config file: " << underon <<
265                     fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
266         return false;
267       }
268
269     } else if (token == "AC_TANK") {              // ============== READING TANKS
270
271       if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
272       Tanks.push_back(new FGTank(AC_cfg));
273       switch(Tanks[numTanks]->GetType()) {
274       case FGTank::ttFUEL:
275         numSelectedFuelTanks++;
276         numFuelTanks++;
277         break;
278       case FGTank::ttOXIDIZER:
279         numSelectedOxiTanks++;
280         numOxiTanks++;
281         break;
282       }
283
284       numTanks++;
285
286     } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
287
288       thrusterFileName = AC_cfg->GetValue("FILE");
289
290       if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
291                                     fullpath + thrusterFileName + ".xml" << endl;
292       FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
293
294       if (Thruster_cfg.IsOpen()) {
295         Thruster_cfg.GetNextConfigLine();
296         thrType = Thruster_cfg.GetValue();
297
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));
302         }
303
304         AC_cfg->GetNextConfigLine();
305         while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
306           *AC_cfg >> token;
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;
316         }
317
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;
325         }
326         Thrusters[numThrusters]->SetdeltaT(dt*rate);
327
328         numThrusters++;
329
330       } else {
331         cerr << "Could not read thruster config file: " << fullpath
332                                                 + thrusterFileName + ".xml" << endl;
333         return false;
334       }
335
336     }
337     AC_cfg->GetNextConfigLine();
338   }
339
340   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
341
342   return true;
343 }
344
345 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346
347 string FGPropulsion::GetPropulsionStrings(void)
348 {
349   string PropulsionStrings = "";
350   bool firstime = true;
351   char buffer[5];
352
353   for (unsigned int i=0;i<Engines.size();i++) {
354     if (firstime)  firstime = false;
355     else           PropulsionStrings += ", ";
356
357     sprintf(buffer, "%d", i);
358
359     switch(Engines[i]->GetType()) {
360     case FGEngine::etPiston:
361       PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
362       break;
363     case FGEngine::etRocket:
364       PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
365       break;
366     case FGEngine::etTurboJet:
367     case FGEngine::etTurboProp:
368     case FGEngine::etTurboShaft:
369       break;
370     default:
371       PropulsionStrings += "INVALID ENGINE TYPE";
372       break;
373     }
374
375     PropulsionStrings += ", ";
376
377     switch(Thrusters[i]->GetType()) {
378     case FGThruster::ttNozzle:
379       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
380       break;
381     case FGThruster::ttRotor:
382       break;
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 + "]");
387       break;
388     default:
389       PropulsionStrings += "INVALID THRUSTER TYPE";
390       break;
391     }    
392   }
393
394   return PropulsionStrings;
395 }
396
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398
399 string FGPropulsion::GetPropulsionValues(void)
400 {
401   char buff[20];
402   string PropulsionValues = "";
403   bool firstime = true;
404
405   for (unsigned int i=0;i<Engines.size();i++) {
406     if (firstime)  firstime = false;
407     else           PropulsionValues += ", ";
408
409     switch(Engines[i]->GetType()) {
410     case FGEngine::etPiston:
411       PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
412       break;
413     case FGEngine::etRocket:
414       PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
415       break;
416     case FGEngine::etTurboJet:
417     case FGEngine::etTurboProp:
418     case FGEngine::etTurboShaft:
419       break;
420     }
421
422     PropulsionValues += ", ";
423
424     switch(Thrusters[i]->GetType()) {
425     case FGThruster::ttNozzle:
426       PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
427       break;
428     case FGThruster::ttRotor:
429       break;
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)));
434       break;
435     }
436   }
437
438   return PropulsionValues;
439 }
440
441 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442
443 FGColumnVector3& FGPropulsion::GetTanksCG(void)
444 {
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();
451     iTank++;
452   }
453   return vXYZtank;
454 }
455
456 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457
458 double FGPropulsion::GetTanksWeight(void)
459 {
460   double Tw = 0.0;
461
462   iTank = Tanks.begin();
463   while (iTank < Tanks.end()) {
464     Tw += (*iTank)->GetContents();
465     iTank++;
466   }
467   return Tw;
468 }
469
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471
472 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
473 {
474   double I = 0.0;
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());
478     iTank++;
479   }
480   return I;
481 }
482
483 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484
485 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
486 {
487   double I = 0.0;
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());
491     iTank++;
492   }
493   return I;
494 }
495
496 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497
498 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
499 {
500   double I = 0.0;
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());
504     iTank++;
505   }
506   return I;
507 }
508
509 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510
511 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
512 {
513   double I = 0.0;
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());
517     iTank++;
518   }
519   return I;
520 }
521
522 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
523
524 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
525 {
526   double I = 0.0;
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());
530     iTank++;
531   }
532   return I;
533 }
534
535 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
536
537 void FGPropulsion::Debug(void)
538 {
539     //TODO: Add your source code here
540 }
541