Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-examples / python / usrp2 / usrp2_wfm_rcv.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2005,2006,2007,2008,2009 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         #FIXME: need named constants and text descriptions available to (gr-)usrp2 even
81         #when usrp(1) module is not built.  A usrp_common module, perhaps?
82         dbid = self.u.daughterboard_id()
83         print "Using RX d'board 0x%04X" % (dbid,)
84         if not (dbid == 0x0001 or #usrp_dbid.BASIC_RX
85                 dbid == 0x0003 or #usrp_dbid.TV_RX
86                 dbid == 0x000c or #usrp_dbid.TV_RX_REV_2
87                 dbid == 0x0040):  #usrp_dbid.TV_RX_REV_3    
88             print "This daughterboard does not cover the required frequency range"
89             print "for this application.  Please use a BasicRX or TVRX daughterboard."
90             raw_input("Press ENTER to continue anyway, or Ctrl-C to exit.")
91
92         chan_filt_coeffs = optfir.low_pass (1,           # gain
93                                             usrp_rate,   # sampling rate
94                                             80e3,        # passband cutoff
95                                             115e3,       # stopband cutoff
96                                             0.1,         # passband ripple
97                                             60)          # stopband attenuation
98         #print len(chan_filt_coeffs)
99         chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs)
100
101         self.guts = blks2.wfm_rcv (demod_rate, audio_decimation)
102
103         self.volume_control = gr.multiply_const_ff(self.vol)
104
105         # sound card as final sink
106         audio_sink = audio.sink (int (audio_rate),
107                                  options.audio_output,
108                                  False)  # ok_to_block
109         
110         # now wire it all together
111         self.connect (self.u, chan_filt, self.guts, self.volume_control, audio_sink)
112
113         self._build_gui(vbox, usrp_rate, demod_rate, audio_rate)
114
115         if options.gain is None:
116             # if no gain was specified, use the mid-point in dB
117             g = self.u.gain_range()
118             options.gain = float(g[0]+g[1])/2
119
120         if options.volume is None:
121             g = self.volume_range()
122             options.volume = float(g[0]+g[1])/2
123             
124         if abs(options.freq) < 1e6:
125             options.freq *= 1e6
126
127         # set initial values
128
129         self.set_gain(options.gain)
130         self.set_vol(options.volume)
131         if not(self.set_freq(options.freq)):
132             self._set_status_msg("Failed to set initial frequency")
133
134
135     def _set_status_msg(self, msg, which=0):
136         self.frame.GetStatusBar().SetStatusText(msg, which)
137
138
139     def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate):
140
141         def _form_set_freq(kv):
142             return self.set_freq(kv['freq'])
143
144
145         if 1:
146             self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP2",
147                                                fft_size=512, sample_rate=usrp_rate,
148                                                ref_scale=1.0, ref_level=0, y_divs=12)
149             self.connect (self.u, self.src_fft)
150             vbox.Add (self.src_fft.win, 4, wx.EXPAND)
151
152         if 1:
153             post_filt_fft = fftsink2.fft_sink_f(self.panel, title="Post Demod", 
154                                                 fft_size=1024, sample_rate=usrp_rate,
155                                                 y_per_div=10, ref_level=0)
156             self.connect (self.guts.fm_demod, post_filt_fft)
157             vbox.Add (post_filt_fft.win, 4, wx.EXPAND)
158
159         if 0:
160             post_deemph_fft = fftsink2.fft_sink_f(self.panel, title="Post Deemph",
161                                                   fft_size=512, sample_rate=audio_rate,
162                                                   y_per_div=10, ref_level=-20)
163             self.connect (self.guts.deemph, post_deemph_fft)
164             vbox.Add (post_deemph_fft.win, 4, wx.EXPAND)
165
166         
167         # control area form at bottom
168         self.myform = myform = form.form()
169
170         hbox = wx.BoxSizer(wx.HORIZONTAL)
171         hbox.Add((5,0), 0)
172         myform['freq'] = form.float_field(
173             parent=self.panel, sizer=hbox, label="Freq", weight=1,
174             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
175
176         hbox.Add((5,0), 0)
177         myform['freq_slider'] = \
178             form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3,
179                                         range=(87.9e6, 108.1e6, 0.1e6),
180                                         callback=self.set_freq)
181         hbox.Add((5,0), 0)
182         vbox.Add(hbox, 0, wx.EXPAND)
183
184         hbox = wx.BoxSizer(wx.HORIZONTAL)
185         hbox.Add((5,0), 0)
186
187         myform['volume'] = \
188             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume",
189                                         weight=3, range=self.volume_range(),
190                                         callback=self.set_vol)
191         hbox.Add((5,0), 1)
192
193         myform['gain'] = \
194             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain",
195                                         weight=3, range=self.u.gain_range(),
196                                         callback=self.set_gain)
197         hbox.Add((5,0), 0)
198         vbox.Add(hbox, 0, wx.EXPAND)
199
200         try:
201             self.knob = powermate.powermate(self.frame)
202             self.rot = 0
203             powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate)
204             powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button)
205         except:
206             pass
207             #print "FYI: No Powermate or Contour Knob found"
208
209
210     def on_rotate (self, event):
211         self.rot += event.delta
212         if (self.state == "FREQ"):
213             if self.rot >= 3:
214                 self.set_freq(self.freq + .1e6)
215                 self.rot -= 3
216             elif self.rot <=-3:
217                 self.set_freq(self.freq - .1e6)
218                 self.rot += 3
219         else:
220             step = self.volume_range()[2]
221             if self.rot >= 3:
222                 self.set_vol(self.vol + step)
223                 self.rot -= 3
224             elif self.rot <=-3:
225                 self.set_vol(self.vol - step)
226                 self.rot += 3
227             
228     def on_button (self, event):
229         if event.value == 0:        # button up
230             return
231         self.rot = 0
232         if self.state == "FREQ":
233             self.state = "VOL"
234         else:
235             self.state = "FREQ"
236         self.update_status_bar ()
237         
238
239     def set_vol (self, vol):
240         g = self.volume_range()
241         self.vol = max(g[0], min(g[1], vol))
242         self.volume_control.set_k(10**(self.vol/10))
243         self.myform['volume'].set_value(self.vol)
244         self.update_status_bar ()
245                                         
246     def set_freq(self, target_freq):
247         """
248         Set the center frequency we're interested in.
249
250         @param target_freq: frequency in Hz
251         @rypte: bool
252
253         Tuning is a two step process.  First we ask the front-end to
254         tune as close to the desired frequency as it can.  Then we use
255         the result of that operation and our target_frequency to
256         determine the value for the digital down converter.
257         """
258         r = self.u.set_center_freq(target_freq)
259         if r:
260             self.freq = target_freq
261             self.myform['freq'].set_value(target_freq)         # update displayed value
262             self.myform['freq_slider'].set_value(target_freq)  # update displayed value
263             self.update_status_bar()
264             self._set_status_msg("OK", 0)
265             return True
266
267         self._set_status_msg("Failed", 0)
268         return False
269
270     def set_gain(self, gain):
271         self.myform['gain'].set_value(gain)     # update displayed value
272         self.u.set_gain(gain)
273
274     def update_status_bar (self):
275         msg = "Volume:%r  Setting:%s" % (self.vol, self.state)
276         self._set_status_msg(msg, 1)
277         self.src_fft.set_baseband_freq(self.freq)
278
279     def volume_range(self):
280         return (-20.0, 0.0, 0.5)
281         
282
283 if __name__ == '__main__':
284     app = stdgui2.stdapp (wfm_rx_block, "USRP2 WFM RX")
285     app.MainLoop ()