Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / python / gnuradio / blks2impl / ofdm_sync_pnac.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2007 Free Software Foundation, Inc.
4
5 # This file is part of GNU Radio
6
7 # GNU Radio is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3, or (at your option)
10 # any later version.
11
12 # GNU Radio is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with GNU Radio; see the file COPYING.  If not, write to
19 # the Free Software Foundation, Inc., 51 Franklin Street,
20 # Boston, MA 02110-1301, USA.
21
22
23 import math
24 from numpy import fft
25 from gnuradio import gr
26
27 class ofdm_sync_pnac(gr.hier_block2):
28     def __init__(self, fft_length, cp_length, kstime, logging=False):
29         """
30         OFDM synchronization using PN Correlation and initial cross-correlation:
31         F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using
32         PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207.
33
34         This implementation is meant to be a more robust version of the Schmidl and Cox receiver design.
35         By correlating against the preamble and using that as the input to the time-delayed correlation,
36         this circuit produces a very clean timing signal at the end of the preamble. The timing is 
37         more accurate and does not have the problem associated with determining the timing from the
38         plateau structure in the Schmidl and Cox.
39
40         This implementation appears to require that the signal is received with a normalized power or signal
41         scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and
42         the peak detection. A better peak detection block might fix this.
43
44         Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails
45         when an integer offset is introduced. Another thing to look at.
46         """
47
48         gr.hier_block2.__init__(self, "ofdm_sync_pnac",
49                                 gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
50                                 gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature
51
52             
53         self.input = gr.add_const_cc(0)
54
55         symbol_length = fft_length + cp_length
56
57         # PN Sync with cross-correlation input
58
59         # cross-correlate with the known symbol
60         kstime = [k.conjugate() for k in kstime[0:fft_length//2]]
61         kstime.reverse()
62         self.crosscorr_filter = gr.fir_filter_ccc(1, kstime)
63         
64         # Create a delay line
65         self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2)
66
67         # Correlation from ML Sync
68         self.conjg = gr.conjugate_cc();
69         self.corr = gr.multiply_cc();
70
71         # Create a moving sum filter for the input
72         self.mag = gr.complex_to_mag_squared()
73         movingsum_taps = (fft_length//1)*[1.0,]
74         self.power = gr.fir_filter_fff(1,movingsum_taps)
75      
76         # Get magnitude (peaks) and angle (phase/freq error)
77         self.c2mag = gr.complex_to_mag_squared()
78         self.angle = gr.complex_to_arg()
79         self.compare = gr.sub_ff()
80         
81         self.sample_and_hold = gr.sample_and_hold_ff()
82
83         #ML measurements input to sampler block and detect
84         self.threshold = gr.threshold_ff(0,0,0)      # threshold detection might need to be tweaked
85         self.peaks = gr.float_to_char()
86
87         self.connect(self, self.input)
88
89         # Cross-correlate input signal with known preamble
90         self.connect(self.input, self.crosscorr_filter)
91
92         # use the output of the cross-correlation as input time-shifted correlation
93         self.connect(self.crosscorr_filter, self.delay)
94         self.connect(self.crosscorr_filter, (self.corr,0))
95         self.connect(self.delay, self.conjg)
96         self.connect(self.conjg, (self.corr,1))
97         self.connect(self.corr, self.c2mag)
98         self.connect(self.corr, self.angle)
99         self.connect(self.angle, (self.sample_and_hold,0))
100         
101         # Get the power of the input signal to compare against the correlation
102         self.connect(self.crosscorr_filter, self.mag, self.power)
103
104         # Compare the power to the correlator output to determine timing peak
105         # When the peak occurs, it peaks above zero, so the thresholder detects this
106         self.connect(self.c2mag, (self.compare,0))
107         self.connect(self.power, (self.compare,1))
108         self.connect(self.compare, self.threshold)
109         self.connect(self.threshold, self.peaks, (self.sample_and_hold,1))
110
111         # Set output signals
112         #    Output 0: fine frequency correction value
113         #    Output 1: timing signal
114         self.connect(self.sample_and_hold, (self,0))
115         self.connect(self.peaks, (self,1))
116
117         if logging:
118             self.connect(self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat"))
119             self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat"))
120             self.connect(self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat"))
121             self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat"))
122             self.connect(self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat"))
123             self.connect(self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat"))
124             self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat"))
125             self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat"))