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