diff --git a/docs/source/verilog.rst b/docs/source/verilog.rst index 759488d..5a32d78 100644 --- a/docs/source/verilog.rst +++ b/docs/source/verilog.rst @@ -42,34 +42,63 @@ 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. +number. The *right* way of doing this is probably using the `CORDIC +`_ algorithm. In OpenOFDM, we use look up +table. + +More specifically, we calculate the phase using the :math:`arctan` function. .. math:: - \angle(\langle I, Q\rangle) = arctan(\frac{Q}{I}) + \theta = \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. +1. Project the complex number to the :math:`[0, \pi/4]` range, so that the + :math:`tan(\theta)` range is :math:`[0, 1]`. #. 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: +Step 1 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 -`_ algorithm. However, this function is -implemented using look up tables in OpenOFDM. +In the lookup table used in step 3, we use :math:`int(tan(\theta)*256)` as the +key, which effectively maps 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. -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. +This :math:`arctan` look up table is generated using the +``scripts/gen_atan_lut.py`` script. The core logic is as follows: + +.. code-block:: python + :linenos: + + SIZE = 2**8 + SCALE = SIZE*2 + data = [] + for i in range(SIZE): + key = float(i)/SIZE + val = int(round(math.atan(key)*SCALE)) + data.append(val) + + +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, +:math:`\pi` is defined as :math:`1608=int(\pi*512)` in +``verilog/common_params.v``. + +The generated lookup table is stored in the ``verilog/atan_lut.coe`` +file (see `COE File Syntax +`_). +Refer to `this guide +`_ on +how to create a look up table in Xilinx ISE. The generated module is stored in +``verilog/coregen/atan_lut.v``. diff --git a/scripts/decode.py b/scripts/decode.py index 28a98dc..4a1860c 100644 --- a/scripts/decode.py +++ b/scripts/decode.py @@ -225,6 +225,7 @@ class ChannelEstimator(object): prod_sum += prod beta = cmath.phase(prod_sum) print "[PILOT OFFSET] %f (%d)" % (beta, int(beta*PHASE_SCALE)) + # beta = 0 carriers = [] for c in self.subcarriers: if c in PILOT_SUBCARRIES: @@ -269,8 +270,8 @@ class ChannelEstimator(object): coarse_offset = cmath.phase(sum([sts[i]*sts[i+16].conjugate() for i in range(len(sts)-16)]))/16 - coarse_offset = int(coarse_offset*256)/256.0 print '[COARSE OFFSET] %f (%d)' % (coarse_offset, int(coarse_offset*PHASE_SCALE)) + # coarse_offset = 0 # coarse correction lts = [c*cmath.exp(complex(0, n*coarse_offset)) @@ -279,7 +280,7 @@ class ChannelEstimator(object): fine_offset = cmath.phase(sum([lts[i]*lts[i+64].conjugate() for i in range(len(lts)-64)]))/64 print '[FINE OFFSET] %f (%d)' % (fine_offset, int(fine_offset*PHASE_SCALE)) - fine_offset = 0 + # fine_offset = 0 self.lts_samples = [c*cmath.exp(complex(0, n*fine_offset)) for n, c in enumerate(lts)] @@ -430,6 +431,7 @@ class Decoder(object): def decode_next(self, *args, **kwargs): trigger = False samples = [] + glbl_index = 0 while True: chunk = array.array('h', self.fh.read(self.window)) chunk = [complex(i, q) for i, q in zip(chunk[::2], chunk[1::2])] @@ -437,6 +439,7 @@ class Decoder(object): trigger = True samples = [] print "Power trigger at %d" % (self.count) + glbl_index = self.count self.count += self.window @@ -448,6 +451,8 @@ class Decoder(object): if start is None: trigger = False else: + print "Decoding packet starting from sample %d" %\ + (glbl_index + start) return self.decode(samples[start:], *args, **kwargs) def find_pkt(self, samples):