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, string fname)
195 struct dirent* dirEntry;
196 struct dirent* coeffdirEntry;
199 ifstream coeffInFile;
201 aircraftDef = aircraft_path + "/" + fname + "/" + fname + ".dat";
202 ifstream aircraftfile(aircraftDef.c_str());
205 aircraftfile >> AircraftName; // String with no embedded spaces
206 aircraftfile >> WingArea; // square feet
207 aircraftfile >> WingSpan; // feet
208 aircraftfile >> cbar; // feet
209 aircraftfile >> Ixx; // slug ft^2
210 aircraftfile >> Iyy; // "
211 aircraftfile >> Izz; // "
212 aircraftfile >> Ixz; // "
213 aircraftfile >> EmptyWeight; // pounds
214 EmptyMass = EmptyWeight / GRAVITY;
217 numTanks = numEngines = 0;
218 numSelectedOxiTanks = numSelectedFuelTanks = 0;
220 while ( !(tag == "EOF") ) {
221 if (tag == "CGLOC") {
222 aircraftfile >> Xcg; // inches
223 aircraftfile >> Ycg; // inches
224 aircraftfile >> Zcg; // inches
225 } else if (tag == "EYEPOINTLOC") {
226 aircraftfile >> Xep; // inches
227 aircraftfile >> Yep; // inches
228 aircraftfile >> Zep; // inches
229 } else if (tag == "TANK") {
230 Tank[numTanks] = new FGTank(aircraftfile);
231 switch(Tank[numTanks]->GetType()) {
233 numSelectedFuelTanks++;
235 case FGTank::ttOXIDIZER:
236 numSelectedOxiTanks++;
240 } else if (tag == "ENGINE") {
242 Engine[numEngines] = new FGEngine(FDMExec, engine_path, tag, numEngines);
247 aircraftfile.close();
250 // Read subdirectory for this aircraft for stability derivative lookup tables:
252 // Build up the path name to the aircraft file by appending the aircraft
253 // name to the "aircraft/" initial path. Initialize the directory entry
254 // structure dirEntry in preparation for reading through the directory.
255 // Build up a path to each file in the directory sequentially and "stat" it
256 // to see if the entry is a directory or a file. If the entry is a file, then
257 // compare it to each string in the Axis[] array to see which axis the
258 // directory represents: Lift, Drag, Side, Roll, Pitch, Yaw. When the match
259 // is found, go into that directory and search for any coefficient files.
260 // Build a new coefficient by passing the full pathname to the coefficient
261 // file to the FGCoefficient constructor.
263 // Note: axis_ctr=0 for the Lift "axis", 1 for Drag, 2 for Side force, 3 for
264 // Roll, 4 for Pitch, and 5 for Yaw. The term coeff_ctr merely keeps
265 // track of the number of coefficients registered for each of the
266 // previously mentioned axis.
268 path = aircraft_path + "/" + AircraftName + "/";
269 if (dir = opendir(path.c_str())) {
271 while (dirEntry = readdir(dir)) {
272 fullpath = path + dirEntry->d_name;
273 stat(fullpath.c_str(),&st);
274 if ((st.st_mode & S_IFMT) == S_IFDIR) {
275 for (int axis_ctr=0; axis_ctr < 6; axis_ctr++) {
276 if (dirEntry->d_name == Axis[axis_ctr]) {
277 if (coeffdir = opendir(fullpath.c_str())) {
278 while (coeffdirEntry = readdir(coeffdir)) {
279 if (coeffdirEntry->d_name[0] != '.') {
280 filename = path + Axis[axis_ctr] + "/" + coeffdirEntry->d_name;
281 stat(filename.c_str(),&st2);
282 if (st2.st_size > 6) {
283 Coeff[axis_ctr][coeff_ctr[axis_ctr]] = new FGCoefficient(FDMExec, filename);
284 coeff_ctr[axis_ctr]++;
294 cerr << "Could not open directory " << path << " for reading" << endl;
299 cerr << "Unable to open aircraft definition file " << fname << endl;
306 bool FGAircraft::Run(void)
308 if (!FGModel::Run()) { // if false then execute this Run()
311 for (int i = 0; i < 3; i++) Forces[i] = Moments[i] = 0.0;
315 FProp(); FAero(); FGear(); FMass();
316 MProp(); MAero(); MGear(); MMass();
319 } else { // skip Run() execution this time
325 void FGAircraft::MassChange()
327 // UPDATE TANK CONTENTS
329 // For each engine, cycle through the tanks and draw an equal amount of
330 // fuel (or oxidizer) from each active tank. The needed amount of fuel is
331 // determined by the engine in the FGEngine class. If more fuel is needed
332 // than is available in the tank, then that amount is considered a shortage,
333 // and will be drawn from the next tank. If the engine cannot be fed what it
334 // needs, it will be considered to be starved, and will shut down.
336 float Oshortage, Fshortage;
338 for (int e=0; e<numEngines; e++) {
339 Fshortage = Oshortage = 0.0;
340 for (int t=0; t<numTanks; t++) {
341 switch(Engine[e]->GetType()) {
342 case FGEngine::etRocket:
344 switch(Tank[t]->GetType()) {
346 if (Tank[t]->GetSelected()) {
347 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
348 numSelectedFuelTanks)*(dt*rate) + Fshortage);
351 case FGTank::ttOXIDIZER:
352 if (Tank[t]->GetSelected()) {
353 Oshortage = Tank[t]->Reduce((Engine[e]->CalcOxidizerNeed()/
354 numSelectedOxiTanks)*(dt*rate) + Oshortage);
360 case FGEngine::etPiston:
361 case FGEngine::etTurboJet:
362 case FGEngine::etTurboProp:
364 if (Tank[t]->GetSelected()) {
365 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
366 numSelectedFuelTanks)*(dt*rate) + Fshortage);
371 if ((Fshortage <= 0.0) || (Oshortage <= 0.0)) Engine[e]->SetStarved();
372 else Engine[e]->SetStarved(false);
375 Weight = EmptyWeight;
376 for (int t=0; t<numTanks; t++)
377 Weight += Tank[t]->GetContents();
379 Mass = Weight / GRAVITY;
383 void FGAircraft::FAero(void)
387 F[0] = F[1] = F[2] = 0.0;
389 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
390 for (int ctr=0; ctr < coeff_ctr[axis_ctr]; ctr++)
391 F[axis_ctr] += Coeff[axis_ctr][ctr]->Value();
393 Forces[0] += F[LiftCoeff]*sin(alpha) - F[DragCoeff]*cos(alpha) - F[SideCoeff]*sin(beta);
394 Forces[1] += F[SideCoeff]*cos(beta);
395 Forces[2] += -F[LiftCoeff]*cos(alpha) - F[DragCoeff]*sin(alpha);
399 void FGAircraft::FGear(void)
407 void FGAircraft::FMass(void)
409 Forces[0] += -GRAVITY*sin(tht) * Mass;
410 Forces[1] += GRAVITY*sin(phi)*cos(tht) * Mass;
411 Forces[2] += GRAVITY*cos(phi)*cos(tht) * Mass;
415 void FGAircraft::FProp(void)
417 for (int i=0;i<numEngines;i++) {
418 Forces[0] += Engine[i]->CalcThrust();
423 void FGAircraft::MAero(void)
425 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
426 for (int ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++)
427 Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->Value();
431 void FGAircraft::MGear(void)
439 void FGAircraft::MMass(void)
444 void FGAircraft::MProp(void)
449 void FGAircraft::GetState(void)
453 alpha = Translation->Getalpha();
454 beta = Translation->Getbeta();
455 phi = Rotation->Getphi();
456 tht = Rotation->Gettht();
457 psi = Rotation->Getpsi();
461 void FGAircraft::PutState(void)