root / gr-wxgui / src / python / number_window.py @ 36649d4e
History | View | Annotate | Download (6.4 kB)
| 1 | #
|
|---|---|
| 2 | # Copyright 2008 Free Software Foundation, Inc.
|
| 3 | #
|
| 4 | # This file is part of GNU Radio
|
| 5 | #
|
| 6 | # GNU Radio is free software; you can redistribute it and/or modify
|
| 7 | # it under the terms of the GNU General Public License as published by
|
| 8 | # the Free Software Foundation; either version 3, or (at your option)
|
| 9 | # any later version.
|
| 10 | #
|
| 11 | # GNU Radio is distributed in the hope that it will be useful,
|
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 14 | # GNU General Public License for more details.
|
| 15 | #
|
| 16 | # You should have received a copy of the GNU General Public License
|
| 17 | # along with GNU Radio; see the file COPYING. If not, write to
|
| 18 | # the Free Software Foundation, Inc., 51 Franklin Street,
|
| 19 | # Boston, MA 02110-1301, USA.
|
| 20 | #
|
| 21 | |
| 22 | ##################################################
|
| 23 | # Imports
|
| 24 | ##################################################
|
| 25 | import common |
| 26 | import numpy |
| 27 | import wx |
| 28 | import pubsub |
| 29 | from constants import * |
| 30 | |
| 31 | ##################################################
|
| 32 | # Constants
|
| 33 | ##################################################
|
| 34 | NEG_INF = float('-inf') |
| 35 | SLIDER_STEPS = 100
|
| 36 | AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0 |
| 37 | DEFAULT_NUMBER_RATE = 5
|
| 38 | DEFAULT_WIN_SIZE = (300, 300) |
| 39 | DEFAULT_GAUGE_RANGE = 1000
|
| 40 | |
| 41 | ##################################################
|
| 42 | # Number window control panel
|
| 43 | ##################################################
|
| 44 | class control_panel(wx.Panel): |
| 45 | """!
|
| 46 | A control panel with wx widgits to control the averaging.
|
| 47 | """ |
| 48 | |
| 49 | def __init__(self, parent): |
| 50 | """!
|
| 51 | Create a new control panel.
|
| 52 | @param parent the wx parent window
|
| 53 | """ |
| 54 | self.parent = parent
|
| 55 | wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER) |
| 56 | control_box = wx.BoxSizer(wx.VERTICAL) |
| 57 | #checkboxes for average and peak hold
|
| 58 | control_box.AddStretchSpacer() |
| 59 | control_box.Add(common.LabelText(self, 'Options'), 0, wx.ALIGN_CENTER) |
| 60 | self.average_check_box = common.CheckBoxController(self, 'Average', parent.ext_controller, parent.average_key) |
| 61 | control_box.Add(self.average_check_box, 0, wx.EXPAND) |
| 62 | self.peak_hold_check_box = common.CheckBoxController(self, 'Peak Hold', parent, PEAK_HOLD_KEY) |
| 63 | control_box.Add(self.peak_hold_check_box, 0, wx.EXPAND) |
| 64 | control_box.AddSpacer(2)
|
| 65 | self.avg_alpha_slider = common.LogSliderController(
|
| 66 | self, 'Avg Alpha', |
| 67 | AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP, SLIDER_STEPS, |
| 68 | parent.ext_controller, parent.avg_alpha_key, |
| 69 | formatter=lambda x: ': %.4f'%x, |
| 70 | ) |
| 71 | parent.ext_controller.subscribe(parent.average_key, self.avg_alpha_slider.Enable)
|
| 72 | control_box.Add(self.avg_alpha_slider, 0, wx.EXPAND) |
| 73 | #run/stop
|
| 74 | control_box.AddStretchSpacer() |
| 75 | self.run_button = common.ToggleButtonController(self, parent, RUNNING_KEY, 'Stop', 'Run') |
| 76 | control_box.Add(self.run_button, 0, wx.EXPAND) |
| 77 | #set sizer
|
| 78 | self.SetSizerAndFit(control_box)
|
| 79 | |
| 80 | ##################################################
|
| 81 | # Numbersink window with label and gauges
|
| 82 | ##################################################
|
| 83 | class number_window(wx.Panel, pubsub.pubsub, common.prop_setter): |
| 84 | def __init__( |
| 85 | self,
|
| 86 | parent, |
| 87 | controller, |
| 88 | size, |
| 89 | title, |
| 90 | units, |
| 91 | show_gauge, |
| 92 | real, |
| 93 | minval, |
| 94 | maxval, |
| 95 | decimal_places, |
| 96 | average_key, |
| 97 | avg_alpha_key, |
| 98 | peak_hold, |
| 99 | msg_key, |
| 100 | ): |
| 101 | pubsub.pubsub.__init__(self)
|
| 102 | wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER) |
| 103 | #setup
|
| 104 | self.peak_val_real = NEG_INF
|
| 105 | self.peak_val_imag = NEG_INF
|
| 106 | self.ext_controller = controller
|
| 107 | self.real = real
|
| 108 | self.units = units
|
| 109 | self.minval = minval
|
| 110 | self.maxval = maxval
|
| 111 | self.decimal_places = decimal_places
|
| 112 | self.average_key = average_key
|
| 113 | self.avg_alpha_key = avg_alpha_key
|
| 114 | #setup the box with display and controls
|
| 115 | self.control_panel = control_panel(self) |
| 116 | main_box = wx.BoxSizer(wx.HORIZONTAL) |
| 117 | sizer = wx.BoxSizer(wx.VERTICAL) |
| 118 | main_box.Add(sizer, 1, wx.EXPAND)
|
| 119 | main_box.Add(self.control_panel, 0, wx.EXPAND) |
| 120 | sizer.Add(common.LabelText(self, title), 1, wx.ALIGN_CENTER) |
| 121 | self.text = wx.StaticText(self, size=(size[0], -1)) |
| 122 | sizer.Add(self.text, 1, wx.EXPAND) |
| 123 | self.gauge_real = wx.Gauge(self, range=DEFAULT_GAUGE_RANGE, style=wx.GA_HORIZONTAL) |
| 124 | self.gauge_imag = wx.Gauge(self, range=DEFAULT_GAUGE_RANGE, style=wx.GA_HORIZONTAL) |
| 125 | #hide/show gauges
|
| 126 | self.show_gauges(show_gauge)
|
| 127 | sizer.Add(self.gauge_real, 1, wx.EXPAND) |
| 128 | sizer.Add(self.gauge_imag, 1, wx.EXPAND) |
| 129 | self.SetSizerAndFit(main_box)
|
| 130 | #initial setup
|
| 131 | self.ext_controller[self.average_key] = self.ext_controller[self.average_key] |
| 132 | self.ext_controller[self.avg_alpha_key] = self.ext_controller[self.avg_alpha_key] |
| 133 | self._register_set_prop(self, PEAK_HOLD_KEY, peak_hold) |
| 134 | self._register_set_prop(self, RUNNING_KEY, True) |
| 135 | #register events
|
| 136 | self.ext_controller.subscribe(msg_key, self.handle_msg) |
| 137 | |
| 138 | def show_gauges(self, show_gauge): |
| 139 | """!
|
| 140 | Show or hide the gauges.
|
| 141 | If this is real, never show the imaginary gauge.
|
| 142 | @param show_gauge true to show
|
| 143 | """ |
| 144 | if show_gauge: self.gauge_real.Show() |
| 145 | else: self.gauge_real.Hide() |
| 146 | if show_gauge and not self.real: self.gauge_imag.Show() |
| 147 | else: self.gauge_imag.Hide() |
| 148 | |
| 149 | def handle_msg(self, msg): |
| 150 | """!
|
| 151 | Handle a message from the message queue.
|
| 152 | Convert the string based message into a float or complex.
|
| 153 | If more than one number was read, only take the last number.
|
| 154 | Perform peak hold operations, set the gauges and display.
|
| 155 | @param msg the number sample as a character array
|
| 156 | """ |
| 157 | if not self[RUNNING_KEY]: return |
| 158 | #set gauge
|
| 159 | def set_gauge_value(gauge, value): |
| 160 | gauge_val = DEFAULT_GAUGE_RANGE*(value-self.minval)/(self.maxval-self.minval) |
| 161 | gauge_val = max(0, gauge_val) #clip |
| 162 | gauge_val = min(DEFAULT_GAUGE_RANGE, gauge_val) #clip |
| 163 | gauge.SetValue(gauge_val) |
| 164 | format_string = "%%.%df"%self.decimal_places |
| 165 | if self.real: |
| 166 | sample = numpy.fromstring(msg, numpy.float32)[-1]
|
| 167 | if self[PEAK_HOLD_KEY]: sample = self.peak_val_real = max(self.peak_val_real, sample) |
| 168 | label_text = "%s %s"%(format_string%sample, self.units) |
| 169 | set_gauge_value(self.gauge_real, sample)
|
| 170 | else:
|
| 171 | sample = numpy.fromstring(msg, numpy.complex64)[-1]
|
| 172 | if self[PEAK_HOLD_KEY]: |
| 173 | self.peak_val_real = max(self.peak_val_real, sample.real) |
| 174 | self.peak_val_imag = max(self.peak_val_imag, sample.imag) |
| 175 | sample = self.peak_val_real + self.peak_val_imag*1j |
| 176 | label_text = "%s + %sj %s"%(format_string%sample.real, format_string%sample.imag, self.units) |
| 177 | set_gauge_value(self.gauge_real, sample.real)
|
| 178 | set_gauge_value(self.gauge_imag, sample.imag)
|
| 179 | #set label text
|
| 180 | self.text.SetLabel(label_text)
|
| 181 | #clear peak hold
|
| 182 | if not self[PEAK_HOLD_KEY]: |
| 183 | self.peak_val_real = NEG_INF
|
| 184 | self.peak_val_imag = NEG_INF
|