diff options
Diffstat (limited to 'gr-wxgui/src/python/number_window.py')
-rw-r--r-- | gr-wxgui/src/python/number_window.py | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/gr-wxgui/src/python/number_window.py b/gr-wxgui/src/python/number_window.py new file mode 100644 index 000000000..ab9d1ebc0 --- /dev/null +++ b/gr-wxgui/src/python/number_window.py @@ -0,0 +1,213 @@ +# +# 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 common +import numpy +import wx +import pubsub +from constants import * +from gnuradio import gr #for gr.prefs +import forms + +################################################## +# Constants +################################################## +NEG_INF = float('-inf') +SLIDER_STEPS = 100 +AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0 +DEFAULT_NUMBER_RATE = gr.prefs().get_long('wxgui', 'number_rate', 5) +DEFAULT_WIN_SIZE = (300, 300) +DEFAULT_GAUGE_RANGE = 1000 +VALUE_REPR_KEY = 'value_repr' +VALUE_REAL_KEY = 'value_real' +VALUE_IMAG_KEY = 'value_imag' + +################################################## +# Number window control panel +################################################## +class control_panel(wx.Panel): + """ + A control panel with wx widgits to control the averaging. + """ + + def __init__(self, parent): + """ + Create a new control panel. + @param parent the wx parent window + """ + self.parent = parent + wx.Panel.__init__(self, parent) + parent[SHOW_CONTROL_PANEL_KEY] = True + parent.subscribe(SHOW_CONTROL_PANEL_KEY, self.Show) + control_box = wx.BoxSizer(wx.VERTICAL) + #checkboxes for average and peak hold + control_box.AddStretchSpacer() + options_box = forms.static_box_sizer( + parent=self, sizer=control_box, label='Options', + bold=True, orient=wx.VERTICAL, + ) + forms.check_box( + sizer=options_box, parent=self, label='Peak Hold', + ps=parent, key=PEAK_HOLD_KEY, + ) + forms.check_box( + sizer=options_box, parent=self, label='Average', + ps=parent, key=AVERAGE_KEY, + ) + #static text and slider for averaging + 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]) + #run/stop + control_box.AddStretchSpacer() + 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) + +################################################## +# Numbersink window with label and gauges +################################################## +class number_window(wx.Panel, pubsub.pubsub): + def __init__( + self, + parent, + controller, + size, + title, + units, + show_gauge, + real, + minval, + maxval, + decimal_places, + average_key, + avg_alpha_key, + peak_hold, + msg_key, + sample_rate_key, + ): + pubsub.pubsub.__init__(self) + wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER) + #setup + self.peak_val_real = NEG_INF + self.peak_val_imag = NEG_INF + self.real = real + self.units = units + self.decimal_places = decimal_places + #proxy the keys + self.proxy(MSG_KEY, controller, msg_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) + #initialize values + self[PEAK_HOLD_KEY] = peak_hold + self[RUNNING_KEY] = True + self[VALUE_REAL_KEY] = minval + self[VALUE_IMAG_KEY] = minval + #setup the box with display and controls + self.control_panel = control_panel(self) + main_box = wx.BoxSizer(wx.HORIZONTAL) + sizer = forms.static_box_sizer( + parent=self, sizer=main_box, label=title, + bold=True, orient=wx.VERTICAL, proportion=1, + ) + main_box.Add(self.control_panel, 0, wx.EXPAND) + sizer.AddStretchSpacer() + forms.static_text( + parent=self, sizer=sizer, + ps=self, key=VALUE_REPR_KEY, width=size[0], + converter=forms.str_converter(), + ) + sizer.AddStretchSpacer() + self.gauge_real = forms.gauge( + parent=self, sizer=sizer, style=wx.GA_HORIZONTAL, + ps=self, key=VALUE_REAL_KEY, length=size[0], + minimum=minval, maximum=maxval, num_steps=DEFAULT_GAUGE_RANGE, + ) + self.gauge_imag = forms.gauge( + parent=self, sizer=sizer, style=wx.GA_HORIZONTAL, + ps=self, key=VALUE_IMAG_KEY, length=size[0], + minimum=minval, maximum=maxval, num_steps=DEFAULT_GAUGE_RANGE, + ) + #hide/show gauges + self.show_gauges(show_gauge) + self.SetSizerAndFit(main_box) + #register events + self.subscribe(MSG_KEY, self.handle_msg) + + def show_gauges(self, show_gauge): + """ + Show or hide the gauges. + If this is real, never show the imaginary gauge. + @param show_gauge true to show + """ + self.gauge_real.ShowItems(show_gauge) + self.gauge_imag.ShowItems(show_gauge and not self.real) + + def handle_msg(self, msg): + """ + Handle a message from the message queue. + Convert the string based message into a float or complex. + If more than one number was read, only take the last number. + Perform peak hold operations, set the gauges and display. + @param event event.data is the number sample as a character array + """ + if not self[RUNNING_KEY]: return + format_string = "%%.%df"%self.decimal_places + if self.real: + sample = numpy.fromstring(msg, numpy.float32)[-1] + if self[PEAK_HOLD_KEY]: sample = self.peak_val_real = max(self.peak_val_real, sample) + label_text = "%s %s"%(format_string%sample, self.units) + self[VALUE_REAL_KEY] = sample + else: + sample = numpy.fromstring(msg, numpy.complex64)[-1] + if self[PEAK_HOLD_KEY]: + self.peak_val_real = max(self.peak_val_real, sample.real) + self.peak_val_imag = max(self.peak_val_imag, sample.imag) + sample = self.peak_val_real + self.peak_val_imag*1j + label_text = "%s + %sj %s"%(format_string%sample.real, format_string%sample.imag, self.units) + self[VALUE_REAL_KEY] = sample.real + self[VALUE_IMAG_KEY] = sample.imag + #set label text + self[VALUE_REPR_KEY] = label_text + #clear peak hold + if not self[PEAK_HOLD_KEY]: + self.peak_val_real = NEG_INF + self.peak_val_imag = NEG_INF |