Jump to content

User Tutorial:BCI2000Remote

From BCI2000 Wiki

Description

BCI2000Remote is a proxy interface class to the BCI2000 Operator module, and allows to start up, configure, and control BCI2000 from other applications. Internally, it maintains a telnet connection to the Operator module, and sends Operator Scripting commands to control it. However, no knowledge of these scripting commands is required in order to use the BCI2000Remote class from your own application.

BCI2000Remote is most useful when writing applications in C++, or in another language for which bindings to the BCI2000RemoteLib library exist, such as Python, or MATLAB.

Note

BCI2000Remote is a means of interfacing with the BCI2000 Operator module meaning that there are potentially hundreds of ways we leverage this tool to have a meaningful interface with BCI2000. Because of this we will illustrate a few different integrations using different languages. This "How-to" will be separated into three separate sections: Python, C++, and Matlab.

Python Tutorial

  1. Find your Python folder. If you have an existing Python environment with your code find it using the Sys Library like this:
    import sys
    
    locate_python = sys.exec_prefix
    print(locate_python)
    
  2. Locate your BCI2000Remote.py file which should have compiled into your prog folder.
  3. Find where in your Python code you wish to integrate BCI2000. In this example I will show the minimum requirements for integration with BCI2000:
    import sys
    sys.path.append('C:\\BCI2000\\prog')
    import BCI2000Remote
    bci = BCI2000Remote.BCI2000Remote()
    //print('Operator path:', bci.OperatorPath)
    bci.WindowVisible = True
    bci.WindowTitle = 'Python controlled'
    bci.SubjectID = 'pysub'
    bci.Connect()
    bci.Execute('cd ${BCI2000LAUNCHDIR}')
    bci.Execute('ADD STATE score 16 0')
    bci.StartupModules(('SignalGenerator', 'DummySignalprocessing', 'DummyApplication'))
    bci.Execute('Wait for Connected')
    bci.SetConfig()
    

    Here is how you import BCI2000Remote:

  4. Here is how you instantiate the operator object:
  5. Here is how to customize the operator and connect to it:
  6. Here is how you add your states:
  7. How to specify your signal source, signal processing, and application modules:
  8. How to connect and issue commands to BCI2000:
  9. And finally, how to tell BCI2000 to set states to a value during runtime:
  10. Now that you've added those components to your code all you have to do is run your code. You should see the BCI2000 operator along with the system log, source watcher, and the timing window. File:BCIRemote 7.jpg

C++ Tutorial

  1. Find your BCI2000RemoteLib dynamic library in your prog folder
  2. Add your Operator executable to the same directory as the program you intend to integrate
  3. Make sure that you include all of your dependencies including BCI2000Remote.h
    #include "BCI2000Remote.h"
    #include <string>
    #include <vector>
    #include <iostream>
    
  4. Next, instantiate the BCI2000Remote object
    int main( int argc, char* argv[] )
    {
      // Instantiate a BCI2000Remote object
      BCI2000Remote bci;
      // Assume that Operator executable resides in the same directory as this program.
      std::string path = ( argc > 0 ) ? argv[0] : "";
      size_t pos = path.find_last_of( "\\/" );
      path = ( pos != std::string::npos ) ? path.substr( 0, pos + 1 ) : "";
      // Start the Operator module, and connect
      bci.OperatorPath( path + "Operator" );
      if( !bci.Connect() )
      {
        std::cerr << bci.Result();
        return -1;
      }
    
  5. Specify your startup modules and any flags you wish to start them with
      // Startup modules
      const char* modules[] = { "SignalGenerator --LogMouse=1", "ARSignalProcessing", "CursorTask" };
      std::vector<std::string> vModules( &modules[0], &modules[0] + sizeof( modules ) / sizeof( *modules ) );
      if( !bci.StartupModules( vModules ) )
      {
        std::cerr << bci.Result();
        return -1;
      }
    
  6. Load any parameter files and set subject information
    bci.LoadParametersRemote( "../parms/examples/CursorTask_SignalGenerator.prm" );
      bci.SubjectID( "SUB" );
      // Start a run
      if( !bci.Start() )
      {
        std::cerr << bci.Result();
        return -1;
      }
    
  7. And here we just have BCI2000 send us back a feedback signal:
      std::string state;
      while( bci.GetSystemState( state ) && state == "Running" )
      {
        double value = 0;
        bci.GetControlSignal( 1, 1, value );
        std::cout << "Control signal: " << value << ", press Enter to proceed" << std::flush;
        std::string line;
        std::getline( std::cin, line );
      }
      return 0;
    }
    
  8. Now that you've added those components to your code all you have to do is compile and run it. You should see the BCI2000 operator along with the system log, source watcher, and the timing window. File:BCIRemote 7.jpg
#include "BCI2000Remote.h"
#include <string>
#include <vector>
#include <iostream>

int main( int argc, char* argv[] )
{
  // Instantiate a BCI2000Remote object
  BCI2000Remote bci;
  // Assume that Operator executable resides in the same directory as this program.
  std::string path = ( argc > 0 ) ? argv[0] : "";
  size_t pos = path.find_last_of( "\\/" );
  path = ( pos != std::string::npos ) ? path.substr( 0, pos + 1 ) : "";
  // Start the Operator module, and connect
  bci.OperatorPath( path + "Operator" );
  if( !bci.Connect() )
  {
    std::cerr << bci.Result();
    return -1;
  }
  // Startup modules
  const char* modules[] = { "SignalGenerator --LogMouse=1", "ARSignalProcessing", "CursorTask" };
  std::vector<std::string> vModules( &modules[0], &modules[0] + sizeof( modules ) / sizeof( *modules ) );
  if( !bci.StartupModules( vModules ) )
  {
    std::cerr << bci.Result();
    return -1;
  }
  // Load a parameter file, and set subject information
  bci.LoadParametersRemote( "../parms/examples/CursorTask_SignalGenerator.prm" );
  bci.SubjectID( "SUB" );
  // Start a run
  if( !bci.Start() )
  {
    std::cerr << bci.Result();
    return -1;
  }
  // Print feedback signal
  std::string state;
  while( bci.GetSystemState( state ) && state == "Running" )
  {
    double value = 0;
    bci.GetControlSignal( 1, 1, value );
    std::cout << "Control signal: " << value << ", press Enter to proceed" << std::flush;
    std::string line;
    std::getline( std::cin, line );
  }
  return 0;
}

Video

PLACEHOLDER

Also see the PsychoPy page for more details on the installation process, APIs, and hooks.


See also