diff options
author | 2012-06-03 23:14:14 -0700 | |
---|---|---|
committer | 2012-06-04 12:34:03 -0700 | |
commit | 51df04b93e8e362edd867abd7efaf1659b8b8b82 (patch) | |
tree | 5c416d400d53da96891465fd5b602e063925622b | |
parent | a5b0698231459ac5b54cf8e8952ac5c2b2b2198b (diff) |
Port the legacy velocity tracker strategy.
For comparison purposes, port the legacy velocity tracker
algorithm as it behaved prior to ICS.
Bug: 6413587
Change-Id: I7e8e56584dcdb1a3c660ca9d8f9c5bd5d868e449
-rw-r--r-- | include/androidfw/VelocityTracker.h | 39 | ||||
-rw-r--r-- | libs/androidfw/VelocityTracker.cpp | 126 |
2 files changed, 165 insertions, 0 deletions
diff --git a/include/androidfw/VelocityTracker.h b/include/androidfw/VelocityTracker.h index 3e6436a6097e..8c24219c0bfe 100644 --- a/include/androidfw/VelocityTracker.h +++ b/include/androidfw/VelocityTracker.h @@ -225,6 +225,45 @@ private: void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; }; + +/* + * Velocity tracker strategy used prior to ICS. + */ +class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + LegacyVelocityTrackerStrategy(); + virtual ~LegacyVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Oldest sample to consider when calculating the velocity. + static const nsecs_t HORIZON = 200 * 1000000; // 100 ms + + // Number of samples to keep. + static const uint32_t HISTORY_SIZE = 20; + + // The minimum duration between samples when estimating velocity. + static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms + + struct Movement { + nsecs_t eventTime; + BitSet32 idBits; + VelocityTracker::Position positions[MAX_POINTERS]; + + inline const VelocityTracker::Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } + }; + + uint32_t mIndex; + Movement mMovements[HISTORY_SIZE]; +}; + } // namespace android #endif // _ANDROIDFW_VELOCITY_TRACKER_H diff --git a/libs/androidfw/VelocityTracker.cpp b/libs/androidfw/VelocityTracker.cpp index fc7557670204..f48ec6226e70 100644 --- a/libs/androidfw/VelocityTracker.cpp +++ b/libs/androidfw/VelocityTracker.cpp @@ -190,6 +190,13 @@ VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { // for acceleration but it typically overestimates the effect. return new IntegratingVelocityTrackerStrategy(2); } + if (!strcmp("legacy", strategy)) { + // Legacy velocity tracker algorithm. Quality: POOR. + // For comparison purposes only. This algorithm is strongly influenced by + // old data points, consistently underestimates velocity and takes a very long + // time to adjust to changes in direction. + return new LegacyVelocityTrackerStrategy(); + } return NULL; } @@ -799,4 +806,123 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, outEstimator->yCoeff[2] = state.yaccel / 2; } + +// --- LegacyVelocityTrackerStrategy --- + +const nsecs_t LegacyVelocityTrackerStrategy::HORIZON; +const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE; +const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION; + +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { + clear(); +} + +LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { +} + +void LegacyVelocityTrackerStrategy::clear() { + mIndex = 0; + mMovements[0].idBits.clear(); +} + +void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); + mMovements[mIndex].idBits = remainingIdBits; +} + +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + if (++mIndex == HISTORY_SIZE) { + mIndex = 0; + } + + Movement& movement = mMovements[mIndex]; + movement.eventTime = eventTime; + movement.idBits = idBits; + uint32_t count = idBits.count(); + for (uint32_t i = 0; i < count; i++) { + movement.positions[i] = positions[i]; + } +} + +bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + const Movement& newestMovement = mMovements[mIndex]; + if (!newestMovement.idBits.hasBit(id)) { + return false; // no data + } + + // Find the oldest sample that contains the pointer and that is not older than HORIZON. + nsecs_t minTime = newestMovement.eventTime - HORIZON; + uint32_t oldestIndex = mIndex; + uint32_t numTouches = 1; + do { + uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; + const Movement& nextOldestMovement = mMovements[nextOldestIndex]; + if (!nextOldestMovement.idBits.hasBit(id) + || nextOldestMovement.eventTime < minTime) { + break; + } + oldestIndex = nextOldestIndex; + } while (++numTouches < HISTORY_SIZE); + + // Calculate an exponentially weighted moving average of the velocity estimate + // at different points in time measured relative to the oldest sample. + // This is essentially an IIR filter. Newer samples are weighted more heavily + // than older samples. Samples at equal time points are weighted more or less + // equally. + // + // One tricky problem is that the sample data may be poorly conditioned. + // Sometimes samples arrive very close together in time which can cause us to + // overestimate the velocity at that time point. Most samples might be measured + // 16ms apart but some consecutive samples could be only 0.5sm apart because + // the hardware or driver reports them irregularly or in bursts. + float accumVx = 0; + float accumVy = 0; + uint32_t index = oldestIndex; + uint32_t samplesUsed = 0; + const Movement& oldestMovement = mMovements[oldestIndex]; + const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); + nsecs_t lastDuration = 0; + + while (numTouches-- > 1) { + if (++index == HISTORY_SIZE) { + index = 0; + } + const Movement& movement = mMovements[index]; + nsecs_t duration = movement.eventTime - oldestMovement.eventTime; + + // If the duration between samples is small, we may significantly overestimate + // the velocity. Consequently, we impose a minimum duration constraint on the + // samples that we include in the calculation. + if (duration >= MIN_DURATION) { + const VelocityTracker::Position& position = movement.getPosition(id); + float scale = 1000000000.0f / duration; // one over time delta in seconds + float vx = (position.x - oldestPosition.x) * scale; + float vy = (position.y - oldestPosition.y) * scale; + accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); + accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); + lastDuration = duration; + samplesUsed += 1; + } + } + + // Report velocity. + const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); + outEstimator->time = newestMovement.eventTime; + outEstimator->confidence = 1; + outEstimator->xCoeff[0] = newestPosition.x; + outEstimator->yCoeff[0] = newestPosition.y; + if (samplesUsed) { + outEstimator->xCoeff[1] = accumVx; + outEstimator->yCoeff[1] = accumVy; + outEstimator->degree = 1; + } else { + outEstimator->degree = 0; + } + return true; +} + } // namespace android |