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