diff options
Diffstat (limited to 'gr-wxgui/python/wxgui/plotter/waterfall_plotter.py')
-rw-r--r-- | gr-wxgui/python/wxgui/plotter/waterfall_plotter.py | 294 |
1 files changed, 0 insertions, 294 deletions
diff --git a/gr-wxgui/python/wxgui/plotter/waterfall_plotter.py b/gr-wxgui/python/wxgui/plotter/waterfall_plotter.py deleted file mode 100644 index a5601e25bc..0000000000 --- a/gr-wxgui/python/wxgui/plotter/waterfall_plotter.py +++ /dev/null @@ -1,294 +0,0 @@ -# -# Copyright 2008, 2009, 2010 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. -# - -import wx -from grid_plotter_base import grid_plotter_base -from OpenGL import GL -import common -import numpy -import gltext -import math -import struct - -LEGEND_LEFT_PAD = 7 -LEGEND_NUM_BLOCKS = 256 -LEGEND_NUM_LABELS = 9 -LEGEND_WIDTH = 8 -LEGEND_FONT_SIZE = 8 -LEGEND_BORDER_COLOR_SPEC = (0, 0, 0) #black -MIN_PADDING = 0, 60, 0, 0 #top, right, bottom, left - -ceil_log2 = lambda x: 2**int(math.ceil(math.log(x)/math.log(2))) - -pack_color = lambda x: struct.unpack('I', struct.pack('BBBB', *x))[0] -unpack_color = lambda x: struct.unpack('BBBB', struct.pack('I', int(x))) - -def _get_rbga(red_pts, green_pts, blue_pts, alpha_pts=[(0, 0), (1, 0)]): - """ - Get an array of 256 rgba values where each index maps to a color. - The scaling for red, green, blue, alpha are specified in piece-wise functions. - The piece-wise functions consist of a set of x, y coordinates. - The x and y values of the coordinates range from 0 to 1. - The coordinates must be specified so that x increases with the index value. - Resulting values are calculated along the line formed between 2 coordinates. - - Args: - red_pts, green_pts, blue_pts, alpha_pts: an array of x,y coordinates for each color element - - Returns: - array of rbga values (4 bytes) each - """ - def _fcn(x, pw): - for (x1, y1), (x2, y2) in zip(pw, pw[1:]): - #linear interpolation - if x <= x2: return float(y1 - y2)/(x1 - x2)*(x - x1) + y1 - raise Exception - return numpy.array([pack_color(map( - lambda pw: int(255*_fcn(i/255.0, pw)), - (red_pts, green_pts, blue_pts, alpha_pts), - )) for i in range(0, 256)], numpy.uint32) - -COLORS = { - 'rgb1': _get_rbga( #http://www.ks.uiuc.edu/Research/vmd/vmd-1.7.1/ug/img47.gif - red_pts = [(0, 0), (.5, 0), (1, 1)], - green_pts = [(0, 0), (.5, 1), (1, 0)], - blue_pts = [(0, 1), (.5, 0), (1, 0)], - ), - 'rgb2': _get_rbga( #http://xtide.ldeo.columbia.edu/~krahmann/coledit/screen.jpg - red_pts = [(0, 0), (3.0/8, 0), (5.0/8, 1), (7.0/8, 1), (1, .5)], - green_pts = [(0, 0), (1.0/8, 0), (3.0/8, 1), (5.0/8, 1), (7.0/8, 0), (1, 0)], - blue_pts = [(0, .5), (1.0/8, 1), (3.0/8, 1), (5.0/8, 0), (1, 0)], - ), - 'rgb3': _get_rbga( - red_pts = [(0, 0), (1.0/3.0, 0), (2.0/3.0, 0), (1, 1)], - green_pts = [(0, 0), (1.0/3.0, 0), (2.0/3.0, 1), (1, 0)], - blue_pts = [(0, 0), (1.0/3.0, 1), (2.0/3.0, 0), (1, 0)], - ), - 'gray': _get_rbga( - red_pts = [(0, 0), (1, 1)], - green_pts = [(0, 0), (1, 1)], - blue_pts = [(0, 0), (1, 1)], - ), -} - -################################################## -# Waterfall Plotter -################################################## -class waterfall_plotter(grid_plotter_base): - def __init__(self, parent): - """ - Create a new channel plotter. - """ - #init - grid_plotter_base.__init__(self, parent, MIN_PADDING) - #setup legend cache - self._legend_cache = self.new_gl_cache(self._draw_legend) - #setup waterfall cache - self._waterfall_cache = self.new_gl_cache(self._draw_waterfall, 50) - #setup waterfall plotter - self.register_init(self._init_waterfall) - self._resize_texture(False) - self._minimum = 0 - self._maximum = 0 - self._fft_size = 1 - self._buffer = list() - self._pointer = 0 - self._counter = 0 - self.set_num_lines(0) - self.set_color_mode(COLORS.keys()[0]) - self.callback = None - - def _init_waterfall(self): - """ - Run gl initialization tasks. - """ - self._waterfall_texture = GL.glGenTextures(1) - - def _draw_waterfall(self): - """ - Draw the waterfall from the texture. - The texture is circularly filled and will wrap around. - Use matrix modeling to shift and scale the texture onto the coordinate plane. - """ - #resize texture - self._resize_texture() - #setup texture - GL.glBindTexture(GL.GL_TEXTURE_2D, self._waterfall_texture) - GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR) - GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) - GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT) - GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE) - #write the buffer to the texture - while self._buffer: - GL.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, self._pointer, self._fft_size, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, self._buffer.pop(0)) - self._pointer = (self._pointer + 1)%self._num_lines - #begin drawing - GL.glEnable(GL.GL_TEXTURE_2D) - GL.glPushMatrix() - #matrix scaling - GL.glTranslatef(self.padding_left, self.padding_top, 0) - GL.glScalef( - float(self.width-self.padding_left-self.padding_right), - float(self.height-self.padding_top-self.padding_bottom), - 1.0, - ) - #draw texture with wrapping - GL.glBegin(GL.GL_QUADS) - prop_y = float(self._pointer)/(self._num_lines-1) - prop_x = float(self._fft_size)/ceil_log2(self._fft_size) - off = 1.0/(self._num_lines-1) - GL.glTexCoord2f(0, prop_y+1-off) - GL.glVertex2f(0, 1) - GL.glTexCoord2f(prop_x, prop_y+1-off) - GL.glVertex2f(1, 1) - GL.glTexCoord2f(prop_x, prop_y) - GL.glVertex2f(1, 0) - GL.glTexCoord2f(0, prop_y) - GL.glVertex2f(0, 0) - GL.glEnd() - GL.glPopMatrix() - GL.glDisable(GL.GL_TEXTURE_2D) - - def _populate_point_label(self, x_val, y_val): - """ - Get the text the will populate the point label. - Give the X value for the current point. - - Args: - x_val: the current x value - y_val: the current y value - - Returns: - a value string with units - """ - return '%s: %s'%(self.x_label, common.eng_format(x_val, self.x_units)) - - def _call_callback(self, x_val, y_val): - if self.callback != None: - self.callback(x_val,y_val) - - def set_callback(self,callback): - self.callback = callback - - def _draw_legend(self): - """ - Draw the color scale legend. - """ - if not self._color_mode: return - legend_height = self.height-self.padding_top-self.padding_bottom - #draw each legend block - block_height = float(legend_height)/LEGEND_NUM_BLOCKS - x = self.width - self.padding_right + LEGEND_LEFT_PAD - for i in range(LEGEND_NUM_BLOCKS): - color = unpack_color(COLORS[self._color_mode][int(255*i/float(LEGEND_NUM_BLOCKS-1))]) - GL.glColor4f(*numpy.array(color)/255.0) - y = self.height - (i+1)*block_height - self.padding_bottom - self._draw_rect(x, y, LEGEND_WIDTH, block_height) - #draw rectangle around color scale border - GL.glColor3f(*LEGEND_BORDER_COLOR_SPEC) - self._draw_rect(x, self.padding_top, LEGEND_WIDTH, legend_height, fill=False) - #draw each legend label - label_spacing = float(legend_height)/(LEGEND_NUM_LABELS-1) - x = self.width - (self.padding_right - LEGEND_LEFT_PAD - LEGEND_WIDTH)/2 - for i in range(LEGEND_NUM_LABELS): - proportion = i/float(LEGEND_NUM_LABELS-1) - dB = proportion*(self._maximum - self._minimum) + self._minimum - y = self.height - i*label_spacing - self.padding_bottom - txt = gltext.Text('%ddB'%int(dB), font_size=LEGEND_FONT_SIZE, centered=True) - txt.draw_text(wx.Point(x, y)) - - def _resize_texture(self, flag=None): - """ - Create the texture to fit the fft_size X num_lines. - - Args: - flag: the set/unset or update flag - """ - if flag is not None: - self._resize_texture_flag = flag - return - if not self._resize_texture_flag: return - self._buffer = list() - self._pointer = 0 - if self._num_lines and self._fft_size: - GL.glBindTexture(GL.GL_TEXTURE_2D, self._waterfall_texture) - data = numpy.zeros(self._num_lines*ceil_log2(self._fft_size)*4, numpy.uint8).tostring() - GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, ceil_log2(self._fft_size), self._num_lines, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, data) - self._resize_texture_flag = False - - def set_color_mode(self, color_mode): - """ - Set the color mode. - New samples will be converted to the new color mode. - Old samples will not be recolorized. - - Args: - color_mode: the new color mode string - """ - self.lock() - if color_mode in COLORS.keys(): - self._color_mode = color_mode - self._legend_cache.changed(True) - self.update() - self.unlock() - - def set_num_lines(self, num_lines): - """ - Set number of lines. - Powers of two only. - - Args: - num_lines: the new number of lines - """ - self.lock() - self._num_lines = num_lines - self._resize_texture(True) - self.update() - self.unlock() - - def set_samples(self, samples, minimum, maximum): - """ - Set the samples to the waterfall. - Convert the samples to color data. - - Args: - samples: the array of floats - minimum: the minimum value to scale - maximum: the maximum value to scale - """ - self.lock() - #set the min, max values - if self._minimum != minimum or self._maximum != maximum: - self._minimum = minimum - self._maximum = maximum - self._legend_cache.changed(True) - if self._fft_size != len(samples): - self._fft_size = len(samples) - self._resize_texture(True) - #normalize the samples to min/max - samples = (samples - minimum)*float(255/(maximum-minimum)) - samples = numpy.clip(samples, 0, 255) #clip - samples = numpy.array(samples, numpy.uint8) - #convert the samples to RGBA data - data = COLORS[self._color_mode][samples].tostring() - self._buffer.append(data) - self._waterfall_cache.changed(True) - self.unlock() |