[6320] | 1 | """ |
---|
| 2 | ------------------------------------------------------------------------------ |
---|
| 3 | Mango 802.11 Reference Design - Experiments Framework - Log File Details |
---|
| 4 | ------------------------------------------------------------------------------ |
---|
| 5 | License: Copyright 2014-2019, Mango Communications. All rights reserved. |
---|
| 6 | Distributed under the WARP license (http://warpproject.org/license) |
---|
| 7 | ------------------------------------------------------------------------------ |
---|
| 8 | This script uses the WLAN Exp Log utilities to prase raw log data and print |
---|
| 9 | details about the Tx and Rx events at the two nodes. |
---|
| 10 | |
---|
| 11 | Hardware Setup: |
---|
| 12 | - None. Parsing log data can be done off-line |
---|
| 13 | |
---|
| 14 | Required Script Changes: |
---|
| 15 | - Set LOGFILE to the file name of your WLAN Exp log HDF5 file |
---|
| 16 | |
---|
| 17 | Description: |
---|
| 18 | This script parses the log file and generates numpy arrays of for all Tx |
---|
| 19 | and Rx events in the logs. |
---|
| 20 | ------------------------------------------------------------------------------ |
---|
| 21 | """ |
---|
| 22 | import os |
---|
| 23 | import sys |
---|
| 24 | import numpy as np |
---|
| 25 | import time |
---|
| 26 | |
---|
| 27 | import wlan_exp.util as wlan_exp_util |
---|
| 28 | |
---|
| 29 | import wlan_exp.log.util as log_util |
---|
| 30 | import wlan_exp.log.util_hdf as hdf_util |
---|
| 31 | import wlan_exp.log.util_sample_data as sample_data_util |
---|
| 32 | |
---|
| 33 | |
---|
| 34 | #----------------------------------------------------------------------------- |
---|
| 35 | # Process command line arguments |
---|
| 36 | #----------------------------------------------------------------------------- |
---|
| 37 | |
---|
| 38 | DEFAULT_LOGFILE = 'ap_two_node_two_flow_capture.hdf5' |
---|
| 39 | logfile_error = False |
---|
| 40 | |
---|
| 41 | # Use log file given as command line argument, if present |
---|
| 42 | if(len(sys.argv) != 1): |
---|
| 43 | LOGFILE = str(sys.argv[1]) |
---|
| 44 | |
---|
| 45 | # Check if the string argument matchs a local file |
---|
| 46 | if not os.path.isfile(LOGFILE): |
---|
| 47 | # User specified non-existant file - give up and exit |
---|
| 48 | logfile_error = True |
---|
| 49 | |
---|
| 50 | else: |
---|
| 51 | # No command line argument - check if default file name exists locally |
---|
| 52 | LOGFILE = DEFAULT_LOGFILE |
---|
| 53 | |
---|
| 54 | if not os.path.isfile(LOGFILE): |
---|
| 55 | # No local file specified or found - check for matching sample data file |
---|
| 56 | try: |
---|
| 57 | LOGFILE = sample_data_util.get_sample_data_file(DEFAULT_LOGFILE) |
---|
| 58 | print("Local log file not found - Using sample data file!") |
---|
| 59 | except IOError as e: |
---|
| 60 | logfile_error = True |
---|
| 61 | |
---|
| 62 | if logfile_error: |
---|
| 63 | print("ERROR: Logfile {0} not found".format(LOGFILE)) |
---|
| 64 | sys.exit() |
---|
| 65 | else: |
---|
| 66 | print("Reading log file '{0}' ({1:5.1f} MB)\n".format(LOGFILE, (os.path.getsize(LOGFILE)/2**20))) |
---|
| 67 | |
---|
| 68 | |
---|
| 69 | #----------------------------------------------------------------------------- |
---|
| 70 | # Main script |
---|
| 71 | #----------------------------------------------------------------------------- |
---|
| 72 | |
---|
| 73 | # Get the log_data from the file |
---|
| 74 | log_data = hdf_util.hdf5_to_log_data(filename=LOGFILE) |
---|
| 75 | |
---|
| 76 | # Get the raw_log_index from the file |
---|
| 77 | raw_log_index = hdf_util.hdf5_to_log_index(filename=LOGFILE) |
---|
| 78 | |
---|
| 79 | # Describe the raw_log_index |
---|
| 80 | log_util.print_log_index_summary(raw_log_index, "Log Index Contents:") |
---|
| 81 | |
---|
| 82 | # Filter log index to include all Rx entries and all Tx entries |
---|
| 83 | log_index = log_util.filter_log_index(raw_log_index, |
---|
| 84 | include_only=['NODE_INFO', 'TIME_INFO', 'RX_OFDM', 'TX_HIGH', 'TX_LOW'], |
---|
| 85 | merge={'RX_OFDM': ['RX_OFDM', 'RX_OFDM_LTG'], |
---|
| 86 | 'TX_HIGH' : ['TX_HIGH', 'TX_HIGH_LTG'], |
---|
| 87 | 'TX_LOW' : ['TX_LOW', 'TX_LOW_LTG']}) |
---|
| 88 | |
---|
| 89 | log_util.print_log_index_summary(log_index, "Filtered Log Index:") |
---|
| 90 | |
---|
| 91 | # Unpack the log into numpy structured arrays |
---|
| 92 | # log_data_to_np_arrays returns a dictionary with one key-value pair per |
---|
| 93 | # entry type included in the log_index argument. The log_index keys are reused |
---|
| 94 | # as the output dictionary keys. Each output dictionary value is a numpy record array |
---|
| 95 | # Refer to wlan_exp_log.log_entries.py for the definition of each record array datatype |
---|
| 96 | log_np = log_util.log_data_to_np_arrays(log_data, log_index) |
---|
| 97 | |
---|
| 98 | |
---|
| 99 | ############################################################################### |
---|
| 100 | # Example 0: Print node info / Time info |
---|
| 101 | log_node_info = log_np['NODE_INFO'][0] |
---|
| 102 | |
---|
| 103 | print("Node Info:") |
---|
| 104 | print(" Node Type : {0}".format(wlan_exp_util.node_type_to_str(log_node_info['node_type']))) |
---|
| 105 | print(" MAC Address : {0}".format(wlan_exp_util.mac_addr_to_str(log_node_info['wlan_mac_addr']))) |
---|
| 106 | print(" Serial Number: {0}".format(wlan_exp_util.sn_to_str(log_node_info['platform_id'], log_node_info['serial_num']))) |
---|
| 107 | print(" WLAN Exp Ver : {0}".format(wlan_exp_util.ver_code_to_str(log_node_info['version']))) |
---|
| 108 | print("") |
---|
| 109 | |
---|
| 110 | if(len(log_np['TIME_INFO']) > 0): |
---|
| 111 | log_time_info = log_np['TIME_INFO'][0] |
---|
| 112 | |
---|
| 113 | print("Experiment Started at: {0}".format(time.ctime(float(log_time_info['host_timestamp'] / 1E6)))) |
---|
| 114 | print("") |
---|
| 115 | |
---|
| 116 | ############################################################################### |
---|
| 117 | # Example 1: Gather some Tx information from the log |
---|
| 118 | # - Since there are only loops, this example can deal with TX_HIGH / TX_LOW |
---|
| 119 | # being an empty list and does not need a try / except. |
---|
| 120 | # |
---|
| 121 | |
---|
| 122 | # Initialize variables |
---|
| 123 | TX_CONSTS = log_util.get_entry_constants('TX_LOW') |
---|
| 124 | log_tx_low = log_np['TX_LOW'] |
---|
| 125 | total_retrans = 0 |
---|
| 126 | |
---|
| 127 | # Print header |
---|
| 128 | print("\nExample 1: Tx Information per Rate:") |
---|
| 129 | print("{0:^35} {1:^32}".format("Rate", "# Tx Pkts")) |
---|
| 130 | print("{0:^30} {1:>15} {2:>15}".format("", "CPU Low", "Re-trans")) |
---|
| 131 | |
---|
| 132 | # For each PHY mode, process the MCS index counts |
---|
| 133 | for phy_mode in wlan_exp_util.phy_modes.keys(): |
---|
| 134 | # Calculate the average time to send a packet for each rate |
---|
| 135 | for mcs in range(0, 8): |
---|
| 136 | # Create an index into the tx_Low array based on the following criteria: |
---|
| 137 | # - the PHY mode matches phy_mode in the above loop |
---|
| 138 | # - the MCS matches the mcs in the above loop |
---|
| 139 | tx_low_idx = ((log_tx_low['phy_mode'] == TX_CONSTS.phy_mode[phy_mode]) & |
---|
| 140 | (log_tx_low['mcs'] == mcs)) |
---|
| 141 | |
---|
| 142 | |
---|
| 143 | # Extract arrays for each PHY mode |
---|
| 144 | tx_low_entries = log_tx_low[tx_low_idx] |
---|
| 145 | |
---|
| 146 | # Calculate retransmissions |
---|
| 147 | # Any packet whose "attempt_number" is larger than 1 is a retransmission |
---|
| 148 | retrans = np.sum(tx_low_entries['attempt_number']>1) |
---|
| 149 | total_retrans += retrans |
---|
| 150 | |
---|
| 151 | # Print info |
---|
| 152 | try: |
---|
| 153 | rate_info = wlan_exp_util.get_rate_info(mcs, phy_mode) |
---|
| 154 | rate_str = wlan_exp_util.rate_info_to_str(rate_info) |
---|
| 155 | print("{0:30} {1:15} {2:15}".format( |
---|
| 156 | rate_str, |
---|
| 157 | len(tx_low_entries), |
---|
| 158 | retrans)) |
---|
| 159 | except: |
---|
| 160 | # Do nothing with unsupported PHY modes |
---|
| 161 | pass |
---|
| 162 | |
---|
| 163 | print("\nTotal Retransmissions: {0:d}".format(total_retrans)) |
---|
| 164 | |
---|
| 165 | |
---|
| 166 | |
---|
| 167 | ############################################################################### |
---|
| 168 | # Example 2: Calculate total number of packets and bytes transmitted to each |
---|
| 169 | # distinct MAC address for each of the MAC addresses in the header |
---|
| 170 | # |
---|
| 171 | |
---|
| 172 | # Skip this example if the log doesn't contain TX events |
---|
| 173 | for tx in ['TX_HIGH', 'TX_LOW']: |
---|
| 174 | if(tx in log_np.keys()): |
---|
| 175 | # Extract all OFDM transmissions |
---|
| 176 | log_tx = log_np[tx] |
---|
| 177 | |
---|
| 178 | # Count number of packets transmitted to each unique address in the 'addr1' field |
---|
| 179 | tx_addrs_1 = log_tx['addr1'] |
---|
| 180 | tx_counts = dict() |
---|
| 181 | |
---|
| 182 | for addr in np.unique(tx_addrs_1): |
---|
| 183 | # Find indexes of all instances where addresses match |
---|
| 184 | addr_idx = tx_addrs_1 == addr |
---|
| 185 | |
---|
| 186 | # Count the number of packets (True values in index array) to this address |
---|
| 187 | tx_pkts_to_addr = np.sum(addr_idx) |
---|
| 188 | |
---|
| 189 | # Count the number of bytes to this address |
---|
| 190 | tx_bytes_to_addr = np.sum(log_tx['length'][addr_idx]) |
---|
| 191 | |
---|
| 192 | # Record the results in the output dictionary |
---|
| 193 | tx_counts[addr] = (tx_pkts_to_addr, tx_bytes_to_addr) |
---|
| 194 | |
---|
| 195 | # Print the results |
---|
| 196 | if (tx == 'TX_HIGH'): |
---|
| 197 | print("\nExample 2: Tx Counts (CPU High):") |
---|
| 198 | else: |
---|
| 199 | print("\nExample 2: Tx Counts (CPU Low - includes retransmissions):") |
---|
| 200 | print("{0:18}\t{1:>7}\t{2:>10}\t{3}".format( |
---|
| 201 | "Dest Addr", |
---|
| 202 | "# Pkts", |
---|
| 203 | "# Bytes", |
---|
| 204 | "MAC Addr Type")) |
---|
| 205 | |
---|
| 206 | for k in sorted(tx_counts.keys()): |
---|
| 207 | # Use the string version of the MAC address as the key for readability |
---|
| 208 | print("{0:18}\t{1:>7}\t{2:>10}\t{3}".format( |
---|
| 209 | wlan_exp_util.mac_addr_to_str(k), |
---|
| 210 | tx_counts[k][0], |
---|
| 211 | tx_counts[k][1], |
---|
| 212 | wlan_exp_util.mac_addr_desc(k))) |
---|
| 213 | |
---|
| 214 | ################################################################################################# |
---|
| 215 | # Example 3: Calculate total number of packets and bytes received from each distinct MAC address |
---|
| 216 | |
---|
| 217 | # Skip this example if the log doesn't contain RX events |
---|
| 218 | if('RX_OFDM' in log_np.keys()): |
---|
| 219 | |
---|
| 220 | # Extract all receptions |
---|
| 221 | log_rx = log_np['RX_OFDM'] |
---|
| 222 | |
---|
| 223 | # Get RX_OFDM entry constants |
---|
| 224 | RX_CONSTS = log_util.get_entry_constants('RX_OFDM') |
---|
| 225 | |
---|
| 226 | # Extract only Rx entries with: |
---|
| 227 | # - Good checksum (FCS = good) |
---|
| 228 | # - Data / Management packets |
---|
| 229 | # |
---|
| 230 | rx_idx = (((log_rx['flags'] & RX_CONSTS.flags.FCS_GOOD) != 0) & |
---|
| 231 | ((log_rx['pkt_type'] == RX_CONSTS.pkt_type.DATA) | |
---|
| 232 | (log_rx['pkt_type'] == RX_CONSTS.pkt_type.QOSDATA) | |
---|
| 233 | (log_rx['pkt_type'] == RX_CONSTS.pkt_type.NULLDATA) | |
---|
| 234 | (log_rx['pkt_type'] == RX_CONSTS.pkt_type.BEACON) | |
---|
| 235 | (log_rx['pkt_type'] == RX_CONSTS.pkt_type.PROBE_RESP))) |
---|
| 236 | |
---|
| 237 | rx_good_data_mgmt = log_rx[rx_idx] |
---|
| 238 | |
---|
| 239 | # Extract addr2 field from all good packets |
---|
| 240 | rx_addrs_2 = rx_good_data_mgmt['addr2'] |
---|
| 241 | |
---|
| 242 | # Build a dictionary using unique MAC addresses as keys |
---|
| 243 | rx_counts = dict() |
---|
| 244 | for addr in np.unique(rx_addrs_2): |
---|
| 245 | # Find indexes of all instances where addresses match |
---|
| 246 | addr_idx = rx_addrs_2 == addr |
---|
| 247 | |
---|
| 248 | # Count the number of packets (True values in index array) from this address |
---|
| 249 | rx_pkts_from_addr = np.sum(addr_idx) |
---|
| 250 | |
---|
| 251 | # Count the number of bytes from this address |
---|
| 252 | try: |
---|
| 253 | # If addr_idx is greater than 1 |
---|
| 254 | rx_bytes_from_addr = np.sum(rx_good_data_mgmt['length'][addr_idx]) |
---|
| 255 | except: |
---|
| 256 | # If addr_idx is one |
---|
| 257 | rx_bytes_from_addr = np.sum(rx_good_data_mgmt['length']) |
---|
| 258 | |
---|
| 259 | # Add the information about the address to the dictionary |
---|
| 260 | rx_counts[addr] = (rx_pkts_from_addr, rx_bytes_from_addr) |
---|
| 261 | |
---|
| 262 | # Print the results |
---|
| 263 | if (len(rx_counts) > 0): |
---|
| 264 | print("\nExample 3: Rx Counts (including duplicates):") |
---|
| 265 | print("{0:18}\t{1:>7}\t{2:>10}\t{3}".format( |
---|
| 266 | "Dest Addr", |
---|
| 267 | "# Pkts", |
---|
| 268 | "# Bytes", |
---|
| 269 | "MAC Addr Type")) |
---|
| 270 | |
---|
| 271 | for k in sorted(rx_counts.keys()): |
---|
| 272 | # Use the string version of the MAC address as the key for readability |
---|
| 273 | print("{0:18}\t{1:>7}\t{2:>10}\t{3}".format( |
---|
| 274 | wlan_exp_util.mac_addr_to_str(k), |
---|
| 275 | rx_counts[k][0], |
---|
| 276 | rx_counts[k][1], |
---|
| 277 | wlan_exp_util.mac_addr_desc(k))) |
---|
| 278 | else: |
---|
| 279 | print("\nExample 3: Rx Counts (including duplicates):") |
---|
| 280 | print("\nNo Data or Management frames recevied with good FCS.") |
---|
| 281 | |
---|
| 282 | |
---|
| 283 | print('') |
---|
| 284 | |
---|
| 285 | # Uncomment this line to open an interactive console after the script runs |
---|
| 286 | # This console will have access to all variables defined above |
---|
| 287 | # wlan_exp_util.debug_here() |
---|