summaryrefslogtreecommitdiff
path: root/gr-wxgui
diff options
context:
space:
mode:
authorMartin Dudok van Heel2010-04-26 19:40:41 +0200
committerMartin Dudok van Heel2010-04-26 19:40:41 +0200
commit49fa13f9fce2037d176c86bf326a7e25a78b72a5 (patch)
treea98cf5026d0d6eec9422b2348546ce258f9bab23 /gr-wxgui
parenta82c784262b9eae78f4c2157b6434495f8ff76bc (diff)
downloadgnuradio-49fa13f9fce2037d176c86bf326a7e25a78b72a5.tar.gz
gnuradio-49fa13f9fce2037d176c86bf326a7e25a78b72a5.tar.bz2
gnuradio-49fa13f9fce2037d176c86bf326a7e25a78b72a5.zip
Add analog CRT screen afterglow emulation for gr-wxgui
Diffstat (limited to 'gr-wxgui')
-rw-r--r--gr-wxgui/src/python/constants.py2
-rw-r--r--gr-wxgui/src/python/fft_window.py51
-rw-r--r--gr-wxgui/src/python/fftsink_gl.py37
-rw-r--r--gr-wxgui/src/python/fftsink_nongl.py60
-rw-r--r--gr-wxgui/src/python/plot.py98
-rw-r--r--gr-wxgui/src/python/plotter/channel_plotter.py1
-rw-r--r--gr-wxgui/src/python/plotter/plotter_base.py36
-rw-r--r--gr-wxgui/src/python/scope_window.py52
-rw-r--r--gr-wxgui/src/python/scopesink_gl.py32
9 files changed, 340 insertions, 29 deletions
diff --git a/gr-wxgui/src/python/constants.py b/gr-wxgui/src/python/constants.py
index 8ff7fa8fe..517a55283 100644
--- a/gr-wxgui/src/python/constants.py
+++ b/gr-wxgui/src/python/constants.py
@@ -27,6 +27,8 @@ ALPHA_KEY = 'alpha'
AUTORANGE_KEY = 'autorange'
AVERAGE_KEY = 'average'
AVG_ALPHA_KEY = 'avg_alpha'
+EMULATE_ANALOG_KEY = 'emulate_analog'
+ANALOG_ALPHA_KEY = 'analog_alpha'
BASEBAND_FREQ_KEY = 'baseband_freq'
BETA_KEY = 'beta'
COLOR_MODE_KEY = 'color_mode'
diff --git a/gr-wxgui/src/python/fft_window.py b/gr-wxgui/src/python/fft_window.py
index e025c28dd..67bb65b26 100644
--- a/gr-wxgui/src/python/fft_window.py
+++ b/gr-wxgui/src/python/fft_window.py
@@ -37,6 +37,7 @@ import forms
##################################################
SLIDER_STEPS = 100
AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
+ANALOG_ALPHA_MIN_EXP, ANALOG_ALPHA_MAX_EXP = -2, 0
DEFAULT_WIN_SIZE = (600, 300)
DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'fft_rate', 30)
DB_DIV_MIN, DB_DIV_MAX = 1, 20
@@ -95,7 +96,38 @@ class control_panel(wx.Panel):
for widget in (avg_alpha_text, avg_alpha_slider):
parent.subscribe(AVERAGE_KEY, widget.Enable)
widget.Enable(parent[AVERAGE_KEY])
+ parent.subscribe(AVERAGE_KEY, widget.ShowItems)
+ #allways show initially, so room is reserved for them
+ widget.ShowItems(True) # (parent[AVERAGE_KEY])
+
+ parent.subscribe(AVERAGE_KEY, self._update_layout)
+
+ forms.check_box(
+ sizer=options_box, parent=self, label='Emulate Analog',
+ ps=parent, key=EMULATE_ANALOG_KEY,
+ )
+ #static text and slider for analog alpha
+ analog_alpha_text = forms.static_text(
+ sizer=options_box, parent=self, label='Analog Alpha',
+ converter=forms.float_converter(lambda x: '%.4f'%x),
+ ps=parent, key=ANALOG_ALPHA_KEY, width=50,
+ )
+ analog_alpha_slider = forms.log_slider(
+ sizer=options_box, parent=self,
+ min_exp=ANALOG_ALPHA_MIN_EXP,
+ max_exp=ANALOG_ALPHA_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=ANALOG_ALPHA_KEY,
+ )
+ for widget in (analog_alpha_text, analog_alpha_slider):
+ parent.subscribe(EMULATE_ANALOG_KEY, widget.Enable)
+ widget.Enable(parent[EMULATE_ANALOG_KEY])
+ parent.subscribe(EMULATE_ANALOG_KEY, widget.ShowItems)
+ #allways show initially, so room is reserved for them
+ widget.ShowItems(True) # (parent[EMULATE_ANALOG_KEY])
+ parent.subscribe(EMULATE_ANALOG_KEY, self._update_layout)
+
#trace menu
for trace in TRACES:
trace_box = wx.BoxSizer(wx.HORIZONTAL)
@@ -142,6 +174,7 @@ class control_panel(wx.Panel):
)
#set sizer
self.SetSizerAndFit(control_box)
+
#mouse wheel event
def on_mouse_wheel(event):
if event.GetWheelRotation() < 0: self._on_incr_ref_level(event)
@@ -159,6 +192,14 @@ class control_panel(wx.Panel):
self.parent[Y_PER_DIV_KEY] = min(DB_DIV_MAX, common.get_clean_incr(self.parent[Y_PER_DIV_KEY]))
def _on_decr_db_div(self, event):
self.parent[Y_PER_DIV_KEY] = max(DB_DIV_MIN, common.get_clean_decr(self.parent[Y_PER_DIV_KEY]))
+ ##################################################
+ # subscriber handlers
+ ##################################################
+ def _update_layout(self,key):
+ # Just ignore the key value we get
+ # we only need to now that the visability or size of something has changed
+ self.parent.Layout()
+ #self.parent.Fit()
##################################################
# FFT window with plotter and control panel
@@ -181,7 +222,10 @@ class fft_window(wx.Panel, pubsub.pubsub):
avg_alpha_key,
peak_hold,
msg_key,
+ emulate_analog,
+ analog_alpha,
):
+
pubsub.pubsub.__init__(self)
#setup
self.samples = EMPTY_TRACE
@@ -202,6 +246,8 @@ class fft_window(wx.Panel, pubsub.pubsub):
self[REF_LEVEL_KEY] = ref_level
self[BASEBAND_FREQ_KEY] = baseband_freq
self[RUNNING_KEY] = True
+ self[EMULATE_ANALOG_KEY] = emulate_analog
+ self[ANALOG_ALPHA_KEY] = analog_alpha
for trace in TRACES:
#a function that returns a function
#so the function wont use local trace
@@ -230,6 +276,8 @@ class fft_window(wx.Panel, pubsub.pubsub):
self.plotter.enable_legend(True)
self.plotter.enable_point_label(True)
self.plotter.enable_grid_lines(True)
+ self.plotter.set_emulate_analog(emulate_analog)
+ self.plotter.set_analog_alpha(analog_alpha)
#setup the box with plot and controls
self.control_panel = control_panel(self)
main_box = wx.BoxSizer(wx.HORIZONTAL)
@@ -245,9 +293,12 @@ class fft_window(wx.Panel, pubsub.pubsub):
Y_PER_DIV_KEY, X_DIVS_KEY,
Y_DIVS_KEY, REF_LEVEL_KEY,
): self.subscribe(key, self.update_grid)
+ self.subscribe(EMULATE_ANALOG_KEY, self.plotter.set_emulate_analog)
+ self.subscribe(ANALOG_ALPHA_KEY, self.plotter.set_analog_alpha)
#initial update
self.update_grid()
+
def autoscale(self, *args):
"""
Autoscale the fft plot to the last frame.
diff --git a/gr-wxgui/src/python/fftsink_gl.py b/gr-wxgui/src/python/fftsink_gl.py
index 9d683d697..564764487 100644
--- a/gr-wxgui/src/python/fftsink_gl.py
+++ b/gr-wxgui/src/python/fftsink_gl.py
@@ -27,6 +27,7 @@ import common
from gnuradio import gr, blks2
from pubsub import pubsub
from constants import *
+import math
##################################################
# FFT sink block (wrapper for old wxgui)
@@ -52,9 +53,20 @@ class _fft_sink_base(gr.hier_block2, common.wxgui_hb):
title='',
size=fft_window.DEFAULT_WIN_SIZE,
peak_hold=False,
+ emulate_analog=False,
+ analog_alpha=None,
):
#ensure avg alpha
if avg_alpha is None: avg_alpha = 2.0/fft_rate
+ #ensure analog alpha
+ if analog_alpha is None:
+ actual_fft_rate=float(sample_rate/fft_size)/float(max(1,int(float((sample_rate/fft_size)/fft_rate))))
+ #print "requested_fft_rate ",fft_rate
+ #print "actual_fft_rate ",actual_fft_rate
+ analog_cutoff_freq=0.5 # Hertz
+ #calculate alpha from wanted cutoff freq
+ analog_alpha = 1.0 - math.exp(-2.0*math.pi*analog_cutoff_freq/actual_fft_rate)
+
#init
gr.hier_block2.__init__(
self,
@@ -73,6 +85,8 @@ class _fft_sink_base(gr.hier_block2, common.wxgui_hb):
)
msgq = gr.msg_queue(2)
sink = gr.message_sink(gr.sizeof_float*fft_size, msgq, True)
+
+
#controller
self.controller = pubsub()
self.controller.subscribe(AVERAGE_KEY, fft.set_average)
@@ -100,6 +114,8 @@ class _fft_sink_base(gr.hier_block2, common.wxgui_hb):
avg_alpha_key=AVG_ALPHA_KEY,
peak_hold=peak_hold,
msg_key=MSG_KEY,
+ emulate_analog=emulate_analog,
+ analog_alpha=analog_alpha,
)
common.register_access_methods(self, self.win)
setattr(self.win, 'set_baseband_freq', getattr(self, 'set_baseband_freq')) #BACKWARDS
@@ -131,11 +147,14 @@ class test_app_block (stdgui2.std_top_block):
fft_size = 256
# build our flow graph
- input_rate = 20.48e3
+ input_rate = 2048.0e3
+
+ #Generate some noise
+ noise =gr.noise_source_c(gr.GR_UNIFORM, 1.0/10)
# Generate a complex sinusoid
#src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
- src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+ src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 57.50e3, 1)
# We add these throttle blocks so that this demo doesn't
# suck down all the CPU available. Normally you wouldn't use these.
@@ -146,17 +165,25 @@ class test_app_block (stdgui2.std_top_block):
ref_level=0, y_per_div=20, y_divs=10)
vbox.Add (sink1.win, 1, wx.EXPAND)
- self.connect(src1, thr1, sink1)
+ combine1=gr.add_cc()
+ self.connect(src1, (combine1,0))
+ self.connect(noise,(combine1,1))
+ self.connect(combine1,thr1, sink1)
#src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
- src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+ src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 57.50e3, 1)
thr2 = gr.throttle(gr.sizeof_float, input_rate)
sink2 = fft_sink_f (panel, title="Real Data", fft_size=fft_size*2,
sample_rate=input_rate, baseband_freq=100e3,
ref_level=0, y_per_div=20, y_divs=10)
vbox.Add (sink2.win, 1, wx.EXPAND)
- self.connect(src2, thr2, sink2)
+ combine2=gr.add_ff()
+ c2f2=gr.complex_to_float()
+
+ self.connect(src2, (combine2,0))
+ self.connect(noise,c2f2,(combine2,1))
+ self.connect(combine2, thr2,sink2)
def main ():
app = stdgui2.stdapp (test_app_block, "FFT Sink Test App")
diff --git a/gr-wxgui/src/python/fftsink_nongl.py b/gr-wxgui/src/python/fftsink_nongl.py
index ca5e91fdb..f1c1f4396 100644
--- a/gr-wxgui/src/python/fftsink_nongl.py
+++ b/gr-wxgui/src/python/fftsink_nongl.py
@@ -37,7 +37,7 @@ class fft_sink_base(object):
y_divs=8, ref_level=50,
sample_rate=1, fft_size=512,
fft_rate=default_fft_rate,
- average=False, avg_alpha=None, title='', peak_hold=False):
+ average=False, avg_alpha=None, title='', peak_hold=False,emulate_analog=False,analog_alpha=0.2):
# initialize common attributes
self.baseband_freq = baseband_freq
@@ -52,6 +52,9 @@ class fft_sink_base(object):
self.avg_alpha = 2.0 / fft_rate
else:
self.avg_alpha = avg_alpha
+ self.emulate_analog = emulate_analog
+ self.analog_alpha = analog_alpha
+
self.title = title
self.peak_hold = peak_hold
self.input_is_real = input_is_real
@@ -75,6 +78,14 @@ class fft_sink_base(object):
self.peak_hold = enable
self.win.set_peak_hold(enable)
+ def set_emulate_analog(self, enable):
+ self.emulate_analog = enable
+ self.win.set_emulate_analog(enable)
+
+ def set_analog_alpha(self, analog_alpha):
+ self.analog_alpha = analog_alpha
+ self.win.set_analog_alpha(analog_alpha)
+
def set_avg_alpha(self, avg_alpha):
self.avg_alpha = avg_alpha
@@ -93,7 +104,7 @@ class fft_sink_f(gr.hier_block2, fft_sink_base):
def __init__(self, parent, baseband_freq=0, ref_scale=2.0,
y_per_div=10, y_divs=8, ref_level=50, sample_rate=1, fft_size=512,
fft_rate=default_fft_rate, average=False, avg_alpha=None,
- title='', size=default_fftsink_size, peak_hold=False):
+ title='', size=default_fftsink_size, peak_hold=False, emulate_analog=False,analog_alpha=0.2):
gr.hier_block2.__init__(self, "fft_sink_f",
gr.io_signature(1, 1, gr.sizeof_float),
@@ -104,7 +115,7 @@ class fft_sink_f(gr.hier_block2, fft_sink_base):
sample_rate=sample_rate, fft_size=fft_size,
fft_rate=fft_rate,
average=average, avg_alpha=avg_alpha, title=title,
- peak_hold=peak_hold)
+ peak_hold=peak_hold,emulate_analog=emulate_analog,analog_alpha=analog_alpha)
self.s2p = gr.stream_to_vector(gr.sizeof_float, self.fft_size)
self.one_in_n = gr.keep_one_in_n(gr.sizeof_float * self.fft_size,
@@ -131,12 +142,14 @@ class fft_sink_f(gr.hier_block2, fft_sink_base):
self.win = fft_window(self, parent, size=size)
self.set_average(self.average)
self.set_peak_hold(self.peak_hold)
+ self.set_emulate_analog(self.emulate_analog)
+ self.set_analog_alpha(self.analog_alpha)
class fft_sink_c(gr.hier_block2, fft_sink_base):
def __init__(self, parent, baseband_freq=0, ref_scale=2.0,
y_per_div=10, y_divs=8, ref_level=50, sample_rate=1, fft_size=512,
fft_rate=default_fft_rate, average=False, avg_alpha=None,
- title='', size=default_fftsink_size, peak_hold=False):
+ title='', size=default_fftsink_size, peak_hold=False, emulate_analog=False,analog_alpha=0.2):
gr.hier_block2.__init__(self, "fft_sink_c",
gr.io_signature(1, 1, gr.sizeof_gr_complex),
@@ -147,7 +160,7 @@ class fft_sink_c(gr.hier_block2, fft_sink_base):
sample_rate=sample_rate, fft_size=fft_size,
fft_rate=fft_rate,
average=average, avg_alpha=avg_alpha, title=title,
- peak_hold=peak_hold)
+ peak_hold=peak_hold, emulate_analog=emulate_analog,analog_alpha=analog_alpha)
self.s2p = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size)
self.one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * self.fft_size,
@@ -173,6 +186,8 @@ class fft_sink_c(gr.hier_block2, fft_sink_base):
self.win = fft_window(self, parent, size=size)
self.set_average(self.average)
+ self.set_emulate_analog(self.emulate_analog)
+ self.set_analog_alpha(self.analog_alpha)
self.set_peak_hold(self.peak_hold)
@@ -236,6 +251,9 @@ class control_panel(wx.Panel):
self.average_check_box = wx.CheckBox(parent=self, style=wx.CHK_2STATE, label="Average")
self.average_check_box.Bind(wx.EVT_CHECKBOX, parent.on_average)
control_box.Add(self.average_check_box, 0, wx.EXPAND)
+ self.emulate_analog_check_box = wx.CheckBox(parent=self, style=wx.CHK_2STATE, label="Emulate Analog")
+ self.emulate_analog_check_box.Bind(wx.EVT_CHECKBOX, parent.on_emulate_analog)
+ control_box.Add(self.emulate_analog_check_box, 0, wx.EXPAND)
self.peak_hold_check_box = wx.CheckBox(parent=self, style=wx.CHK_2STATE, label="Peak Hold")
self.peak_hold_check_box.Bind(wx.EVT_CHECKBOX, parent.on_peak_hold)
control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)
@@ -276,6 +294,7 @@ class control_panel(wx.Panel):
"""
#update checkboxes
self.average_check_box.SetValue(self.parent.fftsink.average)
+ self.emulate_analog_check_box.SetValue(self.parent.fftsink.emulate_analog)
self.peak_hold_check_box.SetValue(self.parent.fftsink.peak_hold)
#update radio buttons
try:
@@ -306,6 +325,10 @@ class fft_window (wx.Panel):
self.peak_hold = False
self.peak_vals = None
+
+ self.emulate_analog=False
+ self.analog_alpha=0.2
+
self.plot.SetEnableGrid (True)
# self.SetEnableZoom (True)
@@ -394,6 +417,14 @@ class fft_window (wx.Panel):
y_range = ymin, ymax
self.plot.Draw (graphics, xAxis=x_range, yAxis=y_range, step=self.fftsink.y_per_div)
+ def set_emulate_analog(self, enable):
+ self.emulate_analog = enable
+ self.plot.set_emulate_analog( enable)
+
+ def set_analog_alpha(self, analog_alpha):
+ self.analog_alpha = analog_alpha
+ self.plot.set_analog_alpha(analog_alpha)
+
def set_peak_hold(self, enable):
self.peak_hold = enable
self.peak_vals = None
@@ -403,6 +434,11 @@ class fft_window (wx.Panel):
self.fftsink.set_average(evt.IsChecked())
self.control_panel.update()
+ def on_emulate_analog(self, evt):
+ # print "on_analog"
+ self.fftsink.set_emulate_analog(evt.IsChecked())
+ self.control_panel.update()
+
def on_peak_hold(self, evt):
# print "on_peak_hold"
self.fftsink.set_peak_hold(evt.IsChecked())
@@ -486,9 +522,11 @@ class fft_window (wx.Panel):
self.id_y_per_div_10 = wx.NewId()
self.id_y_per_div_20 = wx.NewId()
self.id_average = wx.NewId()
+ self.id_emulate_analog = wx.NewId()
self.id_peak_hold = wx.NewId()
self.plot.Bind(wx.EVT_MENU, self.on_average, id=self.id_average)
+ self.plot.Bind(wx.EVT_MENU, self.on_emulate_analog, id=self.id_emulate_analog)
self.plot.Bind(wx.EVT_MENU, self.on_peak_hold, id=self.id_peak_hold)
self.plot.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level)
self.plot.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level)
@@ -504,6 +542,7 @@ class fft_window (wx.Panel):
menu = wx.Menu()
self.popup_menu = menu
menu.AppendCheckItem(self.id_average, "Average")
+ menu.AppendCheckItem(self.id_emulate_analog, "Emulate Analog")
menu.AppendCheckItem(self.id_peak_hold, "Peak Hold")
menu.Append(self.id_incr_ref_level, "Incr Ref Level")
menu.Append(self.id_decr_ref_level, "Decr Ref Level")
@@ -519,6 +558,7 @@ class fft_window (wx.Panel):
self.checkmarks = {
self.id_average : lambda : self.fftsink.average,
+ self.id_emulate_analog : lambda : self.fftsink.emulate_analog,
self.id_peak_hold : lambda : self.fftsink.peak_hold,
self.id_y_per_div_1 : lambda : self.fftsink.y_per_div == 1,
self.id_y_per_div_2 : lambda : self.fftsink.y_per_div == 2,
@@ -561,11 +601,11 @@ class test_app_block (stdgui2.std_top_block):
fft_size = 256
# build our flow graph
- input_rate = 20.48e3
+ input_rate = 100*20.48e3
# Generate a complex sinusoid
- #src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
- src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+ #src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 100*2e3, 1)
+ src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 100*5.75e3, 1)
# We add these throttle blocks so that this demo doesn't
# suck down all the CPU available. Normally you wouldn't use these.
@@ -578,8 +618,8 @@ class test_app_block (stdgui2.std_top_block):
self.connect(src1, thr1, sink1)
- #src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
- src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
+ #src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 100*2e3, 1)
+ src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 100*5.75e3, 1)
thr2 = gr.throttle(gr.sizeof_float, input_rate)
sink2 = fft_sink_f (panel, title="Real Data", fft_size=fft_size*2,
sample_rate=input_rate, baseband_freq=100e3,
diff --git a/gr-wxgui/src/python/plot.py b/gr-wxgui/src/python/plot.py
index c104b0ea5..c5557cb6b 100644
--- a/gr-wxgui/src/python/plot.py
+++ b/gr-wxgui/src/python/plot.py
@@ -36,6 +36,9 @@
#
# May 27, 2007 Johnathan Corgan (jcorgan@corganenterprises.com)
# - Converted from numarray to numpy
+#
+# Apr 23, 2010 Martin Dudok van Heel (http://www.olifantasia.com/gnuradio/contact_olifantasia.gif)
+# - Added Emulate Analog option (emulate after glow of an analog CRT display using IIR)
"""
This is a simple light weight plotting module that can be used with
@@ -422,6 +425,11 @@ class PlotCanvas(wx.Window):
def __init__(self, parent, id = -1, pos=wx.DefaultPosition,
size=wx.DefaultSize, style= wx.DEFAULT_FRAME_STYLE, name= ""):
+
+ self.emulate_analog=False
+ self.alpha=0.3
+ self.decimation=10
+ self.decim_counter=0
"""Constucts a window, which can be a child of a frame, dialog or
any other non-control window"""
@@ -488,6 +496,14 @@ class PlotCanvas(wx.Window):
# platforms at initialization, but little harm done.
self.OnSize(None) # sets the initial size based on client size
# UNCONDITIONAL, needed to create self._Buffer
+
+
+ def set_emulate_analog(self, enable):
+ self.emulate_analog = enable
+
+ def set_analog_alpha(self, analog_alpha):
+ self.alpha = analog_alpha
+
# SaveFile
def SaveFile(self, fileName= ''):
@@ -791,12 +807,19 @@ class PlotCanvas(wx.Window):
if dc == None:
# sets new dc and clears it
- dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer)
- dc.Clear()
-
+ if self.emulate_analog:
+ dc = wx.MemoryDC()
+ dc.SelectObject(self._Buffer)
+ dc.Clear()
+ else:
+ dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer)
+ dc.Clear()
+
dc.BeginDrawing()
# dc.Clear()
-
+
+
+
# set font size for every thing but title and legend
dc.SetFont(self._getFont(self._fontSizeAxis))
@@ -818,6 +841,15 @@ class PlotCanvas(wx.Window):
self.last_draw = (graphics, xAxis, yAxis) # saves most recient values
+ if False:
+ ptx,pty,rectWidth,rectHeight= self._point2ClientCoord(p1, p2)
+ #dc.SetPen(wx.Pen(wx.BLACK))
+ dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) ) #wx.SOLID wx.TRANSPARENT ) )
+ #dc.SetLogicalFunction(wx.INVERT) #wx.XOR wx.INVERT
+ dc.DrawRectangle( ptx,pty, rectWidth,rectHeight)
+ #dc.SetBrush(wx.Brush( wx.WHITE, wx.SOLID ) )
+ #dc.SetLogicalFunction(wx.COPY)
+
# Get ticks and textExtents for axis if required
if self._xSpec is not 'none':
if self._xUseScopeTicks:
@@ -874,8 +906,11 @@ class PlotCanvas(wx.Window):
scale = (self.plotbox_size-textSize_scale) / (p2-p1)* _numpy.array((1,-1))
shift = -p1*scale + self.plotbox_origin + textSize_shift * _numpy.array((1,-1))
self._pointScale= scale # make available for mouse events
- self._pointShift= shift
+ self._pointShift= shift
+
+ #dc.SetLogicalFunction(wx.INVERT) #wx.XOR wx.INVERT
self._drawAxes(dc, p1, p2, scale, shift, xticks, yticks)
+ #dc.SetLogicalFunction(wx.COPY)
graphics.scaleAndShift(scale, shift)
graphics.setPrinterScale(self.printerScale) # thicken up lines and markers if printing
@@ -885,11 +920,44 @@ class PlotCanvas(wx.Window):
dc.SetClippingRegion(ptx,pty,rectWidth,rectHeight)
# Draw the lines and markers
#start = _time.clock()
+
graphics.draw(dc)
# print "entire graphics drawing took: %f second"%(_time.clock() - start)
# remove the clipping region
dc.DestroyClippingRegion()
dc.EndDrawing()
+
+
+ if self.emulate_analog:
+ dc=None
+ self._Buffer.CopyToBuffer(self._Bufferarray) #, format=wx.BitmapBufferFormat_RGB, stride=-1)
+ ## do the IIR filter
+ alpha_int=int(float(self.alpha*256))
+ if True:
+ _numpy.add(self._Bufferarray,0,self._Buffer3array)
+ _numpy.multiply(self._Buffer3array,alpha_int,self._Buffer3array)
+ _numpy.multiply(self._Buffer2array,(256-alpha_int),self._Buffer2array)
+ _numpy.add(self._Buffer3array,self._Buffer2array,self._Buffer2array)
+ _numpy.right_shift(self._Buffer2array,8,self._Buffer2array)
+ elif False:
+ self._Buffer2array=(self._Bufferarray.astype(_numpy.uint32) *alpha_int + self._Buffer2array*(256-alpha_int)).__rshift__(8)
+ elif False:
+ self._Buffer2array *=(256-alpha_int)
+ self._Buffer2array +=self._Bufferarray.astype(_numpy.uint32)*alpha_int
+ self._Buffer2array /=256
+
+ ##copy back to image buffer
+ self._Buffer2.CopyFromBuffer(self._Buffer2array.astype(_numpy.uint8)) #, format=wx.BitmapBufferFormat_RGB, stride=-1)
+
+ #draw to the screen
+ #self.decim_counter=self.decim_counter+1
+ if True: #self.decim_counter>self.decimation:
+ #self.decim_counter=0
+ dc2 = wx.ClientDC( self )
+ dc2.BeginDrawing()
+ dc2.DrawBitmap(self._Buffer2, 0, 0, False)
+ #dc2.DrawBitmap(self._Buffer, 0, 0, False)
+ dc2.EndDrawing()
def Redraw(self, dc= None):
"""Redraw the existing plot."""
@@ -1031,6 +1099,8 @@ class PlotCanvas(wx.Window):
if self.last_PointLabel != None:
self._drawPointLabel(self.last_PointLabel) #erase old
self.last_PointLabel = None
+
+ #paint current buffer to screen
dc = wx.BufferedPaintDC(self, self._Buffer)
def OnSize(self,event):
@@ -1041,7 +1111,23 @@ class PlotCanvas(wx.Window):
# Make new offscreen bitmap: this bitmap will always have the
# current drawing in it, so it can be used to save the image to
# a file, or whatever.
- self._Buffer = wx.EmptyBitmap(Size[0],Size[1])
+ self._Buffer = wx.EmptyBitmap(Size[0],Size[1],24)
+
+
+ if True: #self.emulate_analog:
+ #self._Bufferarray = _numpy.zeros((Size[0], Size[1],3), dtype=_numpy.uint8)
+ self._Bufferarray = _numpy.zeros((Size[0]* Size[1]*3), dtype=_numpy.uint8)
+
+ # Make new second offscreen bitmap: this bitmap will always have the
+ # last drawing in it, so it can be used to do display time dependent processing
+ # like averaging (IIR) or show differences between updates
+ self._Buffer2 = wx.EmptyBitmap(Size[0],Size[1],24)
+ # now the extra buffers for the IIR processing
+ # note the different datatype uint32
+ self._Buffer2array = _numpy.zeros((Size[0]* Size[1]*3), dtype=_numpy.uint32) #dtype=_numpy.float
+ self._Buffer3array = _numpy.zeros((Size[0]* Size[1]*3), dtype=_numpy.uint32) #dtype=_numpy.float
+ # optional you can set the ufunct buffer size to improve speed
+ #_numpy.setbufsize(16*((Size[0]* Size[1]*3)/16 +1))
self._setSize()
self.last_PointLabel = None #reset pointLabel
diff --git a/gr-wxgui/src/python/plotter/channel_plotter.py b/gr-wxgui/src/python/plotter/channel_plotter.py
index ff0a3a160..f046ae5a9 100644
--- a/gr-wxgui/src/python/plotter/channel_plotter.py
+++ b/gr-wxgui/src/python/plotter/channel_plotter.py
@@ -47,6 +47,7 @@ class channel_plotter(grid_plotter_base):
"""
#init
grid_plotter_base.__init__(self, parent, MIN_PADDING)
+ self.set_emulate_analog(False)
#setup legend cache
self._legend_cache = self.new_gl_cache(self._draw_legend, 50)
self.enable_legend(False)
diff --git a/gr-wxgui/src/python/plotter/plotter_base.py b/gr-wxgui/src/python/plotter/plotter_base.py
index dede5a0ad..2fbd5fb9d 100644
--- a/gr-wxgui/src/python/plotter/plotter_base.py
+++ b/gr-wxgui/src/python/plotter/plotter_base.py
@@ -87,7 +87,10 @@ class plotter_base(wx.glcanvas.GLCanvas, common.mutex):
@param parent the parent widgit
"""
attribList = (wx.glcanvas.WX_GL_DOUBLEBUFFER, wx.glcanvas.WX_GL_RGBA)
- wx.glcanvas.GLCanvas.__init__(self, parent, attribList=attribList)
+ wx.glcanvas.GLCanvas.__init__(self, parent, attribList=attribList);
+ self.emulate_analog=False
+ self.analog_alpha=2.0/15
+ self.clear_accum=True
self._gl_init_flag = False
self._resized_flag = True
self._init_fcns = list()
@@ -97,6 +100,13 @@ class plotter_base(wx.glcanvas.GLCanvas, common.mutex):
self.Bind(wx.EVT_SIZE, self._on_size)
self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
+ def set_emulate_analog(self,enable):
+ self.emulate_analog=enable
+ self.clear_accum=True
+
+ def set_analog_alpha(self,analog_alpha):
+ self.analog_alpha=analog_alpha
+
def new_gl_cache(self, draw_fcn, draw_pri=50):
"""
Create a new gl cache.
@@ -131,6 +141,7 @@ class plotter_base(wx.glcanvas.GLCanvas, common.mutex):
"""
self.lock()
self._resized_flag = True
+ self.clear_accum=True
self.unlock()
def _on_paint(self, event):
@@ -160,7 +171,30 @@ class plotter_base(wx.glcanvas.GLCanvas, common.mutex):
self._resized_flag = False
#clear, draw functions, swap
GL.glClear(GL.GL_COLOR_BUFFER_BIT)
+
+ if False:
+ GL.glEnable (GL.GL_LINE_SMOOTH)
+ GL.glEnable (GL.GL_POLYGON_SMOOTH)
+ GL.glEnable (GL.GL_BLEND)
+ GL.glBlendFunc (GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
+ GL.glHint (GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST) #GL.GL_DONT_CARE)
+ GL.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST)
+ #GL.glLineWidth (1.5)
+
+ GL.glEnable(GL.GL_MULTISAMPLE) #Enable Multisampling anti-aliasing
+
+
for fcn in self._draw_fcns: fcn[1]()
+
+ if self.emulate_analog:
+ if self.clear_accum:
+ #GL.glClear(GL.GL_ACCUM_BUFFER_BIT)
+ GL.glAccum(GL.GL_LOAD, 1.0)
+ self.clear_accum=False
+
+ GL.glAccum(GL.GL_MULT, 1.0-self.analog_alpha)
+ GL.glAccum(GL.GL_ACCUM, self.analog_alpha)
+ GL.glAccum(GL.GL_RETURN, 1.0)
self.SwapBuffers()
self.unlock()
diff --git a/gr-wxgui/src/python/scope_window.py b/gr-wxgui/src/python/scope_window.py
index 449046402..a6c7bdb41 100644
--- a/gr-wxgui/src/python/scope_window.py
+++ b/gr-wxgui/src/python/scope_window.py
@@ -36,6 +36,8 @@ import forms
# Constants
##################################################
DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'scope_rate', 30)
+ANALOG_ALPHA_MIN_EXP, ANALOG_ALPHA_MAX_EXP = -2, 0
+SLIDER_STEPS = 100
DEFAULT_WIN_SIZE = (600, 300)
COUPLING_MODES = (
('DC', False),
@@ -83,6 +85,37 @@ class control_panel(wx.Panel):
self.parent = parent
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
control_box = wx.BoxSizer(wx.VERTICAL)
+
+ ##################################################
+ # Emulate Analog
+ ##################################################
+
+ forms.check_box(
+ sizer=control_box, parent=self, label='Emulate Analog',
+ ps=parent, key=EMULATE_ANALOG_KEY,
+ )
+ #static text and slider for analog alpha
+ analog_alpha_text = forms.static_text(
+ sizer=control_box, parent=self, label='Analog Alpha',
+ converter=forms.float_converter(lambda x: '%.4f'%x),
+ ps=parent, key=ANALOG_ALPHA_KEY, width=50,
+ )
+ analog_alpha_slider = forms.log_slider(
+ sizer=control_box, parent=self,
+ min_exp=ANALOG_ALPHA_MIN_EXP,
+ max_exp=ANALOG_ALPHA_MAX_EXP,
+ num_steps=SLIDER_STEPS,
+ ps=parent, key=ANALOG_ALPHA_KEY,
+ )
+ for widget in (analog_alpha_text, analog_alpha_slider):
+ parent.subscribe(EMULATE_ANALOG_KEY, widget.Enable)
+ widget.Enable(parent[EMULATE_ANALOG_KEY])
+ parent.subscribe(EMULATE_ANALOG_KEY, widget.ShowItems)
+ #allways show initially, so room is reserved for them
+ widget.ShowItems(True) # (parent[EMULATE_ANALOG_KEY])
+
+ parent.subscribe(EMULATE_ANALOG_KEY, self._update_layout)
+
##################################################
# Axes Options
##################################################
@@ -359,6 +392,15 @@ class control_panel(wx.Panel):
def _on_decr_y_off(self, event):
self.parent[Y_OFF_KEY] = self.parent[Y_OFF_KEY] - self.parent[Y_PER_DIV_KEY]
+ ##################################################
+ # subscriber handlers
+ ##################################################
+ def _update_layout(self,key):
+ # Just ignore the key value we get
+ # we only need to now that the visability or size of something has changed
+ self.parent.Layout()
+ #self.parent.Fit()
+
##################################################
# Scope window with plotter and control panel
##################################################
@@ -382,6 +424,8 @@ class scope_window(wx.Panel, pubsub.pubsub):
trigger_channel_key,
decimation_key,
msg_key,
+ emulate_analog,
+ analog_alpha,
):
pubsub.pubsub.__init__(self)
#check num inputs
@@ -424,6 +468,8 @@ class scope_window(wx.Panel, pubsub.pubsub):
self[TRIGGER_MODE_KEY] = gr.gr_TRIG_MODE_AUTO
self[TRIGGER_SLOPE_KEY] = gr.gr_TRIG_SLOPE_POS
self[T_FRAC_OFF_KEY] = 0.5
+ self[EMULATE_ANALOG_KEY] = emulate_analog
+ self[ANALOG_ALPHA_KEY] = analog_alpha
for i in range(num_inputs):
self.proxy(common.index_key(AC_COUPLE_KEY, i), controller, common.index_key(ac_couple_key, i))
#init panel and plot
@@ -434,6 +480,8 @@ class scope_window(wx.Panel, pubsub.pubsub):
self.plotter.enable_legend(True)
self.plotter.enable_point_label(True)
self.plotter.enable_grid_lines(True)
+ self.plotter.set_emulate_analog(emulate_analog)
+ self.plotter.set_analog_alpha(analog_alpha)
#setup the box with plot and controls
self.control_panel = control_panel(self)
main_box = wx.BoxSizer(wx.HORIZONTAL)
@@ -451,6 +499,9 @@ class scope_window(wx.Panel, pubsub.pubsub):
XY_MODE_KEY, AUTORANGE_KEY, T_FRAC_OFF_KEY,
TRIGGER_SHOW_KEY, XY_MARKER_KEY, X_CHANNEL_KEY, Y_CHANNEL_KEY,
]: self.subscribe(key, self.update_grid)
+ #register events for plotter settings
+ self.subscribe(EMULATE_ANALOG_KEY, self.plotter.set_emulate_analog)
+ self.subscribe(ANALOG_ALPHA_KEY, self.plotter.set_analog_alpha)
#initial update
self.update_grid()
@@ -615,3 +666,4 @@ class scope_window(wx.Panel, pubsub.pubsub):
self.plotter.set_y_grid(self.get_y_min(), self.get_y_max(), self[Y_PER_DIV_KEY])
#redraw current sample
self.handle_samples()
+
diff --git a/gr-wxgui/src/python/scopesink_gl.py b/gr-wxgui/src/python/scopesink_gl.py
index 2882488e3..204434ce6 100644
--- a/gr-wxgui/src/python/scopesink_gl.py
+++ b/gr-wxgui/src/python/scopesink_gl.py
@@ -27,6 +27,7 @@ import common
from gnuradio import gr
from pubsub import pubsub
from constants import *
+import math
class ac_couple_block(gr.hier_block2):
"""
@@ -75,8 +76,17 @@ class _scope_sink_base(gr.hier_block2, common.wxgui_hb):
ac_couple=False,
num_inputs=1,
frame_rate=scope_window.DEFAULT_FRAME_RATE,
+ emulate_analog=False,
+ analog_alpha=None,
**kwargs #do not end with a comma
):
+ #ensure analog alpha
+ if analog_alpha is None:
+ actual_frame_rate=float(frame_rate)
+ analog_cutoff_freq=0.5 # Hertz
+ #calculate alpha from wanted cutoff freq
+ analog_alpha = 1.0 - math.exp(-2.0*math.pi*analog_cutoff_freq/actual_frame_rate)
+
if not t_scale: t_scale = 10.0/sample_rate
#init
gr.hier_block2.__init__(
@@ -127,6 +137,8 @@ class _scope_sink_base(gr.hier_block2, common.wxgui_hb):
trigger_channel_key=TRIGGER_CHANNEL_KEY,
decimation_key=DECIMATION_KEY,
msg_key=MSG_KEY,
+ emulate_analog=emulate_analog,
+ analog_alpha=analog_alpha,
)
common.register_access_methods(self, self.win)
#connect
@@ -167,10 +179,11 @@ class test_top_block (stdgui2.std_top_block):
def __init__(self, frame, panel, vbox, argv):
stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv)
+ default_input_rate = 1e6
if len(argv) > 1:
- frame_decim = int(argv[1])
+ input_rate = int(argv[1])
else:
- frame_decim = 1
+ input_rate = default_input_rate
if len(argv) > 2:
v_scale = float(argv[2]) # start up at this v_scale value
@@ -180,14 +193,17 @@ class test_top_block (stdgui2.std_top_block):
if len(argv) > 3:
t_scale = float(argv[3]) # start up at this t_scale value
else:
- t_scale = .00003 # old behavior
+ t_scale = .00003*default_input_rate/input_rate # old behavior
- print "frame decim %s v_scale %s t_scale %s" % (frame_decim,v_scale,t_scale)
+ print "input rate %s v_scale %s t_scale %s" % (input_rate,v_scale,t_scale)
- input_rate = 1e6
# Generate a complex sinusoid
- self.src0 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 25.1e3, 1e3)
+ ampl=1.0e3
+ self.src0 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 25.1e3*input_rate/default_input_rate, ampl)
+ self.noise =gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 11.1*25.1e3*input_rate/default_input_rate, ampl/10)
+ #self.noise =gr.noise_source_c(gr.GR_GAUSSIAN, ampl/10)
+ self.combine=gr.add_cc()
# We add this throttle block so that this demo doesn't suck down
# all the CPU available. You normally wouldn't use it...
@@ -199,7 +215,9 @@ class test_top_block (stdgui2.std_top_block):
# Ultimately this will be
# self.connect("src0 throttle scope")
- self.connect(self.src0, self.thr, scope)
+ self.connect(self.src0,(self.combine,0))
+ self.connect(self.noise,(self.combine,1))
+ self.connect(self.combine, self.thr, scope)
def main ():
app = stdgui2.stdapp (test_top_block, "O'Scope Test App")