Updated license from GPL version 2 or later to GPL version 3 or later.
[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 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_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):
77
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,
81                                stripsize=stripsize,
82                                xlabel=xlabel, ylabel=ylabel, 
83                                divbase=divbase, title=title,
84                                parallel=parallel, 
85                                scaling=scaling, autoscale=autoscale)
86                                
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)
90         else:
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)
94
95         gr.hier_block.__init__(self, fg, one, sink)
96
97         self.win = stripchart_window(self, parent, size=size)
98
99
100
101 # ------------------------------------------------------------------------
102
103 myDATA_EVENT = wx.NewEventType()
104 EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
105
106
107 class DataEvent(wx.PyEvent):
108     def __init__(self, data):
109         wx.PyEvent.__init__(self)
110         self.SetEventType (myDATA_EVENT)
111         self.data = data
112
113     def Clone (self): 
114         self.__class__ (self.GetId())
115
116
117 class input_watcher (threading.Thread):
118     def __init__ (self, msgq, evsize, event_receiver, **kwds):
119         threading.Thread.__init__ (self, **kwds)
120         self.setDaemon (1)
121         self.msgq = msgq
122         self.evsize = evsize
123         self.event_receiver = event_receiver
124         self.keep_running = True
125         self.start ()
126
127     def run (self):
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())
132
133             s = msg.to_string()            # get the body of the msg as a string
134
135             # There may be more than one frame in the message.
136             # If so, we take only the last one
137             if nitems > 1:
138                 start = itemsize * (nitems - 1)
139                 s = s[start:start+itemsize]
140
141             complex_data = Numeric.fromstring (s, Numeric.Float32)
142             de = DataEvent (complex_data)
143             wx.PostEvent (self.event_receiver, de)
144             del de
145
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)
151
152         self.y_range = None
153         self.stripchartsink = stripchartsink
154
155         self.SetEnableGrid (True)
156         # self.SetEnableZoom (True)
157         # self.SetBackgroundColour ('black')
158         
159         self.build_popup_menu()
160         
161         EVT_DATA_EVENT (self, self.set_data)
162
163         wx.EVT_CLOSE (self, self.on_close_window)
164         self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
165
166         self.input_watcher = input_watcher(stripchartsink.msgq, 1, self)
167
168
169     def on_close_window (self, event):
170         print "stripchart_window:on_close_window"
171         self.keep_running = False
172
173
174     def set_data (self, evt):
175         indata = evt.data
176         L = len (indata)
177
178         calc_min = min(indata)
179         calc_max = max(indata)
180         d = calc_max - calc_min
181         d = d * 0.1
182         if self.stripchartsink.autoscale == True and self.stripchartsink.parallel == True:
183             self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d)
184
185         N = self.stripchartsink.stripsize
186         if self.stripchartsink.parallel != True:
187             for i in range(1,N):
188                 pooey = N-i
189                 self.stripchartsink.vector[pooey] = self.stripchartsink.vector[pooey-1]
190     
191             self.stripchartsink.vector[0] = indata
192
193         else:
194             self.stripchartsink.vector = indata
195
196         if self.stripchartsink.parallel == True:
197             avg = 0
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]
205
206             avg /= self.stripchartsink.stripsize
207             markers = []
208             placedmarkers = 0
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]))
214                        placedmarkers += 1
215             
216         points = Numeric.zeros((N,2), Numeric.Float64)
217         for i in range(0,N):
218             if self.stripchartsink.scaling == 1.0:
219                 points[i,0] = i
220             else:
221                 points[i,0] = i * self.stripchartsink.scaling
222             points[i,1] = self.stripchartsink.vector[i]
223
224         if self.stripchartsink.parallel == True and placedmarkers > 1:
225             for i in range(0,N):
226                 self.stripchartsink.vector[i] = 0
227
228             marks = plot.PolyMarker(markers, colour='BLACK', marker='triangle_down')
229
230         lines = plot.PolyLine (points, colour='RED')
231
232         # Temporary--I'm find the markers distracting
233         placedmarkers = 0
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)
240
241         else:
242             graphics = plot.PlotGraphics ([lines,marks],
243                                       title=self.stripchartsink.title,
244                                       xLabel = xlab, yLabel = ylab)
245
246         self.Draw (graphics, xAxis=None, yAxis=self.y_range)
247
248         if self.stripchartsink.autoscale == False or self.stripchartsink.parallel == False:
249             self.update_y_range ()
250
251
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)
256
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)
261
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)
266
267     def on_autoscale(self, evt):
268         self.stripchartsink.set_autoscale(evt.IsChecked())
269
270     def on_incr_y_per_div(self, evt):
271         divbase = self.stripchartsink.divbase
272         x1 = 1 * divbase
273         x2 = 2 * divbase
274         x4 = 4 * divbase
275         x10 = 10 * divbase
276         x20 = 20 * 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)))
279
280     def on_decr_y_per_div(self, evt):
281         # print "on_decr_y_per_div"
282         divbase = self.stripchartsink.divbase
283         x1 = 1 * divbase
284         x2 = 2 * divbase
285         x4 = 4 * divbase
286         x10 = 10 * divbase
287         x20 = 20 * divbase
288         self.stripchartsink.set_y_per_div(next_down(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20)))
289
290     def on_y_per_div(self, evt):
291         # print "on_y_per_div"
292         divbase=self.stripchartsink.divbase
293         Id = evt.GetId()
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)
304
305         
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)
310             item.Check(pred())
311         self.PopupMenu(menu, event.GetPosition())
312
313
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()
326
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)
337
338
339         # make a menu
340         menu = wx.Menu()
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
347         v = 1.0*divbase
348         s = "%.3f" % v
349         menu.AppendCheckItem(self.id_y_per_div_1, s)
350         v = 2.0*divbase
351         s = "%.3f" % v
352         menu.AppendCheckItem(self.id_y_per_div_2, s)
353         v = 4.0*divbase
354         s = "%.3f" % v
355         menu.AppendCheckItem(self.id_y_per_div_5, s)
356         v = 10*divbase
357         s = "%.3f" % v
358         menu.AppendCheckItem(self.id_y_per_div_10, s)
359         v = 20*divbase
360         s = "%.3f" % v
361         menu.AppendCheckItem(self.id_y_per_div_20, s)
362
363         self.checkmarks = {
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,
370             }
371
372
373 def next_up(v, seq):
374     """
375     Return the first item in seq that is > v.
376     """
377     for s in seq:
378         if s > v:
379             return s
380     return v
381
382 def next_down(v, seq):
383     """
384     Return the last item in seq that is < v.
385     """
386     rseq = list(seq[:])
387     rseq.reverse()
388
389     for s in rseq:
390         if s < v:
391             return s
392     return v