class Expression(Block): args = List(Input) input = Input() def init(self, func, *args): # HACK: should not directly reference lupa here import lupa if lupa.lua_type(args) == 'table': args = args.values() print ('Expression: ', func, args) self.func = func self.args = list(args) self.output = Signal() # Workaround for list event bug self.input = self.args[0] def _input_changed(self): self.output.copy_traits(self.input, ['label', 'color']) def process(self): args = [chan.last for chan in self.args] self.output.append([self.func(*args)]) self.output.process()
class Normalizer(Block): input = Input() time_factor = Float(0.9999) def __init__(self, input, **config): self.output = Signal() super(Normalizer, self).__init__(**config) self.min = 0.0 self.max = 1.0 self.input = input def process(self): min = np.min(self.input.buffer) / 2 self.min = self.min * self.time_factor + min * (1.0 - self.time_factor) max = np.max(self.input.buffer) * 2 self.max = self.max * self.time_factor + max * (1.0 - self.time_factor) out = (np.array(self.input.new) - self.min) / (self.max - self.min) * 2 self.output.append(out) self.output.process()
class NotchDelay(Block): input = Input() frequency = Float(50.0) samplerate = Int(250) def init(self, input): self.readjust() self.input = input self.output = Signal() self.delayed = Signal() @on_trait_change("frequency,samplerate") def readjust(self): delay = self.samplerate / self.frequency / 2 delay = 3 self.delayline = [0] * int(delay) def process(self): for sample in self.input.new: self.delayline[1:] = self.delayline[:-1] self.delayline[0] = sample self.output.append([self.delayline[-1] + sample]) self.delayed.append([self.delayline[-1] + self.delayline[-2]]) self.output.process() self.delayed.process()
class NotchFilter(Block): input = Input() frequency = Float(50.0) # Find out what module_pole is and rename it module_pole = Float(0.9) nyquist = Float(125.0) @on_trait_change("frequency,module_pole") def compute_filter(self): theta = 2 * np.pi * self.frequency / self.nyquist * 2 zero = np.exp(np.array([1j, -1j]) * theta) pole = self.module_pole * zero self._filter_b = np.poly(zero) self._filter_a = np.poly(pole) def init(self, input): self.compute_filter() self.input = input self.output = Signal() def process(self): buffer = self.input.buffer assert self.input.new_samples == 1 filt = lfilter(self._filter_b, self._filter_a, buffer) self.output.append(filt[-1:]) self.output.process()
class BandPass(Block): input = Input() order = Range(low=2, high=8, value=5) lo = Float hi = Float nyquist = Float(125) def init(self, lo, hi, input): self.lo = lo self.hi = hi self.input = input self.output = Signal() @on_trait_change("lo,hi,order,nyquist") def _range_changed(self): b,a = iirfilter(self.order, (self.lo / self.nyquist, self.hi / self.nyquist)) self._filter_b, self._filter_a = b,a def process(self): buffer = self.input.buffer[-self.order*3:] filt = lfilter(self._filter_b, self._filter_a, buffer) self.output.append(filt[-1:]) self.output.process() @property def range(self): return self.lo, self.hi
class DominantFrequency(Block): input = Input() chunk_size = 256 def init(self, input): self.input = input self.cnt = 0 self.window = np.hanning(self.chunk_size) self.freq = Signal() def process(self): self.cnt += 1 if self.cnt > 20: self.cnt = 0 else: return C = np.fft.rfft(self.input.buffer[-self.chunk_size:] * self.window) C = abs(C) Fs = 250.0 def index_max(values): return max(xrange(len(values)),key=values.__getitem__) freq = index_max(C) freq = freq / Fs self.freq.append([freq]) self.freq.process()
class JitterBuffer(Block): input = Input() buffer_size = 5 def init(self, input, sample_rate): self.input = input self.output = Signal() self.period = 1.0 / sample_rate self.buffer = [] self.started = False def process(self): newbuf = self.input.new newbuf.extend(self.buffer) self.buffer = newbuf if not self.started and len(self.buffer) > self.buffer_size: self.started = True import threading threading.Thread(target=self.run).start() def clock_sample(self): if len(self.buffer): self.output.append([self.buffer.pop()]) self.output.process() else: print('Warning: JitterBuffer ran empty') def run(self): nperiod = int(self.period * 1000000000) ncomputation = nperiod // 10 nconstraint = ncomputation * 2 print(self.period, nperiod, ncomputation, nconstraint) set_realtime(nperiod, ncomputation, nconstraint) start = monotonic() while (True): loop_begin = monotonic() #print ('diff time %f' % (loop_begin - start)) wait_time = start + self.period - monotonic() if wait_time <= 0.000001: start += self.period self.clock_sample() loop_end = monotonic() #print ('clock process took %f seconds' % (loop_end - loop_begin)) elif wait_time > self.period / 2: #print ('%f seconds left, sleeping' % wait_time) thread_yield() else: pass
class DCBlock(Block): input = Input() def __init__(self, input, **config): self.dc = Signal() self.ac = Signal() super(DCBlock, self).__init__(**config) self.input = input def _input_changed(self): traits = self.input.trait_get(['label', 'color']) self.ac.trait_set(**traits) def process(self): for x in self.input.new: new_dc = self.dc.buffer[-1] * 0.95 + x * 0.05 self.dc.append([new_dc]) self.dc.process() self.ac.append([x - self.dc.buffer[-1] for x in self.input.new]) self.ac.process()
class Averager(Block): input = Input() def __init__(self, input): self.output = Signal() self.average = 0.0 self.factor = 0.99 super(Averager, self).__init__(input=input) def process(self): for x in self.input.new: self.average = self.average * self.factor + x * (1.0 - self.factor) self.output.append([self.average]) self.output.process()
class Expression(Block): args = List(Input) input = Input() def init(self, func, *args): self.func = func self.args = list(args) # Workaround for list event bug self.input = args[0] self.output = Signal() def process(self): args = [chan.last for chan in self.args] self.output.append([self.func(*args)]) self.output.process()
class TestClock(QtCore.QThread): def __init__(self, sample_rate): super(TestClock, self).__init__() self.output = Signal() self.period = 1.0 / sample_rate #self.start(QtCore.QThread.HighestPriority) #self.start() threading.Thread(target=self.run).start() def clock_sample(self): self.output.append([monotonic()]) self.output.process() def run(self): nperiod = int(self.period * 1000000000) ncomputation = nperiod // 10 nconstraint = ncomputation * 2 print (self.period, nperiod, ncomputation, nconstraint) set_realtime(nperiod, ncomputation, nconstraint) start = monotonic() while (True): loop_begin = monotonic() #print ('diff time %f' % (loop_begin - start)) wait_time = start + self.period - monotonic() if wait_time <= 0.000001: start += self.period self.clock_sample() loop_end = monotonic() #print ('clock process took %f seconds' % (loop_end - loop_begin)) elif wait_time > self.period / 2: #print ('%f seconds left, sleeping' % wait_time) thread_yield() else: pass
class HeartAnalyzer(Block): input = Input() def init(self): self.beat = Signal('Beat Event') self.bpm = Signal("Beats per Minute") def process(self): square = np.array(self.input.buffer)**2 threshold = np.percentile(square, 98) if np.max(self.input.buffer[:self.input.new_samples]) > threshold: self.beat.append([1.0]) print 'beat event' else: self.beat.append([0.0]) self.beat.process()
class RMS(Block): input = Input() avg_size = Int(42) def init(self, input): self.output = Signal() self.input = input def _input_changed(self): self.output.copy_traits(self.input, ['label', 'color']) def process(self): buf = np.array(self.input.buffer[-self.avg_size:]) rms = sum(buf ** 2) / len(buf) avg = np.sqrt(rms) self.output.append([avg]) self.output.process()
class TestClock(QtCore.QThread): def __init__(self, sample_rate): super(TestClock, self).__init__() self.output = Signal() self.period = 1.0 / sample_rate #self.start(QtCore.QThread.HighestPriority) #self.start() threading.Thread(target=self.run).start() def clock_sample(self): self.output.append([monotonic()]) self.output.process() def run(self): nperiod = int(self.period * 1000000000) ncomputation = nperiod // 10 nconstraint = ncomputation * 2 print(self.period, nperiod, ncomputation, nconstraint) set_realtime(nperiod, ncomputation, nconstraint) start = monotonic() while (True): loop_begin = monotonic() #print ('diff time %f' % (loop_begin - start)) wait_time = start + self.period - monotonic() if wait_time <= 0.000001: start += self.period self.clock_sample() loop_end = monotonic() #print ('clock process took %f seconds' % (loop_end - loop_begin)) elif wait_time > self.period / 2: #print ('%f seconds left, sleeping' % wait_time) thread_yield() else: pass
class HeartAnalyzer(Block): input = Input() def init(self): self.beat = Signal('Beat Event') self.bpm = Signal("Beats per Minute") def process(self): square = np.array(self.input.buffer) ** 2 threshold = np.percentile(square, 98) if np.max(self.input.buffer[:self.input.new_samples]) > threshold: self.beat.append([1.0]) print 'beat event' else: self.beat.append([0.0]) self.beat.process()
class ClockAnalyzer(Block): input = Input() alpha = 0.5 def init(self, input): self.input = input self.last_time = monotonic() self.time_diff = Signal() self.sample_rate = Signal() self.jitter = Signal() def process(self): #self.input.buffer_size = 1024 #if len(self.input.buffer) < 1024: return new_time = monotonic() diff = new_time - self.last_time self.last_time = new_time self.time_diff.append([diff]) self.time_diff.process() sample_rate = 1.0 / moving_average_exp(self.alpha, self.time_diff.buffer) self.sample_rate.append([sample_rate]) self.sample_rate.process() period = 1.0 / sample_rate jitter = abs(diff - period) * 1000 self.jitter.append([jitter]) self.jitter.process()
class Trendline(Block): input = Input() interval = Int(25) def __init__(self, input): self.output = Signal(buffer_size=1000) self.cnt = 0 super(Trendline, self).__init__(input=input) def _input_changed(self): traits = self.input.trait_get(['label', 'color']) self.ac.trait_set(**traits) def process(self): self.cnt += self.input.new_samples if self.cnt >= self.interval: self.cnt = 0 avg = sum(self.input.buffer[-self.interval:]) / self.interval self.output.append([avg]) self.output.process()
class PulseAnalyzer(Block): input = Input() def init(self, input): self.input = input self.output = Signal() self.bpm = Signal() self.gradient = Signal() self.pulse = Signal() self.last_beat = -1 self.timestamp = 0 def _input_changed(self): self.output.copy_traits(self.input, ["label", "color"]) def process(self): avg = np.average(self.output.buffer) max = np.max(self.output.buffer) self.timestamp += 1 self.gradient.append([self.input.buffer[-1] - self.input.buffer[-2]]) buf = np.power(self.gradient.buffer[-50:], 2) s = np.sum(np.hanning(50)[:50] * buf) self.output.append([s]) for i, sample in enumerate(self.output.new): new = 1.0 if sample > max * 0.7 else 0.0 self.pulse.append([new]) if new > self.pulse.buffer[-2]: diff = self.timestamp - self.last_beat bpm = 1.0 / (diff / 250.0) * 60 self.bpm.append([bpm]) self.bpm.process() print("beat event", diff, bpm, self.timestamp, self.last_beat) self.last_beat = self.timestamp self.output.process() self.gradient.process()
class PulseAnalyzer(Block): input = Input() def init(self, input): self.input = input self.output = Signal() self.bpm = Signal() self.gradient = Signal() self.pulse = Signal() self.last_beat = -1 self.timestamp = 0 def _input_changed(self): self.output.copy_traits(self.input, ['label', 'color']) def process(self): avg = np.average(self.output.buffer) max = np.max(self.output.buffer) self.timestamp += 1 self.gradient.append([self.input.buffer[-1] - self.input.buffer[-2]]) buf = np.power(self.gradient.buffer[-50:], 2) s = np.sum(np.hanning(50)[:50] * buf) self.output.append([s]) for i, sample in enumerate(self.output.new): new = 1.0 if sample > max * 0.7 else 0.0 self.pulse.append([new]) if new > self.pulse.buffer[-2]: diff = self.timestamp - self.last_beat bpm = 1.0 / (diff / 250.) * 60 self.bpm.append([bpm]) self.bpm.process() print('beat event', diff, bpm, self.timestamp, self.last_beat) self.last_beat = self.timestamp self.output.process() self.gradient.process()
class Threshold(Block): input = Input() average_period = Float(0.35) epoch = Float(13.0) auto_mode = Bool(True) mode = Enum('increase', 'decrease', 'range') auto_target = Float(90.) low_target = Float(90) high_target = Float(90) def init(self, name): self.FS = 250 self.name=name epoch_samples = int(self.FS * self.epoch) self.signal = Signal(buffer_size=epoch_samples) self.passfail = Signal() self.threshold = 1.0 self.high_threshold = 0.0 self.calc_cnt = 0 self.bar = QtGui.QProgressBar(orientation=QtCore.Qt.Vertical) self.slider = QtGui.QSlider() self.slider.setRange(0, 17) self.bar.setRange(0, 17) self.pass_palette = self.bar.palette() if isinstance(self.input.color, QtGui.QColor): self.color = self.input.color else: self.color = QtGui.QColor(QtGui.qRgb(*self.input.color)) self.bar.setStyleSheet(""" QProgressBar::chunk { background: red; } QProgressBar::chunk[pass='******'] { background: %s ; } """ % self.color.name()) self.button = QtGui.QPushButton("config") self.button.clicked.connect(self.configure_traits) def widget(self): w = QtGui.QGroupBox() w.setTitle(self.name) l = QtGui.QGridLayout() l.addWidget(self.bar, 0, 0) l.addWidget(self.slider, 0, 1) l.addWidget(self.button, 1, 0, 1, 2) w.setLayout(l) w.block = self return w def updateGUI(self): self.bar.setValue(self.signal.last) self.bar.setProperty('pass', self.passfail.last) self.bar.style().polish(self.bar) if self.auto_mode: self.slider.setValue(self.threshold) #print type(self.threshold), self.threshold, self.nameano def process(self): assert self.input.new_samples == 1 avg_period_samples = int(self.average_period * self.FS) avg = sum(self.input.buffer[-avg_period_samples:]) / avg_period_samples self.signal.append([avg]) self.signal.process() self.calc_cnt += 1 if self.auto_mode and self.calc_cnt >= avg_period_samples: self.calc_cnt = 0 if self.mode == 'decrease': self.threshold = np.percentile(self.signal.buffer, self.auto_target) elif self.mode == 'increase': self.threshold = np.percentile(self.signal.buffer, 100 - self.auto_target) else: self.high_threshold = np.percentile(self.signal.buffer, self.high_target) self.threshold = np.percentile(self.signal.buffer, 100 - self.low_target) success = False if self.mode == 'decrease': if avg < self.threshold: success = True elif self.mode == 'increase': if avg > self.threshold: success = True else: if avg > self.threshold and avg < self.high_threshold: success = True self.passfail.append([success]) self.passfail.process()
class Threshold(Block): input = Input() average_period = Float(0.35) epoch = Float(3.0) auto_mode = Bool(True) mode = Enum('increase', 'decrease', 'range') auto_target = Float(0.90) low_target = Float(0.90) high_target = Float(0.90) def init(self, name, input): self.FS = 250 self.name = name epoch_samples = int(self.FS * self.epoch) self.signal = Signal(buffer_size=epoch_samples) self.passfail = Signal() self.ratio = Signal() self.threshold = 1.0 self.high_threshold = 0.0 self.calc_cnt = 0 self.bar = QtGui.QProgressBar(orientation=QtCore.Qt.Vertical) self.slider = QtGui.QSlider() self.slider.setRange(0, 17) self.bar.setRange(0, 17) self.pass_palette = self.bar.palette() self.input = input if isinstance(self.input.color, QtGui.QColor): self.color = self.input.color else: self.color = QtGui.QColor(self.input.color) self.bar.setStyleSheet(""" QProgressBar::chunk { background: red; } QProgressBar::chunk[pass='******'] { background: %s ; } """ % self.color.name()) self.button = QtGui.QPushButton("config") self.button.clicked.connect(self.configure_traits) self._widget = ThresholdWidget(self) def widget(self): return self._widget w = QtGui.QGroupBox() w.setTitle(self.name) l = QtGui.QGridLayout() l.addWidget(self.bar, 0, 0) l.addWidget(self.slider, 0, 1) l.addWidget(self.button, 1, 0, 1, 2) w.setLayout(l) w.block = self return w def updateGUI(self): self._widget.update() return self.bar.setValue(self.signal.last) self.bar.setProperty('pass', self.passfail.last) self.bar.style().polish(self.bar) if self.auto_mode: self.slider.setValue(self.threshold) #print type(self.threshold), self.threshold, self.nameano def process(self): assert self.input.new_samples == 1 avg_period_samples = int(self.average_period * self.FS) avg = sum(self.input.buffer[-avg_period_samples:]) / avg_period_samples self.signal.append([avg]) self.signal.process() self.calc_cnt += 1 if self.auto_mode and self.calc_cnt >= avg_period_samples: self.calc_cnt = 0 if self.mode == 'decrease': self.threshold = np.percentile(self.signal.buffer, 100 * self.auto_target) elif self.mode == 'increase': self.threshold = np.percentile(self.signal.buffer, 100 - 100 * self.auto_target) else: self.high_threshold = np.percentile(self.signal.buffer, self.high_target) self.threshold = np.percentile(self.signal.buffer, 100 - 100 * self.low_target) success = False self.ratio.append([avg / self.threshold]) self.ratio.process() if self.mode == 'decrease': if avg < self.threshold: success = True elif self.mode == 'increase': if avg > self.threshold: success = True else: if avg > self.threshold and avg < self.high_threshold: success = True self.passfail.append([float(success)]) self.passfail.process()
class NotchFilter(Block): input = Input() frequency = Float(50.0) notchWidth = Float(0.1) nyquist = Float(125.0) @on_trait_change("frequency,notchWidth") def compute_filter(self): freqRatio = self.frequency / self.nyquist print self.frequency, self.nyquist, freqRatio print type(self.frequency), type(self.nyquist), type(freqRatio) wn = freqRatio r = 0.1 B, A = np.zeros(3), np.zeros(3) A[0],A[1],A[2] = 1.0, -2.0*r*np.cos(2*np.pi*wn), r*r B[0],B[1],B[2] = 1.0, -2.0*np.cos(2*np.pi*wn), 1.0 self._filter_b = B self._filter_a = A #%Compute zeros #zeros = np.array([np.exp(0+1j *np.pi*freqRatio ), np.exp( 0-1j*np.pi*freqRatio )]) #%Compute poles #poles = (1-self.notchWidth) * zeros #self._filter_b = np.poly( zeros ) # Get moving average filter coefficients #self._filter_a = np.poly( poles ) # Get autoregressive filter coefficients self._filter_b, self._filter_a = iirfilter(4, (45. / self.nyquist, 55. / self.nyquist), 'bandstop') def init(self, input): self.input = input self.output = Signal() self.compute_filter() def process(self): buffer = self.input.buffer filt = lfilter(self._filter_b, self._filter_a, buffer) self.output.append(filt[-1:]) self.output.process() #fft = np.fft.fft(self.input.buffer[-256:] * np.hanning(256)) #fft[90:110] = 0 #fft[120:140] = 0 #fft[100:200] = 0 #ifft = np.fft.ifft(fft) #self.output.append([self.input.buffer[-1]]) #self.output.append(np.real(ifft[-1:])) #self.output.process() @property def range(self): return self.lo, self.hi
class JitterBuffer(Block): input = Input() buffer_size = 5 def init(self, input, sample_rate): self.input = input self.output = Signal() self.period = 1.0 / sample_rate self.buffer = [] self.started = False def process(self): newbuf = self.input.new newbuf.extend(self.buffer) self.buffer = newbuf if not self.started and len(self.buffer) > self.buffer_size: self.started = True import threading threading.Thread(target=self.run).start() def clock_sample(self): if len(self.buffer): self.output.append([self.buffer.pop()]) self.output.process() else: print ('Warning: JitterBuffer ran empty') def run(self): nperiod = int(self.period * 1000000000) ncomputation = nperiod // 10 nconstraint = ncomputation * 2 print (self.period, nperiod, ncomputation, nconstraint) set_realtime(nperiod, ncomputation, nconstraint) start = monotonic() while (True): loop_begin = monotonic() #print ('diff time %f' % (loop_begin - start)) wait_time = start + self.period - monotonic() if wait_time <= 0.000001: start += self.period self.clock_sample() loop_end = monotonic() #print ('clock process took %f seconds' % (loop_end - loop_begin)) elif wait_time > self.period / 2: #print ('%f seconds left, sleeping' % wait_time) thread_yield() else: pass