| www.cortex.salk.edu | September 06, 2008 |
|
:: Style
|
The following information was taken from a state file contributed by Dr. Andrew R. Mitz, at the Laboratory of Systems Neuroscience, NIMH. This code is provided as is.
The purpose of this program is to help validate external data acquisition equipment, like Plexon and AlphaOmega. This Cortex state file reads an existing Cortex data file and tries to re-create the experiment by encoding the same events as in the original experiment, and generating pulses to a digital output port to represent the original spikes. Analog data are ignored. If you would like to open the files directly do so here (hardware.h, replay.c) or read below for a description of how these files work.
One digital input is required for a 10 KHz clock. This clock allows replay to operate with sufficient precision. We use a carefully adjusted signal generator driving bit 0 on Port A. Digital outputs are required for each spike channel that you want to simulate. We use 4 output channels on Port B.
Digital channels are defined in "Hardware definition for clock input and simulated spike outputs" Omit the "hardware.h" include line if you want to hard code the devices. We abstract device names in hardware.h to make software more portable.
FILENAME The file name is hard coded in this #define statement. SKIPTRIALS n Will skip over the first trials of a file before simulation starts PAUSE Set this = 1 to help debug the system, then set to 0 to run a replay MAX_EVENTS Set this as high as your Cortex system allows. It is the number of events that replay can handle in a single trial. We reduce the size of DATA_STRUCTS in CORTEX.CFG to make more space, but we really don't know if that helps. spike_codes[SPIKE_CODE_CNT] These are the event codes that will be treated as spikes ignore_codes[IGNORE_CNT] These are groups of event codes to ignore. IGNORE_CNT is always an even number. Codes are in a range. e.g., to ignore codes 50 to 65:
ignore_codes[1]=65;
Reading a Cortex file from disk using a C compiler that does not recognize unsigned integers is a royal pain. Subroutines char2int() and char2long() do the dirty work of moving signed char data from an array to signed short and signed long integers without interference from the sign bit.
V0.01 20 Sept 2004 reads data files, tracks external clock
V0.02 20 Sept 2004 dumps parts of data files, generates spikes V0.03 5 Oct 2004 SKIPTRIALS option V0.04 6 Oct 2004 cleaned up documentation
#include "css_inc.h"
#include "hardware.h" // define hardware devices
#define SIM_DEV DIO96_DEV // Spike simulation device
#define SIM_CLOCK DIO96_2A_PIOA // A port gets clock input #define SIM_SPIKE_PORT DIO96_2A_PIOB // B port gets up to 8 spikes out #define SIM_CTRL DIO96_2A_CONT // control byte #define SIM_CLOCK_BIT 0x01 // clock bit #define sim_init 0x91 // initialization byte (A=in,B=out,C=split) #define clock_high (DEVinp(SIM_DEV,SIM_CLOCK) & SIM_CLOCK_BIT) #define TRUE 1 #define FALSE 0 #define NULL 0 #define MIN(X,Y) ((X< #define SPIKE_DURATION 8 // number of 0.1 ms clock tics for simulated spike #define FILENAME "za93.1" // test file name #define SKIPTRIALS 358 // skip this many trials before sending data #define PAUSE 0 // 1 = pause during the trials, 0 = avoid pauses
#define trial_number _int0 // trial number
#define reward_port_data _int1 // shadow register for reward port #define counter_port_data _int2 // shadow register for external counter port #define sim_spike_port_data _int3 // shadow register for simulated spike ports #define fh _long0 // file handle #define MAX_EVENTS 200 // buffer size int event_codes [MAX_EVENTS]; long event_times[MAX_EVENTS];
Cortex header structure that Cortex state file programming can tolerate. Cortex does not support structures or unsigned integers.
int h_length; // unsigned short int h_cond_no; // short int h_repeat_no; // unsigned short int h_block_no; int h_trial_no; int h_isi_size; // unsigned short int h_code_size; int h_eog_size; int h_epp_size; char h_i_storage_rate; // unsigned char char h_kHz_resolution; // unsigned char int h_expected_response; // (enum = short) int h_response; // (enum = short) int h_response_error; // (enum = short) long file_position; // global to help debugging
#define SPIKE_CODE_CNT 4 // number of spikes with digital outputs MAXIMUM IS 8
int spike_codes[SPIKE_CODE_CNT]; // maximum of SPIKE_CODE_CNT int spike_counts[SPIKE_CODE_CNT];
#define IGNORE_CNT 6 // number of min/max pairs * 2
int ignore_codes[IGNORE_CNT]; // min,max ranges of events to ignore
void read_header();
void dump_header(); long read_trial(); int wait_for_clock(); int encode_event(int en); void Aencode(int code); int check_keyboard(int waitfor); int char2int(pchar cbuf); long char2long(pchar cbuf) ;
main() {
int spike_chan,chan_bit,i,j,abort; int chan_timer[SPIKE_CODE_CNT]; spike_codes[0]=1; // events that will be treated as spikes spike_codes[1]=2; // array size is SPIKE_CODE_CNT spike_codes[2]=3; spike_codes[3]=4; // ignore events 0 to 10 ignore_codes[0]=9; // Groups of events that will be completely ignored ignore_codes[1]=10; // array size is IGNORE_CNT // ignore events 110 to 113 ignore_codes[2]=110; // Events are ignored from first to last code ignore_codes[3]=113; // in a group // ignore events 211 to 245 ignore_codes[4]=211; ignore_codes[5]=245; // Initialization just before the first trial if (trial_number==0) {
printf("\nOpening file: %s\n",FILENAME); fh=fopen(FILENAME,"rb"); // global file handle if (fh==NULL) {
goto ABORT; else {
reward_port_data=0; counter_port_data=0; sim_spike_port_data=0; DEVoutp(REWARD_DEV,REWARD_PORT,reward_port_data); // Initialize reward port DEVoutp(COUNTER_DEV,COUNTER_PORT,counter_port_data); // Initialize counter port DEVoutp(SIM_DEV,SIM_SPIKE_PORT,sim_spike_port_data); // Initialize simulated spike port DEVoutp(ALAB_DEV,ALAB_CTRL,alphalab_init); // PA,PB,PC_hi=out, PC_lo=in DEVoutp(SIM_DEV,SIM_CTRL,sim_init); // Spike simulation device init printf("Enter F2 to run clock test, or [enter] to skip test.\n"); abort=0; if (check_keyboard(1)==2) {
sim_spike_port_data=0; MS_TIMERset(1,2); // synchronize clock while(MS_TIMERcheck(1)); // wait for clock to transition MS_TIMERset(1,100); // set for 100 ms while(MS_TIMERcheck(1)) {
trial_clock++; sim_spike_port_data= (sim_spike_port_data) ? 0 : 1; // output bit 0 tracks 10 Khz clock DEVoutp(SIM_DEV,SIM_SPIKE_PORT,sim_spike_port_data); if (abort) goto ABORT; printf("%d tics in 100 ms (1000 is ideal)\n",trial_clock); // should get 1000 tics printf("Hit [enter] key to continue.\n"); check_keyboard(1); // wait for a key // **** Most trials start here ********** // trial_number++; trial_clock=0; // reset 0.1 ms clock Cls(); printf(" Use F1 to break out of trial\n"); printf("Reading header for trial %d\n",trial_number); read_header(); // read cortex file header for next trial dump_header(); // print header to the screen printf("Hit [enter] key to continue.\n"); check_keyboard(PAUSE); // wait for a key Cls(); printf("\nReading trial data for trial %d\n",trial_number); total_events=read_trial(); // move data to event_codes[] and event_times[] arrays printf("Events read: %d\n",total_events); printf("Hit F2 to see codes + times, or [enter] key to continue.\n"); if (check_keyboard(PAUSE)==2) {
printf("Hit [enter] key to continue.\n"); check_keyboard(PAUSE); // wait for a key Cls(); if ( trial_number < SKIPTRIALS)
// initialize simulated spike timers for (spike_chan=0; spike_chan < SPIKE_CODE_CNT; spike_chan++)
for (j=0 ; j < SPIKE_CODE_CNT; j++)
while (event_number < (total_events-1)) { // continue until all events are done
if(wait_for_clock())
// service simulated spike timers for (spike_chan=0; spike_chan < SPIKE_CODE_CNT; spike_chan++) { // cycle through the timers
if (chan_timer[spike_chan]==0) { // has a timer run out?
chan_bit=chan_bit << spike_chan; chan_bit=INV(chan_bit); // mask for output bit sim_spike_port_data=sim_spike_port_data & chan_bit; DEVoutp(SIM_DEV,SIM_SPIKE_PORT,sim_spike_port_data); // clear bit // execute all new events that have occurred since the last clock tic. Event times are in milliseconds. while ((trial_clock)/10 >= event_times[event_number]) {
if (PAUSE) {
if ((spike_chan > 0) && (spike_chan < (SPIKE_CODE_CNT-1))){
chan_bit=1; // create bit to enable spike output chan_bit=chan_bit << (spike_chan-1); // shift bit to appropriate position sim_spike_port_data=sim_spike_port_data | chan_bit; // set digital output for that channel DEVoutp(SIM_DEV,SIM_SPIKE_PORT,sim_spike_port_data); chan_timer[spike_chan-1]=SPIKE_DURATION; // start timer event_number++; } // end main()
void read_header() {
char buffer[30]; file_position=ftell(fh); count=fread(buffer,1,26,fh); // read the 13 word header into a buffer if (count==0)
h_cond_no= header_buffer[1]; h_repeat_no= header_buffer[2]; h_block_no= header_buffer[3]; h_trial_no= header_buffer[4]; h_isi_size= header_buffer[5]; h_code_size= header_buffer[6]; h_eog_size= header_buffer[7]; h_epp_size= header_buffer[8]; h_i_storage_rate= header_buffer[9] & 0xFF; // single byte value h_kHz_resolution= header_buffer[9] >> 8 ; // single byte value h_expected_response= header_buffer[10]; h_response= header_buffer[11]; h_response_error= header_buffer[12];
void dump_header() {
printf("Starting at file position %d\n",file_position); printf("length %d cond %d repeat %d block %d\n",h_length,h_cond_no, h_repeat_no,h_block_no); printf("storage rate %d Khz %d\n",h_i_storage_rate,h_kHz_resolution); printf("expected %d response %d error %d\n",h_expected_response,h_response, h_response_error); printf("code size %d timestamps %d EOG size %d EPP size %d\n",h_code_size, h_isi_size,h_eog_size,h_epp_size); file_position=ftell(fh); printf("Ending at file position %d\n",file_position);
long read_trial() {
long trial_start_time; char rbuffer[MAX_EVENTS*4]; int i; events_in_file=h_code_size/2; // number of event codes in the file events_to_read=MIN(MAX_EVENTS,events_in_file); // maximum number of event codes to memory can hold excess_events=MAX(0,(events_in_file-events_to_read)); // number of event codes not replayed. printf("Events in file: %d Events to read: %d excess: %d\n",events_in_file,events_to_read,excess_events); // times file_position=ftell(fh); printf("Reading events at file position %d\n",file_position); fread(rbuffer,1,events_to_read*4,fh); // Get as many event times as will fit in memory trial_start_time=char2long(rbuffer); event_times[0]=0; for (i=1; i < events_to_read; i++)
// codes file_position=ftell(fh); printf("Reading codes at file position %d\n",file_position); fread(rbuffer,1,events_to_read*2,fh); // Get same number of event codes for (i=0; i < events_to_read; i++)
printf("Skipping %d bytes of codes starting at file position %d\n",excess_events*2,file_position); fseek(fh,excess_events*2,SEEK_CUR); // epp + eog file_position=ftell(fh); printf("Skipping %d bytes of analog starting at file position %d\n",(h_epp_size+h_eog_size),file_position); fseek(fh,(h_epp_size+h_eog_size),SEEK_CUR); // skip reminder of trial file_position=ftell(fh); printf("Reading is finished at file position %d\n",file_position); return(events_to_read); // number of events
The purpose of this function is to wait for the next transition of the 10 Khz clock returns with a 1 if an abort was requested
int wait_for_clock() {
if (check_keyboard(0)==1)
return 0;
This function will return -1 if event was ignored, 0 if event was encoded, and n if event is spike number n (n>0)
int encode_event(int en) {
// weed-out globally ignored events for (i=0; i < IGNORE_CNT; i++) {
igmax=ignore_codes[2*i+1]; if ((en >= igmin) && (en <<= igmax)) {
// identify spikes for (i=0; i < SPIKE_CODE_CNT; i++) {
// encode the rest Aencode(en); return (0); // return zero for a regular event
This replaces encode(). All encodes are forwarded to the AlphaLab device. 16 bits of data are sent via DIO ports A and B. Port C is used for handshaking. AlphaLab requires at least 20 microseconds to recognize a change in the strobe from Cortex. The alphalab_ready bit is high when the AlphaLab is ready.
void Aencode(int code) {
DEVoutp(ALAB_DEV,ALAB_BYTE0,(code & 0xFF)); DEVoutp(ALAB_DEV,ALAB_BYTE1,(code/0x100) & 0xFF); encode(code); alphalab_strobe_on; // raise strobe bit while (!alphalab_ready); // wait for ready flag (25us) alphalab_strobe_off; // drop strobe bit
Respond to keyboard requests if waitfor==1, print a prompt and wait. otherwise, return to calling program immediately. Returns 1 to 9 indicating F1 to F9. Returns 20 if the return key was hit.
int check_keyboard(int waitfor) {
if(waitfor)
r=0; switch (a) {
case K_F2: r=2; break; case K_F3: r=3; break; case K_F4: r=4; break; case K_F5: r=5; break; case K_F6: r=6; break; case K_F7: r=7; break; case K_F8: r=8; break; case K_F9: r=9; break; case K_RETURN: r=20; break; if(waitfor) {
return r;
int char2int(pchar cbuf) {
LAST_BYTE=1; // 2 byte conversion word=0; for (i=LAST_BYTE; i >= 0; i--) {
byte= byte | 0x80; // insert bit back into int word= (word << 8) | byte; return(word);
long char2long(pchar cbuf) {
int byte,i,LAST_BYTE; LAST_BYTE=3; // 4 byte conversion dword=0; for (i=LAST_BYTE; i >= 0; i--) {
byte= byte | 0x80; // insert bit back into int dword= (dword << 8) | byte; return(dword);
// Hardware definition
#define DAS16_DEV 0 // device 0 = Compuboard #define DAS16_PIOA 0x10 #define DAS16_PIOB 0x11 #define DAS16_PIOC 0x12 #define DIO96_DEV 1 // device 1 = DIO96 #define DIO96_1A_PIOA 0x00 // Cable 1, Connector A, Port A #define DIO96_1A_PIOB 0x01 // Cable 1, Connector A, Port B #define DIO96_1A_PIOC 0x02 // Cable 1, Connector A, Port C #define DIO96_1A_CONT 0x03 // Cable 1, Connector A, Port control #define DIO96_1B_PIOA 0x04 // Cable 1, Connector B, Port A #define DIO96_1B_PIOB 0x05 // Cable 1, Connector B, Port B #define DIO96_1B_PIOC 0x06 // Cable 1, Connector B, Port C #define DIO96_1B_CONT 0x07 // Cable 1, Connector B, Port control #define DIO96_2A_PIOA 0x08 // Cable 2, Connector A, Port A #define DIO96_2A_PIOB 0x09 // Cable 2, Connector A, Port B #define DIO96_2A_PIOC 0x0A // Cable 2, Connector A, Port C #define DIO96_2A_CONT 0x0B // Cable 2, Connector A, Port control #define DIO96_2B_PIOA 0x0C // Cable 2, Connector B, Port A #define DIO96_2B_PIOB 0x0D // Cable 2, Connector B, Port B #define DIO96_2B_PIOC 0x0E // Cable 2, Connector B, Port C #define DIO96_2B_CONT 0x0F // Cable 2, Connector B, Port control // Use the above hardware definitions to define connections to external devices // counters #define COUNTER_DEV DAS16_DEV // reward counter on PIO12 of Compuboard #define COUNTER_PORT DAS16_PIOC // counters use port C #define COUNTER1_BIT 0x10 #define COUNTER2_BIT 0x20 // touch bar #define BAR_DEV DAS16_DEV #define BAR_PORT DAS16_PIOA #define BAR_BIT 0x1 #define bar_touch (DEVinp(BAR_DEV,BAR_PORT) & BAR_BIT) // AlphaLab event codes #define ALAB_DEV DIO96_DEV // AlphaLab device #define ALAB_BYTE0 DIO96_1B_PIOA // low order byte #define ALAB_BYTE1 DIO96_1B_PIOB // high order byte #define ALAB_HSHAKE DIO96_1B_PIOC // handshaking byte #define ALAB_READY 0x1 // ready bit (input) #define ALAB_STROBE 0x10 // strobe bit (output) #define ALAB_STROBE_NOT 0xFE00 // inverse of strobe bit (hi nibble) #define ALAB_CTRL DIO96_1B_CONT // control byte #define alphalab_init 0x81 // initialization byte (A,B,C_hi=out) #define alphalab_ready (ALAB_READY & DEVinp(ALAB_DEV,ALAB_HSHAKE)) #define alphalab_strobe_on DEVoutp(ALAB_DEV,ALAB_HSHAKE,ALAB_STROBE) #define alphalab_strobe_off DEVoutp(ALAB_DEV,ALAB_HSHAKE,ALAB_STROBE_NOT) // Both reward devices on same port #define REWARD_DEV DAS16_DEV #define REWARD_PORT DAS16_PIOB #define REWARD1_OFFSET 0x0; #define REWARD2_OFFSET 0x10; // SitePlayer Web support (requires #include webinc.h) // SitePlayer uses standard COM2 // #define USE_WEB TRUE // FALSE to disable web support #define WEB_BAUD_DIVISOR BAUD9600 // 9600 #define WEB_COM_PORT COM2 // COM2 #define WEB_COM_BITS N81 // N81 #define WEB_IRQ 3 // IRQ |