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

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

Fixing minor wlan_exp docs issues

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