]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGPropulsion.cpp
Syncing with most recent 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 {
67   Name = "FGPropulsion";
68   numSelectedFuelTanks = numSelectedOxiTanks = 0;
69   numTanks = numEngines = numThrusters = 0;
70   numOxiTanks = numFuelTanks = 0;
71
72   if (debug_lvl & 2) cout << "Instantiated: " << Name << endl;
73 }
74
75 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76
77 FGPropulsion::~FGPropulsion()
78 {
79   for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
80   Engines.clear();
81   if (debug_lvl & 2) cout << "Destroyed:    FGPropulsion" << endl;
82 }
83
84 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85
86 bool FGPropulsion::Run(void)
87 {
88   double PowerAvailable;
89   dt = State->Getdt();
90
91   vForces.InitMatrix();
92   vMoments.InitMatrix();
93
94   if (!FGModel::Run()) {
95     for (unsigned int i=0; i<numEngines; i++) {
96       Thrusters[i]->SetdeltaT(dt*rate);
97       PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
98       Thrusters[i]->Calculate(PowerAvailable);
99       vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
100       vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
101     }
102     return false;
103   } else {
104     return true;
105   }
106 }
107
108 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109
110 bool FGPropulsion::GetSteadyState(void)
111 {
112   double PowerAvailable;
113   double currentThrust = 0, lastThrust=-1;
114   dt = State->Getdt();
115   int steady_count,j=0;
116   bool steady=false;
117
118   vForces.InitMatrix();
119   vMoments.InitMatrix();
120
121   if (!FGModel::Run()) {
122     for (unsigned int i=0; i<numEngines; i++) {
123       Engines[i]->SetTrimMode(true);
124       Thrusters[i]->SetdeltaT(dt*rate);
125       steady=false;
126       steady_count=0;
127       while (!steady && j < 6000) {
128         PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
129         lastThrust = currentThrust;
130         currentThrust = Thrusters[i]->Calculate(PowerAvailable);
131         if(fabs(lastThrust-currentThrust) < 0.0001) {
132           steady_count++;
133           if(steady_count > 120) { steady=true; }
134         } else {
135           steady_count=0;
136         }
137         j++;    
138       }
139       vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
140       vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
141       Engines[i]->SetTrimMode(false);
142     }
143
144     return false;
145   } else {
146     return true;
147   }
148 }
149
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151
152
153 bool FGPropulsion::ICEngineStart(void)
154 {
155   double PowerAvailable;
156   int j;
157   dt = State->Getdt();
158
159   vForces.InitMatrix();
160   vMoments.InitMatrix();
161     
162   for (unsigned int i=0; i<numEngines; i++) {
163     Engines[i]->SetTrimMode(true);
164     Thrusters[i]->SetdeltaT(dt*rate);
165     j=0;
166     while (!Engines[i]->GetRunning() && j < 2000) {
167       PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
168       Thrusters[i]->Calculate(PowerAvailable);
169       j++;    
170     }
171     vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
172     vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
173     Engines[i]->SetTrimMode(false);
174   }
175   return true;
176 }
177
178 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179
180 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
181 {
182   string token, fullpath;
183   string engineFileName, engType;
184   string thrusterFileName, thrType;
185   string parameter;
186   string enginePath = FDMExec->GetEnginePath();
187   double xLoc, yLoc, zLoc, Pitch, Yaw;
188   double P_Factor = 0, Sense = 0.0;
189   int Feed;
190   bool ThrottleAdded = false;
191
192 # ifndef macintosh
193       fullpath = enginePath + "/";
194 # else
195       fullpath = enginePath + ";";
196 # endif
197
198   AC_cfg->GetNextConfigLine();
199
200   while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
201
202     if (token == "AC_ENGINE") {                   // ============ READING ENGINES
203
204       engineFileName = AC_cfg->GetValue("FILE");
205
206       if (debug_lvl > 0) cout << "\n    Reading engine from file: " << fullpath
207                                                 + engineFileName + ".xml"<< endl;
208       FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
209
210       if (Eng_cfg.IsOpen()) {
211         Eng_cfg.GetNextConfigLine();
212         engType = Eng_cfg.GetValue();
213
214         FCS->AddThrottle();
215         ThrottleAdded = true;
216
217         if (engType == "FG_ROCKET") {
218           Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
219         } else if (engType == "FG_PISTON") {
220           Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
221         } else if (engType == "FG_TURBOJET") {
222           Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));
223         } else if (engType == "FG_TURBOSHAFT") {
224           Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));
225         } else if (engType == "FG_TURBOPROP") {
226           Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));
227         } else {
228           cerr << fgred << "    Unrecognized engine type: " << underon << engType
229                     << underoff << " found in config file." << fgdef << endl;
230           return false;
231         }
232
233         AC_cfg->GetNextConfigLine();
234         while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
235           *AC_cfg >> token;
236           if      (token == "XLOC")  { *AC_cfg >> xLoc; }
237           else if (token == "YLOC")  { *AC_cfg >> yLoc; }
238           else if (token == "ZLOC")  { *AC_cfg >> zLoc; }
239           else if (token == "PITCH") { *AC_cfg >> Pitch;}
240           else if (token == "YAW")   { *AC_cfg >> Yaw;}
241           else if (token == "FEED")  {
242             *AC_cfg >> Feed;
243             Engines[numEngines]->AddFeedTank(Feed);
244             if (debug_lvl > 0) cout << "      Feed tank: " << Feed << endl;
245           } else cerr << "Unknown identifier: " << token << " in engine file: "
246                                                         << engineFileName << endl;
247         }
248
249         if (debug_lvl > 0)  {
250           cout << "      X = " << xLoc << endl;
251           cout << "      Y = " << yLoc << endl;
252           cout << "      Z = " << zLoc << endl;
253           cout << "      Pitch = " << Pitch << endl;
254           cout << "      Yaw = " << Yaw << endl;
255         }
256
257         Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
258         Engines[numEngines]->SetEngineNumber(numEngines);
259         numEngines++;
260
261       } else {
262
263         cerr << fgred << "\n  Could not read engine config file: " << underon <<
264                     fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
265         return false;
266       }
267
268     } else if (token == "AC_TANK") {              // ============== READING TANKS
269
270       if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
271       Tanks.push_back(new FGTank(AC_cfg));
272       switch(Tanks[numTanks]->GetType()) {
273       case FGTank::ttFUEL:
274         numSelectedFuelTanks++;
275         numFuelTanks++;
276         break;
277       case FGTank::ttOXIDIZER:
278         numSelectedOxiTanks++;
279         numOxiTanks++;
280         break;
281       }
282
283       numTanks++;
284
285     } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
286
287       thrusterFileName = AC_cfg->GetValue("FILE");
288
289       if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
290                                     fullpath + thrusterFileName + ".xml" << endl;
291       FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
292
293       if (Thruster_cfg.IsOpen()) {
294         Thruster_cfg.GetNextConfigLine();
295         thrType = Thruster_cfg.GetValue();
296
297         if (thrType == "FG_PROPELLER") {
298           Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
299         } else if (thrType == "FG_NOZZLE") {
300           Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));
301         }
302
303         AC_cfg->GetNextConfigLine();
304         while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
305           *AC_cfg >> token;
306           if (token == "XLOC") *AC_cfg >> xLoc;
307           else if (token == "YLOC") *AC_cfg >> yLoc;
308           else if (token == "ZLOC") *AC_cfg >> zLoc;
309           else if (token == "PITCH") *AC_cfg >> Pitch;
310           else if (token == "YAW") *AC_cfg >> Yaw;
311           else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
312           else if (token == "SENSE")   *AC_cfg >> Sense;
313           else cerr << "Unknown identifier: " << token << " in engine file: "
314                                                         << engineFileName << endl;
315         }
316
317         Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
318         Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
319         if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
320           ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
321           cout << "      P-Factor: " << P_Factor << endl;
322           ((FGPropeller*)Thrusters[numThrusters])->SetSense(Sense);
323           cout << "      Sense: " << Sense <<  endl;
324         }
325         Thrusters[numThrusters]->SetdeltaT(dt*rate);
326         Thrusters[numThrusters]->SetThrusterNumber(numThrusters);
327         numThrusters++;
328
329       } else {
330         cerr << "Could not read thruster config file: " << fullpath
331                                                 + thrusterFileName + ".xml" << endl;
332         return false;
333       }
334
335     }
336     AC_cfg->GetNextConfigLine();
337   }
338
339   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
340
341   return true;
342 }
343
344 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345
346 string FGPropulsion::GetPropulsionStrings(void)
347 {
348   string PropulsionStrings = "";
349   bool firstime = true;
350   char buffer[5];
351
352   for (unsigned int i=0;i<Engines.size();i++) {
353     if (firstime)  firstime = false;
354     else           PropulsionStrings += ", ";
355
356     sprintf(buffer, "%d", i);
357
358     switch(Engines[i]->GetType()) {
359     case FGEngine::etPiston:
360       PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
361       break;
362     case FGEngine::etRocket:
363       PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
364       break;
365     case FGEngine::etTurboJet:
366     case FGEngine::etTurboProp:
367     case FGEngine::etTurboShaft:
368       break;
369     default:
370       PropulsionStrings += "INVALID ENGINE TYPE";
371       break;
372     }
373
374     PropulsionStrings += ", ";
375
376     switch(Thrusters[i]->GetType()) {
377     case FGThruster::ttNozzle:
378       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
379       break;
380     case FGThruster::ttRotor:
381       break;
382     case FGThruster::ttPropeller:
383       PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
384       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
385       PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
386       break;
387     default:
388       PropulsionStrings += "INVALID THRUSTER TYPE";
389       break;
390     }    
391   }
392
393   return PropulsionStrings;
394 }
395
396 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397
398 string FGPropulsion::GetPropulsionValues(void)
399 {
400   char buff[20];
401   string PropulsionValues = "";
402   bool firstime = true;
403
404   for (unsigned int i=0;i<Engines.size();i++) {
405     if (firstime)  firstime = false;
406     else           PropulsionValues += ", ";
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