commit-gnuradio
[Top][All Lists]
Advanced

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

[Commit-gnuradio] r8979 - in gnuradio/branches/features/experimental-gui


From: jblum
Subject: [Commit-gnuradio] r8979 - in gnuradio/branches/features/experimental-gui: . plotter
Date: Tue, 22 Jul 2008 16:39:50 -0600 (MDT)

Author: jblum
Date: 2008-07-22 16:39:49 -0600 (Tue, 22 Jul 2008)
New Revision: 8979

Added:
   gnuradio/branches/features/experimental-gui/grc_waterfallsink_test.py
   gnuradio/branches/features/experimental-gui/waterfall_window.py
   gnuradio/branches/features/experimental-gui/waterfallsink.py
Modified:
   gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py
   gnuradio/branches/features/experimental-gui/plotter/plotter_base.py
   gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py
Log:
waterfallsink working

Added: gnuradio/branches/features/experimental-gui/grc_waterfallsink_test.py
===================================================================
--- gnuradio/branches/features/experimental-gui/grc_waterfallsink_test.py       
                        (rev 0)
+++ gnuradio/branches/features/experimental-gui/grc_waterfallsink_test.py       
2008-07-22 22:39:49 UTC (rev 8979)
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+##################################################
+# Gnuradio Python Flow Graph
+# Title: untitled
+# Author: unknown
+# Description: gnuradio flow graph
+# Generated: Mon Jul 21 13:34:59 2008
+##################################################
+
+from gnuradio import blks2
+from gnuradio import gr
+from gnuradio.gr import firdes
+import waterfallsink
+from grc_gnuradio import wxgui as grc_wxgui
+import numpy
+import wx
+
+const = 1, -1j, -1, 1j
+freq_off = 0
+noise = .1
+samp_rate = 25e3
+samples_per_symbol = 4
+
+class waterfall_test(grc_wxgui.top_block_gui):
+
+       def __init__(self, const=const, freq_off=freq_off, noise=noise, 
samp_rate=samp_rate, samples_per_symbol=samples_per_symbol):
+               grc_wxgui.top_block_gui.__init__(
+                       self,
+                       title="GRC - Executing: untitled",
+                       
icon="/usr/local/lib/python2.5/site-packages/grc/data/grc-icon-32.png",
+               )
+
+               ##################################################
+               # Variables
+               ##################################################
+               self.const = const
+               self.freq_off = freq_off
+               _freq_off_control = grc_wxgui.slider_horizontal_control(
+                       window=self.GetWin(),
+                       callback=self.set_freq_off,
+                       label="Frequency Offset",
+                       value=freq_off,
+                       min=-samp_rate/2,
+                       max=samp_rate/2,
+                       num_steps=100,
+               )
+               self.Add(_freq_off_control)
+               self.noise = noise
+               _noise_control = grc_wxgui.slider_horizontal_control(
+                       window=self.GetWin(),
+                       callback=self.set_noise,
+                       label="Noise Voltage",
+                       value=noise,
+                       min=0,
+                       max=2,
+                       num_steps=100,
+               )
+               self.Add(_noise_control)
+               self.samp_rate = samp_rate
+               self.samples_per_symbol = samples_per_symbol
+
+               ##################################################
+               # Blocks
+               ##################################################
+               self.blks2_channel_model = blks2.channel_model(
+                       noise_voltage=noise,
+                       frequency_offset=freq_off/samp_rate,
+                       epsilon=1.0,
+                       taps=(1, ),
+                       noise_seed=42,
+               )
+               self.gr_chunks_to_symbols_xx = gr.chunks_to_symbols_bc((const), 
1)
+               self.gr_fir_filter_xxx = gr.fir_filter_ccc(1, 
([1]*samples_per_symbol))
+               self.gr_repeat = gr.repeat(gr.sizeof_gr_complex*1, 
samples_per_symbol)
+               self.gr_throttle = gr.throttle(gr.sizeof_gr_complex*1, 
samp_rate)
+               self.random_source_x = 
gr.vector_source_b(numpy.random.randint(0, 4, 1000), True)
+               self.wxgui_fftsink2 = waterfallsink.waterfall_sink_c(
+                       self.GetWin(),
+                       baseband_freq=0,
+                       y_per_div=5,
+                       y_divs=8,
+                       ref_level=-20,
+                       sample_rate=samp_rate,
+                       fft_size=512,
+                       frame_rate=30,
+                       average=False,
+                       avg_alpha=None,
+                       title="Waterfall Plot",
+               )
+               self.Add(self.wxgui_fftsink2.win)
+
+               ##################################################
+               # Connections
+               ##################################################
+               self.connect((self.random_source_x, 0), 
(self.gr_chunks_to_symbols_xx, 0))
+               self.connect((self.gr_chunks_to_symbols_xx, 0), 
(self.gr_throttle, 0))
+               self.connect((self.gr_throttle, 0), (self.gr_repeat, 0))
+               self.connect((self.gr_repeat, 0), (self.gr_fir_filter_xxx, 0))
+               self.connect((self.gr_fir_filter_xxx, 0), 
(self.blks2_channel_model, 0))
+               self.connect((self.blks2_channel_model, 0), 
(self.wxgui_fftsink2, 0))
+
+       def set_const(self, const):
+               self.const = const
+
+       def set_freq_off(self, freq_off):
+               self.freq_off = freq_off
+               
self.blks2_channel_model.set_frequency_offset(self.freq_off/self.samp_rate)
+
+       def set_noise(self, noise):
+               self.noise = noise
+               self.blks2_channel_model.set_noise_voltage(self.noise)
+
+       def set_samp_rate(self, samp_rate):
+               self.samp_rate = samp_rate
+               
self.blks2_channel_model.set_frequency_offset(self.freq_off/self.samp_rate)
+
+       def set_samples_per_symbol(self, samples_per_symbol):
+               self.samples_per_symbol = samples_per_symbol
+               self.gr_fir_filter_xxx.set_taps(([1]*self.samples_per_symbol))
+
+if __name__ == '__main__':
+       tb = waterfall_test()
+       tb.Run()
+


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

Modified: gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py
===================================================================
--- gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py      
2008-07-22 18:23:25 UTC (rev 8978)
+++ gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py      
2008-07-22 22:39:49 UTC (rev 8979)
@@ -48,7 +48,7 @@
                Run gl initialization tasks.
                """
                glEnableClientState(GL_VERTEX_ARRAY)
-               self.grid_compiled_list_id = glGenLists(1)
+               self._grid_compiled_list_id = glGenLists(1)
 
        def set_legend(self, legend):
                """!
@@ -68,13 +68,13 @@
                self.clear()
                #store the grid drawing operations
                if self.changed():
-                       glNewList(self.grid_compiled_list_id, GL_COMPILE)
+                       glNewList(self._grid_compiled_list_id, GL_COMPILE)
                        self._draw_grid()
                        self._draw_legend()
                        glEndList()
                        self.changed(False)
                #draw the grid
-               glCallList(self.grid_compiled_list_id)
+               glCallList(self._grid_compiled_list_id)
                #use scissor to prevent drawing outside grid
                glEnable(GL_SCISSOR_TEST)
                glScissor(

Modified: gnuradio/branches/features/experimental-gui/plotter/plotter_base.py
===================================================================
--- gnuradio/branches/features/experimental-gui/plotter/plotter_base.py 
2008-07-22 18:23:25 UTC (rev 8978)
+++ gnuradio/branches/features/experimental-gui/plotter/plotter_base.py 
2008-07-22 22:39:49 UTC (rev 8979)
@@ -46,6 +46,7 @@
                Initialize GL and register events.
                @param parent the parent widgit
                """
+               self.semaphore = threading.Semaphore(1)
                wx.glcanvas.GLCanvas.__init__(self, parent, -1)
                self.changed(False)
                self._gl_init_flag = False
@@ -73,13 +74,18 @@
                        self._gl_init_flag = True
                #check for a change in window size
                if self._resized_flag:
+                       self.semaphore.acquire(True)
                        self.width, self.height = self.GetSize()
                        glViewport(0, 0, self.width, self.height)
                        glMatrixMode(GL_PROJECTION)
                        glLoadIdentity()
                        glOrtho(0, self.width, self.height, 0, 1, 0)
+                       glMatrixMode(GL_MODELVIEW)
+                       glLoadIdentity()
+                       glViewport(0, 0, self.width, self.height)
                        self._resized_flag = False
                        self.changed(True)
+                       self.semaphore.release()
                self.draw()
 
        def update(self): wx.PostEvent(self, wx.PaintEvent())
@@ -100,7 +106,7 @@
 class grid_plotter_base(_plotter_base):
 
        def __init__(self, parent, padding):
-               self.semaphore = threading.Semaphore(1)
+               _plotter_base.__init__(self, parent)
                self.padding_top, self.padding_right, self.padding_bottom, 
self.padding_left = padding
                #store title and unit strings
                self.set_title('Title')
@@ -109,7 +115,6 @@
                #init the grid to some value
                self.set_x_grid(-1, 1, 1)
                self.set_y_grid(-1, 1, 1)
-               _plotter_base.__init__(self, parent)
 
        def set_title(self, title):
                """!

Modified: 
gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py
===================================================================
--- gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py    
2008-07-22 18:23:25 UTC (rev 8978)
+++ gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py    
2008-07-22 22:39:49 UTC (rev 8979)
@@ -25,7 +25,7 @@
 import numpy
 import gltext
 
-PADDING = 35, 35, 40, 60 #top, right, bottom, left
+PADDING = 35, 60, 40, 60 #top, right, bottom, left
 
 ##################################################
 # Waterfall Plotter
@@ -37,13 +37,18 @@
                """
                #init
                grid_plotter_base.__init__(self, parent, PADDING)
+               self._fft_size = None #must be None on init
+               self._num_frames = 256
+               self._frame_ptr = 0
+               self._waterfall_buffer_bottom = GLuint(0)
+               self._waterfall_buffer_top = GLuint(0)
 
        def _gl_init(self):
                """!
                Run gl initialization tasks.
                """
-               self.grid_compiled_list_id = glGenLists(1)
-               
+               self._grid_compiled_list_id = glGenLists(1)
+
        def draw(self):
                """!
                Draw the grid and waveforms.
@@ -51,15 +56,112 @@
                self.semaphore.acquire(True)
                self.clear()
                #store the grid drawing operations
-               if self._changed:
-                       glNewList(self.grid_compiled_list_id, GL_COMPILE)
+               if self.changed():
+                       glNewList(self._grid_compiled_list_id, GL_COMPILE)
                        self._draw_grid()
                        glEndList()
-                       self._changed = False
+                       self.changed(False)
                #draw the grid
-               glCallList(self.grid_compiled_list_id)
+               glCallList(self._grid_compiled_list_id)
+               #use scissor to prevent drawing outside grid
+               glEnable(GL_SCISSOR_TEST)
+               glScissor(
+                       self.padding_left+1,
+                       self.padding_bottom+1,
+                       self.width-self.padding_left-self.padding_right-1,
+                       self.height-self.padding_top-self.padding_bottom-1,
+               )
                #draw the waterfall
-               #TODO
+               self._draw_waterfall()
+               glDisable(GL_SCISSOR_TEST)
                #swap buffer into display
                self.SwapBuffers()
                self.semaphore.release()
+
+       def _draw_waterfall(self):
+               """!
+               Draw the waterfall display using pixels from the PBO.
+               The pixels will be scaled to fit within the grid area.
+               """
+               if self._fft_size is None: return
+               #pixel zoom
+               x_zoom = 
float(self.width-self.padding_left-self.padding_right)/self._fft_size
+               y_zoom = 
float(self.height-self.padding_top-self.padding_bottom+1)/self._num_frames
+               glRasterPos2f(self.padding_left+1, 
self.padding_top+((self._num_frames-self._frame_ptr)*y_zoom)-1)
+               #draw top portion
+               glPixelZoom(x_zoom, y_zoom)
+               glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_top)
+               glDrawPixels(self._fft_size, self._num_frames-self._frame_ptr, 
GL_RGBA, GL_UNSIGNED_BYTE, None)
+               #draw bottom portion
+               glPixelZoom(x_zoom, -y_zoom)
+               glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 
self._waterfall_buffer_bottom)
+               glDrawPixels(self._fft_size, self._frame_ptr, GL_RGBA, 
GL_UNSIGNED_BYTE, None)
+               glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0) #unbind
+
+       def _to_rgba(self, sample):
+               """!
+               Convert the sample into rgba color data.
+               @param sample a normalized floating point sample
+               @return a color array rgba with component values between 0 and 1
+               """
+               if sample < 0.5:
+                       colors = (0, 2*sample, 1 - 2*sample, 0)
+               else:
+                       colors = (2*sample - 1, 2 - 2*sample, 0, 0)
+               #color data
+               return numpy.array(colors, numpy.float32)
+
+       def plot_samples(self, samples, min, max):
+               """!
+               Plot the samples onto a time slice of the waterfall.
+               Create or recreate the PBO when the fft size or number of 
frames changes.
+               Convert the samples to color data.
+               @param samples the array of floats
+               @param min the minimum value to scale
+               @param max the maximum value to scale
+               """
+               self.semaphore.acquire(True)
+               #init or reinit the pixel buffer
+               if len(samples) != self._fft_size:
+                       if self._fft_size is not None:
+                               glDeleteBuffers(1, self._waterfall_buffer_top)
+                               glDeleteBuffers(1, 
self._waterfall_buffer_bottom)
+                       self._fft_size = len(samples)
+                       glGenBuffers(1, self._waterfall_buffer_top)
+                       glGenBuffers(1, self._waterfall_buffer_bottom)
+                       #initial data
+                       data = (chr(0) + chr(0) + chr(0) + chr(0)) * 
self._fft_size*self._num_frames
+                       glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 
self._waterfall_buffer_top)
+                       glBufferData(
+                               GL_PIXEL_UNPACK_BUFFER,
+                               len(data), data,
+                               GL_DYNAMIC_DRAW,
+                       )
+                       glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 
self._waterfall_buffer_bottom)
+                       glBufferData(
+                               GL_PIXEL_UNPACK_BUFFER,
+                               len(data), data,
+                               GL_DYNAMIC_DRAW,
+                       )
+               #normalize the samples to min/max
+               samples = (samples - min)/(max-min)
+               samples = numpy.minimum(samples, 1)
+               samples = numpy.maximum(samples, 0)
+               data = numpy.array(255*numpy.concatenate(map(self._to_rgba, 
samples)), numpy.uint8).tostring()
+               #load the color data into the pixel buffers
+               glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_top)
+               glBufferSubData(
+                       GL_PIXEL_UNPACK_BUFFER,
+                       (self._num_frames-self._frame_ptr-1)*self._fft_size*4,
+                       len(data), data,
+               )
+               glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 
self._waterfall_buffer_bottom)
+               glBufferSubData(
+                       GL_PIXEL_UNPACK_BUFFER,
+                       self._frame_ptr*self._fft_size*4,
+                       len(data), data,
+               )
+               glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0) #unbind
+               #increment the pointer
+               self._frame_ptr = (self._frame_ptr + 1)%self._num_frames
+               self.semaphore.release()

Copied: gnuradio/branches/features/experimental-gui/waterfall_window.py (from 
rev 8963, gnuradio/branches/features/experimental-gui/fft_window.py)
===================================================================
--- gnuradio/branches/features/experimental-gui/waterfall_window.py             
                (rev 0)
+++ gnuradio/branches/features/experimental-gui/waterfall_window.py     
2008-07-22 22:39:49 UTC (rev 8979)
@@ -0,0 +1,244 @@
+#
+# 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 math
+import prop_val
+
+##################################################
+# Constants
+##################################################
+SLIDER_STEPS = 100
+AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
+DEFAULT_FRAME_RATE = 30
+DEFAULT_WIN_SIZE = (600, 300)
+DIV_LEVELS = (1, 2, 5, 10, 20)
+Y_PER_DIV_KEY = 'y_per_div'
+Y_DIVS_KEY = 'y_divs'
+X_DIVS_KEY = 'x_divs'
+REF_LEVEL_KEY = 'ref_level'
+BASEBAND_FREQ_KEY = 'baseband_freq'
+RUNNING_KEY = 'running'
+
+##################################################
+# Waterfall window control panel
+##################################################
+class control_panel(wx.Panel):
+       """!
+       A control panel with wx widgits to control the plotter and fft block 
chain.
+       """
+
+       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
+               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)
+               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.add_listener(parent.average_key, 
self.avg_alpha_slider.Enable)
+               control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND)
+               #radio buttons for div size
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Set dB/div'), 0, 
wx.ALIGN_CENTER)
+               radio_box = wx.BoxSizer(wx.VERTICAL)
+               self.radio_buttons = list()
+               for y_per_div in DIV_LEVELS:
+                       radio_button = wx.RadioButton(self, -1, "%d 
dB/div"%y_per_div)
+                       radio_button.Bind(wx.EVT_RADIOBUTTON, 
self._on_y_per_div)
+                       self.radio_buttons.append(radio_button)
+                       radio_box.Add(radio_button, 0, wx.ALIGN_LEFT)
+               parent.controller.add_listener(Y_PER_DIV_KEY, 
self._on_set_y_per_div)
+               control_box.Add(radio_box, 0, wx.EXPAND)
+               #ref lvl buttons
+               control_box.AddStretchSpacer()
+               control_box.Add(common.LabelText(self, 'Set Ref Level'), 0, 
wx.ALIGN_CENTER)
+               control_box.AddSpacer(2)
+               self._ref_lvl_buttons = common.IncrDecrButtons(self, 
self._on_incr_ref_level, self._on_decr_ref_level)
+               control_box.Add(self._ref_lvl_buttons, 0, wx.ALIGN_CENTER)
+               #run/stop
+               control_box.AddStretchSpacer()
+               self.run_button = common.RunStopButtonController(self, 
parent.controller, RUNNING_KEY)
+               control_box.Add(self.run_button, 0, wx.EXPAND)
+               #set sizer
+               self.SetSizerAndFit(control_box)
+
+       ##################################################
+       # Event handlers
+       ##################################################
+       def _on_set_y_per_div(self, y_per_div):
+               try:
+                       index = list(DIV_LEVELS).index(y_per_div)
+                       self.radio_buttons[index].SetValue(True)
+               except: pass
+       def _on_y_per_div(self, event):
+               selected_radio_button = filter(lambda rb: rb.GetValue(), 
self.radio_buttons)[0]
+               index = self.radio_buttons.index(selected_radio_button)
+               self.parent.controller[Y_PER_DIV_KEY] = DIV_LEVELS[index]
+       def _on_incr_ref_level(self, event):
+               self.parent.set_ref_level(
+                       self.parent.controller[REF_LEVEL_KEY] + 
self.parent.controller[Y_PER_DIV_KEY])
+       def _on_decr_ref_level(self, event):
+               self.parent.set_ref_level(
+                       self.parent.controller[REF_LEVEL_KEY] - 
self.parent.controller[Y_PER_DIV_KEY])
+
+##################################################
+# Waterfall window with plotter and control panel
+##################################################
+class waterfall_window(wx.Panel, common.prop_setter):
+       def __init__(
+               self,
+               parent,
+               controller,
+               size,
+               title,
+               real,
+               fft_size,
+               baseband_freq,
+               sample_rate_key,
+               y_per_div,
+               y_divs,
+               ref_level,
+               average_key,
+               avg_alpha_key,
+               msg_key,
+       ):
+               self.controller = prop_val.prop_val_interface()
+               #ensure y_per_div
+               if y_per_div not in DIV_LEVELS: y_per_div = DIV_LEVELS[0]
+               #setup
+               self.ext_controller = controller
+               self.real = real
+               self.fft_size = fft_size
+               self.sample_rate_key = sample_rate_key
+               self.average_key = average_key
+               self.avg_alpha_key = avg_alpha_key
+               #init panel and plot
+               wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER)
+               self.plotter = plotter.waterfall_plotter(self)
+               self.plotter.SetSize(wx.Size(*size))
+               self.plotter.set_title(title)
+               #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.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.controller, Y_PER_DIV_KEY, 
y_per_div)
+               self._register_set_prop(self.controller, Y_DIVS_KEY, y_divs)
+               self._register_set_prop(self.controller, X_DIVS_KEY, 8) 
#approximate
+               self._register_set_prop(self.controller, REF_LEVEL_KEY, 
ref_level)
+               self._register_set_prop(self.controller, BASEBAND_FREQ_KEY, 
baseband_freq)
+               self._register_set_prop(self.controller, RUNNING_KEY, True)
+               #register events
+               self.ext_controller.add_listener(msg_key, self.handle_msg)
+               self.ext_controller.add_listener(self.sample_rate_key, 
self.update_grid)
+               self.controller.add_listener(BASEBAND_FREQ_KEY, 
self.update_grid)
+               self.controller.add_listener(X_DIVS_KEY, self.update_grid)
+               #initial update
+               self.update_grid()
+
+       def handle_msg(self, msg):
+               """!
+               Handle the message from the fft sink message queue.
+               If complex, reorder the fft samples so the negative bins come 
first.
+               If real, keep take only the positive bins.
+               Send the data to the plotter.
+               @param msg the fft array as a character array
+               """
+               if not self.controller[RUNNING_KEY]: return
+               #convert to floating point numbers
+               samples = numpy.fromstring(msg, numpy.float32)[:self.fft_size] 
#only take first frame
+               num_samps = len(samples)
+               #reorder fft
+               if self.real: samples = samples[:num_samps/2]
+               else: samples = numpy.concatenate((samples[num_samps/2+1:], 
samples[:num_samps/2]))
+               #plot the fft
+               self.plotter.plot_samples(
+                       samples, self.controller[REF_LEVEL_KEY], 
+                       
self.controller[Y_PER_DIV_KEY]*self.controller[Y_DIVS_KEY] + 
self.controller[REF_LEVEL_KEY],
+               )
+               #update the plotter
+               self.plotter.update()
+
+       def update_grid(self, *args):
+               """!
+               Update the plotter grid.
+               This update method is dependent on the variables below.
+               Determine the x and y axis grid parameters.
+               The x axis depends on sample rate, baseband freq, and x divs.
+               The y axis depends on y per div, y divs, and ref level.
+               """
+               #grid parameters
+               sample_rate = self.ext_controller[self.sample_rate_key]
+               baseband_freq = self.controller[BASEBAND_FREQ_KEY]
+               x_divs = self.controller[X_DIVS_KEY]
+               #determine best fitting x_per_div
+               if self.real: x_width = sample_rate/2.0
+               else: x_width = sample_rate/1.0
+               x_per_div = common.get_clean_num(x_width/x_divs)
+               exp = common.get_exp(x_per_div)
+               #calculate units and scalar
+               if exp > 7: x_units, scalar = 'GHz', 1e-9
+               elif exp > 4: x_units, scalar = 'MHz', 1e-6
+               elif exp > 1: x_units, scalar = 'KHz', 1e-3
+               else: x_units, scalar = 'Hz', 1e-0
+               #update the x grid
+               if self.real:
+                       self.plotter.set_x_grid(
+                               scalar*baseband_freq,
+                               scalar*baseband_freq + scalar*sample_rate/2.0,
+                               scalar*x_per_div,
+                       )
+               else:
+                       self.plotter.set_x_grid(
+                               scalar*baseband_freq - scalar*sample_rate/2.0,
+                               scalar*baseband_freq + scalar*sample_rate/2.0,
+                               scalar*x_per_div,
+                       )
+               #update x units
+               self.plotter.set_x_units('Frequency (%s)'%x_units)
+               #update y grid
+               self.plotter.set_y_grid(0, 10, 1)
+               #update y units
+               self.plotter.set_y_units('Time (s)')
+               #update plotter
+               self.plotter.update()

Copied: gnuradio/branches/features/experimental-gui/waterfallsink.py (from rev 
8962, gnuradio/branches/features/experimental-gui/fftsink.py)
===================================================================
--- gnuradio/branches/features/experimental-gui/waterfallsink.py                
                (rev 0)
+++ gnuradio/branches/features/experimental-gui/waterfallsink.py        
2008-07-22 22:39:49 UTC (rev 8979)
@@ -0,0 +1,126 @@
+#
+# 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 waterfall_window
+import common
+from gnuradio import gr, blks2
+from prop_val import prop_val_interface
+
+##################################################
+# Constants
+##################################################
+SAMPLE_RATE_KEY = 'sample_rate'
+AVERAGE_KEY = 'average'
+AVG_ALPHA_KEY = 'avg_alpha'
+MSG_KEY = 'msg'
+
+##################################################
+# Waterfall sink block (wrapper for old wxgui)
+##################################################
+class _waterfall_sink_base(gr.hier_block2, common.prop_setter):
+       """!
+       An fft block with real/complex inputs and a gui window.
+       """
+
+       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,
+               frame_rate=waterfall_window.DEFAULT_FRAME_RATE,
+               average=False,
+               avg_alpha=None,
+               title='',
+               size=waterfall_window.DEFAULT_WIN_SIZE,
+       ):
+               #ensure avg alpha
+               if avg_alpha is None: avg_alpha = 2.0/frame_rate
+               #init
+               gr.hier_block2.__init__(
+                       self,
+                       "waterfall_sink",
+                       gr.io_signature(1, 1, self.item_size),
+                       gr.io_signature(0, 0, 0),
+               )
+               #blocks
+               copy = gr.kludge_copy(self.item_size)
+               fft = self.fft_chain(
+                       sample_rate=sample_rate,
+                       fft_size=fft_size,
+                       frame_rate=frame_rate,
+                       ref_scale=ref_scale,
+                       avg_alpha=avg_alpha,
+                       average=average,
+               )
+               msgq = gr.msg_queue(2)
+               sink = gr.message_sink(gr.sizeof_float*fft_size, msgq, True)
+               #connect
+               self.connect(self, copy, fft, sink)
+               #controller
+               self.controller = prop_val_interface()
+               self.controller.add_listener(AVERAGE_KEY, fft.set_average)
+               self.controller.set_provider(AVERAGE_KEY, fft.average)
+               self.controller.add_listener(AVG_ALPHA_KEY, fft.set_avg_alpha)
+               self.controller.set_provider(AVG_ALPHA_KEY, fft.avg_alpha)
+               self.controller.add_listener(SAMPLE_RATE_KEY, 
fft.set_sample_rate)
+               self.controller.set_provider(SAMPLE_RATE_KEY, 
fft._sd.sample_rate) #FIXME
+               #start input watcher
+               common.input_watcher(msgq, lambda x: 
self.controller.set(MSG_KEY, x))
+               #create window
+               self.win = waterfall_window.waterfall_window(
+                       parent=parent,
+                       controller=self.controller,
+                       size=size,
+                       title=title,
+                       real=self.real,
+                       fft_size=fft_size,
+                       baseband_freq=baseband_freq,
+                       sample_rate_key=SAMPLE_RATE_KEY,
+                       y_per_div=y_per_div,
+                       y_divs=y_divs,
+                       ref_level=ref_level,
+                       average_key=AVERAGE_KEY,
+                       avg_alpha_key=AVG_ALPHA_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)
+               self._register_set_prop(self.controller, AVERAGE_KEY)
+               self._register_set_prop(self.controller, AVG_ALPHA_KEY)
+
+class waterfall_sink_f(_waterfall_sink_base):
+       fft_chain = blks2.logpwrfft_f
+       item_size = gr.sizeof_float
+       real = True
+class waterfall_sink_c(_waterfall_sink_base):
+       fft_chain = blks2.logpwrfft_c
+       item_size = gr.sizeof_gr_complex
+       real = False





reply via email to

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