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
53class ModulationType(Enum):
54    """DL/UL Modulation order."""
55    QPSK = 'QPSK'
56    Q16 = '16QAM'
57    Q64 = '64QAM'
58    Q256 = '256QAM'
59
60
61class LteSimulation(BaseSimulation):
62    """ Single-carrier LTE simulation. """
63
64    # Simulation config keywords contained in the test name
65    PARAM_FRAME_CONFIG = "tddconfig"
66    PARAM_BW = "bw"
67    PARAM_SCHEDULING = "scheduling"
68    PARAM_SCHEDULING_STATIC = "static"
69    PARAM_SCHEDULING_DYNAMIC = "dynamic"
70    PARAM_PATTERN = "pattern"
71    PARAM_TM = "tm"
72    PARAM_UL_PW = 'pul'
73    PARAM_DL_PW = 'pdl'
74    PARAM_BAND = "band"
75    PARAM_MIMO = "mimo"
76    PARAM_DL_MCS = 'dlmcs'
77    PARAM_UL_MCS = 'ulmcs'
78    PARAM_SSF = 'ssf'
79    PARAM_CFI = 'cfi'
80    PARAM_PAGING = 'paging'
81    PARAM_PHICH = 'phich'
82    PARAM_RRC_STATUS_CHANGE_TIMER = "rrcstatuschangetimer"
83    PARAM_DRX = 'drx'
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            drx_connected_mode: a boolean indicating whether cDRX mode is
413                on or off
414            drx_on_duration_timer: number of PDCCH subframes representing
415                DRX on duration
416            drx_inactivity_timer: number of PDCCH subframes to wait before
417                entering DRX mode
418            drx_retransmission_timer: number of consecutive PDCCH subframes
419                to wait for retransmission
420            drx_long_cycle: number of subframes representing one long DRX cycle.
421                One cycle consists of DRX sleep + DRX on duration
422            drx_long_cycle_offset: number representing offset in range
423                0 to drx_long_cycle - 1
424        """
425        def __init__(self):
426            """ Initialize the base station config by setting all its
427            parameters to None. """
428            super().__init__()
429            self.band = None
430            self.dlul_config = None
431            self.ssf_config = None
432            self.bandwidth = None
433            self.mimo_mode = None
434            self.transmission_mode = None
435            self.scheduling_mode = None
436            self.dl_rbs = None
437            self.ul_rbs = None
438            self.dl_mcs = None
439            self.ul_mcs = None
440            self.dl_modulation_order = None
441            self.ul_modulation_order = None
442            self.tbs_pattern_on = None
443            self.dl_channel = None
444            self.cfi = None
445            self.paging_cycle = None
446            self.phich = None
447            self.drx_connected_mode = None
448            self.drx_on_duration_timer = None
449            self.drx_inactivity_timer = None
450            self.drx_retransmission_timer = None
451            self.drx_long_cycle = None
452            self.drx_long_cycle_offset = None
453
454    def __init__(self, simulator, log, dut, test_config, calibration_table):
455        """ Initializes the simulator for a single-carrier LTE simulation.
456
457        Loads a simple LTE simulation enviroment with 1 basestation.
458
459        Args:
460            simulator: a cellular simulator controller
461            log: a logger handle
462            dut: the android device handler
463            test_config: test configuration obtained from the config file
464            calibration_table: a dictionary containing path losses for
465                different bands.
466
467        """
468
469        super().__init__(simulator, log, dut, test_config, calibration_table)
470
471        if not dut.droid.telephonySetPreferredNetworkTypesForSubscription(
472                NETWORK_MODE_LTE_ONLY,
473                dut.droid.subscriptionGetDefaultSubId()):
474            log.error("Couldn't set preferred network type.")
475        else:
476            log.info("Preferred network type set.")
477
478        # Get TBS pattern setting from the test configuration
479        if self.KEY_TBS_PATTERN not in test_config:
480            self.log.warning("The key '{}' is not set in the config file. "
481                             "Setting to true by default.".format(
482                                 self.KEY_TBS_PATTERN))
483        self.primary_config.tbs_pattern_on = test_config.get(
484            self.KEY_TBS_PATTERN, True)
485
486        # Get the 256-QAM setting from the test configuration
487        if self.KEY_DL_256_QAM not in test_config:
488            self.log.warning("The key '{}' is not set in the config file. "
489                             "Setting to false by default.".format(
490                                 self.KEY_DL_256_QAM))
491
492        self.dl_256_qam = test_config.get(self.KEY_DL_256_QAM, False)
493
494        if self.dl_256_qam:
495            if not self.simulator.LTE_SUPPORTS_DL_256QAM:
496                self.log.warning("The key '{}' is set to true but the "
497                                 "simulator doesn't support that modulation "
498                                 "order.".format(self.KEY_DL_256_QAM))
499                self.dl_256_qam = False
500            else:
501                self.primary_config.dl_modulation_order = ModulationType.Q256
502
503        else:
504            self.log.warning('dl modulation 256QAM is not specified in config, '
505                             'setting to default value 64QAM')
506            self.primary_config.dl_modulation_order = ModulationType.Q64
507        # Get the 64-QAM setting from the test configuration
508        if self.KEY_UL_64_QAM not in test_config:
509            self.log.warning("The key '{}' is not set in the config file. "
510                             "Setting to false by default.".format(
511                                 self.KEY_UL_64_QAM))
512
513        self.ul_64_qam = test_config.get(self.KEY_UL_64_QAM, False)
514
515        if self.ul_64_qam:
516            if not self.simulator.LTE_SUPPORTS_UL_64QAM:
517                self.log.warning("The key '{}' is set to true but the "
518                                 "simulator doesn't support that modulation "
519                                 "order.".format(self.KEY_UL_64_QAM))
520                self.ul_64_qam = False
521            else:
522                self.primary_config.ul_modulation_order = ModulationType.Q64
523        else:
524            self.log.warning('ul modulation 64QAM is not specified in config, '
525                             'setting to default value 16QAM')
526            self.primary_config.ul_modulation_order = ModulationType.Q16
527
528        self.simulator.configure_bts(self.primary_config)
529
530    def setup_simulator(self):
531        """ Do initial configuration in the simulator. """
532        self.simulator.setup_lte_scenario()
533
534    def parse_parameters(self, parameters):
535        """ Configs an LTE simulation using a list of parameters.
536
537        Calls the parent method first, then consumes parameters specific to LTE.
538
539        Args:
540            parameters: list of parameters
541        """
542
543        # Instantiate a new configuration object
544        new_config = self.BtsConfig()
545
546        # Setup band
547
548        values = self.consume_parameter(parameters, self.PARAM_BAND, 1)
549
550        if not values:
551            raise ValueError(
552                "The test name needs to include parameter '{}' followed by "
553                "the required band number.".format(self.PARAM_BAND))
554
555        new_config.band = values[1]
556
557        # Set TDD-only configs
558        if self.get_duplex_mode(new_config.band) == DuplexMode.TDD:
559
560            # Sub-frame DL/UL config
561            values = self.consume_parameter(parameters,
562                                            self.PARAM_FRAME_CONFIG, 1)
563            if not values:
564                raise ValueError(
565                    "When a TDD band is selected the frame "
566                    "structure has to be indicated with the '{}' "
567                    "parameter followed by a number from 0 to 6.".format(
568                        self.PARAM_FRAME_CONFIG))
569
570            new_config.dlul_config = int(values[1])
571
572            # Special Sub-Frame configuration
573            values = self.consume_parameter(parameters, self.PARAM_SSF, 1)
574
575            if not values:
576                self.log.warning(
577                    'The {} parameter was not provided. Setting '
578                    'Special Sub-Frame config to 6 by default.'.format(
579                        self.PARAM_SSF))
580                new_config.ssf_config = 6
581            else:
582                new_config.ssf_config = int(values[1])
583
584        # Setup bandwidth
585
586        values = self.consume_parameter(parameters, self.PARAM_BW, 1)
587
588        if not values:
589            raise ValueError(
590                "The test name needs to include parameter {} followed by an "
591                "int value (to indicate 1.4 MHz use 14).".format(
592                    self.PARAM_BW))
593
594        bw = float(values[1])
595
596        if bw == 14:
597            bw = 1.4
598
599        new_config.bandwidth = bw
600
601        # Setup mimo mode
602
603        values = self.consume_parameter(parameters, self.PARAM_MIMO, 1)
604
605        if not values:
606            raise ValueError(
607                "The test name needs to include parameter '{}' followed by the "
608                "mimo mode.".format(self.PARAM_MIMO))
609
610        for mimo_mode in MimoMode:
611            if values[1] == mimo_mode.value:
612                new_config.mimo_mode = mimo_mode
613                break
614        else:
615            raise ValueError("The {} parameter needs to be followed by either "
616                             "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO))
617
618        if (new_config.mimo_mode == MimoMode.MIMO_4x4
619                and not self.simulator.LTE_SUPPORTS_4X4_MIMO):
620            raise ValueError("The test requires 4x4 MIMO, but that is not "
621                             "supported by the cellular simulator.")
622
623        # Setup transmission mode
624
625        values = self.consume_parameter(parameters, self.PARAM_TM, 1)
626
627        if not values:
628            raise ValueError(
629                "The test name needs to include parameter {} followed by an "
630                "int value from 1 to 4 indicating transmission mode.".format(
631                    self.PARAM_TM))
632
633        for tm in TransmissionMode:
634            if values[1] == tm.value[2:]:
635                new_config.transmission_mode = tm
636                break
637        else:
638            raise ValueError("The {} parameter needs to be followed by either "
639                             "TM1, TM2, TM3, TM4, TM7, TM8 or TM9.".format(
640                                 self.PARAM_MIMO))
641
642        # Setup scheduling mode
643
644        values = self.consume_parameter(parameters, self.PARAM_SCHEDULING, 1)
645
646        if not values:
647            new_config.scheduling_mode = SchedulingMode.STATIC
648            self.log.warning(
649                "The test name does not include the '{}' parameter. Setting to "
650                "static by default.".format(self.PARAM_SCHEDULING))
651        elif values[1] == self.PARAM_SCHEDULING_DYNAMIC:
652            new_config.scheduling_mode = SchedulingMode.DYNAMIC
653        elif values[1] == self.PARAM_SCHEDULING_STATIC:
654            new_config.scheduling_mode = SchedulingMode.STATIC
655        else:
656            raise ValueError(
657                "The test name parameter '{}' has to be followed by either "
658                "'dynamic' or 'static'.".format(self.PARAM_SCHEDULING))
659
660        if new_config.scheduling_mode == SchedulingMode.STATIC:
661
662            values = self.consume_parameter(parameters, self.PARAM_PATTERN, 2)
663
664            if not values:
665                self.log.warning(
666                    "The '{}' parameter was not set, using 100% RBs for both "
667                    "DL and UL. To set the percentages of total RBs include "
668                    "the '{}' parameter followed by two ints separated by an "
669                    "underscore indicating downlink and uplink percentages.".
670                    format(self.PARAM_PATTERN, self.PARAM_PATTERN))
671                dl_pattern = 100
672                ul_pattern = 100
673            else:
674                dl_pattern = int(values[1])
675                ul_pattern = int(values[2])
676
677            if not (0 <= dl_pattern <= 100 and 0 <= ul_pattern <= 100):
678                raise ValueError(
679                    "The scheduling pattern parameters need to be two "
680                    "positive numbers between 0 and 100.")
681
682            new_config.dl_rbs, new_config.ul_rbs = (
683                self.allocation_percentages_to_rbs(
684                    new_config.bandwidth, new_config.transmission_mode,
685                    dl_pattern, ul_pattern))
686
687            # Look for a DL MCS configuration in the test parameters. If it is
688            # not present, use a default value.
689            dlmcs = self.consume_parameter(parameters, self.PARAM_DL_MCS, 1)
690
691            if dlmcs:
692                new_config.dl_mcs = int(dlmcs[1])
693            else:
694                self.log.warning(
695                    'The test name does not include the {} parameter. Setting '
696                    'to the max value by default'.format(self.PARAM_DL_MCS))
697                if self.dl_256_qam and new_config.bandwidth == 1.4:
698                    new_config.dl_mcs = 26
699                elif (not self.dl_256_qam
700                      and self.primary_config.tbs_pattern_on
701                      and new_config.bandwidth != 1.4):
702                    new_config.dl_mcs = 28
703                else:
704                    new_config.dl_mcs = 27
705
706            # Look for an UL MCS configuration in the test parameters. If it is
707            # not present, use a default value.
708            ulmcs = self.consume_parameter(parameters, self.PARAM_UL_MCS, 1)
709
710            if ulmcs:
711                new_config.ul_mcs = int(ulmcs[1])
712            else:
713                self.log.warning(
714                    'The test name does not include the {} parameter. Setting '
715                    'to the max value by default'.format(self.PARAM_UL_MCS))
716                if self.ul_64_qam:
717                    new_config.ul_mcs = 28
718                else:
719                    new_config.ul_mcs = 23
720
721        # Configure the simulation for DRX mode
722
723        drx = self.consume_parameter(parameters, self.PARAM_DRX, 5)
724
725        if drx and len(drx) == 6:
726            new_config.drx_connected_mode = True
727            new_config.drx_on_duration_timer = drx[1]
728            new_config.drx_inactivity_timer = drx[2]
729            new_config.drx_retransmission_timer = drx[3]
730            new_config.drx_long_cycle = drx[4]
731            try:
732                long_cycle = int(drx[4])
733                long_cycle_offset = int(drx[5])
734                if long_cycle_offset in range(0, long_cycle):
735                    new_config.drx_long_cycle_offset = long_cycle_offset
736                else:
737                    self.log.error(("The cDRX long cycle offset must be in the "
738                                    "range 0 to (long cycle  - 1). Setting "
739                                    "long cycle offset to 0"))
740                    new_config.drx_long_cycle_offset = 0
741
742            except ValueError:
743                self.log.error(("cDRX long cycle and long cycle offset "
744                                "must be integers. Disabling cDRX mode."))
745                new_config.drx_connected_mode = False
746        else:
747            self.log.warning(("DRX mode was not configured properly. "
748                              "Please provide the following 5 values: "
749                              "1) DRX on duration timer "
750                              "2) Inactivity timer "
751                              "3) Retransmission timer "
752                              "4) Long DRX cycle duration "
753                              "5) Long DRX cycle offset "
754                              "Example: drx_2_6_16_20_0"))
755
756        # Setup LTE RRC status change function and timer for LTE idle test case
757        values = self.consume_parameter(parameters,
758                                        self.PARAM_RRC_STATUS_CHANGE_TIMER, 1)
759        if not values:
760            self.log.info(
761                "The test name does not include the '{}' parameter. Disabled "
762                "by default.".format(self.PARAM_RRC_STATUS_CHANGE_TIMER))
763            self.simulator.set_lte_rrc_state_change_timer(False)
764        else:
765            timer = int(values[1])
766            self.simulator.set_lte_rrc_state_change_timer(True, timer)
767            self.rrc_sc_timer = timer
768
769        # Channel Control Indicator
770        values = self.consume_parameter(parameters, self.PARAM_CFI, 1)
771
772        if not values:
773            self.log.warning('The {} parameter was not provided. Setting '
774                             'CFI to BESTEFFORT.'.format(self.PARAM_CFI))
775            new_config.cfi = 'BESTEFFORT'
776        else:
777            new_config.cfi = values[1]
778
779        # PHICH group size
780        values = self.consume_parameter(parameters, self.PARAM_PHICH, 1)
781
782        if not values:
783            self.log.warning('The {} parameter was not provided. Setting '
784                             'PHICH group size to 1 by default.'.format(
785                                 self.PARAM_PHICH))
786            new_config.phich = '1'
787        else:
788            if values[1] == '16':
789                new_config.phich = '1/6'
790            elif values[1] == '12':
791                new_config.phich = '1/2'
792            elif values[1] in ['1/6', '1/2', '1', '2']:
793                new_config.phich = values[1]
794            else:
795                raise ValueError('The {} parameter can only be followed by 1,'
796                                 '2, 1/2 (or 12) and 1/6 (or 16).'.format(
797                                     self.PARAM_PHICH))
798
799        # Paging cycle duration
800        values = self.consume_parameter(parameters, self.PARAM_PAGING, 1)
801
802        if not values:
803            self.log.warning('The {} parameter was not provided. Setting '
804                             'paging cycle duration to 1280 ms by '
805                             'default.'.format(self.PARAM_PAGING))
806            new_config.paging_cycle = 1280
807        else:
808            try:
809                new_config.paging_cycle = int(values[1])
810            except ValueError:
811                raise ValueError(
812                    'The {} parameter has to be followed by the paging cycle '
813                    'duration in milliseconds.'.format(self.PARAM_PAGING))
814
815        # Get uplink power
816
817        ul_power = self.get_uplink_power_from_parameters(parameters)
818
819        # Power is not set on the callbox until after the simulation is
820        # started. Saving this value in a variable for later
821        self.sim_ul_power = ul_power
822
823        # Get downlink power
824
825        dl_power = self.get_downlink_power_from_parameters(parameters)
826
827        # Power is not set on the callbox until after the simulation is
828        # started. Saving this value in a variable for later
829        self.sim_dl_power = dl_power
830
831        # Setup the base station with the obtained configuration and then save
832        # these parameters in the current configuration object
833        self.simulator.configure_bts(new_config)
834        self.primary_config.incorporate(new_config)
835
836        # Now that the band is set, calibrate the link if necessary
837        self.load_pathloss_if_required()
838
839    def calibrated_downlink_rx_power(self, bts_config, rsrp):
840        """ LTE simulation overrides this method so that it can convert from
841        RSRP to total signal power transmitted from the basestation.
842
843        Args:
844            bts_config: the current configuration at the base station
845            rsrp: desired rsrp, contained in a key value pair
846        """
847
848        power = self.rsrp_to_signal_power(rsrp, bts_config)
849
850        self.log.info(
851            "Setting downlink signal level to {} RSRP ({} dBm)".format(
852                rsrp, power))
853
854        # Use parent method to calculate signal level
855        return super().calibrated_downlink_rx_power(bts_config, power)
856
857    def downlink_calibration(self, rat=None, power_units_conversion_func=None):
858        """ Computes downlink path loss and returns the calibration value.
859
860        See base class implementation for details.
861
862        Args:
863            rat: ignored, replaced by 'lteRsrp'
864            power_units_conversion_func: ignored, replaced by
865                self.rsrp_to_signal_power
866
867        Returns:
868            Dowlink calibration value and measured DL power. Note that the
869            phone only reports RSRP of the primary chain
870        """
871
872        return super().downlink_calibration(
873            rat='lteDbm',
874            power_units_conversion_func=self.rsrp_to_signal_power)
875
876    def rsrp_to_signal_power(self, rsrp, bts_config):
877        """ Converts rsrp to total band signal power
878
879        RSRP is measured per subcarrier, so total band power needs to be
880        multiplied by the number of subcarriers being used.
881
882        Args:
883            rsrp: desired rsrp in dBm
884            bts_config: a base station configuration object
885        Returns:
886            Total band signal power in dBm
887        """
888
889        bandwidth = bts_config.bandwidth
890
891        if bandwidth == 20:  # 100 RBs
892            power = rsrp + 30.79
893        elif bandwidth == 15:  # 75 RBs
894            power = rsrp + 29.54
895        elif bandwidth == 10:  # 50 RBs
896            power = rsrp + 27.78
897        elif bandwidth == 5:  # 25 RBs
898            power = rsrp + 24.77
899        elif bandwidth == 3:  # 15 RBs
900            power = rsrp + 22.55
901        elif bandwidth == 1.4:  # 6 RBs
902            power = rsrp + 18.57
903        else:
904            raise ValueError("Invalid bandwidth value.")
905
906        return power
907
908    def maximum_downlink_throughput(self):
909        """ Calculates maximum achievable downlink throughput in the current
910            simulation state.
911
912        Returns:
913            Maximum throughput in mbps.
914
915        """
916
917        return self.bts_maximum_downlink_throughtput(self.primary_config)
918
919    def bts_maximum_downlink_throughtput(self, bts_config):
920        """ Calculates maximum achievable downlink throughput for a single
921        base station from its configuration object.
922
923        Args:
924            bts_config: a base station configuration object.
925
926        Returns:
927            Maximum throughput in mbps.
928
929        """
930        if bts_config.mimo_mode == MimoMode.MIMO_1x1:
931            streams = 1
932        elif bts_config.mimo_mode == MimoMode.MIMO_2x2:
933            streams = 2
934        elif bts_config.mimo_mode == MimoMode.MIMO_4x4:
935            streams = 4
936        else:
937            raise ValueError('Unable to calculate maximum downlink throughput '
938                             'because the MIMO mode has not been set.')
939
940        bandwidth = bts_config.bandwidth
941        rb_ratio = bts_config.dl_rbs / self.total_rbs_dictionary[bandwidth]
942        mcs = bts_config.dl_mcs
943
944        max_rate_per_stream = None
945
946        tdd_subframe_config = bts_config.dlul_config
947        duplex_mode = self.get_duplex_mode(bts_config.band)
948
949        if duplex_mode == DuplexMode.TDD:
950            if self.dl_256_qam:
951                if mcs == 27:
952                    if bts_config.tbs_pattern_on:
953                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
954                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
955                                'DL']
956                    else:
957                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
958                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
959                                'DL']
960            else:
961                if mcs == 28:
962                    if bts_config.tbs_pattern_on:
963                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
964                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
965                                'DL']
966                    else:
967                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
968                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
969                                'DL']
970
971        elif duplex_mode == DuplexMode.FDD:
972            if (not self.dl_256_qam and bts_config.tbs_pattern_on
973                    and mcs == 28):
974                max_rate_per_stream = {
975                    3: 9.96,
976                    5: 17.0,
977                    10: 34.7,
978                    15: 52.7,
979                    20: 72.2
980                }.get(bandwidth, None)
981            if (not self.dl_256_qam and bts_config.tbs_pattern_on
982                    and mcs == 27):
983                max_rate_per_stream = {
984                    1.4: 2.94,
985                }.get(bandwidth, None)
986            elif (not self.dl_256_qam and not bts_config.tbs_pattern_on
987                  and mcs == 27):
988                max_rate_per_stream = {
989                    1.4: 2.87,
990                    3: 7.7,
991                    5: 14.4,
992                    10: 28.7,
993                    15: 42.3,
994                    20: 57.7
995                }.get(bandwidth, None)
996            elif self.dl_256_qam and bts_config.tbs_pattern_on and mcs == 27:
997                max_rate_per_stream = {
998                    3: 13.2,
999                    5: 22.9,
1000                    10: 46.3,
1001                    15: 72.2,
1002                    20: 93.9
1003                }.get(bandwidth, None)
1004            elif self.dl_256_qam and bts_config.tbs_pattern_on and mcs == 26:
1005                max_rate_per_stream = {
1006                    1.4: 3.96,
1007                }.get(bandwidth, None)
1008            elif (self.dl_256_qam and not bts_config.tbs_pattern_on
1009                  and mcs == 27):
1010                max_rate_per_stream = {
1011                    3: 11.3,
1012                    5: 19.8,
1013                    10: 44.1,
1014                    15: 68.1,
1015                    20: 88.4
1016                }.get(bandwidth, None)
1017            elif (self.dl_256_qam and not bts_config.tbs_pattern_on
1018                  and mcs == 26):
1019                max_rate_per_stream = {
1020                    1.4: 3.96,
1021                }.get(bandwidth, None)
1022
1023        if not max_rate_per_stream:
1024            raise NotImplementedError(
1025                "The calculation for tbs pattern = {} "
1026                "and mcs = {} is not implemented.".format(
1027                    "FULLALLOCATION" if bts_config.tbs_pattern_on else "OFF",
1028                    mcs))
1029
1030        return max_rate_per_stream * streams * rb_ratio
1031
1032    def maximum_uplink_throughput(self):
1033        """ Calculates maximum achievable uplink throughput in the current
1034            simulation state.
1035
1036        Returns:
1037            Maximum throughput in mbps.
1038
1039        """
1040
1041        return self.bts_maximum_uplink_throughtput(self.primary_config)
1042
1043    def bts_maximum_uplink_throughtput(self, bts_config):
1044        """ Calculates maximum achievable uplink throughput for the selected
1045        basestation from its configuration object.
1046
1047        Args:
1048            bts_config: an LTE base station configuration object.
1049
1050        Returns:
1051            Maximum throughput in mbps.
1052
1053        """
1054
1055        bandwidth = bts_config.bandwidth
1056        rb_ratio = bts_config.ul_rbs / self.total_rbs_dictionary[bandwidth]
1057        mcs = bts_config.ul_mcs
1058
1059        max_rate_per_stream = None
1060
1061        tdd_subframe_config = bts_config.dlul_config
1062        duplex_mode = self.get_duplex_mode(bts_config.band)
1063
1064        if duplex_mode == DuplexMode.TDD:
1065            if self.ul_64_qam:
1066                if mcs == 28:
1067                    if bts_config.tbs_pattern_on:
1068                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1069                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
1070                                'UL']
1071                    else:
1072                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1073                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
1074                                'UL']
1075            else:
1076                if mcs == 23:
1077                    if bts_config.tbs_pattern_on:
1078                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1079                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
1080                                'UL']
1081                    else:
1082                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
1083                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
1084                                'UL']
1085
1086        elif duplex_mode == DuplexMode.FDD:
1087            if mcs == 23 and not self.ul_64_qam:
1088                max_rate_per_stream = {
1089                    1.4: 2.85,
1090                    3: 7.18,
1091                    5: 12.1,
1092                    10: 24.5,
1093                    15: 36.5,
1094                    20: 49.1
1095                }.get(bandwidth, None)
1096            elif mcs == 28 and self.ul_64_qam:
1097                max_rate_per_stream = {
1098                    1.4: 4.2,
1099                    3: 10.5,
1100                    5: 17.2,
1101                    10: 35.3,
1102                    15: 53.0,
1103                    20: 72.6
1104                }.get(bandwidth, None)
1105
1106        if not max_rate_per_stream:
1107            raise NotImplementedError(
1108                "The calculation fir mcs = {} is not implemented.".format(
1109                    "FULLALLOCATION" if bts_config.tbs_pattern_on else "OFF",
1110                    mcs))
1111
1112        return max_rate_per_stream * rb_ratio
1113
1114    def allocation_percentages_to_rbs(self, bw, tm, dl, ul):
1115        """ Converts usage percentages to number of DL/UL RBs
1116
1117        Because not any number of DL/UL RBs can be obtained for a certain
1118        bandwidth, this function calculates the number of RBs that most
1119        closely matches the desired DL/UL percentages.
1120
1121        Args:
1122            bw: the bandwidth for the which the RB configuration is requested
1123            tm: the transmission in which the base station will be operating
1124            dl: desired percentage of downlink RBs
1125            ul: desired percentage of uplink RBs
1126        Returns:
1127            a tuple indicating the number of downlink and uplink RBs
1128        """
1129
1130        # Validate the arguments
1131        if (not 0 <= dl <= 100) or (not 0 <= ul <= 100):
1132            raise ValueError("The percentage of DL and UL RBs have to be two "
1133                             "positive between 0 and 100.")
1134
1135        # Get min and max values from tables
1136        max_rbs = self.total_rbs_dictionary[bw]
1137        min_dl_rbs = self.min_dl_rbs_dictionary[bw]
1138        min_ul_rbs = self.min_ul_rbs_dictionary[bw]
1139
1140        def percentage_to_amount(min_val, max_val, percentage):
1141            """ Returns the integer between min_val and max_val that is closest
1142            to percentage/100*max_val
1143            """
1144
1145            # Calculate the value that corresponds to the required percentage.
1146            closest_int = round(max_val * percentage / 100)
1147            # Cannot be less than min_val
1148            closest_int = max(closest_int, min_val)
1149            # RBs cannot be more than max_rbs
1150            closest_int = min(closest_int, max_val)
1151
1152            return closest_int
1153
1154        # Calculate the number of DL RBs
1155
1156        # Get the number of DL RBs that corresponds to
1157        #  the required percentage.
1158        desired_dl_rbs = percentage_to_amount(min_val=min_dl_rbs,
1159                                              max_val=max_rbs,
1160                                              percentage=dl)
1161
1162        if tm == TransmissionMode.TM3 or tm == TransmissionMode.TM4:
1163
1164            # For TM3 and TM4 the number of DL RBs needs to be max_rbs or a
1165            # multiple of the RBG size
1166
1167            if desired_dl_rbs == max_rbs:
1168                dl_rbs = max_rbs
1169            else:
1170                dl_rbs = (math.ceil(desired_dl_rbs / self.rbg_dictionary[bw]) *
1171                          self.rbg_dictionary[bw])
1172
1173        else:
1174            # The other TMs allow any number of RBs between 1 and max_rbs
1175            dl_rbs = desired_dl_rbs
1176
1177        # Calculate the number of UL RBs
1178
1179        # Get the number of UL RBs that corresponds
1180        # to the required percentage
1181        desired_ul_rbs = percentage_to_amount(min_val=min_ul_rbs,
1182                                              max_val=max_rbs,
1183                                              percentage=ul)
1184
1185        # Create a list of all possible UL RBs assignment
1186        # The standard allows any number that can be written as
1187        # 2**a * 3**b * 5**c for any combination of a, b and c.
1188
1189        def pow_range(max_value, base):
1190            """ Returns a range of all possible powers of base under
1191              the given max_value.
1192          """
1193            return range(int(math.ceil(math.log(max_value, base))))
1194
1195        possible_ul_rbs = [
1196            2**a * 3**b * 5**c for a in pow_range(max_rbs, 2)
1197            for b in pow_range(max_rbs, 3)
1198            for c in pow_range(max_rbs, 5)
1199            if 2**a * 3**b * 5**c <= max_rbs] # yapf: disable
1200
1201        # Find the value in the list that is closest to desired_ul_rbs
1202        differences = [abs(rbs - desired_ul_rbs) for rbs in possible_ul_rbs]
1203        ul_rbs = possible_ul_rbs[differences.index(min(differences))]
1204
1205        # Report what are the obtained RB percentages
1206        self.log.info("Requested a {}% / {}% RB allocation. Closest possible "
1207                      "percentages are {}% / {}%.".format(
1208                          dl, ul, round(100 * dl_rbs / max_rbs),
1209                          round(100 * ul_rbs / max_rbs)))
1210
1211        return dl_rbs, ul_rbs
1212
1213    def calibrate(self, band):
1214        """ Calculates UL and DL path loss if it wasn't done before
1215
1216        Before running the base class implementation, configure the base station
1217        to only use one downlink antenna with maximum bandwidth.
1218
1219        Args:
1220            band: the band that is currently being calibrated.
1221        """
1222
1223        # Save initial values in a configuration object so they can be restored
1224        restore_config = self.BtsConfig()
1225        restore_config.mimo_mode = self.primary_config.mimo_mode
1226        restore_config.transmission_mode = self.primary_config.transmission_mode
1227        restore_config.bandwidth = self.primary_config.bandwidth
1228
1229        # Set up a temporary calibration configuration.
1230        temporary_config = self.BtsConfig()
1231        temporary_config.mimo_mode = MimoMode.MIMO_1x1
1232        temporary_config.transmission_mode = TransmissionMode.TM1
1233        temporary_config.bandwidth = max(
1234            self.allowed_bandwidth_dictionary[int(band)])
1235        self.simulator.configure_bts(temporary_config)
1236        self.primary_config.incorporate(temporary_config)
1237
1238        super().calibrate(band)
1239
1240        # Restore values as they were before changing them for calibration.
1241        self.simulator.configure_bts(restore_config)
1242        self.primary_config.incorporate(restore_config)
1243
1244    def start_traffic_for_calibration(self):
1245        """
1246            If TBS pattern is set to full allocation, there is no need to start
1247            IP traffic.
1248        """
1249        if not self.primary_config.tbs_pattern_on:
1250            super().start_traffic_for_calibration()
1251
1252    def stop_traffic_for_calibration(self):
1253        """
1254            If TBS pattern is set to full allocation, IP traffic wasn't started
1255        """
1256        if not self.primary_config.tbs_pattern_on:
1257            super().stop_traffic_for_calibration()
1258
1259    def get_duplex_mode(self, band):
1260        """ Determines if the band uses FDD or TDD duplex mode
1261
1262        Args:
1263            band: a band number
1264        Returns:
1265            an variable of class DuplexMode indicating if band is FDD or TDD
1266        """
1267
1268        if 33 <= int(band) <= 46:
1269            return DuplexMode.TDD
1270        else:
1271            return DuplexMode.FDD
1272
1273    def get_measured_ul_power(self, samples=5, wait_after_sample=3):
1274        """ Calculates UL power using measurements from the callbox and the
1275        calibration data.
1276
1277        Args:
1278            samples: the numble of samples to average
1279            wait_after_sample: time in seconds to wait in between samples
1280
1281        Returns:
1282            the ul power at the UE antenna ports in dBs
1283        """
1284        ul_power_sum = 0
1285        samples_left = samples
1286
1287        while samples_left > 0:
1288            ul_power_sum += self.simulator.get_measured_pusch_power()
1289            samples_left -= 1
1290            time.sleep(wait_after_sample)
1291
1292        # Got enough samples, return calibrated average
1293        if self.dl_path_loss:
1294            return ul_power_sum / samples + self.ul_path_loss
1295        else:
1296            self.log.warning('No uplink calibration data. Returning '
1297                             'uncalibrated values as measured by the '
1298                             'callbox.')
1299            return ul_power_sum / samples
1300