working on sync_long
@ -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
|
||||
|
@ -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
|
||||
|
BIN
docs/source/files/802.11-2012.pdf
Normal file
128
docs/source/files/lts.txt
Normal 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
|
BIN
docs/source/files/samples.dat
Normal 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.
|
||||
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 72 KiB |
BIN
docs/source/images/lts.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
docs/source/images/match_size.png
Normal file
After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 80 KiB |
BIN
docs/source/images/training.png
Normal file
After Width: | Height: | Size: 42 KiB |
@ -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
@ -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])]
|
@ -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
@ -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).
|
@ -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`.
|
||||
|