1#!/usr/bin/env python3
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.
16import time
17from enum import Enum
18
19from acts.controllers.cellular_lib.BaseSimulation import BaseSimulation
20from acts.controllers.cellular_lib.LteCellConfig import LteCellConfig
21from acts.controllers.cellular_lib.NrCellConfig import NrCellConfig
22from acts.controllers.cellular_lib import BaseCellularDut
23
24
25class IPAddressType(Enum):
26    """ IP Address types"""
27    IPV4 = "IPV4"
28    IPV6 = "IPV6"
29    IPV4V6 = "IPV4V6"
30
31
32class TransmissionMode(Enum):
33    """ Transmission modes for LTE (e.g., TM1, TM4, ...) """
34    TM1 = "TM1"
35    TM2 = "TM2"
36    TM3 = "TM3"
37    TM4 = "TM4"
38    TM7 = "TM7"
39    TM8 = "TM8"
40    TM9 = "TM9"
41
42
43class MimoMode(Enum):
44    """ Mimo modes """
45    MIMO_1x1 = "1x1"
46    MIMO_2x2 = "2x2"
47    MIMO_4x4 = "4x4"
48
49
50class SchedulingMode(Enum):
51    """ Traffic scheduling modes (e.g., STATIC, DYNAMIC) """
52    DYNAMIC = "DYNAMIC"
53    STATIC = "STATIC"
54
55
56class DuplexMode(Enum):
57    """ DL/UL Duplex mode """
58    FDD = "FDD"
59    TDD = "TDD"
60
61
62class ModulationType(Enum):
63    """DL/UL Modulation order."""
64    QPSK = 'QPSK'
65    Q16 = '16QAM'
66    Q64 = '64QAM'
67    Q256 = '256QAM'
68
69
70# Bandwidth [MHz] to RB group size
71RBG_DICTIONARY = {20: 4, 15: 4, 10: 3, 5: 2, 3: 2, 1.4: 1}
72
73# Bandwidth [MHz] to total RBs mapping
74TOTAL_RBS_DICTIONARY = {20: 100, 15: 75, 10: 50, 5: 25, 3: 15, 1.4: 6}
75
76# Bandwidth [MHz] to minimum number of DL RBs that can be assigned to a UE
77MIN_DL_RBS_DICTIONARY = {20: 16, 15: 12, 10: 9, 5: 4, 3: 4, 1.4: 2}
78
79# Bandwidth [MHz] to minimum number of UL RBs that can be assigned to a UE
80MIN_UL_RBS_DICTIONARY = {20: 8, 15: 6, 10: 4, 5: 2, 3: 2, 1.4: 1}
81
82
83class LteSimulation(BaseSimulation):
84    """ Single-carrier LTE simulation. """
85    # Test config keywords
86    KEY_FREQ_BANDS = "freq_bands"
87
88    # Cell param keywords
89    PARAM_RRC_STATUS_CHANGE_TIMER = "rrcstatuschangetimer"
90
91    # Units in which signal level is defined in DOWNLINK_SIGNAL_LEVEL_DICTIONARY
92    DOWNLINK_SIGNAL_LEVEL_UNITS = "RSRP"
93
94    # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz.
95    # excellent is set to -62 and also provide a good level for callbox B Tx
96    # power that is limited to some values such as -25 dBm or -30 dBm
97    DOWNLINK_SIGNAL_LEVEL_DICTIONARY = {
98        'excellent': -62,
99        'great': -75,
100        'high': -110,
101        'medium': -115,
102        'weak': -120,
103        'disconnected': -170
104    }
105
106    # Transmitted output power for the phone (dBm)
107    UPLINK_SIGNAL_LEVEL_DICTIONARY = {
108        'max': 27,
109        'high': 13,
110        'medium': 3,
111        'low': -20
112    }
113
114    # Allowed bandwidth for each band.
115    allowed_bandwidth_dictionary = {
116        1: [5, 10, 15, 20],
117        2: [1.4, 3, 5, 10, 15, 20],
118        3: [1.4, 3, 5, 10, 15, 20],
119        4: [1.4, 3, 5, 10, 15, 20],
120        5: [1.4, 3, 5, 10],
121        7: [5, 10, 15, 20],
122        8: [1.4, 3, 5, 10],
123        10: [5, 10, 15, 20],
124        11: [5, 10],
125        12: [1.4, 3, 5, 10],
126        13: [5, 10],
127        14: [5, 10],
128        17: [5, 10],
129        18: [5, 10, 15],
130        19: [5, 10, 15],
131        20: [5, 10, 15, 20],
132        21: [5, 10, 15],
133        22: [5, 10, 15, 20],
134        24: [5, 10],
135        25: [1.4, 3, 5, 10, 15, 20],
136        26: [1.4, 3, 5, 10, 15],
137        27: [1.4, 3, 5, 10],
138        28: [3, 5, 10, 15, 20],
139        29: [3, 5, 10],
140        30: [5, 10],
141        31: [1.4, 3, 5],
142        32: [5, 10, 15, 20],
143        33: [5, 10, 15, 20],
144        34: [5, 10, 15],
145        35: [1.4, 3, 5, 10, 15, 20],
146        36: [1.4, 3, 5, 10, 15, 20],
147        37: [5, 10, 15, 20],
148        38: [20],
149        39: [5, 10, 15, 20],
150        40: [5, 10, 15, 20],
151        41: [5, 10, 15, 20],
152        42: [5, 10, 15, 20],
153        43: [5, 10, 15, 20],
154        44: [3, 5, 10, 15, 20],
155        45: [5, 10, 15, 20],
156        46: [10, 20],
157        47: [10, 20],
158        48: [5, 10, 15, 20],
159        49: [10, 20],
160        50: [3, 5, 10, 15, 20],
161        51: [3, 5],
162        52: [5, 10, 15, 20],
163        65: [5, 10, 15, 20],
164        66: [1.4, 3, 5, 10, 15, 20],
165        67: [5, 10, 15, 20],
166        68: [5, 10, 15],
167        69: [5],
168        70: [5, 10, 15],
169        71: [5, 10, 15, 20],
170        72: [1.4, 3, 5],
171        73: [1.4, 3, 5],
172        74: [1.4, 3, 5, 10, 15, 20],
173        75: [5, 10, 15, 20],
174        76: [5],
175        85: [5, 10],
176        252: [20],
177        255: [20]
178    }
179
180    # Dictionary of lower DL channel number bound for each band.
181    LOWEST_DL_CN_DICTIONARY = {
182        1: 0,
183        2: 600,
184        3: 1200,
185        4: 1950,
186        5: 2400,
187        6: 2650,
188        7: 2750,
189        8: 3450,
190        9: 3800,
191        10: 4150,
192        11: 4750,
193        12: 5010,
194        13: 5180,
195        14: 5280,
196        17: 5730,
197        18: 5850,
198        19: 6000,
199        20: 6150,
200        21: 6450,
201        22: 6600,
202        23: 7500,
203        24: 7700,
204        25: 8040,
205        26: 8690,
206        27: 9040,
207        28: 9210,
208        29: 9660,
209        30: 9770,
210        31: 9870,
211        32: 9920,
212        33: 36000,
213        34: 36200,
214        35: 36350,
215        36: 36950,
216        37: 37550,
217        38: 37750,
218        39: 38250,
219        40: 38650,
220        41: 39650,
221        42: 41590,
222        43: 45590,
223        66: 66436,
224        67: 67336
225    }
226
227    # Peak throughput lookup tables for each TDD subframe
228    # configuration and bandwidth
229    # yapf: disable
230    tdd_config4_tput_lut = {
231        0: {
232            5: {'DL': 3.82, 'UL': 2.63},
233            10: {'DL': 11.31,'UL': 9.03},
234            15: {'DL': 16.9, 'UL': 20.62},
235            20: {'DL': 22.88, 'UL': 28.43}
236        },
237        1: {
238            5: {'DL': 6.13, 'UL': 4.08},
239            10: {'DL': 18.36, 'UL': 9.69},
240            15: {'DL': 28.62, 'UL': 14.21},
241            20: {'DL': 39.04, 'UL': 19.23}
242        },
243        2: {
244            5: {'DL': 5.68, 'UL': 2.30},
245            10: {'DL': 25.51, 'UL': 4.68},
246            15: {'DL': 39.3, 'UL': 7.13},
247            20: {'DL': 53.64, 'UL': 9.72}
248        },
249        3: {
250            5: {'DL': 8.26, 'UL': 3.45},
251            10: {'DL': 23.20, 'UL': 6.99},
252            15: {'DL': 35.35, 'UL': 10.75},
253            20: {'DL': 48.3, 'UL': 14.6}
254        },
255        4: {
256            5: {'DL': 6.16, 'UL': 2.30},
257            10: {'DL': 26.77, 'UL': 4.68},
258            15: {'DL': 40.7, 'UL': 7.18},
259            20: {'DL': 55.6, 'UL': 9.73}
260        },
261        5: {
262            5: {'DL': 6.91, 'UL': 1.12},
263            10: {'DL': 30.33, 'UL': 2.33},
264            15: {'DL': 46.04, 'UL': 3.54},
265            20: {'DL': 62.9, 'UL': 4.83}
266        },
267        6: {
268            5: {'DL': 6.13, 'UL': 4.13},
269            10: {'DL': 14.79, 'UL': 11.98},
270            15: {'DL': 23.28, 'UL': 17.46},
271            20: {'DL': 31.75, 'UL': 23.95}
272        }
273    }
274
275    tdd_config3_tput_lut = {
276        0: {
277            5: {'DL': 5.04, 'UL': 3.7},
278            10: {'DL': 15.11, 'UL': 17.56},
279            15: {'DL': 22.59, 'UL': 30.31},
280            20: {'DL': 30.41, 'UL': 41.61}
281        },
282        1: {
283            5: {'DL': 8.07, 'UL': 5.66},
284            10: {'DL': 24.58, 'UL': 13.66},
285            15: {'DL': 39.05, 'UL': 20.68},
286            20: {'DL': 51.59, 'UL': 28.76}
287        },
288        2: {
289            5: {'DL': 7.59, 'UL': 3.31},
290            10: {'DL': 34.08, 'UL': 6.93},
291            15: {'DL': 53.64, 'UL': 10.51},
292            20: {'DL': 70.55, 'UL': 14.41}
293        },
294        3: {
295            5: {'DL': 10.9, 'UL': 5.0},
296            10: {'DL': 30.99, 'UL': 10.25},
297            15: {'DL': 48.3, 'UL': 15.81},
298            20: {'DL': 63.24, 'UL': 21.65}
299        },
300        4: {
301            5: {'DL': 8.11, 'UL': 3.32},
302            10: {'DL': 35.74, 'UL': 6.95},
303            15: {'DL': 55.6, 'UL': 10.51},
304            20: {'DL': 72.72, 'UL': 14.41}
305        },
306        5: {
307            5: {'DL': 9.28, 'UL': 1.57},
308            10: {'DL': 40.49, 'UL': 3.44},
309            15: {'DL': 62.9, 'UL': 5.23},
310            20: {'DL': 82.21, 'UL': 7.15}
311        },
312        6: {
313            5: {'DL': 8.06, 'UL': 5.74},
314            10: {'DL': 19.82, 'UL': 17.51},
315            15: {'DL': 31.75, 'UL': 25.77},
316            20: {'DL': 42.12, 'UL': 34.91}
317        }
318    }
319
320    tdd_config2_tput_lut = {
321        0: {
322            5: {'DL': 3.11, 'UL': 2.55},
323            10: {'DL': 9.93, 'UL': 11.1},
324            15: {'DL': 13.9, 'UL': 21.51},
325            20: {'DL': 20.02, 'UL': 41.66}
326        },
327        1: {
328            5: {'DL': 5.33, 'UL': 4.27},
329            10: {'DL': 15.14, 'UL': 13.95},
330            15: {'DL': 33.84, 'UL': 19.73},
331            20: {'DL': 44.61, 'UL': 27.35}
332        },
333        2: {
334            5: {'DL': 6.87, 'UL': 3.32},
335            10: {'DL': 17.06, 'UL': 6.76},
336            15: {'DL': 49.63, 'UL': 10.5},
337            20: {'DL': 65.2, 'UL': 14.41}
338        },
339        3: {
340            5: {'DL': 5.41, 'UL': 4.17},
341            10: {'DL': 16.89, 'UL': 9.73},
342            15: {'DL': 44.29, 'UL': 15.7},
343            20: {'DL': 53.95, 'UL': 19.85}
344        },
345        4: {
346            5: {'DL': 8.7, 'UL': 3.32},
347            10: {'DL': 17.58, 'UL': 6.76},
348            15: {'DL': 51.08, 'UL': 10.47},
349            20: {'DL': 66.45, 'UL': 14.38}
350        },
351        5: {
352            5: {'DL': 9.46, 'UL': 1.55},
353            10: {'DL': 19.02, 'UL': 3.48},
354            15: {'DL': 58.89, 'UL': 5.23},
355            20: {'DL': 76.85, 'UL': 7.1}
356        },
357        6: {
358            5: {'DL': 4.74, 'UL': 3.9},
359            10: {'DL': 12.32, 'UL': 13.37},
360            15: {'DL': 27.74, 'UL': 25.02},
361            20: {'DL': 35.48, 'UL': 32.95}
362        }
363    }
364
365    tdd_config1_tput_lut = {
366        0: {
367            5: {'DL': 4.25, 'UL': 3.35},
368            10: {'DL': 8.38, 'UL': 7.22},
369            15: {'DL': 12.41, 'UL': 13.91},
370            20: {'DL': 16.27, 'UL': 24.09}
371        },
372        1: {
373            5: {'DL': 7.28, 'UL': 4.61},
374            10: {'DL': 14.73, 'UL': 9.69},
375            15: {'DL': 21.91, 'UL': 13.86},
376            20: {'DL': 27.63, 'UL': 17.18}
377        },
378        2: {
379            5: {'DL': 10.37, 'UL': 2.27},
380            10: {'DL': 20.92, 'UL': 4.66},
381            15: {'DL': 31.01, 'UL': 7.04},
382            20: {'DL': 42.03, 'UL': 9.75}
383        },
384        3: {
385            5: {'DL': 9.25, 'UL': 3.44},
386            10: {'DL': 18.38, 'UL': 6.95},
387            15: {'DL': 27.59, 'UL': 10.62},
388            20: {'DL': 34.85, 'UL': 13.45}
389        },
390        4: {
391            5: {'DL': 10.71, 'UL': 2.26},
392            10: {'DL': 21.54, 'UL': 4.67},
393            15: {'DL': 31.91, 'UL': 7.2},
394            20: {'DL': 43.35, 'UL': 9.74}
395        },
396        5: {
397            5: {'DL': 12.34, 'UL': 1.08},
398            10: {'DL': 24.78, 'UL': 2.34},
399            15: {'DL': 36.68, 'UL': 3.57},
400            20: {'DL': 49.84, 'UL': 4.81}
401        },
402        6: {
403            5: {'DL': 5.76, 'UL': 4.41},
404            10: {'DL': 11.68, 'UL': 9.7},
405            15: {'DL': 17.34, 'UL': 17.95},
406            20: {'DL': 23.5, 'UL': 23.42}
407        }
408    }
409    # yapf: enable
410
411    # Peak throughput lookup table dictionary
412    tdd_config_tput_lut_dict = {
413        'TDD_CONFIG1':
414        tdd_config1_tput_lut,  # DL 256QAM, UL 64QAM & MAC padding turned OFF
415        'TDD_CONFIG2':
416        tdd_config2_tput_lut,  # DL 256QAM, UL 64 QAM ON & MAC padding OFF
417        'TDD_CONFIG3':
418        tdd_config3_tput_lut,  # DL 256QAM, UL 64QAM & MAC padding ON
419        'TDD_CONFIG4':
420        tdd_config4_tput_lut  # DL 256QAM, UL 64 QAM OFF & MAC padding ON
421    }
422
423    def __init__(self,
424                 simulator,
425                 log,
426                 dut,
427                 test_config,
428                 calibration_table,
429                 nr_mode=None):
430        """ Initializes the simulator for a single-carrier LTE simulation.
431
432        Args:
433            simulator: a cellular simulator controller
434            log: a logger handle
435            dut: a device handler implementing BaseCellularDut
436            test_config: test configuration obtained from the config file
437            calibration_table: a dictionary containing path losses for
438                different bands.
439
440        """
441
442        super().__init__(simulator, log, dut, test_config, calibration_table,
443                         nr_mode)
444
445        self.num_carriers = None
446
447        # Force device to LTE only so that it connects faster
448        try:
449            if self.nr_mode and 'nr' == self.nr_mode:
450                self.dut.set_preferred_network_type(
451                    BaseCellularDut.PreferredNetworkType.NR_LTE)
452            else:
453                self.dut.set_preferred_network_type(
454                    BaseCellularDut.PreferredNetworkType.LTE_ONLY)
455        except Exception as e:
456            # If this fails the test should be able to run anyways, even if it
457            # takes longer to find the cell.
458            self.log.warning('Setting preferred RAT failed: {}'.format(e))
459
460        # Get LTE CA frequency bands setting from the test configuration
461        if self.KEY_FREQ_BANDS not in test_config:
462            self.log.warning("The key '{}' is not set in the config file. "
463                             "Setting to null by default.".format(
464                                 self.KEY_FREQ_BANDS))
465
466        self.freq_bands = test_config.get(self.KEY_FREQ_BANDS, True)
467
468    def setup_simulator(self):
469        """ Do initial configuration in the simulator. """
470        if self.nr_mode and 'nr' == self.nr_mode:
471            self.log.info('Initializes the callbox to Nr Nsa scenario')
472            self.simulator.setup_nr_nsa_scenario()
473        else:
474            self.log.info('Initializes the callbox to LTE scenario')
475            self.simulator.setup_lte_scenario()
476
477    def configure(self, parameters):
478        """ Configures simulation using a dictionary of parameters.
479
480        Processes LTE configuration parameters.
481
482        Args:
483            parameters: a configuration dictionary if there is only one carrier,
484                a list if there are multiple cells.
485        """
486        # If there is a single item, put in a list
487        if not isinstance(parameters, list):
488            parameters = [parameters]
489
490        # Pass only PCC configs to BaseSimulation
491        super().configure(parameters[0])
492
493        new_cell_list = []
494        for cell in parameters:
495            if LteCellConfig.PARAM_BAND not in cell:
496                raise ValueError(
497                    "The configuration dictionary must include a key '{}' with "
498                    "the required band number.".format(
499                        LteCellConfig.PARAM_BAND))
500
501            band = cell[LteCellConfig.PARAM_BAND]
502
503            if isinstance(band, str) and not band.isdigit():
504                # If band starts with n then it is an NR band
505                if band[0] == 'n' and band[1:].isdigit():
506                    # If the remaining string is only the band number, add
507                    # the cell and continue
508                    new_cell_list.append(cell)
509                    continue
510
511                ca_class = band[-1].upper()
512                band_num = band[:-1]
513
514                if ca_class in ['A', 'C']:
515                    # Remove the CA class label and add the cell
516                    cell[LteCellConfig.PARAM_BAND] = band_num
517                    new_cell_list.append(cell)
518                elif ca_class == 'B':
519                    raise RuntimeError('Class B LTE CA not supported.')
520                else:
521                    raise ValueError('Invalid band value: ' + band)
522
523                # Class C means that there are two contiguous carriers
524                if ca_class == 'C':
525                    new_cell_list.append(dict(cell))
526                    bw = int(cell[LteCellConfig.PARAM_BW])
527                    dl_earfcn = LteCellConfig.PARAM_DL_EARFCN
528                    new_cell_list[-1][
529                        dl_earfcn] = self.LOWEST_DL_CN_DICTIONARY[int(
530                            band_num)] + bw * 10 - 2
531            else:
532                # The band is just a number, so just add it to the list
533                new_cell_list.append(cell)
534
535        # verify mimo mode parameter is provided
536        for cell in new_cell_list:
537            if not LteCellConfig.PARAM_MIMO in cell:
538                raise ValueError(
539                    'The config dictionary must include parameter "{}" with the'
540                    ' mimo mode.'.format(self.PARAM_MIMO))
541
542            if cell[LteCellConfig.PARAM_MIMO] not in (m.value
543                                                      for m in MimoMode):
544                raise ValueError(
545                    'The value of {} must be one of the following:'
546                    '1x1, 2x2 or 4x4.'.format(self.PARAM_MIMO))
547
548        # Logs new_cell_list for debug
549        self.log.info('new cell list: {}'.format(new_cell_list))
550
551        self.simulator.set_band_combination(
552            [c[LteCellConfig.PARAM_BAND] for c in new_cell_list],
553            [MimoMode(c[LteCellConfig.PARAM_MIMO]) for c in new_cell_list])
554
555        self.num_carriers = len(new_cell_list)
556
557        # Setup the base stations with the obtain configuration
558        self.cell_configs = []
559        for i in range(self.num_carriers):
560            band = new_cell_list[i][LteCellConfig.PARAM_BAND]
561            if isinstance(band, str) and band[0] == 'n':
562                self.cell_configs.append(NrCellConfig(self.log))
563            else:
564                self.cell_configs.append(LteCellConfig(self.log))
565            self.cell_configs[i].configure(new_cell_list[i])
566            self.simulator.configure_bts(self.cell_configs[i], i)
567
568        # Now that the band is set, calibrate the link if necessary
569        self.load_pathloss_if_required()
570
571        # This shouldn't be a cell parameter but instead a simulation config
572        # Setup LTE RRC status change function and timer for LTE idle test case
573        if self.PARAM_RRC_STATUS_CHANGE_TIMER not in parameters[0]:
574            self.log.info(
575                "The test config does not include the '{}' key. Disabled "
576                "by default.".format(self.PARAM_RRC_STATUS_CHANGE_TIMER))
577            self.simulator.set_lte_rrc_state_change_timer(False)
578        else:
579            timer = int(parameters[0][self.PARAM_RRC_STATUS_CHANGE_TIMER])
580            self.simulator.set_lte_rrc_state_change_timer(True, timer)
581            self.rrc_sc_timer = timer
582
583    def configure_after_started(self):
584        """ Configures after simulation started.
585
586        For some simulators, the additional setup is need after the simulation
587        is started. We could assume the self.configure was run and there
588        self.cell_configs exists and configured.
589        """
590
591        for i in range(len(self.cell_configs)):
592            self.simulator.configure_bts_after_started(
593                self.cell_configs[i], i
594            )
595
596    def calibrated_downlink_rx_power(self, bts_config, rsrp):
597        """ LTE simulation overrides this method so that it can convert from
598        RSRP to total signal power transmitted from the basestation.
599
600        Args:
601            bts_config: the current configuration at the base station
602            rsrp: desired rsrp, contained in a key value pair
603        """
604
605        power = self.rsrp_to_signal_power(rsrp, bts_config)
606
607        self.log.info(
608            "Setting downlink signal level to {} RSRP ({} dBm)".format(
609                rsrp, power))
610
611        # Use parent method to calculate signal level
612        return super().calibrated_downlink_rx_power(bts_config, power)
613
614    def downlink_calibration(self, rat=None, power_units_conversion_func=None):
615        """ Computes downlink path loss and returns the calibration value.
616
617        See base class implementation for details.
618
619        Args:
620            rat: ignored, replaced by 'lteRsrp'
621            power_units_conversion_func: ignored, replaced by
622                self.rsrp_to_signal_power
623
624        Returns:
625            Downlink calibration value and measured DL power. Note that the
626            phone only reports RSRP of the primary chain
627        """
628
629        return super().downlink_calibration(
630            rat='lteDbm',
631            power_units_conversion_func=self.rsrp_to_signal_power)
632
633    def rsrp_to_signal_power(self, rsrp, bts_config):
634        """ Converts rsrp to total band signal power
635
636        RSRP is measured per subcarrier, so total band power needs to be
637        multiplied by the number of subcarriers being used.
638
639        Args:
640            rsrp: desired rsrp in dBm
641            bts_config: a base station configuration object
642        Returns:
643            Total band signal power in dBm
644        """
645
646        bandwidth = bts_config.bandwidth
647
648        if bandwidth == 100:  # This assumes 273 RBs. TODO: b/229163022
649            power = rsrp + 35.15
650        elif bandwidth == 20:  # 100 RBs
651            power = rsrp + 30.79
652        elif bandwidth == 15:  # 75 RBs
653            power = rsrp + 29.54
654        elif bandwidth == 10:  # 50 RBs
655            power = rsrp + 27.78
656        elif bandwidth == 5:  # 25 RBs
657            power = rsrp + 24.77
658        elif bandwidth == 3:  # 15 RBs
659            power = rsrp + 22.55
660        elif bandwidth == 1.4:  # 6 RBs
661            power = rsrp + 18.57
662        else:
663            raise ValueError("Invalid bandwidth value.")
664
665        return power
666
667    def maximum_downlink_throughput(self):
668        """ Calculates maximum achievable downlink throughput in the current
669            simulation state.
670
671        Returns:
672            Maximum throughput in mbps.
673
674        """
675        return sum(
676            self.bts_maximum_downlink_throughtput(self.cell_configs[bts_index])
677            for bts_index in range(self.num_carriers))
678
679    def bts_maximum_downlink_throughtput(self, bts_config):
680        """ Calculates maximum achievable downlink throughput for a single
681        base station from its configuration object.
682
683        Args:
684            bts_config: a base station configuration object.
685
686        Returns:
687            Maximum throughput in mbps.
688
689        """
690        if bts_config.mimo_mode == MimoMode.MIMO_1x1:
691            streams = 1
692        elif bts_config.mimo_mode == MimoMode.MIMO_2x2:
693            streams = 2
694        elif bts_config.mimo_mode == MimoMode.MIMO_4x4:
695            streams = 4
696        else:
697            raise ValueError('Unable to calculate maximum downlink throughput '
698                             'because the MIMO mode has not been set.')
699
700        bandwidth = bts_config.bandwidth
701        rb_ratio = bts_config.dl_rbs / TOTAL_RBS_DICTIONARY[bandwidth]
702        mcs = bts_config.dl_mcs
703
704        max_rate_per_stream = None
705
706        tdd_subframe_config = bts_config.dlul_config
707        duplex_mode = bts_config.get_duplex_mode()
708
709        if duplex_mode == DuplexMode.TDD:
710            if bts_config.dl_256_qam_enabled:
711                if mcs == 27:
712                    if bts_config.mac_padding:
713                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
714                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
715                                'DL']
716                    else:
717                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
718                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
719                                'DL']
720            else:
721                if mcs == 28:
722                    if bts_config.mac_padding:
723                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
724                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
725                                'DL']
726                    else:
727                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
728                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
729                                'DL']
730
731        elif duplex_mode == DuplexMode.FDD:
732            if (not bts_config.dl_256_qam_enabled and bts_config.mac_padding
733                    and mcs == 28):
734                max_rate_per_stream = {
735                    3: 9.96,
736                    5: 17.0,
737                    10: 34.7,
738                    15: 52.7,
739                    20: 72.2
740                }.get(bandwidth, None)
741            if (not bts_config.dl_256_qam_enabled and bts_config.mac_padding
742                    and mcs == 27):
743                max_rate_per_stream = {
744                    1.4: 2.94,
745                }.get(bandwidth, None)
746            elif (not bts_config.dl_256_qam_enabled
747                  and not bts_config.mac_padding and mcs == 27):
748                max_rate_per_stream = {
749                    1.4: 2.87,
750                    3: 7.7,
751                    5: 14.4,
752                    10: 28.7,
753                    15: 42.3,
754                    20: 57.7
755                }.get(bandwidth, None)
756            elif bts_config.dl_256_qam_enabled and bts_config.mac_padding and mcs == 27:
757                max_rate_per_stream = {
758                    3: 13.2,
759                    5: 22.9,
760                    10: 46.3,
761                    15: 72.2,
762                    20: 93.9
763                }.get(bandwidth, None)
764            elif bts_config.dl_256_qam_enabled and bts_config.mac_padding and mcs == 26:
765                max_rate_per_stream = {
766                    1.4: 3.96,
767                }.get(bandwidth, None)
768            elif (bts_config.dl_256_qam_enabled and not bts_config.mac_padding
769                  and mcs == 27):
770                max_rate_per_stream = {
771                    3: 11.3,
772                    5: 19.8,
773                    10: 44.1,
774                    15: 68.1,
775                    20: 88.4
776                }.get(bandwidth, None)
777            elif (bts_config.dl_256_qam_enabled and not bts_config.mac_padding
778                  and mcs == 26):
779                max_rate_per_stream = {
780                    1.4: 3.96,
781                }.get(bandwidth, None)
782
783        if not max_rate_per_stream:
784            raise NotImplementedError(
785                "The calculation for MAC padding = {} "
786                "and mcs = {} is not implemented.".format(
787                    "FULLALLOCATION" if bts_config.mac_padding else "OFF",
788                    mcs))
789
790        return max_rate_per_stream * streams * rb_ratio
791
792    def maximum_uplink_throughput(self):
793        """ Calculates maximum achievable uplink throughput in the current
794            simulation state.
795
796        Returns:
797            Maximum throughput in mbps.
798
799        """
800
801        return self.bts_maximum_uplink_throughtput(self.cell_configs[0])
802
803    def bts_maximum_uplink_throughtput(self, bts_config):
804        """ Calculates maximum achievable uplink throughput for the selected
805        basestation from its configuration object.
806
807        Args:
808            bts_config: an LTE base station configuration object.
809
810        Returns:
811            Maximum throughput in mbps.
812
813        """
814
815        bandwidth = bts_config.bandwidth
816        rb_ratio = bts_config.ul_rbs / TOTAL_RBS_DICTIONARY[bandwidth]
817        mcs = bts_config.ul_mcs
818
819        max_rate_per_stream = None
820
821        tdd_subframe_config = bts_config.dlul_config
822        duplex_mode = bts_config.get_duplex_mode()
823
824        if duplex_mode == DuplexMode.TDD:
825            if bts_config.ul_64_qam_enabled:
826                if mcs == 28:
827                    if bts_config.mac_padding:
828                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
829                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
830                                'UL']
831                    else:
832                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
833                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
834                                'UL']
835            else:
836                if mcs == 23:
837                    if bts_config.mac_padding:
838                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
839                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
840                                'UL']
841                    else:
842                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
843                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
844                                'UL']
845
846        elif duplex_mode == DuplexMode.FDD:
847            if mcs == 23 and not bts_config.ul_64_qam_enabled:
848                max_rate_per_stream = {
849                    1.4: 2.85,
850                    3: 7.18,
851                    5: 12.1,
852                    10: 24.5,
853                    15: 36.5,
854                    20: 49.1
855                }.get(bandwidth, None)
856            elif mcs == 28 and bts_config.ul_64_qam_enabled:
857                max_rate_per_stream = {
858                    1.4: 4.2,
859                    3: 10.5,
860                    5: 17.2,
861                    10: 35.3,
862                    15: 53.0,
863                    20: 72.6
864                }.get(bandwidth, None)
865
866        if not max_rate_per_stream:
867            raise NotImplementedError(
868                "The calculation fir mcs = {} is not implemented.".format(
869                    "FULLALLOCATION" if bts_config.mac_padding else "OFF",
870                    mcs))
871
872        return max_rate_per_stream * rb_ratio
873
874    def calibrate(self, band):
875        """ Calculates UL and DL path loss if it wasn't done before
876
877        Before running the base class implementation, configure the base station
878        to only use one downlink antenna with maximum bandwidth.
879
880        Args:
881            band: the band that is currently being calibrated.
882        """
883
884        # Save initial values in a configuration object so they can be restored
885        restore_config = LteCellConfig(self.log)
886        restore_config.mimo_mode = self.cell_configs[0].mimo_mode
887        restore_config.transmission_mode = \
888            self.cell_configs[0].transmission_mode
889        restore_config.bandwidth = self.cell_configs[0].bandwidth
890
891        # Set up a temporary calibration configuration.
892        temporary_config = LteCellConfig(self.log)
893        temporary_config.mimo_mode = MimoMode.MIMO_1x1
894        temporary_config.transmission_mode = TransmissionMode.TM1
895        temporary_config.bandwidth = max(
896            self.allowed_bandwidth_dictionary[int(band)])
897        self.simulator.configure_bts(temporary_config)
898        self.cell_configs[0].incorporate(temporary_config)
899
900        super().calibrate(band)
901
902        # Restore values as they were before changing them for calibration.
903        self.simulator.configure_bts(restore_config)
904        self.cell_configs[0].incorporate(restore_config)
905
906    def start_traffic_for_calibration(self):
907        """ If MAC padding is enabled, there is no need to start IP traffic. """
908        if not self.cell_configs[0].mac_padding:
909            super().start_traffic_for_calibration()
910
911    def stop_traffic_for_calibration(self):
912        """ If MAC padding is enabled, IP traffic wasn't started. """
913        if not self.cell_configs[0].mac_padding:
914            super().stop_traffic_for_calibration()
915
916    def get_measured_ul_power(self, samples=5, wait_after_sample=3):
917        """ Calculates UL power using measurements from the callbox and the
918        calibration data.
919
920        Args:
921            samples: the numble of samples to average
922            wait_after_sample: time in seconds to wait in between samples
923
924        Returns:
925            the ul power at the UE antenna ports in dBs
926        """
927        ul_power_sum = 0
928        samples_left = samples
929
930        while samples_left > 0:
931            ul_power_sum += self.simulator.get_measured_pusch_power()
932            samples_left -= 1
933            time.sleep(wait_after_sample)
934
935        # Got enough samples, return calibrated average
936        if self.dl_path_loss:
937            return ul_power_sum / samples + self.ul_path_loss
938        else:
939            self.log.warning('No uplink calibration data. Returning '
940                             'uncalibrated values as measured by the '
941                             'callbox.')
942            return ul_power_sum / samples
943
944    def start(self):
945        """ Set the signal level for the secondary carriers, as the base class
946        implementation of this method will only set up downlink power for the
947        primary carrier component.
948
949        After that, attaches the secondary carriers."""
950
951        super().start()
952
953        if self.num_carriers > 1:
954            if self.sim_dl_power:
955                self.log.info('Setting DL power for secondary carriers.')
956
957                for bts_index in range(1, self.num_carriers):
958                    new_config = LteCellConfig(self.log)
959                    new_config.output_power = self.calibrated_downlink_rx_power(
960                        self.cell_configs[bts_index], self.sim_dl_power)
961                    self.simulator.configure_bts(new_config, bts_index)
962                    self.cell_configs[bts_index].incorporate(new_config)
963
964            self.simulator.lte_attach_secondary_carriers(self.freq_bands)
965
966        # For some simulator, configuration is needed after simulation starts
967        self.configure_after_started()
968
969    def send_sms(self, message):
970        """ Sends an SMS message to the DUT.
971
972        Args:
973            message: the SMS message to send.
974        """
975        self.simulator.send_sms(message)
976