commit-gnuradio
[Top][All Lists]
Advanced

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

[Commit-gnuradio] r8893 - gnuradio/branches/features/experimental-gui


From: jblum
Subject: [Commit-gnuradio] r8893 - gnuradio/branches/features/experimental-gui
Date: Tue, 15 Jul 2008 16:52:01 -0600 (MDT)

Author: jblum
Date: 2008-07-15 16:51:59 -0600 (Tue, 15 Jul 2008)
New Revision: 8893

Added:
   gnuradio/branches/features/experimental-gui/grc_scopesink_test.py
   gnuradio/branches/features/experimental-gui/scope_window.py
Modified:
   gnuradio/branches/features/experimental-gui/common.py
   gnuradio/branches/features/experimental-gui/fft_controller.py
   gnuradio/branches/features/experimental-gui/fft_gui.py
   gnuradio/branches/features/experimental-gui/fft_window.py
   gnuradio/branches/features/experimental-gui/fftsink.py
   gnuradio/branches/features/experimental-gui/prop_val.py
   gnuradio/branches/features/experimental-gui/scopesink.py
Log:
scope sink window + block wrapper + scope test

Modified: gnuradio/branches/features/experimental-gui/common.py
===================================================================
--- gnuradio/branches/features/experimental-gui/common.py       2008-07-15 
22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/common.py       2008-07-15 
22:51:59 UTC (rev 8893)
@@ -38,12 +38,12 @@
                threading.Thread.__init__(self)
                self.setDaemon(1)
                self.msgq = msgq
-               self.handle_msg = handle_msg
+               self._handle_msg = handle_msg
                self.keep_running = True
                self.start()
 
        def run(self):
-               while self.keep_running: 
self.handle_msg(self.msgq.delete_head())
+               while self.keep_running: 
self._handle_msg(self.msgq.delete_head().to_string())
 
 ##################################################
 # WX Shared Classes
@@ -87,7 +87,7 @@
                        self._incr_button.Disable()
                        self._decr_button.Disable()
 
-       def Disable(self): self.Enable(False)
+       def Disable(self, disable=True): self.Enable(not disable)
 
 class RunStopButtonController(wx.Button):
        def __init__(self, parent, controller, control_key):
@@ -128,7 +128,7 @@
                        self._slider.Disable()
                        self._label.Disable()
 
-       def Disable(self): self.Enable(False)
+       def Disable(self, disable=True): self.Enable(not disable)
 
 ##################################################
 # Shared Functions

Modified: gnuradio/branches/features/experimental-gui/fft_controller.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_controller.py       
2008-07-15 22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/fft_controller.py       
2008-07-15 22:51:59 UTC (rev 8893)
@@ -115,7 +115,7 @@
        """
        while self._keep_running:
            msg = self._tb.queue().delete_head()
-           self['fft'] = msg
+           self['fft'] = msg.to_string()
            
     def on_exit(self):
        """

Modified: gnuradio/branches/features/experimental-gui/fft_gui.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_gui.py      2008-07-15 
22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/fft_gui.py      2008-07-15 
22:51:59 UTC (rev 8893)
@@ -46,7 +46,8 @@
                                average_key='average',
                                avg_alpha_key='avg_alpha',
                                peak_hold=True,
-                               msg_key='fft')
+                               msg_key='fft',
+                               fft_size=512,)
        
         self.Add(self._win)
 

Modified: gnuradio/branches/features/experimental-gui/fft_window.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fft_window.py   2008-07-15 
22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/fft_window.py   2008-07-15 
22:51:59 UTC (rev 8893)
@@ -40,8 +40,6 @@
 FFT_PLOT_COLOR_SPEC = (0, 0, 1)
 PEAK_VALS_COLOR_SPEC = (0, 1, 0)
 NO_PEAK_VALS = list()
-AVERAGE_KEY = 'average'
-AVG_ALPHA_KEY = 'avg_alpha'
 PEAK_HOLD_KEY = 'peak_hold'
 Y_PER_DIV_KEY = 'y_per_div'
 Y_DIVS_KEY = 'y_divs'
@@ -70,7 +68,7 @@
                #checkboxes for average and peak hold
                control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
-               self.average_check_box = common.CheckBoxController(self, 
'Average', parent.controller, AVERAGE_KEY)
+               self.average_check_box = common.CheckBoxController(self, 
'Average', parent.ext_controller, parent.average_key)
                control_box.Add(self.average_check_box, 0, wx.EXPAND)
                self.peak_hold_check_box = common.CheckBoxController(self, 
'Peak Hold', parent.controller, PEAK_HOLD_KEY)
                control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
@@ -78,9 +76,9 @@
                self.avg_alpha_slider = common.LogSliderController(
                        self, 'Avg Alpha',
                        AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
-                       parent.controller, AVG_ALPHA_KEY,
+                       parent.ext_controller, parent.avg_alpha_key,
                )
-               parent.controller.add_listener(AVERAGE_KEY, 
self.avg_alpha_slider.Enable)
+               parent.ext_controller.add_listener(parent.average_key, 
self.avg_alpha_slider.Enable)
                control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
 
                #radio buttons for div size
@@ -141,6 +139,7 @@
                size,
                title,
                real,
+               fft_size,
                baseband_freq,
                sample_rate_key,
                y_per_div,
@@ -157,6 +156,7 @@
                #setup
                self.ext_controller = controller
                self.real = real
+               self.fft_size = fft_size
                self.sample_rate_key = sample_rate_key
                self.average_key = average_key
                self.avg_alpha_key = avg_alpha_key
@@ -172,17 +172,9 @@
                main_box.Add(self.plotter, 1, wx.EXPAND)
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
-               #bind internal & external keys
-               def bind_controller_keys(controller_master, controller_slave, 
master_key, slave_key):
-                       controller_slave.add_listener(slave_key, lambda x: 
controller_master.set(master_key, x))
-                       controller_master.add_listener(master_key, lambda x: 
controller_slave.set(slave_key, x))
-               for in_key, ext_key in (
-                       (AVERAGE_KEY, average_key),
-                       (AVG_ALPHA_KEY, avg_alpha_key),
-               ): bind_controller_keys(self.ext_controller, self.controller, 
ext_key, in_key)
                #initial setup
-               self.set_average(self.ext_controller[average_key])
-               self.set_avg_alpha(self.ext_controller[avg_alpha_key])
+               self.set_average(self.ext_controller[self.average_key])
+               self.set_avg_alpha(self.ext_controller[self.avg_alpha_key])
                self.set_baseband_freq(baseband_freq)
                self.set_peak_hold(peak_hold)
                self.set_y_per_div(y_per_div)
@@ -192,20 +184,20 @@
                self.set_running(True)
                #register events
                self.ext_controller.add_listener(msg_key, self.handle_msg)
-               self.ext_controller.add_listener(self.sample_rate_key, lambda 
x: self.update_grid())
-               self.controller.add_listener(BASEBAND_FREQ_KEY, lambda x: 
self.update_grid())
-               self.controller.add_listener(Y_PER_DIV_KEY, lambda x: 
self.update_grid())
-               self.controller.add_listener(Y_DIVS_KEY, lambda x: 
self.update_grid())
-               self.controller.add_listener(X_DIVS_KEY, lambda x: 
self.update_grid())
-               self.controller.add_listener(REF_LEVEL_KEY, lambda x: 
self.update_grid())
+               self.ext_controller.add_listener(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)
                #initial update
                self.update_grid()
 
        ##################################################
        # Set parameters on-the-fly
        ##################################################
-       def set_average(self, average): self.controller[AVERAGE_KEY] = average
-       def set_avg_alpha(self, avg_alpha): self.controller[AVG_ALPHA_KEY] = 
avg_alpha
+       def set_average(self, average): self.ext_controller[self.average_key] = 
average
+       def set_avg_alpha(self, avg_alpha): 
self.ext_controller[self.avg_alpha_key] = avg_alpha
        def set_peak_hold(self, peak_hold): self.controller[PEAK_HOLD_KEY] = 
peak_hold
        def set_y_per_div(self, y_per_div): self.controller[Y_PER_DIV_KEY] = 
y_per_div
        def set_y_divs(self, y_divs): self.controller[Y_DIVS_KEY] = y_divs
@@ -224,16 +216,8 @@
                @param msg the fft array as a character array
                """
                if not self.controller[RUNNING_KEY]: return
-               itemsize = int(msg.arg1())
-               nitems = int(msg.arg2())
-               s = msg.to_string()
-               # There may be more than one frame in the message.
-               # If so, we take only the last one
-               if nitems > 1:
-                       start = itemsize * (nitems - 1)
-                       s = s[start:start+itemsize]
                #convert to floating point numbers
-               samples = numpy.fromstring(s, numpy.float32)
+               samples = numpy.fromstring(msg, numpy.float32)[:self.fft_size] 
#only take first frame
                num_samps = len(samples)
                #reorder fft
                if self.real: samples = samples[:num_samps/2]
@@ -258,7 +242,7 @@
                #update the plotter
                self.plotter.update()
 
-       def update_grid(self):
+       def update_grid(self, *args):
                """!
                Update the plotter grid.
                This update method is dependent on the variables below.

Modified: gnuradio/branches/features/experimental-gui/fftsink.py
===================================================================
--- gnuradio/branches/features/experimental-gui/fftsink.py      2008-07-15 
22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/fftsink.py      2008-07-15 
22:51:59 UTC (rev 8893)
@@ -36,7 +36,7 @@
 MSG_KEY = 'msg'
 
 ##################################################
-# FFT Sink Class (wrapper for old wxgui)
+# FFT sink block (wrapper for old wxgui)
 ##################################################
 class _fft_sink_base(gr.hier_block2):
        """!
@@ -86,11 +86,11 @@
                #controller
                self.controller = prop_val_interface()
                self.controller.add_listener(AVERAGE_KEY, fft.set_average)
-               self.controller.set_provider(AVERAGE_KEY, lambda: fft.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, lambda: 
fft.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, lambda: 
fft._sd.sample_rate()) #FIXME
+               self.controller.set_provider(SAMPLE_RATE_KEY, 
fft._sd.sample_rate) #FIXME
                #start input watcher
                common.input_watcher(msgq, lambda x: 
self.controller.set(MSG_KEY, x))
                #create window
@@ -99,7 +99,8 @@
                        controller=self.controller,
                        size=size,
                        title=title,
-                       real = self.real,
+                       real=self.real,
+                       fft_size=fft_size,
                        baseband_freq=baseband_freq,
                        sample_rate_key=SAMPLE_RATE_KEY,
                        y_per_div=y_per_div,
@@ -121,7 +122,7 @@
                self.set_y_divs = self.win.set_y_divs
                self.set_x_divs = self.win.set_x_divs
                self.set_running = self.win.set_running
-               
+
        def set_sample_rate(self, sample_rate):
                self.controller[SAMPLE_RATE_KEY] = sample_rate
 

Added: gnuradio/branches/features/experimental-gui/grc_scopesink_test.py
===================================================================
--- gnuradio/branches/features/experimental-gui/grc_scopesink_test.py           
                (rev 0)
+++ gnuradio/branches/features/experimental-gui/grc_scopesink_test.py   
2008-07-15 22:51:59 UTC (rev 8893)
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+##################################################
+# Gnuradio Python Flow Graph
+# Title: untitled
+# Author: unknown
+# Description: untitled flow graph
+# Generated: Tue Jul 15 12:24:12 2008
+##################################################
+
+from gnuradio import gr
+import scopesink
+from grc_gnuradio import wxgui as grc_wxgui
+import wx
+
+ampl = 1
+freq = 1000
+offset = [-1, 0, 1][1]
+samp_rate = 32000
+
+class scope_test(grc_wxgui.top_block_gui):
+
+       def __init__(self, ampl=ampl, freq=freq, offset=offset, 
samp_rate=samp_rate):
+               grc_wxgui.top_block_gui.__init__(
+                       self,
+                       title="GRC - Executing: untitled",
+                       
icon="/usr/lib/python2.5/site-packages/grc/data/grc-icon-32.png",
+               )
+
+               ##################################################
+               # Variables
+               ##################################################
+               self.ampl = ampl
+               _ampl_control = grc_wxgui.slider_horizontal_control(
+                       window=self.GetWin(), 
+                       callback=self.set_ampl,
+                       label="Amplitude",
+                       value=ampl,
+                       min=0,
+                       max=20,
+                       num_steps=100,
+               )
+               self.Add(_ampl_control)
+               self.freq = freq
+               _freq_control = grc_wxgui.slider_horizontal_control(
+                       window=self.GetWin(), 
+                       callback=self.set_freq,
+                       label="Frequency",
+                       value=freq,
+                       min=0,
+                       max=samp_rate/2,
+                       num_steps=100,
+               )
+               self.Add(_freq_control)
+               self.offset = offset
+               _offset_control = grc_wxgui.radio_buttons_horizontal_control(
+                       window=self.GetWin(), 
+                       callback=self.set_offset,
+                       label="Offset",
+                       index=1,
+                       choices=[-1, 0, 1],
+                       labels=['Neg', 'Zero', 'Pos'],
+               )
+               self.Add(_offset_control)
+               self.samp_rate = samp_rate
+
+               ##################################################
+               # Blocks
+               ##################################################
+               self.gr_sig_source_x = gr.sig_source_f(samp_rate, 
gr.GR_COS_WAVE, freq, ampl, offset*ampl)
+               self.gr_sig_source_x0 = gr.sig_source_f(samp_rate, 
gr.GR_SIN_WAVE, freq, ampl, offset*ampl)
+               self.gr_throttle = gr.throttle(gr.sizeof_float*1, samp_rate)
+               self.wxgui_scopesink2 = scopesink.scope_sink_f(
+                       self.GetWin(), 
+                       title="Scope Plot", 
+                       sample_rate=samp_rate, 
+                       frame_rate=30, 
+                       v_scale=None,
+                       t_scale=.001, 
+                       num_inputs=2,
+               )
+               self.GridAdd(self.wxgui_scopesink2.win, 0, 0, 1, 1)
+
+               ##################################################
+               # Connections
+               ##################################################
+               self.connect((self.gr_throttle, 0), (self.wxgui_scopesink2, 0))
+               self.connect((self.gr_sig_source_x, 0), (self.gr_throttle, 0))
+               self.connect((self.gr_sig_source_x0, 0), 
(self.wxgui_scopesink2, 1))
+
+       def set_ampl(self, ampl):
+               self.ampl = ampl
+               self.gr_sig_source_x.set_amplitude(self.ampl)
+               self.gr_sig_source_x.set_offset(self.offset*self.ampl)
+               self.gr_sig_source_x0.set_amplitude(self.ampl)
+               self.gr_sig_source_x0.set_offset(self.offset*self.ampl)
+
+       def set_freq(self, freq):
+               self.freq = freq
+               self.gr_sig_source_x.set_frequency(self.freq)
+               self.gr_sig_source_x0.set_frequency(self.freq)
+
+       def set_offset(self, offset):
+               self.offset = offset
+               self.gr_sig_source_x.set_offset(self.offset*self.ampl)
+               self.gr_sig_source_x0.set_offset(self.offset*self.ampl)
+
+       def set_samp_rate(self, samp_rate):
+               self.samp_rate = samp_rate
+               self.wxgui_scopesink2.set_sample_rate(self.samp_rate)
+               self.gr_sig_source_x.set_sampling_freq(self.samp_rate)
+               self.gr_sig_source_x0.set_sampling_freq(self.samp_rate)
+
+if __name__ == '__main__':
+       tb = scope_test()
+       tb.Run()
+


Property changes on: 
gnuradio/branches/features/experimental-gui/grc_scopesink_test.py
___________________________________________________________________
Name: svn:executable
   + *

Modified: gnuradio/branches/features/experimental-gui/prop_val.py
===================================================================
--- gnuradio/branches/features/experimental-gui/prop_val.py     2008-07-15 
22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/prop_val.py     2008-07-15 
22:51:59 UTC (rev 8893)
@@ -63,7 +63,7 @@
        with the new value.  If there is no provider for the property, the
        value will be cached.
        """
-       if self._cache == value: return
+       #if self._cache == value: return
        (handler, obj) = self._provider
        if handler is None:
            self._cache = value

Copied: gnuradio/branches/features/experimental-gui/scope_window.py (from rev 
8888, gnuradio/branches/features/experimental-gui/scopesink.py)
===================================================================
--- gnuradio/branches/features/experimental-gui/scope_window.py                 
        (rev 0)
+++ gnuradio/branches/features/experimental-gui/scope_window.py 2008-07-15 
22:51:59 UTC (rev 8893)
@@ -0,0 +1,382 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+##################################################
+# Imports
+##################################################
+import plotter
+import common
+import wx
+import numpy
+import time
+import prop_val
+
+##################################################
+# Constants
+##################################################
+DEFAULT_FRAME_RATE = 30
+DEFAULT_WIN_SIZE = (600, 300)
+DEFAULT_V_SCALE = 1000
+TRIGGER_MODES = (
+       ('Auto', 0),
+       ('Neg', -1),
+       ('Pos', +1),
+)
+TRIGGER_LEVELS = (
+       ('Auto', None),
+       ('+High', 0.75),
+       ('+Med', 0.5),
+       ('+Low', 0.25),
+       ('Zero', 0.0),
+       ('-Low', -0.25),
+       ('-Med', -0.5),
+       ('-High', -0.75),
+)
+CHANNEL_COLOR_SPECS = (
+       (0, 0, 1),
+       (0, 1, 0),
+       (1, 0, 0),
+       (1, 0, 1),
+)
+AUTORANGE_UPDATE_RATE = 0.5 #sec
+RUNNING_KEY = 'running'
+AUTORANGE_KEY = 'autorange'
+AC_COUPLE_KEY = 'ac_couple'
+X_PER_DIV_KEY = 'x_per_div'
+Y_PER_DIV_KEY = 'y_per_div'
+X_OFF_KEY = 'x_off'
+Y_OFF_KEY = 'y_off'
+Y_DIVS_KEY = 'y_divs'
+X_DIVS_KEY = 'x_divs'
+FRAME_RATE_KEY = 'frame_rate'
+
+##################################################
+# Scope window control panel
+##################################################
+class control_panel(wx.Panel):
+       """!
+       A control panel with wx widgits to control the plotter and scope block.
+       """
+       def __init__(self, parent):
+               """!
+               Create a new control panel.
+               @param parent the wx parent window
+               """
+               self.parent = parent
+               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               control_box = wx.BoxSizer(wx.VERTICAL)
+               #trigger defaults
+               self.trigger_channel_index = 0 #ch 1
+               self.trigger_mode_index = 1 #not auto
+               self.trigger_level_index = 0 #auto
+               #trigger options
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Trigger Options'), 0, 
wx.ALIGN_CENTER)
+               control_box.AddSpacer(2)
+               #trigger mode
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               control_box.Add(hbox, 0, wx.EXPAND)
+               hbox.Add(wx.StaticText(self, -1, ' Channel '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               self.trigger_channel_chooser = wx.Choice(self, -1, 
choices=["Ch%d"%ch for ch in range(1, parent.num_inputs+1)])
+               self.trigger_channel_chooser.Bind(wx.EVT_CHOICE, 
self._on_trigger_channel)
+               hbox.Add(self.trigger_channel_chooser, 0, 
wx.ALIGN_CENTER_VERTICAL)
+               #trigger mode
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               control_box.Add(hbox, 0, wx.EXPAND)
+               hbox.Add(wx.StaticText(self, -1, ' Mode '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               self.trigger_mode_chooser = wx.Choice(self, -1, choices=[tm[0] 
for tm in TRIGGER_MODES])
+               self.trigger_mode_chooser.Bind(wx.EVT_CHOICE, 
self._on_trigger_mode)
+               hbox.Add(self.trigger_mode_chooser, 0, wx.ALIGN_CENTER_VERTICAL)
+               #trigger level
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               control_box.Add(hbox, 0, wx.EXPAND)
+               hbox.Add(wx.StaticText(self, -1, ' Level '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               self.trigger_level_chooser = wx.Choice(self, -1, choices=[tl[0] 
for tl in TRIGGER_LEVELS])
+               self.trigger_level_chooser.Bind(wx.EVT_CHOICE, 
self._on_trigger_level)
+               hbox.Add(self.trigger_level_chooser, 0, 
wx.ALIGN_CENTER_VERTICAL)
+               #axes options
+               SPACING = 15
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Axes Options'), 0, 
wx.ALIGN_CENTER)
+               #x axis divs
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               control_box.Add(hbox, 0, wx.EXPAND)
+               hbox.Add(wx.StaticText(self, -1, ' Secs/Div '), 1, 
wx.ALIGN_CENTER_VERTICAL)
+               self.x_buttons = common.IncrDecrButtons(self, 
self._on_incr_x_divs, self._on_decr_x_divs)
+               hbox.Add(self.x_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
+               hbox.AddSpacer(SPACING)
+               #y axis divs
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               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)
+               hbox.Add(self.y_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
+               hbox.AddSpacer(SPACING)
+               #y axis ref lvl
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               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)
+               hbox.Add(self.y_off_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
+               hbox.AddSpacer(SPACING)
+               #misc options
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Range Options'), 0, 
wx.ALIGN_CENTER)
+               #ac couple check box
+               self.ac_couple_check_box = common.CheckBoxController(self, 'AC 
Couple', parent.controller, AC_COUPLE_KEY)
+               control_box.Add(self.ac_couple_check_box, 0, wx.ALIGN_LEFT)
+               #autorange check box
+               self.autorange_check_box = common.CheckBoxController(self, 
'Autorange', parent.controller, AUTORANGE_KEY)
+               control_box.Add(self.autorange_check_box, 0, wx.ALIGN_LEFT)
+               #run/stop
+               control_box.AddStretchSpacer()
+               self.run_button = common.RunStopButtonController(self, 
parent.controller, RUNNING_KEY)
+               control_box.Add(self.run_button, 0, wx.EXPAND)
+               #set sizer
+               self.SetSizerAndFit(control_box)
+               #initial update
+               self.update_trigger_menu()
+               
+       def update_trigger_menu(self, *args):
+               
self.trigger_channel_chooser.SetSelection(self.trigger_channel_index)
+               self.parent.trigger_channel = self.trigger_channel_index
+               self.trigger_mode_chooser.SetSelection(self.trigger_mode_index)
+               self.parent.trigger_mode = 
TRIGGER_MODES[self.trigger_mode_index][1]
+               
self.trigger_level_chooser.SetSelection(self.trigger_level_index)
+               self.parent.trigger_level = 
TRIGGER_LEVELS[self.trigger_level_index][1]
+
+       ##################################################
+       # Event handlers
+       ##################################################
+       def _on_trigger_channel(self, event):
+               self.trigger_channel_index = 
self.trigger_channel_chooser.GetSelection()
+               self.update_trigger_menu()
+       def _on_trigger_mode(self, event):
+               self.trigger_mode_index = 
self.trigger_mode_chooser.GetSelection()
+               self.update_trigger_menu()
+       def _on_trigger_level(self, event):
+               self.trigger_level_index = 
self.trigger_level_chooser.GetSelection()
+               self.update_trigger_menu()
+       
+       def _on_incr_x_divs(self, event):
+               self.parent.set_x_per_div(
+                       
common.get_clean_incr(self.parent.controller[X_PER_DIV_KEY]))
+       def _on_decr_x_divs(self, event):
+               self.parent.set_x_per_div(
+                       
common.get_clean_decr(self.parent.controller[X_PER_DIV_KEY]))
+       def _on_incr_y_divs(self, event):
+               self.parent.set_y_per_div(
+                       
common.get_clean_incr(self.parent.controller[Y_PER_DIV_KEY]))
+       def _on_decr_y_divs(self, event):
+               self.parent.set_y_per_div(
+                       
common.get_clean_decr(self.parent.controller[Y_PER_DIV_KEY]))
+       def _on_incr_y_off(self, event):
+               self.parent.set_y_off(
+                       self.parent.controller[Y_OFF_KEY] + 
self.parent.controller[Y_PER_DIV_KEY])
+       def _on_decr_y_off(self, event):
+               self.parent.set_y_off(
+                       self.parent.controller[Y_OFF_KEY] - 
self.parent.controller[Y_PER_DIV_KEY])
+
+##################################################
+# Scope window with plotter and control panel
+##################################################
+class scope_window(wx.Panel):
+       def __init__(
+               self,
+               parent,
+               controller,
+               size,
+               title,
+               frame_rate,
+               num_inputs,
+               sample_rate_key,
+               x_per_div,
+               y_per_div,
+               ac_couple,
+               scope_trigger_level_key,
+               scope_trigger_mode_key,
+               scope_trigger_channel_key,
+               scope_num_samples_key,
+               msg_key,
+       ):
+               self.controller = prop_val.prop_val_interface()
+               #check num inputs
+               assert num_inputs <= len(CHANNEL_COLOR_SPECS)
+               #setup
+               self.ext_controller = controller
+               self.num_inputs = num_inputs
+               self.sample_rate_key = sample_rate_key
+               autorange = y_per_div is None
+               self.autorange_ts = 0
+               if y_per_div is None: y_per_div = 1
+               self.frame_counter = 0
+               self._init = False #HACK
+               #scope keys
+               self.scope_trigger_level_key = scope_trigger_level_key
+               self.scope_trigger_mode_key = scope_trigger_mode_key
+               self.scope_trigger_channel_key = scope_trigger_channel_key
+               self.scope_num_samples_key = scope_num_samples_key
+               #init panel and plot
+               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               self.plotter = plotter.grid_plotter(self)
+               self.plotter.SetSize(wx.Size(*size))
+               self.plotter.set_title(title)
+               self.plotter.set_legend(self.num_inputs > 1)
+               #setup the box with plot and controls
+               self.control_panel = control_panel(self)
+               main_box = wx.BoxSizer(wx.HORIZONTAL)
+               main_box.Add(self.plotter, 1, wx.EXPAND)
+               main_box.Add(self.control_panel, 0, wx.EXPAND)
+               self.SetSizerAndFit(main_box)
+               #initial setup
+               self.set_running(True)
+               self.set_ac_couple(ac_couple)
+               self.set_autorange(autorange)
+               self.set_x_per_div(x_per_div)
+               self.set_y_per_div(y_per_div)
+               self.set_x_off(0)
+               self.set_y_off(0)
+               self.set_x_divs(8)
+               self.set_y_divs(8)
+               self.set_frame_rate(frame_rate)
+               #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)
+               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)
+               #initial update, dont do this here, wait for handle_msg #HACK
+               #self.update_grid()
+               #self.update_decim()
+
+       ##################################################
+       # Set parameters on-the-fly
+       ##################################################
+       def set_running(self, running): self.controller[RUNNING_KEY] = running
+       def set_ac_couple(self, ac_couple): self.controller[AC_COUPLE_KEY] = 
ac_couple
+       def set_autorange(self, autorange): self.controller[AUTORANGE_KEY] = 
autorange
+       def set_x_per_div(self, x_per_div): self.controller[X_PER_DIV_KEY] = 
x_per_div
+       def set_y_per_div(self, y_per_div): self.controller[Y_PER_DIV_KEY] = 
y_per_div
+       def set_x_off(self, x_off): self.controller[X_OFF_KEY] = x_off
+       def set_y_off(self, y_off): self.controller[Y_OFF_KEY] = y_off
+       def set_x_divs(self, x_divs): self.controller[X_DIVS_KEY] = x_divs
+       def set_y_divs(self, y_divs): self.controller[Y_DIVS_KEY] = y_divs
+       def set_frame_rate(self, frame_rate): self.controller[FRAME_RATE_KEY] = 
frame_rate
+
+       def handle_msg(self, msg):
+               """!
+               Handle the message from the scope sink message queue.
+               Plot the list of arrays of samples onto the grid.
+               Each samples array gets its own channel.
+               @param msg the time domain data as a character array
+               """
+               if not self.controller[RUNNING_KEY]: return
+               #convert to floating point numbers
+               samples = numpy.fromstring(msg, numpy.float32)
+               samps_per_ch = len(samples)/self.num_inputs
+               sampleses = [samples[samps_per_ch*i:samps_per_ch*(i+1)] for i 
in range(self.num_inputs)]
+               if not self._init: #HACK
+                       self._init = True
+                       self.update_grid()
+                       self.update_decim()
+               if self.frame_counter == 0: #decimate
+                       #trigger level (must do before ac coupling)
+                       self.ext_controller[self.scope_trigger_channel_key] = 
self.trigger_channel
+                       self.ext_controller[self.scope_trigger_mode_key] = 
self.trigger_mode
+                       if self.trigger_level is None:
+                               
self.ext_controller[self.scope_trigger_level_key] = ''
+                       else:
+                               samples = sampleses[self.trigger_channel]
+                               
self.ext_controller[self.scope_trigger_level_key] = \
+                               
self.trigger_level*(numpy.max(samples)-numpy.min(samples))/2 + 
numpy.average(samples)
+                       #ac coupling
+                       if self.controller[AC_COUPLE_KEY]:
+                               sampleses = [samples - numpy.average(samples) 
for samples in sampleses]
+                       #autorange
+                       if self.controller[AUTORANGE_KEY] and time.time() - 
self.autorange_ts > AUTORANGE_UPDATE_RATE:
+                               bounds = [common.get_min_max(samples) for 
samples in sampleses]
+                               y_min = min(*[bound[0] for bound in bounds])
+                               y_max = max(*[bound[1] for bound in bounds])
+                               #adjust the y per div
+                               y_per_div = 
common.get_clean_num((y_max-y_min)/self.controller[Y_DIVS_KEY])
+                               if y_per_div != self.controller[Y_PER_DIV_KEY]: 
self.set_y_per_div(y_per_div)
+                               #adjust the y offset
+                               y_off = 
y_per_div*round((y_max+y_min)/2/y_per_div)
+                               if y_off != self.controller[Y_OFF_KEY]: 
self.set_y_off(y_off)
+                               self.autorange_ts = time.time()
+                       #plot each waveform
+                       for i, samples in enumerate(sampleses):
+                               #number of samples to scale to the screen
+                               num_samps = 
int(self.controller[X_PER_DIV_KEY]*self.controller[X_DIVS_KEY]*self.ext_controller[self.sample_rate_key])
+                               #handle num samps out of bounds
+                               while num_samps > len(samples): samples = 
numpy.concatenate((samples, samples))
+                               if num_samps < 2: num_samps = 0
+                               #plot samples
+                               self.plotter.set_waveform(
+                                       channel=i+1,
+                                       samples=samples[:num_samps],
+                                       color_spec=CHANNEL_COLOR_SPECS[i],
+                               )
+                       #update the plotter
+                       self.plotter.update()
+               self.frame_counter = (self.frame_counter + 1)%self.decim
+
+       def update_decim(self, *args):
+               """!
+               Update the frame decimation when the frame rate or sample rate 
changes.
+               """
+               decim = 
self.ext_controller[self.sample_rate_key]/self.ext_controller[self.scope_num_samples_key]/self.controller[FRAME_RATE_KEY]
+               self.decim = max(1, int(decim))
+
+       def update_grid(self, *args):
+               #grid parameters
+               x_per_div = self.controller[X_PER_DIV_KEY]
+               y_per_div = self.controller[Y_PER_DIV_KEY]
+               x_off = self.controller[X_OFF_KEY]
+               y_off = self.controller[Y_OFF_KEY]
+               x_divs = self.controller[X_DIVS_KEY]
+               y_divs = self.controller[Y_DIVS_KEY]
+               #update the x axis
+               exp = common.get_exp(x_per_div)
+               if exp > -2: x_units, scalar = 's', 1e0
+               elif exp > -5: x_units, scalar = 'ms', 1e3
+               elif exp > -8: x_units, scalar = 'us', 1e6
+               else: x_units, scalar = 'ns', 1e9
+               self.plotter.set_x_units('Time (%s)'%x_units)
+               self.plotter.set_x_grid(
+                       scalar*x_off,
+                       scalar*x_per_div*x_divs + scalar*x_off,
+                       scalar*x_per_div,
+               )
+               #update the y axis
+               self.plotter.set_y_units('Units')
+               self.plotter.set_y_grid(
+                       -1*y_per_div*y_divs/2.0 + y_off,
+                       y_per_div*y_divs/2.0 + y_off,
+                       y_per_div,
+               )
+               #update plotter
+               self.plotter.update()

Modified: gnuradio/branches/features/experimental-gui/scopesink.py
===================================================================
--- gnuradio/branches/features/experimental-gui/scopesink.py    2008-07-15 
22:00:46 UTC (rev 8892)
+++ gnuradio/branches/features/experimental-gui/scopesink.py    2008-07-15 
22:51:59 UTC (rev 8893)
@@ -22,353 +22,24 @@
 ##################################################
 # Imports
 ##################################################
-from gnuradio import gr
-import plotter
+import scope_window
 import common
-import wx
-import numpy
-import time
+from gnuradio import gr
+from prop_val import prop_val_interface
 
 ##################################################
 # Constants
 ##################################################
-DEFAULT_FRAME_RATE = 30
-DEFAULT_WIN_SIZE = (640, 240)
-DEFAULT_V_SCALE = 1000
-TRIGGER_MODES = (
-       ('Auto', gr.gr_TRIG_AUTO),
-       ('Neg', gr.gr_TRIG_NEG_SLOPE),
-       ('Pos', gr.gr_TRIG_POS_SLOPE),
-)
-TRIGGER_LEVELS = (
-       ('Auto', None),
-       ('+High', 0.75),
-       ('+Med', 0.5),
-       ('+Low', 0.25),
-       ('Zero', 0.0),
-       ('-Low', -0.25),
-       ('-Med', -0.5),
-       ('-High', -0.75),
-)
-CHANNEL_COLOR_SPECS = (
-       (0, 0, 1),
-       (0, 1, 0),
-       (1, 0, 0),
-       (1, 0, 1),
-)
-AUTORANGE_UPDATE_RATE = 0.5 #sec
+MSG_KEY = 'msg'
+SAMPLE_RATE_KEY = 'sample_rate'
+SCOPE_TRIGGER_LEVEL_KEY = 'scope_trigger_level'
+SCOPE_TRIGGER_MODE_KEY = 'scope_trigger_mode'
+SCOPE_TRIGGER_CHANNEL_KEY = 'scope_trigger_channel'
+SCOPE_NUM_SAMPLES_KEY = 'scope_num_samples'
 
 ##################################################
-# Scope window control panel
+# Scope sink block (wrapper for old wxgui)
 ##################################################
-class control_panel(wx.Panel):
-       """!
-       A control panel with wx widgits to control the plotter and scope block.
-       """
-       def __init__(self, parent):
-               """!
-               Create a new control panel.
-               @param parent the wx parent window
-               """
-               self.parent = parent
-               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
-               control_box = wx.BoxSizer(wx.VERTICAL)
-
-               #trigger options
-               control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Trigger Options'), 0, 
wx.ALIGN_CENTER)
-               control_box.AddSpacer(2)
-               #trigger mode
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               control_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Channel '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               self.trigger_channel_chooser = wx.Choice(self, -1, 
choices=["Ch%d"%ch for ch in range(1, parent.num_inputs+1)])
-               self.trigger_channel_chooser.Bind(wx.EVT_CHOICE, 
self._on_trigger_channel)
-               hbox.Add(self.trigger_channel_chooser, 0, 
wx.ALIGN_CENTER_VERTICAL)
-               #trigger mode
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               control_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Mode '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               self.trigger_mode_chooser = wx.Choice(self, -1, choices=[tm[0] 
for tm in TRIGGER_MODES])
-               self.trigger_mode_chooser.Bind(wx.EVT_CHOICE, 
self._on_trigger_mode)
-               hbox.Add(self.trigger_mode_chooser, 0, wx.ALIGN_CENTER_VERTICAL)
-               #trigger level
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               control_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Level '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               self.trigger_level_chooser = wx.Choice(self, -1, choices=[tl[0] 
for tl in TRIGGER_LEVELS])
-               self.trigger_level_chooser.Bind(wx.EVT_CHOICE, 
self._on_trigger_level)
-               hbox.Add(self.trigger_level_chooser, 0, 
wx.ALIGN_CENTER_VERTICAL)
-
-               #axes options
-               SPACING = 15
-               control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Axes Options'), 0, 
wx.ALIGN_CENTER)
-               #x axis divs
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               control_box.Add(hbox, 0, wx.EXPAND)
-               hbox.Add(wx.StaticText(self, -1, ' Secs/Div '), 1, 
wx.ALIGN_CENTER_VERTICAL)
-               self.x_buttons = common.IncrDecrButtons(self, 
self._on_incr_x_divs, self._on_decr_x_divs)
-               hbox.Add(self.x_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(SPACING)
-               #y axis divs
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               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)
-               hbox.Add(self.y_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(SPACING)
-               #y axis ref lvl
-               hbox = wx.BoxSizer(wx.HORIZONTAL)
-               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)
-               hbox.Add(self.y_off_buttons, 0, wx.ALIGN_CENTER_VERTICAL)
-               hbox.AddSpacer(SPACING)
-               
-               #misc options
-               control_box.AddStretchSpacer()
-               control_box.Add(common.LabelText(self, 'Range Options'), 0, 
wx.ALIGN_CENTER)
-               #ac couple check box
-               self.ac_couple_check_box = wx.CheckBox(parent=self, 
style=wx.CHK_2STATE, label="AC Couple")
-               self.ac_couple_check_box.Bind(wx.EVT_CHECKBOX, 
self._on_ac_couple)
-               control_box.Add(self.ac_couple_check_box, 0, wx.ALIGN_LEFT)
-               #autorange check box
-               self.autorange_check_box = wx.CheckBox(parent=self, 
style=wx.CHK_2STATE, label="Autorange")
-               self.autorange_check_box.Bind(wx.EVT_CHECKBOX, 
self._on_autorange)
-               control_box.Add(self.autorange_check_box, 0, wx.ALIGN_LEFT)
-               #Run button
-               control_box.AddStretchSpacer()
-               self.run_button = wx.Button(self, -1, '', style=wx.BU_EXACTFIT)
-               self.run_button.Bind(wx.EVT_BUTTON, self._on_run)
-               control_box.Add(self.run_button, 0, wx.EXPAND)
-
-               #set sizer
-               self.SetSizerAndFit(control_box)
-               #update
-               self.update()
-
-       def update(self):
-               """!
-               Read the state of the scope plot settings and update the 
control panel.
-               """
-               #update the ac couple check box
-               self.ac_couple_check_box.SetValue(self.parent.ac_couple)
-               #update the autorange check box
-               self.autorange_check_box.SetValue(self.parent.autorange)
-               #update trigger menu
-               
self.trigger_channel_chooser.SetSelection(self.parent.trigger_channel_index)
-               
self.trigger_mode_chooser.SetSelection(self.parent.trigger_mode_index)
-               
self.trigger_level_chooser.SetSelection(self.parent.trigger_level_index)
-               #update the run/stop button
-               if self.parent.running: self.run_button.SetLabel('Stop')
-               else: self.run_button.SetLabel('Run')
-               #update the y adj buttons
-               if self.parent.autorange:
-                       self.y_buttons.Disable()
-                       self.y_off_buttons.Disable()
-               else:
-                       self.y_buttons.Enable()
-                       self.y_off_buttons.Enable()
-
-       ##################################################
-       # Event handlers
-       ##################################################
-       def _on_channel(self, event): 
self.parent.set_input_index(self.channel_chooser.GetSelection())
-       def _on_ac_couple(self, event): 
self.parent.set_ac_couple(event.IsChecked())
-       def _on_autorange(self, event): 
self.parent.set_autorange(event.IsChecked())
-       def _on_trigger_channel(self, event): 
self.parent.set_trigger_channel_index(self.trigger_channel_chooser.GetSelection())
-       def _on_trigger_mode(self, event): 
self.parent.set_trigger_mode_index(self.trigger_mode_chooser.GetSelection())
-       def _on_trigger_level(self, event): 
self.parent.set_trigger_level_index(self.trigger_level_chooser.GetSelection())
-       def _on_incr_x_divs(self, event): 
self.parent.set_x_per_div(common.get_clean_incr(self.parent.x_per_div))
-       def _on_decr_x_divs(self, event): 
self.parent.set_x_per_div(common.get_clean_decr(self.parent.x_per_div))
-       def _on_incr_y_divs(self, event): 
self.parent.set_y_per_div(common.get_clean_incr(self.parent.y_per_div))
-       def _on_decr_y_divs(self, event): 
self.parent.set_y_per_div(common.get_clean_decr(self.parent.y_per_div))
-       def _on_incr_y_off(self, event): 
self.parent.set_y_off(self.parent.y_off + self.parent.y_per_div)
-       def _on_decr_y_off(self, event): 
self.parent.set_y_off(self.parent.y_off - self.parent.y_per_div)
-       def _on_run(self, event): self.parent.set_run(not self.parent.running)
-
-##################################################
-# Scope window with plotter and control panel
-##################################################
-class scope_window(wx.Panel):
-       def __init__(
-               self,
-               parent,
-               size,
-               title,
-               scope,
-               frame_rate,
-               num_inputs,
-               sample_rate,
-               x_per_div,
-               y_per_div,
-               ac_couple,
-       ):
-               #check num inputs
-               assert num_inputs <= len(CHANNEL_COLOR_SPECS)
-               #setup
-               self.running = True
-               self.num_inputs = num_inputs
-               self.sample_rate = sample_rate
-               self.ac_couple = ac_couple
-               self.autorange = y_per_div is None
-               self.autorange_ts = 0
-               self.input_index = 0
-               #triggering
-               self.trigger_channel_index = 0
-               self.trigger_mode_index = 1
-               self.trigger_level_index = 0
-               #x grid settings
-               self.x_divs = 8
-               self.x_per_div = x_per_div
-               self.x_off = 0
-               #y grid settings
-               self.y_divs = 8
-               if y_per_div is None: self.y_per_div = 1
-               else: self.y_per_div = y_per_div
-               self.y_off = 0
-               self.scope = scope
-               self.frame_rate = frame_rate
-               self.frame_counter = 0
-               self._init = False #HACK
-               #init panel and plot
-               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
-               self.plotter = plotter.grid_plotter(self)
-               self.plotter.SetSize(wx.Size(*size))
-               self.plotter.set_title(title)
-               self.plotter.set_legend(self.num_inputs > 1)
-               #setup the box with plot and controls
-               self.control_panel = control_panel(self)
-               main_box = wx.BoxSizer(wx.HORIZONTAL)
-               main_box.Add(self.plotter, 1, wx.EXPAND)
-               main_box.Add(self.control_panel, 0, wx.EXPAND)
-               self.SetSizerAndFit(main_box)
-               #update
-               self.update()
-
-       def plot(self, sampleses):
-               """!
-               Plot the list of arrays of samples onto the grid.
-               Each samples array gets its own channel.
-               @param sampleses the array of arrays
-               """
-               if not self._init: #HACK
-                       self._init = True
-                       self.update()
-               if not self.running: return
-               if self.frame_counter == 0: #decimate
-                       #trigger level (must do before ac coupling)
-                       samples = sampleses[self.trigger_level_index]
-                       trigger_level = 
TRIGGER_LEVELS[self.trigger_level_index][1]
-                       if trigger_level is None: 
self.scope.set_trigger_level_auto()
-                       else: 
self.scope.set_trigger_level(trigger_level*(numpy.max(samples)-numpy.min(samples))/2
 + numpy.average(samples))
-                       #ac coupling
-                       if self.ac_couple:
-                               sampleses = [samples - numpy.average(samples) 
for samples in sampleses]
-                       #autorange
-                       if self.autorange and time.time() - self.autorange_ts > 
AUTORANGE_UPDATE_RATE:
-                               bounds = [common.get_min_max(samples) for 
samples in sampleses]
-                               y_min = min(*[bound[0] for bound in bounds])
-                               y_max = max(*[bound[1] for bound in bounds])
-                               #adjust the y per div
-                               y_per_div = 
common.get_clean_num((y_max-y_min)/self.y_divs)
-                               if self.y_per_div != y_per_div: 
self.set_y_per_div(y_per_div)
-                               #adjust the y offset
-                               y_off = 
self.y_per_div*round((y_max+y_min)/2/self.y_per_div)
-                               if self.y_off != y_off: self.set_y_off(y_off)
-                               self.autorange_ts = time.time()
-                       #plot each waveform
-                       for i, samples in enumerate(sampleses):
-                               #number of samples to scale to the screen
-                               num_samps = 
int(self.x_per_div*self.x_divs*self.sample_rate)
-                               #handle num samps out of bounds
-                               while num_samps > len(samples): samples = 
numpy.concatenate((samples, samples))
-                               if num_samps < 2: num_samps = 0
-                               #plot samples
-                               self.plotter.set_waveform(
-                                       channel=i+1,
-                                       samples=samples[:num_samps],
-                                       color_spec=CHANNEL_COLOR_SPECS[i],
-                               )
-                       #update the plotter
-                       self.plotter.update()
-               self.frame_counter = (self.frame_counter + 1)%self.decim
-
-       def update(self):
-               #update the frame decimation
-               self.decim = max(1, 
int(self.sample_rate/self.scope.get_samples_per_output_record()/self.frame_rate))
-               #update the scope
-               if self._init: #HACK avoid segfaults, only set after a sample 
has arrived
-                       
self.scope.set_trigger_channel(self.trigger_channel_index)
-                       
self.scope.set_trigger_mode(TRIGGER_MODES[self.trigger_mode_index][1])
-                       self.scope.set_sample_rate(self.sample_rate)
-               #update the x axis
-               exp = common.get_exp(self.x_per_div)
-               if exp > -2: x_units, scalar = 's', 1e0
-               elif exp > -5: x_units, scalar = 'ms', 1e3
-               elif exp > -8: x_units, scalar = 'us', 1e6
-               else: x_units, scalar = 'ns', 1e9
-               self.plotter.set_x_units('Time (%s)'%x_units)
-               self.plotter.set_x_grid(
-                       scalar*self.x_off,
-                       scalar*self.x_per_div*self.x_divs + scalar*self.x_off,
-                       scalar*self.x_per_div,
-               )
-               #update the y axis
-               self.plotter.set_y_units('Units')
-               self.plotter.set_y_grid(
-                       -1*self.y_per_div*self.y_divs/2.0 + self.y_off,
-                       self.y_per_div*self.y_divs/2.0 + self.y_off,
-                       self.y_per_div,
-               )
-               #update control panel and plotter
-               self.control_panel.update()
-               self.plotter.update()
-
-       ##################################################
-       # Set parameters on-the-fly
-       ##################################################
-       def set_x_per_div(self, x_per_div):
-               self.x_per_div = x_per_div
-               self.update()
-       def set_x_off(self, x_off):
-               self.x_off = x_off
-               self.update()
-       def set_y_per_div(self, y_per_div):
-               self.y_per_div = y_per_div
-               self.update()
-       def set_y_off(self, y_off):
-               self.y_off = y_off
-               self.update()
-       def set_trigger_channel_index(self, trigger_channel_index):
-               self.trigger_channel_index = trigger_channel_index
-               self.update()
-       def set_trigger_mode_index(self, trigger_mode_index):
-               self.trigger_mode_index = trigger_mode_index
-               self.update()
-       def set_trigger_level_index(self, trigger_level_index):
-               self.trigger_level_index = trigger_level_index
-               self.update()
-       def set_trigger_index(self, trigger_index):
-               self.trigger_index = trigger_index
-               self.update()
-       def set_run(self, running):
-               self.running = running
-               self.update()
-       def set_ac_couple(self, ac_couple):
-               self.ac_couple = ac_couple
-               self.update()
-       def set_autorange(self, autorange):
-               self.autorange = autorange
-               self.update()
-       def set_sample_rate(self, sample_rate):
-               self.sample_rate = sample_rate
-               self.update()
-
-##################################################
-# Scope sink block
-##################################################
 class scope_sink_f(gr.hier_block2):
        """!
        A scope block with a gui window.
@@ -379,9 +50,9 @@
                parent,
                title='',
                sample_rate=1,
-               size=DEFAULT_WIN_SIZE,
-               frame_rate=DEFAULT_FRAME_RATE,
-               v_scale=DEFAULT_V_SCALE,
+               size=scope_window.DEFAULT_WIN_SIZE,
+               frame_rate=scope_window.DEFAULT_FRAME_RATE,
+               v_scale=scope_window.DEFAULT_V_SCALE,
                t_scale=None,
                num_inputs=1,
                ac_couple=False,
@@ -399,28 +70,54 @@
                #connect
                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)
+               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)
+               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)
+               #start input watcher
+               common.input_watcher(msgq, lambda x: 
self.controller.set(MSG_KEY, x))
                #create window
-               self.win = scope_window(
+               self.win = scope_window.scope_window(
                        parent=parent,
+                       controller=self.controller,
                        size=size,
                        title=title,
-                       scope=scope,
                        frame_rate=frame_rate,
                        num_inputs=num_inputs,
-                       sample_rate=sample_rate,
+                       sample_rate_key=SAMPLE_RATE_KEY,
                        x_per_div=t_scale,
                        y_per_div=v_scale,
                        ac_couple=ac_couple,
+                       scope_trigger_level_key=SCOPE_TRIGGER_LEVEL_KEY,
+                       scope_trigger_mode_key=SCOPE_TRIGGER_MODE_KEY,
+                       scope_trigger_channel_key=SCOPE_TRIGGER_CHANNEL_KEY,
+                       scope_num_samples_key=SCOPE_NUM_SAMPLES_KEY,
+                       msg_key=MSG_KEY,
                )
                #register callbacks from window for external use
-               self.set_sample_rate = self.win.set_sample_rate
-               #setup the input watcher
-               common.input_watcher(msgq, self._handle_msg)
+               self.set_running = self.win.set_running
+               self.set_ac_couple = self.win.set_ac_couple
+               self.set_autorange = self.win.set_autorange
+               self.set_x_per_div = self.win.set_x_per_div
+               self.set_y_per_div = self.win.set_y_per_div
+               self.set_x_off = self.win.set_x_off
+               self.set_y_off = self.win.set_y_off
+               self.set_x_divs = self.win.set_x_divs
+               self.set_y_divs = self.win.set_y_divs
+               self.set_frame_rate = self.win.set_frame_rate
 
-       def _handle_msg(self, msg):
-               nchan = int(msg.arg1())    # number of channels of data in msg
-               nsamples = int(msg.arg2()) # number of samples in each channel
-               s = msg.to_string()      # get the body of the msg as a string
-               samples = numpy.fromstring(s, numpy.float32)
-               samps_per_ch = len(samples)/nchan
-               self.win.plot([samples[samps_per_ch*i:samps_per_ch*(i+1)] for i 
in range(nchan)])
+       def set_sample_rate(self, sample_rate):
+               self.controller[SAMPLE_RATE_KEY] = sample_rate





reply via email to

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