]> git.mxchange.org Git - flightgear.git/blob - src/FDM/YASim/FGFDM.cpp
Export the gear compression to the property tree.
[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 "SimpleJet.hpp"
8 #include "Gear.hpp"
9 #include "Atmosphere.hpp"
10 #include "PropEngine.hpp"
11 #include "Propeller.hpp"
12 #include "PistonEngine.hpp"
13
14 #include "FGFDM.hpp"
15 namespace yasim {
16
17 // Some conversion factors
18 static const float KTS2MPS = 0.514444444444;
19 static const float FT2M = 0.3048;
20 static const float DEG2RAD = 0.0174532925199;
21 static const float RPM2RAD = 0.10471975512;
22 static const float LBS2N = 4.44822;
23 static const float LBS2KG = 0.45359237;
24 static const float CM2GALS = 264.172037284;
25 static const float HP2W = 745.700;
26 static const float INHG2PA = 3386.389;
27 static const float K2DEGF = 1.8;
28 static const float CIN2CM = 1.6387064e-5;
29
30 // Stubs, so that this can be compiled without the FlightGear
31 // binary.  What's the best way to handle this?
32
33 //     float fgGetFloat(char* name, float def) { return 0; }
34 //     void fgSetFloat(char* name, float val) {}
35
36 FGFDM::FGFDM()
37 {
38     _nextEngine = 0;
39 }
40
41 FGFDM::~FGFDM()
42 {
43     int i;
44     for(i=0; i<_axes.size(); i++) {
45         AxisRec* a = (AxisRec*)_axes.get(i);
46         delete[] a->name;
47         delete a;
48     }
49     for(i=0; i<_thrusters.size(); i++) {
50         EngRec* er = (EngRec*)_thrusters.get(i);
51         delete[] er->prefix;
52         delete er->eng;
53         delete er;
54     }
55     for(i=0; i<_weights.size(); i++) {
56         WeightRec* wr = (WeightRec*)_weights.get(i);
57         delete[] wr->prop;
58         delete wr;
59     }
60     for(i=0; i<_controlProps.size(); i++)
61         delete (PropOut*)_controlProps.get(i);
62 }
63
64 void FGFDM::iterate(float dt)
65 {
66     getExternalInput(dt);
67     _airplane.iterate(dt);
68     setOutputProperties();
69 }
70
71 Airplane* FGFDM::getAirplane()
72 {
73     return &_airplane;
74 }
75
76 void FGFDM::init()
77 {
78     // Allows the user to start with something other than full fuel
79     _airplane.setFuelFraction(fgGetFloat("/sim/fuel-fraction", 1));
80
81     // This has a nasty habit of being false at startup.  That's not
82     // good.
83     fgSetBool("/controls/gear-down", true);
84 }
85
86 // Not the worlds safest parser.  But it's short & sweet.
87 void FGFDM::startElement(const char* name, const XMLAttributes &atts)
88 {
89     XMLAttributes* a = (XMLAttributes*)&atts;
90     float v[3];
91     char buf[64];
92
93     if(eq(name, "airplane")) {
94         _airplane.setWeight(attrf(a, "mass") * LBS2KG);
95     } else if(eq(name, "approach")) {
96         float spd = attrf(a, "speed") * KTS2MPS;
97         float alt = attrf(a, "alt", 0) * FT2M;
98         float aoa = attrf(a, "aoa", 0) * DEG2RAD;
99         _airplane.setApproach(spd, alt, aoa);
100         _cruiseCurr = false;
101     } else if(eq(name, "cruise")) {
102         float spd = attrf(a, "speed") * KTS2MPS;
103         float alt = attrf(a, "alt") * FT2M;
104         _airplane.setCruise(spd, alt);
105         _cruiseCurr = true;
106     } else if(eq(name, "cockpit")) {
107         v[0] = attrf(a, "x");
108         v[1] = attrf(a, "y");
109         v[2] = attrf(a, "z");
110         _airplane.setPilotPos(v);
111     } else if(eq(name, "wing")) {
112         _airplane.setWing(parseWing(a, name));
113     } else if(eq(name, "hstab")) {
114         _airplane.setTail(parseWing(a, name));
115     } else if(eq(name, "vstab")) {
116         _airplane.addVStab(parseWing(a, name));
117     } else if(eq(name, "propeller")) {
118         parsePropeller(a);
119     } else if(eq(name, "thruster")) {
120         SimpleJet* j = new SimpleJet();
121         _currObj = j;
122         v[0] = attrf(a, "x"); v[1] = attrf(a, "y"); v[2] = attrf(a, "z");
123         j->setPosition(v);
124         _airplane.addThruster(j, 0, v);
125         v[0] = attrf(a, "vx"); v[1] = attrf(a, "vy"); v[2] = attrf(a, "vz");
126         j->setDirection(v);
127         j->setThrust(attrf(a, "thrust") * LBS2N);
128     } else if(eq(name, "jet")) {
129         Jet* j = new Jet();
130         _currObj = j;
131         v[0] = attrf(a, "x");
132         v[1] = attrf(a, "y");
133         v[2] = attrf(a, "z");
134         float mass = attrf(a, "mass") * LBS2KG;
135         j->setMaxThrust(attrf(a, "thrust") * LBS2N,
136                         attrf(a, "afterburner", 0) * LBS2N);
137         j->setVectorAngle(attrf(a, "rotate", 0) * DEG2RAD);
138
139         float n1min = attrf(a, "n1-idle", 55);
140         float n1max = attrf(a, "n1-max", 102);
141         float n2min = attrf(a, "n2-idle", 73);
142         float n2max = attrf(a, "n2-max", 103);
143         j->setRPMs(n1min, n1max, n2min, n2max);
144
145         if(a->hasAttribute("tsfc")) j->setTSFC(attrf(a, "tsfc"));
146         if(a->hasAttribute("egt"))  j->setEGT(attrf(a, "egt"));
147         if(a->hasAttribute("epr"))  j->setEPR(attrf(a, "epr"));
148         if(a->hasAttribute("exhaust-speed"))
149             j->setVMax(attrf(a, "exhaust-speed") * KTS2MPS);
150         
151         j->setPosition(v);
152         _airplane.addThruster(j, mass, v);
153         sprintf(buf, "/engines/engine[%d]", _nextEngine++);
154         EngRec* er = new EngRec();
155         er->eng = j;
156         er->prefix = dup(buf);
157         _thrusters.add(er);
158     } else if(eq(name, "gear")) {
159         Gear* g = new Gear();
160         _currObj = g;
161         v[0] = attrf(a, "x");
162         v[1] = attrf(a, "y");
163         v[2] = attrf(a, "z");
164         g->setPosition(v);
165         v[0] = 0;
166         v[1] = 0;
167         v[2] = attrf(a, "compression", 1);
168         g->setCompression(v);
169         g->setBrake(attrf(a, "skid", 0));
170         g->setStaticFriction(attrf(a, "sfric", 0.8));
171         g->setDynamicFriction(attrf(a, "dfric", 0.7));
172         if(a->hasAttribute("castering"))
173             g->setCastering(true);
174         _airplane.addGear(g);
175     } else if(eq(name, "fuselage")) {
176         float b[3];
177         v[0] = attrf(a, "ax");
178         v[1] = attrf(a, "ay");
179         v[2] = attrf(a, "az");
180         b[0] = attrf(a, "bx");
181         b[1] = attrf(a, "by");
182         b[2] = attrf(a, "bz");
183         float taper = attrf(a, "taper", 1);
184         float mid = attrf(a, "midpoint", 0.5);
185         _airplane.addFuselage(v, b, attrf(a, "width"), taper, mid);
186     } else if(eq(name, "tank")) {
187         v[0] = attrf(a, "x");
188         v[1] = attrf(a, "y");
189         v[2] = attrf(a, "z");
190         float density = 6.0; // gasoline, in lbs/gal
191         if(a->hasAttribute("jet")) density = 6.72; 
192         density *= LBS2KG*CM2GALS;
193         _airplane.addTank(v, attrf(a, "capacity") * LBS2KG, density);
194     } else if(eq(name, "ballast")) {
195         v[0] = attrf(a, "x");
196         v[1] = attrf(a, "y");
197         v[2] = attrf(a, "z");
198         _airplane.addBallast(v, attrf(a, "mass") * LBS2KG);
199     } else if(eq(name, "weight")) {
200         parseWeight(a);
201     } else if(eq(name, "stall")) {
202         Wing* w = (Wing*)_currObj;
203         w->setStall(attrf(a, "aoa") * DEG2RAD);
204         w->setStallWidth(attrf(a, "width", 2) * DEG2RAD);
205         w->setStallPeak(attrf(a, "peak", 1.5));
206     } else if(eq(name, "flap0")) {
207         ((Wing*)_currObj)->setFlap0(attrf(a, "start"), attrf(a, "end"),
208                                     attrf(a, "lift"), attrf(a, "drag"));
209     } else if(eq(name, "flap1")) {
210         ((Wing*)_currObj)->setFlap1(attrf(a, "start"), attrf(a, "end"),
211                                     attrf(a, "lift"), attrf(a, "drag"));
212     } else if(eq(name, "slat")) {
213         ((Wing*)_currObj)->setSlat(attrf(a, "start"), attrf(a, "end"),
214                                    attrf(a, "aoa"), attrf(a, "drag"));
215     } else if(eq(name, "spoiler")) {
216         ((Wing*)_currObj)->setSpoiler(attrf(a, "start"), attrf(a, "end"),
217                                       attrf(a, "lift"), attrf(a, "drag"));
218     } else if(eq(name, "actionpt")) {
219         v[0] = attrf(a, "x");
220         v[1] = attrf(a, "y");
221         v[2] = attrf(a, "z");
222         ((Thruster*)_currObj)->setPosition(v);
223     } else if(eq(name, "dir")) {
224         v[0] = attrf(a, "x");
225         v[1] = attrf(a, "y");
226         v[2] = attrf(a, "z");
227         ((Thruster*)_currObj)->setDirection(v);
228     } else if(eq(name, "control-setting")) {
229         // A cruise or approach control setting
230         const char* axis = a->getValue("axis");
231         float value = attrf(a, "value", 0);
232         if(_cruiseCurr)
233             _airplane.addCruiseControl(parseAxis(axis), value);
234         else
235             _airplane.addApproachControl(parseAxis(axis), value);
236     } else if(eq(name, "control-input")) {
237
238         // A mapping of input property to a control
239         int axis = parseAxis(a->getValue("axis"));
240         int control = parseOutput(a->getValue("control"));
241         int opt = 0;
242         opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0;
243         opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0;
244         opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0;
245         
246         ControlMap* cm = _airplane.getControlMap();
247         if(a->hasAttribute("src0")) {
248                            cm->addMapping(axis, control, _currObj, opt,
249                            attrf(a, "src0"), attrf(a, "src1"), 
250                            attrf(a, "dst0"), attrf(a, "dst1"));
251         } else {
252             cm->addMapping(axis, control, _currObj, opt);
253         }
254     } else if(eq(name, "control-output")) {
255         // A property output for a control on the current object
256         ControlMap* cm = _airplane.getControlMap();
257         int type = parseOutput(a->getValue("control"));
258         int handle = cm->getOutputHandle(_currObj, type);
259
260         PropOut* p = new PropOut();
261         p->prop = fgGetNode(a->getValue("prop"), true);
262         p->handle = handle;
263         p->type = type;
264         p->left = !(a->hasAttribute("side") &&
265                         eq("right", a->getValue("side")));
266         p->min = attrf(a, "min", cm->rangeMin(type));
267         p->max = attrf(a, "max", cm->rangeMax(type));
268         _controlProps.add(p);
269
270     } else if(eq(name, "control-speed")) {
271         ControlMap* cm = _airplane.getControlMap();
272         int type = parseOutput(a->getValue("control"));
273         int handle = cm->getOutputHandle(_currObj, type);
274         float time = attrf(a, "transition-time", 0);
275         
276         cm->setTransitionTime(handle, time);
277     } else {
278         *(int*)0=0; // unexpected tag, boom
279     }
280 }
281
282 void FGFDM::getExternalInput(float dt)
283 {
284     // The control axes
285     ControlMap* cm = _airplane.getControlMap();
286     cm->reset();
287     int i;
288     for(i=0; i<_axes.size(); i++) {
289         AxisRec* a = (AxisRec*)_axes.get(i);
290         float val = fgGetFloat(a->name, 0);
291         cm->setInput(a->handle, val);
292     }
293     cm->applyControls(dt);
294
295     // Weights
296     for(i=0; i<_weights.size(); i++) {
297         WeightRec* wr = (WeightRec*)_weights.get(i);
298         _airplane.setWeight(wr->handle, fgGetFloat(wr->prop));
299     }
300 }
301
302 void FGFDM::setOutputProperties()
303 {
304     char buf[256];
305     int i;
306
307     ControlMap* cm = _airplane.getControlMap();
308     for(i=0; i<_controlProps.size(); i++) {
309         PropOut* p = (PropOut*)_controlProps.get(i);
310         float val = (p->left
311                      ? cm->getOutput(p->handle)
312                      : cm->getOutputR(p->handle));
313         float rmin = cm->rangeMin(p->type);
314         float rmax = cm->rangeMax(p->type);
315         float frac = (val - rmin) / (rmax - rmin);
316         val = frac*(p->max - p->min) + p->min;
317         p->prop->setFloatValue(val);
318     }
319
320     float fuelDensity = 718.95; // default to gasoline: ~6 lb/gal
321     for(i=0; i<_airplane.numTanks(); i++) {
322         fuelDensity = _airplane.getFuelDensity(i);
323         sprintf(buf, "/consumables/fuel/tank[%d]/level-gal_us", i);
324         fgSetFloat(buf, CM2GALS*_airplane.getFuel(i)/fuelDensity);
325     }
326
327     for(i=0; i<_thrusters.size(); i++) {
328         EngRec* er = (EngRec*)_thrusters.get(i);
329         Thruster* t = er->eng;
330
331         sprintf(buf, "%s/fuel-flow-gph", er->prefix);
332         fgSetFloat(buf, (t->getFuelFlow()/fuelDensity) * 3600 * CM2GALS);
333
334         if(t->getPropEngine()) {
335             PropEngine* p = t->getPropEngine();
336
337             sprintf(buf, "%s/rpm", er->prefix);
338             fgSetFloat(buf, p->getOmega() / RPM2RAD);
339         }
340
341         if(t->getPistonEngine()) {
342             PistonEngine* p = t->getPistonEngine();
343             
344             sprintf(buf, "%s/mp-osi", er->prefix);
345             fgSetFloat(buf, p->getMP() * (1/INHG2PA));
346
347             sprintf(buf, "%s/egt-degf", er->prefix);
348             fgSetFloat(buf, p->getEGT() * K2DEGF + 459.4);
349         }
350
351         if(t->getJet()) {
352             Jet* j = t->getJet();
353
354             sprintf(buf, "%s/n1", er->prefix);
355             fgSetFloat(buf, j->getN1());
356
357             sprintf(buf, "%s/n2", er->prefix);
358             fgSetFloat(buf, j->getN2());
359
360             sprintf(buf, "%s/epr", er->prefix);
361             fgSetFloat(buf, j->getEPR());
362
363             sprintf(buf, "%s/egt-degf", er->prefix);
364             fgSetFloat(buf, j->getEGT() * K2DEGF + 459.4);
365         }
366     }
367 }
368
369 Wing* FGFDM::parseWing(XMLAttributes* a, const char* type)
370 {
371     Wing* w = new Wing();
372
373     float defDihed = 0;
374     if(eq(type, "vstab"))
375         defDihed = 90;
376     else
377         w->setMirror(true);
378
379     float pos[3];
380     pos[0] = attrf(a, "x");
381     pos[1] = attrf(a, "y");
382     pos[2] = attrf(a, "z");
383     w->setBase(pos);
384
385     w->setLength(attrf(a, "length"));
386     w->setChord(attrf(a, "chord"));
387     w->setSweep(attrf(a, "sweep", 0) * DEG2RAD);
388     w->setTaper(attrf(a, "taper", 1));
389     w->setDihedral(attrf(a, "dihedral", defDihed) * DEG2RAD);
390     w->setCamber(attrf(a, "camber", 0));
391     w->setIncidence(attrf(a, "incidence", 0) * DEG2RAD);
392
393     float effect = attrf(a, "effectiveness", 1);
394     w->setDragScale(w->getDragScale()*effect);
395
396     _currObj = w;
397     return w;
398 }
399
400 void FGFDM::parsePropeller(XMLAttributes* a)
401 {
402     float cg[3];
403     cg[0] = attrf(a, "x");
404     cg[1] = attrf(a, "y");
405     cg[2] = attrf(a, "z");
406     float mass = attrf(a, "mass") * LBS2KG;
407     float moment = attrf(a, "moment");
408     float radius = attrf(a, "radius");
409     float speed = attrf(a, "cruise-speed") * KTS2MPS;
410     float omega = attrf(a, "cruise-rpm") * RPM2RAD;
411     float power = attrf(a, "cruise-power") * HP2W;
412     float rho = Atmosphere::getStdDensity(attrf(a, "cruise-alt") * FT2M);
413
414     // Hack, fix this pronto:
415     float engP = attrf(a, "eng-power") * HP2W;
416     float engS = attrf(a, "eng-rpm") * RPM2RAD;
417
418     Propeller* prop = new Propeller(radius, speed, omega, rho, power);
419     PistonEngine* eng = new PistonEngine(engP, engS);
420     PropEngine* thruster = new PropEngine(prop, eng, moment);
421     _airplane.addThruster(thruster, mass, cg);
422
423     if(a->hasAttribute("displacement"))
424         eng->setDisplacement(attrf(a, "displacement") * CIN2CM);
425
426     if(a->hasAttribute("compression"))
427         eng->setCompression(attrf(a, "compression"));        
428
429     if(a->hasAttribute("turbo-mul")) {
430         float mul = attrf(a, "turbo-mul");
431         float mp = attrf(a, "wastegate-mp", 1e6) * INHG2PA;
432         eng->setTurboParams(mul, mp);
433     }
434
435     if(a->hasAttribute("takeoff-power")) {
436         float power0 = attrf(a, "takeoff-power") * HP2W;
437         float omega0 = attrf(a, "takeoff-rpm") * RPM2RAD;
438         prop->setTakeoff(omega0, power0);
439     }
440
441     if(a->hasAttribute("max-rpm")) {
442         float max = attrf(a, "max-rpm") * RPM2RAD;
443         float min = attrf(a, "min-rpm") * RPM2RAD;
444         thruster->setVariableProp(min, max);
445     }
446
447     char buf[64];
448     sprintf(buf, "/engines/engine[%d]", _nextEngine++);
449     EngRec* er = new EngRec();
450     er->eng = thruster;
451     er->prefix = dup(buf);
452     _thrusters.add(er);
453
454     _currObj = thruster;
455 }
456
457 // Turns a string axis name into an integer for use by the
458 // ControlMap.  Creates a new axis if this one hasn't been defined
459 // yet.
460 int FGFDM::parseAxis(const char* name)
461 {
462     int i;
463     for(i=0; i<_axes.size(); i++) {
464         AxisRec* a = (AxisRec*)_axes.get(i);
465         if(eq(a->name, name))
466             return a->handle;
467     }
468
469     // Not there, make a new one.
470     AxisRec* a = new AxisRec();
471     a->name = dup(name);
472     a->handle = _airplane.getControlMap()->newInput();
473     _axes.add(a);
474     return a->handle;
475 }
476
477 int FGFDM::parseOutput(const char* name)
478 {
479     if(eq(name, "THROTTLE"))  return ControlMap::THROTTLE;
480     if(eq(name, "MIXTURE"))   return ControlMap::MIXTURE;
481     if(eq(name, "STARTER"))   return ControlMap::STARTER;
482     if(eq(name, "MAGNETOS"))  return ControlMap::MAGNETOS;
483     if(eq(name, "ADVANCE"))   return ControlMap::ADVANCE;
484     if(eq(name, "REHEAT"))    return ControlMap::REHEAT;
485     if(eq(name, "BOOST"))     return ControlMap::BOOST;
486     if(eq(name, "VECTOR"))    return ControlMap::VECTOR;
487     if(eq(name, "PROP"))      return ControlMap::PROP;
488     if(eq(name, "BRAKE"))     return ControlMap::BRAKE;
489     if(eq(name, "STEER"))     return ControlMap::STEER;
490     if(eq(name, "EXTEND"))    return ControlMap::EXTEND;
491     if(eq(name, "INCIDENCE")) return ControlMap::INCIDENCE;
492     if(eq(name, "FLAP0"))     return ControlMap::FLAP0;
493     if(eq(name, "FLAP1"))     return ControlMap::FLAP1;
494     if(eq(name, "SLAT"))      return ControlMap::SLAT;
495     if(eq(name, "SPOILER"))   return ControlMap::SPOILER;
496     *(int*)0=0;
497 }
498
499 void FGFDM::parseWeight(XMLAttributes* a)
500 {
501     WeightRec* wr = new WeightRec();
502
503     float v[3];
504     v[0] = attrf(a, "x");
505     v[1] = attrf(a, "y");
506     v[2] = attrf(a, "z");
507
508     wr->prop = dup(a->getValue("mass-prop"));
509     wr->size = attrf(a, "size", 0);
510     wr->handle = _airplane.addWeight(v, wr->size);
511
512     _weights.add(wr);
513 }
514
515 bool FGFDM::eq(const char* a, const char* b)
516 {
517     // Figure it out for yourself. :)
518     while(*a && *b && *a == *b) { a++; b++; }
519     return !(*a || *b);
520 }
521
522 char* FGFDM::dup(const char* s)
523 {
524     int len=0;
525     while(s[len++]);
526     char* s2 = new char[len+1];
527     char* p = s2;
528     while((*p++ = *s++));
529     s2[len] = 0;
530     return s2;
531 }
532
533 int FGFDM::attri(XMLAttributes* atts, char* attr)
534 {
535     if(!atts->hasAttribute(attr)) *(int*)0=0; // boom
536     return attri(atts, attr, 0);
537 }
538
539 int FGFDM::attri(XMLAttributes* atts, char* attr, int def)
540 {
541     const char* val = atts->getValue(attr);
542     if(val == 0) return def;
543     else         return atol(val);
544 }
545
546 float FGFDM::attrf(XMLAttributes* atts, char* attr)
547 {
548     if(!atts->hasAttribute(attr)) *(int*)0=0; // boom
549     return attrf(atts, attr, 0);
550 }
551
552 float FGFDM::attrf(XMLAttributes* atts, char* attr, float def)
553 {
554     const char* val = atts->getValue(attr);
555     if(val == 0) return def;
556     else         return (float)atof(val);    
557 }
558
559 }; // namespace yasim