Merging ofdm2 branch -r7047:7321 into trunk. This updates the OFDM code to hier_block...
[debian/gnuradio] / gnuradio-core / src / python / gnuradio / blks2impl / ofdm_sync_ml.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 gnuradio import gr
25
26 class ofdm_sync_ml(gr.hier_block2):
27     def __init__(self, fft_length, cp_length, snr, logging):
28         ''' Maximum Likelihood OFDM synchronizer:
29         J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation
30         of Time and Frequency Offset in OFDM Systems," IEEE Trans.
31         Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997.
32         '''
33
34         # FIXME: change the output signature
35         # should be the output of the divider (the normalized peaks) and
36         # the angle value out of the sample and hold block
37         # move sampler out of this block
38
39         gr.hier_block2.__init__(self, "ofdm_sync_ml",
40                                 gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
41                                 gr.io_signature(1, 1, gr.sizeof_gr_complex*fft_length)) # Output signature
42
43         self.input = gr.add_const_cc(0)
44
45         SNR = 10.0**(snr/10.0)
46         rho = SNR / (SNR + 1.0)
47         symbol_length = fft_length + cp_length
48
49         # ML Sync
50
51         # Energy Detection from ML Sync
52
53         self.connect(self, self.input)
54
55         # Create a delay line
56         self.delay = gr.delay(gr.sizeof_gr_complex, fft_length)
57         self.connect(self.input, self.delay)
58
59         # magnitude squared blocks
60         self.magsqrd1 = gr.complex_to_mag_squared()
61         self.magsqrd2 = gr.complex_to_mag_squared()
62         self.adder = gr.add_ff()
63
64         moving_sum_taps = [rho/2 for i in range(cp_length)]
65         self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps)
66         
67         self.connect(self.input,self.magsqrd1)
68         self.connect(self.delay,self.magsqrd2)
69         self.connect(self.magsqrd1,(self.adder,0))
70         self.connect(self.magsqrd2,(self.adder,1))
71         self.connect(self.adder,self.moving_sum_filter)
72         
73
74         # Correlation from ML Sync
75         self.conjg = gr.conjugate_cc();
76         self.mixer = gr.multiply_cc();
77
78         movingsum2_taps = [1.0 for i in range(cp_length)]
79         self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
80         
81         # Correlator data handler
82         self.c2mag = gr.complex_to_mag()
83         self.angle = gr.complex_to_arg()
84         self.connect(self.input,(self.mixer,1))
85         self.connect(self.delay,self.conjg,(self.mixer,0))
86         self.connect(self.mixer,self.movingsum2,self.c2mag)
87         self.connect(self.movingsum2,self.angle)
88
89         # ML Sync output arg, need to find maximum point of this
90         self.diff = gr.sub_ff()
91         self.connect(self.c2mag,(self.diff,0))
92         self.connect(self.moving_sum_filter,(self.diff,1))
93
94         #ML measurements input to sampler block and detect
95         nco_sensitivity = -1.0/fft_length
96         self.f2c = gr.float_to_complex()
97         self.sampler = gr.ofdm_sampler(fft_length,symbol_length)
98         self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005)
99         #self.pk_detect = gr.peak_detector2_fb()
100         self.sample_and_hold = gr.sample_and_hold_ff()
101         self.nco = gr.frequency_modulator_fc(nco_sensitivity)
102         self.sigmix = gr.multiply_cc()
103
104         # Mix the signal with an NCO controlled by the sync loop
105         self.connect(self.input, (self.sigmix,0))
106         self.connect(self.nco, (self.sigmix,1))
107         self.connect(self.sigmix, (self.sampler,0))
108
109         # use the sync loop values to set the sampler and the NCO
110         #     self.diff = theta
111         #     self.angle = epsilon
112                           
113         self.connect(self.diff, self.pk_detect)
114
115         use_dpll = 1
116         if use_dpll:
117             self.dpll = gr.dpll_bb(float(symbol_length),0.01)
118             self.connect(self.pk_detect, self.dpll)
119             self.connect(self.dpll, (self.sampler,1))
120             self.connect(self.dpll, (self.sample_and_hold,1))
121         else:
122             self.connect(self.pk_detect, (self.sampler,1))
123             self.connect(self.pk_detect, (self.sample_and_hold,1))
124             
125         self.connect(self.angle, (self.sample_and_hold,0))
126         self.connect(self.sample_and_hold, self.nco)
127
128         self.connect(self.sampler, self)
129
130         if logging:
131             self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat"))
132             self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat"))
133             self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat"))
134             if use_dpll:
135                 self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat"))
136
137             self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-sigmix_c.dat"))
138             self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_ml-sampler_c.dat"))
139             self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat"))
140             self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-nco_c.dat"))
141             self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
142