VelocityTracker.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. /*
  2. * Copyright (C) 2012 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #define LOG_TAG "VelocityTracker"
  17. //#define LOG_NDEBUG 0
  18. // Log debug messages about velocity tracking.
  19. #define DEBUG_VELOCITY 0
  20. // Log debug messages about the progress of the algorithm itself.
  21. #define DEBUG_STRATEGY 0
  22. #include <array>
  23. #include <inttypes.h>
  24. #include <limits.h>
  25. #include <math.h>
  26. #include <optional>
  27. #include <android-base/stringprintf.h>
  28. #include <cutils/properties.h>
  29. #include <input/VelocityTracker.h>
  30. #include <utils/BitSet.h>
  31. #include <utils/Timers.h>
  32. namespace android {
  33. // Nanoseconds per milliseconds.
  34. static const nsecs_t NANOS_PER_MS = 1000000;
  35. // Threshold for determining that a pointer has stopped moving.
  36. // Some input devices do not send ACTION_MOVE events in the case where a pointer has
  37. // stopped. We need to detect this case so that we can accurately predict the
  38. // velocity after the pointer starts moving again.
  39. static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS;
  40. static float vectorDot(const float* a, const float* b, uint32_t m) {
  41. float r = 0;
  42. for (size_t i = 0; i < m; i++) {
  43. r += *(a++) * *(b++);
  44. }
  45. return r;
  46. }
  47. static float vectorNorm(const float* a, uint32_t m) {
  48. float r = 0;
  49. for (size_t i = 0; i < m; i++) {
  50. float t = *(a++);
  51. r += t * t;
  52. }
  53. return sqrtf(r);
  54. }
  55. #if DEBUG_STRATEGY || DEBUG_VELOCITY
  56. static std::string vectorToString(const float* a, uint32_t m) {
  57. std::string str;
  58. str += "[";
  59. for (size_t i = 0; i < m; i++) {
  60. if (i) {
  61. str += ",";
  62. }
  63. str += android::base::StringPrintf(" %f", *(a++));
  64. }
  65. str += " ]";
  66. return str;
  67. }
  68. #endif
  69. #if DEBUG_STRATEGY
  70. static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
  71. std::string str;
  72. str = "[";
  73. for (size_t i = 0; i < m; i++) {
  74. if (i) {
  75. str += ",";
  76. }
  77. str += " [";
  78. for (size_t j = 0; j < n; j++) {
  79. if (j) {
  80. str += ",";
  81. }
  82. str += android::base::StringPrintf(" %f", a[rowMajor ? i * n + j : j * m + i]);
  83. }
  84. str += " ]";
  85. }
  86. str += " ]";
  87. return str;
  88. }
  89. #endif
  90. // --- VelocityTracker ---
  91. // The default velocity tracker strategy.
  92. // Although other strategies are available for testing and comparison purposes,
  93. // this is the strategy that applications will actually use. Be very careful
  94. // when adjusting the default strategy because it can dramatically affect
  95. // (often in a bad way) the user experience.
  96. const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
  97. VelocityTracker::VelocityTracker(const char* strategy) :
  98. mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
  99. char value[PROPERTY_VALUE_MAX];
  100. // Allow the default strategy to be overridden using a system property for debugging.
  101. if (!strategy) {
  102. int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
  103. if (length > 0) {
  104. strategy = value;
  105. } else {
  106. strategy = DEFAULT_STRATEGY;
  107. }
  108. }
  109. // Configure the strategy.
  110. if (!configureStrategy(strategy)) {
  111. ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
  112. if (!configureStrategy(DEFAULT_STRATEGY)) {
  113. LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
  114. strategy);
  115. }
  116. }
  117. }
  118. VelocityTracker::~VelocityTracker() {
  119. delete mStrategy;
  120. }
  121. bool VelocityTracker::configureStrategy(const char* strategy) {
  122. mStrategy = createStrategy(strategy);
  123. return mStrategy != nullptr;
  124. }
  125. VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
  126. if (!strcmp("impulse", strategy)) {
  127. // Physical model of pushing an object. Quality: VERY GOOD.
  128. // Works with duplicate coordinates, unclean finger liftoff.
  129. return new ImpulseVelocityTrackerStrategy();
  130. }
  131. if (!strcmp("lsq1", strategy)) {
  132. // 1st order least squares. Quality: POOR.
  133. // Frequently underfits the touch data especially when the finger accelerates
  134. // or changes direction. Often underestimates velocity. The direction
  135. // is overly influenced by historical touch points.
  136. return new LeastSquaresVelocityTrackerStrategy(1);
  137. }
  138. if (!strcmp("lsq2", strategy)) {
  139. // 2nd order least squares. Quality: VERY GOOD.
  140. // Pretty much ideal, but can be confused by certain kinds of touch data,
  141. // particularly if the panel has a tendency to generate delayed,
  142. // duplicate or jittery touch coordinates when the finger is released.
  143. return new LeastSquaresVelocityTrackerStrategy(2);
  144. }
  145. if (!strcmp("lsq3", strategy)) {
  146. // 3rd order least squares. Quality: UNUSABLE.
  147. // Frequently overfits the touch data yielding wildly divergent estimates
  148. // of the velocity when the finger is released.
  149. return new LeastSquaresVelocityTrackerStrategy(3);
  150. }
  151. if (!strcmp("wlsq2-delta", strategy)) {
  152. // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
  153. return new LeastSquaresVelocityTrackerStrategy(2,
  154. LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
  155. }
  156. if (!strcmp("wlsq2-central", strategy)) {
  157. // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
  158. return new LeastSquaresVelocityTrackerStrategy(2,
  159. LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
  160. }
  161. if (!strcmp("wlsq2-recent", strategy)) {
  162. // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
  163. return new LeastSquaresVelocityTrackerStrategy(2,
  164. LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
  165. }
  166. if (!strcmp("int1", strategy)) {
  167. // 1st order integrating filter. Quality: GOOD.
  168. // Not as good as 'lsq2' because it cannot estimate acceleration but it is
  169. // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
  170. // the velocity of a fling but this strategy tends to respond to changes in
  171. // direction more quickly and accurately.
  172. return new IntegratingVelocityTrackerStrategy(1);
  173. }
  174. if (!strcmp("int2", strategy)) {
  175. // 2nd order integrating filter. Quality: EXPERIMENTAL.
  176. // For comparison purposes only. Unlike 'int1' this strategy can compensate
  177. // for acceleration but it typically overestimates the effect.
  178. return new IntegratingVelocityTrackerStrategy(2);
  179. }
  180. if (!strcmp("legacy", strategy)) {
  181. // Legacy velocity tracker algorithm. Quality: POOR.
  182. // For comparison purposes only. This algorithm is strongly influenced by
  183. // old data points, consistently underestimates velocity and takes a very long
  184. // time to adjust to changes in direction.
  185. return new LegacyVelocityTrackerStrategy();
  186. }
  187. return nullptr;
  188. }
  189. void VelocityTracker::clear() {
  190. mCurrentPointerIdBits.clear();
  191. mActivePointerId = -1;
  192. mStrategy->clear();
  193. }
  194. void VelocityTracker::clearPointers(BitSet32 idBits) {
  195. BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value);
  196. mCurrentPointerIdBits = remainingIdBits;
  197. if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
  198. mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
  199. }
  200. mStrategy->clearPointers(idBits);
  201. }
  202. void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
  203. while (idBits.count() > MAX_POINTERS) {
  204. idBits.clearLastMarkedBit();
  205. }
  206. if ((mCurrentPointerIdBits.value & idBits.value)
  207. && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
  208. #if DEBUG_VELOCITY
  209. ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
  210. (eventTime - mLastEventTime) * 0.000001f);
  211. #endif
  212. // We have not received any movements for too long. Assume that all pointers
  213. // have stopped.
  214. mStrategy->clear();
  215. }
  216. mLastEventTime = eventTime;
  217. mCurrentPointerIdBits = idBits;
  218. if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
  219. mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
  220. }
  221. mStrategy->addMovement(eventTime, idBits, positions);
  222. #if DEBUG_VELOCITY
  223. ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d",
  224. eventTime, idBits.value, mActivePointerId);
  225. for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
  226. uint32_t id = iterBits.firstMarkedBit();
  227. uint32_t index = idBits.getIndexOfBit(id);
  228. iterBits.clearBit(id);
  229. Estimator estimator;
  230. getEstimator(id, &estimator);
  231. ALOGD(" %d: position (%0.3f, %0.3f), "
  232. "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
  233. id, positions[index].x, positions[index].y,
  234. int(estimator.degree),
  235. vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
  236. vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
  237. estimator.confidence);
  238. }
  239. #endif
  240. }
  241. void VelocityTracker::addMovement(const MotionEvent* event) {
  242. int32_t actionMasked = event->getActionMasked();
  243. switch (actionMasked) {
  244. case AMOTION_EVENT_ACTION_DOWN:
  245. case AMOTION_EVENT_ACTION_HOVER_ENTER:
  246. // Clear all pointers on down before adding the new movement.
  247. clear();
  248. break;
  249. case AMOTION_EVENT_ACTION_POINTER_DOWN: {
  250. // Start a new movement trace for a pointer that just went down.
  251. // We do this on down instead of on up because the client may want to query the
  252. // final velocity for a pointer that just went up.
  253. BitSet32 downIdBits;
  254. downIdBits.markBit(event->getPointerId(event->getActionIndex()));
  255. clearPointers(downIdBits);
  256. break;
  257. }
  258. case AMOTION_EVENT_ACTION_MOVE:
  259. case AMOTION_EVENT_ACTION_HOVER_MOVE:
  260. break;
  261. default:
  262. // Ignore all other actions because they do not convey any new information about
  263. // pointer movement. We also want to preserve the last known velocity of the pointers.
  264. // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
  265. // of the pointers that went up. ACTION_POINTER_UP does include the new position of
  266. // pointers that remained down but we will also receive an ACTION_MOVE with this
  267. // information if any of them actually moved. Since we don't know how many pointers
  268. // will be going up at once it makes sense to just wait for the following ACTION_MOVE
  269. // before adding the movement.
  270. return;
  271. }
  272. size_t pointerCount = event->getPointerCount();
  273. if (pointerCount > MAX_POINTERS) {
  274. pointerCount = MAX_POINTERS;
  275. }
  276. BitSet32 idBits;
  277. for (size_t i = 0; i < pointerCount; i++) {
  278. idBits.markBit(event->getPointerId(i));
  279. }
  280. uint32_t pointerIndex[MAX_POINTERS];
  281. for (size_t i = 0; i < pointerCount; i++) {
  282. pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
  283. }
  284. nsecs_t eventTime;
  285. Position positions[pointerCount];
  286. size_t historySize = event->getHistorySize();
  287. for (size_t h = 0; h < historySize; h++) {
  288. eventTime = event->getHistoricalEventTime(h);
  289. for (size_t i = 0; i < pointerCount; i++) {
  290. uint32_t index = pointerIndex[i];
  291. positions[index].x = event->getHistoricalX(i, h);
  292. positions[index].y = event->getHistoricalY(i, h);
  293. }
  294. addMovement(eventTime, idBits, positions);
  295. }
  296. eventTime = event->getEventTime();
  297. for (size_t i = 0; i < pointerCount; i++) {
  298. uint32_t index = pointerIndex[i];
  299. positions[index].x = event->getX(i);
  300. positions[index].y = event->getY(i);
  301. }
  302. addMovement(eventTime, idBits, positions);
  303. }
  304. bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
  305. Estimator estimator;
  306. if (getEstimator(id, &estimator) && estimator.degree >= 1) {
  307. *outVx = estimator.xCoeff[1];
  308. *outVy = estimator.yCoeff[1];
  309. return true;
  310. }
  311. *outVx = 0;
  312. *outVy = 0;
  313. return false;
  314. }
  315. bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
  316. return mStrategy->getEstimator(id, outEstimator);
  317. }
  318. // --- LeastSquaresVelocityTrackerStrategy ---
  319. LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
  320. uint32_t degree, Weighting weighting) :
  321. mDegree(degree), mWeighting(weighting) {
  322. clear();
  323. }
  324. LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
  325. }
  326. void LeastSquaresVelocityTrackerStrategy::clear() {
  327. mIndex = 0;
  328. mMovements[0].idBits.clear();
  329. }
  330. void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
  331. BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
  332. mMovements[mIndex].idBits = remainingIdBits;
  333. }
  334. void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
  335. const VelocityTracker::Position* positions) {
  336. if (mMovements[mIndex].eventTime != eventTime) {
  337. // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
  338. // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
  339. // the new pointer. If the eventtimes for both events are identical, just update the data
  340. // for this time.
  341. // We only compare against the last value, as it is likely that addMovement is called
  342. // in chronological order as events occur.
  343. mIndex++;
  344. }
  345. if (mIndex == HISTORY_SIZE) {
  346. mIndex = 0;
  347. }
  348. Movement& movement = mMovements[mIndex];
  349. movement.eventTime = eventTime;
  350. movement.idBits = idBits;
  351. uint32_t count = idBits.count();
  352. for (uint32_t i = 0; i < count; i++) {
  353. movement.positions[i] = positions[i];
  354. }
  355. }
  356. /**
  357. * Solves a linear least squares problem to obtain a N degree polynomial that fits
  358. * the specified input data as nearly as possible.
  359. *
  360. * Returns true if a solution is found, false otherwise.
  361. *
  362. * The input consists of two vectors of data points X and Y with indices 0..m-1
  363. * along with a weight vector W of the same size.
  364. *
  365. * The output is a vector B with indices 0..n that describes a polynomial
  366. * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i]
  367. * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized.
  368. *
  369. * Accordingly, the weight vector W should be initialized by the caller with the
  370. * reciprocal square root of the variance of the error in each input data point.
  371. * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]).
  372. * The weights express the relative importance of each data point. If the weights are
  373. * all 1, then the data points are considered to be of equal importance when fitting
  374. * the polynomial. It is a good idea to choose weights that diminish the importance
  375. * of data points that may have higher than usual error margins.
  376. *
  377. * Errors among data points are assumed to be independent. W is represented here
  378. * as a vector although in the literature it is typically taken to be a diagonal matrix.
  379. *
  380. * That is to say, the function that generated the input data can be approximated
  381. * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
  382. *
  383. * The coefficient of determination (R^2) is also returned to describe the goodness
  384. * of fit of the model for the given data. It is a value between 0 and 1, where 1
  385. * indicates perfect correspondence.
  386. *
  387. * This function first expands the X vector to a m by n matrix A such that
  388. * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
  389. * multiplies it by w[i]./
  390. *
  391. * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q
  392. * and an m by n upper triangular matrix R. Because R is upper triangular (lower
  393. * part is all zeroes), we can simplify the decomposition into an m by n matrix
  394. * Q1 and a n by n matrix R1 such that A = Q1 R1.
  395. *
  396. * Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y)
  397. * to find B.
  398. *
  399. * For efficiency, we lay out A and Q column-wise in memory because we frequently
  400. * operate on the column vectors. Conversely, we lay out R row-wise.
  401. *
  402. * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
  403. * http://en.wikipedia.org/wiki/Gram-Schmidt
  404. */
  405. static bool solveLeastSquares(const float* x, const float* y,
  406. const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
  407. #if DEBUG_STRATEGY
  408. ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
  409. vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
  410. vectorToString(w, m).c_str());
  411. #endif
  412. // Expand the X vector to a matrix A, pre-multiplied by the weights.
  413. float a[n][m]; // column-major order
  414. for (uint32_t h = 0; h < m; h++) {
  415. a[0][h] = w[h];
  416. for (uint32_t i = 1; i < n; i++) {
  417. a[i][h] = a[i - 1][h] * x[h];
  418. }
  419. }
  420. #if DEBUG_STRATEGY
  421. ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
  422. #endif
  423. // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
  424. float q[n][m]; // orthonormal basis, column-major order
  425. float r[n][n]; // upper triangular matrix, row-major order
  426. for (uint32_t j = 0; j < n; j++) {
  427. for (uint32_t h = 0; h < m; h++) {
  428. q[j][h] = a[j][h];
  429. }
  430. for (uint32_t i = 0; i < j; i++) {
  431. float dot = vectorDot(&q[j][0], &q[i][0], m);
  432. for (uint32_t h = 0; h < m; h++) {
  433. q[j][h] -= dot * q[i][h];
  434. }
  435. }
  436. float norm = vectorNorm(&q[j][0], m);
  437. if (norm < 0.000001f) {
  438. // vectors are linearly dependent or zero so no solution
  439. #if DEBUG_STRATEGY
  440. ALOGD(" - no solution, norm=%f", norm);
  441. #endif
  442. return false;
  443. }
  444. float invNorm = 1.0f / norm;
  445. for (uint32_t h = 0; h < m; h++) {
  446. q[j][h] *= invNorm;
  447. }
  448. for (uint32_t i = 0; i < n; i++) {
  449. r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
  450. }
  451. }
  452. #if DEBUG_STRATEGY
  453. ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
  454. ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
  455. // calculate QR, if we factored A correctly then QR should equal A
  456. float qr[n][m];
  457. for (uint32_t h = 0; h < m; h++) {
  458. for (uint32_t i = 0; i < n; i++) {
  459. qr[i][h] = 0;
  460. for (uint32_t j = 0; j < n; j++) {
  461. qr[i][h] += q[j][h] * r[j][i];
  462. }
  463. }
  464. }
  465. ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
  466. #endif
  467. // Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
  468. // We just work from bottom-right to top-left calculating B's coefficients.
  469. float wy[m];
  470. for (uint32_t h = 0; h < m; h++) {
  471. wy[h] = y[h] * w[h];
  472. }
  473. for (uint32_t i = n; i != 0; ) {
  474. i--;
  475. outB[i] = vectorDot(&q[i][0], wy, m);
  476. for (uint32_t j = n - 1; j > i; j--) {
  477. outB[i] -= r[i][j] * outB[j];
  478. }
  479. outB[i] /= r[i][i];
  480. }
  481. #if DEBUG_STRATEGY
  482. ALOGD(" - b=%s", vectorToString(outB, n).c_str());
  483. #endif
  484. // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
  485. // SSerr is the residual sum of squares (variance of the error),
  486. // and SStot is the total sum of squares (variance of the data) where each
  487. // has been weighted.
  488. float ymean = 0;
  489. for (uint32_t h = 0; h < m; h++) {
  490. ymean += y[h];
  491. }
  492. ymean /= m;
  493. float sserr = 0;
  494. float sstot = 0;
  495. for (uint32_t h = 0; h < m; h++) {
  496. float err = y[h] - outB[0];
  497. float term = 1;
  498. for (uint32_t i = 1; i < n; i++) {
  499. term *= x[h];
  500. err -= term * outB[i];
  501. }
  502. sserr += w[h] * w[h] * err * err;
  503. float var = y[h] - ymean;
  504. sstot += w[h] * w[h] * var * var;
  505. }
  506. *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
  507. #if DEBUG_STRATEGY
  508. ALOGD(" - sserr=%f", sserr);
  509. ALOGD(" - sstot=%f", sstot);
  510. ALOGD(" - det=%f", *outDet);
  511. #endif
  512. return true;
  513. }
  514. /*
  515. * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
  516. * the default implementation
  517. */
  518. static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
  519. const float* x, const float* y, size_t count) {
  520. // Solving y = a*x^2 + b*x + c
  521. float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
  522. for (size_t i = 0; i < count; i++) {
  523. float xi = x[i];
  524. float yi = y[i];
  525. float xi2 = xi*xi;
  526. float xi3 = xi2*xi;
  527. float xi4 = xi3*xi;
  528. float xiyi = xi*yi;
  529. float xi2yi = xi2*yi;
  530. sxi += xi;
  531. sxi2 += xi2;
  532. sxiyi += xiyi;
  533. sxi2yi += xi2yi;
  534. syi += yi;
  535. sxi3 += xi3;
  536. sxi4 += xi4;
  537. }
  538. float Sxx = sxi2 - sxi*sxi / count;
  539. float Sxy = sxiyi - sxi*syi / count;
  540. float Sxx2 = sxi3 - sxi*sxi2 / count;
  541. float Sx2y = sxi2yi - sxi2*syi / count;
  542. float Sx2x2 = sxi4 - sxi2*sxi2 / count;
  543. float denominator = Sxx*Sx2x2 - Sxx2*Sxx2;
  544. if (denominator == 0) {
  545. ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
  546. return std::nullopt;
  547. }
  548. // Compute a
  549. float numerator = Sx2y*Sxx - Sxy*Sxx2;
  550. float a = numerator / denominator;
  551. // Compute b
  552. numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
  553. float b = numerator / denominator;
  554. // Compute c
  555. float c = syi/count - b * sxi/count - a * sxi2/count;
  556. return std::make_optional(std::array<float, 3>({c, b, a}));
  557. }
  558. bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
  559. VelocityTracker::Estimator* outEstimator) const {
  560. outEstimator->clear();
  561. // Iterate over movement samples in reverse time order and collect samples.
  562. float x[HISTORY_SIZE];
  563. float y[HISTORY_SIZE];
  564. float w[HISTORY_SIZE];
  565. float time[HISTORY_SIZE];
  566. uint32_t m = 0;
  567. uint32_t index = mIndex;
  568. const Movement& newestMovement = mMovements[mIndex];
  569. do {
  570. const Movement& movement = mMovements[index];
  571. if (!movement.idBits.hasBit(id)) {
  572. break;
  573. }
  574. nsecs_t age = newestMovement.eventTime - movement.eventTime;
  575. if (age > HORIZON) {
  576. break;
  577. }
  578. const VelocityTracker::Position& position = movement.getPosition(id);
  579. x[m] = position.x;
  580. y[m] = position.y;
  581. w[m] = chooseWeight(index);
  582. time[m] = -age * 0.000000001f;
  583. index = (index == 0 ? HISTORY_SIZE : index) - 1;
  584. } while (++m < HISTORY_SIZE);
  585. if (m == 0) {
  586. return false; // no data
  587. }
  588. // Calculate a least squares polynomial fit.
  589. uint32_t degree = mDegree;
  590. if (degree > m - 1) {
  591. degree = m - 1;
  592. }
  593. if (degree == 2 && mWeighting == WEIGHTING_NONE) {
  594. // Optimize unweighted, quadratic polynomial fit
  595. std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m);
  596. std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m);
  597. if (xCoeff && yCoeff) {
  598. outEstimator->time = newestMovement.eventTime;
  599. outEstimator->degree = 2;
  600. outEstimator->confidence = 1;
  601. for (size_t i = 0; i <= outEstimator->degree; i++) {
  602. outEstimator->xCoeff[i] = (*xCoeff)[i];
  603. outEstimator->yCoeff[i] = (*yCoeff)[i];
  604. }
  605. return true;
  606. }
  607. } else if (degree >= 1) {
  608. // General case for an Nth degree polynomial fit
  609. float xdet, ydet;
  610. uint32_t n = degree + 1;
  611. if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
  612. && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
  613. outEstimator->time = newestMovement.eventTime;
  614. outEstimator->degree = degree;
  615. outEstimator->confidence = xdet * ydet;
  616. #if DEBUG_STRATEGY
  617. ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
  618. int(outEstimator->degree),
  619. vectorToString(outEstimator->xCoeff, n).c_str(),
  620. vectorToString(outEstimator->yCoeff, n).c_str(),
  621. outEstimator->confidence);
  622. #endif
  623. return true;
  624. }
  625. }
  626. // No velocity data available for this pointer, but we do have its current position.
  627. outEstimator->xCoeff[0] = x[0];
  628. outEstimator->yCoeff[0] = y[0];
  629. outEstimator->time = newestMovement.eventTime;
  630. outEstimator->degree = 0;
  631. outEstimator->confidence = 1;
  632. return true;
  633. }
  634. float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const {
  635. switch (mWeighting) {
  636. case WEIGHTING_DELTA: {
  637. // Weight points based on how much time elapsed between them and the next
  638. // point so that points that "cover" a shorter time span are weighed less.
  639. // delta 0ms: 0.5
  640. // delta 10ms: 1.0
  641. if (index == mIndex) {
  642. return 1.0f;
  643. }
  644. uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
  645. float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime)
  646. * 0.000001f;
  647. if (deltaMillis < 0) {
  648. return 0.5f;
  649. }
  650. if (deltaMillis < 10) {
  651. return 0.5f + deltaMillis * 0.05;
  652. }
  653. return 1.0f;
  654. }
  655. case WEIGHTING_CENTRAL: {
  656. // Weight points based on their age, weighing very recent and very old points less.
  657. // age 0ms: 0.5
  658. // age 10ms: 1.0
  659. // age 50ms: 1.0
  660. // age 60ms: 0.5
  661. float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
  662. * 0.000001f;
  663. if (ageMillis < 0) {
  664. return 0.5f;
  665. }
  666. if (ageMillis < 10) {
  667. return 0.5f + ageMillis * 0.05;
  668. }
  669. if (ageMillis < 50) {
  670. return 1.0f;
  671. }
  672. if (ageMillis < 60) {
  673. return 0.5f + (60 - ageMillis) * 0.05;
  674. }
  675. return 0.5f;
  676. }
  677. case WEIGHTING_RECENT: {
  678. // Weight points based on their age, weighing older points less.
  679. // age 0ms: 1.0
  680. // age 50ms: 1.0
  681. // age 100ms: 0.5
  682. float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
  683. * 0.000001f;
  684. if (ageMillis < 50) {
  685. return 1.0f;
  686. }
  687. if (ageMillis < 100) {
  688. return 0.5f + (100 - ageMillis) * 0.01f;
  689. }
  690. return 0.5f;
  691. }
  692. case WEIGHTING_NONE:
  693. default:
  694. return 1.0f;
  695. }
  696. }
  697. // --- IntegratingVelocityTrackerStrategy ---
  698. IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) :
  699. mDegree(degree) {
  700. }
  701. IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {
  702. }
  703. void IntegratingVelocityTrackerStrategy::clear() {
  704. mPointerIdBits.clear();
  705. }
  706. void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
  707. mPointerIdBits.value &= ~idBits.value;
  708. }
  709. void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
  710. const VelocityTracker::Position* positions) {
  711. uint32_t index = 0;
  712. for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
  713. uint32_t id = iterIdBits.clearFirstMarkedBit();
  714. State& state = mPointerState[id];
  715. const VelocityTracker::Position& position = positions[index++];
  716. if (mPointerIdBits.hasBit(id)) {
  717. updateState(state, eventTime, position.x, position.y);
  718. } else {
  719. initState(state, eventTime, position.x, position.y);
  720. }
  721. }
  722. mPointerIdBits = idBits;
  723. }
  724. bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id,
  725. VelocityTracker::Estimator* outEstimator) const {
  726. outEstimator->clear();
  727. if (mPointerIdBits.hasBit(id)) {
  728. const State& state = mPointerState[id];
  729. populateEstimator(state, outEstimator);
  730. return true;
  731. }
  732. return false;
  733. }
  734. void IntegratingVelocityTrackerStrategy::initState(State& state,
  735. nsecs_t eventTime, float xpos, float ypos) const {
  736. state.updateTime = eventTime;
  737. state.degree = 0;
  738. state.xpos = xpos;
  739. state.xvel = 0;
  740. state.xaccel = 0;
  741. state.ypos = ypos;
  742. state.yvel = 0;
  743. state.yaccel = 0;
  744. }
  745. void IntegratingVelocityTrackerStrategy::updateState(State& state,
  746. nsecs_t eventTime, float xpos, float ypos) const {
  747. const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
  748. const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
  749. if (eventTime <= state.updateTime + MIN_TIME_DELTA) {
  750. return;
  751. }
  752. float dt = (eventTime - state.updateTime) * 0.000000001f;
  753. state.updateTime = eventTime;
  754. float xvel = (xpos - state.xpos) / dt;
  755. float yvel = (ypos - state.ypos) / dt;
  756. if (state.degree == 0) {
  757. state.xvel = xvel;
  758. state.yvel = yvel;
  759. state.degree = 1;
  760. } else {
  761. float alpha = dt / (FILTER_TIME_CONSTANT + dt);
  762. if (mDegree == 1) {
  763. state.xvel += (xvel - state.xvel) * alpha;
  764. state.yvel += (yvel - state.yvel) * alpha;
  765. } else {
  766. float xaccel = (xvel - state.xvel) / dt;
  767. float yaccel = (yvel - state.yvel) / dt;
  768. if (state.degree == 1) {
  769. state.xaccel = xaccel;
  770. state.yaccel = yaccel;
  771. state.degree = 2;
  772. } else {
  773. state.xaccel += (xaccel - state.xaccel) * alpha;
  774. state.yaccel += (yaccel - state.yaccel) * alpha;
  775. }
  776. state.xvel += (state.xaccel * dt) * alpha;
  777. state.yvel += (state.yaccel * dt) * alpha;
  778. }
  779. }
  780. state.xpos = xpos;
  781. state.ypos = ypos;
  782. }
  783. void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
  784. VelocityTracker::Estimator* outEstimator) const {
  785. outEstimator->time = state.updateTime;
  786. outEstimator->confidence = 1.0f;
  787. outEstimator->degree = state.degree;
  788. outEstimator->xCoeff[0] = state.xpos;
  789. outEstimator->xCoeff[1] = state.xvel;
  790. outEstimator->xCoeff[2] = state.xaccel / 2;
  791. outEstimator->yCoeff[0] = state.ypos;
  792. outEstimator->yCoeff[1] = state.yvel;
  793. outEstimator->yCoeff[2] = state.yaccel / 2;
  794. }
  795. // --- LegacyVelocityTrackerStrategy ---
  796. LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
  797. clear();
  798. }
  799. LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
  800. }
  801. void LegacyVelocityTrackerStrategy::clear() {
  802. mIndex = 0;
  803. mMovements[0].idBits.clear();
  804. }
  805. void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
  806. BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
  807. mMovements[mIndex].idBits = remainingIdBits;
  808. }
  809. void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
  810. const VelocityTracker::Position* positions) {
  811. if (++mIndex == HISTORY_SIZE) {
  812. mIndex = 0;
  813. }
  814. Movement& movement = mMovements[mIndex];
  815. movement.eventTime = eventTime;
  816. movement.idBits = idBits;
  817. uint32_t count = idBits.count();
  818. for (uint32_t i = 0; i < count; i++) {
  819. movement.positions[i] = positions[i];
  820. }
  821. }
  822. bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id,
  823. VelocityTracker::Estimator* outEstimator) const {
  824. outEstimator->clear();
  825. const Movement& newestMovement = mMovements[mIndex];
  826. if (!newestMovement.idBits.hasBit(id)) {
  827. return false; // no data
  828. }
  829. // Find the oldest sample that contains the pointer and that is not older than HORIZON.
  830. nsecs_t minTime = newestMovement.eventTime - HORIZON;
  831. uint32_t oldestIndex = mIndex;
  832. uint32_t numTouches = 1;
  833. do {
  834. uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
  835. const Movement& nextOldestMovement = mMovements[nextOldestIndex];
  836. if (!nextOldestMovement.idBits.hasBit(id)
  837. || nextOldestMovement.eventTime < minTime) {
  838. break;
  839. }
  840. oldestIndex = nextOldestIndex;
  841. } while (++numTouches < HISTORY_SIZE);
  842. // Calculate an exponentially weighted moving average of the velocity estimate
  843. // at different points in time measured relative to the oldest sample.
  844. // This is essentially an IIR filter. Newer samples are weighted more heavily
  845. // than older samples. Samples at equal time points are weighted more or less
  846. // equally.
  847. //
  848. // One tricky problem is that the sample data may be poorly conditioned.
  849. // Sometimes samples arrive very close together in time which can cause us to
  850. // overestimate the velocity at that time point. Most samples might be measured
  851. // 16ms apart but some consecutive samples could be only 0.5sm apart because
  852. // the hardware or driver reports them irregularly or in bursts.
  853. float accumVx = 0;
  854. float accumVy = 0;
  855. uint32_t index = oldestIndex;
  856. uint32_t samplesUsed = 0;
  857. const Movement& oldestMovement = mMovements[oldestIndex];
  858. const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
  859. nsecs_t lastDuration = 0;
  860. while (numTouches-- > 1) {
  861. if (++index == HISTORY_SIZE) {
  862. index = 0;
  863. }
  864. const Movement& movement = mMovements[index];
  865. nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
  866. // If the duration between samples is small, we may significantly overestimate
  867. // the velocity. Consequently, we impose a minimum duration constraint on the
  868. // samples that we include in the calculation.
  869. if (duration >= MIN_DURATION) {
  870. const VelocityTracker::Position& position = movement.getPosition(id);
  871. float scale = 1000000000.0f / duration; // one over time delta in seconds
  872. float vx = (position.x - oldestPosition.x) * scale;
  873. float vy = (position.y - oldestPosition.y) * scale;
  874. accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
  875. accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
  876. lastDuration = duration;
  877. samplesUsed += 1;
  878. }
  879. }
  880. // Report velocity.
  881. const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
  882. outEstimator->time = newestMovement.eventTime;
  883. outEstimator->confidence = 1;
  884. outEstimator->xCoeff[0] = newestPosition.x;
  885. outEstimator->yCoeff[0] = newestPosition.y;
  886. if (samplesUsed) {
  887. outEstimator->xCoeff[1] = accumVx;
  888. outEstimator->yCoeff[1] = accumVy;
  889. outEstimator->degree = 1;
  890. } else {
  891. outEstimator->degree = 0;
  892. }
  893. return true;
  894. }
  895. // --- ImpulseVelocityTrackerStrategy ---
  896. ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() {
  897. clear();
  898. }
  899. ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
  900. }
  901. void ImpulseVelocityTrackerStrategy::clear() {
  902. mIndex = 0;
  903. mMovements[0].idBits.clear();
  904. }
  905. void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
  906. BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
  907. mMovements[mIndex].idBits = remainingIdBits;
  908. }
  909. void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
  910. const VelocityTracker::Position* positions) {
  911. if (mMovements[mIndex].eventTime != eventTime) {
  912. // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
  913. // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
  914. // the new pointer. If the eventtimes for both events are identical, just update the data
  915. // for this time.
  916. // We only compare against the last value, as it is likely that addMovement is called
  917. // in chronological order as events occur.
  918. mIndex++;
  919. }
  920. if (mIndex == HISTORY_SIZE) {
  921. mIndex = 0;
  922. }
  923. Movement& movement = mMovements[mIndex];
  924. movement.eventTime = eventTime;
  925. movement.idBits = idBits;
  926. uint32_t count = idBits.count();
  927. for (uint32_t i = 0; i < count; i++) {
  928. movement.positions[i] = positions[i];
  929. }
  930. }
  931. /**
  932. * Calculate the total impulse provided to the screen and the resulting velocity.
  933. *
  934. * The touchscreen is modeled as a physical object.
  935. * Initial condition is discussed below, but for now suppose that v(t=0) = 0
  936. *
  937. * The kinetic energy of the object at the release is E=0.5*m*v^2
  938. * Then vfinal = sqrt(2E/m). The goal is to calculate E.
  939. *
  940. * The kinetic energy at the release is equal to the total work done on the object by the finger.
  941. * The total work W is the sum of all dW along the path.
  942. *
  943. * dW = F*dx, where dx is the piece of path traveled.
  944. * Force is change of momentum over time, F = dp/dt = m dv/dt.
  945. * Then substituting:
  946. * dW = m (dv/dt) * dx = m * v * dv
  947. *
  948. * Summing along the path, we get:
  949. * W = sum(dW) = sum(m * v * dv) = m * sum(v * dv)
  950. * Since the mass stays constant, the equation for final velocity is:
  951. * vfinal = sqrt(2*sum(v * dv))
  952. *
  953. * Here,
  954. * dv : change of velocity = (v[i+1]-v[i])
  955. * dx : change of distance = (x[i+1]-x[i])
  956. * dt : change of time = (t[i+1]-t[i])
  957. * v : instantaneous velocity = dx/dt
  958. *
  959. * The final formula is:
  960. * vfinal = sqrt(2) * sqrt(sum((v[i]-v[i-1])*|v[i]|)) for all i
  961. * The absolute value is needed to properly account for the sign. If the velocity over a
  962. * particular segment descreases, then this indicates braking, which means that negative
  963. * work was done. So for two positive, but decreasing, velocities, this contribution would be
  964. * negative and will cause a smaller final velocity.
  965. *
  966. * Initial condition
  967. * There are two ways to deal with initial condition:
  968. * 1) Assume that v(0) = 0, which would mean that the screen is initially at rest.
  969. * This is not entirely accurate. We are only taking the past X ms of touch data, where X is
  970. * currently equal to 100. However, a touch event that created a fling probably lasted for longer
  971. * than that, which would mean that the user has already been interacting with the touchscreen
  972. * and it has probably already been moving.
  973. * 2) Assume that the touchscreen has already been moving at a certain velocity, calculate this
  974. * initial velocity and the equivalent energy, and start with this initial energy.
  975. * Consider an example where we have the following data, consisting of 3 points:
  976. * time: t0, t1, t2
  977. * x : x0, x1, x2
  978. * v : 0 , v1, v2
  979. * Here is what will happen in each of these scenarios:
  980. * 1) By directly applying the formula above with the v(0) = 0 boundary condition, we will get
  981. * vfinal = sqrt(2*(|v1|*(v1-v0) + |v2|*(v2-v1))). This can be simplified since v0=0
  982. * vfinal = sqrt(2*(|v1|*v1 + |v2|*(v2-v1))) = sqrt(2*(v1^2 + |v2|*(v2 - v1)))
  983. * since velocity is a real number
  984. * 2) If we treat the screen as already moving, then it must already have an energy (per mass)
  985. * equal to 1/2*v1^2. Then the initial energy should be 1/2*v1*2, and only the second segment
  986. * will contribute to the total kinetic energy (since we can effectively consider that v0=v1).
  987. * This will give the following expression for the final velocity:
  988. * vfinal = sqrt(2*(1/2*v1^2 + |v2|*(v2-v1)))
  989. * This analysis can be generalized to an arbitrary number of samples.
  990. *
  991. *
  992. * Comparing the two equations above, we see that the only mathematical difference
  993. * is the factor of 1/2 in front of the first velocity term.
  994. * This boundary condition would allow for the "proper" calculation of the case when all of the
  995. * samples are equally spaced in time and distance, which should suggest a constant velocity.
  996. *
  997. * Note that approach 2) is sensitive to the proper ordering of the data in time, since
  998. * the boundary condition must be applied to the oldest sample to be accurate.
  999. */
  1000. static float kineticEnergyToVelocity(float work) {
  1001. static constexpr float sqrt2 = 1.41421356237;
  1002. return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
  1003. }
  1004. static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) {
  1005. // The input should be in reversed time order (most recent sample at index i=0)
  1006. // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
  1007. static constexpr float SECONDS_PER_NANO = 1E-9;
  1008. if (count < 2) {
  1009. return 0; // if 0 or 1 points, velocity is zero
  1010. }
  1011. if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
  1012. ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
  1013. }
  1014. if (count == 2) { // if 2 points, basic linear calculation
  1015. if (t[1] == t[0]) {
  1016. ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
  1017. return 0;
  1018. }
  1019. return (x[1] - x[0]) / (SECONDS_PER_NANO * (t[1] - t[0]));
  1020. }
  1021. // Guaranteed to have at least 3 points here
  1022. float work = 0;
  1023. for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time
  1024. if (t[i] == t[i-1]) {
  1025. ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]);
  1026. continue;
  1027. }
  1028. float vprev = kineticEnergyToVelocity(work); // v[i-1]
  1029. float vcurr = (x[i] - x[i-1]) / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
  1030. work += (vcurr - vprev) * fabsf(vcurr);
  1031. if (i == count - 1) {
  1032. work *= 0.5; // initial condition, case 2) above
  1033. }
  1034. }
  1035. return kineticEnergyToVelocity(work);
  1036. }
  1037. bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id,
  1038. VelocityTracker::Estimator* outEstimator) const {
  1039. outEstimator->clear();
  1040. // Iterate over movement samples in reverse time order and collect samples.
  1041. float x[HISTORY_SIZE];
  1042. float y[HISTORY_SIZE];
  1043. nsecs_t time[HISTORY_SIZE];
  1044. size_t m = 0; // number of points that will be used for fitting
  1045. size_t index = mIndex;
  1046. const Movement& newestMovement = mMovements[mIndex];
  1047. do {
  1048. const Movement& movement = mMovements[index];
  1049. if (!movement.idBits.hasBit(id)) {
  1050. break;
  1051. }
  1052. nsecs_t age = newestMovement.eventTime - movement.eventTime;
  1053. if (age > HORIZON) {
  1054. break;
  1055. }
  1056. const VelocityTracker::Position& position = movement.getPosition(id);
  1057. x[m] = position.x;
  1058. y[m] = position.y;
  1059. time[m] = movement.eventTime;
  1060. index = (index == 0 ? HISTORY_SIZE : index) - 1;
  1061. } while (++m < HISTORY_SIZE);
  1062. if (m == 0) {
  1063. return false; // no data
  1064. }
  1065. outEstimator->xCoeff[0] = 0;
  1066. outEstimator->yCoeff[0] = 0;
  1067. outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m);
  1068. outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m);
  1069. outEstimator->xCoeff[2] = 0;
  1070. outEstimator->yCoeff[2] = 0;
  1071. outEstimator->time = newestMovement.eventTime;
  1072. outEstimator->degree = 2; // similar results to 2nd degree fit
  1073. outEstimator->confidence = 1;
  1074. #if DEBUG_STRATEGY
  1075. ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
  1076. #endif
  1077. return true;
  1078. }
  1079. } // namespace android