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