class TransmitFeatures(DeviceBase):#, QtGui.QWidget): def __init__(self, stream = None, parent = None, **kargs): DeviceBase.__init__(self, **kargs) #QtGui.QWidget.__init__(self, parent) def configure( self, name = 'Test dev', nb_channel = None, nb_pts = None, nb_feature = 4, sampling_rate =1., digital_port = None, buffer_length= 10., packet_size = 1, ): self.params = {'name' : name, 'nb_channel' : nb_channel, 'nb_pts' : nb_pts, 'nb_feature' : nb_feature, 'digital_port' : digital_port, 'buffer_length' : buffer_length, 'sampling_rate' : sampling_rate, 'packet_size' : packet_size, } self.__dict__.update(self.params) self.configured = True def initialize(self, stream_in, stream_xy): ## Feature stuff #self.feature_names = ['DeltaMean','ThetaMean','AlphaMean','BetaMean','GammaMean','MuMean', 'DeltaMean2','ThetaMean2','AlphaMean2','BetaMean2','GammaMean2','MuMean2'] #self.feature_names = ['DeltaMean2','ThetaMean2','AlphaMean2','BetaMean2','GammaMean2','MuMean2', 'pAlphaO12', 'alpha_cumul', 'alpha_smooth', 'alpha_smooth2', 'pThetaAF34F34', 'pBetaF34', 'R1', 'R2', 'R3', 'R5', 'R6', 'R7', 'R8', 'R9', 'meanKurto'] #self.feature_names = ['alpha_smooth', 'alpha_cumul', 'blink', 'Cristpation'] self.feature_names = ['alpha_smooth','blink', 'Cristpation', 'Beta_contrib', 'Teta_contrib', 'Mu_contrib', 'mean_blink_comp', 'sacc_feat', 'mean_sacc_comp', 'contrib_alpha_O1O2', 'R_engage', 'frontal_theta'] self.feature_indexes = np.arange(self.nb_feature) self.channel_names = [ 'F3', 'F4', 'P7', 'FC6', 'F7', 'F8','T7','P8','FC5','AF4','T8','O2','O1','AF3'] self.channel_indexes = range(self.nb_channel) self.extractor = GetFeatures.GetFeatures(stream_in) ## OSC socket #self.oscIP = '192.168.0.27' # moi : 37.0.0.100 self.oscIP = '37.0.0.104' # je self.oscPort = 9001 self.oscClient = OSC.OSCClient() self.oscMsg = OSC.OSCMessage() self.oscMsg.setAddress("/EEGfeat") self.oscMsgXY = OSC.OSCMessage() self.oscMsgXY.setAddress("/GyroXY") ## Stream In self.stream_in = stream_in self.stream_xy = stream_xy ## Socket In (SUB) self.context = zmq.Context() self.socket_in = self.context.socket(zmq.SUB) self.socket_in.setsockopt(zmq.SUBSCRIBE,'') self.socket_in.connect("tcp://localhost:{}".format(self.stream_in['port'])) ## Data flux self.np_arr_in = self.stream_in['shared_array'].to_numpy_array() self.half_size_in = self.np_arr_in.shape[1]/2 self.sr_in = self.stream_in['sampling_rate'] self.packet_size_in = self.stream_in['packet_size'] ##XY flux self.np_arr_xy = self.stream_xy['shared_array'].to_numpy_array() self.thread_pos = RecvPosThread(socket = self.socket_in, port = self.stream_in['port']) self.thread_pos.start() #print np.shape(self.np_arr_in) print 'Stream In initialized:', self.stream_in['name'], ', Input port: ', self.stream_in['port'], ', Sampling rate: ', self.sr_in ## Stream Out l = int(self.sampling_rate*self.buffer_length) self.buffer_length = (l - l%self.packet_size)/self.sampling_rate self.stream_out = self.streamhandler.new_AnalogSignalSharedMemStream(name = self.name, sampling_rate = self.sampling_rate, nb_channel = self.nb_feature, buffer_length = self.buffer_length, packet_size = self.packet_size, dtype = np.float64, channel_names = self.feature_names, channel_indexes = self.feature_indexes, ) arr_size = self.stream_out['shared_array'].shape[1] assert (arr_size/2)%self.packet_size ==0, 'buffer should be a multilple of packet_size {}/2 {}'.format(arr_size, self.packet_size) self.name_stream_out = 'Stream out' self.sr_out = float(self.sampling_rate) self.np_arr_out = self.stream_out['shared_array'].to_numpy_array() ## Socket Out self.socket_out = self.context.socket(zmq.PUB) self.socket_out.bind("tcp://*:{}".format(self.stream_out['port'])) # Could declare other streams if needed self.streams = [self.stream_out] #print np.shape(self.np_arr_out) print 'Stream Out initialized:', self.name, ', Output port: ', self.stream_out['port'] , ', Sampling rate: ', self.sr_out ## Writer counts self.pos = 0 self.abs_pos = self.pos2 = 0 ## Method to be loopely executed by Qthread #self.thread_send = SendPosThread(fonction = self.simple_transmit) self.thread_copy = SendPosThread(fonction = self.transmit) self._goOn = True def start(self): self.stop_flag = mp.Value('i', 0) #flag pultiproc = global # Wait for input entries time.sleep(0.5) print self.thread_pos.pos self.last_head = (self.thread_pos.pos)%self.half_size_in self.last_head2 = self.last_head print 'first last head : ', self.last_head #self.thread_send.start() self.thread_copy.start() print 'Transmition Started:', self.name self.running = True def stop(self): self.stop_flag.value = 1 self.thread_copy.running = False self.thread_copy.exit() def close(self): self._goOn = False def simple_transmit(self): pos = self.thread_pos.pos self.socket_out.send(msgpack.dumps(pos)) time.sleep(0.007) # 1/Fe in s def transmit(self): t_in = time.time() ## Read what's comin' pos_in = self.thread_pos.pos # pos absolue half_size_in = self.half_size_in head = pos_in%half_size_in # pos relative data = self.np_arr_in[:, head+half_size_in-self.nb_pts : head+half_size_in] #print 'head : ', head ## Compute features features = self.extractor.extract_TCL(head) ## Write out and send position pos2 = self.pos2 half_size = self.np_arr_out.shape[1]/2 self.np_arr_out[:,pos2:pos2+ self.packet_size] = features.reshape(self.nb_feature,self.packet_size) self.np_arr_out[:,pos2+half_size:pos2+self.packet_size+half_size] = features.reshape(self.nb_feature,self.packet_size) #print 'pos2 : ', pos2 self.pos += self.packet_size self.pos = self.pos%self.np_arr_out.shape[1] self.abs_pos += self.packet_size self.pos2 = self.abs_pos%half_size self.socket_out.send(msgpack.dumps(self.abs_pos)) #send OSC #print "paquet send" self.sendOSC(features, self.oscPort) # gyro data dataxy = self.np_arr_xy[:, head+half_size_in] self.sendOSC(dataxy, 9002) t_out = time.time() ## Simulate sample rate out #t_wait = 1/self.sr_out - (t_out - t_in) #print 't wait :', t_wait #if t_wait > 0: # time.sleep(t_wait) #else: # print 'Output stream sampling rate too fast for calculation' #self.stop() time.sleep(1/self.sr_out) def sendOSC(self,features, port): self.oscMsg.append(features) self.oscClient.sendto(self.oscMsg, (self.oscIP, port)) self.oscMsg.clearData() #try: # self.oscClient.sendto(message, (self.oscIP, self.oscPort)) #except: # print 'Connection refused' #pass ## For simple stream transmetter def sendOSCxy(self,dataxy): print "msgsend" self.oscMsg.append(dataxy) self.oscClient.sendto(self.oscMsgXY, (self.oscIP, 9002)) self.oscMsg.clearData() def simple_copy_and_transmit(self): t_in = time.time() pos = self.thread_pos.pos half = self.half_size_in head = pos%half #head2 = pos%(half+1) print 'last head: ', self.last_head, ' head: ', head #if self.last_head != head: # Copy data #self.np_arr_out[:,self.last_head2:head2] = self.np_arr_in[:,self.last_head+half:head+half] self.np_arr_out[:,self.last_head:head+half] = self.np_arr_in[:,self.last_head:head+half] #self.np_arr_out[:,self.last_head2+half:head2+half] = self.np_arr_in[:,self.last_head+half:head+half] self.socket_out.send(msgpack.dumps(pos)) ## Debug mode. use head-1 instead head caus' send pos is out of the real data written (numpy way to use tables) #print 'Value write on pos ', head-1, ' array in: ', self.np_arr_in[1,head-1], ' array out: ', self.np_arr_out[1,head-1] #if self.np_arr_in[1,head-1] != self.np_arr_out[1,head-1]: # print 'Error writing array out pos = ', pos self.last_head = head #self.last_head2 = head2 t_out = time.time() t_wait = 1/self.sr_out - (t_out - t_in) #print 't wait :', t_wait if t_wait > 0: time.sleep(t_wait) # print 'sleep' else: # print 'Output stream sampling rate too fast for calculation' self.stop()
class Topoplot(QtGui.QWidget): def __init__(self, stream = None, parent = None, type_Topo = 'topo'): QtGui.QWidget.__init__(self, parent) assert type(stream).__name__ == 'AnalogSignalSharedMemStream' self.stream = stream self.context = zmq.Context() self.socket = self.context.socket(zmq.SUB) self.socket.setsockopt(zmq.SUBSCRIBE,'') self.socket.connect("tcp://localhost:{}".format(self.stream['port'])) self.thread_pos = RecvPosThread(socket = self.socket, port = self.stream['port']) self.thread_pos.start() # Create parameters n = stream['nb_channel'] self.np_array = self.stream['shared_array'].to_numpy_array() self.half_size = self.np_array.shape[1]/2 sr = self.stream['sampling_rate'] if type_Topo == 'topo': self.imgLevels = (8000,9000) self.iblack = 0 self.colormap = "jet" if type_Topo == 'imp': self.imgLevels = (0,15) self.iblack = 0 self.colormap = "winter" if type_Topo == 'fakeAc': self.imgLevels = (-100,100) self.iblack = 500 self.colormap = "jet" # Matrix initialisation self.data = np.zeros((6,6), dtype=np.int) self.chanName = [ 'F3', 'F4', 'P7', 'FC6', 'F7', 'F8','T7','P8','FC5','AF4','T8','O2','O1','AF3'] self.chanPosX=[2,3,1,4,0,5,0,4,1,4,5,3,2,1] self.chanPosY=[5,5,2,4,5,5,3,2,4,6,3,1,1,6] #Graphical instances self.mainlayout = QtGui.QVBoxLayout() self.setLayout(self.mainlayout) self.win = pg.GraphicsWindow() self.win.setWindowTitle('pyqtgraph example: Isocurve') self.vb = self.win.addViewBox() self.img = pg.ImageItem(self.data) self.vb.addItem(self.img) self.vb.setAspectLocked() self.mainlayout.addWidget(self.win) # Set the sensor names self.setNameChan() # colormap self.lut = [ ] self.cmap = get_cmap(self.colormap , 1000) for i in range(1000): r,g,b = ColorConverter().to_rgb(self.cmap(i) ) self.lut.append([r*255,g*255,b*255]) self.jet_lut = np.array(self.lut, dtype = np.uint8) # associate black to the rest of the matrix (where there is no chan) self.jet_lut[self.iblack,:] = [0,0,0] self.timer = QtCore.QTimer(interval = 100) self.timer.timeout.connect(self.refresh) self.timer.start() self.img.setImage(self.data, lut = self.jet_lut, levels=self.imgLevels) def setNameChan(self): for i in range(0,14): self.txt = pg.TextItem(self.chanName[i]) self.txt.setPos(self.chanPosX[i],self.chanPosY[i]) self.vb.addItem(self.txt) def refresh(self): self.getChanMatrix() self.img.setImage(self.data, lut = self.jet_lut, levels=self.imgLevels) def getChanMatrix(self): pos = (self.thread_pos.pos - 1)%self.half_size dataChan = self.np_array[:,pos] # commence en bas a gauche a (0,0), dim1 = g, dim2 = haut self.data[1,5] = dataChan[13] # AF3 self.data[4,5] = dataChan[9] # AF4 self.data[0,4] = dataChan[4] # F7 self.data[2,4] = dataChan[0] # F3 self.data[3,4] = dataChan[1] # F4 self.data[5,4] = dataChan[5] # F8 self.data[1,3] = dataChan[8] # FC5 self.data[4,3] = dataChan[3] # FC6 self.data[0,2] = dataChan[6] # T7 self.data[5,2] = dataChan[10] # T8 self.data[1,1] = dataChan[2] # P7 self.data[4,1] = dataChan[7] # P8 self.data[2,0] = dataChan[12] # O1 self.data[3,0] = dataChan[11] # O2
class GraphicsWorker: ''' /!\Cette classe n'est jamais instanciée directement. Elle contient le code qui connecte le worker à un port, le code qui met à jour les positions des informations à lire (self.init). Les classes qui en héritent doivent surcharger initPlots (mise en place des graphs dans la fenêtre) et updatePlots (mise à jour des graphiques). Les flux lus sont des flux de positions fournis par pyacq. Les paramètres sont: Le flux à lire. La taille de la fenêtre temporelle traitée, en seconde. (/!\Des comportements étranges peuvent apparaître dans le cas où cette taille proche de la taille du buffer dans lequel le flux est écrit.) Les canaux à traiter. ''' def __init__(self,stream,time_window,channels=None): #FIXME : vérifier le type du stream #Définition de la zone de mémoire à lire self.shared_array=stream['shared_array'].to_numpy_array() self.half_size=self.shared_array.shape[1]/2 #Choix des canaux à traiter (tous par défaut) if channels==None: self.channels=np.arange(self.shared_array.shape[0]) else: self.channels=channels #Définition de la fenêtre temporelle traitée self.time_window=time_window self.init=self.half_size self.interval_length=int(time_window*stream['sampling_rate']) self.data=self.shared_array[:,self.init:self.init+self.interval_length] #initialisation de la connexion self.port=stream['port'] self.context=zmq.Context() self.socket=self.context.socket(zmq.SUB) self.socket.connect("tcp://localhost:%d"%self.port) self.socket.setsockopt(zmq.SUBSCRIBE,'') #initialisation de la pile de réception des positions self.threadPos=RecvPosThread(socket=self.socket,port=self.port) self.threadPos.start() #initialisation du timer de rafraichissement du graphe self.timer=QtCore.QTimer() self.timer.timeout.connect(self.updateGW) #periode de rafraichissement de l'image, ms self.period=100 def initPlots(self): raise NotImplementedError def run(self): self.initPlots() self.timer.start(self.period)#taux de rafraichissement de l'image def updatePlots(self): raise NotImplementedError def updateGW(self): #Réception de la position de la dernière information écrite t=self.threadPos.pos #print t,' shared_array ',self.shared_array self.init=t%(self.half_size)+self.half_size #mise à jour de la fenêtre de lecture self.data=self.shared_array[:,self.init-self.interval_length:self.init] #mise à jour du graph self.updatePlots()
class Topoplot_imp(QtGui.QWidget): def __init__(self, stream = None, parent = None, type_Topo = 'topo'): QtGui.QWidget.__init__(self, parent) assert type(stream).__name__ == 'AnalogSignalSharedMemStream' self.stream = stream self.context = zmq.Context() self.socket = self.context.socket(zmq.SUB) self.socket.setsockopt(zmq.SUBSCRIBE,'') self.socket.connect("tcp://localhost:{}".format(self.stream['port'])) self.thread_pos = RecvPosThread(socket = self.socket, port = self.stream['port']) self.thread_pos.start() # Create parameters n = stream['nb_channel'] self.nb_channel = n self.np_array = self.stream['shared_array'].to_numpy_array() self.half_size = self.np_array.shape[1]/2 sr = self.stream['sampling_rate'] if type_Topo == 'topo': self.imgLevels = (7500,9500) self.iblack = 0 self.colormap = "jet" if type_Topo == 'imp': self.imgLevels = (0,15) self.iblack = 0 self.colormap = "winter" if type_Topo == 'fakeAc': self.imgLevels = (-100,100) self.iblack = 500 self.colormap = "jet" # Matrix initialisation self.data = np.zeros((6,6), dtype=np.int) self.chanName = [ 'F3', 'F4', 'P7', 'FC6', 'F7', 'F8','T7','P8','FC5','AF4','T8','O2','O1','AF3'] self.chanPosX=[2,3,1,4,0,5,0,4,1,4,5,3,2,1] self.chanPosY=[5,5,2,4,5,5,3,2,4,6,3,1,1,6] #Graphical instances self.mainlayout = QtGui.QVBoxLayout() self.setStyleSheet("background-color:black;"); self.setLayout(self.mainlayout) self.win = pg.GraphicsWindow() self.win.setWindowTitle('pyqtgraph example: Isocurve') self.vb = self.win.addViewBox() self.img = pg.ImageItem(self.data) self.vb.addItem(self.img) self.vb.setAspectLocked() self.mainlayout.addWidget(self.win) #window option self.setWindowTitle('Impedances') #~ self.resize(1200, 1768) #~ self.move(7034, 0) self.resize(800, 600) self.move(700, 200) self.osc_setup() # Set the sensor names self.setNameChan() # colormap self.lut = [ ] self.cmap = get_cmap(self.colormap , 2000) for i in range(2000): r,g,b = ColorConverter().to_rgb(self.cmap(i) ) self.lut.append([r*255,g*255,b*255]) self.jet_lut = np.array(self.lut, dtype = np.uint8) # associate black to the rest of the matrix (where there is no chan) self.jet_lut[self.iblack,:] = [0,0,0] self.timer = QtCore.QTimer(interval = 100) self.timer.timeout.connect(self.refresh) self.timer.start() self.img.setImage(self.data, lut = self.jet_lut, levels=self.imgLevels) def setNameChan(self): for i in range(0,14): self.txt = pg.TextItem(self.chanName[i], color = (0,0,0)) self.txt.setPos(self.chanPosX[i],self.chanPosY[i]) self.vb.addItem(self.txt) def refresh(self): self.getChanMatrix() self.img.setImage(self.data, lut = self.jet_lut, levels=self.imgLevels) #envoi des impedances en osc a Jonas self.send_osc() def getChanMatrix(self): pos = (self.thread_pos.pos - 1)%self.half_size dataChan = self.np_array[:,pos] # commence en bas a gauche a (0,0), dim1 = g, dim2 = haut self.data[1,5] = dataChan[13] # AF3 self.data[4,5] = dataChan[9] # AF4 self.data[0,4] = dataChan[4] # F7 self.data[2,4] = dataChan[0] # F3 self.data[3,4] = dataChan[1] # F4 self.data[5,4] = dataChan[5] # F8 self.data[1,3] = dataChan[8] # FC5 self.data[4,3] = dataChan[3] # FC6 self.data[0,2] = dataChan[6] # T7 self.data[5,2] = dataChan[10] # T8 self.data[1,1] = dataChan[2] # P7 self.data[4,1] = dataChan[7] # P8 self.data[2,0] = dataChan[12] # O1 self.data[3,0] = dataChan[11] # O2 def osc_setup(self,ip='192.168.0.105', port=55555): self.brain_ip = ip self.brain_port = port self.client = OSC.OSCClient() self.client.connect((ip,port)) self.bundle = OSC.OSCBundle() self.bundle.setAddress("/led") def send_osc(self): led_colors = self.getLedColors() self.bundle.append(led_colors) self.client.send(self.bundle) self.bundle.clearData() def normalize(self,val_list,min_=0,max_=15): normalized_params = (val_list - min_) / (max_ - min_) return normalized_params def getLedColors(self): pos = (self.thread_pos.pos - 1)%self.half_size data = self.np_array[:,pos] normalized_data = self.normalize(data) led_colors = np.zeros(shape=(3*self.nb_channel), dtype = 'int') led_index = [_channel_order.index(id) for id in _led_order] led_colors[2::3] = (normalized_data[led_index] *255).astype('int') return led_colors
class Topoplot(QtGui.QWidget): def __init__(self, stream = None, parent = None,): QtGui.QWidget.__init__(self, parent) assert stream['type'] == 'signals_stream_sharedmem' self.stream = stream self.context = zmq.Context() self.socket = self.context.socket(zmq.SUB) self.socket.setsockopt(zmq.SUBSCRIBE,'') self.socket.connect("tcp://localhost:{}".format(self.stream['port'])) self.thread_pos = RecvPosThread(socket = self.socket, port = self.stream['port']) self.thread_pos.start() self.mainlayout = QtGui.QVBoxLayout() self.setLayout(self.mainlayout) #self.graphicsview = pg.GraphicsView()#useOpenGL = True) #self.mainlayout.addWidget(self.graphicsview) #Animation : compute surface vertex data self.w = gl.GLViewWidget() self.w.setWindowTitle('topo en cours') self.w.setCameraPosition(distance=30) self.mainlayout.addWidget(self.w) #self.graphicsview.setCentralWidget(self.w) # Create parameters n = stream['nb_channel'] self.np_array = self.stream['shared_array'].to_numpy_array() self.half_size = self.np_array.shape[1]/2 sr = self.stream['sampling_rate'] ## Animated example ## compute surface vertex data cols = 90 rows = 100 x = np.linspace(-8, 8, cols+1).reshape(cols+1,1) y = np.linspace(-8, 8, rows+1).reshape(1,rows+1) d = (x**2 + y**2) * 0.1 d2 = d ** 0.5 + 0.1 ## precompute height values for all frames phi = np.arange(0, np.pi*2, np.pi/20.) print phi self.z = np.sin(d[np.newaxis,...] + phi.reshape(phi.shape[0], 1, 1)) / d2[np.newaxis,...] ## create a surface plot, tell it to use the 'heightColor' shader ## since this does not require normal vectors to render (thus we ## can set computeNormals=False to save time when the mesh updates) self.p1 = gl.GLSurfacePlotItem(x=x[:,0], y = y[0,:], shader='heightColor', computeNormals=False, smooth=False) self.p1.shader()['colorMap'] = np.array([0.2, 2, 0.5, 0.2, 1, 1, 0.2, 0, 2]) self.w.addItem(self.p1) self.index = 1 self.timer = QtCore.QTimer(interval = 100) self.timer.timeout.connect(self.refresh) self.timer.start() def refresh(self): #global p4, z, index z = self.z self.index -= 1 self.p1.setData(z=z[self.index%z.shape[0]]) #print self.np_array[1,self.thread_pos.pos%self.half_size+self.half_size]