Note: In earlier versions of the 802.11 standard, the (BCC) OFDM example was given in Annex G. However, an error existed (e.g. in IEEE Std 802.11-2007) in that the FCS was incorrect (see link or search online for "IEEE 802.11-09/0042r0").
The WiFi (802.11) standards documents are free to download from the IEEE webpages (see, for example, here). (Sadly, you are required to register first, which appears to serve no purpose).
Nonetheless, I became interested in putting together a simple 802.11g (OFDM) modulator. A really useful walk-through of how to do this is provided in Annex I.1 of this document.
However, there are a few gaps in the worked example and some details are perhaps easy to miss. Therefore, I thought I would share the MATLAB scripts I used for filling in the gaps. These are rather verbose, which I find makes them more useful as a reference, but less useful as anything resembling a usable implementation.
Unfortunately, the main script alone vastly exceeds edaboard's character limit, so please download the full code here: View attachment 160187.
As a quick taster, here is the start of the code. This excerpt gets you as far as the end of the STF:
Code Matlab M - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 close all; clear all; clc; % This script works through Annex I.1 of the 802.11-2016 standard. % Make sure we can see the helper subroutines addpath('helpers'); % ======================== % % == I.1.1 Introduction == % % ======================== % % "This example uses the 36 Mb/s data rate..." rate_info_lut = get_rate_info(); rate_info = rate_info_lut(6); % "... and a message of 100 octets" length_bytes = 100; % ======================= % % == I.1.2 The Message == % % ======================= % % The 72-character message a = ['Joy, bright spark of divinity,' char(10) 'Daughter of Elysium,' char(10) 'Fire-insired we trea']; % [Table I-1] The 100-byte PSDU in hex % Comprises: 24-Byte MAC + 72-Byte DATA + 4-Byte CRC. table_I1 = [... '0402002e00'; '6008cd37a6'; '0020d6013c'; 'f1006008ad'; '3baf00004a'; '6f792c2062'; '7269676874'; '2073706172'; '6b206f6620'; '646976696e'; '6974792c0a'; '4461756768'; '746572206f'; '6620456c79'; '7369756d2c'; '0a46697265'; '2d696e7369'; '7265642077'; '6520747265'; '61673321b6']; % Gradually manipulate PSDU into vector of bytes table_I1 = reshape(table_I1.',[],1); psdu_hex = reshape(table_I1, 2, []).'; psdu_dec = hex2dec(psdu_hex); % Separate PSDU into MAC header, message and CRC mac_header_dec = psdu_dec(1:24); message_dec = psdu_dec(25:end-4); crc_dec = psdu_dec(end-3:end); % Sanity check message content message_char = char(message_dec).'; assert(isequal(message_char, a)); % ====================================== % % == I.1.3 Generation of the preamble == % % ====================================== % % ------------------------------ % % -- I.1.3.1 Short sequences -- % % ------------------------------ % % [Table I-2] Frequency domain representation of the short sequences. % Note: We'll use better precision here: sqrt(13/6) instead of 1.472. p = 1+1j; STF_26 = sqrt(13/6)*[0, 0, +p, 0, 0, 0, -p, 0, ... 0, 0, +p, 0, 0, 0, -p, 0, ... 0, 0, -p, 0, 0, 0, +p, 0, ... 0, 0, 0, 0, 0, 0, -p, 0, ... 0, 0, -p, 0, 0, 0, +p, 0, ... 0, 0, +p, 0, 0, 0, +p, 0, ... 0, 0, +p, 0, 0].'; % Zero-pad up to OFDM symbol size table_I2 = [0*(-32:-27).'; STF_26; 0*(27:31).']; % FFT shift to put DC where it belongs and do OFDM IFFT stf_64 = ifft(fftshift(table_I2)); % [Table I-3] One period of IFFT of the short sequences table_I3 = [ 0 0.046 0.046 1 -0.132 0.002 2 -0.013 -0.079 3 0.143 -0.013 4 0.092 0.000 5 0.143 -0.013 6 -0.013 -0.079 7 -0.132 0.002 8 0.046 0.046 9 0.002 -0.132 10 -0.079 -0.013 11 -0.013 0.143 12 0.000 0.092 13 -0.013 0.143 14 -0.079 -0.013 15 0.002 -0.132 16 0.046 0.046 17 -0.132 0.002 18 -0.013 -0.079 19 0.143 -0.013 20 0.092 0.000 21 0.143 -0.013 22 -0.013 -0.079 23 -0.132 0.002 24 0.046 0.046 25 0.002 -0.132 26 -0.079 -0.013 27 -0.013 0.143 28 0.000 0.092 29 -0.013 0.143 30 -0.079 -0.013 31 0.002 -0.132 32 0.046 0.046 33 -0.132 0.002 34 -0.013 -0.079 35 0.143 -0.013 36 0.092 0.000 37 0.143 -0.013 38 -0.013 -0.079 39 -0.132 0.002 40 0.046 0.046 41 0.002 -0.132 42 -0.079 -0.013 43 -0.013 0.143 44 0.000 0.092 45 -0.013 0.143 46 -0.079 -0.013 47 0.002 -0.132 48 0.046 0.046 49 -0.132 0.002 50 -0.013 -0.079 51 0.143 -0.013 52 0.092 0.000 53 0.143 -0.013 54 -0.013 -0.079 55 -0.132 0.002 56 0.046 0.046 57 0.002 -0.132 58 -0.079 -0.013 59 -0.013 0.143 60 0.000 0.092 61 -0.013 0.143 62 -0.079 -0.013 63 0.002 -0.132]; % Discard columns of indices table_I3(:,[1,4,7,10]) = []; % Convert to complex table_I3 = complex(table_I3(:,1:2:end), table_I3(:,2:2:end)); % Stack in order table_I3 = reshape(table_I3.', [], 1); % Sanity check (up to 3 decimal places) table_I3_err = stf_64 - table_I3; table_I3_err = [real(table_I3_err); imag(table_I3_err)]; assert(all(abs(table_I3_err) < 0.001)); % Extend periodically up to 161 samples. N.B. We don't need to do anything % else for STF due to its 16-sample periodicity. stf_161 = repmat(stf_64, ceil(161/64), 1); stf_161 = stf_161(1:161); % Apply window function stf_161(1) = 0.5 * stf_161(1); stf_161(161) = 0.5 * stf_161(161); % [Table I-4] Time domain representation of the short sequence table_I4 = [ 0 0.023 0.023 1 -0.132 0.002 2 -0.013 -0.079 3 0.143 -0.013 4 0.092 0.000 5 0.143 -0.013 6 -0.013 -0.079 7 -0.132 0.002 8 0.046 0.046 9 0.002 -0.132 10 -0.079 -0.013 11 -0.013 0.143 12 0.000 0.092 13 -0.013 0.143 14 -0.079 -0.013 15 0.002 -0.132 16 0.046 0.046 17 -0.132 0.002 18 -0.013 -0.079 19 0.143 -0.013 20 0.092 0.000 21 0.143 -0.013 22 -0.013 -0.079 23 -0.132 0.002 24 0.046 0.046 25 0.002 -0.132 26 -0.079 -0.013 27 -0.013 0.143 28 0.000 0.092 29 -0.013 0.143 30 -0.079 -0.013 31 0.002 -0.132 32 0.046 0.046 33 -0.132 0.002 34 -0.013 -0.079 35 0.143 -0.013 36 0.092 0.000 37 0.143 -0.013 38 -0.013 -0.079 39 -0.132 0.002 40 0.046 0.046 41 0.002 -0.132 42 -0.079 -0.013 43 -0.013 0.143 44 0.000 0.092 45 -0.013 0.143 46 -0.079 -0.013 47 0.002 -0.132 48 0.046 0.046 49 -0.132 0.002 50 -0.013 -0.079 51 0.143 -0.013 52 0.092 0.000 53 0.143 -0.013 54 -0.013 -0.079 55 -0.132 0.002 56 0.046 0.046 57 0.002 -0.132 58 -0.079 -0.013 59 -0.013 0.143 60 0.000 0.092 61 -0.013 0.143 62 -0.079 -0.013 63 0.002 -0.132 64 0.046 0.046 65 -0.132 0.002 66 -0.013 -0.079 67 0.143 -0.013 68 0.092 0.000 69 0.143 -0.013 70 -0.013 -0.079 71 -0.132 0.002 72 0.046 0.046 73 0.002 -0.132 74 -0.079 -0.013 75 -0.013 0.143 76 0.000 0.092 77 -0.013 0.143 78 -0.079 -0.013 79 0.002 -0.132 80 0.046 0.046 81 -0.132 0.002 82 -0.013 -0.079 83 0.143 -0.013 84 0.092 0.000 85 0.143 -0.013 86 -0.013 -0.079 87 -0.132 0.002 88 0.046 0.046 89 0.002 -0.132 90 -0.079 -0.013 91 -0.013 0.143 92 0.000 0.092 93 -0.013 0.143 94 -0.079 -0.013 95 0.002 -0.132 96 0.046 0.046 97 -0.132 0.002 98 -0.013 -0.079 99 0.143 -0.013 100 0.092 0.000 101 0.143 -0.013 102 -0.013 -0.079 103 -0.132 0.002 104 0.046 0.046 105 0.002 -0.132 106 -0.079 -0.013 107 -0.013 0.143 108 0.000 0.092 109 -0.013 0.143 110 -0.079 -0.013 111 0.002 -0.132 112 0.046 0.046 113 -0.132 0.002 114 -0.013 -0.079 115 0.143 -0.013 116 0.092 0.000 117 0.143 -0.013 118 -0.013 -0.079 119 -0.132 0.002 120 0.046 0.046 121 0.002 -0.132 122 -0.079 -0.013 123 -0.013 0.143 124 0.000 0.092 125 -0.013 0.143 126 -0.079 -0.013 127 0.002 -0.132 128 0.046 0.046 129 -0.132 0.002 130 -0.013 -0.079 131 0.143 -0.013 132 0.092 0.000 133 0.143 -0.013 134 -0.013 -0.079 135 -0.132 0.002 136 0.046 0.046 137 0.002 -0.132 138 -0.079 -0.013 139 -0.013 0.143 140 0.000 0.092 141 -0.013 0.143 142 -0.079 -0.013 143 0.002 -0.132 144 0.046 0.046 145 -0.132 0.002 146 -0.013 -0.079 147 0.143 -0.013 148 0.092 0.000 149 0.143 -0.013 150 -0.013 -0.079 151 -0.132 0.002 152 0.046 0.046 153 0.002 -0.132 154 -0.079 -0.013 155 -0.013 0.143 156 0.000 0.092 157 -0.013 0.143 158 -0.079 -0.013 159 0.002 -0.132 160 0.023 0.023 000 000000 00000 000 000000 000000 000 00000 000000]; % Discard columns of indices table_I4(:,[1,4,7,10]) = []; % Convert to complex table_I4 = complex(table_I4(:,1:2:end), table_I4(:,2:2:end)); % Stack in order table_I4 = reshape(table_I4.', [], 1); % Discard zero-padding at the end table_I4 = table_I4(1:161); % Sanity check (up to 3 decimal places) table_I4_err = stf_161 - table_I4; table_I4_err = [real(table_I4_err); imag(table_I4_err)]; assert(all(abs(table_I4_err) < 0.001));
Again, please download the full code (View attachment 160187) and feel free to post any questions here.