commit-gnuradio
[Top][All Lists]
Advanced

[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)
+





reply via email to

[Prev in Thread] Current Thread [Next in Thread]