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 time
18
19from enum import Enum
20
21from acts.controllers import abstract_inst
22
23LTE_ATTACH_RESP = 'ATT'
24LTE_CONN_RESP = 'CONN'
25LTE_IDLE_RESP = 'IDLE'
26LTE_PSWITCHED_ON_RESP = 'ON'
27LTE_PSWITCHED_OFF_RESP = 'OFF'
28
29STATE_CHANGE_TIMEOUT = 20
30
31
32class LteState(Enum):
33    """LTE ON and OFF"""
34    LTE_ON = 'ON'
35    LTE_OFF = 'OFF'
36
37
38class BtsNumber(Enum):
39    """Base station Identifiers."""
40    BTS1 = 'PCC'
41    BTS2 = 'SCC1'
42    BTS3 = 'SCC2'
43    BTS4 = 'SCC3'
44    BTS5 = 'SCC4'
45    BTS6 = 'SCC6'
46    BTS7 = 'SCC7'
47
48
49class LteBandwidth(Enum):
50    """Supported LTE bandwidths."""
51    BANDWIDTH_1MHz = 'B014'
52    BANDWIDTH_3MHz = 'B030'
53    BANDWIDTH_5MHz = 'B050'
54    BANDWIDTH_10MHz = 'B100'
55    BANDWIDTH_15MHz = 'B150'
56    BANDWIDTH_20MHz = 'B200'
57
58
59class DuplexMode(Enum):
60    """Duplex Modes"""
61    FDD = 'FDD'
62    TDD = 'TDD'
63
64
65class SchedulingMode(Enum):
66    """Supported scheduling modes."""
67    RMC = 'RMC'
68    USERDEFINEDCH = 'UDCHannels'
69
70
71class TransmissionModes(Enum):
72    """Supported transmission modes."""
73    TM1 = 'TM1'
74    TM2 = 'TM2'
75    TM3 = 'TM3'
76    TM4 = 'TM4'
77    TM7 = 'TM7'
78    TM8 = 'TM8'
79    TM9 = 'TM9'
80
81
82class UseCarrierSpecific(Enum):
83    """Enable or disable carrier specific."""
84    UCS_ON = 'ON'
85    UCS_OFF = 'OFF'
86
87
88class RbPosition(Enum):
89    """Supported RB postions."""
90    LOW = 'LOW'
91    HIGH = 'HIGH'
92    P5 = 'P5'
93    P10 = 'P10'
94    P23 = 'P23'
95    P35 = 'P35'
96    P48 = 'P48'
97
98
99class ModulationType(Enum):
100    """Supported Modulation Types."""
101    QPSK = 'QPSK'
102    Q16 = 'Q16'
103    Q64 = 'Q64'
104    Q256 = 'Q256'
105
106
107class DciFormat(Enum):
108    """Support DCI Formats for MIMOs"""
109    D1 = 'D1'
110    D1A = 'D1A'
111    D1B = 'D1B'
112    D2 = 'D2'
113    D2A = 'D2A'
114    D2B = 'D2B'
115    D2C = 'D2C'
116
117
118class MimoModes(Enum):
119    """MIMO Modes dl antennas"""
120    MIMO1x1 = 'ONE'
121    MIMO2x2 = 'TWO'
122    MIMO4x4 = 'FOUR'
123
124
125class MimoScenario(Enum):
126    """Supportted mimo scenarios"""
127    SCEN1x1 = 'SCELl:FLEXible SUA1,RF1C,RX1,RF1C,TX1'
128    SCEN2x2 = 'TRO:FLEXible SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2'
129    SCEN4x4 = 'FRO FLEXible SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2,RF2C,TX3,RF4C,TX4'
130
131
132class RrcState(Enum):
133    """States to enable/disable rrc."""
134    RRC_ON = 'ON'
135    RRC_OFF = 'OFF'
136
137
138class MacPadding(Enum):
139    """Enables/Disables Mac Padding."""
140    ON = 'ON'
141    OFF = 'OFF'
142
143
144class ConnectionType(Enum):
145    """Supported Connection Types."""
146    TEST = 'TESTmode'
147    DAU = 'DAPPlication'
148
149
150class RepetitionMode(Enum):
151    """Specifies LTE Measurement Repetition Mode."""
152    SINGLESHOT = 'SINGleshot'
153    CONTINUOUS = 'CONTinuous'
154
155
156class TpcPowerControl(Enum):
157    """Specifies Up Link power control types."""
158    MIN_POWER = 'MINPower'
159    MAX_POWER = 'MAXPower'
160    CONSTANT = 'CONStant'
161    SINGLE = 'SINGle'
162    UDSINGLE = 'UDSingle'
163    UDCONTINUOUS = 'UDContinuous'
164    ALTERNATE = 'ALT0'
165    CLOSED_LOOP = 'CLOop'
166    RP_CONTROL = 'RPControl'
167    FLEX_POWER = 'FULPower'
168
169
170class ReducedPdcch(Enum):
171    """Enables/disables the reduction of PDCCH resources."""
172    ON = 'ON'
173    OFF = 'OFF'
174
175
176class Cmw500(abstract_inst.SocketInstrument):
177
178    def __init__(self, ip_addr, port):
179        """Init method to setup variables for controllers.
180
181        Args:
182              ip_addr: Controller's ip address.
183              port: Port
184        """
185        super(Cmw500, self).__init__(ip_addr, port)
186        self._connect_socket()
187        self._send('*CLS')
188        self._send('*ESE 0;*SRE 0')
189        self._send('*CLS')
190        self._send('*ESE 1;*SRE 4')
191        self._send('SYST:DISP:UPD ON')
192
193    def switch_lte_signalling(self, state):
194        """ Turns LTE signalling ON/OFF.
195
196        Args:
197              state: an instance of LteState indicating the state to which LTE
198                signal has to be set.
199        """
200        if not isinstance(state, LteState):
201            raise ValueError('state should be the instance of LteState.')
202
203        state = state.value
204
205        cmd = 'SOURce:LTE:SIGN:CELL:STATe {}'.format(state)
206        self.send_and_recv(cmd)
207
208        time_elapsed = 0
209        while time_elapsed < STATE_CHANGE_TIMEOUT:
210            response = self.send_and_recv('SOURce:LTE:SIGN:CELL:STATe:ALL?')
211
212            if response == state + ',ADJ':
213                self._logger.info('LTE signalling is now {}.'.format(state))
214                break
215
216            # Wait for a second and increase time count by one
217            time.sleep(1)
218            time_elapsed += 1
219        else:
220            raise CmwError('Failed to turn {} LTE signalling.'.format(state))
221
222    def enable_packet_switching(self):
223        """Enable packet switching in call box."""
224        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion CONNect')
225        self.wait_for_pswitched_state()
226
227    def disable_packet_switching(self):
228        """Disable packet switching in call box."""
229        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion DISConnect')
230        self.wait_for_pswitched_state()
231
232    @property
233    def use_carrier_specific(self):
234        """Gets current status of carrier specific duplex configuration."""
235        return self.send_and_recv('CONFigure:LTE:SIGN:DMODe:UCSPECific?')
236
237    @use_carrier_specific.setter
238    def use_carrier_specific(self, state):
239        """Sets the carrier specific duplex configuration.
240
241        Args:
242            state: ON/OFF UCS configuration.
243        """
244        cmd = 'CONFigure:LTE:SIGN:DMODe:UCSPECific {}'.format(state)
245        self.send_and_recv(cmd)
246
247    def send_and_recv(self, cmd):
248        """Send and recv the status of the command.
249
250        Args:
251            cmd: Command to send.
252
253        Returns:
254            status: returns the status of the command sent.
255        """
256
257        self._send(cmd)
258        if '?' in cmd:
259            status = self._recv()
260            return status
261
262    def configure_mimo_settings(self, mimo):
263        """Sets the mimo scenario for the test.
264
265        Args:
266            mimo: mimo scenario to set.
267        """
268        cmd = 'ROUTe:LTE:SIGN:SCENario:{}'.format(mimo.value)
269        self.send_and_recv(cmd)
270
271    def wait_for_pswitched_state(self, timeout=10):
272        """Wait until pswitched state.
273
274        Args:
275            timeout: timeout for lte pswitched state.
276
277        Raises:
278            CmwError on timeout.
279        """
280        while timeout > 0:
281            state = self.send_and_recv('FETCh:LTE:SIGN:PSWitched:STATe?')
282            if state == LTE_PSWITCHED_ON_RESP:
283                self._logger.debug('Connection to setup initiated.')
284                break
285            elif state == LTE_PSWITCHED_OFF_RESP:
286                self._logger.debug('Connection to setup detached.')
287                break
288
289            # Wait for a second and decrease count by one
290            time.sleep(1)
291            timeout -= 1
292        else:
293            raise CmwError('Failure in setting up/detaching connection')
294
295    def wait_for_attached_state(self, timeout=120):
296        """Attach the controller with device.
297
298        Args:
299            timeout: timeout for phone to get attached.
300
301        Raises:
302            CmwError on time out.
303        """
304        while timeout > 0:
305            state = self.send_and_recv('FETCh:LTE:SIGN:PSWitched:STATe?')
306
307            if state == LTE_ATTACH_RESP:
308                self._logger.debug('Call box attached with device')
309                break
310
311            # Wait for a second and decrease count by one
312            time.sleep(1)
313            timeout -= 1
314        else:
315            raise CmwError('Device could not be attached')
316
317    def wait_for_rrc_state(self, state, timeout=120):
318        """ Waits until a certain RRC state is set.
319
320        Args:
321            state: the RRC state that is being waited for.
322            timeout: timeout for phone to be in connnected state.
323
324        Raises:
325            CmwError on time out.
326        """
327        if state not in [LTE_CONN_RESP, LTE_IDLE_RESP]:
328            raise ValueError(
329                'The allowed values for state are {} and {}.'.format(
330                    LTE_CONN_RESP, LTE_IDLE_RESP))
331
332        while timeout > 0:
333            new_state = self.send_and_recv('SENSe:LTE:SIGN:RRCState?')
334
335            if new_state == state:
336                self._logger.debug('The RRC state is {}.'.format(new_state))
337                break
338
339            # Wait for a second and decrease count by one
340            time.sleep(1)
341            timeout -= 1
342        else:
343            raise CmwError('Timeout before RRC state was {}.'.format(state))
344
345    def reset(self):
346        """System level reset"""
347        self.send_and_recv('*RST; *OPC')
348
349    @property
350    def get_instrument_id(self):
351        """Gets instrument identification number"""
352        return self.send_and_recv('*IDN?')
353
354    def disconnect(self):
355        """Disconnect controller from device and switch to local mode."""
356        self.switch_lte_signalling(LteState.LTE_OFF)
357        self.close_remote_mode()
358        self._close_socket()
359
360    def close_remote_mode(self):
361        """Exits remote mode to local mode."""
362        self.send_and_recv('&GTL')
363
364    def detach(self):
365        """Detach callbox and controller."""
366        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion DETach')
367
368    @property
369    def rrc_connection(self):
370        """Gets the RRC connection state."""
371        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:KRRC?')
372
373    @rrc_connection.setter
374    def rrc_connection(self, state):
375        """Selects whether the RRC connection is kept or released after attach.
376
377        Args:
378            mode: RRC State ON/OFF.
379        """
380        if not isinstance(state, RrcState):
381            raise ValueError('state should be the instance of RrcState.')
382
383        cmd = 'CONFigure:LTE:SIGN:CONNection:KRRC {}'.format(state.value)
384        self.send_and_recv(cmd)
385
386    @property
387    def rrc_connection_timer(self):
388        """Gets the inactivity timeout for disabled rrc connection."""
389        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:RITimer?')
390
391    @rrc_connection_timer.setter
392    def rrc_connection_timer(self, time_in_secs):
393        """Sets the inactivity timeout for disabled rrc connection. By default
394        the timeout is set to 5.
395
396        Args:
397            time_in_secs: timeout of inactivity in rrc connection.
398        """
399        cmd = 'CONFigure:LTE:SIGN:CONNection:RITimer {}'.format(time_in_secs)
400        self.send_and_recv(cmd)
401
402    @property
403    def dl_mac_padding(self):
404        """Gets the state of mac padding."""
405        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:DLPadding?')
406
407    @dl_mac_padding.setter
408    def dl_mac_padding(self, state):
409        """Enables/Disables downlink padding at the mac layer.
410
411        Args:
412            state: ON/OFF
413        """
414        cmd = 'CONFigure:LTE:SIGN:CONNection:DLPadding {}'.format(state.value)
415        self.send_and_recv(cmd)
416
417    @property
418    def connection_type(self):
419        """Gets the connection type applied in callbox."""
420        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:CTYPe?')
421
422    @connection_type.setter
423    def connection_type(self, ctype):
424        """Sets the connection type to be applied.
425
426        Args:
427            ctype: Connection type.
428        """
429        cmd = 'CONFigure:LTE:SIGN:CONNection:CTYPe {}'.format(ctype.value)
430        self.send_and_recv(cmd)
431
432    def get_base_station(self, bts_num=BtsNumber.BTS1):
433        """Gets the base station object based on bts num. By default
434        bts_num set to PCC
435
436        Args:
437            bts_num: base station identifier
438
439        Returns:
440            base station object.
441        """
442        return BaseStation(self, bts_num)
443
444    def init_lte_measurement(self):
445        """Gets the class object for lte measurement which can be used to
446        initiate measurements.
447
448        Returns:
449            lte measurement object.
450        """
451        return LteMeasurement(self)
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(self._bts)
894        return self._cmw.send_and_recv(cmd)
895
896    @dl_antenna.setter
897    def dl_antenna(self, num_antenna):
898        """Sets the dl antenna count of cell.
899
900        Args:
901            num_antenna: Count of number of dl antennas to use.
902        """
903        if not isinstance(num_antenna, MimoModes):
904            raise ValueError('num_antenna should be an instance of MimoModes.')
905        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas {}'.format(
906            self._bts, num_antenna)
907        self._cmw.send_and_recv(cmd)
908
909    @property
910    def reduced_pdcch(self):
911        """Gets the reduction of PDCCH resources state."""
912        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:PDCCh:RPDCch?'.format(
913            self._bts)
914        return self._cmw.send_and_recv(cmd)
915
916    @reduced_pdcch.setter
917    def reduced_pdcch(self, state):
918        """Sets the reduction of PDCCH resources state.
919
920        Args:
921            state: ON/OFF.
922        """
923        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:PDCCh:RPDCch {}'.format(
924            self._bts, state.value)
925        self._cmw.send_and_recv(cmd)
926
927    def tpc_power_control(self, set_type):
928        """Set and execute the Up Link Power Control via TPC.
929
930        Args:
931            set_type: Type of tpc power control.
932        """
933
934        if not isinstance(set_type, TpcPowerControl):
935            raise ValueError('set_type should be the instance of '
936                             'TpCPowerControl.')
937        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:SET {}'.format(
938            self._bts, set_type.value)
939        self._cmw.send_and_recv(cmd)
940        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:PEXecute'.format(self._bts)
941        self._cmw.send_and_recv(cmd)
942
943    @property
944    def tpc_closed_loop_target_power(self):
945        """Gets the target powers for power control with the TPC setup."""
946        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:CLTPower?'.format(self._bts)
947        return self._cmw.send_and_recv(cmd)
948
949    @tpc_closed_loop_target_power.setter
950    def tpc_closed_loop_target_power(self, cltpower):
951        """Sets the target powers for power control with the TPC setup.
952
953        Args:
954            tpower: Target power.
955        """
956        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:CLTPower {}'.format(
957            self._bts, cltpower)
958        self._cmw.send_and_recv(cmd)
959
960    @property
961    def drx_connected_mode(self):
962        """ Gets the Connected DRX LTE cell parameter
963
964        Args:
965            None
966
967        Returns:
968            DRX connected mode (OFF, AUTO, MANUAL)
969        """
970        raise NotImplementedError()
971
972    @drx_connected_mode.setter
973    def drx_connected_mode(self, mode):
974        """  Sets the Connected DRX LTE cell parameter
975
976        Args:
977            mode: DRX Connected mode
978
979        Returns:
980            None
981        """
982        raise NotImplementedError()
983
984    @property
985    def drx_on_duration_timer(self):
986        """ Gets the amount of PDCCH subframes to wait for data after
987            waking up from a DRX cycle
988
989        Args:
990            None
991
992        Returns:
993            DRX mode duration timer
994        """
995        raise NotImplementedError()
996
997    @drx_on_duration_timer.setter
998    def drx_on_duration_timer(self, time):
999        """ Sets the amount of PDCCH subframes to wait for data after
1000            waking up from a DRX cycle
1001
1002        Args:
1003            timer: Length of interval to wait for user data to be transmitted
1004
1005        Returns:
1006            None
1007        """
1008        raise NotImplementedError()
1009
1010    @property
1011    def drx_inactivity_timer(self):
1012        """ Gets the number of PDCCH subframes to wait before entering DRX mode
1013
1014        Args:
1015            None
1016
1017        Returns:
1018            DRX mode inactivity timer
1019        """
1020        raise NotImplementedError()
1021
1022    @drx_inactivity_timer.setter
1023    def drx_inactivity_timer(self, time):
1024        """ Sets the number of PDCCH subframes to wait before entering DRX mode
1025
1026        Args:
1027            timer: Length of the interval to wait
1028
1029        Returns:
1030            None
1031        """
1032        raise NotImplementedError()
1033
1034    @property
1035    def drx_retransmission_timer(self):
1036        """ Gets the number of consecutive PDCCH subframes to wait
1037        for retransmission
1038
1039        Args:
1040            None
1041
1042        Returns:
1043            Number of PDCCH subframes to wait for retransmission
1044        """
1045        raise NotImplementedError()
1046
1047    @drx_retransmission_timer.setter
1048    def drx_retransmission_timer(self, time):
1049        """ Sets the number of consecutive PDCCH subframes to wait
1050        for retransmission
1051
1052        Args:
1053            time: Number of PDCCH subframes to wait
1054            for retransmission
1055
1056        Returns:
1057            None
1058        """
1059        raise NotImplementedError()
1060
1061    @property
1062    def drx_long_cycle(self):
1063        """ Gets the amount of subframes representing a DRX long cycle
1064
1065        Args:
1066            None
1067
1068        Returns:
1069            The amount of subframes representing one long DRX cycle.
1070            One cycle consists of DRX sleep + DRX on duration
1071        """
1072        raise NotImplementedError()
1073
1074    @drx_long_cycle.setter
1075    def drx_long_cycle(self, time):
1076        """ Sets the amount of subframes representing a DRX long cycle
1077
1078        Args:
1079            long_cycle: The amount of subframes representing one long DRX cycle.
1080                One cycle consists of DRX sleep + DRX on duration
1081
1082        Returns:
1083            None
1084        """
1085        raise NotImplementedError()
1086
1087    @property
1088    def drx_long_cycle_offset(self):
1089        """ Gets the offset used to determine long cycle starting
1090        subframe
1091
1092        Args:
1093            None
1094
1095        Returns:
1096            Long cycle offset
1097        """
1098        raise NotImplementedError()
1099
1100    @drx_long_cycle_offset.setter
1101    def drx_long_cycle_offset(self, offset):
1102        """ Sets the offset used to determine long cycle starting
1103        subframe
1104
1105        Args:
1106            offset: Number in range 0...(long cycle - 1)
1107        """
1108        raise NotImplementedError()
1109
1110
1111
1112class LteMeasurement(object):
1113
1114    def __init__(self, cmw):
1115        self._cmw = cmw
1116
1117    def intitilize_measurement(self):
1118        """Initialize measurement modules."""
1119        self._cmw.send_and_recv('INIT:LTE:MEAS:MEValuation')
1120
1121    @property
1122    def measurement_repetition(self):
1123        """Returns the measurement repetition mode that has been set."""
1124        return self._cmw.send_and_recv(
1125            'CONFigure:LTE:MEAS:MEValuation:REPetition?')
1126
1127    @measurement_repetition.setter
1128    def measurement_repetition(self, mode):
1129        """Sets the mode for measuring power levels.
1130
1131        Args:
1132            mode: Single shot/continuous.
1133        """
1134        if not isinstance(mode, RepetitionMode):
1135            raise ValueError('mode should be the instance of Repetition Mode')
1136
1137        cmd = 'CONFigure:LTE:MEAS:MEValuation:REPetition {}'.format(mode.value)
1138        self._cmw.send_and_recv(cmd)
1139
1140    @property
1141    def query_measurement_state(self):
1142        """Returns the states and sub states of measurement."""
1143        return self._cmw.send_and_recv('FETCh:LTE:MEAS:MEValuation:STATe:ALL?')
1144
1145    @property
1146    def measure_tx_power(self):
1147        """Return the current Tx power measurement."""
1148        return self._cmw.send_and_recv(
1149            'FETCh:LTE:MEAS:MEValuation:PMONitor:AVERage?')
1150
1151    def stop_measurement(self):
1152        """Stops the on-going measurement.
1153        This function call does not free up resources allocated for
1154        measurement. Instead it moves from RUN to RDY state.
1155        """
1156        self._cmw.send_and_recv('STOP:LTE:MEAS:MEValuation')
1157
1158    def abort_measurement(self):
1159        """Aborts the measurement abruptly.
1160        This function call will free up the resources allocated for
1161        measurement and all the results will be wiped off.
1162        """
1163        self._cmw.send_and_recv('ABORt:LTE:MEAS:MEValuation')
1164
1165
1166class CmwError(Exception):
1167    """Class to raise exceptions related to cmw."""
1168