[4283] | 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 | |
---|
[4320] | 9 | CHANNEL = 6; % Choose the channel we will receive on |
---|
[4283] | 10 | |
---|
[4776] | 11 | NUM_SAMPLES = 2^25; % Number of samples to request |
---|
[4320] | 12 | |
---|
| 13 | WRITE_PNG_FILES = 1; % Enable writing plots to PNG |
---|
| 14 | |
---|
[4283] | 15 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 16 | % Set up the WARPLab experiment |
---|
| 17 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 18 | |
---|
[4320] | 19 | % Create a node object |
---|
[4776] | 20 | node = wl_initNodes(1); |
---|
[4283] | 21 | |
---|
[4385] | 22 | % Read Trigger IDs into workspace |
---|
[4689] | 23 | trig_in_ids = wl_getTriggerInputIDs(node); |
---|
| 24 | trig_out_ids = wl_getTriggerOutputIDs(node); |
---|
[4385] | 25 | |
---|
| 26 | % For both nodes, we will allow Ethernet to trigger the buffer baseband and the AGC |
---|
[4689] | 27 | wl_triggerManagerCmd(node, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]); |
---|
[4385] | 28 | |
---|
| 29 | % Set the trigger output delays. |
---|
[4485] | 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 | % |
---|
[4689] | 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 |
---|
[4385] | 36 | |
---|
[4373] | 37 | % Get IDs for the interfaces on the board. |
---|
[4689] | 38 | ifc_ids = wl_getInterfaceIDs(node); |
---|
[4373] | 39 | |
---|
[4485] | 40 | % Use RFA as the receiver |
---|
[4689] | 41 | RF_RX = ifc_ids.RF_A; |
---|
| 42 | RF_RX_VEC = ifc_ids.RF_A; |
---|
[4373] | 43 | |
---|
[4320] | 44 | % Check the number of samples |
---|
[4373] | 45 | max_rx_samples = wl_basebandCmd(node, RF_RX, 'rx_buff_max_num_samples'); |
---|
| 46 | |
---|
[4320] | 47 | if ( strcmp( class(node.transport), 'wl_transport_eth_udp_mex' ) ) |
---|
[4373] | 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; |
---|
[4320] | 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'); |
---|
[4373] | 60 | |
---|
[4320] | 61 | NUM_SAMPLES = node.baseband.JAVA_TRANSPORT_MAX_IQ; |
---|
| 62 | end |
---|
| 63 | end |
---|
| 64 | |
---|
[4373] | 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 | |
---|
[4485] | 71 | % Get the sample rate of the node |
---|
| 72 | Ts = 1/(wl_basebandCmd(node, 'tx_buff_clk_freq')); |
---|
[4320] | 73 | |
---|
[4485] | 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 | |
---|
[4320] | 78 | % Create a UDP broadcast trigger and tell each node to be ready for it |
---|
[4283] | 79 | eth_trig = wl_trigger_eth_udp_broadcast; |
---|
[4485] | 80 | wl_triggerManagerCmd(node, 'add_ethernet_trigger', [eth_trig]); |
---|
[4283] | 81 | |
---|
[4320] | 82 | % Set up the interface for the experiment |
---|
[4776] | 83 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'channel', 2.4, CHANNEL); |
---|
[4283] | 84 | |
---|
[4485] | 85 | % Set the gains manually |
---|
[4689] | 86 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'rx_gain_mode', 'manual'); |
---|
[4320] | 87 | RxGainRF = 3; % Rx RF Gain in [1:3] |
---|
[4776] | 88 | RxGainBB = 10; % Rx Baseband Gain in [0:31] |
---|
[4689] | 89 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'rx_gains', RxGainRF, RxGainBB); |
---|
[4283] | 90 | |
---|
| 91 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 92 | % Receive signal using WARPLab |
---|
| 93 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 94 | |
---|
[4320] | 95 | % Set the receive length to the number of samples |
---|
| 96 | wl_basebandCmd(node, 'rx_length', NUM_SAMPLES); |
---|
[4283] | 97 | |
---|
[4320] | 98 | % Open up the transceiver's low-pass filter to its maximum bandwidth (36MHz) |
---|
[4283] | 99 | wl_interfaceCmd(node, RF_RX, 'rx_lpf_corn_freq', 3); |
---|
[4320] | 100 | |
---|
| 101 | % Enable to node to receive data |
---|
[4689] | 102 | wl_interfaceCmd(node, RF_RX, 'rx_en'); |
---|
| 103 | wl_basebandCmd(node, RF_RX, 'rx_buff_en'); |
---|
[4283] | 104 | |
---|
[4320] | 105 | % Trigger the node to receive samples |
---|
[4283] | 106 | eth_trig.send(); |
---|
| 107 | |
---|
[4320] | 108 | % Read the samples from the node |
---|
[4689] | 109 | rx_IQ = wl_basebandCmd(node, RF_RX_VEC, 'read_IQ', 0, NUM_SAMPLES); |
---|
[4283] | 110 | |
---|
[4320] | 111 | % Disable the RX buffers |
---|
[4689] | 112 | wl_basebandCmd(node, ifc_ids.RF_ALL, 'tx_rx_buff_dis'); |
---|
| 113 | wl_interfaceCmd(node, ifc_ids.RF_ALL, 'tx_rx_dis'); |
---|
[4283] | 114 | |
---|
| 115 | %% |
---|
| 116 | close all |
---|
| 117 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 118 | % Visualize results |
---|
| 119 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 120 | |
---|
[4320] | 121 | % Figure 1: Time Series |
---|
[4283] | 122 | t_vec = (0:(NUM_SAMPLES-1))/(40e6); |
---|
| 123 | |
---|
[4320] | 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. |
---|
[4283] | 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 | |
---|
[4320] | 149 | % Figure 2: Spectrogram |
---|
[4283] | 150 | |
---|
[4320] | 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. |
---|
[4283] | 155 | |
---|
[4320] | 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); |
---|
[4283] | 159 | |
---|
[4320] | 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. |
---|
[4283] | 165 | M = floor(sqrt(NUM_SAMPLES)); |
---|
| 166 | N = M; |
---|
| 167 | |
---|
[4320] | 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 |
---|
[4283] | 170 | |
---|
[4320] | 171 | rx_IQ_slice = rx_IQ(1:(M*N)); |
---|
| 172 | rx_IQ_mat = reshape(rx_IQ_slice, M, N).'; |
---|
[4283] | 173 | rx_spectrogram = fft(rx_IQ_mat, N, 2); |
---|
| 174 | |
---|
[4320] | 175 | % Zero out any DC offset |
---|
[4283] | 176 | rx_spectrogram(:,1) = zeros(M,1); |
---|
| 177 | |
---|
[4320] | 178 | % Perform an fftshift so that the DC frequency bin is in the middle |
---|
[4283] | 179 | rx_spectrogram = fftshift(rx_spectrogram,2); |
---|
| 180 | |
---|
[4320] | 181 | % Plot the Spectrogram on a dB scale |
---|
[4283] | 182 | h = figure('units','pixels','Position',[100 100 2000 1000]);clf; |
---|
| 183 | set(h,'PaperPosition',[.25 .25 20 10]); |
---|
| 184 | |
---|
[4320] | 185 | % Plot the entire view |
---|
| 186 | if ( NUM_SAMPLES >= ZOOM_SAMPLE_LIMIT ) |
---|
| 187 | subplot(1,2,1) |
---|
| 188 | end |
---|
[4283] | 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 | |
---|
[4320] | 201 | if ( NUM_SAMPLES >= ZOOM_SAMPLE_LIMIT ) |
---|
| 202 | % Zoom into a small piece in the middle of the spectrogram |
---|
| 203 | subplot(1,2,2) |
---|
[4283] | 204 | |
---|
[4320] | 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)); |
---|
[4283] | 208 | |
---|
[4320] | 209 | y_axis_slice = y_axis( index_range ); |
---|
| 210 | rx_spectrogram_slice = rx_spectrogram( index_range, : ); |
---|
[4283] | 211 | |
---|
[4320] | 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 |
---|
[4283] | 216 | |
---|
[4320] | 217 | xlabel('Frequency (MHz)') |
---|
| 218 | ylabel('Time (s)') |
---|
| 219 | title(sprintf('Spectrogram on dB Scale (%1.4f second view)', zoom_span_time)) |
---|
| 220 | end |
---|
[4283] | 221 | |
---|
| 222 | if(WRITE_PNG_FILES) |
---|
| 223 | print(gcf,'wl_rx_spectrogram','-dpng','-r200','-painters') |
---|
| 224 | end |
---|
| 225 | |
---|
| 226 | |
---|
| 227 | |
---|
| 228 | |
---|
| 229 | |
---|