[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] r9155 - gnuradio/branches/features/experimental-gui
From: |
jcorgan |
Subject: |
[Commit-gnuradio] r9155 - gnuradio/branches/features/experimental-gui |
Date: |
Sun, 3 Aug 2008 00:06:08 -0600 (MDT) |
Author: jcorgan
Date: 2008-08-03 00:06:05 -0600 (Sun, 03 Aug 2008)
New Revision: 9155
Added:
gnuradio/branches/features/experimental-gui/logpwrfft.py
gnuradio/branches/features/experimental-gui/pubsub.py
gnuradio/branches/features/experimental-gui/stream_to_vector_decimator.py
Removed:
gnuradio/branches/features/experimental-gui/const_controller.py
Modified:
gnuradio/branches/features/experimental-gui/README
gnuradio/branches/features/experimental-gui/common.py
gnuradio/branches/features/experimental-gui/const_streamer.py
gnuradio/branches/features/experimental-gui/const_top_block.py
gnuradio/branches/features/experimental-gui/const_window.py
gnuradio/branches/features/experimental-gui/constsink.py
gnuradio/branches/features/experimental-gui/fft_top_block.py
gnuradio/branches/features/experimental-gui/fft_window.py
gnuradio/branches/features/experimental-gui/fftsink.py
gnuradio/branches/features/experimental-gui/scope_window.py
gnuradio/branches/features/experimental-gui/scopesink.py
gnuradio/branches/features/experimental-gui/simple_usrp.py
gnuradio/branches/features/experimental-gui/todo.txt
gnuradio/branches/features/experimental-gui/usrp_const.py
gnuradio/branches/features/experimental-gui/waterfall_window.py
gnuradio/branches/features/experimental-gui/waterfallsink.py
Log:
Refactoring with new hierarchical pubsub interface. Josh, call me :)
Modified: gnuradio/branches/features/experimental-gui/README
===================================================================
--- gnuradio/branches/features/experimental-gui/README 2008-08-03 01:46:05 UTC
(rev 9154)
+++ gnuradio/branches/features/experimental-gui/README 2008-08-03 06:06:05 UTC
(rev 9155)
@@ -1,69 +1,2 @@
Experimental code demonstrating the property value abstraction and layering
of topblock/controller/GUI code.
-
-* fft_top_block.py:
-
-Pure GNU Radio flowgraph implementing FFT stream. Doesn't know anything
-about GUIs or application operation.
-
-* fft_controller.py:
-
-Application controller implementing property/value interface. Manipulates
-the top block in response to property changes/queries.
-
-* fft_gui.py: (Not yet written)
-
-Implements user interface. Doesn't know anything about GNU Radio, but
-simply queries, sets, or listens to properties on the controller.
-
-* usrp_fft.py:
-
-Top-level script. Implements command line parameters, instantiates
-top block, controller and GUI (not yet written).
-
-* prop_val.py:
-
-Implements the property/value interface.
-
-* simple_usrp.py:
-
-Utility class for simplifying USRP operation. May eventually make it into
-blks2.
-
-* gltext.py:
-
-Utility class for plotting text to screen with wx + opengl
-
-* common.py:
-
-Classes and functions shared by the opengl displays
-
-* plotter.py:
-
-Interface to wx + opengl plotting widgit
-
-* fftsink.py:
-
-wxgui window for fft plots
-
-* scopesink.py:
-
-wxgui window for scope plots
-
-* constsink.py:
-
-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
-is temporary code in usrp_fft.py to print a line to the screen every time the
-FFT frame is updated.
Modified: gnuradio/branches/features/experimental-gui/common.py
===================================================================
--- gnuradio/branches/features/experimental-gui/common.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/common.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -93,16 +93,26 @@
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, lambda e: controller.set(control_key,
not controller[control_key]))
- controller.add_listener(control_key, lambda x: self.SetLabel(x
and 'Stop' or 'Run'))
+ 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, lambda e:
controller.set(control_key, bool(e.IsChecked())))
- controller.add_listener(control_key, lambda x:
self.SetValue(bool(x)))
+ 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.
@@ -124,7 +134,7 @@
slider_value = min(max(0, slider_value), slider_steps)
if abs(slider_value - self._slider.GetValue()) > 1:
self._slider.SetValue(slider_value)
- controller.add_listener(control_key, _on_controller_set)
+ controller.subscribe(control_key, _on_controller_set)
def Disable(self, disable=True): self.Enable(not disable)
def Enable(self, enable=True):
@@ -160,7 +170,7 @@
#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.add_listener(control_key, _on_controller_set)
+ controller.subscribe(control_key, _on_controller_set)
def Disable(self, disable=True): self.Enable(not disable)
def Enable(self, enable=True):
Deleted: gnuradio/branches/features/experimental-gui/const_controller.py
Modified: gnuradio/branches/features/experimental-gui/const_streamer.py
===================================================================
--- gnuradio/branches/features/experimental-gui/const_streamer.py
2008-08-03 01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/const_streamer.py
2008-08-03 06:06:05 UTC (rev 9155)
@@ -20,10 +20,12 @@
# Boston, MA 02110-1301, USA.
#
-from gnuradio import gr, blks2
+from gnuradio import gr
+from stream_to_vector_decimator import stream_to_vector_decimator
+from pubsub import pubsub
import simple_usrp
-class const_streamer(gr.hier_block2):
+class const_streamer(gr.hier_block2, pubsub):
"""!
GNU Radio hierarchical block to create a stream of constellation points at
a
particular frame size and rate.
@@ -32,7 +34,7 @@
sample_rate,
bit_rate,
order=2,
- frame_size=1024,
+ frame_len=1024,
frame_rate=30,
costas_alpha=0.1,
costas_max=0.05,
@@ -40,28 +42,27 @@
mm_omega_limit=0.05):
"""!
Create a const_streamer.
- @param sample_rate Incoming sample rate
- @param bit_rate Modulated bit rate/sec
- @param order Constellation order (BPSK=2, QPSK=4,
default is 2)
- @param frame_size Number of constellation points to plot at
a time
- @param frame_rate Number of frames/sec to create (default is
30)
- @param costas_alpha Costas loop 1st order gain constant
(default is 0.1)
- @param costas_max Costas loop maximum offset, rad/sample
(default is 0.05)
- @param mm_gain_mu Timing loop 1st order gain constant
(default is 0.005)
- @param mm_omega_limit Maximum timing offset in bits/sample
(default is 0.05)
+ @param sample_rate Incoming sample rate
+ @param bit_rate Modulated bit rate/sec
+ @param order Constellation order (BPSK=2, QPSK=4,
default is 2)
+ @param frame_len Number of constellation points to plot at a
time
+ @param frame_rate Number of frames/sec to create (default is
30)
+ @param costas_alpha Costas loop 1st order gain constant
(default is 0.1)
+ @param costas_max Costas loop maximum offset, rad/sample
(default is 0.05)
+ @param mm_gain_mu Timing loop 1st order gain constant
(default is 0.005)
+ @param mm_omega_limit Maximum timing offset in bits/sample
(default is 0.05)
"""
gr.hier_block2.__init__(self, "const_streamer",
gr.io_signature(1, 1, gr.sizeof_gr_complex),
# Input signature
- gr.io_signature(1, 1,
gr.sizeof_gr_complex*frame_size)) # Output signature
+ gr.io_signature(1, 1,
gr.sizeof_gr_complex*frame_len)) # Output signature
+ pubsub.__init__(self)
-
-
costas_beta = 0.25*costas_alpha*costas_alpha
costas_min = -costas_max
mm_omega = float(sample_rate)/bit_rate
mm_gain_omega = 0.25*mm_gain_mu*mm_gain_mu
- mm_mu=0.5 # Center of bit
+ mm_mu=0.5
# Costas frequency/phase recovery loop
# Critically damped 2nd order PLL
@@ -83,34 +84,29 @@
self._agc = gr.agc_cc(1e-6);
# Decimate to requested frame rate
- self._decim =
blks2.stream_to_vector_decimator(item_size=gr.sizeof_gr_complex,
- sample_rate=bit_rate,
- vec_rate=frame_rate,
- vec_len=frame_size)
+ self._decimator =
stream_to_vector_decimator(item_size=gr.sizeof_gr_complex,
+ sample_rate=bit_rate,
+ frame_rate=frame_rate,
+ frame_len=frame_len)
- self.connect(self, self._agc, self._costas, self._retime, self._decim,
self)
+ self.connect(self, self._agc, self._costas, self._retime,
self._decimator, self)
+ self._sample_rate = sample_rate
- def set_costas_alpha(self, alpha):
- self._costas.set_alpha(alpha)
+ self.subscribe('sample_rate', self._set_sample_rate)
+ self.subscribe('costas_alpha', lambda x: self._costas.set_alpha(x))
+ self.subscribe('costas_beta', lambda x: self._costas.set_beta(x))
+ self.subscribe('gain_mu', lambda x: self._retime.set_gain_mu(x))
+ self.subscribe('gain_omega', lambda x:
self._retime.set_gain_omega(x))
- def costas_alpha(self):
- return self._costas.alpha()
-
- def set_costas_beta(self, beta):
- self._costas.set_beta(beta)
+ self.publish('sample_rate', lambda : self._sample_rate)
+ self.publish('costas_alpha', lambda : self._costas.alpha())
+ self.publish('costas_beta', lambda : self._costas.beta())
+ self.publish('gain_mu', lambda : self._retime.gain_mu())
+ self.publish('gain_omega', lambda : self._retime.gain_omega())
- def costas_beta(self):
- return self._costas.beta()
- def set_gain_mu(self, gain_mu):
- self._retime.set_gain_mu(gain_mu)
-
- def gain_mu(self):
- return self._retime.gain_mu()
-
- def set_gain_omega(self, gain_omega):
- self._retime.set_gain_omega(gain_omega)
-
- def gain_omega(self):
- return self._retime.gain_omega()
+ def _set_sample_rate(self, sample_rate):
+ self._sample_rate = sample_rate
+ self._sd['sample_rate'] = sample_rate
+
Modified: gnuradio/branches/features/experimental-gui/const_top_block.py
===================================================================
--- gnuradio/branches/features/experimental-gui/const_top_block.py
2008-08-03 01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/const_top_block.py
2008-08-03 06:06:05 UTC (rev 9155)
@@ -21,10 +21,36 @@
#
from gnuradio import gr, blks2
+from pubsub import pubsub
import const_streamer
import simple_usrp
+import threading
-class const_top_block(gr.top_block):
+class _const_thread(threading.Thread):
+ """
+ This background thread simply reads constellation frames from the
+ message queue and sets the top block 'fft' property to their string
+ representation. External code can add one or more listeners to
+ this property to receive notification when an constellationframe
+ comes in.
+ """
+ def __init__(self, msgq, tb):
+ threading.Thread.__init__(self)
+ self._msgq = msgq
+ self._tb = tb
+ self._keep_running = True
+ self.setDaemon(1)
+
+ def run(self):
+ while self._keep_running:
+ msg = self._msgq.delete_head()
+ self._tb['const'] = msg.to_string()
+
+ def stop(self):
+ self._keep_running = False
+
+
+class const_top_block(gr.top_block, pubsub):
"""!
GNU Radio top block to create a stream of constellation points at a
particular frame rate from a USRP. The frames are enqueued in a
gr.msg_queue
@@ -32,7 +58,7 @@
"""
def __init__(self,
order=2,
- frame_size=1024,
+ frame_len=1024,
frame_rate=30,
which=0,
decim=16,
@@ -52,7 +78,7 @@
Create a const_top_block.
@param order Constellation order (BPSK=2, QPSK=4,
default is 2)
- @param frame_size Number of constellation points to plot at
a time
+ @param frame_len Number of constellation points to plot at
a time
@param frame_rate Number of frames/sec to create (default is
30)
@param which USRP # on USB bus (default is 0)
@param decim Receive sample rate decimation (default is
16)
@@ -70,7 +96,8 @@
"""
gr.top_block.__init__(self, "const_top_block")
-
+ pubsub.__init__(self)
+
# Source of samples
self._u = simple_usrp.source_c(which=which,
decim=decim,
@@ -84,10 +111,10 @@
# TODO: make this a configurable matched filter
# self._filt = gr.fir_filter_ccf(1,
[1.0,]*int(self._u.sample_rate()/bit_rate))
- self._const =
const_streamer.const_streamer(sample_rate=self._u.sample_rate(),
+ self._const =
const_streamer.const_streamer(sample_rate=self._u['sample_rate'],
bit_rate=bit_rate,
order=order,
- frame_size=frame_size,
+ frame_len=frame_len,
frame_rate=frame_rate,
costas_alpha=costas_alpha,
costas_max=costas_max,
@@ -95,49 +122,67 @@
mm_omega_limit=mm_omega_limit)
self._msgq = gr.msg_queue(2)
- self._sink = gr.message_sink(gr.sizeof_gr_complex*frame_size,
self._msgq, True)
+ self._sink = gr.message_sink(gr.sizeof_gr_complex*frame_len,
self._msgq, True)
self.connect(self._u, # self._filt,
self._const, self._sink)
- # "Setters", which are called externally to affect flowgraph operation
- def set_gain(self, gain):
- return self._u.set_gain(gain)
+ self.subscribe('decim', self._set_decim)
+ self.proxy('gain', self._u, 'gain')
+ self.proxy('freq', self._u, 'freq')
+ self.proxy('sample_rate', self._const, 'sample_rate')
+ self.proxy('costas_alpha', self._const, 'costas_alpha')
+ self.proxy('costas_beta', self._const, 'costas_beta')
+ self.proxy('gain_mu', self._const, 'gain_mu')
+ self.proxy('gain_omega', self._const, 'gain_omega')
+ self.publish('queue', lambda : self._msgq)
+
+ self._thread = _const_thread(self._msgq, self)
- def set_freq(self, freq):
- return self._u.set_freq(freq)
-
- def set_decim(self, decim):
- self._u.set_decim(decim)
- self._const.set_sample_rate(self._u.sample_rate())
+ def _set_decim(self, decim):
+ self._u['decim'] = decim
+ self._const['sample_rate'] = self._u['sample_rate']
- def set_costas_alpha(self, alpha):
- self._const.set_costas_alpha(alpha)
-
- def set_costas_beta(self, beta):
- self._const.set_costas_beta(beta)
+ def on_init(self):
+ """
+ This method gets called by the external GUI or other code to start the
top block
+ operation.
+ """
+ # Start the queue runner background thread
+ self._thread.start()
- def set_gain_mu(self, gain_mu):
- self._const.set_gain_mu(gain_mu);
+ # Using gr.top_block.start() here keeps Python's SIGINT handler active,
+ # and we agree to handle SIGINT in our code if needed. We must also
call
+ # gr.top_block.stop() and .wait() to properly shut down GNU Radio
+ self.start()
- def set_gain_omega(self, gain_omega):
- self._const.set_gain_omega(gain_omega)
+ def on_exit(self):
+ """
+ This method gets called to shutdown the top block. It stops the
background
+ thread, then calls stop() and wait() on the top block.
+ """
+ self._thread.stop()
+ self.stop()
+ self.wait()
- # Getters, which are called externally to get information about the
flowgraph
- def queue(self):
- return self._msgq
- def sample_rate(self):
- return self._u.sample_rate()
-
- def costas_alpha(self):
- return self._const.costas_alpha()
-
- def costas_beta(self):
- return self._const.costas_beta()
+if __name__ == "__main__":
+ """
+ Example usage of const_top_block. Opens a file and adds a file writer as
a listener
+ to the 'const' property.
+ """
- def gain_mu(self):
- return self._const.gain_mu()
-
- def gain_omega(self):
- return self._const.gain_omega()
+ # Create the top block (uses all default parameters, needs a USRP attached)
+ tb = const_top_block()
+
+ # Write fft frames to a file
+ f = open("const.dat", "w")
+ tb.subscribe('const', lambda x: f.write(x))
+
+ # Start the top block and wait for a key, then stop the top block
+ tb.on_init()
+ try:
+ raw_input("Press any key to exit.\n")
+ except KeyboardInterrupt:
+ print "Application interrupted."
+ tb.on_exit()
Modified: gnuradio/branches/features/experimental-gui/const_window.py
===================================================================
--- gnuradio/branches/features/experimental-gui/const_window.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/const_window.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -27,7 +27,7 @@
import wx
import numpy
import math
-import prop_val
+import pubsub
##################################################
# Constants
@@ -111,7 +111,7 @@
gain_mu_key,
gain_omega_key,
):
- self.controller = prop_val.prop_val_interface()
+ self.controller = pubsub.pubsub()
#setup
self.ext_controller = controller
self.alpha_key = alpha_key
@@ -133,9 +133,9 @@
self.SetSizerAndFit(main_box)
#alpha and gain mu 2nd orders
def set_beta(alpha): self.ext_controller[self.beta_key] =
.25*alpha**2
- self.ext_controller.add_listener(self.alpha_key, set_beta)
+ self.ext_controller.subscribe(self.alpha_key, set_beta)
def set_gain_omega(gain_mu):
self.ext_controller[self.gain_omega_key] = .25*gain_mu**2
- self.ext_controller.add_listener(self.gain_mu_key,
set_gain_omega)
+ self.ext_controller.subscribe(self.gain_mu_key, set_gain_omega)
#initial setup
self.ext_controller[self.alpha_key] =
self.ext_controller[self.alpha_key]
self.ext_controller[self.gain_mu_key] =
self.ext_controller[self.gain_mu_key]
@@ -144,10 +144,10 @@
self._register_set_prop(self.controller, Y_DIVS_KEY, 8)
self._register_set_prop(self.controller, MARKER_KEY, '.')
#register events
- self.ext_controller.add_listener(msg_key, self.handle_msg)
+ self.ext_controller.subscribe(msg_key, self.handle_msg)
for key in (
X_DIVS_KEY, Y_DIVS_KEY,
- ): self.controller.add_listener(key, self.update_grid)
+ ): self.controller.subscribe(key, self.update_grid)
#initial update
self.update_grid()
Modified: gnuradio/branches/features/experimental-gui/constsink.py
===================================================================
--- gnuradio/branches/features/experimental-gui/constsink.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/constsink.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -25,7 +25,7 @@
import const_window
import common
from gnuradio import gr, blks2
-from prop_val import prop_val_interface
+from pubsub import pubsub
##################################################
# Constants
@@ -101,22 +101,24 @@
#connect
self.connect(self, sync, agc, sd, sink)
#controller
- self.controller = prop_val_interface()
- self.controller.add_listener(ALPHA_KEY, sync.set_alpha)
- self.controller.set_provider(ALPHA_KEY, sync.alpha)
- self.controller.add_listener(BETA_KEY, sync.set_beta)
- self.controller.set_provider(BETA_KEY, sync.beta)
- self.controller.add_listener(GAIN_MU_KEY, sync.set_gain_mu)
- self.controller.set_provider(GAIN_MU_KEY, sync.gain_mu)
- self.controller.add_listener(OMEGA_KEY, sync.set_omega)
- self.controller.set_provider(OMEGA_KEY, sync.omega)
- self.controller.add_listener(GAIN_OMEGA_KEY,
sync.set_gain_omega)
- self.controller.set_provider(GAIN_OMEGA_KEY, sync.gain_omega)
- self.controller.add_listener(SAMPLE_RATE_KEY,
sd.set_sample_rate)
- self.controller.add_listener(SAMPLE_RATE_KEY, lambda x:
self.controller.set(OMEGA_KEY, float(x)/symbol_rate))
- self.controller.set_provider(SAMPLE_RATE_KEY, sd.sample_rate)
+ self.controller = pubsub()
+ self.controller.subscribe(ALPHA_KEY, sync.set_alpha)
+ self.controller.publish(ALPHA_KEY, sync.alpha)
+ self.controller.subscribe(BETA_KEY, sync.set_beta)
+ self.controller.publish(BETA_KEY, sync.beta)
+ self.controller.subscribe(GAIN_MU_KEY, sync.set_gain_mu)
+ self.controller.publish(GAIN_MU_KEY, sync.gain_mu)
+ self.controller.subscribe(OMEGA_KEY, sync.set_omega)
+ self.controller.publish(OMEGA_KEY, sync.omega)
+ self.controller.subscribe(GAIN_OMEGA_KEY, sync.set_gain_omega)
+ self.controller.publish(GAIN_OMEGA_KEY, sync.gain_omega)
+ self.controller.subscribe(SAMPLE_RATE_KEY, sd.set_sample_rate)
+ self.controller.subscribe(SAMPLE_RATE_KEY, lambda x:
self.controller.set(OMEGA_KEY, float(x)/symbol_rate))
+ self.controller.publish(SAMPLE_RATE_KEY, sd.sample_rate)
#start input watcher
- common.input_watcher(msgq, lambda x:
self.controller.set(MSG_KEY, x))
+ 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 = const_window.const_window(
parent=parent,
Modified: gnuradio/branches/features/experimental-gui/fft_top_block.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_top_block.py
2008-08-03 01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/fft_top_block.py
2008-08-03 06:06:05 UTC (rev 9155)
@@ -25,10 +25,11 @@
of FFT frames at a specified rate from a USRP.
"""
-from gnuradio import gr, blks2
-from prop_val import prop_val_interface
+from gnuradio import gr
+from pubsub import pubsub
import threading
import simple_usrp
+import logpwrfft
class _fft_thread(threading.Thread):
"""
@@ -53,7 +54,7 @@
self._keep_running = False
-class fft_top_block(gr.top_block, prop_val_interface, threading.Thread):
+class fft_top_block(gr.top_block, pubsub):
"""!
GNU Radio top block to create a stream of FFT frames at a particular
frame rate from a USRP.
@@ -97,7 +98,7 @@
# Initialize parent classes
gr.top_block.__init__(self, "fft_top_block")
- prop_val_interface.__init__(self)
+ pubsub.__init__(self)
"""
Create a hierarchical flow graph
@@ -111,12 +112,12 @@
freq=freq,
antenna=antenna)
- self._fft = blks2.logpwrfft_c(sample_rate=self._u.sample_rate(),
- fft_size=fft_size,
- frame_rate=frame_rate,
- ref_scale=ref_scale,
- avg_alpha=avg_alpha,
- average=average)
+ self._fft = logpwrfft.logpwrfft_c(sample_rate=self._u['sample_rate'],
+ fft_size=fft_size,
+ frame_rate=frame_rate,
+ ref_scale=ref_scale,
+ avg_alpha=avg_alpha,
+ average=average)
self._msgq = gr.msg_queue(2) # OK to discard if bg thread can't keep up
self._sink = gr.message_sink(gr.sizeof_float*fft_size, self._msgq, True)
@@ -126,17 +127,15 @@
"""
Create controller interface
"""
- # Listeners are invoked when a property is set on the top block
- self.add_listener('decim', self._set_decim)
- self.add_listener('gain', lambda x: self._u.set_gain(x))
- self.add_listener('freq', lambda x: self._u.set_freq(x))
- self.add_listener('average', lambda x: self._fft.set_average(x))
- self.add_listener('avg_alpha', lambda x: self._fft.set_avg_alpha(x))
+ # Subscribers are invoked when a property is set on the top block
+ self.subscribe('decim', self._set_decim)
- # Providers are invoked when a property is read on the top block
- self.set_provider('sample_rate', lambda : self._u.sample_rate())
- self.set_provider('average', lambda : self._fft.average())
- self.set_provider('avg_alpha', lambda : self._fft.avg_alpha())
+ # Proxies pass through subscribers and publishers to the supplied object
+ self.proxy('gain', self._u, 'gain')
+ self.proxy('freq', self._u, 'freq')
+ self.proxy('average', self._fft, 'average')
+ self.proxy('avg_alpha', self._fft, 'avg_alpha')
+ self.proxy('sample_rate', self._fft, 'sample_rate')
"""
Create update thread. Updates property 'fft' at desired frame rate
@@ -145,11 +144,11 @@
def _set_decim(self, decim):
"""!
- Listens to property 'decim'. Changes decimation rate and updates
'sample_rate'
- property (which triggers listeners on that property).
+ Listens to property 'decim'. Changes decimation rate and updates
+ fft streamer sample rate.
"""
- self._u.set_decim(decim)
- self._fft.set_sample_rate(self._u.sample_rate())
+ self._u['decim'] = decim
+ self._fft['sample_rate'] = self._u['sample_rate']
def on_init(self):
"""
@@ -185,7 +184,7 @@
# Write fft frames to a file
f = open("fft.dat", "w")
- tb.add_listener('fft', lambda x: f.write(x))
+ tb.subscribe('fft', lambda x: f.write(x))
# Start the top block and wait for a key, then stop the top block
tb.on_init()
Modified: gnuradio/branches/features/experimental-gui/fft_window.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_window.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/fft_window.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -27,7 +27,7 @@
import wx
import numpy
import math
-import prop_val
+import pubsub
##################################################
# Constants
@@ -77,7 +77,7 @@
AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
parent.ext_controller, parent.avg_alpha_key,
)
- parent.ext_controller.add_listener(parent.average_key,
self.avg_alpha_slider.Enable)
+ 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()
@@ -89,7 +89,7 @@
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.controller.add_listener(Y_PER_DIV_KEY,
self._on_set_y_per_div)
+ parent.controller.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()
@@ -145,7 +145,7 @@
peak_hold,
msg_key,
):
- self.controller = prop_val.prop_val_interface()
+ self.controller = pubsub.pubsub()
#ensure y_per_div
if y_per_div not in DIV_LEVELS: y_per_div = DIV_LEVELS[0]
#setup
@@ -178,13 +178,13 @@
self._register_set_prop(self.controller, BASEBAND_FREQ_KEY,
baseband_freq)
self._register_set_prop(self.controller, RUNNING_KEY, True)
#register events
- self.ext_controller.add_listener(msg_key, self.handle_msg)
- self.ext_controller.add_listener(self.sample_rate_key,
self.update_grid)
+ 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.controller.add_listener(key, self.update_grid)
+ ): self.controller.subscribe(key, self.update_grid)
#initial update
self.update_grid()
Modified: gnuradio/branches/features/experimental-gui/fftsink.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fftsink.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/fftsink.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -25,7 +25,7 @@
import fft_window
import common
from gnuradio import gr, blks2
-from prop_val import prop_val_interface
+from pubsub import pubsub
##################################################
# Constants
@@ -84,15 +84,17 @@
#connect
self.connect(self, copy, fft, sink)
#controller
- self.controller = prop_val_interface()
- self.controller.add_listener(AVERAGE_KEY, fft.set_average)
- self.controller.set_provider(AVERAGE_KEY, fft.average)
- self.controller.add_listener(AVG_ALPHA_KEY, fft.set_avg_alpha)
- self.controller.set_provider(AVG_ALPHA_KEY, fft.avg_alpha)
- self.controller.add_listener(SAMPLE_RATE_KEY,
fft.set_sample_rate)
- self.controller.set_provider(SAMPLE_RATE_KEY, fft.sample_rate)
+ 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
- common.input_watcher(msgq, lambda x:
self.controller.set(MSG_KEY, x))
+ 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,
Added: gnuradio/branches/features/experimental-gui/logpwrfft.py
===================================================================
--- gnuradio/branches/features/experimental-gui/logpwrfft.py
(rev 0)
+++ gnuradio/branches/features/experimental-gui/logpwrfft.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -0,0 +1,120 @@
+#
+# 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.
+#
+
+from gnuradio import gr, window, blks2
+from pubsub import pubsub
+from stream_to_vector_decimator import stream_to_vector_decimator
+import math
+
+class _logpwrfft_base(gr.hier_block2, pubsub):
+ """!
+ Create a log10(abs(fft)) stream chain, with real or complex input.
+ """
+
+ def __init__(self, sample_rate, fft_size, ref_scale, frame_rate,
avg_alpha, average):
+ """!
+ Create an log10(abs(fft)) stream chain.
+ Provide access to the setting the filter and sample rate.
+ @param sample_rate Incoming stream sample rate
+ @param fft_size Number of FFT bins
+ @param ref_scale Sets 0 dB value input amplitude
+ @param frame_rate Output frame rate
+ @param avg_alpha FFT averaging (over time) constant [0.0-1.0]
+ @param average Whether to average [True, False]
+ """
+ gr.hier_block2.__init__(self, self._name,
+ gr.io_signature(1, 1, self._item_size),
# Input signature
+ gr.io_signature(1, 1,
gr.sizeof_float*fft_size)) # Output signature
+ pubsub.__init__(self)
+
+ self._sd = stream_to_vector_decimator(item_size=self._item_size,
+ sample_rate=sample_rate,
+ frame_rate=frame_rate,
+ frame_len=fft_size)
+
+ fft_window = window.blackmanharris(fft_size)
+ fft = self._fft_block[0](fft_size, True, fft_window)
+ window_power = sum(map(lambda x: x*x, fft_window))
+
+ c2mag = gr.complex_to_mag(fft_size)
+ self._avg = gr.single_pole_iir_filter_ff(1.0, fft_size)
+ self._log = gr.nlog10_ff(20, fft_size,
+ -10*math.log10(fft_size) #
Adjust for number of bins
+ -10*math.log10(window_power/fft_size) #
Adjust for windowing loss
+ -20*math.log10(ref_scale/2)) #
Adjust for reference scale
+ self.connect(self, self._sd, fft, c2mag, self._avg, self._log, self)
+
+ self._sample_rate = sample_rate
+ self._avg_alpha = avg_alpha
+ self._average = average
+
+ self.subscribe('sample_rate', self._set_sample_rate)
+ self.subscribe('average', self._set_average)
+ self.subscribe('avg_alpha', self._set_avg_alpha)
+ self.publish('sample_rate', lambda: self._sample_rate)
+ self.publish('average', lambda : self._average)
+ self.publish('avg_alpha', lambda: self._avg_alpha)
+
+ self['average'] = False
+ self['avg_alpha'] = avg_alpha
+ self['average'] = average
+
+
+ def _set_sample_rate(self, sample_rate):
+ self._sample_rate = sample_rate
+ self._sd['sample_rate'] = sample_rate
+
+ def _set_average(self, average):
+ """!
+ Set the averaging filter on/off.
+ @param average true to set averaging on
+ """
+ self._average = average
+ if self._average:
+ self._avg.set_taps(self._avg_alpha)
+ else:
+ self._avg.set_taps(1.0)
+
+ def _set_avg_alpha(self, avg_alpha):
+ """!
+ Set the average alpha and set the taps if average was on.
+ @param avg_alpha the new iir filter tap
+ """
+ self._avg_alpha = avg_alpha
+ self._set_average(self._average)
+
+
+class logpwrfft_f(_logpwrfft_base):
+ """!
+ Create an fft block chain, with real input.
+ """
+ _name = "logpwrfft_f"
+ _item_size = gr.sizeof_float
+ _fft_block = (gr.fft_vfc, )
+
+class logpwrfft_c(_logpwrfft_base):
+ """!
+ Create an fft block chain, with complex input.
+ """
+ _name = "logpwrfft_c"
+ _item_size = gr.sizeof_gr_complex
+ _fft_block = (gr.fft_vcc, )
+
Added: gnuradio/branches/features/experimental-gui/pubsub.py
===================================================================
--- gnuradio/branches/features/experimental-gui/pubsub.py
(rev 0)
+++ gnuradio/branches/features/experimental-gui/pubsub.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -0,0 +1,152 @@
+#!/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):
+ if not self.has_key(key): self.__missing__(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']
Modified: gnuradio/branches/features/experimental-gui/scope_window.py
===================================================================
--- gnuradio/branches/features/experimental-gui/scope_window.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/scope_window.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -27,7 +27,7 @@
import wx
import numpy
import time
-import prop_val
+import pubsub
##################################################
# Constants
@@ -95,12 +95,12 @@
control_box.Add(self.trigger_mode_chooser, 0, wx.EXPAND)
#trigger level
self.trigger_level_chooser = common.DropDownController(self,
'Level', TRIGGER_LEVELS, parent.controller, TRIGGER_LEVEL_KEY)
- parent.controller.add_listener(TRIGGER_MODE_KEY, lambda x:
self.trigger_level_chooser.Disable(x==0))
+ parent.controller.subscribe(TRIGGER_MODE_KEY, lambda x:
self.trigger_level_chooser.Disable(x==0))
control_box.Add(self.trigger_level_chooser, 0, wx.EXPAND)
#trigger channel
choices = [('Ch%d'%(i+1), i) for i in range(parent.num_inputs)]
self.trigger_channel_chooser = common.DropDownController(self,
'Channel', choices, parent.controller, TRIGGER_CHANNEL_KEY)
- parent.controller.add_listener(TRIGGER_MODE_KEY, lambda x:
self.trigger_channel_chooser.Disable(x==0))
+ parent.controller.subscribe(TRIGGER_MODE_KEY, lambda x:
self.trigger_channel_chooser.Disable(x==0))
control_box.Add(self.trigger_channel_chooser, 0, wx.EXPAND)
#axes options
SPACING = 15
@@ -118,7 +118,7 @@
control_box.Add(hbox, 0, wx.EXPAND)
hbox.Add(wx.StaticText(self, -1, ' Units/Div '), 1,
wx.ALIGN_CENTER_VERTICAL)
self.y_buttons = common.IncrDecrButtons(self,
self._on_incr_y_divs, self._on_decr_y_divs)
- parent.controller.add_listener(AUTORANGE_KEY,
self.y_buttons.Disable)
+ parent.controller.subscribe(AUTORANGE_KEY,
self.y_buttons.Disable)
hbox.Add(self.y_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
hbox.AddSpacer(SPACING)
#y axis ref lvl
@@ -126,7 +126,7 @@
control_box.Add(hbox, 0, wx.EXPAND)
hbox.Add(wx.StaticText(self, -1, ' Y Offset '), 1,
wx.ALIGN_CENTER_VERTICAL)
self.y_off_buttons = common.IncrDecrButtons(self,
self._on_incr_y_off, self._on_decr_y_off)
- parent.controller.add_listener(AUTORANGE_KEY,
self.y_off_buttons.Disable)
+ parent.controller.subscribe(AUTORANGE_KEY,
self.y_off_buttons.Disable)
hbox.Add(self.y_off_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
hbox.AddSpacer(SPACING)
#misc options
@@ -189,7 +189,7 @@
scope_num_samples_key,
msg_key,
):
- self.controller = prop_val.prop_val_interface()
+ self.controller = pubsub.pubsub()
#check num inputs
assert num_inputs <= len(CHANNEL_COLOR_SPECS)
#setup
@@ -233,14 +233,14 @@
self._register_set_prop(self.controller, TRIGGER_MODE_KEY, 1)
self._register_set_prop(self.controller, TRIGGER_LEVEL_KEY,
None)
#register events
- self.ext_controller.add_listener(msg_key, self.handle_msg)
- self.ext_controller.add_listener(sample_rate_key,
self.update_decim)
- self.controller.add_listener(FRAME_RATE_KEY, self.update_decim)
+ self.ext_controller.subscribe(msg_key, self.handle_msg)
+ self.ext_controller.subscribe(sample_rate_key,
self.update_decim)
+ self.controller.subscribe(FRAME_RATE_KEY, self.update_decim)
for key in (
X_PER_DIV_KEY, Y_PER_DIV_KEY,
X_OFF_KEY, Y_OFF_KEY,
X_DIVS_KEY, Y_DIVS_KEY,
- ): self.controller.add_listener(key, self.update_grid)
+ ): self.controller.subscribe(key, self.update_grid)
#initial update, dont do this here, wait for handle_msg #HACK
#self.update_grid()
#self.update_decim()
Modified: gnuradio/branches/features/experimental-gui/scopesink.py
===================================================================
--- gnuradio/branches/features/experimental-gui/scopesink.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/scopesink.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -25,7 +25,7 @@
import scope_window
import common
from gnuradio import gr
-from prop_val import prop_val_interface
+from pubsub import pubsub
##################################################
# Constants
@@ -71,24 +71,26 @@
for i in range(num_inputs):
self.connect((self, i), (scope, i))
#controller
- self.controller = prop_val_interface()
- self.controller.add_listener(SAMPLE_RATE_KEY,
scope.set_sample_rate)
- self.controller.set_provider(SAMPLE_RATE_KEY, scope.sample_rate)
+ self.controller = pubsub()
+ self.controller.subscribe(SAMPLE_RATE_KEY,
scope.set_sample_rate)
+ self.controller.publish(SAMPLE_RATE_KEY, scope.sample_rate)
def set_trigger_level(level):
if level == '': scope.set_trigger_level_auto()
else: scope.set_trigger_level(level)
- self.controller.add_listener(SCOPE_TRIGGER_LEVEL_KEY,
set_trigger_level)
+ self.controller.subscribe(SCOPE_TRIGGER_LEVEL_KEY,
set_trigger_level)
def set_trigger_mode(mode):
if mode == 0: mode = gr.gr_TRIG_AUTO
elif mode < 0: mode = gr.gr_TRIG_NEG_SLOPE
elif mode > 0: mode = gr.gr_TRIG_POS_SLOPE
else: return
scope.set_trigger_mode(mode)
- self.controller.add_listener(SCOPE_TRIGGER_MODE_KEY,
set_trigger_mode)
- self.controller.add_listener(SCOPE_TRIGGER_CHANNEL_KEY,
scope.set_trigger_channel)
- self.controller.set_provider(SCOPE_NUM_SAMPLES_KEY,
scope.get_samples_per_output_record)
+ self.controller.subscribe(SCOPE_TRIGGER_MODE_KEY,
set_trigger_mode)
+ self.controller.subscribe(SCOPE_TRIGGER_CHANNEL_KEY,
scope.set_trigger_channel)
+ self.controller.publish(SCOPE_NUM_SAMPLES_KEY,
scope.get_samples_per_output_record)
#start input watcher
- common.input_watcher(msgq, lambda x:
self.controller.set(MSG_KEY, x))
+ 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 = scope_window.scope_window(
parent=parent,
Modified: gnuradio/branches/features/experimental-gui/simple_usrp.py
===================================================================
--- gnuradio/branches/features/experimental-gui/simple_usrp.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/simple_usrp.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -26,8 +26,9 @@
"""
from gnuradio import gr, usrp
+from pubsub import pubsub
-class source_c(gr.hier_block2):
+class source_c(gr.hier_block2, pubsub):
def __init__(self,
which=0,
decim=16,
@@ -53,11 +54,20 @@
gr.hier_block2.__init__(self, "simple_usrp.source_c",
gr.io_signature(0, 0, 0), #
Input signature
gr.io_signature(1, 1, gr.sizeof_gr_complex)) #
Output signature
-
+ pubsub.__init__(self)
+
+ self._setup_controller()
self._setup_usrp(which, decim, width_8, no_hb)
self._setup_db(subdev_spec, gain, freq, antenna)
self.connect(self._u, self)
+
+ def _setup_controller(self):
+ self.subscribe('decim', lambda x: self._set_decim(x))
+ self.subscribe('gain', lambda x: self._set_gain(x))
+ self.subscribe('freq', lambda x: self._set_freq(x))
+ self.publish('sample_rate', lambda : self._u.adc_rate()/self._decim)
+
def _setup_usrp(self, which, decim, width_8, no_hb):
if no_hb or decim < 8:
self._fpga_filename = 'std_4rx_0tx.rbf'
@@ -67,7 +77,7 @@
self._u = usrp.source_c(which=which,
fpga_filename=self._fpga_filename)
- self.set_decim(decim)
+ self['decim'] = decim
if width_8:
format = self._u.make_format(8, 8)
self._u.set_format(format)
@@ -77,12 +87,12 @@
subdev_spec = usrp.pick_rx_subdevice(self._u)
self._u.set_mux(usrp.determine_rx_mux_value(self._u, subdev_spec))
self._subdev = usrp.selected_subdev(self._u, subdev_spec)
- self.set_gain(gain)
- self.set_freq(freq)
+ self['gain'] = gain
+ self['freq'] = freq
if antenna is not None:
self.subdev.select_rx_antenna(antenna)
- def set_decim(self, decim):
+ def _set_decim(self, decim):
"""!
Set USRP RX decimation.
@@ -91,7 +101,7 @@
self._decim = decim
self._u.set_decim_rate(decim)
- def set_gain(self, gain):
+ def _set_gain(self, gain):
"""!
Set USRP daughterboard gain.
@@ -102,7 +112,7 @@
gain = float(g[0]+g[1])/2.0
self._subdev.set_gain(gain)
- def set_freq(self, freq):
+ def _set_freq(self, freq):
"""!
Set USRP/Daughterboard RX center frequency.
@@ -113,11 +123,6 @@
freq = float(f[0]+f[1])/2.0
return self._u.tune(0, self._subdev, freq)
- def sample_rate(self):
- """!
- Get USRP RX sample rate in samples/sec.
- """
- return self._u.adc_rate()/self._decim
class sink_c(gr.hier_block2):
def __init__(self,
Added: gnuradio/branches/features/experimental-gui/stream_to_vector_decimator.py
===================================================================
--- gnuradio/branches/features/experimental-gui/stream_to_vector_decimator.py
(rev 0)
+++ gnuradio/branches/features/experimental-gui/stream_to_vector_decimator.py
2008-08-03 06:06:05 UTC (rev 9155)
@@ -0,0 +1,76 @@
+#
+# 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.
+#
+
+from gnuradio import gr
+from pubsub import pubsub
+
+class stream_to_vector_decimator(gr.hier_block2, pubsub):
+ """!
+ Convert the stream to a vector, decimate the vector stream to achieve the
vector rate.
+ """
+ def __init__(self, item_size, sample_rate, frame_rate, frame_len):
+ """!
+ Create the block chain.
+ @param item_size the number of bytes per sample
+ @param sample_rate the rate of incoming samples
+ @param frame_rate the rate of outgoing vectors (same units as
sample_rate)
+ @param frame_len the length of the outgoing vectors in items
+ """
+ pubsub.__init__(self)
+
+ self._frame_rate = frame_rate
+ self._frame_len = frame_len
+ self._sample_rate = sample_rate
+
+ gr.hier_block2.__init__(self, "stream_to_vector_decimator",
+ gr.io_signature(1, 1, item_size), #
Input signature
+ gr.io_signature(1, 1, item_size*frame_len)) #
Output signature
+
+ s2v = gr.stream_to_vector(item_size, frame_len)
+ self.one_in_n = gr.keep_one_in_n(item_size*frame_len, 1)
+ self._update_decimator()
+ self.connect(self, s2v, self.one_in_n, self)
+
+ self.subscribe('sample_rate', self.set_sample_rate)
+ self.subscribe('frame_rate', self.set_frame_rate)
+ self.publish('sample_rate', lambda : self._sample_rate)
+ self.publish('frame_rate', lambda :
self._sample_rate/self._frame_len/self._decim)
+
+ def set_sample_rate(self, sample_rate):
+ """!
+ Set the new sampling rate and update the decimator.
+ @param sample_rate the new rate
+ """
+ self._sample_rate = sample_rate
+ self._update_decimator()
+
+ def set_frame_rate(self, frame_rate):
+ """!
+ Set the new vector rate and update the decimator.
+ @param frame_rate the new rate
+ """
+ self._frame_rate = frame_rate
+ self._update_decimator()
+
+ def _update_decimator(self):
+ self._decim = max(1,
int(self._sample_rate/self._frame_len/self._frame_rate))
+ self.one_in_n.set_n(self._decim)
+
Modified: gnuradio/branches/features/experimental-gui/todo.txt
===================================================================
--- gnuradio/branches/features/experimental-gui/todo.txt 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/todo.txt 2008-08-03
06:06:05 UTC (rev 9155)
@@ -1,6 +1,5 @@
TODO List
--controller is a dict
-controller property with default value for cache
-use iteritems in cli.py
-add/remove listener in cli.py
Modified: gnuradio/branches/features/experimental-gui/usrp_const.py
===================================================================
--- gnuradio/branches/features/experimental-gui/usrp_const.py 2008-08-03
01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/usrp_const.py 2008-08-03
06:06:05 UTC (rev 9155)
@@ -24,8 +24,8 @@
from gnuradio.eng_option import eng_option
import sys
-# Import the controller object
-from const_controller import const_controller
+# Import the top block
+from const_top_block import const_top_block
# Import the GUI object
from const_gui import const_gui
@@ -84,30 +84,30 @@
# the top block it manipulates. The controller implements
# a property/value interface to allow setting, getting, and
# listening properties that affect the application operation.
- controller = const_controller(order=options.order,
- frame_size=1024,
- frame_rate=30,
- which=options.which,
- decim=options.decim,
- width_8=options.width_8,
- no_hb=options.no_hb,
- subdev_spec=options.rx_subdev_spec,
- gain=options.gain,
- freq=options.freq,
- antenna=options.antenna,
- bit_rate=options.rate,
- costas_alpha=options.costas_alpha,
- costas_max=options.costas_max,
- mm_gain_mu=options.mm_gain_mu,
- mm_omega_limit=options.mm_omega_limit)
+ tb = const_top_block(order=options.order,
+ frame_len=1024,
+ frame_rate=30,
+ which=options.which,
+ decim=options.decim,
+ width_8=options.width_8,
+ no_hb=options.no_hb,
+ subdev_spec=options.rx_subdev_spec,
+ gain=options.gain,
+ freq=options.freq,
+ antenna=options.antenna,
+ bit_rate=options.rate,
+ costas_alpha=options.costas_alpha,
+ costas_max=options.costas_max,
+ mm_gain_mu=options.mm_gain_mu,
+ mm_omega_limit=options.mm_omega_limit)
- # Step 2: Create the GUI and pass it the controller
+ # Step 2: Create the GUI and pass it the top block.
# to manipulate. The GUI code doesn't know anything about GNU
# Radio proper; it simply gets, sets, or listens to properties
# on the controller. The 'GUI' can actually be a CLI batch
# program, an interactive shell, or anything that knows what to
# do with the controller properties.
#
- gui = const_gui(controller)
+ gui = const_gui(tb)
gui.run()
Modified: gnuradio/branches/features/experimental-gui/waterfall_window.py
===================================================================
--- gnuradio/branches/features/experimental-gui/waterfall_window.py
2008-08-03 01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/waterfall_window.py
2008-08-03 06:06:05 UTC (rev 9155)
@@ -27,7 +27,7 @@
import wx
import numpy
import math
-import prop_val
+import pubsub
##################################################
# Constants
@@ -79,7 +79,7 @@
AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
parent.ext_controller, parent.avg_alpha_key,
)
- parent.ext_controller.add_listener(parent.average_key,
self.avg_alpha_slider.Enable)
+ parent.ext_controller.subscribe(parent.average_key,
self.avg_alpha_slider.Enable)
control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
#color mode
control_box.AddStretchSpacer()
@@ -153,7 +153,7 @@
avg_alpha_key,
msg_key,
):
- self.controller = prop_val.prop_val_interface()
+ self.controller = pubsub.pubsub()
#setup
self.ext_controller = controller
self.real = real
@@ -174,8 +174,8 @@
main_box.Add(self.control_panel, 0, wx.EXPAND)
self.SetSizerAndFit(main_box)
#plotter listeners
- self.controller.add_listener(COLOR_MODE_KEY,
self.plotter.set_color_mode)
- self.controller.add_listener(NUM_LINES_KEY,
self.plotter.set_num_lines)
+ self.controller.subscribe(COLOR_MODE_KEY,
self.plotter.set_color_mode)
+ self.controller.subscribe(NUM_LINES_KEY,
self.plotter.set_num_lines)
#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]
@@ -188,13 +188,13 @@
self._register_set_prop(self.controller, COLOR_MODE_KEY, 'rgb')
self._register_set_prop(self.controller, RUNNING_KEY, True)
#register events
- self.ext_controller.add_listener(msg_key, self.handle_msg)
- self.ext_controller.add_listener(self.sample_rate_key,
self.update_grid)
- self.ext_controller.add_listener(self.frame_rate_key,
self.update_grid)
- self.controller.add_listener(BASEBAND_FREQ_KEY,
self.update_grid)
- self.controller.add_listener(NUM_LINES_KEY, self.update_grid)
- self.controller.add_listener(Y_DIVS_KEY, self.update_grid)
- self.controller.add_listener(X_DIVS_KEY, self.update_grid)
+ self.ext_controller.subscribe(msg_key, self.handle_msg)
+ self.ext_controller.subscribe(self.sample_rate_key,
self.update_grid)
+ self.ext_controller.subscribe(self.frame_rate_key,
self.update_grid)
+ self.controller.subscribe(BASEBAND_FREQ_KEY, self.update_grid)
+ self.controller.subscribe(NUM_LINES_KEY, self.update_grid)
+ self.controller.subscribe(Y_DIVS_KEY, self.update_grid)
+ self.controller.subscribe(X_DIVS_KEY, self.update_grid)
#initial update
self.update_grid()
Modified: gnuradio/branches/features/experimental-gui/waterfallsink.py
===================================================================
--- gnuradio/branches/features/experimental-gui/waterfallsink.py
2008-08-03 01:46:05 UTC (rev 9154)
+++ gnuradio/branches/features/experimental-gui/waterfallsink.py
2008-08-03 06:06:05 UTC (rev 9155)
@@ -25,7 +25,7 @@
import waterfall_window
import common
from gnuradio import gr, blks2
-from prop_val import prop_val_interface
+from pubsub import pubsub
##################################################
# Constants
@@ -83,16 +83,18 @@
#connect
self.connect(self, copy, fft, sink)
#controller
- self.controller = prop_val_interface()
- self.controller.add_listener(AVERAGE_KEY, fft.set_average)
- self.controller.set_provider(AVERAGE_KEY, fft.average)
- self.controller.add_listener(AVG_ALPHA_KEY, fft.set_avg_alpha)
- self.controller.set_provider(AVG_ALPHA_KEY, fft.avg_alpha)
- self.controller.add_listener(SAMPLE_RATE_KEY,
fft.set_sample_rate)
- self.controller.set_provider(SAMPLE_RATE_KEY, fft.sample_rate)
- self.controller.set_provider(FRAME_RATE_KEY,
fft._sd.frame_rate) #FIXME
+ 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)
+ self.controller.publish(FRAME_RATE_KEY, fft._sd.frame_rate)
#FIXME
#start input watcher
- common.input_watcher(msgq, lambda x:
self.controller.set(MSG_KEY, x))
+ 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 = waterfall_window.waterfall_window(
parent=parent,
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Commit-gnuradio] r9155 - gnuradio/branches/features/experimental-gui,
jcorgan <=