]> git.mxchange.org Git - flightgear.git/blob - src/FDM/YASim/FGFDM.cpp
Somewhere along the line in the recent changes some std::cout were
[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         SG_LOG(SG_FLIGHT,SG_ALERT,"Unexpected tag '"
279                << name << "' found in YASim aircraft description");
280         exit(1);
281     }
282 }
283
284 void FGFDM::getExternalInput(float dt)
285 {
286     // The control axes
287     ControlMap* cm = _airplane.getControlMap();
288     cm->reset();
289     int i;
290     for(i=0; i<_axes.size(); i++) {
291         AxisRec* a = (AxisRec*)_axes.get(i);
292         float val = fgGetFloat(a->name, 0);
293         cm->setInput(a->handle, val);
294     }
295     cm->applyControls(dt);
296
297     // Weights
298     for(i=0; i<_weights.size(); i++) {
299         WeightRec* wr = (WeightRec*)_weights.get(i);
300         _airplane.setWeight(wr->handle, fgGetFloat(wr->prop));
301     }
302 }
303
304 void FGFDM::setOutputProperties()
305 {
306     char buf[256];
307     int i;
308
309     ControlMap* cm = _airplane.getControlMap();
310     for(i=0; i<_controlProps.size(); i++) {
311         PropOut* p = (PropOut*)_controlProps.get(i);
312         float val = (p->left
313                      ? cm->getOutput(p->handle)
314                      : cm->getOutputR(p->handle));
315         float rmin = cm->rangeMin(p->type);
316         float rmax = cm->rangeMax(p->type);
317         float frac = (val - rmin) / (rmax - rmin);
318         val = frac*(p->max - p->min) + p->min;
319         p->prop->setFloatValue(val);
320     }
321
322     float fuelDensity = 718.95; // default to gasoline: ~6 lb/gal
323     for(i=0; i<_airplane.numTanks(); i++) {
324         fuelDensity = _airplane.getFuelDensity(i);
325         sprintf(buf, "/consumables/fuel/tank[%d]/level-gal_us", i);
326         fgSetFloat(buf, CM2GALS*_airplane.getFuel(i)/fuelDensity);
327     }
328
329     for(i=0; i<_thrusters.size(); i++) {
330         EngRec* er = (EngRec*)_thrusters.get(i);
331         Thruster* t = er->eng;
332
333         sprintf(buf, "%s/fuel-flow-gph", er->prefix);
334         fgSetFloat(buf, (t->getFuelFlow()/fuelDensity) * 3600 * CM2GALS);
335
336         if(t->getPropEngine()) {
337             PropEngine* p = t->getPropEngine();
338
339             sprintf(buf, "%s/rpm", er->prefix);
340             fgSetFloat(buf, p->getOmega() / RPM2RAD);
341         }
342
343         if(t->getPistonEngine()) {
344             PistonEngine* p = t->getPistonEngine();
345             
346             sprintf(buf, "%s/mp-osi", er->prefix);
347             fgSetFloat(buf, p->getMP() * (1/INHG2PA));
348
349             sprintf(buf, "%s/egt-degf", er->prefix);
350             fgSetFloat(buf, p->getEGT() * K2DEGF + 459.4);
351         }
352
353         if(t->getJet()) {
354             Jet* j = t->getJet();
355
356             sprintf(buf, "%s/n1", er->prefix);
357             fgSetFloat(buf, j->getN1());
358
359             sprintf(buf, "%s/n2", er->prefix);
360             fgSetFloat(buf, j->getN2());
361
362             sprintf(buf, "%s/epr", er->prefix);
363             fgSetFloat(buf, j->getEPR());
364
365             sprintf(buf, "%s/egt-degf", er->prefix);
366             fgSetFloat(buf, j->getEGT() * K2DEGF + 459.4);
367         }
368     }
369 }
370
371 Wing* FGFDM::parseWing(XMLAttributes* a, const char* type)
372 {
373     Wing* w = new Wing();
374
375     float defDihed = 0;
376     if(eq(type, "vstab"))
377         defDihed = 90;
378     else
379         w->setMirror(true);
380
381     float pos[3];
382     pos[0] = attrf(a, "x");
383     pos[1] = attrf(a, "y");
384     pos[2] = attrf(a, "z");
385     w->setBase(pos);
386
387     w->setLength(attrf(a, "length"));
388     w->setChord(attrf(a, "chord"));
389     w->setSweep(attrf(a, "sweep", 0) * DEG2RAD);
390     w->setTaper(attrf(a, "taper", 1));
391     w->setDihedral(attrf(a, "dihedral", defDihed) * DEG2RAD);
392     w->setCamber(attrf(a, "camber", 0));
393     w->setIncidence(attrf(a, "incidence", 0) * DEG2RAD);
394
395     float effect = attrf(a, "effectiveness", 1);
396     w->setDragScale(w->getDragScale()*effect);
397
398     _currObj = w;
399     return w;
400 }
401
402 void FGFDM::parsePropeller(XMLAttributes* a)
403 {
404     float cg[3];
405     cg[0] = attrf(a, "x");
406     cg[1] = attrf(a, "y");
407     cg[2] = attrf(a, "z");
408     float mass = attrf(a, "mass") * LBS2KG;
409     float moment = attrf(a, "moment");
410     float radius = attrf(a, "radius");
411     float speed = attrf(a, "cruise-speed") * KTS2MPS;
412     float omega = attrf(a, "cruise-rpm") * RPM2RAD;
413     float power = attrf(a, "cruise-power") * HP2W;
414     float rho = Atmosphere::getStdDensity(attrf(a, "cruise-alt") * FT2M);
415
416     // Hack, fix this pronto:
417     float engP = attrf(a, "eng-power") * HP2W;
418     float engS = attrf(a, "eng-rpm") * RPM2RAD;
419
420     Propeller* prop = new Propeller(radius, speed, omega, rho, power);
421     PistonEngine* eng = new PistonEngine(engP, engS);
422     PropEngine* thruster = new PropEngine(prop, eng, moment);
423     _airplane.addThruster(thruster, mass, cg);
424
425     if(a->hasAttribute("displacement"))
426         eng->setDisplacement(attrf(a, "displacement") * CIN2CM);
427
428     if(a->hasAttribute("compression"))
429         eng->setCompression(attrf(a, "compression"));        
430
431     if(a->hasAttribute("turbo-mul")) {
432         float mul = attrf(a, "turbo-mul");
433         float mp = attrf(a, "wastegate-mp", 1e6) * INHG2PA;
434         eng->setTurboParams(mul, mp);
435     }
436
437     if(a->hasAttribute("takeoff-power")) {
438         float power0 = attrf(a, "takeoff-power") * HP2W;
439         float omega0 = attrf(a, "takeoff-rpm") * RPM2RAD;
440         prop->setTakeoff(omega0, power0);
441     }
442
443     if(a->hasAttribute("max-rpm")) {
444         float max = attrf(a, "max-rpm") * RPM2RAD;
445         float min = attrf(a, "min-rpm") * RPM2RAD;
446         thruster->setVariableProp(min, max);
447     }
448
449     char buf[64];
450     sprintf(buf, "/engines/engine[%d]", _nextEngine++);
451     EngRec* er = new EngRec();
452     er->eng = thruster;
453     er->prefix = dup(buf);
454     _thrusters.add(er);
455
456     _currObj = thruster;
457 }
458
459 // Turns a string axis name into an integer for use by the
460 // ControlMap.  Creates a new axis if this one hasn't been defined
461 // yet.
462 int FGFDM::parseAxis(const char* name)
463 {
464     int i;
465     for(i=0; i<_axes.size(); i++) {
466         AxisRec* a = (AxisRec*)_axes.get(i);
467         if(eq(a->name, name))
468             return a->handle;
469     }
470
471     // Not there, make a new one.
472     AxisRec* a = new AxisRec();
473     a->name = dup(name);
474     a->handle = _airplane.getControlMap()->newInput();
475     _axes.add(a);
476     return a->handle;
477 }
478
479 int FGFDM::parseOutput(const char* name)
480 {
481     if(eq(name, "THROTTLE"))  return ControlMap::THROTTLE;
482     if(eq(name, "MIXTURE"))   return ControlMap::MIXTURE;
483     if(eq(name, "STARTER"))   return ControlMap::STARTER;
484     if(eq(name, "MAGNETOS"))  return ControlMap::MAGNETOS;
485     if(eq(name, "ADVANCE"))   return ControlMap::ADVANCE;
486     if(eq(name, "REHEAT"))    return ControlMap::REHEAT;
487     if(eq(name, "BOOST"))     return ControlMap::BOOST;
488     if(eq(name, "VECTOR"))    return ControlMap::VECTOR;
489     if(eq(name, "PROP"))      return ControlMap::PROP;
490     if(eq(name, "BRAKE"))     return ControlMap::BRAKE;
491     if(eq(name, "STEER"))     return ControlMap::STEER;
492     if(eq(name, "EXTEND"))    return ControlMap::EXTEND;
493     if(eq(name, "INCIDENCE")) return ControlMap::INCIDENCE;
494     if(eq(name, "FLAP0"))     return ControlMap::FLAP0;
495     if(eq(name, "FLAP1"))     return ControlMap::FLAP1;
496     if(eq(name, "SLAT"))      return ControlMap::SLAT;
497     if(eq(name, "SPOILER"))   return ControlMap::SPOILER;
498     SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '"
499            << name << "' in YASim aircraft description.");
500     exit(1);
501
502 }
503
504 void FGFDM::parseWeight(XMLAttributes* a)
505 {
506     WeightRec* wr = new WeightRec();
507
508     float v[3];
509     v[0] = attrf(a, "x");
510     v[1] = attrf(a, "y");
511     v[2] = attrf(a, "z");
512
513     wr->prop = dup(a->getValue("mass-prop"));
514     wr->size = attrf(a, "size", 0);
515     wr->handle = _airplane.addWeight(v, wr->size);
516
517     _weights.add(wr);
518 }
519
520 bool FGFDM::eq(const char* a, const char* b)
521 {
522     // Figure it out for yourself. :)
523     while(*a && *b && *a == *b) { a++; b++; }
524     return !(*a || *b);
525 }
526
527 char* FGFDM::dup(const char* s)
528 {
529     int len=0;
530     while(s[len++]);
531     char* s2 = new char[len+1];
532     char* p = s2;
533     while((*p++ = *s++));
534     s2[len] = 0;
535     return s2;
536 }
537
538 int FGFDM::attri(XMLAttributes* atts, char* attr)
539 {
540     if(!atts->hasAttribute(attr)) {
541         SG_LOG(SG_FLIGHT,SG_ALERT,"Missing '" << attr <<
542                "' in YASim aircraft description");
543         exit(1);
544     }
545     return attri(atts, attr, 0);
546 }
547
548 int FGFDM::attri(XMLAttributes* atts, char* attr, int def)
549 {
550     const char* val = atts->getValue(attr);
551     if(val == 0) return def;
552     else         return atol(val);
553 }
554
555 float FGFDM::attrf(XMLAttributes* atts, char* attr)
556 {
557     if(!atts->hasAttribute(attr)) {
558         SG_LOG(SG_FLIGHT,SG_ALERT,"Missing '" << attr <<
559                "' in YASim aircraft description");
560         exit(1);
561     }
562     return attrf(atts, attr, 0);
563 }
564
565 float FGFDM::attrf(XMLAttributes* atts, char* attr, float def)
566 {
567     const char* val = atts->getValue(attr);
568     if(val == 0) return def;
569     else         return (float)atof(val);    
570 }
571
572 }; // namespace yasim