Changeset 8979

Show
Ignore:
Timestamp:
07/22/08 16:39:49
Author:
jblum
Message:

waterfallsink working

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gnuradio/branches/features/experimental-gui/plotter/channel_plotter.py

    r8963 r8979  
    4949                """ 
    5050                glEnableClientState(GL_VERTEX_ARRAY) 
    51                 self.grid_compiled_list_id = glGenLists(1) 
     51                self._grid_compiled_list_id = glGenLists(1) 
    5252 
    5353        def set_legend(self, legend): 
     
    6969                #store the grid drawing operations 
    7070                if self.changed(): 
    71                         glNewList(self.grid_compiled_list_id, GL_COMPILE) 
     71                        glNewList(self._grid_compiled_list_id, GL_COMPILE) 
    7272                        self._draw_grid() 
    7373                        self._draw_legend() 
     
    7575                        self.changed(False) 
    7676                #draw the grid 
    77                 glCallList(self.grid_compiled_list_id) 
     77                glCallList(self._grid_compiled_list_id) 
    7878                #use scissor to prevent drawing outside grid 
    7979                glEnable(GL_SCISSOR_TEST) 
  • gnuradio/branches/features/experimental-gui/plotter/plotter_base.py

    r8963 r8979  
    4747                @param parent the parent widgit 
    4848                """ 
     49                self.semaphore = threading.Semaphore(1) 
    4950                wx.glcanvas.GLCanvas.__init__(self, parent, -1) 
    5051                self.changed(False) 
     
    7475                #check for a change in window size 
    7576                if self._resized_flag: 
     77                        self.semaphore.acquire(True) 
    7678                        self.width, self.height = self.GetSize() 
    7779                        glViewport(0, 0, self.width, self.height) 
     
    7981                        glLoadIdentity() 
    8082                        glOrtho(0, self.width, self.height, 0, 1, 0) 
     83                        glMatrixMode(GL_MODELVIEW) 
     84                        glLoadIdentity() 
     85                        glViewport(0, 0, self.width, self.height) 
    8186                        self._resized_flag = False 
    8287                        self.changed(True) 
     88                        self.semaphore.release() 
    8389                self.draw() 
    8490 
     
    101107 
    102108        def __init__(self, parent, padding): 
    103                 self.semaphore = threading.Semaphore(1
     109                _plotter_base.__init__(self, parent
    104110                self.padding_top, self.padding_right, self.padding_bottom, self.padding_left = padding 
    105111                #store title and unit strings 
     
    110116                self.set_x_grid(-1, 1, 1) 
    111117                self.set_y_grid(-1, 1, 1) 
    112                 _plotter_base.__init__(self, parent) 
    113118 
    114119        def set_title(self, title): 
  • gnuradio/branches/features/experimental-gui/plotter/waterfall_plotter.py

    r8963 r8979  
    2626import gltext 
    2727 
    28 PADDING = 35, 35, 40, 60 #top, right, bottom, left 
     28PADDING = 35, 60, 40, 60 #top, right, bottom, left 
    2929 
    3030################################################## 
     
    3838                #init 
    3939                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) 
    4045 
    4146        def _gl_init(self): 
     
    4348                Run gl initialization tasks. 
    4449                """ 
    45                 self.grid_compiled_list_id = glGenLists(1) 
    46                  
     50                self._grid_compiled_list_id = glGenLists(1) 
     51 
    4752        def draw(self): 
    4853                """! 
     
    5257                self.clear() 
    5358                #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) 
    5661                        self._draw_grid() 
    5762                        glEndList() 
    58                         self._changed = False 
     63                        self.changed(False) 
    5964                #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                ) 
    6174                #draw the waterfall 
    62                 #TODO 
     75                self._draw_waterfall() 
     76                glDisable(GL_SCISSOR_TEST) 
    6377                #swap buffer into display 
    6478                self.SwapBuffers() 
    6579                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  
    3838DEFAULT_WIN_SIZE = (600, 300) 
    3939DIV_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' 
    4440Y_PER_DIV_KEY = 'y_per_div' 
    4541Y_DIVS_KEY = 'y_divs' 
     
    5046 
    5147################################################## 
    52 # FFT window control panel 
     48# Waterfall window control panel 
    5349################################################## 
    5450class control_panel(wx.Panel): 
     
    6561                wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER) 
    6662                control_box = wx.BoxSizer(wx.VERTICAL) 
    67                 #checkboxes for average and peak hold 
     63                #checkboxes for average 
    6864                control_box.AddStretchSpacer() 
    6965                control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER) 
    7066                self.average_check_box = common.CheckBoxController(self, 'Average', parent.ext_controller, parent.average_key) 
    7167                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) 
    7468                control_box.AddSpacer(2) 
    7569                self.avg_alpha_slider = common.LogSliderController( 
     
    125119 
    126120################################################## 
    127 # FFT window with plotter and control panel 
    128 ################################################## 
    129 class fft_window(wx.Panel, common.prop_setter): 
     121# Waterfall window with plotter and control panel 
     122################################################## 
     123class waterfall_window(wx.Panel, common.prop_setter): 
    130124        def __init__( 
    131125                self, 
     
    143137                average_key, 
    144138                avg_alpha_key, 
    145                 peak_hold, 
    146139                msg_key, 
    147140        ): 
     
    156149                self.average_key = average_key 
    157150                self.avg_alpha_key = avg_alpha_key 
    158                 self.peak_vals = NO_PEAK_VALS 
    159151                #init panel and plot 
    160152                wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER) 
    161                 self.plotter = plotter.channel_plotter(self) 
     153                self.plotter = plotter.waterfall_plotter(self) 
    162154                self.plotter.SetSize(wx.Size(*size)) 
    163155                self.plotter.set_title(title) 
     
    171163                self.ext_controller[self.average_key] = self.ext_controller[self.average_key] 
    172164                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) 
    174165                self._register_set_prop(self.controller, Y_PER_DIV_KEY, y_per_div) 
    175166                self._register_set_prop(self.controller, Y_DIVS_KEY, y_divs) 
     
    181172                self.ext_controller.add_listener(msg_key, self.handle_msg) 
    182173                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) 
    188176                #initial update 
    189177                self.update_grid() 
     
    194182                If complex, reorder the fft samples so the negative bins come first. 
    195183                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. 
    198185                @param msg the fft array as a character array 
    199186                """ 
     
    205192                if self.real: samples = samples[:num_samps/2] 
    206193                else: samples = numpy.concatenate((samples[num_samps/2+1:], samples[:num_samps/2])) 
    207                 #peak hold calculation 
    208                 if self.controller[PEAK_HOLD_KEY]: 
    209                         if len(self.peak_vals) != len(samples): self.peak_vals = samples 
    210                         self.peak_vals = numpy.maximum(samples, self.peak_vals) 
    211                 else: self.peak_vals = NO_PEAK_VALS 
    212194                #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], 
    223198                ) 
    224199                #update the plotter 
     
    236211                sample_rate = self.ext_controller[self.sample_rate_key] 
    237212                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] 
    240213                x_divs = self.controller[X_DIVS_KEY] 
    241                 ref_level = self.controller[REF_LEVEL_KEY] 
    242214                #determine best fitting x_per_div 
    243215                if self.real: x_width = sample_rate/2.0 
     
    266238                self.plotter.set_x_units('Frequency (%s)'%x_units) 
    267239                #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
    269241                #update y units 
    270                 self.plotter.set_y_units('Amplitude (dB)') 
     242                self.plotter.set_y_units('Time (s)') 
    271243                #update plotter 
    272244                self.plotter.update() 
  • gnuradio/branches/features/experimental-gui/waterfallsink.py

    r8962 r8979  
    2323# Imports 
    2424################################################## 
    25 import fft_window 
     25import waterfall_window 
    2626import common 
    2727from gnuradio import gr, blks2 
     
    3737 
    3838################################################## 
    39 # FFT sink block (wrapper for old wxgui) 
     39# Waterfall sink block (wrapper for old wxgui) 
    4040################################################## 
    41 class _fft_sink_base(gr.hier_block2, common.prop_setter): 
     41class _waterfall_sink_base(gr.hier_block2, common.prop_setter): 
    4242        """! 
    4343        An fft block with real/complex inputs and a gui window. 
     
    5454                sample_rate=1, 
    5555                fft_size=512, 
    56                 frame_rate=fft_window.DEFAULT_FRAME_RATE, 
     56                frame_rate=waterfall_window.DEFAULT_FRAME_RATE, 
    5757                average=False, 
    5858                avg_alpha=None, 
    5959                title='', 
    60                 size=fft_window.DEFAULT_WIN_SIZE, 
    61                 peak_hold=False, 
     60                size=waterfall_window.DEFAULT_WIN_SIZE, 
    6261        ): 
    6362                #ensure avg alpha 
     
    6665                gr.hier_block2.__init__( 
    6766                        self, 
    68                         "fft_sink", 
     67                        "waterfall_sink", 
    6968                        gr.io_signature(1, 1, self.item_size), 
    7069                        gr.io_signature(0, 0, 0), 
     
    9594                common.input_watcher(msgq, lambda x: self.controller.set(MSG_KEY, x)) 
    9695                #create window 
    97                 self.win = fft_window.fft_window( 
     96                self.win = waterfall_window.waterfall_window( 
    9897                        parent=parent, 
    9998                        controller=self.controller, 
     
    109108                        average_key=AVERAGE_KEY, 
    110109                        avg_alpha_key=AVG_ALPHA_KEY, 
    111                         peak_hold=peak_hold, 
    112110                        msg_key=MSG_KEY, 
    113111                ) 
     
    119117                self._register_set_prop(self.controller, AVG_ALPHA_KEY) 
    120118 
    121 class fft_sink_f(_fft_sink_base): 
     119class waterfall_sink_f(_waterfall_sink_base): 
    122120        fft_chain = blks2.logpwrfft_f 
    123121        item_size = gr.sizeof_float 
    124122        real = True 
    125 class fft_sink_c(_fft_sink_base): 
     123class waterfall_sink_c(_waterfall_sink_base): 
    126124        fft_chain = blks2.logpwrfft_c 
    127125        item_size = gr.sizeof_gr_complex