• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
2#
3#   Copyright 2018 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   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
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   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
18import json
19import logging
20import math
21import os
22import statistics
23from acts import asserts
24from acts import base_test
25from acts import utils
26from acts.controllers import iperf_server as ipf
27from acts.metrics.loggers.blackbox import BlackboxMetricLogger
28from acts.test_decorators import test_tracker_info
29from acts.test_utils.wifi import wifi_performance_test_utils as wputils
30from acts.test_utils.wifi import wifi_retail_ap as retail_ap
31from acts.test_utils.wifi import wifi_test_utils as wutils
32from concurrent.futures import ThreadPoolExecutor
33
34SHORT_SLEEP = 1
35MED_SLEEP = 6
36CONST_3dB = 3.01029995664
37RSSI_ERROR_VAL = float("nan")
38
39
40class WifiRssiTest(base_test.BaseTestClass):
41    """Class to test WiFi RSSI reporting.
42
43    This class tests RSSI reporting on android devices. The class tests RSSI
44    accuracy by checking RSSI over a large attenuation range, checks for RSSI
45    stability over time when attenuation is fixed, and checks that RSSI quickly
46    and reacts to changes attenuation by checking RSSI trajectories over
47    configurable attenuation waveforms.For an example config file to run this
48    test class see example_connectivity_performance_ap_sta.json.
49    """
50
51    def __init__(self, controllers):
52        base_test.BaseTestClass.__init__(self, controllers)
53        test_metrics = [
54            "signal_poll_rssi_shift", "signal_poll_avg_rssi_shift",
55            "scan_rssi_shift", "chain_0_rssi_shift", "chain_1_rssi_shift",
56            "signal_poll_rssi_error", "signal_poll_avg_rssi_error",
57            "scan_rssi_error", "chain_0_rssi_error", "chain_1_rssi_error",
58            "signal_poll_rssi_stdev", "chain_0_rssi_stdev",
59            "chain_1_rssi_stdev"
60        ]
61        for metric in test_metrics:
62            setattr(
63                self,
64                "{}_metric".format(metric),
65                BlackboxMetricLogger.for_test_case(metric_name=metric))
66
67    def setup_class(self):
68        self.dut = self.android_devices[0]
69        req_params = [
70            "RemoteServer", "RetailAccessPoints", "rssi_test_params",
71            "main_network", "testbed_params"
72        ]
73        self.unpack_userparams(req_params)
74        self.test_params = self.rssi_test_params
75        self.num_atten = self.attenuators[0].instrument.num_atten
76        self.iperf_server = self.iperf_servers[0]
77        self.iperf_client = self.iperf_clients[0]
78        self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
79        self.log_path = os.path.join(logging.log_path, "results")
80        utils.create_dir(self.log_path)
81        self.log.info("Access Point Configuration: {}".format(
82            self.access_point.ap_settings))
83        self.testclass_results = []
84
85    def teardown_test(self):
86        self.iperf_server.stop()
87
88    def pass_fail_check_rssi_stability(self, postprocessed_results):
89        """Check the test result and decide if it passed or failed.
90
91        Checks the RSSI test result and fails the test if the standard
92        deviation of signal_poll_rssi is beyond the threshold defined in the
93        config file.
94
95        Args:
96            postprocessed_results: compiled arrays of RSSI measurements
97        """
98        # Set Blackbox metric values
99        self.signal_poll_rssi_stdev_metric.metric_value = max(
100            postprocessed_results["signal_poll_rssi"]["stdev"])
101        self.chain_0_rssi_stdev_metric.metric_value = max(
102            postprocessed_results["chain_0_rssi"]["stdev"])
103        self.chain_1_rssi_stdev_metric.metric_value = max(
104            postprocessed_results["chain_1_rssi"]["stdev"])
105        # Evaluate test pass/fail
106        test_failed = any([
107            stdev > self.test_params["stdev_tolerance"]
108            for stdev in postprocessed_results["signal_poll_rssi"]["stdev"]
109        ])
110        test_message = (
111            "RSSI stability {0}. Standard deviation was {1} dB "
112            "(limit {2}), per chain standard deviation [{3}, {4}] dB".format(
113                "failed" * test_failed + "passed" * (not test_failed), [
114                    float("{:.2f}".format(x))
115                    for x in postprocessed_results["signal_poll_rssi"]["stdev"]
116                ], self.test_params["stdev_tolerance"], [
117                    float("{:.2f}".format(x))
118                    for x in postprocessed_results["chain_0_rssi"]["stdev"]
119                ], [
120                    float("{:.2f}".format(x))
121                    for x in postprocessed_results["chain_1_rssi"]["stdev"]
122                ]))
123        if test_failed:
124            asserts.fail(test_message)
125        asserts.explicit_pass(test_message)
126
127    def pass_fail_check_rssi_accuracy(self, postprocessed_results,
128                                      rssi_under_test, absolute_accuracy):
129        """Check the test result and decide if it passed or failed.
130
131        Checks the RSSI test result and compares and compute its deviation from
132        the predicted RSSI. This computation is done for all reported RSSI
133        values. The test fails if any of the RSSI values specified in
134        rssi_under_test have an average error beyond what is specified in the
135        configuration file.
136
137        Args:
138            postprocessed_results: compiled arrays of RSSI measurements
139            rssi_under_test: list of RSSIs under test, i.e., can cause test to
140            fail
141            absolute_accuracy: boolean indicating whether to look at absolute
142            RSSI accuracy, or centered RSSI accuracy. Centered accuracy is
143            computed after systematic RSSI shifts are removed.
144        """
145        test_failed = False
146        test_message = ""
147        if absolute_accuracy:
148            error_type = "absolute"
149        else:
150            error_type = "centered"
151
152        for key, val in postprocessed_results.items():
153            # Compute the error metrics ignoring invalid RSSI readings
154            # If all readings invalid, set error to RSSI_ERROR_VAL
155            if "rssi" in key and "predicted" not in key:
156                filtered_error = [x for x in val["error"] if not math.isnan(x)]
157                if filtered_error:
158                    avg_shift = statistics.mean(filtered_error)
159                    if absolute_accuracy:
160                        avg_error = statistics.mean(
161                            [abs(x) for x in filtered_error])
162                    else:
163                        avg_error = statistics.mean(
164                            [abs(x - avg_shift) for x in filtered_error])
165                else:
166                    avg_error = RSSI_ERROR_VAL
167                    avg_shift = RSSI_ERROR_VAL
168                # Set Blackbox metric values
169                setattr(
170                    getattr(self, "{}_error_metric".format(key)),
171                    "metric_value", avg_error)
172                setattr(
173                    getattr(self, "{}_shift_metric".format(key)),
174                    "metric_value", avg_shift)
175                # Evaluate test pass/fail
176                rssi_failure = (avg_error > self.test_params["abs_tolerance"]
177                                ) or math.isnan(avg_error)
178                if rssi_failure and key in rssi_under_test:
179                    test_message = test_message + (
180                        "{} failed ({} error = {:.2f} dB, "
181                        "shift = {:.2f} dB)\n").format(key, error_type,
182                                                       avg_error, avg_shift)
183                    test_failed = True
184                elif rssi_failure:
185                    test_message = test_message + (
186                        "{} failed (ignored) ({} error = {:.2f} dB, "
187                        "shift = {:.2f} dB)\n").format(key, error_type,
188                                                       avg_error, avg_shift)
189                else:
190                    test_message = test_message + (
191                        "{} passed ({} error = {:.2f} dB, "
192                        "shift = {:.2f} dB)\n").format(key, error_type,
193                                                       avg_error, avg_shift)
194        if test_failed:
195            asserts.fail(test_message)
196        asserts.explicit_pass(test_message)
197
198    def post_process_rssi_sweep(self, rssi_result):
199        """Postprocesses and saves JSON formatted results.
200
201        Args:
202            rssi_result: dict containing attenuation, rssi and other meta
203            data
204        Returns:
205            postprocessed_results: compiled arrays of RSSI data used in
206            pass/fail check
207        """
208        # Save output as text file
209        results_file_path = "{}/{}.json".format(self.log_path,
210                                                self.current_test_name)
211        with open(results_file_path, 'w') as results_file:
212            json.dump(rssi_result, results_file, indent=4)
213        # Compile results into arrays of RSSIs suitable for plotting
214        # yapf: disable
215        postprocessed_results = collections.OrderedDict(
216            [("signal_poll_rssi", {}),
217             ("signal_poll_avg_rssi", {}),
218             ("scan_rssi", {}),
219             ("chain_0_rssi", {}),
220             ("chain_1_rssi", {}),
221             ("total_attenuation", []),
222             ("predicted_rssi", [])])
223        # yapf: enable
224        for key, val in postprocessed_results.items():
225            if "scan_rssi" in key:
226                postprocessed_results[key]["data"] = [
227                    x for data_point in rssi_result["rssi_result"] for x in
228                    data_point[key][rssi_result["connected_bssid"]]["data"]
229                ]
230                postprocessed_results[key]["mean"] = [
231                    x[key][rssi_result["connected_bssid"]]["mean"]
232                    for x in rssi_result["rssi_result"]
233                ]
234                postprocessed_results[key]["stdev"] = [
235                    x[key][rssi_result["connected_bssid"]]["stdev"]
236                    for x in rssi_result["rssi_result"]
237                ]
238            elif "predicted_rssi" in key:
239                postprocessed_results["total_attenuation"] = [
240                    att + rssi_result["fixed_attenuation"] +
241                    rssi_result["dut_front_end_loss"]
242                    for att in rssi_result["attenuation"]
243                ]
244                postprocessed_results["predicted_rssi"] = [
245                    rssi_result["ap_tx_power"] - att
246                    for att in postprocessed_results["total_attenuation"]
247                ]
248            elif "rssi" in key:
249                postprocessed_results[key]["data"] = [
250                    x for data_point in rssi_result["rssi_result"]
251                    for x in data_point[key]["data"]
252                ]
253                postprocessed_results[key]["mean"] = [
254                    x[key]["mean"] for x in rssi_result["rssi_result"]
255                ]
256                postprocessed_results[key]["stdev"] = [
257                    x[key]["stdev"] for x in rssi_result["rssi_result"]
258                ]
259        # Compute RSSI errors
260        for key, val in postprocessed_results.items():
261            if "chain" in key:
262                postprocessed_results[key]["error"] = [
263                    postprocessed_results[key]["mean"][idx] + CONST_3dB -
264                    postprocessed_results["predicted_rssi"][idx]
265                    for idx in range(
266                        len(postprocessed_results["predicted_rssi"]))
267                ]
268            elif "rssi" in key and "predicted" not in key:
269                postprocessed_results[key]["error"] = [
270                    postprocessed_results[key]["mean"][idx] -
271                    postprocessed_results["predicted_rssi"][idx]
272                    for idx in range(
273                        len(postprocessed_results["predicted_rssi"]))
274                ]
275        return postprocessed_results
276
277    def plot_rssi_vs_attenuation(self, postprocessed_results):
278        """Function to plot RSSI vs attenuation sweeps
279
280        Args:
281            postprocessed_results: compiled arrays of RSSI data.
282        """
283        data_sets = [[
284            postprocessed_results["total_attenuation"],
285            postprocessed_results["total_attenuation"],
286            postprocessed_results["total_attenuation"],
287            postprocessed_results["total_attenuation"],
288            postprocessed_results["total_attenuation"],
289            postprocessed_results["total_attenuation"]
290        ], [
291            postprocessed_results["signal_poll_rssi"]["mean"],
292            postprocessed_results["signal_poll_avg_rssi"]["mean"],
293            postprocessed_results["scan_rssi"]["mean"],
294            postprocessed_results["chain_0_rssi"]["mean"],
295            postprocessed_results["chain_1_rssi"]["mean"],
296            postprocessed_results["predicted_rssi"]
297        ]]
298        legends = [
299            "Signal Poll RSSI", "Signal Poll AVG_RSSI", "Scan RSSI",
300            "Chain 0 RSSI", "Chain 1 RSSI", "Predicted RSSI"
301        ]
302        fig_property = {
303            "title": self.current_test_name,
304            "x_label": 'Attenuation (dB)',
305            "y_label": 'RSSI (dBm)',
306            "linewidth": 3,
307            "markersize": 10
308        }
309        output_file_path = "{}/{}.html".format(self.log_path,
310                                               self.current_test_name)
311        wputils.bokeh_plot(
312            data_sets,
313            legends,
314            fig_property,
315            shaded_region=None,
316            output_file_path=output_file_path)
317
318    def plot_rssi_vs_time(self, rssi_result, postprocessed_results,
319                          center_curves):
320        """Function to plot RSSI vs time.
321
322        Args:
323            rssi_result: dict containing raw RSSI data
324            postprocessed_results: compiled arrays of RSSI data
325            center_curvers: boolean indicating whether to shift curves to align
326            them with predicted RSSIs
327        """
328        x_data = []
329        y_data = []
330        legends = []
331        # yapf: disable
332        rssi_time_series = collections.OrderedDict(
333            [("signal_poll_rssi", []),
334             ("signal_poll_avg_rssi", []),
335             ("scan_rssi", []),
336             ("chain_0_rssi", []),
337             ("chain_1_rssi", []),
338             ("predicted_rssi", [])])
339        # yapf: enable
340        for key, val in rssi_time_series.items():
341            if "predicted_rssi" in key:
342                rssi_time_series[key] = [
343                    x for x in postprocessed_results[key] for copies in range(
344                        len(rssi_result["rssi_result"][0]["signal_poll_rssi"][
345                            "data"]))
346                ]
347            elif "rssi" in key:
348                if center_curves:
349                    filtered_error = [
350                        x for x in postprocessed_results[key]["error"]
351                        if not math.isnan(x)
352                    ]
353                    if filtered_error:
354                        avg_shift = statistics.mean(filtered_error)
355                    else:
356                        avg_shift = 0
357                    rssi_time_series[key] = [
358                        x - avg_shift
359                        for x in postprocessed_results[key]["data"]
360                    ]
361                else:
362                    rssi_time_series[key] = postprocessed_results[key]["data"]
363            time_vec = [
364                self.test_params["polling_frequency"] * x
365                for x in range(len(rssi_time_series[key]))
366            ]
367            if len(rssi_time_series[key]) > 0:
368                x_data.append(time_vec)
369                y_data.append(rssi_time_series[key])
370                legends.append(key)
371        data_sets = [x_data, y_data]
372        fig_property = {
373            "title": self.current_test_name,
374            "x_label": 'Time (s)',
375            "y_label": center_curves * 'Centered' + 'RSSI (dBm)',
376            "linewidth": 3,
377            "markersize": 0
378        }
379        output_file_path = "{}/{}.html".format(self.log_path,
380                                               self.current_test_name)
381        wputils.bokeh_plot(
382            data_sets,
383            legends,
384            fig_property,
385            shaded_region=None,
386            output_file_path=output_file_path)
387
388    def rssi_test(self, iperf_traffic, connected_measurements,
389                  scan_measurements, bssids, polling_frequency,
390                  first_measurement_delay):
391        """Test function to run RSSI tests.
392
393        The function runs an RSSI test in the current device/AP configuration.
394        Function is called from another wrapper function that sets up the
395        testbed for the RvR test
396
397        Args:
398            iperf_traffic: boolean specifying whether or not to run traffic
399            during RSSI tests
400            connected_measurements: number of RSSI measurements to make for the
401            connected AP per attenuation point
402            scan_measurements: number of scans and scan RSSIs to make per
403            attenuation point
404            bssids: list of BSSIDs to monitor in scans
405            polling_frequency: time between connected AP measurements
406        Returns:
407            rssi_result: dict containing rssi_result and meta data
408        """
409        self.log.info("Start running RSSI test.")
410        rssi_result = []
411        # Start iperf traffic if required by test
412        if self.iperf_traffic:
413            self.iperf_server.start(tag=0)
414            if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
415                iperf_server_address = self.dut_ip
416            else:
417                iperf_server_address = self.testbed_params[
418                    "iperf_server_address"]
419            executor = ThreadPoolExecutor(max_workers=1)
420            thread_future = executor.submit(
421                self.iperf_client.start, iperf_server_address, self.iperf_args,
422                0, self.iperf_timeout + SHORT_SLEEP)
423            executor.shutdown(wait=False)
424        for atten in self.rssi_atten_range:
425            # Set Attenuation
426            self.log.info("Setting attenuation to {} dB".format(atten))
427            for attenuator in self.attenuators:
428                attenuator.set_atten(atten)
429            current_rssi = collections.OrderedDict()
430            current_rssi = wputils.get_connected_rssi(
431                self.dut, connected_measurements, polling_frequency,
432                first_measurement_delay)
433            current_rssi["scan_rssi"] = wputils.get_scan_rssi(
434                self.dut, bssids, scan_measurements)
435            rssi_result.append(current_rssi)
436            self.log.info("Connected RSSI at {0:.2f} dB is {1:.2f} dB".format(
437                atten, current_rssi["signal_poll_rssi"]["mean"]))
438        # Stop iperf traffic if needed
439        for attenuator in self.attenuators:
440            attenuator.set_atten(0)
441        if self.iperf_traffic:
442            thread_future.result()
443            self.iperf_server.stop()
444        return rssi_result
445
446    def setup_ap(self):
447        """Function that gets devices ready for the test."""
448        band = self.access_point.band_lookup_by_channel(self.channel)
449        if "2G" in band:
450            frequency = wutils.WifiEnums.channel_2G_to_freq[self.channel]
451        else:
452            frequency = wutils.WifiEnums.channel_5G_to_freq[self.channel]
453        if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
454            self.access_point.set_region(self.testbed_params["DFS_region"])
455        else:
456            self.access_point.set_region(self.testbed_params["default_region"])
457        self.access_point.set_channel(band, self.channel)
458        self.access_point.set_bandwidth(band, self.mode)
459        self.log.info("Access Point Configuration: {}".format(
460            self.access_point.ap_settings))
461
462    def setup_dut(self):
463        """Sets up the DUT in the configuration required by the test."""
464        band = self.access_point.band_lookup_by_channel(self.channel)
465        wutils.wifi_toggle_state(self.dut, True)
466        wutils.reset_wifi(self.dut)
467        self.main_network[band]["channel"] = self.channel
468        self.dut.droid.wifiSetCountryCode(self.test_params["country_code"])
469        wutils.wifi_connect(self.dut, self.main_network[band], num_of_tries=5)
470        self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
471
472    def rssi_test_func(self, iperf_traffic, connected_measurements,
473                       scan_measurements, bssids, polling_frequency,
474                       first_measurement_delay):
475        """Main function to test RSSI.
476
477        The function sets up the AP in the correct channel and mode
478        configuration and called rssi_test to sweep attenuation and measure
479        RSSI
480
481        Returns:
482            rssi_result: dict containing rssi_results and meta data
483        """
484        #Initialize test settings
485        rssi_result = collections.OrderedDict()
486        # Configure AP
487        self.setup_ap()
488        # Initialize attenuators
489        for attenuator in self.attenuators:
490            attenuator.set_atten(self.rssi_atten_range[0])
491        # Connect DUT to Network
492        self.setup_dut()
493        # Run test and log result
494        band = self.access_point.band_lookup_by_channel(self.channel)
495        rssi_result["test_name"] = self.current_test_name
496        rssi_result["ap_settings"] = self.access_point.ap_settings.copy()
497        rssi_result["attenuation"] = list(self.rssi_atten_range)
498        rssi_result["connected_bssid"] = self.main_network[band]["BSSID"]
499        if "{}_{}".format(str(self.channel),
500                          self.mode) in self.testbed_params["ap_tx_power"]:
501            rssi_result["ap_tx_power"] = self.testbed_params["ap_tx_power"][
502                "{}_{}".format(str(self.channel), self.mode)]
503        else:
504            rssi_result["ap_tx_power"] = self.testbed_params["ap_tx_power"][
505                str(self.channel)]
506        rssi_result["fixed_attenuation"] = self.testbed_params[
507            "fixed_attenuation"][str(self.channel)]
508        rssi_result["dut_front_end_loss"] = self.testbed_params[
509            "dut_front_end_loss"][str(self.channel)]
510        rssi_result["rssi_result"] = self.rssi_test(
511            iperf_traffic, connected_measurements, scan_measurements, bssids,
512            polling_frequency, first_measurement_delay)
513        self.testclass_results.append(rssi_result)
514        return rssi_result
515
516    def get_iperf_timeout(self, atten_range, connected_measurements,
517                          polling_frequency, first_measurement_delay,
518                          scan_measurements):
519        """Function to comput iperf session length required in RSSI test.
520
521        Args:
522            atten_range: array of attenuations
523            connected_measurements: number of measurements per atten step
524            polling_frequency: interval between RSSI measurements
525            first_measurement_delay: delay before first measurements
526            scan_measurements: number of scan RSSI measurements per atten step
527        Returns:
528            iperf_timeout: length of iperf session required in rssi test
529        """
530        atten_step_duration = first_measurement_delay + (
531            connected_measurements *
532            polling_frequency) + scan_measurements * MED_SLEEP
533        iperf_timeout = len(atten_range) * atten_step_duration + MED_SLEEP
534        self.log.info("iperf timeout is {}".format(iperf_timeout))
535        return iperf_timeout
536
537    def _test_rssi_vs_atten(self):
538        """ Function that gets called for each test case of rssi_vs_atten
539
540        The function gets called in each rssi test case. The function
541        customizes the test based on the test name of the test that called it
542        """
543        test_params = self.current_test_name.split("_")
544        self.channel = int(test_params[4][2:])
545        self.mode = test_params[5]
546        self.iperf_traffic = "ActiveTraffic" in test_params[6]
547        band = self.access_point.band_lookup_by_channel(self.channel)
548        num_atten_steps = int((self.test_params["rssi_vs_atten_stop"] -
549                               self.test_params["rssi_vs_atten_start"]) /
550                              self.test_params["rssi_vs_atten_step"])
551        self.rssi_atten_range = [
552            self.test_params["rssi_vs_atten_start"] +
553            x * self.test_params["rssi_vs_atten_step"]
554            for x in range(0, num_atten_steps)
555        ]
556        self.iperf_timeout = self.get_iperf_timeout(
557            self.rssi_atten_range,
558            self.test_params["rssi_vs_atten_connected_measurements"],
559            self.test_params["polling_frequency"], MED_SLEEP,
560            self.test_params["rssi_vs_atten_scan_measurements"])
561        if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
562            self.iperf_args = '-i 1 -t {} -J'.format(self.iperf_timeout)
563        else:
564            self.iperf_args = '-i 1 -t {} -J -R'.format(self.iperf_timeout)
565        rssi_result = self.rssi_test_func(
566            self.iperf_traffic,
567            self.test_params["rssi_vs_atten_connected_measurements"],
568            self.test_params["rssi_vs_atten_scan_measurements"],
569            [self.main_network[band]["BSSID"]],
570            self.test_params["polling_frequency"], MED_SLEEP)
571        postprocessed_results = self.post_process_rssi_sweep(rssi_result)
572        self.plot_rssi_vs_attenuation(postprocessed_results)
573        self.pass_fail_check_rssi_accuracy(
574            postprocessed_results, self.test_params["rssi_vs_atten_metrics"],
575            1)
576
577    def _test_rssi_stability(self):
578        """ Function that gets called for each test case of rssi_stability
579
580        The function gets called in each stability test case. The function
581        customizes test based on the test name of the test that called it
582        """
583        test_params = self.current_test_name.split("_")
584        self.channel = int(test_params[3][2:])
585        self.mode = test_params[4]
586        self.iperf_traffic = "ActiveTraffic" in test_params[5]
587        band = self.access_point.band_lookup_by_channel(self.channel)
588        self.rssi_atten_range = self.test_params["rssi_stability_atten"]
589        connected_measurements = int(
590            self.test_params["rssi_stability_duration"] /
591            self.test_params["polling_frequency"])
592        self.iperf_timeout = self.get_iperf_timeout(
593            self.rssi_atten_range, connected_measurements,
594            self.test_params["polling_frequency"], MED_SLEEP, 0)
595        if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
596            self.iperf_args = '-i 1 -t {} -J'.format(self.iperf_timeout)
597        else:
598            self.iperf_args = '-i 1 -t {} -J -R'.format(self.iperf_timeout)
599        rssi_result = self.rssi_test_func(
600            self.iperf_traffic, connected_measurements, 0,
601            [self.main_network[band]["BSSID"]],
602            self.test_params["polling_frequency"], MED_SLEEP)
603        postprocessed_results = self.post_process_rssi_sweep(rssi_result)
604        self.plot_rssi_vs_time(rssi_result, postprocessed_results, 1)
605        self.pass_fail_check_rssi_stability(postprocessed_results)
606
607    def _test_rssi_tracking(self):
608        """ Function that gets called for each test case of rssi_tracking
609
610        The function gets called in each rssi test case. The function
611        customizes the test based on the test name of the test that called it
612        """
613        test_params = self.current_test_name.split("_")
614        self.channel = int(test_params[3][2:])
615        self.mode = test_params[4]
616        self.iperf_traffic = "ActiveTraffic" in test_params[5]
617        band = self.access_point.band_lookup_by_channel(self.channel)
618        self.rssi_atten_range = []
619        for waveform in self.test_params["rssi_tracking_waveforms"]:
620            waveform_vector = []
621            for section in range(len(waveform["atten_levels"]) - 1):
622                section_limits = waveform["atten_levels"][section:section + 2]
623                up_down = (1 - 2 * (section_limits[1] < section_limits[0]))
624                temp_section = list(
625                    range(section_limits[0], section_limits[1] + up_down,
626                          up_down * waveform["step_size"]))
627                temp_section = [
628                    temp_section[idx] for idx in range(len(temp_section))
629                    for n in range(waveform["step_duration"])
630                ]
631                waveform_vector += temp_section
632            waveform_vector = waveform_vector * waveform["repetitions"]
633            self.rssi_atten_range = self.rssi_atten_range + waveform_vector
634        connected_measurements = int(1 / self.test_params["polling_frequency"])
635        self.iperf_timeout = self.get_iperf_timeout(
636            self.rssi_atten_range, connected_measurements,
637            self.test_params["polling_frequency"], 0, 0)
638        if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
639            self.iperf_args = '-i 1 -t {} -J'.format(self.iperf_timeout)
640        else:
641            self.iperf_args = '-i 1 -t {} -J -R'.format(self.iperf_timeout)
642        rssi_result = self.rssi_test_func(
643            self.iperf_traffic, connected_measurements, 0,
644            [self.main_network[band]["BSSID"]],
645            self.test_params["polling_frequency"], 0)
646        postprocessed_results = self.post_process_rssi_sweep(rssi_result)
647        self.plot_rssi_vs_time(rssi_result, postprocessed_results, 1)
648        self.pass_fail_check_rssi_accuracy(postprocessed_results,
649                                           ["signal_poll_rssi"], 0)
650
651    @test_tracker_info(uuid='519689b8-0a3c-4fd9-9227-fd7962d0f1a0')
652    def test_rssi_stability_ch1_VHT20_ActiveTraffic(self):
653        self._test_rssi_stability()
654
655    @test_tracker_info(uuid='23eca2ab-d0b4-4730-9f32-ec2d901ae493')
656    def test_rssi_stability_ch2_VHT20_ActiveTraffic(self):
657        self._test_rssi_stability()
658
659    @test_tracker_info(uuid='63d340c0-dcf9-4e14-87bd-a068a59836b2')
660    def test_rssi_stability_ch3_VHT20_ActiveTraffic(self):
661        self._test_rssi_stability()
662
663    @test_tracker_info(uuid='ddbe88d8-be20-40eb-8f29-55049e3fef28')
664    def test_rssi_stability_ch4_VHT20_ActiveTraffic(self):
665        self._test_rssi_stability()
666
667    @test_tracker_info(uuid='9c06304e-2b60-4619-8fb3-73fd2cb4b854')
668    def test_rssi_stability_ch5_VHT20_ActiveTraffic(self):
669        self._test_rssi_stability()
670
671    @test_tracker_info(uuid='74b656ca-132e-4d66-9584-560287081607')
672    def test_rssi_stability_ch6_VHT20_ActiveTraffic(self):
673        self._test_rssi_stability()
674
675    @test_tracker_info(uuid='23b5f19a-539b-4908-a197-06ce505d3d23')
676    def test_rssi_stability_ch7_VHT20_ActiveTraffic(self):
677        self._test_rssi_stability()
678
679    @test_tracker_info(uuid='e7b85167-f4c4-4adb-a111-04d8a5f10e1a')
680    def test_rssi_stability_ch8_VHT20_ActiveTraffic(self):
681        self._test_rssi_stability()
682
683    @test_tracker_info(uuid='2a0a9393-4b68-4c08-8787-3f35d1a8458b')
684    def test_rssi_stability_ch9_VHT20_ActiveTraffic(self):
685        self._test_rssi_stability()
686
687    @test_tracker_info(uuid='069c7acf-3e7e-4298-91cb-d292c6025ae1')
688    def test_rssi_stability_ch10_VHT20_ActiveTraffic(self):
689        self._test_rssi_stability()
690
691    @test_tracker_info(uuid='95c5a27c-1dea-47a4-a1c5-edf955545f12')
692    def test_rssi_stability_ch11_VHT20_ActiveTraffic(self):
693        self._test_rssi_stability()
694
695    @test_tracker_info(uuid='8aeab023-a096-4fbe-80dd-fd01466f9fac')
696    def test_rssi_stability_ch36_VHT20_ActiveTraffic(self):
697        self._test_rssi_stability()
698
699    @test_tracker_info(uuid='872fed9f-d0bb-4a7b-a2a7-bf8df7740b2d')
700    def test_rssi_stability_ch36_VHT40_ActiveTraffic(self):
701        self._test_rssi_stability()
702
703    @test_tracker_info(uuid='27395fd1-e286-473a-b98e-5a50db2a598a')
704    def test_rssi_stability_ch36_VHT80_ActiveTraffic(self):
705        self._test_rssi_stability()
706
707    @test_tracker_info(uuid='6f6b25e3-1a1e-4a61-930a-1d0aa25ba900')
708    def test_rssi_stability_ch40_VHT20_ActiveTraffic(self):
709        self._test_rssi_stability()
710
711    @test_tracker_info(uuid='c6717da7-855c-4c6e-a6e2-ee42b8feaaab')
712    def test_rssi_stability_ch44_VHT20_ActiveTraffic(self):
713        self._test_rssi_stability()
714
715    @test_tracker_info(uuid='2e34f735-079c-4619-9e74-b96dc8d0597f')
716    def test_rssi_stability_ch44_VHT40_ActiveTraffic(self):
717        self._test_rssi_stability()
718
719    @test_tracker_info(uuid='d543c019-1ff5-41d4-9b37-ccdc593f3edd')
720    def test_rssi_stability_ch48_VHT20_ActiveTraffic(self):
721        self._test_rssi_stability()
722
723    @test_tracker_info(uuid='2bb08914-36b2-4f58-9b3e-c3f3f4fac8ab')
724    def test_rssi_stability_ch149_VHT20_ActiveTraffic(self):
725        self._test_rssi_stability()
726
727    @test_tracker_info(uuid='e2f585f5-7811-4570-b987-23da301eb75d')
728    def test_rssi_stability_ch149_VHT40_ActiveTraffic(self):
729        self._test_rssi_stability()
730
731    @test_tracker_info(uuid='f3e74d5b-73f6-4723-abf3-c9c147db08e3')
732    def test_rssi_stability_ch149_VHT80_ActiveTraffic(self):
733        self._test_rssi_stability()
734
735    @test_tracker_info(uuid='06503ed0-baf3-4cd1-ac5e-4124e3c7f52f')
736    def test_rssi_stability_ch153_VHT20_ActiveTraffic(self):
737        self._test_rssi_stability()
738
739    @test_tracker_info(uuid='0cf8286f-a919-4e29-a9f2-e7738a4afe8f')
740    def test_rssi_stability_ch157_VHT20_ActiveTraffic(self):
741        self._test_rssi_stability()
742
743    @test_tracker_info(uuid='f9a0165c-468b-4096-8f4b-cc80bae564a0')
744    def test_rssi_stability_ch157_VHT40_ActiveTraffic(self):
745        self._test_rssi_stability()
746
747    @test_tracker_info(uuid='4b74dd46-4190-4556-8ad8-c55808e9e847')
748    def test_rssi_stability_ch161_VHT20_ActiveTraffic(self):
749        self._test_rssi_stability()
750
751    @test_tracker_info(uuid='ae54b7cc-d76d-4460-8dcc-2c439265c7c9')
752    def test_rssi_vs_atten_ch1_VHT20_ActiveTraffic(self):
753        self._test_rssi_vs_atten()
754
755    @test_tracker_info(uuid='07fe7899-886d-45ba-9c1d-7daaf9844c9c')
756    def test_rssi_vs_atten_ch2_VHT20_ActiveTraffic(self):
757        self._test_rssi_vs_atten()
758
759    @test_tracker_info(uuid='9e86578b-a6cd-4de9-a79d-eabac5bd5f4e')
760    def test_rssi_vs_atten_ch3_VHT20_ActiveTraffic(self):
761        self._test_rssi_vs_atten()
762
763    @test_tracker_info(uuid='e9d258ca-8e70-408e-b704-782fce7a07c5')
764    def test_rssi_vs_atten_ch4_VHT20_ActiveTraffic(self):
765        self._test_rssi_vs_atten()
766
767    @test_tracker_info(uuid='1c5d71a0-7532-49e4-98a9-1c2d9d8d58d2')
768    def test_rssi_vs_atten_ch5_VHT20_ActiveTraffic(self):
769        self._test_rssi_vs_atten()
770
771    @test_tracker_info(uuid='107f01f3-b6b9-470b-9895-6345edfc9599')
772    def test_rssi_vs_atten_ch6_VHT20_ActiveTraffic(self):
773        self._test_rssi_vs_atten()
774
775    @test_tracker_info(uuid='88cb18b2-30bf-4c01-ac28-15451289e7cd')
776    def test_rssi_vs_atten_ch7_VHT20_ActiveTraffic(self):
777        self._test_rssi_vs_atten()
778
779    @test_tracker_info(uuid='c07a7442-bd1d-40c7-80ed-167e30b8cfaf')
780    def test_rssi_vs_atten_ch8_VHT20_ActiveTraffic(self):
781        self._test_rssi_vs_atten()
782
783    @test_tracker_info(uuid='b8946280-88d5-400d-a417-2bdc9d7e054a')
784    def test_rssi_vs_atten_ch9_VHT20_ActiveTraffic(self):
785        self._test_rssi_vs_atten()
786
787    @test_tracker_info(uuid='a05db91b-740d-4984-a447-79ab438034f0')
788    def test_rssi_vs_atten_ch10_VHT20_ActiveTraffic(self):
789        self._test_rssi_vs_atten()
790
791    @test_tracker_info(uuid='f4d565f8-f060-462c-9b3c-cd1f7d27b3ea')
792    def test_rssi_vs_atten_ch11_VHT20_ActiveTraffic(self):
793        self._test_rssi_vs_atten()
794
795    @test_tracker_info(uuid='a33a93ac-604a-414f-ae96-42dffbe59a93')
796    def test_rssi_vs_atten_ch36_VHT20_ActiveTraffic(self):
797        self._test_rssi_vs_atten()
798
799    @test_tracker_info(uuid='39875ab0-e0e9-464b-8a47-4dedd65f066e')
800    def test_rssi_vs_atten_ch36_VHT40_ActiveTraffic(self):
801        self._test_rssi_vs_atten()
802
803    @test_tracker_info(uuid='c6ff8768-f124-4190-baf2-bbf14b612de3')
804    def test_rssi_vs_atten_ch36_VHT80_ActiveTraffic(self):
805        self._test_rssi_vs_atten()
806
807    @test_tracker_info(uuid='ed4705af-e202-4737-b410-8bab0515e79f')
808    def test_rssi_vs_atten_ch40_VHT20_ActiveTraffic(self):
809        self._test_rssi_vs_atten()
810
811    @test_tracker_info(uuid='1388df99-ecbf-4412-9ded-d66552f37ec5')
812    def test_rssi_vs_atten_ch44_VHT20_ActiveTraffic(self):
813        self._test_rssi_vs_atten()
814
815    @test_tracker_info(uuid='06868677-ad3c-4f50-9b9e-ae8d9455ae4d')
816    def test_rssi_vs_atten_ch44_VHT40_ActiveTraffic(self):
817        self._test_rssi_vs_atten()
818
819    @test_tracker_info(uuid='9b6676de-c736-4603-a9b3-97670bea8f25')
820    def test_rssi_vs_atten_ch48_VHT20_ActiveTraffic(self):
821        self._test_rssi_vs_atten()
822
823    @test_tracker_info(uuid='2641c4b8-0092-4e29-9139-fdb3b3f04d05')
824    def test_rssi_vs_atten_ch149_VHT20_ActiveTraffic(self):
825        self._test_rssi_vs_atten()
826
827    @test_tracker_info(uuid='c8bc3f7d-b459-4e40-9c73-b0bf534c6c08')
828    def test_rssi_vs_atten_ch149_VHT40_ActiveTraffic(self):
829        self._test_rssi_vs_atten()
830
831    @test_tracker_info(uuid='3e08f5b6-9f3c-4905-8b10-82e1ca830cc9')
832    def test_rssi_vs_atten_ch149_VHT80_ActiveTraffic(self):
833        self._test_rssi_vs_atten()
834
835    @test_tracker_info(uuid='2343efe3-fdda-4180-add7-4786d35e29bb')
836    def test_rssi_vs_atten_ch153_VHT20_ActiveTraffic(self):
837        self._test_rssi_vs_atten()
838
839    @test_tracker_info(uuid='89a16974-2399-4356-b720-17b765ff1c3a')
840    def test_rssi_vs_atten_ch157_VHT20_ActiveTraffic(self):
841        self._test_rssi_vs_atten()
842
843    @test_tracker_info(uuid='c8e0e44a-b962-4e71-ba8f-068f268c8823')
844    def test_rssi_vs_atten_ch157_VHT40_ActiveTraffic(self):
845        self._test_rssi_vs_atten()
846
847    @test_tracker_info(uuid='581b5794-239e-4d1c-b0ce-7c6dc5bd373f')
848    def test_rssi_vs_atten_ch161_VHT20_ActiveTraffic(self):
849        self._test_rssi_vs_atten()
850
851    def test_rssi_tracking_ch6_VHT20_ActiveTraffic(self):
852        self._test_rssi_tracking()
853
854    def test_rssi_tracking_ch6_VHT20_NoTraffic(self):
855        self._test_rssi_tracking()
856
857    def test_rssi_tracking_ch36_VHT20_ActiveTraffic(self):
858        self._test_rssi_tracking()
859
860    def test_rssi_tracking_ch36_VHT20_NoTraffic(self):
861        self._test_rssi_tracking()
862
863    def test_rssi_tracking_ch36_VHT40_ActiveTraffic(self):
864        self._test_rssi_tracking()
865
866    def test_rssi_tracking_ch36_VHT40_NoTraffic(self):
867        self._test_rssi_tracking()
868
869    def test_rssi_tracking_ch36_VHT80_ActiveTraffic(self):
870        self._test_rssi_tracking()
871
872    def test_rssi_tracking_ch36_VHT80_NoTraffic(self):
873        self._test_rssi_tracking()
874
875    def test_rssi_tracking_ch149_VHT20_ActiveTraffic(self):
876        self._test_rssi_tracking()
877
878    def test_rssi_tracking_ch149_VHT20_NoTraffic(self):
879        self._test_rssi_tracking()
880
881    def test_rssi_tracking_ch149_VHT40_ActiveTraffic(self):
882        self._test_rssi_tracking()
883
884    def test_rssi_tracking_ch149_VHT40_NoTraffic(self):
885        self._test_rssi_tracking()
886
887    def test_rssi_tracking_ch149_VHT80_ActiveTraffic(self):
888        self._test_rssi_tracking()
889
890    def test_rssi_tracking_ch149_VHT80_NoTraffic(self):
891        self._test_rssi_tracking()
892
893
894class WifiRssi_2GHz_ActiveTraffic_Test(WifiRssiTest):
895    def __init__(self, controllers):
896        super().__init__(controllers)
897        self.tests = ("test_rssi_stability_ch1_VHT20_ActiveTraffic",
898                      "test_rssi_vs_atten_ch1_VHT20_ActiveTraffic",
899                      "test_rssi_stability_ch2_VHT20_ActiveTraffic",
900                      "test_rssi_vs_atten_ch2_VHT20_ActiveTraffic",
901                      "test_rssi_stability_ch6_VHT20_ActiveTraffic",
902                      "test_rssi_vs_atten_ch6_VHT20_ActiveTraffic",
903                      "test_rssi_stability_ch10_VHT20_ActiveTraffic",
904                      "test_rssi_vs_atten_ch10_VHT20_ActiveTraffic",
905                      "test_rssi_stability_ch11_VHT20_ActiveTraffic",
906                      "test_rssi_vs_atten_ch11_VHT20_ActiveTraffic")
907
908
909class WifiRssi_5GHz_ActiveTraffic_Test(WifiRssiTest):
910    def __init__(self, controllers):
911        super().__init__(controllers)
912        self.tests = ("test_rssi_stability_ch36_VHT20_ActiveTraffic",
913                      "test_rssi_vs_atten_ch36_VHT20_ActiveTraffic",
914                      "test_rssi_stability_ch36_VHT40_ActiveTraffic",
915                      "test_rssi_vs_atten_ch36_VHT40_ActiveTraffic",
916                      "test_rssi_stability_ch36_VHT80_ActiveTraffic",
917                      "test_rssi_vs_atten_ch36_VHT80_ActiveTraffic",
918                      "test_rssi_stability_ch40_VHT20_ActiveTraffic",
919                      "test_rssi_vs_atten_ch40_VHT20_ActiveTraffic",
920                      "test_rssi_stability_ch44_VHT20_ActiveTraffic",
921                      "test_rssi_vs_atten_ch44_VHT20_ActiveTraffic",
922                      "test_rssi_stability_ch44_VHT40_ActiveTraffic",
923                      "test_rssi_vs_atten_ch44_VHT40_ActiveTraffic",
924                      "test_rssi_stability_ch48_VHT20_ActiveTraffic",
925                      "test_rssi_vs_atten_ch48_VHT20_ActiveTraffic",
926                      "test_rssi_stability_ch149_VHT20_ActiveTraffic",
927                      "test_rssi_vs_atten_ch149_VHT20_ActiveTraffic",
928                      "test_rssi_stability_ch149_VHT40_ActiveTraffic",
929                      "test_rssi_vs_atten_ch149_VHT40_ActiveTraffic",
930                      "test_rssi_stability_ch149_VHT80_ActiveTraffic",
931                      "test_rssi_vs_atten_ch149_VHT80_ActiveTraffic",
932                      "test_rssi_stability_ch153_VHT20_ActiveTraffic",
933                      "test_rssi_vs_atten_ch153_VHT20_ActiveTraffic",
934                      "test_rssi_stability_ch157_VHT20_ActiveTraffic",
935                      "test_rssi_vs_atten_ch157_VHT20_ActiveTraffic",
936                      "test_rssi_stability_ch157_VHT40_ActiveTraffic",
937                      "test_rssi_vs_atten_ch157_VHT40_ActiveTraffic",
938                      "test_rssi_stability_ch161_VHT20_ActiveTraffic",
939                      "test_rssi_vs_atten_ch161_VHT20_ActiveTraffic")
940
941
942class WifiRssiTrackingTest(WifiRssiTest):
943    def __init__(self, controllers):
944        super().__init__(controllers)
945        self.tests = ("test_rssi_tracking_ch6_VHT20_ActiveTraffic",
946                      "test_rssi_tracking_ch6_VHT20_NoTraffic",
947                      "test_rssi_tracking_ch36_VHT20_ActiveTraffic",
948                      "test_rssi_tracking_ch36_VHT20_NoTraffic",
949                      "test_rssi_tracking_ch36_VHT40_ActiveTraffic",
950                      "test_rssi_tracking_ch36_VHT40_NoTraffic",
951                      "test_rssi_tracking_ch36_VHT80_ActiveTraffic",
952                      "test_rssi_tracking_ch36_VHT80_NoTraffic",
953                      "test_rssi_tracking_ch149_VHT20_ActiveTraffic",
954                      "test_rssi_tracking_ch149_VHT20_NoTraffic",
955                      "test_rssi_tracking_ch149_VHT40_ActiveTraffic",
956                      "test_rssi_tracking_ch149_VHT40_NoTraffic",
957                      "test_rssi_tracking_ch149_VHT80_ActiveTraffic",
958                      "test_rssi_tracking_ch149_VHT80_NoTraffic")
959