1# 2# openwifi side info receive and display program 3# Xianjun jiao. [email protected]; [email protected] 4# 5import os 6import sys 7import socket 8import numpy as np 9import matplotlib.pyplot as plt 10 11def display_side_info(freq_offset, csi, equalizer, waterfall_flag, CSI_LEN, EQUALIZER_LEN): 12 if not hasattr(display_side_info, 'freq_offset_store'): 13 display_side_info.freq_offset_store = np.zeros((256,)) 14 15 len_freq_offset = len(freq_offset) 16 display_side_info.freq_offset_store[:(256-len_freq_offset)] = display_side_info.freq_offset_store[len_freq_offset:] 17 display_side_info.freq_offset_store[(256-len_freq_offset):] = freq_offset 18 19 fig_freq_offset = plt.figure(0) 20 fig_freq_offset.clf() 21 plt.xlabel("packet idx") 22 plt.ylabel("Hz") 23 plt.title("freq offset") 24 plt.plot(display_side_info.freq_offset_store) 25 fig_freq_offset.canvas.flush_events() 26 27 good_row_idx = 0 28 if ( len(equalizer)==0 ): 29 csi_for_plot = csi.T 30 else: 31 equalizer[equalizer == 32767+32767*1j] = 0 32 num_row_equalizer, num_col_equalizer = equalizer.shape 33 equalizer_for_plot = np.zeros((num_row_equalizer, num_col_equalizer)) + 1j*np.zeros((num_row_equalizer, num_col_equalizer)) 34 35 num_row_csi, num_col_csi = csi.shape 36 csi_for_plot = np.zeros((num_row_csi, num_col_csi)) + 1j*np.zeros((num_row_csi, num_col_csi)) 37 38 # only take out the good equalizer result, when output > 2000, it is not good 39 for i in range(num_row_equalizer): 40 if (not (np.any(abs(equalizer[i,:].real)>2000) or np.any(abs(equalizer[i,:].imag)>2000)) ): 41 equalizer_for_plot[good_row_idx,:] = equalizer[i,:] 42 csi_for_plot[good_row_idx,:] = csi[i,:] 43 good_row_idx = good_row_idx + 1 44 45 csi_for_plot = csi_for_plot[0:good_row_idx,:] 46 equalizer_for_plot = equalizer_for_plot[0:good_row_idx,:] 47 csi_for_plot = csi_for_plot.T 48 equalizer_for_plot = equalizer_for_plot.T 49 50 if ( (len(equalizer)==0) or ((len(equalizer)>0)and(good_row_idx>0)) ): 51 fig_csi = plt.figure(1) 52 fig_csi.clf() 53 # if waterfall_flag == 0: 54 ax_abs_csi = fig_csi.add_subplot(211) 55 ax_abs_csi.set_xlabel("subcarrier idx") 56 ax_abs_csi.set_ylabel("abs") 57 ax_abs_csi.set_title("CSI") 58 plt.plot(np.abs(csi_for_plot)) 59 ax_phase_csi = fig_csi.add_subplot(212) 60 ax_phase_csi.set_xlabel("subcarrier idx") 61 ax_phase_csi.set_ylabel("phase") 62 plt.plot(np.angle(csi_for_plot)) 63 fig_csi.canvas.flush_events() 64 65 # else: 66 67 if waterfall_flag == 1: 68 # print(np.abs(display_side_info.csi_mat_for_waterfall)) 69 display_side_info.csi_mat_for_waterfall = np.roll(display_side_info.csi_mat_for_waterfall, 1, axis=0) 70 # print(np.abs(display_side_info.csi_mat_for_waterfall)) 71 72 display_side_info.csi_mat_for_waterfall[0,:] = csi[0,:] 73 74 fig_waterfall = plt.figure(3) 75 fig_waterfall.clf() 76 77 ax_abs_csi_waterfall = fig_waterfall.add_subplot(121) 78 ax_abs_csi_waterfall.set_title('CSI amplitude') 79 ax_abs_csi_waterfall.set_xlabel("subcarrier idx") 80 ax_abs_csi_waterfall.set_ylabel("time") 81 # ax_abs_csi_waterfall_shw = ax_abs_csi_waterfall.imshow(np.abs(display_side_info.csi_mat_for_waterfall), vmin=200, vmax=500) 82 ax_abs_csi_waterfall_shw = ax_abs_csi_waterfall.imshow(np.abs(display_side_info.csi_mat_for_waterfall)) 83 plt.colorbar(ax_abs_csi_waterfall_shw) 84 85 ax_phase_csi_waterfall = fig_waterfall.add_subplot(122) 86 ax_phase_csi_waterfall.set_title('CSI phase') 87 ax_phase_csi_waterfall.set_xlabel("subcarrier idx") 88 ax_phase_csi_waterfall.set_ylabel("time") 89 # ax_phase_csi_waterfall_shw = ax_phase_csi_waterfall.imshow(np.angle(display_side_info.csi_mat_for_waterfall), vmin=-3.14, vmax=3.14) 90 ax_phase_csi_waterfall_shw = ax_phase_csi_waterfall.imshow(np.angle(display_side_info.csi_mat_for_waterfall)) 91 plt.colorbar(ax_phase_csi_waterfall_shw) 92 93 fig_waterfall.canvas.flush_events() 94 95 if ( (len(equalizer)>0) and (good_row_idx>0) ): 96 fig_equalizer = plt.figure(2) 97 fig_equalizer.clf() 98 plt.xlabel("I") 99 plt.ylabel("Q") 100 plt.title("equalizer") 101 plt.scatter(equalizer_for_plot.real, equalizer_for_plot.imag) 102 fig_freq_offset.canvas.flush_events() 103 104def parse_side_info(side_info, num_eq, CSI_LEN, EQUALIZER_LEN, HEADER_LEN): 105 # print(len(side_info), num_eq, CSI_LEN, EQUALIZER_LEN, HEADER_LEN) 106 CSI_LEN_HALF = round(CSI_LEN/2) 107 num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN 108 num_int16_per_trans = num_dma_symbol_per_trans*4 # 64bit per dma symbol 109 num_trans = round(len(side_info)/num_int16_per_trans) 110 # print(len(side_info), side_info.dtype, num_trans) 111 side_info = side_info.reshape([num_trans, num_int16_per_trans]) 112 113 timestamp = side_info[:,0] + pow(2,16)*side_info[:,1] + pow(2,32)*side_info[:,2] + pow(2,48)*side_info[:,3] 114 115 freq_offset = (20e6*np.int16(side_info[:,4])/512)/(2*3.14159265358979323846) 116 117 csi = np.zeros((num_trans, CSI_LEN), dtype='int16') 118 csi = csi + csi*1j 119 120 equalizer = np.zeros((0,0), dtype='int16') 121 if num_eq>0: 122 equalizer = np.zeros((num_trans, num_eq*EQUALIZER_LEN), dtype='int16') 123 equalizer = equalizer + equalizer*1j 124 125 for i in range(num_trans): 126 tmp_vec_i = np.int16(side_info[i,8:(num_int16_per_trans-1):4]) 127 tmp_vec_q = np.int16(side_info[i,9:(num_int16_per_trans-1):4]) 128 tmp_vec = tmp_vec_i + tmp_vec_q*1j 129 # csi[i,:] = tmp_vec[0:CSI_LEN] 130 csi[i,:CSI_LEN_HALF] = tmp_vec[CSI_LEN_HALF:CSI_LEN] 131 csi[i,CSI_LEN_HALF:] = tmp_vec[0:CSI_LEN_HALF] 132 if num_eq>0: 133 equalizer[i,:] = tmp_vec[CSI_LEN:(CSI_LEN+num_eq*EQUALIZER_LEN)] 134 # print(i, len(tmp_vec), len(tmp_vec[0:CSI_LEN]), len(tmp_vec[CSI_LEN:(CSI_LEN+num_eq*EQUALIZER_LEN)])) 135 136 return timestamp, freq_offset, csi, equalizer 137 138UDP_IP = "192.168.10.1" #Local IP to listen 139UDP_PORT = 4000 #Local port to listen 140 141sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP 142sock.bind((UDP_IP, UDP_PORT)) 143sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 464) # for low latency. 464 is the minimum udp length in our case (CSI only) 144 145# align with side_ch_control.v and all related user space, remote files 146MAX_NUM_DMA_SYMBOL = 8192 147CSI_LEN = 56 # length of single CSI 148EQUALIZER_LEN = (56-4) # for non HT, four {32767,32767} will be padded to achieve 52 (non HT should have 48) 149HEADER_LEN = 2 # timestamp and frequency offset 150 151if len(sys.argv)<2: 152 print("Assume num_eq = 8!") 153 num_eq = 8 154else: 155 num_eq = int(sys.argv[1]) 156 print(num_eq) 157 # print(type(num_eq)) 158 159waterfall_flag = 0 160if len(sys.argv)>2: 161 print("Will plot CSI in waterfall!") 162 display_side_info.csi_mat_for_waterfall = np.zeros((64, CSI_LEN)) + 1j*np.zeros((64, CSI_LEN)) 163 waterfall_flag = 1 164 165num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN 166num_byte_per_trans = 8*num_dma_symbol_per_trans 167 168if os.path.exists("side_info.txt"): 169 os.remove("side_info.txt") 170side_info_fd=open('side_info.txt','a') 171 172plt.ion() 173 174while True: 175 try: 176 data, addr = sock.recvfrom(MAX_NUM_DMA_SYMBOL*8) # buffer size 177 # print(addr) 178 # print(len(data), num_byte_per_trans) 179 test_residual = len(data)%num_byte_per_trans 180 if (test_residual != 0): 181 print("Abnormal length") 182 183 side_info = np.frombuffer(data, dtype='uint16') 184 np.savetxt(side_info_fd, side_info) 185 186 timestamp, freq_offset, csi, equalizer = parse_side_info(side_info, num_eq, CSI_LEN, EQUALIZER_LEN, HEADER_LEN) 187 # print(timestamp) 188 # print(freq_offset) 189 # print(csi[0,0:10]) 190 # print(equalizer[0,0:10]) 191 display_side_info(freq_offset, csi, equalizer, waterfall_flag, CSI_LEN, EQUALIZER_LEN) 192 193 except KeyboardInterrupt: 194 print('User quit') 195 break 196 197print('close()') 198side_info_fd.close() 199sock.close() 200