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