• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 json
21import re
22import numpy
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
30
31from functools import partial
32
33LONG_SLEEP = 10
34MEDIUM_SLEEP = 2
35IPERF_TIMEOUT = 10
36SHORT_SLEEP = 1
37SUBFRAME_LENGTH = 0.001
38STOP_COUNTER_LIMIT = 3
39
40
41class CellularFr2PeakThroughputTest(CellularThroughputBaseTest):
42    """Base class to test cellular FR2 throughput
43
44    This class implements cellular FR2 throughput tests on a callbox setup.
45    The class setups up the callbox in the desired configurations, configures
46    and connects the phone, and runs traffic/iperf throughput.
47    """
48
49    def __init__(self, controllers):
50        super().__init__(controllers)
51        base_test.BaseTestClass.__init__(self, controllers)
52        self.testcase_metric_logger = (
53            BlackboxMappedMetricLogger.for_test_case())
54        self.testclass_metric_logger = (
55            BlackboxMappedMetricLogger.for_test_class())
56        self.publish_testcase_metrics = True
57
58    def process_testcase_results(self):
59        """Publish test case metrics and save results"""
60        if self.current_test_name not in self.testclass_results:
61            return
62        testcase_data = self.testclass_results[self.current_test_name]
63        results_file_path = os.path.join(
64            context.get_current_context().get_full_output_path(),
65            '{}.json'.format(self.current_test_name))
66        with open(results_file_path, 'w') as results_file:
67            json.dump(wputils.serialize_dict(testcase_data),
68                      results_file,
69                      indent=4)
70        testcase_result = testcase_data['results'][0]
71        metric_map = {
72            'tcp_udp_tput': testcase_result.get('iperf_throughput',
73                                                float('nan'))
74        }
75        if testcase_data['testcase_params']['endc_combo_config'][
76                'nr_cell_count']:
77            metric_map.update({
78                'nr_min_dl_tput':
79                testcase_result['nr_tput_result']['total']['DL']['min_tput'],
80                'nr_max_dl_tput':
81                testcase_result['nr_tput_result']['total']['DL']['max_tput'],
82                'nr_avg_dl_tput':
83                testcase_result['nr_tput_result']['total']['DL']
84                ['average_tput'],
85                'nr_theoretical_dl_tput':
86                testcase_result['nr_tput_result']['total']['DL']
87                ['theoretical_tput'],
88                'nr_dl_bler':
89                testcase_result['nr_bler_result']['total']['DL']['nack_ratio']
90                * 100,
91                'nr_min_dl_tput':
92                testcase_result['nr_tput_result']['total']['UL']['min_tput'],
93                'nr_max_dl_tput':
94                testcase_result['nr_tput_result']['total']['UL']['max_tput'],
95                'nr_avg_dl_tput':
96                testcase_result['nr_tput_result']['total']['UL']
97                ['average_tput'],
98                'nr_theoretical_dl_tput':
99                testcase_result['nr_tput_result']['total']['UL']
100                ['theoretical_tput'],
101                'nr_ul_bler':
102                testcase_result['nr_bler_result']['total']['UL']['nack_ratio']
103                * 100
104            })
105        if testcase_data['testcase_params']['endc_combo_config'][
106                'lte_cell_count']:
107            metric_map.update({
108                'lte_min_dl_tput':
109                testcase_result['lte_tput_result']['total']['DL']['min_tput'],
110                'lte_max_dl_tput':
111                testcase_result['lte_tput_result']['total']['DL']['max_tput'],
112                'lte_avg_dl_tput':
113                testcase_result['lte_tput_result']['total']['DL']
114                ['average_tput'],
115                'lte_theoretical_dl_tput':
116                testcase_result['lte_tput_result']['total']['DL']
117                ['theoretical_tput'],
118                'lte_dl_bler':
119                testcase_result['lte_bler_result']['total']['DL']['nack_ratio']
120                * 100,
121                'lte_min_dl_tput':
122                testcase_result['lte_tput_result']['total']['UL']['min_tput'],
123                'lte_max_dl_tput':
124                testcase_result['lte_tput_result']['total']['UL']['max_tput'],
125                'lte_avg_dl_tput':
126                testcase_result['lte_tput_result']['total']['UL']
127                ['average_tput'],
128                'lte_theoretical_dl_tput':
129                testcase_result['lte_tput_result']['total']['UL']
130                ['theoretical_tput'],
131                'lte_ul_bler':
132                testcase_result['lte_bler_result']['total']['UL']['nack_ratio']
133                * 100
134            })
135        if self.publish_testcase_metrics:
136            for metric_name, metric_value in metric_map.items():
137                self.testcase_metric_logger.add_metric(metric_name,
138                                                       metric_value)
139
140    def process_testclass_results(self):
141        """Saves CSV with all test results to enable comparison."""
142        results_file_path = os.path.join(
143            context.get_current_context().get_full_output_path(),
144            'results.csv')
145        with open(results_file_path, 'w', newline='') as csvfile:
146            field_names = [
147                'Test Name', 'NR DL Min. Throughput', 'NR DL Max. Throughput',
148                'NR DL Avg. Throughput', 'NR DL Theoretical Throughput',
149                'NR UL Min. Throughput', 'NR UL Max. Throughput',
150                'NR UL Avg. Throughput', 'NR UL Theoretical Throughput',
151                'NR DL BLER (%)', 'NR UL BLER (%)', 'LTE DL Min. Throughput',
152                'LTE DL Max. Throughput', 'LTE DL Avg. Throughput',
153                'LTE DL Theoretical Throughput', 'LTE UL Min. Throughput',
154                'LTE UL Max. Throughput', 'LTE UL Avg. Throughput',
155                'LTE UL Theoretical Throughput', 'LTE DL BLER (%)',
156                'LTE UL BLER (%)', 'TCP/UDP Throughput'
157            ]
158            writer = csv.DictWriter(csvfile, fieldnames=field_names)
159            writer.writeheader()
160
161            for testcase_name, testcase_results in self.testclass_results.items(
162            ):
163                for result in testcase_results['results']:
164                    row_dict = {
165                        'Test Name': testcase_name,
166                        'TCP/UDP Throughput':
167                        result.get('iperf_throughput', 0)
168                    }
169                    if testcase_results['testcase_params'][
170                            'endc_combo_config']['nr_cell_count']:
171                        row_dict.update({
172                            'NR DL Min. Throughput':
173                            result['nr_tput_result']['total']['DL']
174                            ['min_tput'],
175                            'NR DL Max. Throughput':
176                            result['nr_tput_result']['total']['DL']
177                            ['max_tput'],
178                            'NR DL Avg. Throughput':
179                            result['nr_tput_result']['total']['DL']
180                            ['average_tput'],
181                            'NR DL Theoretical Throughput':
182                            result['nr_tput_result']['total']['DL']
183                            ['theoretical_tput'],
184                            'NR UL Min. Throughput':
185                            result['nr_tput_result']['total']['UL']
186                            ['min_tput'],
187                            'NR UL Max. Throughput':
188                            result['nr_tput_result']['total']['UL']
189                            ['max_tput'],
190                            'NR UL Avg. Throughput':
191                            result['nr_tput_result']['total']['UL']
192                            ['average_tput'],
193                            'NR UL Theoretical Throughput':
194                            result['nr_tput_result']['total']['UL']
195                            ['theoretical_tput'],
196                            'NR DL BLER (%)':
197                            result['nr_bler_result']['total']['DL']
198                            ['nack_ratio'] * 100,
199                            'NR UL BLER (%)':
200                            result['nr_bler_result']['total']['UL']
201                            ['nack_ratio'] * 100
202                        })
203                    if testcase_results['testcase_params'][
204                            'endc_combo_config']['lte_cell_count']:
205                        row_dict.update({
206                            'LTE DL Min. Throughput':
207                            result['lte_tput_result']['total']['DL']
208                            ['min_tput'],
209                            'LTE DL Max. Throughput':
210                            result['lte_tput_result']['total']['DL']
211                            ['max_tput'],
212                            'LTE DL Avg. Throughput':
213                            result['lte_tput_result']['total']['DL']
214                            ['average_tput'],
215                            'LTE DL Theoretical Throughput':
216                            result['lte_tput_result']['total']['DL']
217                            ['theoretical_tput'],
218                            'LTE UL Min. Throughput':
219                            result['lte_tput_result']['total']['UL']
220                            ['min_tput'],
221                            'LTE UL Max. Throughput':
222                            result['lte_tput_result']['total']['UL']
223                            ['max_tput'],
224                            'LTE UL Avg. Throughput':
225                            result['lte_tput_result']['total']['UL']
226                            ['average_tput'],
227                            'LTE UL Theoretical Throughput':
228                            result['lte_tput_result']['total']['UL']
229                            ['theoretical_tput'],
230                            'LTE DL BLER (%)':
231                            result['lte_bler_result']['total']['DL']
232                            ['nack_ratio'] * 100,
233                            'LTE UL BLER (%)':
234                            result['lte_bler_result']['total']['UL']
235                            ['nack_ratio'] * 100
236                        })
237                    writer.writerow(row_dict)
238
239    def get_per_cell_power_sweeps(self, testcase_params):
240        """Function to get per cell power sweep lists
241
242        Args:
243            testcase_params: dict containing all test case params
244        Returns:
245            cell_power_sweeps: list of cell power sweeps for each cell under test
246        """
247        cell_power_sweeps = []
248        for cell in testcase_params['endc_combo_config']['cell_list']:
249            if cell['cell_type'] == 'LTE':
250                sweep = [self.testclass_params['lte_cell_power']]
251            else:
252                sweep = [self.testclass_params['nr_cell_power']]
253            cell_power_sweeps.append(sweep)
254        return cell_power_sweeps
255
256    def generate_endc_combo_config(self, test_config):
257        """Function to generate ENDC combo config from CSV test config
258
259        Args:
260            test_config: dict containing ENDC combo config from CSV
261        Returns:
262            endc_combo_config: dictionary with all ENDC combo settings
263        """
264        endc_combo_config = collections.OrderedDict()
265        cell_config_list = []
266
267        lte_cell_count = 1
268        lte_carriers = [1]
269        lte_scc_list = []
270        endc_combo_config['lte_pcc'] = 1
271        lte_cell = {
272            'cell_type':
273            'LTE',
274            'cell_number':
275            1,
276            'pcc':
277            1,
278            'band':
279            test_config['lte_band'],
280            'dl_bandwidth':
281            test_config['lte_bandwidth'],
282            'ul_enabled':
283            1,
284            'duplex_mode':
285            test_config['lte_duplex_mode'],
286            'dl_mimo_config':
287            'D{nss}U{nss}'.format(nss=test_config['lte_dl_mimo_config']),
288            'ul_mimo_config':
289            'D{nss}U{nss}'.format(nss=test_config['lte_ul_mimo_config']),
290            'transmission_mode':
291            'TM1'
292        }
293        cell_config_list.append(lte_cell)
294
295        nr_cell_count = 0
296        nr_dl_carriers = []
297        nr_ul_carriers = []
298        for nr_cell_idx in range(1, test_config['num_dl_cells'] + 1):
299            nr_cell = {
300                'cell_type':
301                'NR5G',
302                'cell_number':
303                nr_cell_idx,
304                'band':
305                test_config['nr_band'],
306                'duplex_mode':
307                test_config['nr_duplex_mode'],
308                'channel':
309                test_config['nr_channel'],
310                'dl_mimo_config':
311                'N{nss}X{nss}'.format(nss=test_config['nr_dl_mimo_config']),
312                'dl_bandwidth_class':
313                'A',
314                'dl_bandwidth':
315                test_config['nr_bandwidth'],
316                'ul_enabled':
317                1 if nr_cell_idx <= test_config['num_ul_cells'] else 0,
318                'ul_bandwidth_class':
319                'A',
320                'ul_mimo_config':
321                'N{nss}X{nss}'.format(nss=test_config['nr_ul_mimo_config']),
322                'subcarrier_spacing':
323                'MU3'
324            }
325            cell_config_list.append(nr_cell)
326            nr_cell_count = nr_cell_count + 1
327            nr_dl_carriers.append(nr_cell_idx)
328            if nr_cell_idx <= test_config['num_ul_cells']:
329                nr_ul_carriers.append(nr_cell_idx)
330
331        endc_combo_config['lte_cell_count'] = lte_cell_count
332        endc_combo_config['nr_cell_count'] = nr_cell_count
333        endc_combo_config['nr_dl_carriers'] = nr_dl_carriers
334        endc_combo_config['nr_ul_carriers'] = nr_ul_carriers
335        endc_combo_config['cell_list'] = cell_config_list
336        endc_combo_config['lte_scc_list'] = lte_scc_list
337        endc_combo_config['lte_carriers'] = lte_carriers
338        return endc_combo_config
339
340    def generate_test_cases(self, bands, channels, nr_mcs_pair_list,
341                            num_dl_cells_list, num_ul_cells_list,
342                            dl_mimo_config, ul_mimo_config, **kwargs):
343        """Function that auto-generates test cases for a test class."""
344        test_cases = []
345        for band, channel, num_ul_cells, num_dl_cells, nr_mcs_pair in itertools.product(
346                bands, channels, num_ul_cells_list, num_dl_cells_list,
347                nr_mcs_pair_list):
348            if num_ul_cells > num_dl_cells:
349                continue
350            if channel not in cputils.PCC_PRESET_MAPPING[band]:
351                continue
352            test_config = {
353                'lte_band': 2,
354                'lte_bandwidth': 'BW20',
355                'lte_duplex_mode': 'FDD',
356                'lte_dl_mimo_config': 1,
357                'lte_ul_mimo_config': 1,
358                'nr_band': band,
359                'nr_bandwidth': 'BW100',
360                'nr_duplex_mode': 'TDD',
361                'nr_channel': channel,
362                'num_dl_cells': num_dl_cells,
363                'num_ul_cells': num_ul_cells,
364                'nr_dl_mimo_config': dl_mimo_config,
365                'nr_ul_mimo_config': ul_mimo_config
366            }
367            endc_combo_config = self.generate_endc_combo_config(test_config)
368            test_name = 'test_fr2_{}_{}_DL_{}CC_mcs{}_{}x{}_UL_{}CC_mcs{}_{}x{}'.format(
369                band, channel, num_dl_cells, nr_mcs_pair[0], dl_mimo_config,
370                dl_mimo_config, num_ul_cells, nr_mcs_pair[1], ul_mimo_config,
371                ul_mimo_config)
372            test_params = collections.OrderedDict(
373                endc_combo_config=endc_combo_config,
374                nr_dl_mcs=nr_mcs_pair[0],
375                nr_ul_mcs=nr_mcs_pair[1],
376                **kwargs)
377            setattr(self, test_name,
378                    partial(self._test_throughput_bler, test_params))
379            test_cases.append(test_name)
380        return test_cases
381
382
383class CellularFr2DlPeakThroughputTest(CellularFr2PeakThroughputTest):
384    """Base class to test cellular FR2 throughput
385
386    This class implements cellular FR2 throughput tests on a callbox setup.
387    The class setups up the callbox in the desired configurations, configures
388    and connects the phone, and runs traffic/iperf throughput.
389    """
390
391    def __init__(self, controllers):
392        super().__init__(controllers)
393        self.testclass_params = self.user_params['throughput_test_params']
394        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
395                                              ['low', 'mid', 'high'],
396                                              [(16, 4), (27, 4)],
397                                              list(range(1, 9)),
398                                              list(range(1, 3)),
399                                              force_contiguous_nr_channel=True,
400                                              dl_mimo_config=2,
401                                              ul_mimo_config=1,
402                                              schedule_scenario="FULL_TPUT",
403                                              traffic_direction='DL',
404                                              transform_precoding=0,
405                                              lte_dl_mcs=4,
406                                              lte_dl_mcs_table='QAM256',
407                                              lte_ul_mcs=4,
408                                              lte_ul_mcs_table='QAM64')
409
410
411class CellularFr2CpOfdmUlPeakThroughputTest(CellularFr2PeakThroughputTest):
412
413    def __init__(self, controllers):
414        super().__init__(controllers)
415        self.testclass_params = self.user_params['throughput_test_params']
416        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
417                                              ['low', 'mid', 'high'],
418                                              [(4, 16), (4, 27)], [1], [1],
419                                              force_contiguous_nr_channel=True,
420                                              dl_mimo_config=2,
421                                              ul_mimo_config=1,
422                                              schedule_scenario="FULL_TPUT",
423                                              traffic_direction='UL',
424                                              transform_precoding=0,
425                                              lte_dl_mcs=4,
426                                              lte_dl_mcs_table='QAM256',
427                                              lte_ul_mcs=4,
428                                              lte_ul_mcs_table='QAM64')
429        self.tests.extend(
430            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
431                                     ['low', 'mid', 'high'],
432                                     [(4, 16), (4, 27)], [1], [1],
433                                     force_contiguous_nr_channel=True,
434                                     dl_mimo_config=2,
435                                     ul_mimo_config=2,
436                                     schedule_scenario="FULL_TPUT",
437                                     traffic_direction='UL',
438                                     transform_precoding=0,
439                                     lte_dl_mcs=4,
440                                     lte_dl_mcs_table='QAM256',
441                                     lte_ul_mcs=4,
442                                     lte_ul_mcs_table='QAM64'))
443        self.tests.extend(
444            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
445                                     ['low', 'mid', 'high'],
446                                     [(4, 16), (4, 27)], [2], [2],
447                                     force_contiguous_nr_channel=True,
448                                     dl_mimo_config=2,
449                                     ul_mimo_config=2,
450                                     schedule_scenario="FULL_TPUT",
451                                     traffic_direction='UL',
452                                     transform_precoding=0,
453                                     lte_dl_mcs=4,
454                                     lte_dl_mcs_table='QAM256',
455                                     lte_ul_mcs=4,
456                                     lte_ul_mcs_table='QAM64'))
457        self.tests.extend(
458            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
459                                     ['low', 'mid', 'high'],
460                                     [(4, 16), (4, 27)], [4], [4],
461                                     force_contiguous_nr_channel=True,
462                                     dl_mimo_config=2,
463                                     ul_mimo_config=2,
464                                     schedule_scenario="FULL_TPUT",
465                                     traffic_direction='UL',
466                                     transform_precoding=0,
467                                     lte_dl_mcs=4,
468                                     lte_dl_mcs_table='QAM256',
469                                     lte_ul_mcs=4,
470                                     lte_ul_mcs_table='QAM64'))
471
472
473class CellularFr2DftsOfdmUlPeakThroughputTest(CellularFr2PeakThroughputTest):
474
475    def __init__(self, controllers):
476        super().__init__(controllers)
477        self.testclass_params = self.user_params['throughput_test_params']
478        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
479                                              ['low', 'mid', 'high'],
480                                              [(4, 16), (4, 27)], [1], [1],
481                                              force_contiguous_nr_channel=True,
482                                              dl_mimo_config=2,
483                                              ul_mimo_config=1,
484                                              schedule_scenario="FULL_TPUT",
485                                              traffic_direction='UL',
486                                              transform_precoding=1,
487                                              lte_dl_mcs=4,
488                                              lte_dl_mcs_table='QAM256',
489                                              lte_ul_mcs=4,
490                                              lte_ul_mcs_table='QAM64')
491        self.tests.extend(
492            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
493                                     ['low', 'mid', 'high'],
494                                     [(4, 16), (4, 27)], [1], [1],
495                                     force_contiguous_nr_channel=True,
496                                     dl_mimo_config=2,
497                                     ul_mimo_config=2,
498                                     schedule_scenario="FULL_TPUT",
499                                     traffic_direction='UL',
500                                     transform_precoding=1,
501                                     lte_dl_mcs=4,
502                                     lte_dl_mcs_table='QAM256',
503                                     lte_ul_mcs=4,
504                                     lte_ul_mcs_table='QAM64'))
505        self.tests.extend(
506            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
507                                     ['low', 'mid', 'high'],
508                                     [(4, 16), (4, 27)], [2], [2],
509                                     force_contiguous_nr_channel=True,
510                                     dl_mimo_config=2,
511                                     ul_mimo_config=2,
512                                     schedule_scenario="FULL_TPUT",
513                                     traffic_direction='UL',
514                                     transform_precoding=1,
515                                     lte_dl_mcs=4,
516                                     lte_dl_mcs_table='QAM256',
517                                     lte_ul_mcs=4,
518                                     lte_ul_mcs_table='QAM64'))
519        self.tests.extend(
520            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
521                                     ['low', 'mid', 'high'],
522                                     [(4, 16), (4, 27)], [4], [4],
523                                     force_contiguous_nr_channel=True,
524                                     dl_mimo_config=2,
525                                     ul_mimo_config=2,
526                                     schedule_scenario="FULL_TPUT",
527                                     traffic_direction='UL',
528                                     transform_precoding=1,
529                                     lte_dl_mcs=4,
530                                     lte_dl_mcs_table='QAM256',
531                                     lte_ul_mcs=4,
532                                     lte_ul_mcs_table='QAM64'))
533
534
535class CellularFr2DlFrequencySweepPeakThroughputTest(
536        CellularFr2PeakThroughputTest):
537    """Base class to test cellular FR2 throughput
538
539    This class implements cellular FR2 throughput tests on a callbox setup.
540    The class setups up the callbox in the desired configurations, configures
541    and connects the phone, and runs traffic/iperf throughput.
542    """
543
544    def __init__(self, controllers):
545        super().__init__(controllers)
546        self.testclass_params = self.user_params['throughput_test_params']
547        self.tests = self.generate_test_cases(
548            ['N257', 'N258', 'N260', 'N261'],
549            self.user_params['throughput_test_params']['frequency_sweep'],
550            [(16, 4), (27, 4)],
551            force_contiguous_nr_channel=False,
552            dl_mimo_config=2,
553            ul_mimo_config=1,
554            schedule_scenario="FULL_TPUT",
555            traffic_direction='DL',
556            transform_precoding=0,
557            lte_dl_mcs=4,
558            lte_dl_mcs_table='QAM256',
559            lte_ul_mcs=4,
560            lte_ul_mcs_table='QAM64')
561
562    def generate_test_cases(self, bands, channels, nr_mcs_pair_list,
563                            num_dl_cells_list, num_ul_cells_list,
564                            dl_mimo_config, ul_mimo_config, **kwargs):
565        """Function that auto-generates test cases for a test class."""
566        test_cases = []
567        for band, channel, num_ul_cells, num_dl_cells, nr_mcs_pair in itertools.product(
568                bands, channels, num_ul_cells_list, num_dl_cells_list,
569                nr_mcs_pair_list):
570            if num_ul_cells > num_dl_cells:
571                continue
572            if channel not in cputils.PCC_PRESET_MAPPING[band]:
573                continue
574            test_config = {
575                'lte_band': 2,
576                'lte_bandwidth': 'BW20',
577                'lte_duplex_mode': 'FDD',
578                'lte_dl_mimo_config': 1,
579                'lte_ul_mimo_config': 1,
580                'nr_band': band,
581                'nr_bandwidth': 'BW100',
582                'nr_duplex_mode': 'TDD',
583                'nr_channel': channel,
584                'num_dl_cells': num_dl_cells,
585                'num_ul_cells': num_ul_cells,
586                'nr_dl_mimo_config': dl_mimo_config,
587                'nr_ul_mimo_config': ul_mimo_config
588            }
589            endc_combo_config = self.generate_endc_combo_config(test_config)
590            test_name = 'test_fr2_{}_{}_DL_{}CC_mcs{}_{}x{}_UL_{}CC_mcs{}_{}x{}'.format(
591                band, channel, num_dl_cells, nr_mcs_pair[0], dl_mimo_config,
592                dl_mimo_config, num_ul_cells, nr_mcs_pair[1], ul_mimo_config,
593                ul_mimo_config)
594            test_params = collections.OrderedDict(
595                endc_combo_config=endc_combo_config,
596                nr_dl_mcs=nr_mcs_pair[0],
597                nr_ul_mcs=nr_mcs_pair[1],
598                **kwargs)
599            setattr(self, test_name,
600                    partial(self._test_throughput_bler, test_params))
601            test_cases.append(test_name)
602        return test_cases
603