blob: 080b5ccb60a57f6b2cafc442333c2a483ff40954 [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)
Roshan Pius0367e082019-09-13 08:07:30 -0700374 wutils.set_wifi_country_code(self.dut,
Omar El Ayachffb5a462019-09-16 21:05:44 -0700375 self.testclass_params['country_code'])
Omar El Ayach39acf802019-08-02 17:52:39 -0700376 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 Ayach49141c02019-09-16 16:43:51 -0700385 self.log.info(
386 "Current Attenuator-DUT Chain Map: {}".format(atten_dut_chain_map))
Omar El Ayach6e518a22019-06-13 13:55:42 -0700387 for idx, atten in enumerate(self.attenuators):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700388 if atten_dut_chain_map[idx] == testcase_params['attenuated_chain']:
Omar El Ayach6e518a22019-06-13 13:55:42 -0700389 atten.offset = atten.instrument.max_atten
390
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700391 def extract_test_id(self, testcase_params, id_fields):
392 test_id = collections.OrderedDict(
393 (param, testcase_params[param]) for param in id_fields)
394 return test_id
395
396 def get_start_atten(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700397 """Gets the starting attenuation for this sensitivity test.
398
399 The function gets the starting attenuation by checking whether a test
400 as the next higher MCS has been executed. If so it sets the starting
401 point a configurable number of dBs below the next MCS's sensitivity.
402
403 Returns:
404 start_atten: starting attenuation for current test
405 """
406 # Get the current and reference test config. The reference test is the
407 # one performed at the current MCS+1
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700408 current_rate = testcase_params['rate']
409 ref_test_params = self.extract_test_id(
410 testcase_params,
411 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
412 if 'legacy' in testcase_params['mode']:
413 if testcase_params['channel'] <= 13:
414 rate_list = self.VALID_RATES['legacy_2GHz']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700415 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700416 rate_list = self.VALID_RATES['legacy_5GHz']
Omar El Ayach03e40612019-05-01 16:25:39 -0700417 ref_index = max(
418 0,
419 rate_list.index(self.RateTuple(current_rate, 1, current_rate))
420 - 1)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700421 ref_test_params['rate'] = rate_list[ref_index].mcs
Omar El Ayach33f80c02018-09-27 15:02:03 -0700422 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700423 ref_test_params['rate'] = current_rate + 1
Omar El Ayach33f80c02018-09-27 15:02:03 -0700424
425 # Check if reference test has been run and set attenuation accordingly
426 previous_params = [
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700427 self.extract_test_id(
428 result['testcase_params'],
429 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700430 for result in self.testclass_results
431 ]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700432
Omar El Ayach33f80c02018-09-27 15:02:03 -0700433 try:
434 ref_index = previous_params.index(ref_test_params)
Omar El Ayacha210d572019-03-14 17:31:38 -0700435 start_atten = self.testclass_results[ref_index][
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700436 'atten_at_range'] - (
437 self.testclass_params['adjacent_mcs_range_gap'])
Omar El Ayach03e40612019-05-01 16:25:39 -0700438 except ValueError:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700439 self.log.warning(
440 'Reference test not found. Starting from {} dB'.format(
441 self.testclass_params['atten_start']))
442 start_atten = self.testclass_params['atten_start']
Omar El Ayach49141c02019-09-16 16:43:51 -0700443 start_atten = max(start_atten, 0)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700444 return start_atten
445
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700446 def compile_test_params(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700447 """Function that generates test params based on the test name."""
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700448 if testcase_params['chain_mask'] in ['0', '1']:
449 testcase_params['attenuated_chain'] = 'DUT-Chain-{}'.format(
450 1 if testcase_params['chain_mask'] == '0' else 0)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700451 else:
Omar El Ayachdf470fb2019-09-16 12:22:28 -0700452 # Set attenuated chain to -1. Do not set to None as this will be
453 # compared to RF chain map which may include None
454 testcase_params['attenuated_chain'] = -1
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800455
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700456 self.testclass_params[
457 'range_ping_loss_threshold'] = 100 - self.testclass_params[
458 'throughput_pct_at_sensitivity']
459 if self.testclass_params['traffic_type'] == 'UDP':
460 testcase_params['iperf_args'] = '-i 1 -t {} -J -u -b {}'.format(
461 self.testclass_params['iperf_duration'],
462 self.testclass_params['UDP_rates'][testcase_params['mode']])
463 elif self.testclass_params['traffic_type'] == 'TCP':
464 testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
465 self.testclass_params['iperf_duration'])
Omar El Ayach14416ac2019-01-30 14:58:19 -0800466
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700467 if self.testclass_params['traffic_type'] != 'ping' and isinstance(
Omar El Ayacha210d572019-03-14 17:31:38 -0700468 self.iperf_client, iperf_client.IPerfClientOverAdb):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700469 testcase_params['iperf_args'] += ' -R'
470 testcase_params['use_client_output'] = True
Omar El Ayach14416ac2019-01-30 14:58:19 -0800471 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700472 testcase_params['use_client_output'] = False
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800473
Omar El Ayach33f80c02018-09-27 15:02:03 -0700474 return testcase_params
475
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700476 def _test_sensitivity(self, testcase_params):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700477 """ Function that gets called for each test case
478
479 The function gets called in each rvr test case. The function customizes
480 the rvr test based on the test name of the test that called it
481 """
482 # Compile test parameters from config and test name
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700483 testcase_params = self.compile_test_params(testcase_params)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700484 testcase_params.update(self.testclass_params)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700485 testcase_params['atten_start'] = self.get_start_atten(testcase_params)
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800486 num_atten_steps = int(
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700487 (testcase_params['atten_stop'] - testcase_params['atten_start']) /
488 testcase_params['atten_step'])
489 testcase_params['atten_range'] = [
490 testcase_params['atten_start'] + x * testcase_params['atten_step']
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800491 for x in range(0, num_atten_steps)
492 ]
Omar El Ayach33f80c02018-09-27 15:02:03 -0700493
494 # Prepare devices and run test
Omar El Ayach03e40612019-05-01 16:25:39 -0700495 self.setup_sensitivity_test(testcase_params)
496 result = self.run_sensitivity_test(testcase_params)
497 self.process_sensitivity_test_results(testcase_params, result)
498
Omar El Ayach33f80c02018-09-27 15:02:03 -0700499 # Post-process results
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800500 self.testclass_results.append(result)
501 self.pass_fail_check(result)
Omar El Ayach33f80c02018-09-27 15:02:03 -0700502
Omar El Ayachffb5a462019-09-16 21:05:44 -0700503 def generate_test_cases(self, channels, modes, chain_mask):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700504 """Function that auto-generates test cases for a test class."""
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700505 test_cases = []
Omar El Ayach33f80c02018-09-27 15:02:03 -0700506 for channel in channels:
Omar El Ayachffb5a462019-09-16 21:05:44 -0700507 requested_modes = set(modes).intersection(
508 set(self.VALID_TEST_CONFIGS[channel]))
509 for mode in requested_modes:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700510 if 'VHT' in mode:
Omar El Ayacha210d572019-03-14 17:31:38 -0700511 rates = self.VALID_RATES[mode]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700512 elif 'HT' in mode:
Omar El Ayacha210d572019-03-14 17:31:38 -0700513 rates = self.VALID_RATES[mode]
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700514 elif 'legacy' in mode and channel < 14:
515 rates = self.VALID_RATES['legacy_2GHz']
516 elif 'legacy' in mode and channel > 14:
517 rates = self.VALID_RATES['legacy_5GHz']
Omar El Ayach33f80c02018-09-27 15:02:03 -0700518 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700519 raise ValueError('Invalid test mode.')
Omar El Ayach96714c82019-01-28 18:51:46 -0800520 for chain, rate in itertools.product(chain_mask, rates):
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700521 testcase_params = collections.OrderedDict(
522 channel=channel,
523 mode=mode,
524 rate=rate.mcs,
525 num_streams=rate.streams,
526 short_gi=1,
527 chain_mask=chain)
528 if chain in ['0', '1'] and rate[1] == 2:
Omar El Ayach96714c82019-01-28 18:51:46 -0800529 # Do not test 2-stream rates in single chain mode
530 continue
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700531 if 'legacy' in mode:
532 testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
533 '_ch{}'.format(
Omar El Ayach03e40612019-05-01 16:25:39 -0700534 channel, mode,
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700535 str(rate.mcs).replace('.', 'p'),
Omar El Ayach03e40612019-05-01 16:25:39 -0700536 rate.streams, chain))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700537 else:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700538 testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
539 '_ch{}'.format(
Omar El Ayach03e40612019-05-01 16:25:39 -0700540 channel, mode, rate.mcs,
541 rate.streams, chain))
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700542 setattr(self, testcase_name,
543 partial(self._test_sensitivity, testcase_params))
544 test_cases.append(testcase_name)
Omar El Ayachab047c02019-09-03 11:38:30 -0700545 return test_cases
Omar El Ayach33f80c02018-09-27 15:02:03 -0700546
547
548class WifiSensitivity_AllChannels_Test(WifiSensitivityTest):
549 def __init__(self, controllers):
550 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700551 self.tests = self.generate_test_cases(
Omar El Ayachffb5a462019-09-16 21:05:44 -0700552 [6, 36, 40, 44, 48, 149, 153, 157, 161],
553 ['VHT20', 'VHT40', 'VHT80'], ['0', '1', '2x2'])
Omar El Ayach9873c082019-09-04 12:14:50 -0700554
555
556class WifiSensitivity_SampleChannels_Test(WifiSensitivityTest):
557 def __init__(self, controllers):
558 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachffb5a462019-09-16 21:05:44 -0700559 self.tests = self.generate_test_cases(
560 [6, 36, 149], ['VHT20', 'VHT40', 'VHT80'], ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700561
562
563class WifiSensitivity_2GHz_Test(WifiSensitivityTest):
564 def __init__(self, controllers):
565 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700566 self.tests = self.generate_test_cases([1, 2, 6, 10, 11],
Omar El Ayachffb5a462019-09-16 21:05:44 -0700567 ['VHT20'], ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700568
569
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800570class WifiSensitivity_5GHz_Test(WifiSensitivityTest):
571 def __init__(self, controllers):
572 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700573 self.tests = self.generate_test_cases(
Omar El Ayachffb5a462019-09-16 21:05:44 -0700574 [36, 40, 44, 48, 149, 153, 157, 161], ['VHT20', 'VHT40', 'VHT80'],
575 ['0', '1', '2x2'])
Omar El Ayach5fbc1222018-12-07 18:10:05 -0800576
577
Omar El Ayach33f80c02018-09-27 15:02:03 -0700578class WifiSensitivity_UNII1_Test(WifiSensitivityTest):
579 def __init__(self, controllers):
580 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachffb5a462019-09-16 21:05:44 -0700581 self.tests = self.generate_test_cases(
582 [36, 40, 44, 48], ['VHT20', 'VHT40', 'VHT80'], ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700583
584
585class WifiSensitivity_UNII3_Test(WifiSensitivityTest):
586 def __init__(self, controllers):
587 base_test.BaseTestClass.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700588 self.tests = self.generate_test_cases([149, 153, 157, 161],
Omar El Ayachffb5a462019-09-16 21:05:44 -0700589 ['VHT20', 'VHT40', 'VHT80'],
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700590 ['0', '1', '2x2'])
Omar El Ayach33f80c02018-09-27 15:02:03 -0700591
592
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700593# Over-the air version of senstivity tests
594class WifiOtaSensitivityTest(WifiSensitivityTest):
595 """Class to test over-the-air senstivity.
596
597 This class implements measures WiFi sensitivity tests in an OTA chamber.
598 It allows setting orientation and other chamber parameters to study
599 performance in varying channel conditions
600 """
601
Omar El Ayach40099d02019-09-12 15:17:33 -0700602 def __init__(self, controllers):
603 WifiSensitivityTest.__init__(self, controllers)
604 self.bb_metric_logger = (
605 wputils.BlackboxMappedMetricLogger.for_test_class())
606
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700607 def setup_class(self):
608 WifiSensitivityTest.setup_class(self)
609 self.ota_chamber = ota_chamber.create(
610 self.user_params['OTAChamber'])[0]
611
612 def teardown_class(self):
613 WifiSensitivityTest.teardown_class(self)
614 self.ota_chamber.reset_chamber()
615
616 def setup_sensitivity_test(self, testcase_params):
617 # Setup turntable
618 self.ota_chamber.set_orientation(testcase_params['orientation'])
619 # Continue test setup
620 WifiSensitivityTest.setup_sensitivity_test(self, testcase_params)
621
622 def process_testclass_results(self):
623 """Saves and plots test results from all executed test cases."""
624 testclass_results_dict = collections.OrderedDict()
625 id_fields = ['mode', 'rate', 'num_streams', 'chain_mask']
626 plots = []
627 for result in self.testclass_results:
628 test_id = self.extract_test_id(result['testcase_params'],
629 id_fields)
630 test_id = tuple(test_id.items())
631 channel = result['testcase_params']['channel']
632 if test_id not in testclass_results_dict:
633 testclass_results_dict[test_id] = collections.OrderedDict()
634 if channel not in testclass_results_dict[test_id]:
635 testclass_results_dict[test_id][channel] = {
636 'orientation': [],
637 'sensitivity': []
638 }
639 testclass_results_dict[test_id][channel]['orientation'].append(
640 result['testcase_params']['orientation'])
641 if result['peak_throughput_pct'] == 100:
642 testclass_results_dict[test_id][channel]['sensitivity'].append(
643 result['sensitivity'])
644 else:
645 testclass_results_dict[test_id][channel]['sensitivity'].append(
646 float('nan'))
647
648 for test_id, test_data in testclass_results_dict.items():
649 test_id_dict = dict(test_id)
650 if 'legacy' in test_id_dict['mode']:
651 test_id_str = '{} {}Mbps, Chain Mask = {}'.format(
652 test_id_dict['mode'], test_id_dict['rate'],
653 test_id_dict['chain_mask'])
Omar El Ayach49141c02019-09-16 16:43:51 -0700654 metric_test_config = '{}_{}_ch{}'.format(
655 test_id_dict['mode'], test_id_dict['rate'],
656 test_id_dict['chain_mask'])
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700657 else:
658 test_id_str = '{} MCS{} Nss{}, Chain Mask = {}'.format(
659 test_id_dict['mode'], test_id_dict['rate'],
660 test_id_dict['num_streams'], test_id_dict['chain_mask'])
Omar El Ayach49141c02019-09-16 16:43:51 -0700661 metric_test_config = '{}_mcs{}_nss{}_ch{}'.format(
662 test_id_dict['mode'], test_id_dict['rate'],
663 test_id_dict['num_streams'], test_id_dict['chain_mask'])
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700664 curr_plot = wputils.BokehFigure(
665 title=str(test_id_str),
666 x_label='Orientation (deg)',
667 primary_y='Sensitivity (dBm)')
668 for channel, channel_results in test_data.items():
669 curr_plot.add_line(
670 channel_results['orientation'],
671 channel_results['sensitivity'],
672 legend='Channel {}'.format(channel))
Omar El Ayach49141c02019-09-16 16:43:51 -0700673 metric_tag = 'ota_summary_ch{}_{}'.format(
674 channel, metric_test_config)
Omar El Ayach40099d02019-09-12 15:17:33 -0700675 metric_name = metric_tag + '.avg_sensitivity'
676 metric_value = sum(channel_results['sensitivity']) / len(
677 channel_results['sensitivity'])
678 self.bb_metric_logger.add_metric(metric_name, metric_value)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700679 current_context = (
680 context.get_current_context().get_full_output_path())
681 output_file_path = os.path.join(current_context,
682 str(test_id_str) + '.html')
683 curr_plot.generate_figure(output_file_path)
684 plots.append(curr_plot)
685 output_file_path = os.path.join(current_context, 'results.html')
686 wputils.BokehFigure.save_figures(plots, output_file_path)
687
688 def get_start_atten(self, testcase_params):
689 """Gets the starting attenuation for this sensitivity test.
690
691 The function gets the starting attenuation by checking whether a test
692 at the same rate configuration has executed. If so it sets the starting
693 point a configurable number of dBs below the reference test.
694
695 Returns:
696 start_atten: starting attenuation for current test
697 """
698 # Get the current and reference test config. The reference test is the
699 # one performed at the current MCS+1
700 ref_test_params = self.extract_test_id(
701 testcase_params,
702 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
703 # Check if reference test has been run and set attenuation accordingly
704 previous_params = [
705 self.extract_test_id(
706 result['testcase_params'],
707 ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
708 for result in self.testclass_results
709 ]
710 try:
711 ref_index = previous_params[::-1].index(ref_test_params)
712 ref_index = len(previous_params) - 1 - ref_index
713 start_atten = self.testclass_results[ref_index][
714 'atten_at_range'] - (
715 self.testclass_params['adjacent_mcs_range_gap'])
716 except ValueError:
717 print('Reference test not found. Starting from {} dB'.format(
718 self.testclass_params['atten_start']))
719 start_atten = self.testclass_params['atten_start']
Omar El Ayach49141c02019-09-16 16:43:51 -0700720 start_atten = max(start_atten, 0)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700721 return start_atten
722
Omar El Ayachffb5a462019-09-16 21:05:44 -0700723 def generate_test_cases(self, channels, modes, requested_rates, chain_mask,
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700724 angles):
725 """Function that auto-generates test cases for a test class."""
726 test_cases = []
727 for channel in channels:
Omar El Ayachffb5a462019-09-16 21:05:44 -0700728 requested_modes = set(modes).intersection(
729 set(self.VALID_TEST_CONFIGS[channel]))
730 for mode in requested_modes:
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700731 if 'VHT' in mode:
732 valid_rates = self.VALID_RATES[mode]
733 elif 'HT' in mode:
734 valid_rates = self.VALID_RATES[mode]
735 elif 'legacy' in mode and channel < 14:
736 valid_rates = self.VALID_RATES['legacy_2GHz']
737 elif 'legacy' in mode and channel > 14:
738 valid_rates = self.VALID_RATES['legacy_5GHz']
739 else:
740 raise ValueError('Invalid test mode.')
741 for chain, rate, angle in itertools.product(
742 chain_mask, valid_rates, angles):
743 testcase_params = collections.OrderedDict(
744 channel=channel,
745 mode=mode,
746 rate=rate.mcs,
747 num_streams=rate.streams,
748 short_gi=1,
749 chain_mask=chain,
750 orientation=angle)
751 if rate not in requested_rates:
752 continue
753 if str(chain) in ['0', '1'] and rate[1] == 2:
754 # Do not test 2-stream rates in single chain mode
755 continue
756 if 'legacy' in mode:
757 testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
758 '_ch{}_{}deg'.format(
759 channel, mode,
760 str(rate.mcs).replace('.', 'p'),
761 rate.streams, chain, angle))
762 else:
763 testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
764 '_ch{}_{}deg'.format(
765 channel, mode, rate.mcs,
766 rate.streams, chain, angle))
767 setattr(self, testcase_name,
768 partial(self._test_sensitivity, testcase_params))
769 test_cases.append(testcase_name)
Omar El Ayachf2f99c32019-09-16 17:18:45 -0700770 return test_cases
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700771
772
773class WifiOtaSensitivity_10Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700774 def __init__(self, controllers):
Omar El Ayach40099d02019-09-12 15:17:33 -0700775 WifiOtaSensitivityTest.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700776 requested_channels = [6, 36, 149]
777 requested_rates = [
778 self.RateTuple(8, 1, 86.7),
Omar El Ayachd9024eb2019-09-16 18:22:46 -0700779 self.RateTuple(2, 1, 21.7),
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700780 self.RateTuple(8, 2, 173.3),
Omar El Ayachd9024eb2019-09-16 18:22:46 -0700781 self.RateTuple(2, 2, 43.3)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700782 ]
Omar El Ayachffb5a462019-09-16 21:05:44 -0700783 self.tests = self.generate_test_cases(
784 requested_channels, ['VHT20', 'VHT80'], requested_rates, ['2x2'],
785 list(range(0, 360, 10)))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700786
787
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700788class WifiOtaSensitivity_SingleChain_10Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700789 def __init__(self, controllers):
Omar El Ayach40099d02019-09-12 15:17:33 -0700790 WifiOtaSensitivityTest.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700791 requested_channels = [6, 36, 149]
792 requested_rates = [
793 self.RateTuple(8, 1, 86.7),
Omar El Ayachd9024eb2019-09-16 18:22:46 -0700794 self.RateTuple(2, 1, 21.7)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700795 ]
Omar El Ayachffb5a462019-09-16 21:05:44 -0700796 self.tests = self.generate_test_cases(
797 requested_channels, ['VHT20', 'VHT80'], requested_rates, ['2x2'],
798 list(range(0, 360, 10)))
Omar El Ayach33f80c02018-09-27 15:02:03 -0700799
800
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700801class WifiOtaSensitivity_45Degree_Test(WifiOtaSensitivityTest):
Omar El Ayach33f80c02018-09-27 15:02:03 -0700802 def __init__(self, controllers):
Omar El Ayach40099d02019-09-12 15:17:33 -0700803 WifiOtaSensitivityTest.__init__(self, controllers)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700804 requested_rates = [
805 self.RateTuple(8, 1, 86.7),
Omar El Ayachd9024eb2019-09-16 18:22:46 -0700806 self.RateTuple(2, 1, 21.7),
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700807 self.RateTuple(8, 2, 173.3),
Omar El Ayachd9024eb2019-09-16 18:22:46 -0700808 self.RateTuple(2, 2, 43.3)
Omar El Ayachbbc6a0a2019-07-22 10:26:11 -0700809 ]
810 self.tests = self.generate_test_cases(
Omar El Ayachffb5a462019-09-16 21:05:44 -0700811 [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], ['VHT20', 'VHT80'],
812 requested_rates, ['2x2'], list(range(0, 360, 45)))