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

Last change on this file was 6229, checked in by murphpo, 3 months ago

updating years

File size: 132.8 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3------------------------------------------------------------------------------
4Mango 802.11 Reference Design Experiments Framework - Node Classes
5------------------------------------------------------------------------------
6License:   Copyright 2014-2018, 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, require_pkt_det = False):
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            require_pkt_det (bool): Require packet detection prior to PHY Rx
1264        """   
1265        if (corr_thresh < 1) or (corr_thresh > 255):
1266            raise AttributeError("'corr_thresh' must be in [1 .. 255].")
1267        if (energy_thresh < 1) or (energy_thresh > 16383):
1268            raise AttributeError("'energy_thresh' must be in [1 .. 16383].")         
1269        if (min_dur < 0) or (min_dur > 15):
1270            raise AttributeError("'min_dur' must be in [0 .. 15].")   
1271        if (post_wait < 0) or (post_wait > 63):
1272            raise AttributeError("'post_wait' must be in [0 .. 63].") 
1273        if type(require_pkt_det) is not bool:
1274            raise AttributeError("require_pkt_det must be a bool.  Provided {0}.".format(type(require_pkt_det)))       
1275        return self.set_low_param(cmds.CMD_PARAM_LOW_PARAM_OFDM_PKT_DET_THRESH, (corr_thresh, energy_thresh, min_dur, post_wait, require_pkt_det))
1276   
1277    def configure_dsss_pkt_det(self, corr_thresh, energy_thresh, require_pkt_det = False):
1278        """Configures the DSSS auto-correlation packet detector thresholds.
1279       
1280        Args:
1281            corr_thresh (int):  Auto-correlation threshold (in [1, 255])
1282            energy_thresh (int):  Energy threshold (in [1, 16383])
1283            require_pkt_det (bool): Require packet detection prior to PHY Rx
1284        """   
1285        if (corr_thresh < 1) or (corr_thresh > 255):
1286            raise AttributeError("'corr_thresh' must be in [1 .. 255].")
1287        if (energy_thresh < 1) or (energy_thresh > 16383):
1288            raise AttributeError("'energy_thresh' must be in [1 .. 16383].")
1289        if type(require_pkt_det) is not bool:
1290            raise AttributeError("require_pkt_det must be a bool.  Provided {0}.".format(type(require_pkt_det)))       
1291        return self.set_low_param(cmds.CMD_PARAM_LOW_PARAM_DSSS_PKT_DET_THRESH, (corr_thresh, energy_thresh, require_pkt_det))
1292
1293
1294    def set_dcf_param(self, param_name, param_val):
1295        """Configures parameters of the DCF MAC in CPU Low.
1296       
1297        These parameters are write-only. These parameters only affect nodes
1298        running the DCF in CPU Low. Other MAC implementations should ignore
1299        these parameter updates.
1300
1301        Args:
1302            param_name (str): Name of the param to change (see table below)
1303            param_val (int): Value to set for param_name (see table below)
1304
1305        This method currently implements the following parameters:
1306
1307        .. list-table::
1308            :header-rows: 1
1309            :widths: 15 20 60
1310
1311            * - Name
1312              - Valid Values
1313              - Description
1314
1315            * - ``'rts_thresh'``
1316              - [0 .. 65535]
1317              - Threshold in bytes for maximum length
1318                packet which will not trigger RTS handshake
1319
1320            * - ``'short_retry_limit'`` and
1321                ``'long_retry_limit'``
1322              - [0 .. 10]
1323              - DCF retry limits, controls maximum number of retransmissions for short and long packets
1324                See `retransmissions <http://warpproject.org/trac/wiki/802.11/MAC/Lower/Retransmissions>`_.
1325                for more details.
1326
1327            * - ``'phy_cs_thresh'``
1328              - [0 .. 1023]
1329              - Physical carrier sensing threshold, in units of digital RSSI. Set to max (1023)
1330                to effectively disable physical carrier sensing
1331
1332            * - ``'cw_exp_min'`` and
1333                ``'cw_exp_max'``
1334              - [0 .. 16]
1335              - Contention window exponent bounds. Contention window is set to random number in [0, (2^CW - 1)],
1336                where CW is bounded by [cw_exp_min, cw_exp_max]
1337
1338
1339        """
1340        if type(param_name) is not str:
1341            raise AttributeError("param_name must be a str.  Provided {0}.".format(type(param_name)))
1342       
1343        if type(param_val) is not int:
1344            raise AttributeError("param_val must be an int.  Provided {0}.".format(type(param_val)))
1345       
1346        # Process paramater
1347        if   (param_name == 'rts_thresh'):
1348            if ((param_val < 0) or (param_val > 65535)):
1349                raise AttributeError("'rts_thresh' must be in [0 .. 65535].")
1350           
1351            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('rts_thresh')")
1352
1353            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_RTS_THRESH, param_values=param_val)
1354           
1355        elif (param_name == 'short_retry_limit'):
1356            if ((param_val < 0) or (param_val > 65535)):
1357                raise AttributeError("'short_retry_limit' must be in [0 .. 65535].")
1358               
1359            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('short_retry_limit')")
1360   
1361            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_DOT11SHORTRETRY, param_values=param_val)
1362           
1363        elif (param_name == 'long_retry_limit'):
1364            if ((param_val < 0) or (param_val > 65535)):
1365                raise AttributeError("'long_retry_limit' must be in [0 .. 65535].")
1366               
1367            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('long_retry_limit')")
1368   
1369            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_DOT11LONGRETRY, param_values=param_val)
1370           
1371        elif (param_name == 'phy_cs_thresh'):
1372            if ((param_val < 0) or (param_val > 1023)):
1373                raise AttributeError("'phy_cs_thresh' must be in [0 .. 1023].")
1374           
1375            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('phy_cs_thresh')")
1376   
1377            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_PHYSICAL_CS_THRESH, param_values=param_val)
1378           
1379        elif (param_name == 'cw_exp_min'):
1380            if ((param_val < 0) or (param_val > 16)):
1381                raise AttributeError("'cw_exp_min' must be in [0 .. 16].")
1382               
1383            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('cw_exp_min')")
1384   
1385            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MIN, param_values=param_val)
1386           
1387        elif (param_name == 'cw_exp_max'):
1388            if ((param_val < 0) or (param_val > 16)):
1389                raise AttributeError("'cw_exp_max' must be in [0 .. 16].")
1390               
1391            self._check_cpu_low_type(low_type=defaults.WLAN_EXP_LOW_DCF, command_name="set_dcf_param('cw_exp_max')")
1392   
1393            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_DCF_CW_EXP_MAX, param_values=param_val)
1394           
1395        else:
1396            msg  = "param_name must be one of the following strings:\n"
1397            msg += "    'rts_thresh', 'short_retry_limit', 'long_retry_limit', \n"
1398            msg += "    'phy_cs_thresh', 'cw_exp_min', 'cw_exp_max' \n"
1399            msg += "Provided '{0}'".format(param_name)
1400            raise AttributeError(msg)
1401
1402
1403    def configure_pkt_det_min_power(self, enable, power_level=None):
1404        """Configure Minimum Power Requirement of Packet Detector.
1405
1406        Args:
1407            enable (bool):      True/False
1408            power_level (int):  [-90,-30] dBm
1409        """
1410        if enable is False:
1411            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_PKT_DET_MIN_POWER, param_values=0)
1412        else:
1413            if power_level is not None:
1414                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:
1415                    # C code expects a u32
1416                    #  Bit 24 is flag for en/disable of min_power logic
1417                    #  Bits [7:0] are u8 interpreted as dB above min threshold
1418                    param = (1 << 24) | ((power_level - cmds.CMD_PARAM_NODE_MIN_MIN_PKT_DET_POWER_DBM) & 0xFF)
1419                    self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_PKT_DET_MIN_POWER, param_values=param)
1420                else:
1421                    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)
1422                    raise ValueError(msg)
1423            else:
1424                msg  = "\nPower level not specified\n"
1425                raise ValueError(msg)
1426
1427
1428    def set_phy_samp_rate(self, phy_samp_rate):
1429        """Sets the PHY sample rate (in MSps)
1430
1431        Args:
1432            phy_samp_rate (int):   
1433                PHY sample rate in MSps (10, 20, 40).  Default is 20 MSps.
1434        """
1435        if (phy_samp_rate not in [10, 20, 40]):
1436            raise AttributeError("'phy_samp_rate' must be in [10, 20, 40].")
1437           
1438        self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_PHY_SAMPLE_RATE, param_values=phy_samp_rate)
1439
1440
1441    def set_random_seed(self, high_seed=None, low_seed=None, gen_random=False):
1442        """Sets the random number generator seed on the node.
1443
1444        Args:
1445            high_seed (int, optional):   Set the random number generator seed
1446                on CPU high
1447            low_seed (int, optional):    Set the random number generator seed
1448                on CPU low
1449            gen_random (bool, optional): If high_seed or low_seed is not
1450                provided, then generate a random seed for the generator.
1451        """
1452        import random
1453        max_seed = 2**32 - 1
1454        min_seed = 0
1455
1456        if (high_seed is None):
1457            if gen_random:
1458                high_seed = random.randint(min_seed, max_seed)
1459        else:
1460            if (high_seed > max_seed) or (high_seed < min_seed):
1461                msg  = "Seed must be an integer between [{0}, {1}]".format(min_seed, max_seed)
1462                raise AttributeError(msg)
1463
1464        if (low_seed is None):
1465            if gen_random:
1466                low_seed  = random.randint(min_seed, max_seed)
1467        else:
1468            if (low_seed > max_seed) or (low_seed < min_seed):
1469                msg  = "Seed must be an integer between [{0}, {1}]".format(min_seed, max_seed)
1470                raise AttributeError(msg)
1471
1472        self.send_cmd(cmds.NodeProcRandomSeed(cmds.CMD_PARAM_WRITE, high_seed, low_seed))
1473
1474
1475    def enable_dsss(self, enable):
1476        """Enable / Disable DSSS receptions on the node
1477
1478        By default DSSS receptions are enabled on the node when possible
1479        (ie PHY sample rate is 20 MHz and Channel is in 2.4 GHz band)
1480       
1481        Args:
1482            enable (bool): 
1483           
1484              * True - enable DSSS receptions on the node
1485              * False - disable DSSS receptions on the node
1486        """
1487        self.send_cmd(cmds.NodeConfigure(dsss_enable=enable))
1488
1489    def enable_ofdm_rx(self, enable):
1490        """Enable / Disable OFDM receptions on the node
1491
1492        OFDM receptions are enabled by default. The OFDM Rx pipeline can
1493        be disabled which will block all OFDM packet detections and OFDM
1494        Rx attempts. When OFDM Rx is disabled only the DSSS Rx pipeline
1495        will attempt to receive new packets.
1496       
1497        Args:
1498            enable (bool): 
1499              * True - enable OFDM receptions on the node
1500              * False - disable OFDM receptions on the node
1501        """
1502        return self.set_low_param(cmds.CMD_PARAM_LOW_PARAM_OFDM_RX_EN, int(enable))
1503       
1504    def set_print_level(self, level):
1505        """Set the verbosity of the wlan_exp output to the node's UART.
1506
1507        Args:
1508            level (int):  Printing level (defaults to ``WARNING``)
1509
1510        Valid print levels can be found in ``wlan_exp.util.uart_print_levels``:
1511       
1512          * ``NONE``    - Do not print messages
1513          * ``ERROR``   - Only print error messages
1514          * ``WARNING`` - Print error and warning messages
1515          * ``INFO``    - Print error, warning and info messages
1516          * ``DEBUG``   - Print error, warning, info and debug messages
1517        """
1518        import wlan_exp.util as util
1519       
1520        valid_levels = ['NONE', 'ERROR', 'WARNING', 'INFO', 'DEBUG',
1521                        util.uart_print_levels['NONE'], 
1522                        util.uart_print_levels['ERROR'],
1523                        util.uart_print_levels['WARNING'],
1524                        util.uart_print_levels['INFO'],
1525                        util.uart_print_levels['DEBUG']]
1526       
1527        if (level in valid_levels):
1528            self.send_cmd(cmds.NodeConfigure(print_level=level))
1529        else:
1530            msg  = "\nInvalid print level {0}.  Print level must be one of: \n".format(level)
1531            msg += "    ['NONE', 'ERROR', 'WARNING', 'INFO', 'DEBUG', \n"
1532            msg += "     uart_print_levels['NONE'], uart_print_levels['ERROR'],\n"
1533            msg += "     uart_print_levels['WARNING'], uart_print_levels['INFO'],\n"
1534            msg += "     uart_print_levels['DEBUG']]"
1535            raise ValueError(msg)
1536           
1537
1538
1539    #--------------------------------------------
1540    # Internal methods to view / configure node attributes
1541    #--------------------------------------------
1542    def _check_allowed_rate(self, mcs, phy_mode, verbose=False):
1543        """Check that rate parameters are allowed
1544
1545        Args:
1546            mcs (int):           Modulation and coding scheme (MCS) index
1547            phy_mode (str, int): PHY mode (from util.phy_modes)
1548
1549        Returns:
1550            valid (bool):  Are all parameters valid?
1551        """
1552        return self._check_supported_rate(mcs, phy_mode, verbose)
1553
1554
1555    def _check_supported_rate(self, mcs, phy_mode, verbose=False):
1556        """Checks that the selected rate parameters are supported by the
1557        current MAC and PHY implementation. This method only checks if a
1558        rate can be used in hardware. The application-specific method
1559        _check_allowed_rate() should be used to confirm a selected rate is
1560        both supported and allowed given the currnet MAC and network state.
1561
1562        Args:
1563            mcs (int): Modulation and coding scheme (MCS) index (in [0 .. 7])
1564            phy_mode (str, int): PHY mode.  Must be one of:
1565
1566                * ``'NONHT'``: Use 802.11 (a/g) rates
1567                * ``'HTMF'``: Use 802.11 (n) rates
1568
1569        Returns:
1570            rate_suppored (bool):  True if the specified rate is supported
1571        """
1572        import wlan_exp.util as util
1573
1574        rate_ok = True
1575
1576        if ((mcs < 0) or (mcs > 7)):
1577            if (verbose):
1578                print("Invalid MCS {0}. MCS must be integer in [0 .. 7]".format(mcs))
1579            rate_ok = False
1580
1581        if (phy_mode not in ['NONHT', 'HTMF', util.phy_modes['NONHT'], util.phy_modes['HTMF']]):
1582            if (verbose):
1583                print("Invalid PHY mode {0}. PHY mode must be one of ['NONHT', 'HTMF', phy_modes['NONHT'], phy_modes['HTMF']]".format(phy_mode))
1584            rate_ok = False
1585
1586        return rate_ok
1587
1588
1589    def _node_set_tx_param(self, cmd, param_name, param, frametype,
1590                               device_list=None, update_default_unicast=None, update_default_multicast=None):
1591        """Sets the data & management transmit parameters of the node.
1592
1593        Args:
1594            cmd (Cmd):          Command to be used to set param
1595            param_name (str):   Name of parameter for error messages
1596            param (int):        Parameter to be set
1597            frametype(str):     `data` or `mgmt`
1598            device_list (list of WlanExpNode / WlanDevice
1599             or 'ALL_UNICAST' or 'ALL_MULTICAST' or 'ALL', optional):
1600                List of 802.11 devices or single 802.11 device for which to set the
1601                unicast packet Tx power to 'power'. A value of 'ALL_UNICAST' will
1602                apply the specified power to all unicast receiver addresses. A value
1603                of `ALL_MULTICAST` will apply it to all multicast receiver addresses.
1604                A value of 'ALL' will apply the specified power to all addresses.
1605            update_default_unicast  (bool): set the default unicast Tx params to the
1606                provided 'power'.
1607            update_default_multicast  (bool): set the default multicast Tx params to the
1608                provided 'power'.         
1609
1610        One of ``device_list`` or ``default`` must be set.         
1611        """
1612        if (device_list is None) and (update_default_unicast is None) and (update_default_multicast is None):
1613            msg  = "\nCannot set the unicast transmit {0}:\n".format(param_name)
1614            msg += "    Must specify either a list of devices, 'ALL' current station infos,\n"
1615            msg += "    or update_default_unicast or update_default_multicast {0}.".format(param_name)
1616            raise ValueError(msg)
1617           
1618        if( (update_default_unicast is True) or (device_list == 'ALL_UNICAST') or (device_list == 'ALL') ):
1619            update_default_unicast = 1;
1620        else:
1621            update_default_unicast = 0;
1622
1623        if( (update_default_multicast is True) or (device_list == 'ALL_MULTICAST') or (device_list == 'ALL') ):
1624            update_default_multicast = 1;
1625        else:
1626            update_default_multicast = 0;
1627           
1628
1629        if(frametype == 'data'):
1630            if(device_list == 'ALL_UNICAST'):
1631                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))
1632            elif(device_list == 'ALL_MULTICAST'):
1633                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))
1634            elif(device_list == 'ALL'):
1635                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))               
1636            elif(device_list is not None):
1637                try:
1638                    for device in device_list:
1639                        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))
1640                except TypeError:
1641                    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))
1642            else:
1643                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))
1644        elif(frametype == 'mgmt'):
1645            if(device_list == 'ALL_UNICAST'):
1646                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))
1647            elif(device_list == 'ALL_MULTICAST'):
1648                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))
1649            elif(device_list == 'ALL'):
1650                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))               
1651            elif(device_list is not None):
1652                try:
1653                    for device in device_list:
1654                        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))
1655                except TypeError:
1656                    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))
1657            else:
1658                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))
1659     
1660    def _set_bb_gain(self, bb_gain):
1661        """Sets the the baseband gain.
1662
1663        Args:
1664            bb_gain (int):  Baseband gain setting [0,3]
1665        """
1666        if bb_gain is not None:
1667            if (bb_gain >= 0) and (bb_gain <=3):
1668                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_BB_GAIN, param_values=bb_gain)
1669            else:
1670                msg  = "\nBB Gain must be in the range [0,3]\n"
1671                raise ValueError(msg)
1672
1673
1674    def _set_linearity_pa(self, linearity_val):
1675        """Sets the the PA linearity.
1676
1677        Args:
1678            linearity_val (int):  Linearity setting [0,3]
1679        """
1680        if linearity_val is not None:
1681            if (linearity_val >= 0) and (linearity_val <=3):
1682                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_LINEARITY_PA, param_values=linearity_val)
1683            else:
1684                msg  = "\nBB Linearity must be in the range [0,3]\n"
1685                raise ValueError(msg)
1686
1687
1688    def _set_interp_filt_scaling(self, scale_int0=0x10, scale_int1=0x10, scale_srrc=0x10):
1689        """Sets the the DAC scaling at the output of each stage of interpolations.
1690
1691        Args:
1692            scale_int0 (int):  Scaling Stage 0    [0,31]
1693            scale_int1 (int):  Scaling Stage 0    [0,31]
1694            scale_srrc (int):  Scaling Stage SRRC [0,31]
1695        """
1696
1697        if (scale_int0 >= 0) and (scale_int0 <=31) and (scale_int1 >= 0) and (scale_int1 <=31) and (scale_srrc >= 0) and (scale_srrc <=31):
1698            self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_AD_SCALING, param_values=[scale_int0, scale_int1, scale_srrc])
1699        else:
1700            msg  = "\nInterp scalings must be in the range [0,31]\n"
1701            raise ValueError(msg)
1702
1703
1704    def _set_linearity_vga(self, linearity_val):
1705        """Sets the the VGA linearity.
1706
1707        Args:
1708            linearity_val (int):  Linearity setting [0,3]
1709        """
1710        if linearity_val is not None:
1711            if (linearity_val >= 0) and (linearity_val <=3):
1712                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_LINEARITY_VGA, param_values=linearity_val)
1713            else:
1714                msg  = "\nBB Linearity must be in the range [0,3]\n"
1715                raise ValueError(msg)
1716
1717
1718    def _set_linearity_upconv(self, linearity_val):
1719        """Sets the the upconversion linearity.
1720
1721        Args:
1722            linearity_val (int):  Linearity setting [0,3]
1723        """
1724        if linearity_val is not None:
1725            if (linearity_val >= 0) and (linearity_val <=3):
1726                self.set_low_param(param_id=cmds.CMD_PARAM_LOW_PARAM_LINEARITY_UPCONV, param_values=linearity_val)
1727            else:
1728                msg  = "\nBB Linearity must be in the range [0,3]\n"
1729                raise ValueError(msg)
1730
1731
1732
1733    #--------------------------------------------
1734    # Scan Commands
1735    #--------------------------------------------
1736    def set_scan_parameters(self, time_per_channel=None, num_probe_tx_per_channel=None, channel_list=None, ssid=None):
1737        """Set the paramters of the wireless network scan state machine.
1738       
1739        Args:
1740            time_per_channel (float, optional): Time (in float sec) to spend
1741                on each channel.  A value of None will not modify the current
1742                time per channel
1743            num_probe_tx_per_channel (int, optional):   Number of probe requests
1744                transmitted while on each channel.  A value of None will not
1745                modify the current number of probe requests per channel.
1746            channel_list (list of int optional): Channel(s) to scan; A value
1747                of None will not modify the current channel list.
1748            ssid (str, optional):  SSID to scan for (as part of probe request);
1749                A value of None will not modify the current SSID.
1750       
1751        Setting ``num_probe_tx_per_chan`` to a non-zero value will enable
1752        active scanning. The node will transmit broadcast Probe Request packets
1753        on every channel and will gather network information from received
1754        Probe Response and Beacon packets. Setting ``num_probe_tx_per_chan=0``
1755        will enable passive scanning. In this mode the node will not transmit
1756        any Probe Request packets and network information will be gathered only
1757        from received Beacon packets.
1758
1759        The blank SSID (``ssid=""``) is interpretted as a wildcard and will
1760        solicit Probe Response packets from networks with any SSID. "Closed"
1761        networks do not respond to the wildcard SSID. These networks will still
1762        be discovered via Beacon receptions.
1763
1764        If the channel list / SSID is not specified, then it will not be
1765        updated on the node (ie it will use the current channel list / SSID)
1766
1767        """
1768        # Check time_per_channel
1769        if time_per_channel is not None:
1770            try:
1771                time_per_channel = float(time_per_channel)
1772            except:
1773                raise AttributeError("time_per_channel must be expressable as a float.")
1774               
1775        # Check channel_list
1776        if channel_list is not None:
1777            tmp_chan_list = []
1778           
1779            if type(channel_list) is str:
1780                # Process pre-defined strings
1781                import wlan_exp.util as util
1782
1783                if   (channel_list == 'ALL'):
1784                    tmp_chan_list = util.wlan_channels
1785                elif (channel_list == 'ALL_2.4GHZ'):
1786                    tmp_chan_list = [x for x in util.wlan_channels if (x <= 14)]
1787                elif (channel_list == 'ALL_5GHZ'):
1788                    tmp_chan_list = [x for x in util.wlan_channels if (x > 14)]
1789                else:
1790                    msg  = "\n    String '{0}' not recognized.".format(channel_list)
1791                    msg += "\n    Please use 'ALL', 'ALL_2.4GHZ', 'ALL_5GHZ' or either an int or list of channels"
1792                    raise AttributeError(msg)
1793
1794            elif type(channel_list) is int:
1795                # Proess scalar integer
1796                tmp_chan_list.append(channel_list)
1797               
1798            else:
1799                # Proess interables
1800                try:
1801                    for channel in channel_list:
1802                        tmp_chan_list.append(channel)
1803                except:
1804                    msg  = "\n    Unable to process channel_list."
1805                    msg += "\n    Please use 'ALL', 'ALL_2.4GHZ', 'ALL_5GHZ' or either an int or list of channels"
1806                    raise AttributeError(msg)
1807
1808            if tmp_chan_list:
1809                channel_list = tmp_chan_list
1810            else:
1811                msg  = "\n    channel_list is empty."
1812                msg += "\n    Please use 'ALL', 'ALL_2.4GHZ', 'ALL_5GHZ' or either an int or list of channels"
1813                raise AttributeError(msg)
1814               
1815        self.send_cmd(cmds.NodeProcScanParam(cmds.CMD_PARAM_WRITE, time_per_channel, num_probe_tx_per_channel, channel_list, ssid))
1816   
1817   
1818    def start_network_scan(self):
1819        """Starts the wireless network scan state machine at the node.
1820       
1821        During a scan, the node cycles through a set of channels and transmits
1822        periodic Probe Request frames on each channel.  Information about
1823        available wireless networks is extracted from received Probe Responses
1824        and Beacon frames. The network scan results can be queried any time
1825        using the ``node.get_network_list()`` method.
1826
1827        Network scans can only be run by unassociated nodes. An associated
1828        node must first reset its BSS state before starting a scan.
1829
1830        The network scan state machine can be stopped with
1831        ``node.stop_network_scan()``. The scan state machine will also be
1832        stopped automatically if the node is configured with a new non-null
1833        BSS state.
1834           
1835        Example:
1836        ::
1837
1838            # Ensure node has null BSS state
1839            n.configure_bss(None)
1840
1841            # Start the scan state machine; scan will use default scan params
1842            #  Use n.set_scan_parameters() to customize scan behavior
1843            n.start_network_scan()
1844
1845            # Wait 5 seconds, retrieve the list of discovered networks
1846            time.sleep(5)
1847            networks = n.get_network_list()
1848
1849            # Stop the scan state machine
1850            n.stop_network_scan()
1851
1852        """
1853        self.send_cmd(cmds.NodeProcScan(enable=True))
1854   
1855   
1856    def stop_network_scan(self):
1857        """Stops the wireless network scan state machine."""
1858        self.send_cmd(cmds.NodeProcScan(enable=False))
1859
1860
1861    def is_scanning(self):
1862        """Queries if the node's wireless network scanning state machine is
1863        currently running.
1864       
1865        Returns:
1866            status (bool):
1867
1868                * True      -- Scan state machine is running
1869                * False     -- Scan state machine is not running
1870        """
1871        return self.send_cmd(cmds.NodeProcScan())
1872
1873
1874
1875    #--------------------------------------------
1876    # Association Commands
1877    #--------------------------------------------
1878    def configure_bss(self):
1879        """Configure the Basic Service Set (BSS) information of the node
1880       
1881        Each node is either a member of no BSS (colloquially "unassociated")
1882        or a member of one BSS.  A node requires a minimum valid bss_info to
1883        be a member of a BSS.  Based on the node type, there is a minimum
1884        set of fields needed for a valid bss_info. 
1885       
1886        This method must be overloaded by sub-classes.
1887       
1888        See http://warpproject.org/trac/wiki/802.11/wlan_exp/bss for more
1889        information about BSSes.
1890        """
1891        raise NotImplementedError()
1892
1893    def get_station_info(self, device_list=None):
1894       
1895        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.')
1896       
1897    def get_bss_members(self):
1898        """Get the BSS members from the node.
1899
1900        The StationInfo() returned by this method can be accessed like a
1901        dictionary and has the following fields:
1902       
1903            +-----------------------------+----------------------------------------------------------------------------------------------------+
1904            | Field                       | Description                                                                                        |
1905            +=============================+====================================================================================================+
1906            | mac_addr                    |  MAC address of station                                                                            |
1907            +-----------------------------+----------------------------------------------------------------------------------------------------+
1908            | id                          |  Identification Index for this station                                                             |
1909            +-----------------------------+----------------------------------------------------------------------------------------------------+
1910            | host_name                   |  String hostname (19 chars max), taken from DHCP DISCOVER packets                                  |
1911            +-----------------------------+----------------------------------------------------------------------------------------------------+
1912            | flags                       |  Station flags.  Value containts 1 bit fields:                                                     |
1913            |                             |      * 0x0001 - 'KEEP'                                                                             |
1914            |                             |      * 0x0002 - 'DISABLE_ASSOC_CHECK'                                                              |
1915            |                             |      * 0x0004 - 'DOZE'                                                                             |
1916            |                             |      * 0x0008 - 'HT_CAPABLE'                                                                       |
1917            |                             |                                                                                                    |
1918            +-----------------------------+----------------------------------------------------------------------------------------------------+
1919            | latest_rx_timestamp         |  Value of System Time in microseconds of last successful Rx from device                            |
1920            +-----------------------------+----------------------------------------------------------------------------------------------------+
1921            | latest_txrx_timestamp       |  Value of System Time in microseconds of last Tx or successful Rx from device                      |           
1922            +-----------------------------+----------------------------------------------------------------------------------------------------+
1923            | latest_rx_seq               |  Sequence number of last packet received from device                                               |
1924            +-----------------------------+----------------------------------------------------------------------------------------------------+
1925            | tx_phy_mcs                  |  Current PHY MCS index in [0:7] for new transmissions to device                                    |
1926            +-----------------------------+----------------------------------------------------------------------------------------------------+
1927            | tx_phy_mode                 |  Current PHY mode for new transmissions to deivce                                                  |
1928            +-----------------------------+----------------------------------------------------------------------------------------------------+
1929            | tx_phy_antenna_mode         |  Current PHY antenna mode in [1:4] for new transmissions to device                                 |
1930            +-----------------------------+----------------------------------------------------------------------------------------------------+
1931            | tx_phy_power                |  Current Tx power in dBm for new transmissions to device                                           |
1932            +-----------------------------+----------------------------------------------------------------------------------------------------+
1933            | tx_mac_flags                |  Flags for Tx MAC config for new transmissions to device.  Value contains 1 bit flags:             |
1934            |                             |                                                                                                    |
1935            |                             |      * None defined                                                                                |
1936            |                             |                                                                                                    |
1937            +-----------------------------+----------------------------------------------------------------------------------------------------+
1938       
1939        Returns:
1940            station_infos (list of StationInfo): 
1941                List of StationInfo() BSS members known to the node
1942
1943        """
1944        ret_val = self.send_cmd(cmds.NodeGetBSSMembers())
1945
1946        return ret_val
1947
1948
1949    def get_station_info_list(self):
1950        """Get the all Station Infos from node.
1951
1952        The StationInfo() returned by this method can be accessed like a
1953        dictionary and has the following fields:
1954       
1955            +-----------------------------+----------------------------------------------------------------------------------------------------+
1956            | Field                       | Description                                                                                        |
1957            +=============================+====================================================================================================+
1958            | mac_addr                    |  MAC address of station                                                                            |
1959            +-----------------------------+----------------------------------------------------------------------------------------------------+
1960            | id                          |  Identification Index for this station                                                             |
1961            +-----------------------------+----------------------------------------------------------------------------------------------------+
1962            | host_name                   |  String hostname (19 chars max), taken from DHCP DISCOVER packets                                  |
1963            +-----------------------------+----------------------------------------------------------------------------------------------------+
1964            | flags                       |  Station flags.  Value containts 1 bit fields:                                                     |
1965            |                             |      * 0x0001 - 'KEEP'                                                                             |
1966            |                             |      * 0x0002 - 'DISABLE_ASSOC_CHECK'                                                              |
1967            |                             |      * 0x0004 - 'DOZE'                                                                             |
1968            |                             |      * 0x0008 - 'HT_CAPABLE'                                                                       |
1969            |                             |                                                                                                    |
1970            +-----------------------------+----------------------------------------------------------------------------------------------------+
1971            | latest_rx_timestamp         |  Value of System Time in microseconds of last successful Rx from device                            |
1972            +-----------------------------+----------------------------------------------------------------------------------------------------+
1973            | latest_txrx_timestamp       |  Value of System Time in microseconds of last Tx or successful Rx from device                      |           
1974            +-----------------------------+----------------------------------------------------------------------------------------------------+
1975            | latest_rx_seq               |  Sequence number of last packet received from device                                               |
1976            +-----------------------------+----------------------------------------------------------------------------------------------------+
1977            | tx_phy_mcs                  |  Current PHY MCS index in [0:7] for new transmissions to device                                    |
1978            +-----------------------------+----------------------------------------------------------------------------------------------------+
1979            | tx_phy_mode                 |  Current PHY mode for new transmissions to deivce                                                  |
1980            +-----------------------------+----------------------------------------------------------------------------------------------------+
1981            | tx_phy_antenna_mode         |  Current PHY antenna mode in [1:4] for new transmissions to device                                 |
1982            +-----------------------------+----------------------------------------------------------------------------------------------------+
1983            | tx_phy_power                |  Current Tx power in dBm for new transmissions to device                                           |
1984            +-----------------------------+----------------------------------------------------------------------------------------------------+
1985            | tx_mac_flags                |  Flags for Tx MAC config for new transmissions to device.  Value contains 1 bit flags:             |
1986            |                             |                                                                                                    |
1987            |                             |      * None defined                                                                                |
1988            |                             |                                                                                                    |
1989            +-----------------------------+----------------------------------------------------------------------------------------------------+
1990       
1991        Returns:
1992            station_infos (list of StationInfo): 
1993                List of all StationInfo() known to the node
1994
1995        """
1996        ret_val = self.send_cmd(cmds.NodeGetStationInfoList())
1997
1998        return ret_val
1999
2000    def get_bss_info(self):
2001       print('WARNING: get_bss_info() is deprecated and will be removed in a future version. Please use get_network_info()')
2002       return self.get_network_info()
2003
2004    def get_bss_config(self):
2005        """Get BSS configuration of the network the node is a member of
2006
2007        Returns a dictionary with the following fields:
2008       
2009            +-----------------------------+----------------------------------------------------------------------------------------------------+
2010            | Field                       | Description                                                                                        |
2011            +=============================+====================================================================================================+
2012            | bssid                       |  BSS ID: 48-bit MAC address                                                                        |
2013            +-----------------------------+----------------------------------------------------------------------------------------------------+
2014            | channel                     |  Primary channel.  In util.wlan_channels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48]     |
2015            +-----------------------------+----------------------------------------------------------------------------------------------------+
2016            | channel_type                |  Channel Type.  Value is one of:                                                                   |
2017            |                             |                                                                                                    |
2018            |                             |      * 0x00 - 'BW20'                                                                               |
2019            |                             |      * 0x01 - 'BW40_SEC_BELOW'                                                                     |
2020            |                             |      * 0x02 - 'BW40_SEC_ABOVE'                                                                     |
2021            |                             |                                                                                                    |
2022            +-----------------------------+----------------------------------------------------------------------------------------------------+
2023            | ssid                        |  SSID (32 chars max)                                                                               |
2024            +-----------------------------+----------------------------------------------------------------------------------------------------+
2025            | ht_capable                  |  1 - Network is capable of HT PHY mode                                                             |
2026            |                             |  0 - Netowrk is not capable of NHT PHY mode                                                        |
2027            +-----------------------------+----------------------------------------------------------------------------------------------------+
2028            | beacon_interval             |  Beacon interval - In time units of 1024 us'                                                       |
2029            +-----------------------------+----------------------------------------------------------------------------------------------------+           
2030            | dtim_period                 | 
2031            +-----------------------------+----------------------------------------------------------------------------------------------------+       
2032           
2033        Returns:
2034            bss_config : 
2035                BSS configuration of the network that the node is a member of (can be None)
2036        """
2037        network_info = self.get_network_info()
2038       
2039        if(network_info is None):
2040            # Node has NULL active_network_info - return None
2041            return None
2042       
2043        # Use the field names of the BSSConfig InfoStruct to transform the network_info
2044        #  into a bss_config dictionary
2045        from wlan_exp.info import BSSConfig
2046       
2047        bss_config_fields = BSSConfig().get_field_names()
2048       
2049        # Construct a dictionary with only BSSConfig fields
2050        bss_config = {}
2051        for f in bss_config_fields:
2052            bss_config[f] = network_info[f]
2053       
2054        return bss_config
2055
2056    def get_network_info(self):
2057        """Get information about the network the node is a member of
2058
2059        The NetworkInfo() returned by this method can be accessed like a
2060        dictionary and has the following fields:
2061       
2062            +-----------------------------+----------------------------------------------------------------------------------------------------+
2063            | Field                       | Description                                                                                        |
2064            +=============================+====================================================================================================+
2065            | bssid                       |  BSS ID: 48-bit MAC address                                                                        |
2066            +-----------------------------+----------------------------------------------------------------------------------------------------+
2067            | channel                     |  Primary channel.  In util.wlan_channels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48]     |
2068            +-----------------------------+----------------------------------------------------------------------------------------------------+
2069            | channel_type                |  Channel Type.  Value is one of:                                                                   |
2070            |                             |                                                                                                    |
2071            |                             |      * 0x00 - 'BW20'                                                                               |
2072            |                             |      * 0x01 - 'BW40_SEC_BELOW'                                                                     |
2073            |                             |      * 0x02 - 'BW40_SEC_ABOVE'                                                                     |
2074            |                             |                                                                                                    |
2075            +-----------------------------+----------------------------------------------------------------------------------------------------+
2076            | ssid                        |  SSID (32 chars max)                                                                               |
2077            +-----------------------------+----------------------------------------------------------------------------------------------------+
2078            | ht_capable                  |  1 - Network is capable of HT PHY mode                                                             |
2079            |                             |  0 - Netowrk is not capable of NHT PHY mode                                                        |
2080            +-----------------------------+----------------------------------------------------------------------------------------------------+
2081            | beacon_interval             |  Beacon interval - In time units of 1024 us'                                                       |
2082            +-----------------------------+----------------------------------------------------------------------------------------------------+           
2083            | dtim_period                 | 
2084            +-----------------------------+----------------------------------------------------------------------------------------------------+
2085            | flags                       |  Value contains 1 bit fields:                                                                      |
2086            |                             |                                                                                                    |
2087            |                             |      * 0x0001 - 'KEEP'                                                                             |
2088            +-----------------------------+----------------------------------------------------------------------------------------------------+
2089            | capabilities                |  Supported capabilities of the BSS.  Value contains 1 bit fields:                                  |
2090            |                             |                                                                                                    |
2091            |                             |      * 0x0001 - 'ESS'                                                                              |
2092            |                             |      * 0x0002 - 'IBSS'                                                                             |
2093            |                             |      * 0x0010 - 'PRIVACY'                                                                          |
2094            |                             |                                                                                                    |
2095            +-----------------------------+----------------------------------------------------------------------------------------------------+
2096            | latest_beacon_rx_time       |  Value of System Time in microseconds of last beacon Rx                                            |
2097            +-----------------------------+----------------------------------------------------------------------------------------------------+
2098            | latest_beacon_rx_power      |  Last observed beacon Rx Power (dBm)                                                               |
2099            +-----------------------------+----------------------------------------------------------------------------------------------------+           
2100
2101           
2102        Returns:
2103            network_info (NetworkInfo): 
2104                Information about network that the node is a member of (can be None)
2105        """
2106        ret_val = self.send_cmd(cmds.NodeGetNetworkInfo())
2107       
2108        if (len(ret_val) == 1):
2109            ret_val = ret_val[0]
2110        else:
2111            ret_val = None
2112
2113        return ret_val
2114
2115
2116    def get_network_list(self):
2117        """Get a list of known networks (NetworkInfo()s) on the node
2118
2119        Returns:
2120            networks (list of NetworkInfo): 
2121                List of NetworkInfo() that are known to the node
2122        """
2123        return self.send_cmd(cmds.NodeGetNetworkInfo("ALL"))
2124
2125
2126
2127    #--------------------------------------------
2128    # Queue Commands
2129    #--------------------------------------------
2130    def queue_tx_data_purge_all(self):
2131        """Purges all data transmit queues on the node.
2132       
2133        This will discard all currently enqueued packets awaiting transmission
2134        at the time the command is received.  This will not discard packets
2135        already submitted to the lower-level MAC for transmission.  Also, this
2136        will not stop additional packets from sources such as LTGs from being
2137        enqueued.
2138       
2139        This command is equivalent to ``reset(queue_data=True)``.
2140        """
2141        self.send_cmd(cmds.QueueTxDataPurgeAll())
2142
2143
2144
2145    #--------------------------------------------
2146    # Braodcast Commands can be found in util.py
2147    #--------------------------------------------
2148
2149
2150
2151    #--------------------------------------------
2152    # Node User Commands
2153    #--------------------------------------------
2154    def send_user_command(self, cmd_id, args=None):
2155        """Send User defined command to the node
2156
2157        See documentation on how-to extend wlan_exp:
2158        http://warpproject.org/trac/wiki/802.11/wlan_exp/Extending
2159
2160        Args:
2161            cmd_id (u32):  User-defined Command ID
2162            args (u32, list of u32):  Scalar or list of u32 command arguments
2163                to send to the node
2164
2165        Returns:
2166            resp_args (list of u32): 
2167                List of u32 response arguments received from the node
2168        """
2169        if cmd_id is None:
2170            raise AttributeError("Command ID must be defined for a user command")
2171
2172        ret_val = self.send_cmd(cmds.UserSendCmd(cmd_id=cmd_id, args=args))
2173
2174        if ret_val is not None:
2175            return ret_val
2176        else:
2177            return []
2178
2179
2180
2181    #--------------------------------------------
2182    # Memory Access Commands - For developer use only
2183    #--------------------------------------------
2184    def _mem_write_high(self, address, values):
2185        """Writes 'values' to CPU High memory starting at 'address'
2186
2187        Args:
2188            address (int):         Address must be in [0 .. (2^32 - 1)]
2189            values (list of int):  Each value must be in [0 .. (2^32 - 1)]
2190        """
2191        # Code below assumes values is iterable - if caller supplies scalar, wrap it in a list
2192        if '__iter__' not in dir(values):
2193            values = [values]
2194
2195        if (self._check_mem_access_args(address, values)):
2196            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_WRITE, high=True, address=address, length=len(values), values=values))
2197
2198
2199    def _mem_read_high(self, address, length):
2200        """Reads 'length' values from CPU High memory starting at 'address'
2201
2202        Args:
2203            address (int):  Address must be in [0 .. (2^32 - 1)]
2204            length (int):   Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2205
2206        Returns:
2207            values (list of u32):  List of u32 values received from the node
2208        """
2209        if (self._check_mem_access_args(address, values=None)):
2210            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_READ, high=True, address=address, length=length))
2211
2212
2213    def _mem_write_low(self, address, values):
2214        """Writes 'values' to CPU Low memory starting at 'address'
2215
2216        Args:
2217            address (int):         Address must be in [0 .. (2^32 - 1)]
2218            values (list of int):  Each value must be in [0 .. (2^32 - 1)]
2219        """
2220        # Code below assumes values is iterable - if caller supplies scalar, wrap it in a list
2221        if '__iter__' not in dir(values):
2222            values = [values]
2223
2224        if (self._check_mem_access_args(address, values)):
2225            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_WRITE, high=False, address=address, length=len(values), values=values))
2226
2227
2228    def _mem_read_low(self, address, length):
2229        """Reads 'length' values from CPU Low memory starting at 'address'
2230
2231        Args:
2232            address (int):  Address must be in [0 .. (2^32 - 1)]
2233            length (int):   Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2234
2235        Returns:
2236            values (list of u32):  List of u32 values received from the node
2237        """
2238        if (self._check_mem_access_args(address, values=None)):
2239            return self.send_cmd(cmds.NodeMemAccess(cmd=cmds.CMD_PARAM_READ, high=False, address=address, length=length))
2240
2241
2242    def _check_mem_access_args(self, address, values=None, length=None):
2243        """Check memory access variables
2244
2245        Args:
2246            address (int):         Address must be in [0 .. (2^32 - 1)]
2247            values (list of int):  Each value must be in [0 .. (2^32 - 1)]
2248            length (int):          Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2249
2250        Returns:
2251            valid (bool):  Are all arguments valid?
2252        """
2253        if ((int(address) != address) or (address >= 2**32) or (address < 0)):
2254            raise Exception('ERROR: address must be integer value in [0 .. (2^32 - 1)]!')
2255
2256        # Caller must pass iterable for values
2257        if (values is not None):
2258            error = False
2259
2260            for v in values:
2261                if (int(v) >= 2**32) or (int(v) < 0):
2262                    error = True
2263
2264            if (error):
2265                raise Exception('ERROR: values must be scalar or iterable of ints in [0 .. (2^32 - 1)]! {0}'.format(values))
2266
2267        if length is not None:
2268            if ((int(length) != length) or (length > 320) or (length <= 0)):
2269                raise Exception('ERROR: length must be an integer [1 .. 320] words (ie, 4 to 1400 bytes)!')
2270
2271        return True
2272
2273
2274    def _eeprom_write(self, address, values):
2275        """Writes 'values' to EEPROM starting at 'address'
2276
2277        Args:
2278            address (int):         Address must be in [0 .. 15999]
2279            values (list of int):  Each value must be in [0 .. 255]
2280        """
2281        # Convert scalar values to a list for processing
2282        if (type(values) is not list):
2283            values = [values]
2284
2285        if (self._check_eeprom_access_args(address=address, values=values, length=len(values))):
2286            if(address >= 16000):
2287                raise Exception('ERROR: EEPROM addresses [16000 .. 16383] are read only!')
2288            else:
2289                return self.send_cmd(cmds.NodeEEPROMAccess(cmd=cmds.CMD_PARAM_WRITE, address=address, length=len(values), values=values))
2290
2291
2292    def _eeprom_read(self, address, length):
2293        """Reads 'length' values from EEPROM starting at 'address'
2294
2295        Args:
2296            address (int):  Address must be in [0 .. 16383]
2297            length (int):   Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2298
2299        Returns:
2300            values (list of u8):  List of u8 values received from the node
2301        """
2302        if (self._check_eeprom_access_args(address=address, length=length)):
2303            return self.send_cmd(cmds.NodeEEPROMAccess(cmd=cmds.CMD_PARAM_READ, address=address, length=length))
2304
2305
2306    def _check_eeprom_access_args(self, address, values=None, length=None):
2307        """Check EEPROM access variables
2308
2309        Args:
2310            address (int):         Address must be in [0 .. 16383]
2311            values (list of int):  Each value must be in [0 .. 255]
2312            length (int):          Length must be in [1 .. 320] (ie fit in a 1400 byte packet)
2313
2314        Returns:
2315            valid (bool):  Are all arguments valid?
2316        """
2317        if ((int(address) != address) or (address >= 16384) or (address < 0)):
2318            raise Exception('ERROR: address must be integer value in [0 .. 16383]!')
2319
2320        if (values is not None):
2321            if (type(values) is not list):
2322                values = [values]
2323
2324            error = False
2325
2326            for value in values:
2327                if (((type(value) is not int) and (type(value) is not long)) or
2328                    (value >= 2**8) or (value < 0)):
2329                    error = True
2330
2331            if (error):
2332                raise Exception('ERROR: values must be scalar or iterable of ints in [0 .. 255]!')
2333
2334        if length is not None:
2335            if ((int(length) != length) or (length > 320) or (length <= 0)):
2336                raise Exception('ERROR: length must be an integer [1 .. 320] words (ie, 4 to 1400 bytes)!')
2337
2338        return True
2339
2340
2341
2342    #-------------------------------------------------------------------------
2343    # Parameter Framework
2344    #   Allows for processing of hardware parameters
2345    #-------------------------------------------------------------------------
2346    def process_parameter(self, identifier, length, values):
2347        import struct
2348       
2349        """Extract values from the parameters"""
2350        if (identifier == NODE_PARAM_ID_WLAN_EXP_VERSION):
2351            if (length == 1):
2352                self.wlan_exp_ver_major = (values[0] & 0xFF000000) >> 24
2353                self.wlan_exp_ver_minor = (values[0] & 0x00FF0000) >> 16
2354                self.wlan_exp_ver_revision = (values[0] & 0x0000FFFF)
2355
2356                # Check to see if there is a version mismatch
2357                self.check_wlan_exp_ver()
2358            else:
2359                raise ex.ParameterError("NODE_DESIGN_VER", "Incorrect length")
2360
2361        elif   (identifier == NODE_PARAM_ID_WLAN_MAC_ADDR):
2362            if (length == 2):
2363                self.wlan_mac_address = ((2**32) * (values[1] & 0xFFFF) + values[0])
2364            else:
2365                raise ex.ParameterError("NODE_WLAN_MAC_ADDR", "Incorrect length")
2366
2367        elif   (identifier == NODE_PARAM_ID_WLAN_SCHEDULER_RESOLUTION):
2368            if (length == 1):
2369                self.scheduler_resolution = values[0]
2370            else:
2371                raise ex.ParameterError("NODE_LTG_RESOLUTION", "Incorrect length")
2372
2373        elif   (identifier == NODE_PARAM_ID_WLAN_MAX_TX_POWER_DBM):
2374            if (length == 1):
2375                # Power is an int transmited as a uint
2376                if (values[0] & 0x80000000):
2377                    self.max_tx_power_dbm = values[0] - 2**32
2378                else:
2379                    self.max_tx_power_dbm = values[0]
2380            else:
2381                raise ex.ParameterError("MAX_TX_POWER_DBM", "Incorrect length")
2382
2383        elif   (identifier == NODE_PARAM_ID_WLAN_MIN_TX_POWER_DBM):
2384            if (length == 1):
2385                # Power is an int transmited as a uint
2386                if (values[0] & 0x80000000):
2387                    self.min_tx_power_dbm = values[0] - 2**32
2388                else:
2389                    self.min_tx_power_dbm = values[0]
2390            else:
2391                raise ex.ParameterError("MIN_TX_POWER_DBM", "Incorrect length")
2392               
2393        elif   (identifier == NODE_PARAM_WLAN_CPU_LOW_COMPILATION_DATE):
2394            if (length == 3):
2395                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2396                temp = temp[0:11]
2397                if type(temp) is not str:
2398                    temp = temp.decode('utf-8')
2399                self.cpu_low_compilation_date = temp
2400                                                 
2401            else:
2402                raise ex.ParameterError("NODE_WLAN_CPU_LOW_COMPILATION_DATE", "Incorrect length")
2403               
2404        elif   (identifier == NODE_PARAM_WLAN_CPU_LOW_COMPILATION_TIME):           
2405            if (length == 3):               
2406                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2407                temp = temp[0:8]
2408                if type(temp) is not str:
2409                    temp = temp.decode('utf-8')
2410                self.cpu_low_compilation_time = temp
2411            else:
2412                raise ex.ParameterError("NODE_WLAN_CPU_LOW_COMPILATION_TIME", "Incorrect length")     
2413               
2414        elif   (identifier == NODE_PARAM_WLAN_CPU_HIGH_COMPILATION_DATE):
2415            if (length == 3):
2416                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2417                temp = temp[0:11]
2418                if type(temp) is not str:
2419                    temp = temp.decode('utf-8')
2420                self.cpu_high_compilation_date = temp
2421            else:
2422                raise ex.ParameterError("NODE_WLAN_CPU_HIGH_COMPILATION_DATE", "Incorrect length")
2423               
2424        elif   (identifier == NODE_PARAM_WLAN_CPU_HIGH_COMPILATION_TIME):
2425            if (length == 3):
2426                temp = struct.pack('LLL', values[0], values[1], values[2]);         
2427                temp = temp[0:8]
2428                if type(temp) is not str:
2429                    temp = temp.decode('utf-8')
2430                self.cpu_high_compilation_time = temp
2431            else:
2432                raise ex.ParameterError("NODE_WLAN_CPU_HIGH_COMPILATION_TIME", "Incorrect length")                 
2433               
2434
2435        else:
2436            super(WlanExpNode, self).process_parameter(identifier, length, values)
2437
2438
2439    #-------------------------------------------------------------------------
2440    # Misc methods for the Node
2441    #-------------------------------------------------------------------------
2442    def _set_node_type(self, node_type):
2443        """Set the node_type and keep the device_type in sync."""
2444        self.node_type   = node_type
2445        self.device_type = self.node_type
2446
2447    def _get_node_type_high(self):
2448        """Get the node type of CPU High
2449
2450        See defaults.py for a list of CPU High node types
2451        """
2452        return (self.node_type & defaults.WLAN_EXP_HIGH_MASK)
2453
2454    def _get_node_type_low(self):
2455        """Get the node type of CPU Low
2456
2457        See defaults.py for a list of CPU Low node types
2458        """
2459        return (self.node_type & defaults.WLAN_EXP_LOW_MASK)
2460
2461    def _check_cpu_high_type(self, high_type, command_name, raise_error=False):
2462        """Check the node CPU High type against the high_type argument
2463
2464        Args:
2465            high_type (int):    Node type for CPU High (see defaults.py)
2466            command_name (str): Name of command calling function
2467            raise_error (bool): Raise an exception?
2468        """
2469        node_high_type = self._get_node_type_high()
2470
2471        if (node_high_type != high_type):
2472            msg  = "WARNING:  CPU High Type mismatch.\n"
2473            msg += "    Command \'{0}()\' ".format(command_name)
2474
2475            try:
2476                msg += "expects {0}, ".format(defaults.WLAN_EXP_HIGH_TYPES[high_type])
2477            except:
2478                msg += "expects UNKNOWN TYPE, "
2479
2480            try:
2481                msg += "node reports {0}\n".format(defaults.WLAN_EXP_HIGH_TYPES[node_high_type])
2482            except:
2483                msg += "reports UNKNOWN TYPE\n"
2484
2485            msg += "    Command may have unintended effects.\n"
2486
2487            if (raise_error):
2488                raise TypeError(msg)
2489            else:
2490                print(msg)
2491
2492    def _check_cpu_low_type(self, low_type, command_name, raise_error=False):
2493        """Check the node CPU Low type against the low_type argument
2494
2495        Args:
2496            low_type (int):     Node type for CPU Low (see defaults.py)
2497            command_name (str): Name of command calling function
2498            raise_error (bool): Raise an exception?
2499        """
2500        node_low_type = self._get_node_type_low()
2501
2502        if (node_low_type != low_type):
2503            msg  = "WARNING:  CPU Low Type mismatch:\n"
2504            msg += "    Command \'{0}\' ".format(command_name)
2505
2506            try:
2507                msg += "expects {0}, ".format(defaults.WLAN_EXP_LOW_TYPES[low_type])
2508            except KeyError:
2509                msg += "expects UNKNOWN TYPE, "
2510
2511            try:
2512                msg += "node reports {0}\n".format(defaults.WLAN_EXP_LOW_TYPES[node_low_type])
2513            except KeyError:
2514                msg += "reports UNKNOWN TYPE\n"
2515
2516            msg += "    Command may have unintended effects.\n"
2517
2518            if (raise_error):
2519                raise TypeError(msg)
2520            else:
2521                print(msg)
2522
2523    def check_wlan_exp_ver(self):
2524        """Check the wlan_exp version of the node against the current wlan_exp
2525        version.
2526        """
2527        ver_str     = version.wlan_exp_ver_str(self.wlan_exp_ver_major,
2528                                               self.wlan_exp_ver_minor,
2529                                               self.wlan_exp_ver_revision)
2530
2531        caller_desc = "During initialization '{0}' returned version {1}".format(self.sn_str, ver_str)
2532
2533        status = version.wlan_exp_ver_check(major=self.wlan_exp_ver_major,
2534                                            minor=self.wlan_exp_ver_minor,
2535                                            revision=self.wlan_exp_ver_revision,
2536                                            caller_desc=caller_desc)
2537
2538        if (status == version.WLAN_EXP_VERSION_NEWER):
2539            print("Please update the C code on the node to the proper wlan_exp version.")
2540
2541        if (status == version.WLAN_EXP_VERSION_OLDER):
2542            print("Please update the wlan_exp installation to match the version on the node.")
2543
2544
2545# End Class WlanExpNode
2546
2547
2548
2549
2550class WlanExpNodeFactory(node.WarpNodeFactory):
2551    """Sub-class of WarpNodeFactory used to help with node configuration
2552    and setup.
2553    """
2554    def __init__(self, network_config=None):
2555        super(WlanExpNodeFactory, self).__init__(network_config)
2556
2557        # Add default classes to the factory
2558        self.node_add_class(defaults.WLAN_EXP_AP_DCF_TYPE,
2559                            defaults.WLAN_EXP_AP_DCF_CLASS_INST,
2560                            defaults.WLAN_EXP_AP_DCF_DESCRIPTION)
2561
2562        self.node_add_class(defaults.WLAN_EXP_STA_DCF_TYPE,
2563                            defaults.WLAN_EXP_STA_DCF_CLASS_INST,
2564                            defaults.WLAN_EXP_STA_DCF_DESCRIPTION)
2565
2566        self.node_add_class(defaults.WLAN_EXP_IBSS_DCF_TYPE,
2567                            defaults.WLAN_EXP_IBSS_DCF_CLASS_INST,
2568                            defaults.WLAN_EXP_IBSS_DCF_DESCRIPTION)
2569
2570        self.node_add_class(defaults.WLAN_EXP_AP_NOMAC_TYPE,
2571                            defaults.WLAN_EXP_AP_NOMAC_CLASS_INST,
2572                            defaults.WLAN_EXP_AP_NOMAC_DESCRIPTION)
2573
2574        self.node_add_class(defaults.WLAN_EXP_STA_NOMAC_TYPE,
2575                            defaults.WLAN_EXP_STA_NOMAC_CLASS_INST,
2576                            defaults.WLAN_EXP_STA_NOMAC_DESCRIPTION)
2577
2578        self.node_add_class(defaults.WLAN_EXP_IBSS_NOMAC_TYPE,
2579                            defaults.WLAN_EXP_IBSS_NOMAC_CLASS_INST,
2580                            defaults.WLAN_EXP_IBSS_NOMAC_DESCRIPTION)
2581
2582
2583    def node_eval_class(self, node_class, network_config):
2584        """Evaluate the node_class string to create a node.
2585
2586        This should be overridden in each sub-class with the same overall
2587        structure but a different import.  Please call the super class so that
2588        the calls will propagate to catch all node types.
2589
2590        network_config is used as part of the node_class string to initialize
2591        the node.
2592        """
2593        # "import wlan_exp.defaults as defaults" performed at the top of the file
2594        import wlan_exp.node_ap as node_ap
2595        import wlan_exp.node_sta as node_sta
2596        import wlan_exp.node_ibss as node_ibss
2597
2598        node = None
2599
2600        try:
2601            node = eval(node_class, globals(), locals())
2602        except:
2603            # We will catch all errors that might occur when trying to create
2604            # the node since this class might not be able to create the node.
2605            pass
2606
2607        if node is None:
2608            return super(WlanExpNodeFactory, self).node_eval_class(node_class,
2609                                                                   network_config)
2610        else:
2611            return node
2612
2613
2614# End Class WlanExpNodeFactory
Note: See TracBrowser for help on using the repository browser.