#!/usr/bin/env python2 ################################################## # GNU Radio Python Flow Graph # Title: Top Block # Generated: Tue Oct 6 22:40:10 2015 ################################################## if __name__ == '__main__': import ctypes import sys if sys.platform.startswith('linux'): try: x11 = ctypes.cdll.LoadLibrary('libX11.so') x11.XInitThreads() except: print "Warning: failed to XInitThreads()" from PyQt4 import Qt, QtCore, QtGui from gnuradio import blocks from gnuradio import audio from gnuradio import eng_notation from gnuradio import filter from gnuradio import fft from gnuradio import gr from gnuradio import qtgui from gnuradio.eng_option import eng_option from gnuradio.filter import firdes from optparse import OptionParser import sip import sys import pyqtgraph as pg #from blocks import BEServer, Threshold import numpy as np import scipy.signal import signal signal.signal(signal.SIGINT, signal.SIG_DFL) cnt = 0 import threading, time import math class InputDict(object): pass class Input(object): """Attribute type for describing signal inputs of Blocks""" # A counter used to find out the order of declaration order = 0 def __init__(self, type=np.float32): self.order = Input.order Input.order += 1 self.type = type """Create a copy from a class attribute for a specific Block instance""" def instantiate(self, block, name): new = Input(type=self.type) new.block = block new.name = name new.cls = self print ('Input.instantiate', self, block, name, new) return new def __getattr__(self, attrname): if 'block' not in self.__dict__: raise ValueError('getattr call on uninstantiated Input', self) if 'source' in self.__dict__: return getattr(self.source, attrname) raise AttributeError(self, 'Input \'%s\' of Block %s does not have attribute \'%s\', nor any sources' % (name, block, attrname)) class Output(object): """Attribute type for describing signal outputs of Blocks""" # A counter used to find out the order of declaration order = 0 def __init__(self, source=None, type=np.float32): print ('Output init', source, type) self.order = Input.order Output.order += 1 self.source = source self.type = type """Create a copy from a class attribute for a specific Block instance""" def instantiate(self, block, name): print ('Output.instantiate', self, block, name) new = Output(type=self.type) new.block = block new.name = name # Get the instance of the source object new.source = block._get_input_instance(self.source) return new def __getattr__(self, attrname): #print ('Output getattr', attrname, self.__dict__['block']) #if hasattr(self, attrname): #if object(self, attrname): # print ('p3') # return super(self).__getattr__(attrname) if hasattr(self.block, attrname): return getattr(self.block, attrname) source = self.__dict__['source'] if source: return getattr(source, attrname) print ('p5') raise AttributeError('Output does not have attribute \'' + attrname + '\', nor any sources') class Block(object): def _get_input_instance(self, input): if not input: return None for attr in dir(self): inp = getattr(self, attr) if type(inp) == Input and inp.cls == input: return inp raise AttributeError('Can not find %s in %s', (input, self)) def __init__(self, *args, **kwargs): inputs = [] outputs = [] # Search block for inputs/outputs, add them to a list # Also replace the declaration with a copy (instance) for attrname in dir(self): attr = getattr(self, attrname) if type(attr) in (Input, Output): print self, 'found type ', attr, attrname #attr = copy.copy(attr) attr = attr.instantiate(self, attrname) print attr.name, attr.block setattr(self, attrname, attr) if type(attr) == Input: inputs.append(attr) elif type(attr) == Output: outputs.append(attr) inputs.sort(key=lambda x: x.order) outputs.sort(key=lambda x: x.order) for i in inputs: print ('Block', self, ' Input', i) for i in outputs: print ('Block', self, ' Output', i) print (self, len(args), len(inputs), inputs) if len(args) < len(inputs): raise ValueError('Not enough arguments for input') args = list(args) for i, input in enumerate(inputs): input.index = i source = args.pop(0) if isinstance(source, Block): source = source.output print ('Assign output', self, source) input.source = source for i, output in enumerate(outputs): output.index = i input_types = [x.type for x in inputs] output_types = [x.type for x in outputs] name = self.__class__.__name__ if hasattr(self, 'general_work'): self.gr_block = gr.basic_block(name, input_types, output_types) self.gr_block.general_work = self.general_work self.init(*args, **kwargs) assert hasattr(self, 'gr_block') class InOutBlock(Block): "A base class for the default case of a block with input and one output" input = Input() output = Output(input) class BandPass(InOutBlock): def init(self, lo, hi): nyquist = self.input.sample_rate / 2.0 Wp = [lo / nyquist, hi / nyquist] #Ws = [(lo - 1) / nyquist, (hi+1) / nyquist] #b, a = scipy.signal.iirdesign(Wp, Ws, 0.1, 60.0) b, a = scipy.signal.iirfilter(6, Wp, btype='bandpass', ftype='ellip', rp=0.1, rs=60.0) #self.gr_block = filter.iir_filter_ffd(a, b, oldstyle=False) self.gr_block = filter.iir_filter_ffd(b, a, oldstyle=False) class NotchFilter(InOutBlock): def init(self, freq=50.0, mod=0.9): theta = 2 * np.pi * 50 / self.input.sample_rate zero = np.exp(np.array([1j, -1j]) * theta) pole = mod * zero a, b = np.poly(pole), np.poly(zero) #notch_ab = numpy.poly(zero), numpy.poly(pole) #notch_ab = scipy.signal.iirfilter(32, [30.0 / 125], btype='low') self.gr_block = filter.iir_filter_ffd(b, a, oldstyle=False) class RMS(InOutBlock): def init(self, alpha=0.01): self.gr_block = blocks.rms_ff(alpha) class DCBlock(InOutBlock): def init(self, taps=16): self.gr_block = filter.dc_blocker_ff(16, long_form=False) class ExponentialAverage(InOutBlock): def init(self, lookback = 1.0): samples = length * self.input.sample_rate scale = 1.0 / samples self.gr_block = blocks.moving_average_ff(int(samples), scale) def general_work(self, input_items, output_items): print ('BarSpectrogram work', len(input_items[0]), output_items, input_items[0][0]) self.gr_block.consume_each(1) self.gr_block.produce_each(1) output_items[0][0] = result self.buffer = input_items[0][-len(self.win):] return 0 class Oscilloscope(Block): input = Input() def init(self, history=512, autoscale=True): self.widget = pg.PlotWidget() self.widget.block = self self.gr_block.set_history(history) self.plot = self.widget.plot() self.plot.setPen(QtGui.QColor(self.input.color)) #self.widget.setYRange(*self.yrange) self.widget.enableAutoRange('y', 0.95 if autoscale else False) self.buffer = [] self.timer = QtCore.QTimer() self.timer.timeout.connect(self.updateGUI) self.timer.start(100) def general_work(self, input_items, output_items): #print ('Oscilloscope work', len(input_items[0]), output_items, input_items[0][0]) # TODO: Make relative to update rate self.gr_block.consume_each(5) self.buffer = input_items[0] return 0 def updateGUI(self): self.plot.setData(self.buffer) self.widget.update() class BarGraph(Block): inputs = InputDict() def init(self): pass class BarSpectrogram(Block): input = Input() def init(self, lo=0, hi=125, bins=256, yrange=750, ratio=False): self.widget = pg.PlotWidget() self.widget.setLabel('bottom', 'Frequency', units='Hz') self.bars = pg.BarGraphItem() self.win = np.hanning(bins) self.win = np.blackman(bins) #self.win = np.ones(bins) self.lo, self.hi = lo, hi self.ratio = ratio FS = self.input.sample_rate self.gr_block.set_history(bins) #num_bars = int(round((self.bins - 1) * (self.hi - self.lo) / FS)) # This is total bullshit: num_bars = len(np.zeros(bins)[lo: hi]) x = np.linspace(self.lo, self.hi, num_bars) self.bars = pg.BarGraphItem(x=x, height=range(num_bars), width=1.0) self.bars.setOpts(brushes=[pg.hsvColor(float(x) / num_bars) for x in range(num_bars)]) self.widget.addItem(self.bars) # TODO: Better autoranging features #self.plot.enableAutoRange('xy', False) self.widget.setYRange(0, yrange) self.widget.enableAutoRange('y', 0.95) self.buffer = np.zeros(bins) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.updateGUI) self.timer.start(10) def general_work(self, input_items, output_items): #print ('BarSpectrogram work', len(input_items[0]), output_items, input_items[0][0]) self.gr_block.consume_each(16) self.buffer = input_items[0][-len(self.win):] return 0 def updateGUI(self): C = np.fft.rfft(self.buffer * self.win) C = abs(C) lo, hi = self.lo, self.hi data = C[lo : hi] if self.ratio: data = data / sum(C) self.bars.setOpts(height=data) #self.widget.setData(input_items[0]) self.widget.update() def widget(self): return self.plot class UDPSource(Block): channel1 = Output() def init(self): self.channel1.sample_rate = 250.0 self.channel1.color = 'orange' self.gr_block = blocks.udp_source(gr.sizeof_float*1, "127.0.0.1", 9999, 1472, True) #class FloatToShort(InOutBlock): # def init(self): # self.gr_block = class AudioSink(Block): input = Input() def init(self): self.gr_block = audio.sink(int(self.input.sample_rate), "", True) import OSC # TODO: Make an OSC Connection class that makes send objects class OSCSend(Block): input = Input() def init(self, address, send_period=0.05): self.samples = int(send_period * self.input.sample_rate) self.client = OSC.OSCClient() self.client.connect(('127.0.0.1', 5510)) # connect to Faust self.address = address self.gr_block.set_history(self.samples) def general_work(self, input_items, output_items): print ('OSCSend work', len(input_items[0]), output_items, input_items[0]) self.gr_block.consume_each(self.samples) oscmsg = OSC.OSCMessage() oscmsg.setAddress(self.address) val = input_items[0][self.samples-1] val = val / 6 * 200 oscmsg.append(val) self.client.send(oscmsg) return 0 class Stream2Vector(Block): input = Input() output = Output(type=(np.float32, 256)) def init(self, bins=256, framerate=2): self.num_samples = int(self.input.sample_rate / framerate) self.gr_block.set_history(bins) self.bins = bins def general_work(self, input_items, output_items): self.gr_block.consume_each(self.num_samples) print 'Stream2Vector work', len(input_items[0]) #output_items[0][:self.bins] = input_items[0][-self.bins:] output_items[0][0] = input_items[0][-self.bins:] #self.gr_block.produce(0, self.bins) self.gr_block.produce(0, 1) return 0 class FFT(Block): input = Input() bins = Output() def init(self, bins=256): self.gr_block = fft.fft_vfc(256, forward=True, window=fft.window.blackmanharris(bins)) from blocks import Threshold class top_block(gr.top_block, Qt.QWidget): def __init__(self): gr.top_block.__init__(self, "Top Block") Qt.QWidget.__init__(self) self.setWindowTitle("Top Block") try: self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) except: pass self.top_scroll_layout = Qt.QVBoxLayout() self.setLayout(self.top_scroll_layout) self.top_scroll = Qt.QScrollArea() self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) self.top_scroll_layout.addWidget(self.top_scroll) self.top_scroll.setWidgetResizable(True) self.top_widget = Qt.QWidget() self.top_scroll.setWidget(self.top_widget) self.top_layout = Qt.QVBoxLayout(self.top_widget) self.top_grid_layout = Qt.QGridLayout() self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "top_block") self.restoreGeometry(self.settings.value("geometry").toByteArray()) ################################################## # Blocks ################################################## self.qtgui_sink_x_0 = qtgui.sink_f( 256, #fftsize firdes.WIN_HANN, #wintype 50, #fc 250, #bw "", #name True, #plotfreq True, #plotwaterfall True, #plottime True, #plotconst ) self.qtgui_sink_x_0.set_update_time(1.0/100) self._qtgui_sink_x_0_win = sip.wrapinstance(self.qtgui_sink_x_0.pyqwidget(), Qt.QWidget) self.top_layout.addWidget(self._qtgui_sink_x_0_win) #test_source = TestSource() test_source = blocks.udp_source(gr.sizeof_float*1, "127.0.0.1", 9999, 1472, True) src = UDPSource() # Signal Conditioning: DC Block and 50 Hz Notch Filter ch1 = NotchFilter(src.channel1) #ch1 = src.channel1 #ch1 = NotchFilter(ch1, freq=100) ch1 = DCBlock(ch1) #ch1 = BandPass(ch1, 1, 40) #notched = NotchFilter(notched) #notched = NotchFilter(notched) alpha = BandPass(ch1, 8, 12) alpha.color = 'green' alpha = RMS(alpha, 0.02) #alpha = ExponentialAverage(alpha, 5.1) smr = BandPass(ch1, 9.5, 12.5) sigs = alpha, smr rmss = map(RMS, sigs) #threshold = Threshold(rmss[1], 'increase') #oscclient = OSCSend(threshold.ratio, '/0x00/filter') #oscclient = OSCSend(rmss[1], '/0x00/filter') audio = BandPass(ch1, 7, 12) audio = ch1 #osci = Oscilloscope(rmss[1]) #osci = Oscilloscope(threshold.ratio) spec = BarSpectrogram(ch1, lo=0, hi=127, bins=256) vec = Stream2Vector(ch1) fft = FFT(vec) waterfall = WaterfallLines(fft.bins) #fft = FFT(Stream2Vector(ch1)) osci = Oscilloscope(ch1) #self.top_layout.addWidget(waterfall.widget) waterfall.widget.show() self.top_layout.addWidget(osci.widget) audiosink = AudioSink(audio) hlayout = Qt.QHBoxLayout() hlayout.addWidget(osci.widget) #\hlayout.addWidget(threshold.widget) #widget = Qt.QWidget() #widget.addLayout(hlayout) #self.top_layout.addLayout(hlayout) self.top_layout.addWidget(spec.widget) visited = self.wireup(osci) #visited = self.wireup(osci0, visited) #visited = self.wireup(oscclient, visited) # visited = self.wireup(threshold, visited) visited = self.wireup(spec, visited) visited = self.wireup(waterfall, visited) def wireup(self, destination, visited=[]): print 'wireup', destination, visited if destination in visited: return visited visited = visited + [destination] for idx in dir(destination): inp = getattr(destination, idx) if type(inp) == Input: self.connect(inp.source.block.gr_block, inp.block.gr_block) return self.wireup(inp.source.block, visited) return visited if __name__ == '__main__': parser = OptionParser(option_class=eng_option, usage="%prog: [options]") (options, args) = parser.parse_args() from distutils.version import StrictVersion if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"): Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster')) qapp = Qt.QApplication(sys.argv) tb = top_block() tb.start() tb.show() def quitting(): tb.stop() #tb.wait() qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting) qapp.exec_() tb = None # to clean up Qt widgets