commit-gnuradio
[Top][All Lists]
Advanced

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

[Commit-gnuradio] r9245 - gnuradio/branches/developers/jblum/glwxgui/gr-


From: jblum
Subject: [Commit-gnuradio] r9245 - gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python
Date: Mon, 11 Aug 2008 18:38:46 -0600 (MDT)

Author: jblum
Date: 2008-08-11 18:38:45 -0600 (Mon, 11 Aug 2008)
New Revision: 9245

Added:
   
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/number_window.py
   
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/numbersink_gl.py
   
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scope_window.py
   
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scopesink_gl.py
Modified:
   gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am
Log:
imported numbersink and scopesink

Modified: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am
===================================================================
--- gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am  
2008-08-12 00:17:37 UTC (rev 9244)
+++ gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/Makefile.am  
2008-08-12 00:38:45 UTC (rev 9245)
@@ -38,13 +38,17 @@
        fftsink2.py                     \
        fftsink_gl.py                   \
        fft_window.py                   \
+       numbersink2.py                  \
+       numbersink_gl.py                \
+       number_window.py                \
        plot.py                         \
        powermate.py                    \
        pubsub.py                       \
        scopesink2.py                   \
+       scopesink_gl.py                 \
+       scope_window.py                 \
        waterfallsink2.py               \
        waterfallsink_gl.py             \
        waterfall_window.py             \
        slider.py                       \
-       stdgui2.py                      \
-       numbersink2.py
+       stdgui2.py

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/number_window.py 
(from rev 9244, gnuradio/branches/features/experimental-gui/number_window.py)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/number_window.py 
                            (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/number_window.py 
    2008-08-12 00:38:45 UTC (rev 9245)
@@ -0,0 +1,183 @@
+#
+# 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 common
+import numpy
+import wx
+import pubsub
+from constants import *
+
+##################################################
+# Constants
+##################################################
+NEG_INF = float('-inf')
+SLIDER_STEPS = 100
+AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
+DEFAULT_NUMBER_RATE = 2
+DEFAULT_WIN_SIZE = (300, 300)
+DEFAULT_GAUGE_RANGE = 1000
+
+##################################################
+# Number window control panel
+##################################################
+class control_panel(wx.Panel):
+       """!
+       A control panel with wx widgits to control the averaging.
+       """
+
+       def __init__(self, parent):
+               """!
+               Create a new control panel.
+               @param parent the wx parent window
+               """
+               self.parent = parent
+               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               control_box = wx.BoxSizer(wx.VERTICAL)
+               #checkboxes for average and peak hold
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
+               self.average_check_box = common.CheckBoxController(self, 
'Average', parent.ext_controller, parent.average_key)
+               control_box.Add(self.average_check_box, 0, wx.EXPAND)
+               self.peak_hold_check_box = common.CheckBoxController(self, 
'Peak Hold', parent, PEAK_HOLD_KEY)
+               control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
+               control_box.AddSpacer(2)
+               self.avg_alpha_slider = common.LogSliderController(
+                       self, 'Avg Alpha',
+                       AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS,
+                       parent.ext_controller, parent.avg_alpha_key,
+               )
+               parent.ext_controller.subscribe(parent.average_key, 
self.avg_alpha_slider.Enable)
+               control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+               #run/stop
+               control_box.AddStretchSpacer()
+               self.run_button = common.RunStopButtonController(self, parent, 
RUNNING_KEY)
+               control_box.Add(self.run_button, 0, wx.EXPAND)
+               #set sizer
+               self.SetSizerAndFit(control_box)
+
+##################################################
+# Numbersink window with label and gauges
+##################################################
+class number_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+       def __init__(
+               self,
+               parent,
+               controller,
+               size,
+               title,
+               units,
+               show_gauge,
+               real,
+               minval,
+               maxval,
+               decimal_places,
+               average_key,
+               avg_alpha_key,
+               peak_hold,
+               msg_key,
+       ):
+               pubsub.pubsub.__init__(self)
+               wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
+               #setup
+               self.peak_val_real = NEG_INF
+               self.peak_val_imag = NEG_INF
+               self.ext_controller = controller
+               self.real = real
+               self.units = units
+               self.minval = minval
+               self.maxval = maxval
+               self.decimal_places = decimal_places
+               self.average_key = average_key
+               self.avg_alpha_key = avg_alpha_key
+               #setup the box with display and controls
+               self.control_panel = control_panel(self)
+               main_box = wx.BoxSizer(wx.HORIZONTAL)
+               sizer = wx.BoxSizer(wx.VERTICAL)
+               main_box.Add(sizer, 1, wx.EXPAND)
+               main_box.Add(self.control_panel, 0, wx.EXPAND)
+               sizer.Add(common.LabelText(self, title), 1, wx.ALIGN_CENTER)
+               self.text = wx.StaticText(self, size=(size[0], -1))
+               sizer.Add(self.text, 1, wx.EXPAND)
+               self.gauge_real = wx.Gauge(self, range=DEFAULT_GAUGE_RANGE, 
style=wx.GA_HORIZONTAL)
+               self.gauge_imag = wx.Gauge(self, range=DEFAULT_GAUGE_RANGE, 
style=wx.GA_HORIZONTAL)
+               #hide/show gauges
+               self.show_gauges(show_gauge)
+               sizer.Add(self.gauge_real, 1, wx.EXPAND)
+               sizer.Add(self.gauge_imag, 1, wx.EXPAND)
+               self.SetSizerAndFit(main_box)
+               #initial setup
+               self.ext_controller[self.average_key] = 
self.ext_controller[self.average_key]
+               self.ext_controller[self.avg_alpha_key] = 
self.ext_controller[self.avg_alpha_key]
+               self._register_set_prop(self, PEAK_HOLD_KEY, peak_hold)
+               self._register_set_prop(self, RUNNING_KEY, True)
+               #register events
+               self.ext_controller.subscribe(msg_key, self.handle_msg)
+
+       def show_gauges(self, show_gauge):
+               """!
+               Show or hide the gauges.
+               If this is real, never show the imaginary gauge.
+               @param show_gauge true to show
+               """
+               if show_gauge: self.gauge_real.Show()
+               else: self.gauge_real.Hide()
+               if show_gauge and not self.real: self.gauge_imag.Show()
+               else: self.gauge_imag.Hide()
+
+       def handle_msg(self, msg):
+               """!
+               Handle a message from the message queue.
+               Convert the string based message into a float or complex.
+               If more than one number was read, only take the last number.
+               Perform peak hold operations, set the gauges and display.
+               @param msg the number sample as a character array
+               """
+               if not self[RUNNING_KEY]: return
+               #set gauge
+               def set_gauge_value(gauge, value):
+                       gauge_val = 
DEFAULT_GAUGE_RANGE*(value-self.minval)/(self.maxval-self.minval)
+                       gauge_val = max(0, gauge_val) #clip
+                       gauge_val = min(DEFAULT_GAUGE_RANGE, gauge_val) #clip
+                       gauge.SetValue(gauge_val)
+               format_string = "%%.%df"%self.decimal_places
+               if self.real:
+                       sample = numpy.fromstring(msg, numpy.float32)[-1]
+                       if self[PEAK_HOLD_KEY]: sample = self.peak_val_real = 
max(self.peak_val_real, sample)
+                       label_text = "%s %s"%(format_string%sample, self.units)
+                       set_gauge_value(self.gauge_real, sample)
+               else:
+                       sample = numpy.fromstring(msg, numpy.complex64)[-1]
+                       if self[PEAK_HOLD_KEY]:
+                               self.peak_val_real = max(self.peak_val_real, 
sample.real)
+                               self.peak_val_imag = max(self.peak_val_imag, 
sample.imag)
+                               sample = self.peak_val_real + 
self.peak_val_imag*1j
+                       label_text = "%s + %sj %s"%(format_string%sample.real, 
format_string%sample.imag, self.units)
+                       set_gauge_value(self.gauge_real, sample.real)
+                       set_gauge_value(self.gauge_imag, sample.imag)
+               #set label text
+               self.text.SetLabel(label_text)
+               #clear peak hold
+               if not self[PEAK_HOLD_KEY]:
+                       self.peak_val_real = NEG_INF
+                       self.peak_val_imag = NEG_INF

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/numbersink_gl.py 
(from rev 9244, gnuradio/branches/features/experimental-gui/numbersink.py)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/numbersink_gl.py 
                            (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/numbersink_gl.py 
    2008-08-12 00:38:45 UTC (rev 9245)
@@ -0,0 +1,139 @@
+#
+# 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 number_window
+import common
+from gnuradio import gr, blks2
+from pubsub import pubsub
+from constants import *
+
+##################################################
+# Number sink block (wrapper for old wxgui)
+##################################################
+class _number_sink_base(gr.hier_block2, common.prop_setter):
+       """!
+       An decimator block with a number window display
+       """
+
+       def __init__(
+               self,
+               parent,
+               unit='units',
+               base_value=None, #ignore (old wrapper)
+               minval=0,
+               maxval=1,
+               factor=1,
+               decimal_places=3,
+               ref_level=0,
+               sample_rate=1,
+               number_rate=number_window.DEFAULT_NUMBER_RATE,
+               average=False,
+               avg_alpha=None,
+               label='Number Plot',
+               size=number_window.DEFAULT_WIN_SIZE,
+               peak_hold=False,
+               show_gauge=True,
+       ):
+               #ensure avg alpha
+               if avg_alpha is None: avg_alpha = 2.0/number_rate
+               #init
+               gr.hier_block2.__init__(
+                       self,
+                       "number_sink",
+                       gr.io_signature(1, 1, self._item_size),
+                       gr.io_signature(0, 0, 0),
+               )
+               #blocks
+               sd = blks2.stream_to_vector_decimator(
+                       item_size=self._item_size,
+                       sample_rate=sample_rate,
+                       vec_rate=number_rate,
+                       vec_len=1,
+               )
+               if self._real:
+                       mult = gr.multiply_const_ff(factor)
+                       add = gr.add_const_ff(ref_level)
+                       self._avg = gr.single_pole_iir_filter_ff(1.0)
+               else:
+                       mult = gr.multiply_const_cc(factor)
+                       add = gr.add_const_cc(ref_level)
+                       self._avg = gr.single_pole_iir_filter_cc(1.0)
+               msgq = gr.msg_queue(2)
+               sink = gr.message_sink(self._item_size, msgq, True)
+               #connect
+               self.connect(self, sd, mult, add, self._avg, sink)
+               #setup averaging
+               self._avg_alpha = avg_alpha
+               self.set_average(average)
+               self.set_avg_alpha(avg_alpha)
+               #controller
+               self.controller = pubsub()
+               self.controller.subscribe(SAMPLE_RATE_KEY, sd.set_sample_rate)
+               self.controller.subscribe(AVERAGE_KEY, self.set_average)
+               self.controller.publish(AVERAGE_KEY, self.get_average)
+               self.controller.subscribe(AVG_ALPHA_KEY, self.set_avg_alpha)
+               self.controller.publish(AVG_ALPHA_KEY, self.get_avg_alpha)
+               #start input watcher
+               def set_msg(msg): self.controller[MSG_KEY] = msg
+               common.input_watcher(msgq, set_msg)
+               #create window
+               self.win = number_window.number_window(
+                       parent=parent,
+                       controller=self.controller,
+                       size=size,
+                       title=label,
+                       units=unit,
+                       real=self._real,
+                       minval=minval,
+                       maxval=maxval,
+                       decimal_places=decimal_places,
+                       show_gauge=show_gauge,
+                       average_key=AVERAGE_KEY,
+                       avg_alpha_key=AVG_ALPHA_KEY,
+                       peak_hold=peak_hold,
+                       msg_key=MSG_KEY,
+               )
+               #register callbacks from window for external use
+               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
+                       setattr(self, attr, getattr(self.win, attr))
+               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
+
+       def get_average(self): return self._average
+       def set_average(self, average):
+               self._average = average
+               if self.get_average(): self._avg.set_taps(self.get_avg_alpha())
+               else: self._avg.set_taps(1.0)
+
+       def get_avg_alpha(self): return self._avg_alpha
+       def set_avg_alpha(self, avg_alpha):
+               self._avg_alpha = avg_alpha
+               self.set_average(self.get_average())
+
+class number_sink_f(_number_sink_base):
+       _item_size = gr.sizeof_float
+       _real = True
+
+class number_sink_c(_number_sink_base):
+       _item_size = gr.sizeof_gr_complex
+       _real = False

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scope_window.py 
(from rev 9244, gnuradio/branches/features/experimental-gui/scope_window.py)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scope_window.py  
                            (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scope_window.py  
    2008-08-12 00:38:45 UTC (rev 9245)
@@ -0,0 +1,330 @@
+#
+# 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 pubsub
+from constants import *
+
+##################################################
+# Constants
+##################################################
+DEFAULT_FRAME_RATE = 30
+DEFAULT_WIN_SIZE = (600, 300)
+DEFAULT_V_SCALE = 1000
+TRIGGER_MODES = (
+       ('Off', 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
+
+##################################################
+# 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 options
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Trigger Options'), 0, 
wx.ALIGN_CENTER)
+               control_box.AddSpacer(2)
+               #trigger mode
+               self.trigger_mode_chooser = common.DropDownController(self, 
'Mode', TRIGGER_MODES, parent, TRIGGER_MODE_KEY)
+               control_box.Add(self.trigger_mode_chooser, 0, wx.EXPAND)
+               #trigger level
+               self.trigger_level_chooser = common.DropDownController(self, 
'Level', TRIGGER_LEVELS, parent, TRIGGER_LEVEL_KEY)
+               parent.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, TRIGGER_CHANNEL_KEY)
+               parent.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
+               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.subscribe(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.subscribe(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, 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, 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, 
RUNNING_KEY)
+               control_box.Add(self.run_button, 0, wx.EXPAND)
+               #set sizer
+               self.SetSizerAndFit(control_box)
+
+       ##################################################
+       # Event handlers
+       ##################################################
+       def _on_incr_x_divs(self, event):
+               self.parent.set_x_per_div(
+                       common.get_clean_incr(self.parent[X_PER_DIV_KEY]))
+       def _on_decr_x_divs(self, event):
+               self.parent.set_x_per_div(
+                       common.get_clean_decr(self.parent[X_PER_DIV_KEY]))
+       def _on_incr_y_divs(self, event):
+               self.parent.set_y_per_div(
+                       common.get_clean_incr(self.parent[Y_PER_DIV_KEY]))
+       def _on_decr_y_divs(self, event):
+               self.parent.set_y_per_div(
+                       common.get_clean_decr(self.parent[Y_PER_DIV_KEY]))
+       def _on_incr_y_off(self, event):
+               self.parent.set_y_off(
+                       self.parent[Y_OFF_KEY] + self.parent[Y_PER_DIV_KEY])
+       def _on_decr_y_off(self, event):
+               self.parent.set_y_off(
+                       self.parent[Y_OFF_KEY] - self.parent[Y_PER_DIV_KEY])
+
+##################################################
+# Scope window with plotter and control panel
+##################################################
+class scope_window(wx.Panel, pubsub.pubsub, common.prop_setter):
+       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,
+       ):
+               pubsub.pubsub.__init__(self)
+               #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.channel_plotter(self)
+               self.plotter.SetSize(wx.Size(*size))
+               self.plotter.set_title(title)
+               self.plotter.enable_legend(self.num_inputs > 1)
+               self.plotter.enable_point_label(True)
+               #setup the box with plot and controls
+               self.control_panel = control_panel(self)
+               main_box = wx.BoxSizer(wx.HORIZONTAL)
+               main_box.Add(self.plotter, 1, wx.EXPAND)
+               main_box.Add(self.control_panel, 0, wx.EXPAND)
+               self.SetSizerAndFit(main_box)
+               #initial setup
+               self._register_set_prop(self, RUNNING_KEY, True)
+               self._register_set_prop(self, AC_COUPLE_KEY, ac_couple)
+               self._register_set_prop(self, AUTORANGE_KEY, autorange)
+               self._register_set_prop(self, X_PER_DIV_KEY, x_per_div)
+               self._register_set_prop(self, Y_PER_DIV_KEY, y_per_div)
+               self._register_set_prop(self, X_OFF_KEY, 0)
+               self._register_set_prop(self, Y_OFF_KEY, 0)
+               self._register_set_prop(self, X_DIVS_KEY, 8)
+               self._register_set_prop(self, Y_DIVS_KEY, 8)
+               self._register_set_prop(self, FRAME_RATE_KEY, frame_rate)
+               self._register_set_prop(self, TRIGGER_CHANNEL_KEY, 1)
+               self._register_set_prop(self, TRIGGER_MODE_KEY, 1)
+               self._register_set_prop(self, TRIGGER_LEVEL_KEY, None)
+               #register events
+               self.ext_controller.subscribe(msg_key, self.handle_msg)
+               self.ext_controller.subscribe(sample_rate_key, 
self.update_decim)
+               self.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.subscribe(key, self.update_grid)
+               #initial update, dont do this here, wait for handle_msg #HACK
+               #self.update_grid()
+               #self.update_decim()
+
+       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[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_KEY]
+                       self.ext_controller[self.scope_trigger_mode_key] = 
self[TRIGGER_MODE_KEY]
+                       trigger_level = self[TRIGGER_LEVEL_KEY]
+                       if trigger_level is None: 
self.ext_controller[self.scope_trigger_level_key] = ''
+                       else:
+                               samples = sampleses[self[TRIGGER_CHANNEL_KEY]]
+                               
self.ext_controller[self.scope_trigger_level_key] = \
+                               
trigger_level*(numpy.max(samples)-numpy.min(samples))/2 + numpy.average(samples)
+                       #ac coupling
+                       if self[AC_COUPLE_KEY]:
+                               sampleses = [samples - numpy.average(samples) 
for samples in sampleses]
+                       #autorange
+                       if self[AUTORANGE_KEY] and time.time() - 
self.autorange_ts > AUTORANGE_UPDATE_RATE:
+                               bounds = [common.get_min_max(samples) for 
samples in sampleses]
+                               y_min = numpy.min(*[bound[0] for bound in 
bounds])
+                               y_max = numpy.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_KEY])
+                               if y_per_div != self[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[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[X_PER_DIV_KEY]*self[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='Ch%d'%(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[FRAME_RATE_KEY]
+               self.decim = max(1, int(decim))
+
+       def update_grid(self, *args):
+               #grid parameters
+               x_per_div = self[X_PER_DIV_KEY]
+               y_per_div = self[Y_PER_DIV_KEY]
+               x_off = self[X_OFF_KEY]
+               y_off = self[Y_OFF_KEY]
+               x_divs = self[X_DIVS_KEY]
+               y_divs = self[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_label('Time', 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_label('Voltage')
+               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()

Copied: 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scopesink_gl.py 
(from rev 9244, gnuradio/branches/features/experimental-gui/scopesink.py)
===================================================================
--- 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scopesink_gl.py  
                            (rev 0)
+++ 
gnuradio/branches/developers/jblum/glwxgui/gr-wxgui/src/python/scopesink_gl.py  
    2008-08-12 00:38:45 UTC (rev 9245)
@@ -0,0 +1,124 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+##################################################
+# Imports
+##################################################
+import scope_window
+import common
+from gnuradio import gr
+from pubsub import pubsub
+from constants import *
+
+##################################################
+# Scope sink block (wrapper for old wxgui)
+##################################################
+class _scope_sink_base(gr.hier_block2, common.prop_setter):
+       """!
+       A scope block with a gui window.
+       """
+
+       def __init__(
+               self,
+               parent,
+               title='',
+               sample_rate=1,
+               size=scope_window.DEFAULT_WIN_SIZE,
+               frame_decim=None, #ignore (old wrapper)
+               v_scale=scope_window.DEFAULT_V_SCALE,
+               t_scale=None,
+               num_inputs=1,
+               ac_couple=False,
+               frame_rate=scope_window.DEFAULT_FRAME_RATE,
+       ):
+               if t_scale is None: t_scale = 0.001
+               #init
+               gr.hier_block2.__init__(
+                       self,
+                       "scope_sink",
+                       gr.io_signature(num_inputs, num_inputs, 
self._item_size),
+                       gr.io_signature(0, 0, 0),
+               )
+               #scope
+               msgq = gr.msg_queue(2)
+               scope = gr.oscope_sink_f(sample_rate, msgq)
+               #connect
+               if self._real:
+                       for i in range(num_inputs):
+                               self.connect((self, i), (scope, i))
+               else:
+                       for i in range(num_inputs):
+                               c2f = gr.complex_to_float() 
+                               self.connect((self, i), c2f)
+                               self.connect((c2f, 0), (scope, 2*i+0))
+                               self.connect((c2f, 1), (scope, 2*i+1))
+                       num_inputs *= 2
+               #controller
+               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.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.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
+               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,
+                       controller=self.controller,
+                       size=size,
+                       title=title,
+                       frame_rate=frame_rate,
+                       num_inputs=num_inputs,
+                       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
+               for attr in filter(lambda a: a.startswith('set_'), 
dir(self.win)):
+                       setattr(self, attr, getattr(self.win, attr))
+               self._register_set_prop(self.controller, SAMPLE_RATE_KEY)
+
+class scope_sink_f(_scope_sink_base):
+       _item_size = gr.sizeof_float
+       _real = True
+
+class scope_sink_c(_scope_sink_base):
+       _item_size = gr.sizeof_gr_complex
+       _real = False





reply via email to

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