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() |
---|