blob: a31dc66837dbb4c57085e05db8ce4d82a8ebc1ae [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 Ayach39acf802019-08-02 17:52:39 -0700367 current_network = self.dut.droid.wifiGetConnectionInfo()
368 valid_connection = wutils.validate_connection(self.dut)
369 if valid_connection and current_network['SSID'] == self.main_network[
370 band]['SSID']:
371 self.log.info('Already connected to desired network')
372 else:
373 wutils.reset_wifi(self.dut)
374 self.dut.droid.wifiSetCountryCode(
375 self.testclass_params['country_code'])
376 self.main_network[band]['channel'] = testcase_params['channel']
377 wutils.wifi_connect(
378 self.dut,
379 self.main_network[band],
380 num_of_tries=5,
381 check_connectivity=False)
382 self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
Omar El Ayach189dcbd2019-08-28 17:26:18 -0700383 atten_dut_chain_map = wputils.get_current_atten_dut_chain_map(
384 self.attenuators, self.dut, self.ping_server)
Omar El Ayach6e518a22019-06-13 13:55:42 -0700385 for idx, atten in enumerate(self.attenuators):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700386 if atten_dut_chain_map[idx] == testcase_params['attenuated_chain']:
Omar El Ayach6e518a22019-06-13 13:55:42 -0700387 atten.offset = atten.instrument.max_atten
388
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700389 def extract_test_id(self, testcase_params, id_fields):
390 test_id = collections.OrderedDict(
391 (param, testcase_params[param]) for param in id_fields)
392 return test_id
393
394 def get_start_atten(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700395 """Gets the starting attenuation for this sensitivity test.
396
397 The function gets the starting attenuation by checking whether a test
398 as the next higher MCS has been executed. If so it sets the starting
399 point a configurable number of dBs below the next MCS's sensitivity.
400
401 Returns:
402 start_atten: starting attenuation for current test
403 """
404 # Get the current and reference test config. The reference test is the
405 # one performed at the current MCS+1
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700406 current_rate = testcase_params['rate']
407 ref_test_params = self.extract_test_id(
408 testcase_params,
409 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
410 if 'legacy' in testcase_params['mode']:
411 if testcase_params['channel'] <= 13:
412 rate_list = self.VALID_RATES['legacy_2GHz']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700413 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700414 rate_list = self.VALID_RATES['legacy_5GHz']
Omar El Ayach03e40612019-05-01 16:25:39 -0700415 ref_index = max(
416 0,
417 rate_list.index(self.RateTuple(current_rate, 1, current_rate))
418 - 1)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700419 ref_test_params['rate'] = rate_list[ref_index].mcs
Omar El Ayach33f80c02018-09-27 15:02:03 -0700420 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700421 ref_test_params['rate'] = current_rate + 1
Omar El Ayach33f80c02018-09-27 15:02:03 -0700422
423 # Check if reference test has been run and set attenuation accordingly
424 previous_params = [
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700425 self.extract_test_id(
426 result['testcase_params'],
427 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700428 for result in self.testclass_results
429 ]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700430
Omar El Ayach33f80c02018-09-27 15:02:03 -0700431 try:
432 ref_index = previous_params.index(ref_test_params)
Omar El Ayacha210d572019-03-14 17:31:38 -0700433 start_atten = self.testclass_results[ref_index][
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700434 'atten_at_range'] - (
435 self.testclass_params['adjacent_mcs_range_gap'])
Omar El Ayach03e40612019-05-01 16:25:39 -0700436 except ValueError:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700437 self.log.warning(
438 'Reference test not found. Starting from {} dB'.format(
439 self.testclass_params['atten_start']))
440 start_atten = self.testclass_params['atten_start']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700441 return start_atten
442
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700443 def compile_test_params(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700444 """Function that generates test params based on the test name."""
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700445 if testcase_params['chain_mask'] in ['0', '1']:
446 testcase_params['attenuated_chain'] = 'DUT-Chain-{}'.format(
447 1 if testcase_params['chain_mask'] == '0' else 0)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700448 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700449 testcase_params['attenuated_chain'] = None
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800450
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700451 self.testclass_params[
452 'range_ping_loss_threshold'] = 100 - self.testclass_params[
453 'throughput_pct_at_sensitivity']
454 if self.testclass_params['traffic_type'] == 'UDP':
455 testcase_params['iperf_args'] = '-i 1 -t {} -J -u -b {}'.format(
456 self.testclass_params['iperf_duration'],
457 self.testclass_params['UDP_rates'][testcase_params['mode']])
458 elif self.testclass_params['traffic_type'] == 'TCP':
459 testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
460 self.testclass_params['iperf_duration'])
Omar El Ayach14416ac2019-01-30 14:58:19 -0800461
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700462 if self.testclass_params['traffic_type'] != 'ping' and isinstance(
Omar El Ayacha210d572019-03-14 17:31:38 -0700463 self.iperf_client, iperf_client.IPerfClientOverAdb):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700464 testcase_params['iperf_args'] += ' -R'
465 testcase_params['use_client_output'] = True
Omar El Ayach14416ac2019-01-30 14:58:19 -0800466 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700467 testcase_params['use_client_output'] = False
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800468
Omar El Ayach33f80c02018-09-27 15:02:03 -0700469 return testcase_params
470
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700471 def _test_sensitivity(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700472 """ Function that gets called for each test case
473
474 The function gets called in each rvr test case. The function customizes
475 the rvr test based on the test name of the test that called it
476 """
477 # Compile test parameters from config and test name
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700478 testcase_params = self.compile_test_params(testcase_params)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700479 testcase_params.update(self.testclass_params)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700480 testcase_params['atten_start'] = self.get_start_atten(testcase_params)
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800481 num_atten_steps = int(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700482 (testcase_params['atten_stop'] - testcase_params['atten_start']) /
483 testcase_params['atten_step'])
484 testcase_params['atten_range'] = [
485 testcase_params['atten_start'] + x * testcase_params['atten_step']
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800486 for x in range(0, num_atten_steps)
487 ]
Omar El Ayach33f80c02018-09-27 15:02:03 -0700488
489 # Prepare devices and run test
Omar El Ayach03e40612019-05-01 16:25:39 -0700490 self.setup_sensitivity_test(testcase_params)
491 result = self.run_sensitivity_test(testcase_params)
492 self.process_sensitivity_test_results(testcase_params, result)
493
Omar El Ayach33f80c02018-09-27 15:02:03 -0700494 # Post-process results
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800495 self.testclass_results.append(result)
496 self.pass_fail_check(result)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700497
Omar El Ayach96714c82019-01-28 18:51:46 -0800498 def generate_test_cases(self, channels, chain_mask):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700499 """Function that auto-generates test cases for a test class."""
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700500 test_cases = []
Omar El Ayach33f80c02018-09-27 15:02:03 -0700501 for channel in channels:
502 for mode in self.VALID_TEST_CONFIGS[channel]:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700503 if 'VHT' in mode:
Omar El Ayacha210d572019-03-14 17:31:38 -0700504 rates = self.VALID_RATES[mode]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700505 elif 'HT' in mode:
Omar El Ayacha210d572019-03-14 17:31:38 -0700506 rates = self.VALID_RATES[mode]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700507 elif 'legacy' in mode and channel < 14:
508 rates = self.VALID_RATES['legacy_2GHz']
509 elif 'legacy' in mode and channel > 14:
510 rates = self.VALID_RATES['legacy_5GHz']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700511 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700512 raise ValueError('Invalid test mode.')
Omar El Ayach96714c82019-01-28 18:51:46 -0800513 for chain, rate in itertools.product(chain_mask, rates):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700514 testcase_params = collections.OrderedDict(
515 channel=channel,
516 mode=mode,
517 rate=rate.mcs,
518 num_streams=rate.streams,
519 short_gi=1,
520 chain_mask=chain)
521 if chain in ['0', '1'] and rate[1] == 2:
Omar El Ayach96714c82019-01-28 18:51:46 -0800522 # Do not test 2-stream rates in single chain mode
523 continue
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700524 if 'legacy' in mode:
525 testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
526 '_ch{}'.format(
Omar El Ayach03e40612019-05-01 16:25:39 -0700527 channel, mode,
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700528 str(rate.mcs).replace('.', 'p'),
Omar El Ayach03e40612019-05-01 16:25:39 -0700529 rate.streams, chain))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700530 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700531 testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
532 '_ch{}'.format(
Omar El Ayach03e40612019-05-01 16:25:39 -0700533 channel, mode, rate.mcs,
534 rate.streams, chain))
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700535 setattr(self, testcase_name,
536 partial(self._test_sensitivity, testcase_params))
537 test_cases.append(testcase_name)
Omar El Ayachab047c02019-09-03 11:38:30 -0700538 return test_cases
Omar El Ayach33f80c02018-09-27 15:02:03 -0700539
540
541class WifiSensitivity_AllChannels_Test(WifiSensitivityTest):
542 def __init__(self, controllers):
543 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700544 self.tests = self.generate_test_cases(
Omar El Ayach9873c082019-09-04 12:14:50 -0700545 [6, 36, 40, 44, 48, 149, 153, 157, 161], ['0', '1', '2x2'])
546
547
548class WifiSensitivity_SampleChannels_Test(WifiSensitivityTest):
549 def __init__(self, controllers):
550 base_test.BaseTestClass.__init__(self, controllers)
551 self.tests = self.generate_test_cases([6, 36, 149], ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700552
553
554class WifiSensitivity_2GHz_Test(WifiSensitivityTest):
555 def __init__(self, controllers):
556 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700557 self.tests = self.generate_test_cases([1, 2, 6, 10, 11],
558 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700559
560
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800561class WifiSensitivity_5GHz_Test(WifiSensitivityTest):
562 def __init__(self, controllers):
563 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700564 self.tests = self.generate_test_cases(
565 [36, 40, 44, 48, 149, 153, 157, 161], ['0', '1', '2x2'])
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800566
567
Omar El Ayach33f80c02018-09-27 15:02:03 -0700568class WifiSensitivity_UNII1_Test(WifiSensitivityTest):
569 def __init__(self, controllers):
570 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700571 self.tests = self.generate_test_cases([36, 40, 44, 48],
572 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700573
574
575class WifiSensitivity_UNII3_Test(WifiSensitivityTest):
576 def __init__(self, controllers):
577 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700578 self.tests = self.generate_test_cases([149, 153, 157, 161],
579 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700580
581
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700582# Over-the air version of senstivity tests
583class WifiOtaSensitivityTest(WifiSensitivityTest):
584 """Class to test over-the-air senstivity.
585
586 This class implements measures WiFi sensitivity tests in an OTA chamber.
587 It allows setting orientation and other chamber parameters to study
588 performance in varying channel conditions
589 """
590
591 def setup_class(self):
592 WifiSensitivityTest.setup_class(self)
593 self.ota_chamber = ota_chamber.create(
594 self.user_params['OTAChamber'])[0]
595
596 def teardown_class(self):
597 WifiSensitivityTest.teardown_class(self)
598 self.ota_chamber.reset_chamber()
599
600 def setup_sensitivity_test(self, testcase_params):
601 # Setup turntable
602 self.ota_chamber.set_orientation(testcase_params['orientation'])
603 # Continue test setup
604 WifiSensitivityTest.setup_sensitivity_test(self, testcase_params)
605
606 def process_testclass_results(self):
607 """Saves and plots test results from all executed test cases."""
608 testclass_results_dict = collections.OrderedDict()
609 id_fields = ['mode', 'rate', 'num_streams', 'chain_mask']
610 plots = []
611 for result in self.testclass_results:
612 test_id = self.extract_test_id(result['testcase_params'],
613 id_fields)
614 test_id = tuple(test_id.items())
615 channel = result['testcase_params']['channel']
616 if test_id not in testclass_results_dict:
617 testclass_results_dict[test_id] = collections.OrderedDict()
618 if channel not in testclass_results_dict[test_id]:
619 testclass_results_dict[test_id][channel] = {
620 'orientation': [],
621 'sensitivity': []
622 }
623 testclass_results_dict[test_id][channel]['orientation'].append(
624 result['testcase_params']['orientation'])
625 if result['peak_throughput_pct'] == 100:
626 testclass_results_dict[test_id][channel]['sensitivity'].append(
627 result['sensitivity'])
628 else:
629 testclass_results_dict[test_id][channel]['sensitivity'].append(
630 float('nan'))
631
632 for test_id, test_data in testclass_results_dict.items():
633 test_id_dict = dict(test_id)
634 if 'legacy' in test_id_dict['mode']:
635 test_id_str = '{} {}Mbps, Chain Mask = {}'.format(
636 test_id_dict['mode'], test_id_dict['rate'],
637 test_id_dict['chain_mask'])
638 else:
639 test_id_str = '{} MCS{} Nss{}, Chain Mask = {}'.format(
640 test_id_dict['mode'], test_id_dict['rate'],
641 test_id_dict['num_streams'], test_id_dict['chain_mask'])
642 curr_plot = wputils.BokehFigure(
643 title=str(test_id_str),
644 x_label='Orientation (deg)',
645 primary_y='Sensitivity (dBm)')
646 for channel, channel_results in test_data.items():
647 curr_plot.add_line(
648 channel_results['orientation'],
649 channel_results['sensitivity'],
650 legend='Channel {}'.format(channel))
651 current_context = (
652 context.get_current_context().get_full_output_path())
653 output_file_path = os.path.join(current_context,
654 str(test_id_str) + '.html')
655 curr_plot.generate_figure(output_file_path)
656 plots.append(curr_plot)
657 output_file_path = os.path.join(current_context, 'results.html')
658 wputils.BokehFigure.save_figures(plots, output_file_path)
659
660 def get_start_atten(self, testcase_params):
661 """Gets the starting attenuation for this sensitivity test.
662
663 The function gets the starting attenuation by checking whether a test
664 at the same rate configuration has executed. If so it sets the starting
665 point a configurable number of dBs below the reference test.
666
667 Returns:
668 start_atten: starting attenuation for current test
669 """
670 # Get the current and reference test config. The reference test is the
671 # one performed at the current MCS+1
672 ref_test_params = self.extract_test_id(
673 testcase_params,
674 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
675 # Check if reference test has been run and set attenuation accordingly
676 previous_params = [
677 self.extract_test_id(
678 result['testcase_params'],
679 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
680 for result in self.testclass_results
681 ]
682 try:
683 ref_index = previous_params[::-1].index(ref_test_params)
684 ref_index = len(previous_params) - 1 - ref_index
685 start_atten = self.testclass_results[ref_index][
686 'atten_at_range'] - (
687 self.testclass_params['adjacent_mcs_range_gap'])
688 except ValueError:
689 print('Reference test not found. Starting from {} dB'.format(
690 self.testclass_params['atten_start']))
691 start_atten = self.testclass_params['atten_start']
692 return start_atten
693
694 def generate_test_cases(self, channels, requested_rates, chain_mask,
695 angles):
696 """Function that auto-generates test cases for a test class."""
697 test_cases = []
698 for channel in channels:
699 for mode in self.VALID_TEST_CONFIGS[channel]:
700 if 'VHT' in mode:
701 valid_rates = self.VALID_RATES[mode]
702 elif 'HT' in mode:
703 valid_rates = self.VALID_RATES[mode]
704 elif 'legacy' in mode and channel < 14:
705 valid_rates = self.VALID_RATES['legacy_2GHz']
706 elif 'legacy' in mode and channel > 14:
707 valid_rates = self.VALID_RATES['legacy_5GHz']
708 else:
709 raise ValueError('Invalid test mode.')
710 for chain, rate, angle in itertools.product(
711 chain_mask, valid_rates, angles):
712 testcase_params = collections.OrderedDict(
713 channel=channel,
714 mode=mode,
715 rate=rate.mcs,
716 num_streams=rate.streams,
717 short_gi=1,
718 chain_mask=chain,
719 orientation=angle)
720 if rate not in requested_rates:
721 continue
722 if str(chain) in ['0', '1'] and rate[1] == 2:
723 # Do not test 2-stream rates in single chain mode
724 continue
725 if 'legacy' in mode:
726 testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
727 '_ch{}_{}deg'.format(
728 channel, mode,
729 str(rate.mcs).replace('.', 'p'),
730 rate.streams, chain, angle))
731 else:
732 testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
733 '_ch{}_{}deg'.format(
734 channel, mode, rate.mcs,
735 rate.streams, chain, angle))
736 setattr(self, testcase_name,
737 partial(self._test_sensitivity, testcase_params))
738 test_cases.append(testcase_name)
739
740
741class WifiOtaSensitivity_10Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700742 def __init__(self, controllers):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700743 WifiSensitivityTest.__init__(self, controllers)
744 requested_channels = [6, 36, 149]
745 requested_rates = [
746 self.RateTuple(8, 1, 86.7),
747 self.RateTuple(0, 1, 7.2),
748 self.RateTuple(8, 2, 173.3),
749 self.RateTuple(0, 2, 14.4)
750 ]
751 self.tests = self.generate_test_cases(requested_channels,
752 requested_rates, ['2x2'],
753 list(range(0, 360, 10)))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700754
755
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700756class WifiOtaSensitivity_SingleChain_10Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700757 def __init__(self, controllers):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700758 WifiSensitivityTest.__init__(self, controllers)
759 requested_channels = [6, 36, 149]
760 requested_rates = [
761 self.RateTuple(8, 1, 86.7),
762 self.RateTuple(0, 1, 7.2),
763 ]
764 self.tests = self.generate_test_cases(requested_channels,
765 requested_rates, ['2x2'],
766 list(range(0, 360, 10)))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700767
768
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700769class WifiOtaSensitivity_45Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700770 def __init__(self, controllers):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700771 WifiSensitivityTest.__init__(self, controllers)
772 requested_rates = [
773 self.RateTuple(8, 1, 86.7),
774 self.RateTuple(0, 1, 7.2),
775 self.RateTuple(8, 2, 173.3),
776 self.RateTuple(0, 2, 14.4)
777 ]
778 self.tests = self.generate_test_cases(
779 [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], requested_rates,
780 ['2x2'], list(range(0, 360, 45)))