Imported Upstream version 3.2.2
[debian/gnuradio] / gr-radio-astronomy / src / python / ra_waterfallsink.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2003,2004,2005,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 2, 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, window
24 from gnuradio.wxgui import stdgui2
25 import wx
26 import gnuradio.wxgui.plot as plot
27 import numpy
28 import os
29 import threading
30 import math    
31
32 default_fftsink_size = (640,240)
33 default_fft_rate = gr.prefs().get_long('wxgui', 'fft_rate', 15)
34
35 def axis_design( x1, x2, nx ):
36     # Given start, end, and number of labels, return value of first label,
37     # increment between labels, number of unlabeled division between labels,
38     # and scale factor.
39
40     dx = abs( x2 - x1 )/float(nx+1)  # allow for space at each end
41     ldx = math.log10(dx)
42     l2 = math.log10(2.)
43     l5 = math.log10(5.)
44     le = math.floor(ldx)
45     lf = ldx - le
46     if lf < l2/2:
47         c = 1
48         dt = 10
49     elif lf < (l2+l5)/2:
50         c = 2
51         dt = 4
52     elif lf < (l5+1)/2:
53         c = 5
54         dt = 5
55     else:
56         c = 1
57         dt = 10
58         le += 1
59     inc = c*pow( 10., le )
60     first = math.ceil( x1/inc )*inc
61     scale = 1.
62     while ( abs(x1*scale) >= 1e5 ) or ( abs(x2*scale) >= 1e5 ):
63         scale *= 1e-3
64     return ( first, inc, dt, scale )
65     
66
67 class waterfall_sink_base(object):
68     def __init__(self, input_is_real=False, baseband_freq=0,
69                  sample_rate=1, fft_size=512,
70                  fft_rate=default_fft_rate,
71                  average=False, avg_alpha=None, title='', ofunc=None, xydfunc=None):
72
73         # initialize common attributes
74         self.baseband_freq = baseband_freq
75         self.sample_rate = sample_rate
76         self.fft_size = fft_size
77         self.fft_rate = fft_rate
78         self.average = average
79         self.ofunc = ofunc
80         self.xydfunc = xydfunc
81         if avg_alpha is None:
82             self.avg_alpha = 2.0 / fft_rate
83         else:
84             self.avg_alpha = avg_alpha
85         self.title = title
86         self.input_is_real = input_is_real
87         self.msgq = gr.msg_queue(2)         # queue up to 2 messages
88
89     def set_average(self, average):
90         self.average = average
91         if average:
92             self.avg.set_taps(self.avg_alpha)
93         else:
94             self.avg.set_taps(1.0)
95
96     def set_avg_alpha(self, avg_alpha):
97         self.avg_alpha = avg_alpha
98
99     def set_baseband_freq(self, baseband_freq):
100         self.baseband_freq = baseband_freq
101
102     def set_sample_rate(self, sample_rate):
103         self.sample_rate = sample_rate
104         self._set_n()
105
106     def _set_n(self):
107         self.one_in_n.set_n(max(1, int(self.sample_rate/self.fft_size/self.fft_rate)))
108         
109 class waterfall_sink_f(gr.hier_block2, waterfall_sink_base):
110     def __init__(self, parent, baseband_freq=0,
111                  ref_level=0, sample_rate=1, fft_size=512,
112                  fft_rate=default_fft_rate, average=False, avg_alpha=None,
113                  title='', size=default_fftsink_size, report=None, span=40, ofunc=None, xydfunc=None):
114
115         gr.hier_block2.__init__(self, "waterfall_sink_f",
116                                 gr.io_signature(1, 1, gr.sizeof_float),
117                                 gr.io_signature(0, 0, 0))
118                                 
119         waterfall_sink_base.__init__(self, input_is_real=True,
120                                      baseband_freq=baseband_freq,
121                                      sample_rate=sample_rate,
122                                      fft_size=fft_size, fft_rate=fft_rate,
123                                      average=average, avg_alpha=avg_alpha,
124                                      title=title)
125                                
126         s2p = gr.serial_to_parallel(gr.sizeof_float, self.fft_size)
127         self.one_in_n = gr.keep_one_in_n(gr.sizeof_float * self.fft_size,
128                                          max(1, int(self.sample_rate/self.fft_size/self.fft_rate)))
129         mywindow = window.blackmanharris(self.fft_size)
130         fft = gr.fft_vfc(self.fft_size, True, mywindow)
131         c2mag = gr.complex_to_mag(self.fft_size)
132         self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
133         log = gr.nlog10_ff(20, self.fft_size, -20*math.log10(self.fft_size))
134         sink = gr.message_sink(gr.sizeof_float * self.fft_size, self.msgq, True)
135
136         self.connect(self, s2p, self.one_in_n, fft, c2mag, self.avg, log, sink)
137         self.win = waterfall_window(self, parent, size=size, report=report,
138                                     ref_level=ref_level, span=span, ofunc=ofunc, xydfunc=xydfunc)
139         self.set_average(self.average)
140
141
142 class waterfall_sink_c(gr.hier_block2, waterfall_sink_base):
143     def __init__(self, parent, baseband_freq=0,
144                  ref_level=0, sample_rate=1, fft_size=512,
145                  fft_rate=default_fft_rate, average=False, avg_alpha=None, 
146                  title='', size=default_fftsink_size, report=None, span=40, ofunc=None, xydfunc=None):
147
148         gr.hier_block2.__init__(self, "waterfall_sink_c",
149                                 gr.io_signature(1, 1, gr.sizeof_gr_complex),
150                                 gr.io_signature(0, 0, 0))
151                                 
152         waterfall_sink_base.__init__(self, input_is_real=False,
153                                      baseband_freq=baseband_freq,
154                                      sample_rate=sample_rate,
155                                      fft_size=fft_size,
156                                      fft_rate=fft_rate,
157                                      average=average, avg_alpha=avg_alpha,
158                                      title=title)
159
160         s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, self.fft_size)
161         self.one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * self.fft_size,
162                                          max(1, int(self.sample_rate/self.fft_size/self.fft_rate)))
163
164         mywindow = window.blackmanharris(self.fft_size)
165         fft = gr.fft_vcc(self.fft_size, True, mywindow)
166         c2mag = gr.complex_to_mag(self.fft_size)
167         self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
168         log = gr.nlog10_ff(20, self.fft_size, -20*math.log10(self.fft_size))
169         sink = gr.message_sink(gr.sizeof_float * self.fft_size, self.msgq, True)
170
171         self.connect(self, s2p, self.one_in_n, fft, c2mag, self.avg, log, sink)
172         self.win = waterfall_window(self, parent, size=size, report=report,
173                                     ref_level=ref_level, span=span, ofunc=ofunc, xydfunc=xydfunc)
174         self.set_average(self.average)
175
176
177 # ------------------------------------------------------------------------
178
179 myDATA_EVENT = wx.NewEventType()
180 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
181
182
183 class DataEvent(wx.PyEvent):
184     def __init__(self, data):
185         wx.PyEvent.__init__(self)
186         self.SetEventType (myDATA_EVENT)
187         self.data = data
188
189     def Clone (self): 
190         self.__class__ (self.GetId())
191
192
193 class input_watcher (threading.Thread):
194     def __init__ (self, msgq, fft_size, event_receiver, **kwds):
195         threading.Thread.__init__ (self, **kwds)
196         self.setDaemon (1)
197         self.msgq = msgq
198         self.fft_size = fft_size
199         self.event_receiver = event_receiver
200         self.keep_running = True
201         self.start ()
202
203     def run (self):
204         while (self.keep_running):
205             msg = self.msgq.delete_head()  # blocking read of message queue
206             itemsize = int(msg.arg1())
207             nitems = int(msg.arg2())
208
209             s = msg.to_string()            # get the body of the msg as a string
210
211             # There may be more than one FFT frame in the message.
212             # If so, we take only the last one
213             if nitems > 1:
214                 start = itemsize * (nitems - 1)
215                 s = s[start:start+itemsize]
216
217             complex_data = numpy.fromstring (s, numpy.float32)
218             de = DataEvent (complex_data)
219             wx.PostEvent (self.event_receiver, de)
220             del de
221     
222
223 class waterfall_window (wx.ScrolledWindow):
224     def __init__ (self, fftsink, parent, id = -1,
225                   pos = wx.DefaultPosition, size = wx.DefaultSize,
226                   style = wx.DEFAULT_FRAME_STYLE, name = "", report=None,
227                   ref_level = 0, span = 50, ofunc=None, xydfunc=None):
228         wx.ScrolledWindow.__init__(self, parent, id, pos, size,
229                                    style|wx.HSCROLL, name)
230         self.parent = parent
231         self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM))
232         self.ref_level = ref_level
233         self.scale_factor = 256./span
234
235         self.ppsh = 128  # pixels per scroll, horizontal
236         self.SetScrollbars( self.ppsh, 0, fftsink.fft_size/self.ppsh, 0 )
237
238         self.fftsink = fftsink
239         self.size = size
240         self.report = report
241         self.ofunc = ofunc
242         self.xydfunc = xydfunc
243
244         dc1 = wx.MemoryDC()
245         dc1.SetFont( wx.SMALL_FONT )
246         self.h_scale = dc1.GetCharHeight() + 3
247         #self.bm_size = ( self.fftsink.fft_size, self.size[1] - self.h_scale )
248         self.im_size = ( self.fftsink.fft_size, self.size[1] - self.h_scale )
249         #self.bm = wx.EmptyBitmap( self.bm_size[0], self.bm_size[1], -1)
250         self.im = wx.EmptyImage( self.im_size[0], self.im_size[1], True )
251         self.im_cur = 0
252
253         self.baseband_freq = None
254
255         self.make_pens()
256
257         wx.EVT_PAINT( self, self.OnPaint )
258         wx.EVT_CLOSE (self, self.on_close_window)
259         #wx.EVT_LEFT_UP(self, self.on_left_up)
260         #wx.EVT_LEFT_DOWN(self, self.on_left_down)
261         EVT_DATA_EVENT (self, self.set_data)
262         
263         self.build_popup_menu()
264         
265         wx.EVT_CLOSE (self, self.on_close_window)
266         self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
267         self.Bind(wx.EVT_MOTION, self.on_motion)
268
269         self.down_pos = None
270
271         self.input_watcher = input_watcher(fftsink.msgq, fftsink.fft_size, self)
272
273     def on_close_window (self, event):
274         self.keep_running = False
275
276     def on_left_down( self, evt ):
277         self.down_pos = evt.GetPosition()
278         self.down_time = evt.GetTimestamp()
279
280     def on_left_up( self, evt ):
281         if self.down_pos:
282             dt = ( evt.GetTimestamp() - self.down_time )/1000.
283             pph = self.fftsink.fft_size/float(self.fftsink.sample_rate)
284             dx =  evt.GetPosition()[0] - self.down_pos[0]
285             if dx != 0:
286                 rt = pph/dx
287             else:
288                 rt = 0
289             t = 'Down time: %f  Delta f: %f  Period: %f' % ( dt, dx/pph, rt )
290             print t
291             if self.report:
292                 self.report(t)
293
294     def on_motion(self, event):
295         if self.xydfunc:
296             pos = event.GetPosition()
297             self.xydfunc(pos)
298
299
300     def const_list(self,const,len):
301         return [const] * len
302
303     def make_colormap(self):
304         r = []
305         r.extend(self.const_list(0,96))
306         r.extend(range(0,255,4))
307         r.extend(self.const_list(255,64))
308         r.extend(range(255,128,-4))
309         
310         g = []
311         g.extend(self.const_list(0,32))
312         g.extend(range(0,255,4))
313         g.extend(self.const_list(255,64))
314         g.extend(range(255,0,-4))
315         g.extend(self.const_list(0,32))
316         
317         b = range(128,255,4)
318         b.extend(self.const_list(255,64))
319         b.extend(range(255,0,-4))
320         b.extend(self.const_list(0,96))
321         return (r,g,b)
322
323     def make_pens(self):
324         (r,g,b) = self.make_colormap()
325         self.rgb = numpy.transpose( numpy.array( (r,g,b) ).astype(numpy.int8) )
326         
327     def OnPaint(self, event):
328         dc = wx.BufferedPaintDC(self)
329         self.DoDrawing( dc )
330
331     def DoDrawing(self,dc):
332         w, h = self.GetClientSizeTuple()
333         w = min( w, self.fftsink.fft_size )
334         if w <= 0:
335             return
336
337         if dc is None:
338             dc = wx.BufferedDC( wx.ClientDC(self), (w,h) )
339
340         dc.SetBackground( wx.Brush( self.GetBackgroundColour(), wx.SOLID ) )
341         dc.Clear()
342
343         x, y = self.GetViewStart()
344         x *= self.ppsh
345
346         ih = min( h - self.h_scale, self.im_size[1] - self.im_cur )
347         r = wx.Rect( x, self.im_cur, w, ih )
348         bm = wx.BitmapFromImage( self.im.GetSubImage(r) )
349         dc.DrawBitmap( bm, 0, self.h_scale )
350         rem = min( self.im_size[1] - ih, h - ih - self.h_scale )
351         if( rem > 0 ):
352             r = wx.Rect( x, 0, w, rem )
353             bm = wx.BitmapFromImage( self.im.GetSubImage(r) )
354             dc.DrawBitmap( bm, 0, ih + self.h_scale )
355         
356         # Draw axis
357         if self.baseband_freq != self.fftsink.baseband_freq:
358             self.baseband_freq = self.fftsink.baseband_freq
359             t = self.fftsink.sample_rate*w/float(self.fftsink.fft_size)
360             self.ax_spec = axis_design( self.baseband_freq - t/2,
361                                         self.baseband_freq + t/2, 7 )
362         dc.SetFont( wx.SMALL_FONT )
363         fo = self.baseband_freq
364         po = self.fftsink.fft_size/2
365         pph = self.fftsink.fft_size/float(self.fftsink.sample_rate)
366         f = math.floor((fo-po/pph)/self.ax_spec[1])*self.ax_spec[1]
367         while True:
368             t = po + ( f - fo )*pph
369             s = str( f*self.ax_spec[3] )
370             e = dc.GetTextExtent( s )
371             if t - e[1]/2 >= x + w:
372                 break
373             dc.DrawText( s, t - x - e[0]/2, 0 )
374             dc.DrawLine( t - x, e[1] - 1, t - x, self.h_scale )
375             dt = self.ax_spec[1]/self.ax_spec[2]*pph
376             for i in range(self.ax_spec[2]-1):
377                 t += dt
378                 if t >= x + w:
379                     break
380                 dc.DrawLine( t - x, e[1] + 1, t - x, self.h_scale )
381             f += self.ax_spec[1]
382
383     def const_list(self,const,len):
384         a = [const]
385         for i in range(1,len):
386             a.append(const)
387         return a
388
389     def make_colormap(self):
390         r = []
391         r.extend(self.const_list(0,96))
392         r.extend(range(0,255,4))
393         r.extend(self.const_list(255,64))
394         r.extend(range(255,128,-4))
395         
396         g = []
397         g.extend(self.const_list(0,32))
398         g.extend(range(0,255,4))
399         g.extend(self.const_list(255,64))
400         g.extend(range(255,0,-4))
401         g.extend(self.const_list(0,32))
402         
403         b = range(128,255,4)
404         b.extend(self.const_list(255,64))
405         b.extend(range(255,0,-4))
406         b.extend(self.const_list(0,96))
407         return (r,g,b)
408
409     def set_data (self, evt):
410         dB = evt.data
411         L = len (dB)
412
413         if self.ofunc != None:
414             self.ofunc(evt.data, L)
415         #dc1 = wx.MemoryDC()
416         #dc1.SelectObject(self.bm)
417
418         # Scroll existing bitmap
419         if 1:
420             #dc1.Blit(0,1,self.bm_size[0],self.bm_size[1]-1,dc1,0,0,
421             #         wx.COPY,False,-1,-1)
422             pass
423         else:
424             for i in range( self.bm_size[1]-1, 0, -1 ):
425                 dc1.Blit( 0, i, self.bm_size[0], 1, dc1, 0, i-1 )
426
427         x = max(abs(self.fftsink.sample_rate), abs(self.fftsink.baseband_freq))
428         if x >= 1e9:
429             sf = 1e-9
430             units = "GHz"
431         elif x >= 1e6:
432             sf = 1e-6
433             units = "MHz"
434         else:
435             sf = 1e-3
436             units = "kHz"
437
438
439         if self.fftsink.input_is_real:     # only plot 1/2 the points
440             d_max = L/2
441             p_width = 2
442         else:
443             d_max = L/2
444             p_width = 1
445
446         scale_factor = self.scale_factor
447         dB -= self.ref_level
448         dB *= scale_factor
449         dB = dB.astype(numpy.int_).clip( min=0, max=255 )
450         if self.fftsink.input_is_real:     # real fft
451             dB = numpy.array( ( dB[0:d_max][::-1], dB[0:d_max] ) )
452         else:                               # complex fft
453             dB = numpy.concatenate( ( dB[d_max:L], dB[0:d_max] ) )
454
455         dB = self.rgb[dB]
456         img = wx.ImageFromData( L, 1, dB.ravel().tostring() )
457         #bm = wx.BitmapFromImage( img )
458         #dc1.DrawBitmap( bm, 0, 0 )
459         ibuf = self.im.GetDataBuffer()
460         self.im_cur -= 1
461         if self.im_cur < 0:
462             self.im_cur = self.im_size[1] - 1
463         start = 3*self.im_cur*self.im_size[0]
464         ibuf[start:start+3*self.im_size[0]] = img.GetData()
465
466         #del dc1
467         self.DoDrawing(None)
468
469     def on_average(self, evt):
470         # print "on_average"
471         self.fftsink.set_average(evt.IsChecked())
472
473     def on_right_click(self, event):
474         menu = self.popup_menu
475         self.PopupMenu(menu, event.GetPosition())
476
477
478     def build_popup_menu(self):
479         id_ref_gain = wx.NewId()
480         self.Bind( wx.EVT_MENU, self.on_ref_gain, id=id_ref_gain )
481
482         # make a menu
483         menu = wx.Menu()
484         self.popup_menu = menu
485         menu.Append( id_ref_gain, "Ref Level and Gain" )
486         self.rg_dialog = None
487
488         self.checkmarks = {
489             #self.id_average : lambda : self.fftsink.average
490             }
491
492     def on_ref_gain( self, evt ):
493         if self.rg_dialog == None:
494             self.rg_dialog = rg_dialog( self.parent, self.set_ref_gain,
495                                         ref=self.ref_level,
496                                         span=256./self.scale_factor )
497         self.rg_dialog.Show( True )
498
499     def set_ref_gain( self, ref, span ):
500         self.ref_level = ref
501         self.scale_factor = 256/span
502
503 class rg_dialog( wx.Dialog ):
504     def __init__( self, parent, set_function, ref=0, span=256./5. ):
505         wx.Dialog.__init__( self, parent, -1, "Waterfall Settings" )
506         self.set_function = set_function
507         #status_bar = wx.StatusBar( self, -1 )
508
509         d_sizer = wx.BoxSizer( wx.VERTICAL )  # dialog sizer
510         f_sizer = wx.BoxSizer( wx.VERTICAL )  # form sizer
511         vs = 10
512
513         #f_sizer.Add( fn_sizer, 0, flag=wx.TOP, border=10 )
514
515         h_sizer = wx.BoxSizer( wx.HORIZONTAL )
516         self.ref = tab_item( self, "Ref Level:", 4, "dB" )
517         self.ref.ctrl.SetValue( "%d" % ref )
518         h_sizer.Add((0,0),1)
519         h_sizer.Add( self.ref, 0 )
520         h_sizer.Add((0,0),1)
521         self.span = tab_item( self, "Range:", 4, "dB" )
522         self.span.ctrl.SetValue( "%d" % span )
523         h_sizer.Add( self.span, 0 )
524         h_sizer.Add((0,0),1)
525         f_sizer.Add( h_sizer, 0, flag=wx.TOP|wx.EXPAND, border=vs )
526
527         d_sizer.Add((0,0),1)
528         d_sizer.Add( f_sizer, 0, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND )
529         d_sizer.Add((0,0),1)
530         d_sizer.Add((0,0),1)
531
532         button_sizer = wx.BoxSizer( wx.HORIZONTAL )
533         apply_button = wx.Button( self, -1, "Apply" )
534         apply_button.Bind( wx.EVT_BUTTON, self.apply_evt )
535         cancel_button = wx.Button( self, -1, "Cancel" )
536         cancel_button.Bind( wx.EVT_BUTTON, self.cancel_evt )
537         ok_button = wx.Button( self, -1, "OK" )
538         ok_button.Bind( wx.EVT_BUTTON, self.ok_evt )
539         button_sizer.Add((0,0),1)
540         button_sizer.Add( apply_button, 0,
541                           flag=wx.ALIGN_CENTER_HORIZONTAL )
542         button_sizer.Add((0,0),1)
543         button_sizer.Add( cancel_button, 0,
544                           flag=wx.ALIGN_CENTER_HORIZONTAL )
545         button_sizer.Add((0,0),1)
546         button_sizer.Add( ok_button, 0,
547                           flag=wx.ALIGN_CENTER_HORIZONTAL )
548         button_sizer.Add((0,0),1)
549         d_sizer.Add( button_sizer, 0,
550                      flag=wx.EXPAND|wx.ALIGN_CENTER|wx.BOTTOM, border=30 )
551         self.SetSizer( d_sizer )
552
553     def apply_evt( self, evt ):
554         self.do_apply()
555
556     def cancel_evt( self, evt ):
557         self.Show( False )
558
559     def ok_evt( self, evt ):
560         self.do_apply()
561         self.Show( False )
562
563     def do_apply( self ):
564         r = float( self.ref.ctrl.GetValue() )
565         g = float( self.span.ctrl.GetValue() )
566         self.set_function( r, g )
567
568 def next_up(v, seq):
569     """
570     Return the first item in seq that is > v.
571     """
572     for s in seq:
573         if s > v:
574             return s
575     return v
576
577 def next_down(v, seq):
578     """
579     Return the last item in seq that is < v.
580     """
581     rseq = list(seq[:])
582     rseq.reverse()
583
584     for s in rseq:
585         if s < v:
586             return s
587     return v
588
589 # One of many copies that should be consolidated . . .
590 def tab_item( parent, label, chars, units, style=wx.TE_RIGHT, value="" ):
591     s = wx.BoxSizer( wx.HORIZONTAL )
592     s.Add( wx.StaticText( parent, -1, label ), 0,
593            flag=wx.ALIGN_CENTER_VERTICAL )
594     s.ctrl = wx.TextCtrl( parent, -1, style=style, value=value )
595     s.ctrl.SetMinSize( ( (1.00+chars)*s.ctrl.GetCharWidth(),
596                                  1.25*s.ctrl.GetCharHeight() ) )
597     s.Add( s.ctrl, -1, flag=wx.LEFT, border=3 )
598     s.Add( wx.StaticText( parent, -1, units ), 0,
599            flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=1 )
600     return s
601
602
603 # ----------------------------------------------------------------
604 # Standalone test app
605 # ----------------------------------------------------------------
606
607 class test_app_flow_graph (stdgui2.std_top_block):
608     def __init__(self, frame, panel, vbox, argv):
609         stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
610
611         fft_size = 512
612
613         # build our flow graph
614         input_rate = 20.000e3
615
616         # Generate a complex sinusoid
617         src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 5.75e3, 1000)
618         #src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000)
619
620         # We add these throttle blocks so that this demo doesn't
621         # suck down all the CPU available.  Normally you wouldn't use these.
622         thr1 = gr.throttle(gr.sizeof_gr_complex, input_rate)
623
624         sink1 = waterfall_sink_c (panel, title="Complex Data",
625                                   fft_size=fft_size,
626                                   sample_rate=input_rate, baseband_freq=0,
627                                   size=(600,144) )
628         vbox.Add (sink1.win, 1, wx.EXPAND)
629         self.connect (src1, thr1, sink1)
630
631         # generate a real sinusoid
632         src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 5.75e3, 1000)
633         #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000)
634         thr2 = gr.throttle(gr.sizeof_float, input_rate)
635         sink2 = waterfall_sink_f (panel, title="Real Data", fft_size=fft_size,
636                                   sample_rate=input_rate, baseband_freq=0)
637         vbox.Add (sink2.win, 1, wx.EXPAND)
638         self.connect (src2, thr2, sink2)
639
640 def main ():
641     app = stdgui2.stdapp (test_app_flow_graph,
642                          "Waterfall Sink Test App")
643     app.MainLoop ()
644
645 if __name__ == '__main__':
646     main ()