Skip to content

Commit 51df04b

Browse files
author
Jeff Brown
committed
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
1 parent a5b0698 commit 51df04b

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

include/androidfw/VelocityTracker.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,45 @@ class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
225225
void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
226226
};
227227

228+
229+
/*
230+
* Velocity tracker strategy used prior to ICS.
231+
*/
232+
class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
233+
public:
234+
LegacyVelocityTrackerStrategy();
235+
virtual ~LegacyVelocityTrackerStrategy();
236+
237+
virtual void clear();
238+
virtual void clearPointers(BitSet32 idBits);
239+
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
240+
const VelocityTracker::Position* positions);
241+
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
242+
243+
private:
244+
// Oldest sample to consider when calculating the velocity.
245+
static const nsecs_t HORIZON = 200 * 1000000; // 100 ms
246+
247+
// Number of samples to keep.
248+
static const uint32_t HISTORY_SIZE = 20;
249+
250+
// The minimum duration between samples when estimating velocity.
251+
static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
252+
253+
struct Movement {
254+
nsecs_t eventTime;
255+
BitSet32 idBits;
256+
VelocityTracker::Position positions[MAX_POINTERS];
257+
258+
inline const VelocityTracker::Position& getPosition(uint32_t id) const {
259+
return positions[idBits.getIndexOfBit(id)];
260+
}
261+
};
262+
263+
uint32_t mIndex;
264+
Movement mMovements[HISTORY_SIZE];
265+
};
266+
228267
} // namespace android
229268

230269
#endif // _ANDROIDFW_VELOCITY_TRACKER_H

libs/androidfw/VelocityTracker.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
190190
// for acceleration but it typically overestimates the effect.
191191
return new IntegratingVelocityTrackerStrategy(2);
192192
}
193+
if (!strcmp("legacy", strategy)) {
194+
// Legacy velocity tracker algorithm. Quality: POOR.
195+
// For comparison purposes only. This algorithm is strongly influenced by
196+
// old data points, consistently underestimates velocity and takes a very long
197+
// time to adjust to changes in direction.
198+
return new LegacyVelocityTrackerStrategy();
199+
}
193200
return NULL;
194201
}
195202

@@ -799,4 +806,123 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
799806
outEstimator->yCoeff[2] = state.yaccel / 2;
800807
}
801808

809+
810+
// --- LegacyVelocityTrackerStrategy ---
811+
812+
const nsecs_t LegacyVelocityTrackerStrategy::HORIZON;
813+
const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE;
814+
const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION;
815+
816+
LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
817+
clear();
818+
}
819+
820+
LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
821+
}
822+
823+
void LegacyVelocityTrackerStrategy::clear() {
824+
mIndex = 0;
825+
mMovements[0].idBits.clear();
826+
}
827+
828+
void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
829+
BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
830+
mMovements[mIndex].idBits = remainingIdBits;
831+
}
832+
833+
void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
834+
const VelocityTracker::Position* positions) {
835+
if (++mIndex == HISTORY_SIZE) {
836+
mIndex = 0;
837+
}
838+
839+
Movement& movement = mMovements[mIndex];
840+
movement.eventTime = eventTime;
841+
movement.idBits = idBits;
842+
uint32_t count = idBits.count();
843+
for (uint32_t i = 0; i < count; i++) {
844+
movement.positions[i] = positions[i];
845+
}
846+
}
847+
848+
bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id,
849+
VelocityTracker::Estimator* outEstimator) const {
850+
outEstimator->clear();
851+
852+
const Movement& newestMovement = mMovements[mIndex];
853+
if (!newestMovement.idBits.hasBit(id)) {
854+
return false; // no data
855+
}
856+
857+
// Find the oldest sample that contains the pointer and that is not older than HORIZON.
858+
nsecs_t minTime = newestMovement.eventTime - HORIZON;
859+
uint32_t oldestIndex = mIndex;
860+
uint32_t numTouches = 1;
861+
do {
862+
uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
863+
const Movement& nextOldestMovement = mMovements[nextOldestIndex];
864+
if (!nextOldestMovement.idBits.hasBit(id)
865+
|| nextOldestMovement.eventTime < minTime) {
866+
break;
867+
}
868+
oldestIndex = nextOldestIndex;
869+
} while (++numTouches < HISTORY_SIZE);
870+
871+
// Calculate an exponentially weighted moving average of the velocity estimate
872+
// at different points in time measured relative to the oldest sample.
873+
// This is essentially an IIR filter. Newer samples are weighted more heavily
874+
// than older samples. Samples at equal time points are weighted more or less
875+
// equally.
876+
//
877+
// One tricky problem is that the sample data may be poorly conditioned.
878+
// Sometimes samples arrive very close together in time which can cause us to
879+
// overestimate the velocity at that time point. Most samples might be measured
880+
// 16ms apart but some consecutive samples could be only 0.5sm apart because
881+
// the hardware or driver reports them irregularly or in bursts.
882+
float accumVx = 0;
883+
float accumVy = 0;
884+
uint32_t index = oldestIndex;
885+
uint32_t samplesUsed = 0;
886+
const Movement& oldestMovement = mMovements[oldestIndex];
887+
const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
888+
nsecs_t lastDuration = 0;
889+
890+
while (numTouches-- > 1) {
891+
if (++index == HISTORY_SIZE) {
892+
index = 0;
893+
}
894+
const Movement& movement = mMovements[index];
895+
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
896+
897+
// If the duration between samples is small, we may significantly overestimate
898+
// the velocity. Consequently, we impose a minimum duration constraint on the
899+
// samples that we include in the calculation.
900+
if (duration >= MIN_DURATION) {
901+
const VelocityTracker::Position& position = movement.getPosition(id);
902+
float scale = 1000000000.0f / duration; // one over time delta in seconds
903+
float vx = (position.x - oldestPosition.x) * scale;
904+
float vy = (position.y - oldestPosition.y) * scale;
905+
accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
906+
accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
907+
lastDuration = duration;
908+
samplesUsed += 1;
909+
}
910+
}
911+
912+
// Report velocity.
913+
const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
914+
outEstimator->time = newestMovement.eventTime;
915+
outEstimator->confidence = 1;
916+
outEstimator->xCoeff[0] = newestPosition.x;
917+
outEstimator->yCoeff[0] = newestPosition.y;
918+
if (samplesUsed) {
919+
outEstimator->xCoeff[1] = accumVx;
920+
outEstimator->yCoeff[1] = accumVy;
921+
outEstimator->degree = 1;
922+
} else {
923+
outEstimator->degree = 0;
924+
}
925+
return true;
926+
}
927+
802928
} // namespace android

0 commit comments

Comments
 (0)