3 # Copyright 2003,2004,2005,2006,2007,2009,2010 Free Software Foundation, Inc.
5 # This file is part of GNU Radio
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)
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.
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.
23 from gnuradio import gr, gru, window
24 from gnuradio.wxgui import stdgui2
30 DIV_LEVELS = (1, 2, 5, 10, 20)
32 default_fftsink_size = (640,240)
33 default_fft_rate = gr.prefs().get_long('wxgui', 'fft_rate', 15)
35 class fft_sink_base(object):
36 def __init__(self, input_is_real=False, baseband_freq=0, y_per_div=10,
37 y_divs=8, ref_level=50,
38 sample_rate=1, fft_size=512,
39 fft_rate=default_fft_rate,
40 average=False, avg_alpha=None, title='', peak_hold=False,use_persistence=False,persist_alpha=0.2):
42 # initialize common attributes
43 self.baseband_freq = baseband_freq
44 self.y_per_div=y_per_div
46 self.ref_level = ref_level
47 self.sample_rate = sample_rate
48 self.fft_size = fft_size
49 self.fft_rate = fft_rate
50 self.average = average
52 self.avg_alpha = 2.0 / fft_rate
54 self.avg_alpha = avg_alpha
55 self.use_persistence = use_persistence
56 self.persist_alpha = persist_alpha
59 self.peak_hold = peak_hold
60 self.input_is_real = input_is_real
61 self.msgq = gr.msg_queue(2) # queue that holds a maximum of 2 messages
63 def set_y_per_div(self, y_per_div):
64 self.y_per_div = y_per_div
66 def set_ref_level(self, ref_level):
67 self.ref_level = ref_level
69 def set_average(self, average):
70 self.average = average
72 self.avg.set_taps(self.avg_alpha)
74 self.avg.set_taps(1.0)
75 self.win.peak_vals = None
77 def set_peak_hold(self, enable):
78 self.peak_hold = enable
79 self.win.set_peak_hold(enable)
81 def set_use_persistence(self, enable):
82 self.use_persistence = enable
83 self.win.set_use_persistence(enable)
85 def set_persist_alpha(self, persist_alpha):
86 self.persist_alpha = persist_alpha
87 self.win.set_persist_alpha(persist_alpha)
89 def set_avg_alpha(self, avg_alpha):
90 self.avg_alpha = avg_alpha
92 def set_baseband_freq(self, baseband_freq):
93 self.baseband_freq = baseband_freq
95 def set_sample_rate(self, sample_rate):
96 self.sample_rate = sample_rate
100 self.one_in_n.set_n(max(1, int(self.sample_rate/self.fft_size/self.fft_rate)))
103 class fft_sink_f(gr.hier_block2, fft_sink_base):
104 def __init__(self, parent, baseband_freq=0, ref_scale=2.0,
105 y_per_div=10, y_divs=8, ref_level=50, sample_rate=1, fft_size=512,
106 fft_rate=default_fft_rate, average=False, avg_alpha=None,
107 title='', size=default_fftsink_size, peak_hold=False, use_persistence=False,persist_alpha=0.2, **kwargs):
109 gr.hier_block2.__init__(self, "fft_sink_f",
110 gr.io_signature(1, 1, gr.sizeof_float),
111 gr.io_signature(0,0,0))
113 fft_sink_base.__init__(self, input_is_real=True, baseband_freq=baseband_freq,
114 y_per_div=y_per_div, y_divs=y_divs, ref_level=ref_level,
115 sample_rate=sample_rate, fft_size=fft_size,
117 average=average, avg_alpha=avg_alpha, title=title,
118 peak_hold=peak_hold,use_persistence=use_persistence,persist_alpha=persist_alpha)
120 self.s2p = gr.stream_to_vector(gr.sizeof_float, self.fft_size)
121 self.one_in_n = gr.keep_one_in_n(gr.sizeof_float * self.fft_size,
122 max(1, int(self.sample_rate/self.fft_size/self.fft_rate)))
124 mywindow = window.blackmanharris(self.fft_size)
125 self.fft = gr.fft_vfc(self.fft_size, True, mywindow)
130 self.c2mag = gr.complex_to_mag(self.fft_size)
131 self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
133 # FIXME We need to add 3dB to all bins but the DC bin
134 self.log = gr.nlog10_ff(20, self.fft_size,
135 -10*math.log10(self.fft_size) # Adjust for number of bins
136 -10*math.log10(power/self.fft_size) # Adjust for windowing loss
137 -20*math.log10(ref_scale/2)) # Adjust for reference scale
139 self.sink = gr.message_sink(gr.sizeof_float * self.fft_size, self.msgq, True)
140 self.connect(self, self.s2p, self.one_in_n, self.fft, self.c2mag, self.avg, self.log, self.sink)
142 self.win = fft_window(self, parent, size=size)
143 self.set_average(self.average)
144 self.set_peak_hold(self.peak_hold)
145 self.set_use_persistence(self.use_persistence)
146 self.set_persist_alpha(self.persist_alpha)
148 class fft_sink_c(gr.hier_block2, fft_sink_base):
149 def __init__(self, parent, baseband_freq=0, ref_scale=2.0,
150 y_per_div=10, y_divs=8, ref_level=50, sample_rate=1, fft_size=512,
151 fft_rate=default_fft_rate, average=False, avg_alpha=None,
152 title='', size=default_fftsink_size, peak_hold=False, use_persistence=False,persist_alpha=0.2, **kwargs):
154 gr.hier_block2.__init__(self, "fft_sink_c",
155 gr.io_signature(1, 1, gr.sizeof_gr_complex),
156 gr.io_signature(0,0,0))
158 fft_sink_base.__init__(self, input_is_real=False, baseband_freq=baseband_freq,
159 y_per_div=y_per_div, y_divs=y_divs, ref_level=ref_level,
160 sample_rate=sample_rate, fft_size=fft_size,
162 average=average, avg_alpha=avg_alpha, title=title,
163 peak_hold=peak_hold, use_persistence=use_persistence,persist_alpha=persist_alpha)
165 self.s2p = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size)
166 self.one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * self.fft_size,
167 max(1, int(self.sample_rate/self.fft_size/self.fft_rate)))
169 mywindow = window.blackmanharris(self.fft_size)
170 self.fft = gr.fft_vcc(self.fft_size, True, mywindow)
175 self.c2mag = gr.complex_to_mag(self.fft_size)
176 self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size)
178 # FIXME We need to add 3dB to all bins but the DC bin
179 self.log = gr.nlog10_ff(20, self.fft_size,
180 -10*math.log10(self.fft_size) # Adjust for number of bins
181 -10*math.log10(power/self.fft_size) # Adjust for windowing loss
182 -20*math.log10(ref_scale/2)) # Adjust for reference scale
184 self.sink = gr.message_sink(gr.sizeof_float * self.fft_size, self.msgq, True)
185 self.connect(self, self.s2p, self.one_in_n, self.fft, self.c2mag, self.avg, self.log, self.sink)
187 self.win = fft_window(self, parent, size=size)
188 self.set_average(self.average)
189 self.set_use_persistence(self.use_persistence)
190 self.set_persist_alpha(self.persist_alpha)
191 self.set_peak_hold(self.peak_hold)
194 # ------------------------------------------------------------------------
196 myDATA_EVENT = wx.NewEventType()
197 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
200 class DataEvent(wx.PyEvent):
201 def __init__(self, data):
202 wx.PyEvent.__init__(self)
203 self.SetEventType (myDATA_EVENT)
207 self.__class__ (self.GetId())
210 class input_watcher (gru.msgq_runner):
211 def __init__ (self, msgq, fft_size, event_receiver, **kwds):
212 self.fft_size = fft_size
213 self.event_receiver = event_receiver
214 gru.msgq_runner.__init__(self, msgq, self.handle_msg)
216 def handle_msg(self, msg):
217 itemsize = int(msg.arg1())
218 nitems = int(msg.arg2())
220 s = msg.to_string() # get the body of the msg as a string
222 # There may be more than one FFT frame in the message.
223 # If so, we take only the last one
225 start = itemsize * (nitems - 1)
226 s = s[start:start+itemsize]
228 complex_data = numpy.fromstring (s, numpy.float32)
229 de = DataEvent (complex_data)
230 wx.PostEvent (self.event_receiver, de)
233 class control_panel(wx.Panel):
235 class LabelText(wx.StaticText):
236 def __init__(self, window, label):
237 wx.StaticText.__init__(self, window, -1, label)
238 font = self.GetFont()
239 font.SetWeight(wx.FONTWEIGHT_BOLD)
240 font.SetUnderlined(True)
243 def __init__(self, parent):
245 wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
246 control_box = wx.BoxSizer(wx.VERTICAL)
248 #checkboxes for average and peak hold
249 control_box.AddStretchSpacer()
250 control_box.Add(self.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER)
251 self.average_check_box = wx.CheckBox(parent=self, style=wx.CHK_2STATE, label="Average")
252 self.average_check_box.Bind(wx.EVT_CHECKBOX, parent.on_average)
253 control_box.Add(self.average_check_box, 0, wx.EXPAND)
254 self.use_persistence_check_box = wx.CheckBox(parent=self, style=wx.CHK_2STATE, label="Persistence")
255 self.use_persistence_check_box.Bind(wx.EVT_CHECKBOX, parent.on_use_persistence)
256 control_box.Add(self.use_persistence_check_box, 0, wx.EXPAND)
257 self.peak_hold_check_box = wx.CheckBox(parent=self, style=wx.CHK_2STATE, label="Peak Hold")
258 self.peak_hold_check_box.Bind(wx.EVT_CHECKBOX, parent.on_peak_hold)
259 control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
261 #radio buttons for div size
262 control_box.AddStretchSpacer()
263 control_box.Add(self.LabelText(self, 'Set dB/div'), 0, wx.ALIGN_CENTER)
264 radio_box = wx.BoxSizer(wx.VERTICAL)
265 self.radio_buttons = list()
266 for y_per_div in DIV_LEVELS:
267 radio_button = wx.RadioButton(self, -1, "%d dB/div"%y_per_div)
268 radio_button.Bind(wx.EVT_RADIOBUTTON, self.on_radio_button_change)
269 self.radio_buttons.append(radio_button)
270 radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
271 control_box.Add(radio_box, 0, wx.EXPAND)
274 control_box.AddStretchSpacer()
275 control_box.Add(self.LabelText(self, 'Adj Ref Lvl'), 0, wx.ALIGN_CENTER)
276 control_box.AddSpacer(2)
277 button_box = wx.BoxSizer(wx.HORIZONTAL)
278 self.ref_plus_button = wx.Button(self, -1, '+', style=wx.BU_EXACTFIT)
279 self.ref_plus_button.Bind(wx.EVT_BUTTON, parent.on_incr_ref_level)
280 button_box.Add(self.ref_plus_button, 0, wx.ALIGN_CENTER)
281 self.ref_minus_button = wx.Button(self, -1, ' - ', style=wx.BU_EXACTFIT)
282 self.ref_minus_button.Bind(wx.EVT_BUTTON, parent.on_decr_ref_level)
283 button_box.Add(self.ref_minus_button, 0, wx.ALIGN_CENTER)
284 control_box.Add(button_box, 0, wx.ALIGN_CENTER)
285 control_box.AddStretchSpacer()
287 self.SetSizerAndFit(control_box)
293 Read the state of the fft plot settings and update the control panel.
296 self.average_check_box.SetValue(self.parent.fftsink.average)
297 self.use_persistence_check_box.SetValue(self.parent.fftsink.use_persistence)
298 self.peak_hold_check_box.SetValue(self.parent.fftsink.peak_hold)
299 #update radio buttons
301 index = list(DIV_LEVELS).index(self.parent.fftsink.y_per_div)
302 self.radio_buttons[index].SetValue(True)
305 def on_radio_button_change(self, evt):
306 selected_radio_button = filter(lambda rb: rb.GetValue(), self.radio_buttons)[0]
307 index = self.radio_buttons.index(selected_radio_button)
308 self.parent.fftsink.set_y_per_div(DIV_LEVELS[index])
310 class fft_window (wx.Panel):
311 def __init__ (self, fftsink, parent, id = -1,
312 pos = wx.DefaultPosition, size = wx.DefaultSize,
313 style = wx.DEFAULT_FRAME_STYLE, name = ""):
315 self.fftsink = fftsink
317 wx.Panel.__init__(self, parent, -1)
318 self.plot = plot.PlotCanvas(self, id, pos, size, style, name)
319 #setup the box with plot and controls
320 self.control_panel = control_panel(self)
321 main_box = wx.BoxSizer (wx.HORIZONTAL)
322 main_box.Add (self.plot, 1, wx.EXPAND)
323 main_box.Add (self.control_panel, 0, wx.EXPAND)
324 self.SetSizerAndFit(main_box)
326 self.peak_hold = False
327 self.peak_vals = None
329 self.use_persistence=False
330 self.persist_alpha=0.2
333 self.plot.SetEnableGrid (True)
334 # self.SetEnableZoom (True)
335 # self.SetBackgroundColour ('black')
337 self.build_popup_menu()
338 self.set_baseband_freq(self.fftsink.baseband_freq)
340 EVT_DATA_EVENT (self, self.set_data)
341 wx.EVT_CLOSE (self, self.on_close_window)
342 self.plot.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
343 self.plot.Bind(wx.EVT_MOTION, self.evt_motion)
345 self.input_watcher = input_watcher(fftsink.msgq, fftsink.fft_size, self)
347 def set_scale(self, freq):
348 x = max(abs(self.fftsink.sample_rate), abs(self.fftsink.baseband_freq))
350 self._scale_factor = 1e-9
352 self._format = "%3.6f"
354 self._scale_factor = 1e-6
356 self._format = "%3.3f"
358 self._scale_factor = 1e-3
360 self._format = "%3.3f"
362 def set_baseband_freq(self, baseband_freq):
364 self.peak_vals = None
365 self.set_scale(baseband_freq)
366 self.fftsink.set_baseband_freq(baseband_freq)
368 def on_close_window (self, event):
369 print "fft_window:on_close_window"
370 self.keep_running = False
373 def set_data (self, evt):
378 if self.peak_vals is None:
381 self.peak_vals = numpy.maximum(dB, self.peak_vals)
383 if self.fftsink.input_is_real: # only plot 1/2 the points
384 x_vals = ((numpy.arange (L/2) * (self.fftsink.sample_rate
385 * self._scale_factor / L))
386 + self.fftsink.baseband_freq * self._scale_factor)
387 self._points = numpy.zeros((len(x_vals), 2), numpy.float64)
388 self._points[:,0] = x_vals
389 self._points[:,1] = dB[0:L/2]
391 self._peak_points = numpy.zeros((len(x_vals), 2), numpy.float64)
392 self._peak_points[:,0] = x_vals
393 self._peak_points[:,1] = self.peak_vals[0:L/2]
395 # the "negative freqs" are in the second half of the array
396 x_vals = ((numpy.arange (-L/2, L/2)
397 * (self.fftsink.sample_rate * self._scale_factor / L))
398 + self.fftsink.baseband_freq * self._scale_factor)
399 self._points = numpy.zeros((len(x_vals), 2), numpy.float64)
400 self._points[:,0] = x_vals
401 self._points[:,1] = numpy.concatenate ((dB[L/2:], dB[0:L/2]))
403 self._peak_points = numpy.zeros((len(x_vals), 2), numpy.float64)
404 self._peak_points[:,0] = x_vals
405 self._peak_points[:,1] = numpy.concatenate ((self.peak_vals[L/2:], self.peak_vals[0:L/2]))
407 lines = [plot.PolyLine (self._points, colour='BLUE'),]
409 lines.append(plot.PolyLine (self._peak_points, colour='GREEN'))
411 graphics = plot.PlotGraphics (lines,
412 title=self.fftsink.title,
413 xLabel = self._units, yLabel = "dB")
414 x_range = x_vals[0], x_vals[-1]
415 ymax = self.fftsink.ref_level
416 ymin = self.fftsink.ref_level - self.fftsink.y_per_div * self.fftsink.y_divs
418 self.plot.Draw (graphics, xAxis=x_range, yAxis=y_range, step=self.fftsink.y_per_div)
420 def set_use_persistence(self, enable):
421 self.use_persistence = enable
422 self.plot.set_use_persistence( enable)
424 def set_persist_alpha(self, persist_alpha):
425 self.persist_alpha = persist_alpha
426 self.plot.set_persist_alpha(persist_alpha)
428 def set_peak_hold(self, enable):
429 self.peak_hold = enable
430 self.peak_vals = None
432 def on_average(self, evt):
434 self.fftsink.set_average(evt.IsChecked())
435 self.control_panel.update()
437 def on_use_persistence(self, evt):
439 self.fftsink.set_use_persistence(evt.IsChecked())
440 self.control_panel.update()
442 def on_peak_hold(self, evt):
443 # print "on_peak_hold"
444 self.fftsink.set_peak_hold(evt.IsChecked())
445 self.control_panel.update()
447 def on_incr_ref_level(self, evt):
448 # print "on_incr_ref_level"
449 self.fftsink.set_ref_level(self.fftsink.ref_level
450 + self.fftsink.y_per_div)
452 def on_decr_ref_level(self, evt):
453 # print "on_decr_ref_level"
454 self.fftsink.set_ref_level(self.fftsink.ref_level
455 - self.fftsink.y_per_div)
457 def on_incr_y_per_div(self, evt):
458 # print "on_incr_y_per_div"
459 self.fftsink.set_y_per_div(next_up(self.fftsink.y_per_div, DIV_LEVELS))
460 self.control_panel.update()
462 def on_decr_y_per_div(self, evt):
463 # print "on_decr_y_per_div"
464 self.fftsink.set_y_per_div(next_down(self.fftsink.y_per_div, DIV_LEVELS))
465 self.control_panel.update()
467 def on_y_per_div(self, evt):
468 # print "on_y_per_div"
470 if Id == self.id_y_per_div_1:
471 self.fftsink.set_y_per_div(1)
472 elif Id == self.id_y_per_div_2:
473 self.fftsink.set_y_per_div(2)
474 elif Id == self.id_y_per_div_5:
475 self.fftsink.set_y_per_div(5)
476 elif Id == self.id_y_per_div_10:
477 self.fftsink.set_y_per_div(10)
478 elif Id == self.id_y_per_div_20:
479 self.fftsink.set_y_per_div(20)
480 self.control_panel.update()
482 def on_right_click(self, event):
483 menu = self.popup_menu
484 for id, pred in self.checkmarks.items():
485 item = menu.FindItemById(id)
487 self.plot.PopupMenu(menu, event.GetPosition())
489 def evt_motion(self, event):
490 if not hasattr(self, "_points"):
491 return # Got here before first window data update
493 # Clip to plotted values
494 (ux, uy) = self.plot.GetXY(event) # Scaled position
495 x_vals = numpy.array(self._points[:,0])
496 if ux < x_vals[0] or ux > x_vals[-1]:
497 tip = self.GetToolTip()
502 # Get nearest X value (is there a better way)?
503 ind = numpy.argmin(numpy.abs(x_vals-ux))
505 db_val = self._points[ind, 1]
506 text = (self._format+" %s dB=%3.3f") % (x_val, self._units, db_val)
508 # Display the tooltip
509 tip = wx.ToolTip(text)
514 def build_popup_menu(self):
515 self.id_incr_ref_level = wx.NewId()
516 self.id_decr_ref_level = wx.NewId()
517 self.id_incr_y_per_div = wx.NewId()
518 self.id_decr_y_per_div = wx.NewId()
519 self.id_y_per_div_1 = wx.NewId()
520 self.id_y_per_div_2 = wx.NewId()
521 self.id_y_per_div_5 = wx.NewId()
522 self.id_y_per_div_10 = wx.NewId()
523 self.id_y_per_div_20 = wx.NewId()
524 self.id_average = wx.NewId()
525 self.id_use_persistence = wx.NewId()
526 self.id_peak_hold = wx.NewId()
528 self.plot.Bind(wx.EVT_MENU, self.on_average, id=self.id_average)
529 self.plot.Bind(wx.EVT_MENU, self.on_use_persistence, id=self.id_use_persistence)
530 self.plot.Bind(wx.EVT_MENU, self.on_peak_hold, id=self.id_peak_hold)
531 self.plot.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level)
532 self.plot.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level)
533 self.plot.Bind(wx.EVT_MENU, self.on_incr_y_per_div, id=self.id_incr_y_per_div)
534 self.plot.Bind(wx.EVT_MENU, self.on_decr_y_per_div, id=self.id_decr_y_per_div)
535 self.plot.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1)
536 self.plot.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2)
537 self.plot.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5)
538 self.plot.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10)
539 self.plot.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20)
543 self.popup_menu = menu
544 menu.AppendCheckItem(self.id_average, "Average")
545 menu.AppendCheckItem(self.id_use_persistence, "Persistence")
546 menu.AppendCheckItem(self.id_peak_hold, "Peak Hold")
547 menu.Append(self.id_incr_ref_level, "Incr Ref Level")
548 menu.Append(self.id_decr_ref_level, "Decr Ref Level")
549 # menu.Append(self.id_incr_y_per_div, "Incr dB/div")
550 # menu.Append(self.id_decr_y_per_div, "Decr dB/div")
551 menu.AppendSeparator()
552 # we'd use RadioItems for these, but they're not supported on Mac
553 menu.AppendCheckItem(self.id_y_per_div_1, "1 dB/div")
554 menu.AppendCheckItem(self.id_y_per_div_2, "2 dB/div")
555 menu.AppendCheckItem(self.id_y_per_div_5, "5 dB/div")
556 menu.AppendCheckItem(self.id_y_per_div_10, "10 dB/div")
557 menu.AppendCheckItem(self.id_y_per_div_20, "20 dB/div")
560 self.id_average : lambda : self.fftsink.average,
561 self.id_use_persistence : lambda : self.fftsink.use_persistence,
562 self.id_peak_hold : lambda : self.fftsink.peak_hold,
563 self.id_y_per_div_1 : lambda : self.fftsink.y_per_div == 1,
564 self.id_y_per_div_2 : lambda : self.fftsink.y_per_div == 2,
565 self.id_y_per_div_5 : lambda : self.fftsink.y_per_div == 5,
566 self.id_y_per_div_10 : lambda : self.fftsink.y_per_div == 10,
567 self.id_y_per_div_20 : lambda : self.fftsink.y_per_div == 20,
573 Return the first item in seq that is > v.
580 def next_down(v, seq):
582 Return the last item in seq that is < v.
593 # ----------------------------------------------------------------
594 # Standalone test app
595 # ----------------------------------------------------------------
597 class test_app_block (stdgui2.std_top_block):
598 def __init__(self, frame, panel, vbox, argv):
599 stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
603 # build our flow graph
604 input_rate = 100*20.48e3
606 # Generate a complex sinusoid
607 #src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 100*2e3, 1)
608 src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 100*5.75e3, 1)
610 # We add these throttle blocks so that this demo doesn't
611 # suck down all the CPU available. Normally you wouldn't use these.
612 thr1 = gr.throttle(gr.sizeof_gr_complex, input_rate)
614 sink1 = fft_sink_c (panel, title="Complex Data", fft_size=fft_size,
615 sample_rate=input_rate, baseband_freq=100e3,
616 ref_level=0, y_per_div=20, y_divs=10)
617 vbox.Add (sink1.win, 1, wx.EXPAND)
619 self.connect(src1, thr1, sink1)
621 #src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 100*2e3, 1)
622 src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 100*5.75e3, 1)
623 thr2 = gr.throttle(gr.sizeof_float, input_rate)
624 sink2 = fft_sink_f (panel, title="Real Data", fft_size=fft_size*2,
625 sample_rate=input_rate, baseband_freq=100e3,
626 ref_level=0, y_per_div=20, y_divs=10)
627 vbox.Add (sink2.win, 1, wx.EXPAND)
629 self.connect(src2, thr2, sink2)
632 app = stdgui2.stdapp (test_app_block, "FFT Sink Test App")
635 if __name__ == '__main__':