]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGPropulsion.cpp
JSBSim sync.
[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       steady_count=0;
129       while (!steady && j < 6000) {
130         PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
131         lastThrust = currentThrust;
132         currentThrust = Thrusters[i]->Calculate(PowerAvailable);
133         if(fabs(lastThrust-currentThrust) < 0.0001) {
134           steady_count++;
135           if(steady_count > 120) { steady=true; }
136         } else {
137           steady_count=0;
138         }
139         j++;    
140       }
141       Forces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
142       Moments += Thrusters[i]->GetMoments();     // sum body frame moments
143       Engines[i]->SetTrimMode(false);
144     }
145
146     return false;
147   } else {
148     return true;
149   }
150 }
151
152 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153
154
155 bool FGPropulsion::ICEngineStart(void)
156 {
157   double PowerAvailable;
158   int j;
159   dt = State->Getdt();
160
161   Forces.InitMatrix();
162   Moments.InitMatrix();
163     
164   for (unsigned int i=0; i<numEngines; i++) {
165     Engines[i]->SetTrimMode(true);
166     Thrusters[i]->SetdeltaT(dt*rate);
167     j=0;
168     while (!Engines[i]->GetRunning() && j < 2000) {
169       PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
170       Thrusters[i]->Calculate(PowerAvailable);
171       j++;    
172     }
173     Forces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
174     Moments += Thrusters[i]->GetMoments();     // sum body frame moments
175     Engines[i]->SetTrimMode(false);
176   }
177   return true;
178 }
179
180 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181
182 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
183 {
184   string token, fullpath;
185   string engineFileName, engType;
186   string thrusterFileName, thrType;
187   string parameter;
188   string enginePath = FDMExec->GetEnginePath();
189   double xLoc, yLoc, zLoc, Pitch, Yaw;
190   double P_Factor = 0, Sense = 0.0;
191   int Feed;
192   bool ThrottleAdded = false;
193
194 # ifndef macintosh
195       fullpath = enginePath + "/";
196 # else
197       fullpath = enginePath + ";";
198 # endif
199
200   AC_cfg->GetNextConfigLine();
201
202   while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
203
204     if (token == "AC_ENGINE") {                   // ============ READING ENGINES
205
206       engineFileName = AC_cfg->GetValue("FILE");
207
208       if (debug_lvl > 0) cout << "\n    Reading engine from file: " << fullpath
209                                                 + engineFileName + ".xml"<< endl;
210       FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
211
212       if (Eng_cfg.IsOpen()) {
213         Eng_cfg.GetNextConfigLine();
214         engType = Eng_cfg.GetValue();
215
216         FCS->AddThrottle();
217         ThrottleAdded = true;
218
219         if (engType == "FG_ROCKET") {
220           Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
221         } else if (engType == "FG_PISTON") {
222           Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
223         } else if (engType == "FG_TURBOJET") {
224           Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
225         } else if (engType == "FG_TURBOSHAFT") {
226           Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
227         } else if (engType == "FG_TURBOPROP") {
228           Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
229         } else {
230           cerr << fgred << "    Unrecognized engine type: " << underon << engType
231                     << underoff << " found in config file." << fgdef << endl;
232           return false;
233         }
234
235         AC_cfg->GetNextConfigLine();
236         while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
237           *AC_cfg >> token;
238           if      (token == "XLOC")  { *AC_cfg >> xLoc; }
239           else if (token == "YLOC")  { *AC_cfg >> yLoc; }
240           else if (token == "ZLOC")  { *AC_cfg >> zLoc; }
241           else if (token == "PITCH") { *AC_cfg >> Pitch;}
242           else if (token == "YAW")   { *AC_cfg >> Yaw;}
243           else if (token == "FEED")  {
244             *AC_cfg >> Feed;
245             Engines[numEngines]->AddFeedTank(Feed);
246             if (debug_lvl > 0) cout << "      Feed tank: " << Feed << endl;
247           } else cerr << "Unknown identifier: " << token << " in engine file: "
248                                                         << engineFileName << endl;
249         }
250
251         if (debug_lvl > 0)  {
252           cout << "      X = " << xLoc << endl;
253           cout << "      Y = " << yLoc << endl;
254           cout << "      Z = " << zLoc << endl;
255           cout << "      Pitch = " << Pitch << endl;
256           cout << "      Yaw = " << Yaw << endl;
257         }
258         
259         Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
260         Engines[numEngines]->SetEngineNumber(numEngines);
261         numEngines++;
262
263       } else {
264
265         cerr << fgred << "\n  Could not read engine config file: " << underon <<
266                     fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
267         return false;
268       }
269
270     } else if (token == "AC_TANK") {              // ============== READING TANKS
271
272       if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
273       Tanks.push_back(new FGTank(AC_cfg));
274       switch(Tanks[numTanks]->GetType()) {
275       case FGTank::ttFUEL:
276         numSelectedFuelTanks++;
277         numFuelTanks++;
278         break;
279       case FGTank::ttOXIDIZER:
280         numSelectedOxiTanks++;
281         numOxiTanks++;
282         break;
283       }
284
285       numTanks++;
286
287     } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
288
289       thrusterFileName = AC_cfg->GetValue("FILE");
290
291       if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
292                                     fullpath + thrusterFileName + ".xml" << endl;
293       FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
294
295       if (Thruster_cfg.IsOpen()) {
296         Thruster_cfg.GetNextConfigLine();
297         thrType = Thruster_cfg.GetValue();
298
299         if (thrType == "FG_PROPELLER") {
300           Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
301         } else if (thrType == "FG_NOZZLE") {
302           Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
303         }
304
305         AC_cfg->GetNextConfigLine();
306         while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
307           *AC_cfg >> token;
308           if (token == "XLOC") *AC_cfg >> xLoc;
309           else if (token == "YLOC") *AC_cfg >> yLoc;
310           else if (token == "ZLOC") *AC_cfg >> zLoc;
311           else if (token == "PITCH") *AC_cfg >> Pitch;
312           else if (token == "YAW") *AC_cfg >> Yaw;
313           else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
314           else if (token == "SENSE")   *AC_cfg >> Sense;
315           else cerr << "Unknown identifier: " << token << " in engine file: "
316                                                         << engineFileName << endl;
317         }
318
319         Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
320         Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
321         if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
322           ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
323           cout << "      P-Factor: " << P_Factor << endl;
324           ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
325           cout << "      Sense: " << Sense <<  endl;
326         }
327         Thrusters[numThrusters]->SetdeltaT(dt*rate);
328
329         numThrusters++;
330
331       } else {
332         cerr << "Could not read thruster config file: " << fullpath
333                                                 + thrusterFileName + ".xml" << endl;
334         return false;
335       }
336
337     }
338     AC_cfg->GetNextConfigLine();
339   }
340
341   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
342
343   return true;
344 }
345
346 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347
348 string FGPropulsion::GetPropulsionStrings(void)
349 {
350   string PropulsionStrings = "";
351   bool firstime = true;
352   char buffer[5];
353
354   for (unsigned int i=0;i<Engines.size();i++) {
355     if (firstime)  firstime = false;
356     else           PropulsionStrings += ", ";
357
358     sprintf(buffer, "%d", i);
359
360     switch(Engines[i]->GetType()) {
361     case FGEngine::etPiston:
362       PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
363       break;
364     case FGEngine::etRocket:
365       PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
366       break;
367     case FGEngine::etTurboJet:
368     case FGEngine::etTurboProp:
369     case FGEngine::etTurboShaft:
370       break;
371     default:
372       PropulsionStrings += "INVALID ENGINE TYPE";
373       break;
374     }
375
376     PropulsionStrings += ", ";
377
378     switch(Thrusters[i]->GetType()) {
379     case FGThruster::ttNozzle:
380       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
381       break;
382     case FGThruster::ttRotor:
383       break;
384     case FGThruster::ttPropeller:
385       PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
386       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
387       PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
388       break;
389     default:
390       PropulsionStrings += "INVALID THRUSTER TYPE";
391       break;
392     }    
393   }
394
395   return PropulsionStrings;
396 }
397
398 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399
400 string FGPropulsion::GetPropulsionValues(void)
401 {
402   char buff[20];
403   string PropulsionValues = "";
404   bool firstime = true;
405
406   for (unsigned int i=0;i<Engines.size();i++) {
407     if (firstime)  firstime = false;
408     else           PropulsionValues += ", ";
409
410     switch(Engines[i]->GetType()) {
411     case FGEngine::etPiston:
412       PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
413       break;
414     case FGEngine::etRocket:
415       PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
416       break;
417     case FGEngine::etTurboJet:
418     case FGEngine::etTurboProp:
419     case FGEngine::etTurboShaft:
420       break;
421     }
422
423     PropulsionValues += ", ";
424
425     switch(Thrusters[i]->GetType()) {
426     case FGThruster::ttNozzle:
427       PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
428       break;
429     case FGThruster::ttRotor:
430       break;
431     case FGThruster::ttPropeller:
432       PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
433       PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
434       PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
435       break;
436     }
437   }
438
439   return PropulsionValues;
440 }
441
442 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443
444 FGColumnVector3& FGPropulsion::GetTanksCG(void)
445 {
446   iTank = Tanks.begin();
447   vXYZtank.InitMatrix();
448   while (iTank < Tanks.end()) {
449     vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
450     vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
451     vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
452     iTank++;
453   }
454   return vXYZtank;
455 }
456
457 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458
459 double FGPropulsion::GetTanksWeight(void)
460 {
461   double Tw = 0.0;
462
463   iTank = Tanks.begin();
464   while (iTank < Tanks.end()) {
465     Tw += (*iTank)->GetContents();
466     iTank++;
467   }
468   return Tw;
469 }
470
471 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472
473 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
474 {
475   double I = 0.0;
476   iTank = Tanks.begin();
477   while (iTank < Tanks.end()) {
478     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
479     iTank++;
480   }
481   return I;
482 }
483
484 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485
486 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
487 {
488   double I = 0.0;
489   iTank = Tanks.begin();
490   while (iTank < Tanks.end()) {
491     I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
492     iTank++;
493   }
494   return I;
495 }
496
497 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498
499 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
500 {
501   double I = 0.0;
502   iTank = Tanks.begin();
503   while (iTank < Tanks.end()) {
504     I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
505     iTank++;
506   }
507   return I;
508 }
509
510 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511
512 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
513 {
514   double I = 0.0;
515   iTank = Tanks.begin();
516   while (iTank < Tanks.end()) {
517     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
518     iTank++;
519   }
520   return I;
521 }
522
523 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524
525 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
526 {
527   double I = 0.0;
528   iTank = Tanks.begin();
529   while (iTank != Tanks.end()) {
530     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
531     iTank++;
532   }
533   return I;
534 }
535
536 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
537
538 void FGPropulsion::Debug(void)
539 {
540     //TODO: Add your source code here
541 }
542