1#!/usr/bin/env python3.4 2# 3# Copyright 2022 - 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 csv 19import itertools 20import numpy 21import json 22import re 23import os 24from acts import context 25from acts import base_test 26from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger 27from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils 28from acts_contrib.test_utils.cellular.performance.CellularThroughputBaseTest import CellularThroughputBaseTest 29from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils 30from acts_contrib.test_utils.wifi.wifi_performance_test_utils.bokeh_figure import BokehFigure 31 32from functools import partial 33 34 35class CellularLteRvrTest(CellularThroughputBaseTest): 36 """Class to test single cell LTE sensitivity""" 37 38 def __init__(self, controllers): 39 base_test.BaseTestClass.__init__(self, controllers) 40 self.testcase_metric_logger = ( 41 BlackboxMappedMetricLogger.for_test_case()) 42 self.testclass_metric_logger = ( 43 BlackboxMappedMetricLogger.for_test_class()) 44 self.publish_testcase_metrics = True 45 self.testclass_params = self.user_params['lte_rvr_test_params'] 46 self.tests = self.generate_test_cases(lte_dl_mcs_table='QAM256', 47 lte_ul_mcs_table='QAM256', 48 lte_ul_mcs=4, 49 transform_precoding=0) 50 51 def process_testclass_results(self): 52 # Plot individual test id results raw data and compile metrics 53 plots = collections.OrderedDict() 54 compiled_data = collections.OrderedDict() 55 for testcase_name, testcase_data in self.testclass_results.items(): 56 cell_config = testcase_data['testcase_params'][ 57 'endc_combo_config']['cell_list'][0] 58 test_id = tuple(('band', cell_config['band'])) 59 if test_id not in plots: 60 # Initialize test id data when not present 61 compiled_data[test_id] = { 62 'mcs': [], 63 'average_throughput': [], 64 'theoretical_throughput': [], 65 'cell_power': [], 66 } 67 plots[test_id] = BokehFigure( 68 title='Band {} ({}) - BLER Curves'.format( 69 cell_config['band'], 70 testcase_data['testcase_params']['lte_dl_mcs_table']), 71 x_label='Cell Power (dBm)', 72 primary_y_label='BLER (Mbps)') 73 test_id_rvr = test_id + tuple('RvR') 74 plots[test_id_rvr] = BokehFigure( 75 title='Band {} ({}) - RvR'.format( 76 cell_config['band'], 77 testcase_data['testcase_params']['lte_dl_mcs_table']), 78 x_label='Cell Power (dBm)', 79 primary_y_label='PHY Rate (Mbps)') 80 # Compile test id data and metrics 81 compiled_data[test_id]['average_throughput'].append( 82 testcase_data['average_throughput_list']) 83 compiled_data[test_id]['cell_power'].append( 84 testcase_data['cell_power_list']) 85 compiled_data[test_id]['mcs'].append( 86 testcase_data['testcase_params']['lte_dl_mcs']) 87 # Add test id to plots 88 plots[test_id].add_line( 89 testcase_data['cell_power_list'], 90 testcase_data['bler_list'], 91 'MCS {}'.format( 92 testcase_data['testcase_params']['lte_dl_mcs']), 93 width=1) 94 plots[test_id_rvr].add_line( 95 testcase_data['cell_power_list'], 96 testcase_data['average_throughput_list'], 97 'MCS {}'.format( 98 testcase_data['testcase_params']['lte_dl_mcs']), 99 width=1, 100 style='dashed') 101 102 # Compute average RvRs and compute metrics over orientations 103 for test_id, test_data in compiled_data.items(): 104 test_id_rvr = test_id + tuple('RvR') 105 cell_power_interp = sorted(set(sum(test_data['cell_power'], []))) 106 average_throughput_interp = [] 107 for mcs, cell_power, throughput in zip( 108 test_data['mcs'], test_data['cell_power'], 109 test_data['average_throughput']): 110 throughput_interp = numpy.interp(cell_power_interp, 111 cell_power[::-1], 112 throughput[::-1]) 113 average_throughput_interp.append(throughput_interp) 114 rvr = numpy.max(average_throughput_interp, 0) 115 plots[test_id_rvr].add_line(cell_power_interp, rvr, 116 'Rate vs. Range') 117 118 figure_list = [] 119 for plot_id, plot in plots.items(): 120 plot.generate_figure() 121 figure_list.append(plot) 122 output_file_path = os.path.join(self.log_path, 'results.html') 123 BokehFigure.save_figures(figure_list, output_file_path) 124 125 def process_testcase_results(self): 126 if self.current_test_name not in self.testclass_results: 127 return 128 testcase_data = self.testclass_results[self.current_test_name] 129 results_file_path = os.path.join( 130 context.get_current_context().get_full_output_path(), 131 '{}.json'.format(self.current_test_name)) 132 with open(results_file_path, 'w') as results_file: 133 json.dump(wputils.serialize_dict(testcase_data), 134 results_file, 135 indent=4) 136 137 bler_list = [] 138 average_throughput_list = [] 139 theoretical_throughput_list = [] 140 cell_power_list = testcase_data['testcase_params']['cell_power_sweep'][ 141 0] 142 for result in testcase_data['results']: 143 bler_list.append( 144 result['throughput_measurements']['lte_bler_result']['total']['DL']['nack_ratio']) 145 average_throughput_list.append( 146 result['throughput_measurements']['lte_tput_result']['total']['DL']['average_tput']) 147 theoretical_throughput_list.append( 148 result['throughput_measurements']['lte_tput_result']['total']['DL']['theoretical_tput']) 149 padding_len = len(cell_power_list) - len(average_throughput_list) 150 average_throughput_list.extend([0] * padding_len) 151 theoretical_throughput_list.extend([0] * padding_len) 152 153 testcase_data['bler_list'] = bler_list 154 testcase_data['average_throughput_list'] = average_throughput_list 155 testcase_data[ 156 'theoretical_throughput_list'] = theoretical_throughput_list 157 testcase_data['cell_power_list'] = cell_power_list 158 159 plot = BokehFigure( 160 title='Band {} - RvR'.format(testcase_data['testcase_params']['endc_combo_config']['cell_list'][0]['band']), 161 x_label='Cell Power (dBm)', 162 primary_y_label='PHY Rate (Mbps)') 163 164 plot.add_line( 165 testcase_data['cell_power_list'], 166 testcase_data['average_throughput_list'], 167 'Average Throughput', 168 width=1) 169 plot.add_line( 170 testcase_data['cell_power_list'], 171 testcase_data['theoretical_throughput_list'], 172 'Average Throughput', 173 width=1, 174 style='dashed') 175 plot.generate_figure() 176 output_file_path = os.path.join(self.log_path, '{}.html'.format(self.current_test_name)) 177 BokehFigure.save_figure(plot, output_file_path) 178 179 def get_per_cell_power_sweeps(self, testcase_params): 180 # get current cell power start 181 cell_power_sweeps = [ 182 list( 183 numpy.arange(self.testclass_params['lte_cell_power_start'], 184 self.testclass_params['lte_cell_power_stop'], 185 self.testclass_params['lte_cell_power_step'])) 186 ] 187 return cell_power_sweeps 188 189 def generate_test_cases(self, lte_dl_mcs_table, 190 lte_ul_mcs_table, lte_ul_mcs, **kwargs): 191 test_cases = [] 192 with open(self.testclass_params['lte_single_cell_configs'], 193 'r') as csvfile: 194 test_configs = csv.DictReader(csvfile) 195 for test_config in test_configs: 196 if int(test_config['skip_test']): 197 continue 198 endc_combo_config = cputils.generate_endc_combo_config_from_csv_row( 199 test_config) 200 test_name = 'test_lte_B{}_dl_{}'.format( 201 test_config['lte_band'], lte_dl_mcs_table) 202 test_params = collections.OrderedDict( 203 endc_combo_config=endc_combo_config, 204 lte_dl_mcs_table=lte_dl_mcs_table, 205 lte_dl_mcs='WCQI', 206 lte_ul_mcs_table=lte_ul_mcs_table, 207 lte_ul_mcs=lte_ul_mcs, 208 **kwargs) 209 setattr(self, test_name, 210 partial(self._test_throughput_bler, test_params)) 211 test_cases.append(test_name) 212 return test_cases 213