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