[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Discuss-gnuradio] Simple display sink in wx
From: |
Martin Dvh |
Subject: |
Re: [Discuss-gnuradio] Simple display sink in wx |
Date: |
Mon, 19 Feb 2007 00:06:12 +0100 |
User-agent: |
Debian Thunderbird 1.0.2 (X11/20070113) |
Achilleas Anastasopoulos wrote:
> Martin,
>
> I think the display code can be uploaded to the trunk.
> All the tests that I have tried work fine.
> We can always clean up the code later if needed.
>
> I can do that if there is no objection
> Achilleas
Fine with me,
Martin
>
>
> Martin Dvh wrote:
>
>> Achilleas Anastasopoulos wrote:
>>
>>> Martin,
>>>
>>> thanks for the block.
>>> Indeed it is exactly what I wanted (and even more)!!!
>>>
>>> One question: since a throttle is not attached to this block,
>>> why do we need separate sample_rate and number_rate parameters?
>>> The only place I saw them used is
>>>
>>> one_in_n = gr.keep_one_in_n(gr.sizeof_float,
>>> max(1, int(sample_rate/number_rate)))
>>>
>>> which implies that only the ratio of sample_rate/number_rate
>>> is important. Unless number_rate actually controls the
>>> real rate at which numbers are displayed (which I cannot figure out
>>> how)...
>>
>>
>> The number_rate limits the outputting of numbers to the screen.
>> If this would get more then around 50 numbers/sec this will use up all
>> display resources and your gui will freeze.
>> (xfree can't display more numbers/sec then your monitors refreshrate
>> anyway)
>>
>> This is the same effect you get when using the fftsink with a too high
>> fft_rate.
>>
>> Martin
>>
>>> Thanks
>>> Achilleas
>>>
>>>
>>> Martin Dvh wrote:
>>>
>>>
>>>> Achilleas Anastasopoulos wrote:
>>>>
>>>>
>>>>> Hi,
>>>>>
>>>>> it would be very useful to have a
>>>>> wx sink that displays the value of its input.
>>>>> This way we can check in real time values
>>>>> that change slowly (eg, snr, bit error rate, etc.)
>>>>
>>>>
>>>>
>>>> Your widh is my command:
>>>> Attached is a patch which adds a number_sink_f and number_sink_c to
>>>> gr-wxgui.
>>>> You must apply it in the root of your gnuradio tree.
>>>> patch -p0 <numbersink.patch
>>>>
>>>> You can also find it in gr-wxgui in my work-in-progress development
>>>> tree in svn:
>>>> gnuradio/branches/developers/nldudok1/general-wip
>>>>
>>>> You use it in the following way:
>>>>
>>>> from gnuradio.wxgui import numbersink
>>>>
>>>> numsink = numbersink.number_sink_f (self, panel,
>>>> unit='volts',label="input level", avg_alpha=1.0e-5,average=True,
>>>> sample_rate=input_rate/sw_decim,
>>>> factor=1.0,base_value=0,
>>>> minval=-100.0, maxval=100.0,
>>>> ref_level=0, decimal_places=5,number_rate=15)
>>>> vbox.Add (numsink.win, 1, wx.EXPAND)
>>>> self.connect(some_float_source,numsink)
>>>>
>>>> You can set several options by right-mouse-click.
>>>> (Show gauge, number of digits, avaraging)
>>>> You use it just like an oscope or fft sink.
>>>>
>>>> the parameters:
>>>> fg flowgraph, the flowgraph (use self if you work with a
>>>> stdgui.gui_flow_graph)
>>>> parent wxguipanel, The wxgui parent (use panel if you work with a
>>>> stdgui.gui_flow_graph)
>>>> unit string, the unit you want to display, for example 'volt'
>>>> or 'Watt', free text.
>>>> base_value float, an offset which is added to the value
>>>> minval float, min value for the gauge
>>>> maxval float, max value for the gauge
>>>> factor float, scaling factor, value is multiplied by this (sort
>>>> of gain)
>>>> decimal_places int, the number of decimal places shown
>>>> ref_level ignored (API compatibility with scope and fft-sink)
>>>> sample_rate float, samplerate of input
>>>> number_rate float or int, rate at which values are displayed
>>>> (framerate, default is 15)
>>>> average bool, use averaging or not
>>>> avg_alpha float, average alpha, determines the speed of averaging
>>>> label string, the label which goes with this value (for example
>>>> 'frequency' or 'input power')
>>>> size default=(640,240), the graphical size of this numbersink
>>>> peak_hold bool, hold at peak or not.
>>>>
>>>>
>>>>
>>>>> Unfortunately there is no how_to_write_a_wx_block
>>>>> so I have no idea how to go about it.
>>>>>
>>>>> If someone can help me by providing a stripped down
>>>>> version of one of the wx sinks (*eg, the oscope, or fft)
>>>>> then I can take a crack at it.
>>>>> Even better if one can implement this :-)
>>>>
>>>>
>>>>
>>>> I hope you like it.
>>>>
>>>> Eric, is this maybe something for trunk.
>>>> I don't know if the code is clean enough.
>>>>
>>>> Greetings,
>>>> Martin
>>>>
>>>>
>>>>> Thanks,
>>>>> Achilleas
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Discuss-gnuradio mailing list
>>>>> address@hidden
>>>>> http://lists.gnu.org/mailman/listinfo/discuss-gnuradio
>>>>>
>>>>
>>>>
>>>>
>>>> ------------------------------------------------------------------------
>>>>
>>>>
>>>> Index: gr-wxgui/src/python/numbersink.py
>>>> ===================================================================
>>>> --- gr-wxgui/src/python/numbersink.py (revision 0)
>>>> +++ gr-wxgui/src/python/numbersink.py (revision 0)
>>>> @@ -0,0 +1,614 @@
>>>> +#!/usr/bin/env python
>>>> +#
>>>> +# Copyright 2003,2004,2005,2006 Free Software Foundation, Inc.
>>>> +# +# This file is part of GNU Radio
>>>> +# +# GNU Radio is free software; you can redistribute it and/or modify
>>>> +# it under the terms of the GNU General Public License as published by
>>>> +# the Free Software Foundation; either version 2, or (at your option)
>>>> +# any later version.
>>>> +# +# GNU Radio is distributed in the hope that it will be useful,
>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +# GNU General Public License for more details.
>>>> +# +# You should have received a copy of the GNU General Public License
>>>> +# along with GNU Radio; see the file COPYING. If not, write to
>>>> +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
>>>> +# Boston, MA 02111-1307, USA.
>>>> +# +
>>>> +from gnuradio import gr, gru, window
>>>> +from gnuradio.wxgui import stdgui
>>>> +import wx
>>>> +#from wx import StaticText
>>>> +import gnuradio.wxgui.plot as plot
>>>> +import Numeric
>>>> +import threading
>>>> +import math +
>>>> +default_numbersink_size = (640,240)
>>>> +default_number_rate = gr.prefs().get_long('wxgui', 'number_rate', 15)
>>>> +
>>>> +class number_sink_base(object):
>>>> + def __init__(self, input_is_real=False, unit='',base_value=0,
>>>> minval=-100.0,maxval=100.0,factor=1.0,decimal_places=10, ref_level=50,
>>>> + sample_rate=1, +
>>>> number_rate=default_number_rate,
>>>> + average=False, avg_alpha=None, label='',
>>>> peak_hold=False):
>>>> +
>>>> + # initialize common attributes
>>>> + self.unit=unit
>>>> + self.base_value = base_value
>>>> + self.minval=minval
>>>> + self.maxval=maxval
>>>> + self.factor=factor
>>>> + self.y_divs = 8
>>>> + self.decimal_places=decimal_places
>>>> + self.ref_level = ref_level
>>>> + self.sample_rate = sample_rate
>>>> + number_size=1
>>>> + self.number_size = number_size
>>>> + self.number_rate = number_rate
>>>> + self.average = average
>>>> + if avg_alpha is None:
>>>> + self.avg_alpha = 2.0 / number_rate
>>>> + else:
>>>> + self.avg_alpha = avg_alpha
>>>> + self.label = label
>>>> + self.peak_hold = peak_hold
>>>> + self.show_gauge = True
>>>> + self.input_is_real = input_is_real
>>>> + self.msgq = gr.msg_queue(2) # queue that holds a
>>>> maximum of 2 messages
>>>> +
>>>> + def set_decimal_places(self, decimal_places):
>>>> + self.decimal_places = decimal_places
>>>> +
>>>> + def set_ref_level(self, ref_level):
>>>> + self.ref_level = ref_level
>>>> +
>>>> + def print_current_value(self, comment):
>>>> + print comment,self.win.current_value
>>>> +
>>>> + def set_average(self, average):
>>>> + self.average = average
>>>> + if average:
>>>> + self.avg.set_taps(self.avg_alpha)
>>>> + self.set_peak_hold(False)
>>>> + else:
>>>> + self.avg.set_taps(1.0)
>>>> +
>>>> + def set_peak_hold(self, enable):
>>>> + self.peak_hold = enable
>>>> + if enable:
>>>> + self.set_average(False)
>>>> + self.win.set_peak_hold(enable)
>>>> +
>>>> + def set_show_gauge(self, enable):
>>>> + self.show_gauge = enable
>>>> + self.win.set_show_gauge(enable)
>>>> +
>>>> + def set_avg_alpha(self, avg_alpha):
>>>> + self.avg_alpha = avg_alpha
>>>> +
>>>> + def set_base_value(self, base_value):
>>>> + self.base_value = base_value
>>>> + +
>>>> +class number_sink_f(gr.hier_block, number_sink_base):
>>>> + def __init__(self, fg, parent,
>>>> unit='',base_value=0,minval=-100.0,maxval=100.0,factor=1.0,
>>>> + decimal_places=10, ref_level=50, sample_rate=1,
>>>> #number_size=512,
>>>> + number_rate=default_number_rate, average=False,
>>>> avg_alpha=None,
>>>> + label='', size=default_numbersink_size,
>>>> peak_hold=False):
>>>> +
>>>> + number_sink_base.__init__(self, unit=unit,
>>>> input_is_real=True, base_value=base_value,
>>>> +
>>>> minval=minval,maxval=maxval,factor=factor,
>>>> + decimal_places=decimal_places,
>>>> ref_level=ref_level,
>>>> + sample_rate=sample_rate,
>>>> #number_size=number_size,
>>>> + number_rate=number_rate,
>>>> + average=average, avg_alpha=avg_alpha,
>>>> label=label,
>>>> + peak_hold=peak_hold)
>>>> + + number_size=1 + #s2p =
>>>> gr.stream_to_vector(gr.sizeof_float, number_size)
>>>> + one_in_n = gr.keep_one_in_n(gr.sizeof_float,
>>>> + max(1,
>>>> int(sample_rate/number_rate)))
>>>> +
>>>> + + #c2mag = gr.complex_to_mag(number_size)
>>>> + self.avg = gr.single_pole_iir_filter_ff(1.0, number_size)
>>>> +
>>>> + # FIXME We need to add 3dB to all bins but the DC bin
>>>> + #log = gr.nlog10_ff(20, number_size,
>>>> + #
>>>> -20*math.log10(number_size)-10*math.log10(power/number_size))
>>>> + sink = gr.message_sink(gr.sizeof_float , self.msgq, True)
>>>> +
>>>> + #fg.connect (s2p, one_in_n, fft, c2mag, self.avg, log, sink)
>>>> + fg.connect(self.avg,one_in_n,sink)
>>>> + gr.hier_block.__init__(self, fg, self.avg, sink)
>>>> + self.win = number_window(self, parent, size=size,label=label)
>>>> + self.set_average(self.average)
>>>> +
>>>> +
>>>> +class number_sink_c(gr.hier_block, number_sink_base):
>>>> + def __init__(self, fg, parent,
>>>> unit='',base_value=0,minval=-100.0,maxval=100.0,factor=1.0,
>>>> + decimal_places=10, ref_level=50, sample_rate=1,
>>>> #number_size=512,
>>>> + number_rate=default_number_rate, average=False,
>>>> avg_alpha=None,
>>>> + label='', size=default_numbersink_size,
>>>> peak_hold=False):
>>>> +
>>>> + number_sink_base.__init__(self, unit=unit,
>>>> input_is_real=False, base_value=base_value,factor=factor,
>>>> +
>>>> minval=minval,maxval=maxval,decimal_places=decimal_places,
>>>> ref_level=ref_level,
>>>> + sample_rate=sample_rate,
>>>> #number_size=number_size,
>>>> + number_rate=number_rate,
>>>> + average=average, avg_alpha=avg_alpha,
>>>> label=label,
>>>> + peak_hold=peak_hold)
>>>> +
>>>> + number_size=1 + one_in_n =
>>>> gr.keep_one_in_n(gr.sizeof_gr_complex,
>>>> + max(1,
>>>> int(sample_rate/number_rate)))
>>>> +
>>>> + + #c2mag = gr.complex_to_mag(number_size)
>>>> + self.avg = gr.single_pole_iir_filter_cc(1.0, number_size)
>>>> +
>>>> + # FIXME We need to add 3dB to all bins but the DC bin
>>>> + #log = gr.nlog10_ff(20, number_size,
>>>> + #
>>>> -20*math.log10(number_size)-10*math.log10(power/number_size))
>>>> + sink = gr.message_sink(gr.sizeof_gr_complex , self.msgq, True)
>>>> +
>>>> + #fg.connect (s2p, one_in_n, fft, c2mag, self.avg, log, sink)
>>>> + fg.connect(self.avg,one_in_n,sink)
>>>> + gr.hier_block.__init__(self, fg, self.avg, sink)
>>>> + self.win = number_window(self, parent, size=size,label=label)
>>>> + self.set_average(self.average)
>>>> +
>>>> +
>>>> +#
>>>> ------------------------------------------------------------------------
>>>>
>>>> +
>>>> +myDATA_EVENT = wx.NewEventType()
>>>> +EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0)
>>>> +
>>>> +
>>>> +class DataEvent(wx.PyEvent):
>>>> + def __init__(self, data):
>>>> + wx.PyEvent.__init__(self)
>>>> + self.SetEventType (myDATA_EVENT)
>>>> + self.data = data
>>>> +
>>>> + def Clone (self): + self.__class__ (self.GetId())
>>>> +
>>>> +
>>>> +class input_watcher (threading.Thread):
>>>> + def __init__ (self, msgq, number_size, event_receiver, **kwds):
>>>> + threading.Thread.__init__ (self, **kwds)
>>>> + self.setDaemon (1)
>>>> + self.msgq = msgq
>>>> + self.number_size = number_size
>>>> + self.event_receiver = event_receiver
>>>> + self.keep_running = True
>>>> + self.start ()
>>>> +
>>>> + def run (self):
>>>> + while (self.keep_running):
>>>> + msg = self.msgq.delete_head() # blocking read of message
>>>> queue
>>>> + itemsize = int(msg.arg1())
>>>> + nitems = int(msg.arg2())
>>>> +
>>>> + s = msg.to_string() # get the body of the msg
>>>> as a string
>>>> +
>>>> + # There may be more than one number in the message.
>>>> + # If so, we take only the last one
>>>> + if nitems > 1:
>>>> + start = itemsize * (nitems - 1)
>>>> + s = s[start:start+itemsize]
>>>> +
>>>> + complex_data = Numeric.fromstring (s, Numeric.Float32)
>>>> + de = DataEvent (complex_data)
>>>> + wx.PostEvent (self.event_receiver, de)
>>>> + del de
>>>> +
>>>> +#========================================================================================
>>>>
>>>>
>>>> +class static_text_window (wx.StaticText): #plot.PlotCanvas):
>>>> + def __init__ (self, parent, numbersink,id = -1,label="number",
>>>> + pos = wx.DefaultPosition, size = wx.DefaultSize,
>>>> + style = wx.DEFAULT_FRAME_STYLE, name = ""):
>>>> + #plot.PlotCanvas.__init__ (self, parent, id, pos, size,
>>>> style, name)
>>>> + wx.StaticText.__init__(self, parent, id, label, pos, size,
>>>> style, name)
>>>> + #self.static_text=wx.StaticText( parent, id, label, pos,
>>>> (size[0]/2,size[1]/2), style, name)
>>>> + #gauge_style = wx.GA_HORIZONTAL
>>>> + #self.gauge=wx.Gauge( parent, id, range=1000,
>>>> pos=(pos[0],pos[1]+size[1]/2),size=(size[0]/2,size[1]/2),
>>>> style=gauge_style, name = "gauge")
>>>> + #wx.BoxSizer.__init__ (self,wx.VERTICAL)
>>>> + #self.Add (self.static_text, 0, wx.EXPAND)
>>>> + #self.Add (self.gauge, 1, wx.EXPAND)
>>>> + self.parent=parent
>>>> + self.label=label
>>>> + #self.y_range = None
>>>> + self.numbersink = numbersink
>>>> + self.peak_hold = False
>>>> + self.peak_vals = None
>>>> +
>>>> + #self.SetEnableGrid (True)
>>>> + # self.SetEnableZoom (True)
>>>> + # self.SetBackgroundColour ('black')
>>>> + + self.build_popup_menu()
>>>> + + #EVT_DATA_EVENT (self, self.set_data)
>>>> + #wx.EVT_CLOSE (self, self.on_close_window)
>>>> + #self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
>>>> + self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
>>>> +
>>>> + #self.input_watcher = input_watcher(numbersink.msgq,
>>>> numbersink.number_size, self)
>>>> +
>>>> +
>>>> + def on_close_window (self, event):
>>>> + print "number_window:on_close_window"
>>>> + self.keep_running = False
>>>> +
>>>> +
>>>> + def set_peak_hold(self, enable):
>>>> + self.peak_hold = enable
>>>> + self.peak_vals = None
>>>> +
>>>> + def update_y_range (self):
>>>> + ymax = self.numbersink.ref_level
>>>> + ymin = self.numbersink.ref_level -
>>>> self.numbersink.decimal_places * self.numbersink.y_divs
>>>> + self.y_range = self._axisInterval ('min', ymin, ymax)
>>>> +
>>>> + def on_average(self, evt):
>>>> + # print "on_average"
>>>> + self.numbersink.set_average(evt.IsChecked())
>>>> +
>>>> + def on_peak_hold(self, evt):
>>>> + # print "on_peak_hold"
>>>> + self.numbersink.set_peak_hold(evt.IsChecked())
>>>> +
>>>> + def on_show_gauge(self, evt):
>>>> + # print "on_show_gauge"
>>>> + #if evt.IsChecked():
>>>> + self.numbersink.set_show_gauge(evt.IsChecked())
>>>> + print evt.IsChecked()
>>>> + # print "show gauge"
>>>> + #else:
>>>> + # self.parent.gauge.Hide()
>>>> + # print "hide gauge"
>>>> +
>>>> + def on_incr_ref_level(self, evt):
>>>> + # print "on_incr_ref_level"
>>>> + self.numbersink.set_ref_level(self.numbersink.ref_level
>>>> + + self.numbersink.decimal_places)
>>>> +
>>>> + def on_decr_ref_level(self, evt):
>>>> + # print "on_decr_ref_level"
>>>> + self.numbersink.set_ref_level(self.numbersink.ref_level
>>>> + - self.numbersink.decimal_places)
>>>> +
>>>> + def on_incr_decimal_places(self, evt):
>>>> + # print "on_incr_decimal_places"
>>>> +
>>>> self.numbersink.set_decimal_places(self.numbersink.decimal_places+1)
>>>> #next_up(self.numbersink.decimal_places, (1,2,5,10,20)))
>>>> +
>>>> + def on_decr_decimal_places(self, evt):
>>>> + # print "on_decr_decimal_places"
>>>> +
>>>> self.numbersink.set_decimal_places(max(self.numbersink.decimal_places-1,0))
>>>>
>>>> #next_down(self.numbersink.decimal_places, (1,2,5,10,20)))
>>>> +
>>>> + def on_decimal_places(self, evt):
>>>> + # print "on_decimal_places"
>>>> + Id = evt.GetId()
>>>> + if Id == self.id_decimal_places_0:
>>>> + self.numbersink.set_decimal_places(0)
>>>> + elif Id == self.id_decimal_places_1:
>>>> + self.numbersink.set_decimal_places(1)
>>>> + elif Id == self.id_decimal_places_2:
>>>> + self.numbersink.set_decimal_places(2)
>>>> + elif Id == self.id_decimal_places_3:
>>>> + self.numbersink.set_decimal_places(3)
>>>> + elif Id == self.id_decimal_places_6:
>>>> + self.numbersink.set_decimal_places(6)
>>>> + elif Id == self.id_decimal_places_9:
>>>> + self.numbersink.set_decimal_places(9)
>>>> +
>>>> + + def on_right_click(self, event):
>>>> + menu = self.popup_menu
>>>> + for id, pred in self.checkmarks.items():
>>>> + item = menu.FindItemById(id)
>>>> + item.Check(pred())
>>>> + self.PopupMenu(menu, event.GetPosition())
>>>> +
>>>> +
>>>> + def build_popup_menu(self):
>>>> + #self.id_hide_gauge = wx.NewId()
>>>> + self.id_show_gauge = wx.NewId()
>>>> + self.id_incr_ref_level = wx.NewId()
>>>> + self.id_decr_ref_level = wx.NewId()
>>>> + self.id_incr_decimal_places = wx.NewId()
>>>> + self.id_decr_decimal_places = wx.NewId()
>>>> + self.id_decimal_places_0 = wx.NewId()
>>>> + self.id_decimal_places_1 = wx.NewId()
>>>> + self.id_decimal_places_2 = wx.NewId()
>>>> + self.id_decimal_places_3 = wx.NewId()
>>>> + self.id_decimal_places_6 = wx.NewId()
>>>> + self.id_decimal_places_9 = wx.NewId()
>>>> + self.id_average = wx.NewId()
>>>> + self.id_peak_hold = wx.NewId()
>>>> +
>>>> + self.Bind(wx.EVT_MENU, self.on_average, id=self.id_average)
>>>> + self.Bind(wx.EVT_MENU, self.on_peak_hold,
>>>> id=self.id_peak_hold)
>>>> + #self.Bind(wx.EVT_MENU, self.on_hide_gauge,
>>>> id=self.id_hide_gauge)
>>>> + self.Bind(wx.EVT_MENU, self.on_show_gauge,
>>>> id=self.id_show_gauge)
>>>> + self.Bind(wx.EVT_MENU, self.on_incr_ref_level,
>>>> id=self.id_incr_ref_level)
>>>> + self.Bind(wx.EVT_MENU, self.on_decr_ref_level,
>>>> id=self.id_decr_ref_level)
>>>> + self.Bind(wx.EVT_MENU, self.on_incr_decimal_places,
>>>> id=self.id_incr_decimal_places)
>>>> + self.Bind(wx.EVT_MENU, self.on_decr_decimal_places,
>>>> id=self.id_decr_decimal_places)
>>>> + self.Bind(wx.EVT_MENU, self.on_decimal_places,
>>>> id=self.id_decimal_places_0)
>>>> + self.Bind(wx.EVT_MENU, self.on_decimal_places,
>>>> id=self.id_decimal_places_1)
>>>> + self.Bind(wx.EVT_MENU, self.on_decimal_places,
>>>> id=self.id_decimal_places_2)
>>>> + self.Bind(wx.EVT_MENU, self.on_decimal_places,
>>>> id=self.id_decimal_places_3)
>>>> + self.Bind(wx.EVT_MENU, self.on_decimal_places,
>>>> id=self.id_decimal_places_6)
>>>> + self.Bind(wx.EVT_MENU, self.on_decimal_places,
>>>> id=self.id_decimal_places_9)
>>>> +
>>>> +
>>>> + # make a menu
>>>> + menu = wx.Menu()
>>>> + self.popup_menu = menu
>>>> + menu.AppendCheckItem(self.id_average, "Average")
>>>> + menu.AppendCheckItem(self.id_peak_hold, "Peak Hold")
>>>> + #menu.Append(self.id_hide_gauge, "Hide gauge")
>>>> + menu.AppendCheckItem(self.id_show_gauge, "Show gauge")
>>>> + menu.Append(self.id_incr_ref_level, "Incr Ref Level")
>>>> + menu.Append(self.id_decr_ref_level, "Decr Ref Level")
>>>> + menu.Append(self.id_incr_decimal_places, "Incr decimal
>>>> places")
>>>> + menu.Append(self.id_decr_decimal_places, "Decr decimal
>>>> places")
>>>> + menu.AppendSeparator()
>>>> + # we'd use RadioItems for these, but they're not supported on
>>>> Mac
>>>> + menu.AppendCheckItem(self.id_decimal_places_0, "0 decimal
>>>> places")
>>>> + menu.AppendCheckItem(self.id_decimal_places_1, "1 decimal
>>>> places")
>>>> + menu.AppendCheckItem(self.id_decimal_places_2, "2 decimal
>>>> places")
>>>> + menu.AppendCheckItem(self.id_decimal_places_3, "3 decimal
>>>> places")
>>>> + menu.AppendCheckItem(self.id_decimal_places_6, "6 decimal
>>>> places")
>>>> + menu.AppendCheckItem(self.id_decimal_places_9, "9 decimal
>>>> places")
>>>> +
>>>> + self.checkmarks = {
>>>> + self.id_average : lambda : self.numbersink.average,
>>>> + self.id_peak_hold : lambda :
>>>> self.numbersink.peak_hold,# self.id_hide_gauge : lambda :
>>>> self.numbersink.hide_gauge,
>>>> + self.id_show_gauge : lambda : self.numbersink.show_gauge,
>>>> + self.id_decimal_places_0 : lambda :
>>>> self.numbersink.decimal_places == 0,
>>>> + self.id_decimal_places_1 : lambda :
>>>> self.numbersink.decimal_places == 1,
>>>> + self.id_decimal_places_2 : lambda :
>>>> self.numbersink.decimal_places == 2,
>>>> + self.id_decimal_places_3 : lambda :
>>>> self.numbersink.decimal_places == 3,
>>>> + self.id_decimal_places_6 : lambda :
>>>> self.numbersink.decimal_places == 6,
>>>> + self.id_decimal_places_9 : lambda :
>>>> self.numbersink.decimal_places == 9,
>>>> + }
>>>> +
>>>> +
>>>> +def next_up(v, seq):
>>>> + """
>>>> + Return the first item in seq that is > v.
>>>> + """
>>>> + for s in seq:
>>>> + if s > v:
>>>> + return s
>>>> + return v
>>>> +
>>>> +def next_down(v, seq):
>>>> + """
>>>> + Return the last item in seq that is < v.
>>>> + """
>>>> + rseq = list(seq[:])
>>>> + rseq.reverse()
>>>> +
>>>> + for s in rseq:
>>>> + if s < v:
>>>> + return s
>>>> + return v
>>>> +
>>>> +
>>>> +#========================================================================================
>>>>
>>>>
>>>> +class number_window (plot.PlotCanvas):
>>>> + def __init__ (self, numbersink, parent, id = -1,label="number",
>>>> + pos = wx.DefaultPosition, size = wx.DefaultSize,
>>>> + style = wx.DEFAULT_FRAME_STYLE, name = ""):
>>>> + plot.PlotCanvas.__init__ (self, parent, id, pos, size, style,
>>>> name)
>>>> + #wx.StaticText.__init__(self, parent, id, label, pos,
>>>> (size[0]/2,size[1]/2), style, name)
>>>> + #print 'parent',parent
>>>> + self.static_text=static_text_window( self, numbersink,id,
>>>> label, pos, (size[0]/2,size[1]/2), style, name)
>>>> + gauge_style = wx.GA_HORIZONTAL
>>>> + vbox=wx.BoxSizer(wx.VERTICAL)
>>>> + vbox.Add (self.static_text, 0, wx.EXPAND)
>>>> + self.current_value=None
>>>> + if numbersink.input_is_real:
>>>> + self.gauge=wx.Gauge( self, id, range=1000,
>>>> pos=(pos[0],pos[1]+size[1]/2),size=(size[0]/2,size[1]/2),
>>>> style=gauge_style, name = "gauge")
>>>> + vbox.Add (self.gauge, 1, wx.EXPAND)
>>>> + else:
>>>> + self.gauge=wx.Gauge( self, id, range=1000,
>>>> pos=(pos[0],pos[1]+size[1]/3),size=(size[0]/2,size[1]/3),
>>>> style=gauge_style, name = "gauge")
>>>> + #hbox=wx.BoxSizer(wx.HORIZONTAL)
>>>> + self.gauge_imag=wx.Gauge( self, id, range=1000,
>>>> pos=(pos[0],pos[1]+size[1]*2/3),size=(size[0]/2,size[1]/3),
>>>> style=gauge_style, name = "gauge_imag")
>>>> + vbox.Add (self.gauge, 1, wx.EXPAND)
>>>> + vbox.Add (self.gauge_imag, 1, wx.EXPAND)
>>>> + #vbox.Add (hbox, 1, wx.EXPAND)
>>>> + self.sizer = vbox
>>>> + self.SetSizer (self.sizer)
>>>> + self.SetAutoLayout (True)
>>>> + self.sizer.Fit (self)
>>>> +
>>>> + self.label=label
>>>> + #self.y_range = None
>>>> + self.numbersink = numbersink
>>>> + self.peak_hold = False
>>>> + self.peak_vals = None
>>>> +
>>>> + #self.SetEnableGrid (True)
>>>> + # self.SetEnableZoom (True)
>>>> + # self.SetBackgroundColour ('black')
>>>> + + #self.build_popup_menu()
>>>> + + EVT_DATA_EVENT (self, self.set_data)
>>>> + wx.EVT_CLOSE (self, self.on_close_window)
>>>> + #self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
>>>> + #self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
>>>> +
>>>> + self.input_watcher = input_watcher(numbersink.msgq,
>>>> numbersink.number_size, self)
>>>> +
>>>> +
>>>> + def on_close_window (self, event):
>>>> + print "number_window:on_close_window"
>>>> + self.keep_running = False
>>>> +
>>>> + def set_show_gauge(self, enable):
>>>> + self.show_gauge = enable
>>>> + if enable:
>>>> + self.gauge.Show()
>>>> + if not self.numbersink.input_is_real:
>>>> + self.gauge_imag.Show()
>>>> + #print 'show'
>>>> + else:
>>>> + self.gauge.Hide()
>>>> + if not self.numbersink.input_is_real:
>>>> + self.gauge_imag.Hide()
>>>> + #print 'hide'
>>>> +
>>>> + def set_data (self, evt):
>>>> + numbers = evt.data
>>>> + L = len (numbers)
>>>> +
>>>> + if self.peak_hold:
>>>> + if self.peak_vals is None:
>>>> + self.peak_vals = numbers
>>>> + else:
>>>> + self.peak_vals = Numeric.maximum(numbers,
>>>> self.peak_vals)
>>>> + numbers = self.peak_vals
>>>> +
>>>> + if self.numbersink.input_is_real:
>>>> + real_value=numbers[0]*self.numbersink.factor +
>>>> self.numbersink.base_value
>>>> + imag_value=0.0
>>>> + self.current_value=real_value
>>>> + else:
>>>> + real_value=numbers[0]*self.numbersink.factor +
>>>> self.numbersink.base_value
>>>> + imag_value=numbers[1]*self.numbersink.factor +
>>>> self.numbersink.base_value
>>>> + self.current_value=complex(real_value,imag_value)
>>>> + #x = max(abs(self.numbersink.sample_rate),
>>>> abs(self.numbersink.base_value))
>>>> + x = max(real_value, imag_value)
>>>> + if x >= 1e9:
>>>> + sf = 1e-9
>>>> + unit_prefix = "G"
>>>> + elif x >= 1e6:
>>>> + sf = 1e-6
>>>> + unit_prefix = "M"
>>>> + elif x>= 1e3:
>>>> + sf = 1e-3
>>>> + unit_prefix = "k"
>>>> + else :
>>>> + sf = 1
>>>> + unit_prefix = ""
>>>> + #self.update_y_range ()
>>>> + if self.numbersink.input_is_real:
>>>> + showtext = "%s: %.*f %s%s" % (self.label,
>>>> self.numbersink.decimal_places,real_value*sf,unit_prefix,self.numbersink.unit)
>>>>
>>>>
>>>> + else:
>>>> + showtext = "%s: %.*f,%.*f %s%s" % (self.label,
>>>> self.numbersink.decimal_places,real_value*sf,
>>>> +
>>>> self.numbersink.decimal_places,imag_value*sf,unit_prefix,self.numbersink.unit)
>>>>
>>>>
>>>> + self.static_text.SetLabel(showtext)
>>>> + #print
>>>> (int(float((real_value-self.numbersink.base_value)*1000.0/(self.numbersink.maxval-self.numbersink.minval)))+500)
>>>>
>>>>
>>>> +
>>>> self.gauge.SetValue(int(float((real_value-self.numbersink.base_value)*1000.0/(self.numbersink.maxval-self.numbersink.minval)))+500)
>>>>
>>>>
>>>> + if not self.numbersink.input_is_real:
>>>> +
>>>> self.gauge.SetValue(int(float((imag_value-self.numbersink.base_value)*1000.0/(self.numbersink.maxval-self.numbersink.minval)))+500)
>>>>
>>>>
>>>> +
>>>> + def set_peak_hold(self, enable):
>>>> + self.peak_hold = enable
>>>> + self.peak_vals = None
>>>> +
>>>> + def update_y_range (self):
>>>> + ymax = self.numbersink.ref_level
>>>> + ymin = self.numbersink.ref_level -
>>>> self.numbersink.decimal_places * self.numbersink.y_divs
>>>> + self.y_range = self._axisInterval ('min', ymin, ymax)
>>>> +
>>>> + def on_average(self, evt):
>>>> + # print "on_average"
>>>> + self.numbersink.set_average(evt.IsChecked())
>>>> +
>>>> + def on_peak_hold(self, evt):
>>>> + # print "on_peak_hold"
>>>> + self.numbersink.set_peak_hold(evt.IsChecked())
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +# ----------------------------------------------------------------
>>>> +# Deprecated interfaces
>>>> +# ----------------------------------------------------------------
>>>> +
>>>> +# returns (block, win).
>>>> +# block requires a single input stream of float
>>>> +# win is a subclass of wxWindow
>>>> +
>>>> +def make_number_sink_f(fg, parent, label, number_size, input_rate,
>>>> ymin = 0, ymax=50):
>>>> + + block = number_sink_f(fg, parent, label=label,
>>>> number_size=number_size, sample_rate=input_rate,
>>>> + decimal_places=(ymax - ymin)/8, ref_level=ymax)
>>>> + return (block, block.win)
>>>> +
>>>> +# returns (block, win).
>>>> +# block requires a single input stream of gr_complex
>>>> +# win is a subclass of wxWindow
>>>> +
>>>> +def make_number_sink_c(fg, parent, label, number_size, input_rate,
>>>> ymin=0, ymax=50):
>>>> + block = number_sink_c(fg, parent, label=label,
>>>> number_size=number_size, sample_rate=input_rate,
>>>> + decimal_places=(ymax - ymin)/8, ref_level=ymax)
>>>> + return (block, block.win)
>>>> +
>>>> +
>>>> +# ----------------------------------------------------------------
>>>> +# Standalone test app
>>>> +# ----------------------------------------------------------------
>>>> +
>>>> +class test_app_flow_graph (stdgui.gui_flow_graph):
>>>> + def __init__(self, frame, panel, vbox, argv):
>>>> + stdgui.gui_flow_graph.__init__ (self, frame, panel, vbox,
>>>> argv)
>>>> +
>>>> + #number_size = 256
>>>> +
>>>> + # build our flow graph
>>>> + input_rate = 20.48e3
>>>> +
>>>> + # Generate a complex sinusoid
>>>> + src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
>>>> + #src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE,
>>>> 5.75e3, 1)
>>>> +
>>>> + # We add these throttle blocks so that this demo doesn't
>>>> + # suck down all the CPU available. Normally you wouldn't use
>>>> these.
>>>> + thr1 = gr.throttle(gr.sizeof_gr_complex, input_rate)
>>>> +
>>>> + #sink1 = number_sink_c (self, panel, label="Complex Data",
>>>> number_size=number_size,
>>>> + # sample_rate=input_rate, base_value=100e3,
>>>> + # ref_level=0, decimal_places=3)
>>>> + #vbox.Add (sink1.win, 1, wx.EXPAND)
>>>> + #self.connect (src1, thr1, sink1)
>>>> +
>>>> + src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
>>>> + #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE,
>>>> 5.75e3, 1)
>>>> + thr2 = gr.throttle(gr.sizeof_float, input_rate)
>>>> + sink2 = number_sink_f (self, panel, unit='Hz',label="Real
>>>> Data", avg_alpha=0.001,#number_size=number_size*2,
>>>> + sample_rate=input_rate, base_value=100e3,
>>>> + ref_level=0, decimal_places=3)
>>>> + vbox.Add (sink2.win, 1, wx.EXPAND)
>>>> + sink3 = number_sink_c (self, panel, unit='V',label="Complex
>>>> Data", avg_alpha=0.001,#number_size=number_size*2,
>>>> + sample_rate=input_rate, base_value=0,
>>>> + ref_level=0, decimal_places=3)
>>>> + vbox.Add (sink3.win, 1, wx.EXPAND)
>>>> + self.connect (src2, thr2, sink2)
>>>> + self.connect (src1, thr1, sink3)
>>>> +def main ():
>>>> + app = stdgui.stdapp (test_app_flow_graph,
>>>> + "Number Sink Test App")
>>>> + app.MainLoop ()
>>>> +
>>>> +if __name__ == '__main__':
>>>> + main ()
>>>>
>>>> Property changes on: gr-wxgui/src/python/numbersink.py
>>>> ___________________________________________________________________
>>>> Name: svn:executable
>>>> + *
>>>>
>>>> Index: gr-wxgui/src/python/Makefile.am
>>>> ===================================================================
>>>> --- gr-wxgui/src/python/Makefile.am (revision 4343)
>>>> +++ gr-wxgui/src/python/Makefile.am (working copy)
>>>> @@ -37,4 +37,5 @@
>>>> scopesink.py \
>>>> waterfallsink.py \
>>>> slider.py \
>>>> - stdgui.py + stdgui.py \
>>>> + numbersink.py
>>>
>>>
>>>
>>
>>
>
- [Discuss-gnuradio] Simple display sink in wx, Achilleas Anastasopoulos, 2007/02/12
- Re: [Discuss-gnuradio] Simple display sink in wx, Martin Dvh, 2007/02/12
- Re: [Discuss-gnuradio] Simple display sink in wx, Achilleas Anastasopoulos, 2007/02/12
- Re: [Discuss-gnuradio] Simple display sink in wx, Martin Dvh, 2007/02/13
- Re: [Discuss-gnuradio] Simple display sink in wx, Achilleas Anastasopoulos, 2007/02/16
- Re: [Discuss-gnuradio] Simple display sink in wx, Eric Blossom, 2007/02/18
- [Discuss-gnuradio] Blocks with different input types, Achilleas Anastasopoulos, 2007/02/18
- Re: [Discuss-gnuradio] Blocks with different input types, Eric Blossom, 2007/02/18
- Re: [Discuss-gnuradio] Simple display sink in wx,
Martin Dvh <=