openofdm/docs/source/sync_long.rst
Jinghao Shi 297162af13 working
2017-04-07 16:51:06 -04:00

3.8 KiB
Raw Blame History

Symbol Alignment

After detecting the packet, the next step is to determine precisely where each OFDM symbol starts. In 802.11, each OFDM symbol is 4 long. At 20 MSPS sampling rate, this means each OFDM symbol contains 80 samples. The task is to group the incoming streaming of samples into 80-sample OFDM symbols. This can be achieved using the long preamble following the short preamble.

802.11 OFDM Packet Structure (Fig 18-4 in 802.11-2012 Std)

As shown in fig_training, the long preamble duration is 8 (160 samples), and contains two identical long training sequence (LTS), 64 samples each. The LTS is known and we can use matched filter to find it.

The match score at sample i can be calculated as follows.


$$Y[i] = \sum_{k=0}^{63}(S[i+k]\overline{H[63-k]})$$

where H is the 64 sample known LTS in time domain, and can be found in Table L-6 in 802.11-2012 std </files/802.11-2012.pdf> (index 64 to 127). A numpy readable file of the LTS (64 samples) can be found here </files/lts.txt>, and can be read like this:

>>> import numpy as np
>>> lts = np.loadtxt('lts.txt').view(complex)





Long Preamble and Matched Filter Result

To plot fig_lts, load the data file (see sec_sample), then:

# in scripts/decode.py
import decode
import numpy as np
from matplotlib import pyplot as plt

fig, ax = plt.subplots(nrows=2, ncols=1, sharex=True)
ax[0].plot([c.real for c in samples][:500])
# lts is from the above code snippet
ax[1].plot([abs(c) for c in np.convolve(samples, lts, mode='same')][:500], '-ro')
plt.show()

fig_lts shows the long preamble samples and also the result of matched filter. We can clearly see two spikes corresponding the two LTS in long preamble. And the spike width is only 1 sample which shows exactly the beginning of each sequence. Suppose the sample index if the first spike is N, then the 160 sample long preamble starts at sample N33.

This all seems nice and dandy, but as it comes to Verilog implementation, we have to make a few compromises.

First, from eq_matched we can see for each sample, we need to perform 64 complex number multiplications, which would consume a lot FPGA resources. Therefore, we need to reduce the matched filter size. The idea is to only use a portion instead of all the LTS samples.

Matched Filter with Various Size (8, 16, 32, 64)

fig_match_size can be plotted as:

lp = decode.LONG_PREAMBLE

fig, ax = plt.subplots(nrows=5, ncols=1, sharex=True)
ax[0].plot([c.real for c in lp])
ax[1].plot([abs(c) for c in np.convolve(lp, lts[:8], mode='same')], '-ro')
ax[2].plot([abs(c) for c in np.convolve(lp, lts[:16], mode='same')], '-ro')
ax[3].plot([abs(c) for c in np.convolve(lp, lts[:32], mode='same')], '-ro');
ax[4].plot([abs(c) for c in np.convolve(lp, lts, mode='same')], '-ro')
plt.show()

fig_match_size shows the long preamble (160 samples) as well as matched filter with different size. It can be seen that using the first 16 samples of LTS is good enough to exhibit two narrow spikes. Therefore, use matched filter of size 16 for symbol alignment. And the first sample of the long preamble starts at N1657, where N16 is the index of the first spike when the filter size is 16 (for completeness, it is N3249 when filter size is 32).