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

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

clarifying platform-agnostic comments throughout wlan_exp

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