commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] r9243 - in gnuradio/branches/developers/jblum/glwxgui:


From: jblum
Subject: [Commit-gnuradio] r9243 - in gnuradio/branches/developers/jblum/glwxgui: config gr-wxgui/src/python gr-wxgui/src/python/plotter
Date: Mon, 11 Aug 2008 18:06:06 -0600 (MDT)

Author: jblum
Date: 2008-08-11 18:06:06 -0600 (Mon, 11 Aug 2008)
New Revision: 9243

Added:
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/common.py
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/constants.py
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fft_window.py
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fftsink_gl.py
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter/
   
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter/Makefile.am
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/pubsub.py
Modified:
   gnuradio/branches/developers/jblum/glwxgui/config/grc_gr_wxgui.m4
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/__init__.py
Log:
imported gl plotter and fftsink

Modified: gnuradio/branches/developers/jblum/glwxgui/config/grc_gr_wxgui.m4
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/config/grc_gr_wxgui.m4   
2008-08-11 23:30:04 UTC (rev 9242)
+++ gnuradio/branches/developers/jblum/glwxgui/config/grc_gr_wxgui.m4   
2008-08-12 00:06:06 UTC (rev 9243)
@@ -41,6 +41,7 @@
         gr-wxgui/gr-wxgui.pc \
         gr-wxgui/src/Makefile \
         gr-wxgui/src/python/Makefile \
+       gr-wxgui/src/python/plotter/Makefile \
     ])
 
     GRC_BUILD_CONDITIONAL(gr-wxgui)

Modified: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am  
2008-08-11 23:30:04 UTC (rev 9242)
+++ gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am  
2008-08-12 00:06:06 UTC (rev 9243)
@@ -21,6 +21,8 @@
 
 include $(top_srcdir)/Makefile.common
 
+SUBDIRS = plotter
+
 # Install this stuff so that it ends up as the gnuradio.wxgui module
 # This usually ends up at:
 #   ${prefix}/lib/python${python_version}/site-packages/gnuradio/wxgui
@@ -30,10 +32,15 @@
 
 ourpython_PYTHON =                     \
        __init__.py                     \
+       common.py                       \
+       constants.py                    \
        form.py                         \
        fftsink2.py                     \
+       fftsink_gl.py                   \
+       fft_window.py                   \
        plot.py                         \
        powermate.py                    \
+       pubsub.py                       \
        scopesink2.py                   \
        waterfallsink2.py               \
        slider.py                       \

Modified: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/__init__.py
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/__init__.py  
2008-08-11 23:30:04 UTC (rev 9242)
+++ gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/__init__.py  
2008-08-12 00:06:06 UTC (rev 9243)
@@ -1 +1,2 @@
-# make this directory a package
+import plotter
+

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/common.py (from 
rev 9242, gnuradio/branches/features/experimental-gui/common.py)
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/common.py    
                        (rev 0)
+++ gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/common.py    
2008-08-12 00:06:06 UTC (rev 9243)
@@ -0,0 +1,256 @@
+#
+# 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 threading
+import numpy
+import math
+import wx
+
+class prop_setter(object):
+       def _register_set_prop(self, controller, control_key, init=None):
+               def set_method(value): controller[control_key] = value
+               if init is not None: set_method(init)
+               setattr(self, 'set_%s'%control_key, set_method)
+
+##################################################
+# Input Watcher Thread
+##################################################
+class input_watcher(threading.Thread):
+       """!
+       Input watcher thread runs forever.
+       Read messages from the message queue.
+       Forward messages to the message handler.
+       """
+       def __init__ (self, msgq, handle_msg):
+               threading.Thread.__init__(self)
+               self.setDaemon(1)
+               self.msgq = msgq
+               self._handle_msg = handle_msg
+               self.keep_running = True
+               self.start()
+
+       def run(self):
+               while self.keep_running: 
self._handle_msg(self.msgq.delete_head().to_string())
+
+##################################################
+# WX Shared Classes
+##################################################
+class LabelText(wx.StaticText):
+       """!
+       Label text to give the wx plots a uniform look.
+       Get the default label text and set the font bold.
+       """
+       def __init__(self, parent, label):
+               wx.StaticText.__init__(self, parent, -1, label)
+               font = self.GetFont()
+               font.SetWeight(wx.FONTWEIGHT_BOLD)
+               self.SetFont(font)
+
+class IncrDecrButtons(wx.BoxSizer):
+       """!
+       A horizontal box sizer with a increment and a decrement button.
+       """
+       def __init__(self, parent, on_incr, on_decr):
+               """!
+               @param parent the parent window
+               @param on_incr the event handler for increment
+               @param on_decr the event handler for decrement
+               """
+               wx.BoxSizer.__init__(self, wx.HORIZONTAL)
+               self._incr_button = wx.Button(parent, -1, '+', 
style=wx.BU_EXACTFIT)
+               self._incr_button.Bind(wx.EVT_BUTTON, on_incr)
+               self.Add(self._incr_button, 0, wx.ALIGN_CENTER_VERTICAL)
+               self._decr_button = wx.Button(parent, -1, ' - ', 
style=wx.BU_EXACTFIT)
+               self._decr_button.Bind(wx.EVT_BUTTON, on_decr)
+               self.Add(self._decr_button, 0, wx.ALIGN_CENTER_VERTICAL)
+
+       def Disable(self, disable=True): self.Enable(not disable)
+       def Enable(self, enable=True):
+               if enable:
+                       self._incr_button.Enable()
+                       self._decr_button.Enable()
+               else:
+                       self._incr_button.Disable()
+                       self._decr_button.Disable()
+
+class RunStopButtonController(wx.Button):
+       def __init__(self, parent, controller, control_key):
+               self._controller = controller
+               self._control_key = control_key
+               wx.Button.__init__(self, parent, -1, '', style=wx.BU_EXACTFIT)
+               self.Bind(wx.EVT_BUTTON, self._evt_button)
+               controller.subscribe(control_key, lambda x: self.SetLabel(x and 
'Stop' or 'Run'))
+
+       def _evt_button(self, e):
+               self._controller[self._control_key] = not 
self._controller[self._control_key]
+
+class CheckBoxController(wx.CheckBox):
+       def __init__(self, parent, label, controller, control_key):
+               self._controller = controller
+               self._control_key = control_key
+               wx.CheckBox.__init__(self, parent, style=wx.CHK_2STATE, 
label=label)
+               self.Bind(wx.EVT_CHECKBOX, self._evt_checkbox)
+               controller.subscribe(control_key, lambda x: 
self.SetValue(bool(x)))
+
+       def _evt_checkbox(self, e):
+               self._controller[self._control_key] = bool(e.IsChecked())
+
+class LogSliderController(wx.BoxSizer):
+       """!
+       Log slider controller with display label and slider.
+       Gives logarithmic scaling to slider operation.
+       """
+       def __init__(self, parent, label, min_exp, max_exp, slider_steps, 
controller, control_key, formatter=lambda x: ': %.6f'%x):
+               wx.BoxSizer.__init__(self, wx.VERTICAL)
+               self._label = wx.StaticText(parent, -1, label + 
formatter(1/3.0))
+               self.Add(self._label, 0, wx.EXPAND)
+               self._slider = wx.Slider(parent, -1, 0, 0, slider_steps, 
style=wx.SL_HORIZONTAL)
+               self.Add(self._slider, 0, wx.EXPAND)
+               def _on_slider_event(event):
+                       controller[control_key] = \
+                       
10**(float(max_exp-min_exp)*self._slider.GetValue()/slider_steps + min_exp)
+               self._slider.Bind(wx.EVT_SLIDER, _on_slider_event)
+               def _on_controller_set(value):
+                       self._label.SetLabel(label + formatter(value))
+                       slider_value = 
slider_steps*(math.log10(value)-min_exp)/(max_exp-min_exp)
+                       slider_value = min(max(0, slider_value), slider_steps)
+                       if abs(slider_value - self._slider.GetValue()) > 1:
+                               self._slider.SetValue(slider_value)
+               controller.subscribe(control_key, _on_controller_set)
+
+       def Disable(self, disable=True): self.Enable(not disable)
+       def Enable(self, enable=True):
+               if enable:
+                       self._slider.Enable()
+                       self._label.Enable()
+               else:
+                       self._slider.Disable()
+                       self._label.Disable()
+
+class DropDownController(wx.BoxSizer):
+       """!
+       Drop down controller with label and chooser.
+       Srop down selection from a set of choices.
+       """
+       def __init__(self, parent, label, choices, controller, control_key):
+               """!
+               @param parent the parent window
+               @param label the label for the drop down
+               @param choices a list of tuples -> (label, value)
+               @param controller the prop val controller
+               @param control_key the prop key for this control
+               """
+               wx.BoxSizer.__init__(self, wx.HORIZONTAL)
+               self._label = wx.StaticText(parent, -1, ' %s '%label)
+               self.Add(self._label, 1, wx.ALIGN_CENTER_VERTICAL)
+               self._chooser = wx.Choice(parent, -1, choices=[c[0] for c in 
choices])
+               def _on_chooser_event(event):
+                       controller[control_key] = 
choices[self._chooser.GetSelection()][1]
+               self._chooser.Bind(wx.EVT_CHOICE, _on_chooser_event)
+               self.Add(self._chooser, 0, wx.ALIGN_CENTER_VERTICAL)
+               def _on_controller_set(value):
+                       #only set the chooser if the value is a possible choice
+                       for i, choice in enumerate(choices):
+                               if value == choice[1]: 
self._chooser.SetSelection(i)
+               controller.subscribe(control_key, _on_controller_set)
+
+       def Disable(self, disable=True): self.Enable(not disable)
+       def Enable(self, enable=True):
+               if enable:
+                       self._chooser.Enable()
+                       self._label.Enable()
+               else:
+                       self._chooser.Disable()
+                       self._label.Disable()
+
+##################################################
+# Shared Functions
+##################################################
+def get_exp(num):
+       """!
+       Get the exponent of the number in base 10.
+       @param num the floating point number
+       @return the exponent as an integer
+       """
+       if num == 0: return 0
+       return int(math.floor(math.log10(abs(num))))
+
+def get_clean_num(num):
+       """!
+       Get the closest clean number match to num with bases 1, 2, 5.
+       @param num the number
+       @return the closest number
+       """
+       if num == 0: return 0
+       if num > 0: sign = 1
+       else: sign = -1
+       exp = get_exp(num)
+       nums = numpy.array((1, 2, 5, 10))*(10**exp)
+       return sign*nums[numpy.argmin(numpy.abs(nums - abs(num)))]
+
+def get_clean_incr(num):
+       """!
+       Get the next higher clean number with bases 1, 2, 5.
+       @param num the number
+       @return the next higher number
+       """
+       num = get_clean_num(num)
+       exp = get_exp(num)
+       base = int(round(num/10**exp))
+       return {
+               -5: -2,
+               -2: -1,
+               -1: -.5,
+               1: 2,
+               2: 5,
+               5: 10,
+       }[base]*(10**exp)
+
+def get_clean_decr(num):
+       """!
+       Get the next lower clean number with bases 1, 2, 5.
+       @param num the number
+       @return the next lower number
+       """
+       num = get_clean_num(num)
+       exp = get_exp(num)
+       base = int(round(num/10**exp))
+       return {
+               -5: -10,
+               -2: -5,
+               -1: -2,
+               1: .5,
+               2: 1,
+               5: 2,
+       }[base]*(10**exp)
+
+def get_min_max(samples):
+       """!
+       Get the minimum and maximum bounds for an array of samples.
+       @param samples the array of real values
+       @return a tuple of min, max
+       """
+       scale_factor = 3
+       mean = numpy.average(samples)
+       rms = scale_factor*((numpy.sum((samples-mean)**2)/len(samples))**.5)
+       min = mean - rms
+       max = mean + rms
+       return min, max

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/constants.py 
(from rev 9242, gnuradio/branches/features/experimental-gui/constants.py)
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/constants.py 
                        (rev 0)
+++ gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/constants.py 
2008-08-12 00:06:06 UTC (rev 9243)
@@ -0,0 +1,59 @@
+#
+# 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.
+#
+
+##################################################
+# Controller Keys
+##################################################
+AC_COUPLE_KEY = 'ac_couple'
+ALPHA_KEY = 'alpha'
+AUTORANGE_KEY = 'autorange'
+AVERAGE_KEY = 'average'
+AVG_ALPHA_KEY = 'avg_alpha'
+BASEBAND_FREQ_KEY = 'baseband_freq'
+BETA_KEY = 'beta'
+COLOR_MODE_KEY = 'color_mode'
+DECIMATION_KEY = 'decimation'
+DYNAMIC_RANGE_KEY = 'dynamic_range'
+FRAME_RATE_KEY = 'frame_rate'
+GAIN_MU_KEY = 'gain_mu'
+GAIN_OMEGA_KEY = 'gain_omega'
+MARKER_KEY = 'marker'
+MSG_KEY = 'msg'
+NUM_LINES_KEY = 'num_lines'
+OMEGA_KEY = 'omega'
+PEAK_HOLD_KEY = 'peak_hold'
+REF_LEVEL_KEY = 'ref_level'
+RUNNING_KEY = 'running'
+SAMPLE_RATE_KEY = 'sample_rate'
+SCOPE_NUM_SAMPLES_KEY = 'scope_num_samples'
+SCOPE_TRIGGER_CHANNEL_KEY = 'scope_trigger_channel'
+SCOPE_TRIGGER_LEVEL_KEY = 'scope_trigger_level'
+SCOPE_TRIGGER_MODE_KEY = 'scope_trigger_mode'
+TRIGGER_CHANNEL_KEY = 'trigger_channel'
+TRIGGER_LEVEL_KEY = 'trigger_level'
+TRIGGER_MODE_KEY = 'trigger_mode'
+X_DIVS_KEY = 'x_divs'
+X_OFF_KEY = 'x_off'
+X_PER_DIV_KEY = 'x_per_div'
+Y_DIVS_KEY = 'y_divs'
+Y_OFF_KEY = 'y_off'
+Y_PER_DIV_KEY = 'y_per_div'
+

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fft_window.py 
(from rev 9242, gnuradio/branches/features/experimental-gui/fft_window.py)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fft_window.py    
                            (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fft_window.py    
    2008-08-12 00:06:06 UTC (rev 9243)
@@ -0,0 +1,268 @@
+#
+# 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
+import pubsub
+from constants import *
+
+##################################################
+# Constants
+##################################################
+SLIDER_STEPS = 100
+AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
+DEFAULT_WIN_SIZE = (600, 300)
+DIV_LEVELS = (1, 2, 5, 10, 20)
+FFT_PLOT_COLOR_SPEC = (0, 0, 1)
+PEAK_VALS_COLOR_SPEC = (0, 1, 0)
+NO_PEAK_VALS = list()
+
+##################################################
+# 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 = common.CheckBoxController(self, 
'Average', parent.ext_controller, parent.average_key)
+               control_box.Add(self.average_check_box, 0, wx.EXPAND)
+               self.peak_hold_check_box = common.CheckBoxController(self, 
'Peak Hold', parent, PEAK_HOLD_KEY)
+               control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
+               control_box.AddSpacer(2)
+               self.avg_alpha_slider = common.LogSliderController(
+                       self, 'Avg Alpha',
+                       AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
+                       parent.ext_controller, parent.avg_alpha_key,
+               )
+               parent.ext_controller.subscribe(parent.average_key, 
self.avg_alpha_slider.Enable)
+               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_y_per_div)
+                       self.radio_buttons.append(radio_button)
+                       radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
+               parent.subscribe(Y_PER_DIV_KEY, self._on_set_y_per_div)
+               control_box.Add(radio_box, 0, wx.EXPAND)
+               #ref lvl buttons
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Set Ref Level'), 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)
+               #run/stop
+               control_box.AddStretchSpacer()
+               self.run_button = common.RunStopButtonController(self, parent, 
RUNNING_KEY)
+               control_box.Add(self.run_button, 0, wx.EXPAND)
+               #set sizer
+               self.SetSizerAndFit(control_box)
+
+       ##################################################
+       # Event handlers
+       ##################################################
+       def _on_set_y_per_div(self, y_per_div):
+               try:
+                       index = list(DIV_LEVELS).index(y_per_div)
+                       self.radio_buttons[index].SetValue(True)
+               except: pass
+       def _on_y_per_div(self, event):
+               selected_radio_button = filter(lambda rb: rb.GetValue(), 
self.radio_buttons)[0]
+               index = self.radio_buttons.index(selected_radio_button)
+               self.parent[Y_PER_DIV_KEY] = DIV_LEVELS[index]
+       def _on_incr_ref_level(self, event):
+               self.parent.set_ref_level(
+                       self.parent[REF_LEVEL_KEY] + self.parent[Y_PER_DIV_KEY])
+       def _on_decr_ref_level(self, event):
+               self.parent.set_ref_level(
+                       self.parent[REF_LEVEL_KEY] - self.parent[Y_PER_DIV_KEY])
+
+##################################################
+# FFT window with plotter and control panel
+##################################################
+class fft_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+       def __init__(
+               self,
+               parent,
+               controller,
+               size,
+               title,
+               real,
+               fft_size,
+               baseband_freq,
+               sample_rate_key,
+               y_per_div,
+               y_divs,
+               ref_level,
+               average_key,
+               avg_alpha_key,
+               peak_hold,
+               msg_key,
+       ):
+               pubsub.pubsub.__init__(self)
+               #ensure y_per_div
+               if y_per_div not in DIV_LEVELS: y_per_div = DIV_LEVELS[0]
+               #setup
+               self.ext_controller = controller
+               self.real = real
+               self.fft_size = fft_size
+               self.sample_rate_key = sample_rate_key
+               self.average_key = average_key
+               self.avg_alpha_key = avg_alpha_key
+               self.peak_vals = NO_PEAK_VALS
+               #init panel and plot
+               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               self.plotter = plotter.channel_plotter(self)
+               self.plotter.SetSize(wx.Size(*size))
+               self.plotter.set_title(title)
+               self.plotter.enable_point_label(True)
+               #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)
+               #initial setup
+               self.ext_controller[self.average_key] = 
self.ext_controller[self.average_key]
+               self.ext_controller[self.avg_alpha_key] = 
self.ext_controller[self.avg_alpha_key]
+               self._register_set_prop(self, PEAK_HOLD_KEY, peak_hold)
+               self._register_set_prop(self, Y_PER_DIV_KEY, y_per_div)
+               self._register_set_prop(self, Y_DIVS_KEY, y_divs)
+               self._register_set_prop(self, X_DIVS_KEY, 8) #approximate
+               self._register_set_prop(self, REF_LEVEL_KEY, ref_level)
+               self._register_set_prop(self, BASEBAND_FREQ_KEY, baseband_freq)
+               self._register_set_prop(self, RUNNING_KEY, True)
+               #register events
+               self.subscribe(PEAK_HOLD_KEY, self.plotter.enable_legend)
+               self.ext_controller.subscribe(msg_key, self.handle_msg)
+               self.ext_controller.subscribe(self.sample_rate_key, 
self.update_grid)
+               for key in (
+                       BASEBAND_FREQ_KEY,
+                       Y_PER_DIV_KEY, X_DIVS_KEY,
+                       Y_DIVS_KEY, REF_LEVEL_KEY,
+               ): self.subscribe(key, self.update_grid)
+               #initial update
+               self.plotter.enable_legend(self[PEAK_HOLD_KEY])
+               self.update_grid()
+
+       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.
+               Plot the samples onto the grid as channel 1.
+               If peak hold is enabled, plot peak vals as channel 2.
+               @param msg the fft array as a character array
+               """
+               if not self[RUNNING_KEY]: return
+               #convert to floating point numbers
+               samples = numpy.fromstring(msg, numpy.float32)[:self.fft_size] 
#only take first frame
+               num_samps = len(samples)
+               #reorder fft
+               if self.real: samples = samples[:num_samps/2]
+               else: samples = numpy.concatenate((samples[num_samps/2:], 
samples[:num_samps/2]))
+               #peak hold calculation
+               if self[PEAK_HOLD_KEY]:
+                       if len(self.peak_vals) != len(samples): self.peak_vals 
= samples
+                       self.peak_vals = numpy.maximum(samples, self.peak_vals)
+               else: self.peak_vals = NO_PEAK_VALS
+               #plot the fft
+               self.plotter.set_waveform(
+                       channel='FFT',
+                       samples=samples,
+                       color_spec=FFT_PLOT_COLOR_SPEC,
+               )
+               #plot the peak hold
+               self.plotter.set_waveform(
+                       channel='Peak',
+                       samples=self.peak_vals,
+                       color_spec=PEAK_VALS_COLOR_SPEC,
+               )
+               #update the plotter
+               self.plotter.update()
+
+       def update_grid(self, *args):
+               """!
+               Update the plotter grid.
+               This update method is dependent on the variables below.
+               Determine the x and y axis grid parameters.
+               The x axis depends on sample rate, baseband freq, and x divs.
+               The y axis depends on y per div, y divs, and ref level.
+               """
+               #grid parameters
+               sample_rate = self.ext_controller[self.sample_rate_key]
+               baseband_freq = self[BASEBAND_FREQ_KEY]
+               y_per_div = self[Y_PER_DIV_KEY]
+               y_divs = self[Y_DIVS_KEY]
+               x_divs = self[X_DIVS_KEY]
+               ref_level = self[REF_LEVEL_KEY]
+               #determine best fitting x_per_div
+               if self.real: x_width = sample_rate/2.0
+               else: x_width = sample_rate/1.0
+               x_per_div = common.get_clean_num(x_width/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*baseband_freq,
+                               scalar*baseband_freq + scalar*sample_rate/2.0,
+                               scalar*x_per_div,
+                       )
+               else:
+                       self.plotter.set_x_grid(
+                               scalar*baseband_freq - scalar*sample_rate/2.0,
+                               scalar*baseband_freq + scalar*sample_rate/2.0,
+                               scalar*x_per_div,
+                       )
+               #update x units
+               self.plotter.set_x_label('Frequency', x_units)
+               #update y grid
+               self.plotter.set_y_grid(ref_level-y_per_div*y_divs, ref_level, 
y_per_div)
+               #update y units
+               self.plotter.set_y_label('Amplitude', 'dB')
+               #update plotter
+               self.plotter.update()

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fftsink_gl.py 
(from rev 9242, gnuradio/branches/features/experimental-gui/fftsink.py)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fftsink_gl.py    
                            (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/fftsink_gl.py    
    2008-08-12 00:06:06 UTC (rev 9243)
@@ -0,0 +1,124 @@
+#
+# 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 fft_window
+import common
+from gnuradio import gr, blks2
+from pubsub import pubsub
+from constants import *
+
+##################################################
+# FFT sink block (wrapper for old wxgui)
+##################################################
+class _fft_sink_base(gr.hier_block2, common.prop_setter):
+       """!
+       An fft block with real/complex inputs and a gui window.
+       """
+
+       def __init__(
+               self,
+               parent,
+               baseband_freq=0,
+               ref_scale=2.0,
+               y_per_div=10,
+               y_divs=8,
+               ref_level=50,
+               sample_rate=1,
+               fft_size=512,
+               fft_rate=gr.prefs().get_long('wxgui', 'fft_rate', 30),
+               average=False,
+               avg_alpha=None,
+               title='',
+               size=fft_window.DEFAULT_WIN_SIZE,
+               peak_hold=False,
+       ):
+               #ensure avg alpha
+               if avg_alpha is None: avg_alpha = 2.0/fft_rate
+               #init
+               gr.hier_block2.__init__(
+                       self,
+                       "fft_sink",
+                       gr.io_signature(1, 1, self._item_size),
+                       gr.io_signature(0, 0, 0),
+               )
+               #blocks
+               copy = gr.kludge_copy(self._item_size)
+               fft = self._fft_chain(
+                       sample_rate=sample_rate,
+                       fft_size=fft_size,
+                       frame_rate=fft_rate,
+                       ref_scale=ref_scale,
+                       avg_alpha=avg_alpha,
+                       average=average,
+               )
+               msgq = gr.msg_queue(2)
+               sink = gr.message_sink(gr.sizeof_float*fft_size, msgq, True)
+               #connect
+               self.connect(self, copy, fft, sink)
+               #controller
+               self.controller = pubsub()
+               self.controller.subscribe(AVERAGE_KEY, fft.set_average)
+               self.controller.publish(AVERAGE_KEY, fft.average)
+               self.controller.subscribe(AVG_ALPHA_KEY, fft.set_avg_alpha)
+               self.controller.publish(AVG_ALPHA_KEY, fft.avg_alpha)
+               self.controller.subscribe(SAMPLE_RATE_KEY, fft.set_sample_rate)
+               self.controller.publish(SAMPLE_RATE_KEY, fft.sample_rate)
+               #start input watcher
+               def setter(p, k, x): # lambdas can't have assignments :(
+                   p[k] = x
+               common.input_watcher(msgq, lambda x: setter(self.controller, 
MSG_KEY, x))
+               #create window
+               self.win = fft_window.fft_window(
+                       parent=parent,
+                       controller=self.controller,
+                       size=size,
+                       title=title,
+                       real=self._real,
+                       fft_size=fft_size,
+                       baseband_freq=baseband_freq,
+                       sample_rate_key=SAMPLE_RATE_KEY,
+                       y_per_div=y_per_div,
+                       y_divs=y_divs,
+                       ref_level=ref_level,
+                       average_key=AVERAGE_KEY,
+                       avg_alpha_key=AVG_ALPHA_KEY,
+                       peak_hold=peak_hold,
+                       msg_key=MSG_KEY,
+               )
+               #register callbacks from window for external use
+               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
+                       setattr(self, attr, getattr(self.win, attr))
+               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
+               self._register_set_prop(self.controller, AVERAGE_KEY)
+               self._register_set_prop(self.controller, AVG_ALPHA_KEY)
+
+class fft_sink_f(_fft_sink_base):
+       _fft_chain = blks2.logpwrfft_f
+       _item_size = gr.sizeof_float
+       _real = True
+
+class fft_sink_c(_fft_sink_base):
+       _fft_chain = blks2.logpwrfft_c
+       _item_size = gr.sizeof_gr_complex
+       _real = False

Copied: gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter 
(from rev 9242, gnuradio/branches/features/experimental-gui/plotter)


Property changes on: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter
___________________________________________________________________
Name: svn:ignore
   + Makefile
Makefile.in
*.pyc


Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter/Makefile.am
 (from rev 9241, 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter/Makefile.am
                          (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/plotter/Makefile.am
  2008-08-12 00:06:06 UTC (rev 9243)
@@ -0,0 +1,37 @@
+#
+# Copyright 2004,2005,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.
+# 
+
+include $(top_srcdir)/Makefile.common
+
+# Install this stuff so that it ends up as the gnuradio.wxgui module
+# This usually ends up at:
+#   ${prefix}/lib/python${python_version}/site-packages/gnuradio/wxgui
+
+ourpythondir = $(grpythondir)/wxgui/plotter
+ourlibdir    = $(grpyexecdir)/wxgui/plotter
+
+ourpython_PYTHON =                     \
+       __init__.py                     \
+       channel_plotter.py              \
+       gltext.py                       \
+       plotter_base.py                 \
+       waterfall_plotter.py
+

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/pubsub.py (from 
rev 9242, gnuradio/branches/features/experimental-gui/pubsub.py)
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/pubsub.py    
                        (rev 0)
+++ gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/pubsub.py    
2008-08-12 00:06:06 UTC (rev 9243)
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+"""!
+Abstract GNU Radio publisher/subscriber interface
+
+This is a proof of concept implementation, will likely change significantly.
+"""
+
+class pubsub(dict):
+    def __init__(self):
+       self._publishers = { }
+       self._subscribers = { }
+       self._proxies = { }
+       
+    def __missing__(self, key, value=None):
+       dict.__setitem__(self, key, value)
+       self._publishers[key] = None
+       self._subscribers[key] = []
+       self._proxies[key] = None
+       
+    def __setitem__(self, key, val):
+       if not self.has_key(key): 
+           self.__missing__(key, val)
+       elif self._proxies[key] is not None:
+           (p, newkey) = self._proxies[key]
+           p[newkey] = val
+       else:
+           dict.__setitem__(self, key, val)
+       for sub in self._subscribers[key]:
+           # Note this means subscribers will get called in the thread
+           # context of the 'set' caller.
+           sub(val)
+
+    def __getitem__(self, key):
+       if not self.has_key(key): self.__missing__(key)
+       if self._proxies[key] is not None:
+           (p, newkey) = self._proxies[key]
+           return p[newkey]
+       elif self._publishers[key] is not None:
+           return self._publishers[key]()
+       else:
+           return dict.__getitem__(self, key)
+
+    def publish(self, key, publisher):
+       if not self.has_key(key): self.__missing__(key)
+        if self._proxies[key] is not None:
+            (p, newkey) = self._proxies[key]
+            p.publish(newkey, publisher)
+        else:
+            self._publishers[key] = publisher
+       
+    def subscribe(self, key, subscriber):
+       if not self.has_key(key): self.__missing__(key)
+        if self._proxies[key] is not None:
+            (p, newkey) = self._proxies[key]
+            p.subscribe(newkey, subscriber)
+        else:
+            self._subscribers[key].append(subscriber)
+       
+    def unpublish(self, key):
+        if self._proxies[key] is not None:
+            (p, newkey) = self._proxies[key]
+            p.unpublish(newkey)
+        else:
+            self._publishers[key] = None
+       
+    def unsubscribe(self, key, subscriber):
+        if self._proxies[key] is not None:
+            (p, newkey) = self._proxies[key]
+            p.unsubscribe(newkey, subscriber)
+        else:
+            self._subscribers[key].remove(subscriber)
+
+    def proxy(self, key, p, newkey=None):
+       if not self.has_key(key): self.__missing__(key)
+       if newkey is None: newkey = key
+       self._proxies[key] = (p, newkey)        
+
+    def unproxy(self, key):
+        self._proxies[key] = None
+
+# Test code
+if __name__ == "__main__":
+    import sys
+    o = pubsub()
+
+    # Non-existent key gets auto-created with None value
+    print "Auto-created key 'foo' value:", o['foo']
+
+    # Add some subscribers
+    # First is a bare function
+    def print_len(x):
+       print "len=%i" % (len(x), )
+    o.subscribe('foo', print_len)
+
+    # The second is a class member function
+    class subber(object):
+       def __init__(self, param):
+           self._param = param
+       def printer(self, x):
+           print self._param, `x`
+    s = subber('param')
+    o.subscribe('foo', s.printer)
+
+    # The third is a lambda function
+    o.subscribe('foo', lambda x: sys.stdout.write('val='+`x`+'\n'))
+
+    # Update key 'foo', will notify subscribers    
+    print "Updating 'foo' with three subscribers:"
+    o['foo'] = 'bar';
+
+    # Remove first subscriber
+    o.unsubscribe('foo', print_len)
+
+    # Update now will only trigger second and third subscriber
+    print "Updating 'foo' after removing a subscriber:"
+    o['foo'] = 'bar2';
+    
+    # Publish a key as a function, in this case, a lambda function
+    o.publish('baz', lambda : 42)
+    print "Published value of 'baz':", o['baz']
+
+    # Unpublish the key
+    o.unpublish('baz')
+
+    # This will return None, as there is no publisher
+    print "Value of 'baz' with no publisher:", o['baz']
+    
+    # Set 'baz' key, it gets cached
+    o['baz'] = 'bazzz'
+
+    # Now will return cached value, since no provider
+    print "Cached value of 'baz' after being set:", o['baz']





reply via email to

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