Difference between revisions of "Dispatcher"

From Bcontrol
m (Synchronizing behavior and electrophysiological recording)
Line 93: Line 93:
: <tt>parsed_events_history{n} = disassemble(current_events_history{n}, raw_events_history{n}, 'parsed_structure', 1);</tt>
: <tt>parsed_events_history{n} = disassemble(current_events_history{n}, raw_events_history{n}, 'parsed_structure', 1);</tt>
== Debugging in Dispatcher ==
== Synchronizing behavior and electrophysiological recording ==
== Synchronizing behavior and electrophysiological recording ==

Revision as of 05:57, 30 August 2007

Overview of Dispatcher

Dispatcher is an interface between protocol code and the Real-Time Linux State Machine (RTLSM). It provides opening and closing of protocols, automatic segmentation of data from the RTLSM into trials, parsing of trials, and an automatic keeping track of trial and state machine history over trials. Dispatcher also takes care of the regular polling of the RTLSM. Finally if you ask it to, Dispatcher will add code to your state machine so a digital signature of the trial number and a time sync signal is sent out a DIO line at the beginning of each trial; this is intended for syncing the behavior system to electrophysiological recording systems.

One of the principal advantages of using Dispatcher is that it is aware of the trial structure, and thus knows precisely which state machine definition corresponded to which trial and exactly when state machine definitions switched, and can therefore precisely interpret events from the RTLSM and parse them for you. (Parsing is described here; as a brief example, if your state machine has states named "woohee" and "dinkle", Dispatcher will automatically provide you with a variable called "parsed_events" that is a structure with elements states.woohee and state.dinkle; the entries in those elements will tell you the time stamps of entering and exiting from those states.)

Dispatcher is written using Solo, but to use the functionality that Dispatcher provides, you don't need to know Solo at all.

Once again, here is documentation on the result of parsing -- i.e., this is documentation on the format in which information about state machine events is returned to you by dispatcher.

Currently (14:07, 1 June 2007 (EDT)) there are only three protocols that work with dispatcher. They can be used as templates for you to make your own protocol. The protocols are PokesplotDemo, Minimal, and CenterPokeFollower. The last is planned as the answer key to one of the excercises, so-- don't look at it unless you have to!

Opening/closing Dispatcher

To start dispatcher, make sure you've called newstartup (See Startup Guide.). The command in the startup process that starts dispatcher is:

  >> dispatcher('init')

To rescan the existing protocols that dispatcher can start,

  >> dispatcher('rescan_protocols')

To close dispatcher,

  >> dispatcher('close')

Template code to write a dispatcher protocol

To write a dispatcher protocol, make a directory in Protocols, "Protocols/@mynewprotocol". Within that directory, place a constructor file, "Protocols/@mynewprotocol/mynewprotocol.m". (You should replace "mynewprotocol" with the name of your choice, of course.) The .m file must contain, at a minimum, what is in this example dispatcher template. With that plus the information below, you are good to go!

You might want to look at existing template protocols, like PokesplotDemo, or Minimal. You might also want to look at the Plugins page, to see what pre-existing code goodies are available for you to pick up.

The action string and its 5 possible values

Dispatcher interacts with your protocol by calling the constructor of your protocol code always with a single argument, a string usually called the "action." (E.g., if your code is in @yourprotocol/yourprotocol.m, it'll be yourprotocol.m that gets called with a single string as an argument.) This single argument can have one of 5 values:

This is the cue for initialization of the protocol: start any windows or GUIs that you may want to have, initialize variables, etc. Send the state machine definition for the first trial to dispatcher, to be forwarded to the RTLSM.
The protocol is to be closed. Delete your figure windows, delete your variables, etc.
Dispatcher has detected that the RTLSM has entered one of the "prepare_next_trial" states. (See trial structure.) Your protocol should prepare the next trial's state machine definition, and send it to dispatcher to be forwarded to the RTLSM. You can send any state machine definition that you like. (But you should make sure that your state machine is guaranteed to hit "check_next_trial_ready" at some point, otherwise the following trial may never start!) At the same time, you should indicate which states in your definition form the "prepare_next_trial" states. Note that this is also called when the experiment starts - at the beginning of the first trial.
An 'update' call (see below) is guaranteed to have been made immediately before the 'prepare_next_trial' call to your protocol. That is, you don't need to process the latest events in your 'prepare_next_trial' code, leave latest event processing to your 'update' code.
(Note: Since the state machine definition can be different from trial to trial, so can the set of trials that compose the "prepare_next_trial" set.)
Dispatcher has detected a state_0 boundary, meaning that a trial has been completed and the next trial is about to start. (See trial structure.)
We are in the middle of a trial. Dispatcher has polled the RTLSM to ask for the latest events, and is reporting them to you.

Variables instantiated by Dispatcher

Any Dispatcher protocol should have, near the top of its code, the line "GetSoloFunctionArgs(obj);", where "obj" is an object of your protocol's class. (This line is already part of the default code in the "do not modify" section of the template.) As a consequence, in addition to calling your protocol code with the above 5 different possible actions strings, Dispatcher will instantiate certain variables within your protocol, so that you have the content of these variables available to you. They will be instantiated as regular local Matlab variables (not SoloParamHandles). In any method of your protocol (i.e., m-files within your @yourprotocol/ directory), the line "GetSoloFunctionArgs(obj);" will instantiate the same variables. The variables are:

The number of trials that have reached one of their "prepare_next_trial" states.
The number of times the RTLSM has gone through state_0.
In between the initial state_0 in a trial, and entering the first of the "prepare_next_trial" states in that trial, n_started_trials=1+n_done_trials. In between entering the first of the "prepare_next_trial" states and the final state_0 in a trial, n_started_trials=n_done_trials.
This is always 1 less than n_started_trials. A trial is completed when the jump to state 0, to start the next trial, occurs.
A structure (see parsing), the result of running disassemble.m with the 'parsed_structure' flag set to 1 on all the events that have occurred from the initial state_0 in the current trial until now.
parsed_events.states.state_0 is guaranteed to have at least one row, and that first row will be of the form [NaN t0] where t0 is the time of the start of the current trial. parsed_events.states.state_0 is guaranteed to have at most two rows. If the second row exists, the trial has been completed, and the second row is guaranteed to be of the form [t1 NaN] where t1 is the time of the end of the current trial.
A cell that is n_completed_trials-by-1 in size. Each element is the parsed_events structure for a completed trial. Thus, parsed_events_history{1} is the parsed_events structure for the 1st trial; parsed_events_history{4} is the parsed_events structure for the 4th trial; and so on.
A structure (see parsing), the result of running disassemble.m with the 'parsed_structure' flag set to 1 on all the events that have occurred from the last time the RTLSM was polled until now. If no events have occurred, all the elements of this structure will be empty. latest_parsed_events is a useful variable when you want to focus just on updating based on the most recent events.
A @StateMachineAssembler object, as was used for the current trial.
This is the object that is used to disassemble events into parsed_events and latest_parsed_events.
A cell that is n_completed_trials-by-1 in size. Each element is the current_assembler for a completed trial. Thus, current_assembler_history{1} is the current_assembler for the 1st trial; current_assembler_history{4} is the current_assembler for the 4th trial; and so on.
A numeric matrix containing all the events from the start of the current trial until now, as obtained directly from the RTLSM.
parsed_events = disassemble(current_assembler, raw_events, 'parsed_structure', 1);
The first row of raw_events is guaranteed to be the transition out of state 0 that initiated the current trial. If the trial is completed, the last row of raw_events is guaranteed to be the transition into state 0.
A cell that is n_completed_trials-by-1 in size. Each element is the raw_events for a completed trial. Thus, raw_events_history{1} is the raw_events for the 1st trial; raw_events_history{4} is the raw_events for the 4th trial; and so on.
parsed_events_history{n} = disassemble(current_events_history{n}, raw_events_history{n}, 'parsed_structure', 1);

Debugging in Dispatcher

Synchronizing behavior and electrophysiological recording

How does one synchronize the behavior system with an electrophysiological recording system? One way is to have the behavior system put out, on a digital line, a signal that timestamps the start of a trial. This section describes how Dispatcher can do that for you. That same synchronizing signal will include a signal encoding the trial number that just started. (This is all done using the @StateMachineAssembler/add_trialnum_indicator.m method.)

If you make the call


then, after that, every StateMachineAssember that you pass to dispatcher (to send to the RTLSM with a dispatcher('send_assembler') call) will be automatically modified so that state_0 will not jump straight to the user's first state. Instead, the RTLSM will first go through a very swift sequence of states that put out, on one of the DIO lines, a binary sequence that is a signature of the trial number that is just starting; after that sequence is sent, the RTLSM will jump to the user's first state. None of the user-defined states are changed-- you define and use your state machine as always, this added functionality is added in for you.

The signal on the DIO line will be, in sequence: High, Low, High, High, Low, High, followed by a a 12-bit binary representation of the trial number (1 is High, 0 is Low), with most significant bit sent out first. That is, we go through a total of 18 states that cover both the initial sync signal and the trial number. The default is to go through each of these states in 1/6th of a ms, the FSM's cycle time (for a total of 3 ms for all 18 states). All of these added states will have the name 'sending_trialnum'.

The DIO line on which all this will happen is determined, using the Settings.m system, with DIOLINES; trialnum_indicator being the setting name. For example, the line

     DIOLINES; trialnum_indicator; 32

in Settings_Custom.conf would mean use DIO line 6 (in binary, 32 is 100000, so it is the 6th bit). If no such setting is found, no DOut is generated.

Note: This can ONLY be used with StateMachineAssemblers that were initialized with the 'full_trial_structure' flag.

Runrats, A dispatcher frontend

Runrats is a simplified GUI to streamline the process of setting up a rig for a behavioral session. In a lab, it would generally be started by a startup script when launching MATLAB. It can be run by typing


at the matlab command line. It will read the directory structure in the Main_Data_Directory settings preference (or if this is not set, it will assume that your settings are in ../SoloData/Settings) and populate a list of experimenters. The technician or researcher running rats then follows the simple steps:

  1. Pick an experimenter
  2. Pick a rat
  3. Press LOAD
  4. When ready, press RUN
  5. When the experiment is over, press END
  6. Goto step 1.

Behind the scenes, the runrats GUI finds the latest settings file for that rat, loads the appropriate protocol and settings. At the end of the experiment, it stops the State Machine, calls the end_session action of the protocol and saves the settings and data.