1#!/usr/bin/env python3.4 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 time 18import math 19from enum import Enum 20 21from acts.controllers.anritsu_lib.md8475a import BtsBandwidth 22from acts.controllers.anritsu_lib.md8475a import BtsPacketRate 23from acts.test_utils.power.tel_simulations.BaseSimulation import BaseSimulation 24from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY 25 26 27class LteSimulation(BaseSimulation): 28 """ Simple LTE simulation with only one basestation. 29 30 """ 31 32 # Simulation config files in the callbox computer. 33 # These should be replaced in the future by setting up 34 # the same configuration manually. 35 LTE_BASIC_SIM_FILE = 'SIM_default_LTE' 36 LTE_BASIC_CELL_FILE = 'CELL_LTE_config' 37 38 # Simulation config keywords contained in the test name 39 PARAM_FRAME_CONFIG = "tddconfig" 40 PARAM_BW = "bw" 41 PARAM_SCHEDULING = "scheduling" 42 PARAM_SCHEDULING_STATIC = "static" 43 PARAM_SCHEDULING_DYNAMIC = "dynamic" 44 PARAM_PATTERN = "pattern" 45 PARAM_TM = "tm" 46 PARAM_UL_PW = 'pul' 47 PARAM_DL_PW = 'pdl' 48 PARAM_BAND = "band" 49 PARAM_MIMO = "mimo" 50 51 # Test config keywords 52 KEY_TBS_PATTERN = "tbs_pattern_on" 53 KEY_DL_256_QAM = "256_qam_dl" 54 KEY_UL_64_QAM = "64_qam_ul" 55 56 class TransmissionMode(Enum): 57 ''' Transmission modes for LTE (e.g., TM1, TM4, ..) 58 59 ''' 60 TM1 = "TM1" 61 TM2 = "TM2" 62 TM3 = "TM3" 63 TM4 = "TM4" 64 TM7 = "TM7" 65 TM8 = "TM8" 66 TM9 = "TM9" 67 68 class MimoMode(Enum): 69 """ Mimo modes """ 70 71 MIMO_1x1 = "1x1" 72 MIMO_2x2 = "2x2" 73 MIMO_4x4 = "4x4" 74 75 class SchedulingMode(Enum): 76 ''' Traffic scheduling modes (e.g., STATIC, DYNAMIC) 77 78 ''' 79 DYNAMIC = "DYNAMIC" 80 STATIC = "STATIC" 81 82 class DuplexMode(Enum): 83 ''' DL/UL Duplex mode 84 85 ''' 86 FDD = "FDD" 87 TDD = "TDD" 88 89 # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz. 90 # Excellent is set to -75 since callbox B Tx power is limited to -30 dBm 91 downlink_rsrp_dictionary = { 92 'excellent': -75, 93 'high': -110, 94 'medium': -115, 95 'weak': -120 96 } 97 98 # Transmitted output power for the phone (dBm) 99 uplink_signal_level_dictionary = { 100 'max': 23, 101 'high': 13, 102 'medium': 3, 103 'low': -20 104 } 105 106 # Total RBs for each bandwidth 107 108 total_rbs_dictionary = { 109 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 100, 110 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 75, 111 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 50, 112 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 25, 113 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 15, 114 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 6 115 } 116 117 # RB groups for each bandwidth 118 119 rbg_dictionary = { 120 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 4, 121 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 4, 122 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 3, 123 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 2, 124 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 2, 125 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 1 126 } 127 128 # Table of minimum number of RBs. This is needed to achieve peak 129 # throughput. 130 131 min_dl_rbs_dictionary = { 132 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 16, 133 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 12, 134 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 9, 135 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 4, 136 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 4, 137 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 2 138 } 139 140 min_ul_rbs_dictionary = { 141 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 8, 142 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 6, 143 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 4, 144 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 2, 145 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 2, 146 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 1 147 } 148 149 # Allowed bandwidth for each band. 150 allowed_bandwidth_dictionary = { 151 1: [5, 10, 15, 20], 152 2: [1.4, 3, 5, 10, 15, 20], 153 3: [1.4, 3, 5, 10, 15, 20], 154 4: [1.4, 3, 5, 10, 15, 20], 155 5: [1.4, 3, 5, 10], 156 7: [5, 10, 15, 20], 157 8: [1.4, 3, 5, 10], 158 10: [5, 10, 15, 20], 159 11: [5, 10], 160 12: [1.4, 3, 5, 10], 161 13: [5, 10], 162 14: [5, 10], 163 17: [5, 10], 164 18: [5, 10, 15], 165 19: [5, 10, 15], 166 20: [5, 10, 15, 20], 167 21: [5, 10, 15], 168 22: [5, 10, 15, 20], 169 24: [5, 10], 170 25: [1.4, 3, 5, 10, 15, 20], 171 26: [1.4, 3, 5, 10, 15], 172 27: [1.4, 3, 5, 10], 173 28: [3, 5, 10, 15, 20], 174 29: [3, 5, 10], 175 30: [5, 10], 176 31: [1.4, 3, 5], 177 32: [5, 10, 15, 20], 178 33: [5, 10, 15, 20], 179 34: [5, 10, 15], 180 35: [1.4, 3, 5, 10, 15, 20], 181 36: [1.4, 3, 5, 10, 15, 20], 182 37: [5, 10, 15, 20], 183 38: [20], 184 39: [5, 10, 15, 20], 185 40: [5, 10, 15, 20], 186 41: [5, 10, 15, 20], 187 42: [5, 10, 15, 20], 188 43: [5, 10, 15, 20], 189 44: [3, 5, 10, 15, 20], 190 45: [5, 10, 15, 20], 191 46: [10, 20], 192 47: [10, 20], 193 48: [5, 10, 15, 20], 194 49: [10, 20], 195 50: [3, 5, 10, 15, 20], 196 51: [3, 5], 197 52: [5, 10, 15, 20], 198 65: [5, 10, 15, 20], 199 66: [1.4, 3, 5, 10, 15, 20], 200 67: [5, 10, 15, 20], 201 68: [5, 10, 15], 202 69: [5], 203 70: [5, 10, 15], 204 71: [5, 10, 15, 20], 205 72: [1.4, 3, 5], 206 73: [1.4, 3, 5], 207 74: [1.4, 3, 5, 10, 15, 20], 208 75: [5, 10, 15, 20], 209 76: [5], 210 85: [5, 10], 211 252: [20], 212 255: [20] 213 } 214 215 def __init__(self, anritsu, log, dut, test_config, calibration_table): 216 """ Configures Anritsu system for LTE simulation with 1 basetation 217 218 Loads a simple LTE simulation enviroment with 1 basestation. 219 220 Args: 221 anritsu: the Anritsu callbox controller 222 log: a logger handle 223 dut: the android device handler 224 test_config: test configuration obtained from the config file 225 calibration_table: a dictionary containing path losses for 226 different bands. 227 228 """ 229 230 super().__init__(anritsu, log, dut, test_config, calibration_table) 231 self.file_path = 'C:\\Users\\MD8475{}\\Documents\\DAN_configs\\'.format( 232 self.anritsu._md8475_version) 233 234 if self.anritsu._md8475_version == 'A': 235 self.sim_file_path = "{}{}.wnssp".format(self.file_path, 236 self.LTE_BASIC_SIM_FILE) 237 self.cell_file_path = "{}{}.wnscp".format(self.file_path, 238 self.LTE_BASIC_CELL_FILE) 239 else: 240 self.sim_file_path = "{}{}.wnssp2".format(self.file_path, 241 self.LTE_BASIC_SIM_FILE) 242 self.cell_file_path = "{}{}.wnscp2".format( 243 self.file_path, self.LTE_BASIC_CELL_FILE) 244 245 anritsu.load_simulation_paramfile(self.sim_file_path) 246 anritsu.load_cell_paramfile(self.cell_file_path) 247 248 if not dut.droid.telephonySetPreferredNetworkTypesForSubscription( 249 NETWORK_MODE_LTE_ONLY, 250 dut.droid.subscriptionGetDefaultSubId()): 251 log.error("Couldn't set preferred network type.") 252 else: 253 log.info("Preferred network type set.") 254 255 # Get TBS pattern setting from the test configuration 256 if self.KEY_TBS_PATTERN not in test_config: 257 self.log.warning("The key '{}' is not set in the config file. " 258 "Setting to true by default.".format( 259 self.KEY_TBS_PATTERN)) 260 261 self.tbs_pattern_on = test_config.get(self.KEY_TBS_PATTERN, True) 262 263 # Get the 256-QAM setting from the test configuration 264 if self.KEY_DL_256_QAM not in test_config: 265 self.log.warning("The key '{}' is not set in the config file. " 266 "Setting to false by default.".format( 267 self.KEY_DL_256_QAM)) 268 269 self.dl_256_qam = test_config.get(self.KEY_DL_256_QAM, False) 270 271 if self.dl_256_qam: 272 if anritsu._md8475_version == 'A': 273 self.log.warning("The key '{}' is set to true but MD8475A " 274 "callbox doesn't support that modulation " 275 "order.".format(self.KEY_DL_256_QAM)) 276 self.dl_256_qam = False 277 else: 278 self.bts1.lte_dl_modulation_order = "256QAM" 279 280 # Get the 64-QAM setting from the test configuration 281 if self.KEY_UL_64_QAM not in test_config: 282 self.log.warning("The key '{}' is not set in the config file. " 283 "Setting to false by default.".format( 284 self.KEY_UL_64_QAM)) 285 286 self.ul_64_qam = test_config.get(self.KEY_UL_64_QAM, False) 287 288 if self.ul_64_qam: 289 if anritsu._md8475_version == 'A': 290 self.log.warning("The key '{}' is set to true but MD8475A " 291 "callbox doesn't support that modulation " 292 "order.".format(self.KEY_UL_64_QAM)) 293 self.ul_64_qam = False 294 else: 295 self.bts1.lte_ul_modulation_order = "64QAM" 296 297 def parse_parameters(self, parameters): 298 """ Configs an LTE simulation using a list of parameters. 299 300 Calls the parent method first, then consumes parameters specific to LTE. 301 302 Args: 303 parameters: list of parameters 304 """ 305 306 super().parse_parameters(parameters) 307 308 # Setup band 309 310 values = self.consume_parameter(parameters, self.PARAM_BAND, 1) 311 312 if not values: 313 raise ValueError( 314 "The test name needs to include parameter '{}' followed by " 315 "the required band number.".format(self.PARAM_BAND)) 316 317 band = values[1] 318 319 self.set_band(self.bts1, band) 320 321 # Set DL/UL frame configuration 322 if self.get_duplex_mode(band) == self.DuplexMode.TDD: 323 324 values = self.consume_parameter(parameters, 325 self.PARAM_FRAME_CONFIG, 1) 326 if not values: 327 raise ValueError("When a TDD band is selected the frame " 328 "structure has to be indicated with the '{}' " 329 "parameter followed by a number from 0 to 6." 330 .format(self.PARAM_FRAME_CONFIG)) 331 332 frame_config = int(values[1]) 333 334 self.set_dlul_configuration(self.bts1, frame_config) 335 336 # Setup bandwidth 337 338 values = self.consume_parameter(parameters, self.PARAM_BW, 1) 339 340 if not values: 341 raise ValueError( 342 "The test name needs to include parameter {} followed by an " 343 "int value (to indicate 1.4 MHz use 14).".format( 344 self.PARAM_BW)) 345 346 bw = float(values[1]) 347 348 if bw == 14: 349 bw = 1.4 350 351 self.set_channel_bandwidth(self.bts1, bw) 352 353 # Setup mimo mode 354 355 values = self.consume_parameter(parameters, self.PARAM_MIMO, 1) 356 357 if not values: 358 raise ValueError( 359 "The test name needs to include parameter '{}' followed by the " 360 "mimo mode.".format(self.PARAM_MIMO)) 361 362 for mimo_mode in LteSimulation.MimoMode: 363 if values[1] == mimo_mode.value: 364 self.set_mimo_mode(self.bts1, mimo_mode) 365 break 366 else: 367 raise ValueError("The {} parameter needs to be followed by either " 368 "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO)) 369 370 if (mimo_mode == LteSimulation.MimoMode.MIMO_4x4 371 and self.anritsu._md8475_version == 'A'): 372 raise ValueError("The test requires 4x4 MIMO, but that is not " 373 "supported by the MD8475A callbox.") 374 375 self.set_mimo_mode(self.bts1, mimo_mode) 376 377 # Setup transmission mode 378 379 values = self.consume_parameter(parameters, self.PARAM_TM, 1) 380 381 if not values: 382 raise ValueError( 383 "The test name needs to include parameter {} followed by an " 384 "int value from 1 to 4 indicating transmission mode.".format( 385 self.PARAM_TM)) 386 387 for tm in LteSimulation.TransmissionMode: 388 if values[1] == tm.value[2:]: 389 self.set_transmission_mode(self.bts1, tm) 390 break 391 else: 392 raise ValueError("The {} parameter needs to be followed by either " 393 "TM1, TM2, TM3, TM4, TM7, TM8 or TM9.".format( 394 self.PARAM_MIMO)) 395 396 # Setup scheduling mode 397 398 values = self.consume_parameter(parameters, self.PARAM_SCHEDULING, 1) 399 400 if not values: 401 scheduling = LteSimulation.SchedulingMode.STATIC 402 self.log.warning( 403 "The test name does not include the '{}' parameter. Setting to " 404 "static by default.".format(self.PARAM_SCHEDULING)) 405 elif values[1] == self.PARAM_SCHEDULING_DYNAMIC: 406 scheduling = LteSimulation.SchedulingMode.DYNAMIC 407 elif values[1] == self.PARAM_SCHEDULING_STATIC: 408 scheduling = LteSimulation.SchedulingMode.STATIC 409 else: 410 raise ValueError( 411 "The test name parameter '{}' has to be followed by either " 412 "'dynamic' or 'static'.".format(self.PARAM_SCHEDULING)) 413 414 if scheduling == LteSimulation.SchedulingMode.STATIC: 415 416 values = self.consume_parameter(parameters, self.PARAM_PATTERN, 2) 417 418 if not values: 419 self.log.warning( 420 "The '{}' parameter was not set, using 100% RBs for both " 421 "DL and UL. To set the percentages of total RBs include " 422 "the '{}' parameter followed by two ints separated by an " 423 "underscore indicating downlink and uplink percentages." 424 .format(self.PARAM_PATTERN, self.PARAM_PATTERN)) 425 dl_pattern = 100 426 ul_pattern = 100 427 else: 428 dl_pattern = int(values[1]) 429 ul_pattern = int(values[2]) 430 431 if not (0 <= dl_pattern <= 100 and 0 <= ul_pattern <= 100): 432 raise ValueError( 433 "The scheduling pattern parameters need to be two " 434 "positive numbers between 0 and 100.") 435 436 dl_rbs, ul_rbs = self.allocation_percentages_to_rbs( 437 self.bts1, dl_pattern, ul_pattern) 438 439 if self.dl_256_qam and bw == 1.4: 440 mcs_dl = 26 441 elif not self.dl_256_qam and self.tbs_pattern_on and bw != 1.4: 442 mcs_dl = 28 443 else: 444 mcs_dl = 27 445 446 if self.ul_64_qam: 447 mcs_ul = 28 448 else: 449 mcs_ul = 23 450 451 self.set_scheduling_mode( 452 self.bts1, 453 LteSimulation.SchedulingMode.STATIC, 454 packet_rate=BtsPacketRate.LTE_MANUAL, 455 nrb_dl=dl_rbs, 456 nrb_ul=ul_rbs, 457 mcs_ul=mcs_ul, 458 mcs_dl=mcs_dl) 459 460 else: 461 462 self.set_scheduling_mode(self.bts1, 463 LteSimulation.SchedulingMode.DYNAMIC) 464 465 # Get uplink power 466 467 ul_power = self.get_uplink_power_from_parameters(parameters) 468 469 # Power is not set on the callbox until after the simulation is 470 # started. Saving this value in a variable for later 471 self.sim_ul_power = ul_power 472 473 # Get downlink power 474 475 dl_power = self.get_downlink_power_from_parameters(parameters) 476 477 # Power is not set on the callbox until after the simulation is 478 # started. Saving this value in a variable for later 479 self.sim_dl_power = dl_power 480 481 def get_uplink_power_from_parameters(self, parameters): 482 """ Reads uplink power from a list of parameters. """ 483 484 values = self.consume_parameter(parameters, self.PARAM_UL_PW, 1) 485 486 if not values or values[1] not in self.uplink_signal_level_dictionary: 487 raise ValueError( 488 "The test name needs to include parameter {} followed by one " 489 "the following values: {}.".format(self.PARAM_UL_PW, [ 490 val for val in self.uplink_signal_level_dictionary.keys() 491 ])) 492 493 return self.uplink_signal_level_dictionary[values[1]] 494 495 def get_downlink_power_from_parameters(self, parameters): 496 """ Reads downlink power from a list of parameters. """ 497 498 values = self.consume_parameter(parameters, self.PARAM_DL_PW, 1) 499 500 if values: 501 if values[1] not in self.downlink_rsrp_dictionary: 502 raise ValueError("Invalid signal level value {}.".format( 503 values[1])) 504 else: 505 return self.downlink_rsrp_dictionary[values[1]] 506 else: 507 # Use default value 508 power = self.downlink_rsrp_dictionary['excellent'] 509 self.log.info( 510 "No DL signal level value was indicated in the test " 511 "parameters. Using default value of {} RSRP.".format(power)) 512 return power 513 514 def set_downlink_rx_power(self, bts, rsrp): 515 """ Sets downlink rx power in RSRP using calibration 516 517 Lte simulation overrides this method so that it can convert from 518 RSRP to total signal power transmitted from the basestation. 519 520 Args: 521 bts: the base station in which to change the signal level 522 rsrp: desired rsrp, contained in a key value pair 523 """ 524 525 power = self.rsrp_to_signal_power(rsrp, bts) 526 527 self.log.info( 528 "Setting downlink signal level to {} RSRP ({} dBm)".format( 529 rsrp, power)) 530 531 # Use parent method to set signal level 532 super().set_downlink_rx_power(bts, power) 533 534 def downlink_calibration(self, 535 bts, 536 rat=None, 537 power_units_conversion_func=None): 538 """ Computes downlink path loss and returns the calibration value 539 540 The bts needs to be set at the desired config (bandwidth, mode, etc) 541 before running the calibration. The phone also needs to be attached 542 to the desired basesation for calibration 543 544 Args: 545 bts: basestation handle 546 rat: ignored, replaced by 'lteRsrp' 547 power_units_conversion_func: ignored, replaced by 548 self.rsrp_to_signal_power 549 550 Returns: 551 Dowlink calibration value and measured DL power. Note that the 552 phone only reports RSRP of the primary chain 553 """ 554 555 return super().downlink_calibration( 556 bts, 557 rat='lteRsrp', 558 power_units_conversion_func=self.rsrp_to_signal_power) 559 560 def rsrp_to_signal_power(self, rsrp, bts): 561 """ Converts rsrp to total band signal power 562 563 RSRP is measured per subcarrier, so total band power needs to be 564 multiplied by the number of subcarriers being used. 565 566 Args: 567 rsrp: desired rsrp in dBm 568 bts: basestation handler for which the unit conversion is done 569 570 Returns: 571 Total band signal power in dBm 572 """ 573 574 bandwidth = bts.bandwidth 575 576 if bandwidth == BtsBandwidth.LTE_BANDWIDTH_20MHz.value: # 100 RBs 577 power = rsrp + 30.79 578 elif bandwidth == BtsBandwidth.LTE_BANDWIDTH_15MHz.value: # 75 RBs 579 power = rsrp + 29.54 580 elif bandwidth == BtsBandwidth.LTE_BANDWIDTH_10MHz.value: # 50 RBs 581 power = rsrp + 27.78 582 elif bandwidth == BtsBandwidth.LTE_BANDWIDTH_5MHz.value: # 25 RBs 583 power = rsrp + 24.77 584 elif bandwidth == BtsBandwidth.LTE_BANDWIDTH_3MHz.value: # 15 RBs 585 power = rsrp + 22.55 586 elif bandwidth == BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: # 6 RBs 587 power = rsrp + 18.57 588 else: 589 raise ValueError("Invalid bandwidth value.") 590 591 return power 592 593 def maximum_downlink_throughput(self): 594 """ Calculates maximum achievable downlink throughput in the current 595 simulation state. 596 597 Returns: 598 Maximum throughput in mbps. 599 600 """ 601 602 return self.bts_maximum_downlink_throughtput(self.bts1) 603 604 def bts_maximum_downlink_throughtput(self, bts): 605 """ Calculates maximum achievable downlink throughput for the selected 606 basestation. 607 608 Args: 609 bts: basestation handle 610 611 Returns: 612 Maximum throughput in mbps. 613 614 """ 615 616 bandwidth = bts.bandwidth 617 rb_ratio = float(bts.nrb_dl) / self.total_rbs_dictionary[bandwidth] 618 streams = float(bts.dl_antenna) 619 mcs = bts.lte_mcs_dl 620 621 max_rate_per_stream = None 622 623 if not self.dl_256_qam and self.tbs_pattern_on and mcs == "28": 624 max_rate_per_stream = { 625 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 9.96, 626 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 17.0, 627 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 34.7, 628 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 52.7, 629 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 72.2 630 }.get(bandwidth, None) 631 if not self.dl_256_qam and self.tbs_pattern_on and mcs == "27": 632 max_rate_per_stream = { 633 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 2.94, 634 }.get(bandwidth, None) 635 elif not self.dl_256_qam and not self.tbs_pattern_on and mcs == "27": 636 max_rate_per_stream = { 637 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 2.87, 638 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 7.7, 639 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 14.4, 640 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 28.7, 641 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 42.3, 642 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 57.7 643 }.get(bandwidth, None) 644 elif self.dl_256_qam and self.tbs_pattern_on and mcs == "27": 645 max_rate_per_stream = { 646 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 13.2, 647 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 22.9, 648 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 46.3, 649 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 72.2, 650 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 93.9 651 }.get(bandwidth, None) 652 elif self.dl_256_qam and self.tbs_pattern_on and mcs == "26": 653 max_rate_per_stream = { 654 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 3.96, 655 }.get(bandwidth, None) 656 elif self.dl_256_qam and not self.tbs_pattern_on and mcs == "27": 657 max_rate_per_stream = { 658 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 11.3, 659 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 19.8, 660 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 44.1, 661 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 68.1, 662 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 88.4 663 }.get(bandwidth, None) 664 elif self.dl_256_qam and not self.tbs_pattern_on and mcs == "26": 665 max_rate_per_stream = { 666 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 3.96, 667 }.get(bandwidth, None) 668 669 if not max_rate_per_stream: 670 raise NotImplementedError( 671 "The calculation for tbs pattern = {} " 672 "and mcs = {} is not implemented.".format( 673 "FULLALLOCATION" if self.tbs_pattern_on else "OFF", mcs)) 674 675 return max_rate_per_stream * streams * rb_ratio 676 677 def maximum_uplink_throughput(self): 678 """ Calculates maximum achievable uplink throughput in the current 679 simulation state. 680 681 Returns: 682 Maximum throughput in mbps. 683 684 """ 685 686 return self.bts_maximum_uplink_throughtput(self.bts1) 687 688 def bts_maximum_uplink_throughtput(self, bts): 689 """ Calculates maximum achievable uplink throughput for the selected 690 basestation. 691 692 Args: 693 bts: basestation handle 694 695 Returns: 696 Maximum throughput in mbps. 697 698 """ 699 700 bandwidth = bts.bandwidth 701 rb_ratio = float(bts.nrb_ul) / self.total_rbs_dictionary[bandwidth] 702 mcs = bts.lte_mcs_ul 703 704 max_rate_per_stream = None 705 if mcs == "23" and not self.ul_64_qam: 706 max_rate_per_stream = { 707 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 2.85, 708 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 7.18, 709 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 12.1, 710 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 24.5, 711 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 36.5, 712 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 49.1 713 }.get(bandwidth, None) 714 elif mcs == "28" and self.ul_64_qam: 715 max_rate_per_stream = { 716 BtsBandwidth.LTE_BANDWIDTH_1dot4MHz.value: 4.2, 717 BtsBandwidth.LTE_BANDWIDTH_3MHz.value: 10.5, 718 BtsBandwidth.LTE_BANDWIDTH_5MHz.value: 17.2, 719 BtsBandwidth.LTE_BANDWIDTH_10MHz.value: 35.3, 720 BtsBandwidth.LTE_BANDWIDTH_15MHz.value: 53.0, 721 BtsBandwidth.LTE_BANDWIDTH_20MHz.value: 72.6 722 }.get(bandwidth, None) 723 724 if not max_rate_per_stream: 725 raise NotImplementedError("The calculation fir mcs = {} is not " 726 "implemented.".format( 727 "FULLALLOCATION" if 728 self.tbs_pattern_on else "OFF", mcs)) 729 730 return max_rate_per_stream * rb_ratio 731 732 def set_transmission_mode(self, bts, tmode): 733 """ Sets the transmission mode for the LTE basetation 734 735 Args: 736 bts: basestation handle 737 tmode: Enum list from class 'TransmissionModeLTE' 738 """ 739 740 # If the selected transmission mode does not support the number of DL 741 # antennas, throw an exception. 742 if (tmode in [self.TransmissionMode.TM1, self.TransmissionMode.TM7] 743 and bts.dl_antenna != '1'): 744 # TM1 and TM7 only support 1 DL antenna 745 raise ValueError("{} allows only one DL antenna. Change the " 746 "number of DL antennas before setting the " 747 "transmission mode.".format(tmode.value)) 748 elif tmode == self.TransmissionMode.TM8 and bts.dl_antenna != '2': 749 # TM8 requires 2 DL antennas 750 raise ValueError("TM2 requires two DL antennas. Change the " 751 "number of DL antennas before setting the " 752 "transmission mode.") 753 elif (tmode in [ 754 self.TransmissionMode.TM2, self.TransmissionMode.TM3, 755 self.TransmissionMode.TM4, self.TransmissionMode.TM9 756 ] and bts.dl_antenna == '1'): 757 # TM2, TM3, TM4 and TM9 require 2 or 4 DL antennas 758 raise ValueError("{} requires at least two DL atennas. Change the " 759 "number of DL antennas before setting the " 760 "transmission mode.".format(tmode.value)) 761 762 # The TM mode is allowed for the current number of DL antennas, so it 763 # is safe to change this setting now 764 bts.transmode = tmode.value 765 766 time.sleep(5) # It takes some time to propagate the new settings 767 768 def set_mimo_mode(self, bts, mimo): 769 """ Sets the number of DL antennas for the desired MIMO mode. 770 771 Args: 772 bts: basestation handle 773 mimo: object of class MimoMode 774 """ 775 776 # If the requested mimo mode is not compatible with the current TM, 777 # warn the user before changing the value. 778 779 if mimo == self.MimoMode.MIMO_1x1: 780 if bts.transmode not in [ 781 self.TransmissionMode.TM1, self.TransmissionMode.TM7 782 ]: 783 self.log.warning( 784 "Using only 1 DL antennas is not allowed with " 785 "the current transmission mode. Changing the " 786 "number of DL antennas will override this " 787 "setting.") 788 bts.dl_antenna = 1 789 elif mimo == self.MimoMode.MIMO_2x2: 790 if bts.transmode not in [ 791 self.TransmissionMode.TM2, self.TransmissionMode.TM3, 792 self.TransmissionMode.TM4, self.TransmissionMode.TM8, 793 self.TransmissionMode.TM9 794 ]: 795 self.log.warning("Using two DL antennas is not allowed with " 796 "the current transmission mode. Changing the " 797 "number of DL antennas will override this " 798 "setting.") 799 bts.dl_antenna = 2 800 elif mimo == self.MimoMode.MIMO_4x4: 801 if bts.transmode not in [ 802 self.TransmissionMode.TM2, self.TransmissionMode.TM3, 803 self.TransmissionMode.TM4, self.TransmissionMode.TM9 804 ]: 805 self.log.warning("Using four DL antennas is not allowed with " 806 "the current transmission mode. Changing the " 807 "number of DL antennas will override this " 808 "setting.") 809 810 bts.dl_antenna = 4 811 else: 812 RuntimeError("The requested MIMO mode is not supported.") 813 814 def set_scheduling_mode(self, 815 bts, 816 scheduling, 817 packet_rate=None, 818 mcs_dl=None, 819 mcs_ul=None, 820 nrb_dl=None, 821 nrb_ul=None): 822 """ Sets the scheduling mode for LTE 823 824 Args: 825 bts: basestation handle 826 scheduling: DYNAMIC or STATIC scheduling (Enum list) 827 mcs_dl: Downlink MCS (only for STATIC scheduling) 828 mcs_ul: Uplink MCS (only for STATIC scheduling) 829 nrb_dl: Number of RBs for downlink (only for STATIC scheduling) 830 nrb_ul: Number of RBs for uplink (only for STATIC scheduling) 831 """ 832 833 bts.lte_scheduling_mode = scheduling.value 834 835 if scheduling == self.SchedulingMode.STATIC: 836 837 if not packet_rate: 838 raise RuntimeError("Packet rate needs to be indicated when " 839 "selecting static scheduling.") 840 841 bts.packet_rate = packet_rate 842 bts.tbs_pattern = "FULLALLOCATION" if self.tbs_pattern_on else "OFF" 843 844 if packet_rate == BtsPacketRate.LTE_MANUAL: 845 846 if not (mcs_dl and mcs_ul and nrb_dl and nrb_ul): 847 raise RuntimeError("When using manual packet rate the " 848 "number of dl/ul RBs and the dl/ul " 849 "MCS needs to be indicated with the " 850 "optional arguments.") 851 852 bts.lte_mcs_dl = mcs_dl 853 bts.lte_mcs_ul = mcs_ul 854 bts.nrb_dl = nrb_dl 855 bts.nrb_ul = nrb_ul 856 857 time.sleep(5) # It takes some time to propagate the new settings 858 859 def allocation_percentages_to_rbs(self, bts, dl, ul): 860 """ Converts usage percentages to number of DL/UL RBs 861 862 Because not any number of DL/UL RBs can be obtained for a certain 863 bandwidth, this function calculates the number of RBs that most 864 closely matches the desired DL/UL percentages. 865 866 Args: 867 bts: base station handle 868 dl: desired percentage of downlink RBs 869 ul: desired percentage of uplink RBs 870 Returns: 871 a tuple indicating the number of downlink and uplink RBs 872 """ 873 874 # Validate the arguments 875 if (not 0 <= dl <= 100) or (not 0 <= ul <= 100): 876 raise ValueError("The percentage of DL and UL RBs have to be two " 877 "positive between 0 and 100.") 878 879 # Get the available number of RBs for the channel bandwidth 880 bw = bts.bandwidth 881 # Get the current transmission mode 882 tm = bts.transmode 883 # Get min and max values from tables 884 max_rbs = self.total_rbs_dictionary[bw] 885 min_dl_rbs = self.min_dl_rbs_dictionary[bw] 886 min_ul_rbs = self.min_ul_rbs_dictionary[bw] 887 888 def percentage_to_amount(min_val, max_val, percentage): 889 """ Returns the integer between min_val and max_val that is closest 890 to percentage/100*max_val 891 """ 892 893 # Calculate the value that corresponds to the required percentage. 894 closest_int = round(max_val * percentage / 100) 895 # Cannot be less than min_val 896 closest_int = max(closest_int, min_val) 897 # RBs cannot be more than max_rbs 898 closest_int = min(closest_int, max_val) 899 900 return closest_int 901 902 # Calculate the number of DL RBs 903 904 # Get the number of DL RBs that corresponds to 905 # the required percentage. 906 desired_dl_rbs = percentage_to_amount( 907 min_val=min_dl_rbs, max_val=max_rbs, percentage=dl) 908 909 if (tm == self.TransmissionMode.TM3.value 910 or tm == self.TransmissionMode.TM4.value): 911 912 # For TM3 and TM4 the number of DL RBs needs to be max_rbs or a 913 # multiple of the RBG size 914 915 if desired_dl_rbs == max_rbs: 916 dl_rbs = max_rbs 917 else: 918 dl_rbs = (math.ceil(desired_dl_rbs / self.rbg_dictionary[bw]) * 919 self.rbg_dictionary[bw]) 920 921 else: 922 # The other TMs allow any number of RBs between 1 and max_rbs 923 dl_rbs = desired_dl_rbs 924 925 # Calculate the number of UL RBs 926 927 # Get the number of UL RBs that corresponds 928 # to the required percentage 929 desired_ul_rbs = percentage_to_amount( 930 min_val=min_ul_rbs, max_val=max_rbs, percentage=ul) 931 932 # Create a list of all possible UL RBs assignment 933 # The standard allows any number that can be written as 934 # 2**a * 3**b * 5**c for any combination of a, b and c. 935 936 def pow_range(max_value, base): 937 """ Returns a range of all possible powers of base under 938 the given max_value. 939 """ 940 return range(int(math.ceil(math.log(max_value, base)))) 941 942 possible_ul_rbs = [ 943 2**a * 3**b * 5**c 944 for a in pow_range(max_rbs, 2) for b in pow_range(max_rbs, 3) 945 for c in pow_range(max_rbs, 5) if 2**a * 3**b * 5**c <= max_rbs 946 ] 947 948 # Find the value in the list that is closest to desired_ul_rbs 949 differences = [abs(rbs - desired_ul_rbs) for rbs in possible_ul_rbs] 950 ul_rbs = possible_ul_rbs[differences.index(min(differences))] 951 952 # Report what are the obtained RB percentages 953 self.log.info("Requested a {}% / {}% RB allocation. Closest possible " 954 "percentages are {}% / {}%.".format( 955 dl, ul, 956 round(100 * dl_rbs / max_rbs), 957 round(100 * ul_rbs / max_rbs))) 958 959 return dl_rbs, ul_rbs 960 961 def set_channel_bandwidth(self, bts, bandwidth): 962 """ Sets the LTE channel bandwidth (MHz) 963 964 Args: 965 bts: basestation handle 966 bandwidth: desired bandwidth (MHz) 967 """ 968 if bandwidth == 20: 969 bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_20MHz 970 elif bandwidth == 15: 971 bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_15MHz 972 elif bandwidth == 10: 973 bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_10MHz 974 elif bandwidth == 5: 975 bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_5MHz 976 elif bandwidth == 3: 977 bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_3MHz 978 elif bandwidth == 1.4: 979 bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_1dot4MHz 980 else: 981 msg = "Bandwidth = {} MHz is not valid for LTE".format(bandwidth) 982 self.log.Error(msg) 983 raise ValueError(msg) 984 time.sleep(5) # It takes some time to propagate the new settings 985 986 def set_dlul_configuration(self, bts, config): 987 """ Sets the frame structure for TDD bands. 988 989 Args: 990 config: the desired frame structure. An int between 0 and 6. 991 """ 992 993 if not 0 <= config <= 6: 994 raise ValueError("The frame structure configuration has to be a " 995 "number between 0 and 6") 996 997 bts.uldl_configuration = config 998 999 # Wait for the setting to propagate 1000 time.sleep(5) 1001 1002 def calibrate(self): 1003 """ Calculates UL and DL path loss if it wasn't done before 1004 1005 This method overrides the baseclass specifically for LTE calibration. 1006 For LTE cal, the simulation is set to TM1 and 1 antenna. 1007 1008 """ 1009 1010 # Set in TM1 mode and 1 antenna for downlink calibration for LTE 1011 init_dl_antenna = None 1012 init_transmode = None 1013 if int(self.bts1.dl_antenna) != 1: 1014 init_dl_antenna = self.bts1.dl_antenna 1015 init_transmode = self.bts1.transmode 1016 self.bts1.dl_antenna = 1 1017 self.bts1.transmode = "TM1" 1018 time.sleep(5) # It takes some time to propagate the new settings 1019 1020 super().calibrate() 1021 1022 if init_dl_antenna is not None: 1023 self.bts1.dl_antenna = init_dl_antenna 1024 self.bts1.transmode = init_transmode 1025 time.sleep(5) # It takes some time to propagate the new settings 1026 1027 def get_duplex_mode(self, band): 1028 """ Determines if the band uses FDD or TDD duplex mode 1029 1030 Args: 1031 band: a band number 1032 Returns: 1033 an variable of class DuplexMode indicating if band is FDD or TDD 1034 """ 1035 1036 if 33 <= int(band) <= 46: 1037 return self.DuplexMode.TDD 1038 else: 1039 return self.DuplexMode.FDD 1040 1041 def set_band(self, bts, band, calibrate_if_necessary=True): 1042 """ Sets the right duplex mode before switching to a new band. 1043 1044 Args: 1045 bts: basestation handle 1046 band: desired band 1047 calibrate_if_necessary: if False calibration will be skipped 1048 """ 1049 1050 bts.duplex_mode = self.get_duplex_mode(band).value 1051 1052 super().set_band(bts, band, calibrate_if_necessary) 1053