blob: be741d0b760223a8e9c00d32a51e91ec3d8a8390 [file] [log] [blame]
Omar El Ayach33f80c02018-09-27 15:02:03 -07001#!/usr/bin/env python3.4
2#
3# Copyright 2017 - The Android Open Source Project
4#
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -07005# Licensed under the Apache License, Version 2.0 (the 'License');
Omar El Ayach33f80c02018-09-27 15:02:03 -07006# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070012# distributed under the License is distributed on an 'AS IS' BASIS,
Omar El Ayach33f80c02018-09-27 15:02:03 -070013# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import collections
Omar El Ayach96714c82019-01-28 18:51:46 -080018import csv
19import itertools
Omar El Ayach33f80c02018-09-27 15:02:03 -070020import json
21import logging
22import os
Omar El Ayach33f80c02018-09-27 15:02:03 -070023from acts import asserts
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070024from acts import context
Omar El Ayach33f80c02018-09-27 15:02:03 -070025from acts import base_test
26from acts import utils
Omar El Ayacha210d572019-03-14 17:31:38 -070027from acts.controllers import iperf_client
Omar El Ayach14416ac2019-01-30 14:58:19 -080028from acts.controllers.utils_lib import ssh
Omar El Ayach5fbc1222018-12-07 18:10:05 -080029from acts.metrics.loggers.blackbox import BlackboxMetricLogger
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070030from acts.test_utils.wifi import ota_chamber
Omar El Ayach6e518a22019-06-13 13:55:42 -070031from acts.test_utils.wifi import wifi_performance_test_utils as wputils
Omar El Ayach33f80c02018-09-27 15:02:03 -070032from acts.test_utils.wifi import wifi_test_utils as wutils
33from acts.test_utils.wifi import wifi_retail_ap as retail_ap
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070034from functools import partial
Omar El Ayach5fbc1222018-12-07 18:10:05 -080035from WifiRvrTest import WifiRvrTest
36from WifiPingTest import WifiPingTest
Omar El Ayach33f80c02018-09-27 15:02:03 -070037
38
Omar El Ayach5fbc1222018-12-07 18:10:05 -080039class WifiSensitivityTest(WifiRvrTest, WifiPingTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -070040 """Class to test WiFi sensitivity tests.
41
42 This class implements measures WiFi sensitivity per rate. It heavily
43 leverages the WifiRvrTest class and introduced minor differences to set
44 specific rates and the access point, and implements a different pass/fail
45 check. For an example config file to run this test class see
46 example_connectivity_performance_ap_sta.json.
47 """
48
Omar El Ayacha210d572019-03-14 17:31:38 -070049 RSSI_POLL_INTERVAL = 0.2
Omar El Ayach33f80c02018-09-27 15:02:03 -070050 VALID_TEST_CONFIGS = {
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070051 1: ['legacy', 'VHT20'],
52 2: ['legacy', 'VHT20'],
53 6: ['legacy', 'VHT20'],
54 10: ['legacy', 'VHT20'],
55 11: ['legacy', 'VHT20'],
56 36: ['legacy', 'VHT20', 'VHT40', 'VHT80'],
57 40: ['legacy', 'VHT20'],
58 44: ['legacy', 'VHT20'],
59 48: ['legacy', 'VHT20'],
60 149: ['legacy', 'VHT20', 'VHT40', 'VHT80'],
61 153: ['legacy', 'VHT20'],
62 157: ['legacy', 'VHT20'],
63 161: ['legacy', 'VHT20']
Omar El Ayach33f80c02018-09-27 15:02:03 -070064 }
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070065 RateTuple = collections.namedtuple(('RateTuple'),
66 ['mcs', 'streams', 'data_rate'])
Omar El Ayacha210d572019-03-14 17:31:38 -070067 #yapf:disable
Omar El Ayach33f80c02018-09-27 15:02:03 -070068 VALID_RATES = {
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070069 'legacy_2GHz': [
Omar El Ayacha210d572019-03-14 17:31:38 -070070 RateTuple(54, 1, 54), RateTuple(48, 1, 48),
71 RateTuple(36, 1, 36), RateTuple(24, 1, 24),
72 RateTuple(18, 1, 18), RateTuple(12, 1, 12),
73 RateTuple(11, 1, 11), RateTuple(9, 1, 9),
74 RateTuple(6, 1, 6), RateTuple(5.5, 1, 5.5),
75 RateTuple(2, 1, 2), RateTuple(1, 1, 1)],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070076 'legacy_5GHz': [
Omar El Ayacha210d572019-03-14 17:31:38 -070077 RateTuple(54, 1, 54), RateTuple(48, 1, 48),
78 RateTuple(36, 1, 36), RateTuple(24, 1, 24),
79 RateTuple(18, 1, 18), RateTuple(12, 1, 12),
80 RateTuple(9, 1, 9), RateTuple(6, 1, 6)],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070081 'HT20': [
Omar El Ayacha210d572019-03-14 17:31:38 -070082 RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
83 RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
84 RateTuple(3, 1, 26), RateTuple(2, 1, 21.7),
85 RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
86 RateTuple(15, 2, 144.4), RateTuple(14, 2, 130),
87 RateTuple(13, 2, 115.6), RateTuple(12, 2, 86.7),
88 RateTuple(11, 2, 57.8), RateTuple(10, 2, 43.4),
89 RateTuple(9, 2, 28.9), RateTuple(8, 2, 14.4)],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -070090 'VHT20': [
Omar El Ayacha210d572019-03-14 17:31:38 -070091 RateTuple(9, 1, 96), RateTuple(8, 1, 86.7),
92 RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
93 RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
94 RateTuple(3, 1, 28.9), RateTuple(2, 1, 21.7),
95 RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
96 RateTuple(9, 2, 192), RateTuple(8, 2, 173.3),
97 RateTuple(7, 2, 144.4), RateTuple(6, 2, 130.3),
98 RateTuple(5, 2, 115.6), RateTuple(4, 2, 86.7),
99 RateTuple(3, 2, 57.8), RateTuple(2, 2, 43.3),
100 RateTuple(1, 2, 28.9), RateTuple(0, 2, 14.4)],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700101 'VHT40': [
Omar El Ayach03e40612019-05-01 16:25:39 -0700102 RateTuple(9, 1, 96), RateTuple(8, 1, 86.7),
103 RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
104 RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
105 RateTuple(3, 1, 28.9), RateTuple(2, 1, 21.7),
106 RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
107 RateTuple(9, 2, 192), RateTuple(8, 2, 173.3),
108 RateTuple(7, 2, 144.4), RateTuple(6, 2, 130.3),
109 RateTuple(5, 2, 115.6), RateTuple(4, 2, 86.7),
110 RateTuple(3, 2, 57.8), RateTuple(2, 2, 43.3),
111 RateTuple(1, 2, 28.9), RateTuple(0, 2, 14.4)],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700112 'VHT80': [
Omar El Ayach03e40612019-05-01 16:25:39 -0700113 RateTuple(9, 1, 96), RateTuple(8, 1, 86.7),
114 RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
115 RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
116 RateTuple(3, 1, 28.9), RateTuple(2, 1, 21.7),
117 RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
118 RateTuple(9, 2, 192), RateTuple(8, 2, 173.3),
119 RateTuple(7, 2, 144.4), RateTuple(6, 2, 130.3),
120 RateTuple(5, 2, 115.6), RateTuple(4, 2, 86.7),
121 RateTuple(3, 2, 57.8), RateTuple(2, 2, 43.3),
122 RateTuple(1, 2, 28.9), RateTuple(0, 2, 14.4)],
Omar El Ayach33f80c02018-09-27 15:02:03 -0700123 }
Omar El Ayacha210d572019-03-14 17:31:38 -0700124 #yapf:enable
Omar El Ayach33f80c02018-09-27 15:02:03 -0700125
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800126 def __init__(self, controllers):
127 base_test.BaseTestClass.__init__(self, controllers)
128 self.failure_count_metric = BlackboxMetricLogger.for_test_case(
129 metric_name='sensitivity')
130
Omar El Ayach33f80c02018-09-27 15:02:03 -0700131 def setup_class(self):
132 """Initializes common test hardware and parameters.
133
134 This function initializes hardwares and compiles parameters that are
135 common to all tests in this class.
136 """
Omar El Ayach656bf3d2019-07-31 15:04:53 -0700137 self.dut = self.android_devices[-1]
Omar El Ayach33f80c02018-09-27 15:02:03 -0700138 req_params = [
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700139 'RetailAccessPoints', 'sensitivity_test_params', 'testbed_params',
140 'RemoteServer'
Omar El Ayach33f80c02018-09-27 15:02:03 -0700141 ]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700142 opt_params = ['main_network', 'golden_files_list']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700143 self.unpack_userparams(req_params, opt_params)
144 self.testclass_params = self.sensitivity_test_params
145 self.num_atten = self.attenuators[0].instrument.num_atten
Omar El Ayach14416ac2019-01-30 14:58:19 -0800146 self.ping_server = ssh.connection.SshConnection(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700147 ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700148 self.iperf_server = self.iperf_servers[0]
Omar El Ayach14416ac2019-01-30 14:58:19 -0800149 self.iperf_client = self.iperf_clients[0]
Omar El Ayacha210d572019-03-14 17:31:38 -0700150 self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700151 self.log.info('Access Point Configuration: {}'.format(
Omar El Ayach33f80c02018-09-27 15:02:03 -0700152 self.access_point.ap_settings))
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700153 self.log_path = os.path.join(logging.log_path, 'results')
Omar El Ayach33f80c02018-09-27 15:02:03 -0700154 utils.create_dir(self.log_path)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700155 if not hasattr(self, 'golden_files_list'):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700156 self.golden_files_list = [
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700157 os.path.join(self.testbed_params['golden_results_path'], file)
Omar El Ayacha210d572019-03-14 17:31:38 -0700158 for file in os.listdir(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700159 self.testbed_params['golden_results_path'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700160 ]
161 self.testclass_results = []
162
163 # Turn WiFi ON
164 for dev in self.android_devices:
165 wutils.wifi_toggle_state(dev, True)
166
Omar El Ayach96714c82019-01-28 18:51:46 -0800167 def teardown_class(self):
168 # Turn WiFi OFF
169 for dev in self.android_devices:
170 wutils.wifi_toggle_state(dev, False)
171 self.process_testclass_results()
172
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800173 def pass_fail_check(self, result):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700174 """Checks sensitivity against golden results and decides on pass/fail.
175
176 Args:
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800177 result: dict containing attenuation, throughput and other meta
Omar El Ayach33f80c02018-09-27 15:02:03 -0700178 data
179 """
180 try:
181 golden_path = next(file_name
182 for file_name in self.golden_files_list
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700183 if 'sensitivity_targets' in file_name)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700184 with open(golden_path, 'r') as golden_file:
185 golden_results = json.load(golden_file)
Omar El Ayacha210d572019-03-14 17:31:38 -0700186 golden_sensitivity = golden_results[
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700187 self.current_test_name]['sensitivity']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700188 except:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700189 golden_sensitivity = float('nan')
Omar El Ayach33f80c02018-09-27 15:02:03 -0700190
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700191 result_string = ('Througput = {}%, Sensitivity = {}.'
192 'Target Sensitivity = {}'.format(
193 result['peak_throughput_pct'],
194 result['sensitivity'], golden_sensitivity))
195 if result['peak_throughput_pct'] < 100:
196 self.log.warning('Result unreliable. Peak rate unstable')
197 if result['sensitivity'] - golden_sensitivity < self.testclass_params[
198 'sensitivity_tolerance']:
199 asserts.explicit_pass('Test Passed. {}'.format(result_string))
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800200 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700201 asserts.fail('Test Failed. {}'.format(result_string))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700202
203 def process_testclass_results(self):
204 """Saves and plots test results from all executed test cases."""
Omar El Ayach96714c82019-01-28 18:51:46 -0800205 # write json output
Omar El Ayach33f80c02018-09-27 15:02:03 -0700206 testclass_results_dict = collections.OrderedDict()
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700207 id_fields = ['mode', 'rate', 'num_streams', 'chain_mask']
Omar El Ayacha210d572019-03-14 17:31:38 -0700208 channels_tested = []
Omar El Ayach33f80c02018-09-27 15:02:03 -0700209 for result in self.testclass_results:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700210 testcase_params = result['testcase_params']
Omar El Ayacha210d572019-03-14 17:31:38 -0700211 test_id = collections.OrderedDict(
212 (key, value) for key, value in testcase_params.items()
213 if key in id_fields)
214 test_id = tuple(test_id.items())
Omar El Ayach6e518a22019-06-13 13:55:42 -0700215 if test_id not in testclass_results_dict:
216 testclass_results_dict[test_id] = collections.OrderedDict()
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700217 channel = testcase_params['channel']
Omar El Ayacha210d572019-03-14 17:31:38 -0700218 if channel not in channels_tested:
219 channels_tested.append(channel)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700220 if result['peak_throughput_pct'] == 100:
Omar El Ayach6e518a22019-06-13 13:55:42 -0700221 testclass_results_dict[test_id][channel] = result[
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700222 'sensitivity']
Omar El Ayach6e518a22019-06-13 13:55:42 -0700223 else:
224 testclass_results_dict[test_id][channel] = ''
Omar El Ayacha210d572019-03-14 17:31:38 -0700225
Omar El Ayach96714c82019-01-28 18:51:46 -0800226 # write csv
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700227 csv_header = ['Mode', 'MCS', 'Streams', 'Chain', 'Rate (Mbps)']
Omar El Ayacha210d572019-03-14 17:31:38 -0700228 for channel in channels_tested:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700229 csv_header.append('Ch. ' + str(channel))
Omar El Ayach96714c82019-01-28 18:51:46 -0800230 results_file_path = os.path.join(self.log_path, 'results.csv')
231 with open(results_file_path, mode='w') as csv_file:
Omar El Ayach96714c82019-01-28 18:51:46 -0800232 writer = csv.DictWriter(csv_file, fieldnames=csv_header)
233 writer.writeheader()
Omar El Ayacha210d572019-03-14 17:31:38 -0700234 for test_id, test_results in testclass_results_dict.items():
235 test_id_dict = dict(test_id)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700236 if 'legacy' in test_id_dict['mode']:
237 rate_list = self.VALID_RATES['legacy_2GHz']
Omar El Ayacha210d572019-03-14 17:31:38 -0700238 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700239 rate_list = self.VALID_RATES[test_id_dict['mode']]
Omar El Ayacha210d572019-03-14 17:31:38 -0700240 data_rate = next(rate.data_rate for rate in rate_list
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700241 if rate[:-1] == (test_id_dict['rate'],
242 test_id_dict['num_streams']))
Omar El Ayacha210d572019-03-14 17:31:38 -0700243 row_value = {
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700244 'Mode': test_id_dict['mode'],
245 'MCS': test_id_dict['rate'],
246 'Streams': test_id_dict['num_streams'],
247 'Chain': test_id_dict['chain_mask'],
248 'Rate (Mbps)': data_rate,
Omar El Ayacha210d572019-03-14 17:31:38 -0700249 }
250 for channel in channels_tested:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700251 row_value['Ch. ' + str(channel)] = test_results.pop(
252 channel, ' ')
Omar El Ayacha210d572019-03-14 17:31:38 -0700253 writer.writerow(row_value)
Omar El Ayach96714c82019-01-28 18:51:46 -0800254
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700255 if not self.testclass_params['traffic_type'].lower() == 'ping':
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800256 WifiRvrTest.process_testclass_results(self)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700257
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800258 def process_rvr_test_results(self, testcase_params, rvr_result):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700259 """Post processes RvR results to compute sensitivity.
260
261 Takes in the results of the RvR tests and computes the sensitivity of
262 the current rate by looking at the point at which throughput drops
263 below the percentage specified in the config file. The function then
264 calls on its parent class process_test_results to plot the result.
265
266 Args:
267 rvr_result: dict containing attenuation, throughput and other meta
268 data
269 """
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700270 rvr_result['peak_throughput'] = max(rvr_result['throughput_receive'])
271 rvr_result['peak_throughput_pct'] = 100
Omar El Ayach33f80c02018-09-27 15:02:03 -0700272 throughput_check = [
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700273 throughput < rvr_result['peak_throughput'] *
274 (self.testclass_params['throughput_pct_at_sensitivity'] / 100)
275 for throughput in rvr_result['throughput_receive']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700276 ]
277 consistency_check = [
278 idx for idx in range(len(throughput_check))
279 if all(throughput_check[idx:])
280 ]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700281 rvr_result['atten_at_range'] = rvr_result['attenuation'][
Omar El Ayach33f80c02018-09-27 15:02:03 -0700282 consistency_check[0] - 1]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700283 rvr_result['range'] = rvr_result['fixed_attenuation'] + (
284 rvr_result['atten_at_range'])
285 rvr_result['sensitivity'] = self.testclass_params['ap_tx_power'] + (
286 self.testbed_params['ap_tx_power_offset'][str(
287 testcase_params['channel'])] - rvr_result['range'])
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800288 WifiRvrTest.process_test_results(self, rvr_result)
289
290 def process_ping_test_results(self, testcase_params, ping_result):
291 """Post processes RvR results to compute sensitivity.
292
293 Takes in the results of the RvR tests and computes the sensitivity of
294 the current rate by looking at the point at which throughput drops
295 below the percentage specified in the config file. The function then
296 calls on its parent class process_test_results to plot the result.
297
298 Args:
299 rvr_result: dict containing attenuation, throughput and other meta
300 data
301 """
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800302 WifiPingTest.process_ping_results(self, testcase_params, ping_result)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700303 ping_result['sensitivity'] = self.testclass_params['ap_tx_power'] + (
304 self.testbed_params['ap_tx_power_offset'][str(
305 testcase_params['channel'])] - ping_result['range'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700306
Omar El Ayach03e40612019-05-01 16:25:39 -0700307 def setup_sensitivity_test(self, testcase_params):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700308 if testcase_params['traffic_type'].lower() == 'ping':
Omar El Ayach03e40612019-05-01 16:25:39 -0700309 self.setup_ping_test(testcase_params)
310 self.run_sensitivity_test = self.run_ping_test
311 self.process_sensitivity_test_results = (
312 self.process_ping_test_results)
313 else:
314 self.setup_rvr_test(testcase_params)
315 self.run_sensitivity_test = self.run_rvr_test
316 self.process_sensitivity_test_results = (
317 self.process_rvr_test_results)
318
Omar El Ayach33f80c02018-09-27 15:02:03 -0700319 def setup_ap(self, testcase_params):
Omar El Ayach5a1496bf2019-01-09 11:43:02 -0800320 """Sets up the AP and attenuator to compensate for AP chain imbalance.
Omar El Ayach33f80c02018-09-27 15:02:03 -0700321
322 Args:
323 testcase_params: dict containing AP and other test params
324 """
325 band = self.access_point.band_lookup_by_channel(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700326 testcase_params['channel'])
327 if '2G' in band:
Omar El Ayacha210d572019-03-14 17:31:38 -0700328 frequency = wutils.WifiEnums.channel_2G_to_freq[
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700329 testcase_params['channel']]
Omar El Ayach33f80c02018-09-27 15:02:03 -0700330 else:
Omar El Ayacha210d572019-03-14 17:31:38 -0700331 frequency = wutils.WifiEnums.channel_5G_to_freq[
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700332 testcase_params['channel']]
Omar El Ayach33f80c02018-09-27 15:02:03 -0700333 if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700334 self.access_point.set_region(self.testbed_params['DFS_region'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700335 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700336 self.access_point.set_region(self.testbed_params['default_region'])
337 self.access_point.set_channel(band, testcase_params['channel'])
338 self.access_point.set_bandwidth(band, testcase_params['mode'])
339 self.access_point.set_power(band, testcase_params['ap_tx_power'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700340 self.access_point.set_rate(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700341 band, testcase_params['mode'], testcase_params['num_streams'],
342 testcase_params['rate'], testcase_params['short_gi'])
Omar El Ayach96714c82019-01-28 18:51:46 -0800343 # Set attenuator offsets and set attenuators to initial condition
344 atten_offsets = self.testbed_params['chain_offset'][str(
345 testcase_params['channel'])]
Omar El Ayach5a1496bf2019-01-09 11:43:02 -0800346 for atten in self.attenuators:
Omar El Ayach96714c82019-01-28 18:51:46 -0800347 if 'AP-Chain-0' in atten.path:
348 atten.offset = atten_offsets[0]
349 elif 'AP-Chain-1' in atten.path:
350 atten.offset = atten_offsets[1]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700351 self.log.info('Access Point Configuration: {}'.format(
Omar El Ayach33f80c02018-09-27 15:02:03 -0700352 self.access_point.ap_settings))
353
Omar El Ayach6e518a22019-06-13 13:55:42 -0700354 def setup_dut(self, testcase_params):
355 """Sets up the DUT in the configuration required by the test.
356
357 Args:
358 testcase_params: dict containing AP and other test params
359 """
Omar El Ayach656bf3d2019-07-31 15:04:53 -0700360 # Check battery level before test
361 if not wputils.health_check(self.dut, 10):
362 asserts.skip('Battery level too low. Skipping test.')
363 # Turn screen off to preserve battery
364 self.dut.go_to_sleep()
Omar El Ayach6e518a22019-06-13 13:55:42 -0700365 band = self.access_point.band_lookup_by_channel(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700366 testcase_params['channel'])
Omar El Ayach656bf3d2019-07-31 15:04:53 -0700367 wutils.reset_wifi(self.dut)
368 self.dut.droid.wifiSetCountryCode(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700369 self.testclass_params['country_code'])
370 self.main_network[band]['channel'] = testcase_params['channel']
Omar El Ayach6e518a22019-06-13 13:55:42 -0700371 wutils.wifi_connect(
Omar El Ayach656bf3d2019-07-31 15:04:53 -0700372 self.dut,
Omar El Ayach6e518a22019-06-13 13:55:42 -0700373 self.main_network[band],
374 num_of_tries=5,
375 check_connectivity=False)
Omar El Ayach656bf3d2019-07-31 15:04:53 -0700376 self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700377 'wlan0')[0]
Omar El Ayach6e518a22019-06-13 13:55:42 -0700378 atten_dut_chain_map = wputils.get_atten_dut_chain_map(
Omar El Ayach656bf3d2019-07-31 15:04:53 -0700379 self.attenuators, self.dut, self.ping_server, self.dut_ip)
Omar El Ayach6e518a22019-06-13 13:55:42 -0700380 for idx, atten in enumerate(self.attenuators):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700381 if atten_dut_chain_map[idx] == testcase_params['attenuated_chain']:
Omar El Ayach6e518a22019-06-13 13:55:42 -0700382 atten.offset = atten.instrument.max_atten
383
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700384 def extract_test_id(self, testcase_params, id_fields):
385 test_id = collections.OrderedDict(
386 (param, testcase_params[param]) for param in id_fields)
387 return test_id
388
389 def get_start_atten(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700390 """Gets the starting attenuation for this sensitivity test.
391
392 The function gets the starting attenuation by checking whether a test
393 as the next higher MCS has been executed. If so it sets the starting
394 point a configurable number of dBs below the next MCS's sensitivity.
395
396 Returns:
397 start_atten: starting attenuation for current test
398 """
399 # Get the current and reference test config. The reference test is the
400 # one performed at the current MCS+1
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700401 current_rate = testcase_params['rate']
402 ref_test_params = self.extract_test_id(
403 testcase_params,
404 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
405 if 'legacy' in testcase_params['mode']:
406 if testcase_params['channel'] <= 13:
407 rate_list = self.VALID_RATES['legacy_2GHz']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700408 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700409 rate_list = self.VALID_RATES['legacy_5GHz']
Omar El Ayach03e40612019-05-01 16:25:39 -0700410 ref_index = max(
411 0,
412 rate_list.index(self.RateTuple(current_rate, 1, current_rate))
413 - 1)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700414 ref_test_params['rate'] = rate_list[ref_index].mcs
Omar El Ayach33f80c02018-09-27 15:02:03 -0700415 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700416 ref_test_params['rate'] = current_rate + 1
Omar El Ayach33f80c02018-09-27 15:02:03 -0700417
418 # Check if reference test has been run and set attenuation accordingly
419 previous_params = [
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700420 self.extract_test_id(
421 result['testcase_params'],
422 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700423 for result in self.testclass_results
424 ]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700425
Omar El Ayach33f80c02018-09-27 15:02:03 -0700426 try:
427 ref_index = previous_params.index(ref_test_params)
Omar El Ayacha210d572019-03-14 17:31:38 -0700428 start_atten = self.testclass_results[ref_index][
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700429 'atten_at_range'] - (
430 self.testclass_params['adjacent_mcs_range_gap'])
Omar El Ayach03e40612019-05-01 16:25:39 -0700431 except ValueError:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700432 self.log.warning(
433 'Reference test not found. Starting from {} dB'.format(
434 self.testclass_params['atten_start']))
435 start_atten = self.testclass_params['atten_start']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700436 return start_atten
437
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700438 def compile_test_params(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700439 """Function that generates test params based on the test name."""
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700440 if testcase_params['chain_mask'] in ['0', '1']:
441 testcase_params['attenuated_chain'] = 'DUT-Chain-{}'.format(
442 1 if testcase_params['chain_mask'] == '0' else 0)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700443 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700444 testcase_params['attenuated_chain'] = None
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800445
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700446 self.testclass_params[
447 'range_ping_loss_threshold'] = 100 - self.testclass_params[
448 'throughput_pct_at_sensitivity']
449 if self.testclass_params['traffic_type'] == 'UDP':
450 testcase_params['iperf_args'] = '-i 1 -t {} -J -u -b {}'.format(
451 self.testclass_params['iperf_duration'],
452 self.testclass_params['UDP_rates'][testcase_params['mode']])
453 elif self.testclass_params['traffic_type'] == 'TCP':
454 testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
455 self.testclass_params['iperf_duration'])
Omar El Ayach14416ac2019-01-30 14:58:19 -0800456
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700457 if self.testclass_params['traffic_type'] != 'ping' and isinstance(
Omar El Ayacha210d572019-03-14 17:31:38 -0700458 self.iperf_client, iperf_client.IPerfClientOverAdb):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700459 testcase_params['iperf_args'] += ' -R'
460 testcase_params['use_client_output'] = True
Omar El Ayach14416ac2019-01-30 14:58:19 -0800461 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700462 testcase_params['use_client_output'] = False
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800463
Omar El Ayach33f80c02018-09-27 15:02:03 -0700464 return testcase_params
465
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700466 def _test_sensitivity(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700467 """ Function that gets called for each test case
468
469 The function gets called in each rvr test case. The function customizes
470 the rvr test based on the test name of the test that called it
471 """
472 # Compile test parameters from config and test name
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700473 testcase_params = self.compile_test_params(testcase_params)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700474 testcase_params.update(self.testclass_params)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700475 testcase_params['atten_start'] = self.get_start_atten(testcase_params)
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800476 num_atten_steps = int(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700477 (testcase_params['atten_stop'] - testcase_params['atten_start']) /
478 testcase_params['atten_step'])
479 testcase_params['atten_range'] = [
480 testcase_params['atten_start'] + x * testcase_params['atten_step']
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800481 for x in range(0, num_atten_steps)
482 ]
Omar El Ayach33f80c02018-09-27 15:02:03 -0700483
484 # Prepare devices and run test
Omar El Ayach03e40612019-05-01 16:25:39 -0700485 self.setup_sensitivity_test(testcase_params)
486 result = self.run_sensitivity_test(testcase_params)
487 self.process_sensitivity_test_results(testcase_params, result)
488
Omar El Ayach33f80c02018-09-27 15:02:03 -0700489 # Post-process results
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800490 self.testclass_results.append(result)
491 self.pass_fail_check(result)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700492
Omar El Ayach96714c82019-01-28 18:51:46 -0800493 def generate_test_cases(self, channels, chain_mask):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700494 """Function that auto-generates test cases for a test class."""
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700495 test_cases = []
Omar El Ayach33f80c02018-09-27 15:02:03 -0700496 for channel in channels:
497 for mode in self.VALID_TEST_CONFIGS[channel]:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700498 if 'VHT' in mode:
Omar El Ayacha210d572019-03-14 17:31:38 -0700499 rates = self.VALID_RATES[mode]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700500 elif 'HT' in mode:
Omar El Ayacha210d572019-03-14 17:31:38 -0700501 rates = self.VALID_RATES[mode]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700502 elif 'legacy' in mode and channel < 14:
503 rates = self.VALID_RATES['legacy_2GHz']
504 elif 'legacy' in mode and channel > 14:
505 rates = self.VALID_RATES['legacy_5GHz']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700506 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700507 raise ValueError('Invalid test mode.')
Omar El Ayach96714c82019-01-28 18:51:46 -0800508 for chain, rate in itertools.product(chain_mask, rates):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700509 testcase_params = collections.OrderedDict(
510 channel=channel,
511 mode=mode,
512 rate=rate.mcs,
513 num_streams=rate.streams,
514 short_gi=1,
515 chain_mask=chain)
516 if chain in ['0', '1'] and rate[1] == 2:
Omar El Ayach96714c82019-01-28 18:51:46 -0800517 # Do not test 2-stream rates in single chain mode
518 continue
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700519 if 'legacy' in mode:
520 testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
521 '_ch{}'.format(
Omar El Ayach03e40612019-05-01 16:25:39 -0700522 channel, mode,
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700523 str(rate.mcs).replace('.', 'p'),
Omar El Ayach03e40612019-05-01 16:25:39 -0700524 rate.streams, chain))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700525 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700526 testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
527 '_ch{}'.format(
Omar El Ayach03e40612019-05-01 16:25:39 -0700528 channel, mode, rate.mcs,
529 rate.streams, chain))
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700530 setattr(self, testcase_name,
531 partial(self._test_sensitivity, testcase_params))
532 test_cases.append(testcase_name)
533 return test_cases
Omar El Ayach33f80c02018-09-27 15:02:03 -0700534
535
536class WifiSensitivity_AllChannels_Test(WifiSensitivityTest):
537 def __init__(self, controllers):
538 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700539 self.tests = self.generate_test_cases(
Omar El Ayach96714c82019-01-28 18:51:46 -0800540 [1, 2, 6, 10, 11, 36, 40, 44, 48, 149, 153, 157, 161],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700541 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700542
543
544class WifiSensitivity_2GHz_Test(WifiSensitivityTest):
545 def __init__(self, controllers):
546 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700547 self.tests = self.generate_test_cases([1, 2, 6, 10, 11],
548 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700549
550
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800551class WifiSensitivity_5GHz_Test(WifiSensitivityTest):
552 def __init__(self, controllers):
553 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700554 self.tests = self.generate_test_cases(
555 [36, 40, 44, 48, 149, 153, 157, 161], ['0', '1', '2x2'])
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800556
557
Omar El Ayach33f80c02018-09-27 15:02:03 -0700558class WifiSensitivity_UNII1_Test(WifiSensitivityTest):
559 def __init__(self, controllers):
560 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700561 self.tests = self.generate_test_cases([36, 40, 44, 48],
562 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700563
564
565class WifiSensitivity_UNII3_Test(WifiSensitivityTest):
566 def __init__(self, controllers):
567 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700568 self.tests = self.generate_test_cases([149, 153, 157, 161],
569 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700570
571
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700572# Over-the air version of senstivity tests
573class WifiOtaSensitivityTest(WifiSensitivityTest):
574 """Class to test over-the-air senstivity.
575
576 This class implements measures WiFi sensitivity tests in an OTA chamber.
577 It allows setting orientation and other chamber parameters to study
578 performance in varying channel conditions
579 """
580
581 def setup_class(self):
582 WifiSensitivityTest.setup_class(self)
583 self.ota_chamber = ota_chamber.create(
584 self.user_params['OTAChamber'])[0]
585
586 def teardown_class(self):
587 WifiSensitivityTest.teardown_class(self)
588 self.ota_chamber.reset_chamber()
589
590 def setup_sensitivity_test(self, testcase_params):
591 # Setup turntable
592 self.ota_chamber.set_orientation(testcase_params['orientation'])
593 # Continue test setup
594 WifiSensitivityTest.setup_sensitivity_test(self, testcase_params)
595
596 def process_testclass_results(self):
597 """Saves and plots test results from all executed test cases."""
598 testclass_results_dict = collections.OrderedDict()
599 id_fields = ['mode', 'rate', 'num_streams', 'chain_mask']
600 plots = []
601 for result in self.testclass_results:
602 test_id = self.extract_test_id(result['testcase_params'],
603 id_fields)
604 test_id = tuple(test_id.items())
605 channel = result['testcase_params']['channel']
606 if test_id not in testclass_results_dict:
607 testclass_results_dict[test_id] = collections.OrderedDict()
608 if channel not in testclass_results_dict[test_id]:
609 testclass_results_dict[test_id][channel] = {
610 'orientation': [],
611 'sensitivity': []
612 }
613 testclass_results_dict[test_id][channel]['orientation'].append(
614 result['testcase_params']['orientation'])
615 if result['peak_throughput_pct'] == 100:
616 testclass_results_dict[test_id][channel]['sensitivity'].append(
617 result['sensitivity'])
618 else:
619 testclass_results_dict[test_id][channel]['sensitivity'].append(
620 float('nan'))
621
622 for test_id, test_data in testclass_results_dict.items():
623 test_id_dict = dict(test_id)
624 if 'legacy' in test_id_dict['mode']:
625 test_id_str = '{} {}Mbps, Chain Mask = {}'.format(
626 test_id_dict['mode'], test_id_dict['rate'],
627 test_id_dict['chain_mask'])
628 else:
629 test_id_str = '{} MCS{} Nss{}, Chain Mask = {}'.format(
630 test_id_dict['mode'], test_id_dict['rate'],
631 test_id_dict['num_streams'], test_id_dict['chain_mask'])
632 curr_plot = wputils.BokehFigure(
633 title=str(test_id_str),
634 x_label='Orientation (deg)',
635 primary_y='Sensitivity (dBm)')
636 for channel, channel_results in test_data.items():
637 curr_plot.add_line(
638 channel_results['orientation'],
639 channel_results['sensitivity'],
640 legend='Channel {}'.format(channel))
641 current_context = (
642 context.get_current_context().get_full_output_path())
643 output_file_path = os.path.join(current_context,
644 str(test_id_str) + '.html')
645 curr_plot.generate_figure(output_file_path)
646 plots.append(curr_plot)
647 output_file_path = os.path.join(current_context, 'results.html')
648 wputils.BokehFigure.save_figures(plots, output_file_path)
649
650 def get_start_atten(self, testcase_params):
651 """Gets the starting attenuation for this sensitivity test.
652
653 The function gets the starting attenuation by checking whether a test
654 at the same rate configuration has executed. If so it sets the starting
655 point a configurable number of dBs below the reference test.
656
657 Returns:
658 start_atten: starting attenuation for current test
659 """
660 # Get the current and reference test config. The reference test is the
661 # one performed at the current MCS+1
662 ref_test_params = self.extract_test_id(
663 testcase_params,
664 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
665 # Check if reference test has been run and set attenuation accordingly
666 previous_params = [
667 self.extract_test_id(
668 result['testcase_params'],
669 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
670 for result in self.testclass_results
671 ]
672 try:
673 ref_index = previous_params[::-1].index(ref_test_params)
674 ref_index = len(previous_params) - 1 - ref_index
675 start_atten = self.testclass_results[ref_index][
676 'atten_at_range'] - (
677 self.testclass_params['adjacent_mcs_range_gap'])
678 except ValueError:
679 print('Reference test not found. Starting from {} dB'.format(
680 self.testclass_params['atten_start']))
681 start_atten = self.testclass_params['atten_start']
682 return start_atten
683
684 def generate_test_cases(self, channels, requested_rates, chain_mask,
685 angles):
686 """Function that auto-generates test cases for a test class."""
687 test_cases = []
688 for channel in channels:
689 for mode in self.VALID_TEST_CONFIGS[channel]:
690 if 'VHT' in mode:
691 valid_rates = self.VALID_RATES[mode]
692 elif 'HT' in mode:
693 valid_rates = self.VALID_RATES[mode]
694 elif 'legacy' in mode and channel < 14:
695 valid_rates = self.VALID_RATES['legacy_2GHz']
696 elif 'legacy' in mode and channel > 14:
697 valid_rates = self.VALID_RATES['legacy_5GHz']
698 else:
699 raise ValueError('Invalid test mode.')
700 for chain, rate, angle in itertools.product(
701 chain_mask, valid_rates, angles):
702 testcase_params = collections.OrderedDict(
703 channel=channel,
704 mode=mode,
705 rate=rate.mcs,
706 num_streams=rate.streams,
707 short_gi=1,
708 chain_mask=chain,
709 orientation=angle)
710 if rate not in requested_rates:
711 continue
712 if str(chain) in ['0', '1'] and rate[1] == 2:
713 # Do not test 2-stream rates in single chain mode
714 continue
715 if 'legacy' in mode:
716 testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
717 '_ch{}_{}deg'.format(
718 channel, mode,
719 str(rate.mcs).replace('.', 'p'),
720 rate.streams, chain, angle))
721 else:
722 testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
723 '_ch{}_{}deg'.format(
724 channel, mode, rate.mcs,
725 rate.streams, chain, angle))
726 setattr(self, testcase_name,
727 partial(self._test_sensitivity, testcase_params))
728 test_cases.append(testcase_name)
729
730
731class WifiOtaSensitivity_10Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700732 def __init__(self, controllers):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700733 WifiSensitivityTest.__init__(self, controllers)
734 requested_channels = [6, 36, 149]
735 requested_rates = [
736 self.RateTuple(8, 1, 86.7),
737 self.RateTuple(0, 1, 7.2),
738 self.RateTuple(8, 2, 173.3),
739 self.RateTuple(0, 2, 14.4)
740 ]
741 self.tests = self.generate_test_cases(requested_channels,
742 requested_rates, ['2x2'],
743 list(range(0, 360, 10)))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700744
745
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700746class WifiOtaSensitivity_SingleChain_10Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700747 def __init__(self, controllers):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700748 WifiSensitivityTest.__init__(self, controllers)
749 requested_channels = [6, 36, 149]
750 requested_rates = [
751 self.RateTuple(8, 1, 86.7),
752 self.RateTuple(0, 1, 7.2),
753 ]
754 self.tests = self.generate_test_cases(requested_channels,
755 requested_rates, ['2x2'],
756 list(range(0, 360, 10)))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700757
758
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700759class WifiOtaSensitivity_45Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700760 def __init__(self, controllers):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700761 WifiSensitivityTest.__init__(self, controllers)
762 requested_rates = [
763 self.RateTuple(8, 1, 86.7),
764 self.RateTuple(0, 1, 7.2),
765 self.RateTuple(8, 2, 173.3),
766 self.RateTuple(0, 2, 14.4)
767 ]
768 self.tests = self.generate_test_cases(
769 [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], requested_rates,
770 ['2x2'], list(range(0, 360, 45)))