3 # Copyright 2003,2004,2005 Free Software Foundation, Inc.
5 # This file is part of GNU Radio
7 # GNU Radio is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3, or (at your option)
12 # GNU Radio is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with GNU Radio; see the file COPYING. If not, write to
19 # the Free Software Foundation, Inc., 51 Franklin Street,
20 # Boston, MA 02110-1301, USA.
23 from gnuradio import gr, gru
24 from gnuradio.wxgui import stdgui
26 import gnuradio.wxgui.plot as plot
33 default_stripchartsink_size = (640,140)
36 class stripchart_sink_base(object):
37 def __init__(self, input_is_real=False, y_per_div=10, ref_level=50,
38 sample_rate=1, stripsize=4,
39 title='',xlabel="X", ylabel="Y", divbase=0.025,
40 parallel=False, scaling=1.0, autoscale=False):
42 # initialize common attributes
44 self.y_per_div=y_per_div
45 self.ref_level = ref_level
46 self.autoscale = autoscale
47 self.sample_rate = sample_rate
48 self.parallel = parallel
52 self.divbase = divbase
53 self.scaling = scaling
54 self.input_is_real = input_is_real
55 self.msgq = gr.msg_queue(2) # queue that holds a maximum of 2 messages
56 self.vector=Numeric.zeros(stripsize,Numeric.Float64)
59 self.stripsize=stripsize
61 def set_y_per_div(self, y_per_div):
62 self.y_per_div = y_per_div
64 def set_ref_level(self, ref_level):
65 self.ref_level = ref_level
67 def set_autoscale(self, auto):
70 class stripchart_sink_f(gr.hier_block, stripchart_sink_base):
71 def __init__(self, fg, parent,
72 y_per_div=10, ref_level=50, sample_rate=1,
73 title='', stripsize=4,
74 size=default_stripchartsink_size,xlabel="X",
75 ylabel="Y", divbase=0.025,
76 parallel=False, scaling=1.0, autoscale=False):
78 stripchart_sink_base.__init__(self, input_is_real=True,
79 y_per_div=y_per_div, ref_level=ref_level,
80 sample_rate=sample_rate,
82 xlabel=xlabel, ylabel=ylabel,
83 divbase=divbase, title=title,
85 scaling=scaling, autoscale=autoscale)
87 if (parallel == True):
88 one = gr.keep_one_in_n (gr.sizeof_float*stripsize, 1)
89 sink = gr.message_sink(gr.sizeof_float*stripsize, self.msgq, True)
91 one = gr.keep_one_in_n (gr.sizeof_float, 1)
92 sink = gr.message_sink(gr.sizeof_float, self.msgq, True)
93 fg.connect (one, sink)
95 gr.hier_block.__init__(self, fg, one, sink)
97 self.win = stripchart_window(self, parent, size=size)
101 # ------------------------------------------------------------------------
103 myDATA_EVENT = wx.NewEventType()
104 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
107 class DataEvent(wx.PyEvent):
108 def __init__(self, data):
109 wx.PyEvent.__init__(self)
110 self.SetEventType (myDATA_EVENT)
114 self.__class__ (self.GetId())
117 class input_watcher (threading.Thread):
118 def __init__ (self, msgq, evsize, event_receiver, **kwds):
119 threading.Thread.__init__ (self, **kwds)
123 self.event_receiver = event_receiver
124 self.keep_running = True
128 while (self.keep_running):
129 msg = self.msgq.delete_head() # blocking read of message queue
130 itemsize = int(msg.arg1())
131 nitems = int(msg.arg2())
133 s = msg.to_string() # get the body of the msg as a string
135 # There may be more than one frame in the message.
136 # If so, we take only the last one
138 start = itemsize * (nitems - 1)
139 s = s[start:start+itemsize]
141 complex_data = Numeric.fromstring (s, Numeric.Float32)
142 de = DataEvent (complex_data)
143 wx.PostEvent (self.event_receiver, de)
146 class stripchart_window(plot.PlotCanvas):
147 def __init__ (self, stripchartsink, parent, id = -1,
148 pos = wx.DefaultPosition, size = wx.DefaultSize,
149 style = wx.DEFAULT_FRAME_STYLE, name = ""):
150 plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name)
153 self.stripchartsink = stripchartsink
155 self.SetEnableGrid (True)
156 # self.SetEnableZoom (True)
157 # self.SetBackgroundColour ('black')
159 self.build_popup_menu()
161 EVT_DATA_EVENT (self, self.set_data)
163 wx.EVT_CLOSE (self, self.on_close_window)
164 self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
166 self.input_watcher = input_watcher(stripchartsink.msgq, 1, self)
169 def on_close_window (self, event):
170 print "stripchart_window:on_close_window"
171 self.keep_running = False
174 def set_data (self, evt):
178 calc_min = min(indata)
179 calc_max = max(indata)
180 d = calc_max - calc_min
182 if self.stripchartsink.autoscale == True and self.stripchartsink.parallel == True:
183 self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d)
185 N = self.stripchartsink.stripsize
186 if self.stripchartsink.parallel != True:
189 self.stripchartsink.vector[pooey] = self.stripchartsink.vector[pooey-1]
191 self.stripchartsink.vector[0] = indata
194 self.stripchartsink.vector = indata
196 if self.stripchartsink.parallel == True:
198 for i in range(0,self.stripchartsink.stripsize):
199 if self.stripchartsink.vector[i] > 0:
200 avg += self.stripchartsink.vector[i]
201 if self.stripchartsink.vector[i] < calc_min:
202 calc_min = self.stripchartsink.vector[i]
203 if self.stripchartsink.vector[i] > calc_max:
204 calc_max = self.stripchartsink.vector[i]
206 avg /= self.stripchartsink.stripsize
209 for i in range(0,self.stripchartsink.stripsize):
210 if (self.stripchartsink.vector[i] > 0 and
211 self.stripchartsink.vector[i] > (avg*5)):
212 markers.append((i*self.stripchartsink.scaling,
213 self.stripchartsink.vector[i]))
216 points = Numeric.zeros((N,2), Numeric.Float64)
218 if self.stripchartsink.scaling == 1.0:
221 points[i,0] = i * self.stripchartsink.scaling
222 points[i,1] = self.stripchartsink.vector[i]
224 if self.stripchartsink.parallel == True and placedmarkers > 1:
226 self.stripchartsink.vector[i] = 0
228 marks = plot.PolyMarker(markers, colour='BLACK', marker='triangle_down')
230 lines = plot.PolyLine (points, colour='RED')
232 # Temporary--I'm find the markers distracting
234 xlab = self.stripchartsink.xlabel
235 ylab = self.stripchartsink.ylabel
236 if (self.stripchartsink.parallel == False) or (placedmarkers <= 1):
237 graphics = plot.PlotGraphics ([lines],
238 title=self.stripchartsink.title,
239 xLabel = xlab, yLabel = ylab)
242 graphics = plot.PlotGraphics ([lines,marks],
243 title=self.stripchartsink.title,
244 xLabel = xlab, yLabel = ylab)
246 self.Draw (graphics, xAxis=None, yAxis=self.y_range)
248 if self.stripchartsink.autoscale == False or self.stripchartsink.parallel == False:
249 self.update_y_range ()
252 def update_y_range (self):
253 ymax = self.stripchartsink.ref_level
254 ymin = self.stripchartsink.ref_level - self.stripchartsink.y_per_div * self.stripchartsink.y_divs
255 self.y_range = self._axisInterval ('min', ymin, ymax)
257 def on_incr_ref_level(self, evt):
258 # print "on_incr_ref_level"
259 self.stripchartsink.set_ref_level(self.stripchartsink.ref_level
260 + self.stripchartsink.y_per_div)
262 def on_decr_ref_level(self, evt):
263 # print "on_decr_ref_level"
264 self.stripchartsink.set_ref_level(self.stripchartsink.ref_level
265 - self.stripchartsink.y_per_div)
267 def on_autoscale(self, evt):
268 self.stripchartsink.set_autoscale(evt.IsChecked())
270 def on_incr_y_per_div(self, evt):
271 divbase = self.stripchartsink.divbase
277 # print "on_incr_y_per_div"
278 self.stripchartsink.set_y_per_div(next_up(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
280 def on_decr_y_per_div(self, evt):
281 # print "on_decr_y_per_div"
282 divbase = self.stripchartsink.divbase
288 self.stripchartsink.set_y_per_div(next_down(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
290 def on_y_per_div(self, evt):
291 # print "on_y_per_div"
292 divbase=self.stripchartsink.divbase
294 if Id == self.id_y_per_div_1:
295 self.stripchartsink.set_y_per_div(1*divbase)
296 elif Id == self.id_y_per_div_2:
297 self.stripchartsink.set_y_per_div(2*divbase)
298 elif Id == self.id_y_per_div_5:
299 self.stripchartsink.set_y_per_div(4*divbase)
300 elif Id == self.id_y_per_div_10:
301 self.stripchartsink.set_y_per_div(10*divbase)
302 elif Id == self.id_y_per_div_20:
303 self.stripchartsink.set_y_per_div(20*divbase)
306 def on_right_click(self, event):
307 menu = self.popup_menu
308 for id, pred in self.checkmarks.items():
309 item = menu.FindItemById(id)
311 self.PopupMenu(menu, event.GetPosition())
314 def build_popup_menu(self):
315 divbase=self.stripchartsink.divbase
316 self.id_incr_ref_level = wx.NewId()
317 self.id_decr_ref_level = wx.NewId()
318 self.id_autoscale = wx.NewId()
319 self.id_incr_y_per_div = wx.NewId()
320 self.id_decr_y_per_div = wx.NewId()
321 self.id_y_per_div_1 = wx.NewId()
322 self.id_y_per_div_2 = wx.NewId()
323 self.id_y_per_div_5 = wx.NewId()
324 self.id_y_per_div_10 = wx.NewId()
325 self.id_y_per_div_20 = wx.NewId()
327 self.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level)
328 self.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level)
329 self.Bind(wx.EVT_MENU, self.on_autoscale, id=self.id_autoscale)
330 self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, id=self.id_incr_y_per_div)
331 self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, id=self.id_decr_y_per_div)
332 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1)
333 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2)
334 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5)
335 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10)
336 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20)
341 self.popup_menu = menu
342 menu.Append(self.id_incr_ref_level, "Incr Ref Level")
343 menu.Append(self.id_decr_ref_level, "Decr Ref Level")
344 menu.AppendSeparator()
345 menu.AppendCheckItem(self.id_autoscale, "Auto Scale")
346 # we'd use RadioItems for these, but they're not supported on Mac
349 menu.AppendCheckItem(self.id_y_per_div_1, s)
352 menu.AppendCheckItem(self.id_y_per_div_2, s)
355 menu.AppendCheckItem(self.id_y_per_div_5, s)
358 menu.AppendCheckItem(self.id_y_per_div_10, s)
361 menu.AppendCheckItem(self.id_y_per_div_20, s)
364 self.id_autoscale : lambda : self.stripchartsink.autoscale,
365 self.id_y_per_div_1 : lambda : self.stripchartsink.y_per_div == 1*divbase,
366 self.id_y_per_div_2 : lambda : self.stripchartsink.y_per_div == 2*divbase,
367 self.id_y_per_div_5 : lambda : self.stripchartsink.y_per_div == 4*divbase,
368 self.id_y_per_div_10 : lambda : self.stripchartsink.y_per_div == 10*divbase,
369 self.id_y_per_div_20 : lambda : self.stripchartsink.y_per_div == 20*divbase,
375 Return the first item in seq that is > v.
382 def next_down(v, seq):
384 Return the last item in seq that is < v.