Update States in BCPy2000

Forum for software developers to discuss BCI2000 software development
Post Reply
abertomeu
Posts: 5
Joined: 20 Jan 2023, 04:23

Update States in BCPy2000

Post by abertomeu » 20 Jan 2023, 04:57

Hi BCI2000 Community,

I built the current version of BCI2000 (downloaded from TortoiseSVN in December 2022) a I want to use the BCPy2000 to develop an Application.
I developed a simple App code in order to learn about it and Everything seems to go perfect. However, when I read the .dat file generated, the states are not updated according to the current phase (I read the file through Matlab R2019b, using load_bcidat)
The code can be observed here:

Code: Select all

from BCPy2000.AppTools.Displays import fullscreen
from BCPy2000.AppTools.StateMonitors import addstatemonitor, addphasemonitor

class BciApplication(BciGenericApplication):
	def Description(self):
		return "Simple App"
		
	
	def Construct(self):
		self.define_param(
			"PythonApp:Design   int    	InitBaseline=       1     0     0   1  	// record a initial and final 20 seconds baseline (boolean)",
			"PythonApp:Screen   int    	ScreenId=           -1    -1    %   %  	// on which screen should the stimulus window be opened - use -1 for last",
			"PythonApp:Screen   float  	WindowSize=         0.8   1.0   0.0 1.0 // size of the stimulus window, proportional to the screen",
		)

		self.define_state(
			"BaselineOn 	1 0 0 0",
		)
	
	def Preflight(self, sigprops):
		trialsPerBlock = int(self.params['TrialsPerBlock'])
		self.totalBlocks = int(self.params['BlocksPerRun'])

		self.cueCounter = 0
		self.blockCounter = 0
		
		siz = float(self.params['WindowSize'])
		screenid = int(self.params['ScreenId'])
		fullscreen(scale=siz, id=screenid, frameless_window=(siz==1))

	def Initialize(self, indim, outdim):

		import psychopy.visual as psypy	
		
		self.scrw,self.scrh = self.screen.size			

		self.screen.color = (0x00,0x00,0x00)

		self.stimulus('Baseline', z=1, stim=psypy.TextBox(self.screen.screen,text='BASELINE', size=[800,200],pos=(0,0),units='pix',font_size=90, font_color=[1,1,1],grid_horz_justification='center', grid_vert_justification='center'),on=False)
		self.stimulus('StartCue', z=1, stim=psypy.TextBox(self.screen.screen,text='BLOCK STARTS', size=[800,200],pos=(0,0),units='pix',font_size=90, font_color=[1,1,1],grid_horz_justification='center', grid_vert_justification='center'),on=False)
		self.stimulus('StopCue', z=1, stim=psypy.TextBox(self.screen.screen,text='BLOCK ENDS', size=[800,200],pos=(0,0),units='pix',font_size=90, font_color=[1,1,1],grid_horz_justification='center', grid_vert_justification='center'),on=False)
		
		if True:	
			addstatemonitor(self, 'Running', showtime=True)
			addstatemonitor(self, 'CurrentBlock')
			addstatemonitor(self, 'CurrentTrial')
			addphasemonitor(self, 'phase', showtime=True)

			m = addstatemonitor(self, 'fs_reg')
			m.func = lambda x: '% 6.1fHz' % x._regfs.get('SamplesPerSecond', 0)
			m.pargs = (self,)
			m = addstatemonitor(self, 'fs_avg')
			m.func = lambda x: '% 6.1fHz' % x.estimated.get('SamplesPerSecond',{}).get('global', 0)
			m.pargs = (self,)
			m = addstatemonitor(self, 'fs_run')
			m.func = lambda x: '% 6.1fHz' % x.estimated.get('SamplesPerSecond',{}).get('running', 0)
			m.pargs = (self,)
			m = addstatemonitor(self, 'fr_run')
			m.func = lambda x: '% 6.1fHz' % x.estimated.get('FramesPerSecond',{}).get('running', 0)
			m.pargs = (self,)
	
	def StartRun(self):
		pass
		
	def Phases(self):

		self.phase(name='baseline',		duration=5000,		next='stopcue')
		self.phase(name='startcue',		duration=1000,		next='baseline')
		self.phase(name='stopcue',		duration=1000,		next='startcue') 
		
		self.design(start='startcue', new_trial='cue', interblock='idle')
		
	def Transition(self, phase):

		if phase == 'baseline':
			self.stimuli['Baseline'].on = True
		else:
			self.stimuli['Baseline'].on = False

		if phase == 'startcue':
			self.stimuli['StartCue'].on = True
			self.states['CurrentBlock'] += 1
		else:
			self.stimuli['StartCue'].on = False

		if phase == 'stopcue':
			self.stimuli['StopCue'].on = True
		else:
			self.stimuli['StopCue'].on = False			
		
		self.states['BaselineOn'] = int(phase in ['baseline'])
		
	def Process(self, sig):
		pass  # or not.
		
	def Frame(self, phase):
		pass
			
	def Event(self, phase, event):
		pass
			
	def StopRun(self):
		pass
Can anyone help me?

Another problem that I don't know how to solve is how to finish the application without using the button Start, I tried changing the states['Running'] to 0, but it doesn't work.

Thanks in advance!

nluczak
Posts: 3
Joined: 20 Jan 2023, 17:22

Re: Update States in BCPy2000

Post by nluczak » 20 Jan 2023, 18:12

Hello, thank you for using BCPy2000! I am the current lead developer for the project and our team has released a number of new changes and fixes in the last year regarding states and events. We reworked the state machine to allow for per-sample access of states and events, as well as some updates to keep in line with the current BCI2000 programming standards. However, one change now prevents the ability to assign new values to states in the Transition def. To maintain backwards-compatibility, we kept the Transition def in, as it is still useful if you want code to execute exactly once with each phase change. However, you can no longer assign new values to states in the Transition def.

Here is the code you sent, modified to work with our recent changes.

Code: Select all

from BCPy2000.AppTools.Displays import fullscreen
from BCPy2000.AppTools.StateMonitors import addstatemonitor, addphasemonitor
class BciApplication(BciGenericApplication):
	def Description(self):
		return "Simple App"
		
	
	def Construct(self):
		self.define_param(
			"PythonApp:Design   int    	InitBaseline=       1     0     0   1  	// record a initial and final 20 seconds baseline (boolean)",
			"PythonApp:Screen   int    	ScreenId=           -1    -1    %   %  	// on which screen should the stimulus window be opened - use -1 for last",
			"PythonApp:Screen   float  	WindowSize=         0.8   1.0   0.0 1.0 // size of the stimulus window, proportional to the screen",
		)
		self.define_state(
			"BaselineOn 	1 0 0 0",
		)
	
	def Preflight(self, sigprops):
		siz = float(self.params['WindowSize'])
		screenid = int(self.params['ScreenId'])
		fullscreen(scale=siz, id=screenid, frameless_window=(siz==1))
	def Initialize(self, indim, outdim):
		import psychopy.visual as psypy	
		
		self.scrw,self.scrh = self.screen.size			
		self.screen.color = (0x00,0x00,0x00)
		self.stimulus('Baseline', z=1, stim=psypy.TextBox(self.screen.screen,text='BASELINE', size=[800,200],pos=(0,0),units='pix',font_size=90, font_color=[1,1,1],grid_horz_justification='center', grid_vert_justification='center'),on=False)
		self.stimulus('StartCue', z=1, stim=psypy.TextBox(self.screen.screen,text='BLOCK STARTS', size=[800,200],pos=(0,0),units='pix',font_size=90, font_color=[1,1,1],grid_horz_justification='center', grid_vert_justification='center'),on=False)
		self.stimulus('StopCue', z=1, stim=psypy.TextBox(self.screen.screen,text='BLOCK ENDS', size=[800,200],pos=(0,0),units='pix',font_size=90, font_color=[1,1,1],grid_horz_justification='center', grid_vert_justification='center'),on=False)
		if True:	
			addstatemonitor(self, 'Running', showtime=True)
			addstatemonitor(self, 'CurrentBlock')
			addstatemonitor(self, 'CurrentTrial')
			addphasemonitor(self, 'phase', showtime=True)
			m = addstatemonitor(self, 'fs_reg')
			m.func = lambda x: '% 6.1fHz' % x._regfs.get('SamplesPerSecond', 0)
			m.pargs = (self,)
			m = addstatemonitor(self, 'fs_avg')
			m.func = lambda x: '% 6.1fHz' % x.estimated.get('SamplesPerSecond',{}).get('global', 0)
			m.pargs = (self,)
			m = addstatemonitor(self, 'fs_run')
			m.func = lambda x: '% 6.1fHz' % x.estimated.get('SamplesPerSecond',{}).get('running', 0)
			m.pargs = (self,)
			m = addstatemonitor(self, 'fr_run')
			m.func = lambda x: '% 6.1fHz' % x.estimated.get('FramesPerSecond',{}).get('running', 0)
			m.pargs = (self,)
	def StartRun(self):
		self.blockCounter = 0
		
	def Phases(self):
		self.phase(name='baseline',		duration=5000,		next='stopcue')
		self.phase(name='startcue',		duration=1000,		next='baseline')
		self.phase(name='stopcue',		duration=1000,		next='startcue')
		
		self.design(start='startcue')
		
	def Transition(self, phase):
		if phase == 'baseline':
			self.stimuli['Baseline'].on = True
			self.baselineOn = 1
			self.blockCounter += 1
		else:
			self.stimuli['Baseline'].on = False
			self.baselineOn = 0
		if phase == 'startcue':
			self.stimuli['StartCue'].on = True
		else:
			self.stimuli['StartCue'].on = False
		if phase == 'stopcue':
			self.stimuli['StopCue'].on = True
		else:
			self.stimuli['StopCue'].on = False	
		
	def Process(self, sig):
		self.states['CurrentBlock'] = self.blockCounter
		self.states['BaselineOn'] = self.baselineOn
		if self.states['CurrentBlock'] >= 3: #You can also use (self.states['CurrentBlock', :]) for per-sample access
			self.states['Running'] = 0       # for example, if you wanted to check each sample in a block you could use
											 # (self.states['CurrentBlock',:]).any() or .all()
											 # you could also access individual samples
											 # self.states['CurrentBlock', 0] would access the first sample in the block
			
	def StopRun(self):
		pass


abertomeu
Posts: 5
Joined: 20 Jan 2023, 04:23

Re: Update States in BCPy2000

Post by abertomeu » 24 Jan 2023, 09:04

Hello nluczak, that's good news and good luck with BCI2000.
Thank you very much for your quick response, I have already tried it and it works perfectly, problem solved! :D

Post Reply

Who is online

Users browsing this forum: No registered users and 38 guests