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