merged trondeau/digital-wip2 r4193:4730 into trunk - improves digital receiver and...
[debian/gnuradio] / gnuradio-examples / python / hier / digital / benchmark_loopback.py
1 #!/usr/bin/env python
2 #!/usr/bin/env python
3 #
4 # Copyright 2005, 2006 Free Software Foundation, Inc.
5
6 # This file is part of GNU Radio
7
8 # GNU Radio is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2, or (at your option)
11 # any later version.
12
13 # GNU Radio is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17
18 # You should have received a copy of the GNU General Public License
19 # along with GNU Radio; see the file COPYING.  If not, write to
20 # the Free Software Foundation, Inc., 51 Franklin Street,
21 # Boston, MA 02110-1301, USA.
22
23
24 from gnuradio import gr, gru, modulation_utils
25 from gnuradio import eng_notation
26 from gnuradio.eng_option import eng_option
27 from optparse import OptionParser
28
29 import random, time, struct, sys, math
30
31 # from current dir
32 from transmit_path_lb import transmit_path
33 from receive_path_lb import receive_path
34 import fusb_options
35
36 class awgn_channel(gr.hier_block2):
37     def __init__(self, sample_rate, noise_voltage, frequency_offset, seed=False):
38         gr.hier_block2.__init__(self, "awgn_channel",
39                                 gr.io_signature(1,1,gr.sizeof_gr_complex), # Input signature
40                                 gr.io_signature(1,1,gr.sizeof_gr_complex)) # Output signature
41
42         # Create the Gaussian noise source
43         if not seed:
44             self.noise = gr.noise_source_c(gr.GR_GAUSSIAN, noise_voltage)
45         else:
46             rseed = int(time.time())
47             self.noise = gr.noise_source_c(gr.GR_GAUSSIAN, noise_voltage, rseed)
48         self.define_component("noise", self.noise)
49         self.define_component("adder", gr.add_cc())
50
51         # Create the frequency offset
52         self.define_component("offset", gr.sig_source_c((sample_rate*1.0), gr.GR_SIN_WAVE,
53                                                         frequency_offset, 1.0, 0.0))
54         self.define_component("mixer", gr.multiply_cc())
55
56         # Connect the components
57         self.connect("self", 0, "mixer", 0)
58         self.connect("offset", 0, "mixer", 1)
59         self.connect("mixer", 0, "adder", 0)
60         self.connect("noise", 0, "adder", 1)
61         self.connect("adder", 0, "self", 0)
62
63
64 class my_graph(gr.hier_block2):
65     def __init__(self, mod_class, demod_class, rx_callback, options):
66         gr.hier_block2.__init__(self, "my_graph",
67                                 gr.io_signature(0,0,0), # Input signature
68                                 gr.io_signature(0,0,0)) # Output signature
69
70         channelon = True;
71
72         SNR = 10.0**(options.snr/10.0)
73         frequency_offset = options.frequency_offset
74         
75         power_in_signal = abs(options.tx_amplitude)**2
76         noise_power = power_in_signal/SNR
77         noise_voltage = math.sqrt(noise_power)
78
79         self.txpath = transmit_path(mod_class, options)
80         self.throttle = gr.throttle(gr.sizeof_gr_complex, options.sample_rate)
81         self.rxpath = receive_path(demod_class, rx_callback, options)
82
83         if channelon:
84             self.channel = awgn_channel(options.sample_rate, noise_voltage, frequency_offset, options.seed)
85
86             # Define the components
87             self.define_component("txpath", self.txpath)
88             self.define_component("throttle", self.throttle)
89             self.define_component("channel", self.channel)
90             self.define_component("rxpath", self.rxpath)
91             
92             # Connect components
93             self.connect("txpath", 0, "throttle", 0)
94             self.connect("throttle", 0, "channel", 0)
95             self.connect("channel", 0, "rxpath", 0)
96         else:
97             # Define the components
98             self.define_component("txpath", self.txpath)
99             self.define_component("throttle", self.throttle)
100             self.define_component("rxpath", self.rxpath)
101         
102             # Connect components
103             self.connect("txpath", 0, "throttle", 0)
104             self.connect("throttle", 0, "rxpath", 0)
105
106
107 # /////////////////////////////////////////////////////////////////////////////
108 #                                   main
109 # /////////////////////////////////////////////////////////////////////////////
110
111 def main():
112
113     global n_rcvd, n_right
114
115     n_rcvd = 0
116     n_right = 0
117     
118     def rx_callback(ok, payload):
119         global n_rcvd, n_right
120         (pktno,) = struct.unpack('!H', payload[0:2])
121         n_rcvd += 1
122         if ok:
123             n_right += 1
124
125         print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
126             ok, pktno, n_rcvd, n_right)
127
128     def send_pkt(payload='', eof=False):
129         return top_block.txpath.send_pkt(payload, eof)
130
131
132     mods = modulation_utils.type_1_mods()
133     demods = modulation_utils.type_1_demods()
134
135     parser = OptionParser(option_class=eng_option, conflict_handler="resolve")
136     expert_grp = parser.add_option_group("Expert")
137     channel_grp = parser.add_option_group("Channel")
138
139     parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
140                       default='dbpsk',
141                       help="Select modulation from: %s [default=%%default]"
142                             % (', '.join(mods.keys()),))
143
144     parser.add_option("-s", "--size", type="eng_float", default=1500,
145                       help="set packet size [default=%default]")
146     parser.add_option("-M", "--megabytes", type="eng_float", default=1.0,
147                       help="set megabytes to transmit [default=%default]")
148     parser.add_option("","--discontinuous", action="store_true", default=False,
149                       help="enable discontinous transmission (bursts of 5 packets)")
150
151     channel_grp.add_option("", "--sample-rate", type="eng_float", default=1e5,
152                            help="set speed of channel/simulation rate to RATE [default=%default]") 
153     channel_grp.add_option("", "--snr", type="eng_float", default=30,
154                            help="set the SNR of the channel in dB [default=%default]")
155     channel_grp.add_option("", "--frequency-offset", type="eng_float", default=0,
156                            help="set frequency offset introduced by channel [default=%default]")
157     channel_grp.add_option("", "--seed", action="store_true", default=False,
158                            help="use a random seed for AWGN noise [default=%default]")
159
160     transmit_path.add_options(parser, expert_grp)
161     receive_path.add_options(parser, expert_grp)
162
163     for mod in mods.values():
164         mod.add_options(expert_grp)
165     for demod in demods.values():
166         demod.add_options(expert_grp)
167
168     (options, args) = parser.parse_args ()
169
170     if len(args) != 0:
171         parser.print_help()
172         sys.exit(1)
173  
174     r = gr.enable_realtime_scheduling()
175     if r != gr.RT_OK:
176         print "Warning: failed to enable realtime scheduling"
177         
178     # Create an instance of a hierarchical block
179     top_block = my_graph(mods[options.modulation], demods[options.modulation], rx_callback, options)
180     
181     # Create an instance of a runtime, passing it the top block
182     runtime = gr.runtime(top_block)
183     runtime.start()
184
185     # generate and send packets
186     nbytes = int(1e6 * options.megabytes)
187     n = 0
188     pktno = 0
189     pkt_size = int(options.size)
190
191     while n < nbytes:
192         send_pkt(struct.pack('!H', pktno) + (pkt_size - 2) * chr(pktno & 0xff))
193         n += pkt_size
194         if options.discontinuous and pktno % 5 == 4:
195             time.sleep(1)
196         pktno += 1
197         
198     send_pkt(eof=True)
199
200     runtime.wait()
201     
202 if __name__ == '__main__':
203     try:
204         main()
205     except KeyboardInterrupt:
206         pass