xref: /aosp_15_r20/external/autotest/client/cros/cellular/base_station_pxt.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from __future__ import absolute_import
7from __future__ import division
8from __future__ import print_function
9
10import six
11import time
12
13import common
14
15from autotest_lib.client.bin import utils
16from autotest_lib.client.cros.cellular import air_state_verifier
17from autotest_lib.client.cros.cellular import base_station_interface
18from autotest_lib.client.cros.cellular import cellular
19from autotest_lib.client.cros.cellular import cellular_logging
20from autotest_lib.client.cros.cellular import cellular_system_error
21
22POLL_SLEEP = 0.2
23
24log = cellular_logging.SetupCellularLogging('base_station_pxt')
25
26
27class BaseStationPxt(base_station_interface.BaseStationInterface):
28    """Wrap an Agilent PXT"""
29
30    def __init__(self, scpi_connection, no_initialization=False):
31        """
32        Creates a PXT call-box object.
33        TODO(byronk): Make a factory that returns a call_box, of either
34        a 8960 or a PXT, or a...
35
36        @param scpi_connection:  The scpi port to send commands over.
37        @param no_initialization: Don't do anything. Useful for unit testing
38        and debugging when you don't want to run all the usual functions.
39        """
40        # TODO(byronk): Use variables longer then 1 char.
41        self.c = scpi_connection
42        if no_initialization:
43            return
44        self.checker_context = self.c.checker_context
45        with self.checker_context:
46            self._Verify()
47            self._Reset()
48
49    def _Verify(self):
50        idn = self.c.Query('*IDN?')
51        if 'E6621' not in idn:
52            raise cellular_system_error.BadScpiCommand(
53                'Not actually a E6621 PXT:  *IDN? says ' + idn)
54
55    def _Reset(self):
56        self.c.Reset()
57        self.Stop()
58
59    def _IsIdle(self):
60        bs_active = self.c.Query('BSE:SIMULator?')
61        return bs_active == 'STOP'
62
63    def Close(self):
64        self.c.Close()
65
66    def GetAirStateVerifier(self):
67        return air_state_verifier.AirStateVerifierBasestation(self)
68
69    def GetDataCounters(self):
70        raise NotImplementedError
71
72    def GetRatUeDataStatus(self):
73        """Get the radio-access-technology-specific status of the UE.
74
75        Unlike GetUeDataStatus, below, this returns a status that depends
76        on the RAT being used.
77        """
78        status = self.c.Query('BSE:STATus:ACELL?')
79        rat = \
80            ConfigDictionaries.FORMAT_TO_DATA_STATUS_TYPE[self.format][status]
81        return rat
82
83    def GetUeDataStatus(self):
84        """Get the UeGenericDataStatus status of the device."""
85        rat = self.GetRatUeDataStatus()
86        return cellular.RatToGenericDataStatus[rat]
87
88    def ResetDataCounters(self):
89        # Keep this here until the implementation of this class
90        # is done. If we never hit this, then we can safely remove it,
91        # but we should think twice about that. The 8960 needs it,
92        # it seems likely that the PXT should.
93        raise NotImplementedError
94
95    def ClearErrors(self):
96        self.c.RetrieveErrors()
97
98    def LogStats(self):
99        # Keep this here until the implementation of this class
100        # is done. If we never hit this, then we can safely remove it,
101        # but we should think twice about that. The 8960 needs it,
102        # it seems likely that the PXT should.
103        raise NotImplementedError
104
105    def SetBsIpV4(self, ip1, ip2):
106        return  # TODO(byronk): Configure the PXT to find.crbug.com:/235643
107
108    def SetBsNetmaskV4(self, netmask):
109        return  # TODO(byronk): Configure the PXT to find. crbug.com:/235643
110
111    def SetPlmn(self, mcc, mnc):
112        raise NotImplementedError
113
114    def SetPower(self, dbm):
115        # TODO(byronk): Setting the RF output of the PXT. crbug.com/235655
116        # and 8960 call boxes to OFF should be off
117        if dbm <= cellular.Power.OFF:
118            self.c.SendStanza(['AMPLitude:ALL -120'])
119        else:
120            self.c.SendStanza(['AMPLitude:ALL %s' % dbm])
121
122    def SetTechnology(self, technology):
123        # TODO(byronk):  The set technology step likely belongs in the
124        # constructor.
125
126        # Print out a helpful message on a key error.
127        try:
128            self.format = ConfigDictionaries.TECHNOLOGY_TO_FORMAT[technology]
129        except KeyError:
130            raise KeyError('%s not in %s ' % (
131                technology,
132                ConfigDictionaries.TECHNOLOGY_TO_FORMAT))
133        self.technology = technology
134
135    def SetUeDnsV4(self, dns1, dns2):
136        """Set the DNS values provided to the UE.  Emulator must be stopped."""
137        return  # TODO(byronk): Configure the PXT to find. crbug.com/235643
138                # the data server
139
140    def SetUeIpV4(self, ip1, ip2=None):
141        """
142        Set the IP addresses provided to the UE. Emulator must be stopped.
143        """
144        return  # TODO(byronk) crbug.com:/235643: Configure the PXT to find
145                # the data server
146
147    def Start(self):
148        commands = [
149            '*CLS',
150            'STATus:PRESet',
151            # Enable conn checks
152            'BSE:CONFig:RRC:CTIMer:STATus ON',
153            # Check freq (secs)
154            'BSE:CONFig:RRC:CTIMer:LENGth 5',
155            'SIGN:MODE BSE',
156            'SCENArio:LOAD "FDD_Combined_v6.3.lbmf"',
157            'BSE:CONF:PROF 20MH',
158            'FREQ:BAND 13',
159            'BSE:SIMULator RUN'
160        ]
161        self.c.SendStanza(commands)
162
163    def Stop(self):
164        self.c.SendStanza(['BSE:SIMULator STOP'])
165        # Make sure the call status goes to idle before continuing.
166        utils.poll_for_condition(
167            self._IsIdle,
168            timeout=cellular.DEFAULT_TIMEOUT,
169            exception=cellular_system_error.BadState(
170                'PXT did not enter IDLE state'))
171
172    def SupportedTechnologies(self):
173        return [cellular.Technology.LTE]
174
175    def WaitForStatusChange(self,
176                            interested=None,
177                            timeout=cellular.DEFAULT_TIMEOUT):
178        """When UE status changes (to a value in |interested|),
179        return the value.
180
181        Arguments:
182            interested: if non-None, only transitions to these states will
183              cause a return
184            timeout: in seconds.
185        Returns: state
186        Raises: cellular_system_error.InstrumentTimeout
187        """
188        start = time.time()
189        # TODO(byronk): consider utils.poll_for_condition()
190        while time.time() - start <= timeout:
191            state = self.GetUeDataStatus()
192            if state in interested:
193                return state
194            time.sleep(POLL_SLEEP)
195
196        state = self.GetUeDataStatus()
197        if state in interested:
198            return state
199
200        raise cellular_system_error.InstrumentTimeout(
201            'Timed out waiting for state in %s.  State was %s.' %
202            (interested, state))
203
204
205class ConfigStanzas(object):
206    # p 22 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf
207
208    def _Parse( command_sequence):
209        """Split and remove comments from a config stanza."""
210        return [line for line in command_sequence.splitlines()
211                if line and not line.startswith('#')]
212
213    LTE = _Parse(""" """)
214
215    # TODO(byronk): ConfigStanza should not be. These belong somewhere in
216    # the PXT class.
217    WCDMA_MAX = _Parse("""
218# RAB3: 64 Up/384 down
219# http://wireless.agilent.com/rfcomms/refdocs/
220#        wcdma/wcdmala_hpib_call_service.html#CACBDEAH
221CALL:UPLink:TXPower:LEVel:MAXimum 24
222CALL:SERVICE:GPRS:RAB GPRSRAB3
223""")
224
225    # p 20 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf
226    CDMA_2000_MAX = _Parse("""
227CALL:SCHannel:FORWard:DRATe BPS153600
228CALL:CELL:SOPTion:RCONfig3 SOFS33
229""")
230
231    # p 19 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf
232    EVDO_1X_MAX = _Parse("""
233CALL:CELL:CONTrol:CATTribute:ISTate:PCCCycle ATSP
234# Default data application
235CALL:APPLication:SESSion DPAPlication
236# Give DUT 100% of channel
237CALL:CELL:APPLication:ATDPackets 100
238""")
239
240    GPRS_MAX = _Parse("""
241call:bch:scel gprs
242call:pdtch:mslot:config d1u1
243call:cell:tbflow:t3192 ms1500
244""")
245
246    EGPRS_MAX = _Parse("""
247call:bch:scel egprs
248call:pdtch:mslot:config d4u1
249call:cell:tbflow:t3192 ms1500
250""")
251
252    CAT_08 = _Parse("""
253call:pow:stat ON
254call:ms:pow:targ 0
255call:cell:rlc:rees OFF
256call:hsdpa:ms:hsdschannel:cat:control:auto off
257call:hsdpa:ms:hsdschannel:cat:man 8
258call:hsdpa:service:psdata:hsdschannel:config cqiv
259call:hsdpa:service:psdata:cqi 22
260call:serv:gprs:rab PHSP
261call:serv:rbt:rab HSDP12
262call:serv:psd:srb:mapp UEDD
263call:hsup:serv:psd:edpd:ccod:max T2T4
264call:hsup:edch:tti MS10
265call:hsup:serv:psd:ergc:inf:stat Off
266""")
267
268    CAT_10 = _Parse("""
269call:pow:stat ON
270call:ms:pow:targ 0
271call:cell:rlc:rees OFF
272call:hsdpa:ms:hsdschannel:cat:control:auto off
273call:hsdpa:ms:hsdschannel:cat:man 10
274call:serv:gprs:rab PHSP
275call:serv:rbt:rab HSDP12
276call:hsdpa:service:psdata:hsdschannel:config cqiv
277call:hsdpa:service:psdata:cqi 22
278call:serv:psd:srb:mapp UEDD
279call:hsup:serv:psd:edpd:ccod:max T2T4
280call:hsup:edch:tti MS2
281call:hsup:serv:psd:ergc:inf:stat Off
282""")
283
284
285class ConfigDictionaries(object):
286    TECHNOLOGY_TO_FORMAT_RAW = {
287        cellular.Technology.GPRS: 'GSM/GPRS',
288        cellular.Technology.EGPRS: 'GSM/GPRS',
289
290        cellular.Technology.WCDMA: 'WCDMA',
291        cellular.Technology.HSDPA: 'WCDMA',
292        cellular.Technology.HSUPA: 'WCDMA',
293        cellular.Technology.HSDUPA: 'WCDMA',
294        cellular.Technology.HSPA_PLUS: 'WCDMA',
295
296        cellular.Technology.CDMA_2000: 'IS-2000/IS-95/AMPS',
297
298        cellular.Technology.EVDO_1X: 'IS-856',
299
300        cellular.Technology.LTE: 'LTE',
301    }
302
303    # Put each value in "" marks to quote it for GPIB
304    TECHNOLOGY_TO_FORMAT = dict([
305        (x, '"%s"' % y) for
306        x, y in six.iteritems(TECHNOLOGY_TO_FORMAT_RAW)])
307
308    TECHNOLOGY_TO_CONFIG_STANZA = {
309        cellular.Technology.CDMA_2000: ConfigStanzas.CDMA_2000_MAX,
310        cellular.Technology.EVDO_1X: ConfigStanzas.EVDO_1X_MAX,
311        cellular.Technology.GPRS: ConfigStanzas.GPRS_MAX,
312        cellular.Technology.EGPRS: ConfigStanzas.EGPRS_MAX,
313        cellular.Technology.WCDMA: ConfigStanzas.WCDMA_MAX,
314        cellular.Technology.HSDPA: ConfigStanzas.CAT_08,
315        cellular.Technology.HSUPA: ConfigStanzas.CAT_08,
316        cellular.Technology.HSDUPA: ConfigStanzas.CAT_08,
317        cellular.Technology.HSPA_PLUS: ConfigStanzas.CAT_10,
318        cellular.Technology.LTE: ConfigStanzas.LTE,
319    }
320    # TODO(byronk): remove these. Not used for LTE. Check for external deps
321    # http://wireless.agilent.com/rfcomms/refdocs/
322    #        gsmgprs/prog_synch_callstategprs.html#CHDDFBAJ
323    # NB:  We have elided a few states of the GSM state machine here.
324    CALL_STATUS_DATA_TO_STATUS_GSM_GPRS = {
325        'IDLE': cellular.UeGsmDataStatus.IDLE,
326        'ATTG': cellular.UeGsmDataStatus.ATTACHING,
327        'DET': cellular.UeGsmDataStatus.DETACHING,
328        'ATT': cellular.UeGsmDataStatus.ATTACHED,
329        'STAR': cellular.UeGsmDataStatus.ATTACHING,
330        'END': cellular.UeGsmDataStatus.PDP_DEACTIVATING,
331        'TRAN': cellular.UeGsmDataStatus.PDP_ACTIVE,
332        'PDPAG': cellular.UeGsmDataStatus.PDP_ACTIVATING,
333        'PDP': cellular.UeGsmDataStatus.PDP_ACTIVE,
334        'PDPD': cellular.UeGsmDataStatus.PDP_DEACTIVATING,
335        'DCON': cellular.UeGsmDataStatus.PDP_ACTIVE,
336        'SUSP': cellular.UeGsmDataStatus.IDLE,
337    }
338
339    # http://wireless.agilent.com/rfcomms/refdocs/
340    #        wcdma/wcdma_gen_call_proc_status.html#CJADGAHG
341    CALL_STATUS_DATA_TO_STATUS_WCDMA = {
342        'IDLE': cellular.UeGsmDataStatus.IDLE,
343        'ATTG': cellular.UeGsmDataStatus.ATTACHING,
344        'DET': cellular.UeGsmDataStatus.DETACHING,
345        'OFF': cellular.UeGsmDataStatus.NONE,
346        'PDPAG': cellular.UeGsmDataStatus.PDP_ACTIVATING,
347        'PDP': cellular.UeGsmDataStatus.PDP_ACTIVE,
348        'PDPD': cellular.UeGsmDataStatus.PDP_DEACTIVATING,
349    }
350
351    # http://wireless.agilent.com/rfcomms/refdocs/
352    #        cdma2k/cdma2000_hpib_call_status.html#CJABGBCF
353    CALL_STATUS_DATA_TO_STATUS_CDMA_2000 = {
354        'OFF': cellular.UeC2kDataStatus.OFF,
355        'DORM': cellular.UeC2kDataStatus.DORMANT,
356        'DCON': cellular.UeC2kDataStatus.DATA_CONNECTED,
357    }
358
359    # http://wireless.agilent.com/rfcomms/refdocs/
360    #        1xevdo/1xevdo_hpib_call_status.html#BABCGBCD
361    CALL_STATUS_DATA_TO_STATUS_EVDO = {
362        'CCL': cellular.UeEvdoDataStatus.CONNECTION_CLOSING,
363        'CNEG': cellular.UeEvdoDataStatus.CONNECTION_NEGOTIATE,
364        'CREQ': cellular.UeEvdoDataStatus.CONNECTION_REQUEST,
365        'DCON': cellular.UeEvdoDataStatus.DATA_CONNECTED,
366        'DORM': cellular.UeEvdoDataStatus.DORMANT,
367        'HAND': cellular.UeEvdoDataStatus.HANDOFF,
368        'IDLE': cellular.UeEvdoDataStatus.IDLE,
369        'PAG': cellular.UeEvdoDataStatus.PAGING,
370        'SCL': cellular.UeEvdoDataStatus.SESSION_CLOSING,
371        'SNEG': cellular.UeEvdoDataStatus.SESSION_NEGOTIATE,
372        'SOP': cellular.UeEvdoDataStatus.SESSION_OPEN,
373        'UREQ': cellular.UeEvdoDataStatus.UATI_REQUEST,
374    }
375
376    #lte status from BSE:STATus:ACELL? on the PXT
377    #OFF | IDLE | CON | REG |
378    #LOOP | REL | UNAV
379
380    CALL_STATUS_DATA_TO_STATUS_LTE = {
381        'OFF': cellular.UeLteDataStatus.OFF,
382        'IDLE': cellular.UeLteDataStatus.IDLE,
383        'CON': cellular.UeLteDataStatus.CONNECTED,
384        'REG': cellular.UeLteDataStatus.REGISTERED,
385        'LOOP': cellular.UeLteDataStatus.LOOPBACK,
386        'REL': cellular.UeLteDataStatus.RELEASE,
387        'UNAV': cellular.UeLteDataStatus.UNAVAILABLE,
388    }
389    FORMAT_TO_DATA_STATUS_TYPE = {
390        '"GSM/GPRS"': CALL_STATUS_DATA_TO_STATUS_GSM_GPRS,
391        '"WCDMA"': CALL_STATUS_DATA_TO_STATUS_WCDMA,
392        '"IS-2000/IS-95/AMPS"': CALL_STATUS_DATA_TO_STATUS_CDMA_2000,
393        '"IS-856"': CALL_STATUS_DATA_TO_STATUS_EVDO,
394        '"LTE"': CALL_STATUS_DATA_TO_STATUS_LTE,
395    }
396