3 # Copyright 2007 Free Software Foundation, Inc.
5 # This file is part of GNU Radio
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)
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.
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.
24 from gnuradio import gr
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.
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
40 self.input = gr.add_const_cc(0)
42 SNR = 10.0**(snr/10.0)
43 rho = SNR / (SNR + 1.0)
44 symbol_length = fft_length + cp_length
48 # Energy Detection from ML Sync
51 self.delay = gr.delay(gr.sizeof_gr_complex, fft_length)
52 self.fg.connect(self.input, self.delay)
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()
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)
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)
69 # Correlation from ML Sync
70 self.conjg = gr.conjugate_cc();
71 self.mixer = gr.multiply_cc();
73 movingsum2_taps = [1.0 for i in range(cp_length)]
74 self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
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)
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))
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()
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))
103 # use the sync loop values to set the sampler and the NCO
105 # self.angle = epsilon
107 self.fg.connect(self.diff, self.pk_detect)
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))
116 self.fg.connect(self.pk_detect, (self.sampler,1))
117 self.fg.connect(self.pk_detect, (self.sample_and_hold,1))
119 self.fg.connect(self.angle, (self.sample_and_hold,0))
120 self.fg.connect(self.sample_and_hold, self.nco)
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"))
127 self.fg.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat"))
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"))
135 gr.hier_block.__init__(self, fg, self.input, self.sampler)