dix: optimize precision in device velocity estimation
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
c184b91d9a
commit
1a9f9ac50f
|
@ -90,11 +90,12 @@ InitVelocityData(DeviceVelocityPtr s)
|
|||
s->corr_mul = 10.0; /* dots per 10 milisecond should be usable */
|
||||
s->const_acceleration = 1.0; /* no acceleration/deceleration */
|
||||
s->reset_time = 300;
|
||||
s->last_reset = FALSE;
|
||||
s->last_dx = 0;
|
||||
s->last_dy = 0;
|
||||
s->use_softening = 1;
|
||||
s->min_acceleration = 1.0; /* don't decelerate */
|
||||
s->coupling = 0.2;
|
||||
s->coupling = 0.25;
|
||||
s->profile_private = NULL;
|
||||
memset(&s->statistics, 0, sizeof(s->statistics));
|
||||
memset(&s->filters, 0, sizeof(s->filters));
|
||||
|
@ -135,6 +136,12 @@ AccelerationDefaultCleanup(DeviceIntPtr pDev){
|
|||
/**
|
||||
Initialize a filter chain.
|
||||
Expected result is a series of filters, each progressively more integrating.
|
||||
|
||||
This allows for two strategies: Either you have one filter which is reasonable
|
||||
and is being coupled to account for fast-changing input, or you have 'one for
|
||||
every situation'. You might want to have loose coupling then, i.e. > 1.
|
||||
E.g. you could start around 1/2 of your anticipated delta t and
|
||||
scale up until several motion deltas are 'averaged'.
|
||||
*/
|
||||
void
|
||||
InitFilterChain(DeviceVelocityPtr s, float rdecay, float progression, int stages, int lutsize)
|
||||
|
@ -164,9 +171,21 @@ CleanupFilterChain(DeviceVelocityPtr s)
|
|||
InitFilterStage(&s->filters[fn], 0, 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
StuffFilterChain(DeviceVelocityPtr s, float value)
|
||||
{
|
||||
int fn;
|
||||
|
||||
for(fn = 0; fn < MAX_VELOCITY_FILTERS; fn++){
|
||||
if(s->filters[fn].rdecay != 0)
|
||||
s->filters[fn].current = value;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust weighting decay and lut in sync
|
||||
* Adjust weighting decay and lut for a stage
|
||||
* The weight fn is designed so its integral 0->inf is unity, so we end
|
||||
* up with a stable (basically IIR) filter. It always draws
|
||||
* towards its more current input values, which have more weight the older
|
||||
|
@ -227,13 +246,12 @@ FeedFilterStage(FilterStagePtr s, float value, int tdiff){
|
|||
|
||||
/**
|
||||
* Select the most filtered matching result. Also, the first
|
||||
* mismatching filter will be set to value (coupling).
|
||||
* mismatching filter may be set to value (coupling).
|
||||
*/
|
||||
static inline float
|
||||
QueryFilterChain(
|
||||
DeviceVelocityPtr s,
|
||||
float value,
|
||||
float maxdiv)
|
||||
float value)
|
||||
{
|
||||
int fn, rfn = 0, cfn = -1;
|
||||
float cur, result = value;
|
||||
|
@ -246,20 +264,21 @@ QueryFilterChain(
|
|||
cur = s->filters[fn].current;
|
||||
|
||||
if (fabs(value - cur) <= 1.0f ||
|
||||
fabs(value - cur) / (value + cur) <= maxdiv){
|
||||
fabs(value - cur) / (value + cur) <= s->coupling){
|
||||
result = cur;
|
||||
rfn = fn; /*remember result determining filter */
|
||||
rfn = fn + 1; /*remember result determining filter */
|
||||
} else if(cfn == -1){
|
||||
cfn = fn; /* rememeber first mismatching filter */
|
||||
}
|
||||
}
|
||||
|
||||
s->statistics.filter_usecount[rfn]++;
|
||||
#ifdef SERIOUS_DEBUGGING
|
||||
ErrorF("(dix ptraccel) result from filter stage %i, input %.2f, output %.2f\n", rfn, value, result);
|
||||
#ifdef PTRACCEL_DEBUGGING
|
||||
ErrorF("(dix ptraccel) result from stage %i, input %.2f, output %.2f\n",
|
||||
rfn, value, result);
|
||||
#endif
|
||||
|
||||
/* override one current (coupling) so the filter
|
||||
/* override first mismatching current (coupling) so the filter
|
||||
* catches up quickly. */
|
||||
if(cfn != -1)
|
||||
s->filters[cfn].current = result;
|
||||
|
@ -296,7 +315,11 @@ GetAxis(int dx, int dy){
|
|||
* return true if non-visible state reset is suggested
|
||||
*/
|
||||
static short
|
||||
ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
||||
ProcessVelocityData(
|
||||
DeviceVelocityPtr s,
|
||||
int dx,
|
||||
int dy,
|
||||
int time)
|
||||
{
|
||||
float cvelocity;
|
||||
|
||||
|
@ -312,7 +335,7 @@ ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
|||
dy += s->last_dy;
|
||||
diff += s->last_diff;
|
||||
s->last_diff = time - s->lrm_time; /* prevent repeating add-up */
|
||||
#ifdef SERIOUS_DEBUGGING
|
||||
#ifdef PTRACCEL_DEBUGGING
|
||||
ErrorF("(dix ptracc) axial correction\n");
|
||||
#endif
|
||||
}else{
|
||||
|
@ -320,9 +343,9 @@ ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
|||
}
|
||||
|
||||
/*
|
||||
* cvelocity is not a real velocity yet, more a motion delta. contant
|
||||
* cvelocity is not a real velocity yet, more a motion delta. constant
|
||||
* acceleration is multiplied here to make the velocity an on-screen
|
||||
* velocity (px/t as opposed to [insert unit]/t). This is intended to
|
||||
* velocity (pix/t as opposed to [insert unit]/t). This is intended to
|
||||
* make multiple devices with widely varying ConstantDecelerations respond
|
||||
* similar to acceleration controls.
|
||||
*/
|
||||
|
@ -330,10 +353,10 @@ ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
|||
|
||||
s->lrm_time = time;
|
||||
|
||||
if (s->reset_time < 0 || diff < 0) { /* disabled or timer overrun? */
|
||||
if (s->reset_time < 0 || diff < 0) { /* reset disabled or timer overrun? */
|
||||
/* simply set velocity from current movement, no reset. */
|
||||
s->velocity = cvelocity;
|
||||
return 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (diff == 0)
|
||||
|
@ -345,23 +368,38 @@ ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
|||
|
||||
/* short-circuit: when nv-reset the rest can be skipped */
|
||||
if(reset == TRUE){
|
||||
s->velocity = cvelocity;
|
||||
return TRUE;
|
||||
StuffFilterChain(s, cvelocity);
|
||||
s->velocity = cvelocity;
|
||||
s->last_reset = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(s->last_reset == TRUE){
|
||||
/*
|
||||
* when here, we're probably processing the second mickey of a starting
|
||||
* stroke. This happens to be the first time we can reasonably pretend
|
||||
* that cvelocity is an actual velocity. Thus, to opt precision, we
|
||||
* stuff that into the filter chain.
|
||||
*/
|
||||
s->last_reset = FALSE;
|
||||
StuffFilterChain(s, cvelocity);
|
||||
s->velocity = cvelocity;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* feed into filter chain */
|
||||
FeedFilterChain(s, cvelocity, diff);
|
||||
|
||||
/* perform coupling and decide final value */
|
||||
s->velocity = QueryFilterChain(s, cvelocity, s->coupling);
|
||||
s->velocity = QueryFilterChain(s, cvelocity);
|
||||
|
||||
#ifdef SERIOUS_DEBUGGING
|
||||
#ifdef PTRACCEL_DEBUGGING
|
||||
ErrorF("(dix ptracc) guess: vel=%.3f diff=%d |%i|%i|%i|%i|\n",
|
||||
s->velocity, diff,
|
||||
s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
|
||||
s->statistics.filter_usecount[2], s->statistics.filter_usecount[3]);
|
||||
#endif
|
||||
return reset;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -689,15 +727,15 @@ acceleratePointerPredictable(DeviceIntPtr pDev, int first_valuator,
|
|||
/* invoke acceleration profile to determine acceleration */
|
||||
mult = velocitydata->Profile(velocitydata,
|
||||
pDev->ptrfeed->ctrl.threshold,
|
||||
(float)(pDev->ptrfeed->ctrl.num) /
|
||||
(float)(pDev->ptrfeed->ctrl.den));
|
||||
(float)pDev->ptrfeed->ctrl.num /
|
||||
(float)pDev->ptrfeed->ctrl.den);
|
||||
|
||||
#ifdef SERIOUS_DEBUGGING
|
||||
#ifdef PTRACCEL_DEBUGGING
|
||||
ErrorF("(dix ptracc) resulting speed multiplier : %.3f\n", mult);
|
||||
#endif
|
||||
/* enforce min_acceleration */
|
||||
if (mult < velocitydata->min_acceleration) {
|
||||
#ifdef SERIOUS_DEBUGGING
|
||||
#ifdef PTRACCEL_DEBUGGING
|
||||
ErrorF("(dix ptracc) enforced min multiplier : %.3f\n",
|
||||
velocitydata->min_acceleration);
|
||||
#endif
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
|
||||
#include <input.h> /* DeviceIntPtr */
|
||||
|
||||
/* maximum number of filters to approximate velocity.
|
||||
* ABI-breaker!
|
||||
*/
|
||||
#define MAX_VELOCITY_FILTERS 8
|
||||
|
||||
/* constants for acceleration profiles;
|
||||
|
@ -53,7 +56,7 @@ typedef float (*PointerAccelerationProfileFunc)
|
|||
float /*threshold*/, float /*acc*/);
|
||||
|
||||
/**
|
||||
* a filter stage contains the data for the adaptive IIR filtering.
|
||||
* a filter stage contains the data for adaptive IIR filtering.
|
||||
* To improve results, one may run several parallel filters
|
||||
* which have different decays. Since more integration means more
|
||||
* delay, a given filter only does good matches in a specific phase of
|
||||
|
@ -77,7 +80,8 @@ typedef struct _DeviceVelocityRec {
|
|||
float velocity; /* velocity as guessed by algorithm */
|
||||
int lrm_time; /* time the last motion event was processed */
|
||||
int last_dx, last_dy; /* last motion delta */
|
||||
int last_diff; /* last time-diff */
|
||||
int last_diff; /* last time-difference */
|
||||
Bool last_reset; /* whether a nv-reset occurred just before */
|
||||
float corr_mul; /* config: multiply this into velocity */
|
||||
float const_acceleration; /* config: (recipr.) const deceleration */
|
||||
float min_acceleration; /* config: minimum acceleration */
|
||||
|
@ -89,7 +93,7 @@ typedef struct _DeviceVelocityRec {
|
|||
void* profile_private;/* extended data, see SetAccelerationProfile() */
|
||||
struct { /* to be able to query this information */
|
||||
int profile_number;
|
||||
int filter_usecount[MAX_VELOCITY_FILTERS];
|
||||
int filter_usecount[MAX_VELOCITY_FILTERS +1];
|
||||
} statistics;
|
||||
} DeviceVelocityRec, *DeviceVelocityPtr;
|
||||
|
||||
|
|
Loading…
Reference in New Issue