doc
@ -162,3 +162,5 @@ texinfo_documents = [
|
|||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||||
|
|
||||||
|
numfig = True
|
||||||
|
107
docs/source/detection.rst
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
Packet Detection
|
||||||
|
================
|
||||||
|
|
||||||
|
802.11 OFDM packets start with a short PLCP Preamble sequence to help the
|
||||||
|
receiver detect the beginning of the packet. The short preamble duration is
|
||||||
|
8 us. At 20 MSPS sampling rate, it contains 10 repeating sequence of 16 I/Q
|
||||||
|
samples, or 160 samples in total. The short preamble also helps the receiver
|
||||||
|
for coarse frequency offset correction , which will be discussed separately in
|
||||||
|
:ref:`freq_offset`.
|
||||||
|
|
||||||
|
|
||||||
|
Power Trigger
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- **Module**: ``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``,
|
||||||
|
``SR_SKIP_SAMPLE``.
|
||||||
|
|
||||||
|
The core idea of detecting the short preamble is to utilize its repeating nature
|
||||||
|
by calculating the auto correlation metric. But before that, we need to make sure
|
||||||
|
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
|
||||||
|
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
|
||||||
|
signals during the intimal hardware stabilization phase.
|
||||||
|
|
||||||
|
The logic of the ``power_trigger`` module is quite simple: after skipping
|
||||||
|
certain number of initial samples, it waits for significant power increase and
|
||||||
|
triggers the ``trigger`` signal upon detection. The ``trigger`` signal is
|
||||||
|
asserted until the power level is smaller than a threshold for certain number of
|
||||||
|
continuous samples.
|
||||||
|
|
||||||
|
Short Preamble Detection
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- **Module**: ``sync_short.v``
|
||||||
|
- **Input**: ``sample_in`` (16B I + 16B Q), ``sample_in_strobe`` (1B)
|
||||||
|
- **Output**: ``short_preamble_detected`` (1B)
|
||||||
|
- **Setting Registers**: ``SR_MIN_PLATEAU``
|
||||||
|
|
||||||
|
|
||||||
|
.. _fig_short_preamble:
|
||||||
|
.. figure:: /images/short_preamble.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
In-Phase of Short Preamble.
|
||||||
|
|
||||||
|
:numref:`fig_short_preamble` shows the in-phase of the beginning of a packet.
|
||||||
|
Some repeating patterns can clearly be seen. We can utilize this characteristic
|
||||||
|
and calculate the auto correlation metric of incoming signals to detect such
|
||||||
|
pattern:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
corr[i] = \frac{\left\lVert\sum_{i=0}^{N}{S[i]*\overline{S[i+16]}}\right\rVert}
|
||||||
|
{\sum_{i=0}^{N}{S[i]*\overline{S[i]}}}
|
||||||
|
|
||||||
|
where :math:`S[i]` is the :math:`\langle I,Q \rangle` sample expressed as a
|
||||||
|
complex number, and :math:`\overline{S[i]}` is its conjugate, :math:`N` is the
|
||||||
|
correlation window size. The correlation
|
||||||
|
reaches 1 if the incoming signal is repeating itself every 16 samples. If the
|
||||||
|
correlation stays high for certain number of continuous samples, then a short
|
||||||
|
preamble can be declared.
|
||||||
|
|
||||||
|
.. _fig_corr:
|
||||||
|
.. figure:: /images/corr.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Auto Correlation of the Short Preamble samples (N=48).
|
||||||
|
|
||||||
|
: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
|
||||||
|
that for the very first 20 samples or so, the correlation value is also very
|
||||||
|
high. This is because the silence also repeats itself (at arbitrary interval)!
|
||||||
|
That's why we first use the ``power_trigger`` module to detect actual packet
|
||||||
|
activity and only perform short preamble detection on non-silent samples.
|
||||||
|
|
||||||
|
A straight forward implementation would require
|
||||||
|
both multiplication and division. However, on FPGAs devision consumes a lot of
|
||||||
|
resources so we really want to avoid it. In current implementation, we use a
|
||||||
|
fixed threshold (0.75) for the correlation so that we can use bit-shift to
|
||||||
|
achieve the purpose. In particular, we calculate ``numerator>>1 + numerator>>2``
|
||||||
|
and compare that with the denominator. For the correlation window size, we set
|
||||||
|
:math:`N=16`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _fig_sync_short:
|
||||||
|
|
||||||
|
.. figure:: /images/sync_short.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
``sync_short`` Module Diagram
|
||||||
|
|
||||||
|
:numref:`fig_sync_short` shows the internal module diagram of the ``sync_short``
|
||||||
|
module. In addition to the number of consecutive samples with correlation
|
||||||
|
larger than 0.75, the ``sync_short`` module also checks if the incoming signal
|
||||||
|
has both positive (> 25%) and negative (> 25%) samples to further eliminate
|
||||||
|
false positives (e.g., when the incoming signals are constant non-zero values).
|
||||||
|
Again, the thresholds (25%) are chosen so that we can use only bit-shifts for
|
||||||
|
the calculation.
|
BIN
docs/source/files/vtc04_freq_offset.pdf
Normal file
@ -2,3 +2,72 @@
|
|||||||
|
|
||||||
Frequency Offset Correction
|
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).
|
||||||
|
|
||||||
|
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
|
||||||
|
symbol. Before we get into how exactly the correction is done. Let's see
|
||||||
|
visually how each correction step helps in the final constellation plane.
|
||||||
|
|
||||||
|
.. _fig_cons:
|
||||||
|
.. figure:: /images/cons.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Constellation Points Without Any Correction
|
||||||
|
|
||||||
|
.. figure:: /images/cons_w_coarse.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Constellation Points With Only Coarse Correction
|
||||||
|
|
||||||
|
.. figure:: /images/cons_w_coarse_fine.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Constellation Points With both Coarse and Fine Correction
|
||||||
|
|
||||||
|
.. _fig_cons_full:
|
||||||
|
.. figure:: /images/cons_w_coarse_fine_pilot.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Constellation Points With Coarse, Fine and Pilot Correction
|
||||||
|
|
||||||
|
:numref:`fig_cons` to :numref:`fig_cons_full` shows the constellation points of
|
||||||
|
a 64-QAM modulated 802.11a packet.
|
||||||
|
|
||||||
|
Coarse CFO Correction
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The coarse CFO can be estimated using the short preamble as follows:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\alpha_{ST} = \frac{1}{16}\angle(\sum_{i=0}^{N}\overline{S[i]}S[i+16])
|
||||||
|
|
||||||
|
where :math:`\angle(\cdot)` is the phase of complex number and :math:`N \le 144
|
||||||
|
(160 - 16)` is the subset of short preambles utilized. The intuition is that the
|
||||||
|
phase difference between S[i] and S[i+16] represents the accumulated CFO over 16
|
||||||
|
samples.
|
||||||
|
|
||||||
|
|
||||||
|
After getting :math:`\alpha_{ST}`, each following I/Q samples (starting from
|
||||||
|
long preamble) are corrected as:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
BIN
docs/source/images/complex_to_mag_wave.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
docs/source/images/cons.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/source/images/cons_w_coarse.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/source/images/cons_w_coarse_fine.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/source/images/cons_w_coarse_fine_pilot.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
docs/source/images/corr.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
docs/source/images/phase_wave1.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
docs/source/images/short_preamble.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
docs/source/images/sync_short.png
Normal file
After Width: | Height: | Size: 36 KiB |
@ -10,8 +10,10 @@ Welcome to OpenOFDM's documentation!
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
power
|
detection
|
||||||
freq_offset
|
freq_offset
|
||||||
|
setting
|
||||||
|
verilog
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
Packet Detection
|
|
||||||
================
|
|
||||||
|
|
||||||
802.11 OFDM packets start with a short PLCP Preamble sequence to help the
|
|
||||||
receiver detect the beginning of the packet. The short preamble duration is
|
|
||||||
8 us. At 20 MSPS sampling rate, it contains 10 repeating sequence of 16 I/Q
|
|
||||||
samples. The short preamble also helps the receiver for coarse frequency offset
|
|
||||||
correction , which will be discussed separately in :ref:`freq_offset`.
|
|
||||||
|
|
||||||
The core idea of detecting the short preamble is to utilize its repeating nature
|
|
||||||
by calculating the auto correlation metric. But before that, we need to make sure
|
|
||||||
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 very high but does not represent packet beginning.
|
|
||||||
|
|
||||||
Power Trigger
|
|
||||||
-------------
|
|
||||||
|
|
||||||
- **Module**: ``power_trigger.v``
|
|
||||||
- **Input**: ``sample_in`` (16B I + 16B Q), ``sample_in_strobe`` (1B)
|
|
||||||
- **Output**: ``trigger`` (1B)
|
|
||||||
|
|
||||||
The first module in the pipeline is the ``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
|
|
||||||
signals during the intimal hardware stabilization phase.
|
|
||||||
|
|
32
docs/source/setting.rst
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Setting Registers
|
||||||
|
=================
|
||||||
|
|
||||||
|
- **Module**: ``usrp/setting_reg.v``
|
||||||
|
- **Input**: ``set_stb``, ``set_addr`` and ``set_data``
|
||||||
|
- **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
|
||||||
|
common inputs for such purpose:
|
||||||
|
|
||||||
|
- ``set_stb (1)``: asserts high when the setting data is valid
|
||||||
|
- ``set_addr (8)``: register address (256 registers possible in total)
|
||||||
|
- ``set_data (32)``: the register value
|
||||||
|
|
||||||
|
|
||||||
|
Here is a list of setting registers in OpenOFDM.
|
||||||
|
|
||||||
|
.. table:: List of Setting Registers in OpenOFDM.
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+
|
||||||
|
| Name | Addr | Module | Bit Width | Default Value | Description |
|
||||||
|
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+
|
||||||
|
| SR_POWRE_THRES | 3 | power_trigger.v | 16 | 100 | Threshold for power trigger |
|
||||||
|
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+
|
||||||
|
| SR_POWER_WINDOW | 4 | power_trigger.v | 16 | 80 | Number of samples to wait before reset the trigger signal |
|
||||||
|
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+
|
||||||
|
| SR_SKIP_SAMPLE | 5 | power_trigger.v | 32 | 5000000 | Number of samples to skip initially |
|
||||||
|
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+
|
||||||
|
| SR_MIN_PLATEAU | 6 | sync_short.v | 32 | 100 | Minimum number of plateau samples to declare a short preamble |
|
||||||
|
+-----------------+------+-----------------+-----------+---------------+---------------------------------------------------------------+
|
75
docs/source/verilog.rst
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Magnitude Estimation
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
**Module**: ``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
|
||||||
|
multiplication, we use the `Magnitude Estimator Trick from DSP Guru
|
||||||
|
<https://dspguru.com/dsp/tricks/magnitude-estimator/>`_. In particular, the
|
||||||
|
magnitude of complex number :math:`\langle I, Q\rangle` is estimated as:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
M \approx \alpha*max(|I|, |Q|) + \beta*min(|I|, |Q|)
|
||||||
|
|
||||||
|
And we set :math:`\alpha = 1` and :math:`\beta = 0.25` so that only simple
|
||||||
|
bit-shift is needed.
|
||||||
|
|
||||||
|
.. _fig_complex_to_mag_wave:
|
||||||
|
.. figure:: /images/complex_to_mag_wave.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Waveform of ``complex_to_mag`` Module
|
||||||
|
|
||||||
|
:numref:`fig_complex_to_mag_wave` shows the waveform of the ``complex_to_mag``
|
||||||
|
module. In the first clock cycle, we calculate ``abs_i`` and ``abs_q``. In the
|
||||||
|
second cycle, ``max`` and ``min`` are determined. In the final cycle, the
|
||||||
|
magnitude is calculated.
|
||||||
|
|
||||||
|
|
||||||
|
Phase Estimation
|
||||||
|
----------------
|
||||||
|
|
||||||
|
**Module**:: ``phase.v``
|
||||||
|
|
||||||
|
When correcting the frequency offset, we need to estimate the phase of a complex
|
||||||
|
number, which can be calculated using the :math:`arctan` function.
|
||||||
|
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\angle(\langle I, Q\rangle) = arctan(\frac{Q}{I})
|
||||||
|
|
||||||
|
The overall steps are:
|
||||||
|
|
||||||
|
1. Project the complex number to the :math:`[0, \pi/4]` range.
|
||||||
|
#. Calculate :math:`arctan` (division required)
|
||||||
|
#. Looking up the quantized :math:`arctan` table
|
||||||
|
#. Project the phase back to the :math:`[-\pi, \pi)` range
|
||||||
|
|
||||||
|
Here we use both quantization and look up table techniques.
|
||||||
|
|
||||||
|
The first step can be achieved by this transformation:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\langle I, Q\rangle \rightarrow \langle max(|I|, |Q|), min(|I|, |Q|)\rangle
|
||||||
|
|
||||||
|
|
||||||
|
The *right* way to calculate :math:`arctan` is probably using the `CORDIC
|
||||||
|
<https://dspguru.com/dsp/faqs/cordic/>`_ algorithm. However, this function is
|
||||||
|
implemented using look up tables in OpenOFDM.
|
||||||
|
|
||||||
|
In the table, we use :math:`int(tan(\angle)*256)` as the key, which effective
|
||||||
|
map the :math:`[0.0, 1.0]` range of :math:`tan` function to the integer range of
|
||||||
|
:math:`[0, 256]`. In other words, we quantize the :math:`[0, \pi/4]` quadrant
|
||||||
|
into 256 slices.
|