One-shot / debounce / timer box?

Concerning processing components: filters, file load/save, visualizations, communication ...
Post Reply
daves
Posts: 3
Joined: Thu Feb 02, 2017 6:13 pm

One-shot / debounce / timer box?

Post by daves »

Hi,

I'm using OpenViBE (with an OpenBCI Cyton board) to try to build a neurofeedback system, starting from the neurofeedback example.

I want to add a feature I've seen in a commercial system. It's a kind of noise gate. When the trainee is moving around, clenching jaw muscles, etc., a large and noisy signal is produced. At this point, the system shuts down the reward signal, and leaves it shut down until it's been quiet again for some period (such as 1 second). Then it goes back to normal operation.

I figured that I could create a gate signal that would be 0 or 1 using a Simple DSP with this expression:

(x > -100 && x < 100) ? 1 : 0

Then I just multiply the reward signal by that. Seems to work. But the gate signal sometimes bounces rapidly between 0 and 1. I don't know how to add the time delay so it stays at 0 for awhile. Any suggestions?

Thanks,
Dave

jtlindgren
Posts: 775
Joined: Tue Dec 04, 2012 3:53 pm
Location: INRIA Rennes, FRANCE

Re: One-shot / debounce / timer box?

Post by jtlindgren »

HI Dave, thats indeed a bit of a puzzle you have there!

Without implementing an actual delay box or such, what I imagine could be done is something like this,

1) Change your gate signal to (-1,1) and then to a stimulation stream using the Sign Change Detector box.
2) Delay the 'toggle on' stimulation with Lua Stimulator box (you'll find a delay example in box-tutorials/lua) while passing the 'Toggle off' immediately
3) Use the stimulus outputs of the Lua Stimulator to control Stream Switch box which will send data to the display only when the gate is not activated.

I have a feeling it might work, but I havent tried it -- let us know how it goes. One problem with it might be that with this design the display will get a noncontinuous stream. :(


Cheers,
Jussi

daves
Posts: 3
Joined: Thu Feb 02, 2017 6:13 pm

Re: One-shot / debounce / timer box?

Post by daves »

Hi Jussi, thanks for the response!

Yesterday I started working with the Python box as a possible solution using pure signal processing. What you're suggesting sounds simpler, I just need to first learn more about how stimulations fit into the overall architecture (have not worked with them yet). If I find a good solution, I'll post it here.

Best,
Dave

daves
Posts: 3
Joined: Thu Feb 02, 2017 6:13 pm

Re: One-shot / debounce / timer box?

Post by daves »

Here's what I've come up with. I couldn't figure out how to use a stimulation stream for this; once the gate is closed, if the signal again exceeds the limits, I need the recovery timer to reset itself (i.e. the gate can only re-open after the signal has been within limits for the full recovery time). Anyway, this seems to do the trick.

I've made no attempt to optimize the performance.

Cheers,
Dave

Code: Select all

import numpy
from datetime import datetime, timedelta

class MyOVBox(OVBox):
	def __init__(self):
		OVBox.__init__(self)
		self.signalHeader = None
		self.timeLastOutside = None # per-channel
		self.gateOpen = None # states also per-channel

	def initialize(self):
		self.minSignal = float(self.setting['minimum signal'])
		self.maxSignal = float(self.setting['maximum signal'])
		self.recoveryTime = float(self.setting['recovery time (milliseconds)']) / 1000.0

	def process(self):
		for iInput, chunks in enumerate(self.input):
			for iChunk, chunk in enumerate(chunks):
				if type(chunk) == OVSignalHeader:
					self.signalHeader = chunks.pop()
					outputHeader = OVSignalHeader(
					self.signalHeader.startTime, 
					self.signalHeader.endTime, 
					self.signalHeader.dimensionSizes, 
					self.signalHeader.dimensionLabels,
					self.signalHeader.samplingRate)
					#print self.signalHeader.dimensionSizes
					#print self.signalHeader.dimensionLabels
					self.output[iInput].append(outputHeader)
					self.nSamples = self.signalHeader.dimensionSizes[1]
					self.nChannels = self.signalHeader.dimensionSizes[0]
					print 'nChannels = %d, nSamples = %d' % (self.nChannels, self.nSamples)
					if not self.timeLastOutside:
						# time each channel was last outside range, init to -1 second
						self.timeLastOutside = [-1] * self.nChannels
					if not self.gateOpen:
						# all gates initially open
						self.gateOpen = [True] * self.nChannels

				elif type(chunk) == OVSignalBuffer:
					chunks.pop()
					#print 'OVSignalBuffer start %s end %s' % (chunk.startTime, chunk.endTime)
					buffer = numpy.array(chunk).reshape(tuple(self.signalHeader.dimensionSizes))
					buffer.flags.writeable = True
					#print buffer[0,0]
					#print buffer[1,0]
					tick = (chunk.endTime - chunk.startTime) / self.nSamples
					for iChannel in range(self.nChannels):
						open = self.gateOpen[iChannel]
						for iSample in range(self.nSamples):
							sample = buffer[iChannel, iSample]
							outside = sample > self.maxSignal or sample < self.minSignal
							sampleTime = chunk.startTime + (tick * iSample)
							if open:
								# todo make this optional - clamp output to an open signal
								buffer[iChannel, iSample] = 1.0
								if outside:
									open = False
									#print 'iChannel %d: out of range value = %f, sample = %s, time = %s' % \
									#	(iChannel, sample, iSample, sampleTime)
									self.timeLastOutside[iChannel] = sampleTime
							else:
								# todo allow value other than 0 to be configured for closed gate
								buffer[iChannel, iSample] = 0.0
								if outside:
									self.timeLastOutside[iChannel] = sampleTime
								else:
									if sampleTime > self.timeLastOutside[iChannel] + self.recoveryTime:
										open = True
						self.gateOpen[iChannel] = open
						#print 'iChannel = %d, gate open = %s, timeLastOutside = %f' % \
						#	(iChannel, open, self.timeLastOutside[iChannel])
					
					outChunk = OVSignalBuffer(chunk.startTime, chunk.endTime, buffer.flatten().tolist())
					self.output[iInput].append(outChunk)
					
				elif type(chunk) == OVSignalEnd:
					self.output[iInput].append(chunks.pop())

box = MyOVBox()

jtlindgren
Posts: 775
Joined: Tue Dec 04, 2012 3:53 pm
Location: INRIA Rennes, FRANCE

Re: One-shot / debounce / timer box?

Post by jtlindgren »

Hi Dave, thanks for posting your solution! Hopefully it will help other people who are trying to develop signal gate -like functionality.


Cheers,
Jussi

Post Reply