X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=gr-wxgui%2Fsrc%2Fpython%2Fwaterfall_window.py;h=6536ada10100287b3348c8e8b6bea7e0227a9c14;hb=c81312cee781a6912eb87f430096f3757e056b28;hp=f24b142a7d25a33c5f648f071b99543c7ee7210b;hpb=3fc664ececbafa04ab5a117b418cb7b4fc0652c6;p=debian%2Fgnuradio diff --git a/gr-wxgui/src/python/waterfall_window.py b/gr-wxgui/src/python/waterfall_window.py index f24b142a..6536ada1 100644 --- a/gr-wxgui/src/python/waterfall_window.py +++ b/gr-wxgui/src/python/waterfall_window.py @@ -16,7 +16,7 @@ # 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. +# Boston, MA 02110-1`301, USA. # ################################################## @@ -30,6 +30,7 @@ import math import pubsub from constants import * from gnuradio import gr #for gr.prefs +import forms ################################################## # Constants @@ -37,9 +38,11 @@ from gnuradio import gr #for gr.prefs SLIDER_STEPS = 100 AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0 DEFAULT_FRAME_RATE = gr.prefs().get_long('wxgui', 'waterfall_rate', 30) +DEFAULT_COLOR_MODE = gr.prefs().get_string('wxgui', 'waterfall_color', 'rgb1') DEFAULT_WIN_SIZE = (600, 300) DIV_LEVELS = (1, 2, 5, 10, 20) MIN_DYNAMIC_RANGE, MAX_DYNAMIC_RANGE = 10, 200 +DYNAMIC_RANGE_STEP = 10. COLOR_MODES = ( ('RGB1', 'rgb1'), ('RGB2', 'rgb2'), @@ -61,57 +64,80 @@ class control_panel(wx.Panel): @param parent the wx parent window """ self.parent = parent - wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER) + wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER) + parent[SHOW_CONTROL_PANEL_KEY] = True + parent.subscribe(SHOW_CONTROL_PANEL_KEY, self.Show) control_box = wx.BoxSizer(wx.VERTICAL) control_box.AddStretchSpacer() - control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER) - #color mode - control_box.AddStretchSpacer() - self.color_mode_chooser = common.DropDownController(self, 'Color', COLOR_MODES, parent, COLOR_MODE_KEY) - control_box.Add(self.color_mode_chooser, 0, wx.EXPAND) + options_box = forms.static_box_sizer( + parent=self, sizer=control_box, label='Options', + bold=True, orient=wx.VERTICAL, + ) #average + forms.check_box( + sizer=options_box, parent=self, label='Average', + ps=parent, key=AVERAGE_KEY, + ) + avg_alpha_text = forms.static_text( + sizer=options_box, parent=self, label='Avg Alpha', + converter=forms.float_converter(lambda x: '%.4f'%x), + ps=parent, key=AVG_ALPHA_KEY, width=50, + ) + avg_alpha_slider = forms.log_slider( + sizer=options_box, parent=self, + min_exp=AVG_ALPHA_MIN_EXP, + max_exp=AVG_ALPHA_MAX_EXP, + num_steps=SLIDER_STEPS, + ps=parent, key=AVG_ALPHA_KEY, + ) + for widget in (avg_alpha_text, avg_alpha_slider): + parent.subscribe(AVERAGE_KEY, widget.Enable) + widget.Enable(parent[AVERAGE_KEY]) + #begin axes box control_box.AddStretchSpacer() - 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, - formatter=lambda x: ': %.4f'%x, + axes_box = forms.static_box_sizer( + parent=self, sizer=control_box, label='Axes Options', + bold=True, orient=wx.VERTICAL, + ) + #num lines buttons + forms.incr_decr_buttons( + parent=self, sizer=axes_box, label='Time Scale', + on_incr=self._on_incr_time_scale, on_decr=self._on_decr_time_scale, ) - parent.ext_controller.subscribe(parent.average_key, self.avg_alpha_slider.Enable) - control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND) #dyanmic range buttons - control_box.AddStretchSpacer() - control_box.Add(common.LabelText(self, 'Dynamic Range'), 0, wx.ALIGN_CENTER) - control_box.AddSpacer(2) - self._dynamic_range_buttons = common.IncrDecrButtons(self, self._on_incr_dynamic_range, self._on_decr_dynamic_range) - control_box.Add(self._dynamic_range_buttons, 0, wx.ALIGN_CENTER) + forms.incr_decr_buttons( + parent=self, sizer=axes_box, label='Dyn Range', + on_incr=self._on_incr_dynamic_range, on_decr=self._on_decr_dynamic_range, + ) #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) - #num lines buttons - control_box.AddStretchSpacer() - control_box.Add(common.LabelText(self, 'Set Time Scale'), 0, wx.ALIGN_CENTER) - control_box.AddSpacer(2) - self._time_scale_buttons = common.IncrDecrButtons(self, self._on_incr_time_scale, self._on_decr_time_scale) - control_box.Add(self._time_scale_buttons, 0, wx.ALIGN_CENTER) + forms.incr_decr_buttons( + parent=self, sizer=axes_box, label='Ref Level', + on_incr=self._on_incr_ref_level, on_decr=self._on_decr_ref_level, + ) + #color mode + forms.drop_down( + parent=self, sizer=axes_box, width=100, + ps=parent, key=COLOR_MODE_KEY, label='Color', + choices=map(lambda x: x[1], COLOR_MODES), + labels=map(lambda x: x[0], COLOR_MODES), + ) #autoscale - control_box.AddStretchSpacer() - self.autoscale_button = wx.Button(self, label='Autoscale', style=wx.BU_EXACTFIT) - self.autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale) - control_box.Add(self.autoscale_button, 0, wx.EXPAND) + forms.single_button( + parent=self, sizer=axes_box, label='Autoscale', + callback=self.parent.autoscale, + ) #clear - self.clear_button = wx.Button(self, label='Clear', style=wx.BU_EXACTFIT) - self.clear_button.Bind(wx.EVT_BUTTON, self._on_clear_button) - control_box.Add(self.clear_button, 0, wx.EXPAND) + control_box.AddStretchSpacer() + forms.single_button( + parent=self, sizer=control_box, label='Clear', + callback=self._on_clear_button, + ) #run/stop - self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run') - control_box.Add(self.run_button, 0, wx.EXPAND) + forms.toggle_button( + sizer=control_box, parent=self, + true_label='Stop', false_label='Run', + ps=parent, key=RUNNING_KEY, + ) #set sizer self.SetSizerAndFit(control_box) @@ -119,34 +145,33 @@ class control_panel(wx.Panel): # Event handlers ################################################## def _on_clear_button(self, event): - self.parent.set_num_lines(self.parent[NUM_LINES_KEY]) + self.parent[NUM_LINES_KEY] = self.parent[NUM_LINES_KEY] def _on_incr_dynamic_range(self, event): - self.parent.set_dynamic_range( - min(self.parent[DYNAMIC_RANGE_KEY] + 10, MAX_DYNAMIC_RANGE)) + self.parent[DYNAMIC_RANGE_KEY] = min(MAX_DYNAMIC_RANGE, common.get_clean_incr(self.parent[DYNAMIC_RANGE_KEY])) def _on_decr_dynamic_range(self, event): - self.parent.set_dynamic_range( - max(self.parent[DYNAMIC_RANGE_KEY] - 10, MIN_DYNAMIC_RANGE)) + self.parent[DYNAMIC_RANGE_KEY] = max(MIN_DYNAMIC_RANGE, common.get_clean_decr(self.parent[DYNAMIC_RANGE_KEY])) def _on_incr_ref_level(self, event): - self.parent.set_ref_level( - self.parent[REF_LEVEL_KEY] + self.parent[DYNAMIC_RANGE_KEY]*.1) + self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] + self.parent[DYNAMIC_RANGE_KEY]/DYNAMIC_RANGE_STEP def _on_decr_ref_level(self, event): - self.parent.set_ref_level( - self.parent[REF_LEVEL_KEY] - self.parent[DYNAMIC_RANGE_KEY]*.1) + self.parent[REF_LEVEL_KEY] = self.parent[REF_LEVEL_KEY] - self.parent[DYNAMIC_RANGE_KEY]/DYNAMIC_RANGE_STEP def _on_incr_time_scale(self, event): - old_rate = self.parent.ext_controller[self.parent.frame_rate_key] - self.parent.ext_controller[self.parent.frame_rate_key] *= 0.75 - if self.parent.ext_controller[self.parent.frame_rate_key] == old_rate: - self.parent.ext_controller[self.parent.decimation_key] += 1 + old_rate = self.parent[FRAME_RATE_KEY] + self.parent[FRAME_RATE_KEY] *= 0.75 + if self.parent[FRAME_RATE_KEY] < 1.0: + self.parent[FRAME_RATE_KEY] = 1.0 + + if self.parent[FRAME_RATE_KEY] == old_rate: + self.parent[DECIMATION_KEY] += 1 def _on_decr_time_scale(self, event): - old_rate = self.parent.ext_controller[self.parent.frame_rate_key] - self.parent.ext_controller[self.parent.frame_rate_key] *= 1.25 - if self.parent.ext_controller[self.parent.frame_rate_key] == old_rate: - self.parent.ext_controller[self.parent.decimation_key] -= 1 + old_rate = self.parent[FRAME_RATE_KEY] + self.parent[FRAME_RATE_KEY] *= 1.25 + if self.parent[FRAME_RATE_KEY] == old_rate: + self.parent[DECIMATION_KEY] -= 1 ################################################## # Waterfall window with plotter and control panel ################################################## -class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter): +class waterfall_window(wx.Panel, pubsub.pubsub): def __init__( self, parent, @@ -169,49 +194,47 @@ class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter): pubsub.pubsub.__init__(self) #setup self.samples = list() - self.ext_controller = controller self.real = real self.fft_size = fft_size - self.decimation_key = decimation_key - self.sample_rate_key = sample_rate_key - self.frame_rate_key = frame_rate_key - self.average_key = average_key - self.avg_alpha_key = avg_alpha_key + #proxy the keys + self.proxy(MSG_KEY, controller, msg_key) + self.proxy(DECIMATION_KEY, controller, decimation_key) + self.proxy(FRAME_RATE_KEY, controller, frame_rate_key) + self.proxy(AVERAGE_KEY, controller, average_key) + self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key) + self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key) #init panel and plot - wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER) + wx.Panel.__init__(self, parent, style=wx.SIMPLE_BORDER) self.plotter = plotter.waterfall_plotter(self) self.plotter.SetSize(wx.Size(*size)) self.plotter.set_title(title) self.plotter.enable_point_label(True) + self.plotter.enable_grid_lines(False) + #plotter listeners + self.subscribe(COLOR_MODE_KEY, self.plotter.set_color_mode) + self.subscribe(NUM_LINES_KEY, self.plotter.set_num_lines) + #initialize values + self[DYNAMIC_RANGE_KEY] = dynamic_range + self[NUM_LINES_KEY] = num_lines + self[Y_DIVS_KEY] = 8 + self[X_DIVS_KEY] = 8 #approximate + self[REF_LEVEL_KEY] = ref_level + self[BASEBAND_FREQ_KEY] = baseband_freq + self[COLOR_MODE_KEY] = COLOR_MODES[0][1] + self[COLOR_MODE_KEY] = DEFAULT_COLOR_MODE + self[RUNNING_KEY] = True #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) - #plotter listeners - self.subscribe(COLOR_MODE_KEY, self.plotter.set_color_mode) - self.subscribe(NUM_LINES_KEY, self.plotter.set_num_lines) - #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, DYNAMIC_RANGE_KEY, dynamic_range) - self._register_set_prop(self, NUM_LINES_KEY, num_lines) - self._register_set_prop(self, Y_DIVS_KEY, 8) - self._register_set_prop(self, X_DIVS_KEY, 8) #approximate - self._register_set_prop(self, REF_LEVEL_KEY, ref_level) - self._register_set_prop(self, BASEBAND_FREQ_KEY, baseband_freq) - self._register_set_prop(self, COLOR_MODE_KEY, COLOR_MODES[0][1]) - self._register_set_prop(self, RUNNING_KEY, True) #register events - self.ext_controller.subscribe(msg_key, self.handle_msg) - self.ext_controller.subscribe(self.decimation_key, self.update_grid) - self.ext_controller.subscribe(self.sample_rate_key, self.update_grid) - self.ext_controller.subscribe(self.frame_rate_key, self.update_grid) - self.subscribe(BASEBAND_FREQ_KEY, self.update_grid) - self.subscribe(NUM_LINES_KEY, self.update_grid) - self.subscribe(Y_DIVS_KEY, self.update_grid) - self.subscribe(X_DIVS_KEY, self.update_grid) + self.subscribe(MSG_KEY, self.handle_msg) + for key in ( + DECIMATION_KEY, SAMPLE_RATE_KEY, FRAME_RATE_KEY, + BASEBAND_FREQ_KEY, X_DIVS_KEY, Y_DIVS_KEY, NUM_LINES_KEY, + ): self.subscribe(key, self.update_grid) #initial update self.update_grid() @@ -222,16 +245,10 @@ class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter): Does not affect the current data in the waterfall. """ if not len(self.samples): return - #get the peak level (max of the samples) - peak_level = numpy.max(self.samples) - #get the noise floor (averge the smallest samples) - noise_floor = numpy.average(numpy.sort(self.samples)[:len(self.samples)/4]) - #padding - noise_floor -= abs(noise_floor)*.5 - peak_level += abs(peak_level)*.1 + min_level, max_level = common.get_min_max_fft(self.samples) #set the range and level - self.set_ref_level(peak_level) - self.set_dynamic_range(peak_level - noise_floor) + self[DYNAMIC_RANGE_KEY] = common.get_clean_num(max_level - min_level) + self[REF_LEVEL_KEY] = DYNAMIC_RANGE_STEP*round(.5+max_level/DYNAMIC_RANGE_STEP) def handle_msg(self, msg): """ @@ -246,8 +263,8 @@ class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter): self.samples = 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:], samples[:num_samps/2])) + if self.real: samples = samples[:(num_samps+1)/2] + else: samples = numpy.concatenate((samples[num_samps/2+1:], samples[:(num_samps+1)/2])) #plot the fft self.plotter.set_samples( samples=samples, @@ -266,8 +283,10 @@ class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter): The y axis depends on y per div, y divs, and ref level. """ #grid parameters - sample_rate = self.ext_controller[self.sample_rate_key] - frame_rate = self.ext_controller[self.frame_rate_key] + sample_rate = self[SAMPLE_RATE_KEY] + frame_rate = self[FRAME_RATE_KEY] + if frame_rate < 1.0 : + frame_rate = 1.0 baseband_freq = self[BASEBAND_FREQ_KEY] num_lines = self[NUM_LINES_KEY] y_divs = self[Y_DIVS_KEY] @@ -276,28 +295,25 @@ class waterfall_window(wx.Panel, pubsub.pubsub, common.prop_setter): 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) - coeff, exp, prefix = common.get_si_components(abs(baseband_freq) + abs(sample_rate/2.0)) #update the x grid if self.real: self.plotter.set_x_grid( baseband_freq, baseband_freq + sample_rate/2.0, - x_per_div, - 10**(-exp), + x_per_div, True, ) else: self.plotter.set_x_grid( baseband_freq - sample_rate/2.0, baseband_freq + sample_rate/2.0, - x_per_div, - 10**(-exp), + x_per_div, True, ) #update x units - self.plotter.set_x_label('Frequency', prefix+'Hz') + self.plotter.set_x_label('Frequency', 'Hz') #update y grid duration = float(num_lines)/frame_rate y_per_div = common.get_clean_num(duration/y_divs) - self.plotter.set_y_grid(0, duration, y_per_div) + self.plotter.set_y_grid(0, duration, y_per_div, True) #update y units self.plotter.set_y_label('Time', 's') #update plotter