1#!/usr/bin/env python3
2#
3#   Copyright 2019 - 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
18import ntpath
19import time
20import acts.controllers.cellular_simulator as cc
21from acts.controllers.cellular_lib import LteSimulation
22from acts.controllers.anritsu_lib import md8475a
23from acts.controllers.anritsu_lib import _anritsu_utils as anritsu
24
25
26class MD8475CellularSimulator(cc.AbstractCellularSimulator):
27
28    MD8475_VERSION = 'A'
29
30    # Indicates if it is able to use 256 QAM as the downlink modulation for LTE
31    LTE_SUPPORTS_DL_256QAM = False
32
33    # Indicates if it is able to use 64 QAM as the uplink modulation for LTE
34    LTE_SUPPORTS_UL_64QAM = False
35
36    # Indicates if 4x4 MIMO is supported for LTE
37    LTE_SUPPORTS_4X4_MIMO = False
38
39    # The maximum number of carriers that this simulator can support for LTE
40    LTE_MAX_CARRIERS = 2
41
42    # The maximum power that the equipment is able to transmit
43    MAX_DL_POWER = -10
44
45    # Simulation config files in the callbox computer.
46    # These should be replaced in the future by setting up
47    # the same configuration manually.
48    LTE_BASIC_SIM_FILE = 'SIM_default_LTE.wnssp'
49    LTE_BASIC_CELL_FILE = 'CELL_LTE_config.wnscp'
50    LTE_CA_BASIC_SIM_FILE = 'SIM_LTE_CA.wnssp'
51    LTE_CA_BASIC_CELL_FILE = 'CELL_LTE_CA_config.wnscp'
52
53    # Filepath to the config files stored in the Anritsu callbox. Needs to be
54    # formatted to replace {} with either A or B depending on the model.
55    CALLBOX_CONFIG_PATH = 'C:\\Users\\MD8475A\\Documents\\DAN_configs\\'
56
57    def __init__(self, ip_address):
58        """ Initializes the cellular simulator.
59
60        Args:
61            ip_address: the ip address of the MD8475 instrument
62        """
63        super().__init__()
64
65        try:
66            self.anritsu = md8475a.MD8475A(ip_address,
67                                           md8475_version=self.MD8475_VERSION)
68        except anritsu.AnritsuError:
69            raise cc.CellularSimulatorError('Could not connect to MD8475.')
70
71        self.bts = None
72
73    def destroy(self):
74        """ Sends finalization commands to the cellular equipment and closes
75        the connection. """
76        self.anritsu.stop_simulation()
77        self.anritsu.disconnect()
78
79    def setup_lte_scenario(self):
80        """ Configures the equipment for an LTE simulation. """
81        cell_file_name = self.LTE_BASIC_CELL_FILE
82        sim_file_name = self.LTE_BASIC_SIM_FILE
83
84        cell_file_path = ntpath.join(self.CALLBOX_CONFIG_PATH, cell_file_name)
85        sim_file_path = ntpath.join(self.CALLBOX_CONFIG_PATH, sim_file_name)
86
87        self.anritsu.load_simulation_paramfile(sim_file_path)
88        self.anritsu.load_cell_paramfile(cell_file_path)
89
90        # MD4875A supports only 2 carriers. The MD4875B class adds other cells.
91        self.bts = [
92            self.anritsu.get_BTS(md8475a.BtsNumber.BTS1),
93            self.anritsu.get_BTS(md8475a.BtsNumber.BTS2)
94        ]
95
96    def set_band_combination(self, bands, mimo_modes):
97        """ Prepares the test equipment for the indicated band combination.
98
99        The reason why this is implemented in a separate method and not calling
100        LteSimulation.BtsConfig for each separate band is that configuring each
101        ssc cannot be done separately, as it is necessary to know which
102        carriers are on the same band in order to decide which RF outputs can
103        be shared in the test equipment.
104
105        Args:
106            bands: a list of bands represented as ints or strings
107            mimo_modes: a list of LteSimulation.MimoMode to use for each carrier
108        """
109        self.num_carriers = len(bands)
110
111        # Validate the number of carriers.
112        if self.num_carriers > self.LTE_MAX_CARRIERS:
113            raise cc.CellularSimulatorError('The test equipment supports up '
114                                            'to {} carriers.'.format(
115                                                self.LTE_MAX_CARRIERS))
116
117        # Initialize the base stations in the test equipment
118        self.anritsu.set_simulation_model(
119            *[md8475a.BtsTechnology.LTE for _ in range(self.num_carriers)],
120            reset=False)
121
122        # If base stations use different bands, make sure that the RF cards are
123        # not being shared by setting the right maximum MIMO modes
124        if self.num_carriers == 2:
125            # RF cards are never shared when doing 2CA so 4X4 can be done in
126            # both base stations.
127            self.bts[0].mimo_support = md8475a.LteMimoMode.MIMO_4X4
128            self.bts[1].mimo_support = md8475a.LteMimoMode.MIMO_4X4
129        elif self.num_carriers == 3:
130            # 4X4 can only be done in the second base station if it is shared
131            # with the primary. If the RF cards cannot be shared, then at most
132            # 2X2 can be done.
133            self.bts[0].mimo_support = md8475a.LteMimoMode.MIMO_4X4
134            if bands[0] == bands[1]:
135                self.bts[1].mimo_support = md8475a.LteMimoMode.MIMO_4X4
136            else:
137                self.bts[1].mimo_support = md8475a.LteMimoMode.MIMO_2X2
138            self.bts[2].mimo_support = md8475a.LteMimoMode.MIMO_2X2
139        elif self.num_carriers > 3:
140            raise NotImplementedError('The controller doesn\'t implement more '
141                                      'than 3 carriers for MD8475B yet.')
142
143        # Enable carrier aggregation if there is more than one carrier
144        if self.num_carriers > 1:
145            self.anritsu.set_carrier_aggregation_enabled()
146
147        # Restart the simulation as changing the simulation model will stop it.
148        self.anritsu.start_simulation()
149
150    def set_input_power(self, bts_index, input_power):
151        """ Sets the input power for the indicated base station.
152
153        Args:
154            bts_index: the base station number
155            input_power: the new input power
156        """
157        nrb_ul = int(self.bts[bts_index].nrb_ul)
158        max_nrb_ul = self.bts[bts_index].max_nrb_ul
159        input_level = str(
160            round(input_power - 10 * math.log10(nrb_ul / max_nrb_ul), 1))
161        if nrb_ul < max_nrb_ul:
162            self.log.info('Number of UL RBs ({}) is less than the maximum RB '
163                          'allocation ({}). Increasing UL reference power to '
164                          '{} dbm to compensate'.format(
165                              nrb_ul, max_nrb_ul, input_level))
166        self.bts[bts_index].input_level = input_level
167
168    def set_output_power(self, bts_index, output_power):
169        """ Sets the output power for the indicated base station.
170
171        Args:
172            bts_index: the base station number
173            output_power: the new output power
174        """
175        self.bts[bts_index].output_level = output_power
176
177    def set_downlink_channel_number(self, bts_index, channel_number):
178        """ Sets the downlink channel number for the indicated base station.
179
180        Args:
181            bts_index: the base station number
182            channel_number: the new channel number
183        """
184        # Temporarily adding this line to workaround a bug in the
185        # Anritsu callbox in which the channel number needs to be set
186        # to a different value before setting it to the final one.
187        self.bts[bts_index].dl_channel = str(int(channel_number + 1))
188        time.sleep(8)
189        self.bts[bts_index].dl_channel = str(int(channel_number))
190
191    def set_dl_256_qam_enabled(self, bts_index, enabled):
192        """ Determines what MCS table should be used for the downlink.
193
194        Args:
195            bts_index: the base station number
196            enabled: whether 256 QAM should be used
197        """
198        if enabled and not self.LTE_SUPPORTS_DL_256QAM:
199            raise RuntimeError('256 QAM is not supported')
200        self.bts[bts_index].lte_dl_modulation_order = \
201            md8475a.ModulationType.Q256 if enabled else md8475a.ModulationType.Q64
202
203    def set_ul_64_qam_enabled(self, bts_index, enabled):
204        """ Determines what MCS table should be used for the uplink.
205
206        Args:
207            bts_index: the base station number
208            enabled: whether 64 QAM should be used
209        """
210        self.bts[bts_index].lte_ul_modulation_order = \
211            md8475a.ModulationType.Q64 if enabled else md8475a.ModulationType.Q16
212
213    def set_mac_padding(self, bts_index, mac_padding):
214        """ Enables or disables MAC padding in the indicated base station.
215
216        Args:
217            bts_index: the base station number
218            mac_padding: the new MAC padding setting
219        """
220        if mac_padding:
221            self.bts[bts_index].tbs_pattern = 'FULLALLOCATION'
222        else:
223            self.bts[bts_index].tbs_pattern = 'OFF'
224
225    def set_lte_rrc_state_change_timer(self, enabled, time=10):
226        """ Configures the LTE RRC state change timer.
227
228        Args:
229            enabled: a boolean indicating if the timer should be on or off.
230            time: time in seconds for the timer to expire
231        """
232        self.anritsu.set_lte_rrc_status_change(enabled)
233        if enabled:
234            self.anritsu.set_lte_rrc_status_change_timer(time)
235
236    def set_cfi(self, bts_index, cfi):
237        """ Sets the Channel Format Indicator for the indicated base station.
238
239        Args:
240            bts_index: the base station number
241            cfi: the new CFI setting
242        """
243        self.bts[bts_index].cfi = cfi
244
245    def set_paging_cycle(self, bts_index, cycle_duration):
246        """ Sets the paging cycle duration for the indicated base station.
247
248        Args:
249            bts_index: the base station number
250            cycle_duration: the new paging cycle duration in milliseconds
251        """
252        # TODO (b/146068532): implement.
253        self.bts[bts_index].paging_duration = cycle_duration
254
255    def set_phich_resource(self, bts_index, phich):
256        """ Sets the PHICH Resource setting for the indicated base station.
257
258        Args:
259            bts_index: the base station number
260            phich: the new PHICH resource setting
261        """
262        self.bts[bts_index].phich_resource = phich
263
264    def set_drx_connected_mode(self, bts_index, active):
265        """ Sets the DRX connected mode
266
267        Args:
268            bts_index: the base station number
269            active: Boolean indicating whether cDRX mode
270                is active
271        """
272        mode = 'MANUAL' if active else 'OFF'
273        self.bts[bts_index].drx_connected_mode = mode
274
275    def set_drx_on_duration_timer(self, bts_index, timer):
276        """ Sets the amount of PDCCH subframes to wait for data after
277            waking up from a DRX cycle
278
279        Args:
280            bts_index: the base station number
281            timer: Number of PDCCH subframes to wait and check for user data
282                after waking from the DRX cycle
283        """
284        self.bts[bts_index].drx_on_duration_timer = timer
285
286    def set_drx_inactivity_timer(self, bts_index, timer):
287        """ Sets the number of PDCCH subframes to wait before entering DRX mode
288
289        Args:
290            bts_index: the base station number
291            timer: The time interval to wait before entering DRX mode
292        """
293        self.bts[bts_index].drx_inactivity_timer = timer
294
295    def set_drx_retransmission_timer(self, bts_index, timer):
296        """ Sets the number of consecutive PDCCH subframes to wait
297        for retransmission
298
299        Args:
300            bts_index: the base station number
301            timer: Number of PDCCH subframes to remain active
302
303        """
304        self.bts[bts_index].drx_retransmission_timer = timer
305
306    def set_drx_long_cycle(self, bts_index, cycle):
307        """ Sets the amount of subframes representing a DRX long cycle.
308
309        Args:
310            bts_index: the base station number
311            cycle: The amount of subframes representing one long DRX cycle.
312                One cycle consists of DRX sleep + DRX on duration
313        """
314        self.bts[bts_index].drx_long_cycle = cycle
315
316    def set_drx_long_cycle_offset(self, bts_index, offset):
317        """ Sets the offset used to determine the subframe number
318        to begin the long drx cycle
319
320        Args:
321            bts_index: the base station number
322            offset: Number in range 0 to (long cycle - 1)
323        """
324        self.bts[bts_index].drx_long_cycle_offset = offset
325
326    def set_band(self, bts_index, band):
327        """ Sets the right duplex mode before switching to a new band.
328
329        Args:
330            bts_index: the base station number
331            band: desired band
332        """
333        bts = self.bts[bts_index]
334
335        # The callbox won't restore the band-dependent default values if the
336        # request is to switch to the same band as the one the base station is
337        # currently using. To ensure that default values are restored, go to a
338        # different band before switching.
339        if int(bts.band) == band:
340            # Using bands 1 and 2 but it could be any others
341            bts.band = '1' if band != 1 else '2'
342            # Switching to config.band will be handled by the parent class
343            # implementation of this method.
344
345        bts.duplex_mode = self.get_duplex_mode(band).value
346        bts.band = band
347        time.sleep(5)  # It takes some time to propagate the new band
348
349    def get_duplex_mode(self, band):
350        """ Determines if the band uses FDD or TDD duplex mode
351
352        Args:
353            band: a band number
354        Returns:
355            an variable of class DuplexMode indicating if band is FDD or TDD
356        """
357
358        if 33 <= int(band) <= 46:
359            return LteSimulation.DuplexMode.TDD
360        else:
361            return LteSimulation.DuplexMode.FDD
362
363    def set_tdd_config(self, bts_index, config):
364        """ Sets the frame structure for TDD bands.
365
366        Args:
367            bts_index: the base station number
368            config: the desired frame structure. An int between 0 and 6.
369        """
370
371        if not 0 <= config <= 6:
372            raise ValueError("The frame structure configuration has to be a "
373                             "number between 0 and 6")
374
375        self.bts[bts_index].uldl_configuration = config
376
377        # Wait for the setting to propagate
378        time.sleep(5)
379
380    def set_ssf_config(self, bts_index, ssf_config):
381        """ Sets the Special Sub-Frame config number for the indicated
382        base station.
383
384        Args:
385            bts_index: the base station number
386            ssf_config: the new ssf config number
387        """
388        # Cast to int in case it was passed as a string
389        ssf_config = int(ssf_config)
390
391        if not 0 <= ssf_config <= 9:
392            raise ValueError('The Special Sub-Frame configuration has to be a '
393                             'number between 0 and 9.')
394
395        self.bts[bts_index].tdd_special_subframe = ssf_config
396
397    def set_bandwidth(self, bts_index, bandwidth):
398        """ Sets the LTE channel bandwidth (MHz)
399
400        Args:
401            bts_index: the base station number
402            bandwidth: desired bandwidth (MHz)
403        """
404        bts = self.bts[bts_index]
405
406        if bandwidth == 20:
407            bts.bandwidth = md8475a.BtsBandwidth.LTE_BANDWIDTH_20MHz
408        elif bandwidth == 15:
409            bts.bandwidth = md8475a.BtsBandwidth.LTE_BANDWIDTH_15MHz
410        elif bandwidth == 10:
411            bts.bandwidth = md8475a.BtsBandwidth.LTE_BANDWIDTH_10MHz
412        elif bandwidth == 5:
413            bts.bandwidth = md8475a.BtsBandwidth.LTE_BANDWIDTH_5MHz
414        elif bandwidth == 3:
415            bts.bandwidth = md8475a.BtsBandwidth.LTE_BANDWIDTH_3MHz
416        elif bandwidth == 1.4:
417            bts.bandwidth = md8475a.BtsBandwidth.LTE_BANDWIDTH_1dot4MHz
418        else:
419            msg = "Bandwidth = {} MHz is not valid for LTE".format(bandwidth)
420            self.log.error(msg)
421            raise ValueError(msg)
422        time.sleep(5)  # It takes some time to propagate the new settings
423
424    def set_mimo_mode(self, bts_index, mimo):
425        """ Sets the number of DL antennas for the desired MIMO mode.
426
427        Args:
428            bts_index: the base station number
429            mimo: object of class MimoMode
430        """
431
432        bts = self.bts[bts_index]
433
434        # If the requested mimo mode is not compatible with the current TM,
435        # warn the user before changing the value.
436
437        if mimo == LteSimulation.MimoMode.MIMO_1x1:
438            if bts.transmode not in [
439                    LteSimulation.TransmissionMode.TM1,
440                    LteSimulation.TransmissionMode.TM7
441            ]:
442                self.log.warning(
443                    "Using only 1 DL antennas is not allowed with "
444                    "the current transmission mode. Changing the "
445                    "number of DL antennas will override this "
446                    "setting.")
447            bts.dl_antenna = 1
448        elif mimo == LteSimulation.MimoMode.MIMO_2x2:
449            if bts.transmode not in [
450                    LteSimulation.TransmissionMode.TM2,
451                    LteSimulation.TransmissionMode.TM3,
452                    LteSimulation.TransmissionMode.TM4,
453                    LteSimulation.TransmissionMode.TM8,
454                    LteSimulation.TransmissionMode.TM9
455            ]:
456                self.log.warning("Using two DL antennas is not allowed with "
457                                 "the current transmission mode. Changing the "
458                                 "number of DL antennas will override this "
459                                 "setting.")
460            bts.dl_antenna = 2
461        elif mimo == LteSimulation.MimoMode.MIMO_4x4 and \
462            self.LTE_SUPPORTS_4X4_MIMO:
463            if bts.transmode not in [
464                    LteSimulation.TransmissionMode.TM2,
465                    LteSimulation.TransmissionMode.TM3,
466                    LteSimulation.TransmissionMode.TM4,
467                    LteSimulation.TransmissionMode.TM9
468            ]:
469                self.log.warning("Using four DL antennas is not allowed with "
470                                 "the current transmission mode. Changing the "
471                                 "number of DL antennas will override this "
472                                 "setting.")
473
474            bts.dl_antenna = 4
475        else:
476            RuntimeError("The requested MIMO mode is not supported.")
477
478    def set_scheduling_mode(self, bts_index, scheduling, mcs_dl, mcs_ul,
479                            nrb_dl, nrb_ul):
480        """ Sets the scheduling mode for LTE
481
482        Args:
483            bts_index: the base station number
484            scheduling: DYNAMIC or STATIC scheduling (Enum list)
485            mcs_dl: Downlink MCS (only for STATIC scheduling)
486            mcs_ul: Uplink MCS (only for STATIC scheduling)
487            nrb_dl: Number of RBs for downlink (only for STATIC scheduling)
488            nrb_ul: Number of RBs for uplink (only for STATIC scheduling)
489        """
490
491        bts = self.bts[bts_index]
492        bts.lte_scheduling_mode = scheduling.value
493
494        if scheduling == LteSimulation.SchedulingMode.STATIC:
495
496            if not all([nrb_dl, nrb_ul, mcs_dl, mcs_ul]):
497                raise ValueError('When the scheduling mode is set to manual, '
498                                 'the RB and MCS parameters are required.')
499
500            bts.packet_rate = md8475a.BtsPacketRate.LTE_MANUAL
501            bts.lte_mcs_dl = mcs_dl
502            bts.lte_mcs_ul = mcs_ul
503            bts.nrb_dl = nrb_dl
504            bts.nrb_ul = nrb_ul
505
506        time.sleep(5)  # It takes some time to propagate the new settings
507
508    def lte_attach_secondary_carriers(self, ue_capability_enquiry):
509        """ Activates the secondary carriers for CA. Requires the DUT to be
510        attached to the primary carrier first.
511
512        Args:
513            ue_capability_enquiry: UE capability enquiry message to be sent to
514        the UE before starting carrier aggregation.
515        """
516
517        # Trigger UE capability enquiry from network to get
518        # UE supported CA band combinations. Here freq_bands is a hex string.
519        self.anritsu.trigger_ue_capability_enquiry(ue_capability_enquiry)
520
521        testcase = self.anritsu.get_AnritsuTestCases()
522        # A bug in the instrument's software (b/139547391) requires the test
523        # procedure to be set to whatever was the previous configuration before
524        # setting it to MULTICELL.
525        testcase.procedure = md8475a.TestProcedure(testcase.procedure)
526        testcase.procedure = md8475a.TestProcedure.PROCEDURE_MULTICELL
527        testcase.power_control = md8475a.TestPowerControl.POWER_CONTROL_DISABLE
528        testcase.measurement_LTE = md8475a.TestMeasurement.MEASUREMENT_DISABLE
529
530        # Enable the secondary carrier base stations for CA
531        for bts_index in range(1, self.num_carriers):
532            self.bts[bts_index].dl_cc_enabled = True
533
534        self.anritsu.start_testcase()
535
536        retry_counter = 0
537        self.log.info("Waiting for the test case to start...")
538        time.sleep(5)
539
540        while self.anritsu.get_testcase_status() == "0":
541            retry_counter += 1
542            if retry_counter == 3:
543                raise RuntimeError(
544                    "The test case failed to start after {} "
545                    "retries. The connection between the phone "
546                    "and the base station might be unstable.".format(
547                        retry_counter))
548            time.sleep(10)
549
550    def set_transmission_mode(self, bts_index, tmode):
551        """ Sets the transmission mode for the LTE basetation
552
553        Args:
554            bts_index: the base station number
555            tmode: Enum list from class 'TransmissionModeLTE'
556        """
557
558        bts = self.bts[bts_index]
559
560        # If the selected transmission mode does not support the number of DL
561        # antennas, throw an exception.
562        if (tmode in [
563                LteSimulation.TransmissionMode.TM1,
564                LteSimulation.TransmissionMode.TM7
565        ] and bts.dl_antenna != '1'):
566            # TM1 and TM7 only support 1 DL antenna
567            raise ValueError("{} allows only one DL antenna. Change the "
568                             "number of DL antennas before setting the "
569                             "transmission mode.".format(tmode.value))
570        elif (tmode == LteSimulation.TransmissionMode.TM8
571              and bts.dl_antenna != '2'):
572            # TM8 requires 2 DL antennas
573            raise ValueError("TM2 requires two DL antennas. Change the "
574                             "number of DL antennas before setting the "
575                             "transmission mode.")
576        elif (tmode in [
577                LteSimulation.TransmissionMode.TM2,
578                LteSimulation.TransmissionMode.TM3,
579                LteSimulation.TransmissionMode.TM4,
580                LteSimulation.TransmissionMode.TM9
581        ] and bts.dl_antenna == '1'):
582            # TM2, TM3, TM4 and TM9 require 2 or 4 DL antennas
583            raise ValueError("{} requires at least two DL atennas. Change the "
584                             "number of DL antennas before setting the "
585                             "transmission mode.".format(tmode.value))
586
587        # The TM mode is allowed for the current number of DL antennas, so it
588        # is safe to change this setting now
589        bts.transmode = tmode.value
590
591        time.sleep(5)  # It takes some time to propagate the new settings
592
593    def wait_until_attached(self, timeout=120):
594        """ Waits until the DUT is attached to the primary carrier.
595
596        Args:
597            timeout: after this amount of time the method will raise a
598                CellularSimulatorError exception. Default is 120 seconds.
599        """
600        try:
601            self.anritsu.wait_for_registration_state(time_to_wait=timeout)
602        except anritsu.AnritsuError:
603            raise cc.CellularSimulatorError('The phone did not attach before '
604                                            'the timeout period ended.')
605
606    def wait_until_communication_state(self, timeout=120):
607        """ Waits until the DUT is in Communication state.
608
609        Args:
610            timeout: after this amount of time the method will raise a
611                CellularSimulatorError exception. Default is 120 seconds.
612        """
613        try:
614            self.anritsu.wait_for_communication_state(time_to_wait=timeout)
615        except anritsu.AnritsuError:
616            raise cc.CellularSimulatorError('The phone was not in '
617                                            'Communication state before '
618                                            'the timeout period ended.')
619
620    def wait_until_idle_state(self, timeout=120):
621        """ Waits until the DUT is in Idle state.
622
623        Args:
624            timeout: after this amount of time the method will raise a
625                CellularSimulatorError exception. Default is 120 seconds.
626        """
627        try:
628            self.anritsu.wait_for_idle_state(time_to_wait=timeout)
629        except anritsu.AnritsuError:
630            raise cc.CellularSimulatorError('The phone was not in Idle state '
631                                            'before the time the timeout '
632                                            'period ended.')
633
634    def detach(self):
635        """ Turns off all the base stations so the DUT loose connection."""
636        if self.anritsu.get_smartstudio_status() == \
637            md8475a.ProcessingStatus.PROCESS_STATUS_NOTRUN.value:
638            self.log.info('Device cannot be detached because simulation is '
639                          'not running.')
640            return
641        self.anritsu.set_simulation_state_to_poweroff()
642
643    def stop(self):
644        """ Stops current simulation. After calling this method, the simulator
645        will need to be set up again. """
646        self.anritsu.stop_simulation()
647
648    def start_data_traffic(self):
649        """ Starts transmitting data from the instrument to the DUT. """
650        try:
651            self.anritsu.start_ip_traffic()
652        except md8475a.AnritsuError as inst:
653            # This typically happens when traffic is already running.
654            # TODO (b/141962691): continue only if traffic is running
655            self.log.warning(str(inst))
656        time.sleep(4)
657
658    def stop_data_traffic(self):
659        """ Stops transmitting data from the instrument to the DUT. """
660        try:
661            self.anritsu.stop_ip_traffic()
662        except md8475a.AnritsuError as inst:
663            # This typically happens when traffic has already been stopped
664            # TODO (b/141962691): continue only if traffic is stopped
665            self.log.warning(str(inst))
666        time.sleep(2)
667
668    def get_measured_pusch_power(self):
669        """ Queries PUSCH power measured at the callbox.
670
671        Returns:
672            The PUSCH power in the primary input port.
673        """
674        # Try three times before raising an exception. This is needed because
675        # the callbox sometimes reports an active chain as 'DEACTIVE'.
676        retries_left = 3
677
678        while retries_left > 0:
679
680            ul_pusch = self.anritsu.get_measured_pusch_power().split(',')[0]
681
682            if ul_pusch != 'DEACTIVE':
683                return float(ul_pusch)
684
685            time.sleep(3)
686            retries_left -= 1
687            self.log.info('Chain shows as inactive. %d retries left.' %
688                          retries_left)
689
690        raise cc.CellularSimulatorError('Could not get measured PUSCH power.')
691
692
693class MD8475BCellularSimulator(MD8475CellularSimulator):
694
695    MD8475_VERSION = 'B'
696
697    # Indicates if it is able to use 256 QAM as the downlink modulation for LTE
698    LTE_SUPPORTS_DL_256QAM = True
699
700    # Indicates if it is able to use 64 QAM as the uplink modulation for LTE
701    LTE_SUPPORTS_UL_64QAM = True
702
703    # Indicates if 4x4 MIMO is supported for LTE
704    LTE_SUPPORTS_4X4_MIMO = True
705
706    # The maximum number of carriers that this simulator can support for LTE
707    LTE_MAX_CARRIERS = 4
708
709    # The maximum power that the equipment is able to transmit
710    MAX_DL_POWER = -10
711
712    # Simulation config files in the callbox computer.
713    # These should be replaced in the future by setting up
714    # the same configuration manually.
715    LTE_BASIC_SIM_FILE = 'SIM_default_LTE.wnssp2'
716    LTE_BASIC_CELL_FILE = 'CELL_LTE_config.wnscp2'
717    LTE_CA_BASIC_SIM_FILE = 'SIM_LTE_CA.wnssp2'
718    LTE_CA_BASIC_CELL_FILE = 'CELL_LTE_CA_config.wnscp2'
719
720    # Filepath to the config files stored in the Anritsu callbox. Needs to be
721    # formatted to replace {} with either A or B depending on the model.
722    CALLBOX_CONFIG_PATH = 'C:\\Users\\MD8475B\\Documents\\DAN_configs\\'
723
724    def setup_lte_scenario(self):
725        """ The B model can support up to five carriers. """
726
727        super().setup_lte_scenario()
728
729        self.bts.extend([
730            self.anritsu.get_BTS(md8475a.BtsNumber.BTS3),
731            self.anritsu.get_BTS(md8475a.BtsNumber.BTS4),
732            self.anritsu.get_BTS(md8475a.BtsNumber.BTS5)
733        ])
734