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
29WCDMA_ATTACH_RESP = 'ATT'
30WCDMA_CESTABLISHED_RESP = 'CEST'
31WCDMA_PSWITCHED_ON_RESP = 'ON'
32
33STATE_CHANGE_TIMEOUT = 20
34
35
36class IPAddressType(Enum):
37    """ IP Address types"""
38    IPV4 = "IPV4"
39    IPV6 = "IPV6"
40    IPV4V6 = "IPV46"
41
42
43class SignallingState(Enum):
44    """Signalling states common to all RATs."""
45    ON = 'ON'
46    OFF = 'OFF'
47    ReadyForHandover = 'RFH'
48
49
50class SccActivationMode(Enum):
51    """Activation mode to use for SCCs."""
52    AUTO = 'AUTO'
53    MANUAL = 'MAN'
54    SEMI_AUTO = 'SEM'
55
56
57class SccState(Enum):
58    """Secondary component carrier states."""
59    ON = 'ON'
60    OFF = 'OFF'
61    RRC = 'RRC'
62    MAC = 'MAC'
63
64
65class LteState(Enum):
66    """LTE ON and OFF"""
67    LTE_ON = 'ON'
68    LTE_OFF = 'OFF'
69
70
71class BtsNumber(Enum):
72    """Base station Identifiers."""
73    BTS1 = 'PCC'
74    BTS2 = 'SCC1'
75    BTS3 = 'SCC2'
76    BTS4 = 'SCC3'
77    BTS5 = 'SCC4'
78    BTS6 = 'SCC6'
79    BTS7 = 'SCC7'
80
81
82class LteBandwidth(Enum):
83    """Supported LTE bandwidths."""
84    BANDWIDTH_1MHz = 'B014'
85    BANDWIDTH_3MHz = 'B030'
86    BANDWIDTH_5MHz = 'B050'
87    BANDWIDTH_10MHz = 'B100'
88    BANDWIDTH_15MHz = 'B150'
89    BANDWIDTH_20MHz = 'B200'
90
91
92def BandwidthFromFloat(bw):
93    if bw == 20:
94        return LteBandwidth.BANDWIDTH_20MHz
95    elif bw == 15:
96        return LteBandwidth.BANDWIDTH_15MHz
97    elif bw == 10:
98        return LteBandwidth.BANDWIDTH_10MHz
99    elif bw == 5:
100        return LteBandwidth.BANDWIDTH_5MHz
101    elif bw == 3:
102        return LteBandwidth.BANDWIDTH_3MHz
103    elif bw == 1.4:
104        return LteBandwidth.BANDWIDTH_1MHz
105
106    raise ValueError('Bandwidth {} MHz is not valid for LTE'.format(bandwidth))
107
108
109class DrxMode(Enum):
110    """DRX Modes."""
111    DRXS = 'DRXS'
112    DRXL = 'DRXL'
113    USER_DEFINED = 'UDEF'
114    ON = 'ON'
115    OFF = 'OFF'
116
117
118class DuplexMode(Enum):
119    """Duplex Modes"""
120    FDD = 'FDD'
121    TDD = 'TDD'
122
123
124class SchedulingMode(Enum):
125    """Supported scheduling modes."""
126    RMC = 'RMC'
127    USERDEFINEDCH = 'UDCHannels'
128
129
130class TransmissionModes(Enum):
131    """Supported transmission modes."""
132    TM1 = 'TM1'
133    TM2 = 'TM2'
134    TM3 = 'TM3'
135    TM4 = 'TM4'
136    TM7 = 'TM7'
137    TM8 = 'TM8'
138    TM9 = 'TM9'
139
140
141class UseCarrierSpecific(Enum):
142    """Enable or disable carrier specific."""
143    UCS_ON = 'ON'
144    UCS_OFF = 'OFF'
145
146
147class RbPosition(Enum):
148    """Supported RB positions."""
149    LOW = 'LOW'
150    HIGH = 'HIGH'
151    P5 = 'P5'
152    P10 = 'P10'
153    P23 = 'P23'
154    P35 = 'P35'
155    P48 = 'P48'
156
157
158class ModulationType(Enum):
159    """Supported Modulation Types."""
160    QPSK = 'QPSK'
161    Q16 = 'Q16'
162    Q64 = 'Q64'
163    Q256 = 'Q256'
164
165
166class DciFormat(Enum):
167    """Support DCI Formats for MIMOs"""
168    D1 = 'D1'
169    D1A = 'D1A'
170    D1B = 'D1B'
171    D2 = 'D2'
172    D2A = 'D2A'
173    D2B = 'D2B'
174    D2C = 'D2C'
175
176
177class MimoModes(Enum):
178    """MIMO Modes dl antennas"""
179    MIMO1x1 = 'ONE'
180    MIMO2x2 = 'TWO'
181    MIMO4x4 = 'FOUR'
182
183
184class MimoScenario(Enum):
185    """Supported mimo scenarios"""
186    SCEN1x1 = 'SCELl:FLEXible SUA1,RF1C,RX1,RF1C,TX1'
187    SCEN2x2 = 'TRO:FLEXible SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2'
188    SCEN4x4 = 'FRO FLEXible SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2,RF2C,TX3,RF4C,TX4'
189
190
191class RrcState(Enum):
192    """States to enable/disable rrc."""
193    RRC_ON = 'ON'
194    RRC_OFF = 'OFF'
195
196
197class MacPadding(Enum):
198    """Enables/Disables Mac Padding."""
199    ON = 'ON'
200    OFF = 'OFF'
201
202
203class ConnectionType(Enum):
204    """Supported Connection Types."""
205    TEST = 'TESTmode'
206    DAU = 'DAPPlication'
207
208
209class RepetitionMode(Enum):
210    """Specifies LTE Measurement Repetition Mode."""
211    SINGLESHOT = 'SINGleshot'
212    CONTINUOUS = 'CONTinuous'
213
214
215class TpcPowerControl(Enum):
216    """Specifies Up Link power control types."""
217    MIN_POWER = 'MINPower'
218    MAX_POWER = 'MAXPower'
219    CONSTANT = 'CONStant'
220    SINGLE = 'SINGle'
221    UDSINGLE = 'UDSingle'
222    UDCONTINUOUS = 'UDContinuous'
223    ALTERNATE = 'ALT0'
224    CLOSED_LOOP = 'CLOop'
225    RP_CONTROL = 'RPControl'
226    FLEX_POWER = 'FULPower'
227
228
229class ReducedPdcch(Enum):
230    """Enables/disables the reduction of PDCCH resources."""
231    ON = 'ON'
232    OFF = 'OFF'
233
234
235class Cmw500(abstract_inst.SocketInstrument):
236    def __init__(self, ip_addr, port):
237        """Init method to setup variables for controllers.
238
239        Args:
240              ip_addr: Controller's ip address.
241              port: Port
242        """
243        super(Cmw500, self).__init__(ip_addr, port)
244        self._connect_socket()
245        self._send('*CLS')
246        self._send('*ESE 0;*SRE 0')
247        self._send('*CLS')
248        self._send('*ESE 1;*SRE 4')
249        self._send('SYST:DISP:UPD ON')
250
251    def switch_lte_signalling(self, state):
252        """ Turns LTE signalling ON/OFF.
253
254        Args:
255              state: an instance of LteState indicating the state to which LTE
256                signal has to be set.
257        """
258        if not isinstance(state, LteState):
259            raise ValueError('state should be the instance of LteState.')
260
261        state = state.value
262
263        cmd = 'SOURce:LTE:SIGN:CELL:STATe {}'.format(state)
264        self.send_and_recv(cmd)
265
266        time_elapsed = 0
267        while time_elapsed < STATE_CHANGE_TIMEOUT:
268            response = self.send_and_recv('SOURce:LTE:SIGN:CELL:STATe:ALL?')
269
270            if response == state + ',ADJ':
271                self._logger.info('LTE signalling is now {}.'.format(state))
272                break
273
274            # Wait for a second and increase time count by one
275            time.sleep(1)
276            time_elapsed += 1
277        else:
278            raise CmwError('Failed to turn {} LTE signalling.'.format(state))
279
280    def switch_scc_state(self, scc_index, state):
281        """ Changes the SCC to the requested state.
282
283        Args:
284            scc_index: the SCC number to modify.
285            state: an instance of SccState indicating which SCC state to set.
286        """
287        cmd = 'CALL:LTE:SIGN:SCC{}:ACTion {}'.format(scc_index, state.value)
288        self.send_and_recv(cmd)
289        self.wait_for_scc_state(scc_index, [state])
290
291    def wait_for_scc_state(self, scc_index, allowed, timeout=120):
292        """ Polls for a SCC to reach the requested state.
293
294        Args:
295            scc_index: an integer defining the scc number.
296            allowed: a list of SccStates defining the allowed states.
297            timeout: the maximum amount of time to wait for the requested state.
298        """
299        self.wait_for_response(
300            'FETCh:LTE:SIGN:SCC{}:STATe?'.format(scc_index),
301            [a.value for a in allowed],
302            timeout,
303        )
304
305    def wait_for_response(self, cmd, allowed, timeout=120):
306        """Polls a specific query command until an allowed response is returned.
307
308        Args:
309            cmd: the query command string.
310            allowed: a collection of allowed string responses.
311            timeout: the maximum amount of time to wait for an allowed response.
312        """
313        time_elapsed = 0
314        while time_elapsed < timeout:
315            response = self.send_and_recv(cmd)
316
317            if response in allowed:
318                return
319
320            time.sleep(1)
321            time_elapsed += 1
322
323        raise CmwError('Failed to wait for valid response.')
324
325    def enable_packet_switching(self):
326        """Enable packet switching in call box."""
327        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion CONNect')
328        self.wait_for_pswitched_state()
329
330    def disable_packet_switching(self):
331        """Disable packet switching in call box."""
332        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion DISConnect')
333        self.wait_for_pswitched_state()
334
335    @property
336    def use_carrier_specific(self):
337        """Gets current status of carrier specific duplex configuration."""
338        return self.send_and_recv('CONFigure:LTE:SIGN:DMODe:UCSPECific?')
339
340    @use_carrier_specific.setter
341    def use_carrier_specific(self, state):
342        """Sets the carrier specific duplex configuration.
343
344        Args:
345            state: ON/OFF UCS configuration.
346        """
347        cmd = 'CONFigure:LTE:SIGN:DMODe:UCSPECific {}'.format(state)
348        self.send_and_recv(cmd)
349
350    def send_and_recv(self, cmd):
351        """Send and recv the status of the command.
352
353        Args:
354            cmd: Command to send.
355
356        Returns:
357            status: returns the status of the command sent.
358        """
359
360        self._send(cmd)
361        if '?' in cmd:
362            status = self._recv()
363            return status
364
365    def configure_mimo_settings(self, mimo):
366        """Sets the mimo scenario for the test.
367
368        Args:
369            mimo: mimo scenario to set.
370        """
371        cmd = 'ROUTe:LTE:SIGN:SCENario:{}'.format(mimo.value)
372        self.send_and_recv(cmd)
373
374    def wait_for_pswitched_state(self, timeout=10):
375        """Wait until pswitched state.
376
377        Args:
378            timeout: timeout for lte pswitched state.
379
380        Raises:
381            CmwError on timeout.
382        """
383        while timeout > 0:
384            state = self.send_and_recv('FETCh:LTE:SIGN:PSWitched:STATe?')
385            if state == LTE_PSWITCHED_ON_RESP:
386                self._logger.debug('Connection to setup initiated.')
387                break
388            elif state == LTE_PSWITCHED_OFF_RESP:
389                self._logger.debug('Connection to setup detached.')
390                break
391
392            # Wait for a second and decrease count by one
393            time.sleep(1)
394            timeout -= 1
395        else:
396            raise CmwError('Failure in setting up/detaching connection')
397
398    def wait_for_attached_state(self, timeout=120):
399        """Attach the controller with device.
400
401        Args:
402            timeout: timeout for phone to get attached.
403
404        Raises:
405            CmwError on time out.
406        """
407        while timeout > 0:
408            state = self.send_and_recv('FETCh:LTE:SIGN:PSWitched:STATe?')
409
410            if state == LTE_ATTACH_RESP:
411                self._logger.debug('Call box attached with device')
412                break
413
414            # Wait for a second and decrease count by one
415            time.sleep(1)
416            timeout -= 1
417        else:
418            raise CmwError('Device could not be attached')
419
420    def wait_for_rrc_state(self, state, timeout=120):
421        """ Waits until a certain RRC state is set.
422
423        Args:
424            state: the RRC state that is being waited for.
425            timeout: timeout for phone to be in connected state.
426
427        Raises:
428            CmwError on time out.
429        """
430        if state not in [LTE_CONN_RESP, LTE_IDLE_RESP]:
431            raise ValueError(
432                'The allowed values for state are {} and {}.'.format(
433                    LTE_CONN_RESP, LTE_IDLE_RESP))
434
435        while timeout > 0:
436            new_state = self.send_and_recv('SENSe:LTE:SIGN:RRCState?')
437
438            if new_state == state:
439                self._logger.debug('The RRC state is {}.'.format(new_state))
440                break
441
442            # Wait for a second and decrease count by one
443            time.sleep(1)
444            timeout -= 1
445        else:
446            raise CmwError('Timeout before RRC state was {}.'.format(state))
447
448    def stop_all_signalling(self):
449        """Turns off all signaling applications, generators, or measurements."""
450        self.send_and_recv('SYSTem:SIGNaling:ALL:OFF')
451        self.wait_until_quiet()
452
453    def wait_until_quiet(self):
454        """Waits for all pending operations to stop on the callbox."""
455        self.send_and_recv('*OPC?')
456
457    def reset(self):
458        """System level reset"""
459        self.send_and_recv('*RST; *OPC')
460
461    @property
462    def get_instrument_id(self):
463        """Gets instrument identification number"""
464        return self.send_and_recv('*IDN?')
465
466    def disconnect(self):
467        """Disconnect controller from device and switch to local mode."""
468        self.switch_lte_signalling(LteState.LTE_OFF)
469        self.close_remote_mode()
470        self._close_socket()
471
472    def close_remote_mode(self):
473        """Exits remote mode to local mode."""
474        self.send_and_recv('&GTL')
475
476    def detach(self):
477        """Detach callbox and controller."""
478        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion DETach')
479
480    @property
481    def rrc_connection(self):
482        """Gets the RRC connection state."""
483        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:KRRC?')
484
485    @rrc_connection.setter
486    def rrc_connection(self, state):
487        """Selects whether the RRC connection is kept or released after attach.
488
489        Args:
490            mode: RRC State ON/OFF.
491        """
492        if not isinstance(state, RrcState):
493            raise ValueError('state should be the instance of RrcState.')
494
495        cmd = 'CONFigure:LTE:SIGN:CONNection:KRRC {}'.format(state.value)
496        self.send_and_recv(cmd)
497
498    @property
499    def rrc_connection_timer(self):
500        """Gets the inactivity timeout for disabled rrc connection."""
501        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:RITimer?')
502
503    @rrc_connection_timer.setter
504    def rrc_connection_timer(self, time_in_secs):
505        """Sets the inactivity timeout for disabled rrc connection. By default
506        the timeout is set to 5.
507
508        Args:
509            time_in_secs: timeout of inactivity in rrc connection.
510        """
511        cmd = 'CONFigure:LTE:SIGN:CONNection:RITimer {}'.format(time_in_secs)
512        self.send_and_recv(cmd)
513
514    @property
515    def scc_activation_mode(self):
516        """Gets the activation mode to use for SCCs when establishing a
517        connection.
518        """
519        return self.send_and_recv('CONFigure:LTE:SIGN:SCC:AMODe?')
520
521    @scc_activation_mode.setter
522    def scc_activation_mode(self, activation_mode):
523        """Sets the activation mode to use with SCCs when establishing a
524        connection.
525
526        Args:
527            activation_mode: the scc activation mode to use.
528        """
529        if not isinstance(activation_mode, SccActivationMode):
530            raise ValueError('state should be the instance of RrcState.')
531
532        cmd = 'CONFigure:LTE:SIGN:SCC:AMODe {}'.format(activation_mode.value)
533        self.send_and_recv(cmd)
534
535    @property
536    def dl_mac_padding(self):
537        """Gets the state of mac padding."""
538        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:DLPadding?')
539
540    @dl_mac_padding.setter
541    def dl_mac_padding(self, state):
542        """Enables/Disables downlink padding at the mac layer.
543
544        Args:
545            state: ON/OFF
546        """
547        cmd = 'CONFigure:LTE:SIGN:CONNection:DLPadding {}'.format(state.value)
548        self.send_and_recv(cmd)
549
550    @property
551    def connection_type(self):
552        """Gets the connection type applied in callbox."""
553        return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:CTYPe?')
554
555    @connection_type.setter
556    def connection_type(self, ctype):
557        """Sets the connection type to be applied.
558
559        Args:
560            ctype: Connection type.
561        """
562        cmd = 'CONFigure:LTE:SIGN:CONNection:CTYPe {}'.format(ctype.value)
563        self.send_and_recv(cmd)
564
565    @property
566    def drx_connected_mode(self):
567        """ Gets the Connected DRX LTE cell parameter
568
569        Args:
570            None
571
572        Returns:
573            DRX connected mode (ON, OFF, USER_DEFINED, DRX_S, DRX_L)
574        """
575        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ENABle?'
576        return self.send_and_recv(cmd)
577
578    @drx_connected_mode.setter
579    def drx_connected_mode(self, mode):
580        """  Sets the Connected DRX LTE cell parameter
581
582        Args:
583            mode: DRX mode
584
585        Returns:
586            None
587        """
588        if not isinstance(mode, DrxMode):
589            raise ValueError('state should be the instance of DrxMode.')
590
591        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ENABle {}'.format(mode.value)
592        self.send_and_recv(cmd)
593
594    @property
595    def drx_on_duration_timer(self):
596        """ Gets the amount of PDCCH subframes to wait for data after
597            waking up from a DRX cycle
598
599        Args:
600            None
601
602        Returns:
603            DRX mode duration timer
604        """
605        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ODTimer?'
606        return self.send_and_recv(cmd)
607
608    @drx_on_duration_timer.setter
609    def drx_on_duration_timer(self, time):
610        """ Sets the amount of PDCCH subframes to wait for data after
611            waking up from a DRX cycle
612
613        Args:
614            timer: Length of interval to wait for user data to be transmitted
615
616        Returns:
617            None
618        """
619        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ODTimer {}'.format(time)
620        self.send_and_recv(cmd)
621
622    @property
623    def drx_inactivity_timer(self):
624        """ Gets the number of PDCCH subframes to wait before entering DRX mode
625
626        Args:
627            None
628
629        Returns:
630            DRX mode inactivity timer
631        """
632        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ITIMer?'
633        return self.send_and_recv(cmd)
634
635    @drx_inactivity_timer.setter
636    def drx_inactivity_timer(self, time):
637        """ Sets the number of PDCCH subframes to wait before entering DRX mode
638
639        Args:
640            timer: Length of the interval to wait
641
642        Returns:
643            None
644        """
645        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ITIMer {}'.format(time)
646        self.send_and_recv(cmd)
647
648    @property
649    def drx_retransmission_timer(self):
650        """ Gets the number of consecutive PDCCH subframes to wait
651        for retransmission
652
653        Args:
654            None
655
656        Returns:
657            Number of PDCCH subframes to wait for retransmission
658        """
659        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:RTIMer?'
660        self.send_and_recv(cmd)
661
662    @drx_retransmission_timer.setter
663    def drx_retransmission_timer(self, time):
664        """ Sets the number of consecutive PDCCH subframes to wait
665        for retransmission
666
667        Args:
668            time: Number of PDCCH subframes to wait
669            for retransmission
670
671        Returns:
672            None
673        """
674        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:RTIMer {}'.format(time)
675        self.send_and_recv(cmd)
676
677    @property
678    def drx_long_cycle(self):
679        """ Gets the amount of subframes representing a DRX long cycle
680
681        Args:
682            None
683
684        Returns:
685            The amount of subframes representing one long DRX cycle.
686            One cycle consists of DRX sleep + DRX on duration
687        """
688        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:LDCYcle?'
689        return self.send_and_recv(cmd)
690
691    @drx_long_cycle.setter
692    def drx_long_cycle(self, long_cycle):
693        """ Sets the amount of subframes representing a DRX long cycle
694
695        Args:
696            long_cycle: The amount of subframes representing one long DRX cycle.
697                One cycle consists of DRX sleep + DRX on duration
698
699        Returns:
700            None
701        """
702        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:LDCYcle {}'.format(
703            long_cycle)
704        self.send_and_recv(cmd)
705
706    @property
707    def drx_long_cycle_offset(self):
708        """ Gets the offset used to determine long cycle starting
709        subframe
710
711        Args:
712            None
713
714        Returns:
715            Long cycle offset
716        """
717        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:SOFFset?'
718        return self.send_and_recv(cmd)
719
720    @drx_long_cycle_offset.setter
721    def drx_long_cycle_offset(self, offset):
722        """ Sets the offset used to determine long cycle starting
723        subframe
724
725        Args:
726            offset: Number in range 0...(long cycle - 1)
727        """
728        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:SOFFset {}'.format(offset)
729        self.send_and_recv(cmd)
730
731    @property
732    def apn(self):
733        """Gets the callbox network Access Point Name."""
734        cmd = 'CONFigure:LTE:SIGNaling:CONNection:APN?'
735        return self.send_and_recv(cmd)
736
737    @apn.setter
738    def apn(self, apn):
739        """Sets the callbox network Access Point Name.
740
741        Args:
742            apn: the APN name
743        """
744        cmd = 'CONFigure:LTE:SIGNaling:CONNection:APN {}'.format(apn)
745        self.send_and_recv(cmd)
746
747    @property
748    def ip_type(self):
749        """Gets the callbox network IP type."""
750        cmd = 'CONFigure:LTE:SIGNaling:CONNection:IPVersion?'
751        return self.send_and_recv(cmd)
752
753    @ip_type.setter
754    def ip_type(self, ip_type):
755        """ Configures the callbox network IP type.
756
757        Args:
758            ip_type: the network type to use.
759        """
760        if not isinstance(ip_type, IPAddressType):
761            raise ValueError('state should be the instance of IPAddressType.')
762
763        cmd = 'CONFigure:LTE:SIGNaling:CONNection:IPVersion {}'.format(
764            ip_type.value)
765        return self.send_and_recv(cmd)
766
767    @property
768    def mtu(self):
769        """Gets the callbox network Maximum Transmission Unit."""
770        cmd = 'CONFigure:DATA:CONTrol:MTU?'
771        return self.send_and_recv(cmd)
772
773    @mtu.setter
774    def mtu(self, mtu):
775        """Sets the callbox network Maximum Transmission Unit.
776
777        Args:
778            mtu: the MTU size.
779        """
780        cmd = 'CONFigure:DATA:CONTrol:MTU {}'.format(mtu)
781        self.send_and_recv(cmd)
782
783    def get_base_station(self, bts_num=BtsNumber.BTS1):
784        """Gets the base station object based on bts num. By default
785        bts_num set to PCC
786
787        Args:
788            bts_num: base station identifier
789
790        Returns:
791            base station object.
792        """
793        return BaseStation(self, bts_num)
794
795    def init_lte_measurement(self):
796        """Gets the class object for lte measurement which can be used to
797        initiate measurements.
798
799        Returns:
800            lte measurement object.
801        """
802        return LteMeasurement(self)
803
804    def set_sms(self, message):
805        """Sets the SMS message to be sent to the DUT.
806
807        Args:
808            message: the SMS message to send.
809        """
810        cmd = 'CONFigure:LTE:SIGN:SMS:OUTGoing:INTernal "{}"'.format(message)
811        self.send_and_recv(cmd)
812
813    def send_sms(self):
814        """Sends the currently set SMS message."""
815        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion SMS;*OPC?')
816        timeout = time.time() + STATE_CHANGE_TIMEOUT
817        while time.time() < timeout:
818            state = self.send_and_recv(
819                'SENSe:LTE:SIGN:SMS:OUTGoing:INFO:LMSent?')
820            if state == "SUCC":
821                return
822            time.sleep(1)
823
824        raise CmwError('Failed to send SMS message')
825
826
827class BaseStation(object):
828    """Class to interact with different base stations"""
829    def __init__(self, cmw, bts_num):
830        if not isinstance(bts_num, BtsNumber):
831            raise ValueError('bts_num should be an instance of BtsNumber.')
832        self._bts = bts_num.value
833        self._cmw = cmw
834
835    @property
836    def scc_state(self):
837        """Gets the scc state of cell."""
838        cmd = 'FETCh:LTE:SIGN:{}:STATe?'.format(self._bts)
839        return self._cmw.send_and_recv(cmd)
840
841    @property
842    def duplex_mode(self):
843        """Gets current duplex of cell."""
844        cmd = 'CONFigure:LTE:SIGN:{}:DMODe?'.format(self._bts)
845        return self._cmw.send_and_recv(cmd)
846
847    @duplex_mode.setter
848    def duplex_mode(self, mode):
849        """Sets the Duplex mode of cell.
850
851        Args:
852            mode: String indicating FDD or TDD.
853        """
854        if not isinstance(mode, DuplexMode):
855            raise ValueError('mode should be an instance of DuplexMode.')
856
857        cmd = 'CONFigure:LTE:SIGN:{}:DMODe {}'.format(self._bts, mode.value)
858        self._cmw.send_and_recv(cmd)
859
860    @property
861    def band(self):
862        """Gets the current band of cell."""
863        cmd = 'CONFigure:LTE:SIGN:{}:BAND?'.format(self._bts)
864        return self._cmw.send_and_recv(cmd)
865
866    @band.setter
867    def band(self, band):
868        """Sets the Band of cell.
869
870        Args:
871            band: band of cell.
872        """
873        cmd = 'CONFigure:LTE:SIGN:{}:BAND {}'.format(self._bts, band)
874        self._cmw.send_and_recv(cmd)
875
876    @property
877    def dl_channel(self):
878        """Gets the downlink channel of cell."""
879        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL?'.format(self._bts)
880        return self._cmw.send_and_recv(cmd)
881
882    @dl_channel.setter
883    def dl_channel(self, channel):
884        """Sets the downlink channel number of cell.
885
886        Args:
887            channel: downlink channel number of cell.
888        """
889        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL {}'.format(
890            self._bts, channel)
891        self._cmw.send_and_recv(cmd)
892
893    @property
894    def ul_channel(self):
895        """Gets the uplink channel of cell."""
896        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL?'.format(self._bts)
897        return self._cmw.send_and_recv(cmd)
898
899    @ul_channel.setter
900    def ul_channel(self, channel):
901        """Sets the up link channel number of cell.
902
903        Args:
904            channel: up link channel number of cell.
905        """
906        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL {}'.format(
907            self._bts, channel)
908        self._cmw.send_and_recv(cmd)
909
910    @property
911    def bandwidth(self):
912        """Get the channel bandwidth of the cell."""
913        cmd = 'CONFigure:LTE:SIGN:CELL:BANDwidth:{}:DL?'.format(self._bts)
914        return self._cmw.send_and_recv(cmd)
915
916    @bandwidth.setter
917    def bandwidth(self, bandwidth):
918        """Sets the channel bandwidth of the cell.
919
920        Args:
921            bandwidth: channel bandwidth of cell.
922        """
923        if not isinstance(bandwidth, LteBandwidth):
924            raise ValueError('bandwidth should be an instance of '
925                             'LteBandwidth.')
926        cmd = 'CONFigure:LTE:SIGN:CELL:BANDwidth:{}:DL {}'.format(
927            self._bts, bandwidth.value)
928        self._cmw.send_and_recv(cmd)
929
930    @property
931    def ul_frequency(self):
932        """Get the uplink frequency of the cell."""
933        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL? MHZ'.format(
934            self._bts)
935        return self._cmw.send_and_recv(cmd)
936
937    @ul_frequency.setter
938    def ul_frequency(self, freq):
939        """Get the uplink frequency of the cell.
940
941        Args:
942            freq: uplink frequency of the cell.
943        """
944        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:UL {} MHZ'.format(
945            self._bts, freq)
946        self._cmw.send_and_recv(cmd)
947
948    @property
949    def dl_frequency(self):
950        """Get the downlink frequency of the cell"""
951        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL? MHZ'.format(
952            self._bts)
953        return self._cmw.send_and_recv(cmd)
954
955    @dl_frequency.setter
956    def dl_frequency(self, freq):
957        """Get the downlink frequency of the cell.
958
959        Args:
960            freq: downlink frequency of the cell.
961        """
962        cmd = 'CONFigure:LTE:SIGN:RFSettings:{}:CHANnel:DL {} MHZ'.format(
963            self._bts, freq)
964        self._cmw.send_and_recv(cmd)
965
966    @property
967    def transmode(self):
968        """Gets the TM of cell."""
969        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:TRANsmission?'.format(
970            self._bts)
971        return self._cmw.send_and_recv(cmd)
972
973    @transmode.setter
974    def transmode(self, tm_mode):
975        """Sets the TM of cell.
976
977        Args:
978            tm_mode: TM of cell.
979        """
980        if not isinstance(tm_mode, TransmissionModes):
981            raise ValueError('tm_mode should be an instance of '
982                             'Transmission modes.')
983
984        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:TRANsmission {}'.format(
985            self._bts, tm_mode.value)
986        self._cmw.send_and_recv(cmd)
987
988    @property
989    def downlink_power_level(self):
990        """Gets RSPRE level."""
991        cmd = 'CONFigure:LTE:SIGN:DL:{}:RSEPre:LEVel?'.format(self._bts)
992        return self._cmw.send_and_recv(cmd)
993
994    @downlink_power_level.setter
995    def downlink_power_level(self, pwlevel):
996        """Modifies RSPRE level.
997
998        Args:
999            pwlevel: power level in dBm.
1000        """
1001        cmd = 'CONFigure:LTE:SIGN:DL:{}:RSEPre:LEVel {}'.format(
1002            self._bts, pwlevel)
1003        self._cmw.send_and_recv(cmd)
1004
1005    @property
1006    def uplink_power_control(self):
1007        """Gets open loop nominal power directly."""
1008        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:OLNPower?'.format(self._bts)
1009        return self._cmw.send_and_recv(cmd)
1010
1011    @uplink_power_control.setter
1012    def uplink_power_control(self, ul_power):
1013        """Sets open loop nominal power directly.
1014
1015        Args:
1016            ul_power: uplink power level.
1017        """
1018        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:OLNPower {}'.format(
1019            self._bts, ul_power)
1020        self._cmw.send_and_recv(cmd)
1021
1022    @property
1023    def uldl_configuration(self):
1024        """Gets uldl configuration of the cell."""
1025        cmd = 'CONFigure:LTE:SIGN:CELL:{}:ULDL?'.format(self._bts)
1026        return self._cmw.send_and_recv(cmd)
1027
1028    @uldl_configuration.setter
1029    def uldl_configuration(self, uldl):
1030        """Sets the ul-dl configuration.
1031
1032        Args:
1033            uldl: Configuration value ranging from 0 to 6.
1034        """
1035        if uldl not in range(0, 7):
1036            raise ValueError('uldl configuration value should be between'
1037                             ' 0 and 6 inclusive.')
1038
1039        cmd = 'CONFigure:LTE:SIGN:CELL:{}:ULDL {}'.format(self._bts, uldl)
1040        self._cmw.send_and_recv(cmd)
1041
1042    @property
1043    def tdd_special_subframe(self):
1044        """Gets special subframe of the cell."""
1045        cmd = 'CONFigure:LTE:SIGN:CELL:{}:SSUBframe?'.format(self._bts)
1046        return self._cmw.send_and_recv(cmd)
1047
1048    @tdd_special_subframe.setter
1049    def tdd_special_subframe(self, sframe):
1050        """Sets the tdd special subframe of the cell.
1051
1052        Args:
1053            sframe: Integer value ranging from 1 to 9.
1054        """
1055        if sframe not in range(0, 10):
1056            raise ValueError('tdd special subframe should be between 0 and 9'
1057                             ' inclusive.')
1058
1059        cmd = 'CONFigure:LTE:SIGN:CELL:{}:SSUBframe {}'.format(
1060            self._bts, sframe)
1061        self._cmw.send_and_recv(cmd)
1062
1063    @property
1064    def scheduling_mode(self):
1065        """Gets the current scheduling mode."""
1066        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:STYPe?'.format(self._bts)
1067        return self._cmw.send_and_recv(cmd)
1068
1069    @scheduling_mode.setter
1070    def scheduling_mode(self, mode):
1071        """Sets the scheduling type for the cell.
1072
1073        Args:
1074            mode: Selects the channel mode to be scheduled.
1075        """
1076        if not isinstance(mode, SchedulingMode):
1077            raise ValueError('mode should be the instance of scheduling mode.')
1078
1079        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:STYPe {}'.format(
1080            self._bts, mode.value)
1081        self._cmw.send_and_recv(cmd)
1082
1083    @property
1084    def rb_configuration_dl(self):
1085        """Gets rmc's rb configuration for down link. This function returns
1086        Number of Resource blocks, Resource block position and Modulation type.
1087        """
1088        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:{}:DL?'.format(
1089            self._bts, self.scheduling_mode)
1090        return self._cmw.send_and_recv(cmd)
1091
1092    @rb_configuration_dl.setter
1093    def rb_configuration_dl(self, rb_config):
1094        """Sets the rb configuration for down link for scheduling type.
1095
1096        Args:
1097            rb_config: Tuple containing Number of resource blocks, resource
1098            block position and modulation type.
1099
1100        Raises:
1101            ValueError: If tuple unpacking fails.
1102        """
1103        if self.scheduling_mode == 'RMC':
1104            rb, rb_pos, modulation = rb_config
1105
1106            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:RMC:DL {},{},'
1107                   '{}'.format(self._bts, rb, rb_pos, modulation))
1108            self._cmw.send_and_recv(cmd)
1109
1110        elif self.scheduling_mode == 'UDCH':
1111            rb, start_rb, modulation, tbs = rb_config
1112
1113            self.validate_rb(rb)
1114
1115            if not isinstance(modulation, ModulationType):
1116                raise ValueError('Modulation should be of type '
1117                                 'ModulationType.')
1118
1119            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:UDCHannels:DL {},{},'
1120                   '{},{}'.format(self._bts, rb, start_rb, modulation.value,
1121                                  tbs))
1122            self._cmw.send_and_recv(cmd)
1123
1124    @property
1125    def rb_configuration_ul(self):
1126        """Gets rb configuration for up link. This function returns
1127        Number of Resource blocks, Resource block position and Modulation type.
1128        """
1129        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:{}:UL?'.format(
1130            self._bts, self.scheduling_mode)
1131        return self._cmw.send_and_recv(cmd)
1132
1133    @rb_configuration_ul.setter
1134    def rb_configuration_ul(self, rb_config):
1135        """Sets the rb configuration for down link for scheduling mode.
1136
1137        Args:
1138            rb_config: Tuple containing Number of resource blocks, resource
1139            block position and modulation type.
1140
1141        Raises:
1142            ValueError: If tuple unpacking fails.
1143        """
1144        if self.scheduling_mode == 'RMC':
1145            rb, rb_pos, modulation = rb_config
1146
1147            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:RMC:UL {},{},'
1148                   '{}'.format(self._bts, rb, rb_pos, modulation))
1149            self._cmw.send_and_recv(cmd)
1150
1151        elif self.scheduling_mode == 'UDCH':
1152            rb, start_rb, modulation, tbs = rb_config
1153
1154            self.validate_rb(rb)
1155
1156            if not isinstance(modulation, ModulationType):
1157                raise ValueError('Modulation should be of type '
1158                                 'ModulationType.')
1159            cmd = ('CONFigure:LTE:SIGN:CONNection:{}:UDCHannels:UL {},{},'
1160                   '{},{}'.format(self._bts, rb, start_rb, modulation.value,
1161                                  tbs))
1162            self._cmw.send_and_recv(cmd)
1163
1164    def validate_rb(self, rb):
1165        """Validates if rb is within the limits for bandwidth set.
1166
1167        Args:
1168            rb: No. of resource blocks.
1169
1170        Raises:
1171            ValueError if rb out of range.
1172        """
1173        bandwidth = self.bandwidth
1174
1175        if bandwidth == LteBandwidth.BANDWIDTH_1MHz.value:
1176            if not 0 <= rb <= 6:
1177                raise ValueError('RB should be between 0 to 6 inclusive'
1178                                 ' for 1.4Mhz.')
1179        elif bandwidth == LteBandwidth.BANDWIDTH_3MHz.value:
1180            if not 0 <= rb <= 10:
1181                raise ValueError('RB should be between 0 to 10 inclusive'
1182                                 ' for 3 Mhz.')
1183        elif bandwidth == LteBandwidth.BANDWIDTH_5MHz.value:
1184            if not 0 <= rb <= 25:
1185                raise ValueError('RB should be between 0 to 25 inclusive'
1186                                 ' for 5 Mhz.')
1187        elif bandwidth == LteBandwidth.BANDWIDTH_10MHz.value:
1188            if not 0 <= rb <= 50:
1189                raise ValueError('RB should be between 0 to 50 inclusive'
1190                                 ' for 10 Mhz.')
1191        elif bandwidth == LteBandwidth.BANDWIDTH_15MHz.value:
1192            if not 0 <= rb <= 75:
1193                raise ValueError('RB should be between 0 to 75 inclusive'
1194                                 ' for 15 Mhz.')
1195        elif bandwidth == LteBandwidth.BANDWIDTH_20MHz.value:
1196            if not 0 <= rb <= 100:
1197                raise ValueError('RB should be between 0 to 100 inclusive'
1198                                 ' for 20 Mhz.')
1199
1200    @property
1201    def rb_position_dl(self):
1202        """Gets the position of the allocated down link resource blocks within
1203        the channel band-width.
1204        """
1205        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:DL?'.format(
1206            self._bts)
1207        return self._cmw.send_and_recv(cmd)
1208
1209    @rb_position_dl.setter
1210    def rb_position_dl(self, rbpos):
1211        """Selects the position of the allocated down link resource blocks
1212        within the channel band-width
1213
1214        Args:
1215            rbpos: position of resource blocks.
1216        """
1217        if not isinstance(rbpos, RbPosition):
1218            raise ValueError('rbpos should be the instance of RbPosition.')
1219
1220        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:DL {}'.format(
1221            self._bts, rbpos.value)
1222        self._cmw.send_and_recv(cmd)
1223
1224    @property
1225    def rb_position_ul(self):
1226        """Gets the position of the allocated up link resource blocks within
1227        the channel band-width.
1228        """
1229        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:UL?'.format(
1230            self._bts)
1231        return self._cmw.send_and_recv(cmd)
1232
1233    @rb_position_ul.setter
1234    def rb_position_ul(self, rbpos):
1235        """Selects the position of the allocated up link resource blocks
1236        within the channel band-width.
1237
1238        Args:
1239            rbpos: position of resource blocks.
1240        """
1241        if not isinstance(rbpos, RbPosition):
1242            raise ValueError('rbpos should be the instance of RbPosition.')
1243
1244        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:RMC:RBPosition:UL {}'.format(
1245            self._bts, rbpos.value)
1246        self._cmw.send_and_recv(cmd)
1247
1248    @property
1249    def dci_format(self):
1250        """Gets the downlink control information (DCI) format."""
1251        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:DCIFormat?'.format(self._bts)
1252        return self._cmw.send_and_recv(cmd)
1253
1254    @dci_format.setter
1255    def dci_format(self, dci_format):
1256        """Selects the downlink control information (DCI) format.
1257
1258        Args:
1259            dci_format: supported dci.
1260        """
1261        if not isinstance(dci_format, DciFormat):
1262            raise ValueError('dci_format should be the instance of DciFormat.')
1263
1264        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:DCIFormat {}'.format(
1265            self._bts, dci_format.value)
1266        self._cmw.send_and_recv(cmd)
1267
1268    @property
1269    def dl_antenna(self):
1270        """Gets dl antenna count of cell."""
1271        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas?'.format(
1272            self._bts)
1273        return self._cmw.send_and_recv(cmd)
1274
1275    @dl_antenna.setter
1276    def dl_antenna(self, num_antenna):
1277        """Sets the dl antenna count of cell.
1278
1279        Args:
1280            num_antenna: Count of number of dl antennas to use.
1281        """
1282        if not isinstance(num_antenna, MimoModes):
1283            raise ValueError('num_antenna should be an instance of MimoModes.')
1284        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas {}'.format(
1285            self._bts, num_antenna.value)
1286        self._cmw.send_and_recv(cmd)
1287
1288    @property
1289    def reduced_pdcch(self):
1290        """Gets the reduction of PDCCH resources state."""
1291        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:PDCCh:RPDCch?'.format(
1292            self._bts)
1293        return self._cmw.send_and_recv(cmd)
1294
1295    @reduced_pdcch.setter
1296    def reduced_pdcch(self, state):
1297        """Sets the reduction of PDCCH resources state.
1298
1299        Args:
1300            state: ON/OFF.
1301        """
1302        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:PDCCh:RPDCch {}'.format(
1303            self._bts, state.value)
1304        self._cmw.send_and_recv(cmd)
1305
1306    @property
1307    def tpc_power_control(self):
1308        """Gets the type of uplink power control used."""
1309        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:SET?'.format(self._bts)
1310        return self._cmw.send_and_recv(cmd)
1311
1312    @tpc_power_control.setter
1313    def tpc_power_control(self, set_type):
1314        """Set and execute the Up Link Power Control via TPC.
1315
1316        Args:
1317            set_type: Type of tpc power control.
1318        """
1319
1320        if not isinstance(set_type, TpcPowerControl):
1321            raise ValueError('set_type should be the instance of '
1322                             'TpCPowerControl.')
1323        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:SET {}'.format(
1324            self._bts, set_type.value)
1325        self._cmw.send_and_recv(cmd)
1326        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:PEXecute'.format(self._bts)
1327        self._cmw.send_and_recv(cmd)
1328
1329    @property
1330    def tpc_closed_loop_target_power(self):
1331        """Gets the target powers for power control with the TPC setup."""
1332        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:CLTPower?'.format(self._bts)
1333        return self._cmw.send_and_recv(cmd)
1334
1335    @tpc_closed_loop_target_power.setter
1336    def tpc_closed_loop_target_power(self, cltpower):
1337        """Sets the target powers for power control with the TPC setup.
1338
1339        Args:
1340            tpower: Target power.
1341        """
1342        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:CLTPower {}'.format(
1343            self._bts, cltpower)
1344        self._cmw.send_and_recv(cmd)
1345
1346
1347class LteMeasurementState(Enum):
1348    """Possible measurement states"""
1349    OFF = 'OFF'
1350    READY = 'RDY'
1351    RUN = 'RUN'
1352
1353
1354def _try_parse(s, fun):
1355    try:
1356        return fun(s)
1357    except ValueError:
1358        return None
1359
1360
1361class LteMeasurement(object):
1362    """ Class for measuring LTE performance """
1363
1364    INDEX_TX = 17
1365    INDEX_TX_MIN = 17
1366    INDEX_TX_MAX = 18
1367
1368    def __init__(self, cmw):
1369        self._cmw = cmw
1370
1371    @property
1372    def sample_count(self):
1373        """Gets the number of samples to use when calculating results."""
1374        cmd = 'CONFigure:LTE:MEAS:MEValuation:SCOunt:MODulation?'
1375        return self._cmw.send_and_recv(cmd)
1376
1377    @sample_count.setter
1378    def sample_count(self, sample_count):
1379        cmd = 'CONFigure:LTE:MEAS:MEValuation:SCOunt:MODulation {}'.format(
1380            sample_count)
1381        self._cmw.send_and_recv(cmd)
1382
1383    @property
1384    def average(self):
1385        """Gets the average values of the most recent measurement.
1386        Invalid measurements are set to None.
1387        """
1388        values = self._cmw.send_and_recv(
1389            'FETch:LTE:MEAS:MEValuation:MODulation:AVERage?').split(',')
1390        for i in range(0, 2):
1391            values[i] = _try_parse(values[i], int)
1392        for i in range(2, len(values)):
1393            values[i] = _try_parse(values[i], float)
1394        return values
1395
1396    @property
1397    def extrema(self):
1398        """Gets the extrema values of the most recent measurement.
1399        Invalid measurements are set to None.
1400        """
1401        values = self._cmw.send_and_recv(
1402            'FETch:LTE:MEAS:MEValuation:MODulation:EXTReme?').split(',')
1403        for i in range(0, 2):
1404            values[i] = _try_parse(values[i], int)
1405        for i in range(2, len(values)):
1406            values[i] = _try_parse(values[i], float)
1407        return values
1408
1409    @property
1410    def stdev(self):
1411        """Gets the standard deviation of the most recent measurement.
1412        Invalid measurements are set to None.
1413        """
1414        values = self._cmw.send_and_recv(
1415            'FETch:LTE:MEAS:MEValuation:MODulation:SDEV?').split(',')
1416        for i in range(0, 2):
1417            values[i] = _try_parse(values[i], int)
1418        for i in range(2, len(values)):
1419            values[i] = _try_parse(values[i], float)
1420        return values
1421
1422    @property
1423    def tx_average(self):
1424        """Gets the measured average Tx power (in dBm)."""
1425        value = self.average[self.INDEX_TX]
1426        if value is None:
1427            raise ValueError("LteMeasurement has no value for tx_average")
1428        return value
1429
1430    @property
1431    def tx_min(self):
1432        """Gets the measured minimum Tx power (in dBm)."""
1433        value = self.extrema[self.INDEX_TX_MIN]
1434        if value is None:
1435            raise ValueError("LteMeasurement has no value for tx_min")
1436        return value
1437
1438    @property
1439    def tx_max(self):
1440        """Gets the measured maximum Tx power (in dBm)."""
1441        value = self.extrema[self.INDEX_TX_MAX]
1442        if value is None:
1443            raise ValueError("LteMeasurement has no value for tx_max")
1444        return value
1445
1446    @property
1447    def tx_stdev(self):
1448        """Gets the measured Tx power standard deviation (in dBm)."""
1449        value = self.stdev[self.INDEX_TX]
1450        if value is None:
1451            raise ValueError(
1452                "LteMeasurement has no value for standard_deviation")
1453        return value
1454
1455    @property
1456    def measurement_repetition(self):
1457        """Returns the measurement repetition mode that has been set."""
1458        return self._cmw.send_and_recv(
1459            'CONFigure:LTE:MEAS:MEValuation:REPetition?')
1460
1461    @measurement_repetition.setter
1462    def measurement_repetition(self, mode):
1463        """Sets the mode for measuring power levels.
1464
1465        Args:
1466            mode: Single shot/continuous.
1467        """
1468        if not isinstance(mode, RepetitionMode):
1469            raise ValueError('mode should be the instance of Repetition Mode')
1470
1471        cmd = 'CONFigure:LTE:MEAS:MEValuation:REPetition {}'.format(mode.value)
1472        self._cmw.send_and_recv(cmd)
1473
1474    @property
1475    def query_measurement_state(self):
1476        """Returns the states and sub states of measurement."""
1477        return self._cmw.send_and_recv('FETCh:LTE:MEAS:MEValuation:STATe:ALL?')
1478
1479    @property
1480    def measure_tx_power(self):
1481        """Return the current Tx power measurement."""
1482        return self._cmw.send_and_recv(
1483            'FETCh:LTE:MEAS:MEValuation:PMONitor:AVERage?')
1484
1485    @property
1486    def state(self):
1487        """Gets the state of the measurement."""
1488        cmd = 'FETCh:LTE:MEAS:MEValuation:STATe?'
1489        return LteMeasurementState(self._cmw.send_and_recv(cmd))
1490
1491    def initialize_measurement(self):
1492        """Initialize measurement modules."""
1493        self._cmw.send_and_recv(
1494            'CONF:LTE:MEAS:MEV:RES:ALL ON,ON,ON,ON,ON,ON,ON,ON,ON,ON,ON,ON')
1495        self._cmw.send_and_recv('ROUTe:LTE:MEAS:SCENario:CSPath "LTE Sig1"')
1496        self._cmw.send_and_recv('INIT:LTE:MEAS:MEValuation;*OPC?')
1497        self._wait_for_state({LteMeasurementState.RUN})
1498
1499    def run_measurement(self):
1500        """Runs a single Tx multievaluation measurement to completion."""
1501        self.stop_measurement()
1502        self.measurement_repetition = RepetitionMode.SINGLESHOT
1503        self.initialize_measurement()
1504        self._wait_for_state({LteMeasurementState.READY}, timeout=120)
1505
1506    def stop_measurement(self):
1507        """Stops the on-going measurement.
1508        This function call does not free up resources allocated for
1509        measurement. Instead it moves from RUN to RDY state.
1510        """
1511        self._cmw.send_and_recv('STOP:LTE:MEAS:MEValuation')
1512        self._wait_for_state(
1513            {LteMeasurementState.OFF, LteMeasurementState.READY})
1514
1515    def abort_measurement(self):
1516        """Aborts the measurement abruptly.
1517        This function call will free up the resources allocated for
1518        measurement and all the results will be wiped off.
1519        """
1520        self._cmw.send_and_recv('ABORt:LTE:MEAS:MEValuation')
1521        self._wait_for_state({LteMeasurementState.OFF})
1522
1523    def _wait_for_state(self, states, timeout=10):
1524        """Polls the measurement state until it reaches an allowable state
1525
1526        Args:
1527            states: the allowed states
1528            timeout: the maximum amount time to wait (in seconds)
1529        """
1530        while timeout > 0:
1531            if self.state in states:
1532                return
1533
1534            time.sleep(1)
1535            timeout -= 1
1536
1537        raise CmwError(
1538            'Failed to wait for LTE measurement state: {}.'.format(states))
1539
1540
1541class CmwError(Exception):
1542    """Class to raise exceptions related to cmw."""
1543