1 /*
2 * Copyright 2024 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 #include <input/VelocityControl.h>
18
19 #include <limits>
20
21 #include <gtest/gtest.h>
22 #include <input/AccelerationCurve.h>
23 #include <utils/Timers.h>
24
25 namespace android {
26
27 namespace {
28
29 constexpr float EPSILON = 0.001;
30 constexpr float COUNTS_PER_MM = 800 / 25.4;
31
32 } // namespace
33
34 class CurvedVelocityControlTest : public testing::Test {
35 protected:
36 CurvedVelocityControl mCtrl;
37
moveWithoutCheckingResult(nsecs_t eventTime,float deltaX,float deltaY)38 void moveWithoutCheckingResult(nsecs_t eventTime, float deltaX, float deltaY) {
39 mCtrl.move(eventTime, &deltaX, &deltaY);
40 }
41
moveAndCheckRatio(nsecs_t eventTime,const float deltaX,const float deltaY,float expectedRatio)42 void moveAndCheckRatio(nsecs_t eventTime, const float deltaX, const float deltaY,
43 float expectedRatio) {
44 float newDeltaX = deltaX, newDeltaY = deltaY;
45 mCtrl.move(eventTime, &newDeltaX, &newDeltaY);
46 ASSERT_NEAR(expectedRatio * deltaX, newDeltaX, EPSILON)
47 << "Expected ratio of " << expectedRatio << " in X, but actual ratio was "
48 << newDeltaX / deltaX;
49 ASSERT_NEAR(expectedRatio * deltaY, newDeltaY, EPSILON)
50 << "Expected ratio of " << expectedRatio << " in Y, but actual ratio was "
51 << newDeltaY / deltaY;
52 }
53 };
54
TEST_F(CurvedVelocityControlTest,SegmentSelection)55 TEST_F(CurvedVelocityControlTest, SegmentSelection) {
56 // To make the maths simple, use a "curve" that's actually just a sequence of steps.
57 mCtrl.setCurve({
58 {10, 2, 0},
59 {20, 3, 0},
60 {30, 4, 0},
61 {std::numeric_limits<double>::infinity(), 5, 0},
62 });
63
64 // Establish a velocity of 16 mm/s.
65 moveWithoutCheckingResult(0, 0, 0);
66 moveWithoutCheckingResult(10'000'000, 0.16 * COUNTS_PER_MM, 0);
67 moveWithoutCheckingResult(20'000'000, 0.16 * COUNTS_PER_MM, 0);
68 moveWithoutCheckingResult(30'000'000, 0.16 * COUNTS_PER_MM, 0);
69 ASSERT_NO_FATAL_FAILURE(
70 moveAndCheckRatio(40'000'000, 0.16 * COUNTS_PER_MM, 0, /*expectedRatio=*/3));
71
72 // Establish a velocity of 50 mm/s.
73 mCtrl.reset();
74 moveWithoutCheckingResult(100'000'000, 0, 0);
75 moveWithoutCheckingResult(110'000'000, 0.50 * COUNTS_PER_MM, 0);
76 moveWithoutCheckingResult(120'000'000, 0.50 * COUNTS_PER_MM, 0);
77 moveWithoutCheckingResult(130'000'000, 0.50 * COUNTS_PER_MM, 0);
78 ASSERT_NO_FATAL_FAILURE(
79 moveAndCheckRatio(140'000'000, 0.50 * COUNTS_PER_MM, 0, /*expectedRatio=*/5));
80 }
81
TEST_F(CurvedVelocityControlTest,RatioDefaultsToFirstSegmentWhenVelocityIsUnknown)82 TEST_F(CurvedVelocityControlTest, RatioDefaultsToFirstSegmentWhenVelocityIsUnknown) {
83 mCtrl.setCurve({
84 {10, 3, 0},
85 {20, 2, 0},
86 {std::numeric_limits<double>::infinity(), 4, 0},
87 });
88
89 // Only send two moves, which won't be enough for VelocityTracker to calculate a velocity from.
90 moveWithoutCheckingResult(0, 0, 0);
91 ASSERT_NO_FATAL_FAILURE(
92 moveAndCheckRatio(10'000'000, 0.25 * COUNTS_PER_MM, 0, /*expectedRatio=*/3));
93 }
94
TEST_F(CurvedVelocityControlTest,VelocityCalculatedUsingBothAxes)95 TEST_F(CurvedVelocityControlTest, VelocityCalculatedUsingBothAxes) {
96 mCtrl.setCurve({
97 {8.0, 3, 0},
98 {8.1, 2, 0},
99 {std::numeric_limits<double>::infinity(), 4, 0},
100 });
101
102 // Establish a velocity of 8.06 (= √65 = √(7²+4²)) mm/s between the two axes.
103 moveWithoutCheckingResult(0, 0, 0);
104 moveWithoutCheckingResult(10'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM);
105 moveWithoutCheckingResult(20'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM);
106 moveWithoutCheckingResult(30'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM);
107 ASSERT_NO_FATAL_FAILURE(moveAndCheckRatio(40'000'000, 0.07 * COUNTS_PER_MM,
108 0.04 * COUNTS_PER_MM,
109 /*expectedRatio=*/2));
110 }
111
TEST_F(CurvedVelocityControlTest,ReciprocalTerm)112 TEST_F(CurvedVelocityControlTest, ReciprocalTerm) {
113 mCtrl.setCurve({
114 {10, 2, 0},
115 {20, 3, -10},
116 {std::numeric_limits<double>::infinity(), 3, 0},
117 });
118
119 // Establish a velocity of 15 mm/s.
120 moveWithoutCheckingResult(0, 0, 0);
121 moveWithoutCheckingResult(10'000'000, 0, 0.15 * COUNTS_PER_MM);
122 moveWithoutCheckingResult(20'000'000, 0, 0.15 * COUNTS_PER_MM);
123 moveWithoutCheckingResult(30'000'000, 0, 0.15 * COUNTS_PER_MM);
124 // Expected ratio is 3 - 10 / 15 = 2.33333...
125 ASSERT_NO_FATAL_FAILURE(
126 moveAndCheckRatio(40'000'000, 0, 0.15 * COUNTS_PER_MM, /*expectedRatio=*/2.33333));
127 }
128
129 } // namespace android