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