[Top][All Lists]
[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']
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Commit-gnuradio] r9243 - in gnuradio/branches/developers/jblum/glwxgui: config gr-wxgui/src/python gr-wxgui/src/python/plotter,
jblum <=