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_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.
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
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
43 self.input = gr.add_const_cc(0)
45 SNR = 10.0**(snr/10.0)
46 rho = SNR / (SNR + 1.0)
47 symbol_length = fft_length + cp_length
51 # Energy Detection from ML Sync
53 self.connect(self, self.input)
56 self.delay = gr.delay(gr.sizeof_gr_complex, fft_length)
57 self.connect(self.input, self.delay)
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()
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)
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)
74 # Correlation from ML Sync
75 self.conjg = gr.conjugate_cc();
76 self.mixer = gr.multiply_cc();
78 movingsum2_taps = [1.0 for i in range(cp_length)]
79 self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
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)
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))
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()
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))
109 # use the sync loop values to set the sampler and the NCO
111 # self.angle = epsilon
113 self.connect(self.diff, self.pk_detect)
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))
122 self.connect(self.pk_detect, (self.sampler,1))
123 self.connect(self.pk_detect, (self.sample_and_hold,1))
125 self.connect(self.angle, (self.sample_and_hold,0))
126 self.connect(self.sample_and_hold, self.nco)
128 self.connect(self.sampler, self)
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"))
135 self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat"))
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"))