1# Copyright 2021 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import math 6import time 7from enum import Enum 8 9import common 10 11from autotest_lib.server.cros.cellular.simulation_utils.BaseSimulation import BaseSimulation 12from autotest_lib.server.cros.cellular.simulation_utils import BaseCellularDut 13 14 15class TransmissionMode(Enum): 16 """ Transmission modes for LTE (e.g., TM1, TM4, ...) """ 17 TM1 = "TM1" 18 TM2 = "TM2" 19 TM3 = "TM3" 20 TM4 = "TM4" 21 TM7 = "TM7" 22 TM8 = "TM8" 23 TM9 = "TM9" 24 25 26class MimoMode(Enum): 27 """ Mimo modes """ 28 MIMO_1x1 = "1x1" 29 MIMO_2x2 = "2x2" 30 MIMO_4x4 = "4x4" 31 32 33class SchedulingMode(Enum): 34 """ Traffic scheduling modes (e.g., STATIC, DYNAMIC) """ 35 DYNAMIC = "DYNAMIC" 36 STATIC = "STATIC" 37 38 39class DuplexMode(Enum): 40 """ DL/UL Duplex mode """ 41 FDD = "FDD" 42 TDD = "TDD" 43 44 45class ModulationType(Enum): 46 """DL/UL Modulation order.""" 47 QPSK = 'QPSK' 48 Q16 = '16QAM' 49 Q64 = '64QAM' 50 Q256 = '256QAM' 51 52 53class LteSimulation(BaseSimulation): 54 """ Single-carrier LTE simulation. """ 55 56 # Simulation config keywords contained in the test name 57 PARAM_FRAME_CONFIG = "tddconfig" 58 PARAM_BW = "bw" 59 PARAM_SCHEDULING = "scheduling" 60 PARAM_SCHEDULING_STATIC = "static" 61 PARAM_SCHEDULING_DYNAMIC = "dynamic" 62 PARAM_PATTERN = "pattern" 63 PARAM_TM = "tm" 64 PARAM_UL_PW = 'pul' 65 PARAM_DL_PW = 'pdl' 66 PARAM_BAND = "band" 67 PARAM_MIMO = "mimo" 68 PARAM_DL_MCS = 'dlmcs' 69 PARAM_UL_MCS = 'ulmcs' 70 PARAM_SSF = 'ssf' 71 PARAM_CFI = 'cfi' 72 PARAM_PAGING = 'paging' 73 PARAM_PHICH = 'phich' 74 PARAM_RRC_STATUS_CHANGE_TIMER = "rrcstatuschangetimer" 75 PARAM_DRX = 'drx' 76 77 # Test config keywords 78 KEY_TBS_PATTERN = "tbs_pattern_on" 79 KEY_DL_256_QAM = "256_qam_dl" 80 KEY_UL_64_QAM = "64_qam_ul" 81 82 # Units in which signal level is defined in DOWNLINK_SIGNAL_LEVEL_DICTIONARY 83 DOWNLINK_SIGNAL_LEVEL_UNITS = "RSRP" 84 85 # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz. 86 # Excellent is set to -75 since callbox B Tx power is limited to -30 dBm 87 DOWNLINK_SIGNAL_LEVEL_DICTIONARY = { 88 'excellent': -75, 89 'high': -110, 90 'medium': -115, 91 'weak': -120, 92 'disconnected': -170 93 } 94 95 # Transmitted output power for the phone (dBm) 96 UPLINK_SIGNAL_LEVEL_DICTIONARY = { 97 'max': 24, 98 'high': 13, 99 'medium': 3, 100 'low': -20 101 } 102 103 # Bandwidth [MHz] to total RBs mapping 104 total_rbs_dictionary = {20: 100, 15: 75, 10: 50, 5: 25, 3: 15, 1.4: 6} 105 106 # Bandwidth [MHz] to RB group size 107 rbg_dictionary = {20: 4, 15: 4, 10: 3, 5: 2, 3: 2, 1.4: 1} 108 109 # Bandwidth [MHz] to minimum number of DL RBs that can be assigned to a UE 110 min_dl_rbs_dictionary = {20: 16, 15: 12, 10: 9, 5: 4, 3: 4, 1.4: 2} 111 112 # Bandwidth [MHz] to minimum number of UL RBs that can be assigned to a UE 113 min_ul_rbs_dictionary = {20: 8, 15: 6, 10: 4, 5: 2, 3: 2, 1.4: 1} 114 115 # Allowed bandwidth for each band. 116 allowed_bandwidth_dictionary = { 117 1: [5, 10, 15, 20], 118 2: [1.4, 3, 5, 10, 15, 20], 119 3: [1.4, 3, 5, 10, 15, 20], 120 4: [1.4, 3, 5, 10, 15, 20], 121 5: [1.4, 3, 5, 10], 122 7: [5, 10, 15, 20], 123 8: [1.4, 3, 5, 10], 124 10: [5, 10, 15, 20], 125 11: [5, 10], 126 12: [1.4, 3, 5, 10], 127 13: [5, 10], 128 14: [5, 10], 129 17: [5, 10], 130 18: [5, 10, 15], 131 19: [5, 10, 15], 132 20: [5, 10, 15, 20], 133 21: [5, 10, 15], 134 22: [5, 10, 15, 20], 135 24: [5, 10], 136 25: [1.4, 3, 5, 10, 15, 20], 137 26: [1.4, 3, 5, 10, 15], 138 27: [1.4, 3, 5, 10], 139 28: [3, 5, 10, 15, 20], 140 29: [3, 5, 10], 141 30: [5, 10], 142 31: [1.4, 3, 5], 143 32: [5, 10, 15, 20], 144 33: [5, 10, 15, 20], 145 34: [5, 10, 15], 146 35: [1.4, 3, 5, 10, 15, 20], 147 36: [1.4, 3, 5, 10, 15, 20], 148 37: [5, 10, 15, 20], 149 38: [20], 150 39: [5, 10, 15, 20], 151 40: [5, 10, 15, 20], 152 41: [5, 10, 15, 20], 153 42: [5, 10, 15, 20], 154 43: [5, 10, 15, 20], 155 44: [3, 5, 10, 15, 20], 156 45: [5, 10, 15, 20], 157 46: [10, 20], 158 47: [10, 20], 159 48: [5, 10, 15, 20], 160 49: [10, 20], 161 50: [3, 5, 10, 15, 20], 162 51: [3, 5], 163 52: [5, 10, 15, 20], 164 65: [5, 10, 15, 20], 165 66: [1.4, 3, 5, 10, 15, 20], 166 67: [5, 10, 15, 20], 167 68: [5, 10, 15], 168 69: [5], 169 70: [5, 10, 15], 170 71: [5, 10, 15, 20], 171 72: [1.4, 3, 5], 172 73: [1.4, 3, 5], 173 74: [1.4, 3, 5, 10, 15, 20], 174 75: [5, 10, 15, 20], 175 76: [5], 176 85: [5, 10], 177 252: [20], 178 255: [20] 179 } 180 181 # Peak throughput lookup tables for each TDD subframe 182 # configuration and bandwidth 183 # yapf: disable 184 tdd_config4_tput_lut = { 185 0: { 186 5: {'DL': 3.82, 'UL': 2.63}, 187 10: {'DL': 11.31,'UL': 9.03}, 188 15: {'DL': 16.9, 'UL': 20.62}, 189 20: {'DL': 22.88, 'UL': 28.43} 190 }, 191 1: { 192 5: {'DL': 6.13, 'UL': 4.08}, 193 10: {'DL': 18.36, 'UL': 9.69}, 194 15: {'DL': 28.62, 'UL': 14.21}, 195 20: {'DL': 39.04, 'UL': 19.23} 196 }, 197 2: { 198 5: {'DL': 5.68, 'UL': 2.30}, 199 10: {'DL': 25.51, 'UL': 4.68}, 200 15: {'DL': 39.3, 'UL': 7.13}, 201 20: {'DL': 53.64, 'UL': 9.72} 202 }, 203 3: { 204 5: {'DL': 8.26, 'UL': 3.45}, 205 10: {'DL': 23.20, 'UL': 6.99}, 206 15: {'DL': 35.35, 'UL': 10.75}, 207 20: {'DL': 48.3, 'UL': 14.6} 208 }, 209 4: { 210 5: {'DL': 6.16, 'UL': 2.30}, 211 10: {'DL': 26.77, 'UL': 4.68}, 212 15: {'DL': 40.7, 'UL': 7.18}, 213 20: {'DL': 55.6, 'UL': 9.73} 214 }, 215 5: { 216 5: {'DL': 6.91, 'UL': 1.12}, 217 10: {'DL': 30.33, 'UL': 2.33}, 218 15: {'DL': 46.04, 'UL': 3.54}, 219 20: {'DL': 62.9, 'UL': 4.83} 220 }, 221 6: { 222 5: {'DL': 6.13, 'UL': 4.13}, 223 10: {'DL': 14.79, 'UL': 11.98}, 224 15: {'DL': 23.28, 'UL': 17.46}, 225 20: {'DL': 31.75, 'UL': 23.95} 226 } 227 } 228 229 tdd_config3_tput_lut = { 230 0: { 231 5: {'DL': 5.04, 'UL': 3.7}, 232 10: {'DL': 15.11, 'UL': 17.56}, 233 15: {'DL': 22.59, 'UL': 30.31}, 234 20: {'DL': 30.41, 'UL': 41.61} 235 }, 236 1: { 237 5: {'DL': 8.07, 'UL': 5.66}, 238 10: {'DL': 24.58, 'UL': 13.66}, 239 15: {'DL': 39.05, 'UL': 20.68}, 240 20: {'DL': 51.59, 'UL': 28.76} 241 }, 242 2: { 243 5: {'DL': 7.59, 'UL': 3.31}, 244 10: {'DL': 34.08, 'UL': 6.93}, 245 15: {'DL': 53.64, 'UL': 10.51}, 246 20: {'DL': 70.55, 'UL': 14.41} 247 }, 248 3: { 249 5: {'DL': 10.9, 'UL': 5.0}, 250 10: {'DL': 30.99, 'UL': 10.25}, 251 15: {'DL': 48.3, 'UL': 15.81}, 252 20: {'DL': 63.24, 'UL': 21.65} 253 }, 254 4: { 255 5: {'DL': 8.11, 'UL': 3.32}, 256 10: {'DL': 35.74, 'UL': 6.95}, 257 15: {'DL': 55.6, 'UL': 10.51}, 258 20: {'DL': 72.72, 'UL': 14.41} 259 }, 260 5: { 261 5: {'DL': 9.28, 'UL': 1.57}, 262 10: {'DL': 40.49, 'UL': 3.44}, 263 15: {'DL': 62.9, 'UL': 5.23}, 264 20: {'DL': 82.21, 'UL': 7.15} 265 }, 266 6: { 267 5: {'DL': 8.06, 'UL': 5.74}, 268 10: {'DL': 19.82, 'UL': 17.51}, 269 15: {'DL': 31.75, 'UL': 25.77}, 270 20: {'DL': 42.12, 'UL': 34.91} 271 } 272 } 273 274 tdd_config2_tput_lut = { 275 0: { 276 5: {'DL': 3.11, 'UL': 2.55}, 277 10: {'DL': 9.93, 'UL': 11.1}, 278 15: {'DL': 13.9, 'UL': 21.51}, 279 20: {'DL': 20.02, 'UL': 41.66} 280 }, 281 1: { 282 5: {'DL': 5.33, 'UL': 4.27}, 283 10: {'DL': 15.14, 'UL': 13.95}, 284 15: {'DL': 33.84, 'UL': 19.73}, 285 20: {'DL': 44.61, 'UL': 27.35} 286 }, 287 2: { 288 5: {'DL': 6.87, 'UL': 3.32}, 289 10: {'DL': 17.06, 'UL': 6.76}, 290 15: {'DL': 49.63, 'UL': 10.5}, 291 20: {'DL': 65.2, 'UL': 14.41} 292 }, 293 3: { 294 5: {'DL': 5.41, 'UL': 4.17}, 295 10: {'DL': 16.89, 'UL': 9.73}, 296 15: {'DL': 44.29, 'UL': 15.7}, 297 20: {'DL': 53.95, 'UL': 19.85} 298 }, 299 4: { 300 5: {'DL': 8.7, 'UL': 3.32}, 301 10: {'DL': 17.58, 'UL': 6.76}, 302 15: {'DL': 51.08, 'UL': 10.47}, 303 20: {'DL': 66.45, 'UL': 14.38} 304 }, 305 5: { 306 5: {'DL': 9.46, 'UL': 1.55}, 307 10: {'DL': 19.02, 'UL': 3.48}, 308 15: {'DL': 58.89, 'UL': 5.23}, 309 20: {'DL': 76.85, 'UL': 7.1} 310 }, 311 6: { 312 5: {'DL': 4.74, 'UL': 3.9}, 313 10: {'DL': 12.32, 'UL': 13.37}, 314 15: {'DL': 27.74, 'UL': 25.02}, 315 20: {'DL': 35.48, 'UL': 32.95} 316 } 317 } 318 319 tdd_config1_tput_lut = { 320 0: { 321 5: {'DL': 4.25, 'UL': 3.35}, 322 10: {'DL': 8.38, 'UL': 7.22}, 323 15: {'DL': 12.41, 'UL': 13.91}, 324 20: {'DL': 16.27, 'UL': 24.09} 325 }, 326 1: { 327 5: {'DL': 7.28, 'UL': 4.61}, 328 10: {'DL': 14.73, 'UL': 9.69}, 329 15: {'DL': 21.91, 'UL': 13.86}, 330 20: {'DL': 27.63, 'UL': 17.18} 331 }, 332 2: { 333 5: {'DL': 10.37, 'UL': 2.27}, 334 10: {'DL': 20.92, 'UL': 4.66}, 335 15: {'DL': 31.01, 'UL': 7.04}, 336 20: {'DL': 42.03, 'UL': 9.75} 337 }, 338 3: { 339 5: {'DL': 9.25, 'UL': 3.44}, 340 10: {'DL': 18.38, 'UL': 6.95}, 341 15: {'DL': 27.59, 'UL': 10.62}, 342 20: {'DL': 34.85, 'UL': 13.45} 343 }, 344 4: { 345 5: {'DL': 10.71, 'UL': 2.26}, 346 10: {'DL': 21.54, 'UL': 4.67}, 347 15: {'DL': 31.91, 'UL': 7.2}, 348 20: {'DL': 43.35, 'UL': 9.74} 349 }, 350 5: { 351 5: {'DL': 12.34, 'UL': 1.08}, 352 10: {'DL': 24.78, 'UL': 2.34}, 353 15: {'DL': 36.68, 'UL': 3.57}, 354 20: {'DL': 49.84, 'UL': 4.81} 355 }, 356 6: { 357 5: {'DL': 5.76, 'UL': 4.41}, 358 10: {'DL': 11.68, 'UL': 9.7}, 359 15: {'DL': 17.34, 'UL': 17.95}, 360 20: {'DL': 23.5, 'UL': 23.42} 361 } 362 } 363 # yapf: enable 364 365 # Peak throughput lookup table dictionary 366 tdd_config_tput_lut_dict = { 367 'TDD_CONFIG1': 368 tdd_config1_tput_lut, # DL 256QAM, UL 64QAM & TBS turned OFF 369 'TDD_CONFIG2': 370 tdd_config2_tput_lut, # DL 256QAM, UL 64 QAM turned ON & TBS OFF 371 'TDD_CONFIG3': 372 tdd_config3_tput_lut, # DL 256QAM, UL 64QAM & TBS turned ON 373 'TDD_CONFIG4': 374 tdd_config4_tput_lut # DL 256QAM, UL 64 QAM turned OFF & TBS ON 375 } 376 377 class BtsConfig(BaseSimulation.BtsConfig): 378 """ Extension of the BaseBtsConfig to implement parameters that are 379 exclusive to LTE. 380 381 Attributes: 382 band: an integer indicating the required band number. 383 dlul_config: an integer indicating the TDD config number. 384 ssf_config: an integer indicating the Special Sub-Frame config. 385 bandwidth: a float indicating the required channel bandwidth. 386 mimo_mode: an instance of LteSimulation.MimoMode indicating the 387 required MIMO mode for the downlink signal. 388 transmission_mode: an instance of LteSimulation.TransmissionMode 389 indicating the required TM. 390 scheduling_mode: an instance of LteSimulation.SchedulingMode 391 indicating whether to use Static or Dynamic scheduling. 392 dl_rbs: an integer indicating the number of downlink RBs 393 ul_rbs: an integer indicating the number of uplink RBs 394 dl_mcs: an integer indicating the MCS for the downlink signal 395 ul_mcs: an integer indicating the MCS for the uplink signal 396 dl_modulation_order: a string indicating a DL modulation scheme 397 ul_modulation_order: a string indicating an UL modulation scheme 398 tbs_pattern_on: a boolean indicating whether full allocation mode 399 should be used or not 400 dl_channel: an integer indicating the downlink channel number 401 cfi: an integer indicating the Control Format Indicator 402 paging_cycle: an integer indicating the paging cycle duration in 403 milliseconds 404 phich: a string indicating the PHICH group size parameter 405 drx_connected_mode: a boolean indicating whether cDRX mode is 406 on or off 407 drx_on_duration_timer: number of PDCCH subframes representing 408 DRX on duration 409 drx_inactivity_timer: number of PDCCH subframes to wait before 410 entering DRX mode 411 drx_retransmission_timer: number of consecutive PDCCH subframes 412 to wait for retransmission 413 drx_long_cycle: number of subframes representing one long DRX cycle. 414 One cycle consists of DRX sleep + DRX on duration 415 drx_long_cycle_offset: number representing offset in range 416 0 to drx_long_cycle - 1 417 """ 418 419 def __init__(self): 420 """ Initialize the base station config by setting all its 421 parameters to None. """ 422 super(LteSimulation.BtsConfig, self).__init__() 423 self.band = None 424 self.dlul_config = None 425 self.ssf_config = None 426 self.bandwidth = None 427 self.mimo_mode = None 428 self.transmission_mode = None 429 self.scheduling_mode = None 430 self.dl_rbs = None 431 self.ul_rbs = None 432 self.dl_mcs = None 433 self.ul_mcs = None 434 self.dl_modulation_order = None 435 self.ul_modulation_order = None 436 self.tbs_pattern_on = None 437 self.dl_channel = None 438 self.cfi = None 439 self.paging_cycle = None 440 self.phich = None 441 self.drx_connected_mode = None 442 self.drx_on_duration_timer = None 443 self.drx_inactivity_timer = None 444 self.drx_retransmission_timer = None 445 self.drx_long_cycle = None 446 self.drx_long_cycle_offset = None 447 448 def __init__(self, simulator, log, dut, test_config, calibration_table): 449 """ Initializes the simulator for a single-carrier LTE simulation. 450 451 Loads a simple LTE simulation environment with 1 basestation. 452 453 Args: 454 simulator: a cellular simulator controller 455 log: a logger handle 456 dut: a device handler implementing BaseCellularDut 457 test_config: test configuration obtained from the config file 458 calibration_table: a dictionary containing path losses for 459 different bands. 460 461 """ 462 463 super(LteSimulation, self).__init__(simulator, log, dut, test_config, 464 calibration_table) 465 466 self.dut.set_preferred_network_type( 467 BaseCellularDut.PreferredNetworkType.LTE_ONLY) 468 469 # Get TBS pattern setting from the test configuration 470 if self.KEY_TBS_PATTERN not in test_config: 471 self.log.warning("The key '{}' is not set in the config file. " 472 "Setting to true by default.".format( 473 self.KEY_TBS_PATTERN)) 474 self.primary_config.tbs_pattern_on = test_config.get( 475 self.KEY_TBS_PATTERN, True) 476 477 # Get the 256-QAM setting from the test configuration 478 if self.KEY_DL_256_QAM not in test_config: 479 self.log.warning("The key '{}' is not set in the config file. " 480 "Setting to false by default.".format( 481 self.KEY_DL_256_QAM)) 482 483 self.dl_256_qam = test_config.get(self.KEY_DL_256_QAM, False) 484 485 if self.dl_256_qam: 486 if not self.simulator.LTE_SUPPORTS_DL_256QAM: 487 self.log.warning("The key '{}' is set to true but the " 488 "simulator doesn't support that modulation " 489 "order.".format(self.KEY_DL_256_QAM)) 490 self.dl_256_qam = False 491 else: 492 self.primary_config.dl_modulation_order = ModulationType.Q256 493 494 else: 495 self.log.warning( 496 'dl modulation 256QAM is not specified in config, ' 497 'setting to default value 64QAM') 498 self.primary_config.dl_modulation_order = ModulationType.Q64 499 # Get the 64-QAM setting from the test configuration 500 if self.KEY_UL_64_QAM not in test_config: 501 self.log.warning("The key '{}' is not set in the config file. " 502 "Setting to false by default.".format( 503 self.KEY_UL_64_QAM)) 504 505 self.ul_64_qam = test_config.get(self.KEY_UL_64_QAM, False) 506 507 if self.ul_64_qam: 508 if not self.simulator.LTE_SUPPORTS_UL_64QAM: 509 self.log.warning("The key '{}' is set to true but the " 510 "simulator doesn't support that modulation " 511 "order.".format(self.KEY_UL_64_QAM)) 512 self.ul_64_qam = False 513 else: 514 self.primary_config.ul_modulation_order = ModulationType.Q64 515 else: 516 self.log.warning('ul modulation 64QAM is not specified in config, ' 517 'setting to default value 16QAM') 518 self.primary_config.ul_modulation_order = ModulationType.Q16 519 520 self.simulator.configure_bts(self.primary_config) 521 522 def setup_simulator(self): 523 """ Do initial configuration in the simulator. """ 524 self.simulator.setup_lte_scenario() 525 526 def parse_parameters(self, parameters): 527 """ Configs an LTE simulation using a list of parameters. 528 529 Calls the parent method first, then consumes parameters specific to LTE. 530 531 Args: 532 parameters: list of parameters 533 """ 534 535 # Instantiate a new configuration object 536 new_config = self.BtsConfig() 537 538 # Setup band 539 540 values = self.consume_parameter(parameters, self.PARAM_BAND, 1) 541 542 if not values: 543 raise ValueError( 544 "The test name needs to include parameter '{}' followed by " 545 "the required band number.".format(self.PARAM_BAND)) 546 547 new_config.band = values[1] 548 549 # Set TDD-only configs 550 if self.get_duplex_mode(new_config.band) == DuplexMode.TDD: 551 552 # Sub-frame DL/UL config 553 values = self.consume_parameter(parameters, 554 self.PARAM_FRAME_CONFIG, 1) 555 if not values: 556 raise ValueError( 557 "When a TDD band is selected the frame " 558 "structure has to be indicated with the '{}' " 559 "parameter followed by a number from 0 to 6.".format( 560 self.PARAM_FRAME_CONFIG)) 561 562 new_config.dlul_config = int(values[1]) 563 564 # Special Sub-Frame configuration 565 values = self.consume_parameter(parameters, self.PARAM_SSF, 1) 566 567 if not values: 568 self.log.warning( 569 'The {} parameter was not provided. Setting ' 570 'Special Sub-Frame config to 6 by default.'.format( 571 self.PARAM_SSF)) 572 new_config.ssf_config = 6 573 else: 574 new_config.ssf_config = int(values[1]) 575 576 # Setup bandwidth 577 578 values = self.consume_parameter(parameters, self.PARAM_BW, 1) 579 580 if not values: 581 raise ValueError( 582 "The test name needs to include parameter {} followed by an " 583 "int value (to indicate 1.4 MHz use 14).".format( 584 self.PARAM_BW)) 585 586 bw = float(values[1]) 587 588 if bw == 14: 589 bw = 1.4 590 591 new_config.bandwidth = bw 592 593 # Setup mimo mode 594 595 values = self.consume_parameter(parameters, self.PARAM_MIMO, 1) 596 597 if not values: 598 raise ValueError( 599 "The test name needs to include parameter '{}' followed by the " 600 "mimo mode.".format(self.PARAM_MIMO)) 601 602 for mimo_mode in MimoMode: 603 if values[1] == mimo_mode.value: 604 new_config.mimo_mode = mimo_mode 605 break 606 else: 607 raise ValueError("The {} parameter needs to be followed by either " 608 "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO)) 609 610 if (new_config.mimo_mode == MimoMode.MIMO_4x4 611 and not self.simulator.LTE_SUPPORTS_4X4_MIMO): 612 raise ValueError("The test requires 4x4 MIMO, but that is not " 613 "supported by the cellular simulator.") 614 615 # Setup transmission mode 616 617 values = self.consume_parameter(parameters, self.PARAM_TM, 1) 618 619 if not values: 620 raise ValueError( 621 "The test name needs to include parameter {} followed by an " 622 "int value from 1 to 4 indicating transmission mode.". 623 format(self.PARAM_TM)) 624 625 for tm in TransmissionMode: 626 if values[1] == tm.value[2:]: 627 new_config.transmission_mode = tm 628 break 629 else: 630 raise ValueError("The {} parameter needs to be followed by either " 631 "TM1, TM2, TM3, TM4, TM7, TM8 or TM9.".format( 632 self.PARAM_MIMO)) 633 634 # Setup scheduling mode 635 636 values = self.consume_parameter(parameters, self.PARAM_SCHEDULING, 1) 637 638 if not values: 639 new_config.scheduling_mode = SchedulingMode.STATIC 640 self.log.warning( 641 "The test name does not include the '{}' parameter. Setting to " 642 "static by default.".format(self.PARAM_SCHEDULING)) 643 elif values[1] == self.PARAM_SCHEDULING_DYNAMIC: 644 new_config.scheduling_mode = SchedulingMode.DYNAMIC 645 elif values[1] == self.PARAM_SCHEDULING_STATIC: 646 new_config.scheduling_mode = SchedulingMode.STATIC 647 else: 648 raise ValueError( 649 "The test name parameter '{}' has to be followed by either " 650 "'dynamic' or 'static'.".format(self.PARAM_SCHEDULING)) 651 652 if new_config.scheduling_mode == SchedulingMode.STATIC: 653 654 values = self.consume_parameter(parameters, self.PARAM_PATTERN, 2) 655 656 if not values: 657 self.log.warning( 658 "The '{}' parameter was not set, using 100% RBs for both " 659 "DL and UL. To set the percentages of total RBs include " 660 "the '{}' parameter followed by two ints separated by an " 661 "underscore indicating downlink and uplink percentages." 662 .format(self.PARAM_PATTERN, self.PARAM_PATTERN)) 663 dl_pattern = 100 664 ul_pattern = 100 665 else: 666 dl_pattern = int(values[1]) 667 ul_pattern = int(values[2]) 668 669 if not (0 <= dl_pattern <= 100 and 0 <= ul_pattern <= 100): 670 raise ValueError( 671 "The scheduling pattern parameters need to be two " 672 "positive numbers between 0 and 100.") 673 674 new_config.dl_rbs, new_config.ul_rbs = ( 675 self.allocation_percentages_to_rbs( 676 new_config.bandwidth, new_config.transmission_mode, 677 dl_pattern, ul_pattern)) 678 679 # Look for a DL MCS configuration in the test parameters. If it is 680 # not present, use a default value. 681 dlmcs = self.consume_parameter(parameters, self.PARAM_DL_MCS, 1) 682 683 if dlmcs: 684 new_config.dl_mcs = int(dlmcs[1]) 685 else: 686 self.log.warning( 687 'The test name does not include the {} parameter. Setting ' 688 'to the max value by default'.format( 689 self.PARAM_DL_MCS)) 690 if self.dl_256_qam and new_config.bandwidth == 1.4: 691 new_config.dl_mcs = 26 692 elif (not self.dl_256_qam 693 and self.primary_config.tbs_pattern_on 694 and new_config.bandwidth != 1.4): 695 new_config.dl_mcs = 28 696 else: 697 new_config.dl_mcs = 27 698 699 # Look for an UL MCS configuration in the test parameters. If it is 700 # not present, use a default value. 701 ulmcs = self.consume_parameter(parameters, self.PARAM_UL_MCS, 1) 702 703 if ulmcs: 704 new_config.ul_mcs = int(ulmcs[1]) 705 else: 706 self.log.warning( 707 'The test name does not include the {} parameter. Setting ' 708 'to the max value by default'.format( 709 self.PARAM_UL_MCS)) 710 if self.ul_64_qam: 711 new_config.ul_mcs = 28 712 else: 713 new_config.ul_mcs = 23 714 715 # Configure the simulation for DRX mode 716 717 drx = self.consume_parameter(parameters, self.PARAM_DRX, 5) 718 719 if drx and len(drx) == 6: 720 new_config.drx_connected_mode = True 721 new_config.drx_on_duration_timer = drx[1] 722 new_config.drx_inactivity_timer = drx[2] 723 new_config.drx_retransmission_timer = drx[3] 724 new_config.drx_long_cycle = drx[4] 725 try: 726 long_cycle = int(drx[4]) 727 long_cycle_offset = int(drx[5]) 728 if long_cycle_offset in range(0, long_cycle): 729 new_config.drx_long_cycle_offset = long_cycle_offset 730 else: 731 self.log.error( 732 ("The cDRX long cycle offset must be in the " 733 "range 0 to (long cycle - 1). Setting " 734 "long cycle offset to 0")) 735 new_config.drx_long_cycle_offset = 0 736 737 except ValueError: 738 self.log.error(("cDRX long cycle and long cycle offset " 739 "must be integers. Disabling cDRX mode.")) 740 new_config.drx_connected_mode = False 741 else: 742 self.log.warning(("DRX mode was not configured properly. " 743 "Please provide the following 5 values: " 744 "1) DRX on duration timer " 745 "2) Inactivity timer " 746 "3) Retransmission timer " 747 "4) Long DRX cycle duration " 748 "5) Long DRX cycle offset " 749 "Example: drx_2_6_16_20_0")) 750 751 # Setup LTE RRC status change function and timer for LTE idle test case 752 values = self.consume_parameter(parameters, 753 self.PARAM_RRC_STATUS_CHANGE_TIMER, 1) 754 if not values: 755 self.log.info( 756 "The test name does not include the '{}' parameter. Disabled " 757 "by default.".format(self.PARAM_RRC_STATUS_CHANGE_TIMER)) 758 self.simulator.set_lte_rrc_state_change_timer(False) 759 else: 760 timer = int(values[1]) 761 self.simulator.set_lte_rrc_state_change_timer(True, timer) 762 self.rrc_sc_timer = timer 763 764 # Channel Control Indicator 765 values = self.consume_parameter(parameters, self.PARAM_CFI, 1) 766 767 if not values: 768 self.log.warning('The {} parameter was not provided. Setting ' 769 'CFI to BESTEFFORT.'.format(self.PARAM_CFI)) 770 new_config.cfi = 'BESTEFFORT' 771 else: 772 new_config.cfi = values[1] 773 774 # PHICH group size 775 values = self.consume_parameter(parameters, self.PARAM_PHICH, 1) 776 777 if not values: 778 self.log.warning('The {} parameter was not provided. Setting ' 779 'PHICH group size to 1 by default.'.format( 780 self.PARAM_PHICH)) 781 new_config.phich = '1' 782 else: 783 if values[1] == '16': 784 new_config.phich = '1/6' 785 elif values[1] == '12': 786 new_config.phich = '1/2' 787 elif values[1] in ['1/6', '1/2', '1', '2']: 788 new_config.phich = values[1] 789 else: 790 raise ValueError('The {} parameter can only be followed by 1,' 791 '2, 1/2 (or 12) and 1/6 (or 16).'.format( 792 self.PARAM_PHICH)) 793 794 # Paging cycle duration 795 values = self.consume_parameter(parameters, self.PARAM_PAGING, 1) 796 797 if not values: 798 self.log.warning('The {} parameter was not provided. Setting ' 799 'paging cycle duration to 1280 ms by ' 800 'default.'.format(self.PARAM_PAGING)) 801 new_config.paging_cycle = 1280 802 else: 803 try: 804 new_config.paging_cycle = int(values[1]) 805 except ValueError: 806 raise ValueError( 807 'The {} parameter has to be followed by the paging cycle ' 808 'duration in milliseconds.'.format(self.PARAM_PAGING)) 809 810 # Get uplink power 811 812 ul_power = self.get_uplink_power_from_parameters(parameters) 813 814 # Power is not set on the callbox until after the simulation is 815 # started. Saving this value in a variable for later 816 self.sim_ul_power = ul_power 817 818 # Get downlink power 819 820 dl_power = self.get_downlink_power_from_parameters(parameters) 821 822 # Power is not set on the callbox until after the simulation is 823 # started. Saving this value in a variable for later 824 self.sim_dl_power = dl_power 825 826 # Setup the base station with the obtained configuration and then save 827 # these parameters in the current configuration object 828 self.simulator.configure_bts(new_config) 829 self.primary_config.incorporate(new_config) 830 831 # Now that the band is set, calibrate the link if necessary 832 self.load_pathloss_if_required() 833 834 def calibrated_downlink_rx_power(self, bts_config, rsrp): 835 """ LTE simulation overrides this method so that it can convert from 836 RSRP to total signal power transmitted from the basestation. 837 838 Args: 839 bts_config: the current configuration at the base station 840 rsrp: desired rsrp, contained in a key value pair 841 """ 842 843 power = self.rsrp_to_signal_power(rsrp, bts_config) 844 845 self.log.info( 846 "Setting downlink signal level to {} RSRP ({} dBm)".format( 847 rsrp, power)) 848 849 # Use parent method to calculate signal level 850 return super(LteSimulation, 851 self).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 Downlink 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" 1024 if bts_config.tbs_pattern_on else "OFF", 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" 1106 if bts_config.tbs_pattern_on else "OFF", 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 1297 def send_sms(self, sms_message): 1298 """ Sets the SMS message for the simulation. """ 1299 self.simulator.send_sms(sms_message) 1300