working on sync_long

This commit is contained in:
Jinghao Shi 2017-04-07 16:48:34 -04:00
parent df46bc5309
commit 652c8c1bb7
16 changed files with 303 additions and 27 deletions

View File

@ -158,9 +158,20 @@ texinfo_documents = [
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
# =============================================================
# Custom configurations
# =============================================================
#
# Enable figure numbering
numfig = True
# global macros
rst_prolog = """
.. |project| replace:: OpenOFDM
.. |us| replace:: :math:`\mu s`
"""
math_number_all = True

View File

@ -12,7 +12,7 @@ for coarse frequency offset correction , which will be discussed separately in
Power Trigger
-------------
- **Module**: ``power_trigger.v``
- **Module**: :file:`power_trigger.v`
- **Input**: ``sample_in`` (16B I + 16B Q), ``sample_in_strobe`` (1B)
- **Output**: ``trigger`` (1B)
- **Setting Registers**: ``SR_POWER_THRES``, ``SR_POWER_WINDOW``,
@ -24,7 +24,7 @@ we are trying to detect short preamble from "meaningful" signals. One example of
"un-meaningful" signal is constant power levels, whose auto correlation metric
is also very high (nearly 1) but obviously does not represent packet beginning.
The first module in the pipeline is the ``power_trigger.v``. It takes the I/Q
The first module in the pipeline is the :file:`power_trigger.v`. It takes the I/Q
samples as input and asserts the ``trigger`` signal during a potential packet
activity. Optionally, it can be configured to skip the first certain number of
samples before detecting a power trigger. This is useful to skip the spurious
@ -39,7 +39,7 @@ continuous samples.
Short Preamble Detection
------------------------
- **Module**: ``sync_short.v``
- **Module**: :file:`sync_short.v`
- **Input**: ``sample_in`` (16B I + 16B Q), ``sample_in_strobe`` (1B)
- **Output**: ``short_preamble_detected`` (1B)
- **Setting Registers**: ``SR_MIN_PLATEAU``
@ -74,6 +74,21 @@ preamble can be declared.
Auto Correlation of the Short Preamble samples (N=48).
To plot :numref:`fig_corr`, load the samples (see :ref:`sec_sample`), then:
.. code-block:: python
from matplotlib import pyplot as plt
fig, ax = plt.subplots(nrows=2, ncols=1, sharex=True)
ax[0].plot([s.real for s in samples[:500]], '-bo')
ax[1].plot([abs(sum([samples[i+j]*samples[i+j+16].conjugate()
for j in range(0, 48)]))/
sum([abs(samples[i+j])**2 for j in range(0, 48)])
for i in range(0, 500)], '-ro')
plt.show()
:numref:`fig_corr` shows the auto correlation value of the samples in
:numref:`fig_short_preamble`. We can see that the correlation value is almost 1
during the short preamble period, but drops quickly after that. We can also see

Binary file not shown.

128
docs/source/files/lts.txt Normal file
View File

@ -0,0 +1,128 @@
-1.559999999999999998e-01
0.000000000000000000e+00
1.200000000000000025e-02
-9.800000000000000377e-02
9.199999999999999845e-02
-1.059999999999999970e-01
-9.199999999999999845e-02
-1.150000000000000050e-01
-3.000000000000000062e-03
-5.399999999999999939e-02
7.499999999999999722e-02
7.399999999999999634e-02
-1.270000000000000018e-01
2.100000000000000130e-02
-1.219999999999999973e-01
1.700000000000000122e-02
-3.500000000000000333e-02
1.509999999999999953e-01
-5.600000000000000117e-02
2.199999999999999872e-02
-5.999999999999999778e-02
-8.100000000000000255e-02
7.000000000000000666e-02
-1.400000000000000029e-02
8.200000000000000344e-02
-9.199999999999999845e-02
-1.310000000000000053e-01
-6.500000000000000222e-02
-5.700000000000000205e-02
-3.899999999999999994e-02
3.699999999999999817e-02
-9.800000000000000377e-02
6.199999999999999956e-02
6.199999999999999956e-02
1.189999999999999947e-01
4.000000000000000083e-03
-2.199999999999999872e-02
-1.610000000000000042e-01
5.899999999999999689e-02
1.499999999999999944e-02
2.400000000000000050e-02
5.899999999999999689e-02
-1.370000000000000107e-01
4.700000000000000011e-02
1.000000000000000021e-03
1.150000000000000050e-01
5.299999999999999850e-02
-4.000000000000000083e-03
9.800000000000000377e-02
2.599999999999999881e-02
-3.799999999999999906e-02
1.059999999999999970e-01
-1.150000000000000050e-01
5.500000000000000028e-02
5.999999999999999778e-02
8.799999999999999489e-02
2.100000000000000130e-02
-2.800000000000000058e-02
9.700000000000000289e-02
-8.300000000000000433e-02
4.000000000000000083e-02
1.110000000000000014e-01
-5.000000000000000104e-03
1.199999999999999956e-01
1.559999999999999998e-01
0.000000000000000000e+00
-5.000000000000000104e-03
-1.199999999999999956e-01
4.000000000000000083e-02
-1.110000000000000014e-01
9.700000000000000289e-02
8.300000000000000433e-02
2.100000000000000130e-02
2.800000000000000058e-02
5.999999999999999778e-02
-8.799999999999999489e-02
-1.150000000000000050e-01
-5.500000000000000028e-02
-3.799999999999999906e-02
-1.059999999999999970e-01
9.800000000000000377e-02
-2.599999999999999881e-02
5.299999999999999850e-02
4.000000000000000083e-03
1.000000000000000021e-03
-1.150000000000000050e-01
-1.370000000000000107e-01
-4.700000000000000011e-02
2.400000000000000050e-02
-5.899999999999999689e-02
5.899999999999999689e-02
-1.499999999999999944e-02
-2.199999999999999872e-02
1.610000000000000042e-01
1.189999999999999947e-01
-4.000000000000000083e-03
6.199999999999999956e-02
-6.199999999999999956e-02
3.699999999999999817e-02
9.800000000000000377e-02
-5.700000000000000205e-02
3.899999999999999994e-02
-1.310000000000000053e-01
6.500000000000000222e-02
8.200000000000000344e-02
9.199999999999999845e-02
7.000000000000000666e-02
1.400000000000000029e-02
-5.999999999999999778e-02
8.100000000000000255e-02
-5.600000000000000117e-02
-2.199999999999999872e-02
-3.500000000000000333e-02
-1.509999999999999953e-01
-1.219999999999999973e-01
-1.700000000000000122e-02
-1.270000000000000018e-01
-2.100000000000000130e-02
7.499999999999999722e-02
-7.399999999999999634e-02
-3.000000000000000062e-03
5.399999999999999939e-02
-9.199999999999999845e-02
1.150000000000000050e-01
9.199999999999999845e-02
1.059999999999999970e-01
1.200000000000000025e-02
9.800000000000000377e-02

Binary file not shown.

View File

@ -3,14 +3,14 @@
Frequency Offset Correction
===========================
This paper [1]_ explains why frequency offset occurs and how to correct it. In a
nutshell, there are two types of frequency offsets. The first is called
**Carrier Frequency Offset (CFO)** and is caused by the difference between the
transmitter and receiver's Local Oscillator (LO). This symptom of this offset is
a phase rotation of incoming I/Q samples (time domain). The second is **Sampling
Frequency Offset (SFO)** and is caused by the sampling effect. The symptom of
this offset is a phase rotation of constellation points after FFT (frequency
domain).
:download:`This paper </files/vtc04_freq_offset.pdf>` [1]_ explains why
frequency offset occurs and how to correct it. In a nutshell, there are two
types of frequency offsets. The first is called **Carrier Frequency Offset
(CFO)** and is caused by the difference between the transmitter and receiver's
Local Oscillator (LO). This symptom of this offset is a phase rotation of
incoming I/Q samples (time domain). The second is **Sampling Frequency Offset
(SFO)** and is caused by the sampling effect. The symptom of this offset is a
phase rotation of constellation points after FFT (frequency domain).
The CFO can be corrected with the help of short preamble (Coarse) long preamble
(Fine). And the SFO can be corrected using the pilot sub-carriers in each OFDM
@ -64,10 +64,10 @@ long preamble) are corrected as:
S'[m] = S[m]e^{-jm\alpha_{ST}}, m = 0, 1, 2, \ldots
In OpenOFDM, the coarse CFO is calculated in the ``sync_short`` module, and we
In |project|, the coarse CFO is calculated in the ``sync_short`` module, and we
set :math:`N=64`. The ``prod_avg`` in :numref:`fig_sync_short` is fed into a
``moving_avg`` module with window size set to 64.
.. [1] Sourour, Essam, Hussein El-Ghoroury, and Dale McNeill. "Frequency Offset Estimation and Correction in the IEEE 802.11 a WLAN." Vehicular Technology Conference, 2004. VTC2004-Fall. 2004 IEEE 60th. Vol. 7. IEEE, 2004.
.. [1] Sourour, Essam, Hussein El-Ghoroury, and Dale McNeill. "Frequency Offset Estimation and Correction in the IEEE 802.11 a WLAN." Vehicular Technology Conference, 2004. VTC2004-Fall. 2004 IEEE 60th. Vol. 7. IEEE, 2004.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 72 KiB

BIN
docs/source/images/lts.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -3,15 +3,17 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to OpenOFDM's documentation!
====================================
Welcome to |project|'s documentation!
=====================================
.. toctree::
:maxdepth: 2
:caption: Contents:
sample
detection
freq_offset
sync_long
setting
verilog

19
docs/source/sample.rst Normal file
View File

@ -0,0 +1,19 @@
.. _sec_sample:
Sample File
===========
Throughout this documentation we will be using a sample file that contains the
I/Q samples of a 802.11a packet at 24 Mbps (16-QAM). It'll be helpful to use a
interactive iPython session and exercise various steps discussed in the
document.
Download the sample file from :download:`here </files/samples.dat>`, the data
can be loaded as follows:
.. code-block:: python
import scipy
wave = scipy.fromfile('samples.dat', dtype=scipy.int16)
samples = [complex(i, q) for i, q in zip(wave[::2], wave[1::2])]

View File

@ -6,7 +6,7 @@ Setting Registers
- **Output**: ``out``, ``changed``
To enable dynamic configuration of decoding parameters at runtime, the USRP N210
provides the setting register mechanism. Most modules in OpenOFDM have three
provides the setting register mechanism. Most modules in |project| have three
common inputs for such purpose:
- ``set_stb (1)``: asserts high when the setting data is valid
@ -14,9 +14,9 @@ common inputs for such purpose:
- ``set_data (32)``: the register value
Here is a list of setting registers in OpenOFDM.
Here is a list of setting registers in |project|.
.. table:: List of Setting Registers in OpenOFDM.
.. table:: List of Setting Registers in |project|.
:align: center
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+

101
docs/source/sync_long.rst Normal file
View File

@ -0,0 +1,101 @@
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 |us| 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.
.. _fig_training:
.. figure:: /images/training.png
:align: center
802.11 OFDM Packet Structure (Fig 18-4 in 802.11-2012 Std)
As shown in :numref:`fig_training`, the long preamble duration is 8 |us| (160
samples), and contains two identical long training sequence (LTS), 64 samples each.
The LTS is known and we can use `matched filter
<https://en.wikipedia.org/wiki/Matched_filter>`_ to find it.
The match *score* at sample :math:`i` can be calculated as follows.
.. math::
:label: eq_matched
Y[i] = \sum_{k=0}^{63}(S[i+k]\overline{H[63-k]})
where :math:`H` is the 64 sample known LTS in time domain, and can be found in
Table L-6 in :download:`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 :download:`here
</files/lts.txt>`, and can be read like this:
.. code-block:: python
>>> import numpy as np
>>> lts = np.loadtxt('lts.txt').view(complex)
.. _fig_lts:
.. figure:: /images/lts.png
:align: center
Long Preamble and Matched Filter Result
To plot :numref:`fig_lts`, load the data file (see :ref:`sec_sample`), then:
.. code-block:: python
# 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()
:numref:`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 :math:`N`, then
the 160 sample long preamble starts at sample :math:`N-33`.
This all seems nice and dandy, but as it comes to Verilog implementation, we
have to make a few compromises.
First, from :eq:`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.
.. _fig_match_size:
.. figure:: /images/match_size.png
:align: center
Matched Filter with Various Size (8, 16, 32, 64)
:numref:`fig_match_size` can be plotted as:
.. code-block:: python
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()
:numref:`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, |project|
use matched filter of size 16 for symbol alignment. And the first sample of the
long preamble starts at :math:`N_{16}-57`, where :math:`N_{16}` is the index of the
first spike when the filter size is 16 (:math:`N_{32}-49` when filter size is
32).

View File

@ -3,13 +3,13 @@ Verilog Hacks
Because of the limited capability of FPGA computation, compromises often need to
made in the actual Verilog implementation. The most used techniques include
quantization and look up table. In OpenOFDM, these approximations are used.
quantization and look up table. In |project|, these approximations are used.
Magnitude Estimation
--------------------
**Module**: ``complex_to_mag.v``
**Module**: :file:`complex_to_mag.v`
In the ``sync_short`` module, we need to calculate the magnitude of the
``prod_avg``, whose real and imagine part are both 32-bits. To avoid 32-bit
@ -39,11 +39,11 @@ magnitude is calculated.
Phase Estimation
----------------
**Module**:: ``phase.v``
**Module**:: :file:`phase.v`
When correcting the frequency offset, we need to estimate the phase of a complex
number. The *right* way of doing this is probably using the `CORDIC
<https://dspguru.com/dsp/faqs/cordic/>`_ algorithm. In OpenOFDM, we use look up
<https://dspguru.com/dsp/faqs/cordic/>`_ algorithm. In |project|, we use look up
table.
More specifically, we calculate the phase using the :math:`arctan` function.
@ -91,9 +91,9 @@ This :math:`arctan` look up table is generated using the
Note that we also scale up the :math:`arctan` values to distinguish adjacent
values. This also systematically scale up :math:`\pi` in OpenOFDM. In fact,
values. This also systematically scale up :math:`\pi` in |project|. In fact,
:math:`\pi` is defined as :math:`1608=int(\pi*512)` in
``verilog/common_params.v``.
:file:`verilog/common_params.v`.
The generated lookup table is stored in the ``verilog/atan_lut.coe``
file (see `COE File Syntax
@ -101,4 +101,4 @@ file (see `COE File Syntax
Refer to `this guide
<https://www.xilinx.com/itp/xilinx10/isehelp/cgn_p_memed_single_block.htm>`_ on
how to create a look up table in Xilinx ISE. The generated module is stored in
``verilog/coregen/atan_lut.v``.
:file:`verilog/coregen/atan_lut.v`.