3 # Copyright 2004,2005,2006 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 2, 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
25 from gnuradio import audio
26 from gnuradio.eng_option import eng_option
27 from optparse import OptionParser
29 class ofdm_receiver(gr.hier_block):
30 def __init__(self, fg, fft_length, symbol_length, snr):
31 self.input = gr.add_const_cc(0) # Kluge that goes away with hier_block2
35 cpsize = symbol_length - fft_length;
37 SNR = 10.0**(snr/10.0)
38 rho = SNR / (SNR + 1.0)
42 # Energy Detection from ML Sync
45 delayline = [0.0 for i in range(fft_length+1)]
46 delayline[fft_length] = 1.0
47 self.delay = gr.fir_filter_ccf(1,delayline)
48 self.fg.connect(self.input, self.delay)
50 # magnitude squared blocks
51 self.magsqrd1 = gr.complex_to_mag_squared()
52 self.magsqrd2 = gr.complex_to_mag_squared()
53 self.adder = gr.add_ff()
55 moving_sum_taps = [rho/2 for i in range(cpsize)]
56 self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps)
58 self.fg.connect(self.input,self.magsqrd1)
59 self.fg.connect(self.delay,self.magsqrd2)
60 self.fg.connect(self.magsqrd1,(self.adder,0))
61 self.fg.connect(self.magsqrd2,(self.adder,1))
62 self.fg.connect(self.adder,self.moving_sum_filter)
65 # Correlation from ML Sync
66 self.conjg = gr.conjugate_cc();
67 self.mixer = gr.multiply_cc();
69 movingsum2_taps = [1.0 for i in range(cpsize)]
70 self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
73 # Correlator data handler
74 self.c2mag = gr.complex_to_mag()
75 self.angle = gr.complex_to_arg()
76 self.fg.connect(self.input,(self.mixer,1))
77 self.fg.connect(self.delay,self.conjg,(self.mixer,0))
78 self.fg.connect(self.mixer,self.movingsum2,self.c2mag)
79 self.fg.connect(self.movingsum2,self.angle)
81 # ML Sync output arg, need to find maximum point of this
82 self.diff = gr.sub_ff()
83 self.fg.connect(self.c2mag,(self.diff,0))
84 self.fg.connect(self.moving_sum_filter,(self.diff,1))
86 #ML measurements input to sampler block and detect
87 nco_sensitivity = 1.0/fft_length
88 self.f2c = gr.float_to_complex()
89 self.sampler = gr.ofdm_sampler(fft_length,symbol_length)
90 self.pkt_detect = gr.peak_detector_ff(0.2, 0.25, 30, 0.0001)
91 self.dpll = gr.dpll_ff(float(symbol_length),0.01)
92 self.sample_and_hold = gr.sample_and_hold_ff()
93 self.nco = gr.frequency_modulator_fc(nco_sensitivity)
94 self.inv = gr.multiply_const_ff(-1)
95 self.sigmix = gr.multiply_cc()
97 # Mix the signal with an NCO controlled by the sync loop
98 self.fg.connect(self.input, (self.sigmix,0))
99 self.fg.connect(self.nco, (self.sigmix,1))
100 self.fg.connect(self.sigmix, (self.sampler,0))
105 peak_null = gr.null_sink(gr.sizeof_float)
108 peak_trigger = gr.vector_source_f(data, True)
110 self.fg.connect(self.pkt_detect, peak_null)
111 self.fg.connect(peak_trigger, self.f2c, (self.sampler,1))
112 self.fg.connect(peak_trigger, (self.sample_and_hold,1))
114 # use the sync loop values to set the sampler and the NCO
116 # self.angle = epsilon
118 self.fg.connect(self.diff, self.pkt_detect)
120 if not sample_trigger:
122 self.fg.connect(self.pkt_detect, self.dpll,self.f2c, (self.sampler,1))
123 self.fg.connect(self.dpll, (self.sample_and_hold,1))
125 self.fg.connect(self.pkt_detect, self.f2c, (self.sampler,1))
126 self.fg.connect(self.pkt_detect, (self.sample_and_hold,1))
128 self.fg.connect(self.angle, (self.sample_and_hold,0))
129 self.fg.connect(self.sample_and_hold, self.inv, self.nco)
133 self.fg.connect(self.diff, gr.file_sink(gr.sizeof_float, "theta_f.dat"))
134 self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "epsilon_f.dat"))
136 self.fg.connect(self.dpll, gr.file_sink(gr.sizeof_float, "dpll_pulses.dat"))
138 self.fg.connect(peak_trigger, gr.file_sink(gr.sizeof_float, "peaks_f.dat"))
140 self.fg.connect(self.pkt_detect, gr.file_sink(gr.sizeof_float, "peaks_f.dat"))
142 self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "sample_and_hold_f.dat"))
143 self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "nco_c.dat"))
144 self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "input_c.dat"))
145 self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "output_c.dat"))
147 gr.hier_block.__init__(self, fg, self.input, self.sampler)