diff --git a/src/FDM/YASim/Airplane.cpp b/src/FDM/YASim/Airplane.cpp index 9f1f1de0d..da81beba2 100644 --- a/src/FDM/YASim/Airplane.cpp +++ b/src/FDM/YASim/Airplane.cpp @@ -207,7 +207,8 @@ void Airplane::addVStab(Wing* vstab) _vstabs.add(vstab); } -void Airplane::addFuselage(float* front, float* back, float width) +void Airplane::addFuselage(float* front, float* back, float width, + float taper, float mid) { Fuselage* f = new Fuselage(); int i; @@ -216,6 +217,8 @@ void Airplane::addFuselage(float* front, float* back, float width) f->back[i] = back[i]; } f->width = width; + f->taper = taper; + f->mid = mid; _fuselages.add(f); } @@ -358,12 +361,18 @@ float Airplane::compileWing(Wing* w) int i; for(i=0; inumSurfaces(); i++) { Surface* s = (Surface*)w->getSurface(i); + + float td = s->getTotalDrag(); + s->setTotalDrag(td); + _model.addSurface(s); + float mass = w->getSurfaceWeight(i); + mass = mass * Math::sqrt(mass); float pos[3]; s->getPosition(pos); - _model.getBody()->addMass(w->getSurfaceWeight(i), pos); - wgt += w->getSurfaceWeight(i); + _model.getBody()->addMass(mass, pos); + wgt += mass; } return wgt; } @@ -380,11 +389,22 @@ float Airplane::compileFuselage(Fuselage* f) int j; for(j=0; jmid) + scale = f->taper+(1-f->taper) * (frac / f->mid); + else + scale = f->taper+(1-f->taper) * (frac - f->mid) / (1 - f->mid); + + // Where are we? float pos[3]; Math::mul3(frac, fwd, pos); Math::add3(f->back, pos, pos); - _model.getBody()->addMass(segWgt, pos); - wgt += segWgt; + + // _Mass_ weighting goes as surface area^(3/2) + float mass = scale*segWgt * Math::sqrt(scale*segWgt); + _model.getBody()->addMass(mass, pos); + wgt += mass; // Make a Surface too Surface* s = new Surface(); @@ -392,7 +412,7 @@ float Airplane::compileFuselage(Fuselage* f) float sideDrag = len/wid; s->setYDrag(sideDrag); s->setZDrag(sideDrag); - s->setTotalDrag(segWgt); + s->setTotalDrag(scale*segWgt); // FIXME: fails for fuselages aligned along the Y axis float o[9]; diff --git a/src/FDM/YASim/Airplane.hpp b/src/FDM/YASim/Airplane.hpp index 2f350f81a..de0038b63 100644 --- a/src/FDM/YASim/Airplane.hpp +++ b/src/FDM/YASim/Airplane.hpp @@ -32,7 +32,8 @@ public: void setTail(Wing* tail); void addVStab(Wing* vstab); - void addFuselage(float* front, float* back, float width); + void addFuselage(float* front, float* back, float width, + float taper=1, float mid=0.5); int addTank(float* pos, float cap, float fuelDensity); void addGear(Gear* g, float transitionTime); void addThruster(Thruster* t, float mass, float* cg); @@ -58,6 +59,7 @@ public: float getFuelDensity(int tank); // kg/m^3 void compile(); // generate point masses & such, then solve + void stabilizeThrust(); // Solution output values int getSolutionIterations(); @@ -70,7 +72,7 @@ public: private: struct Tank { float pos[3]; float cap; float fill; float density; int handle; }; - struct Fuselage { float front[3], back[3], width; }; + struct Fuselage { float front[3], back[3], width, taper, mid; }; struct GearRec { Gear* gear; Surface* surf; float wgt; float time; }; struct ThrustRec { Thruster* thruster; int handle; float cg[3]; float mass; }; @@ -85,7 +87,6 @@ private: float compileWing(Wing* w); float compileFuselage(Fuselage* f); void compileGear(GearRec* gr); - void stabilizeThrust(); void applyDragFactor(float factor); void applyLiftRatio(float factor); float clamp(float val, float min, float max); diff --git a/src/FDM/YASim/Atmosphere.cpp b/src/FDM/YASim/Atmosphere.cpp index 9f519d510..0ace3869f 100644 --- a/src/FDM/YASim/Atmosphere.cpp +++ b/src/FDM/YASim/Atmosphere.cpp @@ -35,6 +35,9 @@ float Atmosphere::data[][4] = {{ 0, 288.20, 101325, 1.22500 }, // P in pascals (N/m^2), rho is kg/m^3, T in kelvin. const float R = 287.1; +// Specific heat ratio for air, at "low" temperatures. +const float GAMMA = 1.4; + float Atmosphere::getStdTemperature(float alt) { return getRecord(alt, 1); @@ -95,7 +98,18 @@ float Atmosphere::calcDensity(float pressure, float temp) float Atmosphere::calcMach(float spd, float temp) { - return spd / Math::sqrt(1.4 * R * temp); + return spd / Math::sqrt(GAMMA * R * temp); +} + +void Atmosphere::calcStaticAir(float p0, float t0, float d0, float v, + float* pOut, float* tOut, float* dOut) +{ + const static float C0 = ((GAMMA-1)/(2*R*GAMMA)); + const static float C1 = 1/(GAMMA-1); + + *tOut = t0 + (v*v) * C0; + *dOut = d0 * Math::pow(*tOut / t0, C1); + *pOut = (*dOut) * R * (*tOut); } float Atmosphere::getRecord(float alt, int recNum) diff --git a/src/FDM/YASim/Atmosphere.hpp b/src/FDM/YASim/Atmosphere.hpp index 2e9c0512f..2c10ff321 100644 --- a/src/FDM/YASim/Atmosphere.hpp +++ b/src/FDM/YASim/Atmosphere.hpp @@ -13,6 +13,13 @@ public: static float calcVEAS(float spd, float pressure, float temp); static float calcMach(float spd, float temp); static float calcDensity(float pressure, float temp); + + // Given ambient ("0") pressure/density/temperature values, + // calculate the properties of static air (air accelerated to the + // aircraft's speed) at a given velocity. Includes + // compressibility, but not shock effects. + static void calcStaticAir(float p0, float t0, float d0, float v, + float* pOut, float* tOut, float* dOut); private: static float getRecord(float alt, int idx); diff --git a/src/FDM/YASim/ControlMap.cpp b/src/FDM/YASim/ControlMap.cpp index ae75a7958..25a7db746 100644 --- a/src/FDM/YASim/ControlMap.cpp +++ b/src/FDM/YASim/ControlMap.cpp @@ -1,6 +1,7 @@ #include "Jet.hpp" #include "Thruster.hpp" #include "PropEngine.hpp" +#include "PistonEngine.hpp" #include "Gear.hpp" #include "Wing.hpp" #include "Math.hpp" @@ -19,11 +20,8 @@ ControlMap::~ControlMap() delete v; } - for(i=0; i<_outputs.size(); i++) { - OutRec* o = (OutRec*)_outputs.get(i); - delete[] o->values; - delete o; - } + for(i=0; i<_outputs.size(); i++) + delete (OutRec*)_outputs.get(i); } int ControlMap::newInput() @@ -32,6 +30,21 @@ int ControlMap::newInput() return _inputs.add(v); } +void ControlMap::addMapping(int input, int type, void* object, int options, + float src0, float src1, float dst0, float dst1) +{ + addMapping(input, type, object, options); + + // The one we just added is last in the list (ugly, awful hack!) + Vector* maps = (Vector*)_inputs.get(input); + MapRec* m = (MapRec*)maps->get(maps->size() - 1); + + m->src0 = src0; + m->src1 = src1; + m->dst0 = dst0; + m->dst1 = dst1; +} + void ControlMap::addMapping(int input, int type, void* object, int options) { // See if the output object already exists @@ -50,25 +63,22 @@ void ControlMap::addMapping(int input, int type, void* object, int options) out = new OutRec(); out->type = type; out->object = object; - out->n = 0; - out->values = 0; _outputs.add(out); } - - // Make space for the new input value - int idx = out->n++; - delete[] out->values; - out->values = new float[out->n]; - - // Add the new option tag - out->options.add((void*)options); - + // Make a new input record MapRec* map = new MapRec(); map->out = out; - map->idx = idx; + map->opt = options; + map->idx = out->maps.add(map); - // And add it to the approproate vector. + // The default ranges differ depending on type! + map->src1 = map->dst1 = 1; + map->src0 = map->dst0 = 0; + if(type==FLAP0 || type==FLAP1 || type==STEER) + map->src0 = map->dst0 = -1; + + // And add it to the approproate vectors. Vector* maps = (Vector*)_inputs.get(input); maps->add(map); } @@ -76,22 +86,29 @@ void ControlMap::addMapping(int input, int type, void* object, int options) void ControlMap::reset() { // Set all the values to zero - int i; - for(i=0; i<_outputs.size(); i++) { + for(int i=0; i<_outputs.size(); i++) { OutRec* o = (OutRec*)_outputs.get(i); - int j; - for(j=0; jn; j++) - o->values[j] = 0; + for(int j=0; jmaps.size(); j++) + ((MapRec*)o->maps.get(j))->val = 0; } } -void ControlMap::setInput(int input, float value) +void ControlMap::setInput(int input, float val) { Vector* maps = (Vector*)_inputs.get(input); - int i; - for(i=0; isize(); i++) { - MapRec* map = (MapRec*)maps->get(i); - map->out->values[map->idx] = value; + for(int i=0; isize(); i++) { + MapRec* m = (MapRec*)maps->get(i); + + float val2 = val; + + // Do the scaling operation. Clamp to [src0:src1], rescale to + // [0:1] within that range, then map to [dst0:dst1]. + if(val2 < m->src0) val2 = m->src0; + if(val2 > m->src1) val2 = m->src1; + val2 = (val2 - m->src0) / (m->src1 - m->src0); + val2 = m->dst0 + val2 * (m->dst1 - m->dst0); + + m->val = val2; } } @@ -105,15 +122,16 @@ void ControlMap::applyControls() // control axes like ailerons. float lval = 0, rval = 0; int i; - for(i=0; in; i++) { - float val = o->values[i]; - int opt = (int)o->options.get(i); - if(opt & OPT_SQUARE) + for(i=0; imaps.size(); i++) { + MapRec* m = (MapRec*)o->maps.get(i); + float val = m->val; + + if(m->opt & OPT_SQUARE) val = val * Math::abs(val); - if(opt & OPT_INVERT) + if(m->opt & OPT_INVERT) val = -val; lval += val; - if(opt & OPT_SPLIT) + if(m->opt & OPT_SPLIT) rval -= val; else rval += val; @@ -125,6 +143,7 @@ void ControlMap::applyControls() case MIXTURE: ((Thruster*)obj)->setMixture(lval); break; case ADVANCE: ((PropEngine*)obj)->setAdvance(lval); break; case REHEAT: ((Jet*)obj)->setReheat(lval); break; + case VECTOR: ((Jet*)obj)->setRotation(lval); break; case BRAKE: ((Gear*)obj)->setBrake(lval); break; case STEER: ((Gear*)obj)->setRotation(lval); break; case EXTEND: ((Gear*)obj)->setExtension(lval); break; @@ -132,6 +151,9 @@ void ControlMap::applyControls() case FLAP0: ((Wing*)obj)->setFlap0(lval, rval); break; case FLAP1: ((Wing*)obj)->setFlap1(lval, rval); break; case SPOILER: ((Wing*)obj)->setSpoiler(lval, rval); break; + case BOOST: + ((Thruster*)obj)->getPistonEngine()->setBoost(lval); + break; } } } diff --git a/src/FDM/YASim/ControlMap.hpp b/src/FDM/YASim/ControlMap.hpp index 722f030ef..627e5e484 100644 --- a/src/FDM/YASim/ControlMap.hpp +++ b/src/FDM/YASim/ControlMap.hpp @@ -11,7 +11,8 @@ public: enum OutputType { THROTTLE, MIXTURE, ADVANCE, REHEAT, PROP, BRAKE, STEER, EXTEND, - INCIDENCE, FLAP0, FLAP1, SLAT, SPOILER }; + INCIDENCE, FLAP0, FLAP1, SLAT, SPOILER, VECTOR, + BOOST }; enum { OPT_SPLIT = 0x01, OPT_INVERT = 0x02, @@ -26,6 +27,12 @@ public: // of object! void addMapping(int input, int output, void* object, int options=0); + // An additional form to specify a mapping range. Input values + // outside of [src0:src1] are clamped, and are then mapped to + // [dst0:dst1] before being set on the object. + void addMapping(int input, int output, void* object, int options, + float src0, float src1, float dst0, float dst1); + // Resets our accumulated input values. Call before any // setInput() invokations. void reset(); @@ -38,9 +45,9 @@ public: void applyControls(); private: - struct OutRec { int type; void* object; int n; - float* values; Vector options; }; - struct MapRec { OutRec* out; int idx; }; + struct OutRec { int type; void* object; Vector maps; }; + struct MapRec { OutRec* out; int idx; int opt; float val; + float src0; float src1; float dst0; float dst1; }; // A list of (sub)Vectors containing a bunch of MapRec objects for // each input handle. diff --git a/src/FDM/YASim/FGFDM.cpp b/src/FDM/YASim/FGFDM.cpp index 6c7903409..299a6389d 100644 --- a/src/FDM/YASim/FGFDM.cpp +++ b/src/FDM/YASim/FGFDM.cpp @@ -4,6 +4,7 @@ #include
#include "Jet.hpp" +#include "SimpleJet.hpp" #include "Gear.hpp" #include "Atmosphere.hpp" #include "PropEngine.hpp" @@ -23,6 +24,8 @@ static const float LBS2KG = 0.45359237; static const float CM2GALS = 264.172037284; static const float HP2W = 745.700; static const float INHG2PA = 3386.389; +static const float K2DEGF = 1.8; +static const float CIN2CM = 1.6387064e-5; // Stubs, so that this can be compiled without the FlightGear // binary. What's the best way to handle this? @@ -43,16 +46,10 @@ FGFDM::~FGFDM() delete[] a->name; delete a; } - for(i=0; i<_pistons.size(); i++) { - EngRec* er = (EngRec*)_pistons.get(i); + for(i=0; i<_thrusters.size(); i++) { + EngRec* er = (EngRec*)_thrusters.get(i); delete[] er->prefix; - delete (PropEngine*)er->eng; - delete er; - } - for(i=0; i<_jets.size(); i++) { - EngRec* er = (EngRec*)_pistons.get(i); - delete[] er->prefix; - delete (Jet*)er->eng; + delete er->eng; delete er; } for(i=0; i<_weights.size(); i++) { @@ -77,12 +74,8 @@ Airplane* FGFDM::getAirplane() void FGFDM::init() { - // We don't want to use these ties (we set the values ourselves) - fgUntie("/consumables/fuel/tank[0]/level-gal_us"); - fgUntie("/consumables/fuel/tank[1]/level-gal_us"); - // Allows the user to start with something other than full fuel - _airplane.setFuelFraction(fgGetFloat("/yasim/fuel-fraction", 1)); + _airplane.setFuelFraction(fgGetFloat("/sim/fuel-fraction", 1)); // This has a nasty habit of being false at startup. That's not // good. @@ -122,6 +115,15 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) _airplane.addVStab(parseWing(a, name)); } else if(eq(name, "propeller")) { parsePropeller(a); + } else if(eq(name, "thruster")) { + SimpleJet* j = new SimpleJet(); + _currObj = j; + v[0] = attrf(a, "x"); v[1] = attrf(a, "y"); v[2] = attrf(a, "z"); + j->setPosition(v); + _airplane.addThruster(j, 0, v); + v[0] = attrf(a, "vx"); v[1] = attrf(a, "vy"); v[2] = attrf(a, "vz"); + j->setDirection(v); + j->setThrust(attrf(a, "thrust") * LBS2N); } else if(eq(name, "jet")) { Jet* j = new Jet(); _currObj = j; @@ -129,15 +131,29 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) v[1] = attrf(a, "y"); v[2] = attrf(a, "z"); float mass = attrf(a, "mass") * LBS2KG; - j->setDryThrust(attrf(a, "thrust") * LBS2N); - j->setReheatThrust(attrf(a, "afterburner", 0) * LBS2N); + j->setMaxThrust(attrf(a, "thrust") * LBS2N, + attrf(a, "afterburner", 0) * LBS2N); + j->setVectorAngle(attrf(a, "rotate", 0) * DEG2RAD); + + float n1min = attrf(a, "n1-idle", 55); + float n1max = attrf(a, "n1-max", 102); + float n2min = attrf(a, "n2-idle", 73); + float n2max = attrf(a, "n2-max", 103); + j->setRPMs(n1min, n1max, n2min, n2max); + + if(a->hasAttribute("tsfc")) j->setTSFC(attrf(a, "tsfc")); + if(a->hasAttribute("egt")) j->setEGT(attrf(a, "egt")); + if(a->hasAttribute("epr")) j->setEPR(attrf(a, "epr")); + if(a->hasAttribute("exhaust-speed")) + j->setVMax(attrf(a, "exhaust-speed") * KTS2MPS); + j->setPosition(v); _airplane.addThruster(j, mass, v); sprintf(buf, "/engines/engine[%d]", _nextEngine++); EngRec* er = new EngRec(); er->eng = j; er->prefix = dup(buf); - _jets.add(er); + _thrusters.add(er); } else if(eq(name, "gear")) { Gear* g = new Gear(); _currObj = g; @@ -151,6 +167,8 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) g->setCompression(v); g->setStaticFriction(attrf(a, "sfric", 0.8)); g->setDynamicFriction(attrf(a, "dfric", 0.7)); + if(a->hasAttribute("castering")) + g->setCastering(true); float transitionTime = attrf(a, "retract-time", 0); _airplane.addGear(g, transitionTime); } else if(eq(name, "fuselage")) { @@ -161,14 +179,16 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) b[0] = attrf(a, "bx"); b[1] = attrf(a, "by"); b[2] = attrf(a, "bz"); - _airplane.addFuselage(v, b, attrf(a, "width")); + float taper = attrf(a, "taper", 1); + float mid = attrf(a, "midpoint", 0.5); + _airplane.addFuselage(v, b, attrf(a, "width"), taper, mid); } else if(eq(name, "tank")) { v[0] = attrf(a, "x"); v[1] = attrf(a, "y"); v[2] = attrf(a, "z"); float density = 6.0; // gasoline, in lbs/gal if(a->hasAttribute("jet")) density = 6.72; - density *= LBS2KG/CM2GALS; + density *= LBS2KG*CM2GALS; _airplane.addTank(v, attrf(a, "capacity") * LBS2KG, density); } else if(eq(name, "ballast")) { v[0] = attrf(a, "x"); @@ -213,10 +233,17 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0; opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0; opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0; - _airplane.getControlMap()->addMapping(parseAxis(axis), - parseOutput(output), - _currObj, - opt); + + ControlMap* cm = _airplane.getControlMap(); + if(a->hasAttribute("src0")) { + cm->addMapping(parseAxis(axis), parseOutput(output), + _currObj, opt, + attrf(a, "src0"), attrf(a, "src1"), + attrf(a, "dst0"), attrf(a, "dst1")); + } else { + cm->addMapping(parseAxis(axis), parseOutput(output), + _currObj, opt); + } } else { // assert: must be under a "cruise" or "approach" tag float value = attrf(a, "value", 0); @@ -257,29 +284,52 @@ void FGFDM::setOutputProperties() { char buf[256]; int i; + float fuelDensity = 718.95; // default to gasoline: ~6 lb/gal for(i=0; i<_airplane.numTanks(); i++) { + fuelDensity = _airplane.getFuelDensity(i); sprintf(buf, "/consumables/fuel/tank[%d]/level-gal_us", i); - fgSetFloat(buf, - CM2GALS*_airplane.getFuel(i)/_airplane.getFuelDensity(i)); + fgSetFloat(buf, CM2GALS*_airplane.getFuel(i)/fuelDensity); } - for(i=0; i<_pistons.size(); i++) { - EngRec* er = (EngRec*)_pistons.get(i); - PropEngine* p = (PropEngine*)er->eng; - - sprintf(buf, "%s/rpm", er->prefix); - fgSetFloat(buf, p->getOmega() / RPM2RAD); + for(i=0; i<_thrusters.size(); i++) { + EngRec* er = (EngRec*)_thrusters.get(i); + Thruster* t = er->eng; sprintf(buf, "%s/fuel-flow-gph", er->prefix); - fgSetFloat(buf, p->getFuelFlow() * (3600*2.2/5)); // FIXME, wrong - } + fgSetFloat(buf, (t->getFuelFlow()/fuelDensity) * 3600 * CM2GALS); - for(i=0; i<_jets.size(); i++) { - EngRec* er = (EngRec*)_jets.get(i); - Jet* j = (Jet*)er->eng; - - sprintf(buf, "%s/fuel-flow-gph", er->prefix); - fgSetFloat(buf, j->getFuelFlow() * (3600*2.2/6)); // FIXME, wrong + if(t->getPropEngine()) { + PropEngine* p = t->getPropEngine(); + + sprintf(buf, "%s/rpm", er->prefix); + fgSetFloat(buf, p->getOmega() / RPM2RAD); + } + + if(t->getPistonEngine()) { + PistonEngine* p = t->getPistonEngine(); + + sprintf(buf, "%s/mp-osi", er->prefix); + fgSetFloat(buf, p->getMP() * (1/INHG2PA)); + + sprintf(buf, "%s/egt-degf", er->prefix); + fgSetFloat(buf, p->getEGT() * K2DEGF + 459.4); + } + + if(t->getJet()) { + Jet* j = t->getJet(); + + sprintf(buf, "%s/n1", er->prefix); + fgSetFloat(buf, j->getN1()); + + sprintf(buf, "%s/n2", er->prefix); + fgSetFloat(buf, j->getN2()); + + sprintf(buf, "%s/epr", er->prefix); + fgSetFloat(buf, j->getEPR()); + + sprintf(buf, "%s/egt-degf", er->prefix); + fgSetFloat(buf, j->getEGT() * K2DEGF + 459.4); + } } } @@ -337,6 +387,12 @@ void FGFDM::parsePropeller(XMLAttributes* a) PropEngine* thruster = new PropEngine(prop, eng, moment); _airplane.addThruster(thruster, mass, cg); + if(a->hasAttribute("displacement")) + eng->setDisplacement(attrf(a, "displacement") * CIN2CM); + + if(a->hasAttribute("compression")) + eng->setCompression(attrf(a, "compression")); + if(a->hasAttribute("turbo-mul")) { float mul = attrf(a, "turbo-mul"); float mp = attrf(a, "wastegate-mp", 1e6) * INHG2PA; @@ -360,7 +416,7 @@ void FGFDM::parsePropeller(XMLAttributes* a) EngRec* er = new EngRec(); er->eng = thruster; er->prefix = dup(buf); - _pistons.add(er); + _thrusters.add(er); _currObj = thruster; } @@ -391,6 +447,8 @@ int FGFDM::parseOutput(const char* name) if(eq(name, "MIXTURE")) return ControlMap::MIXTURE; if(eq(name, "ADVANCE")) return ControlMap::ADVANCE; if(eq(name, "REHEAT")) return ControlMap::REHEAT; + if(eq(name, "BOOST")) return ControlMap::BOOST; + if(eq(name, "VECTOR")) return ControlMap::VECTOR; if(eq(name, "PROP")) return ControlMap::PROP; if(eq(name, "BRAKE")) return ControlMap::BRAKE; if(eq(name, "STEER")) return ControlMap::STEER; @@ -401,7 +459,7 @@ int FGFDM::parseOutput(const char* name) if(eq(name, "SLAT")) return ControlMap::SLAT; if(eq(name, "SPOILER")) return ControlMap::SPOILER; // error here... - return -1; + return *(int*)0; } void FGFDM::parseWeight(XMLAttributes* a) diff --git a/src/FDM/YASim/FGFDM.hpp b/src/FDM/YASim/FGFDM.hpp index 8d2925a1f..f9506dbd2 100644 --- a/src/FDM/YASim/FGFDM.hpp +++ b/src/FDM/YASim/FGFDM.hpp @@ -19,6 +19,7 @@ public: ~FGFDM(); void init(); void iterate(float dt); + void getExternalInput(float dt=1e6); Airplane* getAirplane(); @@ -27,10 +28,9 @@ public: private: struct AxisRec { char* name; int handle; }; - struct EngRec { char* prefix; void* eng; }; + struct EngRec { char* prefix; Thruster* eng; }; struct WeightRec { char* prop; float size; int handle; }; - void getExternalInput(float dt); void setOutputProperties(); Wing* parseWing(XMLAttributes* a, const char* name); @@ -56,8 +56,7 @@ private: Vector _weights; // Engine types. Contains an EngRec structure. - Vector _jets; - Vector _pistons; + Vector _thrusters; // Parsing temporaries void* _currObj; diff --git a/src/FDM/YASim/Gear.cpp b/src/FDM/YASim/Gear.cpp index cc27877fd..e50c4d000 100644 --- a/src/FDM/YASim/Gear.cpp +++ b/src/FDM/YASim/Gear.cpp @@ -16,6 +16,7 @@ Gear::Gear() _brake = 0; _rot = 0; _extension = 1; + _castering = false; } void Gear::setPosition(float* position) @@ -65,6 +66,11 @@ void Gear::setExtension(float extension) _extension = Math::clamp(extension, 0, 1); } +void Gear::setCastering(bool c) +{ + _castering = c; +} + void Gear::getPosition(float* out) { int i; @@ -128,6 +134,11 @@ float Gear::getCompressFraction() return _frac; } +bool Gear::getCastering() +{ + return _castering; +} + void Gear::calcForce(RigidBody* body, float* v, float* rot, float* ground) { // Init the return values @@ -191,6 +202,10 @@ void Gear::calcForce(RigidBody* body, float* v, float* rot, float* ground) _wow = (fmag - damp) * -Math::dot3(cmpr, ground); Math::mul3(-_wow, ground, _force); + // Castering gear feel no force in the ground plane + if(_castering) + return; + // Wheels are funky. Split the velocity along the ground plane // into rolling and skidding components. Assuming small angles, // we generate "forward" and "left" unit vectors (the compression @@ -237,8 +252,8 @@ void Gear::calcForce(RigidBody* body, float* v, float* rot, float* ground) float Gear::calcFriction(float wgt, float v) { - // How slow is stopped? 50 cm/second? - const float STOP = 0.5; + // How slow is stopped? 10 cm/second? + const float STOP = 0.1; const float iSTOP = 1/STOP; v = Math::abs(v); if(v < STOP) return v*iSTOP * wgt * _sfric; diff --git a/src/FDM/YASim/Gear.hpp b/src/FDM/YASim/Gear.hpp index 6e432b6dd..f4170ea66 100644 --- a/src/FDM/YASim/Gear.hpp +++ b/src/FDM/YASim/Gear.hpp @@ -37,7 +37,8 @@ public: void setBrake(float brake); void setRotation(float rotation); void setExtension(float extension); - + void setCastering(bool castering); + void getPosition(float* out); void getCompression(float* out); float getSpring(); @@ -47,6 +48,7 @@ public: float getBrake(); float getRotation(); float getExtension(); + bool getCastering(); // Takes a velocity of the aircraft relative to ground, a rotation // vector, and a ground plane (all specified in local coordinates) @@ -63,6 +65,7 @@ public: private: float calcFriction(float wgt, float v); + bool _castering; float _pos[3]; float _cmpr[3]; float _spring; diff --git a/src/FDM/YASim/Jet.cpp b/src/FDM/YASim/Jet.cpp index 001196b53..82d1172a5 100644 --- a/src/FDM/YASim/Jet.cpp +++ b/src/FDM/YASim/Jet.cpp @@ -5,25 +5,90 @@ namespace yasim { Jet::Jet() { - _rho0 = Atmosphere::getStdDensity(0); - _thrust = 0; - _abThrust = 0; + _maxThrust = 0; + _abFactor = 1; _reheat = 0; + _rotControl = 0; + _maxRot = 0; + + // Initialize parameters for an early-ish subsonic turbojet. More + // recent turbofans will typically have a lower vMax, epr0, and + // tsfc. + _vMax = 800; + _epr0 = 3.0; + _tsfc = 0.8; + _egt0 = 1050; + _n1Min = 55; + _n1Max = 102; + _n2Min = 73; + _n2Max = 103; + setSpooling(4); // 4 second spool time? s'bout right. + + // And initialize to an engine that is idling + _n1 = _n1Min; + _n2 = _n2Min; + + // And sanify the remaining junk, just in case. + _epr = 1; + _fuelFlow = 0; + _egt = 273; + _tempCorrect = 1; + _pressureCorrect = 1; } void Jet::stabilize() { - return; // no-op for now + // Just run it for an hour, there's no need to iterate given the + // algorithms used. + integrate(3600); } -void Jet::setDryThrust(float thrust) +void Jet::setMaxThrust(float thrust, float afterburner) { - _thrust = thrust; + _maxThrust = thrust; + if(afterburner == 0) _abFactor = 1; + else _abFactor = afterburner/thrust; } -void Jet::setReheatThrust(float thrust) +void Jet::setVMax(float spd) { - _abThrust = thrust; + _vMax = spd; +} + +void Jet::setTSFC(float tsfc) +{ + _tsfc = tsfc; +} + +void Jet::setRPMs(float idleN1, float maxN1, float idleN2, float maxN2) +{ + _n1Min = idleN1; + _n1Max = maxN1; + _n2Min = idleN2; + _n2Max = maxN2; +} + +void Jet::setEGT(float takeoffEGT) +{ + _egt0 = takeoffEGT; +} + +void Jet::setEPR(float takeoffEPR) +{ + _epr0 = takeoffEPR; +} + +void Jet::setSpooling(float time) +{ + // 2.3 = -ln(0.1), i.e. x=2.3 is the 90% point we're defining + // The extra fudge factor is there because the N1 speed (which + // determines thrust) lags the N2 speed. + _decay = 1.5 * 2.3 / time; +} + +void Jet::setVectorAngle(float angle) +{ + _maxRot = angle; } void Jet::setReheat(float reheat) @@ -31,12 +96,108 @@ void Jet::setReheat(float reheat) _reheat = Math::clamp(reheat, 0, 1); } +void Jet::setRotation(float rot) +{ + if(rot < 0) rot = 0; + if(rot > 1) rot = 1; + _rotControl = rot; +} + + +float Jet::getN1() +{ + return _n1 * _tempCorrect; +} + +float Jet::getN2() +{ + return _n2 * _tempCorrect; +} + +float Jet::getEPR() +{ + return _epr; +} + +float Jet::getEGT() +{ + // Exactly zero means "off" -- return the ambient temperature + if(_egt == 0) return _temp; + + return _egt * _tempCorrect * _tempCorrect; +} + +float Jet::getFuelFlow() +{ + return _fuelFlow * _pressureCorrect; +} + +void Jet::integrate(float dt) +{ + // Sea-level values + const static float P0 = Atmosphere::getStdPressure(0); + const static float T0 = Atmosphere::getStdTemperature(0); + const static float D0 = Atmosphere::getStdDensity(0); + + float speed = -Math::dot3(_wind, _dir); + + float statT, statP, statD; + Atmosphere::calcStaticAir(_pressure, _temp, _rho, speed, + &statP, &statT, &statD); + _pressureCorrect = statP/P0; + _tempCorrect = Math::sqrt(statT/T0); + + // Linearly taper maxThrust to zero at vMax + float vCorr = 1 - (speed/_vMax); + + float maxThrust = _maxThrust * vCorr * (statD/D0); + _thrust = maxThrust * _throttle; + + // Now get a "beta" (i.e. EPR - 1) value. The output values are + // expressed as functions of beta. + float ibeta0 = 1/(_epr0 - 1); + float betaTarget = (_epr0 - 1) * (_thrust/_maxThrust) * (P0/_pressure) + * (_temp/statT); + float n2Target = _n2Min + (betaTarget*ibeta0) * (_n2Max - _n2Min); + + // Note that this "first" beta value is used to compute a target + // for N2 only Integrate the N2 speed and back-calculate a beta1 + // target. The N1 speed will seek to this. + _n2 = (_n2 + dt*_decay * n2Target) / (1 + dt*_decay); + + float betaN2 = (_epr0-1) * (_n2 - _n2Min) / (_n2Max - _n2Min); + float n1Target = _n1Min + betaN2*ibeta0 * (_n1Max - _n1Min); + _n1 = (_n1 + dt*_decay * n1Target) / (1 + dt*_decay); + + // The actual thrust produced is keyed to the N1 speed. Add the + // afterburners in at the end. + float betaN1 = (_epr0-1) * (_n1 - _n1Min) / (_n1Max - _n1Min); + _thrust *= betaN1/(betaTarget+.00001); // blowup protection + _thrust *= 1 + _reheat*(_abFactor-1); + + // Finally, calculate the output variables. Use a 80/20 mix of + // the N2/N1 speeds as the key. + float beta = 0.8*betaN2 + 0.2*betaN1; + _epr = beta + 1; + float ff0 = _maxThrust*_tsfc*(1/(3600*9.8)); // takeoff fuel flow, kg/s + _fuelFlow = ff0 * beta*ibeta0; + _fuelFlow *= 1 + (3.5 * _reheat * _abFactor); // Afterburners take + // 3.5 times as much + // fuel per thrust unit + _egt = T0 + beta*ibeta0 * (_egt0 - T0); +} + void Jet::getThrust(float* out) { - float t = _thrust * _throttle; - t += (_abThrust - _thrust) * _reheat; - t *= _rho / _rho0; - Math::mul3(t, _dir, out); + Math::mul3(_thrust, _dir, out); + + // Rotate about the Y axis for thrust vectoring + float angle = _rotControl * _maxRot; + float s = Math::sin(angle); + float c = Math::cos(angle); + float o0 = out[0]; + out[0] = c * o0 + s * out[2]; + out[2] = -s * o0 + c * out[2]; } void Jet::getTorque(float* out) @@ -51,14 +212,4 @@ void Jet::getGyro(float* out) return; } -float Jet::getFuelFlow() -{ - return 0; -} - -void Jet::integrate(float dt) -{ - return; -} - }; // namespace yasim diff --git a/src/FDM/YASim/Jet.hpp b/src/FDM/YASim/Jet.hpp index 40b4c4d9e..52d2ebe59 100644 --- a/src/FDM/YASim/Jet.hpp +++ b/src/FDM/YASim/Jet.hpp @@ -5,18 +5,35 @@ namespace yasim { -// Incredibly dumb placeholder for a Jet implementation. But it does -// what's important, which is provide thrust. class Jet : public Thruster { public: Jet(); virtual Jet* getJet() { return this; } + + void setMaxThrust(float thrust, float afterburner=0); + void setVMax(float spd); + void setTSFC(float tsfc); + void setRPMs(float idleN1, float maxN1, float idleN2, float maxN2); + void setEGT(float takeoffEGT); + void setEPR(float takeoffEPR); + void setVectorAngle(float angle); - void setDryThrust(float thrust); - void setReheatThrust(float thrust); + // The time it takes the engine to reach 90% thrust from idle + void setSpooling(float time); + + // Sets the reheat control void setReheat(float reheat); + // Sets the thrust vector control (0-1) + void setRotation(float rot); + + float getN1(); + float getN2(); + float getEPR(); + float getEGT(); + + // From Thruster: virtual void getThrust(float* out); virtual void getTorque(float* out); virtual void getGyro(float* out); @@ -25,10 +42,33 @@ public: virtual void stabilize(); private: - float _thrust; - float _abThrust; - float _rho0; float _reheat; + + float _maxThrust; // Max dry thrust at sea level + float _abFactor; // Afterburner thrust multiplier + + float _maxRot; + float _rotControl; + + float _decay; // time constant for the exponential integration + float _vMax; // speed at which thrust is zero + float _epr0; // EPR at takeoff thrust + float _tsfc; // TSFC ((lb/hr)/lb) at takeoff thrust and zero airspeed + float _egt0; // EGT at takeoff thrust + float _n1Min; // N1 at ground idle + float _n1Max; // N1 at takeoff thrust + float _n2Min; // N2 at ground idle + float _n2Max; // N2 at takeoff thrust + + float _thrust; // Current thrust + float _epr; // Current EPR + float _n1; // Current UNCORRECTED N1 (percent) + float _n2; // Current UNCORRECTED N2 (percent) + float _fuelFlow; // Current UNCORRECTED fuel flow (kg/s) + float _egt; // Current UNCORRECTED EGT (kelvin) + + float _tempCorrect; // Intake temp / std temp (273 K) + float _pressureCorrect; // Intake pressure / std pressure }; }; // namespace yasim diff --git a/src/FDM/YASim/Makefile.am b/src/FDM/YASim/Makefile.am index c4f7fdcd5..715ee97dc 100644 --- a/src/FDM/YASim/Makefile.am +++ b/src/FDM/YASim/Makefile.am @@ -4,6 +4,6 @@ libYASim_a_SOURCES = YASim.cxx Airplane.cpp Atmosphere.cpp ControlMap.cpp \ FGFDM.cpp Gear.cpp Glue.cpp Integrator.cpp Jet.cpp \ Math.cpp Model.cpp PistonEngine.cpp Propeller.cpp \ PropEngine.cpp RigidBody.cpp Surface.cpp \ - Thruster.cpp Wing.cpp + Thruster.cpp Wing.cpp SimpleJet.cpp INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/FDM/YASim/PistonEngine.cpp b/src/FDM/YASim/PistonEngine.cpp index 710af2bcd..73f3c9b82 100644 --- a/src/FDM/YASim/PistonEngine.cpp +++ b/src/FDM/YASim/PistonEngine.cpp @@ -3,11 +3,16 @@ #include "PistonEngine.hpp" namespace yasim { +const static float HP2W = 745.7; +const static float CIN2CM = 1.6387064e-5; + PistonEngine::PistonEngine(float power, float speed) { + _boost = 1; + // Presume a BSFC (in lb/hour per HP) of 0.45. In SI that becomes - // (2.2 lb/kg, 745.7 W/hp, 3600 sec/hour) 3.69e-07 kg/Ws. - _f0 = power * 3.69e-07; + // (2.2 lb/kg, 745.7 W/hp, 3600 sec/hour) 7.62e-08 kg/Ws. + _f0 = power * 7.62e-08; _power0 = power; _omega0 = speed; @@ -23,6 +28,12 @@ PistonEngine::PistonEngine(float power, float speed) _turbo = 1; _maxMP = 1e6; // No waste gate on non-turbo engines. + + // Guess at reasonable values for these guys. Displacements run + // at about 2 cubic inches per horsepower or so, at least for + // non-turbocharged engines. + _compression = 8; + _displacement = power * (2*CIN2CM/HP2W); } void PistonEngine::setTurboParams(float turbo, float maxMP) @@ -32,13 +43,23 @@ void PistonEngine::setTurboParams(float turbo, float maxMP) // This changes the "sea level" manifold air density float P0 = Atmosphere::getStdPressure(0); - float P = P0 * _turbo; + float P = P0 * (1 + _boost * (_turbo - 1)); if(P > _maxMP) P = _maxMP; float T = Atmosphere::getStdTemperature(0) * Math::pow(P/P0, 2./7.); _rho0 = P / (287.1 * T); } -float PistonEngine::getPower() +void PistonEngine::setDisplacement(float d) +{ + _displacement = d; +} + +void PistonEngine::setCompression(float c) +{ + _compression = c; +} + +float PistonEngine::getMaxPower() { return _power0; } @@ -53,22 +74,52 @@ void PistonEngine::setMixture(float m) _mixture = m; } -void PistonEngine::calc(float P, float T, float speed, - float* torqueOut, float* fuelFlowOut) +void PistonEngine::setBoost(float boost) { - // The actual fuel flow - float fuel = _mixture * _mixCoeff * speed; + _boost = boost; +} - // manifold air density - if(_turbo != 1) { - float P1 = P * _turbo; - if(P1 > _maxMP) P1 = _maxMP; - T *= Math::pow(P1/P, 2./7.); - P = P1; - } - float density = P / (287.1 * T); - - float rho = density * _throttle; +float PistonEngine::getTorque() +{ + return _torque; +} + +float PistonEngine::getFuelFlow() +{ + return _fuelFlow; +} + +float PistonEngine::getMP() +{ + return _mp; +} + +float PistonEngine::getEGT() +{ + return _egt; +} + +void PistonEngine::calc(float pressure, float temp, float speed) +{ + // Calculate manifold pressure as ambient pressure modified for + // turbocharging and reduced by the throttle setting. According + // to Dave Luff, minimum throttle at sea level corresponds to 6" + // manifold pressure. Assume that this means that minimum MP is + // always 20% of ambient pressure. + _mp = pressure * (1 + _boost*(_turbo-1)); // turbocharger + _mp *= (0.2 + 0.8 * _throttle); // throttle + if(_mp > _maxMP) _mp = _maxMP; // wastegate + + // Air entering the manifold does so rapidly, and thus the + // pressure change can be assumed to be adiabatic. Calculate a + // temperature change, and use that to get the density. + float T = temp * Math::pow(_mp/pressure, 2.0/7.0); + float rho = _mp / (287.1 * T); + + // The actual fuel flow is determined only by engine RPM and the + // mixture setting. Not all of this will burn with the same + // efficiency. + _fuelFlow = _mixture * speed * _mixCoeff; // How much fuel could be burned with ideal (i.e. uncorrected!) // combustion. @@ -81,18 +132,39 @@ void PistonEngine::calc(float P, float T, float speed, // interpolate. This vaguely matches a curve I copied out of a // book for a single engine. Shrug. float burned; - float r = fuel/burnable; + float r = _fuelFlow/burnable; if (burnable == 0) burned = 0; - else if(r < .625) burned = fuel; + else if(r < .625) burned = _fuelFlow; else if(r > 1.375) burned = burnable; - else burned = fuel + (burnable-fuel)*(r-.625)*(4.0/3.0); + else + burned = _fuelFlow + (burnable-_fuelFlow)*(r-.625)*(4.0/3.0); // And finally the power is just the reference power scaled by the - // amount of fuel burned. + // amount of fuel burned, and torque is that divided by RPM. float power = _power0 * burned/_f0; + _torque = power/speed; - *torqueOut = power/speed; - *fuelFlowOut = fuel; + // Now EGT. This one gets a little goofy. We can calculate the + // work done by an isentropically expanding exhaust gas as the + // mass of the gas times the specific heat times the change in + // temperature. The mass is just the engine displacement times + // the manifold density, plus the mass of the fuel, which we know. + // The change in temperature can be calculated adiabatically as a + // function of the exhaust gas temperature and the compression + // ratio (which we know). So just rearrange the equation to get + // EGT as a function of engine power. Cool. I'm using a value of + // 1300 J/(kg*K) for the exhaust gas specific heat. I found this + // on a web page somewhere; no idea if it's accurate. Also, + // remember that four stroke engines do one combustion cycle every + // TWO revolutions, so the displacement per revolution is half of + // what we'd expect. And diddle the work done by the gas a bit to + // account for non-thermodynamic losses like internal friction; + // 10% should do it. + + float massFlow = _fuelFlow + (rho * 0.5 * _displacement * speed); + float specHeat = 1300; + float corr = 1.0/(Math::pow(_compression, 0.4) - 1); + _egt = corr * (power * 1.1) / (massFlow * specHeat); } }; // namespace yasim diff --git a/src/FDM/YASim/PistonEngine.hpp b/src/FDM/YASim/PistonEngine.hpp index 5a7e09e2b..8552d86cb 100644 --- a/src/FDM/YASim/PistonEngine.hpp +++ b/src/FDM/YASim/PistonEngine.hpp @@ -8,33 +8,43 @@ public: // Initializes an engine from known "takeoff" parameters. PistonEngine(float power, float spd); void setTurboParams(float mul, float maxMP); + void setDisplacement(float d); + void setCompression(float c); void setThrottle(float throttle); void setMixture(float mixture); + void setBoost(float boost); // fraction of turbo-mul used - float getPower(); + float getMaxPower(); // max sea-level power - // Calculates power output and fuel flow, based on a given - // throttle setting (0-1 corresponding to the fraction of - // "available" manifold pressure), mixture (fuel flux per rpm, - // 0-1, where 1 is "max rich", or a little bit more than needed - // for rated power at sea level) - void calc(float pressure, float temp, float speed, - float* powerOut, float* fuelFlowOut); + void calc(float pressure, float temp, float speed); + float getTorque(); + float getFuelFlow(); + float getMP(); + float getEGT(); private: + // Static configuration: float _power0; // reference power setting float _omega0; // " engine speed float _rho0; // " manifold air density float _f0; // "ideal" fuel flow at P0/omega0 float _mixCoeff; // fuel flow per omega at full mixture + float _turbo; // (or super-)charger pressure multiplier + float _maxMP; // wastegate setting + float _displacement; // piston stroke volume + float _compression; // compression ratio (>1) // Runtime settables: float _throttle; float _mixture; + float _boost; - float _turbo; - float _maxMP; + // Runtime state/output: + float _mp; + float _torque; + float _fuelFlow; + float _egt; }; }; // namespace yasim diff --git a/src/FDM/YASim/PropEngine.cpp b/src/FDM/YASim/PropEngine.cpp index 2c028bbc8..ef9bd65fc 100644 --- a/src/FDM/YASim/PropEngine.cpp +++ b/src/FDM/YASim/PropEngine.cpp @@ -79,9 +79,10 @@ void PropEngine::stabilize() bool goingUp = false; float step = 10; while(true) { - float etau, ptau, dummy; + float ptau, dummy; _prop->calc(_rho, speed, _omega, &dummy, &ptau); - _eng->calc(_pressure, _temp, _omega, &etau, &dummy); + _eng->calc(_pressure, _temp, _omega); + float etau = _eng->getTorque(); float tdiff = etau - ptau; if(Math::abs(tdiff/_moment) < 0.1) @@ -110,9 +111,10 @@ void PropEngine::integrate(float dt) _eng->setThrottle(_throttle); _eng->setMixture(_mixture); - _prop->calc(_rho, speed, _omega, - &thrust, &propTorque); - _eng->calc(_pressure, _temp, _omega, &engTorque, &_fuelFlow); + _prop->calc(_rho, speed, _omega, &thrust, &propTorque); + _eng->calc(_pressure, _temp, _omega); + engTorque = _eng->getTorque(); + _fuelFlow = _eng->getFuelFlow(); // Turn the thrust into a vector and save it Math::mul3(thrust, _dir, _thrust); diff --git a/src/FDM/YASim/SimpleJet.cpp b/src/FDM/YASim/SimpleJet.cpp new file mode 100644 index 000000000..505bef119 --- /dev/null +++ b/src/FDM/YASim/SimpleJet.cpp @@ -0,0 +1,46 @@ +#include "Math.hpp" +#include "SimpleJet.hpp" + +namespace yasim { + +SimpleJet::SimpleJet() +{ + _thrust = 0; +} + +void SimpleJet::setThrust(float thrust) +{ + _thrust = thrust; +} + +void SimpleJet::getThrust(float* out) +{ + Math::mul3(_thrust * _throttle, _dir, out); +} + +void SimpleJet::getTorque(float* out) +{ + out[0] = out[1] = out[2] = 0; +} + +void SimpleJet::getGyro(float* out) +{ + out[0] = out[1] = out[2] = 0; +} + +float SimpleJet::getFuelFlow() +{ + return 0; +} + +void SimpleJet::integrate(float dt) +{ + return; +} + +void SimpleJet::stabilize() +{ + return; +} + +}; // namespace yasim diff --git a/src/FDM/YASim/SimpleJet.hpp b/src/FDM/YASim/SimpleJet.hpp new file mode 100644 index 000000000..97174e651 --- /dev/null +++ b/src/FDM/YASim/SimpleJet.hpp @@ -0,0 +1,25 @@ +#ifndef _SIMPLEJET_HPP +#define _SIMPLEJET_HPP + +#include "Thruster.hpp" + +namespace yasim { + +// As simple a Thruster subclass as you can find. It makes thrust. Period. +class SimpleJet : public Thruster +{ +public: + SimpleJet(); + void setThrust(float thrust); + virtual void getThrust(float* out); + virtual void getTorque(float* out); + virtual void getGyro(float* out); + virtual float getFuelFlow(); + virtual void integrate(float dt); + virtual void stabilize(); +private: + float _thrust; +}; + +}; // namespace yasim +#endif // _SIMPLEJET_HPP diff --git a/src/FDM/YASim/YASim.cxx b/src/FDM/YASim/YASim.cxx index 1e8b2b009..bfc6fc6d5 100644 --- a/src/FDM/YASim/YASim.cxx +++ b/src/FDM/YASim/YASim.cxx @@ -39,9 +39,12 @@ void YASim::printDEBUG() if(debugCount >= 3) { debugCount = 0; -// printf("RPM %.1f FF %.1f\n", -// fgGetFloat("/engines/engine[0]/rpm"), -// fgGetFloat("/engines/engine[0]/fuel-flow-gph")); +// printf("N1 %5.1f N2 %5.1f FF %7.1f EPR %4.2f EGT %6.1f\n", +// fgGetFloat("/engines/engine[0]/n1"), +// fgGetFloat("/engines/engine[0]/n2"), +// fgGetFloat("/engines/engine[0]/fuel-flow-gph"), +// fgGetFloat("/engines/engine[0]/epr"), +// fgGetFloat("/engines/engine[0]/egt")); // printf("gear: %f\n", fgGetFloat("/controls/gear-down")); @@ -59,14 +62,9 @@ YASim::YASim(double dt) set_delta_t(dt); _fdm = new FGFDM(); - // Because the integration method is via fourth-order Runge-Kutta, - // we only get an "output" state for every 4 times the internal - // forces are calculated. So divide dt by four to account for - // this, and only run an iteration every fourth time through - // update. - _dt = dt * 4; + _dt = dt; + _fdm->getAirplane()->getModel()->getIntegrator()->setInterval(_dt); - _updateCount = 0; } void YASim::report() @@ -97,6 +95,25 @@ void YASim::report() } } +void YASim::bind() +{ + // Run the superclass bind to set up a bunch of property ties + FGInterface::bind(); + + // Now UNtie the ones that we are going to set ourselves. + fgUntie("/consumables/fuel/tank[0]/level-gal_us"); + fgUntie("/consumables/fuel/tank[1]/level-gal_us"); + + char buf[256]; + for(int i=0; i<_fdm->getAirplane()->getModel()->numThrusters(); i++) { + sprintf(buf, "/engines/engine[%d]/fuel-flow-gph", i); fgUntie(buf); + sprintf(buf, "/engines/engine[%d]/rpm", i); fgUntie(buf); + sprintf(buf, "/engines/engine[%d]/mp-osi", i); fgUntie(buf); + sprintf(buf, "/engines/engine[%d]/egt-degf", i); fgUntie(buf); + } + +} + void YASim::init() { Airplane* a = _fdm->getAirplane(); @@ -141,7 +158,9 @@ void YASim::init() sprintf(buf, "/controls/propeller-pitch[%d]", i); fgSetFloat(buf, 1); sprintf(buf, "/controls/afterburner[%d]", i); fgSetFloat(buf, 0); } - + + fgSetFloat("/controls/slats", 0); + fgSetFloat("/controls/spoilers", 0); // Are we at ground level? If so, lift the plane up so the gear // clear the ground @@ -167,8 +186,11 @@ void YASim::init() // Blank the state, and copy in ours State s; m->setState(&s); - copyToYASim(true); + + _fdm->getExternalInput(); + _fdm->getAirplane()->stabilizeThrust(); + set_inited(true); } @@ -180,18 +202,11 @@ void YASim::update(int iterations) int i; for(i=0; i= 4) { - copyToYASim(false); _fdm->iterate(_dt); copyFromYASim(); printDEBUG(); - - _updateCount = 0; - } } } @@ -420,7 +435,7 @@ void YASim::copyFromYASim() pe->getTorque(tmp); float power = Math::mag3(tmp) * pe->getOmega(); - float maxPower = pe->getPistonEngine()->getPower(); + float maxPower = pe->getPistonEngine()->getMaxPower(); fge->set_MaxHP(maxPower * W2HP); fge->set_Percentage_Power(100 * power/maxPower); diff --git a/src/FDM/YASim/YASim.hxx b/src/FDM/YASim/YASim.hxx index 7238a49a6..02f9440bc 100644 --- a/src/FDM/YASim/YASim.hxx +++ b/src/FDM/YASim/YASim.hxx @@ -11,6 +11,7 @@ public: // Load externally set stuff into the FDM virtual void init(); + virtual void bind(); // Run an iteration virtual void update(int iterations); @@ -23,7 +24,6 @@ public: void printDEBUG(); yasim::FGFDM* _fdm; - int _updateCount; float _dt; };