Imported Upstream version 3.0
[debian/gnuradio] / gnuradio-examples / python / usrp / usrp_nbfm_rcv.py
1 #!/usr/bin/env python
2
3 from gnuradio import gr, gru, eng_notation, optfir
4 from gnuradio import audio
5 from gnuradio import usrp
6 from gnuradio import blks
7 from gnuradio.eng_option import eng_option
8 from gnuradio.wxgui import slider, powermate
9 from gnuradio.wxgui import stdgui, fftsink, form
10 from optparse import OptionParser
11 import usrp_dbid
12 import sys
13 import math
14 import wx
15
16
17 #////////////////////////////////////////////////////////////////////////
18 #                           Control Stuff
19 #////////////////////////////////////////////////////////////////////////
20
21 class my_graph (stdgui.gui_flow_graph):
22     def __init__(self,frame,panel,vbox,argv):
23         stdgui.gui_flow_graph.__init__ (self,frame,panel,vbox,argv)
24
25         parser=OptionParser(option_class=eng_option)
26         parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
27                           help="select USRP Rx side A or B (default=A)")
28         parser.add_option("-f", "--freq", type="eng_float", default=146.585e6,
29                           help="set frequency to FREQ", metavar="FREQ")
30         parser.add_option("-g", "--gain", type="eng_float", default=None,
31                           help="set gain in dB (default is midpoint)")
32         parser.add_option("-V", "--volume", type="eng_float", default=None,
33                           help="set volume (default is midpoint)")
34         parser.add_option("-O", "--audio-output", type="string", default="",
35                           help="pcm device name.  E.g., hw:0,0 or surround51 or /dev/dsp")
36         parser.add_option("-N", "--no-gui", action="store_true", default=False)
37
38         (options, args) = parser.parse_args()
39         if len(args) != 0:
40             parser.print_help()
41             sys.exit(1)
42         
43         if options.freq < 1e6:
44             options.freq *= 1e6
45             
46         self.frame = frame
47         self.panel = panel
48         
49         self.state = "FREQ"
50         self.freq = 0
51         self.freq_step = 25e3
52
53         self.rxpath = receive_path(self, options.rx_subdev_spec, options.gain, options.audio_output)
54
55         self._build_gui(vbox, options.no_gui)
56
57         # set initial values
58
59         if options.volume is not None:
60             self.set_volume(options.volume)
61
62         if not(self.set_freq(options.freq)):
63             self._set_status_msg("Failed to set initial frequency")
64
65         self.set_gain(self.rxpath.gain)               # update gui
66         self.set_volume(self.rxpath.volume)           # update gui
67         self.set_squelch(self.rxpath.threshold())     # update gui
68
69
70     def _set_status_msg(self, msg, which=0):
71         self.frame.GetStatusBar().SetStatusText(msg, which)
72
73
74     def _build_gui(self, vbox, no_gui):
75
76         def _form_set_freq(kv):
77             return self.set_freq(kv['freq'])
78
79
80         self.src_fft = None
81         if 1 and not(no_gui):
82             self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP",
83                                                fft_size=512, sample_rate=self.rxpath.if_rate,
84                                                ref_level=80, y_per_div=20)
85             self.connect (self.rxpath.u, self.src_fft)
86             vbox.Add (self.src_fft.win, 4, wx.EXPAND)
87
88         if 1 and not(no_gui):
89             rx_fft = fftsink.fft_sink_c (self, self.panel, title="Post s/w DDC",
90                                          fft_size=512, sample_rate=self.rxpath.quad_rate,
91                                          ref_level=80, y_per_div=20)
92             self.connect (self.rxpath.ddc, rx_fft)
93             vbox.Add (rx_fft.win, 4, wx.EXPAND)
94         
95         if 1 and not(no_gui):
96             post_deemph_fft = fftsink.fft_sink_f (self, self.panel, title="Post Deemph",
97                                                   fft_size=512, sample_rate=self.rxpath.audio_rate,
98                                                   y_per_div=10, ref_level=-40)
99             self.connect (self.rxpath.fmrx.deemph, post_deemph_fft)
100             vbox.Add (post_deemph_fft.win, 4, wx.EXPAND)
101
102         if 0:
103             post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", 
104                                                 fft_size=512, sample_rate=audio_rate,
105                                                 y_per_div=10, ref_level=-40)
106             self.connect (self.guts.audio_filter, post_filt)
107             vbox.Add (fft_win4, 4, wx.EXPAND)
108
109         
110         # control area form at bottom
111         self.myform = myform = form.form()
112
113         hbox = wx.BoxSizer(wx.HORIZONTAL)
114         hbox.Add((5,0), 0)
115         myform['freq'] = form.float_field(
116             parent=self.panel, sizer=hbox, label="Freq", weight=1,
117             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
118
119         #hbox.Add((5,0), 0)
120         #myform['freq_slider'] = \
121         #    form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3,
122         #                                range=(87.9e6, 108.1e6, 0.1e6),
123         #                                callback=self.set_freq)
124
125         hbox.Add((5,0), 0)
126         vbox.Add(hbox, 0, wx.EXPAND)
127
128         hbox = wx.BoxSizer(wx.HORIZONTAL)
129         hbox.Add((5,0), 0)
130
131         myform['volume'] = \
132             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume",
133                                         weight=3, range=self.volume_range(),
134                                         callback=self.set_volume)
135         hbox.Add((5,0), 0)
136         myform['squelch'] = \
137             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Squelch",
138                                         weight=3, range=self.rxpath.squelch_range(),
139                                         callback=self.set_squelch)
140         hbox.Add((5,0), 0)
141         myform['gain'] = \
142             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain",
143                                         weight=3, range=self.rxpath.subdev.gain_range(),
144                                         callback=self.set_gain)
145         hbox.Add((5,0), 0)
146         vbox.Add(hbox, 0, wx.EXPAND)
147
148         try:
149             self.knob = powermate.powermate(self.frame)
150             self.rot = 0
151             powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate)
152             powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button)
153         except:
154             print "FYI: No Powermate or Contour Knob found"
155
156
157     def on_rotate (self, event):
158         self.rot += event.delta
159         if (self.state == "FREQ"):
160             if self.rot >= 3:
161                 self.set_freq(self.freq + self.freq_step)
162                 self.rot -= 3
163             elif self.rot <=-3:
164                 self.set_freq(self.freq - self.freq_step)
165                 self.rot += 3
166         else:
167             step = self.volume_range()[2]
168             if self.rot >= 3:
169                 self.set_volume(self.rxpath.volume + step)
170                 self.rot -= 3
171             elif self.rot <=-3:
172                 self.set_volume(self.rxpath.volume - step)
173                 self.rot += 3
174             
175     def on_button (self, event):
176         if event.value == 0:        # button up
177             return
178         self.rot = 0
179         if self.state == "FREQ":
180             self.state = "VOL"
181         else:
182             self.state = "FREQ"
183         self.update_status_bar ()
184         
185
186     def set_squelch(self, threshold_in_db):
187         self.rxpath.set_squelch(threshold_in_db)
188         self.myform['squelch'].set_value(self.rxpath.threshold())
189
190     def set_volume (self, vol):
191         self.rxpath.set_volume(vol)
192         self.myform['volume'].set_value(self.rxpath.volume)
193         self.update_status_bar ()
194                                         
195     def set_freq(self, target_freq):
196         r = self.rxpath.set_freq(target_freq)
197         if r:
198             self.freq = target_freq
199             self.myform['freq'].set_value(target_freq)         # update displayed value
200             #self.myform['freq_slider'].set_value(target_freq)  # update displayed value
201             self.update_status_bar()
202             self._set_status_msg("OK", 0)
203             return True
204
205         self._set_status_msg("Failed", 0)
206         return False
207
208     def set_gain(self, gain):
209         self.myform['gain'].set_value(gain)     # update displayed value
210         self.rxpath.set_gain(gain)
211
212     def update_status_bar (self):
213         msg = "Volume:%r  Setting:%s" % (self.rxpath.volume, self.state)
214         self._set_status_msg(msg, 1)
215         if self.src_fft:
216             self.src_fft.set_baseband_freq(self.freq)
217
218     def volume_range(self):
219         return (-20.0, 0.0, 0.5)
220         
221
222 #////////////////////////////////////////////////////////////////////////
223 #                           Receive Path
224 #////////////////////////////////////////////////////////////////////////
225
226 USE_SIMPLE_SQUELCH = False
227
228 class receive_path(gr.hier_block):
229     def __init__(self, fg, subdev_spec, gain, audio_output):
230
231         self.u = usrp.source_c ()
232         adc_rate = self.u.adc_rate()
233
234         self.if_rate = 256e3                              # 256 kS/s
235         usrp_decim = int(adc_rate // self.if_rate)
236         if_decim = 4
237         self.u.set_decim_rate(usrp_decim)
238         self.quad_rate = self.if_rate // if_decim         #  64 kS/s
239         audio_decim = 2
240         self.audio_rate = self.quad_rate // audio_decim   #  32 kS/s
241
242
243         if subdev_spec is None:
244             subdev_spec = usrp.pick_rx_subdevice(self.u)
245         self.subdev = usrp.selected_subdev(self.u, subdev_spec)
246         print "Using RX d'board %s" % (self.subdev.side_and_name(),)
247
248         self.u.set_mux(usrp.determine_rx_mux_value(self.u, subdev_spec))
249
250         # Create filter to get actual channel we want
251         chan_coeffs = gr.firdes.low_pass (1.0,                # gain
252                                           self.if_rate,       # sampling rate
253                                           13e3,               # low pass cutoff freq
254                                           4e3,                # width of trans. band
255                                           gr.firdes.WIN_HANN) # filter type 
256
257         print "len(rx_chan_coeffs) =", len(chan_coeffs)
258
259         # Decimating Channel filter with frequency translation
260         # complex in and out, float taps
261         self.ddc = gr.freq_xlating_fir_filter_ccf(if_decim,       # decimation rate
262                                                   chan_coeffs,    # taps
263                                                   0,              # frequency translation amount
264                                                   self.if_rate)   # input sample rate
265
266         if USE_SIMPLE_SQUELCH:
267             self.squelch = gr.simple_squelch_cc(20)
268         else:
269             self.squelch = blks.standard_squelch(fg, self.audio_rate)
270
271         # instantiate the guts of the single channel receiver
272         self.fmrx = blks.nbfm_rx(fg, self.audio_rate, self.quad_rate)
273
274         # audio gain / mute block
275         self._audio_gain = gr.multiply_const_ff(1.0)
276
277         # sound card as final sink
278         audio_sink = audio.sink (int(self.audio_rate), audio_output)
279         
280         # now wire it all together
281         if USE_SIMPLE_SQUELCH:
282             fg.connect (self.u, self.ddc, self.squelch, self.fmrx,
283                         self._audio_gain, audio_sink)
284         else:
285             fg.connect (self.u, self.ddc, self.fmrx, self.squelch,
286                         self._audio_gain, audio_sink)
287
288         gr.hier_block.__init__(self, fg, self.u, audio_sink)
289
290         if gain is None:
291             # if no gain was specified, use the mid-point in dB
292             g = self.subdev.gain_range()
293             gain = float(g[0]+g[1])/2
294
295         self.set_gain(gain)
296
297         v = self.volume_range()
298         self.set_volume((v[0]+v[1])/2)
299         s = self.squelch_range()
300         self.set_squelch((s[0]+s[1])/2)
301
302     def volume_range(self):
303         return (-20.0, 0.0, 0.5)
304
305     def set_volume (self, vol):
306         g = self.volume_range()
307         self.volume = max(g[0], min(g[1], vol))
308         self._update_audio_gain()
309
310     def _update_audio_gain(self):
311         self._audio_gain.set_k(10**(self.volume/10))
312         
313     def squelch_range(self):
314         r = self.squelch.squelch_range()
315         #print "squelch_range: ", r
316         return r
317     
318     def set_squelch(self, threshold):
319         #print "SQL =", threshold
320         self.squelch.set_threshold(threshold)
321
322     def threshold(self):
323         t = self.squelch.threshold()
324         #print "t =", t
325         return t
326
327     def set_freq(self, target_freq):
328         """
329         Set the center frequency we're interested in.
330
331         @param target_freq: frequency in Hz
332         @rypte: bool
333
334         Tuning is a two step process.  First we ask the front-end to
335         tune as close to the desired frequency as it can.  Then we use
336         the result of that operation and our target_frequency to
337         determine the value for the digital down converter in the
338         FPGA.  Finally, we feed any residual_freq to the s/w freq
339         translator.
340         """
341
342         r = usrp.tune(self.u, 0, self.subdev, target_freq)
343         if r:
344             # Use residual_freq in s/w freq translater
345             # print "residual_freq =", r.residual_freq
346             self.ddc.set_center_freq(-r.residual_freq)
347             return True
348
349         return False
350
351     def set_gain(self, gain):
352         self.gain = gain
353         self.subdev.set_gain(gain)
354
355
356 # ////////////////////////////////////////////////////////////////////////
357 #                                Main
358 # ////////////////////////////////////////////////////////////////////////
359
360 if __name__ == '__main__':
361     app = stdgui.stdapp (my_graph, "USRP NBFM RX")
362     app.MainLoop ()