REM >DiscAp REM Discriminated Approach REM ---------------------------------- REM by Rudolf Cardinal progname$="DiscAp" version_date$="3 Dec 98" debug% = 0 :REM debugging? REM Trains rats according to a classical contingency REM The boxes run simultaneously and synchronously. REM REM 12 Jan 98 - added spaces at end of time display routines REM 14 Jan 98 - prints end time to screen; seeds random number generator REM 16 Jan 98 - BJE agrees that CS/(CS+VI) [RNC] is better than CS/VI [Cella] REM as a way of calculating a ratio of responding REM 18 Jan 98 - Running display of RR; stores time sums as it goes REM 27 Jan 98 - Spools but doesn't print trial-by-trial results REM 14 Feb 98 - Fixed minor printing bug REM 15 Sep 98 - renamed from TrainClas to DiscrimAp REM - FNget_filename REM - log and data files (for importing into Access/Excel) REM - set the type of the log file to Text REM - support for debug% REM - uses libraries REM 17 Oct 98 - library modularisation continued, code cleaned up REM 3 Dec 98 - session number recorded (entered by user) REM 12 Dec 98 - RR printed to 2dp REM REM VI -> CS -> US REM VI = houselight on REM traylight off REM dipper down REM 30-90 s REM CS = houselight off REM traylight on REM (dipper still down) REM 5 s REM US = houselight on REM traylight off REM dipper up REM 5 s REM REM Null state at start/end = houselight off, traylight off REM REM For each phase, the number of magazine entries and the duration spent REM in the magazine is recorded. Duration is more important. REM REM Cella also had code to record "pre-pokes", just before the CS; REM the code was bypassed in the latest version and I have not REM implemented this. REM REM Foible of Basic: the IF..THEN construct. Easy to miss out THEN. REM Arachnid principle: can't schedule two events on one switch! REM REM POLICY DECISION 1: if the rat remains in the magazine as the state REM changes, is that counted as a "new" poke during the second state? REM ANSWER: Yes. This avoids the silly situation of having time spent "in" REM without ever having a poke made. REM ================================== REM Libraries REM ================================== PROCinit :REM Arachnid init PROCkill_all :REM Arachnid init LIBRARY ".ProgLibs.Filename" LIBRARY ".ProgLibs.DateTime" LIBRARY ".ProgLibs.Ascii" LIBRARY ".ProgLibs.Arachnid" LIBRARY ".ProgLibs.BoxConst" PROCcombined_boxes REM ================================== REM Constants REM ================================== ntrials% = 30 :REM number of trials per rat (normally 30) vi_min% = 3000 :REM minimum VI (normally 30 sec = 3000 cs) vi_max% = 9000 :REM maximum VI (normally 90 sec = 9000 cs) cs_time% = 500 :REM 5 sec us_time% = 500 :REM 5 sec IF debug% = 1 THEN ntrials%=10 vi_min%=100:vi_max%=300:cs_time%=100:us_time%=100 ENDIF not_watching% = 0 :REM handy constants, must be different vi_state% = 1 cs_state% = 2 us_state% = 3 null_state% = 4 yes%=1 no%=0 REM ================================== REM Variable assignments REM ================================== DIM state%(nboxes%) :REM what phase is the box in? DIM vi_time%(ntrials%) :REM how long is the VI for this trial? DIM trial%(nboxes%) :REM which trial is this box doing? DIM cs_pokedur%(nboxes%,ntrials%) :REM nose poke duration during CS DIM us_pokedur%(nboxes%,ntrials%) DIM vi_pokedur%(nboxes%,ntrials%) :REM nose poke duration during VI DIM counting%(nboxes%) :REM are we timing? DIM watching%(nboxes%) :REM what state are we timing for? DIM start_time%(nboxes%) :REM holds TIME of switch onset DIM finished%(nboxes%) :REM has the box been shut down? DIM cs_pokes%(nboxes%,ntrials%) :REM # pokes during CS DIM us_pokes%(nboxes%,ntrials%) DIM vi_pokes%(nboxes%,ntrials%) :REM # pokes during VI DIM rresp_trial(nboxes%,ntrials%) :REM ratio of responding per trial DIM vi_pokesum%(nboxes%), vi_dursum%(nboxes%) DIM cs_pokesum%(nboxes%), cs_dursum%(nboxes%) DIM us_pokesum%(nboxes%), us_dursum%(nboxes%) DIM final_rr(nboxes%) DIM box_name$(nboxes%) :REM what's the rat called? DIM session_number%(nboxes%) :REM what session is it? REM ================================= REM Initialize screen REM ================================= MODE 12:CLS dummy=RND(-TIME): REM randomise random number generator PRINT progname$;" by Rudolf Cardinal, ";version_date$;"."; IF debug%=1 THEN COLOUR3:PRINT" *** DEBUGGING! ***":COLOUR 7 ELSE PRINT PRINT INPUT LINE "What's today's experiment called? " exp_title$ PRINT OSCLI("CAT") PRINT datafile$ = FNget_filename("DATA FILE - enter filename (no spaces etc.)") logfile$ = FNget_filename("TEXT LOG - enter filename (no spaces etc.)") date_time$ = FNdate_time_code PRINT PRINT "And the rats... (NB no commas!)" FOR i% = 1 TO nboxes% PRINT " Rat in box ";i%;": "; INPUT LINE "" box_name$(i%) INPUT " Session: " session_number%(i%) PRINT NEXT REM ========================================== REM Init hardware, assign events and run REM ========================================== PROCstart PROCwait(E%): *AE END REM ============================================ REM ******************************************** REM ============================================ DEF PROCstart REM assign random VI intervals (so each rat experiences the same) REM note: RND(n) where n>1 gives random number from 1..n REM and we want random numbers from 30-90 sec (3000 - 9000 csec) REM so we've got vi_min%=3000 and vi_max%=9000. REM The "totaltime" bit is just for show. totaltime_cs% = 0 FOR i% = 1 TO ntrials% vi_time%(i%) = RND(vi_max%-vi_min%+1) - 1 + vi_min% totaltime_cs% += vi_time%(i%) NEXT totaltime_cs% += ntrials% * cs_time% totaltime_cs% += ntrials% * us_time% totaltime_min% = totaltime_cs% / (100*60) totaltime_sec% = (totaltime_cs% - (totaltime_min% * 100 * 60)) / 100 PRINT "Estimated running time is ";totaltime_min%;" min ";totaltime_sec%;" sec." REM clear the boxes to the null state FOR box% = 1 TO nboxes% REM the following stay off throughout the program PROCswitch_off(leftlevercontrol%(box%),E%) PROCswitch_off(rightlevercontrol%(box%),E%) PROCswitch_off(leftlight%(box%),E%) PROCswitch_off(rightlight%(box%),E%) finished%(box%) = no% trial%(box%) = 1 counting%(box%) = no% watching%(box%) = not_watching% PROCset_null(box%) REM can't schedule an "On" and an "Off" event, so must use REM a single "Over" [=change] event. PROCpipe_switch(nosepoke%(box%),Over,1,"FNmagazine_changed(",box%,E%) NEXT REM when we're ready, initialise into VI state and go PRINT"Ready to run, press a key to start..." IF GET PROCdisplay_startup FOR box% = 1 TO nboxes% PROCset_vi(box%) PROCdisplay_box(box%) NEXT ENDPROC DEF FNentrypoint(box%,R%) IF R%=0 =0 CASE state%(box%) OF WHEN vi_state%: REM end of VI PROCstop_counting(box%) PROCset_cs(box%) PROCdisplay_state(box%) WHEN cs_state%: REM end of CS PROCstop_counting(box%) PROCset_us(box%) PROCdisplay_state(box%) WHEN us_state%: REM end of US REM NB don't want to be counting during trial change, REM might get pokes going to the wrong place PROCstop_counting(box%) REM Move to next trial now. vi_pokesum%(box%) += vi_pokes%(box%,trial%(box%)) vi_dursum%(box%) += vi_pokedur%(box%,trial%(box%)) cs_pokesum%(box%) += cs_pokes%(box%,trial%(box%)) cs_dursum%(box%) += cs_pokedur%(box%,trial%(box%)) us_pokesum%(box%) += us_pokes%(box%,trial%(box%)) us_dursum%(box%) += us_pokedur%(box%,trial%(box%)) trial%(box%) += 1 IF trial%(box%) <= ntrials% THEN PROCset_vi(box%) PROCdisplay_box(box%) ELSE PROCfinish(box%) ENDIF ENDCASE =0 DEF PROCset_null(box%) PROCswitch_off(houselight%(box%),E%) PROCswitch_off(traylight%(box%),E%) PROCswitch_off(dipper%(box%),E%) state%(box%) = null_state% ENDPROC DEF PROCset_vi(box%) PROCswitch_on(houselight%(box%),E%) PROCswitch_off(traylight%(box%),E%) PROCswitch_off(dipper%(box%),E%) state%(box%) = vi_state% REM set timer for the VI; when it expires, go to FNentrypoint PROCpipe_timer(box%,vi_time%(trial%(box%)),0,"FNentrypoint(",box%,E%) PROCstart_counting(box%) ENDPROC DEF PROCset_cs(box%) PROCswitch_off(houselight%(box%),E%) PROCswitch_on(traylight%(box%),E%) PROCswitch_off(dipper%(box%),E%) :REM dipper should be down anyway state%(box%) = cs_state% REM After cs_time%, go to FNentrypoint PROCpipe_timer(box%,cs_time%,0,"FNentrypoint(",box%,E%) PROCstart_counting(box%) ENDPROC DEF PROCset_us(box%) PROCswitch_on(houselight%(box%),E%) PROCswitch_off(traylight%(box%),E%) PROCswitch_on(dipper%(box%),E%) state%(box%) = us_state% REM After us_time%, go to FNentrypoint PROCpipe_timer(box%,us_time%,0,"FNentrypoint(",box%,E%) PROCstart_counting(box%) ENDPROC DEF PROCfinish(box%) PROCkill_switch(nosepoke%(box%),E%) PROCset_null(box%) REM don't call display_box as trial% now too big REM I'm scared that if I start reporting for a box before they've REM all finished, the other three will be inaccurately timed for REM the last session. Hence this check. finished%(box%) = yes% FOR i% = 1 TO nboxes% IF finished%(i%) <> yes% THEN ENDPROC NEXT VDU 2 :REM turn on printer CLS OSCLI("SPOOL "+logfile$) :REM output to disk PRINT"**********************************************************" PRINT"Results from !";progname$;" on ";TIME$ PRINT"Experiment: ";exp_title$ PRINT"Date/time code: ";date_time$ PRINT"Log filename: ";logfile$ PRINT"Data filename: ";datafile$ PRINT"**********************************************************"' FOR i% = 1 TO nboxes% VDU 2: PROCfinal_output(i%) :REM that procedure does VDU 3. NEXT OSCLI("SPOOL") OSCLI("SETTYPE "+logfile$+" TEXT") :REM set file type VDU 3 :REM printer off, close file REM Now create the file for importing into Access/Excel REM Times in seconds. datachan%=OPENOUT(datafile$) PROCprint_line(datachan%,"PROGNAME,DATE_TIME,RAT,SESSION,BOX,VI_POKES,CS_POKES,US_POKES,VI_DURATION,CS_DURATION,US_DURATION,VI_PERCENT,CS_PERCENT,US_PERCENT,RATIO_RESP") vi_timesum% = FNvi_time_until(ntrials%) cs_timesum% = cs_time% * ntrials% us_timesum% = us_time% * ntrials% FOR i% = 1 TO nboxes% REM a bit inelegant to duplicate this code from PROCfinal_output REM but easy vi_percent = 100 * vi_dursum%(i%) / vi_timesum% cs_percent = 100 * cs_dursum%(i%) / cs_timesum% us_percent = 100 * us_dursum%(i%) / us_timesum% final_rr=FNratio_resp(cs_dursum%(i%),vi_dursum%(i%),cs_timesum%,vi_timesum%) d$ = progname$+","+date_time$+","+box_name$(i%)+","+STR$(session_number%(i%))+","+STR$(i%)+"," d$ = d$ + STR$(vi_pokesum%(i%))+","+STR$(cs_pokesum%(i%))+","+STR$(us_pokesum%(i%))+"," d$ = d$ + STR$(vi_dursum%(i%)/100)+","+STR$(cs_dursum%(i%)/100)+","+STR$(us_dursum%(i%)/100)+"," d$ = d$ + STR$(vi_percent)+","+STR$(cs_percent)+","+STR$(us_percent)+","+STR$(final_rr) PROCprint_line(datachan%,d$) NEXT CLOSE#datachan% PRINT'"Finished at ";TIME$ PRINT"If you want that again, use PROCfinal_output(box number)."' at_save% = @% @% = &020208 :REM page 58 of the BASIC ref. manual; 2=fixed width, 02=two dp, 08=field width FOR i%=1 TO nboxes% PRINT " Final RR for box ";i%;" (";box_name$(i%);") = ";final_rr(i%) NEXT PRINT @% = at_save% ENDPROC DEF PROCstart_counting(box%) counting%(box%) = yes% IF FNswitch(nosepoke%(box%),E%)=On THEN PROCenter_magazine(box%) REM as the event is scheduled on a transition, it'll get lost if REM the switch is active at startup, without this extra check. ENDPROC DEF PROCstop_counting(box%) IF watching%(box%) <> not_watching% THEN PROCleave_magazine(box%) counting%(box%) = no% ENDPROC DEF FNmagazine_changed(box%,R%) IF R%=0 =0 IF counting%(box%) = no% THEN =0 IF FNswitch(nosepoke%(box%),E%)=On THEN PROCenter_magazine(box%) ELSE PROCleave_magazine(box%) ENDIF =0 DEF PROCenter_magazine(box%) IF trial%(box%) > ntrials% THEN ENDPROC REM this is to prevent overrun at the end of the program, where REM trial%(box%) can be increased beyond ntrials% before the program REM terminates. There are probably more elegant ways, but this is safe. start_time%(box%) = TIME watching%(box%) = state%(box%) CASE state%(box%) OF WHEN vi_state%: vi_pokes%(box%,trial%(box%)) += 1 WHEN cs_state%: cs_pokes%(box%,trial%(box%)) += 1 WHEN us_state%: us_pokes%(box%,trial%(box%)) += 1 ENDCASE PROCdisplay_switch(box%) ENDPROC DEF PROCleave_magazine(box%) REM when it starts counting, if the switch is off REM then Arachnid comes here (even if the switch REM hasn't undergone a transition. REM And *somehow* this is getting called with box%=0... CASE watching%(box%) OF WHEN vi_state%: vi_pokedur%(box%,trial%(box%))+=TIME-start_time%(box%) WHEN cs_state%: cs_pokedur%(box%,trial%(box%))+=TIME-start_time%(box%) WHEN us_state%: us_pokedur%(box%,trial%(box%))+=TIME-start_time%(box%) WHEN not_watching%: ENDCASE watching%(box%) = not_watching% PROCdisplay_switch(box%) ENDPROC DEF PROCdisplay_startup CLS PRINT progname$;", by Rudolf Cardinal, ";version_date$;"."; IF debug%=1 THEN COLOUR3:PRINT" *** DEBUGGING! ***":COLOUR 7 ELSE PRINT PRINT"Developed from an experimental design by Cella Olmstead." PRINT"Experiment: ";:COLOUR1:PRINTexp_title$:COLOUR7 PRINT PRINT"Number of trials: ";ntrials% PRINT"Timing parameters: VI=";vi_min%/100;"-";vi_max%/100;"s, CS=";cs_time%/100;"s, US=";us_time%/100;"s" PRINT"Started at: ";TIME$ PRINT"Total running time: ";totaltime_min%;" min ";totaltime_sec%;" sec" PRINT PRINT"- Times shown below are in seconds." PRINT"- System accuracy is about +/-1cs per event." PRINT"- The first row (VI-poketime etc.) shows times for the previous trial." PRINT"- The second row (VI-total etc.) shows cumulative time." PRINT PRINT"Box Trial VI-time State In? VI-poketime CS-poketime US-poketime" PRINT" VI-total CS-total US-total RR" PRINT"----------------------------------------------------------------------------" REM 0 1 2 3 4 5 6 7 display_firstline%=VPOS ENDPROC DEF PROCdisplay_box(box%) line% = FNdisplay_line(box%) PRINTTAB(0,line%);box%; PRINTTAB(5,line%);trial%(box%); PRINTTAB(12,line%);vi_time%(trial%(box%))/100; REM Safety feature: since trials are numbered 1-ntrials, REM I can use trial%-1 even from the first trial. REM Messy, but it's only cosmetic code anyway. REM And at least you know now. PRINTTAB(33,line%);vi_pokedur%(box%,trial%(box%)-1)/100;" "; PRINTTAB(46,line%);cs_pokedur%(box%,trial%(box%)-1)/100;" "; PRINTTAB(59,line%);us_pokedur%(box%,trial%(box%)-1)/100;" "; PRINTTAB(33,line%+1);vi_dursum%(box%)/100;" "; PRINTTAB(46,line%+1);cs_dursum%(box%)/100;" "; PRINTTAB(59,line%+1);us_dursum%(box%)/100;" "; PRINTTAB(73,line%+1);INT(FNratio_resp(cs_dursum%(box%),vi_dursum%(box%),cs_time% * (trial%(box%)-1),FNvi_time_until(trial%(box%)-1)));" "; REM The INT() function rounds down, so is wrong here. Oh well. PROCdisplay_state(box%) PROCdisplay_switch(box%) ENDPROC DEF PROCdisplay_state(box%) line% = FNdisplay_line(box%) PRINTTAB(21,line%); CASE state%(box%) OF WHEN null_state%: PRINT"Null"; WHEN vi_state%: PRINT"VI "; WHEN cs_state%: PRINT"CS "; WHEN us_state%: PRINT"US "; ENDCASE ENDPROC DEF PROCdisplay_switch(box%) IF box%=0 THEN PRINT"some berk is calling for box 0" line% = FNdisplay_line(box%) PRINTTAB(28,line%); CASE FNswitch(nosepoke%(box%),E%) OF WHEN On: PRINT"*"; WHEN Off: PRINT" "; ENDCASE ENDPROC DEF FNdisplay_line(box%) = display_firstline% + (box%-1)*2 DEF FNratio_resp(cs_poketime%, vi_poketime%, cs_total%, vi_total%) REM should be undefined if rat doesn't move (return -1) REM 0 if approach all in VI REM 100% if approach all in CS IF (cs_poketime% + vi_poketime%) = 0 THEN =-1 cs_proportion = cs_poketime% / cs_total% vi_proportion = vi_poketime% / vi_total% rr = cs_proportion / (cs_proportion + vi_proportion) = rr * 100 DEF FNvi_time_until(t%) sum%=0 FOR vtr%=1 TO t% sum%+=vi_time%(vtr%) NEXT =sum% DEF PROCfinal_output(box%) vi_timesum% = FNvi_time_until(ntrials%) cs_timesum% = cs_time% * ntrials% us_timesum% = us_time% * ntrials% vi_percent = 100 * vi_dursum%(box%) / vi_timesum% cs_percent = 100 * cs_dursum%(box%) / cs_timesum% us_percent = 100 * us_dursum%(box%) / us_timesum% final_rr(box%)=FNratio_resp(cs_dursum%(box%),vi_dursum%(box%),cs_timesum%,vi_timesum%) PRINT PRINT"Box ";box%;" - ";box_name$(box%);" (session ";session_number%(box%);")" PRINT"=====================================================================" PRINT" Trials = ";ntrials%;"; CS presented for ";cs_time%/100;"s; US presented for ";us_time%/100;"s" PRINT" VI-pokes = ";vi_pokesum%(box%);", VI-duration = ";vi_dursum%(box%)/100;"s, %time in during VI = ";vi_percent;"%" PRINT" CS-pokes = ";cs_pokesum%(box%);", CS-duration = ";cs_dursum%(box%)/100;"s, %time in during CS = ";cs_percent;"%" PRINT" US-pokes = ";us_pokesum%(box%);", US-duration = ";us_dursum%(box%)/100;"s, %time in during US = ";us_percent;"%" PRINT" Overall ratio of responding = ";final_rr(box%) PRINT VDU 3 : REM 27-Jan-98: the details bore me... :-) PRINT" Trial VI-time | VI# VIdur CS# CSdur US# USdur RResp" PRINT" ------------------+-------------------------------------------------" FOR t% = 1 TO ntrials% PRINTTAB(2);t%; PRINTTAB(9);vi_time%(t%)/100;"s"; PRINTTAB(20);"|"; PRINTTAB(22);vi_pokes%(box%,t%); PRINTTAB(26);vi_pokedur%(box%,t%)/100;"s"; PRINTTAB(37);cs_pokes%(box%,t%); PRINTTAB(41);cs_pokedur%(box%,t%)/100;"s"; PRINTTAB(51);us_pokes%(box%,t%); PRINTTAB(56);us_pokedur%(box%,t%)/100;"s"; PRINTTAB(65);FNratio_resp(cs_pokedur%(box%,t%),vi_pokedur%(box%,t%),cs_time%,vi_time%(t%)) NEXT PRINT REM0 1 2 3 4 5 6 ENDPROC