xref: /aosp_15_r20/hardware/interfaces/vibrator/aidl/vts/pwle_v2_utils.h (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1 /*
2  * Copyright (C) 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 #ifndef VIBRATOR_HAL_PWLE_V2_UTILS_H
18 #define VIBRATOR_HAL_PWLE_V2_UTILS_H
19 
20 #include <aidl/android/hardware/vibrator/IVibrator.h>
21 #include "test_utils.h"
22 
23 using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
24 using aidl::android::hardware::vibrator::IVibrator;
25 using aidl::android::hardware::vibrator::PwleV2Primitive;
26 
27 namespace aidl {
28 namespace android {
29 namespace hardware {
30 namespace vibrator {
31 namespace testing {
32 namespace pwlev2 {
33 
34 static constexpr int32_t COMPOSE_PWLE_V2_MIN_REQUIRED_SIZE = 16;
35 static constexpr int32_t COMPOSE_PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
36 static constexpr int32_t COMPOSE_PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
37 static constexpr int32_t COMPOSE_PWLE_V2_MIN_REQUIRED_SENSITIVITY_DB_SL = 10;
38 
39 namespace {
40 /**
41  * Returns a vector of (frequency in Hz, acceleration in dB) pairs, where the acceleration
42  * value denotes the minimum output required at the corresponding frequency to be perceptible
43  * by a human.
44  */
getMinPerceptibleLevel()45 static std::vector<std::pair<float, float>> getMinPerceptibleLevel() {
46     return {{0.4f, -97.81f},   {2.0f, -69.86f},   {3.0f, -62.81f},    {4.0f, -58.81f},
47             {5.0f, -56.69f},   {6.0f, -54.77f},   {7.2f, -52.85f},    {8.0f, -51.77f},
48             {8.64f, -50.84f},  {10.0f, -48.90f},  {10.37f, -48.52f},  {12.44f, -46.50f},
49             {14.93f, -44.43f}, {15.0f, -44.35f},  {17.92f, -41.96f},  {20.0f, -40.36f},
50             {21.5f, -39.60f},  {25.0f, -37.48f},  {25.8f, -36.93f},   {30.0f, -34.31f},
51             {35.0f, -33.13f},  {40.0f, -32.81f},  {50.0f, -31.94f},   {60.0f, -31.77f},
52             {70.0f, -31.59f},  {72.0f, -31.55f},  {80.0f, -31.77f},   {86.4f, -31.94f},
53             {90.0f, -31.73f},  {100.0f, -31.90f}, {103.68f, -31.77f}, {124.42f, -31.70f},
54             {149.3f, -31.38f}, {150.0f, -31.35f}, {179.16f, -31.02f}, {200.0f, -30.86f},
55             {215.0f, -30.35f}, {250.0f, -28.98f}, {258.0f, -28.68f},  {300.0f, -26.81f},
56             {400.0f, -19.81f}};
57 }
58 
interpolateLinearly(const std::vector<float> & xAxis,const std::vector<float> & yAxis,float x)59 static float interpolateLinearly(const std::vector<float>& xAxis, const std::vector<float>& yAxis,
60                                  float x) {
61     EXPECT_TRUE(!xAxis.empty());
62     EXPECT_TRUE(xAxis.size() == yAxis.size());
63 
64     if (x <= xAxis.front()) return yAxis.front();
65     if (x >= xAxis.back()) return yAxis.back();
66 
67     auto it = std::upper_bound(xAxis.begin(), xAxis.end(), x);
68     int i = std::distance(xAxis.begin(), it) - 1;  // Index of the lower bound
69 
70     const float& x0 = xAxis[i];
71     const float& y0 = yAxis[i];
72     const float& x1 = xAxis[i + 1];
73     const float& y1 = yAxis[i + 1];
74 
75     return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
76 }
77 
minPerceptibleDbCurve(float frequency)78 static float minPerceptibleDbCurve(float frequency) {
79     // Initialize minPerceptibleMap only once
80     static auto minPerceptibleMap = []() -> std::function<float(float)> {
81         static std::vector<float> minPerceptibleFrequencies;
82         static std::vector<float> minPerceptibleAccelerations;
83 
84         auto minPerceptibleLevel = getMinPerceptibleLevel();
85         // Sort the 'minPerceptibleLevel' data in ascending order based on the
86         // frequency values (first element of each pair).
87         std::sort(minPerceptibleLevel.begin(), minPerceptibleLevel.end(),
88                   [](const auto& a, const auto& b) { return a.first < b.first; });
89 
90         for (const auto& entry : minPerceptibleLevel) {
91             minPerceptibleFrequencies.push_back(entry.first);
92             minPerceptibleAccelerations.push_back(entry.second);
93         }
94 
95         return [&](float freq) {
96             return interpolateLinearly(minPerceptibleFrequencies, minPerceptibleAccelerations,
97                                        freq);
98         };
99     }();
100 
101     return minPerceptibleMap(frequency);
102 }
103 
convertSensitivityLevelToDecibel(int sl,float frequency)104 static float convertSensitivityLevelToDecibel(int sl, float frequency) {
105     return sl + minPerceptibleDbCurve(frequency);
106 }
107 
convertDecibelToAcceleration(float db)108 static float convertDecibelToAcceleration(float db) {
109     return std::pow(10.0f, db / 20.0f);
110 }
111 }  // namespace
112 
convertSensitivityLevelToAcceleration(int sl,float frequency)113 static float convertSensitivityLevelToAcceleration(int sl, float frequency) {
114     return pwlev2::convertDecibelToAcceleration(
115             pwlev2::convertSensitivityLevelToDecibel(sl, frequency));
116 }
117 
getPwleV2FrequencyMinHz(const std::shared_ptr<IVibrator> & vibrator)118 static float getPwleV2FrequencyMinHz(const std::shared_ptr<IVibrator>& vibrator) {
119     std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
120     EXPECT_OK(vibrator->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap));
121     EXPECT_TRUE(!frequencyToOutputAccelerationMap.empty());
122     // We can't use ASSERT_TRUE() above because this is a non-void function,
123     // but we need to return to assure we don't crash from a null dereference.
124     if (frequencyToOutputAccelerationMap.empty()) {
125         return std::numeric_limits<float>::quiet_NaN();
126     }
127 
128     auto entry = std::min_element(
129             frequencyToOutputAccelerationMap.begin(), frequencyToOutputAccelerationMap.end(),
130             [](const auto& a, const auto& b) { return a.frequencyHz < b.frequencyHz; });
131 
132     return entry->frequencyHz;
133 }
134 
getPwleV2FrequencyMaxHz(const std::shared_ptr<IVibrator> & vibrator)135 static float getPwleV2FrequencyMaxHz(const std::shared_ptr<IVibrator>& vibrator) {
136     std::vector<FrequencyAccelerationMapEntry> frequencyToOutputAccelerationMap;
137     EXPECT_OK(vibrator->getFrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap));
138     EXPECT_TRUE(!frequencyToOutputAccelerationMap.empty());
139     // We can't use ASSERT_TRUE() above because this is a non-void function,
140     // but we need to return to assure we don't crash from a null dereference.
141     if (frequencyToOutputAccelerationMap.empty()) {
142         return std::numeric_limits<float>::quiet_NaN();
143     }
144 
145     auto entry = std::max_element(
146             frequencyToOutputAccelerationMap.begin(), frequencyToOutputAccelerationMap.end(),
147             [](const auto& a, const auto& b) { return a.frequencyHz < b.frequencyHz; });
148 
149     return entry->frequencyHz;
150 }
151 
composeValidPwleV2Effect(const std::shared_ptr<IVibrator> & vibrator)152 static CompositePwleV2 composeValidPwleV2Effect(const std::shared_ptr<IVibrator>& vibrator) {
153     int32_t minDurationMs;
154     EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDurationMs));
155     int32_t maxDurationMs;
156     EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs));
157     float minFrequency = getPwleV2FrequencyMinHz(vibrator);
158     float maxFrequency = getPwleV2FrequencyMaxHz(vibrator);
159     int32_t maxCompositionSize;
160     EXPECT_OK(vibrator->getPwleV2CompositionSizeMax(&maxCompositionSize));
161 
162     CompositePwleV2 composite;
163 
164     composite.pwlePrimitives.emplace_back(0.1f, minFrequency, minDurationMs);
165     composite.pwlePrimitives.emplace_back(0.5f, maxFrequency, maxDurationMs);
166 
167     float variedFrequency = (minFrequency + maxFrequency) / 2.0f;
168     for (int i = 0; i < maxCompositionSize - 2; i++) {
169         composite.pwlePrimitives.emplace_back(0.7f, variedFrequency, minDurationMs);
170     }
171 
172     return composite;
173 }
174 
composePwleV2EffectWithTooManyPoints(const std::shared_ptr<IVibrator> & vibrator)175 static CompositePwleV2 composePwleV2EffectWithTooManyPoints(
176         const std::shared_ptr<IVibrator>& vibrator) {
177     int32_t minDurationMs, maxCompositionSize;
178     EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDurationMs));
179     EXPECT_OK(vibrator->getPwleV2CompositionSizeMax(&maxCompositionSize));
180     float maxFrequency = getPwleV2FrequencyMaxHz(vibrator);
181 
182     std::vector<PwleV2Primitive> pwleEffect(maxCompositionSize + 1);  // +1 to exceed the limit
183 
184     std::fill(pwleEffect.begin(), pwleEffect.end(),
185               PwleV2Primitive(/*amplitude=*/0.2f, maxFrequency, minDurationMs));
186 
187     CompositePwleV2 composite;
188     composite.pwlePrimitives = pwleEffect;
189 
190     return composite;
191 }
192 
getPwleV2SharpnessRange(const std::shared_ptr<IVibrator> & vibrator,std::vector<FrequencyAccelerationMapEntry> freqToOutputAccelerationMap)193 static std::pair<float, float> getPwleV2SharpnessRange(
194         const std::shared_ptr<IVibrator>& vibrator,
195         std::vector<FrequencyAccelerationMapEntry> freqToOutputAccelerationMap) {
196     std::pair<float, float> sharpnessRange = {-1, -1};
197 
198     // Sort the entries by frequency in ascending order
199     std::sort(freqToOutputAccelerationMap.begin(), freqToOutputAccelerationMap.end(),
200               [](const auto& a, const auto& b) { return a.frequencyHz < b.frequencyHz; });
201 
202     for (const auto& entry : freqToOutputAccelerationMap) {
203         float minAcceptableOutputAcceleration = convertSensitivityLevelToAcceleration(
204                 pwlev2::COMPOSE_PWLE_V2_MIN_REQUIRED_SENSITIVITY_DB_SL, entry.frequencyHz);
205 
206         if (sharpnessRange.first < 0 &&
207             minAcceptableOutputAcceleration <= entry.maxOutputAccelerationGs) {
208             sharpnessRange.first = entry.frequencyHz;  // Found the lower bound
209         } else if (sharpnessRange.first >= 0 &&
210                    minAcceptableOutputAcceleration >= entry.maxOutputAccelerationGs) {
211             sharpnessRange.second = entry.frequencyHz;  // Found the upper bound
212             return sharpnessRange;
213         }
214     }
215 
216     if (sharpnessRange.first >= 0) {
217         // If only the lower bound was found, set the upper bound to the max frequency.
218         sharpnessRange.second = getPwleV2FrequencyMaxHz(vibrator);
219     }
220 
221     return sharpnessRange;
222 }
223 }  // namespace pwlev2
224 }  // namespace testing
225 }  // namespace vibrator
226 }  // namespace hardware
227 }  // namespace android
228 }  // namespace aidl
229 #endif  // VIBRATOR_HAL_PWLE_V2_UTILS_H
230