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 --------------------------------------------------------------------------------
38 ********************************************************************************
39 COMMENTS, REFERENCES, and NOTES
40 ********************************************************************************
41 [1] Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
42 Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420 Naval Postgraduate
44 [2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
46 [3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
47 NASA-Ames", NASA CR-2497, January 1975
48 [4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
49 Wiley & Sons, 1979 ISBN 0-471-03032-5
50 [5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
51 1982 ISBN 0-471-08936-2
53 The aerodynamic coefficients used in this model are:
56 CL0 - Reference lift at zero alpha
57 CD0 - Reference drag at zero alpha
58 CDM - Drag due to Mach
59 CLa - Lift curve slope (w.r.t. alpha)
60 CDa - Drag curve slope (w.r.t. alpha)
61 CLq - Lift due to pitch rate
62 CLM - Lift due to Mach
63 CLadt - Lift due to alpha rate
65 Cmadt - Pitching Moment due to alpha rate
66 Cm0 - Reference Pitching moment at zero alpha
67 Cma - Pitching moment slope (w.r.t. alpha)
68 Cmq - Pitch damping (pitch moment due to pitch rate)
69 CmM - Pitch Moment due to Mach
72 Cyb - Side force due to sideslip
73 Cyr - Side force due to yaw rate
75 Clb - Dihedral effect (roll moment due to sideslip)
76 Clp - Roll damping (roll moment due to roll rate)
77 Clr - Roll moment due to yaw rate
78 Cnb - Weathercocking stability (yaw moment due to sideslip)
79 Cnp - Rudder adverse yaw (yaw moment due to roll rate)
80 Cnr - Yaw damping (yaw moment due to yaw rate)
83 CLDe - Lift due to elevator
84 CDDe - Drag due to elevator
85 CyDr - Side force due to rudder
86 CyDa - Side force due to aileron
88 CmDe - Pitch moment due to elevator
89 ClDa - Roll moment due to aileron
90 ClDr - Roll moment due to rudder
91 CnDr - Yaw moment due to rudder
92 CnDa - Yaw moment due to aileron
94 This class expects to be run in a directory which contains the subdirectory
95 structure shown below (where example aircraft X-15 is shown):
99 X-15.dat reset00 reset01 reset02 ...
113 F-16.dat reset00 reset01 ...
120 The file structure is arranged so that various modeled aircraft are stored in
121 their own subdirectory. Each aircraft subdirectory is named after the aircraft.
122 There should be a file present in the specific aircraft subdirectory (e.g.
123 aircraft/X-15) with the same name as the directory with a .dat appended. This
124 file contains mass properties information, name of aircraft, etc. for the
125 aircraft. In that same directory are reset files numbered starting from 0 (two
126 digit numbers), e.g. reset03. Within each reset file are values for important
127 state variables for specific flight conditions (altitude, airspeed, etc.). Also
128 within this directory are the directories containing lookup tables for the
129 stability derivatives for the aircraft.
131 ********************************************************************************
133 *******************************************************************************/
135 #include <sys/stat.h>
136 #include <sys/types.h>
139 # include <Include/compiler.h>
140 # ifdef FG_HAVE_STD_INCLUDES
149 #include "FGAircraft.h"
150 #include "FGTranslation.h"
151 #include "FGRotation.h"
152 #include "FGAtmosphere.h"
154 #include "FGFDMExec.h"
156 #include "FGPosition.h"
157 #include "FGAuxiliary.h"
158 #include "FGOutput.h"
160 /*******************************************************************************
161 ************************************ CODE **************************************
162 *******************************************************************************/
164 FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
170 for (i=0;i<6;i++) coeff_ctr[i] = 0;
172 Axis[LiftCoeff] = "CLIFT";
173 Axis[DragCoeff] = "CDRAG";
174 Axis[SideCoeff] = "CSIDE";
175 Axis[RollCoeff] = "CROLL";
176 Axis[PitchCoeff] = "CPITCH";
177 Axis[YawCoeff] = "CYAW";
181 FGAircraft::~FGAircraft(void)
186 bool FGAircraft::LoadAircraft(string aircraft_path, string engine_path,
196 struct dirent* dirEntry;
197 struct dirent* coeffdirEntry;
200 ifstream coeffInFile;
202 aircraftDef = aircraft_path + "/" + fname + "/" + fname + ".dat";
203 ifstream aircraftfile(aircraftDef.c_str());
206 aircraftfile >> AircraftName; // String with no embedded spaces
207 aircraftfile >> WingArea; // square feet
208 aircraftfile >> WingSpan; // feet
209 aircraftfile >> cbar; // feet
210 aircraftfile >> Ixx; // slug ft^2
211 aircraftfile >> Iyy; // "
212 aircraftfile >> Izz; // "
213 aircraftfile >> Ixz; // "
214 aircraftfile >> EmptyWeight; // pounds
215 EmptyMass = EmptyWeight / GRAVITY;
218 numTanks = numEngines = 0;
219 numSelectedOxiTanks = numSelectedFuelTanks = 0;
221 while ( !(tag == "EOF") ) {
222 if (tag == "CGLOC") {
223 aircraftfile >> Xcg; // inches
224 aircraftfile >> Ycg; // inches
225 aircraftfile >> Zcg; // inches
226 } else if (tag == "EYEPOINTLOC") {
227 aircraftfile >> Xep; // inches
228 aircraftfile >> Yep; // inches
229 aircraftfile >> Zep; // inches
230 } else if (tag == "TANK") {
231 Tank[numTanks] = new FGTank(aircraftfile);
232 switch(Tank[numTanks]->GetType()) {
234 numSelectedFuelTanks++;
236 case FGTank::ttOXIDIZER:
237 numSelectedOxiTanks++;
241 } else if (tag == "ENGINE") {
243 Engine[numEngines] = new FGEngine(FDMExec, engine_path, tag,
249 aircraftfile.close();
252 // Read subdirectory for this aircraft for stability derivative lookup tables:
254 // Build up the path name to the aircraft file by appending the aircraft
255 // name to the "aircraft/" initial path. Initialize the directory entry
256 // structure dirEntry in preparation for reading through the directory.
257 // Build up a path to each file in the directory sequentially and "stat" it
258 // to see if the entry is a directory or a file. If the entry is a file, then
259 // compare it to each string in the Axis[] array to see which axis the
260 // directory represents: Lift, Drag, Side, Roll, Pitch, Yaw. When the match
261 // is found, go into that directory and search for any coefficient files.
262 // Build a new coefficient by passing the full pathname to the coefficient
263 // file to the FGCoefficient constructor.
265 // Note: axis_ctr=0 for the Lift "axis", 1 for Drag, 2 for Side force, 3 for
266 // Roll, 4 for Pitch, and 5 for Yaw. The term coeff_ctr merely keeps
267 // track of the number of coefficients registered for each of the
268 // previously mentioned axis.
270 path = aircraft_path + "/" + AircraftName + "/";
271 if (dir = opendir(path.c_str())) {
273 while (dirEntry = readdir(dir)) {
274 fullpath = path + dirEntry->d_name;
275 stat(fullpath.c_str(),&st);
276 if ((st.st_mode & S_IFMT) == S_IFDIR) {
277 for (int axis_ctr=0; axis_ctr < 6; axis_ctr++) {
278 if (dirEntry->d_name == Axis[axis_ctr]) {
279 if (coeffdir = opendir(fullpath.c_str())) {
280 while (coeffdirEntry = readdir(coeffdir)) {
281 if (coeffdirEntry->d_name[0] != '.') {
282 filename = path + Axis[axis_ctr] + "/" + coeffdirEntry->d_name;
283 stat(filename.c_str(),&st2);
284 if (st2.st_size > 6) {
285 Coeff[axis_ctr][coeff_ctr[axis_ctr]] = new FGCoefficient(FDMExec, filename);
286 coeff_ctr[axis_ctr]++;
296 cerr << "Could not open directory " << path << " for reading" << endl;
301 cerr << "Unable to open aircraft definition file " << fname << endl;
308 bool FGAircraft::Run(void)
310 if (!FGModel::Run()) { // if false then execute this Run()
313 for (int i = 0; i < 3; i++) Forces[i] = Moments[i] = 0.0;
317 FProp(); FAero(); FGear(); FMass();
318 MProp(); MAero(); MGear(); MMass();
321 } else { // skip Run() execution this time
327 void FGAircraft::MassChange()
329 // UPDATE TANK CONTENTS
331 // For each engine, cycle through the tanks and draw an equal amount of
332 // fuel (or oxidizer) from each active tank. The needed amount of fuel is
333 // determined by the engine in the FGEngine class. If more fuel is needed
334 // than is available in the tank, then that amount is considered a shortage,
335 // and will be drawn from the next tank. If the engine cannot be fed what it
336 // needs, it will be considered to be starved, and will shut down.
338 float Oshortage, Fshortage;
340 for (int e=0; e<numEngines; e++) {
341 Fshortage = Oshortage = 0.0;
342 for (int t=0; t<numTanks; t++) {
343 switch(Engine[e]->GetType()) {
344 case FGEngine::etRocket:
346 switch(Tank[t]->GetType()) {
348 if (Tank[t]->GetSelected()) {
349 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
350 numSelectedFuelTanks)*(dt*rate) + Fshortage);
353 case FGTank::ttOXIDIZER:
354 if (Tank[t]->GetSelected()) {
355 Oshortage = Tank[t]->Reduce((Engine[e]->CalcOxidizerNeed()/
356 numSelectedOxiTanks)*(dt*rate) + Oshortage);
362 case FGEngine::etPiston:
363 case FGEngine::etTurboJet:
364 case FGEngine::etTurboProp:
366 if (Tank[t]->GetSelected()) {
367 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
368 numSelectedFuelTanks)*(dt*rate) + Fshortage);
373 if ((Fshortage <= 0.0) || (Oshortage <= 0.0)) Engine[e]->SetStarved();
374 else Engine[e]->SetStarved(false);
377 Weight = EmptyWeight;
378 for (int t=0; t<numTanks; t++)
379 Weight += Tank[t]->GetContents();
381 Mass = Weight / GRAVITY;
385 void FGAircraft::FAero(void)
389 F[0] = F[1] = F[2] = 0.0;
391 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
392 for (int ctr=0; ctr < coeff_ctr[axis_ctr]; ctr++)
393 F[axis_ctr] += Coeff[axis_ctr][ctr]->Value();
395 Forces[0] += F[LiftCoeff]*sin(alpha) - F[DragCoeff]*cos(alpha) - F[SideCoeff]*sin(beta);
396 Forces[1] += F[SideCoeff]*cos(beta);
397 Forces[2] += -F[LiftCoeff]*cos(alpha) - F[DragCoeff]*sin(alpha);
401 void FGAircraft::FGear(void)
409 void FGAircraft::FMass(void)
411 Forces[0] += -GRAVITY*sin(tht) * Mass;
412 Forces[1] += GRAVITY*sin(phi)*cos(tht) * Mass;
413 Forces[2] += GRAVITY*cos(phi)*cos(tht) * Mass;
417 void FGAircraft::FProp(void)
419 for (int i=0;i<numEngines;i++) {
420 Forces[0] += Engine[i]->CalcThrust();
425 void FGAircraft::MAero(void)
427 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
428 for (int ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++)
429 Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->Value();
433 void FGAircraft::MGear(void)
441 void FGAircraft::MMass(void)
446 void FGAircraft::MProp(void)
451 void FGAircraft::GetState(void)
455 alpha = Translation->Getalpha();
456 beta = Translation->Getbeta();
457 phi = Rotation->Getphi();
458 tht = Rotation->Gettht();
459 psi = Rotation->Getpsi();
463 void FGAircraft::PutState(void)