Imported Upstream version 3.2.2
[debian/gnuradio] / gr-radio-astronomy / src / python / ra_stripchartsink.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2003,2004,2005,2007 Free Software Foundation, Inc.
4
5 # This file is part of GNU Radio
6
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)
10 # any later version.
11
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.
16
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.
21
22
23 from gnuradio import gr, gru
24 from gnuradio.wxgui import stdgui2
25 import wx
26 import gnuradio.wxgui.plot as plot
27 import Numeric
28 import threading
29 import math    
30 import ephem
31 import time
32
33 default_stripchartsink_size = (640,140)
34 global_yvalues = []
35
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):
41
42         # initialize common attributes
43         self.y_divs = 8
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
49         self.title = title
50         self.xlabel = xlabel
51         self.ylabel = ylabel
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)
57         self.wcnt = 0
58         self.timecnt = 0
59         self.stripsize=stripsize
60
61     def set_y_per_div(self, y_per_div):
62         self.y_per_div = y_per_div
63
64     def set_ref_level(self, ref_level):
65         self.ref_level = ref_level
66
67     def set_autoscale(self, auto):
68         self.autoscale = auto
69
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):
77
78         if parallel == 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))
82         else:
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))
86                 
87         
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,
91                                stripsize=stripsize,
92                                xlabel=xlabel, ylabel=ylabel, 
93                                divbase=divbase, title=title,
94                                parallel=parallel, 
95                                scaling=scaling, autoscale=autoscale)
96                                
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)
100         else:
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)
104
105         self.win = stripchart_window(self, parent, size=size)
106
107
108
109 # ------------------------------------------------------------------------
110
111 myDATA_EVENT = wx.NewEventType()
112 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
113
114
115 class DataEvent(wx.PyEvent):
116     def __init__(self, data):
117         wx.PyEvent.__init__(self)
118         self.SetEventType (myDATA_EVENT)
119         self.data = data
120
121     def Clone (self): 
122         self.__class__ (self.GetId())
123
124
125 class input_watcher (threading.Thread):
126     def __init__ (self, msgq, evsize, event_receiver, **kwds):
127         threading.Thread.__init__ (self, **kwds)
128         self.setDaemon (1)
129         self.msgq = msgq
130         self.evsize = evsize
131         self.event_receiver = event_receiver
132         self.keep_running = True
133         self.start ()
134
135     def run (self):
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())
140
141             s = msg.to_string()            # get the body of the msg as a string
142
143             # There may be more than one frame in the message.
144             # If so, we take only the last one
145             if nitems > 1:
146                 start = itemsize * (nitems - 1)
147                 s = s[start:start+itemsize]
148
149             complex_data = Numeric.fromstring (s, Numeric.Float32)
150             de = DataEvent (complex_data)
151             wx.PostEvent (self.event_receiver, de)
152             del de
153
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)
159
160         self.y_range = None
161         self.stripchartsink = stripchartsink
162
163         self.SetEnableGrid (True)
164         # self.SetEnableZoom (True)
165         # self.SetBackgroundColour ('black')
166         
167         self.build_popup_menu()
168         
169         EVT_DATA_EVENT (self, self.set_data)
170
171         wx.EVT_CLOSE (self, self.on_close_window)
172         self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
173
174         self.input_watcher = input_watcher(stripchartsink.msgq, 1, self)
175
176
177     def on_close_window (self, event):
178         print "stripchart_window:on_close_window"
179         self.keep_running = False
180
181
182     def set_data (self, evt):
183         indata = evt.data
184         L = len (indata)
185
186         calc_min = min(indata)
187         calc_max = max(indata)
188         d = calc_max - calc_min
189         d = d * 0.1
190         if self.stripchartsink.autoscale == True and self.stripchartsink.parallel == True:
191             self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d)
192
193         N = self.stripchartsink.stripsize
194         if self.stripchartsink.parallel != True:
195             for i in range(1,N):
196                 pooey = N-i
197                 self.stripchartsink.vector[pooey] = self.stripchartsink.vector[pooey-1]
198     
199             self.stripchartsink.vector[0] = indata
200
201         else:
202             self.stripchartsink.vector = indata
203
204         if self.stripchartsink.parallel == True:
205             avg = 0
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]
213
214             avg /= self.stripchartsink.stripsize
215             markers = []
216             placedmarkers = 0
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]))
222                        placedmarkers += 1
223             
224         points = Numeric.zeros((N,2), Numeric.Float64)
225         for i in range(0,N):
226             if self.stripchartsink.scaling == 1.0:
227                 points[i,0] = i
228             else:
229                 points[i,0] = i * self.stripchartsink.scaling
230             points[i,1] = self.stripchartsink.vector[i]
231
232         if self.stripchartsink.parallel == True and placedmarkers > 1:
233             for i in range(0,N):
234                 self.stripchartsink.vector[i] = 0
235
236             marks = plot.PolyMarker(markers, colour='BLACK', marker='triangle_down')
237
238         lines = plot.PolyLine (points, colour='RED')
239
240         # Temporary--I'm find the markers distracting
241         placedmarkers = 0
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)
248
249         else:
250             graphics = plot.PlotGraphics ([lines,marks],
251                                       title=self.stripchartsink.title,
252                                       xLabel = xlab, yLabel = ylab)
253
254         self.Draw (graphics, xAxis=None, yAxis=self.y_range)
255
256         if self.stripchartsink.autoscale == False or self.stripchartsink.parallel == False:
257             self.update_y_range ()
258
259
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)
264
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)
269
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)
274
275     def on_autoscale(self, evt):
276         self.stripchartsink.set_autoscale(evt.IsChecked())
277
278     def on_incr_y_per_div(self, evt):
279         divbase = self.stripchartsink.divbase
280         x1 = 1 * divbase
281         x2 = 2 * divbase
282         x4 = 4 * divbase
283         x10 = 10 * divbase
284         x20 = 20 * 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)))
287
288     def on_decr_y_per_div(self, evt):
289         # print "on_decr_y_per_div"
290         divbase = self.stripchartsink.divbase
291         x1 = 1 * divbase
292         x2 = 2 * divbase
293         x4 = 4 * divbase
294         x10 = 10 * divbase
295         x20 = 20 * divbase
296         self.stripchartsink.set_y_per_div(next_down(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
297
298     def on_y_per_div(self, evt):
299         # print "on_y_per_div"
300         divbase=self.stripchartsink.divbase
301         Id = evt.GetId()
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)
312
313         
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)
318             item.Check(pred())
319         self.PopupMenu(menu, event.GetPosition())
320
321
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()
334
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)
345
346
347         # make a menu
348         menu = wx.Menu()
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
355         v = 1.0*divbase
356         s = "%.3f" % v
357         menu.AppendCheckItem(self.id_y_per_div_1, s)
358         v = 2.0*divbase
359         s = "%.3f" % v
360         menu.AppendCheckItem(self.id_y_per_div_2, s)
361         v = 4.0*divbase
362         s = "%.3f" % v
363         menu.AppendCheckItem(self.id_y_per_div_5, s)
364         v = 10*divbase
365         s = "%.3f" % v
366         menu.AppendCheckItem(self.id_y_per_div_10, s)
367         v = 20*divbase
368         s = "%.3f" % v
369         menu.AppendCheckItem(self.id_y_per_div_20, s)
370
371         self.checkmarks = {
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,
378             }
379
380
381 def next_up(v, seq):
382     """
383     Return the first item in seq that is > v.
384     """
385     for s in seq:
386         if s > v:
387             return s
388     return v
389
390 def next_down(v, seq):
391     """
392     Return the last item in seq that is < v.
393     """
394     rseq = list(seq[:])
395     rseq.reverse()
396
397     for s in rseq:
398         if s < v:
399             return s
400     return v