1
0
Fork 0

YASim moved one liners

This commit is contained in:
Henning Stahlke 2017-03-01 09:34:35 +01:00
parent ce4df326d7
commit 6efa1ab821
10 changed files with 124 additions and 442 deletions

View file

@ -176,106 +176,12 @@ void Model::iterate()
_integrator.calcNewInterval();
}
bool Model::isCrashed()
{
return _crashed;
}
void Model::setCrashed(bool crashed)
{
_crashed = crashed;
}
float Model::getAGL()
{
return _agl;
}
State* Model::getState()
{
return _s;
}
void Model::setState(State* s)
{
_integrator.setState(s);
_s = _integrator.getState();
}
RigidBody* Model::getBody()
{
return &_body;
}
Integrator* Model::getIntegrator()
{
return &_integrator;
}
Surface* Model::getSurface(int handle)
{
return (Surface*)_surfaces.get(handle);
}
Rotorgear* Model::getRotorgear(void)
{
return &_rotorgear;
}
int Model::addThruster(Thruster* t)
{
return _thrusters.add(t);
}
Hook* Model::getHook(void)
{
return _hook;
}
Launchbar* Model::getLaunchbar(void)
{
return _launchbar;
}
int Model::numThrusters()
{
return _thrusters.size();
}
Thruster* Model::getThruster(int handle)
{
return (Thruster*)_thrusters.get(handle);
}
void Model::setThruster(int handle, Thruster* t)
{
_thrusters.set(handle, t);
}
int Model::addSurface(Surface* surf)
{
return _surfaces.add(surf);
}
int Model::addGear(Gear* gear)
{
return _gears.add(gear);
}
void Model::addHook(Hook* hook)
{
_hook = hook;
}
void Model::addLaunchbar(Launchbar* launchbar)
{
_launchbar = launchbar;
}
int Model::addHitch(Hitch* hitch)
{
return _hitches.add(hitch);
}
void Model::setGroundCallback(Ground* ground_cb)
{
@ -283,30 +189,20 @@ void Model::setGroundCallback(Ground* ground_cb)
_ground_cb = ground_cb;
}
Ground* Model::getGroundCallback(void)
{
return _ground_cb;
}
void Model::setGroundEffect(float* pos, float span, float mul)
void Model::setGroundEffect(const float* pos, const float span, const float mul)
{
Math::set3(pos, _geRefPoint);
_wingSpan = span;
_groundEffect = mul;
}
void Model::setAir(float pressure, float temp, float density)
void Model::setAir(const float pressure, const float temp, const float density)
{
_pressure = pressure;
_temp = temp;
_rho = density;
}
void Model::setWind(float* wind)
{
Math::set3(wind, _wind);
}
void Model::updateGround(State* s)
{
float dummy[3];
@ -582,7 +478,7 @@ void Model::newState(State* s)
// Calculates the airflow direction at the given point and for the
// specified aircraft velocity.
void Model::localWind(float* pos, State* s, float* out, float alt, bool is_rotor)
void Model::localWind(const float* pos, yasim::State* s, float* out, float alt, bool is_rotor)
{
float tmp[3], lwind[3], lrot[3], lv[3];

View file

@ -26,49 +26,49 @@ public:
Model();
virtual ~Model();
RigidBody* getBody();
Integrator* getIntegrator();
RigidBody* getBody() { return &_body; }
Integrator* getIntegrator() { return &_integrator; }
void setTurbulence(Turbulence* turb) { _turb = turb; }
State* getState();
State* getState() { return _s; }
void setState(State* s);
bool isCrashed();
void setCrashed(bool crashed);
float getAGL();
bool isCrashed() { return _crashed; }
void setCrashed(bool crashed) { _crashed = crashed; }
float getAGL() { return _agl; }
void iterate();
// Externally-managed subcomponents
int addThruster(Thruster* t);
int addSurface(Surface* surf);
int addGear(Gear* gear);
void addHook(Hook* hook);
void addLaunchbar(Launchbar* launchbar);
Surface* getSurface(int handle);
Rotorgear* getRotorgear(void);
int addThruster(Thruster* t) { return _thrusters.add(t); }
int addSurface(Surface* surf) { return _surfaces.add(surf); }
int addGear(Gear* gear) { return _gears.add(gear); }
void addHook(Hook* hook) { _hook = hook; }
void addLaunchbar(Launchbar* launchbar) { _launchbar = launchbar; }
Surface* getSurface(int handle) { return (Surface*)_surfaces.get(handle); }
Rotorgear* getRotorgear(void) { return &_rotorgear; }
Gear* getGear(int handle);
Hook* getHook(void);
int addHitch(Hitch* hitch);
Launchbar* getLaunchbar(void);
Hook* getHook(void) { return _hook; }
int addHitch(Hitch* hitch) { return _hitches.add(hitch); }
Launchbar* getLaunchbar(void) { return _launchbar; }
// Semi-private methods for use by the Airplane solver.
int numThrusters();
Thruster* getThruster(int handle);
void setThruster(int handle, Thruster* t);
int numThrusters() { return _thrusters.size(); }
Thruster* getThruster(int handle) { return (Thruster*)_thrusters.get(handle); }
void setThruster(int handle, Thruster* t) { _thrusters.set(handle, t); }
void initIteration();
void getThrust(float* out);
void setGroundCallback(Ground* ground_cb);
Ground* getGroundCallback(void);
Ground* getGroundCallback(void) { return _ground_cb; }
//
// Per-iteration settables
//
void setGroundEffect(float* pos, float span, float mul);
void setWind(float* wind);
void setAir(float pressure, float temp, float density);
void setGroundEffect(const float* pos, const float span, const float mul);
void setWind(float* wind) { Math::set3(wind, _wind); }
void setAir(const float pressure, const float temp, const float density);
void updateGround(State* s);
@ -80,8 +80,7 @@ private:
void initRotorIteration();
void calcGearForce(Gear* g, float* v, float* rot, float* ground);
float gearFriction(float wgt, float v, Gear* g);
void localWind(float* pos, State* s, float* out, float alt,
bool is_rotor = false);
void localWind(const float* pos, State* s, float* out, float alt, bool is_rotor = false);
Integrator _integrator;
RigidBody _body;

View file

@ -1,5 +1,4 @@
#include <Main/fg_props.hxx>
#include "Math.hpp"
#include "RigidBody.hpp"
namespace yasim {
@ -54,7 +53,7 @@ void RigidBody::setMass(int handle, float mass)
_bodyN->getChild("mass", handle, true)->getNode("mass", true)->setFloatValue(mass);
}
void RigidBody::setMass(int handle, float mass, float* pos, bool isStatic)
void RigidBody::setMass(int handle, float mass, const float* pos, bool isStatic)
{
_masses[handle].m = mass;
_masses[handle].isStatic = isStatic;
@ -69,16 +68,6 @@ void RigidBody::setMass(int handle, float mass, float* pos, bool isStatic)
}
}
int RigidBody::numMasses()
{
return _nMasses;
}
float RigidBody::getMass(int handle)
{
return _masses[handle].m;
}
void RigidBody::getMassPosition(int handle, float* out)
{
out[0] = _masses[handle].p[0];
@ -86,24 +75,14 @@ void RigidBody::getMassPosition(int handle, float* out)
out[2] = _masses[handle].p[2];
}
float RigidBody::getTotalMass()
{
return _totalMass;
}
// Calcualtes the rotational velocity of a particular point. All
// coordinates are local!
void RigidBody::pointVelocity(float* pos, float* rot, float* out)
void RigidBody::pointVelocity(const float* pos, const float* rot, float* out)
{
Math::sub3(pos, _cg, out); // out = pos-cg
Math::cross3(rot, out, out); // = rot cross (pos-cg)
}
void RigidBody::setGyro(float* angularMomentum)
{
Math::set3(angularMomentum, _gyro);
}
void RigidBody::_recalcStatic()
{
// aggregate all masses that do not change (e.g. fuselage, wings) into one point mass
@ -227,17 +206,7 @@ void RigidBody::reset()
_force[0] = _force[1] = _force[2] = 0;
}
void RigidBody::addForce(float* force)
{
Math::add3(_force, force, _force);
}
void RigidBody::addTorque(float* torque)
{
Math::add3(_torque, torque, _torque);
}
void RigidBody::addForce(float* pos, float* force)
void RigidBody::addForce(const float* pos, const float* force)
{
addForce(force);
@ -249,48 +218,29 @@ void RigidBody::addForce(float* pos, float* force)
addTorque(t);
}
/// not used (?)
void RigidBody::setBodySpin(float* rotation)
{
Math::set3(rotation, _spin);
}
void RigidBody::getCG(float* cgOut)
{
Math::set3(_cg, cgOut);
}
/// return acceleration at c.g.
void RigidBody::getAccel(float* accelOut)
{
Math::mul3(1/_totalMass, _force, accelOut);
}
/// return acceleration at pos (unused?)
void RigidBody::getAccel(float* pos, float* accelOut)
{
getAccel(accelOut);
// centripetal accelerations
// Calculate normalized spin axis "a" and rate (radians/sec) from spin vector.
// Turn the "spin" vector into a normalized spin axis "a" and a
// radians/sec scalar "rate".
float a[3];
float rate = Math::mag3(_spin);
if (rate != 0) {
float a[3], v[3];
Math::sub3(_cg, pos, v); // distance = cg - pos;
if (v[0] == 0 && v[1] == 0 && v[2] == 0) // nothing to do
return;
Math::mul3(1/rate, _spin, a);
// d_a = a * (distance dot a); (projection of distance vector on a, |a|=1)
Math::mul3(Math::dot3(v, a), a, a);
Math::add3(v, a, v); // v = distance + projection;
Math::set3(_spin, a);
if (rate !=0 )
Math::mul3(1/rate, a, a);
//an else branch is not neccesary. a, which is a=(0,0,0) in the else case, is only used in a dot product
float v[3];
Math::sub3(_cg, pos, v); // v = cg - pos
Math::mul3(Math::dot3(v, a), a, a); // a = a * (v dot a)
Math::add3(v, a, v); // v = v + a
// Now v contains the vector from pos to the rotation axis.
// Multiply by the square of the rotation rate to get the linear
// acceleration.
// nothing to do in the next two lines, if rate == 0
Math::mul3(rate*rate, v, v);
Math::add3(v, accelOut, accelOut);
}
}
void RigidBody::getAngularAccel(float* accelOut)
{

View file

@ -2,6 +2,7 @@
#define _RIGIDBODY_HPP
#include <simgear/props/props.hxx>
#include "Vector.hpp"
#include "Math.hpp"
namespace yasim {
@ -34,16 +35,16 @@ public:
// Modifies a previously-added point mass (fuel tank running dry,
// gear going up, swing wing swinging, pilot bailing out, etc...)
void setMass(int handle, float mass);
void setMass(int handle, float mass, float* pos, bool isStatic = false);
void setMass(int handle, float mass, const float* pos, bool isStatic = false);
int numMasses();
float getMass(int handle);
int numMasses() { return _nMasses; }
float getMass(int handle) { return _masses[handle].m; }
void getMassPosition(int handle, float* out);
float getTotalMass();
float getTotalMass() { return _totalMass; }
// The velocity, in local coordinates, of the specified point on a
// body rotating about its c.g. with velocity rot.
void pointVelocity(float* pos, float* rot, float* out);
void pointVelocity(const float* pos, const float* rot, float* out);
// Sets the "gyroscope" for the body. This is the total
// "intrinsic" angular momentum of the body; that is, rotations of
@ -51,7 +52,7 @@ public:
// frame. Because angular momentum is additive in this way, we
// don't need to specify specific gyro objects; just add all their
// momenta together and set it here.
void setGyro(float* angularMomentum);
void setGyro(const float* angularMomentum) { Math::set3(angularMomentum, _gyro); }
// When masses are moved or changed, this object needs to
@ -63,15 +64,14 @@ public:
// Resets the current force/torque parameters to zero.
void reset();
// Applies a force at the center of gravity.
void addForce(const float* force) { Math::add3(_force, force, _force); }
// Applies a force at the specified position.
void addForce(float* pos, float* force);
// Applies a force at the center of gravity.
void addForce(float* force);
void addForce(const float* pos, const float* force);
// Adds a torque with the specified axis and magnitude
void addTorque(float* torque);
void addTorque(const float* torque) { Math::add3(_torque, torque, _torque); }
// Sets the rotation rate of the body (about its c.g.) within the
// surrounding environment. This is needed to compute torque on
@ -79,17 +79,15 @@ public:
// rotation. NOTE: the rotation vector, like all other
// coordinates used here, is specified IN THE LOCAL COORDINATE
// SYSTEM.
void setBodySpin(float* rotation);
void setBodySpin(const float* rotation) { Math::set3(rotation, _spin); }
// Returns the center of gravity of the masses, in the body
// coordinate system.
void getCG(float* cgOut);
void getCG(float* cgOut) { Math::set3(_cg, cgOut); }
// Returns the acceleration of the body's c.g. relative to the
// rest of the world, specified in local coordinates.
void getAccel(float* accelOut);
void getAccel(float* accelOut) { Math::mul3(1/_totalMass, _force, accelOut); }
// Returns the acceleration of a specific location in local
// coordinates. If the body is rotating, this will be different

View file

@ -962,7 +962,7 @@ float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
);
}
void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
void Rotor::getDownWash(const float *pos, const float *v_heli, float *downwash)
{
float pos2rotor[3],tmp[3];
Math::sub3(_base,pos,pos2rotor);
@ -1632,7 +1632,7 @@ void Rotorgear::compile()
}
}
void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
void Rotorgear::getDownWash(const float *pos, const float * v_heli, float *downwash)
{
float tmp[3];
downwash[0]=downwash[1]=downwash[2]=0;

View file

@ -107,7 +107,7 @@ public:
void updateDirectionsAndPositions(float *rot);
void getTip(float* tip);
void calcLiftFactor(float* v, float rho, State *s);
void getDownWash(float *pos, float * v_heli, float *downwash);
void getDownWash(const float* pos, const float* v_heli, float* downwash);
int getNumberOfBlades(){return _number_of_blades;}
void setDownwashFactor(float value);
@ -287,7 +287,7 @@ public:
float getEnginePropFactor() {return _engine_prop_factor;}
Vector* getRotors() { return &_rotors;}
void initRotorIteration(float *lrot,float dt);
void getDownWash(float *pos, float * v_heli, float *downwash);
void getDownWash(const float* pos, const float* v_heli, float* downwash);
int getValueforFGSet(int j,char *b,float *f);
};

View file

@ -1,5 +1,4 @@
#include <Main/fg_props.hxx>
#include "Math.hpp"
#include "Surface.hpp"
namespace yasim {
@ -53,7 +52,7 @@ Surface::Surface( Version * version ) :
}
void Surface::setPosition(float* p)
void Surface::setPosition(const float* p)
{
int i;
for(i=0; i<3; i++) _pos[i] = p[i];
@ -64,83 +63,11 @@ void Surface::setPosition(float* p)
}
}
void Surface::getPosition(float* out)
void Surface::setOrientation(const float* o)
{
int i;
for(i=0; i<3; i++) out[i] = _pos[i];
for(int i=0; i<9; i++) _orient[i] = o[i];
}
void Surface::setChord(float chord)
{
_chord = chord;
}
void Surface::setTotalDrag(float c0)
{
_c0 = c0;
}
float Surface::getTotalDrag()
{
return _c0;
}
void Surface::setXDrag(float cx)
{
_cx = cx;
}
void Surface::setYDrag(float cy)
{
_cy = cy;
}
void Surface::setZDrag(float cz)
{
_cz = cz;
}
float Surface::getXDrag()
{
return _cx;
}
void Surface::setBaseZDrag(float cz0)
{
_cz0 = cz0;
}
void Surface::setStallPeak(int i, float peak)
{
_peaks[i] = peak;
}
void Surface::setStall(int i, float alpha)
{
_stalls[i] = alpha;
}
void Surface::setStallWidth(int i, float width)
{
_widths[i] = width;
}
void Surface::setOrientation(float* o)
{
int i;
for(i=0; i<9; i++)
_orient[i] = o[i];
}
void Surface::setIncidence(float angle)
{
_incidence = angle;
}
void Surface::setTwist(float angle)
{
_twist = angle;
}
void Surface::setSlatParams(float stallDelta, float dragPenalty)
{
@ -168,17 +95,6 @@ void Surface::setFlapPos(float pos)
}
}
void Surface::setFlapEffectiveness(float effectiveness)
{
_flapEffectiveness = effectiveness;
}
double Surface::getFlapEffectiveness()
{
return _flapEffectiveness;
}
void Surface::setSlatPos(float pos)
{
if (_slatPos != pos) {

View file

@ -3,6 +3,7 @@
#include <simgear/props/props.hxx>
#include "Version.hpp"
#include "Math.hpp"
namespace yasim {
@ -21,11 +22,11 @@ public:
static void resetIDgen() { s_idGenerator = 0; };
// Position of this surface in local coords
void setPosition(float* p);
void getPosition(float* out);
void setPosition(const float* p);
void getPosition(float* out) { Math::set3(_pos, out); }
// Distance scale along the X axis
void setChord(float chord);
void setChord(float chord) { _chord = chord; }
// Slats act to move the stall peak by the specified angle, and
// increase drag by the multiplier specified.
@ -44,37 +45,37 @@ public:
void setSpoilerPos(float pos);
// Modifier for flap lift coefficient, useful for simulating flap blowing etc.
void setFlapEffectiveness(float effectiveness);
double getFlapEffectiveness();
void setFlapEffectiveness(float effectiveness) { _flapEffectiveness = effectiveness; }
double getFlapEffectiveness() { return _flapEffectiveness; }
// local -> Surface coords
void setOrientation(float* o);
void setOrientation(const float* o);
// For variable-incidence control surfaces. The angle is a
// negative rotation about the surface's Y axis, in radians, so
// positive is "up" (i.e. "positive AoA")
void setIncidence(float angle);
void setIncidence(float angle) { _incidence = angle; }
// The offset from base incidence for this surface.
void setTwist(float angle);
void setTwist(float angle) { _twist = angle; }
void setTotalDrag(float c0);
float getTotalDrag();
void setTotalDrag(float c0) { _c0 = c0; }
float getTotalDrag() { return _c0; }
void setXDrag(float cx);
void setYDrag(float cy);
void setZDrag(float cz);
float getXDrag();
void setXDrag(float cx) { _cx = cx; }
void setYDrag(float cy) { _cy = cy; }
void setZDrag(float cz) { _cz = cz; }
float getXDrag() { return _cx; }
// zero-alpha Z drag ("camber") specified as a fraction of cz
void setBaseZDrag(float cz0);
void setBaseZDrag(float cz0) { _cz0 = cz0; }
// i: 0 == forward, 1 == backwards
void setStallPeak(int i, float peak);
void setStallPeak(int i, float peak) { _peaks[i] = peak; }
// i: 0 == fwd/+z, 1 == fwd/-z, 2 == rev/+z, 3 == rev/-z
void setStall(int i, float alpha);
void setStallWidth(int i, float width);
void setStall(int i, float alpha) { _stalls[i] = alpha; }
void setStallWidth(int i, float width) { _widths[i] = width; }
// Induced drag multiplier
void setInducedDrag(float mul) { _inducedDrag = mul; }

View file

@ -54,82 +54,6 @@ Wing::~Wing()
}
}
int Wing::numSurfaces()
{
return _surfs.size();
}
Surface* Wing::getSurface(int n)
{
return ((SurfRec*)_surfs.get(n))->surface;
}
float Wing::getSurfaceWeight(int n)
{
return ((SurfRec*)_surfs.get(n))->weight;
}
void Wing::setMirror(bool mirror)
{
_mirror = mirror;
}
void Wing::setBase(float* base)
{
int i;
for(i=0; i<3; i++) _base[i] = base[i];
}
void Wing::setLength(float length)
{
_length = length;
}
void Wing::setChord(float chord)
{
_chord = chord;
}
void Wing::setTaper(float taper)
{
_taper = taper;
}
void Wing::setSweep(float sweep)
{
_sweep = sweep;
}
void Wing::setDihedral(float dihedral)
{
_dihedral = dihedral;
}
void Wing::setStall(float aoa)
{
_stall = aoa;
}
void Wing::setStallWidth(float angle)
{
_stallWidth = angle;
}
void Wing::setStallPeak(float fraction)
{
_stallPeak = fraction;
}
void Wing::setTwist(float angle)
{
_twist = angle;
}
void Wing::setCamber(float camber)
{
_camber = camber;
}
void Wing::setIncidence(float incidence)
{
_incidence = incidence;
@ -372,22 +296,16 @@ void Wing::compile()
}
}
}
// Last of all, re-set the incidence in case setIncidence() was
// called before we were compiled.
setIncidence(_incidence);
}
float Wing::getDragScale()
{
return _dragScale;
}
void Wing::setDragScale(float scale)
{
_dragScale = scale;
int i;
for(i=0; i<_surfs.size(); i++) {
for(int i=0; i<_surfs.size(); i++) {
SurfRec* s = (SurfRec*)_surfs.get(i);
s->surface->setTotalDrag(scale * s->weight);
}
@ -396,16 +314,10 @@ void Wing::setDragScale(float scale)
void Wing::setLiftRatio(float ratio)
{
_liftRatio = ratio;
int i;
for(i=0; i<_surfs.size(); i++)
for(int i=0; i<_surfs.size(); i++)
((SurfRec*)_surfs.get(i))->surface->setZDrag(ratio);
}
float Wing::getLiftRatio()
{
return _liftRatio;
}
Surface* Wing::newSurface(float* pos, float* orient, float chord,
bool flap0, bool flap1, bool slat, bool spoiler)
{

View file

@ -16,30 +16,42 @@ public:
~Wing();
// Do we mirror ourselves about the XZ plane?
void setMirror(bool mirror);
void setMirror(bool mirror) { _mirror = mirror; }
bool isMirrored() { return _mirror; };
// Wing geometry:
void setBase(float* base); // in local coordinates
void setLength(float length); // dist. ALONG wing (not span!)
void setChord(float chord); // at base, measured along X axis
void setTaper(float taper); // fraction, 0-1
void setSweep(float sweep); // radians
void setDihedral(float dihedral); // radians, positive is "up"
// Wing geometry in local coordinates:
// base point of wing
void setBase(const float* base) { Math::set3(base, _base); }
void getBase(float* base) { Math::set3(_base, base); };
// dist. ALONG wing (not span!)
void setLength(float length) { _length = length; }
float getLength() { return _length; };
// at base, measured along X axis
void setChord(float chord) { _chord = chord; }
float getChord() { return _chord; };
// fraction of chord at wing tip, 0..1
void setTaper(float taper) { _taper = taper; }
float getTaper() { return _taper; };
// radians
void setSweep(float sweep) { _sweep = sweep; }
float getSweep() { return _sweep; };
// radians, positive is "up"
void setDihedral(float dihedral) { _dihedral = dihedral; }
float getDihedral() { return _dihedral; };
void setStall(float aoa);
void setStallWidth(float angle);
void setStallPeak(float fraction);
void setTwist(float angle);
void setCamber(float camber);
void setIncidence(float incidence);
void setTwist(float angle) { _twist = angle; }
// parameters for stall curve
void setStall(float aoa) { _stall = aoa; }
void setStallWidth(float angle) { _stallWidth = angle; }
void setStallPeak(float fraction) { _stallPeak = fraction; }
void setCamber(float camber) { _camber = camber; }
void setInducedDrag(float drag) { _inducedDrag = drag; }
void setFlap0Params(float start, float end, float lift, float drag);
void setFlap1Params(float start, float end, float lift, float drag);
void setSpoilerParams(float start, float end, float lift, float drag);
@ -56,7 +68,6 @@ public:
// Compile the thing into a bunch of Surface objects
void compile();
void getTip(float* tip) { Math::set3(_tip, tip);};
bool isMirrored() { return _mirror; };
// valid only after Wing::compile() was called
float getSpan() { return _wingspan; };
@ -64,20 +75,19 @@ public:
float getAspectRatio() { return _aspectRatio; };
float getMAC() { return _meanChord; };
// Query the list of Surface objects
int numSurfaces();
Surface* getSurface(int n);
float getSurfaceWeight(int n);
int numSurfaces() { return _surfs.size(); }
Surface* getSurface(int n) { return ((SurfRec*)_surfs.get(n))->surface; }
float getSurfaceWeight(int n) { return ((SurfRec*)_surfs.get(n))->weight; }
// The overall drag coefficient for the wing as a whole. Units are
// arbitrary.
void setDragScale(float scale);
float getDragScale();
float getDragScale() { return _dragScale; }
// The ratio of force along the Z (lift) direction of each wing
// segment to that along the X (drag) direction.
void setLiftRatio(float ratio);
float getLiftRatio();
float getLiftRatio() { return _liftRatio; }
private:
void interp(float* v1, float* v2, float frac, float* out);