Changeset 8979
- Timestamp:
- 07/22/08 16:39:49
- Files:
-
- gnuradio/branches/features/experimental-gui/grc_waterfallsink_test.py (added)
- gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py (modified) (3 diffs)
- gnuradio/branches/features/experimental-gui/plotter/plotter_base.py (modified) (5 diffs)
- gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py (modified) (4 diffs)
- gnuradio/branches/features/experimental-gui/waterfall_window.py (copied) (copied from gnuradio/branches/features/experimental-gui/fft_window.py) (12 diffs)
- gnuradio/branches/features/experimental-gui/waterfallsink.py (copied) (copied from gnuradio/branches/features/experimental-gui/fftsink.py) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py
r8963 r8979 49 49 """ 50 50 glEnableClientState(GL_VERTEX_ARRAY) 51 self. grid_compiled_list_id = glGenLists(1)51 self._grid_compiled_list_id = glGenLists(1) 52 52 53 53 def set_legend(self, legend): … … 69 69 #store the grid drawing operations 70 70 if self.changed(): 71 glNewList(self. grid_compiled_list_id, GL_COMPILE)71 glNewList(self._grid_compiled_list_id, GL_COMPILE) 72 72 self._draw_grid() 73 73 self._draw_legend() … … 75 75 self.changed(False) 76 76 #draw the grid 77 glCallList(self. grid_compiled_list_id)77 glCallList(self._grid_compiled_list_id) 78 78 #use scissor to prevent drawing outside grid 79 79 glEnable(GL_SCISSOR_TEST) gnuradio/branches/features/experimental-gui/plotter/plotter_base.py
r8963 r8979 47 47 @param parent the parent widgit 48 48 """ 49 self.semaphore = threading.Semaphore(1) 49 50 wx.glcanvas.GLCanvas.__init__(self, parent, -1) 50 51 self.changed(False) … … 74 75 #check for a change in window size 75 76 if self._resized_flag: 77 self.semaphore.acquire(True) 76 78 self.width, self.height = self.GetSize() 77 79 glViewport(0, 0, self.width, self.height) … … 79 81 glLoadIdentity() 80 82 glOrtho(0, self.width, self.height, 0, 1, 0) 83 glMatrixMode(GL_MODELVIEW) 84 glLoadIdentity() 85 glViewport(0, 0, self.width, self.height) 81 86 self._resized_flag = False 82 87 self.changed(True) 88 self.semaphore.release() 83 89 self.draw() 84 90 … … 101 107 102 108 def __init__(self, parent, padding): 103 self.semaphore = threading.Semaphore(1)109 _plotter_base.__init__(self, parent) 104 110 self.padding_top, self.padding_right, self.padding_bottom, self.padding_left = padding 105 111 #store title and unit strings … … 110 116 self.set_x_grid(-1, 1, 1) 111 117 self.set_y_grid(-1, 1, 1) 112 _plotter_base.__init__(self, parent)113 118 114 119 def set_title(self, title): gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py
r8963 r8979 26 26 import gltext 27 27 28 PADDING = 35, 35, 40, 60 #top, right, bottom, left28 PADDING = 35, 60, 40, 60 #top, right, bottom, left 29 29 30 30 ################################################## … … 38 38 #init 39 39 grid_plotter_base.__init__(self, parent, PADDING) 40 self._fft_size = None #must be None on init 41 self._num_frames = 256 42 self._frame_ptr = 0 43 self._waterfall_buffer_bottom = GLuint(0) 44 self._waterfall_buffer_top = GLuint(0) 40 45 41 46 def _gl_init(self): … … 43 48 Run gl initialization tasks. 44 49 """ 45 self. grid_compiled_list_id = glGenLists(1)46 50 self._grid_compiled_list_id = glGenLists(1) 51 47 52 def draw(self): 48 53 """! … … 52 57 self.clear() 53 58 #store the grid drawing operations 54 if self. _changed:55 glNewList(self. grid_compiled_list_id, GL_COMPILE)59 if self.changed(): 60 glNewList(self._grid_compiled_list_id, GL_COMPILE) 56 61 self._draw_grid() 57 62 glEndList() 58 self. _changed = False63 self.changed(False) 59 64 #draw the grid 60 glCallList(self.grid_compiled_list_id) 65 glCallList(self._grid_compiled_list_id) 66 #use scissor to prevent drawing outside grid 67 glEnable(GL_SCISSOR_TEST) 68 glScissor( 69 self.padding_left+1, 70 self.padding_bottom+1, 71 self.width-self.padding_left-self.padding_right-1, 72 self.height-self.padding_top-self.padding_bottom-1, 73 ) 61 74 #draw the waterfall 62 #TODO 75 self._draw_waterfall() 76 glDisable(GL_SCISSOR_TEST) 63 77 #swap buffer into display 64 78 self.SwapBuffers() 65 79 self.semaphore.release() 80 81 def _draw_waterfall(self): 82 """! 83 Draw the waterfall display using pixels from the PBO. 84 The pixels will be scaled to fit within the grid area. 85 """ 86 if self._fft_size is None: return 87 #pixel zoom 88 x_zoom = float(self.width-self.padding_left-self.padding_right)/self._fft_size 89 y_zoom = float(self.height-self.padding_top-self.padding_bottom+1)/self._num_frames 90 glRasterPos2f(self.padding_left+1, self.padding_top+((self._num_frames-self._frame_ptr)*y_zoom)-1) 91 #draw top portion 92 glPixelZoom(x_zoom, y_zoom) 93 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_top) 94 glDrawPixels(self._fft_size, self._num_frames-self._frame_ptr, GL_RGBA, GL_UNSIGNED_BYTE, None) 95 #draw bottom portion 96 glPixelZoom(x_zoom, -y_zoom) 97 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_bottom) 98 glDrawPixels(self._fft_size, self._frame_ptr, GL_RGBA, GL_UNSIGNED_BYTE, None) 99 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0) #unbind 100 101 def _to_rgba(self, sample): 102 """! 103 Convert the sample into rgba color data. 104 @param sample a normalized floating point sample 105 @return a color array rgba with component values between 0 and 1 106 """ 107 if sample < 0.5: 108 colors = (0, 2*sample, 1 - 2*sample, 0) 109 else: 110 colors = (2*sample - 1, 2 - 2*sample, 0, 0) 111 #color data 112 return numpy.array(colors, numpy.float32) 113 114 def plot_samples(self, samples, min, max): 115 """! 116 Plot the samples onto a time slice of the waterfall. 117 Create or recreate the PBO when the fft size or number of frames changes. 118 Convert the samples to color data. 119 @param samples the array of floats 120 @param min the minimum value to scale 121 @param max the maximum value to scale 122 """ 123 self.semaphore.acquire(True) 124 #init or reinit the pixel buffer 125 if len(samples) != self._fft_size: 126 if self._fft_size is not None: 127 glDeleteBuffers(1, self._waterfall_buffer_top) 128 glDeleteBuffers(1, self._waterfall_buffer_bottom) 129 self._fft_size = len(samples) 130 glGenBuffers(1, self._waterfall_buffer_top) 131 glGenBuffers(1, self._waterfall_buffer_bottom) 132 #initial data 133 data = (chr(0) + chr(0) + chr(0) + chr(0)) * self._fft_size*self._num_frames 134 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_top) 135 glBufferData( 136 GL_PIXEL_UNPACK_BUFFER, 137 len(data), data, 138 GL_DYNAMIC_DRAW, 139 ) 140 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_bottom) 141 glBufferData( 142 GL_PIXEL_UNPACK_BUFFER, 143 len(data), data, 144 GL_DYNAMIC_DRAW, 145 ) 146 #normalize the samples to min/max 147 samples = (samples - min)/(max-min) 148 samples = numpy.minimum(samples, 1) 149 samples = numpy.maximum(samples, 0) 150 data = numpy.array(255*numpy.concatenate(map(self._to_rgba, samples)), numpy.uint8).tostring() 151 #load the color data into the pixel buffers 152 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_top) 153 glBufferSubData( 154 GL_PIXEL_UNPACK_BUFFER, 155 (self._num_frames-self._frame_ptr-1)*self._fft_size*4, 156 len(data), data, 157 ) 158 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self._waterfall_buffer_bottom) 159 glBufferSubData( 160 GL_PIXEL_UNPACK_BUFFER, 161 self._frame_ptr*self._fft_size*4, 162 len(data), data, 163 ) 164 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0) #unbind 165 #increment the pointer 166 self._frame_ptr = (self._frame_ptr + 1)%self._num_frames 167 self.semaphore.release() gnuradio/branches/features/experimental-gui/waterfall_window.py
r8963 r8979 38 38 DEFAULT_WIN_SIZE = (600, 300) 39 39 DIV_LEVELS = (1, 2, 5, 10, 20) 40 FFT_PLOT_COLOR_SPEC = (0, 0, 1)41 PEAK_VALS_COLOR_SPEC = (0, 1, 0)42 NO_PEAK_VALS = list()43 PEAK_HOLD_KEY = 'peak_hold'44 40 Y_PER_DIV_KEY = 'y_per_div' 45 41 Y_DIVS_KEY = 'y_divs' … … 50 46 51 47 ################################################## 52 # FFTwindow control panel48 # Waterfall window control panel 53 49 ################################################## 54 50 class control_panel(wx.Panel): … … 65 61 wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER) 66 62 control_box = wx.BoxSizer(wx.VERTICAL) 67 #checkboxes for average and peak hold63 #checkboxes for average 68 64 control_box.AddStretchSpacer() 69 65 control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER) 70 66 self.average_check_box = common.CheckBoxController(self, 'Average', parent.ext_controller, parent.average_key) 71 67 control_box.Add(self.average_check_box, 0, wx.EXPAND) 72 self.peak_hold_check_box = common.CheckBoxController(self, 'Peak Hold', parent.controller, PEAK_HOLD_KEY)73 control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND)74 68 control_box.AddSpacer(2) 75 69 self.avg_alpha_slider = common.LogSliderController( … … 125 119 126 120 ################################################## 127 # FFTwindow with plotter and control panel128 ################################################## 129 class fft_window(wx.Panel, common.prop_setter):121 # Waterfall window with plotter and control panel 122 ################################################## 123 class waterfall_window(wx.Panel, common.prop_setter): 130 124 def __init__( 131 125 self, … … 143 137 average_key, 144 138 avg_alpha_key, 145 peak_hold,146 139 msg_key, 147 140 ): … … 156 149 self.average_key = average_key 157 150 self.avg_alpha_key = avg_alpha_key 158 self.peak_vals = NO_PEAK_VALS159 151 #init panel and plot 160 152 wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER) 161 self.plotter = plotter. channel_plotter(self)153 self.plotter = plotter.waterfall_plotter(self) 162 154 self.plotter.SetSize(wx.Size(*size)) 163 155 self.plotter.set_title(title) … … 171 163 self.ext_controller[self.average_key] = self.ext_controller[self.average_key] 172 164 self.ext_controller[self.avg_alpha_key] = self.ext_controller[self.avg_alpha_key] 173 self._register_set_prop(self.controller, PEAK_HOLD_KEY, peak_hold)174 165 self._register_set_prop(self.controller, Y_PER_DIV_KEY, y_per_div) 175 166 self._register_set_prop(self.controller, Y_DIVS_KEY, y_divs) … … 181 172 self.ext_controller.add_listener(msg_key, self.handle_msg) 182 173 self.ext_controller.add_listener(self.sample_rate_key, self.update_grid) 183 for key in ( 184 BASEBAND_FREQ_KEY, 185 Y_PER_DIV_KEY, X_DIVS_KEY, 186 Y_DIVS_KEY, REF_LEVEL_KEY, 187 ): self.controller.add_listener(key, self.update_grid) 174 self.controller.add_listener(BASEBAND_FREQ_KEY, self.update_grid) 175 self.controller.add_listener(X_DIVS_KEY, self.update_grid) 188 176 #initial update 189 177 self.update_grid() … … 194 182 If complex, reorder the fft samples so the negative bins come first. 195 183 If real, keep take only the positive bins. 196 Plot the samples onto the grid as channel 1. 197 If peak hold is enabled, plot peak vals as channel 2. 184 Send the data to the plotter. 198 185 @param msg the fft array as a character array 199 186 """ … … 205 192 if self.real: samples = samples[:num_samps/2] 206 193 else: samples = numpy.concatenate((samples[num_samps/2+1:], samples[:num_samps/2])) 207 #peak hold calculation208 if self.controller[PEAK_HOLD_KEY]:209 if len(self.peak_vals) != len(samples): self.peak_vals = samples210 self.peak_vals = numpy.maximum(samples, self.peak_vals)211 else: self.peak_vals = NO_PEAK_VALS212 194 #plot the fft 213 self.plotter.set_waveform( 214 channel=1, 215 samples=samples, 216 color_spec=FFT_PLOT_COLOR_SPEC, 217 ) 218 #plot the peak hold 219 self.plotter.set_waveform( 220 channel=2, 221 samples=self.peak_vals, 222 color_spec=PEAK_VALS_COLOR_SPEC, 195 self.plotter.plot_samples( 196 samples, self.controller[REF_LEVEL_KEY], 197 self.controller[Y_PER_DIV_KEY]*self.controller[Y_DIVS_KEY] + self.controller[REF_LEVEL_KEY], 223 198 ) 224 199 #update the plotter … … 236 211 sample_rate = self.ext_controller[self.sample_rate_key] 237 212 baseband_freq = self.controller[BASEBAND_FREQ_KEY] 238 y_per_div = self.controller[Y_PER_DIV_KEY]239 y_divs = self.controller[Y_DIVS_KEY]240 213 x_divs = self.controller[X_DIVS_KEY] 241 ref_level = self.controller[REF_LEVEL_KEY]242 214 #determine best fitting x_per_div 243 215 if self.real: x_width = sample_rate/2.0 … … 266 238 self.plotter.set_x_units('Frequency (%s)'%x_units) 267 239 #update y grid 268 self.plotter.set_y_grid( ref_level-y_per_div*y_divs, ref_level, y_per_div)240 self.plotter.set_y_grid(0, 10, 1) 269 241 #update y units 270 self.plotter.set_y_units(' Amplitude (dB)')242 self.plotter.set_y_units('Time (s)') 271 243 #update plotter 272 244 self.plotter.update() gnuradio/branches/features/experimental-gui/waterfallsink.py
r8962 r8979 23 23 # Imports 24 24 ################################################## 25 import fft_window25 import waterfall_window 26 26 import common 27 27 from gnuradio import gr, blks2 … … 37 37 38 38 ################################################## 39 # FFTsink block (wrapper for old wxgui)39 # Waterfall sink block (wrapper for old wxgui) 40 40 ################################################## 41 class _ fft_sink_base(gr.hier_block2, common.prop_setter):41 class _waterfall_sink_base(gr.hier_block2, common.prop_setter): 42 42 """! 43 43 An fft block with real/complex inputs and a gui window. … … 54 54 sample_rate=1, 55 55 fft_size=512, 56 frame_rate= fft_window.DEFAULT_FRAME_RATE,56 frame_rate=waterfall_window.DEFAULT_FRAME_RATE, 57 57 average=False, 58 58 avg_alpha=None, 59 59 title='', 60 size=fft_window.DEFAULT_WIN_SIZE, 61 peak_hold=False, 60 size=waterfall_window.DEFAULT_WIN_SIZE, 62 61 ): 63 62 #ensure avg alpha … … 66 65 gr.hier_block2.__init__( 67 66 self, 68 " fft_sink",67 "waterfall_sink", 69 68 gr.io_signature(1, 1, self.item_size), 70 69 gr.io_signature(0, 0, 0), … … 95 94 common.input_watcher(msgq, lambda x: self.controller.set(MSG_KEY, x)) 96 95 #create window 97 self.win = fft_window.fft_window(96 self.win = waterfall_window.waterfall_window( 98 97 parent=parent, 99 98 controller=self.controller, … … 109 108 average_key=AVERAGE_KEY, 110 109 avg_alpha_key=AVG_ALPHA_KEY, 111 peak_hold=peak_hold,112 110 msg_key=MSG_KEY, 113 111 ) … … 119 117 self._register_set_prop(self.controller, AVG_ALPHA_KEY) 120 118 121 class fft_sink_f(_fft_sink_base):119 class waterfall_sink_f(_waterfall_sink_base): 122 120 fft_chain = blks2.logpwrfft_f 123 121 item_size = gr.sizeof_float 124 122 real = True 125 class fft_sink_c(_fft_sink_base):123 class waterfall_sink_c(_waterfall_sink_base): 126 124 fft_chain = blks2.logpwrfft_c 127 125 item_size = gr.sizeof_gr_complex
