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->corr_mul = 10.0; /* dots per 10 milisecond should be usable */
|
||||||
s->const_acceleration = 1.0; /* no acceleration/deceleration */
|
s->const_acceleration = 1.0; /* no acceleration/deceleration */
|
||||||
s->reset_time = 300;
|
s->reset_time = 300;
|
||||||
|
s->last_reset = FALSE;
|
||||||
s->last_dx = 0;
|
s->last_dx = 0;
|
||||||
s->last_dy = 0;
|
s->last_dy = 0;
|
||||||
s->use_softening = 1;
|
s->use_softening = 1;
|
||||||
s->min_acceleration = 1.0; /* don't decelerate */
|
s->min_acceleration = 1.0; /* don't decelerate */
|
||||||
s->coupling = 0.2;
|
s->coupling = 0.25;
|
||||||
s->profile_private = NULL;
|
s->profile_private = NULL;
|
||||||
memset(&s->statistics, 0, sizeof(s->statistics));
|
memset(&s->statistics, 0, sizeof(s->statistics));
|
||||||
memset(&s->filters, 0, sizeof(s->filters));
|
memset(&s->filters, 0, sizeof(s->filters));
|
||||||
|
@ -135,6 +136,12 @@ AccelerationDefaultCleanup(DeviceIntPtr pDev){
|
||||||
/**
|
/**
|
||||||
Initialize a filter chain.
|
Initialize a filter chain.
|
||||||
Expected result is a series of filters, each progressively more integrating.
|
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
|
void
|
||||||
InitFilterChain(DeviceVelocityPtr s, float rdecay, float progression, int stages, int lutsize)
|
InitFilterChain(DeviceVelocityPtr s, float rdecay, float progression, int stages, int lutsize)
|
||||||
|
@ -164,9 +171,21 @@ CleanupFilterChain(DeviceVelocityPtr s)
|
||||||
InitFilterStage(&s->filters[fn], 0, 0);
|
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
|
* 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
|
* up with a stable (basically IIR) filter. It always draws
|
||||||
* towards its more current input values, which have more weight the older
|
* 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
|
* 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
|
static inline float
|
||||||
QueryFilterChain(
|
QueryFilterChain(
|
||||||
DeviceVelocityPtr s,
|
DeviceVelocityPtr s,
|
||||||
float value,
|
float value)
|
||||||
float maxdiv)
|
|
||||||
{
|
{
|
||||||
int fn, rfn = 0, cfn = -1;
|
int fn, rfn = 0, cfn = -1;
|
||||||
float cur, result = value;
|
float cur, result = value;
|
||||||
|
@ -246,20 +264,21 @@ QueryFilterChain(
|
||||||
cur = s->filters[fn].current;
|
cur = s->filters[fn].current;
|
||||||
|
|
||||||
if (fabs(value - cur) <= 1.0f ||
|
if (fabs(value - cur) <= 1.0f ||
|
||||||
fabs(value - cur) / (value + cur) <= maxdiv){
|
fabs(value - cur) / (value + cur) <= s->coupling){
|
||||||
result = cur;
|
result = cur;
|
||||||
rfn = fn; /*remember result determining filter */
|
rfn = fn + 1; /*remember result determining filter */
|
||||||
} else if(cfn == -1){
|
} else if(cfn == -1){
|
||||||
cfn = fn; /* rememeber first mismatching filter */
|
cfn = fn; /* rememeber first mismatching filter */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s->statistics.filter_usecount[rfn]++;
|
s->statistics.filter_usecount[rfn]++;
|
||||||
#ifdef SERIOUS_DEBUGGING
|
#ifdef PTRACCEL_DEBUGGING
|
||||||
ErrorF("(dix ptraccel) result from filter stage %i, input %.2f, output %.2f\n", rfn, value, result);
|
ErrorF("(dix ptraccel) result from stage %i, input %.2f, output %.2f\n",
|
||||||
|
rfn, value, result);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* override one current (coupling) so the filter
|
/* override first mismatching current (coupling) so the filter
|
||||||
* catches up quickly. */
|
* catches up quickly. */
|
||||||
if(cfn != -1)
|
if(cfn != -1)
|
||||||
s->filters[cfn].current = result;
|
s->filters[cfn].current = result;
|
||||||
|
@ -296,7 +315,11 @@ GetAxis(int dx, int dy){
|
||||||
* return true if non-visible state reset is suggested
|
* return true if non-visible state reset is suggested
|
||||||
*/
|
*/
|
||||||
static short
|
static short
|
||||||
ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
ProcessVelocityData(
|
||||||
|
DeviceVelocityPtr s,
|
||||||
|
int dx,
|
||||||
|
int dy,
|
||||||
|
int time)
|
||||||
{
|
{
|
||||||
float cvelocity;
|
float cvelocity;
|
||||||
|
|
||||||
|
@ -312,7 +335,7 @@ ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
||||||
dy += s->last_dy;
|
dy += s->last_dy;
|
||||||
diff += s->last_diff;
|
diff += s->last_diff;
|
||||||
s->last_diff = time - s->lrm_time; /* prevent repeating add-up */
|
s->last_diff = time - s->lrm_time; /* prevent repeating add-up */
|
||||||
#ifdef SERIOUS_DEBUGGING
|
#ifdef PTRACCEL_DEBUGGING
|
||||||
ErrorF("(dix ptracc) axial correction\n");
|
ErrorF("(dix ptracc) axial correction\n");
|
||||||
#endif
|
#endif
|
||||||
}else{
|
}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
|
* 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
|
* make multiple devices with widely varying ConstantDecelerations respond
|
||||||
* similar to acceleration controls.
|
* similar to acceleration controls.
|
||||||
*/
|
*/
|
||||||
|
@ -330,10 +353,10 @@ ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time)
|
||||||
|
|
||||||
s->lrm_time = 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. */
|
/* simply set velocity from current movement, no reset. */
|
||||||
s->velocity = cvelocity;
|
s->velocity = cvelocity;
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff == 0)
|
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 */
|
/* short-circuit: when nv-reset the rest can be skipped */
|
||||||
if(reset == TRUE){
|
if(reset == TRUE){
|
||||||
|
StuffFilterChain(s, cvelocity);
|
||||||
s->velocity = cvelocity;
|
s->velocity = cvelocity;
|
||||||
|
s->last_reset = TRUE;
|
||||||
return 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 */
|
/* feed into filter chain */
|
||||||
FeedFilterChain(s, cvelocity, diff);
|
FeedFilterChain(s, cvelocity, diff);
|
||||||
|
|
||||||
/* perform coupling and decide final value */
|
/* 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",
|
ErrorF("(dix ptracc) guess: vel=%.3f diff=%d |%i|%i|%i|%i|\n",
|
||||||
s->velocity, diff,
|
s->velocity, diff,
|
||||||
s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
|
s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
|
||||||
s->statistics.filter_usecount[2], s->statistics.filter_usecount[3]);
|
s->statistics.filter_usecount[2], s->statistics.filter_usecount[3]);
|
||||||
#endif
|
#endif
|
||||||
return reset;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -689,15 +727,15 @@ acceleratePointerPredictable(DeviceIntPtr pDev, int first_valuator,
|
||||||
/* invoke acceleration profile to determine acceleration */
|
/* invoke acceleration profile to determine acceleration */
|
||||||
mult = velocitydata->Profile(velocitydata,
|
mult = velocitydata->Profile(velocitydata,
|
||||||
pDev->ptrfeed->ctrl.threshold,
|
pDev->ptrfeed->ctrl.threshold,
|
||||||
(float)(pDev->ptrfeed->ctrl.num) /
|
(float)pDev->ptrfeed->ctrl.num /
|
||||||
(float)(pDev->ptrfeed->ctrl.den));
|
(float)pDev->ptrfeed->ctrl.den);
|
||||||
|
|
||||||
#ifdef SERIOUS_DEBUGGING
|
#ifdef PTRACCEL_DEBUGGING
|
||||||
ErrorF("(dix ptracc) resulting speed multiplier : %.3f\n", mult);
|
ErrorF("(dix ptracc) resulting speed multiplier : %.3f\n", mult);
|
||||||
#endif
|
#endif
|
||||||
/* enforce min_acceleration */
|
/* enforce min_acceleration */
|
||||||
if (mult < velocitydata->min_acceleration) {
|
if (mult < velocitydata->min_acceleration) {
|
||||||
#ifdef SERIOUS_DEBUGGING
|
#ifdef PTRACCEL_DEBUGGING
|
||||||
ErrorF("(dix ptracc) enforced min multiplier : %.3f\n",
|
ErrorF("(dix ptracc) enforced min multiplier : %.3f\n",
|
||||||
velocitydata->min_acceleration);
|
velocitydata->min_acceleration);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
|
|
||||||
#include <input.h> /* DeviceIntPtr */
|
#include <input.h> /* DeviceIntPtr */
|
||||||
|
|
||||||
|
/* maximum number of filters to approximate velocity.
|
||||||
|
* ABI-breaker!
|
||||||
|
*/
|
||||||
#define MAX_VELOCITY_FILTERS 8
|
#define MAX_VELOCITY_FILTERS 8
|
||||||
|
|
||||||
/* constants for acceleration profiles;
|
/* constants for acceleration profiles;
|
||||||
|
@ -53,7 +56,7 @@ typedef float (*PointerAccelerationProfileFunc)
|
||||||
float /*threshold*/, float /*acc*/);
|
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
|
* To improve results, one may run several parallel filters
|
||||||
* which have different decays. Since more integration means more
|
* which have different decays. Since more integration means more
|
||||||
* delay, a given filter only does good matches in a specific phase of
|
* 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 */
|
float velocity; /* velocity as guessed by algorithm */
|
||||||
int lrm_time; /* time the last motion event was processed */
|
int lrm_time; /* time the last motion event was processed */
|
||||||
int last_dx, last_dy; /* last motion delta */
|
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 corr_mul; /* config: multiply this into velocity */
|
||||||
float const_acceleration; /* config: (recipr.) const deceleration */
|
float const_acceleration; /* config: (recipr.) const deceleration */
|
||||||
float min_acceleration; /* config: minimum acceleration */
|
float min_acceleration; /* config: minimum acceleration */
|
||||||
|
@ -89,7 +93,7 @@ typedef struct _DeviceVelocityRec {
|
||||||
void* profile_private;/* extended data, see SetAccelerationProfile() */
|
void* profile_private;/* extended data, see SetAccelerationProfile() */
|
||||||
struct { /* to be able to query this information */
|
struct { /* to be able to query this information */
|
||||||
int profile_number;
|
int profile_number;
|
||||||
int filter_usecount[MAX_VELOCITY_FILTERS];
|
int filter_usecount[MAX_VELOCITY_FILTERS +1];
|
||||||
} statistics;
|
} statistics;
|
||||||
} DeviceVelocityRec, *DeviceVelocityPtr;
|
} DeviceVelocityRec, *DeviceVelocityPtr;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue