]> git.mxchange.org Git - flightgear.git/blob - src/FDM/YASim/FGFDM.cpp
Tweaks to fix directory change.
[flightgear.git] / src / FDM / YASim / FGFDM.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include <Main/fg_props.hxx>
5
6 #include "Jet.hpp"
7 #include "Gear.hpp"
8 #include "Atmosphere.hpp"
9 #include "PropEngine.hpp"
10 #include "Propeller.hpp"
11 #include "PistonEngine.hpp"
12
13 #include "FGFDM.hpp"
14 namespace yasim {
15
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;
26
27 // Stubs, so that this can be compiled without the FlightGear
28 // binary.  What's the best way to handle this?
29
30 //     float fgGetFloat(char* name, float def) { return 0; }
31 //     void fgSetFloat(char* name, float val) {}
32
33 FGFDM::FGFDM()
34 {
35     _nextEngine = 0;
36 }
37
38 FGFDM::~FGFDM()
39 {
40     for(int i=0; i<_axes.size(); i++) {
41         AxisRec* a = (AxisRec*)_axes.get(i);
42         delete[] a->name;
43         delete a;
44     }
45     for(int i=0; i<_pistons.size(); i++) {
46         EngRec* er = (EngRec*)_pistons.get(i);
47         delete[] er->prefix;
48         delete (PropEngine*)er->eng;
49         delete er;
50     }
51     for(int i=0; i<_jets.size(); i++) {
52         EngRec* er = (EngRec*)_pistons.get(i);
53         delete[] er->prefix;
54         delete (Jet*)er->eng;
55         delete er;
56     }
57     for(int i=0; i<_weights.size(); i++) {
58         WeightRec* wr = (WeightRec*)_weights.get(i);
59         delete[] wr->prop;
60         delete wr;
61     }
62     
63 }
64
65 void FGFDM::iterate(float dt)
66 {
67     getExternalInput(dt);
68     _airplane.iterate(dt);
69     setOutputProperties();
70 }
71
72 Airplane* FGFDM::getAirplane()
73 {
74     return &_airplane;
75 }
76
77 void FGFDM::init()
78 {
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");
91
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);
95
96     // Allows the user to start with something other than full fuel
97     _airplane.setFuelFraction(fgGetFloat("/yasim/fuel-fraction", 1));
98
99     // This has a nasty habit of being false at startup.  That's not
100     // good.
101     fgSetBool("/controls/gear-down", true);
102 }
103
104 // Not the worlds safest parser.  But it's short & sweet.
105 void FGFDM::startElement(const char* name, const XMLAttributes &atts)
106 {
107     XMLAttributes* a = (XMLAttributes*)&atts;
108     float v[3];
109     char buf[64];
110
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);
118         _cruiseCurr = false;
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);
123         _cruiseCurr = true;
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")) {
136         parsePropeller(a);
137     } else if(eq(name, "jet")) {
138         Jet* j = new Jet();
139         _currObj = j;
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);
146         j->setPosition(v);
147         _airplane.addThruster(j, mass, v);
148         sprintf(buf, "/engines/engine[%d]", _nextEngine++);
149         EngRec* er = new EngRec();
150         er->eng = j;
151         er->prefix = dup(buf);
152         _jets.add(er);
153     } else if(eq(name, "gear")) {
154         Gear* g = new Gear();
155         _currObj = g;
156         v[0] = attrf(a, "x");
157         v[1] = attrf(a, "y");
158         v[2] = attrf(a, "z");
159         g->setPosition(v);
160         v[0] = 0;
161         v[1] = 0;
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")) {
169         float b[3];
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")) {
191         parseWeight(a);
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");
224             int opt = 0;
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),
229                                                   parseOutput(output),
230                                                   _currObj,
231                                                   opt);
232         } else {
233             // assert: must be under a "cruise" or "approach" tag
234             float value = attrf(a, "value", 0);
235             if(_cruiseCurr)
236                 _airplane.addCruiseControl(parseAxis(axis), value);
237             else
238                 _airplane.addApproachControl(parseAxis(axis), value);
239         }
240     } else {
241         *(int*)0=0; // unexpected tag, boom
242     }
243 }
244
245 void FGFDM::getExternalInput(float dt)
246 {
247     // The control axes
248     ControlMap* cm = _airplane.getControlMap();
249     cm->reset();
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);
254     }
255     cm->applyControls();
256
257     // Weights
258     for(int i=0; i<_weights.size(); i++) {
259         WeightRec* wr = (WeightRec*)_weights.get(i);
260         _airplane.setWeight(wr->handle, fgGetFloat(wr->prop));
261     }
262
263     // Gear state
264     _airplane.setGearState(fgGetBool("/controls/gear-down"), dt);
265 }
266
267 void FGFDM::setOutputProperties()
268 {
269     char buf[256];
270     for(int i=0; i<_airplane.numTanks(); i++) {
271         sprintf(buf, "/consumables/fuel/tank[%d]/level-gal_us", i);
272         fgSetFloat(buf,
273                    CM2GALS*_airplane.getFuel(i)/_airplane.getFuelDensity(i));
274     }
275
276     for(int i=0; i<_pistons.size(); i++) {
277         EngRec* er = (EngRec*)_pistons.get(i);
278         PropEngine* p = (PropEngine*)er->eng;
279
280         sprintf(buf, "%s/rpm", er->prefix);
281         fgSetFloat(buf, p->getOmega() * (30/3.15149265358979));
282
283         sprintf(buf, "%s/fuel-flow-gph", er->prefix);
284         fgSetFloat(buf, p->getFuelFlow() * (3600*2.2/5)); // FIXME, wrong
285     }
286
287     for(int i=0; i<_jets.size(); i++) {
288         EngRec* er = (EngRec*)_jets.get(i);
289         Jet* j = (Jet*)er->eng;
290         
291         sprintf(buf, "%s/fuel-flow-gph", er->prefix);
292         fgSetFloat(buf, j->getFuelFlow() * (3600*2.2/6)); // FIXME, wrong
293     }
294 }
295
296 Wing* FGFDM::parseWing(XMLAttributes* a, const char* type)
297 {
298     Wing* w = new Wing();
299
300     float defDihed = 0;
301     if(eq(type, "vstab"))
302         defDihed = 90;
303     else
304         w->setMirror(true);
305
306     float pos[3];
307     pos[0] = attrf(a, "x");
308     pos[1] = attrf(a, "y");
309     pos[2] = attrf(a, "z");
310     w->setBase(pos);
311
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);
319
320     float effect = attrf(a, "effectiveness", 1);
321     w->setDragScale(w->getDragScale()*effect);
322
323     _currObj = w;
324     return w;
325 }
326
327 void FGFDM::parsePropeller(XMLAttributes* a)
328 {
329     float cg[3];
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);
340
341     // Hack, fix this pronto:
342     float engP = attrf(a, "eng-power") * HP2W;
343     float engS = attrf(a, "eng-rpm") * RPM2RAD;
344
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);
349
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);
354     }
355
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);
360     }
361
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);
366     }
367
368     char buf[64];
369     sprintf(buf, "/engines/engine[%d]", _nextEngine++);
370     EngRec* er = new EngRec();
371     er->eng = thruster;
372     er->prefix = dup(buf);
373     _pistons.add(er);
374
375     _currObj = thruster;
376 }
377
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
380 // yet.
381 int FGFDM::parseAxis(const char* name)
382 {
383     for(int i=0; i<_axes.size(); i++) {
384         AxisRec* a = (AxisRec*)_axes.get(i);
385         if(eq(a->name, name))
386             return a->handle;
387     }
388
389     // Not there, make a new one.
390     AxisRec* a = new AxisRec();
391     a->name = dup(name);
392     a->handle = _airplane.getControlMap()->newInput();
393     _axes.add(a);
394     return a->handle;
395 }
396
397 int FGFDM::parseOutput(const char* name)
398 {
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;
412     // error here...
413     return -1;
414 }
415
416 void FGFDM::parseWeight(XMLAttributes* a)
417 {
418     WeightRec* wr = new WeightRec();
419
420     float v[3];
421     v[0] = attrf(a, "x");
422     v[1] = attrf(a, "y");
423     v[2] = attrf(a, "z");
424
425     wr->prop = dup(a->getValue("mass-prop"));
426     wr->size = attrf(a, "size", 0);
427     wr->handle = _airplane.addWeight(v, wr->size);
428
429     _weights.add(wr);
430 }
431
432 bool FGFDM::eq(const char* a, const char* b)
433 {
434     // Figure it out for yourself. :)
435     while(*a && *b && *a++ == *b++);
436     return !(*a || *b);
437 }
438
439 char* FGFDM::dup(const char* s)
440 {
441     int len=0;
442     while(s[len++]);
443     char* s2 = new char[len+1];
444     char* p = s2;
445     while((*p++ = *s++));
446     s2[len] = 0;
447     return s2;
448 }
449
450 int FGFDM::attri(XMLAttributes* atts, char* attr)
451 {
452     if(!atts->hasAttribute(attr)) *(int*)0=0; // boom
453     return attri(atts, attr, 0);
454 }
455
456 int FGFDM::attri(XMLAttributes* atts, char* attr, int def)
457 {
458     const char* val = atts->getValue(attr);
459     if(val == 0) return def;
460     else         return atol(val);
461 }
462
463 float FGFDM::attrf(XMLAttributes* atts, char* attr)
464 {
465     if(!atts->hasAttribute(attr)) *(int*)0=0; // boom
466     return attrf(atts, attr, 0);
467 }
468
469 float FGFDM::attrf(XMLAttributes* atts, char* attr, float def)
470 {
471     const char* val = atts->getValue(attr);
472     if(val == 0) return def;
473     else         return (float)atof(val);    
474 }
475
476 }; // namespace yasim