4 #include <Main/fg_props.hxx>
8 #include "Atmosphere.hpp"
9 #include "PropEngine.hpp"
10 #include "Propeller.hpp"
11 #include "PistonEngine.hpp"
16 // Some conversion factors
17 static const float KTS2MPS = 0.514444444444;
18 static const float FT2M = 0.3048;
19 static const float DEG2RAD = 0.0174532925199;
20 static const float RPM2RAD = 0.10471975512;
21 static const float LBS2N = 4.44822;
22 static const float LBS2KG = 0.45359237;
23 static const float CM2GALS = 264.172037284;
24 static const float HP2W = 745.700;
25 static const float INHG2PA = 3386.389;
27 // Stubs, so that this can be compiled without the FlightGear
28 // binary. What's the best way to handle this?
30 // float fgGetFloat(char* name, float def) { return 0; }
31 // void fgSetFloat(char* name, float val) {}
40 for(int i=0; i<_axes.size(); i++) {
41 AxisRec* a = (AxisRec*)_axes.get(i);
45 for(int i=0; i<_pistons.size(); i++) {
46 EngRec* er = (EngRec*)_pistons.get(i);
48 delete (PropEngine*)er->eng;
51 for(int i=0; i<_jets.size(); i++) {
52 EngRec* er = (EngRec*)_pistons.get(i);
57 for(int i=0; i<_weights.size(); i++) {
58 WeightRec* wr = (WeightRec*)_weights.get(i);
65 void FGFDM::iterate(float dt)
68 _airplane.iterate(dt);
69 setOutputProperties();
72 Airplane* FGFDM::getAirplane()
79 // We don't want to use these ties (we set the values ourselves,
80 // and this works only for the first piston engine anyway).
81 fgUntie("/engines/engine[0]/rpm");
82 fgUntie("/engines/engine[0]/egt-degf");
83 fgUntie("/engines/engine[0]/cht-degf");
84 fgUntie("/engines/engine[0]/oil-temperature-degf");
85 fgUntie("/engines/engine[0]/mp-osi");
86 fgUntie("/engines/engine[0]/fuel-flow-gph");
87 fgUntie("/engines/engine[0]/running");
88 fgUntie("/engines/engine[0]/cranking");
89 fgUntie("/consumables/fuel/tank[0]/level-gal_us");
90 fgUntie("/consumables/fuel/tank[1]/level-gal_us");
92 // Set these to sane values. We don't support engine start yet.
93 fgSetBool("/engines/engine[0]/running", true);
94 fgSetBool("/engines/engine[0]/cranking", false);
96 // Allows the user to start with something other than full fuel
97 _airplane.setFuelFraction(fgGetFloat("/yasim/fuel-fraction", 1));
99 // This has a nasty habit of being false at startup. That's not
101 fgSetBool("/controls/gear-down", true);
104 // Not the worlds safest parser. But it's short & sweet.
105 void FGFDM::startElement(const char* name, const XMLAttributes &atts)
107 XMLAttributes* a = (XMLAttributes*)&atts;
111 if(eq(name, "airplane")) {
112 _airplane.setWeight(attrf(a, "mass") * LBS2KG);
113 } else if(eq(name, "approach")) {
114 float spd = attrf(a, "speed") * KTS2MPS;
115 float alt = attrf(a, "alt", 0) * FT2M;
116 float aoa = attrf(a, "aoa", 0) * DEG2RAD;
117 _airplane.setApproach(spd, alt, aoa);
119 } else if(eq(name, "cruise")) {
120 float spd = attrf(a, "speed") * KTS2MPS;
121 float alt = attrf(a, "alt") * FT2M;
122 _airplane.setCruise(spd, alt);
124 } else if(eq(name, "cockpit")) {
125 v[0] = attrf(a, "x");
126 v[1] = attrf(a, "y");
127 v[2] = attrf(a, "z");
128 _airplane.setPilotPos(v);
129 } else if(eq(name, "wing")) {
130 _airplane.setWing(parseWing(a, name));
131 } else if(eq(name, "hstab")) {
132 _airplane.setTail(parseWing(a, name));
133 } else if(eq(name, "vstab")) {
134 _airplane.addVStab(parseWing(a, name));
135 } else if(eq(name, "propeller")) {
137 } else if(eq(name, "jet")) {
140 v[0] = attrf(a, "x");
141 v[1] = attrf(a, "y");
142 v[2] = attrf(a, "z");
143 float mass = attrf(a, "mass") * LBS2KG;
144 j->setDryThrust(attrf(a, "thrust") * LBS2N);
145 j->setReheatThrust(attrf(a, "afterburner", 0) * LBS2N);
147 _airplane.addThruster(j, mass, v);
148 sprintf(buf, "/engines/engine[%d]", _nextEngine++);
149 EngRec* er = new EngRec();
151 er->prefix = dup(buf);
153 } else if(eq(name, "gear")) {
154 Gear* g = new Gear();
156 v[0] = attrf(a, "x");
157 v[1] = attrf(a, "y");
158 v[2] = attrf(a, "z");
162 v[2] = attrf(a, "compression", 1);
163 g->setCompression(v);
164 g->setStaticFriction(attrf(a, "sfric", 0.8));
165 g->setDynamicFriction(attrf(a, "dfric", 0.7));
166 float transitionTime = attrf(a, "retract-time", 0);
167 _airplane.addGear(g, transitionTime);
168 } else if(eq(name, "fuselage")) {
170 v[0] = attrf(a, "ax");
171 v[1] = attrf(a, "ay");
172 v[2] = attrf(a, "az");
173 b[0] = attrf(a, "bx");
174 b[1] = attrf(a, "by");
175 b[2] = attrf(a, "bz");
176 _airplane.addFuselage(v, b, attrf(a, "width"));
177 } else if(eq(name, "tank")) {
178 v[0] = attrf(a, "x");
179 v[1] = attrf(a, "y");
180 v[2] = attrf(a, "z");
181 float density = 6.0; // gasoline, in lbs/gal
182 if(a->hasAttribute("jet")) density = 6.72;
183 density *= LBS2KG/CM2GALS;
184 _airplane.addTank(v, attrf(a, "capacity") * LBS2KG, density);
185 } else if(eq(name, "ballast")) {
186 v[0] = attrf(a, "x");
187 v[1] = attrf(a, "y");
188 v[2] = attrf(a, "z");
189 _airplane.addBallast(v, attrf(a, "mass") * LBS2KG);
190 } else if(eq(name, "weight")) {
192 } else if(eq(name, "stall")) {
193 Wing* w = (Wing*)_currObj;
194 w->setStall(attrf(a, "aoa") * DEG2RAD);
195 w->setStallWidth(attrf(a, "width", 2) * DEG2RAD);
196 w->setStallPeak(attrf(a, "peak", 1.5));
197 } else if(eq(name, "flap0")) {
198 ((Wing*)_currObj)->setFlap0(attrf(a, "start"), attrf(a, "end"),
199 attrf(a, "lift"), attrf(a, "drag"));
200 } else if(eq(name, "flap1")) {
201 ((Wing*)_currObj)->setFlap1(attrf(a, "start"), attrf(a, "end"),
202 attrf(a, "lift"), attrf(a, "drag"));
203 } else if(eq(name, "slat")) {
204 ((Wing*)_currObj)->setSlat(attrf(a, "start"), attrf(a, "end"),
205 attrf(a, "aoa"), attrf(a, "drag"));
206 } else if(eq(name, "spoiler")) {
207 ((Wing*)_currObj)->setSpoiler(attrf(a, "start"), attrf(a, "end"),
208 attrf(a, "lift"), attrf(a, "drag"));
209 } else if(eq(name, "actionpt")) {
210 v[0] = attrf(a, "x");
211 v[1] = attrf(a, "y");
212 v[2] = attrf(a, "z");
213 ((Thruster*)_currObj)->setPosition(v);
214 } else if(eq(name, "dir")) {
215 v[0] = attrf(a, "x");
216 v[1] = attrf(a, "y");
217 v[2] = attrf(a, "z");
218 ((Thruster*)_currObj)->setDirection(v);
219 } else if(eq(name, "control")) {
220 const char* axis = a->getValue("axis");
221 if(a->hasAttribute("output")) {
222 // assert: output type must match _currObj type!
223 const char* output = a->getValue("output");
225 opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0;
226 opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0;
227 opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0;
228 _airplane.getControlMap()->addMapping(parseAxis(axis),
233 // assert: must be under a "cruise" or "approach" tag
234 float value = attrf(a, "value", 0);
236 _airplane.addCruiseControl(parseAxis(axis), value);
238 _airplane.addApproachControl(parseAxis(axis), value);
241 *(int*)0=0; // unexpected tag, boom
245 void FGFDM::getExternalInput(float dt)
248 ControlMap* cm = _airplane.getControlMap();
250 for(int i=0; i<_axes.size(); i++) {
251 AxisRec* a = (AxisRec*)_axes.get(i);
252 float val = fgGetFloat(a->name, 0);
253 cm->setInput(a->handle, val);
258 for(int i=0; i<_weights.size(); i++) {
259 WeightRec* wr = (WeightRec*)_weights.get(i);
260 _airplane.setWeight(wr->handle, fgGetFloat(wr->prop));
264 _airplane.setGearState(fgGetBool("/controls/gear-down"), dt);
267 void FGFDM::setOutputProperties()
270 for(int i=0; i<_airplane.numTanks(); i++) {
271 sprintf(buf, "/consumables/fuel/tank[%d]/level-gal_us", i);
273 CM2GALS*_airplane.getFuel(i)/_airplane.getFuelDensity(i));
276 for(int i=0; i<_pistons.size(); i++) {
277 EngRec* er = (EngRec*)_pistons.get(i);
278 PropEngine* p = (PropEngine*)er->eng;
280 sprintf(buf, "%s/rpm", er->prefix);
281 fgSetFloat(buf, p->getOmega() * (30/3.15149265358979));
283 sprintf(buf, "%s/fuel-flow-gph", er->prefix);
284 fgSetFloat(buf, p->getFuelFlow() * (3600*2.2/5)); // FIXME, wrong
287 for(int i=0; i<_jets.size(); i++) {
288 EngRec* er = (EngRec*)_jets.get(i);
289 Jet* j = (Jet*)er->eng;
291 sprintf(buf, "%s/fuel-flow-gph", er->prefix);
292 fgSetFloat(buf, j->getFuelFlow() * (3600*2.2/6)); // FIXME, wrong
296 Wing* FGFDM::parseWing(XMLAttributes* a, const char* type)
298 Wing* w = new Wing();
301 if(eq(type, "vstab"))
307 pos[0] = attrf(a, "x");
308 pos[1] = attrf(a, "y");
309 pos[2] = attrf(a, "z");
312 w->setLength(attrf(a, "length"));
313 w->setChord(attrf(a, "chord"));
314 w->setSweep(attrf(a, "sweep", 0) * DEG2RAD);
315 w->setTaper(attrf(a, "taper", 1));
316 w->setDihedral(attrf(a, "dihedral", defDihed) * DEG2RAD);
317 w->setCamber(attrf(a, "camber", 0));
318 w->setIncidence(attrf(a, "incidence", 0) * DEG2RAD);
320 float effect = attrf(a, "effectiveness", 1);
321 w->setDragScale(w->getDragScale()*effect);
327 void FGFDM::parsePropeller(XMLAttributes* a)
330 cg[0] = attrf(a, "x");
331 cg[1] = attrf(a, "y");
332 cg[2] = attrf(a, "z");
333 float mass = attrf(a, "mass") * LBS2KG;
334 float moment = attrf(a, "moment");
335 float radius = attrf(a, "radius");
336 float speed = attrf(a, "cruise-speed") * KTS2MPS;
337 float omega = attrf(a, "cruise-rpm") * RPM2RAD;
338 float power = attrf(a, "cruise-power") * HP2W;
339 float rho = Atmosphere::getStdDensity(attrf(a, "cruise-alt") * FT2M);
341 // Hack, fix this pronto:
342 float engP = attrf(a, "eng-power") * HP2W;
343 float engS = attrf(a, "eng-rpm") * RPM2RAD;
345 Propeller* prop = new Propeller(radius, speed, omega, rho, power);
346 PistonEngine* eng = new PistonEngine(engP, engS);
347 PropEngine* thruster = new PropEngine(prop, eng, moment);
348 _airplane.addThruster(thruster, mass, cg);
350 if(a->hasAttribute("turbo-mul")) {
351 float mul = attrf(a, "turbo-mul");
352 float mp = attrf(a, "wastegate-mp", 1e6) * INHG2PA;
353 eng->setTurboParams(mul, mp);
356 if(a->hasAttribute("takeoff-power")) {
357 float power0 = attrf(a, "takeoff-power") * HP2W;
358 float omega0 = attrf(a, "takeoff-rpm") * RPM2RAD;
359 prop->setTakeoff(omega0, power0);
362 if(a->hasAttribute("max-rpm")) {
363 float max = attrf(a, "max-rpm") * RPM2RAD;
364 float min = attrf(a, "min-rpm") * RPM2RAD;
365 thruster->setVariableProp(min, max);
369 sprintf(buf, "/engines/engine[%d]", _nextEngine++);
370 EngRec* er = new EngRec();
372 er->prefix = dup(buf);
378 // Turns a string axis name into an integer for use by the
379 // ControlMap. Creates a new axis if this one hasn't been defined
381 int FGFDM::parseAxis(const char* name)
383 for(int i=0; i<_axes.size(); i++) {
384 AxisRec* a = (AxisRec*)_axes.get(i);
385 if(eq(a->name, name))
389 // Not there, make a new one.
390 AxisRec* a = new AxisRec();
392 a->handle = _airplane.getControlMap()->newInput();
397 int FGFDM::parseOutput(const char* name)
399 if(eq(name, "THROTTLE")) return ControlMap::THROTTLE;
400 if(eq(name, "MIXTURE")) return ControlMap::MIXTURE;
401 if(eq(name, "ADVANCE")) return ControlMap::ADVANCE;
402 if(eq(name, "REHEAT")) return ControlMap::REHEAT;
403 if(eq(name, "PROP")) return ControlMap::PROP;
404 if(eq(name, "BRAKE")) return ControlMap::BRAKE;
405 if(eq(name, "STEER")) return ControlMap::STEER;
406 if(eq(name, "EXTEND")) return ControlMap::EXTEND;
407 if(eq(name, "INCIDENCE")) return ControlMap::INCIDENCE;
408 if(eq(name, "FLAP0")) return ControlMap::FLAP0;
409 if(eq(name, "FLAP1")) return ControlMap::FLAP1;
410 if(eq(name, "SLAT")) return ControlMap::SLAT;
411 if(eq(name, "SPOILER")) return ControlMap::SPOILER;
416 void FGFDM::parseWeight(XMLAttributes* a)
418 WeightRec* wr = new WeightRec();
421 v[0] = attrf(a, "x");
422 v[1] = attrf(a, "y");
423 v[2] = attrf(a, "z");
425 wr->prop = dup(a->getValue("mass-prop"));
426 wr->size = attrf(a, "size", 0);
427 wr->handle = _airplane.addWeight(v, wr->size);
432 bool FGFDM::eq(const char* a, const char* b)
434 // Figure it out for yourself. :)
435 while(*a && *b && *a++ == *b++);
439 char* FGFDM::dup(const char* s)
443 char* s2 = new char[len+1];
445 while((*p++ = *s++));
450 int FGFDM::attri(XMLAttributes* atts, char* attr)
452 if(!atts->hasAttribute(attr)) *(int*)0=0; // boom
453 return attri(atts, attr, 0);
456 int FGFDM::attri(XMLAttributes* atts, char* attr, int def)
458 const char* val = atts->getValue(attr);
459 if(val == 0) return def;
460 else return atol(val);
463 float FGFDM::attrf(XMLAttributes* atts, char* attr)
465 if(!atts->hasAttribute(attr)) *(int*)0=0; // boom
466 return attrf(atts, attr, 0);
469 float FGFDM::attrf(XMLAttributes* atts, char* attr, float def)
471 const char* val = atts->getValue(attr);
472 if(val == 0) return def;
473 else return (float)atof(val);
476 }; // namespace yasim