Latest YASim changes.
This commit is contained in:
parent
2b34388ea6
commit
f0e6716953
20 changed files with 693 additions and 186 deletions
|
@ -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; i<w->numSurfaces(); 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; j<segs; j++) {
|
||||
float frac = (j+0.5) / segs;
|
||||
|
||||
float scale = 1;
|
||||
if(frac < f->mid)
|
||||
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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -14,6 +14,13 @@ public:
|
|||
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);
|
||||
static float data[][4];
|
||||
|
|
|
@ -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; j<o->n; j++)
|
||||
o->values[j] = 0;
|
||||
for(int j=0; j<o->maps.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; i<maps->size(); i++) {
|
||||
MapRec* map = (MapRec*)maps->get(i);
|
||||
map->out->values[map->idx] = value;
|
||||
for(int i=0; i<maps->size(); 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; i<o->n; i++) {
|
||||
float val = o->values[i];
|
||||
int opt = (int)o->options.get(i);
|
||||
if(opt & OPT_SQUARE)
|
||||
for(i=0; i<o->maps.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <Main/fg_props.hxx>
|
||||
|
||||
#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;
|
||||
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, (t->getFuelFlow()/fuelDensity) * 3600 * CM2GALS);
|
||||
|
||||
if(t->getPropEngine()) {
|
||||
PropEngine* p = t->getPropEngine();
|
||||
|
||||
sprintf(buf, "%s/rpm", er->prefix);
|
||||
fgSetFloat(buf, p->getOmega() / RPM2RAD);
|
||||
|
||||
sprintf(buf, "%s/fuel-flow-gph", er->prefix);
|
||||
fgSetFloat(buf, p->getFuelFlow() * (3600*2.2/5)); // FIXME, wrong
|
||||
}
|
||||
|
||||
for(i=0; i<_jets.size(); i++) {
|
||||
EngRec* er = (EngRec*)_jets.get(i);
|
||||
Jet* j = (Jet*)er->eng;
|
||||
if(t->getPistonEngine()) {
|
||||
PistonEngine* p = t->getPistonEngine();
|
||||
|
||||
sprintf(buf, "%s/fuel-flow-gph", er->prefix);
|
||||
fgSetFloat(buf, j->getFuelFlow() * (3600*2.2/6)); // FIXME, wrong
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -37,6 +37,7 @@ 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);
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 setDryThrust(float thrust);
|
||||
void setReheatThrust(float thrust);
|
||||
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);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 PistonEngine::getTorque()
|
||||
{
|
||||
return _torque;
|
||||
}
|
||||
|
||||
float rho = density * _throttle;
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
46
src/FDM/YASim/SimpleJet.cpp
Normal file
46
src/FDM/YASim/SimpleJet.cpp
Normal file
|
@ -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
|
25
src/FDM/YASim/SimpleJet.hpp
Normal file
25
src/FDM/YASim/SimpleJet.hpp
Normal file
|
@ -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
|
|
@ -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();
|
||||
|
@ -142,6 +159,8 @@ void YASim::init()
|
|||
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<iterations; i++) {
|
||||
// Remember, update only every 4th call
|
||||
_updateCount++;
|
||||
if(_updateCount >= 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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue