xref: /aosp_15_r20/external/autotest/server/cros/cellular/callbox_utils/cmw500.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Copyright 2021 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from enum import Enum
6import time
7
8from autotest_lib.server.cros.cellular import abstract_inst
9
10LTE_ATTACH_RESP = 'ATT'
11LTE_CONN_RESP = 'CONN'
12LTE_IDLE_RESP = 'IDLE'
13LTE_PSWITCHED_ON_RESP = 'ON'
14LTE_PSWITCHED_OFF_RESP = 'OFF'
15
16STATE_CHANGE_TIMEOUT = 20
17
18
19class LteState(Enum):
20    """LTE ON and OFF"""
21    LTE_ON = 'ON'
22    LTE_OFF = 'OFF'
23
24
25class BtsNumber(Enum):
26    """Base station Identifiers."""
27    BTS1 = 'PCC'
28    BTS2 = 'SCC1'
29    BTS3 = 'SCC2'
30    BTS4 = 'SCC3'
31    BTS5 = 'SCC4'
32    BTS6 = 'SCC6'
33    BTS7 = 'SCC7'
34
35
36class LteBandwidth(Enum):
37    """Supported LTE bandwidths."""
38    BANDWIDTH_1MHz = 'B014'
39    BANDWIDTH_3MHz = 'B030'
40    BANDWIDTH_5MHz = 'B050'
41    BANDWIDTH_10MHz = 'B100'
42    BANDWIDTH_15MHz = 'B150'
43    BANDWIDTH_20MHz = 'B200'
44
45
46class DuplexMode(Enum):
47    """Duplex Modes"""
48    FDD = 'FDD'
49    TDD = 'TDD'
50
51
52class SchedulingMode(Enum):
53    """Supported scheduling modes."""
54    RMC = 'RMC'
55    USERDEFINEDCH = 'UDCHannels'
56
57
58class TransmissionModes(Enum):
59    """Supported transmission modes."""
60    TM1 = 'TM1'
61    TM2 = 'TM2'
62    TM3 = 'TM3'
63    TM4 = 'TM4'
64    TM7 = 'TM7'
65    TM8 = 'TM8'
66    TM9 = 'TM9'
67
68
69class UseCarrierSpecific(Enum):
70    """Enable or disable carrier specific."""
71    UCS_ON = 'ON'
72    UCS_OFF = 'OFF'
73
74
75class RbPosition(Enum):
76    """Supported RB positions."""
77    LOW = 'LOW'
78    HIGH = 'HIGH'
79    P5 = 'P5'
80    P10 = 'P10'
81    P23 = 'P23'
82    P35 = 'P35'
83    P48 = 'P48'
84
85
86class ModulationType(Enum):
87    """Supported Modulation Types."""
88    QPSK = 'QPSK'
89    Q16 = 'Q16'
90    Q64 = 'Q64'
91    Q256 = 'Q256'
92
93
94class DciFormat(Enum):
95    """Support DCI Formats for MIMOs"""
96    D1 = 'D1'
97    D1A = 'D1A'
98    D1B = 'D1B'
99    D2 = 'D2'
100    D2A = 'D2A'
101    D2B = 'D2B'
102    D2C = 'D2C'
103
104
105class MimoModes(Enum):
106    """MIMO Modes dl antennas"""
107    MIMO1x1 = 'ONE'
108    MIMO2x2 = 'TWO'
109    MIMO4x4 = 'FOUR'
110
111
112class MimoScenario(Enum):
113    """Supported mimo scenarios"""
114    SCEN1x1 = 'SCELl:FLEXible SUA1,RF1C,RX1,RF1C,TX1'
115    SCEN2x2 = 'TRO:FLEXible SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2'
116    SCEN4x4 = 'FRO FLEXible SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2,RF2C,TX3,RF4C,TX4'
117
118
119class RrcState(Enum):
120    """States to enable/disable rrc."""
121    RRC_ON = 'ON'
122    RRC_OFF = 'OFF'
123
124
125class MacPadding(Enum):
126    """Enables/Disables Mac Padding."""
127    ON = 'ON'
128    OFF = 'OFF'
129
130
131class ConnectionType(Enum):
132    """Supported Connection Types."""
133    TEST = 'TESTmode'
134    DAU = 'DAPPlication'
135
136
137class RepetitionMode(Enum):
138    """Specifies LTE Measurement Repetition Mode."""
139    SINGLESHOT = 'SINGleshot'
140    CONTINUOUS = 'CONTinuous'
141
142
143class TpcPowerControl(Enum):
144    """Specifies Up Link power control types."""
145    MIN_POWER = 'MINPower'
146    MAX_POWER = 'MAXPower'
147    CONSTANT = 'CONStant'
148    SINGLE = 'SINGle'
149    UDSINGLE = 'UDSingle'
150    UDCONTINUOUS = 'UDContinuous'
151    ALTERNATE = 'ALT0'
152    CLOSED_LOOP = 'CLOop'
153    RP_CONTROL = 'RPControl'
154    FLEX_POWER = 'FULPower'
155
156
157class ReducedPdcch(Enum):
158    """Enables/disables the reduction of PDCCH resources."""
159    ON = 'ON'
160    OFF = 'OFF'
161
162
163class Cmw500(abstract_inst.SocketInstrument):
164    """ Base class for interfacing with the CMW500 Callbox device """
165
166    def __init__(self, ip_addr, port):
167        """Init method to setup variables for controllers.
168
169        Args:
170              ip_addr: Controller's ip address.
171              port: Port
172        """
173        super(Cmw500, self).__init__(ip_addr, port)
174        self._connect_socket()
175        self._send('*CLS')
176        self._send('*ESE 0;*SRE 0')
177        self._send('*CLS')
178        self._send('*ESE 1;*SRE 4')
179        self._send('SYST:DISP:UPD ON')
180
181    def switch_lte_signalling(self, state):
182        """ Turns LTE signalling ON/OFF.
183
184        Args:
185              state: an instance of LteState indicating the state to which LTE
186                signal has to be set.
187        """
188        if not isinstance(state, LteState):
189            raise ValueError('state should be the instance of LteState.')
190
191        state = state.value
192
193        cmd = 'SOURce:LTE:SIGN:CELL:STATe {}'.format(state)
194        self.send_and_recv(cmd)
195
196        time_elapsed = 0
197        while time_elapsed < STATE_CHANGE_TIMEOUT:
198            response = self.send_and_recv('SOURce:LTE:SIGN:CELL:STATe:ALL?')
199
200            if response == state + ',ADJ':
201                self._logger.info('LTE signalling is now {}.'.format(state))
202                break
203
204            # Wait for a second and increase time count by one
205            time.sleep(1)
206            time_elapsed += 1
207        else:
208            raise CmwError('Failed to turn {} LTE signalling.'.format(state))
209
210    def enable_packet_switching(self):
211        """Enable packet switching in call box."""
212        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion CONNect')
213        self.wait_for_pswitched_state()
214
215    def disable_packet_switching(self):
216        """Disable packet switching in call box."""
217        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion DISConnect')
218        self.wait_for_pswitched_state()
219
220    @property
221    def use_carrier_specific(self):
222        """Gets current status of carrier specific duplex configuration."""
223        return self.send_and_recv('CONFigure:LTE:SIGN:DMODe:UCSPECific?')
224
225    @use_carrier_specific.setter
226    def use_carrier_specific(self, state):
227        """Sets the carrier specific duplex configuration.
228
229        Args:
230            state: ON/OFF UCS configuration.
231        """
232        cmd = 'CONFigure:LTE:SIGN:DMODe:UCSPECific {}'.format(state)
233        self.send_and_recv(cmd)
234
235    def send_and_recv(self, cmd):
236        """Send and recv the status of the command.
237
238        Args:
239            cmd: Command to send.
240
241        Returns:
242            status: returns the status of the command sent.
243        """
244
245        self._send(cmd)
246        if '?' in cmd:
247            status = self._recv()
248            return status
249
250    def configure_mimo_settings(self, mimo):
251        """Sets the mimo scenario for the test.
252
253        Args:
254            mimo: mimo scenario to set.
255        """
256        cmd = 'ROUTe:LTE:SIGN:SCENario:{}'.format(mimo.value)
257        self.send_and_recv(cmd)
258
259    def wait_for_pswitched_state(self, timeout=10):
260        """Wait until pswitched state.
261
262        Args:
263            timeout: timeout for lte pswitched state.
264
265        Raises:
266            CmwError on timeout.
267        """
268        while timeout > 0:
269            state = self.send_and_recv('FETCh:LTE:SIGN:PSWitched:STATe?')
270            if state == LTE_PSWITCHED_ON_RESP:
271                self._logger.debug('Connection to setup initiated.')
272                break
273            elif state == LTE_PSWITCHED_OFF_RESP:
274                self._logger.debug('Connection to setup detached.')
275                break
276
277            # Wait for a second and decrease count by one
278            time.sleep(1)
279            timeout -= 1
280        else:
281            raise CmwError('Failure in setting up/detaching connection')
282
283    def wait_for_attached_state(self, timeout=120):
284        """Attach the controller with device.
285
286        Args:
287            timeout: timeout for phone to get attached.
288
289        Raises:
290            CmwError on time out.
291        """
292        while timeout > 0:
293            state = self.send_and_recv('FETCh:LTE:SIGN:PSWitched:STATe?')
294
295            if state == LTE_ATTACH_RESP:
296                self._logger.debug('Call box attached with device')
297                break
298
299            # Wait for a second and decrease count by one
300            time.sleep(1)
301            timeout -= 1
302        else:
303            raise CmwError('Device could not be attached')
304
305    def wait_for_rrc_state(self, state, timeout=120):
306        """ Waits until a certain RRC state is set.
307
308        Args:
309            state: the RRC state that is being waited for.
310            timeout: timeout for phone to be in connected state.
311
312        Raises:
313            CmwError on time out.
314        """
315        if state not in [LTE_CONN_RESP, LTE_IDLE_RESP]:
316            raise ValueError(
317                    'The allowed values for state are {} and {}.'.format(
318                            LTE_CONN_RESP, LTE_IDLE_RESP))
319
320        while timeout > 0:
321            new_state = self.send_and_recv('SENSe:LTE:SIGN:RRCState?')
322            if new_state == state:
323                self._logger.debug('The RRC state is {}.'.format(new_state))
324                break
325
326            # Wait for a second and decrease count by one
327            time.sleep(1)
328            timeout -= 1
329        else:
330            raise CmwError('Timeout before RRC state was {}.'.format(state))
331
332    def reset(self):
333        """System level reset"""
334        self.send_and_recv('*RST; *OPC')
335
336    @property
337    def get_instrument_id(self):
338        """Gets instrument identification number"""
339        return self.send_and_recv('*IDN?')
340
341    def disconnect(self):
342        """Disconnect controller from device and switch to local mode."""
343        self.switch_lte_signalling(LteState.LTE_OFF)
344        self.close_remote_mode()
345        self._close_socket()
346
347    def close_remote_mode(self):
348        """Exits remote mode to local mode."""
349        self.send_and_recv('&GTL')
350
351    def detach(self):
352        """Detach callbox and controller."""
353        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion DETach')
354
355    @property
356    def rrc_connection(self):
357        """Gets the RRC connection state."""
358        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:KRRC?')
359
360    @rrc_connection.setter
361    def rrc_connection(self, state):
362        """Selects whether the RRC connection is kept or released after attach.
363
364        Args:
365            mode: RRC State ON/OFF.
366        """
367        if not isinstance(state, RrcState):
368            raise ValueError('state should be the instance of RrcState.')
369
370        cmd = 'CONFigure:LTE:SIGN:CONNection:KRRC {}'.format(state.value)
371        self.send_and_recv(cmd)
372
373    @property
374    def rrc_connection_timer(self):
375        """Gets the inactivity timeout for disabled rrc connection."""
376        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:RITimer?')
377
378    @rrc_connection_timer.setter
379    def rrc_connection_timer(self, time_in_secs):
380        """Sets the inactivity timeout for disabled rrc connection. By default
381        the timeout is set to 5.
382
383        Args:
384            time_in_secs: timeout of inactivity in rrc connection.
385        """
386        cmd = 'CONFigure:LTE:SIGN:CONNection:RITimer {}'.format(time_in_secs)
387        self.send_and_recv(cmd)
388
389    @property
390    def dl_mac_padding(self):
391        """Gets the state of mac padding."""
392        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:DLPadding?')
393
394    @dl_mac_padding.setter
395    def dl_mac_padding(self, state):
396        """Enables/Disables downlink padding at the mac layer.
397
398        Args:
399            state: ON/OFF
400        """
401        cmd = 'CONFigure:LTE:SIGN:CONNection:DLPadding {}'.format(state.value)
402        self.send_and_recv(cmd)
403
404    @property
405    def connection_type(self):
406        """Gets the connection type applied in callbox."""
407        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:CTYPe?')
408
409    @connection_type.setter
410    def connection_type(self, ctype):
411        """Sets the connection type to be applied.
412
413        Args:
414            ctype: Connection type.
415        """
416        cmd = 'CONFigure:LTE:SIGN:CONNection:CTYPe {}'.format(ctype.value)
417        self.send_and_recv(cmd)
418
419    def get_base_station(self, bts_num=BtsNumber.BTS1):
420        """Gets the base station object based on bts num. By default
421        bts_num set to PCC
422
423        Args:
424            bts_num: base station identifier
425
426        Returns:
427            base station object.
428        """
429        return BaseStation(self, bts_num)
430
431    def init_lte_measurement(self):
432        """Gets the class object for lte measurement which can be used to
433        initiate measurements.
434
435        Returns:
436            lte measurement object.
437        """
438        return LteMeasurement(self)
439
440    def set_sms(self, sms_message):
441        """Sets the SMS message to be sent by the callbox."""
442        self.send_and_recv('CONFigure:LTE:SIGN:SMS:OUTGoing:INTernal "%s"' % sms_message)
443
444    def send_sms(self):
445        """Sends the SMS message."""
446        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion SMS; *OPC?')
447        timeout = time.time() + STATE_CHANGE_TIMEOUT
448        while "SUCC" != self.send_and_recv('SENSe:LTE:SIGN:SMS:OUTGoing:INFO:LMSent?'):
449            if time.time() > timeout:
450                raise CmwError("SENSe:LTE:SIGN:SMS:OUTGoing:INFO:LMSent? never returns status 'SUCC' instead got (%s)" % self.send_and_recv('SENSe:LTE:SIGN:SMS:OUTGoing:INFO:LMSent?'))
451            time.sleep(2)
452
453
454class BaseStation(object):
455    """Class to interact with different base stations"""
456
457    def __init__(self, cmw, bts_num):
458        if not isinstance(bts_num, BtsNumber):
459            raise ValueError('bts_num should be an instance of BtsNumber.')
460        self._bts = bts_num.value
461        self._cmw = cmw
462
463    @property
464    def duplex_mode(self):
465        """Gets current duplex of cell."""
466        cmd = 'CONFigure:LTE:SIGN:{}:DMODe?'.format(self._bts)
467        return self._cmw.send_and_recv(cmd)
468
469    @duplex_mode.setter
470    def duplex_mode(self, mode):
471        """Sets the Duplex mode of cell.
472
473        Args:
474            mode: String indicating FDD or TDD.
475        """
476        if not isinstance(mode, DuplexMode):
477            raise ValueError('mode should be an instance of DuplexMode.')
478
479        cmd = 'CONFigure:LTE:SIGN:{}:DMODe {}'.format(self._bts, mode.value)
480        self._cmw.send_and_recv(cmd)
481
482    @property
483    def band(self):
484        """Gets the current band of cell."""
485        cmd = 'CONFigure:LTE:SIGN:{}:BAND?'.format(self._bts)
486        return self._cmw.send_and_recv(cmd)
487
488    @band.setter
489    def band(self, band):
490        """Sets the Band of cell.
491
492        Args:
493            band: band of cell.
494        """
495        cmd = 'CONFigure:LTE:SIGN:{}:BAND {}'.format(self._bts, band)
496        self._cmw.send_and_recv(cmd)
497
498    @property
499    def dl_channel(self):
500        """Gets the downlink channel of cell."""
501        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL?'.format(self._bts)
502        return self._cmw.send_and_recv(cmd)
503
504    @dl_channel.setter
505    def dl_channel(self, channel):
506        """Sets the downlink channel number of cell.
507
508        Args:
509            channel: downlink channel number of cell.
510        """
511        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL {}'.format(
512                self._bts, channel)
513        self._cmw.send_and_recv(cmd)
514
515    @property
516    def ul_channel(self):
517        """Gets the uplink channel of cell."""
518        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL?'.format(self._bts)
519        return self._cmw.send_and_recv(cmd)
520
521    @ul_channel.setter
522    def ul_channel(self, channel):
523        """Sets the up link channel number of cell.
524
525        Args:
526            channel: up link channel number of cell.
527        """
528        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL {}'.format(
529                self._bts, channel)
530        self._cmw.send_and_recv(cmd)
531
532    @property
533    def bandwidth(self):
534        """Get the channel bandwidth of the cell."""
535        cmd = 'CONFigure:LTE:SIGN:CELL:BANDwidth:{}:DL?'.format(self._bts)
536        return self._cmw.send_and_recv(cmd)
537
538    @bandwidth.setter
539    def bandwidth(self, bandwidth):
540        """Sets the channel bandwidth of the cell.
541
542        Args:
543            bandwidth: channel bandwidth of cell.
544        """
545        if not isinstance(bandwidth, LteBandwidth):
546            raise ValueError('bandwidth should be an instance of '
547                             'LteBandwidth.')
548        cmd = 'CONFigure:LTE:SIGN:CELL:BANDwidth:{}:DL {}'.format(
549                self._bts, bandwidth.value)
550        self._cmw.send_and_recv(cmd)
551
552    @property
553    def ul_frequency(self):
554        """Get the uplink frequency of the cell."""
555        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL? MHZ'.format(
556                self._bts)
557        return self._cmw.send_and_recv(cmd)
558
559    @ul_frequency.setter
560    def ul_frequency(self, freq):
561        """Get the uplink frequency of the cell.
562
563        Args:
564            freq: uplink frequency of the cell.
565        """
566        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL {} MHZ'.format(
567                self._bts, freq)
568        self._cmw.send_and_recv(cmd)
569
570    @property
571    def dl_frequency(self):
572        """Get the downlink frequency of the cell"""
573        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL? MHZ'.format(
574                self._bts)
575        return self._cmw.send_and_recv(cmd)
576
577    @dl_frequency.setter
578    def dl_frequency(self, freq):
579        """Get the downlink frequency of the cell.
580
581        Args:
582            freq: downlink frequency of the cell.
583        """
584        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL {} MHZ'.format(
585                self._bts, freq)
586        self._cmw.send_and_recv(cmd)
587
588    @property
589    def transmode(self):
590        """Gets the TM of cell."""
591        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:TRANsmission?'.format(
592                self._bts)
593        return self._cmw.send_and_recv(cmd)
594
595    @transmode.setter
596    def transmode(self, tm_mode):
597        """Sets the TM of cell.
598
599        Args:
600            tm_mode: TM of cell.
601        """
602        if not isinstance(tm_mode, TransmissionModes):
603            raise ValueError('tm_mode should be an instance of '
604                             'Transmission modes.')
605
606        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:TRANsmission {}'.format(
607                self._bts, tm_mode.value)
608        self._cmw.send_and_recv(cmd)
609
610    @property
611    def downlink_power_level(self):
612        """Gets RSPRE level."""
613        cmd = 'CONFigure:LTE:SIGN:DL:{}:RSEPre:LEVel?'.format(self._bts)
614        return self._cmw.send_and_recv(cmd)
615
616    @downlink_power_level.setter
617    def downlink_power_level(self, pwlevel):
618        """Modifies RSPRE level.
619
620        Args:
621            pwlevel: power level in dBm.
622        """
623        cmd = 'CONFigure:LTE:SIGN:DL:{}:RSEPre:LEVel {}'.format(
624                self._bts, pwlevel)
625        self._cmw.send_and_recv(cmd)
626
627    @property
628    def uplink_power_control(self):
629        """Gets open loop nominal power directly."""
630        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:OLNPower?'.format(self._bts)
631        return self._cmw.send_and_recv(cmd)
632
633    @uplink_power_control.setter
634    def uplink_power_control(self, ul_power):
635        """Sets open loop nominal power directly.
636
637        Args:
638            ul_power: uplink power level.
639        """
640        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:OLNPower {}'.format(
641                self._bts, ul_power)
642        self._cmw.send_and_recv(cmd)
643
644    @property
645    def uldl_configuration(self):
646        """Gets uldl configuration of the cell."""
647        cmd = 'CONFigure:LTE:SIGN:CELL:{}:ULDL?'.format(self._bts)
648        return self._cmw.send_and_recv(cmd)
649
650    @uldl_configuration.setter
651    def uldl_configuration(self, uldl):
652        """Sets the ul-dl configuration.
653
654        Args:
655            uldl: Configuration value ranging from 0 to 6.
656        """
657        if uldl not in range(0, 7):
658            raise ValueError('uldl configuration value should be between'
659                             ' 0 and 6 inclusive.')
660
661        cmd = 'CONFigure:LTE:SIGN:CELL:{}:ULDL {}'.format(self._bts, uldl)
662        self._cmw.send_and_recv(cmd)
663
664    @property
665    def tdd_special_subframe(self):
666        """Gets special subframe of the cell."""
667        cmd = 'CONFigure:LTE:SIGN:CELL:{}:SSUBframe?'.format(self._bts)
668        return self._cmw.send_and_recv(cmd)
669
670    @tdd_special_subframe.setter
671    def tdd_special_subframe(self, sframe):
672        """Sets the tdd special subframe of the cell.
673
674        Args:
675            sframe: Integer value ranging from 1 to 9.
676        """
677        if sframe not in range(0, 10):
678            raise ValueError('tdd special subframe should be between 0 and 9'
679                             ' inclusive.')
680
681        cmd = 'CONFigure:LTE:SIGN:CELL:{}:SSUBframe {}'.format(
682                self._bts, sframe)
683        self._cmw.send_and_recv(cmd)
684
685    @property
686    def scheduling_mode(self):
687        """Gets the current scheduling mode."""
688        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:STYPe?'.format(self._bts)
689        return self._cmw.send_and_recv(cmd)
690
691    @scheduling_mode.setter
692    def scheduling_mode(self, mode):
693        """Sets the scheduling type for the cell.
694
695        Args:
696            mode: Selects the channel mode to be scheduled.
697        """
698        if not isinstance(mode, SchedulingMode):
699            raise ValueError('mode should be the instance of scheduling mode.')
700
701        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:STYPe {}'.format(
702                self._bts, mode.value)
703        self._cmw.send_and_recv(cmd)
704
705    @property
706    def rb_configuration_dl(self):
707        """Gets rmc's rb configuration for down link. This function returns
708        Number of Resource blocks, Resource block position and Modulation type.
709        """
710        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:{}:DL?'.format(
711                self._bts, self.scheduling_mode)
712        return self._cmw.send_and_recv(cmd)
713
714    @rb_configuration_dl.setter
715    def rb_configuration_dl(self, rb_config):
716        """Sets the rb configuration for down link for scheduling type.
717
718        Args:
719            rb_config: Tuple containing Number of resource blocks, resource
720            block position and modulation type.
721
722        Raises:
723            ValueError: If tuple unpacking fails.
724        """
725        if self.scheduling_mode == 'RMC':
726            rb, rb_pos, modulation = rb_config
727
728            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:RMC:DL {},{},'
729                   '{}'.format(self._bts, rb, rb_pos, modulation))
730            self._cmw.send_and_recv(cmd)
731
732        elif self.scheduling_mode == 'UDCH':
733            rb, start_rb, modulation, tbs = rb_config
734
735            self.validate_rb(rb)
736
737            if not isinstance(modulation, ModulationType):
738                raise ValueError('Modulation should be of type '
739                                 'ModulationType.')
740
741            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:UDCHannels:DL {},{},'
742                   '{},{}'.format(self._bts, rb, start_rb, modulation.value,
743                                  tbs))
744            self._cmw.send_and_recv(cmd)
745
746    @property
747    def rb_configuration_ul(self):
748        """Gets rb configuration for up link. This function returns
749        Number of Resource blocks, Resource block position and Modulation type.
750        """
751        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:{}:UL?'.format(
752                self._bts, self.scheduling_mode)
753        return self._cmw.send_and_recv(cmd)
754
755    @rb_configuration_ul.setter
756    def rb_configuration_ul(self, rb_config):
757        """Sets the rb configuration for down link for scheduling mode.
758
759        Args:
760            rb_config: Tuple containing Number of resource blocks, resource
761            block position and modulation type.
762
763        Raises:
764            ValueError: If tuple unpacking fails.
765        """
766        if self.scheduling_mode == 'RMC':
767            rb, rb_pos, modulation = rb_config
768
769            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:RMC:UL {},{},'
770                   '{}'.format(self._bts, rb, rb_pos, modulation))
771            self._cmw.send_and_recv(cmd)
772
773        elif self.scheduling_mode == 'UDCH':
774            rb, start_rb, modulation, tbs = rb_config
775
776            self.validate_rb(rb)
777
778            if not isinstance(modulation, ModulationType):
779                raise ValueError('Modulation should be of type '
780                                 'ModulationType.')
781            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:UDCHannels:UL {},{},'
782                   '{},{}'.format(self._bts, rb, start_rb, modulation.value,
783                                  tbs))
784            self._cmw.send_and_recv(cmd)
785
786    def validate_rb(self, rb):
787        """Validates if rb is within the limits for bandwidth set.
788
789        Args:
790            rb: No. of resource blocks.
791
792        Raises:
793            ValueError if rb out of range.
794        """
795        bandwidth = self.bandwidth
796
797        if bandwidth == LteBandwidth.BANDWIDTH_1MHz.value:
798            if not 0 <= rb <= 6:
799                raise ValueError('RB should be between 0 to 6 inclusive'
800                                 ' for 1.4Mhz.')
801        elif bandwidth == LteBandwidth.BANDWIDTH_3MHz.value:
802            if not 0 <= rb <= 10:
803                raise ValueError('RB should be between 0 to 10 inclusive'
804                                 ' for 3 Mhz.')
805        elif bandwidth == LteBandwidth.BANDWIDTH_5MHz.value:
806            if not 0 <= rb <= 25:
807                raise ValueError('RB should be between 0 to 25 inclusive'
808                                 ' for 5 Mhz.')
809        elif bandwidth == LteBandwidth.BANDWIDTH_10MHz.value:
810            if not 0 <= rb <= 50:
811                raise ValueError('RB should be between 0 to 50 inclusive'
812                                 ' for 10 Mhz.')
813        elif bandwidth == LteBandwidth.BANDWIDTH_15MHz.value:
814            if not 0 <= rb <= 75:
815                raise ValueError('RB should be between 0 to 75 inclusive'
816                                 ' for 15 Mhz.')
817        elif bandwidth == LteBandwidth.BANDWIDTH_20MHz.value:
818            if not 0 <= rb <= 100:
819                raise ValueError('RB should be between 0 to 100 inclusive'
820                                 ' for 20 Mhz.')
821
822    @property
823    def rb_position_dl(self):
824        """Gets the position of the allocated down link resource blocks within
825        the channel band-width.
826        """
827        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:DL?'.format(
828                self._bts)
829        return self._cmw.send_and_recv(cmd)
830
831    @rb_position_dl.setter
832    def rb_position_dl(self, rbpos):
833        """Selects the position of the allocated down link resource blocks
834        within the channel band-width
835
836        Args:
837            rbpos: position of resource blocks.
838        """
839        if not isinstance(rbpos, RbPosition):
840            raise ValueError('rbpos should be the instance of RbPosition.')
841
842        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:DL {}'.format(
843                self._bts, rbpos.value)
844        self._cmw.send_and_recv(cmd)
845
846    @property
847    def rb_position_ul(self):
848        """Gets the position of the allocated up link resource blocks within
849        the channel band-width.
850        """
851        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:UL?'.format(
852                self._bts)
853        return self._cmw.send_and_recv(cmd)
854
855    @rb_position_ul.setter
856    def rb_position_ul(self, rbpos):
857        """Selects the position of the allocated up link resource blocks
858        within the channel band-width.
859
860        Args:
861            rbpos: position of resource blocks.
862        """
863        if not isinstance(rbpos, RbPosition):
864            raise ValueError('rbpos should be the instance of RbPosition.')
865
866        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:UL {}'.format(
867                self._bts, rbpos.value)
868        self._cmw.send_and_recv(cmd)
869
870    @property
871    def dci_format(self):
872        """Gets the downlink control information (DCI) format."""
873        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:DCIFormat?'.format(self._bts)
874        return self._cmw.send_and_recv(cmd)
875
876    @dci_format.setter
877    def dci_format(self, dci_format):
878        """Selects the downlink control information (DCI) format.
879
880        Args:
881            dci_format: supported dci.
882        """
883        if not isinstance(dci_format, DciFormat):
884            raise ValueError('dci_format should be the instance of DciFormat.')
885
886        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:DCIFormat {}'.format(
887                self._bts, dci_format)
888        self._cmw.send_and_recv(cmd)
889
890    @property
891    def dl_antenna(self):
892        """Gets dl antenna count of cell."""
893        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas?'.format(
894                self._bts)
895        return self._cmw.send_and_recv(cmd)
896
897    @dl_antenna.setter
898    def dl_antenna(self, num_antenna):
899        """Sets the dl antenna count of cell.
900
901        Args:
902            num_antenna: Count of number of dl antennas to use.
903        """
904        if not isinstance(num_antenna, MimoModes):
905            raise ValueError('num_antenna should be an instance of MimoModes.')
906        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas {}'.format(
907                self._bts, num_antenna)
908        self._cmw.send_and_recv(cmd)
909
910    @property
911    def reduced_pdcch(self):
912        """Gets the reduction of PDCCH resources state."""
913        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:PDCCh:RPDCch?'.format(
914                self._bts)
915        return self._cmw.send_and_recv(cmd)
916
917    @reduced_pdcch.setter
918    def reduced_pdcch(self, state):
919        """Sets the reduction of PDCCH resources state.
920
921        Args:
922            state: ON/OFF.
923        """
924        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:PDCCh:RPDCch {}'.format(
925                self._bts, state.value)
926        self._cmw.send_and_recv(cmd)
927
928    def tpc_power_control(self, set_type):
929        """Set and execute the Up Link Power Control via TPC.
930
931        Args:
932            set_type: Type of tpc power control.
933        """
934
935        if not isinstance(set_type, TpcPowerControl):
936            raise ValueError('set_type should be the instance of '
937                             'TpCPowerControl.')
938        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:SET {}'.format(
939                self._bts, set_type.value)
940        self._cmw.send_and_recv(cmd)
941        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:PEXecute'.format(self._bts)
942        self._cmw.send_and_recv(cmd)
943
944    @property
945    def tpc_closed_loop_target_power(self):
946        """Gets the target powers for power control with the TPC setup."""
947        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:CLTPower?'.format(self._bts)
948        return self._cmw.send_and_recv(cmd)
949
950    @tpc_closed_loop_target_power.setter
951    def tpc_closed_loop_target_power(self, cltpower):
952        """Sets the target powers for power control with the TPC setup.
953
954        Args:
955            tpower: Target power.
956        """
957        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:CLTPower {}'.format(
958                self._bts, cltpower)
959        self._cmw.send_and_recv(cmd)
960
961    @property
962    def drx_connected_mode(self):
963        """ Gets the Connected DRX LTE cell parameter
964
965        Args:
966            None
967
968        Returns:
969            DRX connected mode (OFF, AUTO, MANUAL)
970        """
971        raise NotImplementedError()
972
973    @drx_connected_mode.setter
974    def drx_connected_mode(self, mode):
975        """  Sets the Connected DRX LTE cell parameter
976
977        Args:
978            mode: DRX Connected mode
979
980        Returns:
981            None
982        """
983        raise NotImplementedError()
984
985    @property
986    def drx_on_duration_timer(self):
987        """ Gets the amount of PDCCH subframes to wait for data after
988            waking up from a DRX cycle
989
990        Args:
991            None
992
993        Returns:
994            DRX mode duration timer
995        """
996        raise NotImplementedError()
997
998    @drx_on_duration_timer.setter
999    def drx_on_duration_timer(self, time):
1000        """ Sets the amount of PDCCH subframes to wait for data after
1001            waking up from a DRX cycle
1002
1003        Args:
1004            timer: Length of interval to wait for user data to be transmitted
1005
1006        Returns:
1007            None
1008        """
1009        raise NotImplementedError()
1010
1011    @property
1012    def drx_inactivity_timer(self):
1013        """ Gets the number of PDCCH subframes to wait before entering DRX mode
1014
1015        Args:
1016            None
1017
1018        Returns:
1019            DRX mode inactivity timer
1020        """
1021        raise NotImplementedError()
1022
1023    @drx_inactivity_timer.setter
1024    def drx_inactivity_timer(self, time):
1025        """ Sets the number of PDCCH subframes to wait before entering DRX mode
1026
1027        Args:
1028            timer: Length of the interval to wait
1029
1030        Returns:
1031            None
1032        """
1033        raise NotImplementedError()
1034
1035    @property
1036    def drx_retransmission_timer(self):
1037        """ Gets the number of consecutive PDCCH subframes to wait
1038        for retransmission
1039
1040        Args:
1041            None
1042
1043        Returns:
1044            Number of PDCCH subframes to wait for retransmission
1045        """
1046        raise NotImplementedError()
1047
1048    @drx_retransmission_timer.setter
1049    def drx_retransmission_timer(self, time):
1050        """ Sets the number of consecutive PDCCH subframes to wait
1051        for retransmission
1052
1053        Args:
1054            time: Number of PDCCH subframes to wait
1055            for retransmission
1056
1057        Returns:
1058            None
1059        """
1060        raise NotImplementedError()
1061
1062    @property
1063    def drx_long_cycle(self):
1064        """ Gets the amount of subframes representing a DRX long cycle
1065
1066        Args:
1067            None
1068
1069        Returns:
1070            The amount of subframes representing one long DRX cycle.
1071            One cycle consists of DRX sleep + DRX on duration
1072        """
1073        raise NotImplementedError()
1074
1075    @drx_long_cycle.setter
1076    def drx_long_cycle(self, time):
1077        """ Sets the amount of subframes representing a DRX long cycle
1078
1079        Args:
1080            long_cycle: The amount of subframes representing one long DRX cycle.
1081                One cycle consists of DRX sleep + DRX on duration
1082
1083        Returns:
1084            None
1085        """
1086        raise NotImplementedError()
1087
1088    @property
1089    def drx_long_cycle_offset(self):
1090        """ Gets the offset used to determine long cycle starting
1091        subframe
1092
1093        Args:
1094            None
1095
1096        Returns:
1097            Long cycle offset
1098        """
1099        raise NotImplementedError()
1100
1101    @drx_long_cycle_offset.setter
1102    def drx_long_cycle_offset(self, offset):
1103        """ Sets the offset used to determine long cycle starting
1104        subframe
1105
1106        Args:
1107            offset: Number in range 0...(long cycle - 1)
1108        """
1109        raise NotImplementedError()
1110
1111
1112class LteMeasurement(object):
1113    """ Class for measuring LTE performance """
1114
1115    def __init__(self, cmw):
1116        self._cmw = cmw
1117
1118    def intitilize_measurement(self):
1119        """Initialize measurement modules."""
1120        self._cmw.send_and_recv('INIT:LTE:MEAS:MEValuation')
1121
1122    @property
1123    def measurement_repetition(self):
1124        """Returns the measurement repetition mode that has been set."""
1125        return self._cmw.send_and_recv(
1126                'CONFigure:LTE:MEAS:MEValuation:REPetition?')
1127
1128    @measurement_repetition.setter
1129    def measurement_repetition(self, mode):
1130        """Sets the mode for measuring power levels.
1131
1132        Args:
1133            mode: Single shot/continuous.
1134        """
1135        if not isinstance(mode, RepetitionMode):
1136            raise ValueError('mode should be the instance of Repetition Mode')
1137
1138        cmd = 'CONFigure:LTE:MEAS:MEValuation:REPetition {}'.format(mode.value)
1139        self._cmw.send_and_recv(cmd)
1140
1141    @property
1142    def query_measurement_state(self):
1143        """Returns the states and sub states of measurement."""
1144        return self._cmw.send_and_recv('FETCh:LTE:MEAS:MEValuation:STATe:ALL?')
1145
1146    @property
1147    def measure_tx_power(self):
1148        """Return the current Tx power measurement."""
1149        return self._cmw.send_and_recv(
1150                'FETCh:LTE:MEAS:MEValuation:PMONitor:AVERage?')
1151
1152    def stop_measurement(self):
1153        """Stops the on-going measurement.
1154        This function call does not free up resources allocated for
1155        measurement. Instead it moves from RUN to RDY state.
1156        """
1157        self._cmw.send_and_recv('STOP:LTE:MEAS:MEValuation')
1158
1159    def abort_measurement(self):
1160        """Aborts the measurement abruptly.
1161        This function call will free up the resources allocated for
1162        measurement and all the results will be wiped off.
1163        """
1164        self._cmw.send_and_recv('ABORt:LTE:MEAS:MEValuation')
1165
1166
1167class CmwError(Exception):
1168    """Class to raise exceptions related to cmw."""
1169