]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGPropulsion.cpp
Sync with latest JSBSim CVS
[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 {
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);
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 (Forces) delete Forces;
84   if (Moments) delete Moments;
85   if (debug_lvl & 2) cout << "Destroyed:    FGPropulsion" << endl;
86 }
87
88 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
90 bool FGPropulsion::Run(void) {
91   float PowerAvailable;
92   dt = State->Getdt();
93
94   Forces->InitMatrix();
95   Moments->InitMatrix();
96
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
104     }
105
106     return false;
107   } else {
108     return true;
109   }
110 }
111
112 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113
114 bool FGPropulsion::GetSteadyState(void) {
115   float PowerAvailable;
116   float currentThrust = 0, lastThrust=-1;
117   dt = State->Getdt();
118   int steady_count,j=0;
119   bool steady=false;
120
121   Forces->InitMatrix();
122   Moments->InitMatrix();
123
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       steady=false;
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   float 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()) != "/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()) != "/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         numEngines++;
258
259       } else {
260
261         cerr << "Could not read engine config file: " << fullpath
262                                                   + engineFileName + ".xml" << endl;
263         return false;
264       }
265
266     } else if (token == "AC_TANK") {              // ============== READING TANKS
267
268       if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
269       Tanks.push_back(new FGTank(AC_cfg));
270       switch(Tanks[numTanks]->GetType()) {
271       case FGTank::ttFUEL:
272         numSelectedFuelTanks++;
273         numFuelTanks++;
274         break;
275       case FGTank::ttOXIDIZER:
276         numSelectedOxiTanks++;
277         numOxiTanks++;
278         break;
279       }
280
281       numTanks++;
282
283     } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
284
285       thrusterFileName = AC_cfg->GetValue("FILE");
286
287       if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
288                                     fullpath + thrusterFileName + ".xml" << endl;
289       FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
290
291       if (Thruster_cfg.IsOpen()) {
292         Thruster_cfg.GetNextConfigLine();
293         thrType = Thruster_cfg.GetValue();
294
295         if (thrType == "FG_PROPELLER") {
296           Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
297         } else if (thrType == "FG_NOZZLE") {
298           Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
299         }
300
301         AC_cfg->GetNextConfigLine();
302         while ((token = AC_cfg->GetValue()) != "/AC_THRUSTER") {
303           *AC_cfg >> token;
304           if (token == "XLOC") *AC_cfg >> xLoc;
305           else if (token == "YLOC") *AC_cfg >> yLoc;
306           else if (token == "ZLOC") *AC_cfg >> zLoc;
307           else if (token == "PITCH") *AC_cfg >> Pitch;
308           else if (token == "YAW") *AC_cfg >> Yaw;
309           else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
310           else if (token == "SENSE")   *AC_cfg >> Sense;
311           else cerr << "Unknown identifier: " << token << " in engine file: "
312                                                         << engineFileName << endl;
313         }
314
315         Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
316         Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
317         if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
318           ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
319           cout << "      P-Factor: " << P_Factor << endl;
320           ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
321           cout << "      Sense: " << Sense <<  endl;
322         }
323         Thrusters[numThrusters]->SetdeltaT(dt*rate);
324
325         numThrusters++;
326
327       } else {
328         cerr << "Could not read thruster config file: " << fullpath
329                                                 + thrusterFileName + ".xml" << endl;
330         return false;
331       }
332
333     }
334     AC_cfg->GetNextConfigLine();
335   }
336
337   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
338
339   return true;
340 }
341
342 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343
344 string FGPropulsion::GetPropulsionStrings(void)
345 {
346   string PropulsionStrings = "";
347   bool firstime = true;
348
349   for (unsigned int i=0;i<Engines.size();i++) {
350     if (!firstime) {
351       PropulsionStrings += ", ";
352       firstime = false;
353     }
354
355     switch(Engines[i]->GetType()) {
356     case FGEngine::etPiston:
357       PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail");
358       break;
359     case FGEngine::etRocket:
360       PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress");
361       break;
362     case FGEngine::etTurboJet:
363     case FGEngine::etTurboProp:
364     case FGEngine::etTurboShaft:
365       break;
366     default:
367       PropulsionStrings += "INVALID ENGINE TYPE";
368       break;
369     }
370
371     PropulsionStrings += ", ";
372
373     switch(Thrusters[i]->GetType()) {
374     case FGThruster::ttNozzle:
375       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust");
376       break;
377     case FGThruster::ttRotor:
378       break;
379     case FGThruster::ttPropeller:
380       PropulsionStrings += (Thrusters[i]->GetName() + "_Torque, ");
381       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust, ");
382       PropulsionStrings += (Thrusters[i]->GetName() + "_RPM");
383       break;
384     default:
385       PropulsionStrings += "INVALID THRUSTER TYPE";
386       break;
387     }    
388   }
389
390   return PropulsionStrings;
391 }
392
393 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
394
395 string FGPropulsion::GetPropulsionValues(void)
396 {
397   char buff[20];
398   string PropulsionValues = "";
399   bool firstime = true;
400
401   for (unsigned int i=0;i<Engines.size();i++) {
402     if (!firstime) {
403       PropulsionValues += ", ";
404       firstime = false;
405     }
406
407     switch(Engines[i]->GetType()) {
408     case FGEngine::etPiston:
409       PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetPowerAvailable(), 10, buff)));
410       break;
411     case FGEngine::etRocket:
412       PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
413       break;
414     case FGEngine::etTurboJet:
415     case FGEngine::etTurboProp:
416     case FGEngine::etTurboShaft:
417       break;
418     }
419
420     PropulsionValues += ", ";
421
422     switch(Thrusters[i]->GetType()) {
423     case FGThruster::ttNozzle:
424       PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
425       break;
426     case FGThruster::ttRotor:
427       break;
428     case FGThruster::ttPropeller:
429       PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetTorque(), 10, buff)) + ", ");
430       PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetThrust(), 10, buff)) + ", ");
431       PropulsionValues += (string(gcvt(((FGPropeller*)Thrusters[i])->GetRPM(), 10, buff)));
432       break;
433     }
434   }
435
436   return PropulsionValues;
437 }
438
439 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
440
441 FGColumnVector3& FGPropulsion::GetTanksCG(void)
442 {
443   iTank = Tanks.begin();
444   vXYZtank.InitMatrix();
445   while (iTank < Tanks.end()) {
446     vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
447     vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
448     vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
449     iTank++;
450   }
451   return vXYZtank;
452 }
453
454 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
455
456 float FGPropulsion::GetTanksWeight(void)
457 {
458   float Tw = 0.0;
459
460   iTank = Tanks.begin();
461   while (iTank < Tanks.end()) {
462     Tw += (*iTank)->GetContents();
463     iTank++;
464   }
465   return Tw;
466 }
467
468 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469
470 float FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
471 {
472   float I = 0.0;
473   iTank = Tanks.begin();
474   while (iTank < Tanks.end()) {
475     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
476     iTank++;
477   }
478   return I;
479 }
480
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482
483 float FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
484 {
485   float I = 0.0;
486   iTank = Tanks.begin();
487   while (iTank < Tanks.end()) {
488     I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
489     iTank++;
490   }
491   return I;
492 }
493
494 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495
496 float FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
497 {
498   float I = 0.0;
499   iTank = Tanks.begin();
500   while (iTank < Tanks.end()) {
501     I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
502     iTank++;
503   }
504   return I;
505 }
506
507 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508
509 float FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
510 {
511   float I = 0.0;
512   iTank = Tanks.begin();
513   while (iTank < Tanks.end()) {
514     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
515     iTank++;
516   }
517   return I;
518 }
519
520 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521
522 float FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
523 {
524   float I = 0.0;
525   iTank = Tanks.begin();
526   while (iTank != Tanks.end()) {
527     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
528     iTank++;
529   }
530   return I;
531 }
532
533 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534
535 void FGPropulsion::Debug(void)
536 {
537     //TODO: Add your source code here
538 }
539