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.
16
17import math
18from enum import Enum
19
20from acts.test_utils.power.tel_simulations.BaseSimulation import BaseSimulation
21from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY
22
23
24class TransmissionMode(Enum):
25    """ Transmission modes for LTE (e.g., TM1, TM4, ...) """
26    TM1 = "TM1"
27    TM2 = "TM2"
28    TM3 = "TM3"
29    TM4 = "TM4"
30    TM7 = "TM7"
31    TM8 = "TM8"
32    TM9 = "TM9"
33
34
35class MimoMode(Enum):
36    """ Mimo modes """
37    MIMO_1x1 = "1x1"
38    MIMO_2x2 = "2x2"
39    MIMO_4x4 = "4x4"
40
41
42class SchedulingMode(Enum):
43    """ Traffic scheduling modes (e.g., STATIC, DYNAMIC) """
44    DYNAMIC = "DYNAMIC"
45    STATIC = "STATIC"
46
47
48class DuplexMode(Enum):
49    """ DL/UL Duplex mode """
50    FDD = "FDD"
51    TDD = "TDD"
52
53
54class ModulationType(Enum):
55    """DL/UL Modulation order."""
56    QPSK = 'QPSK'
57    Q16 = '16QAM'
58    Q64 = '64QAM'
59    Q256 = '256QAM'
60
61
62class LteSimulation(BaseSimulation):
63    """ Single-carrier LTE simulation. """
64
65    # Simulation config keywords contained in the test name
66    PARAM_FRAME_CONFIG = "tddconfig"
67    PARAM_BW = "bw"
68    PARAM_SCHEDULING = "scheduling"
69    PARAM_SCHEDULING_STATIC = "static"
70    PARAM_SCHEDULING_DYNAMIC = "dynamic"
71    PARAM_PATTERN = "pattern"
72    PARAM_TM = "tm"
73    PARAM_UL_PW = 'pul'
74    PARAM_DL_PW = 'pdl'
75    PARAM_BAND = "band"
76    PARAM_MIMO = "mimo"
77    PARAM_DL_MCS = 'dlmcs'
78    PARAM_UL_MCS = 'ulmcs'
79    PARAM_SSF = 'ssf'
80    PARAM_CFI = 'cfi'
81    PARAM_PAGING = 'paging'
82    PARAM_PHICH = 'phich'
83    PARAM_RRC_STATUS_CHANGE_TIMER = "rrcstatuschangetimer"
84
85    # Test config keywords
86    KEY_TBS_PATTERN = "tbs_pattern_on"
87    KEY_DL_256_QAM = "256_qam_dl"
88    KEY_UL_64_QAM = "64_qam_ul"
89
90    # Units in which signal level is defined in DOWNLINK_SIGNAL_LEVEL_DICTIONARY
91    DOWNLINK_SIGNAL_LEVEL_UNITS = "RSRP"
92
93    # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz.
94    # Excellent is set to -75 since callbox B Tx power is limited to -30 dBm
95    DOWNLINK_SIGNAL_LEVEL_DICTIONARY = {
96        'excellent': -75,
97        'high': -110,
98        'medium': -115,
99        'weak': -120
100    }
101
102    # Transmitted output power for the phone (dBm)
103    UPLINK_SIGNAL_LEVEL_DICTIONARY = {
104        'max': 27,
105        'high': 13,
106        'medium': 3,
107        'low': -20
108    }
109
110    # Bandwidth [MHz] to total RBs mapping
111    total_rbs_dictionary = {20: 100, 15: 75, 10: 50, 5: 25, 3: 15, 1.4: 6}
112
113    # Bandwidth [MHz] to RB group size
114    rbg_dictionary = {20: 4, 15: 4, 10: 3, 5: 2, 3: 2, 1.4: 1}
115
116    # Bandwidth [MHz] to minimum number of DL RBs that can be assigned to a UE
117    min_dl_rbs_dictionary = {20: 16, 15: 12, 10: 9, 5: 4, 3: 4, 1.4: 2}
118
119    # Bandwidth [MHz] to minimum number of UL RBs that can be assigned to a UE
120    min_ul_rbs_dictionary = {20: 8, 15: 6, 10: 4, 5: 2, 3: 2, 1.4: 1}
121
122    # Allowed bandwidth for each band.
123    allowed_bandwidth_dictionary = {
124        1: [5, 10, 15, 20],
125        2: [1.4, 3, 5, 10, 15, 20],
126        3: [1.4, 3, 5, 10, 15, 20],
127        4: [1.4, 3, 5, 10, 15, 20],
128        5: [1.4, 3, 5, 10],
129        7: [5, 10, 15, 20],
130        8: [1.4, 3, 5, 10],
131        10: [5, 10, 15, 20],
132        11: [5, 10],
133        12: [1.4, 3, 5, 10],
134        13: [5, 10],
135        14: [5, 10],
136        17: [5, 10],
137        18: [5, 10, 15],
138        19: [5, 10, 15],
139        20: [5, 10, 15, 20],
140        21: [5, 10, 15],
141        22: [5, 10, 15, 20],
142        24: [5, 10],
143        25: [1.4, 3, 5, 10, 15, 20],
144        26: [1.4, 3, 5, 10, 15],
145        27: [1.4, 3, 5, 10],
146        28: [3, 5, 10, 15, 20],
147        29: [3, 5, 10],
148        30: [5, 10],
149        31: [1.4, 3, 5],
150        32: [5, 10, 15, 20],
151        33: [5, 10, 15, 20],
152        34: [5, 10, 15],
153        35: [1.4, 3, 5, 10, 15, 20],
154        36: [1.4, 3, 5, 10, 15, 20],
155        37: [5, 10, 15, 20],
156        38: [20],
157        39: [5, 10, 15, 20],
158        40: [5, 10, 15, 20],
159        41: [5, 10, 15, 20],
160        42: [5, 10, 15, 20],
161        43: [5, 10, 15, 20],
162        44: [3, 5, 10, 15, 20],
163        45: [5, 10, 15, 20],
164        46: [10, 20],
165        47: [10, 20],
166        48: [5, 10, 15, 20],
167        49: [10, 20],
168        50: [3, 5, 10, 15, 20],
169        51: [3, 5],
170        52: [5, 10, 15, 20],
171        65: [5, 10, 15, 20],
172        66: [1.4, 3, 5, 10, 15, 20],
173        67: [5, 10, 15, 20],
174        68: [5, 10, 15],
175        69: [5],
176        70: [5, 10, 15],
177        71: [5, 10, 15, 20],
178        72: [1.4, 3, 5],
179        73: [1.4, 3, 5],
180        74: [1.4, 3, 5, 10, 15, 20],
181        75: [5, 10, 15, 20],
182        76: [5],
183        85: [5, 10],
184        252: [20],
185        255: [20]
186    }
187
188    # Peak throughput lookup tables for each TDD subframe
189    # configuration and bandwidth
190    # yapf: disable
191    tdd_config4_tput_lut = {
192        0: {
193            5: {'DL': 3.82, 'UL': 2.63},
194            10: {'DL': 11.31,'UL': 9.03},
195            15: {'DL': 16.9, 'UL': 20.62},
196            20: {'DL': 22.88, 'UL': 28.43}
197        },
198        1: {
199            5: {'DL': 6.13, 'UL': 4.08},
200            10: {'DL': 18.36, 'UL': 9.69},
201            15: {'DL': 28.62, 'UL': 14.21},
202            20: {'DL': 39.04, 'UL': 19.23}
203        },
204        2: {
205            5: {'DL': 5.68, 'UL': 2.30},
206            10: {'DL': 25.51, 'UL': 4.68},
207            15: {'DL': 39.3, 'UL': 7.13},
208            20: {'DL': 53.64, 'UL': 9.72}
209        },
210        3: {
211            5: {'DL': 8.26, 'UL': 3.45},
212            10: {'DL': 23.20, 'UL': 6.99},
213            15: {'DL': 35.35, 'UL': 10.75},
214            20: {'DL': 48.3, 'UL': 14.6}
215        },
216        4: {
217            5: {'DL': 6.16, 'UL': 2.30},
218            10: {'DL': 26.77, 'UL': 4.68},
219            15: {'DL': 40.7, 'UL': 7.18},
220            20: {'DL': 55.6, 'UL': 9.73}
221        },
222        5: {
223            5: {'DL': 6.91, 'UL': 1.12},
224            10: {'DL': 30.33, 'UL': 2.33},
225            15: {'DL': 46.04, 'UL': 3.54},
226            20: {'DL': 62.9, 'UL': 4.83}
227        },
228        6: {
229            5: {'DL': 6.13, 'UL': 4.13},
230            10: {'DL': 14.79, 'UL': 11.98},
231            15: {'DL': 23.28, 'UL': 17.46},
232            20: {'DL': 31.75, 'UL': 23.95}
233        }
234    }
235
236    tdd_config3_tput_lut = {
237        0: {
238            5: {'DL': 5.04, 'UL': 3.7},
239            10: {'DL': 15.11, 'UL': 17.56},
240            15: {'DL': 22.59, 'UL': 30.31},
241            20: {'DL': 30.41, 'UL': 41.61}
242        },
243        1: {
244            5: {'DL': 8.07, 'UL': 5.66},
245            10: {'DL': 24.58, 'UL': 13.66},
246            15: {'DL': 39.05, 'UL': 20.68},
247            20: {'DL': 51.59, 'UL': 28.76}
248        },
249        2: {
250            5: {'DL': 7.59, 'UL': 3.31},
251            10: {'DL': 34.08, 'UL': 6.93},
252            15: {'DL': 53.64, 'UL': 10.51},
253            20: {'DL': 70.55, 'UL': 14.41}
254        },
255        3: {
256            5: {'DL': 10.9, 'UL': 5.0},
257            10: {'DL': 30.99, 'UL': 10.25},
258            15: {'DL': 48.3, 'UL': 15.81},
259            20: {'DL': 63.24, 'UL': 21.65}
260        },
261        4: {
262            5: {'DL': 8.11, 'UL': 3.32},
263            10: {'DL': 35.74, 'UL': 6.95},
264            15: {'DL': 55.6, 'UL': 10.51},
265            20: {'DL': 72.72, 'UL': 14.41}
266        },
267        5: {
268            5: {'DL': 9.28, 'UL': 1.57},
269            10: {'DL': 40.49, 'UL': 3.44},
270            15: {'DL': 62.9, 'UL': 5.23},
271            20: {'DL': 82.21, 'UL': 7.15}
272        },
273        6: {
274            5: {'DL': 8.06, 'UL': 5.74},
275            10: {'DL': 19.82, 'UL': 17.51},
276            15: {'DL': 31.75, 'UL': 25.77},
277            20: {'DL': 42.12, 'UL': 34.91}
278        }
279    }
280
281    tdd_config2_tput_lut = {
282        0: {
283            5: {'DL': 3.11, 'UL': 2.55},
284            10: {'DL': 9.93, 'UL': 11.1},
285            15: {'DL': 13.9, 'UL': 21.51},
286            20: {'DL': 20.02, 'UL': 41.66}
287        },
288        1: {
289            5: {'DL': 5.33, 'UL': 4.27},
290            10: {'DL': 15.14, 'UL': 13.95},
291            15: {'DL': 33.84, 'UL': 19.73},
292            20: {'DL': 44.61, 'UL': 27.35}
293        },
294        2: {
295            5: {'DL': 6.87, 'UL': 3.32},
296            10: {'DL': 17.06, 'UL': 6.76},
297            15: {'DL': 49.63, 'UL': 10.5},
298            20: {'DL': 65.2, 'UL': 14.41}
299        },
300        3: {
301            5: {'DL': 5.41, 'UL': 4.17},
302            10: {'DL': 16.89, 'UL': 9.73},
303            15: {'DL': 44.29, 'UL': 15.7},
304            20: {'DL': 53.95, 'UL': 19.85}
305        },
306        4: {
307            5: {'DL': 8.7, 'UL': 3.32},
308            10: {'DL': 17.58, 'UL': 6.76},
309            15: {'DL': 51.08, 'UL': 10.47},
310            20: {'DL': 66.45, 'UL': 14.38}
311        },
312        5: {
313            5: {'DL': 9.46, 'UL': 1.55},
314            10: {'DL': 19.02, 'UL': 3.48},
315            15: {'DL': 58.89, 'UL': 5.23},
316            20: {'DL': 76.85, 'UL': 7.1}
317        },
318        6: {
319            5: {'DL': 4.74, 'UL': 3.9},
320            10: {'DL': 12.32, 'UL': 13.37},
321            15: {'DL': 27.74, 'UL': 25.02},
322            20: {'DL': 35.48, 'UL': 32.95}
323        }
324    }
325
326    tdd_config1_tput_lut = {
327        0: {
328            5: {'DL': 4.25, 'UL': 3.35},
329            10: {'DL': 8.38, 'UL': 7.22},
330            15: {'DL': 12.41, 'UL': 13.91},
331            20: {'DL': 16.27, 'UL': 24.09}
332        },
333        1: {
334            5: {'DL': 7.28, 'UL': 4.61},
335            10: {'DL': 14.73, 'UL': 9.69},
336            15: {'DL': 21.91, 'UL': 13.86},
337            20: {'DL': 27.63, 'UL': 17.18}
338        },
339        2: {
340            5: {'DL': 10.37, 'UL': 2.27},
341            10: {'DL': 20.92, 'UL': 4.66},
342            15: {'DL': 31.01, 'UL': 7.04},
343            20: {'DL': 42.03, 'UL': 9.75}
344        },
345        3: {
346            5: {'DL': 9.25, 'UL': 3.44},
347            10: {'DL': 18.38, 'UL': 6.95},
348            15: {'DL': 27.59, 'UL': 10.62},
349            20: {'DL': 34.85, 'UL': 13.45}
350        },
351        4: {
352            5: {'DL': 10.71, 'UL': 2.26},
353            10: {'DL': 21.54, 'UL': 4.67},
354            15: {'DL': 31.91, 'UL': 7.2},
355            20: {'DL': 43.35, 'UL': 9.74}
356        },
357        5: {
358            5: {'DL': 12.34, 'UL': 1.08},
359            10: {'DL': 24.78, 'UL': 2.34},
360            15: {'DL': 36.68, 'UL': 3.57},
361            20: {'DL': 49.84, 'UL': 4.81}
362        },
363        6: {
364            5: {'DL': 5.76, 'UL': 4.41},
365            10: {'DL': 11.68, 'UL': 9.7},
366            15: {'DL': 17.34, 'UL': 17.95},
367            20: {'DL': 23.5, 'UL': 23.42}
368        }
369    }
370    # yapf: enable
371
372    # Peak throughput lookup table dictionary
373    tdd_config_tput_lut_dict = {
374        'TDD_CONFIG1':
375        tdd_config1_tput_lut,  # DL 256QAM, UL 64QAM & TBS turned OFF
376        'TDD_CONFIG2':
377        tdd_config2_tput_lut,  # DL 256QAM, UL 64 QAM turned ON & TBS OFF
378        'TDD_CONFIG3':
379        tdd_config3_tput_lut,  # DL 256QAM, UL 64QAM & TBS turned ON
380        'TDD_CONFIG4':
381        tdd_config4_tput_lut  # DL 256QAM, UL 64 QAM turned OFF & TBS ON
382    }
383
384    class BtsConfig(BaseSimulation.BtsConfig):
385        """ Extension of the BaseBtsConfig to implement parameters that are
386         exclusive to LTE.
387
388        Attributes:
389            band: an integer indicating the required band number.
390            dlul_config: an integer indicating the TDD config number.
391            ssf_config: an integer indicating the Special Sub-Frame config.
392            bandwidth: a float indicating the required channel bandwidth.
393            mimo_mode: an instance of LteSimulation.MimoMode indicating the
394                required MIMO mode for the downlink signal.
395            transmission_mode: an instance of LteSimulation.TransmissionMode
396                indicating the required TM.
397            scheduling_mode: an instance of LteSimulation.SchedulingMode
398                indicating wether to use Static or Dynamic scheduling.
399            dl_rbs: an integer indicating the number of downlink RBs
400            ul_rbs: an integer indicating the number of uplink RBs
401            dl_mcs: an integer indicating the MCS for the downlink signal
402            ul_mcs: an integer indicating the MCS for the uplink signal
403            dl_modulation_order: a string indicating a DL modulation scheme
404            ul_modulation_order: a string indicating an UL modulation scheme
405            tbs_pattern_on: a boolean indicating whether full allocation mode
406                should be used or not
407            dl_channel: an integer indicating the downlink channel number
408            cfi: an integer indicating the Control Format Indicator
409            paging_cycle: an integer indicating the paging cycle duration in
410                milliseconds
411            phich: a string indicating the PHICH group size parameter
412        """
413        def __init__(self):
414            """ Initialize the base station config by setting all its
415            parameters to None. """
416            super().__init__()
417            self.band = None
418            self.dlul_config = None
419            self.ssf_config = None
420            self.bandwidth = None
421            self.mimo_mode = None
422            self.transmission_mode = None
423            self.scheduling_mode = None
424            self.dl_rbs = None
425            self.ul_rbs = None
426            self.dl_mcs = None
427            self.ul_mcs = None
428            self.dl_modulation_order = None
429            self.ul_modulation_order = None
430            self.tbs_pattern_on = None
431            self.dl_channel = None
432            self.dl_cc_enabled = None
433            self.cfi = None
434            self.paging_cycle = None
435            self.phich = None
436
437    def __init__(self, simulator, log, dut, test_config, calibration_table):
438        """ Initializes the simulator for a single-carrier LTE simulation.
439
440        Loads a simple LTE simulation enviroment with 1 basestation.
441
442        Args:
443            simulator: a cellular simulator controller
444            log: a logger handle
445            dut: the android device handler
446            test_config: test configuration obtained from the config file
447            calibration_table: a dictionary containing path losses for
448                different bands.
449
450        """
451
452        super().__init__(simulator, log, dut, test_config, calibration_table)
453
454        if not dut.droid.telephonySetPreferredNetworkTypesForSubscription(
455                NETWORK_MODE_LTE_ONLY,
456                dut.droid.subscriptionGetDefaultSubId()):
457            log.error("Couldn't set preferred network type.")
458        else:
459            log.info("Preferred network type set.")
460
461        # Get TBS pattern setting from the test configuration
462        if self.KEY_TBS_PATTERN not in test_config:
463            self.log.warning("The key '{}' is not set in the config file. "
464                             "Setting to true by default.".format(
465                                 self.KEY_TBS_PATTERN))
466        self.primary_config.tbs_pattern_on = test_config.get(
467            self.KEY_TBS_PATTERN, True)
468
469        # Get the 256-QAM setting from the test configuration
470        if self.KEY_DL_256_QAM not in test_config:
471            self.log.warning("The key '{}' is not set in the config file. "
472                             "Setting to false by default.".format(
473                                 self.KEY_DL_256_QAM))
474
475        self.dl_256_qam = test_config.get(self.KEY_DL_256_QAM, False)
476
477        if self.dl_256_qam:
478            if not self.simulator.LTE_SUPPORTS_DL_256QAM:
479                self.log.warning("The key '{}' is set to true but the "
480                                 "simulator doesn't support that modulation "
481                                 "order.".format(self.KEY_DL_256_QAM))
482                self.dl_256_qam = False
483            else:
484                self.primary_config.dl_modulation_order = ModulationType.Q256
485
486        else:
487            self.log.warning('dl modulation 256QAM is not specified in config, '
488                             'setting to default value 64QAM')
489            self.primary_config.dl_modulation_order = ModulationType.Q64
490        # Get the 64-QAM setting from the test configuration
491        if self.KEY_UL_64_QAM not in test_config:
492            self.log.warning("The key '{}' is not set in the config file. "
493                             "Setting to false by default.".format(
494                                 self.KEY_UL_64_QAM))
495
496        self.ul_64_qam = test_config.get(self.KEY_UL_64_QAM, False)
497
498        if self.ul_64_qam:
499            if not self.simulator.LTE_SUPPORTS_UL_64QAM:
500                self.log.warning("The key '{}' is set to true but the "
501                                 "simulator doesn't support that modulation "
502                                 "order.".format(self.KEY_UL_64_QAM))
503                self.ul_64_qam = False
504            else:
505                self.primary_config.ul_modulation_order = ModulationType.Q64
506        else:
507            self.log.warning('ul modulation 64QAM is not specified in config, '
508                             'setting to default value 16QAM')
509            self.primary_config.ul_modulation_order = ModulationType.Q16
510
511        self.simulator.configure_bts(self.primary_config)
512
513    def setup_simulator(self):
514        """ Do initial configuration in the simulator. """
515        self.simulator.setup_lte_scenario()
516
517    def parse_parameters(self, parameters):
518        """ Configs an LTE simulation using a list of parameters.
519
520        Calls the parent method first, then consumes parameters specific to LTE.
521
522        Args:
523            parameters: list of parameters
524        """
525
526        # Instantiate a new configuration object
527        new_config = self.BtsConfig()
528
529        # Setup band
530
531        values = self.consume_parameter(parameters, self.PARAM_BAND, 1)
532
533        if not values:
534            raise ValueError(
535                "The test name needs to include parameter '{}' followed by "
536                "the required band number.".format(self.PARAM_BAND))
537
538        new_config.band = values[1]
539
540        # Set TDD-only configs
541        if self.get_duplex_mode(new_config.band) == DuplexMode.TDD:
542
543            # Sub-frame DL/UL config
544            values = self.consume_parameter(parameters,
545                                            self.PARAM_FRAME_CONFIG, 1)
546            if not values:
547                raise ValueError(
548                    "When a TDD band is selected the frame "
549                    "structure has to be indicated with the '{}' "
550                    "parameter followed by a number from 0 to 6.".format(
551                        self.PARAM_FRAME_CONFIG))
552
553            new_config.dlul_config = int(values[1])
554
555            # Special Sub-Frame configuration
556            values = self.consume_parameter(parameters, self.PARAM_SSF, 1)
557
558            if not values:
559                self.log.warning(
560                    'The {} parameter was not provided. Setting '
561                    'Special Sub-Frame config to 6 by default.'.format(
562                        self.PARAM_SSF))
563                new_config.ssf_config = 6
564            else:
565                new_config.ssf_config = int(values[1])
566
567        # Setup bandwidth
568
569        values = self.consume_parameter(parameters, self.PARAM_BW, 1)
570
571        if not values:
572            raise ValueError(
573                "The test name needs to include parameter {} followed by an "
574                "int value (to indicate 1.4 MHz use 14).".format(
575                    self.PARAM_BW))
576
577        bw = float(values[1])
578
579        if bw == 14:
580            bw = 1.4
581
582        new_config.bandwidth = bw
583
584        # Setup mimo mode
585
586        values = self.consume_parameter(parameters, self.PARAM_MIMO, 1)
587
588        if not values:
589            raise ValueError(
590                "The test name needs to include parameter '{}' followed by the "
591                "mimo mode.".format(self.PARAM_MIMO))
592
593        for mimo_mode in MimoMode:
594            if values[1] == mimo_mode.value:
595                new_config.mimo_mode = mimo_mode
596                break
597        else:
598            raise ValueError("The {} parameter needs to be followed by either "
599                             "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO))
600
601        if (new_config.mimo_mode == MimoMode.MIMO_4x4
602                and not self.simulator.LTE_SUPPORTS_4X4_MIMO):
603            raise ValueError("The test requires 4x4 MIMO, but that is not "
604                             "supported by the cellular simulator.")
605
606        # Setup transmission mode
607
608        values = self.consume_parameter(parameters, self.PARAM_TM, 1)
609
610        if not values:
611            raise ValueError(
612                "The test name needs to include parameter {} followed by an "
613                "int value from 1 to 4 indicating transmission mode.".format(
614                    self.PARAM_TM))
615
616        for tm in TransmissionMode:
617            if values[1] == tm.value[2:]:
618                new_config.transmission_mode = tm
619                break
620        else:
621            raise ValueError("The {} parameter needs to be followed by either "
622                             "TM1, TM2, TM3, TM4, TM7, TM8 or TM9.".format(
623                                 self.PARAM_MIMO))
624
625        # Setup scheduling mode
626
627        values = self.consume_parameter(parameters, self.PARAM_SCHEDULING, 1)
628
629        if not values:
630            new_config.scheduling_mode = SchedulingMode.STATIC
631            self.log.warning(
632                "The test name does not include the '{}' parameter. Setting to "
633                "static by default.".format(self.PARAM_SCHEDULING))
634        elif values[1] == self.PARAM_SCHEDULING_DYNAMIC:
635            new_config.scheduling_mode = SchedulingMode.DYNAMIC
636        elif values[1] == self.PARAM_SCHEDULING_STATIC:
637            new_config.scheduling_mode = SchedulingMode.STATIC
638        else:
639            raise ValueError(
640                "The test name parameter '{}' has to be followed by either "
641                "'dynamic' or 'static'.".format(self.PARAM_SCHEDULING))
642
643        if new_config.scheduling_mode == SchedulingMode.STATIC:
644
645            values = self.consume_parameter(parameters, self.PARAM_PATTERN, 2)
646
647            if not values:
648                self.log.warning(
649                    "The '{}' parameter was not set, using 100% RBs for both "
650                    "DL and UL. To set the percentages of total RBs include "
651                    "the '{}' parameter followed by two ints separated by an "
652                    "underscore indicating downlink and uplink percentages.".
653                    format(self.PARAM_PATTERN, self.PARAM_PATTERN))
654                dl_pattern = 100
655                ul_pattern = 100
656            else:
657                dl_pattern = int(values[1])
658                ul_pattern = int(values[2])
659
660            if not (0 <= dl_pattern <= 100 and 0 <= ul_pattern <= 100):
661                raise ValueError(
662                    "The scheduling pattern parameters need to be two "
663                    "positive numbers between 0 and 100.")
664
665            new_config.dl_rbs, new_config.ul_rbs = (
666                self.allocation_percentages_to_rbs(
667                    new_config.bandwidth, new_config.transmission_mode,
668                    dl_pattern, ul_pattern))
669
670            # Look for a DL MCS configuration in the test parameters. If it is
671            # not present, use a default value.
672            dlmcs = self.consume_parameter(parameters, self.PARAM_DL_MCS, 1)
673
674            if dlmcs:
675                new_config.dl_mcs = int(dlmcs[1])
676            else:
677                self.log.warning(
678                    'The test name does not include the {} parameter. Setting '
679                    'to the max value by default'.format(self.PARAM_DL_MCS))
680                if self.dl_256_qam and new_config.bandwidth == 1.4:
681                    new_config.dl_mcs = 26
682                elif (not self.dl_256_qam
683                      and self.primary_config.tbs_pattern_on
684                      and new_config.bandwidth != 1.4):
685                    new_config.dl_mcs = 28
686                else:
687                    new_config.dl_mcs = 27
688
689            # Look for an UL MCS configuration in the test parameters. If it is
690            # not present, use a default value.
691            ulmcs = self.consume_parameter(parameters, self.PARAM_UL_MCS, 1)
692
693            if ulmcs:
694                new_config.ul_mcs = int(ulmcs[1])
695            else:
696                self.log.warning(
697                    'The test name does not include the {} parameter. Setting '
698                    'to the max value by default'.format(self.PARAM_UL_MCS))
699                if self.ul_64_qam:
700                    new_config.ul_mcs = 28
701                else:
702                    new_config.ul_mcs = 23
703
704        # Setup LTE RRC status change function and timer for LTE idle test case
705        values = self.consume_parameter(parameters,
706                                        self.PARAM_RRC_STATUS_CHANGE_TIMER, 1)
707        if not values:
708            self.log.info(
709                "The test name does not include the '{}' parameter. Disabled "
710                "by default.".format(self.PARAM_RRC_STATUS_CHANGE_TIMER))
711            self.simulator.set_lte_rrc_state_change_timer(False)
712        else:
713            timer = int(values[1])
714            self.simulator.set_lte_rrc_state_change_timer(True, timer)
715            self.rrc_sc_timer = timer
716
717        # Channel Control Indicator
718        values = self.consume_parameter(parameters, self.PARAM_CFI, 1)
719
720        if not values:
721            self.log.warning('The {} parameter was not provided. Setting '
722                             'CFI to BESTEFFORT.'.format(self.PARAM_CFI))
723            new_config.cfi = 'BESTEFFORT'
724        else:
725            new_config.cfi = values[1]
726
727        # PHICH group size
728        values = self.consume_parameter(parameters, self.PARAM_PHICH, 1)
729
730        if not values:
731            self.log.warning('The {} parameter was not provided. Setting '
732                             'PHICH group size to 1 by default.'.format(
733                                 self.PARAM_PHICH))
734            new_config.phich = '1'
735        else:
736            if values[1] == '16':
737                new_config.phich = '1/6'
738            elif values[1] == '12':
739                new_config.phich = '1/2'
740            elif values[1] in ['1/6', '1/2', '1', '2']:
741                new_config.phich = values[1]
742            else:
743                raise ValueError('The {} parameter can only be followed by 1,'
744                                 '2, 1/2 (or 12) and 1/6 (or 16).'.format(
745                                     self.PARAM_PHICH))
746
747        # Paging cycle duration
748        values = self.consume_parameter(parameters, self.PARAM_PAGING, 1)
749
750        if not values:
751            self.log.warning('The {} parameter was not provided. Setting '
752                             'paging cycle duration to 1280 ms by '
753                             'default.'.format(self.PARAM_PAGING))
754            new_config.paging_cycle = 1280
755        else:
756            try:
757                new_config.paging_cycle = int(values[1])
758            except ValueError:
759                raise ValueError(
760                    'The {} parameter has to be followed by the paging cycle '
761                    'duration in milliseconds.'.format(self.PARAM_PAGING))
762
763        # Get uplink power
764
765        ul_power = self.get_uplink_power_from_parameters(parameters)
766
767        # Power is not set on the callbox until after the simulation is
768        # started. Saving this value in a variable for later
769        self.sim_ul_power = ul_power
770
771        # Get downlink power
772
773        dl_power = self.get_downlink_power_from_parameters(parameters)
774
775        # Power is not set on the callbox until after the simulation is
776        # started. Saving this value in a variable for later
777        self.sim_dl_power = dl_power
778
779        # Setup the base station with the obtained configuration and then save
780        # these parameters in the current configuration object
781        self.simulator.configure_bts(new_config)
782        self.primary_config.incorporate(new_config)
783
784        # Now that the band is set, calibrate the link if necessary
785        self.load_pathloss_if_required()
786
787    def calibrated_downlink_rx_power(self, bts_config, rsrp):
788        """ LTE simulation overrides this method so that it can convert from
789        RSRP to total signal power transmitted from the basestation.
790
791        Args:
792            bts_config: the current configuration at the base station
793            rsrp: desired rsrp, contained in a key value pair
794        """
795
796        power = self.rsrp_to_signal_power(rsrp, bts_config)
797
798        self.log.info(
799            "Setting downlink signal level to {} RSRP ({} dBm)".format(
800                rsrp, power))
801
802        # Use parent method to calculate signal level
803        return super().calibrated_downlink_rx_power(bts_config, power)
804
805    def downlink_calibration(self, rat=None, power_units_conversion_func=None):
806        """ Computes downlink path loss and returns the calibration value.
807
808        See base class implementation for details.
809
810        Args:
811            rat: ignored, replaced by 'lteRsrp'
812            power_units_conversion_func: ignored, replaced by
813                self.rsrp_to_signal_power
814
815        Returns:
816            Dowlink calibration value and measured DL power. Note that the
817            phone only reports RSRP of the primary chain
818        """
819
820        return super().downlink_calibration(
821            rat='lteDbm',
822            power_units_conversion_func=self.rsrp_to_signal_power)
823
824    def rsrp_to_signal_power(self, rsrp, bts_config):
825        """ Converts rsrp to total band signal power
826
827        RSRP is measured per subcarrier, so total band power needs to be
828        multiplied by the number of subcarriers being used.
829
830        Args:
831            rsrp: desired rsrp in dBm
832            bts_config: a base station configuration object
833        Returns:
834            Total band signal power in dBm
835        """
836
837        bandwidth = bts_config.bandwidth
838
839        if bandwidth == 20:  # 100 RBs
840            power = rsrp + 30.79
841        elif bandwidth == 15:  # 75 RBs
842            power = rsrp + 29.54
843        elif bandwidth == 10:  # 50 RBs
844            power = rsrp + 27.78
845        elif bandwidth == 5:  # 25 RBs
846            power = rsrp + 24.77
847        elif bandwidth == 3:  # 15 RBs
848            power = rsrp + 22.55
849        elif bandwidth == 1.4:  # 6 RBs
850            power = rsrp + 18.57
851        else:
852            raise ValueError("Invalid bandwidth value.")
853
854        return power
855
856    def maximum_downlink_throughput(self):
857        """ Calculates maximum achievable downlink throughput in the current
858            simulation state.
859
860        Returns:
861            Maximum throughput in mbps.
862
863        """
864
865        return self.bts_maximum_downlink_throughtput(self.primary_config)
866
867    def bts_maximum_downlink_throughtput(self, bts_config):
868        """ Calculates maximum achievable downlink throughput for a single
869        base station from its configuration object.
870
871        Args:
872            bts_config: a base station configuration object.
873
874        Returns:
875            Maximum throughput in mbps.
876
877        """
878        if bts_config.mimo_mode == MimoMode.MIMO_1x1:
879            streams = 1
880        elif bts_config.mimo_mode == MimoMode.MIMO_2x2:
881            streams = 1
882        elif bts_config.mimo_mode == MimoMode.MIMO_4x4:
883            streams = 1
884        else:
885            raise ValueError('Unable to calculate maximum downlink throughput '
886                             'because the MIMO mode has not been set.')
887
888        bandwidth = bts_config.bandwidth
889        rb_ratio = bts_config.dl_rbs / self.total_rbs_dictionary[bandwidth]
890        mcs = bts_config.dl_mcs
891
892        max_rate_per_stream = None
893
894        tdd_subframe_config = bts_config.dlul_config
895        duplex_mode = self.get_duplex_mode(bts_config.band)
896
897        if duplex_mode == DuplexMode.TDD.value:
898            if self.dl_256_qam:
899                if mcs == "27":
900                    if bts_config.tbs_pattern_on:
901                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
902                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
903                                'DL']
904                    else:
905                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
906                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
907                                'DL']
908            else:
909                if mcs == "28":
910                    if bts_config.tbs_pattern_on:
911                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
912                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
913                                'DL']
914                    else:
915                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
916                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
917                                'DL']
918
919        elif duplex_mode == DuplexMode.FDD.value:
920            if (not self.dl_256_qam and bts_config.tbs_pattern_on
921                    and mcs == "28"):
922                max_rate_per_stream = {
923                    3: 9.96,
924                    5: 17.0,
925                    10: 34.7,
926                    15: 52.7,
927                    20: 72.2
928                }.get(bandwidth, None)
929            if (not self.dl_256_qam and bts_config.tbs_pattern_on
930                    and mcs == "27"):
931                max_rate_per_stream = {
932                    1.4: 2.94,
933                }.get(bandwidth, None)
934            elif (not self.dl_256_qam and not bts_config.tbs_pattern_on
935                  and mcs == "27"):
936                max_rate_per_stream = {
937                    1.4: 2.87,
938                    3: 7.7,
939                    5: 14.4,
940                    10: 28.7,
941                    15: 42.3,
942                    20: 57.7
943                }.get(bandwidth, None)
944            elif self.dl_256_qam and bts_config.tbs_pattern_on and mcs == "27":
945                max_rate_per_stream = {
946                    3: 13.2,
947                    5: 22.9,
948                    10: 46.3,
949                    15: 72.2,
950                    20: 93.9
951                }.get(bandwidth, None)
952            elif self.dl_256_qam and bts_config.tbs_pattern_on and mcs == "26":
953                max_rate_per_stream = {
954                    1.4: 3.96,
955                }.get(bandwidth, None)
956            elif (self.dl_256_qam and not bts_config.tbs_pattern_on
957                  and mcs == "27"):
958                max_rate_per_stream = {
959                    3: 11.3,
960                    5: 19.8,
961                    10: 44.1,
962                    15: 68.1,
963                    20: 88.4
964                }.get(bandwidth, None)
965            elif (self.dl_256_qam and not bts_config.tbs_pattern_on
966                  and mcs == "26"):
967                max_rate_per_stream = {
968                    1.4: 3.96,
969                }.get(bandwidth, None)
970
971        if not max_rate_per_stream:
972            raise NotImplementedError(
973                "The calculation for tbs pattern = {} "
974                "and mcs = {} is not implemented.".format(
975                    "FULLALLOCATION" if bts_config.tbs_pattern_on else "OFF",
976                    mcs))
977
978        return max_rate_per_stream * streams * rb_ratio
979
980    def maximum_uplink_throughput(self):
981        """ Calculates maximum achievable uplink throughput in the current
982            simulation state.
983
984        Returns:
985            Maximum throughput in mbps.
986
987        """
988
989        return self.bts_maximum_uplink_throughtput(self.primary_config)
990
991    def bts_maximum_uplink_throughtput(self, bts_config):
992        """ Calculates maximum achievable uplink throughput for the selected
993        basestation from its configuration object.
994
995        Args:
996            bts_config: an LTE base station configuration object.
997
998        Returns:
999            Maximum throughput in mbps.
1000
1001        """
1002
1003        bandwidth = bts_config.bandwidth
1004        rb_ratio = bts_config.ul_rbs / self.total_rbs_dictionary[bandwidth]
1005        mcs = bts_config.ul_mcs
1006
1007        max_rate_per_stream = None
1008
1009        tdd_subframe_config = bts_config.dlul_config
1010        duplex_mode = self.get_duplex_mode(bts_config.band)
1011
1012        if duplex_mode == DuplexMode.TDD.value:
1013            if self.ul_64_qam:
1014                if mcs == "28":
1015                    if bts_config.tbs_pattern_on:
1016                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1017                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
1018                                'UL']
1019                    else:
1020                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1021                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
1022                                'UL']
1023            else:
1024                if mcs == "23":
1025                    if bts_config.tbs_pattern_on:
1026                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1027                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
1028                                'UL']
1029                    else:
1030                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1031                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
1032                                'UL']
1033
1034        elif duplex_mode == DuplexMode.FDD.value:
1035            if mcs == "23" and not self.ul_64_qam:
1036                max_rate_per_stream = {
1037                    1.4: 2.85,
1038                    3: 7.18,
1039                    5: 12.1,
1040                    10: 24.5,
1041                    15: 36.5,
1042                    20: 49.1
1043                }.get(bandwidth, None)
1044            elif mcs == "28" and self.ul_64_qam:
1045                max_rate_per_stream = {
1046                    1.4: 4.2,
1047                    3: 10.5,
1048                    5: 17.2,
1049                    10: 35.3,
1050                    15: 53.0,
1051                    20: 72.6
1052                }.get(bandwidth, None)
1053
1054        if not max_rate_per_stream:
1055            raise NotImplementedError(
1056                "The calculation fir mcs = {} is not implemented.".format(
1057                    "FULLALLOCATION" if bts_config.tbs_pattern_on else "OFF",
1058                    mcs))
1059
1060        return max_rate_per_stream * rb_ratio
1061
1062    def allocation_percentages_to_rbs(self, bw, tm, dl, ul):
1063        """ Converts usage percentages to number of DL/UL RBs
1064
1065        Because not any number of DL/UL RBs can be obtained for a certain
1066        bandwidth, this function calculates the number of RBs that most
1067        closely matches the desired DL/UL percentages.
1068
1069        Args:
1070            bw: the bandwidth for the which the RB configuration is requested
1071            tm: the transmission in which the base station will be operating
1072            dl: desired percentage of downlink RBs
1073            ul: desired percentage of uplink RBs
1074        Returns:
1075            a tuple indicating the number of downlink and uplink RBs
1076        """
1077
1078        # Validate the arguments
1079        if (not 0 <= dl <= 100) or (not 0 <= ul <= 100):
1080            raise ValueError("The percentage of DL and UL RBs have to be two "
1081                             "positive between 0 and 100.")
1082
1083        # Get min and max values from tables
1084        max_rbs = self.total_rbs_dictionary[bw]
1085        min_dl_rbs = self.min_dl_rbs_dictionary[bw]
1086        min_ul_rbs = self.min_ul_rbs_dictionary[bw]
1087
1088        def percentage_to_amount(min_val, max_val, percentage):
1089            """ Returns the integer between min_val and max_val that is closest
1090            to percentage/100*max_val
1091            """
1092
1093            # Calculate the value that corresponds to the required percentage.
1094            closest_int = round(max_val * percentage / 100)
1095            # Cannot be less than min_val
1096            closest_int = max(closest_int, min_val)
1097            # RBs cannot be more than max_rbs
1098            closest_int = min(closest_int, max_val)
1099
1100            return closest_int
1101
1102        # Calculate the number of DL RBs
1103
1104        # Get the number of DL RBs that corresponds to
1105        #  the required percentage.
1106        desired_dl_rbs = percentage_to_amount(min_val=min_dl_rbs,
1107                                              max_val=max_rbs,
1108                                              percentage=dl)
1109
1110        if tm == TransmissionMode.TM3 or tm == TransmissionMode.TM4:
1111
1112            # For TM3 and TM4 the number of DL RBs needs to be max_rbs or a
1113            # multiple of the RBG size
1114
1115            if desired_dl_rbs == max_rbs:
1116                dl_rbs = max_rbs
1117            else:
1118                dl_rbs = (math.ceil(desired_dl_rbs / self.rbg_dictionary[bw]) *
1119                          self.rbg_dictionary[bw])
1120
1121        else:
1122            # The other TMs allow any number of RBs between 1 and max_rbs
1123            dl_rbs = desired_dl_rbs
1124
1125        # Calculate the number of UL RBs
1126
1127        # Get the number of UL RBs that corresponds
1128        # to the required percentage
1129        desired_ul_rbs = percentage_to_amount(min_val=min_ul_rbs,
1130                                              max_val=max_rbs,
1131                                              percentage=ul)
1132
1133        # Create a list of all possible UL RBs assignment
1134        # The standard allows any number that can be written as
1135        # 2**a * 3**b * 5**c for any combination of a, b and c.
1136
1137        def pow_range(max_value, base):
1138            """ Returns a range of all possible powers of base under
1139              the given max_value.
1140          """
1141            return range(int(math.ceil(math.log(max_value, base))))
1142
1143        possible_ul_rbs = [
1144            2**a * 3**b * 5**c for a in pow_range(max_rbs, 2)
1145            for b in pow_range(max_rbs, 3)
1146            for c in pow_range(max_rbs, 5)
1147            if 2**a * 3**b * 5**c <= max_rbs] # yapf: disable
1148
1149        # Find the value in the list that is closest to desired_ul_rbs
1150        differences = [abs(rbs - desired_ul_rbs) for rbs in possible_ul_rbs]
1151        ul_rbs = possible_ul_rbs[differences.index(min(differences))]
1152
1153        # Report what are the obtained RB percentages
1154        self.log.info("Requested a {}% / {}% RB allocation. Closest possible "
1155                      "percentages are {}% / {}%.".format(
1156                          dl, ul, round(100 * dl_rbs / max_rbs),
1157                          round(100 * ul_rbs / max_rbs)))
1158
1159        return dl_rbs, ul_rbs
1160
1161    def calibrate(self, band):
1162        """ Calculates UL and DL path loss if it wasn't done before
1163
1164        Before running the base class implementation, configure the base station
1165        to only use one downlink antenna with maximum bandwidth.
1166
1167        Args:
1168            band: the band that is currently being calibrated.
1169        """
1170
1171        # Save initial values in a configuration object so they can be restored
1172        restore_config = self.BtsConfig()
1173        restore_config.mimo_mode = self.primary_config.mimo_mode
1174        restore_config.transmission_mode = self.primary_config.transmission_mode
1175        restore_config.bandwidth = self.primary_config.bandwidth
1176
1177        # Set up a temporary calibration configuration.
1178        temporary_config = self.BtsConfig()
1179        temporary_config.mimo_mode = MimoMode.MIMO_1x1
1180        temporary_config.transmission_mode = TransmissionMode.TM1
1181        temporary_config.bandwidth = max(
1182            self.allowed_bandwidth_dictionary[int(band)])
1183        self.simulator.configure_bts(temporary_config)
1184        self.primary_config.incorporate(temporary_config)
1185
1186        super().calibrate(band)
1187
1188        # Restore values as they were before changing them for calibration.
1189        self.simulator.configure_bts(restore_config)
1190        self.primary_config.incorporate(restore_config)
1191
1192    def start_traffic_for_calibration(self):
1193        """
1194            If TBS pattern is set to full allocation, there is no need to start
1195            IP traffic.
1196        """
1197        if not self.primary_config.tbs_pattern_on:
1198            super().start_traffic_for_calibration()
1199
1200    def stop_traffic_for_calibration(self):
1201        """
1202            If TBS pattern is set to full allocation, IP traffic wasn't started
1203        """
1204        if not self.primary_config.tbs_pattern_on:
1205            super().stop_traffic_for_calibration()
1206
1207    def get_duplex_mode(self, band):
1208        """ Determines if the band uses FDD or TDD duplex mode
1209
1210        Args:
1211            band: a band number
1212        Returns:
1213            an variable of class DuplexMode indicating if band is FDD or TDD
1214        """
1215
1216        if 33 <= int(band) <= 46:
1217            return DuplexMode.TDD
1218        else:
1219            return DuplexMode.FDD
1220