BCPy2000 is a software project which allows a platform for rapid, flexible development of experimental brain-computer interface systems. It is based on, and hosted by, the BCI2000 project. It differs in that this implementation is based in Python 3.6, taking advantage of various high-level packages: Psychopy for stimulus presentation, and NumPy and SciPy for signal processing and classification. Because it is based on the BCI2000 system, it is modular, comprising of an application module which presents the stimuli, a signal source module which waits for data to come in from hardware and to then send these blocks of data to the signal processing module while synchronizing to the hardware clock, and a signal processing module which acts as an intermediate between the signal source and the application. Each of these modules are independent of the others, allowing for you to pick and choose which modules you wish to run as Python modules or BCI2000 compiled modules, for example you can use BCI2000's Signal Source module which supports a wide range of EEG acquisition modules. BCPy2000 is made in mind for developers as well as clinicians with some experience in Python. This project aims to allow you to build your own experiments and modules based on your own python code tailored for your needs. We provide a demo implementation which exemplifies some higher level Python tools which will give you a good idea of where to start.
For this project you will need two things, our portable Python package, and BCI2000. You can find the 32 bit version of our portable Python here and the 64 version [here.] BCPy2000 will be compiled automatically with other BCI2000 programs. Compilation is only supported in BCI2000 with the Visual Studio or MinGW compilers. You can find these instructions at Programming_Howto:Building_BCI2000. Make sure that when you are configuring BCI2000 in CMake, you choose the "BUILD_BCPY2000" and "BUILD_CONTRIB" flags. Once you have done this you can configure and generate your BCI2000 solution.
Once you are in Visual Studio, you can edit the Python Application, Signal Processing, and Source modules and build them individually or all together (note that when you first open this project you will want to build the entire solution first). All three modules share a filer and wrapper classes classes which allow Python functions to be loaded dynamically from a DLL at runtime. These files are where you will be able to make any necessary customization of the interface between the BCI2000 C++ code and the Python code should you want to. After building the application, source, and signal processing modules, then, from your main BCI home directory you will need to navigate to src\contrib\BCPy2000\demo and copy the contents of the batch, data, parms, prog, and python folders into each corresponding folder in the main directory. The next step in the configuration process is to configure Python as a system variable. First you will want to unzip one of our Portable Python packages. Make sure that you have chosen either the 32 or 64 bit version of Python based on how you built your BCI2000 installation. Now that you have unzipped the portable Python package, you will need to set it’s file directory’s PATH in the System Variables settings menu which you can access by searching "System Variables" after pressing the Windows key. Add a new Path Called PYTHONHOME_BCI2000 so as to not have it interfere with any other Python installations you may already have installed. The portable Python package is where you will find all of your Python dependencies, including the BCPy2000 Python code as well as many other high level Python packages. This is where you will do most of your customization. Under the Python site packages you will find important files pertaining to our implementation under the BCPy2000 folder, and the Psychopy folder. You can also add new packages to the portable python version by going into the command prompt, changing your local directory to Python’s directory and installing any new packages by using pip, Python’s native package installer and manager. Now that you have properly installed all of the necessary components for Python we can go into how to implement your own experiment.
Once you have set up your portable Python package and have compiled, built, and copied the executables to each corresponding folder you can run a batch file. We included a triangle application which asks the user to imagine moving their hands or feet in order to guide a cursor into one of three corners of a triangle while simultaneously giving the user color and sound feedback which corresponds to an imagination task and a sound. To open the triangle application you need to open the Triangle application batch file "PythonDemo1_Triangle_p3.bat." Once opened you should first see the operator window which allows you to set configurations, run, and suspend the running of the application. You can edit a myriad of variables and customize the experiment in the configuration menu. Under the visualize you can allow for the BCI2000 operator do show timing and raw brain signal. Another important section is the source section in which you can define number of channels, sampling rate, and block size along with many other important source generation configurations. Under data you can edit the specifics of how data is stored and where it is stored. You can also change how the Python application and signal processing modules are configured. One useful flag is the "show signal time" modifier which allows for developers to monitor several states during runtime. You can also save any changes that you made to the configuration by hitting "save configuration" and saving it into the /parms/ folder. Once you are happy with your configuration you can hit "set config" which will initialize the application, and then "start run" which will start the stimuli.
self.params is a dictionary of parameters, indexed by parameter name. Parameter values are stored as strings, or as lists of strings (for list parameters) or nested lists of strings (for matrix parameters). Numeric values can be obtained by the appropriate call to int or float -- for example: ch_ind = map(int, self.params['TransmitChList'])
self.states is a dictionary of the current values of the BCI2000 state variables, indexed by name and returning values of type int.
The first thing which you see at the top of the file are the imports. These are the packages necessary for a wide variety of functionality including the BCPy2000 interface, the Psychopy visuals, native timing, sound, and complex numerical calculations. The structure of each BCPy2000 Python application is composed of one class containing 9 separate definitions.
This is simply there for you to give a short description of what the application is.
The construct defines all of the parameters and states which you want to record and store for your application. Parameters are the things which comprise our application’s functionality. States are essentially event markers. Each state reflects important events during our real-time system operation and are saved to the data file with each associated sample.
Here we set up our screen with which we will later create our visual stimuli on. We also specify things like our screen size, screen ID, and instantiate the actual screen.
This is where we set up stimuli. First we import psychopy and the BCPy2000/Psychopy renderer. This is important to import here so that openGL, which is how psychopy renders stimuli, is executed in the correct thread. We also draw any stimuli(visual, and auditory) and then register all of those stimuli in this definition. We also can add state monitors here which allow for us to have the option for developers to ensure that the application is doing what they want it to do. Developers can use these statemonitors to check a myriad of internal components (states) such as the current block of data that is being written, the current trial, the samples per second, and the frames per second.
This starts the visual stimulus and is called when the start button is pressed. This is where you want to set your stimuli's on state to true.
This is where we define phases using calls to self.phase and self.design. These phases correspond to the states which we set up in the constructor definition. Here we can set the duration and name of each phase as well as what the next phase is supposed to be.
This presents stimuli and updates state variables to record what is going on.
This processes each new signal packet. Which will be recorded in the experiment’s data file.
This is called when we either close the application or reconfigure it. This is where you set all of your states to their off state.