3 # Copyright 2003,2004,2005,2007 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 stdgui2
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_block2, stripchart_sink_base):
71 def __init__(self, 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):
79 gr.hier_block2.__init__(self, "stripchart_sink_f",
80 gr.io_signature(1, 1, gr.sizeof_float),
81 gr.io_signature(0, 0, 0))
83 gr.hier_block2.__init__(self, "stripchart_sink_f",
84 gr.io_signature(1, 1, gr.sizeof_float*stripsize),
85 gr.io_signature(0, 0, 0))
88 stripchart_sink_base.__init__(self, input_is_real=True,
89 y_per_div=y_per_div, ref_level=ref_level,
90 sample_rate=sample_rate,
92 xlabel=xlabel, ylabel=ylabel,
93 divbase=divbase, title=title,
95 scaling=scaling, autoscale=autoscale)
97 if (parallel == True):
98 one = gr.keep_one_in_n (gr.sizeof_float*stripsize, 1)
99 sink = gr.message_sink(gr.sizeof_float*stripsize, self.msgq, True)
101 one = gr.keep_one_in_n (gr.sizeof_float, 1)
102 sink = gr.message_sink(gr.sizeof_float, self.msgq, True)
103 self.connect (self, one, sink)
105 self.win = stripchart_window(self, parent, size=size)
109 # ------------------------------------------------------------------------
111 myDATA_EVENT = wx.NewEventType()
112 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
115 class DataEvent(wx.PyEvent):
116 def __init__(self, data):
117 wx.PyEvent.__init__(self)
118 self.SetEventType (myDATA_EVENT)
122 self.__class__ (self.GetId())
125 class input_watcher (threading.Thread):
126 def __init__ (self, msgq, evsize, event_receiver, **kwds):
127 threading.Thread.__init__ (self, **kwds)
131 self.event_receiver = event_receiver
132 self.keep_running = True
136 while (self.keep_running):
137 msg = self.msgq.delete_head() # blocking read of message queue
138 itemsize = int(msg.arg1())
139 nitems = int(msg.arg2())
141 s = msg.to_string() # get the body of the msg as a string
143 # There may be more than one frame in the message.
144 # If so, we take only the last one
146 start = itemsize * (nitems - 1)
147 s = s[start:start+itemsize]
149 complex_data = Numeric.fromstring (s, Numeric.Float32)
150 de = DataEvent (complex_data)
151 wx.PostEvent (self.event_receiver, de)
154 class stripchart_window(plot.PlotCanvas):
155 def __init__ (self, stripchartsink, parent, id = -1,
156 pos = wx.DefaultPosition, size = wx.DefaultSize,
157 style = wx.DEFAULT_FRAME_STYLE, name = ""):
158 plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name)
161 self.stripchartsink = stripchartsink
163 self.SetEnableGrid (True)
164 # self.SetEnableZoom (True)
165 # self.SetBackgroundColour ('black')
167 self.build_popup_menu()
169 EVT_DATA_EVENT (self, self.set_data)
171 wx.EVT_CLOSE (self, self.on_close_window)
172 self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
174 self.input_watcher = input_watcher(stripchartsink.msgq, 1, self)
177 def on_close_window (self, event):
178 print "stripchart_window:on_close_window"
179 self.keep_running = False
182 def set_data (self, evt):
186 calc_min = min(indata)
187 calc_max = max(indata)
188 d = calc_max - calc_min
190 if self.stripchartsink.autoscale == True and self.stripchartsink.parallel == True:
191 self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d)
193 N = self.stripchartsink.stripsize
194 if self.stripchartsink.parallel != True:
197 self.stripchartsink.vector[pooey] = self.stripchartsink.vector[pooey-1]
199 self.stripchartsink.vector[0] = indata
202 self.stripchartsink.vector = indata
204 if self.stripchartsink.parallel == True:
206 for i in range(0,self.stripchartsink.stripsize):
207 if self.stripchartsink.vector[i] > 0:
208 avg += self.stripchartsink.vector[i]
209 if self.stripchartsink.vector[i] < calc_min:
210 calc_min = self.stripchartsink.vector[i]
211 if self.stripchartsink.vector[i] > calc_max:
212 calc_max = self.stripchartsink.vector[i]
214 avg /= self.stripchartsink.stripsize
217 for i in range(0,self.stripchartsink.stripsize):
218 if (self.stripchartsink.vector[i] > 0 and
219 self.stripchartsink.vector[i] > (avg*5)):
220 markers.append((i*self.stripchartsink.scaling,
221 self.stripchartsink.vector[i]))
224 points = Numeric.zeros((N,2), Numeric.Float64)
226 if self.stripchartsink.scaling == 1.0:
229 points[i,0] = i * self.stripchartsink.scaling
230 points[i,1] = self.stripchartsink.vector[i]
232 if self.stripchartsink.parallel == True and placedmarkers > 1:
234 self.stripchartsink.vector[i] = 0
236 marks = plot.PolyMarker(markers, colour='BLACK', marker='triangle_down')
238 lines = plot.PolyLine (points, colour='RED')
240 # Temporary--I'm find the markers distracting
242 xlab = self.stripchartsink.xlabel
243 ylab = self.stripchartsink.ylabel
244 if (self.stripchartsink.parallel == False) or (placedmarkers <= 1):
245 graphics = plot.PlotGraphics ([lines],
246 title=self.stripchartsink.title,
247 xLabel = xlab, yLabel = ylab)
250 graphics = plot.PlotGraphics ([lines,marks],
251 title=self.stripchartsink.title,
252 xLabel = xlab, yLabel = ylab)
254 self.Draw (graphics, xAxis=None, yAxis=self.y_range)
256 if self.stripchartsink.autoscale == False or self.stripchartsink.parallel == False:
257 self.update_y_range ()
260 def update_y_range (self):
261 ymax = self.stripchartsink.ref_level
262 ymin = self.stripchartsink.ref_level - self.stripchartsink.y_per_div * self.stripchartsink.y_divs
263 self.y_range = self._axisInterval ('min', ymin, ymax)
265 def on_incr_ref_level(self, evt):
266 # print "on_incr_ref_level"
267 self.stripchartsink.set_ref_level(self.stripchartsink.ref_level
268 + self.stripchartsink.y_per_div)
270 def on_decr_ref_level(self, evt):
271 # print "on_decr_ref_level"
272 self.stripchartsink.set_ref_level(self.stripchartsink.ref_level
273 - self.stripchartsink.y_per_div)
275 def on_autoscale(self, evt):
276 self.stripchartsink.set_autoscale(evt.IsChecked())
278 def on_incr_y_per_div(self, evt):
279 divbase = self.stripchartsink.divbase
285 # print "on_incr_y_per_div"
286 self.stripchartsink.set_y_per_div(next_up(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
288 def on_decr_y_per_div(self, evt):
289 # print "on_decr_y_per_div"
290 divbase = self.stripchartsink.divbase
296 self.stripchartsink.set_y_per_div(next_down(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
298 def on_y_per_div(self, evt):
299 # print "on_y_per_div"
300 divbase=self.stripchartsink.divbase
302 if Id == self.id_y_per_div_1:
303 self.stripchartsink.set_y_per_div(1*divbase)
304 elif Id == self.id_y_per_div_2:
305 self.stripchartsink.set_y_per_div(2*divbase)
306 elif Id == self.id_y_per_div_5:
307 self.stripchartsink.set_y_per_div(4*divbase)
308 elif Id == self.id_y_per_div_10:
309 self.stripchartsink.set_y_per_div(10*divbase)
310 elif Id == self.id_y_per_div_20:
311 self.stripchartsink.set_y_per_div(20*divbase)
314 def on_right_click(self, event):
315 menu = self.popup_menu
316 for id, pred in self.checkmarks.items():
317 item = menu.FindItemById(id)
319 self.PopupMenu(menu, event.GetPosition())
322 def build_popup_menu(self):
323 divbase=self.stripchartsink.divbase
324 self.id_incr_ref_level = wx.NewId()
325 self.id_decr_ref_level = wx.NewId()
326 self.id_autoscale = wx.NewId()
327 self.id_incr_y_per_div = wx.NewId()
328 self.id_decr_y_per_div = wx.NewId()
329 self.id_y_per_div_1 = wx.NewId()
330 self.id_y_per_div_2 = wx.NewId()
331 self.id_y_per_div_5 = wx.NewId()
332 self.id_y_per_div_10 = wx.NewId()
333 self.id_y_per_div_20 = wx.NewId()
335 self.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level)
336 self.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level)
337 self.Bind(wx.EVT_MENU, self.on_autoscale, id=self.id_autoscale)
338 self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, id=self.id_incr_y_per_div)
339 self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, id=self.id_decr_y_per_div)
340 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1)
341 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2)
342 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5)
343 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10)
344 self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20)
349 self.popup_menu = menu
350 menu.Append(self.id_incr_ref_level, "Incr Ref Level")
351 menu.Append(self.id_decr_ref_level, "Decr Ref Level")
352 menu.AppendSeparator()
353 menu.AppendCheckItem(self.id_autoscale, "Auto Scale")
354 # we'd use RadioItems for these, but they're not supported on Mac
357 menu.AppendCheckItem(self.id_y_per_div_1, s)
360 menu.AppendCheckItem(self.id_y_per_div_2, s)
363 menu.AppendCheckItem(self.id_y_per_div_5, s)
366 menu.AppendCheckItem(self.id_y_per_div_10, s)
369 menu.AppendCheckItem(self.id_y_per_div_20, s)
372 self.id_autoscale : lambda : self.stripchartsink.autoscale,
373 self.id_y_per_div_1 : lambda : self.stripchartsink.y_per_div == 1*divbase,
374 self.id_y_per_div_2 : lambda : self.stripchartsink.y_per_div == 2*divbase,
375 self.id_y_per_div_5 : lambda : self.stripchartsink.y_per_div == 4*divbase,
376 self.id_y_per_div_10 : lambda : self.stripchartsink.y_per_div == 10*divbase,
377 self.id_y_per_div_20 : lambda : self.stripchartsink.y_per_div == 20*divbase,
383 Return the first item in seq that is > v.
390 def next_down(v, seq):
392 Return the last item in seq that is < v.