switch source package format to 3.0 quilt
[debian/gnuradio] / gr-wxgui / src / python / number_window.py
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 from gnuradio import gr #for gr.prefs
31 import forms
32
33 ##################################################
34 # Constants
35 ##################################################
36 NEG_INF = float('-inf')
37 SLIDER_STEPS = 100
38 AVG_ALPHA_MIN_EXP, AVG_ALPHA_MAX_EXP = -3, 0
39 DEFAULT_NUMBER_RATE = gr.prefs().get_long('wxgui', 'number_rate', 5)
40 DEFAULT_WIN_SIZE = (300, 300)
41 DEFAULT_GAUGE_RANGE = 1000
42 VALUE_REPR_KEY = 'value_repr'
43 VALUE_REAL_KEY = 'value_real'
44 VALUE_IMAG_KEY = 'value_imag'
45
46 ##################################################
47 # Number window control panel
48 ##################################################
49 class control_panel(wx.Panel):
50         """
51         A control panel with wx widgits to control the averaging.
52         """
53
54         def __init__(self, parent):
55                 """
56                 Create a new control panel.
57                 @param parent the wx parent window
58                 """
59                 self.parent = parent
60                 wx.Panel.__init__(self, parent)
61                 parent[SHOW_CONTROL_PANEL_KEY] = True
62                 parent.subscribe(SHOW_CONTROL_PANEL_KEY, self.Show)
63                 control_box = wx.BoxSizer(wx.VERTICAL)
64                 #checkboxes for average and peak hold
65                 control_box.AddStretchSpacer()
66                 options_box = forms.static_box_sizer(
67                         parent=self, sizer=control_box, label='Options',
68                         bold=True, orient=wx.VERTICAL,
69                 )
70                 forms.check_box(
71                         sizer=options_box, parent=self, label='Peak Hold',
72                         ps=parent, key=PEAK_HOLD_KEY,
73                 )
74                 forms.check_box(
75                         sizer=options_box, parent=self, label='Average',
76                         ps=parent, key=AVERAGE_KEY,
77                 )
78                 #static text and slider for averaging
79                 avg_alpha_text = forms.static_text(
80                         sizer=options_box, parent=self, label='Avg Alpha',
81                         converter=forms.float_converter(lambda x: '%.4f'%x),
82                         ps=parent, key=AVG_ALPHA_KEY, width=50,
83                 )
84                 avg_alpha_slider = forms.log_slider(
85                         sizer=options_box, parent=self,
86                         min_exp=AVG_ALPHA_MIN_EXP,
87                         max_exp=AVG_ALPHA_MAX_EXP,
88                         num_steps=SLIDER_STEPS,
89                         ps=parent, key=AVG_ALPHA_KEY,
90                 )
91                 for widget in (avg_alpha_text, avg_alpha_slider):
92                         parent.subscribe(AVERAGE_KEY, widget.Enable)
93                         widget.Enable(parent[AVERAGE_KEY])
94                 #run/stop
95                 control_box.AddStretchSpacer()
96                 forms.toggle_button(
97                         sizer=control_box, parent=self,
98                         true_label='Stop', false_label='Run',
99                         ps=parent, key=RUNNING_KEY,
100                 )
101                 #set sizer
102                 self.SetSizerAndFit(control_box)
103
104 ##################################################
105 # Numbersink window with label and gauges
106 ##################################################
107 class number_window(wx.Panel, pubsub.pubsub):
108         def __init__(
109                 self,
110                 parent,
111                 controller,
112                 size,
113                 title,
114                 units,
115                 show_gauge,
116                 real,
117                 minval,
118                 maxval,
119                 decimal_places,
120                 average_key,
121                 avg_alpha_key,
122                 peak_hold,
123                 msg_key,
124                 sample_rate_key,
125         ):
126                 pubsub.pubsub.__init__(self)
127                 wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
128                 #setup
129                 self.peak_val_real = NEG_INF
130                 self.peak_val_imag = NEG_INF
131                 self.real = real
132                 self.units = units
133                 self.decimal_places = decimal_places
134                 #proxy the keys
135                 self.proxy(MSG_KEY, controller, msg_key)
136                 self.proxy(AVERAGE_KEY, controller, average_key)
137                 self.proxy(AVG_ALPHA_KEY, controller, avg_alpha_key)
138                 self.proxy(SAMPLE_RATE_KEY, controller, sample_rate_key)
139                 #initialize values
140                 self[PEAK_HOLD_KEY] = peak_hold
141                 self[RUNNING_KEY] = True
142                 self[VALUE_REAL_KEY] = minval
143                 self[VALUE_IMAG_KEY] = minval
144                 #setup the box with display and controls
145                 self.control_panel = control_panel(self)
146                 main_box = wx.BoxSizer(wx.HORIZONTAL)
147                 sizer = forms.static_box_sizer(
148                         parent=self, sizer=main_box, label=title,
149                         bold=True, orient=wx.VERTICAL, proportion=1,
150                 )
151                 main_box.Add(self.control_panel, 0, wx.EXPAND)
152                 sizer.AddStretchSpacer()
153                 forms.static_text(
154                         parent=self, sizer=sizer,
155                         ps=self, key=VALUE_REPR_KEY, width=size[0],
156                         converter=forms.str_converter(),
157                 )
158                 sizer.AddStretchSpacer()
159                 self.gauge_real = forms.gauge(
160                         parent=self, sizer=sizer, style=wx.GA_HORIZONTAL,
161                         ps=self, key=VALUE_REAL_KEY, length=size[0],
162                         minimum=minval, maximum=maxval, num_steps=DEFAULT_GAUGE_RANGE,
163                 )
164                 self.gauge_imag = forms.gauge(
165                         parent=self, sizer=sizer, style=wx.GA_HORIZONTAL,
166                         ps=self, key=VALUE_IMAG_KEY, length=size[0],
167                         minimum=minval, maximum=maxval, num_steps=DEFAULT_GAUGE_RANGE,
168                 )
169                 #hide/show gauges
170                 self.show_gauges(show_gauge)
171                 self.SetSizerAndFit(main_box)
172                 #register events
173                 self.subscribe(MSG_KEY, self.handle_msg)
174
175         def show_gauges(self, show_gauge):
176                 """
177                 Show or hide the gauges.
178                 If this is real, never show the imaginary gauge.
179                 @param show_gauge true to show
180                 """
181                 self.gauge_real.ShowItems(show_gauge)
182                 self.gauge_imag.ShowItems(show_gauge and not self.real)
183
184         def handle_msg(self, msg):
185                 """
186                 Handle a message from the message queue.
187                 Convert the string based message into a float or complex.
188                 If more than one number was read, only take the last number.
189                 Perform peak hold operations, set the gauges and display.
190                 @param event event.data is the number sample as a character array
191                 """
192                 if not self[RUNNING_KEY]: return
193                 format_string = "%%.%df"%self.decimal_places
194                 if self.real:
195                         sample = numpy.fromstring(msg, numpy.float32)[-1]
196                         if self[PEAK_HOLD_KEY]: sample = self.peak_val_real = max(self.peak_val_real, sample)
197                         label_text = "%s %s"%(format_string%sample, self.units)
198                         self[VALUE_REAL_KEY] = sample
199                 else:
200                         sample = numpy.fromstring(msg, numpy.complex64)[-1]
201                         if self[PEAK_HOLD_KEY]:
202                                 self.peak_val_real = max(self.peak_val_real, sample.real)
203                                 self.peak_val_imag = max(self.peak_val_imag, sample.imag)
204                                 sample = self.peak_val_real + self.peak_val_imag*1j
205                         label_text = "%s + %sj %s"%(format_string%sample.real, format_string%sample.imag, self.units)
206                         self[VALUE_REAL_KEY] = sample.real
207                         self[VALUE_IMAG_KEY] = sample.imag
208                 #set label text
209                 self[VALUE_REPR_KEY] = label_text
210                 #clear peak hold
211                 if not self[PEAK_HOLD_KEY]:
212                         self.peak_val_real = NEG_INF
213                         self.peak_val_imag = NEG_INF