• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
16import time
17from enum import Enum
18
19from acts.controllers.cellular_lib.BaseSimulation import BaseSimulation
20from acts.controllers.cellular_lib.LteCellConfig import LteCellConfig
21from acts.controllers.cellular_lib.NrCellConfig import NrCellConfig
22from acts.controllers.cellular_lib import BaseCellularDut
23
24
25class IPAddressType(Enum):
26    """ IP Address types"""
27    IPV4 = "IPV4"
28    IPV6 = "IPV6"
29    IPV4V6 = "IPV4V6"
30
31
32class TransmissionMode(Enum):
33    """ Transmission modes for LTE (e.g., TM1, TM4, ...) """
34    TM1 = "TM1"
35    TM2 = "TM2"
36    TM3 = "TM3"
37    TM4 = "TM4"
38    TM7 = "TM7"
39    TM8 = "TM8"
40    TM9 = "TM9"
41
42
43class MimoMode(Enum):
44    """ Mimo modes """
45    MIMO_1x1 = "1x1"
46    MIMO_2x2 = "2x2"
47    MIMO_4x4 = "4x4"
48
49
50class SchedulingMode(Enum):
51    """ Traffic scheduling modes (e.g., STATIC, DYNAMIC) """
52    DYNAMIC = "DYNAMIC"
53    STATIC = "STATIC"
54
55
56class DuplexMode(Enum):
57    """ DL/UL Duplex mode """
58    FDD = "FDD"
59    TDD = "TDD"
60
61
62class ModulationType(Enum):
63    """DL/UL Modulation order."""
64    QPSK = 'QPSK'
65    Q16 = '16QAM'
66    Q64 = '64QAM'
67    Q256 = '256QAM'
68
69
70# Bandwidth [MHz] to RB group size
71RBG_DICTIONARY = {20: 4, 15: 4, 10: 3, 5: 2, 3: 2, 1.4: 1}
72
73# Bandwidth [MHz] to total RBs mapping
74TOTAL_RBS_DICTIONARY = {20: 100, 15: 75, 10: 50, 5: 25, 3: 15, 1.4: 6}
75
76# Bandwidth [MHz] to minimum number of DL RBs that can be assigned to a UE
77MIN_DL_RBS_DICTIONARY = {20: 16, 15: 12, 10: 9, 5: 4, 3: 4, 1.4: 2}
78
79# Bandwidth [MHz] to minimum number of UL RBs that can be assigned to a UE
80MIN_UL_RBS_DICTIONARY = {20: 8, 15: 6, 10: 4, 5: 2, 3: 2, 1.4: 1}
81
82
83class LteSimulation(BaseSimulation):
84    """ Single-carrier LTE simulation. """
85    # Test config keywords
86    KEY_FREQ_BANDS = "freq_bands"
87
88    # Cell param keywords
89    PARAM_RRC_STATUS_CHANGE_TIMER = "rrcstatuschangetimer"
90
91    # Units in which signal level is defined in DOWNLINK_SIGNAL_LEVEL_DICTIONARY
92    DOWNLINK_SIGNAL_LEVEL_UNITS = "RSRP"
93
94    # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz.
95    # Excellent is set to -75 since callbox B Tx power is limited to -30 dBm
96    DOWNLINK_SIGNAL_LEVEL_DICTIONARY = {
97        'excellent': -62,
98        'high': -110,
99        'medium': -115,
100        'weak': -120,
101        'disconnected': -170
102    }
103
104    # Transmitted output power for the phone (dBm)
105    UPLINK_SIGNAL_LEVEL_DICTIONARY = {
106        'max': 27,
107        'high': 13,
108        'medium': 3,
109        'low': -20
110    }
111
112    # Allowed bandwidth for each band.
113    allowed_bandwidth_dictionary = {
114        1: [5, 10, 15, 20],
115        2: [1.4, 3, 5, 10, 15, 20],
116        3: [1.4, 3, 5, 10, 15, 20],
117        4: [1.4, 3, 5, 10, 15, 20],
118        5: [1.4, 3, 5, 10],
119        7: [5, 10, 15, 20],
120        8: [1.4, 3, 5, 10],
121        10: [5, 10, 15, 20],
122        11: [5, 10],
123        12: [1.4, 3, 5, 10],
124        13: [5, 10],
125        14: [5, 10],
126        17: [5, 10],
127        18: [5, 10, 15],
128        19: [5, 10, 15],
129        20: [5, 10, 15, 20],
130        21: [5, 10, 15],
131        22: [5, 10, 15, 20],
132        24: [5, 10],
133        25: [1.4, 3, 5, 10, 15, 20],
134        26: [1.4, 3, 5, 10, 15],
135        27: [1.4, 3, 5, 10],
136        28: [3, 5, 10, 15, 20],
137        29: [3, 5, 10],
138        30: [5, 10],
139        31: [1.4, 3, 5],
140        32: [5, 10, 15, 20],
141        33: [5, 10, 15, 20],
142        34: [5, 10, 15],
143        35: [1.4, 3, 5, 10, 15, 20],
144        36: [1.4, 3, 5, 10, 15, 20],
145        37: [5, 10, 15, 20],
146        38: [20],
147        39: [5, 10, 15, 20],
148        40: [5, 10, 15, 20],
149        41: [5, 10, 15, 20],
150        42: [5, 10, 15, 20],
151        43: [5, 10, 15, 20],
152        44: [3, 5, 10, 15, 20],
153        45: [5, 10, 15, 20],
154        46: [10, 20],
155        47: [10, 20],
156        48: [5, 10, 15, 20],
157        49: [10, 20],
158        50: [3, 5, 10, 15, 20],
159        51: [3, 5],
160        52: [5, 10, 15, 20],
161        65: [5, 10, 15, 20],
162        66: [1.4, 3, 5, 10, 15, 20],
163        67: [5, 10, 15, 20],
164        68: [5, 10, 15],
165        69: [5],
166        70: [5, 10, 15],
167        71: [5, 10, 15, 20],
168        72: [1.4, 3, 5],
169        73: [1.4, 3, 5],
170        74: [1.4, 3, 5, 10, 15, 20],
171        75: [5, 10, 15, 20],
172        76: [5],
173        85: [5, 10],
174        252: [20],
175        255: [20]
176    }
177
178    # Dictionary of lower DL channel number bound for each band.
179    LOWEST_DL_CN_DICTIONARY = {
180        1: 0,
181        2: 600,
182        3: 1200,
183        4: 1950,
184        5: 2400,
185        6: 2650,
186        7: 2750,
187        8: 3450,
188        9: 3800,
189        10: 4150,
190        11: 4750,
191        12: 5010,
192        13: 5180,
193        14: 5280,
194        17: 5730,
195        18: 5850,
196        19: 6000,
197        20: 6150,
198        21: 6450,
199        22: 6600,
200        23: 7500,
201        24: 7700,
202        25: 8040,
203        26: 8690,
204        27: 9040,
205        28: 9210,
206        29: 9660,
207        30: 9770,
208        31: 9870,
209        32: 9920,
210        33: 36000,
211        34: 36200,
212        35: 36350,
213        36: 36950,
214        37: 37550,
215        38: 37750,
216        39: 38250,
217        40: 38650,
218        41: 39650,
219        42: 41590,
220        43: 45590,
221        66: 66436,
222        67: 67336
223    }
224
225    # Peak throughput lookup tables for each TDD subframe
226    # configuration and bandwidth
227    # yapf: disable
228    tdd_config4_tput_lut = {
229        0: {
230            5: {'DL': 3.82, 'UL': 2.63},
231            10: {'DL': 11.31,'UL': 9.03},
232            15: {'DL': 16.9, 'UL': 20.62},
233            20: {'DL': 22.88, 'UL': 28.43}
234        },
235        1: {
236            5: {'DL': 6.13, 'UL': 4.08},
237            10: {'DL': 18.36, 'UL': 9.69},
238            15: {'DL': 28.62, 'UL': 14.21},
239            20: {'DL': 39.04, 'UL': 19.23}
240        },
241        2: {
242            5: {'DL': 5.68, 'UL': 2.30},
243            10: {'DL': 25.51, 'UL': 4.68},
244            15: {'DL': 39.3, 'UL': 7.13},
245            20: {'DL': 53.64, 'UL': 9.72}
246        },
247        3: {
248            5: {'DL': 8.26, 'UL': 3.45},
249            10: {'DL': 23.20, 'UL': 6.99},
250            15: {'DL': 35.35, 'UL': 10.75},
251            20: {'DL': 48.3, 'UL': 14.6}
252        },
253        4: {
254            5: {'DL': 6.16, 'UL': 2.30},
255            10: {'DL': 26.77, 'UL': 4.68},
256            15: {'DL': 40.7, 'UL': 7.18},
257            20: {'DL': 55.6, 'UL': 9.73}
258        },
259        5: {
260            5: {'DL': 6.91, 'UL': 1.12},
261            10: {'DL': 30.33, 'UL': 2.33},
262            15: {'DL': 46.04, 'UL': 3.54},
263            20: {'DL': 62.9, 'UL': 4.83}
264        },
265        6: {
266            5: {'DL': 6.13, 'UL': 4.13},
267            10: {'DL': 14.79, 'UL': 11.98},
268            15: {'DL': 23.28, 'UL': 17.46},
269            20: {'DL': 31.75, 'UL': 23.95}
270        }
271    }
272
273    tdd_config3_tput_lut = {
274        0: {
275            5: {'DL': 5.04, 'UL': 3.7},
276            10: {'DL': 15.11, 'UL': 17.56},
277            15: {'DL': 22.59, 'UL': 30.31},
278            20: {'DL': 30.41, 'UL': 41.61}
279        },
280        1: {
281            5: {'DL': 8.07, 'UL': 5.66},
282            10: {'DL': 24.58, 'UL': 13.66},
283            15: {'DL': 39.05, 'UL': 20.68},
284            20: {'DL': 51.59, 'UL': 28.76}
285        },
286        2: {
287            5: {'DL': 7.59, 'UL': 3.31},
288            10: {'DL': 34.08, 'UL': 6.93},
289            15: {'DL': 53.64, 'UL': 10.51},
290            20: {'DL': 70.55, 'UL': 14.41}
291        },
292        3: {
293            5: {'DL': 10.9, 'UL': 5.0},
294            10: {'DL': 30.99, 'UL': 10.25},
295            15: {'DL': 48.3, 'UL': 15.81},
296            20: {'DL': 63.24, 'UL': 21.65}
297        },
298        4: {
299            5: {'DL': 8.11, 'UL': 3.32},
300            10: {'DL': 35.74, 'UL': 6.95},
301            15: {'DL': 55.6, 'UL': 10.51},
302            20: {'DL': 72.72, 'UL': 14.41}
303        },
304        5: {
305            5: {'DL': 9.28, 'UL': 1.57},
306            10: {'DL': 40.49, 'UL': 3.44},
307            15: {'DL': 62.9, 'UL': 5.23},
308            20: {'DL': 82.21, 'UL': 7.15}
309        },
310        6: {
311            5: {'DL': 8.06, 'UL': 5.74},
312            10: {'DL': 19.82, 'UL': 17.51},
313            15: {'DL': 31.75, 'UL': 25.77},
314            20: {'DL': 42.12, 'UL': 34.91}
315        }
316    }
317
318    tdd_config2_tput_lut = {
319        0: {
320            5: {'DL': 3.11, 'UL': 2.55},
321            10: {'DL': 9.93, 'UL': 11.1},
322            15: {'DL': 13.9, 'UL': 21.51},
323            20: {'DL': 20.02, 'UL': 41.66}
324        },
325        1: {
326            5: {'DL': 5.33, 'UL': 4.27},
327            10: {'DL': 15.14, 'UL': 13.95},
328            15: {'DL': 33.84, 'UL': 19.73},
329            20: {'DL': 44.61, 'UL': 27.35}
330        },
331        2: {
332            5: {'DL': 6.87, 'UL': 3.32},
333            10: {'DL': 17.06, 'UL': 6.76},
334            15: {'DL': 49.63, 'UL': 10.5},
335            20: {'DL': 65.2, 'UL': 14.41}
336        },
337        3: {
338            5: {'DL': 5.41, 'UL': 4.17},
339            10: {'DL': 16.89, 'UL': 9.73},
340            15: {'DL': 44.29, 'UL': 15.7},
341            20: {'DL': 53.95, 'UL': 19.85}
342        },
343        4: {
344            5: {'DL': 8.7, 'UL': 3.32},
345            10: {'DL': 17.58, 'UL': 6.76},
346            15: {'DL': 51.08, 'UL': 10.47},
347            20: {'DL': 66.45, 'UL': 14.38}
348        },
349        5: {
350            5: {'DL': 9.46, 'UL': 1.55},
351            10: {'DL': 19.02, 'UL': 3.48},
352            15: {'DL': 58.89, 'UL': 5.23},
353            20: {'DL': 76.85, 'UL': 7.1}
354        },
355        6: {
356            5: {'DL': 4.74, 'UL': 3.9},
357            10: {'DL': 12.32, 'UL': 13.37},
358            15: {'DL': 27.74, 'UL': 25.02},
359            20: {'DL': 35.48, 'UL': 32.95}
360        }
361    }
362
363    tdd_config1_tput_lut = {
364        0: {
365            5: {'DL': 4.25, 'UL': 3.35},
366            10: {'DL': 8.38, 'UL': 7.22},
367            15: {'DL': 12.41, 'UL': 13.91},
368            20: {'DL': 16.27, 'UL': 24.09}
369        },
370        1: {
371            5: {'DL': 7.28, 'UL': 4.61},
372            10: {'DL': 14.73, 'UL': 9.69},
373            15: {'DL': 21.91, 'UL': 13.86},
374            20: {'DL': 27.63, 'UL': 17.18}
375        },
376        2: {
377            5: {'DL': 10.37, 'UL': 2.27},
378            10: {'DL': 20.92, 'UL': 4.66},
379            15: {'DL': 31.01, 'UL': 7.04},
380            20: {'DL': 42.03, 'UL': 9.75}
381        },
382        3: {
383            5: {'DL': 9.25, 'UL': 3.44},
384            10: {'DL': 18.38, 'UL': 6.95},
385            15: {'DL': 27.59, 'UL': 10.62},
386            20: {'DL': 34.85, 'UL': 13.45}
387        },
388        4: {
389            5: {'DL': 10.71, 'UL': 2.26},
390            10: {'DL': 21.54, 'UL': 4.67},
391            15: {'DL': 31.91, 'UL': 7.2},
392            20: {'DL': 43.35, 'UL': 9.74}
393        },
394        5: {
395            5: {'DL': 12.34, 'UL': 1.08},
396            10: {'DL': 24.78, 'UL': 2.34},
397            15: {'DL': 36.68, 'UL': 3.57},
398            20: {'DL': 49.84, 'UL': 4.81}
399        },
400        6: {
401            5: {'DL': 5.76, 'UL': 4.41},
402            10: {'DL': 11.68, 'UL': 9.7},
403            15: {'DL': 17.34, 'UL': 17.95},
404            20: {'DL': 23.5, 'UL': 23.42}
405        }
406    }
407    # yapf: enable
408
409    # Peak throughput lookup table dictionary
410    tdd_config_tput_lut_dict = {
411        'TDD_CONFIG1':
412        tdd_config1_tput_lut,  # DL 256QAM, UL 64QAM & MAC padding turned OFF
413        'TDD_CONFIG2':
414        tdd_config2_tput_lut,  # DL 256QAM, UL 64 QAM ON & MAC padding OFF
415        'TDD_CONFIG3':
416        tdd_config3_tput_lut,  # DL 256QAM, UL 64QAM & MAC padding ON
417        'TDD_CONFIG4':
418        tdd_config4_tput_lut  # DL 256QAM, UL 64 QAM OFF & MAC padding ON
419    }
420
421    def __init__(self,
422                 simulator,
423                 log,
424                 dut,
425                 test_config,
426                 calibration_table,
427                 nr_mode=None):
428        """ Initializes the simulator for a single-carrier LTE simulation.
429
430        Args:
431            simulator: a cellular simulator controller
432            log: a logger handle
433            dut: a device handler implementing BaseCellularDut
434            test_config: test configuration obtained from the config file
435            calibration_table: a dictionary containing path losses for
436                different bands.
437
438        """
439
440        super().__init__(simulator, log, dut, test_config, calibration_table,
441                         nr_mode)
442
443        self.num_carriers = None
444
445        # Force device to LTE only so that it connects faster
446        try:
447            if self.nr_mode and 'nr' == self.nr_mode:
448                self.dut.set_preferred_network_type(
449                    BaseCellularDut.PreferredNetworkType.NR_LTE)
450            else:
451                self.dut.set_preferred_network_type(
452                    BaseCellularDut.PreferredNetworkType.LTE_ONLY)
453        except Exception as e:
454            # If this fails the test should be able to run anyways, even if it
455            # takes longer to find the cell.
456            self.log.warning('Setting preferred RAT failed: {}'.format(e))
457
458        # Get LTE CA frequency bands setting from the test configuration
459        if self.KEY_FREQ_BANDS not in test_config:
460            self.log.warning("The key '{}' is not set in the config file. "
461                             "Setting to null by default.".format(
462                                 self.KEY_FREQ_BANDS))
463
464        self.freq_bands = test_config.get(self.KEY_FREQ_BANDS, True)
465
466    def setup_simulator(self):
467        """ Do initial configuration in the simulator. """
468        if self.nr_mode and 'nr' == self.nr_mode:
469            self.log.info('Initializes the callbox to Nr Nsa scenario')
470            self.simulator.setup_nr_nsa_scenario()
471        else:
472            self.log.info('Initializes the callbox to LTE scenario')
473            self.simulator.setup_lte_scenario()
474
475    def configure(self, parameters):
476        """ Configures simulation using a dictionary of parameters.
477
478        Processes LTE configuration parameters.
479
480        Args:
481            parameters: a configuration dictionary if there is only one carrier,
482                a list if there are multiple cells.
483        """
484        # If there is a single item, put in a list
485        if not isinstance(parameters, list):
486            parameters = [parameters]
487
488        # Pass only PCC configs to BaseSimulation
489        super().configure(parameters[0])
490
491        new_cell_list = []
492        for cell in parameters:
493            if LteCellConfig.PARAM_BAND not in cell:
494                raise ValueError(
495                    "The configuration dictionary must include a key '{}' with "
496                    "the required band number.".format(
497                        LteCellConfig.PARAM_BAND))
498
499            band = cell[LteCellConfig.PARAM_BAND]
500
501            if isinstance(band, str) and not band.isdigit():
502                # If band starts with n then it is an NR band
503                if band[0] == 'n' and band[1:].isdigit():
504                    # If the remaining string is only the band number, add
505                    # the cell and continue
506                    new_cell_list.append(cell)
507                    continue
508
509                ca_class = band[-1].upper()
510                band_num = band[:-1]
511
512                if ca_class in ['A', 'C']:
513                    # Remove the CA class label and add the cell
514                    cell[LteCellConfig.PARAM_BAND] = band_num
515                    new_cell_list.append(cell)
516                elif ca_class == 'B':
517                    raise RuntimeError('Class B LTE CA not supported.')
518                else:
519                    raise ValueError('Invalid band value: ' + band)
520
521                # Class C means that there are two contiguous carriers
522                if ca_class == 'C':
523                    new_cell_list.append(dict(cell))
524                    bw = int(cell[LteCellConfig.PARAM_BW])
525                    dl_earfcn = LteCellConfig.PARAM_DL_EARFCN
526                    new_cell_list[-1][
527                        dl_earfcn] = self.LOWEST_DL_CN_DICTIONARY[int(
528                            band_num)] + bw * 10 - 2
529            else:
530                # The band is just a number, so just add it to the list
531                new_cell_list.append(cell)
532
533        # verify mimo mode parameter is provided
534        for cell in new_cell_list:
535            if not LteCellConfig.PARAM_MIMO in cell:
536                raise ValueError(
537                    'The config dictionary must include parameter "{}" with the'
538                    ' mimo mode.'.format(self.PARAM_MIMO))
539
540            if cell[LteCellConfig.PARAM_MIMO] not in (m.value
541                                                      for m in MimoMode):
542                raise ValueError(
543                    'The value of {} must be one of the following:'
544                    '1x1, 2x2 or 4x4.'.format(self.PARAM_MIMO))
545
546        # Logs new_cell_list for debug
547        self.log.info('new cell list: {}'.format(new_cell_list))
548
549        self.simulator.set_band_combination(
550            [c[LteCellConfig.PARAM_BAND] for c in new_cell_list],
551            [MimoMode(c[LteCellConfig.PARAM_MIMO]) for c in new_cell_list])
552
553        self.num_carriers = len(new_cell_list)
554
555        # Setup the base stations with the obtain configuration
556        self.cell_configs = []
557        for i in range(self.num_carriers):
558            band = new_cell_list[i][LteCellConfig.PARAM_BAND]
559            if isinstance(band, str) and band[0] == 'n':
560                self.cell_configs.append(NrCellConfig(self.log))
561            else:
562                self.cell_configs.append(LteCellConfig(self.log))
563            self.cell_configs[i].configure(new_cell_list[i])
564            self.simulator.configure_bts(self.cell_configs[i], i)
565
566        # Now that the band is set, calibrate the link if necessary
567        self.load_pathloss_if_required()
568
569        # This shouldn't be a cell parameter but instead a simulation config
570        # Setup LTE RRC status change function and timer for LTE idle test case
571        if self.PARAM_RRC_STATUS_CHANGE_TIMER not in parameters[0]:
572            self.log.info(
573                "The test config does not include the '{}' key. Disabled "
574                "by default.".format(self.PARAM_RRC_STATUS_CHANGE_TIMER))
575            self.simulator.set_lte_rrc_state_change_timer(False)
576        else:
577            timer = int(parameters[0][self.PARAM_RRC_STATUS_CHANGE_TIMER])
578            self.simulator.set_lte_rrc_state_change_timer(True, timer)
579            self.rrc_sc_timer = timer
580
581    def calibrated_downlink_rx_power(self, bts_config, rsrp):
582        """ LTE simulation overrides this method so that it can convert from
583        RSRP to total signal power transmitted from the basestation.
584
585        Args:
586            bts_config: the current configuration at the base station
587            rsrp: desired rsrp, contained in a key value pair
588        """
589
590        power = self.rsrp_to_signal_power(rsrp, bts_config)
591
592        self.log.info(
593            "Setting downlink signal level to {} RSRP ({} dBm)".format(
594                rsrp, power))
595
596        # Use parent method to calculate signal level
597        return super().calibrated_downlink_rx_power(bts_config, power)
598
599    def downlink_calibration(self, rat=None, power_units_conversion_func=None):
600        """ Computes downlink path loss and returns the calibration value.
601
602        See base class implementation for details.
603
604        Args:
605            rat: ignored, replaced by 'lteRsrp'
606            power_units_conversion_func: ignored, replaced by
607                self.rsrp_to_signal_power
608
609        Returns:
610            Downlink calibration value and measured DL power. Note that the
611            phone only reports RSRP of the primary chain
612        """
613
614        return super().downlink_calibration(
615            rat='lteDbm',
616            power_units_conversion_func=self.rsrp_to_signal_power)
617
618    def rsrp_to_signal_power(self, rsrp, bts_config):
619        """ Converts rsrp to total band signal power
620
621        RSRP is measured per subcarrier, so total band power needs to be
622        multiplied by the number of subcarriers being used.
623
624        Args:
625            rsrp: desired rsrp in dBm
626            bts_config: a base station configuration object
627        Returns:
628            Total band signal power in dBm
629        """
630
631        bandwidth = bts_config.bandwidth
632
633        if bandwidth == 100:  # This assumes 273 RBs. TODO: b/229163022
634            power = rsrp + 35.15
635        elif bandwidth == 20:  # 100 RBs
636            power = rsrp + 30.79
637        elif bandwidth == 15:  # 75 RBs
638            power = rsrp + 29.54
639        elif bandwidth == 10:  # 50 RBs
640            power = rsrp + 27.78
641        elif bandwidth == 5:  # 25 RBs
642            power = rsrp + 24.77
643        elif bandwidth == 3:  # 15 RBs
644            power = rsrp + 22.55
645        elif bandwidth == 1.4:  # 6 RBs
646            power = rsrp + 18.57
647        else:
648            raise ValueError("Invalid bandwidth value.")
649
650        return power
651
652    def maximum_downlink_throughput(self):
653        """ Calculates maximum achievable downlink throughput in the current
654            simulation state.
655
656        Returns:
657            Maximum throughput in mbps.
658
659        """
660        return sum(
661            self.bts_maximum_downlink_throughtput(self.cell_configs[bts_index])
662            for bts_index in range(self.num_carriers))
663
664    def bts_maximum_downlink_throughtput(self, bts_config):
665        """ Calculates maximum achievable downlink throughput for a single
666        base station from its configuration object.
667
668        Args:
669            bts_config: a base station configuration object.
670
671        Returns:
672            Maximum throughput in mbps.
673
674        """
675        if bts_config.mimo_mode == MimoMode.MIMO_1x1:
676            streams = 1
677        elif bts_config.mimo_mode == MimoMode.MIMO_2x2:
678            streams = 2
679        elif bts_config.mimo_mode == MimoMode.MIMO_4x4:
680            streams = 4
681        else:
682            raise ValueError('Unable to calculate maximum downlink throughput '
683                             'because the MIMO mode has not been set.')
684
685        bandwidth = bts_config.bandwidth
686        rb_ratio = bts_config.dl_rbs / TOTAL_RBS_DICTIONARY[bandwidth]
687        mcs = bts_config.dl_mcs
688
689        max_rate_per_stream = None
690
691        tdd_subframe_config = bts_config.dlul_config
692        duplex_mode = bts_config.get_duplex_mode()
693
694        if duplex_mode == DuplexMode.TDD:
695            if bts_config.dl_256_qam_enabled:
696                if mcs == 27:
697                    if bts_config.mac_padding:
698                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
699                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
700                                'DL']
701                    else:
702                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
703                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
704                                'DL']
705            else:
706                if mcs == 28:
707                    if bts_config.mac_padding:
708                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
709                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
710                                'DL']
711                    else:
712                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
713                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
714                                'DL']
715
716        elif duplex_mode == DuplexMode.FDD:
717            if (not bts_config.dl_256_qam_enabled and bts_config.mac_padding
718                    and mcs == 28):
719                max_rate_per_stream = {
720                    3: 9.96,
721                    5: 17.0,
722                    10: 34.7,
723                    15: 52.7,
724                    20: 72.2
725                }.get(bandwidth, None)
726            if (not bts_config.dl_256_qam_enabled and bts_config.mac_padding
727                    and mcs == 27):
728                max_rate_per_stream = {
729                    1.4: 2.94,
730                }.get(bandwidth, None)
731            elif (not bts_config.dl_256_qam_enabled
732                  and not bts_config.mac_padding and mcs == 27):
733                max_rate_per_stream = {
734                    1.4: 2.87,
735                    3: 7.7,
736                    5: 14.4,
737                    10: 28.7,
738                    15: 42.3,
739                    20: 57.7
740                }.get(bandwidth, None)
741            elif bts_config.dl_256_qam_enabled and bts_config.mac_padding and mcs == 27:
742                max_rate_per_stream = {
743                    3: 13.2,
744                    5: 22.9,
745                    10: 46.3,
746                    15: 72.2,
747                    20: 93.9
748                }.get(bandwidth, None)
749            elif bts_config.dl_256_qam_enabled and bts_config.mac_padding and mcs == 26:
750                max_rate_per_stream = {
751                    1.4: 3.96,
752                }.get(bandwidth, None)
753            elif (bts_config.dl_256_qam_enabled and not bts_config.mac_padding
754                  and mcs == 27):
755                max_rate_per_stream = {
756                    3: 11.3,
757                    5: 19.8,
758                    10: 44.1,
759                    15: 68.1,
760                    20: 88.4
761                }.get(bandwidth, None)
762            elif (bts_config.dl_256_qam_enabled and not bts_config.mac_padding
763                  and mcs == 26):
764                max_rate_per_stream = {
765                    1.4: 3.96,
766                }.get(bandwidth, None)
767
768        if not max_rate_per_stream:
769            raise NotImplementedError(
770                "The calculation for MAC padding = {} "
771                "and mcs = {} is not implemented.".format(
772                    "FULLALLOCATION" if bts_config.mac_padding else "OFF",
773                    mcs))
774
775        return max_rate_per_stream * streams * rb_ratio
776
777    def maximum_uplink_throughput(self):
778        """ Calculates maximum achievable uplink throughput in the current
779            simulation state.
780
781        Returns:
782            Maximum throughput in mbps.
783
784        """
785
786        return self.bts_maximum_uplink_throughtput(self.cell_configs[0])
787
788    def bts_maximum_uplink_throughtput(self, bts_config):
789        """ Calculates maximum achievable uplink throughput for the selected
790        basestation from its configuration object.
791
792        Args:
793            bts_config: an LTE base station configuration object.
794
795        Returns:
796            Maximum throughput in mbps.
797
798        """
799
800        bandwidth = bts_config.bandwidth
801        rb_ratio = bts_config.ul_rbs / TOTAL_RBS_DICTIONARY[bandwidth]
802        mcs = bts_config.ul_mcs
803
804        max_rate_per_stream = None
805
806        tdd_subframe_config = bts_config.dlul_config
807        duplex_mode = bts_config.get_duplex_mode()
808
809        if duplex_mode == DuplexMode.TDD:
810            if bts_config.ul_64_qam_enabled:
811                if mcs == 28:
812                    if bts_config.mac_padding:
813                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
814                            'TDD_CONFIG3'][tdd_subframe_config][bandwidth][
815                                'UL']
816                    else:
817                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
818                            'TDD_CONFIG2'][tdd_subframe_config][bandwidth][
819                                'UL']
820            else:
821                if mcs == 23:
822                    if bts_config.mac_padding:
823                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
824                            'TDD_CONFIG4'][tdd_subframe_config][bandwidth][
825                                'UL']
826                    else:
827                        max_rate_per_stream = self.tdd_config_tput_lut_dict[
828                            'TDD_CONFIG1'][tdd_subframe_config][bandwidth][
829                                'UL']
830
831        elif duplex_mode == DuplexMode.FDD:
832            if mcs == 23 and not bts_config.ul_64_qam_enabled:
833                max_rate_per_stream = {
834                    1.4: 2.85,
835                    3: 7.18,
836                    5: 12.1,
837                    10: 24.5,
838                    15: 36.5,
839                    20: 49.1
840                }.get(bandwidth, None)
841            elif mcs == 28 and bts_config.ul_64_qam_enabled:
842                max_rate_per_stream = {
843                    1.4: 4.2,
844                    3: 10.5,
845                    5: 17.2,
846                    10: 35.3,
847                    15: 53.0,
848                    20: 72.6
849                }.get(bandwidth, None)
850
851        if not max_rate_per_stream:
852            raise NotImplementedError(
853                "The calculation fir mcs = {} is not implemented.".format(
854                    "FULLALLOCATION" if bts_config.mac_padding else "OFF",
855                    mcs))
856
857        return max_rate_per_stream * rb_ratio
858
859    def calibrate(self, band):
860        """ Calculates UL and DL path loss if it wasn't done before
861
862        Before running the base class implementation, configure the base station
863        to only use one downlink antenna with maximum bandwidth.
864
865        Args:
866            band: the band that is currently being calibrated.
867        """
868
869        # Save initial values in a configuration object so they can be restored
870        restore_config = LteCellConfig(self.log)
871        restore_config.mimo_mode = self.cell_configs[0].mimo_mode
872        restore_config.transmission_mode = \
873            self.cell_configs[0].transmission_mode
874        restore_config.bandwidth = self.cell_configs[0].bandwidth
875
876        # Set up a temporary calibration configuration.
877        temporary_config = LteCellConfig(self.log)
878        temporary_config.mimo_mode = MimoMode.MIMO_1x1
879        temporary_config.transmission_mode = TransmissionMode.TM1
880        temporary_config.bandwidth = max(
881            self.allowed_bandwidth_dictionary[int(band)])
882        self.simulator.configure_bts(temporary_config)
883        self.cell_configs[0].incorporate(temporary_config)
884
885        super().calibrate(band)
886
887        # Restore values as they were before changing them for calibration.
888        self.simulator.configure_bts(restore_config)
889        self.cell_configs[0].incorporate(restore_config)
890
891    def start_traffic_for_calibration(self):
892        """ If MAC padding is enabled, there is no need to start IP traffic. """
893        if not self.cell_configs[0].mac_padding:
894            super().start_traffic_for_calibration()
895
896    def stop_traffic_for_calibration(self):
897        """ If MAC padding is enabled, IP traffic wasn't started. """
898        if not self.cell_configs[0].mac_padding:
899            super().stop_traffic_for_calibration()
900
901    def get_measured_ul_power(self, samples=5, wait_after_sample=3):
902        """ Calculates UL power using measurements from the callbox and the
903        calibration data.
904
905        Args:
906            samples: the numble of samples to average
907            wait_after_sample: time in seconds to wait in between samples
908
909        Returns:
910            the ul power at the UE antenna ports in dBs
911        """
912        ul_power_sum = 0
913        samples_left = samples
914
915        while samples_left > 0:
916            ul_power_sum += self.simulator.get_measured_pusch_power()
917            samples_left -= 1
918            time.sleep(wait_after_sample)
919
920        # Got enough samples, return calibrated average
921        if self.dl_path_loss:
922            return ul_power_sum / samples + self.ul_path_loss
923        else:
924            self.log.warning('No uplink calibration data. Returning '
925                             'uncalibrated values as measured by the '
926                             'callbox.')
927            return ul_power_sum / samples
928
929    def start(self):
930        """ Set the signal level for the secondary carriers, as the base class
931        implementation of this method will only set up downlink power for the
932        primary carrier component.
933
934        After that, attaches the secondary carriers."""
935
936        super().start()
937
938        if self.num_carriers > 1:
939            if self.sim_dl_power:
940                self.log.info('Setting DL power for secondary carriers.')
941
942                for bts_index in range(1, self.num_carriers):
943                    new_config = LteCellConfig(self.log)
944                    new_config.output_power = self.calibrated_downlink_rx_power(
945                        self.cell_configs[bts_index], self.sim_dl_power)
946                    self.simulator.configure_bts(new_config, bts_index)
947                    self.cell_configs[bts_index].incorporate(new_config)
948
949            self.simulator.lte_attach_secondary_carriers(self.freq_bands)
950
951    def send_sms(self, message):
952        """ Sends an SMS message to the DUT.
953
954        Args:
955            message: the SMS message to send.
956        """
957        self.simulator.send_sms(message)
958