doc
@ -162,3 +162,5 @@ texinfo_documents = [
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
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
|
||||
===========================
|
||||
|
||||
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
|
||||
:caption: Contents:
|
||||
|
||||
power
|
||||
detection
|
||||
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.
|