Imported Upstream version 3.0.4
[debian/gnuradio] / gr-radio-astronomy / src / python / ra_stripchartsink.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2003,2004,2005 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 stdgui
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 def default_cfunc(datum):
37     return(datum)
38
39 class stripchart_sink_base(object):
40     def __init__(self, input_is_real=False, y_per_div=10, ref_level=50,
41                  sample_rate=1, stripsize=4,
42                  title='',xlabel="X", ylabel="Y", divbase=0.025,
43                  cfunc=default_cfunc, parallel=False, scaling=1.0, autoscale=False):
44
45         # initialize common attributes
46         self.y_divs = 8
47         self.y_per_div=y_per_div
48         self.ref_level = ref_level
49         self.autoscale = autoscale
50         self.sample_rate = sample_rate
51         self.parallel = parallel
52         self.title = title
53         self.xlabel = xlabel
54         self.ylabel = ylabel
55         self.divbase = divbase
56         self.scaling = scaling
57         self.cfunc = cfunc
58         self.input_is_real = input_is_real
59         self.msgq = gr.msg_queue(2)         # queue that holds a maximum of 2 messages
60         self.vector=Numeric.zeros(stripsize,Numeric.Float64)
61         self.wcnt = 0
62         self.timecnt = 0
63         self.stripsize=stripsize
64
65     def set_y_per_div(self, y_per_div):
66         self.y_per_div = y_per_div
67
68     def set_ref_level(self, ref_level):
69         self.ref_level = ref_level
70
71     def set_autoscale(self, auto):
72         self.autoscale = auto
73
74 class stripchart_sink_f(gr.hier_block, stripchart_sink_base):
75     def __init__(self, fg, parent,
76                  y_per_div=10, ref_level=50, sample_rate=1,
77                  title='', stripsize=4,
78                  size=default_stripchartsink_size,xlabel="X", 
79                  ylabel="Y", divbase=0.025, cfunc=default_cfunc,
80                  parallel=False, scaling=1.0, autoscale=False):
81
82         stripchart_sink_base.__init__(self, input_is_real=True,
83                                y_per_div=y_per_div, ref_level=ref_level,
84                                sample_rate=sample_rate,
85                                stripsize=stripsize,
86                                xlabel=xlabel, ylabel=ylabel, 
87                                divbase=divbase, title=title,
88                                cfunc=cfunc, parallel=parallel, 
89                                scaling=scaling, autoscale=autoscale)
90                                
91         if (parallel == True):
92             one = gr.keep_one_in_n (gr.sizeof_float*stripsize, 1)
93             sink = gr.message_sink(gr.sizeof_float*stripsize, self.msgq, True)
94         else:
95             one = gr.keep_one_in_n (gr.sizeof_float, 1)
96             sink = gr.message_sink(gr.sizeof_float, self.msgq, True)
97         fg.connect (one, sink)
98
99         gr.hier_block.__init__(self, fg, one, sink)
100
101         self.win = stripchart_window(self, parent, size=size)
102
103
104
105 # ------------------------------------------------------------------------
106
107 myDATA_EVENT = wx.NewEventType()
108 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
109
110
111 class DataEvent(wx.PyEvent):
112     def __init__(self, data):
113         wx.PyEvent.__init__(self)
114         self.SetEventType (myDATA_EVENT)
115         self.data = data
116
117     def Clone (self): 
118         self.__class__ (self.GetId())
119
120
121 class input_watcher (threading.Thread):
122     def __init__ (self, msgq, evsize, event_receiver, **kwds):
123         threading.Thread.__init__ (self, **kwds)
124         self.setDaemon (1)
125         self.msgq = msgq
126         self.evsize = evsize
127         self.event_receiver = event_receiver
128         self.keep_running = True
129         self.start ()
130
131     def run (self):
132         while (self.keep_running):
133             msg = self.msgq.delete_head()  # blocking read of message queue
134             itemsize = int(msg.arg1())
135             nitems = int(msg.arg2())
136
137             s = msg.to_string()            # get the body of the msg as a string
138
139             # There may be more than one frame in the message.
140             # If so, we take only the last one
141             if nitems > 1:
142                 start = itemsize * (nitems - 1)
143                 s = s[start:start+itemsize]
144
145             complex_data = Numeric.fromstring (s, Numeric.Float32)
146             de = DataEvent (complex_data)
147             wx.PostEvent (self.event_receiver, de)
148             del de
149
150 class stripchart_window(plot.PlotCanvas):
151     def __init__ (self, stripchartsink, parent, id = -1,
152                   pos = wx.DefaultPosition, size = wx.DefaultSize,
153                   style = wx.DEFAULT_FRAME_STYLE, name = ""):
154         plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name)
155
156         self.y_range = None
157         self.stripchartsink = stripchartsink
158
159         self.SetEnableGrid (True)
160         # self.SetEnableZoom (True)
161         # self.SetBackgroundColour ('black')
162         
163         self.build_popup_menu()
164         
165         EVT_DATA_EVENT (self, self.set_data)
166
167         wx.EVT_CLOSE (self, self.on_close_window)
168         self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
169
170         self.input_watcher = input_watcher(stripchartsink.msgq, 1, self)
171
172
173     def on_close_window (self, event):
174         print "stripchart_window:on_close_window"
175         self.keep_running = False
176
177
178     def set_data (self, evt):
179         indata = evt.data
180         L = len (indata)
181
182         calc_min = min(indata)
183         calc_max = max(indata)
184         d = calc_max - calc_min
185         d *= 0.1
186         if self.stripchartsink.autoscale == True and self.stripchartsink.parallel == True:
187             self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d)
188
189         if (self.stripchartsink.parallel != True):
190             indata = self.stripchartsink.cfunc(indata)
191
192         N = self.stripchartsink.stripsize
193         if self.stripchartsink.parallel != True:
194             for i in range(1,N):
195                 pooey = N-i
196                 self.stripchartsink.vector[pooey] = self.stripchartsink.vector[pooey-1]
197     
198             self.stripchartsink.vector[0] = indata
199
200         else:
201             self.stripchartsink.vector = indata
202
203         if self.stripchartsink.parallel == True:
204             avg = 0
205             for i in range(0,self.stripchartsink.stripsize):
206                 if self.stripchartsink.vector[i] > 0:
207                     avg += self.stripchartsink.vector[i]
208                 if self.stripchartsink.vector[i] < calc_min:
209                     calc_min = self.stripchartsink.vector[i]
210                 if self.stripchartsink.vector[i] > calc_max:
211                     calc_max = self.stripchartsink.vector[i]
212
213             avg /= self.stripchartsink.stripsize
214             markers = []
215             placedmarkers = 0
216             for i in range(0,self.stripchartsink.stripsize):
217                 if (self.stripchartsink.vector[i] > 0 and
218                     self.stripchartsink.vector[i] > (avg*5)):
219                        markers.append((i*self.stripchartsink.scaling,
220                        self.stripchartsink.vector[i]))
221                        placedmarkers += 1
222             
223         points = Numeric.zeros((N,2), Numeric.Float64)
224         for i in range(0,N):
225             if self.stripchartsink.scaling == 1.0:
226                 points[i,0] = i
227             else:
228                 points[i,0] = i * self.stripchartsink.scaling
229             points[i,1] = self.stripchartsink.vector[i]
230
231         if self.stripchartsink.parallel == True and placedmarkers > 1:
232             for i in range(0,N):
233                 self.stripchartsink.vector[i] = 0
234
235             marks = plot.PolyMarker(markers, colour='BLACK', marker='triangle_down')
236
237         lines = plot.PolyLine (points, colour='RED')
238
239         # Temporary--I'm find the markers distracting
240         placedmarkers = 0
241         xlab = self.stripchartsink.xlabel
242         ylab = self.stripchartsink.ylabel
243         if (self.stripchartsink.parallel == False) or (placedmarkers <= 1):
244             graphics = plot.PlotGraphics ([lines],
245                                       title=self.stripchartsink.title,
246                                       xLabel = xlab, yLabel = ylab)
247
248         else:
249             graphics = plot.PlotGraphics ([lines,marks],
250                                       title=self.stripchartsink.title,
251                                       xLabel = xlab, yLabel = ylab)
252
253         self.Draw (graphics, xAxis=None, yAxis=self.y_range)
254
255         if self.stripchartsink.autoscale == False or self.stripchartsink.parallel == False:
256             self.update_y_range ()
257
258
259     def update_y_range (self):
260         ymax = self.stripchartsink.ref_level
261         ymin = self.stripchartsink.ref_level - self.stripchartsink.y_per_div * self.stripchartsink.y_divs
262         self.y_range = self._axisInterval ('min', ymin, ymax)
263
264     def on_incr_ref_level(self, evt):
265         # print "on_incr_ref_level"
266         self.stripchartsink.set_ref_level(self.stripchartsink.ref_level
267                                    + self.stripchartsink.y_per_div)
268
269     def on_decr_ref_level(self, evt):
270         # print "on_decr_ref_level"
271         self.stripchartsink.set_ref_level(self.stripchartsink.ref_level
272                                    - self.stripchartsink.y_per_div)
273
274     def on_autoscale(self, evt):
275         self.stripchartsink.set_autoscale(evt.IsChecked())
276
277     def on_incr_y_per_div(self, evt):
278         divbase = self.stripchartsink.divbase
279         x1 = 1 * divbase
280         x2 = 2 * divbase
281         x4 = 4 * divbase
282         x10 = 10 * divbase
283         x20 = 20 * divbase
284         # print "on_incr_y_per_div"
285         self.stripchartsink.set_y_per_div(next_up(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
286
287     def on_decr_y_per_div(self, evt):
288         # print "on_decr_y_per_div"
289         divbase = self.stripchartsink.divbase
290         x1 = 1 * divbase
291         x2 = 2 * divbase
292         x4 = 4 * divbase
293         x10 = 10 * divbase
294         x20 = 20 * divbase
295         self.stripchartsink.set_y_per_div(next_down(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
296
297     def on_y_per_div(self, evt):
298         # print "on_y_per_div"
299         divbase=self.stripchartsink.divbase
300         Id = evt.GetId()
301         if Id == self.id_y_per_div_1:
302             self.stripchartsink.set_y_per_div(1*divbase)
303         elif Id == self.id_y_per_div_2:
304             self.stripchartsink.set_y_per_div(2*divbase)
305         elif Id == self.id_y_per_div_5:
306             self.stripchartsink.set_y_per_div(4*divbase)
307         elif Id == self.id_y_per_div_10:
308             self.stripchartsink.set_y_per_div(10*divbase)
309         elif Id == self.id_y_per_div_20:
310             self.stripchartsink.set_y_per_div(20*divbase)
311
312         
313     def on_right_click(self, event):
314         menu = self.popup_menu
315         for id, pred in self.checkmarks.items():
316             item = menu.FindItemById(id)
317             item.Check(pred())
318         self.PopupMenu(menu, event.GetPosition())
319
320
321     def build_popup_menu(self):
322         divbase=self.stripchartsink.divbase
323         self.id_incr_ref_level = wx.NewId()
324         self.id_decr_ref_level = wx.NewId()
325         self.id_autoscale = wx.NewId()
326         self.id_incr_y_per_div = wx.NewId()
327         self.id_decr_y_per_div = wx.NewId()
328         self.id_y_per_div_1 = wx.NewId()
329         self.id_y_per_div_2 = wx.NewId()
330         self.id_y_per_div_5 = wx.NewId()
331         self.id_y_per_div_10 = wx.NewId()
332         self.id_y_per_div_20 = wx.NewId()
333
334         self.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level)
335         self.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level)
336         self.Bind(wx.EVT_MENU, self.on_autoscale, id=self.id_autoscale)
337         self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, id=self.id_incr_y_per_div)
338         self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, id=self.id_decr_y_per_div)
339         self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1)
340         self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2)
341         self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5)
342         self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10)
343         self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20)
344
345
346         # make a menu
347         menu = wx.Menu()
348         self.popup_menu = menu
349         menu.Append(self.id_incr_ref_level, "Incr Ref Level")
350         menu.Append(self.id_decr_ref_level, "Decr Ref Level")
351         menu.AppendSeparator()
352         menu.AppendCheckItem(self.id_autoscale, "Auto Scale")
353         # we'd use RadioItems for these, but they're not supported on Mac
354         v = 1.0*divbase
355         s = "%.3f" % v
356         menu.AppendCheckItem(self.id_y_per_div_1, s)
357         v = 2.0*divbase
358         s = "%.3f" % v
359         menu.AppendCheckItem(self.id_y_per_div_2, s)
360         v = 4.0*divbase
361         s = "%.3f" % v
362         menu.AppendCheckItem(self.id_y_per_div_5, s)
363         v = 10*divbase
364         s = "%.3f" % v
365         menu.AppendCheckItem(self.id_y_per_div_10, s)
366         v = 20*divbase
367         s = "%.3f" % v
368         menu.AppendCheckItem(self.id_y_per_div_20, s)
369
370         self.checkmarks = {
371             self.id_autoscale : lambda : self.stripchartsink.autoscale,
372             self.id_y_per_div_1 : lambda : self.stripchartsink.y_per_div == 1*divbase,
373             self.id_y_per_div_2 : lambda : self.stripchartsink.y_per_div == 2*divbase,
374             self.id_y_per_div_5 : lambda : self.stripchartsink.y_per_div == 4*divbase,
375             self.id_y_per_div_10 : lambda : self.stripchartsink.y_per_div == 10*divbase,
376             self.id_y_per_div_20 : lambda : self.stripchartsink.y_per_div == 20*divbase,
377             }
378
379
380 def next_up(v, seq):
381     """
382     Return the first item in seq that is > v.
383     """
384     for s in seq:
385         if s > v:
386             return s
387     return v
388
389 def next_down(v, seq):
390     """
391     Return the last item in seq that is < v.
392     """
393     rseq = list(seq[:])
394     rseq.reverse()
395
396     for s in rseq:
397         if s < v:
398             return s
399     return v