Adds usrp2 example directory, WFM receiver. Default audio rate works out to 32015...
[debian/gnuradio] / gnuradio-examples / python / usrp2 / usrp2_wfm_rcv.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2005,2006,2007,2008 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 3, 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 from gnuradio import gr, gru, eng_notation, optfir
24 from gnuradio import audio
25 from gnuradio import usrp2
26 from gnuradio import blks2
27 from gnuradio.eng_option import eng_option
28 from gnuradio.wxgui import slider, powermate
29 from gnuradio.wxgui import stdgui2, fftsink2, form
30 from optparse import OptionParser
31 import sys
32 import math
33 import wx
34
35 class wfm_rx_block (stdgui2.std_top_block):
36     def __init__(self,frame,panel,vbox,argv):
37         stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv)
38
39         parser = OptionParser(option_class=eng_option)
40         parser.add_option("-e", "--interface", type="string", default="eth0",
41                           help="select Ethernet interface, default is eth0")
42         parser.add_option("-m", "--mac-addr", type="string", default="",
43                           help="select USRP by MAC address, default is auto-select")
44         #parser.add_option("-A", "--antenna", default=None,
45         #                  help="select Rx Antenna (only on RFX-series boards)")
46         parser.add_option("-f", "--freq", type="eng_float", default=100.1,
47                           help="set frequency to FREQ", metavar="FREQ")
48         parser.add_option("-g", "--gain", type="eng_float", default=None,
49                           help="set gain in dB (default is midpoint)")
50         parser.add_option("-V", "--volume", type="eng_float", default=None,
51                           help="set volume (default is midpoint)")
52         parser.add_option("-O", "--audio-output", type="string", default="",
53                           help="pcm device name.  E.g., hw:0,0 or surround51 or /dev/dsp")
54
55         (options, args) = parser.parse_args()
56         if len(args) != 0:
57             parser.print_help()
58             sys.exit(1)
59         
60         self.frame = frame
61         self.panel = panel
62         
63         self.vol = 0
64         self.state = "FREQ"
65         self.freq = 0
66
67         # build graph
68         
69         self.u = usrp2.source_32fc(options.interface, options.mac_addr)
70
71         adc_rate = self.u.adc_rate()                # 100 MS/s
72         usrp_decim = 312
73         self.u.set_decim(usrp_decim)
74         usrp_rate = adc_rate / usrp_decim           # ~320 kS/s
75         chanfilt_decim = 1
76         demod_rate = usrp_rate / chanfilt_decim
77         audio_decimation = 10
78         audio_rate = demod_rate / audio_decimation  # ~32 kHz
79
80         print "Using RX d'board 0x%04X" % (self.u.daughterboard_id(),)
81
82         chan_filt_coeffs = optfir.low_pass (1,           # gain
83                                             usrp_rate,   # sampling rate
84                                             80e3,        # passband cutoff
85                                             115e3,       # stopband cutoff
86                                             0.1,         # passband ripple
87                                             60)          # stopband attenuation
88         #print len(chan_filt_coeffs)
89         chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs)
90
91         self.guts = blks2.wfm_rcv (demod_rate, audio_decimation)
92
93         self.volume_control = gr.multiply_const_ff(self.vol)
94
95         # sound card as final sink
96         audio_sink = audio.sink (int (audio_rate),
97                                  options.audio_output,
98                                  False)  # ok_to_block
99         
100         # now wire it all together
101         self.connect (self.u, chan_filt, self.guts, self.volume_control, audio_sink)
102
103         self._build_gui(vbox, usrp_rate, demod_rate, audio_rate)
104
105         if options.gain is None:
106             # if no gain was specified, use the mid-point in dB
107             g = self.u.gain_range()
108             options.gain = float(g[0]+g[1])/2
109
110         if options.volume is None:
111             g = self.volume_range()
112             options.volume = float(g[0]+g[1])/2
113             
114         if abs(options.freq) < 1e6:
115             options.freq *= 1e6
116
117         # set initial values
118
119         self.set_gain(options.gain)
120         self.set_vol(options.volume)
121         if not(self.set_freq(options.freq)):
122             self._set_status_msg("Failed to set initial frequency")
123
124
125     def _set_status_msg(self, msg, which=0):
126         self.frame.GetStatusBar().SetStatusText(msg, which)
127
128
129     def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate):
130
131         def _form_set_freq(kv):
132             return self.set_freq(kv['freq'])
133
134
135         if 1:
136             self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP2",
137                                                fft_size=512, sample_rate=usrp_rate,
138                                                ref_scale=1.0, ref_level=0, y_divs=12)
139             self.connect (self.u, self.src_fft)
140             vbox.Add (self.src_fft.win, 4, wx.EXPAND)
141
142         if 1:
143             post_filt_fft = fftsink2.fft_sink_f(self.panel, title="Post Demod", 
144                                                 fft_size=1024, sample_rate=usrp_rate,
145                                                 y_per_div=10, ref_level=0)
146             self.connect (self.guts.fm_demod, post_filt_fft)
147             vbox.Add (post_filt_fft.win, 4, wx.EXPAND)
148
149         if 0:
150             post_deemph_fft = fftsink2.fft_sink_f(self.panel, title="Post Deemph",
151                                                   fft_size=512, sample_rate=audio_rate,
152                                                   y_per_div=10, ref_level=-20)
153             self.connect (self.guts.deemph, post_deemph_fft)
154             vbox.Add (post_deemph_fft.win, 4, wx.EXPAND)
155
156         
157         # control area form at bottom
158         self.myform = myform = form.form()
159
160         hbox = wx.BoxSizer(wx.HORIZONTAL)
161         hbox.Add((5,0), 0)
162         myform['freq'] = form.float_field(
163             parent=self.panel, sizer=hbox, label="Freq", weight=1,
164             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
165
166         hbox.Add((5,0), 0)
167         myform['freq_slider'] = \
168             form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3,
169                                         range=(87.9e6, 108.1e6, 0.1e6),
170                                         callback=self.set_freq)
171         hbox.Add((5,0), 0)
172         vbox.Add(hbox, 0, wx.EXPAND)
173
174         hbox = wx.BoxSizer(wx.HORIZONTAL)
175         hbox.Add((5,0), 0)
176
177         myform['volume'] = \
178             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume",
179                                         weight=3, range=self.volume_range(),
180                                         callback=self.set_vol)
181         hbox.Add((5,0), 1)
182
183         myform['gain'] = \
184             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain",
185                                         weight=3, range=self.u.gain_range(),
186                                         callback=self.set_gain)
187         hbox.Add((5,0), 0)
188         vbox.Add(hbox, 0, wx.EXPAND)
189
190         try:
191             self.knob = powermate.powermate(self.frame)
192             self.rot = 0
193             powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate)
194             powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button)
195         except:
196             pass
197             #print "FYI: No Powermate or Contour Knob found"
198
199
200     def on_rotate (self, event):
201         self.rot += event.delta
202         if (self.state == "FREQ"):
203             if self.rot >= 3:
204                 self.set_freq(self.freq + .1e6)
205                 self.rot -= 3
206             elif self.rot <=-3:
207                 self.set_freq(self.freq - .1e6)
208                 self.rot += 3
209         else:
210             step = self.volume_range()[2]
211             if self.rot >= 3:
212                 self.set_vol(self.vol + step)
213                 self.rot -= 3
214             elif self.rot <=-3:
215                 self.set_vol(self.vol - step)
216                 self.rot += 3
217             
218     def on_button (self, event):
219         if event.value == 0:        # button up
220             return
221         self.rot = 0
222         if self.state == "FREQ":
223             self.state = "VOL"
224         else:
225             self.state = "FREQ"
226         self.update_status_bar ()
227         
228
229     def set_vol (self, vol):
230         g = self.volume_range()
231         self.vol = max(g[0], min(g[1], vol))
232         self.volume_control.set_k(10**(self.vol/10))
233         self.myform['volume'].set_value(self.vol)
234         self.update_status_bar ()
235                                         
236     def set_freq(self, target_freq):
237         """
238         Set the center frequency we're interested in.
239
240         @param target_freq: frequency in Hz
241         @rypte: bool
242
243         Tuning is a two step process.  First we ask the front-end to
244         tune as close to the desired frequency as it can.  Then we use
245         the result of that operation and our target_frequency to
246         determine the value for the digital down converter.
247         """
248         r = self.u.set_center_freq(target_freq)
249         if r:
250             self.freq = target_freq
251             self.myform['freq'].set_value(target_freq)         # update displayed value
252             self.myform['freq_slider'].set_value(target_freq)  # update displayed value
253             self.update_status_bar()
254             self._set_status_msg("OK", 0)
255             return True
256
257         self._set_status_msg("Failed", 0)
258         return False
259
260     def set_gain(self, gain):
261         self.myform['gain'].set_value(gain)     # update displayed value
262         self.u.set_gain(gain)
263
264     def update_status_bar (self):
265         msg = "Volume:%r  Setting:%s" % (self.vol, self.state)
266         self._set_status_msg(msg, 1)
267         self.src_fft.set_baseband_freq(self.freq)
268
269     def volume_range(self):
270         return (-20.0, 0.0, 0.5)
271         
272
273 if __name__ == '__main__':
274     app = stdgui2.stdapp (wfm_rx_block, "USRP2 WFM RX")
275     app.MainLoop ()