[2045] | 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
[2049] | 2 | % wl_example_8x2_array.m |
---|
[2045] | 3 | % |
---|
[4689] | 4 | % Description: |
---|
| 5 | % See warpproject.org/trac/wiki/WARPLab/Examples/8x2Array |
---|
[2045] | 6 | % |
---|
| 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 8 | clear; |
---|
| 9 | figure(1);clf; |
---|
| 10 | |
---|
| 11 | USE_AGC = true; |
---|
[2051] | 12 | RUN_CONTINOUSLY = false; |
---|
[2045] | 13 | |
---|
[4359] | 14 | MAX_TX_LEN = 32768; |
---|
| 15 | |
---|
[2045] | 16 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 17 | % Set up the WARPLab experiment |
---|
| 18 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 19 | |
---|
[4689] | 20 | % Create a vector of node objects |
---|
| 21 | % |
---|
| 22 | % This experiment uses 3 nodes: 2 will act as a transmitter and 1 will act as a receiver: |
---|
| 23 | % nodes(1): Primary transmitter |
---|
| 24 | % nodes(2): Secondary transmitter (receives clocks and triggers from primary transmitter) |
---|
| 25 | % nodes(3): Receiver |
---|
| 26 | % |
---|
[4702] | 27 | nodes = wl_initNodes(3); |
---|
[2045] | 28 | |
---|
[4359] | 29 | node_tx1 = nodes(1); |
---|
| 30 | node_tx2 = nodes(2); |
---|
[4689] | 31 | node_rx = nodes(3); |
---|
[4359] | 32 | |
---|
[4702] | 33 | |
---|
[4689] | 34 | % Create a UDP broadcast trigger and tell each node to be ready for it |
---|
[2045] | 35 | eth_trig = wl_trigger_eth_udp_broadcast; |
---|
[4689] | 36 | wl_triggerManagerCmd(nodes, 'add_ethernet_trigger', [eth_trig]); |
---|
[2045] | 37 | |
---|
[4478] | 38 | % Read Trigger IDs into workspace |
---|
[4689] | 39 | trig_in_ids = wl_getTriggerInputIDs(node_tx1); |
---|
| 40 | trig_out_ids = wl_getTriggerOutputIDs(node_tx1); |
---|
[2045] | 41 | |
---|
[4689] | 42 | % For the primary transmit node, we will allow Ethernet to trigger the baseband buffers, |
---|
| 43 | % the AGC, and external output pin 0 (which is mapped to pin 8 on the debug header). We |
---|
| 44 | % also will allow Ethernet to trigger the same signals for the receiving node. |
---|
| 45 | % |
---|
| 46 | wl_triggerManagerCmd([node_tx1, node_rx], 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC, trig_out_ids.EXT_OUT_P0], [trig_in_ids.ETH_A]); |
---|
[2045] | 47 | |
---|
[4689] | 48 | % For the secondary transmit node, we will allow external input pin 3 (mapped to pin 15 |
---|
| 49 | % on the debug header) to trigger the baseband buffers, and the AGC |
---|
| 50 | % |
---|
[4702] | 51 | % Note that the below line selects both P0 and P3. This will allow the |
---|
| 52 | % script to work with either the CM-PLL (where output P0 directly |
---|
| 53 | % connects to input P0) or the CM-MMCX (where output P0 is usually |
---|
| 54 | % connected to input P3 since both neighbor ground pins). |
---|
| 55 | wl_triggerManagerCmd(node_tx2, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.EXT_IN_P0, trig_in_ids.EXT_IN_P3]); |
---|
[2045] | 56 | |
---|
[4689] | 57 | % For the secondary transmit node, we enable the debounce circuity on the external input |
---|
| 58 | % pin 3 to guard against any noise in the trigger signal. |
---|
| 59 | % |
---|
[4983] | 60 | wl_triggerManagerCmd(node_tx2, 'input_config_debounce_mode', [trig_in_ids.EXT_IN_P0, trig_in_ids.EXT_IN_P3], false); |
---|
[2045] | 61 | |
---|
[4983] | 62 | % To better align the transmitters, we artificially delay the primary |
---|
| 63 | % transmitter's trigger outputs that drive the baseband buffers. An |
---|
| 64 | % external trigger output on the primary transmitter will appear as a |
---|
| 65 | % trigger input at the secondary transmitter a minimum of 5 clock cycles |
---|
| 66 | % later (31.25ns). It will appear a minimum of 9 clock cycles later |
---|
| 67 | % (56.25ns) when the debounce circuitry is enabled. |
---|
| 68 | % |
---|
| 69 | wl_triggerManagerCmd(node_tx1, 'output_config_delay', [trig_out_ids.BASEBAND, trig_out_ids.AGC], 31.25); % 31.25ns delay |
---|
| 70 | |
---|
[4689] | 71 | % Also, we need to delay the AGC on the receiver so that the transmitted waveform has time |
---|
| 72 | % to begin and propagate to the receiver so it can be sampled appropriately. |
---|
| 73 | % |
---|
| 74 | wl_triggerManagerCmd(node_rx, 'output_config_delay', [trig_out_ids.AGC], 3000); % 3000ns delay |
---|
[2045] | 75 | |
---|
[4689] | 76 | % Get IDs for the interfaces on the boards |
---|
| 77 | % NOTE: This example assumes each board has the same interface capabilities, we only |
---|
| 78 | % need to get the IDs from one of the boards |
---|
| 79 | % |
---|
[4702] | 80 | ifc_ids_4RF = wl_getInterfaceIDs(node_tx1); |
---|
| 81 | ifc_ids_2RF = wl_getInterfaceIDs(node_rx); |
---|
[2045] | 82 | |
---|
[4702] | 83 | % Set up the interface for the experiment |
---|
| 84 | wl_interfaceCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'tx_gains', 3, 30); |
---|
[2045] | 85 | |
---|
[4702] | 86 | % Set the channel |
---|
| 87 | % NOTE: Due to the different number of interfaces, we need to issue |
---|
| 88 | % multiple commands to the differnt types of nodes |
---|
| 89 | % |
---|
[4983] | 90 | wl_interfaceCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'channel', 2.4, 1); |
---|
| 91 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'channel', 2.4, 1); |
---|
[4702] | 92 | |
---|
[2045] | 93 | if(USE_AGC) |
---|
[4702] | 94 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'rx_gain_mode', 'automatic'); |
---|
| 95 | wl_basebandCmd(node_rx,'agc_target',-8); |
---|
[2045] | 96 | else |
---|
[4689] | 97 | RxGainRF = 1; % Rx RF Gain in [1:3] |
---|
| 98 | RxGainBB = 4; % Rx Baseband Gain in [0:31] |
---|
| 99 | |
---|
[4702] | 100 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'rx_gain_mode', 'manual'); |
---|
| 101 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'rx_gains', RxGainRF, RxGainBB); |
---|
[2045] | 102 | end |
---|
| 103 | |
---|
[4702] | 104 | wl_interfaceCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'tx_lpf_corn_freq', 2); % Configure Tx for 36MHz of bandwidth |
---|
| 105 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'rx_lpf_corn_freq', 3); % Configure Rx for 36MHz of bandwidth |
---|
[2045] | 106 | |
---|
[4689] | 107 | % Read the transmitter's maximum I/Q buffer length |
---|
[4702] | 108 | maximum_buffer_len = wl_basebandCmd(node_tx1, ifc_ids_4RF.RF_A, 'tx_buff_max_num_samples'); |
---|
[2045] | 109 | |
---|
[4689] | 110 | % Our transmission length for this example does not need to fill the entire transmit buffer, |
---|
| 111 | % so we will use the smaller of two values: the maximum buffer length the board can support |
---|
| 112 | % or an arbitrary value defined by this script. |
---|
| 113 | % |
---|
| 114 | txLength = min(MAX_TX_LEN, maximum_buffer_len); |
---|
[4359] | 115 | |
---|
[4689] | 116 | % Set up the baseband for the experiment |
---|
| 117 | wl_basebandCmd(nodes, 'tx_delay', 0); |
---|
| 118 | wl_basebandCmd(nodes, 'tx_length', txLength); |
---|
| 119 | wl_basebandCmd(nodes, 'rx_length', txLength); |
---|
[4359] | 120 | |
---|
| 121 | |
---|
[2045] | 122 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 123 | % Signal processing to generate transmit signal |
---|
[4689] | 124 | % |
---|
| 125 | % NOTE: We can send any signal we want out of each of the 8 transmit antennas. |
---|
| 126 | % For visualization, we'll send "pink" noise of 1MHz out of each, but centered |
---|
| 127 | % at different parts of the 40MHz band. |
---|
| 128 | % |
---|
[2045] | 129 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 130 | |
---|
[4689] | 131 | % First generate the preamble for AGC |
---|
| 132 | % NOTE: The preamble corresponds to the short symbols from the 802.11a PHY standard |
---|
| 133 | % |
---|
[2045] | 134 | shortSymbol_freq = [0 0 0 0 0 0 0 0 1+i 0 0 0 -1+i 0 0 0 -1-i 0 0 0 1-i 0 0 0 -1-i 0 0 0 1-i 0 0 0 0 0 0 0 1-i 0 0 0 -1-i 0 0 0 1-i 0 0 0 -1-i 0 0 0 -1+i 0 0 0 1+i 0 0 0 0 0 0 0].'; |
---|
| 135 | shortSymbol_freq = [zeros(32,1);shortSymbol_freq;zeros(32,1)]; |
---|
| 136 | shortSymbol_time = ifft(fftshift(shortSymbol_freq)); |
---|
| 137 | shortSymbol_time = (shortSymbol_time(1:32).')./max(abs(shortSymbol_time)); |
---|
[4689] | 138 | shortsyms_rep = repmat(shortSymbol_time,1,30); |
---|
| 139 | preamble_single = shortsyms_rep; |
---|
| 140 | preamble_single = preamble_single(:); |
---|
[2045] | 141 | |
---|
| 142 | shifts = floor(linspace(0,31,8)); |
---|
| 143 | for k = 1:8 |
---|
[4689] | 144 | % Shift preamble for each antenna so we don't have accidental beamforming |
---|
[2045] | 145 | preamble(:,k) = circshift(preamble_single,shifts(k)); |
---|
| 146 | end |
---|
| 147 | |
---|
[4689] | 148 | % Constants for generating the payload |
---|
| 149 | Ts = 1 / (wl_basebandCmd(node_tx1, 'tx_buff_clk_freq')); |
---|
| 150 | BW = 1; %MHz |
---|
[2045] | 151 | |
---|
[4689] | 152 | % Generate the payload |
---|
| 153 | payload = complex(randn(txLength-length(preamble),8),randn(txLength-length(preamble),8)); |
---|
| 154 | payload_freq = fftshift(fft(payload)); |
---|
| 155 | freqVec = linspace(-((1/Ts)/2e6),((1/Ts)/2e6), txLength - length(preamble)); |
---|
| 156 | noise_centerFreqs = linspace(-12,12,8); |
---|
| 157 | |
---|
[2045] | 158 | for k = 1:8 |
---|
| 159 | payload_freq((freqVec < (noise_centerFreqs(k) - BW/2)) | (freqVec > (noise_centerFreqs(k) + BW/2)),k)=0; |
---|
| 160 | end |
---|
| 161 | |
---|
[4689] | 162 | payload = ifft(fftshift(payload_freq)); |
---|
| 163 | txData = [preamble;payload]; |
---|
[2045] | 164 | |
---|
| 165 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 166 | % Transmit and receive signal using WARPLab |
---|
| 167 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 168 | |
---|
[4702] | 169 | wl_basebandCmd(node_tx1, ifc_ids_4RF.RF_ALL_VEC, 'write_IQ', txData(:,1:4)); % First 4 columns of txData is for primary tx |
---|
| 170 | wl_basebandCmd(node_tx2, ifc_ids_4RF.RF_ALL_VEC, 'write_IQ', txData(:,5:8)); % Second 4 columns of txData is for secondary tx |
---|
[2045] | 171 | |
---|
[4702] | 172 | wl_basebandCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'tx_buff_en'); |
---|
| 173 | wl_basebandCmd(node_rx, ifc_ids_2RF.RF_ALL, 'rx_buff_en'); |
---|
[2045] | 174 | |
---|
[4702] | 175 | wl_interfaceCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'tx_en'); |
---|
| 176 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'rx_en'); |
---|
[2045] | 177 | |
---|
| 178 | |
---|
| 179 | set(gcf, 'KeyPressFcn','RUN_CONTINOUSLY=0;'); |
---|
| 180 | fprintf('Press any key to halt experiment\n') |
---|
| 181 | |
---|
| 182 | while(1) |
---|
| 183 | eth_trig.send(); |
---|
| 184 | |
---|
[4702] | 185 | rx_IQ = wl_basebandCmd(node_rx, ifc_ids_2RF.RF_ALL_VEC, 'read_IQ', 0, txLength); |
---|
[2045] | 186 | |
---|
| 187 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 188 | % Visualize results |
---|
| 189 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
| 190 | t = [0:Ts:(txLength-1)*Ts].'; |
---|
| 191 | figure(1); |
---|
| 192 | ax(1) = subplot(2,2,1); |
---|
| 193 | plot(t,real(rx_IQ(:,1))) |
---|
| 194 | title('Re\{rx\_IQ_{RFA}\}') |
---|
| 195 | xlabel('Time (s)') |
---|
| 196 | axis([0, max(t),-1,1]) |
---|
| 197 | ax(2) = subplot(2,2,2); |
---|
| 198 | plot(t,real(rx_IQ(:,2))) |
---|
[2046] | 199 | title('Re\{rx\_IQ_{RFB}\}') |
---|
[2045] | 200 | xlabel('Time (s)') |
---|
| 201 | %linkaxes(ax,'x') |
---|
| 202 | axis([0, max(t),-1,1]) |
---|
| 203 | |
---|
| 204 | FFTSIZE = 1024; |
---|
| 205 | |
---|
| 206 | ax(1) = subplot(2,2,3); |
---|
| 207 | rx_IQ_slice = rx_IQ(2049:end,1); |
---|
| 208 | rx_IQ_rs = reshape(rx_IQ_slice,FFTSIZE,length(rx_IQ_slice)/FFTSIZE); |
---|
| 209 | f = linspace(-20,20,FFTSIZE); |
---|
| 210 | fft_mag = abs(fftshift(fft(rx_IQ_rs))); |
---|
| 211 | plot(f,20*log10(mean(fft_mag,2))) |
---|
| 212 | title('FFT Magnitude of rx\_IQ_{RFA}') |
---|
| 213 | xlabel('Frequency (MHz)') |
---|
| 214 | axis([-20, 20,-20,40]) |
---|
| 215 | |
---|
| 216 | ax(2) = subplot(2,2,4); |
---|
| 217 | rx_IQ_slice = rx_IQ(2049:end,2); |
---|
| 218 | rx_IQ_rs = reshape(rx_IQ_slice,FFTSIZE,length(rx_IQ_slice)/FFTSIZE); |
---|
| 219 | f = linspace(-20,20,FFTSIZE); |
---|
| 220 | fft_mag = abs(fftshift(fft(rx_IQ_rs))); |
---|
| 221 | plot(f,20*log10(mean(fft_mag,2))) |
---|
| 222 | title('FFT Magnitude of rx\_IQ_{RFB}') |
---|
| 223 | xlabel('Frequency (MHz)') |
---|
| 224 | %linkaxes(ax,'x') |
---|
| 225 | axis([-20, 20,-20,40]) |
---|
| 226 | |
---|
| 227 | |
---|
| 228 | drawnow |
---|
| 229 | |
---|
| 230 | if (~RUN_CONTINOUSLY) |
---|
| 231 | break |
---|
| 232 | end |
---|
| 233 | |
---|
| 234 | end |
---|
| 235 | |
---|
[4689] | 236 | % Disable the TX / RX on all nodes |
---|
[4702] | 237 | wl_basebandCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'tx_rx_buff_dis'); |
---|
| 238 | wl_basebandCmd(node_rx, ifc_ids_2RF.RF_ALL, 'tx_rx_buff_dis'); |
---|
[4689] | 239 | |
---|
[4702] | 240 | wl_interfaceCmd([node_tx1, node_tx2], ifc_ids_4RF.RF_ALL, 'tx_rx_dis'); |
---|
| 241 | wl_interfaceCmd(node_rx, ifc_ids_2RF.RF_ALL, 'tx_rx_dis'); |
---|
[4689] | 242 | |
---|
[4702] | 243 | |
---|