357 lines
12 KiB
C++
357 lines
12 KiB
C++
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "yasim-common.hpp"
|
|
#include "Jet.hpp"
|
|
#include "Thruster.hpp"
|
|
#include "PropEngine.hpp"
|
|
#include "PistonEngine.hpp"
|
|
#include "TurbineEngine.hpp"
|
|
#include "Gear.hpp"
|
|
#include "Hook.hpp"
|
|
#include "Launchbar.hpp"
|
|
#include "Wing.hpp"
|
|
#include "Rotor.hpp"
|
|
#include "Math.hpp"
|
|
#include "Propeller.hpp"
|
|
#include "Hitch.hpp"
|
|
|
|
#include "ControlMap.hpp"
|
|
namespace yasim {
|
|
|
|
ControlMap::~ControlMap()
|
|
{
|
|
int i;
|
|
for(i=0; i<_inputs.size(); i++) {
|
|
Vector* v = (Vector*)_inputs.get(i);
|
|
int j;
|
|
for(j=0; j<v->size(); j++)
|
|
delete (MapRec*)v->get(j);
|
|
delete v;
|
|
}
|
|
|
|
for(i=0; i<_outputs.size(); i++)
|
|
delete (OutRec*)_outputs.get(i);
|
|
|
|
for(i=0; i<_properties.size(); i++) {
|
|
PropHandle* p = (PropHandle*)_properties.get(i);
|
|
delete[] p->name;
|
|
delete p;
|
|
}
|
|
}
|
|
|
|
/**
|
|
input : index to _inputs
|
|
type: identifier (see enum OutputType)
|
|
*/
|
|
void ControlMap::addMapping(int input, Control control, void* object, int options,
|
|
float src0, float src1, float dst0, float dst1)
|
|
{
|
|
addMapping(input, control, 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;
|
|
}
|
|
|
|
/**
|
|
input : index to _inputs
|
|
type: identifier (see enum OutputType)
|
|
*/
|
|
void ControlMap::addMapping(int input, Control control, void* object, int options)
|
|
{
|
|
// See if the output object already exists
|
|
OutRec* out {nullptr};
|
|
for(int i = 0; i < _outputs.size(); i++) {
|
|
OutRec* o = (OutRec*)_outputs.get(i);
|
|
if(o->object == object && o->control == control) {
|
|
out = o;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create one if it doesn't
|
|
if(out == nullptr) {
|
|
out = new OutRec();
|
|
out->control = control;
|
|
out->object = object;
|
|
_outputs.add(out);
|
|
}
|
|
|
|
// Make a new input record
|
|
MapRec* map = new MapRec();
|
|
map->out = out;
|
|
map->opt = options;
|
|
map->idx = out->maps.add(map);
|
|
|
|
// The default ranges differ depending on type!
|
|
map->src1 = map->dst1 = rangeMax(control);
|
|
map->src0 = map->dst0 = rangeMin(control);
|
|
|
|
// And add it to the approproate vectors.
|
|
Vector* maps = (Vector*)_inputs.get(input);
|
|
maps->add(map);
|
|
}
|
|
|
|
void ControlMap::reset()
|
|
{
|
|
// Set all the values to zero
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControlMap::setInput(int input, float val)
|
|
{
|
|
Vector* maps = (Vector*)_inputs.get(input);
|
|
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].
|
|
val2 = Math::clamp(val2, m->src0, m->src1);
|
|
val2 = (val2 - m->src0) / (m->src1 - m->src0);
|
|
m->val = m->dst0 + val2 * (m->dst1 - m->dst0);
|
|
}
|
|
}
|
|
|
|
int ControlMap::getOutputHandle(void* obj, Control control)
|
|
{
|
|
for(int i=0; i<_outputs.size(); i++) {
|
|
OutRec* o = (OutRec*)_outputs.get(i);
|
|
if(o->object == obj && o->control == control)
|
|
return i;
|
|
}
|
|
fprintf(stderr, "ControlMap::getOutputHandle cannot find *%d, control %d \n", obj, control);
|
|
return -1;
|
|
}
|
|
|
|
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++) {
|
|
OutRec* o = (OutRec*)_outputs.get(outrec);
|
|
|
|
// Generate a summed value. Note the check for "split"
|
|
// control axes like ailerons.
|
|
float lval = 0, rval = 0;
|
|
int i;
|
|
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(m->opt & OPT_INVERT)
|
|
val = -val;
|
|
lval += val;
|
|
if(m->opt & OPT_SPLIT)
|
|
rval -= val;
|
|
else
|
|
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->control) - rangeMin(o->control));
|
|
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->control) {
|
|
case THROTTLE: ((Thruster*)obj)->setThrottle(lval); break;
|
|
case MIXTURE: ((Thruster*)obj)->setMixture(lval); break;
|
|
case CONDLEVER: ((TurbineEngine*)((PropEngine*)
|
|
obj)->getEngine())->setCondLever(lval); break;
|
|
case STARTER: ((Thruster*)obj)->setStarter(lval != 0.0); break;
|
|
case MAGNETOS: ((PropEngine*)obj)->setMagnetos((int)lval); break;
|
|
case ADVANCE: ((PropEngine*)obj)->setAdvance(lval); break;
|
|
case PROPPITCH: ((PropEngine*)obj)->setPropPitch(lval); break;
|
|
case PROPFEATHER: ((PropEngine*)obj)->setPropFeather((int)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;
|
|
case HEXTEND: ((Hook*)obj)->setExtension(lval); break;
|
|
case LEXTEND: ((Launchbar*)obj)->setExtension(lval); break;
|
|
case LACCEL: ((Launchbar*)obj)->setAcceleration(lval); break;
|
|
case CASTERING:((Gear*)obj)->setCastering(lval != 0); break;
|
|
case SLAT: ((Wing*)obj)->setFlapPos(WING_SLAT,lval); break;
|
|
case FLAP0: ((Wing*)obj)->setFlapPos(WING_FLAP0, lval, rval); break;
|
|
case FLAP0EFFECTIVENESS: ((Wing*)obj)->setFlapEffectiveness(WING_FLAP0,lval); break;
|
|
case FLAP1: ((Wing*)obj)->setFlapPos(WING_FLAP1,lval, rval); break;
|
|
case FLAP1EFFECTIVENESS: ((Wing*)obj)->setFlapEffectiveness(WING_FLAP1,lval); break;
|
|
case SPOILER: ((Wing*)obj)->setFlapPos(WING_SPOILER, lval, rval); break;
|
|
case COLLECTIVE: ((Rotor*)obj)->setCollective(lval); break;
|
|
case CYCLICAIL: ((Rotor*)obj)->setCyclicail(lval,rval); break;
|
|
case CYCLICELE: ((Rotor*)obj)->setCyclicele(lval,rval); break;
|
|
case TILTPITCH: ((Rotor*)obj)->setTiltPitch(lval); break;
|
|
case TILTYAW: ((Rotor*)obj)->setTiltYaw(lval); break;
|
|
case TILTROLL: ((Rotor*)obj)->setTiltRoll(lval); break;
|
|
case ROTORBALANCE:
|
|
((Rotor*)obj)->setRotorBalance(lval); break;
|
|
case ROTORBRAKE: ((Rotorgear*)obj)->setRotorBrake(lval); break;
|
|
case ROTORENGINEON:
|
|
((Rotorgear*)obj)->setEngineOn((int)lval); break;
|
|
case ROTORENGINEMAXRELTORQUE:
|
|
((Rotorgear*)obj)->setRotorEngineMaxRelTorque(lval); break;
|
|
case ROTORRELTARGET:
|
|
((Rotorgear*)obj)->setRotorRelTarget(lval); break;
|
|
case REVERSE_THRUST: ((Jet*)obj)->setReverse(lval != 0); break;
|
|
case BOOST:
|
|
((PistonEngine*)((Thruster*)obj)->getEngine())->setBoost(lval);
|
|
break;
|
|
case WASTEGATE:
|
|
((PistonEngine*)((Thruster*)obj)->getEngine())->setWastegate(lval);
|
|
break;
|
|
case WINCHRELSPEED: ((Hitch*)obj)->setWinchRelSpeed(lval); break;
|
|
case HITCHOPEN: ((Hitch*)obj)->setOpen(lval!=0); break;
|
|
case PLACEWINCH: ((Hitch*)obj)->setWinchPositionAuto(lval!=0); break;
|
|
case FINDAITOW: ((Hitch*)obj)->findBestAIObject(lval!=0); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
float ControlMap::rangeMin(Control control)
|
|
{
|
|
// The minimum of the range for each type of control
|
|
switch(control) {
|
|
case FLAP0: return -1; // [-1:1]
|
|
case FLAP1: return -1;
|
|
case STEER: return -1;
|
|
case CYCLICELE: return -1;
|
|
case CYCLICAIL: return -1;
|
|
case COLLECTIVE: return -1;
|
|
case WINCHRELSPEED: return -1;
|
|
case MAGNETOS: return 0; // [0:3]
|
|
case FLAP0EFFECTIVENESS: return 1; // [0:10]
|
|
case FLAP1EFFECTIVENESS: return 1; // [0:10]
|
|
default: return 0; // [0:1]
|
|
}
|
|
}
|
|
|
|
float ControlMap::rangeMax(Control control)
|
|
{
|
|
// The maximum of the range for each type of control
|
|
switch(control) {
|
|
case FLAP0: return 1; // [-1:1]
|
|
case FLAP1: return 1;
|
|
case STEER: return 1;
|
|
case MAGNETOS: return 3; // [0:3]
|
|
case FLAP0EFFECTIVENESS: return 10;// [0:10]
|
|
case FLAP1EFFECTIVENESS: return 10;// [0:10]
|
|
default: return 1; // [0:1]
|
|
}
|
|
}
|
|
|
|
/// register property name, return ID (int)
|
|
int ControlMap::propertyHandle(const char* name)
|
|
{
|
|
for(int i=0; i < _properties.size(); i++) {
|
|
PropHandle* p = (PropHandle*)_properties.get(i);
|
|
if(eq(p->name, name))
|
|
return p->handle;
|
|
}
|
|
|
|
// create new
|
|
PropHandle* p = new PropHandle();
|
|
p->name = dup(name);
|
|
|
|
fgGetNode(p->name, true);
|
|
|
|
Vector* v = new Vector();
|
|
p->handle = _inputs.add(v);
|
|
_properties.add(p);
|
|
return p->handle;
|
|
}
|
|
|
|
|
|
ControlMap::Control ControlMap::parseControl(const char* name)
|
|
{
|
|
if(eq(name, "THROTTLE")) return THROTTLE;
|
|
if(eq(name, "MIXTURE")) return MIXTURE;
|
|
if(eq(name, "CONDLEVER")) return CONDLEVER;
|
|
if(eq(name, "STARTER")) return STARTER;
|
|
if(eq(name, "MAGNETOS")) return MAGNETOS;
|
|
if(eq(name, "ADVANCE")) return ADVANCE;
|
|
if(eq(name, "REHEAT")) return REHEAT;
|
|
if(eq(name, "BOOST")) return BOOST;
|
|
if(eq(name, "VECTOR")) return VECTOR;
|
|
if(eq(name, "PROP")) return PROP;
|
|
if(eq(name, "BRAKE")) return BRAKE;
|
|
if(eq(name, "STEER")) return STEER;
|
|
if(eq(name, "EXTEND")) return EXTEND;
|
|
if(eq(name, "HEXTEND")) return HEXTEND;
|
|
if(eq(name, "LEXTEND")) return LEXTEND;
|
|
if(eq(name, "LACCEL")) return LACCEL;
|
|
if(eq(name, "INCIDENCE")) return INCIDENCE;
|
|
if(eq(name, "FLAP0")) return FLAP0;
|
|
if(eq(name, "FLAP0EFFECTIVENESS")) return FLAP0EFFECTIVENESS;
|
|
if(eq(name, "FLAP1")) return FLAP1;
|
|
if(eq(name, "FLAP1EFFECTIVENESS")) return FLAP1EFFECTIVENESS;
|
|
if(eq(name, "SLAT")) return SLAT;
|
|
if(eq(name, "SPOILER")) return SPOILER;
|
|
if(eq(name, "CASTERING")) return CASTERING;
|
|
if(eq(name, "PROPPITCH")) return PROPPITCH;
|
|
if(eq(name, "PROPFEATHER")) return PROPFEATHER;
|
|
if(eq(name, "COLLECTIVE")) return COLLECTIVE;
|
|
if(eq(name, "CYCLICAIL")) return CYCLICAIL;
|
|
if(eq(name, "CYCLICELE")) return CYCLICELE;
|
|
if(eq(name, "TILTROLL")) return TILTROLL;
|
|
if(eq(name, "TILTPITCH")) return TILTPITCH;
|
|
if(eq(name, "TILTYAW")) return TILTYAW;
|
|
if(eq(name, "ROTORGEARENGINEON")) return ROTORENGINEON;
|
|
if(eq(name, "ROTORBRAKE")) return ROTORBRAKE;
|
|
if(eq(name, "ROTORENGINEMAXRELTORQUE")) return ROTORENGINEMAXRELTORQUE;
|
|
if(eq(name, "ROTORRELTARGET")) return ROTORRELTARGET;
|
|
if(eq(name, "ROTORBALANCE")) return ROTORBALANCE;
|
|
if(eq(name, "REVERSE_THRUST")) return REVERSE_THRUST;
|
|
if(eq(name, "WASTEGATE")) return WASTEGATE;
|
|
if(eq(name, "WINCHRELSPEED")) return WINCHRELSPEED;
|
|
if(eq(name, "HITCHOPEN")) return HITCHOPEN;
|
|
if(eq(name, "PLACEWINCH")) return PLACEWINCH;
|
|
if(eq(name, "FINDAITOW")) return FINDAITOW;
|
|
SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '" << name
|
|
<< "' in YASim aircraft description.");
|
|
exit(1);
|
|
}
|
|
|
|
} // namespace yasim
|