Merging OFDM features branch r5661:5759 into trunk. OFDM works over the air with...
[debian/gnuradio] / gnuradio-core / src / python / gnuradio / blksimpl / 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 2, 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_block):
27     def __init__(self, fg, 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         self.fg = fg
35
36         # FIXME: when converting to hier_block2's, the output signature
37         # should be the output of the divider (the normalized peaks) and
38         # the angle value out of the sample and hold block
39
40         self.input = gr.add_const_cc(0)
41
42         SNR = 10.0**(snr/10.0)
43         rho = SNR / (SNR + 1.0)
44         symbol_length = fft_length + cp_length
45
46         # ML Sync
47
48         # Energy Detection from ML Sync
49
50         # Create a delay line
51         self.delay = gr.delay(gr.sizeof_gr_complex, fft_length)
52         self.fg.connect(self.input, self.delay)
53
54         # magnitude squared blocks
55         self.magsqrd1 = gr.complex_to_mag_squared()
56         self.magsqrd2 = gr.complex_to_mag_squared()
57         self.adder = gr.add_ff()
58
59         moving_sum_taps = [rho/2 for i in range(cp_length)]
60         self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps)
61         
62         self.fg.connect(self.input,self.magsqrd1)
63         self.fg.connect(self.delay,self.magsqrd2)
64         self.fg.connect(self.magsqrd1,(self.adder,0))
65         self.fg.connect(self.magsqrd2,(self.adder,1))
66         self.fg.connect(self.adder,self.moving_sum_filter)
67         
68
69         # Correlation from ML Sync
70         self.conjg = gr.conjugate_cc();
71         self.mixer = gr.multiply_cc();
72
73         movingsum2_taps = [1.0 for i in range(cp_length)]
74         self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
75         
76         # Correlator data handler
77         self.c2mag = gr.complex_to_mag()
78         self.angle = gr.complex_to_arg()
79         self.fg.connect(self.input,(self.mixer,1))
80         self.fg.connect(self.delay,self.conjg,(self.mixer,0))
81         self.fg.connect(self.mixer,self.movingsum2,self.c2mag)
82         self.fg.connect(self.movingsum2,self.angle)
83
84         # ML Sync output arg, need to find maximum point of this
85         self.diff = gr.sub_ff()
86         self.fg.connect(self.c2mag,(self.diff,0))
87         self.fg.connect(self.moving_sum_filter,(self.diff,1))
88
89         #ML measurements input to sampler block and detect
90         nco_sensitivity = -1.0/fft_length
91         self.f2c = gr.float_to_complex()
92         self.sampler = gr.ofdm_sampler(fft_length,symbol_length)
93         self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005)
94         self.sample_and_hold = gr.sample_and_hold_ff()
95         self.nco = gr.frequency_modulator_fc(nco_sensitivity)
96         self.sigmix = gr.multiply_cc()
97
98         # Mix the signal with an NCO controlled by the sync loop
99         self.fg.connect(self.input, (self.sigmix,0))
100         self.fg.connect(self.nco, (self.sigmix,1))
101         self.fg.connect(self.sigmix, (self.sampler,0))
102
103         # use the sync loop values to set the sampler and the NCO
104         #     self.diff = theta
105         #     self.angle = epsilon
106                           
107         self.fg.connect(self.diff, self.pk_detect)
108
109         use_dpll = 1
110         if use_dpll:
111             self.dpll = gr.dpll_bb(float(symbol_length),0.01)
112             self.fg.connect(self.pk_detect, self.dpll)
113             self.fg.connect(self.dpll, (self.sampler,1))
114             self.fg.connect(self.dpll, (self.sample_and_hold,1))
115         else:
116             self.fg.connect(self.pk_detect, (self.sampler,1))
117             self.fg.connect(self.pk_detect, (self.sample_and_hold,1))
118             
119         self.fg.connect(self.angle, (self.sample_and_hold,0))
120         self.fg.connect(self.sample_and_hold, self.nco)
121
122         if logging:
123             self.fg.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat"))
124             self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat"))
125             self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat"))
126             if use_dpll:
127                 self.fg.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat"))
128
129             self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-sigmix_c.dat"))
130             self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_ml-sampler_c.dat"))
131             self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat"))
132             self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-nco_c.dat"))
133             self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
134
135         gr.hier_block.__init__(self, fg, self.input, self.sampler)