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

Last change on this file was 5911, checked in by chunter, 2 months ago

Fixed the bounds on cw_exp_min and cw_exp_max being enforced by Python. A value of 0 is perfectly valid and would ensure a contention window of [0, 0].

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