Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-examples / python / usrp / usrp_tv_rcv.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2005,2006,2007 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 """
24 Realtime capture and display of analog Tv stations.
25 Can also use a file as source or sink
26 When you use an output file you can show the results frame-by-frame using ImageMagick
27 When you want to use the realtime sdl display window you must first install gr-video-sdl (is in gnuradio cvs).
28 When you use a file source, in stead of the usrp, make sure you capture interleaved shorts.
29 (Use usrp_rx_file.py, or use usrp_rx_cfile.py --output-shorts if you have a recent enough usrp_rx_cfile.py)
30 There is no synchronisation yet. The sync blocks are in development but not yet in cvs.
31
32 """
33 from gnuradio import gr, gru, eng_notation, optfir
34 try:
35   from gnuradio import video_sdl
36 except:
37   print "FYI: gr-video-sdl is not installed"
38   print "realtime SDL video output window will not be available"
39 from gnuradio import usrp
40 from gnuradio.eng_option import eng_option
41 from gnuradio.wxgui import slider, powermate
42 from gnuradio.wxgui import stdgui2, fftsink2, form
43 from optparse import OptionParser
44 from usrpm import usrp_dbid
45 import sys
46 import math
47 import wx
48
49 # To debug, insert this in your test code...
50 #import os
51 #print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),)
52 #raw_input ('Press Enter to continue: ')
53 # remainder of your test code follows...
54
55 def pick_subdevice(u):
56     """
57     The user didn't specify a subdevice on the command line.
58     Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A.
59
60     @return a subdev_spec
61     """
62     return usrp.pick_subdev(u, (usrp_dbid.TV_RX,
63                                 usrp_dbid.TV_RX_REV_2,
64                                 usrp_dbid.TV_RX_REV_3,
65                                 usrp_dbid.BASIC_RX))
66
67
68 class tv_rx_block (stdgui2.std_top_block):
69     def __init__(self,frame,panel,vbox,argv):
70         stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv)
71
72         usage="%prog: [options] [input_filename]. \n If you don't specify an input filename the usrp will be used as source\n " \
73               "Make sure your input capture file containes interleaved shorts not complex floats"
74         parser=OptionParser(option_class=eng_option)
75         parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
76                           help="select USRP Rx side A or B (default=A)")
77         parser.add_option("-d", "--decim", type="int", default=64,
78                           help="set fgpa decimation rate to DECIM [default=%default]")
79         parser.add_option("-f", "--freq", type="eng_float", default=519.25e6,
80                           help="set frequency to FREQ", metavar="FREQ")
81         parser.add_option("-g", "--gain", type="eng_float", default=None,
82                           help="set gain in dB (default is midpoint)")
83         parser.add_option("-c", "--contrast", type="eng_float", default=1.0,
84                           help="set contrast (default is 1.0)")
85         parser.add_option("-b", "--brightness", type="eng_float", default=0.0,
86                           help="set brightness (default is 0)")
87         parser.add_option("-8", "--width-8", action="store_true", default=False,
88                           help="Enable 8-bit samples across USB")
89         parser.add_option("-p", "--pal", action="store_true", default=False,
90                           help="PAL video format (this is the default)")
91         parser.add_option("-n", "--ntsc", action="store_true", default=False,
92                           help="NTSC video format")
93         parser.add_option("-o", "--out-filename", type="string", default="sdl",
94                           help="For example out_raw_uchar.gray. If you don't specify an output filename you will get a video_sink_sdl realtime output window. You then need to have gr-video-sdl installed)")
95         parser.add_option("-r", "--repeat", action="store_false", default=True,
96                           help="repeat file in a loop")
97         parser.add_option("-N", "--no-hb", action="store_true", default=False,
98                           help="don't use halfband filter in usrp")
99
100         (options, args) = parser.parse_args()
101         if not ((len(args) == 1) or (len(args) == 0)):
102             parser.print_help()
103             sys.exit(1)
104         
105         if len(args) == 1:
106           filename = args[0]
107         else:
108           filename = None
109
110         self.frame = frame
111         self.panel = panel
112         
113         self.contrast = options.contrast
114         self.brightness = options.brightness
115         self.state = "FREQ"
116         self.freq = 0
117
118         # build graph
119
120         self.u=None
121
122         usrp_decim = options.decim # 32
123
124         if not (options.out_filename=="sdl"):
125           options.repeat=False
126
127         if not ((filename is None) or (filename=="usrp")):
128           self.filesource = gr.file_source(gr.sizeof_short,filename,options.repeat) # file is data source
129           self.istoc = gr.interleaved_short_to_complex()
130           self.connect(self.filesource,self.istoc)
131           adc_rate=64e6
132           self.src=self.istoc
133           options.gain=0.0
134           self.gain=0.0
135         else:
136           if options.no_hb or (options.decim<8):
137             self.fpga_filename="std_4rx_0tx.rbf" #contains 4 Rx paths without halfbands and 0 tx paths
138           else:
139             self.fpga_filename="std_2rxhb_2tx.rbf" # contains 2 Rx paths with halfband filters and 2 tx paths (the default)
140           self.u = usrp.source_c(0,fpga_filename=self.fpga_filename)                    # usrp is data source
141           if options.width_8:
142               sample_width = 8
143               sample_shift = 8
144               format = self.u.make_format(sample_width, sample_shift)
145               r = self.u.set_format(format)
146           adc_rate = self.u.adc_rate()                # 64 MS/s
147           self.u.set_decim_rate(usrp_decim)
148           if options.rx_subdev_spec is None:
149             options.rx_subdev_spec = pick_subdevice(self.u)
150           self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec))
151           self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
152           print "Using RX d'board %s" % (self.subdev.side_and_name(),)
153           if options.gain is None:
154             # if no gain was specified, use the mid-point in dB
155             g = self.subdev.gain_range()
156             options.gain = float(g[0]+g[1])/2
157           self.src=self.u
158
159         usrp_rate = adc_rate / usrp_decim           # 320 kS/s
160
161         f2uc=gr.float_to_uchar()
162         # sdl window as final sink
163         if not (options.pal or options.ntsc):
164           options.pal=True #set default to PAL
165         if options.pal:
166           lines_per_frame=625.0
167           frames_per_sec=25.0
168           show_width=768
169         elif options.ntsc:
170           lines_per_frame=525.0
171           frames_per_sec=29.97002997
172           show_width=640
173         width=int(usrp_rate/(lines_per_frame*frames_per_sec))
174         height=int(lines_per_frame)
175
176         if (options.out_filename=="sdl"):
177           #Here comes the tv screen, you have to build and install gr-video-sdl for this (subproject of gnuradio, only in cvs for now)
178           try:
179             video_sink = video_sdl.sink_uc ( frames_per_sec, width, height,0,show_width,height)
180           except:
181             print "gr-video-sdl is not installed"
182             print "realtime \"sdl\" video output window is not available"
183             raise SystemExit, 1
184           self.dst=video_sink
185         else:
186           print "You can use the imagemagick display tool to show the resulting imagesequence"
187           print "use the following line to show the demodulated TV-signal:"
188           print "display -depth 8 -size " +str(width)+ "x" + str(height) + " gray:" + options.out_filename
189           print "(Use the spacebar to advance to next frames)" 
190           options.repeat=False
191           file_sink=gr.file_sink(gr.sizeof_char, options.out_filename)
192           self.dst =file_sink 
193
194         self.agc=gr.agc_cc(1e-7,1.0,1.0) #1e-7
195         self.am_demod = gr.complex_to_mag ()
196         self.set_blacklevel=gr.add_const_ff(0.0)
197         self.invert_and_scale = gr.multiply_const_ff (0.0) #-self.contrast *128.0*255.0/(200.0)
198
199         # now wire it all together
200         #sample_rate=options.width*options.height*options.framerate
201
202         process_type='do_no_sync'
203         if process_type=='do_no_sync':
204           self.connect (self.src, self.agc,self.am_demod,self.invert_and_scale, self.set_blacklevel,f2uc,self.dst)
205         elif process_type=='do_tv_sync_adv':
206           #defaults: gr.tv_sync_adv (double sampling_freq, unsigned int tv_format,bool output_active_video_only=false, bool do_invert=false, double wanted_black_level=0.0, double wanted_white_level=255.0, double avg_alpha=0.1, double initial_gain=1.0, double initial_offset=0.0,bool debug=false)
207           self.tv_sync_adv=gr.tv_sync_adv(usrp_rate,0,False,False,0.0,255.0,0.01,1.0,0.0,False) #note, this block is not yet in cvs
208           self.connect (self.src, self.am_demod,self.invert_and_scale,self.tv_sync_adv,s2f,f2uc,self.dst) 
209         elif process_type=='do_nullsink':
210           #self.connect (self.src, self.am_demod,self.invert_and_scale,f2uc,video_sink)
211           c2r=gr.complex_to_real()
212           nullsink=gr.null_sink(gr.sizeof_float)
213           self.connect (self.src, c2r,nullsink) #video_sink)
214         elif process_type=='do_tv_sync_corr':
215           frame_size=width*height #int(usrp_rate/25.0)
216           nframes=10# 32
217           search_window=20*nframes 
218           debug=False
219           video_alpha=0.3 #0.1
220           corr_alpha=0.3
221           tv_corr=gr.tv_correlator_ff(frame_size,nframes, search_window, video_alpha, corr_alpha,debug) #Note: this block is not yet in cvs
222           shift=gr.add_const_ff(-0.7)
223           self.connect (self.src, self.agc,self.am_demod,tv_corr,self.invert_and_scale, self.set_blacklevel,f2uc,self.dst) #self.agc,
224         else: # process_type=='do_test_image':
225           src_vertical_bars = gr.sig_source_f (usrp_rate, gr.GR_SIN_WAVE, 10.0 *usrp_rate/320, 255,128)
226           self.connect(src_vertical_bars,f2uc,self.dst)
227
228         self._build_gui(vbox, usrp_rate, usrp_rate, usrp_rate)
229
230    
231         if abs(options.freq) < 1e6:
232             options.freq *= 1e6
233
234         # set initial values
235         self.set_gain(options.gain)
236         self.set_contrast(self.contrast)
237         self.set_brightness(options.brightness)
238         if not(self.set_freq(options.freq)):
239             self._set_status_msg("Failed to set initial frequency")
240
241
242     def _set_status_msg(self, msg, which=0):
243         self.frame.GetStatusBar().SetStatusText(msg, which)
244
245
246     def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate):
247
248         def _form_set_freq(kv):
249             return self.set_freq(kv['freq'])
250
251
252         if 0:
253             self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP",
254                                                fft_size=512, sample_rate=usrp_rate)
255             self.connect (self.src, self.src_fft)
256             vbox.Add (self.src_fft.win, 4, wx.EXPAND)
257
258         if 0:
259             post_demod_fft = fftsink.fft_sink_f (self, self.panel, title="Post Demod",
260                                                   fft_size=512, sample_rate=demod_rate,
261                                                   y_per_div=10, ref_level=-40)
262             self.connect (self.am_demod, post_demod_fft)
263             vbox.Add (post_demod_fft.win, 4, wx.EXPAND)
264
265         if 0:
266             post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", 
267                                                 fft_size=512, sample_rate=audio_rate,
268                                                 y_per_div=10, ref_level=-40)
269             self.connect (self.set_blacklevel, post_filt)
270             vbox.Add (fft_win4, 4, wx.EXPAND)
271
272         
273         # control area form at bottom
274         self.myform = myform = form.form()
275
276         if not (self.u is None):
277           hbox = wx.BoxSizer(wx.HORIZONTAL)
278           hbox.Add((5,0), 0)
279           myform['freq'] = form.float_field(
280             parent=self.panel, sizer=hbox, label="Freq", weight=1,
281             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
282
283           hbox.Add((5,0), 0)
284           myform['freq_slider'] = \
285               form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3,
286                                         range=(50.25e6, 900.25e6, 0.25e6),
287                                         callback=self.set_freq)
288           hbox.Add((5,0), 0)
289           vbox.Add(hbox, 0, wx.EXPAND)
290
291         hbox = wx.BoxSizer(wx.HORIZONTAL)
292         hbox.Add((5,0), 0)
293
294         myform['contrast'] = \
295             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Contrast",
296                                         weight=3, range=(-2.0, 2.0, 0.1),
297                                         callback=self.set_contrast)
298         hbox.Add((5,0), 1)
299
300         myform['brightness'] = \
301             form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Brightness",
302                                         weight=3, range=(-255.0, 255.0, 1.0),
303                                         callback=self.set_brightness)
304         hbox.Add((5,0), 0)
305
306         if not (self.u is None):
307           myform['gain'] = \
308               form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain",
309                                         weight=3, range=self.subdev.gain_range(),
310                                         callback=self.set_gain)
311           hbox.Add((5,0), 0)
312         vbox.Add(hbox, 0, wx.EXPAND)
313
314         try:
315             self.knob = powermate.powermate(self.frame)
316             self.rot = 0
317             powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate)
318             powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button)
319         except:
320             print "FYI: No Powermate or Contour Knob found"
321
322
323     def on_rotate (self, event):
324         self.rot += event.delta
325         if (self.state == "FREQ"):
326             if self.rot >= 3:
327                 self.set_freq(self.freq + .1e6)
328                 self.rot -= 3
329             elif self.rot <=-3:
330                 self.set_freq(self.freq - .1e6)
331                 self.rot += 3
332         elif (self.state == "CONTRAST"):
333             step = 0.1
334             if self.rot >= 3:
335                 self.set_contrast(self.contrast + step)
336                 self.rot -= 3
337             elif self.rot <=-3:
338                 self.set_contrast(self.contrast - step)
339                 self.rot += 3
340         else:
341             step = 1
342             if self.rot >= 3:
343                 self.set_brightness(self.brightness + step)
344                 self.rot -= 3
345             elif self.rot <=-3:
346                 self.set_brightness(self.brightness - step)
347                 self.rot += 3
348             
349     def on_button (self, event):
350         if event.value == 0:        # button up
351             return
352         self.rot = 0
353         if self.state == "FREQ":
354             self.state = "CONTRAST"
355         elif self.state == "CONTRAST":
356             self.state = "BRIGHTNESS"
357         else:
358             self.state = "FREQ"
359         self.update_status_bar ()
360         
361
362     def set_contrast (self, contrast):
363         self.contrast = contrast
364         self.invert_and_scale.set_k(-self.contrast *128.0*255.0/(200.0))
365         self.myform['contrast'].set_value(self.contrast)
366         self.update_status_bar ()
367
368     def set_brightness (self, brightness):
369         self.brightness = brightness
370         self.set_blacklevel.set_k(self.brightness +255.0)
371         self.myform['brightness'].set_value(self.brightness)
372         self.update_status_bar ()
373                                         
374     def set_freq(self, target_freq):
375         """
376         Set the center frequency we're interested in.
377
378         @param target_freq: frequency in Hz
379         @rypte: bool
380
381         Tuning is a two step process.  First we ask the front-end to
382         tune as close to the desired frequency as it can.  Then we use
383         the result of that operation and our target_frequency to
384         determine the value for the digital down converter.
385         """
386         if not (self.u is None):
387           r = usrp.tune(self.u, 0, self.subdev, target_freq)
388           if r:
389               self.freq = target_freq
390               self.myform['freq'].set_value(target_freq)         # update displayed value
391               self.myform['freq_slider'].set_value(target_freq)  # update displayed value
392               self.update_status_bar()
393               self._set_status_msg("OK", 0)
394               return True
395
396         self._set_status_msg("Failed", 0)
397         return False
398
399     def set_gain(self, gain):
400         if not (self.u is None):
401           self.gain=gain
402           self.myform['gain'].set_value(gain)     # update displayed value
403           self.subdev.set_gain(gain)
404           self.update_status_bar()
405
406     def update_status_bar (self):
407         msg = "Setting:%s Contrast:%r Brightness:%r Gain: %r" % (self.state, self.contrast,self.brightness,self.gain)
408         self._set_status_msg(msg, 1)
409         #self.src_fft.set_baseband_freq(self.freq)
410
411         
412
413 if __name__ == '__main__':
414     app = stdgui2.stdapp (tv_rx_block, "USRP TV RX black-and-white")
415     app.MainLoop ()