source: ResearchApps/PHY/WARPLAB/WARPLab7/M_Code_Examples/wl_example_siso_ofdm_txrx.m

Last change on this file was 5621, checked in by chunter, 6 years ago

1) Improved examples' handling of rssi_length to remove the requirement that the number of IQ samples transmitted/received be divisible by 4
2) Disabled RF interfaces in energy trigger example in the scenario that the energy trigger does not execute

File size: 23.1 KB
Line 
1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2% wl_example_siso_ofdm_txrx.m
3% A detailed write-up of this example is available on the wiki:
4% http://warpproject.org/trac/wiki/WARPLab/Examples/OFDM
5%
6% Copyright (c) 2015 Mango Communications - All Rights Reserved
7% Distributed under the WARP License (http://warpproject.org/license)
8%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9clear
10
11% Params:
12USE_WARPLAB_TXRX        = 1;           % Enable WARPLab-in-the-loop (otherwise sim-only)
13WRITE_PNG_FILES         = 0;           % Enable writing plots to PNG
14CHANNEL                 = 11;          % Channel to tune Tx and Rx radios
15
16% Waveform params
17N_OFDM_SYMS             = 500;         % Number of OFDM symbols
18MOD_ORDER               = 16;           % Modulation order (2/4/16/64 = BSPK/QPSK/16-QAM/64-QAM)
19TX_SCALE                = 1.0;          % Scale for Tx waveform ([0:1])
20
21% OFDM params
22SC_IND_PILOTS           = [8 22 44 58];                           % Pilot subcarrier indices
23SC_IND_DATA             = [2:7 9:21 23:27 39:43 45:57 59:64];     % Data subcarrier indices
24N_SC                    = 64;                                     % Number of subcarriers
25CP_LEN                  = 16;                                     % Cyclic prefix length
26N_DATA_SYMS             = N_OFDM_SYMS * length(SC_IND_DATA);      % Number of data symbols (one per data-bearing subcarrier per OFDM symbol)
27INTERP_RATE             = 2;                                      % Interpolation rate (must be 2)
28
29% Rx processing params
30FFT_OFFSET                    = 4;           % Number of CP samples to use in FFT (on average)
31LTS_CORR_THRESH               = 0.8;         % Normalized threshold for LTS correlation
32DO_APPLY_CFO_CORRECTION       = 1;           % Enable CFO estimation/correction
33DO_APPLY_PHASE_ERR_CORRECTION = 1;           % Enable Residual CFO estimation/correction
34DO_APPLY_SFO_CORRECTION       = 1;           % Enable SFO estimation/correction
35
36DECIMATE_RATE                 = INTERP_RATE;
37
38% WARPLab experiment params
39USE_AGC                 = true;        % Use the AGC if running on WARP hardware
40MAX_TX_LEN              = 2^20;        % Maximum number of samples to use for this experiment
41TRIGGER_OFFSET_TOL_NS   = 3000;        % Trigger time offset toleration between Tx and Rx that can be accomodated
42
43
44
45if(USE_WARPLAB_TXRX)
46
47    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48    % Set up the WARPLab experiment
49    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50
51    NUMNODES = 2;
52
53    % Create a vector of node objects
54    nodes   = wl_initNodes(NUMNODES);
55    node_tx = nodes(1);
56    node_rx = nodes(2);
57
58    % Create a UDP broadcast trigger and tell each node to be ready for it
59    eth_trig = wl_trigger_eth_udp_broadcast;
60    wl_triggerManagerCmd(nodes, 'add_ethernet_trigger', [eth_trig]);
61
62    % Read Trigger IDs into workspace
63    trig_in_ids  = wl_getTriggerInputIDs(nodes(1));
64    trig_out_ids = wl_getTriggerOutputIDs(nodes(1));
65
66    % For both nodes, we will allow Ethernet to trigger the buffer baseband and the AGC
67    wl_triggerManagerCmd(nodes, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]);
68
69    % Set the trigger output delays.
70    nodes.wl_triggerManagerCmd('output_config_delay', [trig_out_ids.BASEBAND], 0);
71    nodes.wl_triggerManagerCmd('output_config_delay', [trig_out_ids.AGC], TRIGGER_OFFSET_TOL_NS);
72
73    % Get IDs for the interfaces on the boards.
74    ifc_ids_TX = wl_getInterfaceIDs(node_tx);
75    ifc_ids_RX = wl_getInterfaceIDs(node_rx);
76
77    % Set up the TX / RX nodes and RF interfaces
78    TX_RF     = ifc_ids_TX.RF_A;
79    TX_RF_VEC = ifc_ids_TX.RF_A;
80    TX_RF_ALL = ifc_ids_TX.RF_ALL;
81   
82    RX_RF     = ifc_ids_RX.RF_A;
83    RX_RF_VEC = ifc_ids_RX.RF_A;
84    RX_RF_ALL = ifc_ids_RX.RF_ALL;
85
86    % Set up the interface for the experiment
87    wl_interfaceCmd(node_tx, TX_RF_ALL, 'channel', 2.4, CHANNEL);
88    wl_interfaceCmd(node_rx, RX_RF_ALL, 'channel', 2.4, CHANNEL);
89
90    wl_interfaceCmd(node_tx, TX_RF_ALL, 'tx_gains', 3, 30);
91
92    if(USE_AGC)
93        wl_interfaceCmd(node_rx, RX_RF_ALL, 'rx_gain_mode', 'automatic');
94        wl_basebandCmd(nodes, 'agc_target', -13);
95    else
96        wl_interfaceCmd(node_rx, RX_RF_ALL, 'rx_gain_mode', 'manual');
97        RxGainRF = 2;                  % Rx RF Gain in [1:3]
98        RxGainBB = 12;                 % Rx Baseband Gain in [0:31]
99        wl_interfaceCmd(node_rx, RX_RF_ALL, 'rx_gains', RxGainRF, RxGainBB);
100    end
101
102    % Get parameters from the node
103    SAMP_FREQ    = wl_basebandCmd(nodes(1), 'tx_buff_clk_freq');
104    Ts           = 1/SAMP_FREQ;
105
106    % We will read the transmitter's maximum I/Q buffer length
107    % and assign that value to a temporary variable.
108    %
109    % NOTE:  We assume that the buffers sizes are the same for all interfaces
110
111    maximum_buffer_len = min(MAX_TX_LEN, wl_basebandCmd(node_tx, TX_RF_VEC, 'tx_buff_max_num_samples'));
112    example_mode_string = 'hw';
113else
114    % Use sane defaults for hardware-dependent params in sim-only version
115    maximum_buffer_len = min(MAX_TX_LEN, 2^20);
116    SAMP_FREQ           = 40e6;
117    example_mode_string = 'sim';
118end
119
120%% Define a half-band 2x interpolation filter response
121interp_filt2 = zeros(1,43);
122interp_filt2([1 3 5 7 9 11 13 15 17 19 21]) = [12 -32 72 -140 252 -422 682 -1086 1778 -3284 10364];
123interp_filt2([23 25 27 29 31 33 35 37 39 41 43]) = interp_filt2(fliplr([1 3 5 7 9 11 13 15 17 19 21]));
124interp_filt2(22) = 16384;
125interp_filt2 = interp_filt2./max(abs(interp_filt2));
126
127% Define the preamble
128% Note: The STS symbols in the preamble meet the requirements needed by the
129% AGC core at the receiver. Details on the operation of the AGC are
130% available on the wiki: http://warpproject.org/trac/wiki/WARPLab/AGC
131sts_f = zeros(1,64);
132sts_f(1:27) = [0 0 0 0 -1-1i 0 0 0 -1-1i 0 0 0 1+1i 0 0 0 1+1i 0 0 0 1+1i 0 0 0 1+1i 0 0];
133sts_f(39:64) = [0 0 1+1i 0 0 0 -1-1i 0 0 0 1+1i 0 0 0 -1-1i 0 0 0 -1-1i 0 0 0 1+1i 0 0 0];
134sts_t = ifft(sqrt(13/6).*sts_f, 64);
135sts_t = sts_t(1:16);
136
137% LTS for CFO and channel estimation
138lts_f = [0 1 -1 -1 1 1 -1 1 -1 1 -1 -1 -1 -1 -1 1 1 -1 -1 1 -1 1 -1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 -1 -1 1 1 -1 1 -1 1 1 1 1 1 1 -1 -1 1 1 -1 1 -1 1 1 1 1];
139lts_t = ifft(lts_f, 64);
140
141% Use 30 copies of the 16-sample STS for extra AGC settling margin
142preamble = [repmat(sts_t, 1, 30)  lts_t(33:64) lts_t lts_t];
143
144% Sanity check variables that affect the number of Tx samples
145num_samps_needed = ceil((TRIGGER_OFFSET_TOL_NS*1e-9) / (1/SAMP_FREQ)) + ...
146                    INTERP_RATE*((N_OFDM_SYMS * (N_SC + CP_LEN)) + length(preamble) +  ceil(length(interp_filt2)/2));
147
148                               
149if(num_samps_needed > maximum_buffer_len)
150    fprintf('Too many OFDM symbols for TX_NUM_SAMPS!\n');
151    fprintf('Raise MAX_TX_LEN to %d, or \n', num_samps_needed);
152    fprintf('Reduce N_OFDM_SYMS to %d\n', floor(((maximum_buffer_len - ceil((TRIGGER_OFFSET_TOL_NS*1e-9) / (1/SAMP_FREQ)))/INTERP_RATE - (length(preamble) +  ceil(length(interp_filt2)/2)))/(N_SC + CP_LEN)));
153    return;
154end
155
156%% Generate a payload of random integers
157tx_data = randi(MOD_ORDER, 1, N_DATA_SYMS) - 1;
158
159% Functions for data -> complex symbol mapping (like qammod, avoids comm toolbox requirement)
160% These anonymous functions implement the modulation mapping from IEEE 802.11-2012 Section 18.3.5.8
161modvec_bpsk   =  (1/sqrt(2))  .* [-1 1];
162modvec_16qam  =  (1/sqrt(10)) .* [-3 -1 +3 +1];
163modvec_64qam  =  (1/sqrt(43)) .* [-7 -5 -1 -3 +7 +5 +1 +3];
164
165mod_fcn_bpsk  = @(x) complex(modvec_bpsk(1+x),0);
166mod_fcn_qpsk  = @(x) complex(modvec_bpsk(1+bitshift(x, -1)), modvec_bpsk(1+mod(x, 2)));
167mod_fcn_16qam = @(x) complex(modvec_16qam(1+bitshift(x, -2)), modvec_16qam(1+mod(x,4)));
168mod_fcn_64qam = @(x) complex(modvec_64qam(1+bitshift(x, -3)), modvec_64qam(1+mod(x,8)));
169
170% Map the data values on to complex symbols
171switch MOD_ORDER
172    case 2         % BPSK
173        tx_syms = arrayfun(mod_fcn_bpsk, tx_data);
174    case 4         % QPSK
175        tx_syms = arrayfun(mod_fcn_qpsk, tx_data);
176    case 16        % 16-QAM
177        tx_syms = arrayfun(mod_fcn_16qam, tx_data);
178    case 64        % 64-QAM
179        tx_syms = arrayfun(mod_fcn_64qam, tx_data);
180    otherwise
181        fprintf('Invalid MOD_ORDER (%d)!  Must be in [2, 4, 16, 64]\n', MOD_ORDER);
182        return;
183end
184
185% Reshape the symbol vector to a matrix with one column per OFDM symbol
186tx_syms_mat = reshape(tx_syms, length(SC_IND_DATA), N_OFDM_SYMS);
187
188% Define the pilot tone values as BPSK symbols
189pilots = [1 1 -1 1].';
190
191% Repeat the pilots across all OFDM symbols
192pilots_mat = repmat(pilots, 1, N_OFDM_SYMS);
193
194%% IFFT
195
196% Construct the IFFT input matrix
197ifft_in_mat = zeros(N_SC, N_OFDM_SYMS);
198
199% Insert the data and pilot values; other subcarriers will remain at 0
200ifft_in_mat(SC_IND_DATA, :)   = tx_syms_mat;
201ifft_in_mat(SC_IND_PILOTS, :) = pilots_mat;
202
203%Perform the IFFT
204tx_payload_mat = ifft(ifft_in_mat, N_SC, 1);
205
206% Insert the cyclic prefix
207if(CP_LEN > 0)
208    tx_cp = tx_payload_mat((end-CP_LEN+1 : end), :);
209    tx_payload_mat = [tx_cp; tx_payload_mat];
210end
211
212% Reshape to a vector
213tx_payload_vec = reshape(tx_payload_mat, 1, numel(tx_payload_mat));
214
215% Construct the full time-domain OFDM waveform
216tx_vec = [preamble tx_payload_vec];
217
218% Pad with zeros for transmission to deal with delay through the
219% interpolation filter
220tx_vec_padded = [tx_vec, zeros(1, ceil(length(interp_filt2)/2))];
221
222%% Interpolate
223% Zero pad then filter (same as interp or upfirdn without signal processing toolbox)
224if( INTERP_RATE ~= 2)
225   fprintf('Error: INTERP_RATE must equal 2\n'); 
226   return;
227end
228
229tx_vec_2x = zeros(1, 2*numel(tx_vec_padded));
230tx_vec_2x(1:2:end) = tx_vec_padded;
231tx_vec_air = filter(interp_filt2, 1, tx_vec_2x);
232
233
234% Scale the Tx vector to +/- 1
235tx_vec_air = TX_SCALE .* tx_vec_air ./ max(abs(tx_vec_air));
236
237TX_NUM_SAMPS = length(tx_vec_air);
238
239if(USE_WARPLAB_TXRX)
240    wl_basebandCmd(nodes, 'tx_delay', 0);
241    wl_basebandCmd(nodes, 'tx_length', TX_NUM_SAMPS);                                                        % Number of samples to send
242    wl_basebandCmd(nodes, 'rx_length', TX_NUM_SAMPS + ceil((TRIGGER_OFFSET_TOL_NS*1e-9) / (1/SAMP_FREQ)));   % Number of samples to receive
243end
244
245
246%% WARPLab Tx/Rx
247if(USE_WARPLAB_TXRX)
248    % Write the Tx waveform to the Tx node
249    wl_basebandCmd(node_tx, TX_RF_VEC, 'write_IQ', tx_vec_air(:));
250
251    % Enable the Tx and Rx radios
252    wl_interfaceCmd(node_tx, TX_RF, 'tx_en');
253    wl_interfaceCmd(node_rx, RX_RF, 'rx_en');
254
255    % Enable the Tx and Rx buffers
256    wl_basebandCmd(node_tx, TX_RF, 'tx_buff_en');
257    wl_basebandCmd(node_rx, RX_RF, 'rx_buff_en');
258
259    % Trigger the Tx/Rx cycle at both nodes
260    eth_trig.send();
261
262    % Retrieve the received waveform from the Rx node
263    rx_vec_air = wl_basebandCmd(node_rx, RX_RF_VEC, 'read_IQ', 0, TX_NUM_SAMPS + (ceil((TRIGGER_OFFSET_TOL_NS*1e-9) / (1/SAMP_FREQ))));
264
265    rx_vec_air = rx_vec_air(:).';
266
267    % Disable the Tx/Rx radios and buffers
268    wl_basebandCmd(node_tx, TX_RF_ALL, 'tx_rx_buff_dis');
269    wl_basebandCmd(node_rx, RX_RF_ALL, 'tx_rx_buff_dis');
270   
271    wl_interfaceCmd(node_tx, TX_RF_ALL, 'tx_rx_dis');
272    wl_interfaceCmd(node_rx, RX_RF_ALL, 'tx_rx_dis');
273else
274    % Sim-only mode: Apply wireless degradations here for sim (noise, fading, etc)
275
276    % Perfect (ie. Rx=Tx):
277    % rx_vec_air = tx_vec_air;
278
279    % AWGN:
280    rx_vec_air = [tx_vec_air, zeros(1,ceil((TRIGGER_OFFSET_TOL_NS*1e-9) / (1/SAMP_FREQ)))];
281    rx_vec_air = rx_vec_air + 0*complex(randn(1,length(rx_vec_air)), randn(1,length(rx_vec_air)));
282
283    % CFO:
284    % rx_vec_air = tx_vec_air .* exp(-1i*2*pi*1e-4*[0:length(tx_vec_air)-1]);
285end
286
287%% Decimate
288if( INTERP_RATE ~= 2)
289   fprintf('Error: INTERP_RATE must equal 2\n'); 
290   return;
291end
292
293raw_rx_dec = filter(interp_filt2, 1, rx_vec_air);
294raw_rx_dec = raw_rx_dec(1:2:end);
295
296
297%% Correlate for LTS
298
299% Complex cross correlation of Rx waveform with time-domain LTS
300lts_corr = abs(conv(conj(fliplr(lts_t)), sign(raw_rx_dec)));
301
302% Skip early and late samples - avoids occasional false positives from pre-AGC samples
303lts_corr = lts_corr(32:end-32);
304
305% Find all correlation peaks
306lts_peaks = find(lts_corr(1:800) > LTS_CORR_THRESH*max(lts_corr));
307
308% Select best candidate correlation peak as LTS-payload boundary
309[LTS1, LTS2] = meshgrid(lts_peaks,lts_peaks);
310[lts_second_peak_index,y] = find(LTS2-LTS1 == length(lts_t));
311
312% Stop if no valid correlation peak was found
313if(isempty(lts_second_peak_index))
314    fprintf('No LTS Correlation Peaks Found!\n');
315    return;
316end
317
318% Set the sample indices of the payload symbols and preamble
319% The "+32" corresponds to the 32-sample cyclic prefix on the preamble LTS
320% The "-160" corresponds to the length of the preamble LTS (2.5 copies of 64-sample LTS)
321payload_ind = lts_peaks(max(lts_second_peak_index)) + 32;
322lts_ind = payload_ind-160;
323
324if(DO_APPLY_CFO_CORRECTION)
325    %Extract LTS (not yet CFO corrected)
326    rx_lts = raw_rx_dec(lts_ind : lts_ind+159);
327    rx_lts1 = rx_lts(-64+-FFT_OFFSET + [97:160]);
328    rx_lts2 = rx_lts(-FFT_OFFSET + [97:160]);
329
330    %Calculate coarse CFO est
331    rx_cfo_est_lts = mean(unwrap(angle(rx_lts2 .* conj(rx_lts1))));
332    rx_cfo_est_lts = rx_cfo_est_lts/(2*pi*64);
333else
334    rx_cfo_est_lts = 0;
335end
336
337% Apply CFO correction to raw Rx waveform
338rx_cfo_corr_t = exp(-1i*2*pi*rx_cfo_est_lts*[0:length(raw_rx_dec)-1]);
339rx_dec_cfo_corr = raw_rx_dec .* rx_cfo_corr_t;
340
341% Re-extract LTS for channel estimate
342rx_lts = rx_dec_cfo_corr(lts_ind : lts_ind+159);
343rx_lts1 = rx_lts(-64+-FFT_OFFSET + [97:160]);
344rx_lts2 = rx_lts(-FFT_OFFSET + [97:160]);
345
346rx_lts1_f = fft(rx_lts1);
347rx_lts2_f = fft(rx_lts2);
348
349% Calculate channel estimate from average of 2 training symbols
350rx_H_est = lts_f .* (rx_lts1_f + rx_lts2_f)/2;
351
352%% Rx payload processing
353
354% Extract the payload samples (integral number of OFDM symbols following preamble)
355payload_vec = rx_dec_cfo_corr(payload_ind : payload_ind+N_OFDM_SYMS*(N_SC+CP_LEN)-1);
356payload_mat = reshape(payload_vec, (N_SC+CP_LEN), N_OFDM_SYMS);
357
358% Remove the cyclic prefix, keeping FFT_OFFSET samples of CP (on average)
359payload_mat_noCP = payload_mat(CP_LEN-FFT_OFFSET+[1:N_SC], :);
360
361% Take the FFT
362syms_f_mat = fft(payload_mat_noCP, N_SC, 1);
363
364% Equalize (zero-forcing, just divide by complex chan estimates)
365syms_eq_mat = syms_f_mat ./ repmat(rx_H_est.', 1, N_OFDM_SYMS);
366
367if DO_APPLY_SFO_CORRECTION
368    % SFO manifests as a frequency-dependent phase whose slope increases
369    % over time as the Tx and Rx sample streams drift apart from one
370    % another. To correct for this effect, we calculate this phase slope at
371    % each OFDM symbol using the pilot tones and use this slope to
372    % interpolate a phase correction for each data-bearing subcarrier.
373
374    % Extract the pilot tones and "equalize" them by their nominal Tx values
375    pilots_f_mat = syms_eq_mat(SC_IND_PILOTS, :);
376    pilots_f_mat_comp = pilots_f_mat.*pilots_mat;
377
378    % Calculate the phases of every Rx pilot tone
379    pilot_phases = unwrap(angle(fftshift(pilots_f_mat_comp,1)), [], 1);
380
381    % Calculate slope of pilot tone phases vs frequency in each OFDM symbol
382    pilot_spacing_mat = repmat(mod(diff(fftshift(SC_IND_PILOTS)),64).', 1, N_OFDM_SYMS);                       
383    pilot_slope_mat = mean(diff(pilot_phases) ./ pilot_spacing_mat);
384
385    % Calculate the SFO correction phases for each OFDM symbol
386    pilot_phase_sfo_corr = fftshift((-32:31).' * pilot_slope_mat, 1);
387    pilot_phase_corr = exp(-1i*(pilot_phase_sfo_corr));
388
389    % Apply the pilot phase correction per symbol
390    syms_eq_mat = syms_eq_mat .* pilot_phase_corr;
391else
392    % Define an empty SFO correction matrix (used by plotting code below)
393    pilot_phase_sfo_corr = zeros(N_SC, N_OFDM_SYMS);
394end
395
396
397if DO_APPLY_PHASE_ERR_CORRECTION
398    % Extract the pilots and calculate per-symbol phase error
399    pilots_f_mat = syms_eq_mat(SC_IND_PILOTS, :);
400    pilots_f_mat_comp = pilots_f_mat.*pilots_mat;
401    pilot_phase_err = angle(mean(pilots_f_mat_comp));
402else
403    % Define an empty phase correction vector (used by plotting code below)
404    pilot_phase_err = zeros(1, N_OFDM_SYMS);
405end
406pilot_phase_err_corr = repmat(pilot_phase_err, N_SC, 1);
407pilot_phase_corr = exp(-1i*(pilot_phase_err_corr));
408
409% Apply the pilot phase correction per symbol
410syms_eq_pc_mat = syms_eq_mat .* pilot_phase_corr;
411payload_syms_mat = syms_eq_pc_mat(SC_IND_DATA, :);
412
413%% Demodulate
414rx_syms = reshape(payload_syms_mat, 1, N_DATA_SYMS);
415
416demod_fcn_bpsk = @(x) double(real(x)>0);
417demod_fcn_qpsk = @(x) double(2*(real(x)>0) + 1*(imag(x)>0));
418demod_fcn_16qam = @(x) (8*(real(x)>0)) + (4*(abs(real(x))<0.6325)) + (2*(imag(x)>0)) + (1*(abs(imag(x))<0.6325));
419demod_fcn_64qam = @(x) (32*(real(x)>0)) + (16*(abs(real(x))<0.6172)) + (8*((abs(real(x))<(0.9258))&&((abs(real(x))>(0.3086))))) + (4*(imag(x)>0)) + (2*(abs(imag(x))<0.6172)) + (1*((abs(imag(x))<(0.9258))&&((abs(imag(x))>(0.3086)))));
420
421switch(MOD_ORDER)
422    case 2         % BPSK
423        rx_data = arrayfun(demod_fcn_bpsk, rx_syms);
424    case 4         % QPSK
425        rx_data = arrayfun(demod_fcn_qpsk, rx_syms);
426    case 16        % 16-QAM
427        rx_data = arrayfun(demod_fcn_16qam, rx_syms);
428    case 64        % 64-QAM
429        rx_data = arrayfun(demod_fcn_64qam, rx_syms);
430end
431
432%% Plot Results
433cf = 0;
434
435% Tx signal
436cf = cf + 1;
437figure(cf); clf;
438
439subplot(2,1,1);
440plot(real(tx_vec_air), 'b');
441axis([0 length(tx_vec_air) -TX_SCALE TX_SCALE])
442grid on;
443title('Tx Waveform (I)');
444
445subplot(2,1,2);
446plot(imag(tx_vec_air), 'r');
447axis([0 length(tx_vec_air) -TX_SCALE TX_SCALE])
448grid on;
449title('Tx Waveform (Q)');
450
451if(WRITE_PNG_FILES)
452    print(gcf,sprintf('wl_ofdm_plots_%s_txIQ', example_mode_string), '-dpng', '-r96', '-painters')
453end
454
455% Rx signal
456cf = cf + 1;
457figure(cf); clf;
458subplot(2,1,1);
459plot(real(rx_vec_air), 'b');
460axis([0 length(rx_vec_air) -TX_SCALE TX_SCALE])
461grid on;
462title('Rx Waveform (I)');
463
464subplot(2,1,2);
465plot(imag(rx_vec_air), 'r');
466axis([0 length(rx_vec_air) -TX_SCALE TX_SCALE])
467grid on;
468title('Rx Waveform (Q)');
469
470if(WRITE_PNG_FILES)
471    print(gcf,sprintf('wl_ofdm_plots_%s_rxIQ', example_mode_string), '-dpng', '-r96', '-painters')
472end
473
474% Rx LTS correlation
475cf = cf + 1;
476figure(cf); clf;
477lts_to_plot = lts_corr;
478plot(lts_to_plot, '.-b', 'LineWidth', 1);
479hold on;
480grid on;
481line([1 length(lts_to_plot)], LTS_CORR_THRESH*max(lts_to_plot)*[1 1], 'LineStyle', '--', 'Color', 'r', 'LineWidth', 2);
482title('LTS Correlation and Threshold')
483xlabel('Sample Index')
484myAxis = axis();
485axis([1, 1000, myAxis(3), myAxis(4)])
486
487if(WRITE_PNG_FILES)
488    print(gcf,sprintf('wl_ofdm_plots_%s_ltsCorr', example_mode_string), '-dpng', '-r96', '-painters')
489end
490
491% Channel Estimates
492cf = cf + 1;
493
494rx_H_est_plot = repmat(complex(NaN,NaN),1,length(rx_H_est));
495rx_H_est_plot(SC_IND_DATA) = rx_H_est(SC_IND_DATA);
496rx_H_est_plot(SC_IND_PILOTS) = rx_H_est(SC_IND_PILOTS);
497
498x = (20/N_SC) * (-(N_SC/2):(N_SC/2 - 1));
499
500figure(cf); clf;
501subplot(2,1,1);
502stairs(x - (20/(2*N_SC)), fftshift(real(rx_H_est_plot)), 'b', 'LineWidth', 2);
503hold on
504stairs(x - (20/(2*N_SC)), fftshift(imag(rx_H_est_plot)), 'r', 'LineWidth', 2);
505hold off
506axis([min(x) max(x) -1.1*max(abs(rx_H_est_plot)) 1.1*max(abs(rx_H_est_plot))])
507grid on;
508title('Channel Estimates (I and Q)')
509
510subplot(2,1,2);
511bh = bar(x, fftshift(abs(rx_H_est_plot)),1,'LineWidth', 1);
512shading flat
513set(bh,'FaceColor',[0 0 1])
514axis([min(x) max(x) 0 1.1*max(abs(rx_H_est_plot))])
515grid on;
516title('Channel Estimates (Magnitude)')
517xlabel('Baseband Frequency (MHz)')
518
519if(WRITE_PNG_FILES)
520    print(gcf,sprintf('wl_ofdm_plots_%s_chanEst', example_mode_string), '-dpng', '-r96', '-painters')
521end
522
523%% Pilot phase error estimate
524cf = cf + 1;
525figure(cf); clf;
526subplot(2,1,1)
527plot(pilot_phase_err, 'b', 'LineWidth', 2);
528title('Phase Error Estimates')
529xlabel('OFDM Symbol Index')
530ylabel('Radians')
531axis([1 N_OFDM_SYMS -3.2 3.2])
532grid on
533
534h = colorbar;
535set(h,'Visible','off');
536
537subplot(2,1,2)
538imagesc(1:N_OFDM_SYMS, (SC_IND_DATA - N_SC/2), fftshift(pilot_phase_sfo_corr,1))
539xlabel('OFDM Symbol Index')
540ylabel('Subcarrier Index')
541title('Phase Correction for SFO')
542colorbar
543myAxis = caxis();
544if(myAxis(2)-myAxis(1) < (pi))
545   caxis([-pi/2 pi/2])
546end
547
548
549if(WRITE_PNG_FILES)
550    print(gcf,sprintf('wl_ofdm_plots_%s_phaseError', example_mode_string), '-dpng', '-r96', '-painters')
551end
552
553%% Symbol constellation
554cf = cf + 1;
555figure(cf); clf;
556
557plot(payload_syms_mat(:),'ro','MarkerSize',1);
558axis square; axis(1.5*[-1 1 -1 1]);
559grid on;
560hold on;
561
562plot(tx_syms_mat(:),'bo');
563title('Tx and Rx Constellations')
564legend('Rx','Tx','Location','EastOutside');
565
566if(WRITE_PNG_FILES)
567    print(gcf,sprintf('wl_ofdm_plots_%s_constellations', example_mode_string), '-dpng', '-r96', '-painters')
568end
569
570
571% EVM & SNR
572cf = cf + 1;
573figure(cf); clf;
574
575evm_mat = abs(payload_syms_mat - tx_syms_mat).^2;
576aevms = mean(evm_mat(:));
577snr = 10*log10(1./aevms);
578
579subplot(2,1,1)
580plot(100*evm_mat(:),'o','MarkerSize',1)
581axis tight
582hold on
583plot([1 length(evm_mat(:))], 100*[aevms, aevms],'r','LineWidth',4)
584myAxis = axis;
585h = text(round(.05*length(evm_mat(:))), 100*aevms+ .1*(myAxis(4)-myAxis(3)), sprintf('Effective SNR: %.1f dB', snr));
586set(h,'Color',[1 0 0])
587set(h,'FontWeight','bold')
588set(h,'FontSize',10)
589set(h,'EdgeColor',[1 0 0])
590set(h,'BackgroundColor',[1 1 1])
591hold off
592xlabel('Data Symbol Index')
593ylabel('EVM (%)');
594legend('Per-Symbol EVM','Average EVM','Location','NorthWest');
595title('EVM vs. Data Symbol Index')
596grid on
597
598subplot(2,1,2)
599imagesc(1:N_OFDM_SYMS, (SC_IND_DATA - N_SC/2), 100*fftshift(evm_mat,1))
600
601grid on
602xlabel('OFDM Symbol Index')
603ylabel('Subcarrier Index')
604title('EVM vs. (Subcarrier & OFDM Symbol)')
605h = colorbar;
606set(get(h,'title'),'string','EVM (%)');
607myAxis = caxis();
608if (myAxis(2)-myAxis(1)) < 5
609    caxis([myAxis(1), myAxis(1)+5])
610end
611
612if(WRITE_PNG_FILES)
613    print(gcf,sprintf('wl_ofdm_plots_%s_evm', example_mode_string), '-dpng', '-r96', '-painters')
614end
615
616%% Calculate Rx stats
617
618sym_errs = sum(tx_data ~= rx_data);
619bit_errs = length(find(dec2bin(bitxor(tx_data, rx_data),8) == '1'));
620rx_evm   = sqrt(sum((real(rx_syms) - real(tx_syms)).^2 + (imag(rx_syms) - imag(tx_syms)).^2)/(length(SC_IND_DATA) * N_OFDM_SYMS));
621
622fprintf('\nResults:\n');
623fprintf('Num Bytes:   %d\n', N_DATA_SYMS * log2(MOD_ORDER) / 8);
624fprintf('Sym Errors:  %d (of %d total symbols)\n', sym_errs, N_DATA_SYMS);
625fprintf('Bit Errors:  %d (of %d total bits)\n', bit_errs, N_DATA_SYMS * log2(MOD_ORDER));
626
627cfo_est_lts = rx_cfo_est_lts*(SAMP_FREQ/INTERP_RATE);
628cfo_est_phaseErr = mean(diff(unwrap(pilot_phase_err)))/(4e-6*2*pi);
629cfo_total_ppm = ((cfo_est_lts + cfo_est_phaseErr) /  ((2.412+(.005*(CHANNEL-1)))*1e9)) * 1e6;
630
631fprintf('CFO Est:     %3.2f kHz (%3.2f ppm)\n', (cfo_est_lts + cfo_est_phaseErr)*1e-3, cfo_total_ppm);
632fprintf('     LTS CFO Est:                  %3.2f kHz\n', cfo_est_lts*1e-3);
633fprintf('     Phase Error Residual CFO Est: %3.2f kHz\n', cfo_est_phaseErr*1e-3);
634
635if DO_APPLY_SFO_CORRECTION
636    drift_sec = pilot_slope_mat / (2*pi*312500);
637    sfo_est_ppm =  1e6*mean((diff(drift_sec) / 4e-6));
638    sfo_est = sfo_est_ppm*20;
639    fprintf('SFO Est:     %3.2f Hz (%3.2f ppm)\n', sfo_est, sfo_est_ppm);
640
641end
642
643
644
Note: See TracBrowser for help on using the repository browser.