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

Last change on this file was 6192, checked in by murphpo, 11 days ago

Improved error checking in helper for mem_write_high/low methods. Now accepts scalar or iterable of anything that can be cast as an int in [0, 232-1].

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