Closed Loop Guide

Overview

This guide is intended to show how to use/write libraries of closed loop 'plugin' experiments for use in NeuroRighter.  'Plugin,' in this case, means that your own code gets injected in to NeuroRighter and gains access to many of NeuroRighter's useful features (such as recording and arbitrary stimulation/DAQ control).  You can think of closed loop code like a parasitic insect that crawls into NeuroRighter and uses all of NeuroRighter's innards for it's own purposes.  To do write new experiments, as of revision 318, you will need:

What NeuroRighter Closed Loop Can Do:

What NeuroRighter Closed Loop CAN'T Do:

What You need to do in order to use the Closed Loop system:

The easiest way to use the closed loop system is to just use one of the 'plugin' experiments that someone else already wrote.  There are a bunch of them up on the google code site:

Feel free to download these and give them a whirl. To do this, fire up NeuroRighter, using whatever hardware/display/spike detection/file saving settings you prefer.  On the 'Stimulation' tab, you will see a box labeled 'Closed Loop Experiment'.  

First you need to load up an experiment library.  An experiment library is a collection of 'plugin' experiments that are all stored in the same file.  To load this library, click on the '...' button in the Closed Loop Experiment box. 

The file extension you are looking for is '.dll,' which stands for 'dynamic-link library.'  You can think of a DLL as code that doesn't actually run itself (like an EXE), but is used by other programs.  DLL files are used a lot in Windows programming- they are a great way for programs to have access to the same code, like the code for making buttons on application windows or interacting with hardware.  This means that you might see several DLL files lying around on your computer. Make sure to select one that you know is an experiment library.

After you select a library, the drop down bar underneath the load button will contain the names of all the closed loop experiments that are stored in that library.  If you selected a DLL that doesn't contain any experiments, no options will be displayed.  Each experiment is a different class in the DLL.  For those of you who don't plan on creating your own experiments, in object oriented programming (OOP), a class is a piece of code that defines the properties of a new type of object.  Objects are all the 'nouns' in programs, and code describes how those 'nouns' interact.  If you are planning on writing your own code, you will want to familiarize yourself with this concept.

After you have selected your experiment, you might want or need to check the 'Use pre-defined stimulation waveform' box.  When this button is checked, the experiment is given a copy of the waveform that is specified in the 'manual stim' box.  This means that you can easily tune your experiment for different cultures just by changing the values in the manual stim box, but on the flip side means that your experiment will be using the same waveform for every stimulus (depending on how the experiment is written- more on that later). 

       

When you are done with all that, hit the 'start experiment' button and your set.  Make sure you have your file saving stuff set up beforehand.  Note that if you need to stop your experiment in the middle, you can do so by hitting the stop button in the Closed Loop Experiment box.  If the experiment was written correctly, it should stop within a second.  If not, you might need to close down NeuroRighter.  And then have a word with whoever wrote that experiment.

What you need to do in order to create your own experiment:

If you need to perform a closed loop experiment that hasn't been written yet, you will need to write your own DLL.  This basically means you are writing a small program that has access to functions/methods provided by NeuroRighter.  If this sounds like it is more work than  it is worth, try seeing if you can create your protocol using the open-loop stimulation system first, and then come back here.  The following instructions are based on our experiences writing these experiments in C# using Microsoft Visual Studio (2008-2010), if you use another language or development environment please add anything that comes to mind.  

Note:If you are running NeuroRighter out of Visual Studio's debugger, we recommend including this project as part of the same solution file and debugging them simultaneously.  If that last sentence didn't make any sense to you, it's a Visual Studio thing.

Overview

You are going to do the following:

1. Create a library file

2. Write an experiment to add to that library

3. Within that experiment, you will 'jack in' to NeuroRighter by writing a couple of special methods that NeuroRighter uses

4. Within those methods, you can read data from NeuroRighter through buffers that are contained in the 'DatSrv' object, and

5. Write data into NeuroRighter through buffers that are contained in the 'StimSrv' object.

Creating the Library

First, create a new DLL project.  In visual studio, a 'project' is a file that organizes all the stuff you need to build a specific program or library.  By creating a DLL project, visual studio will know that all the code you are going to write will be compiled into a single DLL, and will not contain any 'main' methods.  

Next, add NeuroRighter.exe as a reference.  You will need to reference a version that can run closed loop code, and for safety's sake we recommend you reference the actual copy of NeuroRighter that you plan on using. 

Once you add NeuroRighter as a reference, you will be given access to five libraries that you can reference with the 'using' keyword in C# (this is just telling your closed loop code which parts of NeuroRighter it is allowed to use):

NeuroRighter.Output is the most important of these five.  You must reference this library in every closed loop experiment you write, as it contains the definition of the 'ClosedLoopExperiment' abstract class that you are going to implement.  Implementing this class gives you access to the DatSrv and StimSrv objects, which are used for all stimulation and recording.

NeuroRighter.DataTypes defines the data types that are used by DatSrv and StimSrv (SpikeEvent, AuxOutEvent, etc...).

NeuroRighter.Network provides simple tools for sending messages over an internet port to a server you have setup elsewhere. For example, this can be used to communicate with a real-time linux machine (Like the RACS in the Potter Lab) instead of NeuroRighter's native I/O in order to get a lower latency response or stimulate remotely. 

NeuroRighter.dbg is an optional library that gives you access to the real time debugger.  This tool will allow you to precisely log events to a file in the directory that you save your .spk/.stim files.  

Don't worry about the other libraries.  They are there to help organize NeuroRighter, and they will not provide you with anything if you add them.

Basic Structure of the Plugin

Now onto the actual code!  Create a new class for each experiment protocol you want to create.  Each class should inherit from the abstract class 'ClosedLoopExperiment.'  This means that each of these new classes will

The simplest closed loop experiment you can run:

Where 'ExperimentLibrary' is the name of the DLL you are going to produce, and 'MyClosedLoopExperiment' is the name of the experiment you are creating for that library.

the idea here is that you are writing chunks of code that will get executed somewhere inside NeuroRighter, using objects that organize NeuroRighter's code so it's easier to deal with than going under the hood.  You write these chunks of code by overriding methods of the base abstract class ClosedLoopExperiment.  Since the Setup() method is marked 'abstract' in the base class, you need to override it to have a functional experiment.  You have the option to override the 'BuffLoadEvent()' method, but it is not required.  

Types of code: Setup() versus Loop()

To reiterate, your experiment is a piece of code that NeuroRighter will 'suck up' and execute within itself.  You are given two ways of formatting this code: the  Run()method, and the 'BuffLoadEvent' method.  Both of these methods are called by NeuroRighter when it loads the plugin.  Run() is called when you hit the closed loop start button.  It executes once, and when it finishes, the experiment stops.  'BuffLoadEvent' is called every time NeuroRighter attempts to load data into the DAQ, and is useful for time-sensitive stuff (like making sure you have generated all your stimuli on time).

Run() is the more basic of these two methods.  Setup() is called by NeuroRighter when you hit the 'start' closed loop button, after NeuroRighter finishes setting up recording and stimulation.  When Run()finishes, the experiment is over and recording stops.  As the experiment designer, it is your responsibility to make sure that your  Run() method will stop if the 'isFinished' boolean ever goes to true, as this indicates that the user is trying to shut down the experiment.  One common way of doing this is using a while loop:

note that it is very important to add some amount of delay, in this case a 1 second long timer, to your while loop.  The closed loop code is run at very high priority- this makes the real-timeness of the system more reliable, but means that if your while loop doesn't do anything it could get stuck in an infinite loop and your only recourse will be to shut down the computer. 

Loop() is used for precisely timed  events. It is executed every time the computer polls the NI cards to obtain new data. The rate at which this occurs is set in Hardware settings under general.

Reading Data from NeuroRighter

In order to read data, you need to use the DatSrv object.  For those of you familiar with Meabench, DatSrv is analogous to the program rawsrv.  It contains a set of buffers that save the most recent values read into NeuroRighter, whether raw values off of your preamp, auxillary analog or digital values from the NI Daq, or filtered outputs from the spike, SALPA, LFP and EEG streams.  Additionally, DatSrv contains buffers of the most recent events that NeuroRighter has intercepted- things like spikes, and recorded stimuli.  The amount of data that NRDatSrv keeps on track is set in Hardware settings- the default is 10 seconds, meaning at any given point in time, you can look at the last 10 seconds of data that NeuroRighter has processed.  If you need to look back further than that, you should either configure your hardware settings to allow for a longer buffer, or save the information has it passes by (during the 10 seconds that you have access to it).  

DatSrv has two kinds of buffers:

Raw Data Buffers- these are buffers that contain data sampled at whatever your sampling frequency is (default is 25khz), going back as far as you configure (default is 10 seconds).

DatSrv.auxAnalogSrv:  This buffer contains all of the data recorded on the auxiliary analog channels over the course of the last 10 seconds.  If your hardware settings are not configured to record auxiliary analog channels, this buffer is null.

DatSrv.eegSrv:  This buffer contains all of the EEG data recorded over the course of the last 10 seconds.  If your hardware settings are not configured to record EEG signals, this buffer is null. 

DatSrv.filteredElectrodeSrv: This buffer contains all of the data recorded from the 'spike filter' over the course of the last 10 seconds.  If your hardware settings are not configured to use the spike filter, this buffer is null. 

DatSrv.lfpSrv: This buffer contains all of the LFP data recorded over the course of the last 10 seconds.  If your hardware settings are not configured to record LFP signals, this buffer is null. 

DatSrv.rawElectrodeSrv: This buffer contains all of the data that came directly off of the DAQ over the course of the last 10 seconds.  This buffer is always available.

DatSrv.salpaElectrodeSrv: This buffer contains all of the data recorded from the SALPA filter over the course of the last 10 seconds.  If your hardware settings are not configured to use SALPA, this buffer is null. 

Event Data Buffers- These are buffers that contain discrete events.  Like the Raw Data Buffers, they also contain all the data over the time period specified in hardware settings, but since they are not sampled continously, the amount of data in these buffers can fluctuate (for example, if the firing rate of your specimen increased in the last 10 seconds, the number of spikes in DatSrv.spikeSrv will increase).

DatSrv.spikeSrv: This buffer contains all the spikes in the last 10 seconds.

DatSrv.auxDigitalSrv:  This buffer contains all of the data recorded on the auxiliary digital channels over the course of the last 10 seconds.  If your hardware settings are not configured to record auxiliary digital channels, this buffer is null.

DatSrv.stimSrv:  This buffer contains all of the electrical stimuli that were recorded in the last 10 seconds.  If your hardware settings are not configured to record stimulation, this buffer is null.

Reading: Reading from these buffers is accomplished using the ReadFromBuffer method.  This method takes as an argument the index where reading should start and the index where the reading should stop.  For example, say we wanted to read all the spikes that occurred between times 10 seconds and 12 seconds:

ulong start = (ulong) (10*DatSrv.spikeSrv.sampleFrequencyHz);

ulong stop = (ulong) (12*DatSrv.spikeSrv.sampleFrequencyHz);

DatSrv.spikeSrv.ReadFromBuffer(start, stop);

Note that the above code isn't very useful, as we don't actually get to use all the spikes we just read.  To do that, you need to create a space to store those spikes.  

EventBuffer<SpikeEvent> recordedSpikes;

ulong start = (ulong) (10*DatSrv.spikeSrv.sampleFrequencyHz);

ulong stop = (ulong) (12*DatSrv.spikeSrv.sampleFrequencyHz);

recordedSpikes = DatSrv.spikeSrv.ReadFromBuffer(start, stop);

Now you have all the spikes recorded between seconds 10 and 12 in the object 'recordedSpikes.'

Stimulating with NeuroRighter

In order to stimulate with NeuroRighter, you need to use the StimSrv object.  Like DatSrv, StimSrv is mostly just an accumulation of buffers for individual streams you can write to within NeuroRighter.

StimSrv.AuxOut: This buffer is used to write values to the analog auxiliary channels.

StimSrv.DigitalOut: This buffer is used to write digital values out to the auxiliary digital port.

StimSrv.StimOut: This buffer is used to write electrical stimuli out.

Writing to these buffers is accomplished with the writeToBuffer method.  For example, say we wanted to stimulate at times  1.2, 1.5, 1.7 seconds, on channels 3, 7 9, using the waveform generated by the 'manual stim' gui.    One way of doing this:

//construct the stimuli

double[] times = {1.2, 1.5,1.7};

int[] channels = {3,7,9};

double[] waveform = cannedWaveform; //this uses the waveform created by the 'manual stim' gui

List<StimulusOutEvent> stimuli = new List<StimulusOutEvent>();

for (int i = 0;i<3;i++)

{

stimuli.Add(new StimulusOutEvent(channels[i],(ulong)(times[i]*fs), waveform));

}

//add the stimuli to the buffer

StimSrv.StimOut.writeToBuffer(stimuli);

Note that all times in the closed loop are in samples since the experiment began.  ulongs, or unsigned long integers, are used so that experiments can go for over a day.  In fact, using ulong precision, you could theoretically have a closed loop experiment run for 5.8 million years without exploding.  If you do successfully run an experiment that long, please inform us by any means necessary.