1 /*******************************************************************************
6 Purpose: Encapsulates an aircraft
9 ------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
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
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
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.
25 Further information about the GNU General Public License can also be found on
26 the world wide web at http://www.gnu.org.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 Models the aircraft reactions and forces. This class is instantiated by the
31 FGFDMExec class and scheduled as an FDM entry. LoadAircraft() is supplied with a
32 name of a valid, registered aircraft, and the data file is parsed.
35 --------------------------------------------------------------------------------
37 04/03/99 JSB Changed Aero() method to correct body axis force calculation
38 from wind vector. Fix provided by Tony Peden.
39 05/03/99 JSB Changed (for the better?) the way configurations are read in.
41 ********************************************************************************
42 COMMENTS, REFERENCES, and NOTES
43 ********************************************************************************
44 [1] Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
45 Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420 Naval Postgraduate
47 [2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
49 [3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
50 NASA-Ames", NASA CR-2497, January 1975
51 [4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
52 Wiley & Sons, 1979 ISBN 0-471-03032-5
53 [5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
54 1982 ISBN 0-471-08936-2
56 The aerodynamic coefficients used in this model are:
59 CL0 - Reference lift at zero alpha
60 CD0 - Reference drag at zero alpha
61 CDM - Drag due to Mach
62 CLa - Lift curve slope (w.r.t. alpha)
63 CDa - Drag curve slope (w.r.t. alpha)
64 CLq - Lift due to pitch rate
65 CLM - Lift due to Mach
66 CLadt - Lift due to alpha rate
68 Cmadt - Pitching Moment due to alpha rate
69 Cm0 - Reference Pitching moment at zero alpha
70 Cma - Pitching moment slope (w.r.t. alpha)
71 Cmq - Pitch damping (pitch moment due to pitch rate)
72 CmM - Pitch Moment due to Mach
75 Cyb - Side force due to sideslip
76 Cyr - Side force due to yaw rate
78 Clb - Dihedral effect (roll moment due to sideslip)
79 Clp - Roll damping (roll moment due to roll rate)
80 Clr - Roll moment due to yaw rate
81 Cnb - Weathercocking stability (yaw moment due to sideslip)
82 Cnp - Rudder adverse yaw (yaw moment due to roll rate)
83 Cnr - Yaw damping (yaw moment due to yaw rate)
86 CLDe - Lift due to elevator
87 CDDe - Drag due to elevator
88 CyDr - Side force due to rudder
89 CyDa - Side force due to aileron
91 CmDe - Pitch moment due to elevator
92 ClDa - Roll moment due to aileron
93 ClDr - Roll moment due to rudder
94 CnDr - Yaw moment due to rudder
95 CnDa - Yaw moment due to aileron
97 This class expects to be run in a directory which contains the subdirectory
98 structure shown below (where example aircraft X-15 is shown):
102 X-15.dat reset00 reset01 reset02 ...
116 F-16.dat reset00 reset01 ...
123 The file structure is arranged so that various modeled aircraft are stored in
124 their own subdirectory. Each aircraft subdirectory is named after the aircraft.
125 There should be a file present in the specific aircraft subdirectory (e.g.
126 aircraft/X-15) with the same name as the directory with a .dat appended. This
127 file contains mass properties information, name of aircraft, etc. for the
128 aircraft. In that same directory are reset files numbered starting from 0 (two
129 digit numbers), e.g. reset03. Within each reset file are values for important
130 state variables for specific flight conditions (altitude, airspeed, etc.). Also
131 within this directory are the directories containing lookup tables for the
132 stability derivatives for the aircraft.
134 ********************************************************************************
136 *******************************************************************************/
138 #include <sys/stat.h>
139 #include <sys/types.h>
142 # ifndef __BORLANDC__
143 # include <Include/compiler.h>
145 # ifdef FG_HAVE_STD_INCLUDES
154 #include "FGAircraft.h"
155 #include "FGTranslation.h"
156 #include "FGRotation.h"
157 #include "FGAtmosphere.h"
159 #include "FGFDMExec.h"
161 #include "FGPosition.h"
162 #include "FGAuxiliary.h"
163 #include "FGOutput.h"
165 /*******************************************************************************
166 ************************************ CODE **************************************
167 *******************************************************************************/
169 FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
175 for (i=0;i<6;i++) coeff_ctr[i] = 0;
179 FGAircraft::~FGAircraft(void)
183 bool FGAircraft::LoadAircraftEx(string aircraft_path, string engine_path, string fname)
190 string holding_string;
192 ifstream coeffInFile;
194 aircraftDef = aircraft_path + "/" + fname + "/" + fname + ".cfg";
195 ifstream aircraftfile(aircraftDef.c_str());
196 cout << "Reading Aircraft Configuration File: " << aircraftDef << endl;
198 numTanks = numEngines = 0;
199 numSelectedOxiTanks = numSelectedFuelTanks = 0;
201 while (!aircraftfile.fail()) {
202 holding_string.erase();
203 aircraftfile >> holding_string;
204 // if (holding_string.compare("//",0,2) != 0) {
205 if ( !(holding_string.substr(0, 2) == "//") ) {
207 if (holding_string == "AIRCRAFT") {
208 cout << "Reading in Aircraft parameters ..." << endl;
209 } else if (holding_string == "AERODYNAMICS") {
210 cout << "Reading in Aerodynamic parameters ..." << endl;
211 } else if (holding_string == "AC_NAME") {
212 aircraftfile >> AircraftName; // String with no embedded spaces
213 cout << "Aircraft Name: " << AircraftName << endl;
214 } else if (holding_string == "AC_WINGAREA") {
215 aircraftfile >> WingArea;
216 cout << "Aircraft Wing Area: " << WingArea << endl;
217 } else if (holding_string == "AC_WINGSPAN") {
218 aircraftfile >> WingSpan;
219 cout << "Aircraft WingSpan: " << WingSpan << endl;
220 } else if (holding_string == "AC_CHORD") {
221 aircraftfile >> cbar;
222 cout << "Aircraft Chord: " << cbar << endl;
223 } else if (holding_string == "AC_IXX") {
225 cout << "Aircraft Ixx: " << Ixx << endl;
226 } else if (holding_string == "AC_IYY") {
228 cout << "Aircraft Iyy: " << Iyy << endl;
229 } else if (holding_string == "AC_IZZ") {
231 cout << "Aircraft Izz: " << Izz << endl;
232 } else if (holding_string == "AC_IXZ") {
234 cout << "Aircraft Ixz: " << Ixz << endl;
235 } else if (holding_string == "AC_EMPTYWT") {
236 aircraftfile >> EmptyWeight;
237 EmptyMass = EmptyWeight / GRAVITY;
238 cout << "Aircraft Empty Weight: " << EmptyWeight << endl;
239 } else if (holding_string == "AC_CGLOC") {
240 aircraftfile >> Xcg >> Ycg >> Zcg;
241 cout << "Aircraft C.G.: " << Xcg << " " << Ycg << " " << Zcg << endl;
242 } else if (holding_string == "AC_EYEPTLOC") {
243 aircraftfile >> Xep >> Yep >> Zep;
244 cout << "Pilot Eyepoint: " << Xep << " " << Yep << " " << Zep << endl;
245 } else if (holding_string == "AC_TANK") {
246 Tank[numTanks] = new FGTank(aircraftfile);
247 switch(Tank[numTanks]->GetType()) {
249 numSelectedFuelTanks++;
250 cout << "Reading in Fuel Tank #" << numSelectedFuelTanks << " parameters ..." << endl;
252 case FGTank::ttOXIDIZER:
253 numSelectedOxiTanks++;
254 cout << "Reading in Oxidizer Tank #" << numSelectedOxiTanks << " parameters ..." << endl;
259 } else if (holding_string == "AC_ENGINE") {
262 cout << "Reading in " << tag << " Engine parameters ..." << endl;
263 Engine[numEngines] = new FGEngine(FDMExec, engine_path, tag, numEngines);
266 } else if (holding_string == "}") {
268 } else if (holding_string == "{") {
270 } else if (holding_string == "LIFT") {
272 cout << " Lift Coefficients ..." << endl;
274 streampos gpos = aircraftfile.tellg();
276 if ( !(tag == "}") ) {
277 aircraftfile.seekg(gpos);
278 Coeff[LiftCoeff][coeff_ctr[LiftCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
279 coeff_ctr[LiftCoeff]++;
281 cout << " None found ..." << endl;
284 } else if (holding_string == "DRAG") {
286 cout << " Drag Coefficients ..." << endl;
288 streampos gpos = aircraftfile.tellg();
290 if ( !(tag == "}") ) {
291 aircraftfile.seekg(gpos);
292 Coeff[DragCoeff][coeff_ctr[DragCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
293 coeff_ctr[DragCoeff]++;
295 cout << " None found ..." << endl;
298 } else if (holding_string == "SIDE") {
300 cout << " Side Coefficients ..." << endl;
302 streampos gpos = aircraftfile.tellg();
304 if ( !(tag == "}") ) {
305 aircraftfile.seekg(gpos);
306 Coeff[SideCoeff][coeff_ctr[SideCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
307 coeff_ctr[SideCoeff]++;
309 cout << " None found ..." << endl;
312 } else if (holding_string == "ROLL") {
314 cout << " Roll Coefficients ..." << endl;
316 streampos gpos = aircraftfile.tellg();
318 if ( !(tag == "}") ) {
319 aircraftfile.seekg(gpos);
320 Coeff[RollCoeff][coeff_ctr[RollCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
321 coeff_ctr[RollCoeff]++;
323 cout << " None found ..." << endl;
326 } else if (holding_string == "PITCH") {
328 cout << " Pitch Coefficients ..." << endl;
330 streampos gpos = aircraftfile.tellg();
332 if ( !(tag == "}") ) {
333 aircraftfile.seekg(gpos);
334 Coeff[PitchCoeff][coeff_ctr[PitchCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
335 coeff_ctr[PitchCoeff]++;
337 cout << " None found ..." << endl;
340 } else if (holding_string == "YAW") {
342 cout << " Yaw Coefficients ..." << endl;
344 streampos gpos = aircraftfile.tellg();
346 if ( !(tag == "}") ) {
347 aircraftfile.seekg(gpos);
348 Coeff[YawCoeff][coeff_ctr[YawCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
349 coeff_ctr[YawCoeff]++;
351 cout << " None found ..." << endl;
358 aircraftfile.getline(scratch, 127);
361 cout << "End of Configuration File Parsing." << endl;
366 bool FGAircraft::Run(void)
368 if (!FGModel::Run()) { // if false then execute this Run()
371 for (int i = 0; i < 3; i++) Forces[i] = Moments[i] = 0.0;
375 FProp(); FAero(); FGear(); FMass();
376 MProp(); MAero(); MGear(); MMass();
379 } else { // skip Run() execution this time
385 void FGAircraft::MassChange()
387 // UPDATE TANK CONTENTS
389 // For each engine, cycle through the tanks and draw an equal amount of
390 // fuel (or oxidizer) from each active tank. The needed amount of fuel is
391 // determined by the engine in the FGEngine class. If more fuel is needed
392 // than is available in the tank, then that amount is considered a shortage,
393 // and will be drawn from the next tank. If the engine cannot be fed what it
394 // needs, it will be considered to be starved, and will shut down.
396 float Oshortage, Fshortage;
398 for (int e=0; e<numEngines; e++) {
399 Fshortage = Oshortage = 0.0;
400 for (int t=0; t<numTanks; t++) {
401 switch(Engine[e]->GetType()) {
402 case FGEngine::etRocket:
404 switch(Tank[t]->GetType()) {
406 if (Tank[t]->GetSelected()) {
407 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
408 numSelectedFuelTanks)*(dt*rate) + Fshortage);
411 case FGTank::ttOXIDIZER:
412 if (Tank[t]->GetSelected()) {
413 Oshortage = Tank[t]->Reduce((Engine[e]->CalcOxidizerNeed()/
414 numSelectedOxiTanks)*(dt*rate) + Oshortage);
420 case FGEngine::etPiston:
421 case FGEngine::etTurboJet:
422 case FGEngine::etTurboProp:
424 if (Tank[t]->GetSelected()) {
425 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
426 numSelectedFuelTanks)*(dt*rate) + Fshortage);
431 if ((Fshortage <= 0.0) || (Oshortage <= 0.0)) Engine[e]->SetStarved();
432 else Engine[e]->SetStarved(false);
435 Weight = EmptyWeight;
436 for (int t=0; t<numTanks; t++)
437 Weight += Tank[t]->GetContents();
439 Mass = Weight / GRAVITY;
443 void FGAircraft::FAero(void)
447 F[0] = F[1] = F[2] = 0.0;
449 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
450 for (int ctr=0; ctr < coeff_ctr[axis_ctr]; ctr++)
451 F[axis_ctr] += Coeff[axis_ctr][ctr]->TotalValue();
453 Forces[0] += F[DragCoeff]*cos(alpha)*cos(beta) - F[SideCoeff]*cos(alpha)*sin(beta) - F[LiftCoeff]*sin(alpha);
454 Forces[1] += F[DragCoeff]*sin(beta) + F[SideCoeff]*cos(beta);
455 Forces[2] += F[DragCoeff]*sin(alpha)*cos(beta) - F[SideCoeff]*sin(alpha)*sin(beta) + F[LiftCoeff]*cos(alpha);
459 void FGAircraft::FGear(void)
467 void FGAircraft::FMass(void)
469 Forces[0] += -GRAVITY*sin(tht) * Mass;
470 Forces[1] += GRAVITY*sin(phi)*cos(tht) * Mass;
471 Forces[2] += GRAVITY*cos(phi)*cos(tht) * Mass;
475 void FGAircraft::FProp(void)
477 for (int i=0;i<numEngines;i++) {
478 Forces[0] += Engine[i]->CalcThrust();
483 void FGAircraft::MAero(void)
487 for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
488 for (ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++) {
489 Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->TotalValue();
495 void FGAircraft::MGear(void)
503 void FGAircraft::MMass(void)
508 void FGAircraft::MProp(void)
513 void FGAircraft::GetState(void)
517 alpha = Translation->Getalpha();
518 beta = Translation->Getbeta();
519 phi = Rotation->Getphi();
520 tht = Rotation->Gettht();
521 psi = Rotation->Getpsi();
525 void FGAircraft::PutState(void)