source: ReferenceDesigns/w3_802.11/python/wlan_exp/node.py

Last change on this file was 6074, checked in by chunter, 3 months ago

added support for mcs/phy_mode updates to wlan_exp (Python)

File size: 129.4 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3------------------------------------------------------------------------------
4Mango 802.11 Reference Design Experiments Framework - Node Classes
5------------------------------------------------------------------------------
6License:   Copyright 2014-2017, Mango Communications. All rights reserved.
7           Distributed under the WARP license (http://warpproject.org/license)
8------------------------------------------------------------------------------
9
10"""
11import sys
12
13import wlan_exp.transport.node as node
14import wlan_exp.transport.exception as ex
15
16import wlan_exp.version as version
17import wlan_exp.defaults as defaults
18import wlan_exp.cmds as cmds
19import wlan_exp.device as wlan_device
20
21
22__all__ = ['WlanExpNode', 'WlanExpNodeFactory']
23
24# Fix to support Python 2.x and 3.x
25if sys.version[0]=="3": long=None
26
27# Node Parameter Identifiers
28#   Values here must match the C counterparts in *_node.h
29#
30# If additional hardware parameters are needed for sub-classes of WlanExpNode,
31# please make sure that the values of these hardware parameters are not reused.
32#
33NODE_PARAM_ID_WLAN_EXP_VERSION                  = 5
34NODE_PARAM_ID_WLAN_SCHEDULER_RESOLUTION         = 6
35NODE_PARAM_ID_WLAN_MAC_ADDR                     = 7
36NODE_PARAM_ID_WLAN_MAX_TX_POWER_DBM             = 8
37NODE_PARAM_ID_WLAN_MIN_TX_POWER_DBM             = 9
38NODE_PARAM_WLAN_CPU_LOW_COMPILATION_DATE        = 10
39NODE_PARAM_WLAN_CPU_LOW_COMPILATION_TIME        = 11
40NODE_PARAM_WLAN_CPU_HIGH_COMPILATION_DATE       = 12
41NODE_PARAM_WLAN_CPU_HIGH_COMPILATION_TIME       = 13
42
43
44class WlanExpNode(node.WarpNode, wlan_device.WlanDevice):
45    """Class for 802.11 Reference Design Experiments Framwork node.
46
47    Args:
48        network_config (transport.NetworkConfiguration) : Network configuration of the node
49
50
51    Attributes:
52        node_type (int): Unique type of the node
53        node_id (int): Unique identification for this node
54        name (str): User specified name for this node (supplied by user scripts)
55        description (str): String description of this node (auto-generated)
56        serial_number (int): Node's serial number, read from EEPROM on hardware
57        fpga_dna (int): Node's FPGA'a unique identification (on select hardware)
58        hw_ver (int): WARP hardware version of this node
59        transport (transport.Transport): Node's transport object
60        transport_broadcast (transport.Transport): Node's broadcast transport object
61
62        device_type (int): Unique type of the ``WlanDevice`` (inherited from ``WlanDevice``)
63        wlan_mac_address (int): Wireless MAC address of the node (inherited from ``WlanDevice``)
64        ht_capable (bool): Indicates if device has PHY capable of HT (802.11n) rates
65            (inherited from ``WlanDevice``)
66
67        scheduler_resolution (int): Minimum resolution (in us) of the scheduler.  This
68            is also the minimum time between LTGs.
69        log_max_size (int): Maximum size of event log (in bytes)
70        log_total_bytes_read (int): Number of bytes read from the event log
71        log_num_wraps (int): Number of times the event log has wrapped
72        log_next_read_index (int): Index in to event log of next read
73        wlan_exp_ver_major (int): ``wlan_exp`` Major version running on this node
74        wlan_exp_ver_minor (int): ``wlan_exp`` Minor version running on this node
75        wlan_exp_ver_revision (int): ``wlan_exp`` Revision version running on this node
76        max_tx_power_dbm(int): Maximum transmit power of the node (in dBm)
77        min_tx_power_dbm(int): Minimum transmit power of the node (in dBm)
78
79    """
80
81    scheduler_resolution               = None
82
83    log_max_size                       = None
84    log_total_bytes_read               = None
85    log_num_wraps                      = None
86    log_next_read_index                = None
87
88    wlan_exp_ver_major                 = None
89    wlan_exp_ver_minor                 = None
90    wlan_exp_ver_revision              = None
91
92    max_tx_power_dbm                   = None
93    min_tx_power_dbm                   = None
94
95    def __init__(self, network_config=None):
96        super(WlanExpNode, self).__init__(network_config)
97
98        (self.wlan_exp_ver_major, self.wlan_exp_ver_minor,
99                self.wlan_exp_ver_revision) = version.wlan_exp_ver()
100
101        self.scheduler_resolution           = 1
102
103        self.log_total_bytes_read           = 0
104        self.log_num_wraps                  = 0
105        self.log_next_read_index            = 0
106
107        # As of v1.5 all 802.11 Ref Design nodes are HT capable
108        self.ht_capable = True
109
110    #-------------------------------------------------------------------------
111    # Node Commands
112    #-------------------------------------------------------------------------
113
114
115    #--------------------------------------------
116    # Log Commands
117    #--------------------------------------------
118    def log_configure(self, log_enable=None, log_wrap_enable=None,
119                            log_full_payloads=None, log_txrx_mpdu=None, 
120                            log_txrx_ctrl=None):
121        """Configure log with the given flags.
122
123        By default all attributes are set to None.  Only attributes that
124        are given values will be updated on the node.  If an attribute is
125        not specified, then that attribute will retain the same value on
126        the node.
127
128        Args:
129            log_enable (bool):        Enable the event log
130                (Default value on Node: TRUE)
131            log_wrap_enable (bool):   Enable event log wrapping
132                (Default value on Node: FALSE)
133            log_full_payloads (bool): Record full Tx/Rx payloads in event log
134                (Default value on Node: FALSE)
135            log_txrx_mpdu (bool):     Enable Tx/Rx log entries for MPDU frames
136                (Default value on Node: TRUE)
137            log_txrx_ctrl (bool):     Enable Tx/Rx log entries for CTRL frames
138                (Default value on Node: TRUE)
139        """
140        self.send_cmd(cmds.LogConfigure(log_enable, log_wrap_enable,
141                                        log_full_payloads, log_txrx_mpdu, 
142                                        log_txrx_ctrl))
143
144
145    def log_get(self, size, offset=0, max_req_size=2**23):
146        """Low level method to retrieve log data from a wlan_exp node.
147
148        Args:
149            size (int):                   Number of bytes to read from the log
150            offset (int, optional):       Starting byte to read from the log
151            max_req_size (int, optional): Max request size that the transport
152                will fragment the request into.
153
154        Returns:
155            buffer (transport.Buffer): 
156                Data from the log corresponding to the input parameters
157
158        There is no guarentee that this will return data aligned to event
159        boundaries.  Use ``log_get_indexes()`` to get event aligned boundaries.
160
161        Log reads are not destructive. Log entries will only be destroyed by a
162        log reset or if the log wraps.
163
164        During a given ``log_get()`` command, the ETH_B Ethernet interface of
165        the node will not be able to respond to any other Ethernet packets
166        that are sent to the node.  This could cause the node to drop
167        incoming wlan_exp packets from other wlan_exp instances accessing
168        the node. Therefore, for large requests, having a smaller
169        ``max_req_size`` will allow the transport to fragement the command and
170        allow the node to be responsive to multiple hosts.
171
172        Some basic analysis shows that fragment sizes of 2**23 (8 MB)
173        add about 2% overhead to the receive time and each command takes less
174        than 1 second (~0.9 sec), which is the default transport timeout.
175        """
176        cmd_size = size
177        log_size = self.log_get_size()
178
179        # C code uses size=0xFFFFFFFF (CMD_PARAM_LOG_GET_ALL_ENTRIES) as magic value to return
180        #  all valid data from offset to end of log data array
181        if (cmd_size != cmds.CMD_PARAM_LOG_GET_ALL_ENTRIES) and ((size + offset) > log_size):
182            cmd_size = log_size - offset
183
184            print("WARNING:  Trying to get {0} bytes from the log at offset {1}".format(size, offset))
185            print("    while log only has {0} bytes.".format(log_size))
186            print("    Truncating command to {0} bytes at offset {1}.".format(cmd_size, offset))
187
188        return self.send_cmd(cmds.LogGetEvents(cmd_size, offset), max_req_size=max_req_size)
189
190
191    def log_get_all_new(self, log_tail_pad=500, max_req_size=2**23):
192        """Get all "new" entries in the log.
193       
194        In order to make reading log data easier, the ``log_get_all_new()``
195        command was created.  It utilizes the ``log_get_indexex()`` and
196        ``log_get()`` commmands to understand the state of the log and to
197        retrieve data from the log.  The Python node object also maintains
198        internal state about what log data has been read from the node to
199        determine if there is "new" data in the log.  Since this state data is
200        maintained in Python and not on the node itself, it is possible for
201        multiple host scripts to read log data using ``log_get_all_new()``. The
202        internal state maintained for ``log_get_all_new()`` is only reset when
203        using the node reset method (i.e. ``node.reset(log=True)``).
204       
205        When a node is adding entries to its log, it will allocate the
206        memory for an entry from the log and then fill in the contents.  This
207        means at any given time, the last log entry may have incomplete
208        information.  Currently, the log maintains the index where the next
209        log entry will be recorded (i.e. the ``next_index``), which does not
210        provide any state about whether the last log entry has been completed.
211        Therefore, in order to not retrieve incomplete information,
212        ``log_get_all_new()`` has an argument ``log_tail_pad=N`` to not read
213        the last N "new" bytes of log data. 
214       
215        Unfortunately, this means that using a ``log_tail_pad`` other than
216        zero will result in return data that is not aligned to a log entry
217        boundary.  This should not be an issue if the goal is to periodically
218        read the log data from the node and store it in a file (as seen in
219        the ``log_capture_continuous.py`` example).  However, this can be an
220        issue if trying to periodically read the log and process the log data
221        directly.  In this case, ``log_tail_pad`` must be zero and the code
222        has to assume the last entry is invalid.  This has not come up very
223        much, but please let us know if this is an issue via the forums.       
224
225        Args:
226            log_tail_pad (int, optional): Number of bytes from the current end
227                of the "new" entries that will not be read during the call. 
228                This is to deal with the case where the node is in the process
229                of modifying the last log entry so it my contain incomplete
230                data and should not be read.
231
232        Returns:
233            buffer (transport.Buffer):
234                Data from the log that contains all entries since the last
235                time the log was read.
236        """
237        import wlan_exp.transport.message as message
238
239        return_val = message.Buffer()
240
241        # Check if the log is full to interpret the indexes correctly
242        if (self.log_is_full()):
243            log_size  = self.log_get_size()
244            read_size = log_size - self.log_next_read_index - log_tail_pad
245
246            # Read the data from the node
247            if (read_size > 0):
248                return_val = self.log_get(offset=self.log_next_read_index,
249                                          size=read_size,
250                                          max_req_size=max_req_size)
251
252                # Only increment index by how much was actually read
253                read_size  = return_val.get_buffer_size()
254
255                if (read_size > 0):
256                    self.log_next_read_index += read_size
257                else:
258                    print("WARNING:  Not able to read data from node.")
259            else:
260                print("WARNING:  No new data on node.")
261
262        # Log is not full
263        else:
264            (next_index, _, num_wraps) = self.log_get_indexes()
265
266            if ((self.log_next_read_index == 0) and (self.log_num_wraps == 0)):
267                # This is the first read of the log by this python object
268                if (num_wraps != 0):
269                    # Need to advance the num_wraps to the current num_wraps so
270                    # that the code does not get into a bad state with log reading.
271                    msg  = "\nWARNING:  On first read, the log on the node has already wrapped.\n"
272                    msg += "    Skipping the first {0} wraps of log data.\n".format(num_wraps)
273                    print(msg)
274                    self.log_num_wraps = num_wraps
275
276            # Check if log has wrapped
277            if (num_wraps == self.log_num_wraps):
278                # Since log has not wrapped, then read to the (next_index - log_tail_pad)
279                if (next_index > (self.log_next_read_index + log_tail_pad)):
280
281                    return_val = self.log_get(offset=self.log_next_read_index,
282                                              size=(next_index - self.log_next_read_index - log_tail_pad),
283                                              max_req_size=max_req_size)
284
285                    # Only increment index by how much was actually read
286                    read_size  = return_val.get_buffer_size()
287                    if (read_size > 0):
288                        self.log_next_read_index += read_size
289                    else:
290                        print("WARNING:  Not able to read data from node.")
291                else:
292                    print("WARNING:  No new data on node.")
293            else:
294                # Log has wrapped.  Get all the entries on the old wrap
295                if (next_index != 0):
296
297                    return_val = self.log_get(offset=self.log_next_read_index,
298                                              size=cmds.CMD_PARAM_LOG_GET_ALL_ENTRIES,
299                                              max_req_size=max_req_size)
300
301                    # The amount of data returned from the node should not be zero
302                    read_size  = return_val.get_buffer_size()
303                    if (read_size > 0):
304                        self.log_next_read_index = 0
305                        self.log_num_wraps       = num_wraps
306                    else:
307                        print("WARNING:  Not able to read data from node.")
308                else:
309                    print("WARNING:  No new data on node.")
310
311        return return_val
312
313
314    def log_get_size(self):
315        """Get the size of the node's current log (in bytes).
316
317        Returns:
318            num_bytes (int): Number of bytes in the log
319        """
320        (capacity, size)  = self.send_cmd(cmds.LogGetCapacity())
321
322        # Check the maximum size of the log and update the node state
323        if self.log_max_size is None:
324            self.log_max_size = capacity
325        else:
326            if (self.log_max_size != capacity):
327                msg  = "EVENT LOG WARNING:  Log capacity changed.\n"
328                msg += "    Went from {0} bytes to ".format(self.log_max_size)
329                msg += "{0} bytes.\n".format(capacity)
330                print(msg)
331                self.log_max_size = capacity
332
333        return size
334
335
336    def log_get_capacity(self):
337        """Get the total capacity of the node's log memory allocation (in bytes).
338
339        Returns:
340            capacity (int): Number of byte allocated for the log.
341        """
342        return self.log_max_size
343
344
345    def log_get_indexes(self):
346        """Get the indexes that describe the state of the event log.
347
348        Returns:
349            indexes (tuple):
350           
351                #. oldest_index (int): Log index of the oldest event in the log
352                #. next_index (int):   Log index where the next event will be recorded
353                #. num_wraps (int):    Number of times the log has wrapped
354       
355        """
356        (next_index, oldest_index, num_wraps, _) = self.send_cmd(cmds.LogGetStatus())
357
358        # Check that the log is in a good state
359        if ((num_wraps < self.log_num_wraps) or
360            ((num_wraps == self.log_num_wraps) and
361             (next_index < self.log_next_read_index))):
362            msg  = "\n!!! Event Log Corrupted.  Please reset the log. !!!\n"
363            print(msg)
364
365        return (next_index, oldest_index, num_wraps)
366
367
368    def log_get_flags(self):
369        """Get the flags that describe the event log configuration.
370
371        Returns:
372            flags (int): Integer describing the configuration of the event log.
373           
374                * ``0x0001`` - Logging enabled
375                * ``0x0002`` - Log wrapping enabled
376                * ``0x0004`` - Full payload logging enabled
377                * ``0x0008`` - Tx/Rx log entries for MPDU frames enabled
378                * ``0x0010`` - Tx/Rx log entries for CTRL frames enabled
379               
380        """
381        (_, _, _, flags) = self.send_cmd(cmds.LogGetStatus())
382
383        return flags
384
385
386    def log_is_full(self):
387        """Return whether the log is full or not.
388
389        Returns:
390            status (bool): True if the log is full; False if the log is not full.
391        """
392        (next_index, oldest_index, num_wraps, flags) = self.send_cmd(cmds.LogGetStatus())
393
394        if (((flags & cmds.CMD_PARAM_LOG_CONFIG_FLAG_WRAP) != cmds.CMD_PARAM_LOG_CONFIG_FLAG_WRAP) and
395            ((next_index == 0) and (oldest_index == 0) and (num_wraps == (self.log_num_wraps + 1)))):
396            return True
397        else:
398            return False
399
400
401    def log_write_exp_info(self, info_type, message=None):
402        """Write the experiment information provided to the log.
403
404        Args:
405            info_type (int): Type of the experiment info.  This is an arbitrary
406                16 bit number chosen by the experimentor
407            message (int, str, bytes, optional): Information to be placed in
408                the event log.
409
410        Message must be able to be converted to bytearray with 'UTF-8' format.
411        """
412        self.send_cmd(cmds.LogAddExpInfoEntry(info_type, message))
413
414
415    def log_write_time(self, time_id=None):
416        """Adds the current time in microseconds to the log.
417
418        Args:
419            time_id (int, optional):  User providied identifier to be used for
420                the TIME_INFO log entry.  If none is provided, a random number
421                will be inserted.
422        """
423        return self.send_cmd(cmds.NodeProcTime(cmds.CMD_PARAM_TIME_ADD_TO_LOG, cmds.CMD_PARAM_RSVD_TIME, time_id))
424
425
426
427    #--------------------------------------------
428    # Counts Commands
429    #--------------------------------------------
430    def counts_get_txrx(self, *args, **kwargs):
431        """DEPRECATED: Old name for current get_txrx_counts method
432        """       
433        print("WARNING: the counts_get_txrx() method has been renamed get_txrx_counts!\n")
434        print(" The old method will be removed in a future release. Please update your script\n")
435        print(" to use get_txrx_counts\n")
436        return self.get_txrx_counts(*args, **kwargs)
437
438    def get_txrx_counts(self, device_list=None, return_zeroed_counts_if_none=False):
439        """Get the Tx/Rx counts data structurs from the node.
440
441        Args:
442            device_list (list of WlanExpNode, WlanExpNode, WlanDevice, optional):
443                List of devices for which to get counts.  See note below for
444                more information.
445            return_zeroed_counts_if_none(bool, optional):  If no counts exist
446                on the node for the specified device(s), return a zeroed counts
447                dictionary with proper timestamps instead of None.
448
449        Returns:
450            counts_dictionary (list of TxRxCounts, TxRxCounts):
451                TxRxCounts() for the device(s) specified.
452
453
454        The TxRxCounts() structure returned by this method can be accessed like
455        a dictionary and has the following fields:
456       
457            +-----------------------------+-----------------------------------------------------------------------------------------------------+
458            | Field                       | Description                                                                                         |
459            +=============================+=====================================================================================================+
460            | retrieval_timestamp         |  Value of System Time in microseconds when structure retrieved from the node                        |
461            +-----------------------------+-----------------------------------------------------------------------------------------------------+
462            | mac_addr                    |  MAC address of remote node whose statics are recorded here                                         |
463            +-----------------------------+-----------------------------------------------------------------------------------------------------+
464            | data_num_rx_bytes           |  Total number of bytes received in DATA packets from remote node (only non-duplicates)              |
465            +-----------------------------+-----------------------------------------------------------------------------------------------------+
466            | data_num_rx_bytes_total     |  Total number of bytes received in DATA packets from remote node (including duplicates)             |
467            +-----------------------------+-----------------------------------------------------------------------------------------------------+
468            | data_num_tx_bytes_success   |  Total number of bytes successfully transmitted in DATA packets to remote node                      |
469            +-----------------------------+-----------------------------------------------------------------------------------------------------+
470            | data_num_tx_bytes_total     |  Total number of bytes transmitted (successfully or not) in DATA packets to remote node             |
471            +-----------------------------+-----------------------------------------------------------------------------------------------------+
472            | data_num_rx_packets         |  Total number of DATA packets received from remote node                                             |
473            +-----------------------------+-----------------------------------------------------------------------------------------------------+
474            | data_num_tx_packets_success |  Total number of DATA packets successfully transmitted to remote node                               |
475            +-----------------------------+-----------------------------------------------------------------------------------------------------+
476            | data_num_tx_packets_total   |  Total number of DATA packets transmitted (successfully or not) to remote node                      |
477            +-----------------------------+-----------------------------------------------------------------------------------------------------+
478            | data_num_tx_attempts        |  Total number of low-level attempts of DATA packets to remote node (includes re-transmissions)      |
479            +-----------------------------+-----------------------------------------------------------------------------------------------------+
480            | mgmt_num_rx_bytes           |  Total number of bytes received in management packets from remote node (only non-duplicates)        |
481            +-----------------------------+-----------------------------------------------------------------------------------------------------+
482            | mgmt_num_rx_bytes_total     |  Total number of bytes received in management packets from remote node (including duplicates)       |           
483            +-----------------------------+-----------------------------------------------------------------------------------------------------+
484            | mgmt_num_tx_bytes_success   |  Total number of bytes successfully transmitted in management packets to remote node                |
485            +-----------------------------+-----------------------------------------------------------------------------------------------------+
486            | mgmt_num_tx_bytes_total     |  Total number of bytes transmitted (successfully or not) in management packets to remote node       |
487            +-----------------------------+-----------------------------------------------------------------------------------------------------+
488            | mgmt_num_rx_packets         |  Total number of management packets received from remote node (only non-duplicates)                 |
489            +-----------------------------+-----------------------------------------------------------------------------------------------------+
490            | mgmt_num_rx_packets_total   |  Total number of management packets received from remote node (including duplicates)                |
491            +-----------------------------+-----------------------------------------------------------------------------------------------------+
492            | mgmt_num_tx_packets_success |  Total number of management packets successfully transmitted to remote node                         |
493            +-----------------------------+-----------------------------------------------------------------------------------------------------+
494            | mgmt_num_tx_packets_total   |  Total number of management packets transmitted (successfully or not) to remote node                |
495            +-----------------------------+-----------------------------------------------------------------------------------------------------+
496            | mgmt_num_tx_attempts        |  Total number of low-level attempts of management packets to remote node (includes re-transmissions)|
497            +-----------------------------+-----------------------------------------------------------------------------------------------------+
498
499
500        If the ``device_list`` is a single device, then a single dictionary or
501        None is returned.  If the ``device_list`` is a list of devices, then a
502        list of dictionaries will be returned in the same order as the devices
503        in the list.  If any of the counts are not there, None will be inserted
504        in the list.  If the ``device_list`` is not specified, then all the
505        counts on the node will be returned.
506        """
507        ret_val = []
508        if not device_list is None:
509            if (type(device_list) is list):
510                for device in device_list:
511                    counts = self.send_cmd(cmds.CountsGetTxRx(device, return_zeroed_counts_if_none))
512                    if (len(counts) == 1):
513                        ret_val.append(counts)
514                    else:
515                        ret_val.append(None)
516            else:
517                ret_val = self.send_cmd(cmds.CountsGetTxRx(device_list, return_zeroed_counts_if_none))
518                if (len(ret_val) == 1):
519                    ret_val = ret_val[0]
520                else:
521                    ret_val = None
522        else:
523            ret_val = self.send_cmd(cmds.CountsGetTxRx())
524
525        return ret_val
526
527
528
529    #--------------------------------------------
530    # Local Traffic Generation (LTG) Commands
531    #--------------------------------------------
532    def ltg_configure(self, traffic_flow, auto_start=False):
533        """Configure the node for the given traffic flow.
534
535        Args:
536            traffic_flow (ltg.FlowConfig):  FlowConfig (or subclass of
537                FlowConfig) that describes the parameters of the LTG.
538            auto_start (bool, optional): Automatically start the LTG or wait
539                until it is explictly started.
540
541        Returns:
542            ID (int):  Identifier for the LTG flow
543
544        The ``trafic_flow`` argument configures the behavior of the LTG,
545        including the traffic's destination, packet payloads, and packet
546        generation timing. The reference code implements three flow
547        configurations by default:
548
549         - ``FlowConfigCBR()``: a constant bit rate (CBR) source targeted to a
550           specifc destination address. Packet lengths and the packet
551           creation interval are constant.
552         - ``FlowConfigAllAssocCBR()``: a CBR source that targets traffic to
553           all nodes in the station info list.
554         - ``FlowConfigRandomRandom()``: a source that targets traffic to a
555           specific destination address, where each packet has a random
556           length and packets are created at random intervals.
557
558        Refer to the :doc:`ltg` documentation for details on these flow configs
559        and the underlying classes that can be used to build custom flow
560        configurations.
561
562        Examples:
563            The code below illustrates a few LTG configuration examples. ``n1``
564            and ``n2`` here are a wlan_exp node objects and the LTG traffic in
565            each flow will go from ``n1`` to ``n2``.
566            ::
567
568                import wlan_exp.ltg as ltg
569               
570                # Configure a CBR LTG addressed to a single node
571                ltg_id = n1.ltg_configure(ltg.FlowConfigCBR(dest_addr=n2.wlan_mac_address, payload_length=40, interval=0.01), auto_start=True)
572
573                # Configure a backlogged traffic source with constant packet size
574                ltg_id = n1.ltg_configure(ltg.FlowConfigCBR(dest_addr=n2.wlan_mac_address, payload_length=1000, interval=0), auto_start=True)
575
576                # Configure a random traffic source
577                ltg_id = n1.ltg_configure(ltg.FlowConfigRandomRandom(dest_addr=n2.wlan_mac_address,
578                                               min_payload_length=100,
579                                               max_payload_length=500,
580                                               min_interval=0,
581                                               max_interval=0.1),
582                                            auto_start=True)
583       
584        """
585        traffic_flow.enforce_min_resolution(self.scheduler_resolution)
586        return self.send_cmd(cmds.LTGConfigure(traffic_flow, auto_start))
587
588
589    def ltg_get_status(self, ltg_id_list):
590        """Get the status of the LTG flows.
591
592        Args:
593            ltg_id_list (list of int, int): List of LTG flow identifiers or
594                single LTG flow identifier
595
596        If the ltg_id_list is a single ID, then a single status tuple is
597        returned.  If the ltg_id_list is a list of IDs, then a list of status
598        tuples will be returned in the same order as the IDs in the list.
599
600        Returns:
601            status (tuple):
602           
603                #. valid (bool): Is the LTG ID valid? (True/False)
604                #. running (bool): Is the LTG currently running? (True/False)
605                #. start_timestamp (int): Timestamp when the LTG started
606                #. stop_timestamp (int): Timestamp when the LTG stopped
607           
608        """
609        ret_val = []
610        if (type(ltg_id_list) is list):
611            for ltg_id in ltg_id_list:
612                result = self.send_cmd(cmds.LTGStatus(ltg_id))
613                if not result[0]:
614                    self._print_ltg_error(cmds.CMD_PARAM_LTG_ERROR, "get status for LTG {0}".format(ltg_id))
615                ret_val.append(result)
616        else:
617            result = self.send_cmd(cmds.LTGStatus(ltg_id_list))
618            if not result[0]:
619                self._print_ltg_error(cmds.CMD_PARAM_LTG_ERROR, "get status for LTG {0}".format(ltg_id_list))
620            ret_val.append(result)
621
622        return ret_val
623
624
625    def ltg_remove(self, ltg_id_list):
626        """Remove the LTG flows.
627
628        Args:
629            ltg_id_list (list of int, int): List of LTG flow identifiers or
630                single LTG flow identifier
631        """
632        if (type(ltg_id_list) is list):
633            for ltg_id in ltg_id_list:
634                status = self.send_cmd(cmds.LTGRemove(ltg_id))
635                self._print_ltg_error(status, "remove LTG {0}".format(ltg_id))
636        else:
637            status = self.send_cmd(cmds.LTGRemove(ltg_id_list))
638            self._print_ltg_error(status, "remove LTG {0}".format(ltg_id_list))
639
640
641    def ltg_start(self, ltg_id_list):
642        """Start the LTG flow.
643
644        Args:
645            ltg_id_list (list of int, int): List of LTG flow identifiers or
646                single LTG flow identifier
647        """
648        if (type(ltg_id_list) is list):
649            for ltg_id in ltg_id_list:
650                status = self.send_cmd(cmds.LTGStart(ltg_id))
651                self._print_ltg_error(status, "start LTG {0}".format(ltg_id))
652        else:
653            status = self.send_cmd(cmds.LTGStart(ltg_id_list))
654            self._print_ltg_error(status, "start LTG {0}".format(ltg_id_list))
655
656
657    def ltg_stop(self, ltg_id_list):
658        """Stop the LTG flow to the given nodes.
659
660        Args:
661            ltg_id_list (list of int, int): List of LTG flow identifiers or
662                single LTG flow identifier
663        """
664        if (type(ltg_id_list) is list):
665            for ltg_id in ltg_id_list:
666                status = self.send_cmd(cmds.LTGStop(ltg_id))
667                self._print_ltg_error(status, "stop LTG {0}".format(ltg_id))
668        else:
669            status = self.send_cmd(cmds.LTGStop(ltg_id_list))
670            self._print_ltg_error(status, "stop LTG {0}".format(ltg_id_list))
671
672
673    def ltg_remove_all(self):
674        """Stops and removes all LTG flows on the node."""
675        status = self.send_cmd(cmds.LTGRemove())
676        self._print_ltg_error(status, "remove all LTGs")
677
678
679    def ltg_start_all(self):
680        """Start all LTG flows on the node."""
681        status = self.send_cmd(cmds.LTGStart())
682        self._print_ltg_error(status, "start all LTGs")
683
684
685    def ltg_stop_all(self):
686        """Stop all LTG flows on the node."""
687        status = self.send_cmd(cmds.LTGStop())
688        self._print_ltg_error(status, "stop all LTGs")
689
690
691    def _print_ltg_error(self, status, msg):
692        """Print an LTG error message.
693
694        Args:
695            status (int): Status of LTG command
696            msg (str):    Message to print as part of LTG Error
697        """
698        if (status == cmds.CMD_PARAM_LTG_ERROR):
699            print("LTG ERROR: Could not {0} on '{1}'".format(msg, self.description))
700
701
702
703    #--------------------------------------------
704    # Configure Node Attribute Commands
705    #--------------------------------------------
706    def reset_all(self):
707        """Resets all portions of a node.
708
709        This includes:
710       
711          * Log
712          * Counts
713          * Local Traffic Generators (LTGs)
714          * Queues
715          * BSS (i.e. all network state)
716          * Observed Networks
717
718        See the reset() command for a list of all portions of the node that
719        will be reset.
720        """
721        status = self.reset(log=True,
722                            txrx_counts=True,
723                            ltg=True,
724                            queue_data=True,
725                            bss=True,
726                            network_list=True)
727
728        if (status == cmds.CMD_PARAM_LTG_ERROR):
729            print("LTG ERROR: Could not stop all LTGs on '{0}'".format(self.description))
730
731
732    def reset(self, log=False, txrx_counts=False, ltg=False, queue_data=False,
733                    bss=False, network_list=False ):
734        """Resets the state of node depending on the attributes.
735
736        Args:
737            log (bool):          Reset the log data.  This will reset both the
738                log data on the node as well as the local variables used to
739                track log reads for ``log_get_all_new()``.  This will
740                not alter any log settings set by ``log_configure()``.
741            txrx_counts (bool):  Reset the TX/RX Counts.  This will zero out
742                all of the packet / byte counts as well as the
743                ``latest_txrx_timestamp`` for all nodes in the station info
744                list.  It will also remove all promiscuous counts.
745            ltg (bool):          Remove all LTGs.  This will stop and remove
746                any LTGs that are on the node.
747            queue_data (bool):   Purge all TX queue data.  This will discard
748                all currently enqueued packets awaiting transmission at the
749                time the command is received.  This will not discard packets
750                already submitted to the lower-level MAC for transmission.
751                Also, this will not stop additional packets from sources
752                such as LTGs from being enqueued.
753            bss (bool):          Reset network state.  This will nullify
754                the node's active BSS and removes all entries from the
755                station info list.  However, it will not remove the BSS from
756                the network list.  This will produce the following OTA
757                transmissions:
758               
759                  * For AP, a deauthentication frame to each associated station
760                  * For STA, a disassociation frame to its AP
761                  * For IBSS, nothing.
762                   
763            network_list (bool): Remove all BSS entries from the network list,
764                excluding the BSS entry for the node's current network (if any)           
765        """
766        flags = 0;
767
768        if log:
769            flags += cmds.CMD_PARAM_NODE_RESET_FLAG_LOG
770            self.log_total_bytes_read = 0
771            self.log_num_wraps        = 0
772            self.log_next_read_index  = 0
773
774        if txrx_counts:      flags += cmds.CMD_PARAM_NODE_RESET_FLAG_TXRX_COUNTS
775        if ltg:              flags += cmds.CMD_PARAM_NODE_RESET_FLAG_LTG
776        if queue_data:       flags += cmds.CMD_PARAM_NODE_RESET_FLAG_TX_DATA_QUEUE
777        if bss:              flags += cmds.CMD_PARAM_NODE_RESET_FLAG_BSS
778        if network_list:     flags += cmds.CMD_PARAM_NODE_RESET_FLAG_NETWORK_LIST
779
780        # Send the reset command
781        self.send_cmd(cmds.NodeResetState(flags))
782
783
784    def get_wlan_mac_address(self):
785        """Get the WLAN MAC Address of the node.
786
787        Returns:
788            MAC Address (int): 
789                Wireless Medium Access Control (MAC) address of the node.
790        """
791        addr = self.send_cmd(cmds.NodeProcWLANMACAddr(cmds.CMD_PARAM_READ))
792
793        if (addr != self.wlan_mac_address):
794            import wlan_exp.util as util
795
796            msg  = "WARNING:  WLAN MAC address mismatch.\n"
797            msg += "    Received MAC Address:  {0}".format(util.mac_addr_to_str(addr))
798            msg += "    Original MAC Address:  {0}".format(util.mac_addr_to_str(self.wlan_mac_address))
799            print(msg)
800
801        return addr
802
803
804    def set_mac_time(self, time, time_id=None):
805        """Sets the MAC time on the node.
806
807        Args:
808            time (int):              Time to which the node's MAC time will
809                be set (int in microseconds)
810            time_id (int, optional): Identifier used as part of the TIME_INFO
811                log entry created by this command.  If not specified, then a
812                random number will be used.
813        """
814        if type(time) not in [int, long]:
815            raise AttributeError("Time must be expressed in int microseconds")
816       
817        self.send_cmd(cmds.NodeProcTime(cmds.CMD_PARAM_WRITE, time, time_id))
818
819
820    def get_mac_time(self):
821        """Gets the MAC time from the node.
822
823        Returns:
824            time (int):  Timestamp of the MAC time of the node in int microseconds
825        """
826        node_time = self.send_cmd(cmds.NodeProcTime(cmds.CMD_PARAM_READ, cmds.CMD_PARAM_RSVD_TIME))
827
828        return node_time[0]
829
830
831    def get_system_time(self):
832        """Gets the system time from the node.
833
834        Returns:
835            Time (int):  Timestamp of the System time of the node in int microseconds
836        """
837        node_time = self.send_cmd(cmds.NodeProcTime(cmds.CMD_PARAM_READ, cmds.CMD_PARAM_RSVD_TIME))
838
839        return node_time[1]
840
841
842    def enable_beacon_mac_time_update(self, enable):
843        """Enable / Disable MAC time update from received beacons.
844       
845        Args:
846            enable (bool):  ``True`` enables and ``False`` disables MAC time
847                updates from received beacons
848        """
849        self.send_cmd(cmds.NodeConfigure(beacon_mac_time_update=enable))
850       
851    def enable_ethernet_portal(self, enable):
852        """Enable / Disable Ethernet Portal opertion of the node. When the portal is enabled the node
853        bridges its ETH A Ethernet connection to the wireless medium. When the portal is disabled the node
854        ignores all packets on ETH A and will not send any Ethernet transmissions on ETH A.
855
856        The Ethernet portal is enabled by default. Disabling the portal with this command is "sticky" - the
857        portal will remain disabled until explicity re-enabled or until the node is rebooted. Changes to BSS
858        state will not affect whether the Ethernet portal is enabled or disabled.
859       
860        Args:
861            enable (bool):  ``True`` enables and ``False`` disables Ethernet
862                portal functionality
863        """
864        self.send_cmd(cmds.NodeConfigure(portal_enable=enable))
865
866
867    def set_radio_channel(self, channel):
868        """Re-tune the node's RF interfaces to a new center frequency.
869       
870        This method directly controls the RF hardware. This can be disruptive
871        to MAC operation if the node is currently associated with other nodes
872        in a BSS which manages its own channel state.
873       
874        This method will immediately change the center frequency of the node's
875        RF interfaces.  As a result this method can only be safely used on a
876        node which is not currently a member of a BSS. To change the center
877        frequency of nodes participating in a BSS use the
878        ``node.configure_bss(channel=N)`` method on every node in the BSS.
879       
880        Args:
881            channel (int):  Channel index for new center frequency. Must be a
882                valid channel index.  See ``wlan_channels`` in util.py for the
883                list of supported channel indexes.
884        """
885        import wlan_exp.util as util
886       
887        if channel in util.wlan_channels:
888            self.send_cmd(cmds.NodeProcChannel(cmds.CMD_PARAM_WRITE, channel))
889        else:
890            raise AttributeError("Channel must be in util.py wlan_channels")
891
892
893    def set_low_to_high_rx_filter(self, mac_header=None, fcs=None):
894        """Configures the filter that controls which received packets are
895        passed from CPU Low to CPU High.
896       
897        The filter will always pass received packets where the destination
898        address matches the node's MAC address. The filter can be configured
899        to drop or pass other packets. This filter effectively controls which
900        packets are written to the node's log.
901
902        Args:
903            mac_header (str, optional): MAC header filter.  Values can be:
904               
905               - ``'MPDU_TO_ME'`` -- Pass all unicast-to-me or multicast data or management packet
906               - ``'ALL_MPDU'``   -- Pass all data and management packets; no destination address filter
907               - ``'ALL'``        -- Pass all packets; no packet type or address filters
908           
909            fcs (str, optional): FCS status filter.  Values can be:
910
911               - ``'GOOD'``       -- Pass only packets with good checksum result
912               - ``'ALL'``        -- Pass packets with any checksum result
913
914        At boot the filter defaults to ``mac_header='ALL'`` and ``fcs='ALL'``.
915        """
916        self.send_cmd(cmds.NodeSetLowToHighFilter(cmds.CMD_PARAM_WRITE, mac_header, fcs))
917
918
919    #------------------------
920    # Tx Rate commands
921
922    def set_tx_rate_data(self, mcs, phy_mode, device_list=None, update_default_unicast=None, update_default_multicast=None):
923        """Sets the packet transmit rate (mcs, phy_mode) for data frames.
924
925        Transmit parameters are maintained on a per-MAC address basis.
926        The ``device_list`` argument can be used to select particular devices
927        for which the Tx parameter update applies. The ``update_default_x``
928        arguments can be used to specify that the provided power should be
929        used for any future additions to the node's device list.
930
931        Args:
932            mcs (int): Modulation and coding scheme (MCS) index (in [0 .. 7])
933            phy_mode (str, int): PHY mode.  Must be one of:
934
935                * ``'NONHT'``: Use 802.11 (a/g) rates
936                * ``'HTMF'``: Use 802.11 (n) rates
937
938            device_list (list of WlanExpNode / WlanDevice
939             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
940                List of 802.11 devices or single 802.11 device for which to set the
941                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
942                apply the specified power to all unicast receiver addresses. A value
943                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
944                A value of 'ALL' will apply the specified power to all addresses.
945            update_default_unicast  (bool): set the default unicast Tx params to the
946                provided 'power'.
947            update_default_multicast  (bool): set the default multicast Tx params to the
948                provided 'power'.               
949
950        One of ``device_list`` or ``update_default_unicast`` or ``update_default_multicast``
951            must be set. 
952        """
953        if self._check_allowed_rate(mcs=mcs, phy_mode=phy_mode):
954            self._node_set_tx_param(cmds.NodeProcTxRate, 'rate', (mcs, phy_mode), 
955                                        'data', device_list, update_default_unicast, update_default_multicast)
956        else:
957            self._check_allowed_rate(mcs=mcs, phy_mode=phy_mode, verbose=True)
958            raise AttributeError("Tx rate, (mcs, phy_mode) tuple, not supported by the design. See above error message.")
959
960    def set_tx_rate_mgmt(self, mcs, phy_mode, device_list=None, update_default_unicast=None, update_default_multicast=None):
961        """Sets the packet transmit rate (mcs, phy_mode) for management frames.
962
963        Transmit parameters are maintained on a per-MAC address basis.
964        The ``device_list`` argument can be used to select particular devices
965        for which the Tx parameter update applies. The ``update_default_x``
966        arguments can be used to specify that the provided power should be
967        used for any future additions to the node's device list.
968
969        Args:
970            mcs (int): Modulation and coding scheme (MCS) index (in [0 .. 7])
971            phy_mode (str, int): PHY mode.  Must be one of:
972
973                * ``'NONHT'``: Use 802.11 (a/g) rates
974                * ``'HTMF'``: Use 802.11 (n) rates
975
976            device_list (list of WlanExpNode / WlanDevice
977             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
978                List of 802.11 devices or single 802.11 device for which to set the
979                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
980                apply the specified power to all unicast receiver addresses. A value
981                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
982                A value of 'ALL' will apply the specified power to all addresses.
983            update_default_unicast  (bool): set the default unicast Tx params to the
984                provided 'power'.
985            update_default_multicast  (bool): set the default multicast Tx params to the
986                provided 'power'.               
987
988        One of ``device_list`` or ``update_default_unicast`` or ``update_default_multicast``
989            must be set. 
990        """
991        if self._check_allowed_rate(mcs=mcs, phy_mode=phy_mode):
992            self._node_set_tx_param(cmds.NodeProcTxRate, 'rate', (mcs, phy_mode), 
993                                        'mgmt', device_list, update_default_unicast, update_default_multicast)
994        else:
995            self._check_allowed_rate(mcs=mcs, phy_mode=phy_mode, verbose=True)
996            raise AttributeError("Tx rate, (mcs, phy_mode) tuple, not supported by the design. See above error message.")
997
998    #------------------------
999    # Tx Antenna Mode commands
1000
1001    def set_tx_ant_mode_data(self, ant_mode, device_list=None, update_default_unicast=None, update_default_multicast=None):   
1002        """Sets the packet transmit antenna mode for data frames.
1003
1004        Transmit parameters are maintained on a per-MAC address basis.
1005        The ``device_list`` argument can be used to select particular devices
1006        for which the Tx parameter update applies. The ``update_default_x``
1007        arguments can be used to specify that the provided power should be
1008        used for any future additions to the node's device list.
1009        Args:
1010            ant_mode (str):  Antenna mode; must be one of:
1011
1012                * ``'RF_A'``: transmit on RF A interface
1013                * ``'RF_B'``: transmit on RF B interface
1014
1015            device_list (list of WlanExpNode / WlanDevice
1016             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
1017                List of 802.11 devices or single 802.11 device for which to set the
1018                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
1019                apply the specified power to all unicast receiver addresses. A value
1020                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
1021                A value of 'ALL' will apply the specified power to all addresses.
1022            update_default_unicast  (bool): set the default unicast Tx params to the
1023                provided 'ant_mode'.
1024            update_default_multicast  (bool): set the default multicast Tx params to the
1025                provided 'ant_mode'.               
1026
1027        One of ``device_list`` or ``update_default_unicast`` or ``update_default_multicast``
1028            must be set. 
1029        """
1030        if ant_mode is None:
1031            raise AttributeError("Invalid ant_mode: {0}".format(ant_mode))           
1032        self._node_set_tx_param(cmds.NodeProcTxAntMode, 'antenna_mode', 
1033                                        ant_mode, 'data', device_list, update_default_unicast, update_default_multicast)
1034                                       
1035    def set_tx_ant_mode_mgmt(self, ant_mode, device_list=None, update_default_unicast=None, update_default_multicast=None):   
1036        """Sets the packet transmit antenna mode for management frames.
1037
1038        Transmit parameters are maintained on a per-MAC address basis.
1039        The ``device_list`` argument can be used to select particular devices
1040        for which the Tx parameter update applies. The ``update_default_x``
1041        arguments can be used to specify that the provided power should be
1042        used for any future additions to the node's device list.
1043
1044        Args:
1045            ant_mode (str):  Antenna mode; must be one of:
1046
1047                * ``'RF_A'``: transmit on RF A interface
1048                * ``'RF_B'``: transmit on RF B interface
1049
1050            device_list (list of WlanExpNode / WlanDevice
1051             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
1052                List of 802.11 devices or single 802.11 device for which to set the
1053                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
1054                apply the specified power to all unicast receiver addresses. A value
1055                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
1056                A value of 'ALL' will apply the specified power to all addresses.
1057            update_default_unicast  (bool): set the default unicast Tx params to the
1058                provided 'ant_mode'.
1059            update_default_multicast  (bool): set the default multicast Tx params to the
1060                provided 'ant_mode'.               
1061
1062        One of ``device_list`` or ``update_default_unicast`` or ``update_default_multicast``
1063            must be set. 
1064        """
1065        if ant_mode is None:
1066            raise AttributeError("Invalid ant_mode: {0}".format(ant_mode))           
1067        self._node_set_tx_param(cmds.NodeProcTxAntMode, 'antenna_mode', 
1068                                        ant_mode, 'mgmt', device_list, update_default_unicast, update_default_multicast)
1069
1070    #------------------------
1071    # Rx Antenna Mode commands
1072
1073    def set_rx_ant_mode(self, ant_mode):
1074        """Sets the receive antenna mode for a node.
1075
1076        Args:
1077            ant_mode (str):  Antenna mode; must be one of:
1078
1079                * ``'RF_A'``: receive on RF A interface
1080                * ``'RF_B'``: receive on RF B interface
1081
1082        """
1083        if ant_mode is None:
1084            raise AttributeError("Invalid ant_mode: {0}".format(ant_mode))
1085           
1086        self.send_cmd(cmds.NodeProcRxAntMode(cmds.CMD_PARAM_WRITE, ant_mode))
1087
1088
1089    def get_rx_ant_mode(self):
1090        """Gets the current receive antenna mode for a node.
1091
1092        Returns:
1093            ant_mode (str): 
1094                Rx Antenna mode; must be one of:
1095
1096                  * ``'RF_A'``: receive on RF A interface
1097                  * ``'RF_B'``: receive on RF B interface
1098
1099        """
1100        return self.send_cmd(cmds.NodeProcRxAntMode(cmds.CMD_PARAM_READ))
1101
1102
1103    #------------------------
1104    # Tx Power commands
1105
1106    def set_tx_power_data(self, power, device_list=None, update_default_unicast=None, update_default_multicast=None):
1107        """Sets the transmit power for data frames.
1108
1109        This function is used to set the tranmsit power for frames of type
1110        data. Transmit parameters are maintained on a per-MAC address basis.
1111        The ``device_list`` argument can be used to select particular devices
1112        for which the Tx parameter update applies. The ``update_default_x``
1113        arguments can be used to specify that the provided power should be
1114        used for any future additions to the node's device list.
1115
1116        Args:
1117            power (int):  Transmit power in dBm (a value between
1118                ``node.max_tx_power_dbm`` and ``node.min_tx_power_dbm``)
1119            device_list (list of WlanExpNode / WlanDevice
1120             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
1121                List of 802.11 devices or single 802.11 device for which to set the
1122                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
1123                apply the specified power to all unicast receiver addresses. A value
1124                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
1125                A value of 'ALL' will apply the specified power to all addresses.
1126            update_default_unicast  (bool): set the default unicast Tx params to the
1127                provided 'power'.
1128            update_default_multicast  (bool): set the default multicast Tx params to the
1129                provided 'power'.               
1130
1131        One of ``device_list`` or ``update_default_unicast`` or ``update_default_multicast``
1132            must be set. 
1133        """
1134        self._node_set_tx_param(cmds.NodeProcTxPower, 'tx power', 
1135                                        (power, self.max_tx_power_dbm, self.min_tx_power_dbm), 
1136                                        'data', device_list, update_default_unicast, update_default_multicast)
1137
1138    def set_tx_power_mgmt(self, power, device_list=None, update_default_unicast=None, update_default_multicast=None):
1139        """Sets the transmit power for management frames.
1140
1141        This function is used to set the tranmsit power for frames of type
1142        management. Transmit parameters are maintained on a per-MAC address basis.
1143        The ``device_list`` argument can be used to select particular devices
1144        for which the Tx parameter update applies. The ``update_default_x``
1145        arguments can be used to specify that the provided power should be
1146        used for any future additions to the node's device list.
1147
1148        Args:
1149            power (int):  Transmit power in dBm (a value between
1150                ``node.max_tx_power_dbm`` and ``node.min_tx_power_dbm``)
1151            device_list (list of WlanExpNode / WlanDevice
1152             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
1153                List of 802.11 devices or single 802.11 device for which to set the
1154                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
1155                apply the specified power to all unicast receiver addresses. A value
1156                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
1157                A value of 'ALL' will apply the specified power to all addresses.
1158            update_default_unicast  (bool): set the default unicast Tx params to the
1159                provided 'power'.
1160            update_default_multicast  (bool): set the default multicast Tx params to the
1161                provided 'power'.         
1162               
1163        One of ``device_list`` or ``update_default_unicast`` or ``update_default_multicast``
1164            must be set. 
1165        """
1166        self._node_set_tx_param(cmds.NodeProcTxPower, 'tx power', 
1167                                        (power, self.max_tx_power_dbm, self.min_tx_power_dbm), 
1168                                        'mgmt', device_list, update_default_unicast, update_default_multicast)
1169
1170    def set_tx_power_ctrl(self, power):
1171        """Sets the control packet transmit power of the node.
1172
1173        Only the Tx power of the control packets can be set via wlan_exp.  The
1174        rate of control packets is determined by the 802.11 standard and
1175        control packets will be sent on whatever antenna that cause the control
1176        packet to be generated (ie an ack for a reception will go out on the
1177        same antenna on which the reception occurred).
1178
1179        Args:
1180            power (int):  Transmit power in dBm (a value between
1181                ``node.max_tx_power_dbm`` and ``node.min_tx_power_dbm``)
1182        """
1183        self.send_cmd(cmds.NodeProcTxPower(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_CTRL, 0, 0,
1184                                           (power, self.max_tx_power_dbm, self.min_tx_power_dbm), cmds.CMD_PARAM_TXPARAM_ADDR_NONE))
1185
1186
1187    def set_tx_power(self, power):
1188        """Sets the transmit power of the node.
1189
1190        This command will set all transmit power fields on the node to the same
1191        value:
1192       
1193            * Default Unicast Management Packet Tx Power for new station infos
1194            * Default Unicast Data Packet Tx Power for new station infos
1195            * Default Multicast Management Packet Tx Power for new station infos
1196            * Default Multicast Data Packet Tx Power for new station infos
1197            * Control Packet Tx Power
1198
1199        It will also update the transmit power for any frames destined for any
1200        known stations.
1201
1202        Args:
1203            power (int):  Transmit power in dBm (a value between
1204                ``node.max_tx_power_dbm`` and ``node.min_tx_power_dbm``)
1205        """
1206        self.send_cmd(cmds.NodeProcTxPower(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_ALL, 1, 1,
1207                                           (power, self.max_tx_power_dbm, self.min_tx_power_dbm), cmds.CMD_PARAM_TXPARAM_ADDR_ALL))
1208
1209
1210
1211    #------------------------
1212    # Other commands
1213
1214    def set_low_param(self, param_id, param_values):
1215        """Set a CPU Low parameter
1216
1217        This command provides a generic data pipe to set parameters in CPU Low. 
1218        Currently supported parameters are defined in cmds.py and use the
1219        naming convention:  CMD_PARAM_LOW_PARAM_*   In some cases, additional
1220        commands have been added to the node that utilize ``set_low_param()``
1221        in order to add error checking.
1222
1223        See http://warpproject.org/trac/wiki/802.11/wlan_exp/Extending
1224        for more information about how to extend ``set_low_param()`` to
1225        control additional parameters.
1226
1227        Args:
1228            param_id (int):              ID of parameter to be set
1229            param_values (list of int):  Value(s) to set the parameter
1230        """
1231        if(param_values is not None):
1232            try:
1233                v0 = param_values[0]
1234            except TypeError:
1235                v0 = param_values
1236
1237            if((type(v0) is not int) and (type(v0) is not long)) or (v0 >= 2**32):
1238                raise Exception('ERROR: parameter values must be scalar or iterable of ints in [0,2^32-1]!')
1239
1240        try:
1241            values = list(param_values)
1242        except TypeError:
1243            values = [param_values]
1244
1245        self.send_cmd(cmds.NodeLowParam(cmds.CMD_PARAM_WRITE, param_id=param_id, param_values=values))
1246
1247
1248    def set_dcf_param(self, param_name, param_val):
1249        """Configures parameters of the DCF MAC in CPU Low.
1250       
1251        These parameters are write-only. These parameters only affect nodes
1252        running the DCF in CPU Low. Other MAC implementations should ignore
1253        these parameter updates.
1254
1255        Args:
1256            param_name (str): Name of the param to change (see table below)
1257            param_val (int): Value to set for param_name (see table below)
1258
1259        This method currently implements the following parameters:
1260
1261        .. list-table::
1262            :header-rows: 1
1263            :widths: 15 20 60
1264
1265            * - Name
1266              - Valid Values
1267              - Description
1268
1269            * - ``'rts_thresh'``
1270              - [0 .. 65535]
1271              - Threshold in bytes for maximum length
1272                packet which will not trigger RTS handshake
1273
1274            * - ``'short_retry_limit'`` and
1275                ``'long_retry_limit'``
1276              - [0 .. 10]
1277              - DCF retry limits, controls maximum number of retransmissions for short and long packets
1278                See `retransmissions <http://warpproject.org/trac/wiki/802.11/MAC/Lower/Retransmissions>`_.
1279                for more details.
1280
1281            * - ``'phy_cs_thresh'``
1282              - [0 .. 1023]
1283              - Physical carrier sensing threshold, in units of digital RSSI. Set to max (1023)
1284                to effectively disable physical carrier sensing
1285
1286            * - ``'cw_exp_min'`` and
1287                ``'cw_exp_max'``
1288              - [0 .. 16]
1289              - Contention window exponent bounds. Contention window is set to random number in [0, (2^CW - 1)],
1290                where CW is bounded by [cw_exp_min, cw_exp_max]
1291
1292
1293        """
1294        if type(param_name) is not str:
1295            raise AttributeError("param_name must be a str.  Provided {0}.".format(type(param_name)))
1296       
1297        if type(param_val) is not int:
1298            raise AttributeError("param_val must be an int.  Provided {0}.".format(type(param_val)))
1299       
1300        # Process paramater
1301        if   (param_name == 'rts_thresh'):
1302            if ((param_val < 0) or (param_val > 65535)):
1303                raise AttributeError("'rts_thresh' must be in [0 .. 65535].")
1304           
1305            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('rts_thresh')")
1306
1307            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_RTS_THRESH, param_values=param_val)
1308           
1309        elif (param_name == 'short_retry_limit'):
1310            if ((param_val < 0) or (param_val > 65535)):
1311                raise AttributeError("'short_retry_limit' must be in [0 .. 65535].")
1312               
1313            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('short_retry_limit')")
1314   
1315            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_DOT11SHORTRETRY, param_values=param_val)
1316           
1317        elif (param_name == 'long_retry_limit'):
1318            if ((param_val < 0) or (param_val > 65535)):
1319                raise AttributeError("'long_retry_limit' must be in [0 .. 65535].")
1320               
1321            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('long_retry_limit')")
1322   
1323            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_DOT11LONGRETRY, param_values=param_val)
1324           
1325        elif (param_name == 'phy_cs_thresh'):
1326            if ((param_val < 0) or (param_val > 1023)):
1327                raise AttributeError("'phy_cs_thresh' must be in [0 .. 1023].")
1328           
1329            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('phy_cs_thresh')")
1330   
1331            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_PHYSICAL_CS_THRESH, param_values=param_val)
1332           
1333        elif (param_name == 'cw_exp_min'):
1334            if ((param_val < 0) or (param_val > 16)):
1335                raise AttributeError("'cw_exp_min' must be in [0 .. 16].")
1336               
1337            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('cw_exp_min')")
1338   
1339            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MIN, param_values=param_val)
1340           
1341        elif (param_name == 'cw_exp_max'):
1342            if ((param_val < 0) or (param_val > 16)):
1343                raise AttributeError("'cw_exp_max' must be in [0 .. 16].")
1344               
1345            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('cw_exp_max')")
1346   
1347            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MAX, param_values=param_val)
1348           
1349        else:
1350            msg  = "param_name must be one of the following strings:\n"
1351            msg += "    'rts_thresh', 'short_retry_limit', 'long_retry_limit', \n"
1352            msg += "    'phy_cs_thresh', 'cw_exp_min', 'cw_exp_max' \n"
1353            msg += "Provided '{0}'".format(param_name)
1354            raise AttributeError(msg)
1355
1356
1357    def configure_pkt_det_min_power(self, enable, power_level=None):
1358        """Configure Minimum Power Requirement of Packet Detector.
1359
1360        Args:
1361            enable (bool):      True/False
1362            power_level (int):  [-90,-30] dBm
1363        """
1364        if enable is False:
1365            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_PKT_DET_MIN_POWER, param_values=0)
1366        else:
1367            if power_level is not None:
1368                if power_level >= cmds.CMD_PARAM_NODE_MIN_MIN_PKT_DET_POWER_DBM and power_level <= cmds.CMD_PARAM_NODE_MAX_MIN_PKT_DET_POWER_DBM:
1369                    # C code expects a u32
1370                    #  Bit 24 is flag for en/disable of min_power logic
1371                    #  Bits [7:0] are u8 interpreted as dB above min threshold
1372                    param = (1 << 24) | ((power_level - cmds.CMD_PARAM_NODE_MIN_MIN_PKT_DET_POWER_DBM) & 0xFF)
1373                    self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_PKT_DET_MIN_POWER, param_values=param)
1374                else:
1375                    msg  = "\nPower level must be in the range of [{0},{1}]\n".format(cmds.CMD_PARAM_NODE_MIN_MIN_PKT_DET_POWER_DBM, cmds.CMD_PARAM_NODE_MAX_MIN_PKT_DET_POWER_DBM)
1376                    raise ValueError(msg)
1377            else:
1378                msg  = "\nPower level not specified\n"
1379                raise ValueError(msg)
1380
1381
1382    def set_phy_samp_rate(self, phy_samp_rate):
1383        """Sets the PHY sample rate (in MSps)
1384
1385        Args:
1386            phy_samp_rate (int):   
1387                PHY sample rate in MSps (10, 20, 40).  Default is 20 MSps.
1388        """
1389        if (phy_samp_rate not in [10, 20, 40]):
1390            raise AttributeError("'phy_samp_rate' must be in [10, 20, 40].")
1391           
1392        self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_PHY_SAMPLE_RATE, param_values=phy_samp_rate)
1393
1394
1395    def set_random_seed(self, high_seed=None, low_seed=None, gen_random=False):
1396        """Sets the random number generator seed on the node.
1397
1398        Args:
1399            high_seed (int, optional):   Set the random number generator seed
1400                on CPU high
1401            low_seed (int, optional):    Set the random number generator seed
1402                on CPU low
1403            gen_random (bool, optional): If high_seed or low_seed is not
1404                provided, then generate a random seed for the generator.
1405        """
1406        import random
1407        max_seed = 2**32 - 1
1408        min_seed = 0
1409
1410        if (high_seed is None):
1411            if gen_random:
1412                high_seed = random.randint(min_seed, max_seed)
1413        else:
1414            if (high_seed > max_seed) or (high_seed < min_seed):
1415                msg  = "Seed must be an integer between [{0}, {1}]".format(min_seed, max_seed)
1416                raise AttributeError(msg)
1417
1418        if (low_seed is None):
1419            if gen_random:
1420                low_seed  = random.randint(min_seed, max_seed)
1421        else:
1422            if (low_seed > max_seed) or (low_seed < min_seed):
1423                msg  = "Seed must be an integer between [{0}, {1}]".format(min_seed, max_seed)
1424                raise AttributeError(msg)
1425
1426        self.send_cmd(cmds.NodeProcRandomSeed(cmds.CMD_PARAM_WRITE, high_seed, low_seed))
1427
1428
1429    def enable_dsss(self, enable):
1430        """Enable / Disable DSSS receptions on the node
1431
1432        By default DSSS receptions are enabled on the node when possible
1433        (ie PHY sample rate is 20 MHz and Channel is in 2.4 GHz band)
1434       
1435        Args:
1436            enable (bool): 
1437           
1438              * True - enable DSSS receptions on the node
1439              * False - disable DSSS receptions on the node
1440        """
1441        self.send_cmd(cmds.NodeConfigure(dsss_enable=enable))
1442
1443
1444    def set_print_level(self, level):
1445        """Set the verbosity of the wlan_exp output to the node's UART.
1446
1447        Args:
1448            level (int):  Printing level (defaults to ``WARNING``)
1449
1450        Valid print levels can be found in ``wlan_exp.util.uart_print_levels``:
1451       
1452          * ``NONE``    - Do not print messages
1453          * ``ERROR``   - Only print error messages
1454          * ``WARNING`` - Print error and warning messages
1455          * ``INFO``    - Print error, warning and info messages
1456          * ``DEBUG``   - Print error, warning, info and debug messages
1457        """
1458        import wlan_exp.util as util
1459       
1460        valid_levels = ['NONE', 'ERROR', 'WARNING', 'INFO', 'DEBUG',
1461                        util.uart_print_levels['NONE'], 
1462                        util.uart_print_levels['ERROR'],
1463                        util.uart_print_levels['WARNING'],
1464                        util.uart_print_levels['INFO'],
1465                        util.uart_print_levels['DEBUG']]
1466       
1467        if (level in valid_levels):
1468            self.send_cmd(cmds.NodeConfigure(print_level=level))
1469        else:
1470            msg  = "\nInvalid print level {0}.  Print level must be one of: \n".format(level)
1471            msg += "    ['NONE', 'ERROR', 'WARNING', 'INFO', 'DEBUG', \n"
1472            msg += "     uart_print_levels['NONE'], uart_print_levels['ERROR'],\n"
1473            msg += "     uart_print_levels['WARNING'], uart_print_levels['INFO'],\n"
1474            msg += "     uart_print_levels['DEBUG']]"
1475            raise ValueError(msg)
1476           
1477
1478
1479    #--------------------------------------------
1480    # Internal methods to view / configure node attributes
1481    #--------------------------------------------
1482    def _check_allowed_rate(self, mcs, phy_mode, verbose=False):
1483        """Check that rate parameters are allowed
1484
1485        Args:
1486            mcs (int):           Modulation and coding scheme (MCS) index
1487            phy_mode (str, int): PHY mode (from util.phy_modes)
1488
1489        Returns:
1490            valid (bool):  Are all parameters valid?
1491        """
1492        return self._check_supported_rate(mcs, phy_mode, verbose)
1493
1494
1495    def _check_supported_rate(self, mcs, phy_mode, verbose=False):
1496        """Checks that the selected rate parameters are supported by the
1497        current MAC and PHY implementation. This method only checks if a
1498        rate can be used in hardware. The application-specific method
1499        _check_allowed_rate() should be used to confirm a selected rate is
1500        both supported and allowed given the currnet MAC and network state.
1501
1502        Args:
1503            mcs (int): Modulation and coding scheme (MCS) index (in [0 .. 7])
1504            phy_mode (str, int): PHY mode.  Must be one of:
1505
1506                * ``'NONHT'``: Use 802.11 (a/g) rates
1507                * ``'HTMF'``: Use 802.11 (n) rates
1508
1509        Returns:
1510            rate_suppored (bool):  True if the specified rate is supported
1511        """
1512        import wlan_exp.util as util
1513
1514        rate_ok = True
1515
1516        if ((mcs < 0) or (mcs > 7)):
1517            if (verbose):
1518                print("Invalid MCS {0}. MCS must be integer in [0 .. 7]".format(mcs))
1519            rate_ok = False
1520
1521        if (phy_mode not in ['NONHT', 'HTMF', util.phy_modes['NONHT'], util.phy_modes['HTMF']]):
1522            if (verbose):
1523                print("Invalid PHY mode {0}. PHY mode must be one of ['NONHT', 'HTMF', phy_modes['NONHT'], phy_modes['HTMF']]".format(phy_mode))
1524            rate_ok = False
1525
1526        return rate_ok
1527
1528
1529    def _node_set_tx_param(self, cmd, param_name, param, frametype,
1530                               device_list=None, update_default_unicast=None, update_default_multicast=None):
1531        """Sets the data & management transmit parameters of the node.
1532
1533        Args:
1534            cmd (Cmd):          Command to be used to set param
1535            param_name (str):   Name of parameter for error messages
1536            param (int):        Parameter to be set
1537            frametype(str):     `data` or `mgmt`
1538            device_list (list of WlanExpNode / WlanDevice
1539             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
1540                List of 802.11 devices or single 802.11 device for which to set the
1541                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
1542                apply the specified power to all unicast receiver addresses. A value
1543                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
1544                A value of 'ALL' will apply the specified power to all addresses.
1545            update_default_unicast  (bool): set the default unicast Tx params to the
1546                provided 'power'.
1547            update_default_multicast  (bool): set the default multicast Tx params to the
1548                provided 'power'.         
1549
1550        One of ``device_list`` or ``default`` must be set.         
1551        """
1552        if (device_list is None) and (update_default_unicast is None) and (update_default_multicast is None):
1553            msg  = "\nCannot set the unicast transmit {0}:\n".format(param_name)
1554            msg += "    Must specify either a list of devices, 'ALL' current station infos,\n"
1555            msg += "    or update_default_unicast or update_default_multicast {0}.".format(param_name)
1556            raise ValueError(msg)
1557           
1558        if( (update_default_unicast is True) or (device_list == 'ALL_UNICAST') or (device_list == 'ALL') ):
1559            update_default_unicast = 1;
1560        else:
1561            update_default_unicast = 0;
1562
1563        if( (update_default_multicast is True) or (device_list == 'ALL_MULTICAST') or (device_list == 'ALL') ):
1564            update_default_multicast = 1;
1565        else:
1566            update_default_multicast = 0;
1567           
1568
1569        if(frametype == 'data'):
1570            if(device_list == 'ALL_UNICAST'):
1571                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_DATA, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST))
1572            elif(device_list == 'ALL_MULTICAST'):
1573                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_DATA, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST))
1574            elif(device_list == 'ALL'):
1575                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_DATA, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_ALL))               
1576            elif(device_list is not None):
1577                try:
1578                    for device in device_list:
1579                        self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_DATA, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_SINGLE, device))
1580                except TypeError:
1581                    self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_DATA, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_SINGLE, device_list))
1582            else:
1583                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_DATA, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_NONE))
1584        elif(frametype == 'mgmt'):
1585            if(device_list == 'ALL_UNICAST'):
1586                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_MGMT, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_ALL_UNICAST))
1587            elif(device_list == 'ALL_MULTICAST'):
1588                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_MGMT, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_ALL_MULTICAST))
1589            elif(device_list == 'ALL'):
1590                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_MGMT, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_ALL))               
1591            elif(device_list is not None):
1592                try:
1593                    for device in device_list:
1594                        self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_MGMT, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_SINGLE, device))
1595                except TypeError:
1596                    self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_MGMT, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_SINGLE, device_list))
1597            else:
1598                self.send_cmd(cmd(cmds.CMD_PARAM_WRITE, cmds.CMD_PARAM_TXPARAM_MGMT, update_default_unicast, update_default_multicast, param, cmds.CMD_PARAM_TXPARAM_ADDR_NONE))
1599
1600
1601    def _set_bb_gain(self, bb_gain):
1602        """Sets the the baseband gain.
1603
1604        Args:
1605            bb_gain (int):  Baseband gain setting [0,3]
1606        """
1607        if bb_gain is not None:
1608            if (bb_gain >= 0) and (bb_gain <=3):
1609                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_BB_GAIN, param_values=bb_gain)
1610            else:
1611                msg  = "\nBB Gain must be in the range [0,3]\n"
1612                raise ValueError(msg)
1613
1614
1615    def _set_linearity_pa(self, linearity_val):
1616        """Sets the the PA linearity.
1617
1618        Args:
1619            linearity_val (int):  Linearity setting [0,3]
1620        """
1621        if linearity_val is not None:
1622            if (linearity_val >= 0) and (linearity_val <=3):
1623                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_LINEARITY_PA, param_values=linearity_val)
1624            else:
1625                msg  = "\nBB Linearity must be in the range [0,3]\n"
1626                raise ValueError(msg)
1627
1628
1629    def _set_interp_filt_scaling(self, scale_int0=0x10, scale_int1=0x10, scale_srrc=0x10):
1630        """Sets the the DAC scaling at the output of each stage of interpolations.
1631
1632        Args:
1633            scale_int0 (int):  Scaling Stage 0    [0,31]
1634            scale_int1 (int):  Scaling Stage 0    [0,31]
1635            scale_srrc (int):  Scaling Stage SRRC [0,31]
1636        """
1637
1638        if (scale_int0 >= 0) and (scale_int0 <=31) and (scale_int1 >= 0) and (scale_int1 <=31) and (scale_srrc >= 0) and (scale_srrc <=31):
1639            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_AD_SCALING, param_values=[scale_int0, scale_int1, scale_srrc])
1640        else:
1641            msg  = "\nInterp scalings must be in the range [0,31]\n"
1642            raise ValueError(msg)
1643
1644
1645    def _set_linearity_vga(self, linearity_val):
1646        """Sets the the VGA linearity.
1647
1648        Args:
1649            linearity_val (int):  Linearity setting [0,3]
1650        """
1651        if linearity_val is not None:
1652            if (linearity_val >= 0) and (linearity_val <=3):
1653                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_LINEARITY_VGA, param_values=linearity_val)
1654            else:
1655                msg  = "\nBB Linearity must be in the range [0,3]\n"
1656                raise ValueError(msg)
1657
1658
1659    def _set_linearity_upconv(self, linearity_val):
1660        """Sets the the upconversion linearity.
1661
1662        Args:
1663            linearity_val (int):  Linearity setting [0,3]
1664        """
1665        if linearity_val is not None:
1666            if (linearity_val >= 0) and (linearity_val <=3):
1667                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_LINEARITY_UPCONV, param_values=linearity_val)
1668            else:
1669                msg  = "\nBB Linearity must be in the range [0,3]\n"
1670                raise ValueError(msg)
1671
1672
1673
1674    #--------------------------------------------
1675    # Scan Commands
1676    #--------------------------------------------
1677    def set_scan_parameters(self, time_per_channel=None, num_probe_tx_per_channel=None, channel_list=None, ssid=None):
1678        """Set the paramters of the wireless network scan state machine.
1679       
1680        Args:
1681            time_per_channel (float, optional): Time (in float sec) to spend
1682                on each channel.  A value of None will not modify the current
1683                time per channel
1684            num_probe_tx_per_channel (int, optional):   Number of probe requests
1685                transmitted while on each channel.  A value of None will not
1686                modify the current number of probe requests per channel.
1687            channel_list (list of int optional): Channel(s) to scan; A value
1688                of None will not modify the current channel list.
1689            ssid (str, optional):  SSID to scan for (as part of probe request);
1690                A value of None will not modify the current SSID.
1691       
1692        Setting ``num_probe_tx_per_chan`` to a non-zero value will enable
1693        active scanning. The node will transmit broadcast Probe Request packets
1694        on every channel and will gather network information from received
1695        Probe Response and Beacon packets. Setting ``num_probe_tx_per_chan=0``
1696        will enable passive scanning. In this mode the node will not transmit
1697        any Probe Request packets and network information will be gathered only
1698        from received Beacon packets.
1699
1700        The blank SSID (``ssid=""``) is interpretted as a wildcard and will
1701        solicit Probe Response packets from networks with any SSID. "Closed"
1702        networks do not respond to the wildcard SSID. These networks will still
1703        be discovered via Beacon receptions.
1704
1705        If the channel list / SSID is not specified, then it will not be
1706        updated on the node (ie it will use the current channel list / SSID)
1707
1708        """
1709        # Check time_per_channel
1710        if time_per_channel is not None:
1711            try:
1712                time_per_channel = float(time_per_channel)
1713            except:
1714                raise AttributeError("time_per_channel must be expressable as a float.")
1715               
1716        # Check channel_list
1717        if channel_list is not None:
1718            tmp_chan_list = []
1719           
1720            if type(channel_list) is str:
1721                # Process pre-defined strings
1722                import wlan_exp.util as util
1723
1724                if   (channel_list == 'ALL'):
1725                    tmp_chan_list = util.wlan_channels
1726                elif (channel_list == 'ALL_2.4GHZ'):
1727                    tmp_chan_list = [x for x in util.wlan_channels if (x <= 14)]
1728                elif (channel_list == 'ALL_5GHZ'):
1729                    tmp_chan_list = [x for x in util.wlan_channels if (x > 14)]
1730                else:
1731                    msg  = "\n    String '{0}' not recognized.".format(channel_list)
1732                    msg += "\n    Please use 'ALL', 'ALL_2.4GHZ', 'ALL_5GHZ' or either an int or list of channels"
1733                    raise AttributeError(msg)
1734
1735            elif type(channel_list) is int:
1736                # Proess scalar integer
1737                tmp_chan_list.append(channel_list)
1738               
1739            else:
1740                # Proess interables
1741                try:
1742                    for channel in channel_list:
1743                        tmp_chan_list.append(channel)
1744                except:
1745                    msg  = "\n    Unable to process channel_list."
1746                    msg += "\n    Please use 'ALL', 'ALL_2.4GHZ', 'ALL_5GHZ' or either an int or list of channels"
1747                    raise AttributeError(msg)
1748
1749            if tmp_chan_list:
1750                channel_list = tmp_chan_list
1751            else:
1752                msg  = "\n    channel_list is empty."
1753                msg += "\n    Please use 'ALL', 'ALL_2.4GHZ', 'ALL_5GHZ' or either an int or list of channels"
1754                raise AttributeError(msg)
1755               
1756        self.send_cmd(cmds.NodeProcScanParam(cmds.CMD_PARAM_WRITE, time_per_channel, num_probe_tx_per_channel, channel_list, ssid))
1757   
1758   
1759    def start_network_scan(self):
1760        """Starts the wireless network scan state machine at the node.
1761       
1762        During a scan, the node cycles through a set of channels and transmits
1763        periodic Probe Request frames on each channel.  Information about
1764        available wireless networks is extracted from received Probe Responses
1765        and Beacon frames. The network scan results can be queried any time
1766        using the ``node.get_network_list()`` method.
1767
1768        Network scans can only be run by unassociated nodes. An associated
1769        node must first reset its BSS state before starting a scan.
1770
1771        The network scan state machine can be stopped with
1772        ``node.stop_network_scan()``. The scan state machine will also be
1773        stopped automatically if the node is configured with a new non-null
1774        BSS state.
1775           
1776        Example:
1777        ::
1778
1779            # Ensure node has null BSS state
1780            n.configure_bss(None)
1781
1782            # Start the scan state machine; scan will use default scan params
1783            #  Use n.set_scan_parameters() to customize scan behavior
1784            n.start_network_scan()
1785
1786            # Wait 5 seconds, retrieve the list of discovered networks
1787            time.sleep(5)
1788            networks = n.get_network_list()
1789
1790            # Stop the scan state machine
1791            n.stop_network_scan()
1792
1793        """
1794        self.send_cmd(cmds.NodeProcScan(enable=True))
1795   
1796   
1797    def stop_network_scan(self):
1798        """Stops the wireless network scan state machine."""
1799        self.send_cmd(cmds.NodeProcScan(enable=False))
1800
1801
1802    def is_scanning(self):
1803        """Queries if the node's wireless network scanning state machine is
1804        currently running.
1805       
1806        Returns:
1807            status (bool):
1808
1809                * True      -- Scan state machine is running
1810                * False     -- Scan state machine is not running
1811        """
1812        return self.send_cmd(cmds.NodeProcScan())
1813
1814
1815
1816    #--------------------------------------------
1817    # Association Commands
1818    #--------------------------------------------
1819    def configure_bss(self):
1820        """Configure the Basic Service Set (BSS) information of the node
1821       
1822        Each node is either a member of no BSS (colloquially "unassociated")
1823        or a member of one BSS.  A node requires a minimum valid bss_info to
1824        be a member of a BSS.  Based on the node type, there is a minimum
1825        set of fields needed for a valid bss_info. 
1826       
1827        This method must be overloaded by sub-classes.
1828       
1829        See http://warpproject.org/trac/wiki/802.11/wlan_exp/bss for more
1830        information about BSSes.
1831        """
1832        raise NotImplementedError()
1833
1834    def get_station_info(self, device_list=None):
1835       
1836        raise DeprecationWarning('Error: get_station_info() is deprecated. Use get_bss_members() to retrieve the list of associated stations or get_station_info_list() to retrieve the list of all known stations.')
1837       
1838    def get_bss_members(self):
1839        """Get the BSS members from the node.
1840
1841        The StationInfo() returned by this method can be accessed like a
1842        dictionary and has the following fields:
1843       
1844            +-----------------------------+----------------------------------------------------------------------------------------------------+
1845            | Field                       | Description                                                                                        |
1846            +=============================+====================================================================================================+
1847            | mac_addr                    |  MAC address of station                                                                            |
1848            +-----------------------------+----------------------------------------------------------------------------------------------------+
1849            | id                          |  Identification Index for this station                                                             |
1850            +-----------------------------+----------------------------------------------------------------------------------------------------+
1851            | host_name                   |  String hostname (19 chars max), taken from DHCP DISCOVER packets                                  |
1852            +-----------------------------+----------------------------------------------------------------------------------------------------+
1853            | flags                       |  Station flags.  Value containts 1 bit fields:                                                     |
1854            |                             |      * 0x0001 - 'KEEP'                                                                             |
1855            |                             |      * 0x0002 - 'DISABLE_ASSOC_CHECK'                                                              |
1856            |                             |      * 0x0004 - 'DOZE'                                                                             |
1857            |                             |      * 0x0008 - 'HT_CAPABLE'                                                                       |
1858            |                             |                                                                                                    |
1859            +-----------------------------+----------------------------------------------------------------------------------------------------+
1860            | latest_rx_timestamp         |  Value of System Time in microseconds of last successful Rx from device                            |
1861            +-----------------------------+----------------------------------------------------------------------------------------------------+
1862            | latest_txrx_timestamp       |  Value of System Time in microseconds of last Tx or successful Rx from device                      |           
1863            +-----------------------------+----------------------------------------------------------------------------------------------------+
1864            | latest_rx_seq               |  Sequence number of last packet received from device                                               |
1865            +-----------------------------+----------------------------------------------------------------------------------------------------+
1866            | tx_phy_mcs                  |  Current PHY MCS index in [0:7] for new transmissions to device                                    |
1867            +-----------------------------+----------------------------------------------------------------------------------------------------+
1868            | tx_phy_mode                 |  Current PHY mode for new transmissions to deivce                                                  |
1869            +-----------------------------+----------------------------------------------------------------------------------------------------+
1870            | tx_phy_antenna_mode         |  Current PHY antenna mode in [1:4] for new transmissions to device                                 |
1871            +-----------------------------+----------------------------------------------------------------------------------------------------+
1872            | tx_phy_power                |  Current Tx power in dBm for new transmissions to device                                           |
1873            +-----------------------------+----------------------------------------------------------------------------------------------------+
1874            | tx_mac_flags                |  Flags for Tx MAC config for new transmissions to device.  Value contains 1 bit flags:             |
1875            |                             |                                                                                                    |
1876            |                             |      * None defined                                                                                |
1877            |                             |                                                                                                    |
1878            +-----------------------------+----------------------------------------------------------------------------------------------------+
1879       
1880        Returns:
1881            station_infos (list of StationInfo): 
1882                List of StationInfo() BSS members known to the node
1883
1884        """
1885        ret_val = self.send_cmd(cmds.NodeGetBSSMembers())
1886
1887        return ret_val
1888
1889
1890    def get_station_info_list(self):
1891        """Get the all Station Infos from node.
1892
1893        The StationInfo() returned by this method can be accessed like a
1894        dictionary and has the following fields:
1895       
1896            +-----------------------------+----------------------------------------------------------------------------------------------------+
1897            | Field                       | Description                                                                                        |
1898            +=============================+====================================================================================================+
1899            | mac_addr                    |  MAC address of station                                                                            |
1900            +-----------------------------+----------------------------------------------------------------------------------------------------+
1901            | id                          |  Identification Index for this station                                                             |
1902            +-----------------------------+----------------------------------------------------------------------------------------------------+
1903            | host_name                   |  String hostname (19 chars max), taken from DHCP DISCOVER packets                                  |
1904            +-----------------------------+----------------------------------------------------------------------------------------------------+
1905            | flags                       |  Station flags.  Value containts 1 bit fields:                                                     |
1906            |                             |      * 0x0001 - 'KEEP'                                                                             |
1907            |                             |      * 0x0002 - 'DISABLE_ASSOC_CHECK'                                                              |
1908            |                             |      * 0x0004 - 'DOZE'                                                                             |
1909            |                             |      * 0x0008 - 'HT_CAPABLE'                                                                       |
1910            |                             |                                                                                                    |
1911            +-----------------------------+----------------------------------------------------------------------------------------------------+
1912            | latest_rx_timestamp         |  Value of System Time in microseconds of last successful Rx from device                            |
1913            +-----------------------------+----------------------------------------------------------------------------------------------------+
1914            | latest_txrx_timestamp       |  Value of System Time in microseconds of last Tx or successful Rx from device                      |           
1915            +-----------------------------+----------------------------------------------------------------------------------------------------+
1916            | latest_rx_seq               |  Sequence number of last packet received from device                                               |
1917            +-----------------------------+----------------------------------------------------------------------------------------------------+
1918            | tx_phy_mcs                  |  Current PHY MCS index in [0:7] for new transmissions to device                                    |
1919            +-----------------------------+----------------------------------------------------------------------------------------------------+
1920            | tx_phy_mode                 |  Current PHY mode for new transmissions to deivce                                                  |
1921            +-----------------------------+----------------------------------------------------------------------------------------------------+
1922            | tx_phy_antenna_mode         |  Current PHY antenna mode in [1:4] for new transmissions to device                                 |
1923            +-----------------------------+----------------------------------------------------------------------------------------------------+
1924            | tx_phy_power                |  Current Tx power in dBm for new transmissions to device                                           |
1925            +-----------------------------+----------------------------------------------------------------------------------------------------+
1926            | tx_mac_flags                |  Flags for Tx MAC config for new transmissions to device.  Value contains 1 bit flags:             |
1927            |                             |                                                                                                    |
1928            |                             |      * None defined                                                                                |
1929            |                             |                                                                                                    |
1930            +-----------------------------+----------------------------------------------------------------------------------------------------+
1931       
1932        Returns:
1933            station_infos (list of StationInfo): 
1934                List of all StationInfo() known to the node
1935
1936        """
1937        ret_val = self.send_cmd(cmds.NodeGetStationInfoList())
1938
1939        return ret_val
1940
1941    def get_bss_info(self):
1942       print('WARNING: get_bss_info() is deprecated and will be removed in a future version. Please use get_network_info()')
1943       return self.get_network_info()
1944
1945    def get_bss_config(self):
1946        """Get BSS configuration of the network the node is a member of
1947
1948        Returns a dictionary with the following fields:
1949       
1950            +-----------------------------+----------------------------------------------------------------------------------------------------+
1951            | Field                       | Description                                                                                        |
1952            +=============================+====================================================================================================+
1953            | bssid                       |  BSS ID: 48-bit MAC address                                                                        |
1954            +-----------------------------+----------------------------------------------------------------------------------------------------+
1955            | channel                     |  Primary channel.  In util.wlan_channels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48]     |
1956            +-----------------------------+----------------------------------------------------------------------------------------------------+
1957            | channel_type                |  Channel Type.  Value is one of:                                                                   |
1958            |                             |                                                                                                    |
1959            |                             |      * 0x00 - 'BW20'                                                                               |
1960            |                             |      * 0x01 - 'BW40_SEC_BELOW'                                                                     |
1961            |                             |      * 0x02 - 'BW40_SEC_ABOVE'                                                                     |
1962            |                             |                                                                                                    |
1963            +-----------------------------+----------------------------------------------------------------------------------------------------+
1964            | ssid                        |  SSID (32 chars max)                                                                               |
1965            +-----------------------------+----------------------------------------------------------------------------------------------------+
1966            | ht_capable                  |  1 - Network is capable of HT PHY mode                                                             |
1967            |                             |  0 - Netowrk is not capable of NHT PHY mode                                                        |
1968            +-----------------------------+----------------------------------------------------------------------------------------------------+
1969            | beacon_interval             |  Beacon interval - In time units of 1024 us'                                                       |
1970            +-----------------------------+----------------------------------------------------------------------------------------------------+           
1971            | dtim_period                 | 
1972            +-----------------------------+----------------------------------------------------------------------------------------------------+       
1973           
1974        Returns:
1975            bss_config : 
1976                BSS configuration of the network that the node is a member of (can be None)
1977        """
1978        network_info = self.get_network_info()
1979       
1980        if(network_info is None):
1981            # Node has NULL active_network_info - return None
1982            return None
1983       
1984        # Use the field names of the BSSConfig InfoStruct to transform the network_info
1985        #  into a bss_config dictionary
1986        from wlan_exp.info import BSSConfig
1987       
1988        bss_config_fields = BSSConfig().get_field_names()
1989       
1990        # Construct a dictionary with only BSSConfig fields
1991        bss_config = {}
1992        for f in bss_config_fields:
1993            bss_config[f] = network_info[f]
1994       
1995        return bss_config
1996
1997    def get_network_info(self):
1998        """Get information about the network the node is a member of
1999
2000        The NetworkInfo() returned by this method can be accessed like a
2001        dictionary and has the following fields:
2002       
2003            +-----------------------------+----------------------------------------------------------------------------------------------------+
2004            | Field                       | Description                                                                                        |
2005            +=============================+====================================================================================================+
2006            | bssid                       |  BSS ID: 48-bit MAC address                                                                        |
2007            +-----------------------------+----------------------------------------------------------------------------------------------------+
2008            | channel                     |  Primary channel.  In util.wlan_channels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48]     |
2009            +-----------------------------+----------------------------------------------------------------------------------------------------+
2010            | channel_type                |  Channel Type.  Value is one of:                                                                   |
2011            |                             |                                                                                                    |
2012            |                             |      * 0x00 - 'BW20'                                                                               |
2013            |                             |      * 0x01 - 'BW40_SEC_BELOW'                                                                     |
2014            |                             |      * 0x02 - 'BW40_SEC_ABOVE'                                                                     |
2015            |                             |                                                                                                    |
2016            +-----------------------------+----------------------------------------------------------------------------------------------------+
2017            | ssid                        |  SSID (32 chars max)                                                                               |
2018            +-----------------------------+----------------------------------------------------------------------------------------------------+
2019            | ht_capable                  |  1 - Network is capable of HT PHY mode                                                             |
2020            |                             |  0 - Netowrk is not capable of NHT PHY mode                                                        |
2021            +-----------------------------+----------------------------------------------------------------------------------------------------+
2022            | beacon_interval             |  Beacon interval - In time units of 1024 us'                                                       |
2023            +-----------------------------+----------------------------------------------------------------------------------------------------+           
2024            | dtim_period                 | 
2025            +-----------------------------+----------------------------------------------------------------------------------------------------+
2026            | flags                       |  Value contains 1 bit fields:                                                                      |
2027            |                             |                                                                                                    |
2028            |                             |      * 0x0001 - 'KEEP'                                                                             |
2029            +-----------------------------+----------------------------------------------------------------------------------------------------+
2030            | capabilities                |  Supported capabilities of the BSS.  Value contains 1 bit fields:                                  |
2031            |                             |                                                                                                    |
2032            |                             |      * 0x0001 - 'ESS'                                                                              |
2033            |                             |      * 0x0002 - 'IBSS'                                                                             |
2034            |                             |      * 0x0010 - 'PRIVACY'                                                                          |
2035            |                             |                                                                                                    |
2036            +-----------------------------+----------------------------------------------------------------------------------------------------+
2037            | latest_beacon_rx_time       |  Value of System Time in microseconds of last beacon Rx                                            |
2038            +-----------------------------+----------------------------------------------------------------------------------------------------+
2039            | latest_beacon_rx_power      |  Last observed beacon Rx Power (dBm)                                                               |
2040            +-----------------------------+----------------------------------------------------------------------------------------------------+           
2041
2042           
2043        Returns:
2044            network_info (NetworkInfo): 
2045                Information about network that the node is a member of (can be None)
2046        """
2047        ret_val = self.send_cmd(cmds.NodeGetNetworkInfo())
2048       
2049        if (len(ret_val) == 1):
2050            ret_val = ret_val[0]
2051        else:
2052            ret_val = None
2053
2054        return ret_val
2055
2056
2057    def get_network_list(self):
2058        """Get a list of known networks (NetworkInfo()s) on the node
2059
2060        Returns:
2061            networks (list of NetworkInfo): 
2062                List of NetworkInfo() that are known to the node
2063        """
2064        return self.send_cmd(cmds.NodeGetNetworkInfo("ALL"))
2065
2066
2067
2068    #--------------------------------------------
2069    # Queue Commands
2070    #--------------------------------------------
2071    def queue_tx_data_purge_all(self):
2072        """Purges all data transmit queues on the node.
2073       
2074        This will discard all currently enqueued packets awaiting transmission
2075        at the time the command is received.  This will not discard packets
2076        already submitted to the lower-level MAC for transmission.  Also, this
2077        will not stop additional packets from sources such as LTGs from being
2078        enqueued.
2079       
2080        This command is equivalent to ``reset(queue_data=True)``.
2081        """
2082        self.send_cmd(cmds.QueueTxDataPurgeAll())
2083
2084
2085
2086    #--------------------------------------------
2087    # Braodcast Commands can be found in util.py
2088    #--------------------------------------------
2089
2090
2091
2092    #--------------------------------------------
2093    # Node User Commands
2094    #--------------------------------------------
2095    def send_user_command(self, cmd_id, args=None):
2096        """Send User defined command to the node
2097
2098        See documentation on how-to extend wlan_exp:
2099        http://warpproject.org/trac/wiki/802.11/wlan_exp/Extending
2100
2101        Args:
2102            cmd_id (u32):  User-defined Command ID
2103            args (u32, list of u32):  Scalar or list of u32 command arguments
2104                to send to the node
2105
2106        Returns:
2107            resp_args (list of u32): 
2108                List of u32 response arguments received from the node
2109        """
2110        if cmd_id is None:
2111            raise AttributeError("Command ID must be defined for a user command")
2112
2113        ret_val = self.send_cmd(cmds.UserSendCmd(cmd_id=cmd_id, args=args))
2114
2115        if ret_val is not None:
2116            return ret_val
2117        else:
2118            return []
2119
2120
2121
2122    #--------------------------------------------
2123    # Memory Access Commands - For developer use only
2124    #--------------------------------------------
2125    def _mem_write_high(self, address, values):
2126        """Writes 'values' to CPU High memory starting at 'address'
2127
2128        Args:
2129            address (int):         Address must be in [0 .. (2^32 - 1)]
2130            values (list of int):  Each value must be in [0 .. (2^32 - 1)]
2131        """
2132        # Convert scalar values to a list for processing
2133        if (type(values) is not list):
2134            values = [values]
2135
2136        if (self._check_mem_access_args(address, values)):
2137            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_WRITE, high=True, address=address, length=len(values), values=values))
2138
2139
2140    def _mem_read_high(self, address, length):
2141        """Reads 'length' values from CPU High memory starting at 'address'
2142
2143        Args:
2144            address (int):  Address must be in [0 .. (2^32 - 1)]
2145            length (int):   Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2146
2147        Returns:
2148            values (list of u32):  List of u32 values received from the node
2149        """
2150        if (self._check_mem_access_args(address, values=None)):
2151            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_READ, high=True, address=address, length=length))
2152
2153
2154    def _mem_write_low(self, address, values):
2155        """Writes 'values' to CPU Low memory starting at 'address'
2156
2157        Args:
2158            address (int):         Address must be in [0 .. (2^32 - 1)]
2159            values (list of int):  Each value must be in [0 .. (2^32 - 1)]
2160        """
2161        # Convert scalar values to a list for processing
2162        if (type(values) is not list):
2163            values = [values]
2164
2165        if (self._check_mem_access_args(address, values)):
2166            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_WRITE, high=False, address=address, length=len(values), values=values))
2167
2168
2169    def _mem_read_low(self, address, length):
2170        """Reads 'length' values from CPU Low memory starting at 'address'
2171
2172        Args:
2173            address (int):  Address must be in [0 .. (2^32 - 1)]
2174            length (int):   Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2175
2176        Returns:
2177            values (list of u32):  List of u32 values received from the node
2178        """
2179        if (self._check_mem_access_args(address, values=None)):
2180            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_READ, high=False, address=address, length=length))
2181
2182
2183    def _check_mem_access_args(self, address, values=None, length=None):
2184        """Check memory access variables
2185
2186        Args:
2187            address (int):         Address must be in [0 .. (2^32 - 1)]
2188            values (list of int):  Each value must be in [0 .. (2^32 - 1)]
2189            length (int):          Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2190
2191        Returns:
2192            valid (bool):  Are all arguments valid?
2193        """
2194        if ((int(address) != address) or (address >= 2**32) or (address < 0)):
2195            raise Exception('ERROR: address must be integer value in [0 .. (2^32 - 1)]!')
2196
2197        if (values is not None):
2198            if (type(values) is not list):
2199                values = [values]
2200
2201            error = False
2202
2203            for value in values:
2204                if (((type(value) is not int) and (type(value) is not long)) or
2205                    (value >= 2**32) or (value < 0)):
2206                    error = True
2207
2208            if (error):
2209                raise Exception('ERROR: values must be scalar or iterable of ints in [0 .. (2^32 - 1)]!')
2210
2211        if length is not None:
2212            if ((int(length) != length) or (length > 320) or (length <= 0)):
2213                raise Exception('ERROR: length must be an integer [1 .. 320] words (ie, 4 to 1400 bytes)!')
2214
2215        return True
2216
2217
2218    def _eeprom_write(self, address, values):
2219        """Writes 'values' to EEPROM starting at 'address'
2220
2221        Args:
2222            address (int):         Address must be in [0 .. 15999]
2223            values (list of int):  Each value must be in [0 .. 255]
2224        """
2225        # Convert scalar values to a list for processing
2226        if (type(values) is not list):
2227            values = [values]
2228
2229        if (self._check_eeprom_access_args(address=address, values=values, length=len(values))):
2230            if(address >= 16000):
2231                raise Exception('ERROR: EEPROM addresses [16000 .. 16383] are read only!')
2232            else:
2233                return self.send_cmd(cmds.NodeEEPROMAccess(cmd=cmds.CMD_PARAM_WRITE, address=address, length=len(values), values=values))
2234
2235
2236    def _eeprom_read(self, address, length):
2237        """Reads 'length' values from EEPROM starting at 'address'
2238
2239        Args:
2240            address (int):  Address must be in [0 .. 16383]
2241            length (int):   Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2242
2243        Returns:
2244            values (list of u8):  List of u8 values received from the node
2245        """
2246        if (self._check_eeprom_access_args(address=address, length=length)):
2247            return self.send_cmd(cmds.NodeEEPROMAccess(cmd=cmds.CMD_PARAM_READ, address=address, length=length))
2248
2249
2250    def _check_eeprom_access_args(self, address, values=None, length=None):
2251        """Check EEPROM access variables
2252
2253        Args:
2254            address (int):         Address must be in [0 .. 16383]
2255            values (list of int):  Each value must be in [0 .. 255]
2256            length (int):          Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2257
2258        Returns:
2259            valid (bool):  Are all arguments valid?
2260        """
2261        if ((int(address) != address) or (address >= 16384) or (address < 0)):
2262            raise Exception('ERROR: address must be integer value in [0 .. 16383]!')
2263
2264        if (values is not None):
2265            if (type(values) is not list):
2266                values = [values]
2267
2268            error = False
2269
2270            for value in values:
2271                if (((type(value) is not int) and (type(value) is not long)) or
2272                    (value >= 2**8) or (value < 0)):
2273                    error = True
2274
2275            if (error):
2276                raise Exception('ERROR: values must be scalar or iterable of ints in [0 .. 255]!')
2277
2278        if length is not None:
2279            if ((int(length) != length) or (length > 320) or (length <= 0)):
2280                raise Exception('ERROR: length must be an integer [1 .. 320] words (ie, 4 to 1400 bytes)!')
2281
2282        return True
2283
2284
2285
2286    #-------------------------------------------------------------------------
2287    # Parameter Framework
2288    #   Allows for processing of hardware parameters
2289    #-------------------------------------------------------------------------
2290    def process_parameter(self, identifier, length, values):
2291        import struct
2292       
2293        """Extract values from the parameters"""
2294        if (identifier == NODE_PARAM_ID_WLAN_EXP_VERSION):
2295            if (length == 1):
2296                self.wlan_exp_ver_major = (values[0] & 0xFF000000) >> 24
2297                self.wlan_exp_ver_minor = (values[0] & 0x00FF0000) >> 16
2298                self.wlan_exp_ver_revision = (values[0] & 0x0000FFFF)
2299
2300                # Check to see if there is a version mismatch
2301                self.check_wlan_exp_ver()
2302            else:
2303                raise ex.ParameterError("NODE_DESIGN_VER", "Incorrect length")
2304
2305        elif   (identifier == NODE_PARAM_ID_WLAN_MAC_ADDR):
2306            if (length == 2):
2307                self.wlan_mac_address = ((2**32) * (values[1] & 0xFFFF) + values[0])
2308            else:
2309                raise ex.ParameterError("NODE_WLAN_MAC_ADDR", "Incorrect length")
2310
2311        elif   (identifier == NODE_PARAM_ID_WLAN_SCHEDULER_RESOLUTION):
2312            if (length == 1):
2313                self.scheduler_resolution = values[0]
2314            else:
2315                raise ex.ParameterError("NODE_LTG_RESOLUTION", "Incorrect length")
2316
2317        elif   (identifier == NODE_PARAM_ID_WLAN_MAX_TX_POWER_DBM):
2318            if (length == 1):
2319                # Power is an int transmited as a uint
2320                if (values[0] & 0x80000000):
2321                    self.max_tx_power_dbm = values[0] - 2**32
2322                else:
2323                    self.max_tx_power_dbm = values[0]
2324            else:
2325                raise ex.ParameterError("MAX_TX_POWER_DBM", "Incorrect length")
2326
2327        elif   (identifier == NODE_PARAM_ID_WLAN_MIN_TX_POWER_DBM):
2328            if (length == 1):
2329                # Power is an int transmited as a uint
2330                if (values[0] & 0x80000000):
2331                    self.min_tx_power_dbm = values[0] - 2**32
2332                else:
2333                    self.min_tx_power_dbm = values[0]
2334            else:
2335                raise ex.ParameterError("MIN_TX_POWER_DBM", "Incorrect length")
2336               
2337        elif   (identifier == NODE_PARAM_WLAN_CPU_LOW_COMPILATION_DATE):
2338            if (length == 3):
2339                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2340                temp = temp[0:11]
2341                if type(temp) is not str:
2342                    temp = temp.decode('utf-8')
2343                self.cpu_low_compilation_date = temp
2344                                                 
2345            else:
2346                raise ex.ParameterError("NODE_WLAN_CPU_LOW_COMPILATION_DATE", "Incorrect length")
2347               
2348        elif   (identifier == NODE_PARAM_WLAN_CPU_LOW_COMPILATION_TIME):           
2349            if (length == 3):               
2350                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2351                temp = temp[0:8]
2352                if type(temp) is not str:
2353                    temp = temp.decode('utf-8')
2354                self.cpu_low_compilation_time = temp
2355            else:
2356                raise ex.ParameterError("NODE_WLAN_CPU_LOW_COMPILATION_TIME", "Incorrect length")     
2357               
2358        elif   (identifier == NODE_PARAM_WLAN_CPU_HIGH_COMPILATION_DATE):
2359            if (length == 3):
2360                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2361                temp = temp[0:11]
2362                if type(temp) is not str:
2363                    temp = temp.decode('utf-8')
2364                self.cpu_high_compilation_date = temp
2365            else:
2366                raise ex.ParameterError("NODE_WLAN_CPU_HIGH_COMPILATION_DATE", "Incorrect length")
2367               
2368        elif   (identifier == NODE_PARAM_WLAN_CPU_HIGH_COMPILATION_TIME):
2369            if (length == 3):
2370                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2371                temp = temp[0:8]
2372                if type(temp) is not str:
2373                    temp = temp.decode('utf-8')
2374                self.cpu_high_compilation_time = temp
2375            else:
2376                raise ex.ParameterError("NODE_WLAN_CPU_HIGH_COMPILATION_TIME", "Incorrect length")                 
2377               
2378
2379        else:
2380            super(WlanExpNode, self).process_parameter(identifier, length, values)
2381
2382
2383    #-------------------------------------------------------------------------
2384    # Misc methods for the Node
2385    #-------------------------------------------------------------------------
2386    def _set_node_type(self, node_type):
2387        """Set the node_type and keep the device_type in sync."""
2388        self.node_type   = node_type
2389        self.device_type = self.node_type
2390
2391    def _get_node_type_high(self):
2392        """Get the node type of CPU High
2393
2394        See defaults.py for a list of CPU High node types
2395        """
2396        return (self.node_type & defaults.WLAN_EXP_HIGH_MASK)
2397
2398    def _get_node_type_low(self):
2399        """Get the node type of CPU Low
2400
2401        See defaults.py for a list of CPU Low node types
2402        """
2403        return (self.node_type & defaults.WLAN_EXP_LOW_MASK)
2404
2405    def _check_cpu_high_type(self, high_type, command_name, raise_error=False):
2406        """Check the node CPU High type against the high_type argument
2407
2408        Args:
2409            high_type (int):    Node type for CPU High (see defaults.py)
2410            command_name (str): Name of command calling function
2411            raise_error (bool): Raise an exception?
2412        """
2413        node_high_type = self._get_node_type_high()
2414
2415        if (node_high_type != high_type):
2416            msg  = "WARNING:  CPU High Type mismatch.\n"
2417            msg += "    Command \'{0}()\' ".format(command_name)
2418
2419            try:
2420                msg += "expects {0}, ".format(defaults.WLAN_EXP_HIGH_TYPES[high_type])
2421            except:
2422                msg += "expects UNKNOWN TYPE, "
2423
2424            try:
2425                msg += "node reports {0}\n".format(defaults.WLAN_EXP_HIGH_TYPES[node_high_type])
2426            except:
2427                msg += "reports UNKNOWN TYPE\n"
2428
2429            msg += "    Command may have unintended effects.\n"
2430
2431            if (raise_error):
2432                raise TypeError(msg)
2433            else:
2434                print(msg)
2435
2436    def _check_cpu_low_type(self, low_type, command_name, raise_error=False):
2437        """Check the node CPU Low type against the low_type argument
2438
2439        Args:
2440            low_type (int):     Node type for CPU Low (see defaults.py)
2441            command_name (str): Name of command calling function
2442            raise_error (bool): Raise an exception?
2443        """
2444        node_low_type = self._get_node_type_low()
2445
2446        if (node_low_type != low_type):
2447            msg  = "WARNING:  CPU Low Type mismatch:\n"
2448            msg += "    Command \'{0}\' ".format(command_name)
2449
2450            try:
2451                msg += "expects {0}, ".format(defaults.WLAN_EXP_LOW_TYPES[low_type])
2452            except KeyError:
2453                msg += "expects UNKNOWN TYPE, "
2454
2455            try:
2456                msg += "node reports {0}\n".format(defaults.WLAN_EXP_LOW_TYPES[node_low_type])
2457            except KeyError:
2458                msg += "reports UNKNOWN TYPE\n"
2459
2460            msg += "    Command may have unintended effects.\n"
2461
2462            if (raise_error):
2463                raise TypeError(msg)
2464            else:
2465                print(msg)
2466
2467    def check_wlan_exp_ver(self):
2468        """Check the wlan_exp version of the node against the current wlan_exp
2469        version.
2470        """
2471        ver_str     = version.wlan_exp_ver_str(self.wlan_exp_ver_major,
2472                                               self.wlan_exp_ver_minor,
2473                                               self.wlan_exp_ver_revision)
2474
2475        caller_desc = "During initialization '{0}' returned version {1}".format(self.sn_str, ver_str)
2476
2477        status = version.wlan_exp_ver_check(major=self.wlan_exp_ver_major,
2478                                            minor=self.wlan_exp_ver_minor,
2479                                            revision=self.wlan_exp_ver_revision,
2480                                            caller_desc=caller_desc)
2481
2482        if (status == version.WLAN_EXP_VERSION_NEWER):
2483            print("Please update the C code on the node to the proper wlan_exp version.")
2484
2485        if (status == version.WLAN_EXP_VERSION_OLDER):
2486            print("Please update the wlan_exp installation to match the version on the node.")
2487
2488
2489# End Class WlanExpNode
2490
2491
2492
2493
2494class WlanExpNodeFactory(node.WarpNodeFactory):
2495    """Sub-class of WarpNodeFactory used to help with node configuration
2496    and setup.
2497    """
2498    def __init__(self, network_config=None):
2499        super(WlanExpNodeFactory, self).__init__(network_config)
2500
2501        # Add default classes to the factory
2502        self.node_add_class(defaults.WLAN_EXP_AP_DCF_TYPE,
2503                            defaults.WLAN_EXP_AP_DCF_CLASS_INST,
2504                            defaults.WLAN_EXP_AP_DCF_DESCRIPTION)
2505
2506        self.node_add_class(defaults.WLAN_EXP_STA_DCF_TYPE,
2507                            defaults.WLAN_EXP_STA_DCF_CLASS_INST,
2508                            defaults.WLAN_EXP_STA_DCF_DESCRIPTION)
2509
2510        self.node_add_class(defaults.WLAN_EXP_IBSS_DCF_TYPE,
2511                            defaults.WLAN_EXP_IBSS_DCF_CLASS_INST,
2512                            defaults.WLAN_EXP_IBSS_DCF_DESCRIPTION)
2513
2514        self.node_add_class(defaults.WLAN_EXP_AP_NOMAC_TYPE,
2515                            defaults.WLAN_EXP_AP_NOMAC_CLASS_INST,
2516                            defaults.WLAN_EXP_AP_NOMAC_DESCRIPTION)
2517
2518        self.node_add_class(defaults.WLAN_EXP_STA_NOMAC_TYPE,
2519                            defaults.WLAN_EXP_STA_NOMAC_CLASS_INST,
2520                            defaults.WLAN_EXP_STA_NOMAC_DESCRIPTION)
2521
2522        self.node_add_class(defaults.WLAN_EXP_IBSS_NOMAC_TYPE,
2523                            defaults.WLAN_EXP_IBSS_NOMAC_CLASS_INST,
2524                            defaults.WLAN_EXP_IBSS_NOMAC_DESCRIPTION)
2525
2526
2527    def node_eval_class(self, node_class, network_config):
2528        """Evaluate the node_class string to create a node.
2529
2530        This should be overridden in each sub-class with the same overall
2531        structure but a different import.  Please call the super class so that
2532        the calls will propagate to catch all node types.
2533
2534        network_config is used as part of the node_class string to initialize
2535        the node.
2536        """
2537        # "import wlan_exp.defaults as defaults" performed at the top of the file
2538        import wlan_exp.node_ap as node_ap
2539        import wlan_exp.node_sta as node_sta
2540        import wlan_exp.node_ibss as node_ibss
2541
2542        node = None
2543
2544        try:
2545            node = eval(node_class, globals(), locals())
2546        except:
2547            # We will catch all errors that might occur when trying to create
2548            # the node since this class might not be able to create the node.
2549            pass
2550
2551        if node is None:
2552            return super(WlanExpNodeFactory, self).node_eval_class(node_class,
2553                                                                   network_config)
2554        else:
2555            return node
2556
2557
2558# End Class WlanExpNodeFactory
Note: See TracBrowser for help on using the repository browser.