1
0
Fork 0

Added a property output feature to ControlMap that allows arbitrary binding

and scaling of control values to properties.  Also added a time interpolation
feature that replaces the hacked-in "retract-time" feature for the gear in
a more general way (applicable to flaps, too!).  Incompatibly breaks
the XML syntax; get new files!
This commit is contained in:
andy 2002-03-01 06:47:28 +00:00
parent 9bae80d94a
commit ab381e5c01
7 changed files with 185 additions and 81 deletions

View file

@ -52,6 +52,9 @@ Airplane::~Airplane()
void Airplane::iterate(float dt)
{
// The gear might have moved. Change their aerodynamics.
updateGearState();
_model.iterate();
// FIXME: Consume fuel
@ -108,27 +111,12 @@ Gear* Airplane::getGear(int g)
return ((GearRec*)_gears.get(g))->gear;
}
void Airplane::setGearState(bool down, float dt)
void Airplane::updateGearState()
{
int i;
for(i=0; i<_gears.size(); i++) {
for(int i=0; i<_gears.size(); i++) {
GearRec* gr = (GearRec*)_gears.get(i);
if(gr->time == 0) {
// Non-extensible
gr->gear->setExtension(1);
gr->surf->setXDrag(1);
gr->surf->setYDrag(1);
gr->surf->setZDrag(1);
continue;
}
float ext = gr->gear->getExtension();
float diff = dt / gr->time;
if(!down) diff = -diff;
float ext = gr->gear->getExtension() + diff;
if(ext < 0) ext = 0;
if(ext > 1) ext = 1;
gr->gear->setExtension(ext);
gr->surf->setXDrag(ext);
gr->surf->setYDrag(ext);
gr->surf->setZDrag(ext);
@ -236,12 +224,11 @@ int Airplane::addTank(float* pos, float cap, float density)
return _tanks.add(t);
}
void Airplane::addGear(Gear* gear, float transitionTime)
void Airplane::addGear(Gear* gear)
{
GearRec* g = new GearRec();
g->gear = gear;
g->surf = 0;
g->time = transitionTime;
_gears.add(g);
}
@ -589,9 +576,6 @@ void Airplane::compile()
// Do this after solveGear, because it creates "gear" objects that
// we don't want to affect.
compileContactPoints();
// Drop the gear (use a really big dt)
setGearState(true, 1000000);
}
void Airplane::solveGear()
@ -678,16 +662,13 @@ void Airplane::runCruise()
Control* c = (Control*)_cruiseControls.get(i);
_controls.setInput(c->control, c->val);
}
_controls.applyControls();
_controls.applyControls(1000000); // Huge dt value
// The local wind
float wind[3];
Math::mul3(-1, _cruiseState.v, wind);
Math::vmul33(_cruiseState.orient, wind, wind);
// Gear are up (if they're non-retractable, this is a noop)
setGearState(false, 100000);
// Cruise is by convention at 50% tank capacity
setFuelFraction(0.5);
@ -700,6 +681,8 @@ void Airplane::runCruise()
}
stabilizeThrust();
updateGearState();
// Precompute thrust in the model, and calculate aerodynamic forces
_model.getBody()->reset();
_model.initIteration();
@ -719,7 +702,7 @@ void Airplane::runApproach()
Control* c = (Control*)_approachControls.get(i);
_controls.setInput(c->control, c->val);
}
_controls.applyControls();
_controls.applyControls(1000000);
// The local wind
float wind[3];
@ -729,9 +712,6 @@ void Airplane::runApproach()
// Approach is by convention at 20% tank capacity
setFuelFraction(0.2);
// Gear are down
setGearState(true, 100000);
// Run the thrusters until they get to a stable setting. FIXME:
// this is lots of wasted work.
for(i=0; i<_thrusters.size(); i++) {
@ -741,6 +721,8 @@ void Airplane::runApproach()
}
stabilizeThrust();
updateGearState();
// Precompute thrust in the model, and calculate aerodynamic forces
_model.getBody()->reset();
_model.initIteration();

View file

@ -35,7 +35,7 @@ public:
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 addGear(Gear* g);
void addThruster(Thruster* t, float mass, float* cg);
void addBallast(float* pos, float mass);
@ -51,7 +51,6 @@ public:
int numGear();
Gear* getGear(int g);
void setGearState(bool down, float dt);
int numTanks();
void setFuelFraction(float frac); // 0-1, total amount of fuel
@ -74,7 +73,7 @@ private:
struct Tank { float pos[3]; float cap; float fill;
float density; int handle; };
struct Fuselage { float front[3], back[3], width, taper, mid; };
struct GearRec { Gear* gear; Surface* surf; float wgt; float time; };
struct GearRec { Gear* gear; Surface* surf; float wgt; };
struct ThrustRec { Thruster* thruster;
int handle; float cg[3]; float mass; };
struct Control { int control; float val; };
@ -94,6 +93,7 @@ private:
void addContactPoint(float* pos);
void compileContactPoints();
float normFactor(float f);
void updateGearState();
Model _model;
ControlMap _controls;

View file

@ -63,6 +63,7 @@ void ControlMap::addMapping(int input, int type, void* object, int options)
out = new OutRec();
out->type = type;
out->object = object;
out->oldL = out->oldR = out->time = 0;
_outputs.add(out);
}
@ -73,12 +74,8 @@ void ControlMap::addMapping(int input, int type, void* object, int options)
map->idx = out->maps.add(map);
// 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;
if(type==MAGNETOS)
map->src1 = map->dst1 = 3;
map->src1 = map->dst1 = rangeMax(type);
map->src0 = map->dst0 = rangeMin(type);
// And add it to the approproate vectors.
Vector* maps = (Vector*)_inputs.get(input);
@ -91,7 +88,7 @@ void ControlMap::reset()
for(int i=0; i<_outputs.size(); i++) {
OutRec* o = (OutRec*)_outputs.get(i);
for(int j=0; j<o->maps.size(); j++)
((MapRec*)o->maps.get(j))->val = 0;
((MapRec*)(o->maps.get(j)))->val = 0;
}
}
@ -114,7 +111,31 @@ void ControlMap::setInput(int input, float val)
}
}
void ControlMap::applyControls()
int ControlMap::getOutputHandle(void* obj, int type)
{
for(int i=0; i<_outputs.size(); i++) {
OutRec* o = (OutRec*)_outputs.get(i);
if(o->object == obj && o->type == type)
return i;
}
}
void ControlMap::setTransitionTime(int handle, float time)
{
((OutRec*)_outputs.get(handle))->time = time;
}
float ControlMap::getOutput(int handle)
{
return ((OutRec*)_outputs.get(handle))->oldL;
}
float ControlMap::getOutputR(int handle)
{
return ((OutRec*)_outputs.get(handle))->oldR;
}
void ControlMap::applyControls(float dt)
{
int outrec;
for(outrec=0; outrec<_outputs.size(); outrec++) {
@ -139,6 +160,25 @@ void ControlMap::applyControls()
rval += val;
}
// If there is a finite transition time, clamp the values to
// the maximum travel allowed in this dt.
if(o->time > 0) {
float dl = lval - o->oldL;
float dr = rval - o->oldR;
float adl = Math::abs(dl);
float adr = Math::abs(dr);
float max = (dt/o->time) * (rangeMax(o->type) - rangeMin(o->type));
if(adl > max) dl = dl*max/adl;
if(adr > max) dr = dr*max/adr;
lval = o->oldL + dl;
rval = o->oldR + dr;
}
o->oldL = lval;
o->oldR = rval;
void* obj = o->object;
switch(o->type) {
case THROTTLE: ((Thruster*)obj)->setThrottle(lval); break;
@ -162,4 +202,28 @@ void ControlMap::applyControls()
}
}
}; // namespace yasim
float ControlMap::rangeMin(int type)
{
// The minimum of the range for each type of control
switch(type) {
case FLAP0: return -1; // [-1:1]
case FLAP1: return -1;
case STEER: return -1;
case MAGNETOS: return 0; // [0:3]
default: return 0; // [0:1]
}
}
float ControlMap::rangeMax(int type)
{
// The maximum of the range for each type of control
switch(type) {
case FLAP0: return 1; // [-1:1]
case FLAP1: return 1;
case STEER: return 1;
case MAGNETOS: return 3; // [0:3]
default: return 1; // [0:1]
}
}
} // namespace yasim

View file

@ -43,10 +43,31 @@ public:
void setInput(int input, float value);
// Calculates and applies the settings received since the last reset().
void applyControls();
void applyControls(float dt);
// Returns the input/output range appropriate for the given
// control. Ailerons go from -1 to 1, while throttles are never
// lower than zero, etc...
static float rangeMin(int type);
static float rangeMax(int type);
// Each output record is identified by both an object/type tuple
// and a numeric handle.
int getOutputHandle(void* obj, int type);
// Sets the transition time for the control output to swing
// through its full range.
void setTransitionTime(int handle, float time);
// Retrieves the current value of the control output. Controls
// with OPT_SPLIT settable on inputs will have a separately
// computed "right side" value.
float getOutput(int handle);
float getOutputR(int handle);
private:
struct OutRec { int type; void* object; Vector maps; };
struct OutRec { int type; void* object; Vector maps;
float oldL, oldR, time; };
struct MapRec { OutRec* out; int idx; int opt; float val;
float src0; float src1; float dst0; float dst1; };

View file

@ -57,7 +57,8 @@ FGFDM::~FGFDM()
delete[] wr->prop;
delete wr;
}
for(i=0; i<_controlProps.size(); i++)
delete (PropOut*)_controlProps.get(i);
}
void FGFDM::iterate(float dt)
@ -170,8 +171,7 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts)
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);
_airplane.addGear(g);
} else if(eq(name, "fuselage")) {
float b[3];
v[0] = attrf(a, "ax");
@ -225,34 +225,55 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts)
v[1] = attrf(a, "y");
v[2] = attrf(a, "z");
((Thruster*)_currObj)->setDirection(v);
} else if(eq(name, "control")) {
} else if(eq(name, "control-setting")) {
// A cruise or approach control setting
const char* axis = a->getValue("axis");
if(a->hasAttribute("output")) {
// assert: output type must match _currObj type!
const char* output = a->getValue("output");
int opt = 0;
opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0;
opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0;
opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0;
float value = attrf(a, "value", 0);
if(_cruiseCurr)
_airplane.addCruiseControl(parseAxis(axis), value);
else
_airplane.addApproachControl(parseAxis(axis), value);
} else if(eq(name, "control-input")) {
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);
}
// A mapping of input property to a control
int axis = parseAxis(a->getValue("axis"));
int control = parseOutput(a->getValue("control"));
int opt = 0;
opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0;
opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0;
opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0;
ControlMap* cm = _airplane.getControlMap();
if(a->hasAttribute("src0")) {
cm->addMapping(axis, control, _currObj, opt,
attrf(a, "src0"), attrf(a, "src1"),
attrf(a, "dst0"), attrf(a, "dst1"));
} else {
// assert: must be under a "cruise" or "approach" tag
float value = attrf(a, "value", 0);
if(_cruiseCurr)
_airplane.addCruiseControl(parseAxis(axis), value);
else
_airplane.addApproachControl(parseAxis(axis), value);
cm->addMapping(axis, control, _currObj, opt);
}
} else if(eq(name, "control-output")) {
// A property output for a control on the current object
ControlMap* cm = _airplane.getControlMap();
int type = parseOutput(a->getValue("control"));
int handle = cm->getOutputHandle(_currObj, type);
PropOut* p = new PropOut();
p->prop = fgGetNode(a->getValue("prop"), true);
p->handle = handle;
p->type = type;
p->left = !(a->hasAttribute("side") &&
eq("right", a->getValue("side")));
p->min = attrf(a, "min", cm->rangeMin(type));
p->max = attrf(a, "max", cm->rangeMax(type));
_controlProps.add(p);
} else if(eq(name, "control-speed")) {
ControlMap* cm = _airplane.getControlMap();
int type = parseOutput(a->getValue("control"));
int handle = cm->getOutputHandle(_currObj, type);
float time = attrf(a, "transition-time", 0);
cm->setTransitionTime(handle, time);
} else {
*(int*)0=0; // unexpected tag, boom
}
@ -269,22 +290,33 @@ void FGFDM::getExternalInput(float dt)
float val = fgGetFloat(a->name, 0);
cm->setInput(a->handle, val);
}
cm->applyControls();
cm->applyControls(dt);
// Weights
for(i=0; i<_weights.size(); i++) {
WeightRec* wr = (WeightRec*)_weights.get(i);
_airplane.setWeight(wr->handle, fgGetFloat(wr->prop));
}
// Gear state
_airplane.setGearState(fgGetBool("/controls/gear-down"), dt);
}
void FGFDM::setOutputProperties()
{
char buf[256];
int i;
ControlMap* cm = _airplane.getControlMap();
for(i=0; i<_controlProps.size(); i++) {
PropOut* p = (PropOut*)_controlProps.get(i);
float val = (p->left
? cm->getOutput(p->handle)
: cm->getOutputR(p->handle));
float rmin = cm->rangeMin(p->type);
float rmax = cm->rangeMax(p->type);
float frac = (val - rmin) / (rmax - rmin);
val = frac*(p->max - p->min) + p->min;
p->prop->setFloatValue(val);
}
float fuelDensity = 718.95; // default to gasoline: ~6 lb/gal
for(i=0; i<_airplane.numTanks(); i++) {
fuelDensity = _airplane.getFuelDensity(i);
@ -461,8 +493,7 @@ int FGFDM::parseOutput(const char* name)
if(eq(name, "FLAP1")) return ControlMap::FLAP1;
if(eq(name, "SLAT")) return ControlMap::SLAT;
if(eq(name, "SPOILER")) return ControlMap::SPOILER;
// error here...
return *(int*)0;
*(int*)0=0;
}
void FGFDM::parseWeight(XMLAttributes* a)
@ -484,7 +515,7 @@ void FGFDM::parseWeight(XMLAttributes* a)
bool FGFDM::eq(const char* a, const char* b)
{
// Figure it out for yourself. :)
while(*a && *b && *a++ == *b++);
while(*a && *b && *a == *b) { a++; b++; }
return !(*a || *b);
}

View file

@ -30,6 +30,8 @@ private:
struct AxisRec { char* name; int handle; };
struct EngRec { char* prefix; Thruster* eng; };
struct WeightRec { char* prop; float size; int handle; };
struct PropOut { SGPropertyNode* prop; int handle, type; bool left;
float min, max; };
void setOutputProperties();
@ -58,6 +60,9 @@ private:
// Engine types. Contains an EngRec structure.
Vector _thrusters;
// Output properties for the ControlMap
Vector _controlProps;
// Parsing temporaries
void* _currObj;
bool _cruiseCurr;

View file

@ -159,9 +159,10 @@ void YASim::init()
fgSetFloat("/controls/spoilers", 0);
// Are we at ground level? If so, lift the plane up so the gear
// clear the ground
// clear the ground.
double runway_altitude =
fgGetDouble("/environment/ground-elevation-m") * SG_METER_TO_FEET;
fgSetBool("/controls/gear-down", false);
if(get_Altitude() - runway_altitude < 50) {
float minGearZ = 1e18;
for(i=0; i<a->numGear(); i++) {
@ -172,6 +173,7 @@ void YASim::init()
minGearZ = pos[2];
}
_set_Altitude(runway_altitude - minGearZ*M2FT);
fgSetBool("/controls/gear-down", true);
}
// The pilot's eyepoint
@ -418,7 +420,6 @@ void YASim::copyFromYASim()
SGPropertyNode * node = fgGetNode("gear/gear", i, true);
node->setBoolValue("has-brake", g->getBrake() != 0);
node->setBoolValue("wow", g->getCompressFraction() != 0);
node->setBoolValue("position", g->getExtension());
}
for(i=0; i<model->numThrusters(); i++) {