Merge remote branch 'nldudok1/gr-wxgui_emulate_analog' into master
authorJohnathan Corgan <jcorgan@corganenterprises.com>
Thu, 13 May 2010 19:29:59 +0000 (12:29 -0700)
committerJohnathan Corgan <jcorgan@corganenterprises.com>
Thu, 13 May 2010 19:29:59 +0000 (12:29 -0700)
* nldudok1/gr-wxgui_emulate_analog:
  Add analog CRT screen afterglow emulation for gr-wxgui

Conflicts:
gr-wxgui/src/python/fftsink_gl.py
gr-wxgui/src/python/fftsink_nongl.py

gr-wxgui/src/python/constants.py
gr-wxgui/src/python/fft_window.py
gr-wxgui/src/python/fftsink_gl.py
gr-wxgui/src/python/fftsink_nongl.py
gr-wxgui/src/python/plot.py
gr-wxgui/src/python/plotter/channel_plotter.py
gr-wxgui/src/python/plotter/plotter_base.py
gr-wxgui/src/python/scope_window.py
gr-wxgui/src/python/scopesink_gl.py

index 825f71c3223369ac43ee263685a4bc38c84702a6..2e7f33a0cb3d72a40c14bfd0cc9a06050c187560 100644 (file)
@@ -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'
index 4ee5520f76167271af35a699266fd20521e9e6c3..c56dbd7e6ecdf51d16b137766b712dd2fb5c2857 100644 (file)
@@ -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
@@ -97,7 +98,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)
@@ -144,6 +176,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)
@@ -161,6 +194,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
@@ -183,7 +224,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
@@ -204,6 +248,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
@@ -232,6 +278,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)
@@ -247,9 +295,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.
index 8ddea9a8e3fd0109a737a8f5f49a28478e604b5d..0d725ea14b3063a25f71b121cfd8965d84de63cf 100644 (file)
@@ -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)
@@ -53,10 +54,21 @@ class _fft_sink_base(gr.hier_block2, common.wxgui_hb):
                size=fft_window.DEFAULT_WIN_SIZE,
                peak_hold=False,
                win=None,
+                emulate_analog=False,
+                analog_alpha=None,
                **kwargs #do not end with a comma
        ):
                #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,
@@ -76,6 +88,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)
@@ -103,6 +117,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
@@ -134,11 +150,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.
@@ -149,17 +168,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")
index 937eb27cce936ad14c5243ecc66568bb750833cb..8735e98aef391d52cdab64de0ed2d965f33f0a94 100644 (file)
@@ -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, **kwargs):
+                 title='', size=default_fftsink_size, peak_hold=False, emulate_analog=False,analog_alpha=0.2, **kwargs):
 
         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, **kwargs):
+                 title='', size=default_fftsink_size, peak_hold=False, emulate_analog=False,analog_alpha=0.2, **kwargs):
 
         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,
index c104b0ea5105ffc5094d906a32528a01d9bd45d6..c5557cb6ba6562ae2579ffef591a3a8ee19f8f68 100644 (file)
@@ -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
index ff0a3a160d9cb7ddc48bd4538377617a2a1133dc..f046ae5a9f49004b6b3310e320792491df3626df 100644 (file)
@@ -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)
index dede5a0ad51b8d5abbf231c02ef40634d66acd77..2fbd5fb9dbbc3368c04ba9949e25a1c21c8eb8fc 100644 (file)
@@ -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()
 
index f7c0ffa82e6174a779b4fe412b3feaf8eb775c64..aace8688f2a2e46ed65bace7e711b493bd019b8e 100644 (file)
@@ -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),
@@ -88,6 +90,37 @@ class control_panel(wx.Panel):
                parent[SHOW_CONTROL_PANEL_KEY] = True
                parent.subscribe(SHOW_CONTROL_PANEL_KEY, self.Show)
                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
                ##################################################
@@ -364,6 +397,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
 ##################################################
@@ -388,6 +430,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
@@ -430,6 +474,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
@@ -440,6 +486,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)
@@ -457,6 +505,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()
 
@@ -621,3 +672,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()
+
index 358361de62d6953ee6f969ee1db7dd5e64a17a20..a12517883cfe4f4a3f77042c336ed981cb70200d 100644 (file)
@@ -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):
        """
@@ -76,8 +77,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__(
@@ -129,6 +139,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
@@ -169,10 +181,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
@@ -182,14 +195,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...
@@ -201,7 +217,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")