xref: /openwifi/user_space/side_ch_ctl_src/side_info_display.py (revision e556af35c696ecef552192823e107caf37eb6af9)
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