Backend Overview

Initialization

The Backend’s initialization starts in Backend.__init__(). This executes when the Backend object is created in python/src/main.py. Note that the instance variables initialized in __init__() must be pickleable since the the Backend object is pickled when it becomes a new process. Instance variables can also be initialized at the beginning of Backend.run() to avoid this issue.

Here’s a brief description of some of the important instance variables in the Backend class in the order they are defined:

  • current_command: The current Command that is being processed by the Backend.

  • handlers: A dict mapping Command to Backend handler methods.

  • settings: The GeneralSettings object used to hold some basic settings.

  • output_settings: The OutputDataSettings object holding settings related to decoder output, such as latching.

  • feat_settings: The FeatureSettings object holding settings related to decoder input features, such as the state and feature history.

  • features: Current list of Feature objects used to compute the feature input to the decoder.

  • autoencoder: The current autoencoder, which is a type of Feature essentially.

  • decoder: The current movement intent decoder object, which we can train and test, among other functionalities.

  • trainer: The current Trainer object, which specifies settings related to training.

  • collector: The current Collector object, which specifies settings related to collecting raw data.

  • tester: The current Tester object, which specifies settings related to testing a decoder.

  • results: Current list of Result objects, which are used to compute test results such as MSE.

  • data: The current Data object, which holds the input and output of the decoder.

  • test_results: The actual test results which were computed using the self.results list of Result objects.

There are still many more instance variables defined, many of which are very important as well.

Run Loop

The Backend process’ run loop is contained in Backend.run(). This function first initializes logging, gpu support, movement detectors, socket-related events and queues, starts all the threads, and then enters the process’ main run loop.

In this run loop, the Backend first gets a command from the self.command_queue, which is just a standard Python Queue, using a predefined timeout. If this command is Commands.QUIT, the run loop exits and we disconnect the socket, stop all the threads, and exit the process. Otherwise, we execute the handler function for the given command, which is located in the dictionary self.handlers, which maps Commands to Backend member functions. After completing the command, the Backend responds with either CommandResponses.SUCCESS or CommandResponses.ERROR, along with any necessary parameters.

Globals

In python/src/backend/globals.py we define a few variables that are used by multiple Backend threads.

Namely, we define raw_data which holds the raw EMG or EEG data received from the OpenBCI modules. Editing this object requires holding the raw_data_lock mutex, preventing the Backend and its multiple threads from editing the raw data simultaneously.

The raw data settings are also stored in the global variable raw_data_settings. This doesn’t require a mutex lock since only the Backend will edit this object.

The last global variable is the filter. This Filter object is used to filter the raw EMG or EEG data. This is stored in a global variable because the Backend’s plot_thread sends it to the Plotter process so that we can display the filtered raw data. The filter_graphs variable is the flag that determines whether the Plotter process should filter its data before graphing.

Threads

The Backend currently uses 6 threads (not including the logging thread):

  • Socket Listening Thread: Listens for a new connection from a Frontend (unless already connected)

  • Socket Recv Thread: Receives Commands and AsyncCommands from the Frontend.

  • Socket Send Thread: Sends CommandResponses, AsyncCommandResponses and str console print statements to the Frontend.

  • GUI Update Thread: Sends dict updates to the GUI at 2 Hz containing values like FPS.

  • Plot Thread: When active, sends raw data from globals.raw_data.full_buffer to the Plotter process for graphing.

  • EMG Thread: When active, receives raw EMG or EEG data from external OpenBCI Board and adds it to globals.raw_data.