1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
2 | % wl_example_siso_spectrogram.m |
---|
3 | % |
---|
4 | % In this example, we will use a single WARPLab receiver to plot a |
---|
5 | % spectrogram of a received waveform. |
---|
6 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
7 | clear; |
---|
8 | |
---|
9 | CHANNEL = 6; % Choose the channel we will receive on |
---|
10 | |
---|
11 | NUM_SAMPLES = 2^25; % Number of samples to request |
---|
12 | |
---|
13 | WRITE_PNG_FILES = 1; % Enable writing plots to PNG |
---|
14 | |
---|
15 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
16 | % Set up the WARPLab experiment |
---|
17 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
18 | |
---|
19 | % Create a node object |
---|
20 | node = wl_initNodes(1); |
---|
21 | |
---|
22 | % Read Trigger IDs into workspace |
---|
23 | trig_in_ids = wl_getTriggerInputIDs(node); |
---|
24 | trig_out_ids = wl_getTriggerOutputIDs(node); |
---|
25 | |
---|
26 | % For both nodes, we will allow Ethernet to trigger the buffer baseband and the AGC |
---|
27 | wl_triggerManagerCmd(node, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]); |
---|
28 | |
---|
29 | % Set the trigger output delays. |
---|
30 | % |
---|
31 | % NOTE: We are waiting 3000 ns before starting the AGC so that there is time for the inputs |
---|
32 | % to settle before sampling the waveform to calculate the RX gains. |
---|
33 | % |
---|
34 | node.wl_triggerManagerCmd('output_config_delay', [trig_out_ids.BASEBAND], 0); |
---|
35 | node.wl_triggerManagerCmd('output_config_delay', [trig_out_ids.AGC], 3000); % 3000 ns delay before starting the AGC |
---|
36 | |
---|
37 | % Get IDs for the interfaces on the board. |
---|
38 | ifc_ids = wl_getInterfaceIDs(node); |
---|
39 | |
---|
40 | % Use RFA as the receiver |
---|
41 | RF_RX = ifc_ids.RF_A; |
---|
42 | RF_RX_VEC = ifc_ids.RF_A; |
---|
43 | |
---|
44 | % Check the number of samples |
---|
45 | max_rx_samples = wl_basebandCmd(node, RF_RX, 'rx_buff_max_num_samples'); |
---|
46 | |
---|
47 | if ( strcmp( class(node.transport), 'wl_transport_eth_udp_mex' ) ) |
---|
48 | if ( NUM_SAMPLES > node.baseband.MEX_TRANSPORT_MAX_IQ ) |
---|
49 | fprintf('\nWARNING: Requested %d samples. Due to Matlab memory limitations, the mex transport only supports %d samples.', NUM_SAMPLES, node.baseband.MEX_TRANSPORT_MAX_IQ); |
---|
50 | fprintf('WARNING: If your computer has enough physical memory, you can adjust this limit using node.baseband.MEX_TRANSPORT_MAX_IQ '); |
---|
51 | fprintf('WARNING: up to a maximum of %d samples.\n\n', max_rx_samples); |
---|
52 | |
---|
53 | NUM_SAMPLES = node.baseband.MEX_TRANSPORT_MAX_IQ; |
---|
54 | end |
---|
55 | else |
---|
56 | if ( NUM_SAMPLES > node.baseband.JAVA_TRANSPORT_MAX_IQ ) |
---|
57 | fprintf('\nWARNING: WARPLab by default only supports %d samples for the spectrogram\n', node.baseband.JAVA_TRANSPORT_MAX_IQ); |
---|
58 | fprintf('WARNING: using the java transport. Please use the MEX transport to increase the\n'); |
---|
59 | fprintf('WARNING: number of samples in the spectrogram.\n\n'); |
---|
60 | |
---|
61 | NUM_SAMPLES = node.baseband.JAVA_TRANSPORT_MAX_IQ; |
---|
62 | end |
---|
63 | end |
---|
64 | |
---|
65 | if ( NUM_SAMPLES > max_rx_samples ) |
---|
66 | fprintf('\nWARNING: Requested %d samples. Node only supports %d samples.\n\n', NUM_SAMPLES, max_rx_samples); |
---|
67 | |
---|
68 | NUM_SAMPLES = max_rx_samples; |
---|
69 | end |
---|
70 | |
---|
71 | % Get the sample rate of the node |
---|
72 | Ts = 1/(wl_basebandCmd(node, 'tx_buff_clk_freq')); |
---|
73 | |
---|
74 | % Print information to the console |
---|
75 | fprintf('WARPLab Spectrogram Example:\n'); |
---|
76 | fprintf(' Generating spectrogram using %.4f seconds of data (%d samples).\n', (NUM_SAMPLES * Ts), NUM_SAMPLES ); |
---|
77 | |
---|
78 | % Create a UDP broadcast trigger and tell each node to be ready for it |
---|
79 | eth_trig = wl_trigger_eth_udp_broadcast; |
---|
80 | wl_triggerManagerCmd(node, 'add_ethernet_trigger', [eth_trig]); |
---|
81 | |
---|
82 | % Set up the interface for the experiment |
---|
83 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'channel', 2.4, CHANNEL); |
---|
84 | |
---|
85 | % Set the gains manually |
---|
86 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'rx_gain_mode', 'manual'); |
---|
87 | RxGainRF = 3; % Rx RF Gain in [1:3] |
---|
88 | RxGainBB = 10; % Rx Baseband Gain in [0:31] |
---|
89 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'rx_gains', RxGainRF, RxGainBB); |
---|
90 | |
---|
91 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
92 | % Receive signal using WARPLab |
---|
93 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
94 | |
---|
95 | % Set the receive length to the number of samples |
---|
96 | wl_basebandCmd(node, 'rx_length', NUM_SAMPLES); |
---|
97 | |
---|
98 | % Open up the transceiver's low-pass filter to its maximum bandwidth (36MHz) |
---|
99 | wl_interfaceCmd(node, RF_RX, 'rx_lpf_corn_freq', 3); |
---|
100 | |
---|
101 | % Enable to node to receive data |
---|
102 | wl_interfaceCmd(node, RF_RX, 'rx_en'); |
---|
103 | wl_basebandCmd(node, RF_RX, 'rx_buff_en'); |
---|
104 | |
---|
105 | % Trigger the node to receive samples |
---|
106 | eth_trig.send(); |
---|
107 | |
---|
108 | % Read the samples from the node |
---|
109 | rx_IQ = wl_basebandCmd(node, RF_RX_VEC, 'read_IQ', 0, NUM_SAMPLES); |
---|
110 | |
---|
111 | % Disable the RX buffers |
---|
112 | wl_basebandCmd(node, ifc_ids.RF_ALL, 'tx_rx_buff_dis'); |
---|
113 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'tx_rx_dis'); |
---|
114 | |
---|
115 | %% |
---|
116 | close all |
---|
117 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
118 | % Visualize results |
---|
119 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
120 | |
---|
121 | % Figure 1: Time Series |
---|
122 | t_vec = (0:(NUM_SAMPLES-1))/(40e6); |
---|
123 | |
---|
124 | % MATLAB can really slow down when trying to plot a lot of samples. There |
---|
125 | % isn't much point to plotting a vector that is much longer than the number |
---|
126 | % of pixels in the plot, so we will decimate the vector down to ~10000 samples. |
---|
127 | |
---|
128 | rx_IQ_decimate = rx_IQ(1:floor(NUM_SAMPLES/10000):end); |
---|
129 | t_vec_decimate = t_vec(1:floor(NUM_SAMPLES/10000):end); |
---|
130 | |
---|
131 | figure(1);clf; |
---|
132 | ax(1) = subplot(2,1,1); |
---|
133 | plot(t_vec_decimate, real(rx_IQ_decimate),'b-') |
---|
134 | xlabel('Time (seconds)') |
---|
135 | title('I Received Waveform') |
---|
136 | |
---|
137 | ax(2) = subplot(2,1,2); |
---|
138 | plot(t_vec_decimate, real(rx_IQ_decimate),'b-') |
---|
139 | xlabel('Time (seconds)') |
---|
140 | title('Q Received Waveform') |
---|
141 | |
---|
142 | linkaxes(ax,'xy') |
---|
143 | axis tight |
---|
144 | |
---|
145 | if(WRITE_PNG_FILES) |
---|
146 | print(gcf,'wl_rx_timeseries','-dpng','-r96','-painters') |
---|
147 | end |
---|
148 | |
---|
149 | % Figure 2: Spectrogram |
---|
150 | |
---|
151 | % MATLAB has a sophisticated 'spectrogram' function in the Signal Processing |
---|
152 | % Toolbox. In this example, we'll build our own crude spectrogram by |
---|
153 | % reshaping the received vector into a matrix and taking many FFTs. Note: |
---|
154 | % our approach has zero overlap in the FFTs. |
---|
155 | |
---|
156 | % Limit under which we will not show a zoomed in portion of the spectrogram. |
---|
157 | zoom_span_time = 100/1000; % 100 ms |
---|
158 | ZOOM_SAMPLE_LIMIT = 4 * (zoom_span_time * 40e6); |
---|
159 | |
---|
160 | |
---|
161 | % First, we need to split our receive vector into M pieces of N samples |
---|
162 | % each. The relative size of M and N affects whether you want more |
---|
163 | % resolution in time(M) or more resolution in frequency (N). By default, we |
---|
164 | % will make M equal to N, but this is not a fundamental requirement. |
---|
165 | M = floor(sqrt(NUM_SAMPLES)); |
---|
166 | N = M; |
---|
167 | |
---|
168 | % Now we can reshape the received vector into a matrix of M rows and N columns; |
---|
169 | % Reshape the long vector of M*N to a matrix so we can take M FFTs of length N |
---|
170 | |
---|
171 | rx_IQ_slice = rx_IQ(1:(M*N)); |
---|
172 | rx_IQ_mat = reshape(rx_IQ_slice, M, N).'; |
---|
173 | rx_spectrogram = fft(rx_IQ_mat, N, 2); |
---|
174 | |
---|
175 | % Zero out any DC offset |
---|
176 | rx_spectrogram(:,1) = zeros(M,1); |
---|
177 | |
---|
178 | % Perform an fftshift so that the DC frequency bin is in the middle |
---|
179 | rx_spectrogram = fftshift(rx_spectrogram,2); |
---|
180 | |
---|
181 | % Plot the Spectrogram on a dB scale |
---|
182 | h = figure('units','pixels','Position',[100 100 2000 1000]);clf; |
---|
183 | set(h,'PaperPosition',[.25 .25 20 10]); |
---|
184 | |
---|
185 | % Plot the entire view |
---|
186 | if ( NUM_SAMPLES >= ZOOM_SAMPLE_LIMIT ) |
---|
187 | subplot(1,2,1) |
---|
188 | end |
---|
189 | |
---|
190 | x_axis = linspace(-20,20,N); |
---|
191 | y_axis = (0:(M-1)) / (40e6 / N); |
---|
192 | imagesc(x_axis,y_axis,20*log10(abs(rx_spectrogram)./max(abs(rx_spectrogram(:))))); |
---|
193 | caxis([-50 0]) |
---|
194 | colorbar |
---|
195 | axis square |
---|
196 | |
---|
197 | xlabel('Frequency (MHz)') |
---|
198 | ylabel('Time (s)') |
---|
199 | title(sprintf('Spectrogram on dB Scale (%1.4f second view)', max(t_vec))) |
---|
200 | |
---|
201 | if ( NUM_SAMPLES >= ZOOM_SAMPLE_LIMIT ) |
---|
202 | % Zoom into a small piece in the middle of the spectrogram |
---|
203 | subplot(1,2,2) |
---|
204 | |
---|
205 | % Let's zoom in on a chunk out of the entire reception |
---|
206 | zoom_span_index = ceil(zoom_span_time * (40e6 / N)); |
---|
207 | index_range = floor((M/2)-(zoom_span_index/2)):floor((M/2)+(zoom_span_index/2)); |
---|
208 | |
---|
209 | y_axis_slice = y_axis( index_range ); |
---|
210 | rx_spectrogram_slice = rx_spectrogram( index_range, : ); |
---|
211 | |
---|
212 | imagesc(x_axis, y_axis_slice, 20 * log10(abs(rx_spectrogram_slice)./max(abs(rx_spectrogram(:))))); |
---|
213 | caxis([-50 0]) |
---|
214 | colorbar |
---|
215 | axis square |
---|
216 | |
---|
217 | xlabel('Frequency (MHz)') |
---|
218 | ylabel('Time (s)') |
---|
219 | title(sprintf('Spectrogram on dB Scale (%1.4f second view)', zoom_span_time)) |
---|
220 | end |
---|
221 | |
---|
222 | if(WRITE_PNG_FILES) |
---|
223 | print(gcf,'wl_rx_spectrogram','-dpng','-r200','-painters') |
---|
224 | end |
---|
225 | |
---|
226 | |
---|
227 | |
---|
228 | |
---|
229 | |
---|