[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] r8816 - gnuradio/branches/features/experimental-gui
From: |
jblum |
Subject: |
[Commit-gnuradio] r8816 - gnuradio/branches/features/experimental-gui |
Date: |
Mon, 7 Jul 2008 15:22:18 -0600 (MDT) |
Author: jblum
Date: 2008-07-07 15:22:11 -0600 (Mon, 07 Jul 2008)
New Revision: 8816
Added:
gnuradio/branches/features/experimental-gui/fft_window.py
gnuradio/branches/features/experimental-gui/wxgui_app.py
Modified:
gnuradio/branches/features/experimental-gui/README
gnuradio/branches/features/experimental-gui/fft_controller.py
gnuradio/branches/features/experimental-gui/usrp_fft.py
Log:
fft + usrp + controller demo
Modified: gnuradio/branches/features/experimental-gui/README
===================================================================
--- gnuradio/branches/features/experimental-gui/README 2008-07-06 22:28:02 UTC
(rev 8815)
+++ gnuradio/branches/features/experimental-gui/README 2008-07-07 21:22:11 UTC
(rev 8816)
@@ -54,6 +54,14 @@
wxgui window for constellation plots
+* wxgui_app.py:
+
+A wx gui app that takes a controller for on_init and on_exit
+
+* fft_window.py:
+
+Test wxopengl fft window that takes a controller
+
STATUS:
Currently, the top block and controller are written, but the GUI is not. There
Modified: gnuradio/branches/features/experimental-gui/fft_controller.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_controller.py
2008-07-06 22:28:02 UTC (rev 8815)
+++ gnuradio/branches/features/experimental-gui/fft_controller.py
2008-07-07 21:22:11 UTC (rev 8816)
@@ -38,7 +38,7 @@
# These could be expanded to member functions to implement more advanced
# application logic, but here lambdas do the trick.
- self.add_listener('decim', lambda x: self._tb.set_decim(x))
+ self.add_listener('decim', self.set_decim)
self.add_listener('gain', lambda x: self._tb.set_gain(x))
self.add_listener('freq', lambda x: self._tb.set_freq(x))
self.add_listener('avg_alpha', lambda x: self._tb.set_avg_alpha(x))
@@ -48,12 +48,21 @@
# like to know about here. Can be member functions or lambdas, and
# would make calls on the top block to retrieve info.
#
- # self.set_provider('foo', lambda: return 'bar')
- # self.set_provider('decim', lambda: return self._tb.decim())
+ # self.set_provider('foo', lambda: 'bar')
+ # self.set_provider('decim', lambda: self._tb.decim())
+ #!!! extreme badness below !!!
+ self.set_provider('sample_rate', lambda: self._tb._u.sample_rate())
+ self.set_provider('average', lambda: self._tb._fft._average)
+ self.set_provider('avg_alpha', lambda: self._tb._fft._avg_alpha)
+
# The controller is a thread
self.setDaemon(1)
self._keep_running = True
+
+ def set_decim(self, decim): #an update in decim will cause the sample_rate
listeners to be called
+ self._tb.set_decim(decim)
+ self['sample_rate'] = self['sample_rate']
def on_init(self):
"""
Copied: gnuradio/branches/features/experimental-gui/fft_window.py (from rev
8815, gnuradio/branches/features/experimental-gui/fftsink.py)
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_window.py
(rev 0)
+++ gnuradio/branches/features/experimental-gui/fft_window.py 2008-07-07
21:22:11 UTC (rev 8816)
@@ -0,0 +1,305 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+##################################################
+# Imports
+##################################################
+import plotter
+import common
+import wx
+import numpy
+import math
+
+##################################################
+# Constants
+##################################################
+SLIDER_STEPS = 100
+AVG_ALPHA_SLIDER_MIN, AVG_ALPHA_SLIDER_MAX = -4, 0
+DEFAULT_FRAME_RATE = 30
+DEFAULT_WIN_SIZE = (640, 240)
+DIV_LEVELS = (1, 2, 5, 10, 20)
+FFT_PLOT_COLOR_SPEC = (0, 0, 1)
+PEAK_VALS_COLOR_SPEC = (0, 1, 0)
+
+##################################################
+# FFT window control panel
+##################################################
+class control_panel(wx.Panel):
+ """!
+ A control panel with wx widgits to control the plotter and fft block
chain.
+ """
+
+ def __init__(self, parent):
+ """!
+ Create a new control panel.
+ @param parent the wx parent window
+ """
+ self.parent = parent
+ wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+ control_box = wx.BoxSizer(wx.VERTICAL)
+
+ #checkboxes for average and peak hold
+ control_box.AddStretchSpacer()
+ control_box.Add(common.LabelText(self, 'Options'), 0,
wx.ALIGN_CENTER)
+ self.average_check_box = wx.CheckBox(parent=self,
style=wx.CHK_2STATE, label="Average")
+ self.average_check_box.Bind(wx.EVT_CHECKBOX, self._on_average)
+ control_box.Add(self.average_check_box, 0, wx.EXPAND)
+ self.peak_hold_check_box = wx.CheckBox(parent=self,
style=wx.CHK_2STATE, label="Peak Hold")
+ self.peak_hold_check_box.Bind(wx.EVT_CHECKBOX,
self._on_peak_hold)
+ control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
+ control_box.AddSpacer(2)
+ self.avg_alpha_label = wx.StaticText(self, -1, '0'*15)
+ control_box.Add(self.avg_alpha_label, 0, wx.EXPAND)
+ self.avg_alpha_slider = wx.Slider(self, -1, 0, 0, SLIDER_STEPS,
style=wx.SL_HORIZONTAL)
+ self.avg_alpha_slider.Bind(wx.EVT_SLIDER, self._on_avg_alpha)
+ control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+
+ #radio buttons for div size
+ control_box.AddStretchSpacer()
+ control_box.Add(common.LabelText(self, 'Set dB/div'), 0,
wx.ALIGN_CENTER)
+ radio_box = wx.BoxSizer(wx.VERTICAL)
+ self.radio_buttons = list()
+ for y_per_div in DIV_LEVELS:
+ radio_button = wx.RadioButton(self, -1, "%d
dB/div"%y_per_div)
+ radio_button.Bind(wx.EVT_RADIOBUTTON,
self._on_radio_button_change)
+ self.radio_buttons.append(radio_button)
+ radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
+ control_box.Add(radio_box, 0, wx.EXPAND)
+
+ #ref lvl buttons
+ control_box.AddStretchSpacer()
+ control_box.Add(common.LabelText(self, 'Adj Ref Lvl'), 0,
wx.ALIGN_CENTER)
+ control_box.AddSpacer(2)
+ self._ref_lvl_buttons = common.IncrDecrButtons(self,
self._on_incr_ref_level, self._on_decr_ref_level)
+ control_box.Add(self._ref_lvl_buttons, 0, wx.ALIGN_CENTER)
+ control_box.AddStretchSpacer()
+
+ #run/stop
+ control_box.AddStretchSpacer()
+ self.run_button = wx.Button(self, -1, '', style=wx.BU_EXACTFIT)
+ self.run_button.Bind(wx.EVT_BUTTON, self._on_run)
+ control_box.Add(self.run_button, 0, wx.EXPAND)
+
+ #set sizer
+ self.SetSizerAndFit(control_box)
+
+ def update(self):
+ """!
+ Read the state of the fft plot settings and update the control
panel.
+ """
+ #update the run/stop button
+ if self.parent.running: self.run_button.SetLabel('Stop')
+ else: self.run_button.SetLabel('Run')
+ #update checkboxes
+ self.average_check_box.SetValue(self.parent.average)
+ self.peak_hold_check_box.SetValue(self.parent.peak_hold)
+ #update avg alpha
+ self.avg_alpha_label.SetLabel('Avg Alpha:
%.3g'%self.parent.avg_alpha)
+ slider_value =
SLIDER_STEPS*(math.log10(self.parent.avg_alpha)-AVG_ALPHA_SLIDER_MIN)/(AVG_ALPHA_SLIDER_MAX-AVG_ALPHA_SLIDER_MIN)
+ if abs(slider_value - self.avg_alpha_slider.GetValue()) > 1:
+ self.avg_alpha_slider.SetValue(slider_value)
+ if self.parent.average:
+ self.avg_alpha_label.Enable()
+ self.avg_alpha_slider.Enable()
+ else:
+ self.avg_alpha_label.Disable()
+ self.avg_alpha_slider.Disable()
+ #update radio buttons
+ try:
+ index = list(DIV_LEVELS).index(self.parent.y_per_div)
+ self.radio_buttons[index].SetValue(True)
+ except: pass
+
+ ##################################################
+ # Event handlers
+ ##################################################
+ def _on_radio_button_change(self, event):
+ selected_radio_button = filter(lambda rb: rb.GetValue(),
self.radio_buttons)[0]
+ index = self.radio_buttons.index(selected_radio_button)
+ self.parent.set_y_per_div(DIV_LEVELS[index])
+ def _on_average(self, event): self.parent.set_average(event.IsChecked())
+ def _on_peak_hold(self, event):
self.parent.set_peak_hold(event.IsChecked())
+ def _on_incr_ref_level(self, event): self.parent.incr_ref_level()
+ def _on_decr_ref_level(self, event): self.parent.decr_ref_level()
+ def _on_avg_alpha(self, event):
+
self.parent.set_avg_alpha(10**(float(AVG_ALPHA_SLIDER_MAX-AVG_ALPHA_SLIDER_MIN)*self.avg_alpha_slider.GetValue()/SLIDER_STEPS
+ AVG_ALPHA_SLIDER_MIN))
+ def _on_run(self, event): self.parent.set_run(not self.parent.running)
+
+##################################################
+# FFT window with plotter and control panel
+##################################################
+class fft_window(wx.Panel):
+ def __init__(
+ self,
+ parent,
+ controller,
+ size,
+ title,
+ real,
+ baseband_freq,
+ sample_rate_key,
+ y_per_div,
+ y_divs,
+ ref_level,
+ average_key,
+ avg_alpha_key,
+ peak_hold,
+ msg_key,
+ ):
+ #ensure y_per_div
+ if y_per_div not in DIV_LEVELS: y_per_div = DIV_LEVELS[0]
+ #setup
+ self.controller = controller
+ self.running = True
+ self.real = real
+ self.baseband_freq = baseband_freq
+ self.sample_rate_key = sample_rate_key
+ self.x_divs = 8.0 #approximate
+ self.y_per_div = y_per_div
+ self.y_divs = y_divs
+ self.ref_level = ref_level
+ self.average_key = average_key
+ self.avg_alpha_key = avg_alpha_key
+ self.peak_hold = peak_hold
+ self.peak_vals = []
+ #init panel and plot
+ wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+ self.plotter = plotter.grid_plotter(self)
+ self.plotter.SetSize(wx.Size(*size))
+ self.plotter.set_title(title)
+ self.plotter.set_y_units('Amplitude (dB)')
+ #setup the box with plot and controls
+ self.control_panel = control_panel(self)
+ main_box = wx.BoxSizer(wx.HORIZONTAL)
+ main_box.Add(self.plotter, 1, wx.EXPAND)
+ main_box.Add(self.control_panel, 0, wx.EXPAND)
+ self.SetSizerAndFit(main_box)
+ #setup controller
+ self.controller.add_listener(msg_key, self.handle_msg)
+ self.controller.add_listener(self.sample_rate_key, lambda x:
self.update())
+ self.controller.add_listener(self.average_key, lambda x:
self.update())
+ self.controller.add_listener(self.avg_alpha_key, lambda x:
self.update())
+ #update
+ self.update()
+
+ def handle_msg(self, msg):
+ """!
+ Handle the message from the fft sink message queue.
+ If complex, reorder the fft samples so the negative bins come
first.
+ If real, keep take only the positive bins.
+ @param msg the fft array as a character array
+ """
+ #convert to floating point numbers
+ samples = numpy.fromstring(msg, numpy.float32)
+ num_samps = len(samples)
+ #reorder fft
+ if self.real: samples = samples[:num_samps/2]
+ else: samples = numpy.concatenate((samples[num_samps/2+1:],
samples[:num_samps/2]))
+ #plot
+ self.plot(samples)
+
+ def plot(self, samples):
+ """!
+ Plot the samples onto the grid as channel 1.
+ If peak hold is enabled, plot peak vals as channel 2.
+ @param samples the fft array
+ """
+ if not self.running: return
+ #peak hold calculation
+ if self.peak_hold:
+ if len(self.peak_vals) != len(samples): self.peak_vals
= samples
+ self.peak_vals = numpy.maximum(samples, self.peak_vals)
+ #plot the fft
+ self.plotter.set_waveform(
+ channel=1,
+ samples=samples,
+ color_spec=FFT_PLOT_COLOR_SPEC,
+ )
+ #plot the peak hold
+ self.plotter.set_waveform(
+ channel=2,
+ samples=self.peak_vals,
+ color_spec=PEAK_VALS_COLOR_SPEC,
+ )
+ #update the plotter
+ self.plotter.update()
+
+ def update(self):
+ self.sample_rate = self.controller[self.sample_rate_key]
+ self.average = self.controller[self.average_key]
+ self.avg_alpha = self.controller[self.avg_alpha_key]
+ #update peak hold
+ if not self.peak_hold: self.peak_vals = []
+ #determine best fitting x_per_div
+ if self.real: x_width = self.sample_rate/2.0
+ else: x_width = self.sample_rate
+ x_per_div = common.get_clean_num(x_width/self.x_divs)
+ exp = common.get_exp(x_per_div)
+ #calculate units and scalar
+ if exp > 7: x_units, scalar = 'GHz', 1e-9
+ elif exp > 4: x_units, scalar = 'MHz', 1e-6
+ elif exp > 1: x_units, scalar = 'KHz', 1e-3
+ else: x_units, scalar = 'Hz', 1e-0
+ #update the x grid
+ if self.real:
+ self.plotter.set_x_grid(
+ scalar*self.baseband_freq,
+ scalar*self.baseband_freq +
scalar*self.sample_rate/2.0,
+ scalar*x_per_div,
+ )
+ else:
+ self.plotter.set_x_grid(
+ scalar*self.baseband_freq -
scalar*self.sample_rate/2.0,
+ scalar*self.baseband_freq +
scalar*self.sample_rate/2.0,
+ scalar*x_per_div,
+ )
+ #update x units
+ self.plotter.set_x_units('Frequency (%s)'%x_units)
+ #update y grid
+
self.plotter.set_y_grid(self.ref_level-self.y_per_div*self.y_divs,
self.ref_level, self.y_per_div)
+ #update control panel and plotter
+ self.control_panel.update()
+ self.plotter.update()
+
+ ##################################################
+ # Set parameters on-the-fly
+ ##################################################
+ def set_baseband_freq(self, baseband_freq):
+ self.baseband_freq = baseband_freq
+ self.update()
+ def set_average(self, average):
+ self.controller[self.average_key] = average
+ def set_avg_alpha(self, avg_alpha):
+ self.controller[self.avg_alpha_key] = avg_alpha
+ def set_peak_hold(self, peak_hold):
+ self.peak_hold = peak_hold
+ self.update()
+ def set_y_per_div(self, y_per_div):
+ self.y_per_div = y_per_div
+ self.update()
+ def set_ref_level(self, ref_level):
+ self.ref_level = ref_level
+ self.update()
+ def incr_ref_level(self): self.set_ref_level(self.ref_level +
self.y_per_div)
+ def decr_ref_level(self): self.set_ref_level(self.ref_level -
self.y_per_div)
+ def set_run(self, running):
+ self.running = running
+ self.update()
Modified: gnuradio/branches/features/experimental-gui/usrp_fft.py
===================================================================
--- gnuradio/branches/features/experimental-gui/usrp_fft.py 2008-07-06
22:28:02 UTC (rev 8815)
+++ gnuradio/branches/features/experimental-gui/usrp_fft.py 2008-07-07
21:22:11 UTC (rev 8816)
@@ -103,8 +103,35 @@
# gui.run()
# END
+
+ from wxgui_app import wxgui_app
+ from fft_window import fft_window
+
+ app = wxgui_app(
+ controller=controller,
+ title='fft demo',
+ )
+
+ win = fft_window(
+ parent=app.GetWin(),
+ controller=controller,
+ size=(600, 300),
+ title='fft window',
+ real=False,
+ baseband_freq=0.0,
+ sample_rate_key='sample_rate',
+ y_per_div=10,
+ y_divs=10,
+ ref_level=0,
+ average_key='average',
+ avg_alpha_key='avg_alpha',
+ peak_hold=True,
+ msg_key='fft',
+ )
+
+ app.Add(win)
+ app.Run()
-
# Temporary until GUI is written
#
# This simply adds a listener that prints a line whenever the FFT
Added: gnuradio/branches/features/experimental-gui/wxgui_app.py
===================================================================
--- gnuradio/branches/features/experimental-gui/wxgui_app.py
(rev 0)
+++ gnuradio/branches/features/experimental-gui/wxgui_app.py 2008-07-07
21:22:11 UTC (rev 8816)
@@ -0,0 +1,88 @@
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import wx
+
+default_gui_size = (200, 100)
+
+class wxgui_app(wx.App):
+ """wx gui app and grid sizer."""
+
+ def __init__(self, controller, title='', size=default_gui_size):
+ """!
+ Initialize the gr top block.
+ Create the wx gui elements.
+ @param controller the control interface
+ @param title the main window title
+ @param size the main window size tuple in pixels
+ """
+ #initialize
+ self._size = size
+ self._controller = controller
+ wx.App.__init__(self)
+ #create gui elements
+ self._wx_frame = wx.Frame(None , -1, title)
+ self._wx_grid = wx.GridBagSizer(1, 1)
+ self._wx_vbox = wx.BoxSizer(wx.VERTICAL)
+
+ def OnExit(self, event):
+ self._wx_frame.Destroy()
+ self.GetController().on_exit()
+ exit(0)
+
+ def Run(self):
+ #setup gui elements at runtime so sizing can be determined
+ self._wx_frame.SetSizeHints(*self._size)
+ self._wx_vbox.Add(self._wx_grid, 0, wx.EXPAND)
+ self._wx_frame.Bind(wx.EVT_CLOSE, self.OnExit)
+ self._wx_frame.SetSizerAndFit(self._wx_vbox)
+ self.SetTopWindow(self._wx_frame)
+ self._wx_frame.Show()
+ self.GetController().on_init()
+ self.MainLoop()
+
+ def GetController(self):
+ return self._controller
+
+ def GetWin(self):
+ """!
+ Get the window for wx elements to fit within.
+ @return the wx frame
+ """
+ return self._wx_frame
+
+ def Add(self, win):
+ """!
+ Add a window to the wx vbox.
+ @param win the wx window
+ """
+ self._wx_vbox.Add(win, 1, wx.EXPAND)
+
+ def GridAdd(self, win, row, col, row_span=1, col_span=1):
+ """!
+ Add a window to the wx grid at the given position.
+ @param win the wx window
+ @param row the row specification (integer >= 0)
+ @param col the column specification (integer >= 0)
+ @param row_span the row span specification (integer >= 1)
+ @param col_span the column span specification (integer >= 1)
+ """
+ self._wx_grid.Add(win, wx.GBPosition(row, col),
wx.GBSpan(row_span, col_span), wx.EXPAND)
+
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Commit-gnuradio] r8816 - gnuradio/branches/features/experimental-gui,
jblum <=