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 *******************************************************************************/
136 #include <sys/stat.h>
137 #include <sys/types.h>
140 #include "FGAircraft.h"
141 #include "FGTranslation.h"
142 #include "FGRotation.h"
143 #include "FGAtmosphere.h"
145 #include "FGFDMExec.h"
147 #include "FGPosition.h"
148 #include "FGAuxiliary.h"
149 #include "FGOutput.h"
151 /*******************************************************************************
152 ************************************ CODE **************************************
153 *******************************************************************************/
155 FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
159 strcpy(Name,"FGAircraft");
161 for (i=0;i<6;i++) Axis[i] = (char*)malloc(7);
162 for (i=0;i<6;i++) coeff_ctr[i] = 0;
164 strcpy(Axis[LiftCoeff],"CLIFT");
165 strcpy(Axis[DragCoeff],"CDRAG");
166 strcpy(Axis[SideCoeff],"CSIDE");
167 strcpy(Axis[RollCoeff],"CROLL");
168 strcpy(Axis[PitchCoeff],"CPITCH");
169 strcpy(Axis[YawCoeff],"CYAW");
173 FGAircraft::~FGAircraft(void)
178 bool FGAircraft::LoadAircraft(char* fname)
183 char aircraftDef[256];
187 struct dirent* dirEntry;
188 struct dirent* coeffdirEntry;
191 ifstream coeffInFile;
193 sprintf(aircraftDef, "/h/curt/projects/FlightGear/Simulator/FDM/JSBsim/aircraft/%s/%s.dat", fname, fname);
194 ifstream aircraftfile(aircraftDef);
197 aircraftfile >> AircraftName; // String with no embedded spaces
198 aircraftfile >> WingArea; // square feet
199 aircraftfile >> WingSpan; // feet
200 aircraftfile >> cbar; // feet
201 aircraftfile >> Ixx; // slug ft^2
202 aircraftfile >> Iyy; // "
203 aircraftfile >> Izz; // "
204 aircraftfile >> Ixz; // "
205 aircraftfile >> EmptyWeight; // pounds
206 EmptyMass = EmptyWeight / GRAVITY;
209 numTanks = numEngines = 0;
210 numSelectedOxiTanks = numSelectedFuelTanks = 0;
212 while (strstr(tag,"EOF") == 0) {
213 if (strstr(tag,"CGLOC")) {
214 aircraftfile >> Xcg; // inches
215 aircraftfile >> Ycg; // inches
216 aircraftfile >> Zcg; // inches
217 } else if (strstr(tag,"EYEPOINTLOC")) {
218 aircraftfile >> Xep; // inches
219 aircraftfile >> Yep; // inches
220 aircraftfile >> Zep; // inches
221 } else if (strstr(tag,"TANK")) {
222 Tank[numTanks] = new FGTank(aircraftfile);
223 switch(Tank[numTanks]->GetType()) {
225 numSelectedOxiTanks++;
228 numSelectedFuelTanks++;
232 } else if (strstr(tag,"ENGINE")) {
234 Engine[numEngines] = new FGEngine(FDMExec, tag, numEngines);
239 aircraftfile.close();
242 // Read subdirectory for this aircraft for stability derivative lookup tables:
244 // Build up the path name to the aircraft file by appending the aircraft
245 // name to the "aircraft/" initial path. Initialize the directory entry
246 // structure dirEntry in preparation for reading through the directory.
247 // Build up a path to each file in the directory sequentially and "stat" it
248 // to see if the entry is a directory or a file. If the entry is a file, then
249 // compare it to each string in the Axis[] array to see which axis the
250 // directory represents: Lift, Drag, Side, Roll, Pitch, Yaw. When the match
251 // is found, go into that directory and search for any coefficient files.
252 // Build a new coefficient by passing the full pathname to the coefficient
253 // file to the FGCoefficient constructor.
255 // Note: axis_ctr=0 for the Lift "axis", 1 for Drag, 2 for Side force, 3 for
256 // Roll, 4 for Pitch, and 5 for Yaw. The term coeff_ctr merely keeps
257 // track of the number of coefficients registered for each of the
258 // previously mentioned axis.
260 sprintf(path,"/h/curt/projects/FlightGear/Simulator/FDM/JSBsim/aircraft/%s/",AircraftName);
261 if (dir = opendir(path)) {
263 while (dirEntry = readdir(dir)) {
264 sprintf(fullpath,"%s%s",path,dirEntry->d_name);
266 if ((st.st_mode & S_IFMT) == S_IFDIR) {
267 for (int axis_ctr=0; axis_ctr < 6; axis_ctr++) {
268 if (strstr(dirEntry->d_name,Axis[axis_ctr])) {
269 if (coeffdir = opendir(fullpath)) {
270 while (coeffdirEntry = readdir(coeffdir)) {
271 if (coeffdirEntry->d_name[0] != '.') {
272 sprintf(filename,"%s%s/%s",path,Axis[axis_ctr],coeffdirEntry->d_name);
274 if (st2.st_size > 6) {
275 Coeff[axis_ctr][coeff_ctr[axis_ctr]] = new FGCoefficient(FDMExec, filename);
276 coeff_ctr[axis_ctr]++;
286 cerr << "Could not open directory " << path << " for reading" << endl;
291 cerr << "Unable to open aircraft definition file " << fname << endl;
298 bool FGAircraft::Run(void)
300 if (!FGModel::Run()) { // if false then execute this Run()
303 for (int i = 0; i < 3; i++) Forces[i] = Moments[i] = 0.0;
307 FProp(); FAero(); FGear(); FMass();
308 MProp(); MAero(); MGear(); MMass();
311 } else { // skip Run() execution this time
317 void FGAircraft::MassChange()
319 // UPDATE TANK CONTENTS
321 // For each engine, cycle through the tanks and draw an equal amount of
322 // fuel (or oxidizer) from each active tank. The needed amount of fuel is
323 // determined by the engine in the FGEngine class. If more fuel is needed
324 // than is available in the tank, then that amount is considered a shortage,
325 // and will be drawn from the next tank. If the engine cannot be fed what it
326 // needs, it will be considered to be starved, and will shut down.
328 float Oshortage, Fshortage;
330 for (int e=0; e<numEngines; e++) {
331 Fshortage = Oshortage = 0.0;
332 for (int t=0; t<numTanks; t++) {
333 switch(Engine[e]->GetType()) {
335 switch(Tank[t]->GetType()) {
337 if (Tank[t]->GetSelected()) {
338 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
339 numSelectedFuelTanks)*(dt*rate) + Fshortage);
343 if (Tank[t]->GetSelected()) {
344 Oshortage = Tank[t]->Reduce((Engine[e]->CalcOxidizerNeed()/
345 numSelectedOxiTanks)*(dt*rate) + Oshortage);
350 default: // piston, turbojet, turbofan, etc.
351 if (Tank[t]->GetSelected()) {
352 Fshortage = Tank[t]->Reduce((Engine[e]->CalcFuelNeed()/
353 numSelectedFuelTanks)*(dt*rate) + Fshortage);
358 if ((Fshortage <= 0.0) || (Oshortage <= 0.0)) Engine[e]->SetStarved();
359 else Engine[e]->SetStarved(false);
362 Weight = EmptyWeight;
363 for (int t=0; t<numTanks; t++)
364 Weight += Tank[t]->GetContents();
366 Mass = Weight / GRAVITY;
370 void FGAircraft::FAero(void)
374 F[0] = F[1] = F[2] = 0.0;
376 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
377 for (int ctr=0; ctr < coeff_ctr[axis_ctr]; ctr++)
378 F[axis_ctr] += Coeff[axis_ctr][ctr]->Value();
380 Forces[0] += F[LiftCoeff]*sin(alpha) - F[DragCoeff]*cos(alpha) - F[SideCoeff]*sin(beta);
381 Forces[1] += F[SideCoeff]*cos(beta);
382 Forces[2] += -F[LiftCoeff]*cos(alpha) - F[DragCoeff]*sin(alpha);
386 void FGAircraft::FGear(void)
394 void FGAircraft::FMass(void)
396 Forces[0] += -GRAVITY*sin(tht) * Mass;
397 Forces[1] += GRAVITY*sin(phi)*cos(tht) * Mass;
398 Forces[2] += GRAVITY*cos(phi)*cos(tht) * Mass;
402 void FGAircraft::FProp(void)
404 for (int i=0;i<numEngines;i++) {
405 Forces[0] += Engine[i]->CalcThrust();
410 void FGAircraft::MAero(void)
412 for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
413 for (int ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++)
414 Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->Value();
418 void FGAircraft::MGear(void)
426 void FGAircraft::MMass(void)
431 void FGAircraft::MProp(void)
436 void FGAircraft::GetState(void)
440 alpha = Translation->Getalpha();
441 beta = Translation->Getbeta();
442 phi = Rotation->Getphi();
443 tht = Rotation->Gettht();
444 psi = Rotation->Getpsi();
448 void FGAircraft::PutState(void)