commit-gnuradio
[Top][All Lists]
Advanced

[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,





reply via email to

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