diff --git a/Examples/MultipleScreens/Stimuli/im1.png b/Examples/MultipleScreens/Stimuli/im1.png new file mode 100644 index 0000000000000000000000000000000000000000..daf95943e2fa305103fe89a1baf8b8432a557ace Binary files /dev/null and b/Examples/MultipleScreens/Stimuli/im1.png differ diff --git a/Examples/MultipleScreens/Stimuli/im2.png b/Examples/MultipleScreens/Stimuli/im2.png new file mode 100644 index 0000000000000000000000000000000000000000..a51860bcc506c505b6bb84bb022091e87092d0d2 Binary files /dev/null and b/Examples/MultipleScreens/Stimuli/im2.png differ diff --git a/Examples/MultipleScreens/Stimuli/im3.png b/Examples/MultipleScreens/Stimuli/im3.png new file mode 100644 index 0000000000000000000000000000000000000000..d01a1f3ff22d3ac098fd423681a7bd18d9debc8c Binary files /dev/null and b/Examples/MultipleScreens/Stimuli/im3.png differ diff --git a/Examples/MultipleScreens/Stimuli/im4.png b/Examples/MultipleScreens/Stimuli/im4.png new file mode 100644 index 0000000000000000000000000000000000000000..1f7b0d7fcea20e82368315d00f7f9327bf84bd3c Binary files /dev/null and b/Examples/MultipleScreens/Stimuli/im4.png differ diff --git a/Examples/MultipleScreens/exampleExtraScreens.m b/Examples/MultipleScreens/exampleExtraScreens.m new file mode 100644 index 0000000000000000000000000000000000000000..f1b2ec83cc8984883b6052f0053c066db4d1af4a --- /dev/null +++ b/Examples/MultipleScreens/exampleExtraScreens.m @@ -0,0 +1,54 @@ +% This is a simple example showing the use of multiple monitors with OTBR Toolbox. +% The main monitor can be used as usual. The extra monitors can currently only display images. + +% Tobias Otto +% 1.0 +% 07.04.2021 + +% 07.04.2021, Tobias: first draft + +%% Init toolbox +initOTBR + +%% Init variables +monitorA = 1; +monitorB = 2; + +%% Init screens and load images +try + initWindow(monitorB); % Main Screen on monitor 3 + initExtraWindow(monitorA); % Main Screen on monitor 2 + + % load stimuli + im1 = loadImage('.\Stimuli\im1.png'); + im2 = loadImage('.\Stimuli\im2.png'); + im3 = loadImage('.\Stimuli\im3.png'); + im4 = loadImage('.\Stimuli\im4.png'); + + %% Show two stimuli - as simple as possible + showStimuli(im1, 1); % Stimulus 1 on position 1 + showExtraStimuli(im2, monitorA) % Stimulus 2 on extra screen (see help showExtraStimuli for more info) + + WaitSecs(3); + + %% Show more stimuli - more complex + % Present two stimuli on main screen + % First stimulus with position as argument + showStimuli(im1, 1, 'dontFlip'); % Stimulus 1 on position 1 + % Second stimulus freely positioned + showStimuli(im2, 'x', 10, 'y', 80); + + % Present two stimuli on extra screen + showExtraStimuli(im3, monitorA, 'x', 50, 'y', 75, 'dontFlip'); + showExtraStimuli(im4, monitorA, 'x', 70, 'y', 10); + + WaitSecs(3); + + %% Clean up + closeExtraWindow(monitorA); + closeWindow; + +catch + closeWindow; + errorSave; +end \ No newline at end of file diff --git a/Examples/MultipleScreens/initOTBR.m b/Examples/MultipleScreens/initOTBR.m new file mode 100644 index 0000000000000000000000000000000000000000..363a0b2dd38e08f3782766f2d9ee517ca20bcca9 --- /dev/null +++ b/Examples/MultipleScreens/initOTBR.m @@ -0,0 +1,503 @@ +% initOTBR +% +% Call this script to use all functions of the OTBR Toolbox. +% +% Copyright 2007-2021 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://biopsytoolbox.sourceforge.net/ +% +% See also: mySetup + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.9 +% Last Change: 12.03.2021 + +% 17.05.2018, Tobias: release version based on start.m +% 07.06.2018, Tobias: split mySetup into hardware and paradigm specific files +% 14.06.2018, Tobias: avoid usage of temp +% 20.12.2018, Tobias: allowing raspberry3 as string +% 31.01.2019, Tobias: bugfix for raspberry3 ip interface +% 13.03.2019, Tobias: Turn off pagination (problem that less waits for user input) +% 16.07.2019, Tobias: define variable interface to avoid problem in deployed pograms +% 12.03.2020, Tobias: added init done at the end +% 14.08.2020, Tobias: added MATLAB version to global struct +% 08.09.2020, Tobias: bugfix for parallel port support +% 12.03.2021, Tobias: changed position of psychstart for new psych toolbox (3.0.16); added GStreamer info +% 18.03.2021, Tobias: added own PsychStartup version for new GStreamr versions + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% ----------------------------------------- +% | DO NOT edit anything beyond this line | +% ----------------------------------------- +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +year = datestr(now,'yyyy'); +clc +disp(' ==================================================================='); +disp(' Open Toolbox for Behavioral Research'); +disp(' ==================================================================='); +disp(' Developed at the Ruhr University in Bochum, '); +disp(' Institute of Cognitive Neuroscience'); +disp(' '); +disp(' Cite the Modular and Open Platform for Behavioral Research Toolbox'); +disp(' where any of the toolbox-functions are used or results obtained '); +disp(' using the toolbox are published.'); +disp(' '); +disp([' Copyright 2007-' year ', CC-GNU GPL by attribution']); +disp(' http://biopsytoolbox.sourceforge.net/'); +disp(' '); +disp(' '); +disp('Initializing ...'); + +%% Add global variables +global SETUP +global WINDOW +global NETWORK +NETWORK.num = 0; % Number of active network connections +WINDOW.handle = []; +on = 1; +off = 0; +interface = -1; + +%% Get setup information +if(~exist('myHardwareSetup', 'file') && exist('myParadigmSetup', 'file')) + disp(' ================================================================'); + disp(' Sorry, but I can''t find the file(s) myHardwareSetup.m') + disp(' or myParadigmSetup.m !'); + disp(' Please copy them into the current working directory'); + disp(' ================================================================'); + error('Please solve problem and try again'); +else + myHardwareSetup; + myParadigmSetup; +end + +% Order fields +SETUP = orderfields(SETUP); + +%% Initializations and minor computations +% For Monitor dimensions +SETUP.screen.width = SETUP.screen.width/SETUP.screen.ScreenWidth; +SETUP.screen.height = SETUP.screen.height/SETUP.screen.ScreenHeight; +SETUP.screen.X = SETUP.screen.X/SETUP.screen.ScreenWidth; +SETUP.screen.Y = SETUP.screen.Y/SETUP.screen.ScreenHeight; +% For screen color +SETUP.screen.bgColor= SETUP.screen.bgColor*255; + +% For touchscreen +SETUP.touchScreen.time = 0; +SETUP.touchScreen.lastTime = 0; +SETUP.touchScreen.localValue = [0 0]; +SETUP.touchScreen.lastLocalValue = [0 0]; +SETUP.touchScreen.globalValue = [0 0]; +SETUP.touchScreen.lastGlobalValue = [0 0]; +SETUP.touchScreen.screenIndex = 0; +SETUP.touchScreen.lastScreenIndex = 0; +SETUP.touchScreen.found = 0; +SETUP.touchScreen.offIndex = -1; +SETUP.touchScreen.deltaP = SETUP.touchScreen.deltaP/mean([SETUP.screen.ScreenWidth, SETUP.screen.ScreenHeight]); + +% For ephys (photo diode) +SETUP.ephys.photodiode = SETUP.ephys.photodiode./ ... + [SETUP.screen.ScreenWidth SETUP.screen.ScreenHeight SETUP.screen.ScreenWidth SETUP.screen.ScreenHeight]; + +% Convert strings of feeder type to numbers +switch lower(SETUP.io.feederType) + case 'time' + SETUP.io.feederType = 1; + case 'amount' + SETUP.io.feederType = 2; +end + +% Init variable to control initializations +SETUP.networkDIO.initDone = 0; % Set this value anyway, even if we use other hardware + +% Init raspberry +if(~isfield(SETUP.raspberry, 'num')) + SETUP.raspberry.num = 0; +end + +% General init done +SETUP.initDone = 0; + +%% Check inputs in myParadigmSetup & myHardwareSetup +% %%%%%%%%%%%%%%%%%%%%%%%% +if(any(SETUP.screen.X > 1)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' At least one entry of the left lower corner in X direction is '); + disp(' bigger than the screen width itself.'); + disp(' Check entries in section 1'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(any(SETUP.screen.Y > 1)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' At least one entry of the left lower corner in Y direction is '); + disp(' bigger than the screen height itself.'); + disp(' Check entries in section 1'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(length(SETUP.screen.X) ~= length(SETUP.screen.Y)) + disp(' ================================================================'); + disp(' Check entries in mySetup.m! '); + disp(' The number of switch positions in X and Y direction aren''t equal'); + disp(' Correct this problem in section 6'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(length(SETUP.screen.X)+length(SETUP.screen.Y) ~= ... + length(SETUP.screen.width)+length(SETUP.screen.height)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' The number of entries of switch positions aren''t equal to the'); + disp(' entries in switch width and height. Correct this problem in '); + disp(' section 1 or 2.'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(strcmpi(SETUP.io.interface, 'NetworkBox') && isempty(SETUP.networkDIO.odroidIP)) + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' The network IO device (ACN) needs an IP address as string'); + disp(' Correct this problem in section 2.'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +%% Set new state for rand !!! +% Get info about the environment +if(strcmpi(SETUP.platform, 'Windows64Bit')) + % Get SETUP.platformNum info + tmpVer = regexp(version, ' ', 'split'); + SETUP.platformNum = 1; + SETUP.MATLABVerStr = tmpVer{1}; + tmpVer = regexp(SETUP.MATLABVerStr, '\.', 'split'); + SETUP.MATLABVer = str2double(tmpVer{1})+str2double(tmpVer{2})*0.1; + + % Check MATLAB version + tmp0 = version('-release'); + tmp1 = tmp0(end); + tmp2 = str2double(tmp0(1:end-1)); + + if(tmp2 < 2012 || strcmpi('2012a', tmp0)) + error('This MATLAB version is too old. Please update at least to MATLAB 2012b'); + elseif(tmp2 >= 2012 && tmp2 < 2015) + RandStream.setGlobalStream(RandStream('mt19937ar','Seed',sum(100*clock))); + elseif(tmp2 >= 2015) + rng('shuffle'); + end +elseif(strcmpi(SETUP.platform, 'RaspberryPi2') || strcmpi(SETUP.platform, 'RaspberryPi3')) + % Get SETUP.platformNum info + SETUP.platformNum = 2; + SETUP.MATLABVerStr = ''; + SETUP.MATLABVer = []; + + % Put code here to initialize the random number generator! +else + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' The name of the SETUP.platform is wrong specified!'); + disp(' Correct this problem in section (1c)'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +% Doesn't hurt (for really old functions of the biopsy toolbox) +tic + +%% Add path for psychtoolbox toolbox - only if program is not deployed +if(~isdeployed) + % Check for correct entry of the Open Toolbox for Behavioral Research + if(exist([SETUP.toolboxPath filesep 'IO'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Network'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Screen'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Sound'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Tools'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Video'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Touchscreen'],'dir')) + disp(['--> Found Open Toolbox for Behavioral Research in: ' SETUP.toolboxPath]) + else + disp(' ===================================================================='); + disp(' Check the entry of the Biopsychology Toolbox path'); + disp(' There is no toolbox in this path or the folder structure is broken'); + disp(' Please correct this problem in the myHardwareSetup.m file in section 1a.'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Check for correct entry of the PsychToolbox (most important entries) + if(exist([SETUP.psychToolboxPath filesep 'PsychBasic'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychOneliners'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychRects'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychSound'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychJava'],'dir')) + disp(['--> Found Psychtoolbox in: ' SETUP.psychToolboxPath]) + else + disp(' ===================================================================='); + disp(' Check the entry of the Psych Toolbox path'); + disp(' There is no toolbox in this path or the folder structure is broken'); + disp(' Please correct this problem in the myHardwareSetup.m file in section 1b.'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Add path for helper function + addpath([SETUP.toolboxPath filesep 'Tools']); + + % Get all folders + psychPath = removeSVNEntriesFromString(genpath(SETUP.psychToolboxPath)); + + % Set path for psych toolbox + addpath(psychPath); + + %% Change paths according to the Psychophysics Toolbox PostInstRoutine + % Is this a Release2007a or later Matlab? + if(SETUP.platformNum == 1) + % This is a R2007a or post R2007a Matlab: + % Add MatlabWindowsFilesR2007a subfolder to Matlab path: + addpath([SETUP.psychToolboxPath '\PsychBasic\MatlabWindowsFilesR2007a\']); + disp('--> using Matlab release 2007a or later'); + elseif(SETUP.platformNum == 2) + % This is a Raspberry system + % Add Octave3LinuxFilesARM subfolder to Matlab path: + addpath([SETUP.psychToolboxPath '/PsychBasic/Octave3LinuxFilesARM/']); + disp('--> using Octave on Raspberry Pi'); + else + % This is a pre-R2007a Matlab: + % Add MatlabWindowsFilesR11 subfolder to Matlab path: + disp(' --------------------------------------------'); + disp(' --> Matlab release prior to R2007a detected.'); + disp(' Sorry this version is no longer supported!'); + disp(' Please upgrade to a newer version of MATLAB'); + disp(' --------------------------------------------'); + error('Please solve problem and try again'); + end + + %% Call PsychStartup for Psych toolbox + try + % Call original PsychStartup + PsychStartup; + + % If that didn't work try our version + gstr1 = getenv('GSTREAMER_1_0_ROOT_X86_64'); + gstr2 = getenv('GSTREAMER_1_0_ROOT_MSVC_X86_64'); + if(isempty(gstr1) && isempty(gstr2)) + PsychStartup_OTBR; + end + catch + disp(' ================================================================'); + disp(' Call of PsychStartup didn''t work out! You can use the Open '); + disp(' Toolbox for Behavioral Research anyway, but problems may occur!'); + disp(' Please install gstreamer on your system to use the Psychophysics'); + disp(' Toolbox completely!'); + disp(' '); + disp(' Make sure to install the MSVC gstreamer variant!'); + disp(' --> http://psychtoolbox.org/docs/GStreamer'); + disp(' ================================================================'); + end + + %% Set path for OTBR toolbox - only if program is not deployed + % Get all folders + bioPath = removeSVNEntriesFromString(genpath(SETUP.toolboxPath)); + + % Set path for biopsy toolbox finally + addpath(bioPath); + + % Check which interface is used + switch lower(SETUP.io.interface) + case 'parallel' + interface = 2; + case 'keyboard' + interface = 3; + case 'networkbox' + interface = 7; + case 'networkboxmatlab' + interface = 8; + case 'raspberrypi2' + interface = 9; + case 'raspberrypi3' + interface = 9; + case 'networkboxacn' + interface = 10; + otherwise + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' Please specify correct IO interface in myHardwareSetup. Correct this') + disp(' problem in section 2') + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Put path entry on top of the path list (like the psycho phsyics toolbox) + addpath([SETUP.toolboxPath filesep 'IO' filesep 'Interface' num2str(interface)]); + + % Save interface number to SETUP struct + SETUP.io.interfaceNum = interface; +end + +%% Copy necessary dlls into working directory - only if program is not deployed +% Check for windows and MATLAB systems only (SETUP.platformNum == 1) +foundHW = 0; +if(~isdeployed) + if(interface ~= 3 && SETUP.platformNum == 1) + curDir = cd; + switch interface + case 2 + if(isempty(dir('inpoutx64.dll'))) + copyfile([SETUP.toolboxPath '\IO\Interface2\inpoutx64.dll'] , curDir); + disp('Copied inpoutx64.dll (InpOut32Drv Driver Interface DLL) into the current working directory'); + disp('Modified for x64 compatibility and built by Phillip Gibbons (Phil@highrez.co.uk).'); + disp('See http://www.highrez.co.uk/Downloads/InpOut32 or the Highrez Forums (http://forums.highrez.co.uk) for information.'); + disp('Based on the original written by Logix4U (www.logix4u.net).'); + foundHW = 1; + end + end + end +end + +% Check for all systems (Windows and MATLAB/Octave and Raspberry), if we +% haven't found something yet +if(interface ~= 3 && foundHW == 0) + switch interface + case 7 + SETUP.networkDIO.initDone = 0; + case 8 + SETUP.networkDIO.initDone = 0; + case 9 + SETUP.raspberryPi2.initDone = 0; + case 10 + SETUP.networkDIO.initDone = 0; + end +end + +%% Set verbosity of Psychtoolbox +if(SETUP.platformNum == 1) + Screen('Preference', 'Verbosity', SETUP.psychToolbox.verbosity); +elseif(SETUP.platformNum == 2) + % This is a Raspberry system --> set to zero anyway! + % Otherwise Octave will get stuck and waits for space press + Screen('Preference', 'Verbosity', 0); + disp(' --> Setting psych toolbox verbosity to 0'); + % Turn off pagination (problem that less waits for user input) + more off +end + +%% Remap pin numbers to names for keyboard use during programming +SETUP.io.remapNames = []; % Struct where the names are stored + +% Copy field names and delete unnecessary names +names = fieldnames(SETUP.io); +names(strcmpi(names,'inputKeys')) = []; +names(strcmpi(names,'remapNames')) = []; +names(strcmpi(names,'interface')) = []; +names(strcmpi(names,'feederType')) = []; +names(strcmpi(names,'interfaceNum')) = []; +counter = 1; + +for i=1:length(names) + if(~isempty(SETUP.io.(names{i})) && ~ischar(SETUP.io.(names{i}))) + len = length(SETUP.io.(names{i})); + for j=1:len + if(SETUP.io.(names{i}) > 0) + SETUP.io.remapNames{1,SETUP.io.(names{i})(j)} = names{i}; + counter = counter + 1; + else + disp(' ================================================================'); + disp([' The value entered in SETUP.io.' names{i} ' is not valid !!!']); + disp(' Please enter values bigger than zero or an empty matrix []'); + disp(' if no device is attached. See myHardwareSetup.m for details'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + end + end +end + +%% Init timer (WINDOW only) +if(SETUP.platformNum == 1) + %% Init timer for manual shaping + if(SETUP.manualShaping.status == 1) + disp(' ================================================================'); + disp(' !!! Manual shaping mode is used !!!'); + disp(' Please call closeWindow to stop manual shaping.'); + disp(' ================================================================'); + + SETUP.manualShaping.reward = 0; + SETUP.manualShaping.timer = timer('TimerFcn','callback_manualShaping', ... + 'BusyMode','Queue', ... + 'Period',0.1 , ... + 'ExecutionMode','fixedSpacing'); + start(SETUP.manualShaping.timer); + end +end + +%% Add entry in struct for startStopDevice timers +% Let's start with 32 entries. If someone needs more it's added +% automatically. This is done here only for performance reasons +SETUP.startStop.startStopTimer = []; +SETUP.startStop.startStopTimerIndex = zeros(1,32); + +%% Init sound +% If enabled initialize the psych toolbox sound +if(SETUP.sound.enable == 1) + disp(' --> Initializing PsychToolbox Sound ...'); + % Delete handles if exist -> try catch in case InitializePsychSound + % wasn't called before + try + closeSound; + end + + % Init audio! + initSound; +end + +%% Init UDP viewer +% open udp communication with control pc +if isfield(SETUP,'udp') && ~isempty(SETUP.udp.ip) + SETUP.udp.obj = udp(SETUP.udp.ip, SETUP.udp.port, ... + 'localport', SETUP.udp.lclPort); + fopen(SETUP.udp.obj); +end + +%% Init memory mapped file +% create memory mapped file(s) for gaze-tracker start the tracker(s) +if ~isempty(SETUP.tracker.bufFleNme) + for i=1:length(SETUP.tracker.bufFleNme) + % create the memory mapped file for data exchange + if ~exist(SETUP.tracker.bufFleNme{i},'file') + f = fopen(SETUP.tracker.bufFleNme{i},'w+'); + fwrite(f,[0 0 0 0 0],'double'); + fclose(f); + end + % map file to memeoy + SETUP.tracker.bufFle{i} = ... + memmapfile(SETUP.tracker.bufFleNme{i},'Format','double'); + % % start each tracker on a separate worker + % SETUP.tracker.job{i} = batch(@startPointGrey,0,... + % {SETUP.tracker.cfgFleNme{i},SETUP.tracker.bufFleNme{i}}); + end +end + +%% Init naming scheme for keyboard answers +if(strcmpi(SETUP.io.interface, 'Keyboard')) + disp(' --> Init naming scheme for keyboard answers ...'); + KbName('UnifyKeyNames'); + disp(' done!'); +end + +%% Clear needless variables +clear names counter bioPath i j needless pos startPos interface curDir len +clear tmp0 k ans year tmp2 tmp1 pc psychPath foundHW tmpVer + +%% Done +% If we reach this point everything went probably fine ;) +SETUP.initDone = 1; +disp(' ... done'); diff --git a/Examples/MultipleScreens/myHardwareSetup.m b/Examples/MultipleScreens/myHardwareSetup.m new file mode 100644 index 0000000000000000000000000000000000000000..2e67270d272e0dfab79bc4b0f318139083d6c0ab --- /dev/null +++ b/Examples/MultipleScreens/myHardwareSetup.m @@ -0,0 +1,193 @@ +function myHardwareSetup +% This function initializes the OTBR Toolbox and sets all basic parameters +% of the hardware in your setup. This function needs to be stored on each +% experimental computer. All required hardware definitions are stored in +% the global variable SETUP created by this function. Edit this function when +% building a new setup. Description of specific field names and values can +% be found in the function.. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://sourceforge.net/projects/biopsytoolbox/ +% +% See also: initOTBR, myParadigmSetup + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.0 +% Last Change: 05.06.2018 +% +% 05.06.2018, Tobias: first draft + +%% Init global struct +global SETUP + +%% (1a) Please enter path to the BiopsyToolbox folder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SETUP.toolboxPath = 'D:\Matlab\OTBR-Toolbox-Gitlab\dev'; + +%% (1b) Please enter path to the Psychophysics Toolbox folder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SETUP.psychToolboxPath = 'D:\Matlab\PsychToolboxOrig\PsychToolbox3Orig_GitHub_Branches\Psychtoolbox-3.0.16\Psychtoolbox'; + +%% (1c) Please specify platform of your experimental computer +% This version of the Biopsy Toolbox supports these platforms: +% 'Windows64Bit' --> Computer with a 64 bit Windows operating system +% installed and a 64 bit MATLAB +% 'RaspberryPi3' --> Raspberry Pi with Octave +SETUP.platform = 'Windows64Bit'; + +%% (2) Please choose the IO device +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% The BiopsyToolbox provides nativ access to two different IO devices: the +% IO-Warrior 40 and a self made parallel port Interface. Please choose the +% interface you want to use. +% For training purposes the keyboard can be used as well. +% 'IOWarrior' -> Grants access to the IO-Warrior 40 (WINDOWS ONLY) +% 'Parallel' -> Grants access to the self made parallel port interface (WINDOWS ONLY) +% 'WarriorBox' -> Grants access to the self made IO-Warrior interface (WINDOWS ONLY) +% 'FBIScience' -> Grants access to the FBI-Science IO device (WINDOWS ONLY) +% 'NetworkBox' -> Grants access to the Odroid network IO device (Biopsychology) +% 'NetworkBoxACN'-> Grants access to the Odroid network IO device (ACN) +% 'Keyboard' -> For testing your program in your office using the keyboard +% 'RaspberryPi3' -> Grants access to the IO pins of the Raspberry 3B+ +SETUP.io.interface = 'Keyboard'; + +% Add IP address of NetworkBox as string here. E.g. '192.168.0.1' +% Otherwise leave this field empty! +SETUP.networkDIO.odroidIP = []; + +%% (3a) Please specify the OUTPUT devices +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each device is connected to a separate pin/output at the output device. +% Please specify the pin numbers for the devices used in the setup. Unused +% outputs should contain an empty matrix. +SETUP.io.houseLight = []; +SETUP.io.feeder = 4; +SETUP.io.feederLight = 5; +SETUP.io.feederPower = []; +SETUP.io.punishment = []; + +%% (3b) Please specify the feeder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This section defines if the amount of food given to a subject is defined +% in time (seconds) or in amount of food (e.g. number of pellets) +% Define time in seconds --> 'time' +% Define amount in exact numbers --> 'amount' +SETUP.io.feederType = 'time'; + +% Manual shaping (WINDOWS ONLY) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Set this value to one, if you want to manually reward by pressing the 'f' +SETUP.manualShaping.status = 0; + +%% (4) Please specify the number(s) of the INPUT pins +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each input device (e.g. real switch or pecking key) +% must be specified here. Physicsal switches are connected to a single +% pin/input to the IO device. Please be aware that the first pin number +% corresponds to the first input key (doesn't matter on which input pin the +% key is connected). That means that the first response key could be +% connected to pin 7 and the second response key to pin 3. That would +% result in [7 3]. +% If you are using touch screens only and no external input device you can +% leave this entry empty: SETUP.io.inputKeys = []; +SETUP.io.inputKeys = [1 2 3 5 6 7 8 9]; + +%% (5) Please specify monitor dimensions +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Screen width and height in centimeters +SETUP.screen.ScreenWidth = 16.5; +SETUP.screen.ScreenHeight = 12.8; + +%% (6) TOUCH SCREEN SETTINGS (WINDOWS ONLY) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Set this value to one, if you want to use a touch screen +SETUP.touchScreen.on = 0; + +% Set to one to plot a red dot to the recognised touch location +% Not recommended during experiments! +SETUP.touchScreen.display = 0; + +% Enter minimum time between two touches (in seconds) +SETUP.touchScreen.deltaT = 0.01; + +% Enter minimum distance between two touches (in centimeters) +% After call of initWindow the relative value is computed and replaced +SETUP.touchScreen.deltaP = 0.5; + +%% (7) Set Psych toolbox verbosity +% Set the verbosity of the psychophysics toolbox +% 0 - Disable all output - Same as using the SuppressAllWarnings flag. +% 1 - Only output critical errors. +% 2 - Output warnings as well. +% 3 - Output startup information and a bit of additional information. +% This is the default. +% 4 - Be pretty verbose about information and hints to optimize your code +% and system. +% 5 - Levels 5 and higher enable very verbose debugging output, mostly +% useful for debugging PTB itself, not generally useful for end-users. +% +% For normal use 1 is fine! +SETUP.psychToolbox.verbosity = 1; + +%% (8) UDP communication for remote status display +% contact details of the remote listener +SETUP.udp.ip = [];'192.168.2.1'; % remote ip +SETUP.udp.port = 5006; % remote port +SETUP.udp.lclPort = 5005; % local port + +%% (9) EPHYS SETTINGS +% %%%%%%%%%%%%%%%%%%%% +% Enter values for the white bar at the top of the screen. This is useful +% to get the exact timing when the stimulus was presented on the monitor. +% The coordinates are entered with reference to the left lower corner of +% the screen. +% The values are entered in centimeters and have the following meaning: +% 1. left lower corner, X coordinate +% 2. left lower corner, Y coordinate +% 3. width of bar +% 4. height of bar +% +% Defaults should be okay! + +% White bar at the top of the screen, stretched over the whole screen +% [left, bottom, width, height] +SETUP.ephys.photodiode = [0, SETUP.screen.ScreenHeight-2, SETUP.screen.ScreenWidth, SETUP.screen.ScreenHeight]; + +%% (10) Raspberry 3B+ SETTINGS +% %%%%%%%%%%%%%%%%%%%%%%%%%%% +% Enter the IP addresses of your Raspberry 3B+ as string systems here. +% The order of the IP addresses is important, because the system that is +% entered first is known as system one, the second system entered is known +% as system two, ... +% e.g. SETUP.raspberry.ip{1} = '192.168.0.20'; +% e.g. SETUP.raspberry.ip{2} = '192.168.0.21'; +SETUP.raspberry.ip{1} = []; + +%% (11) memory mapped file(s) for gaze tracker +% ---> more information needed +SETUP.tracker.bufFleNme = {}; % match fileNames in videoTracker.m +SETUP.tracker.cfgFleNme = {}; % configuration file of tracker +SETUP.tracker.bufFle = {}; % generate it using tracker.preview +SETUP.tracker.frameRate = 150; + +%% (12) Select trigger counter for MRI experiments +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% With this entry the program for recording the triggers is selected. +% The program is part of the OTBR Toolbox and will be started automatically +% if desired. +% Two options are available: +% * 'Parallel' --> to detect triggers at the parallel port +% * 'Network' --> to detect triggers with a network IO device +% * [] --> Leave empty if you want to start the program manually! +SETUP.MRI.triggerCounter = []; + +% Name of the application to be started. +% Please make sure that the application is located in the +% "Current Directory". If necessary, it must be copied from the folder +% <OTBR-Toolbox>\Tools\MRI-Counter into the "Current Directory" +% Leave empty if you want to start the program manually! +% e.g. SETUP.MRI.triggerCounterName = 'triggerCounter2_MCR9-2.exe'; +SETUP.MRI.triggerCounterName = []; diff --git a/Examples/MultipleScreens/myParadigmSetup.m b/Examples/MultipleScreens/myParadigmSetup.m new file mode 100644 index 0000000000000000000000000000000000000000..9bccd8f05d6f7fd0ef6d97d7ef1fc7be497f11ef --- /dev/null +++ b/Examples/MultipleScreens/myParadigmSetup.m @@ -0,0 +1,79 @@ +function myParadigmSetup +% This function initializes the OTBR Toolbox and sets all basic parameters +% of the SOFTWARE in your setup. This function needs to be stored on each +% experimental computer. All required hardware definitions are stored in +% the global variable SETUP created by this function. Edit this function when +% building a new setup. Description of specific field names and values can +% be found in the function.. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://sourceforge.net/projects/biopsytoolbox/ +% +% See also: initOTBR, myHardwareSetup + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.2 +% Last Change: 18.07.2019 +% +% 05.06.2018, Tobias: first draft +% 06.02.2019, Tobias: type punishment +% 18.07.2019, Tobias: typo in switch position comments + +%% Init global struct +global SETUP + +%% (1) Switch/Image positions in centimeters +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Specify the position of the left upper corner of the switch with +% reference to the left upper corner of the screen. +% REMEMBER for the TOUCH SCREEN that the first number entered in the +% following vectors corresponds to index 1 in the keyBuffer output. The +% second value to index 2 and so on. But only for touchscreens! +SETUP.screen.X = [0.001, 5.5, 11, 0.001, 5.5, 11, 0.001, 5.5, 11]; +SETUP.screen.Y = [0.1 0.1 0.1 4.2 4.2 4.2 8.3 8.3 8.3]; + +%% (2) Width and height of the switches/ images in centimeters +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Specify the width and height of the switches in centimeters +SETUP.screen.width = [5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.5]; +SETUP.screen.height = [4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2]; + +%% (3) Background color +% Change to background color from black to any another color using short +% names (pre defined in MATLAB) or a RGB triplet ranged from 0 to 1 +% +% [1 1 0] --> yellow +% [1 0 1] --> magenta +% [0 1 1] --> cyan +% [1 0 0] --> red +% [0 1 0] --> green +% [0 0 1] --> blue +% [1 1 1] --> white +% [0 0 0] --> black +% +% or anything between zero and one ... +SETUP.screen.bgColor = [0 0 0]; + +%% (4) Transparency +% Set this value to 1, if you want to enable transparency for your stimuli. +% This is a global setting and transparency has to be enabled for each +% stimulus individually during call of showStimuli +SETUP.screen.transparency = 1; + +%% (5) Sound support +% %%%%%%%%%%%%%%%%%%% +% Set this value to one to enable sound support. This should always be the +% case, because some functions use sound options. If this is disabled the +% keyBuffer will not work with the optional argument 'sound' and an error +% occurs. +SETUP.sound.enable = 0; + +%% (6) Image to be used as negative feedback +% If specified the given image will be shown during punishment! +% Specify the path to the image as string or leave empty otherwise! +% Image position is full screen by default: [0 0 1 1] +SETUP.punishment.img = []; +SETUP.punishment.imgPos = [0 0 1 1]; %[left, bottom, width, height] figure coordinate diff --git a/Examples/SkinnerBox_Autoshaping/Result/SeeResultsHere b/Examples/SkinnerBox_Autoshaping/Result/SeeResultsHere new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Examples/SkinnerBox_Autoshaping/Stimuli/stim1.png b/Examples/SkinnerBox_Autoshaping/Stimuli/stim1.png new file mode 100644 index 0000000000000000000000000000000000000000..766ece462e499e9b923432599a5d6ff2e931cf55 Binary files /dev/null and b/Examples/SkinnerBox_Autoshaping/Stimuli/stim1.png differ diff --git a/Examples/SkinnerBox_Autoshaping/Stimuli/stim2.png b/Examples/SkinnerBox_Autoshaping/Stimuli/stim2.png new file mode 100644 index 0000000000000000000000000000000000000000..18f8d2a82850c7ab197ba2c84ea0a6f54de44058 Binary files /dev/null and b/Examples/SkinnerBox_Autoshaping/Stimuli/stim2.png differ diff --git a/Examples/SkinnerBox_Autoshaping/autoshaping.m b/Examples/SkinnerBox_Autoshaping/autoshaping.m new file mode 100644 index 0000000000000000000000000000000000000000..f97b742b81c45c69b4c76988e962a0462c071c20 --- /dev/null +++ b/Examples/SkinnerBox_Autoshaping/autoshaping.m @@ -0,0 +1,80 @@ +function autoshaping +% Example paradigm for the OTBR Toolbox on Windows +% Make sure to define the screen befor you start this example: +% par.screenNum = 0; +% 0 = one screen only +% 1 = first windows screen +% 2 = second windows screen + +% Tobias Otto +% 1.0 +% 11.03.2021 + +% 11.03.2021, Tobias: first draft + +%% Init toolbox +initOTBR; + +%% Init variables for paradigm +par.screenNum = 2; % 0 = one screen only | 1= first screen | 2 = second screen +par.subject = 'ThisIsJustATest'; + +par.numTrials = 10; +par.iti = 3; % Iti in seconds +par.feeding = 2; % Feeding time in seconds +par.ansTime = 4; % Time in seconds to answer +par.numPecks = 1; % Number of pecks needed +par.stimPos = 5; % Position of stimulus (Positions are defined in myParadigmSetup.m) + +par.savePath = fullfile(pwd, 'Result'); +par.imPath = fullfile(pwd, 'Stimuli'); + +% Technical information +par.info = getBasicInformation; +par.infoText = 'Example autoshaping program'; + +%% Start loop +try + %% Init toolbox + initWindow(par.screenNum); + + %% Load stimuli + im = loadImage(fullfile(par.imPath, 'stim1.png')); + + %% Start main loop + % turn off all devices, clear screen and turn on the house light + startExperiment; + + % Start loop + for i=1:par.numTrials + %% Start ITI + showStimuli; + WaitSecs(par.iti); + + %% Show stimulus + showStimuli(im, par.stimPos); + + %% Wait for an answer + % Save response to get performance + out(i).ans = keyBuffer(par.ansTime, 'goodKey', par.stimPos, par.numPecks); + + %% Start feeding + feeding(par.feeding); + end + + %% Clean up and save + % Close window and cleans up + stopExperiment + + % Save + save2File(par, out, 'subject', par.subject, 'path', par.savePath) + + % User info + res = [out.ans]; + disp(['Performance of subject ' par.subject ': ' num2str(sum([res.goodKey])/par.numTrials*100) '%']); + +catch + closeWindow; + errorSave; +end + diff --git a/Examples/SkinnerBox_Autoshaping/initOTBR.m b/Examples/SkinnerBox_Autoshaping/initOTBR.m new file mode 100644 index 0000000000000000000000000000000000000000..639055646add51e649c3cb0744cb6077a0f214dc --- /dev/null +++ b/Examples/SkinnerBox_Autoshaping/initOTBR.m @@ -0,0 +1,490 @@ +% initOTBR +% +% Call this script to use all functions of the IKN Toolbox. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://biopsytoolbox.sourceforge.net/ +% +% See also: mySetup + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.9 +% Last Change: 08.09.2020 + +% 17.05.2018, Tobias: release version based on start.m +% 07.06.2018, Tobias: split mySetup into hardware and paradigm specific files +% 14.06.2018, Tobias: avoid usage of temp +% 20.12.2018, Tobias: allowing raspberry3 as string +% 31.01.2019, Tobias: bugfix for raspberry3 ip interface +% 13.03.2019, Tobias: Turn off pagination (problem that less waits for user input) +% 16.07.2019, Tobias: define variable interface to avoid problem in deployed pograms +% 12.03.2020, Tobias: added init done at the end +% 14.08.2020, Tobias: added MATLAB version to global struct +% 08.09.2020, Tobias: bugfix for parallel port support + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% ----------------------------------------- +% | DO NOT edit anything beyond this line | +% ----------------------------------------- +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +year = datestr(now,'yyyy'); +clc +disp(' ==================================================================='); +disp(' Open Toolbox for Behavioral Research'); +disp(' ==================================================================='); +disp(' Developed at the Ruhr University in Bochum, '); +disp(' Institute of Cognitive Neuroscience'); +disp(' '); +disp(' Cite the Modular and Open Platform for Behavioral Research Toolbox'); +disp(' where any of the toolbox-functions are used or results obtained '); +disp(' using the toolbox are published.'); +disp(' '); +disp([' Copyright 2007-' year ', CC-GNU GPL by attribution']); +disp(' http://biopsytoolbox.sourceforge.net/'); +disp(' '); +disp(' '); +disp('Initializing ...'); + +%% Add global variables +global SETUP +global WINDOW +global NETWORK +NETWORK.num = 0; % Number of active network connections +WINDOW.handle = []; +on = 1; +off = 0; +interface = -1; + +%% Get setup information +if(~exist('myHardwareSetup', 'file') && exist('myParadigmSetup', 'file')) + disp(' ================================================================'); + disp(' Sorry, but I can''t find the file(s) myHardwareSetup.m') + disp(' or myParadigmSetup.m !'); + disp(' Please copy them into the current working directory'); + disp(' ================================================================'); + error('Please solve problem and try again'); +else + myHardwareSetup; + myParadigmSetup; +end + +% Order fields +SETUP = orderfields(SETUP); + +%% Initializations and minor computations +% For Monitor dimensions +SETUP.screen.width = SETUP.screen.width/SETUP.screen.ScreenWidth; +SETUP.screen.height = SETUP.screen.height/SETUP.screen.ScreenHeight; +SETUP.screen.X = SETUP.screen.X/SETUP.screen.ScreenWidth; +SETUP.screen.Y = SETUP.screen.Y/SETUP.screen.ScreenHeight; +% For screen color +SETUP.screen.bgColor= SETUP.screen.bgColor*255; + +% For touchscreen +SETUP.touchScreen.time = 0; +SETUP.touchScreen.lastTime = 0; +SETUP.touchScreen.localValue = [0 0]; +SETUP.touchScreen.lastLocalValue = [0 0]; +SETUP.touchScreen.globalValue = [0 0]; +SETUP.touchScreen.lastGlobalValue = [0 0]; +SETUP.touchScreen.screenIndex = 0; +SETUP.touchScreen.lastScreenIndex = 0; +SETUP.touchScreen.found = 0; +SETUP.touchScreen.offIndex = -1; +SETUP.touchScreen.deltaP = SETUP.touchScreen.deltaP/mean([SETUP.screen.ScreenWidth, SETUP.screen.ScreenHeight]); + +% For ephys (photo diode) +SETUP.ephys.photodiode = SETUP.ephys.photodiode./ ... + [SETUP.screen.ScreenWidth SETUP.screen.ScreenHeight SETUP.screen.ScreenWidth SETUP.screen.ScreenHeight]; + +% Convert strings of feeder type to numbers +switch lower(SETUP.io.feederType) + case 'time' + SETUP.io.feederType = 1; + case 'amount' + SETUP.io.feederType = 2; +end + +% Init variable to control initializations +SETUP.networkDIO.initDone = 0; % Set this value anyway, even if we use other hardware + +% Init raspberry +if(~isfield(SETUP.raspberry, 'num')) + SETUP.raspberry.num = 0; +end + +% General init done +SETUP.initDone = 0; + +%% Check inputs in myParadigmSetup & myHardwareSetup +% %%%%%%%%%%%%%%%%%%%%%%%% +if(any(SETUP.screen.X > 1)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' At least one entry of the left lower corner in X direction is '); + disp(' bigger than the screen width itself.'); + disp(' Check entries in section 1'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(any(SETUP.screen.Y > 1)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' At least one entry of the left lower corner in Y direction is '); + disp(' bigger than the screen height itself.'); + disp(' Check entries in section 1'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(length(SETUP.screen.X) ~= length(SETUP.screen.Y)) + disp(' ================================================================'); + disp(' Check entries in mySetup.m! '); + disp(' The number of switch positions in X and Y direction aren''t equal'); + disp(' Correct this problem in section 6'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(length(SETUP.screen.X)+length(SETUP.screen.Y) ~= ... + length(SETUP.screen.width)+length(SETUP.screen.height)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' The number of entries of switch positions aren''t equal to the'); + disp(' entries in switch width and height. Correct this problem in '); + disp(' section 1 or 2.'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(strcmpi(SETUP.io.interface, 'NetworkBox') && isempty(SETUP.networkDIO.odroidIP)) + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' The network IO device (ACN) needs an IP address as string'); + disp(' Correct this problem in section 2.'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +%% Set new state for rand !!! +% Get info about the environment +if(strcmpi(SETUP.platform, 'Windows64Bit')) + % Get SETUP.platformNum info + tmpVer = regexp(version, ' ', 'split'); + SETUP.platformNum = 1; + SETUP.MATLABVerStr = tmpVer{1}; + tmpVer = regexp(SETUP.MATLABVerStr, '\.', 'split'); + SETUP.MATLABVer = str2double(tmpVer{1})+str2double(tmpVer{2})*0.1; + + % Check MATLAB version + tmp0 = version('-release'); + tmp1 = tmp0(end); + tmp2 = str2double(tmp0(1:end-1)); + + if(tmp2 < 2012 || strcmpi('2012a', tmp0)) + error('This MATLAB version is too old. Please update at least to MATLAB 2012b'); + elseif(tmp2 >= 2012 && tmp2 < 2015) + RandStream.setGlobalStream(RandStream('mt19937ar','Seed',sum(100*clock))); + elseif(tmp2 >= 2015) + rng('shuffle'); + end +elseif(strcmpi(SETUP.platform, 'RaspberryPi2') || strcmpi(SETUP.platform, 'RaspberryPi3')) + % Get SETUP.platformNum info + SETUP.platformNum = 2; + SETUP.MATLABVerStr = ''; + SETUP.MATLABVer = []; + + % Put code here to initialize the random number generator! +else + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' The name of the SETUP.platform is wrong specified!'); + disp(' Correct this problem in section (1c)'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +% Necessary for subfunctions ... since they use toc +tic + +%% Add path for psychtoolbox toolbox - only if program is not deployed +if(~isdeployed) + % Check for correct entry of the Open Toolbox for Behavioral Research + if(exist([SETUP.toolboxPath filesep 'IO'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Network'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Screen'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Sound'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Tools'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Video'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Touchscreen'],'dir')) + disp(['--> Found Open Toolbox for Behavioral Research in: ' SETUP.toolboxPath]) + else + disp(' ===================================================================='); + disp(' Check the entry of the Biopsychology Toolbox path'); + disp(' There is no toolbox in this path or the folder structure is broken'); + disp(' Please correct this problem in the myHardwareSetup.m file in section 1a.'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Check for correct entry of the PsychToolbox (most important entries) + if(exist([SETUP.psychToolboxPath filesep 'PsychBasic'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychOneliners'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychRects'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychSound'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychJava'],'dir')) + disp(['--> Found Psychtoolbox in: ' SETUP.psychToolboxPath]) + else + disp(' ===================================================================='); + disp(' Check the entry of the Psych Toolbox path'); + disp(' There is no toolbox in this path or the folder structure is broken'); + disp(' Please correct this problem in the myHardwareSetup.m file in section 1b.'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Add path for helper function + addpath([SETUP.toolboxPath filesep 'Tools']); + + % Get all folders + psychPath = removeSVNEntriesFromString(genpath(SETUP.psychToolboxPath)); + + % Set path for psych toolbox + addpath(psychPath); + + %% Change paths according to the Psychophysics Toolbox PostInstRoutine + % Is this a Release2007a or later Matlab? + if(SETUP.platformNum == 1) + % This is a R2007a or post R2007a Matlab: + % Add MatlabWindowsFilesR2007a subfolder to Matlab path: + addpath([SETUP.psychToolboxPath '\PsychBasic\MatlabWindowsFilesR2007a\']); + disp('--> using Matlab release 2007a or later'); + elseif(SETUP.platformNum == 2) + % This is a Raspberry system + % Add Octave3LinuxFilesARM subfolder to Matlab path: + addpath([SETUP.psychToolboxPath '/PsychBasic/Octave3LinuxFilesARM/']); + disp('--> using Octave on Raspberry Pi'); + else + % This is a pre-R2007a Matlab: + % Add MatlabWindowsFilesR11 subfolder to Matlab path: + disp(' --------------------------------------------'); + disp(' --> Matlab release prior to R2007a detected.'); + disp(' Sorry this version is no longer supported!'); + disp(' Please upgrade to a newer version of MATLAB'); + disp(' --------------------------------------------'); + error('Please solve problem and try again'); + end + + %% Set path for biopsy toolbox - only if program is not deployed + % Get all folders + bioPath = removeSVNEntriesFromString(genpath(SETUP.toolboxPath)); + + % Set path for biopsy toolbox finally + addpath(bioPath); + + % Check which interface is used + switch lower(SETUP.io.interface) + case 'parallel' + interface = 2; + case 'keyboard' + interface = 3; + case 'networkbox' + interface = 7; + case 'networkboxmatlab' + interface = 8; + case 'raspberrypi2' + interface = 9; + case 'raspberrypi3' + interface = 9; + case 'networkboxacn' + interface = 10; + otherwise + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' Please specify correct IO interface in myHardwareSetup. Correct this') + disp(' problem in section 2') + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Put path entry on top of the path list (like the psycho phsyics toolbox) + addpath([SETUP.toolboxPath filesep 'IO' filesep 'Interface' num2str(interface)]); + + % Save interface number to SETUP struct + SETUP.io.interfaceNum = interface; +end + +%% Copy necessary dlls into working directory - only if program is not deployed +% Check for windows and MATLAB systems only (SETUP.platformNum == 1) +foundHW = 0; +if(~isdeployed) + if(interface ~= 3 && SETUP.platformNum == 1) + curDir = cd; + switch interface + case 2 + if(isempty(dir('inpoutx64.dll'))) + copyfile([SETUP.toolboxPath '\IO\Interface2\inpoutx64.dll'] , curDir); + disp('Copied inpoutx64.dll (InpOut32Drv Driver Interface DLL) into the current working directory'); + disp('Modified for x64 compatibility and built by Phillip Gibbons (Phil@highrez.co.uk).'); + disp('See http://www.highrez.co.uk/Downloads/InpOut32 or the Highrez Forums (http://forums.highrez.co.uk) for information.'); + disp('Based on the original written by Logix4U (www.logix4u.net).'); + foundHW = 1; + end + end + end +end + +% Check for all systems (Windows and MATLAB/Octave and Raspberry), if we +% haven't found something yet +if(interface ~= 3 && foundHW == 0) + switch interface + case 7 + SETUP.networkDIO.initDone = 0; + case 8 + SETUP.networkDIO.initDone = 0; + case 9 + SETUP.raspberryPi2.initDone = 0; + case 10 + SETUP.networkDIO.initDone = 0; + end +end + +%% Set verbosity of Psychtoolbox +if(SETUP.platformNum == 1) + Screen('Preference', 'Verbosity', SETUP.psychToolbox.verbosity); +elseif(SETUP.platformNum == 2) + % This is a Raspberry system --> set to zero anyway! + % Otherwise Octave will get stuck and waits for space press + Screen('Preference', 'Verbosity', 0); + disp(' --> Setting psych toolbox verbosity to 0'); + % Turn off pagination (problem that less waits for user input) + more off +end + +%% Remap pin numbers to names for keyboard use during programming +SETUP.io.remapNames = []; % Struct where the names are stored + +% Copy field names and delete unnecessary names +names = fieldnames(SETUP.io); +names(strcmpi(names,'inputKeys')) = []; +names(strcmpi(names,'remapNames')) = []; +names(strcmpi(names,'interface')) = []; +names(strcmpi(names,'feederType')) = []; +names(strcmpi(names,'interfaceNum')) = []; +counter = 1; + +for i=1:length(names) + if(~isempty(SETUP.io.(names{i})) && ~ischar(SETUP.io.(names{i}))) + len = length(SETUP.io.(names{i})); + for j=1:len + if(SETUP.io.(names{i}) > 0) + SETUP.io.remapNames{1,SETUP.io.(names{i})(j)} = names{i}; + counter = counter + 1; + else + disp(' ================================================================'); + disp([' The value entered in SETUP.io.' names{i} ' is not valid !!!']); + disp(' Please enter values bigger than zero or an empty matrix []'); + disp(' if no device is attached. See myHardwareSetup.m for details'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + end + end +end + +%% Init timer (WINDOW only) +if(SETUP.platformNum == 1) + %% Init timer for manual shaping + if(SETUP.manualShaping.status == 1) + disp(' ================================================================'); + disp(' !!! Manual shaping mode is used !!!'); + disp(' Please call closeWindow to stop manual shaping.'); + disp(' ================================================================'); + + SETUP.manualShaping.reward = 0; + SETUP.manualShaping.timer = timer('TimerFcn','callback_manualShaping', ... + 'BusyMode','Queue', ... + 'Period',0.1 , ... + 'ExecutionMode','fixedSpacing'); + start(SETUP.manualShaping.timer); + end +end + +%% Add entry in struct for startStopDevice timers +% Let's start with 32 entries. If someone needs more it's added +% automatically. This is done here only for performance reasons +SETUP.startStop.startStopTimer = []; +SETUP.startStop.startStopTimerIndex = zeros(1,32); + +%% Init sound +% If enabled initialize the psych toolbox sound +if(SETUP.sound.enable == 1) + disp(' --> Initializing PsychToolbox Sound ...'); + % Delete handles if exist -> try catch in case InitializePsychSound + % wasn't called before + try + closeSound; + end + + % Init audio! + initSound; +end + +%% Init UDP viewer +% open udp communication with control pc +if isfield(SETUP,'udp') && ~isempty(SETUP.udp.ip) + SETUP.udp.obj = udp(SETUP.udp.ip, SETUP.udp.port, ... + 'localport', SETUP.udp.lclPort); + fopen(SETUP.udp.obj); +end + +%% Init memory mapped file +% create memory mapped file(s) for gaze-tracker start the tracker(s) +if ~isempty(SETUP.tracker.bufFleNme) + for i=1:length(SETUP.tracker.bufFleNme) + % create the memory mapped file for data exchange + if ~exist(SETUP.tracker.bufFleNme{i},'file') + f = fopen(SETUP.tracker.bufFleNme{i},'w+'); + fwrite(f,[0 0 0 0 0],'double'); + fclose(f); + end + % map file to memeoy + SETUP.tracker.bufFle{i} = ... + memmapfile(SETUP.tracker.bufFleNme{i},'Format','double'); + % % start each tracker on a separate worker + % SETUP.tracker.job{i} = batch(@startPointGrey,0,... + % {SETUP.tracker.cfgFleNme{i},SETUP.tracker.bufFleNme{i}}); + end +end + +%% Init naming scheme for keyboard answers +if(strcmpi(SETUP.io.interface, 'Keyboard')) + disp(' --> Init naming scheme for keyboard answers ...'); + KbName('UnifyKeyNames'); + disp(' done!'); +end + +%% Call PsychStartup finally +try + PsychStartup; +catch + disp(' ================================================================'); + disp(' Call of PsychStartup didn''t work out! You can use the Open '); + disp(' Toolbox for Behavioral Research anyway, but problems may occur!'); + disp(' Please install gstreamer on your system to use the Psychophysics'); + disp(' Toolbox completely!'); + disp(' ================================================================'); +end + +%% Clear needless variables +clear names counter bioPath i j needless pos startPos interface curDir len +clear tmp0 k ans year tmp2 tmp1 pc psychPath foundHW tmpVer + +%% Done +% If we reach this point everything went probably fine ;) +SETUP.initDone = 1; +disp(' ... done'); diff --git a/Examples/SkinnerBox_Autoshaping/myHardwareSetup.m b/Examples/SkinnerBox_Autoshaping/myHardwareSetup.m new file mode 100644 index 0000000000000000000000000000000000000000..b017bc4750c9c18e88b489df515088397951ddd5 --- /dev/null +++ b/Examples/SkinnerBox_Autoshaping/myHardwareSetup.m @@ -0,0 +1,193 @@ +function myHardwareSetup +% This function initializes the OTBR Toolbox and sets all basic parameters +% of the hardware in your setup. This function needs to be stored on each +% experimental computer. All required hardware definitions are stored in +% the global variable SETUP created by this function. Edit this function when +% building a new setup. Description of specific field names and values can +% be found in the function.. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://sourceforge.net/projects/biopsytoolbox/ +% +% See also: start + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.0 +% Last Change: 05.06.2018 +% +% 05.06.2018, Tobias: first draft + +%% Init global struct +global SETUP + +%% (1a) Please enter path to the BiopsyToolbox folder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SETUP.toolboxPath = 'D:\Matlab\IKNToolbox\OTBRToolbox'; + +%% (1b) Please enter path to the Psychophysics Toolbox folder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SETUP.psychToolboxPath = 'D:\Matlab\PsychToolboxOrig\PsychToolbox3Orig_GitHub_Branches\Psychtoolbox-3.0.15\Psychtoolbox'; + +%% (1c) Please specify platform of your experimental computer +% This version of the Biopsy Toolbox supports these platforms: +% 'Windows64Bit' --> Computer with a 64 bit Windows operating system +% installed and a 64 bit MATLAB +% 'RaspberryPi3' --> Raspberry Pi with Octave +SETUP.platform = 'Windows64Bit'; + +%% (2) Please choose the IO device +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% The BiopsyToolbox provides nativ access to two different IO devices: the +% IO-Warrior 40 and a self made parallel port Interface. Please choose the +% interface you want to use. +% For training purposes the keyboard can be used as well. +% 'IOWarrior' -> Grants access to the IO-Warrior 40 (WINDOWS ONLY) +% 'Parallel' -> Grants access to the self made parallel port interface (WINDOWS ONLY) +% 'WarriorBox' -> Grants access to the self made IO-Warrior interface (WINDOWS ONLY) +% 'FBIScience' -> Grants access to the FBI-Science IO device (WINDOWS ONLY) +% 'NetworkBox' -> Grants access to the Odroid network IO device (Biopsychology) +% 'NetworkBoxACN'-> Grants access to the Odroid network IO device (ACN) +% 'Keyboard' -> For testing your program in your office using the keyboard +% 'RaspberryPi3' -> Grants access to the IO pins of the Raspberry 3B+ +SETUP.io.interface = 'Keyboard'; + +% Add IP address of NetworkBox as string here. E.g. '192.168.0.1' +% Otherwise leave this field empty! +SETUP.networkDIO.odroidIP = []; + +%% (3a) Please specify the OUTPUT devices +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each device is connected to a separate pin/output at the output device. +% Please specify the pin numbers for the devices used in the setup. Unused +% outputs should contain an empty matrix. +SETUP.io.houseLight = []; +SETUP.io.feeder = 4; +SETUP.io.feederLight = 5; +SETUP.io.feederPower = []; +SETUP.io.punishment = []; + +%% (3b) Please specify the feeder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This section defines if the amount of food given to a subject is defined +% in time (seconds) or in amount of food (e.g. number of pellets) +% Define time in seconds --> 'time' +% Define amount in exact numbers --> 'amount' +SETUP.io.feederType = 'time'; + +% Manual shaping (WINDOWS ONLY) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Set this value to one, if you want to manually reward by pressing the 'f' +SETUP.manualShaping.status = 0; + +%% (4) Please specify the number(s) of the INPUT pins +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each input device (e.g. real switch or pecking key) +% must be specified here. Physicsal switches are connected to a single +% pin/input to the IO device. Please be aware that the first pin number +% corresponds to the first input key (doesn't matter on which input pin the +% key is connected). That means that the first response key could be +% connected to pin 7 and the second response key to pin 3. That would +% result in [7 3]. +% If you are using touch screens only and no external input device you can +% leave this entry empty: SETUP.io.inputKeys = []; +SETUP.io.inputKeys = [1 2 3 5 6 7 8 9]; + +%% (5) Please specify monitor dimensions +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Screen width and height in centimeters +SETUP.screen.ScreenWidth = 16.5; +SETUP.screen.ScreenHeight = 12.8; + +%% (6) TOUCH SCREEN SETTINGS (WINDOWS ONLY) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Set this value to one, if you want to use a touch screen +SETUP.touchScreen.on = 0; + +% Set to one to plot a red dot to the recognised touch location +% Not recommended during experiments! +SETUP.touchScreen.display = 0; + +% Enter minimum time between two touches (in seconds) +SETUP.touchScreen.deltaT = 0.01; + +% Enter minimum distance between two touches (in centimeters) +% After call of initWindow the relative value is computed and replaced +SETUP.touchScreen.deltaP = 0.5; + +%% (7) Set Psych toolbox verbosity +% Set the verbosity of the psychophysics toolbox +% 0 - Disable all output - Same as using the SuppressAllWarnings flag. +% 1 - Only output critical errors. +% 2 - Output warnings as well. +% 3 - Output startup information and a bit of additional information. +% This is the default. +% 4 - Be pretty verbose about information and hints to optimize your code +% and system. +% 5 - Levels 5 and higher enable very verbose debugging output, mostly +% useful for debugging PTB itself, not generally useful for end-users. +% +% For normal use 1 is fine! +SETUP.psychToolbox.verbosity = 1; + +%% (8) UDP communication for remote status display +% contact details of the remote listener +SETUP.udp.ip = [];'192.168.2.1'; % remote ip +SETUP.udp.port = 5006; % remote port +SETUP.udp.lclPort = 5005; % local port + +%% (9) EPHYS SETTINGS +% %%%%%%%%%%%%%%%%%%%% +% Enter values for the white bar at the top of the screen. This is useful +% to get the exact timing when the stimulus was presented on the monitor. +% The coordinates are entered with reference to the left lower corner of +% the screen. +% The values are entered in centimeters and have the following meaning: +% 1. left lower corner, X coordinate +% 2. left lower corner, Y coordinate +% 3. width of bar +% 4. height of bar +% +% Defaults should be okay! + +% White bar at the top of the screen, stretched over the whole screen +% [left, bottom, width, height] +SETUP.ephys.photodiode = [0, SETUP.screen.ScreenHeight-2, SETUP.screen.ScreenWidth, SETUP.screen.ScreenHeight]; + +%% (10) Raspberry 3B+ SETTINGS +% %%%%%%%%%%%%%%%%%%%%%%%%%%% +% Enter the IP addresses of your Raspberry 3B+ as string systems here. +% The order of the IP addresses is important, because the system that is +% entered first is known as system one, the second system entered is known +% as system two, ... +% e.g. SETUP.raspberry.ip{1} = '192.168.0.20'; +% e.g. SETUP.raspberry.ip{2} = '192.168.0.21'; +SETUP.raspberry.ip{1} = []; + +%% (11) memory mapped file(s) for gaze tracker +% ---> more information needed +SETUP.tracker.bufFleNme = {}; % match fileNames in videoTracker.m +SETUP.tracker.cfgFleNme = {}; % configuration file of tracker +SETUP.tracker.bufFle = {}; % generate it using tracker.preview +SETUP.tracker.frameRate = 150; + +%% (12) Select trigger counter for MRI experiments +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% With this entry the program for recording the triggers is selected. +% The program is part of the OTBR Toolbox and will be started automatically +% if desired. +% Two options are available: +% * 'Parallel' --> to detect triggers at the parallel port +% * 'Network' --> to detect triggers with a network IO device +% * [] --> Leave empty if you want to start the program manually! +SETUP.MRI.triggerCounter = []; + +% Name of the application to be started. +% Please make sure that the application is located in the +% "Current Directory". If necessary, it must be copied from the folder +% <OTBR-Toolbox>\Tools\MRI-Counter into the "Current Directory" +% Leave empty if you want to start the program manually! +% e.g. SETUP.MRI.triggerCounterName = 'triggerCounter2_MCR9-2.exe'; +SETUP.MRI.triggerCounterName = []; diff --git a/Examples/SkinnerBox_Autoshaping/myParadigmSetup.m b/Examples/SkinnerBox_Autoshaping/myParadigmSetup.m new file mode 100644 index 0000000000000000000000000000000000000000..a75bd6b4ab7c8fc0517e5324a6aca52f5c744c70 --- /dev/null +++ b/Examples/SkinnerBox_Autoshaping/myParadigmSetup.m @@ -0,0 +1,79 @@ +function myParadigmSetup +% This function initializes the OTBR Toolbox and sets all basic parameters +% of the SOFTWARE in your setup. This function needs to be stored on each +% experimental computer. All required hardware definitions are stored in +% the global variable SETUP created by this function. Edit this function when +% building a new setup. Description of specific field names and values can +% be found in the function.. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://sourceforge.net/projects/biopsytoolbox/ +% +% See also: start + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.2 +% Last Change: 18.07.2019 +% +% 05.06.2018, Tobias: first draft +% 06.02.2019, Tobias: type punishment +% 18.07.2019, Tobias: typo in switch position comments + +%% Init global struct +global SETUP + +%% (1) Switch/Image positions in centimeters +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Specify the position of the left upper corner of the switch with +% reference to the left upper corner of the screen. +% REMEMBER for the TOUCH SCREEN that the first number entered in the +% following vectors corresponds to index 1 in the keyBuffer output. The +% second value to index 2 and so on. But only for touchscreens! +SETUP.screen.X = [0.001, 5.5, 11, 0.001, 5.5, 11, 0.001, 5.5, 11]; +SETUP.screen.Y = [0.1 0.1 0.1 4.2 4.2 4.2 8.3 8.3 8.3]; + +%% (2) Width and height of the switches/ images in centimeters +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Specify the width and height of the switches in centimeters +SETUP.screen.width = [5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.5]; +SETUP.screen.height = [4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2]; + +%% (3) Background color +% Change to background color from black to any another color using short +% names (pre defined in MATLAB) or a RGB triplet ranged from 0 to 1 +% +% [1 1 0] --> yellow +% [1 0 1] --> magenta +% [0 1 1] --> cyan +% [1 0 0] --> red +% [0 1 0] --> green +% [0 0 1] --> blue +% [1 1 1] --> white +% [0 0 0] --> black +% +% or anything between zero and one ... +SETUP.screen.bgColor = [0 0 0]; + +%% (4) Transparency +% Set this value to 1, if you want to enable transparency for your stimuli. +% This is a global setting and transparency has to be enabled for each +% stimulus individually during call of showStimuli +SETUP.screen.transparency = 1; + +%% (5) Sound support +% %%%%%%%%%%%%%%%%%%% +% Set this value to one to enable sound support. This should always be the +% case, because some functions use sound options. If this is disabled the +% keyBuffer will not work with the optional argument 'sound' and an error +% occurs. +SETUP.sound.enable = 0; + +%% (6) Image to be used as negative feedback +% If specified the given image will be shown during punishment! +% Specify the path to the image as string or leave empty otherwise! +% Image position is full screen by default: [0 0 1 1] +SETUP.punishment.img = []; +SETUP.punishment.imgPos = [0 0 1 1]; %[left, bottom, width, height] figure coordinate diff --git a/Examples/SkinnerBox_SimpleExample/Result/SeeResultsHere b/Examples/SkinnerBox_SimpleExample/Result/SeeResultsHere new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Examples/SkinnerBox_SimpleExample/Stimuli/stim1.png b/Examples/SkinnerBox_SimpleExample/Stimuli/stim1.png new file mode 100644 index 0000000000000000000000000000000000000000..766ece462e499e9b923432599a5d6ff2e931cf55 Binary files /dev/null and b/Examples/SkinnerBox_SimpleExample/Stimuli/stim1.png differ diff --git a/Examples/SkinnerBox_SimpleExample/Stimuli/stim2.png b/Examples/SkinnerBox_SimpleExample/Stimuli/stim2.png new file mode 100644 index 0000000000000000000000000000000000000000..18f8d2a82850c7ab197ba2c84ea0a6f54de44058 Binary files /dev/null and b/Examples/SkinnerBox_SimpleExample/Stimuli/stim2.png differ diff --git a/Examples/SkinnerBox_SimpleExample/initOTBR.m b/Examples/SkinnerBox_SimpleExample/initOTBR.m new file mode 100644 index 0000000000000000000000000000000000000000..639055646add51e649c3cb0744cb6077a0f214dc --- /dev/null +++ b/Examples/SkinnerBox_SimpleExample/initOTBR.m @@ -0,0 +1,490 @@ +% initOTBR +% +% Call this script to use all functions of the IKN Toolbox. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://biopsytoolbox.sourceforge.net/ +% +% See also: mySetup + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.9 +% Last Change: 08.09.2020 + +% 17.05.2018, Tobias: release version based on start.m +% 07.06.2018, Tobias: split mySetup into hardware and paradigm specific files +% 14.06.2018, Tobias: avoid usage of temp +% 20.12.2018, Tobias: allowing raspberry3 as string +% 31.01.2019, Tobias: bugfix for raspberry3 ip interface +% 13.03.2019, Tobias: Turn off pagination (problem that less waits for user input) +% 16.07.2019, Tobias: define variable interface to avoid problem in deployed pograms +% 12.03.2020, Tobias: added init done at the end +% 14.08.2020, Tobias: added MATLAB version to global struct +% 08.09.2020, Tobias: bugfix for parallel port support + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% ----------------------------------------- +% | DO NOT edit anything beyond this line | +% ----------------------------------------- +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +year = datestr(now,'yyyy'); +clc +disp(' ==================================================================='); +disp(' Open Toolbox for Behavioral Research'); +disp(' ==================================================================='); +disp(' Developed at the Ruhr University in Bochum, '); +disp(' Institute of Cognitive Neuroscience'); +disp(' '); +disp(' Cite the Modular and Open Platform for Behavioral Research Toolbox'); +disp(' where any of the toolbox-functions are used or results obtained '); +disp(' using the toolbox are published.'); +disp(' '); +disp([' Copyright 2007-' year ', CC-GNU GPL by attribution']); +disp(' http://biopsytoolbox.sourceforge.net/'); +disp(' '); +disp(' '); +disp('Initializing ...'); + +%% Add global variables +global SETUP +global WINDOW +global NETWORK +NETWORK.num = 0; % Number of active network connections +WINDOW.handle = []; +on = 1; +off = 0; +interface = -1; + +%% Get setup information +if(~exist('myHardwareSetup', 'file') && exist('myParadigmSetup', 'file')) + disp(' ================================================================'); + disp(' Sorry, but I can''t find the file(s) myHardwareSetup.m') + disp(' or myParadigmSetup.m !'); + disp(' Please copy them into the current working directory'); + disp(' ================================================================'); + error('Please solve problem and try again'); +else + myHardwareSetup; + myParadigmSetup; +end + +% Order fields +SETUP = orderfields(SETUP); + +%% Initializations and minor computations +% For Monitor dimensions +SETUP.screen.width = SETUP.screen.width/SETUP.screen.ScreenWidth; +SETUP.screen.height = SETUP.screen.height/SETUP.screen.ScreenHeight; +SETUP.screen.X = SETUP.screen.X/SETUP.screen.ScreenWidth; +SETUP.screen.Y = SETUP.screen.Y/SETUP.screen.ScreenHeight; +% For screen color +SETUP.screen.bgColor= SETUP.screen.bgColor*255; + +% For touchscreen +SETUP.touchScreen.time = 0; +SETUP.touchScreen.lastTime = 0; +SETUP.touchScreen.localValue = [0 0]; +SETUP.touchScreen.lastLocalValue = [0 0]; +SETUP.touchScreen.globalValue = [0 0]; +SETUP.touchScreen.lastGlobalValue = [0 0]; +SETUP.touchScreen.screenIndex = 0; +SETUP.touchScreen.lastScreenIndex = 0; +SETUP.touchScreen.found = 0; +SETUP.touchScreen.offIndex = -1; +SETUP.touchScreen.deltaP = SETUP.touchScreen.deltaP/mean([SETUP.screen.ScreenWidth, SETUP.screen.ScreenHeight]); + +% For ephys (photo diode) +SETUP.ephys.photodiode = SETUP.ephys.photodiode./ ... + [SETUP.screen.ScreenWidth SETUP.screen.ScreenHeight SETUP.screen.ScreenWidth SETUP.screen.ScreenHeight]; + +% Convert strings of feeder type to numbers +switch lower(SETUP.io.feederType) + case 'time' + SETUP.io.feederType = 1; + case 'amount' + SETUP.io.feederType = 2; +end + +% Init variable to control initializations +SETUP.networkDIO.initDone = 0; % Set this value anyway, even if we use other hardware + +% Init raspberry +if(~isfield(SETUP.raspberry, 'num')) + SETUP.raspberry.num = 0; +end + +% General init done +SETUP.initDone = 0; + +%% Check inputs in myParadigmSetup & myHardwareSetup +% %%%%%%%%%%%%%%%%%%%%%%%% +if(any(SETUP.screen.X > 1)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' At least one entry of the left lower corner in X direction is '); + disp(' bigger than the screen width itself.'); + disp(' Check entries in section 1'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(any(SETUP.screen.Y > 1)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' At least one entry of the left lower corner in Y direction is '); + disp(' bigger than the screen height itself.'); + disp(' Check entries in section 1'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(length(SETUP.screen.X) ~= length(SETUP.screen.Y)) + disp(' ================================================================'); + disp(' Check entries in mySetup.m! '); + disp(' The number of switch positions in X and Y direction aren''t equal'); + disp(' Correct this problem in section 6'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(length(SETUP.screen.X)+length(SETUP.screen.Y) ~= ... + length(SETUP.screen.width)+length(SETUP.screen.height)) + disp(' ================================================================'); + disp(' Check entries in myParadigmSetup.m! '); + disp(' The number of entries of switch positions aren''t equal to the'); + disp(' entries in switch width and height. Correct this problem in '); + disp(' section 1 or 2.'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +if(strcmpi(SETUP.io.interface, 'NetworkBox') && isempty(SETUP.networkDIO.odroidIP)) + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' The network IO device (ACN) needs an IP address as string'); + disp(' Correct this problem in section 2.'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +%% Set new state for rand !!! +% Get info about the environment +if(strcmpi(SETUP.platform, 'Windows64Bit')) + % Get SETUP.platformNum info + tmpVer = regexp(version, ' ', 'split'); + SETUP.platformNum = 1; + SETUP.MATLABVerStr = tmpVer{1}; + tmpVer = regexp(SETUP.MATLABVerStr, '\.', 'split'); + SETUP.MATLABVer = str2double(tmpVer{1})+str2double(tmpVer{2})*0.1; + + % Check MATLAB version + tmp0 = version('-release'); + tmp1 = tmp0(end); + tmp2 = str2double(tmp0(1:end-1)); + + if(tmp2 < 2012 || strcmpi('2012a', tmp0)) + error('This MATLAB version is too old. Please update at least to MATLAB 2012b'); + elseif(tmp2 >= 2012 && tmp2 < 2015) + RandStream.setGlobalStream(RandStream('mt19937ar','Seed',sum(100*clock))); + elseif(tmp2 >= 2015) + rng('shuffle'); + end +elseif(strcmpi(SETUP.platform, 'RaspberryPi2') || strcmpi(SETUP.platform, 'RaspberryPi3')) + % Get SETUP.platformNum info + SETUP.platformNum = 2; + SETUP.MATLABVerStr = ''; + SETUP.MATLABVer = []; + + % Put code here to initialize the random number generator! +else + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' The name of the SETUP.platform is wrong specified!'); + disp(' Correct this problem in section (1c)'); + disp(' ================================================================'); + error('Please solve problem and try again'); +end + +% Necessary for subfunctions ... since they use toc +tic + +%% Add path for psychtoolbox toolbox - only if program is not deployed +if(~isdeployed) + % Check for correct entry of the Open Toolbox for Behavioral Research + if(exist([SETUP.toolboxPath filesep 'IO'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Network'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Screen'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Sound'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Tools'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Video'],'dir') && ... + exist([SETUP.toolboxPath filesep 'Touchscreen'],'dir')) + disp(['--> Found Open Toolbox for Behavioral Research in: ' SETUP.toolboxPath]) + else + disp(' ===================================================================='); + disp(' Check the entry of the Biopsychology Toolbox path'); + disp(' There is no toolbox in this path or the folder structure is broken'); + disp(' Please correct this problem in the myHardwareSetup.m file in section 1a.'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Check for correct entry of the PsychToolbox (most important entries) + if(exist([SETUP.psychToolboxPath filesep 'PsychBasic'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychOneliners'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychRects'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychSound'],'dir') && ... + exist([SETUP.psychToolboxPath filesep 'PsychJava'],'dir')) + disp(['--> Found Psychtoolbox in: ' SETUP.psychToolboxPath]) + else + disp(' ===================================================================='); + disp(' Check the entry of the Psych Toolbox path'); + disp(' There is no toolbox in this path or the folder structure is broken'); + disp(' Please correct this problem in the myHardwareSetup.m file in section 1b.'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Add path for helper function + addpath([SETUP.toolboxPath filesep 'Tools']); + + % Get all folders + psychPath = removeSVNEntriesFromString(genpath(SETUP.psychToolboxPath)); + + % Set path for psych toolbox + addpath(psychPath); + + %% Change paths according to the Psychophysics Toolbox PostInstRoutine + % Is this a Release2007a or later Matlab? + if(SETUP.platformNum == 1) + % This is a R2007a or post R2007a Matlab: + % Add MatlabWindowsFilesR2007a subfolder to Matlab path: + addpath([SETUP.psychToolboxPath '\PsychBasic\MatlabWindowsFilesR2007a\']); + disp('--> using Matlab release 2007a or later'); + elseif(SETUP.platformNum == 2) + % This is a Raspberry system + % Add Octave3LinuxFilesARM subfolder to Matlab path: + addpath([SETUP.psychToolboxPath '/PsychBasic/Octave3LinuxFilesARM/']); + disp('--> using Octave on Raspberry Pi'); + else + % This is a pre-R2007a Matlab: + % Add MatlabWindowsFilesR11 subfolder to Matlab path: + disp(' --------------------------------------------'); + disp(' --> Matlab release prior to R2007a detected.'); + disp(' Sorry this version is no longer supported!'); + disp(' Please upgrade to a newer version of MATLAB'); + disp(' --------------------------------------------'); + error('Please solve problem and try again'); + end + + %% Set path for biopsy toolbox - only if program is not deployed + % Get all folders + bioPath = removeSVNEntriesFromString(genpath(SETUP.toolboxPath)); + + % Set path for biopsy toolbox finally + addpath(bioPath); + + % Check which interface is used + switch lower(SETUP.io.interface) + case 'parallel' + interface = 2; + case 'keyboard' + interface = 3; + case 'networkbox' + interface = 7; + case 'networkboxmatlab' + interface = 8; + case 'raspberrypi2' + interface = 9; + case 'raspberrypi3' + interface = 9; + case 'networkboxacn' + interface = 10; + otherwise + disp(' ================================================================'); + disp(' Check entries in myHardwareSetup.m! '); + disp(' Please specify correct IO interface in myHardwareSetup. Correct this') + disp(' problem in section 2') + disp(' ================================================================'); + error('Please solve problem and try again'); + end + + % Put path entry on top of the path list (like the psycho phsyics toolbox) + addpath([SETUP.toolboxPath filesep 'IO' filesep 'Interface' num2str(interface)]); + + % Save interface number to SETUP struct + SETUP.io.interfaceNum = interface; +end + +%% Copy necessary dlls into working directory - only if program is not deployed +% Check for windows and MATLAB systems only (SETUP.platformNum == 1) +foundHW = 0; +if(~isdeployed) + if(interface ~= 3 && SETUP.platformNum == 1) + curDir = cd; + switch interface + case 2 + if(isempty(dir('inpoutx64.dll'))) + copyfile([SETUP.toolboxPath '\IO\Interface2\inpoutx64.dll'] , curDir); + disp('Copied inpoutx64.dll (InpOut32Drv Driver Interface DLL) into the current working directory'); + disp('Modified for x64 compatibility and built by Phillip Gibbons (Phil@highrez.co.uk).'); + disp('See http://www.highrez.co.uk/Downloads/InpOut32 or the Highrez Forums (http://forums.highrez.co.uk) for information.'); + disp('Based on the original written by Logix4U (www.logix4u.net).'); + foundHW = 1; + end + end + end +end + +% Check for all systems (Windows and MATLAB/Octave and Raspberry), if we +% haven't found something yet +if(interface ~= 3 && foundHW == 0) + switch interface + case 7 + SETUP.networkDIO.initDone = 0; + case 8 + SETUP.networkDIO.initDone = 0; + case 9 + SETUP.raspberryPi2.initDone = 0; + case 10 + SETUP.networkDIO.initDone = 0; + end +end + +%% Set verbosity of Psychtoolbox +if(SETUP.platformNum == 1) + Screen('Preference', 'Verbosity', SETUP.psychToolbox.verbosity); +elseif(SETUP.platformNum == 2) + % This is a Raspberry system --> set to zero anyway! + % Otherwise Octave will get stuck and waits for space press + Screen('Preference', 'Verbosity', 0); + disp(' --> Setting psych toolbox verbosity to 0'); + % Turn off pagination (problem that less waits for user input) + more off +end + +%% Remap pin numbers to names for keyboard use during programming +SETUP.io.remapNames = []; % Struct where the names are stored + +% Copy field names and delete unnecessary names +names = fieldnames(SETUP.io); +names(strcmpi(names,'inputKeys')) = []; +names(strcmpi(names,'remapNames')) = []; +names(strcmpi(names,'interface')) = []; +names(strcmpi(names,'feederType')) = []; +names(strcmpi(names,'interfaceNum')) = []; +counter = 1; + +for i=1:length(names) + if(~isempty(SETUP.io.(names{i})) && ~ischar(SETUP.io.(names{i}))) + len = length(SETUP.io.(names{i})); + for j=1:len + if(SETUP.io.(names{i}) > 0) + SETUP.io.remapNames{1,SETUP.io.(names{i})(j)} = names{i}; + counter = counter + 1; + else + disp(' ================================================================'); + disp([' The value entered in SETUP.io.' names{i} ' is not valid !!!']); + disp(' Please enter values bigger than zero or an empty matrix []'); + disp(' if no device is attached. See myHardwareSetup.m for details'); + disp(' ================================================================'); + error('Please solve problem and try again'); + end + end + end +end + +%% Init timer (WINDOW only) +if(SETUP.platformNum == 1) + %% Init timer for manual shaping + if(SETUP.manualShaping.status == 1) + disp(' ================================================================'); + disp(' !!! Manual shaping mode is used !!!'); + disp(' Please call closeWindow to stop manual shaping.'); + disp(' ================================================================'); + + SETUP.manualShaping.reward = 0; + SETUP.manualShaping.timer = timer('TimerFcn','callback_manualShaping', ... + 'BusyMode','Queue', ... + 'Period',0.1 , ... + 'ExecutionMode','fixedSpacing'); + start(SETUP.manualShaping.timer); + end +end + +%% Add entry in struct for startStopDevice timers +% Let's start with 32 entries. If someone needs more it's added +% automatically. This is done here only for performance reasons +SETUP.startStop.startStopTimer = []; +SETUP.startStop.startStopTimerIndex = zeros(1,32); + +%% Init sound +% If enabled initialize the psych toolbox sound +if(SETUP.sound.enable == 1) + disp(' --> Initializing PsychToolbox Sound ...'); + % Delete handles if exist -> try catch in case InitializePsychSound + % wasn't called before + try + closeSound; + end + + % Init audio! + initSound; +end + +%% Init UDP viewer +% open udp communication with control pc +if isfield(SETUP,'udp') && ~isempty(SETUP.udp.ip) + SETUP.udp.obj = udp(SETUP.udp.ip, SETUP.udp.port, ... + 'localport', SETUP.udp.lclPort); + fopen(SETUP.udp.obj); +end + +%% Init memory mapped file +% create memory mapped file(s) for gaze-tracker start the tracker(s) +if ~isempty(SETUP.tracker.bufFleNme) + for i=1:length(SETUP.tracker.bufFleNme) + % create the memory mapped file for data exchange + if ~exist(SETUP.tracker.bufFleNme{i},'file') + f = fopen(SETUP.tracker.bufFleNme{i},'w+'); + fwrite(f,[0 0 0 0 0],'double'); + fclose(f); + end + % map file to memeoy + SETUP.tracker.bufFle{i} = ... + memmapfile(SETUP.tracker.bufFleNme{i},'Format','double'); + % % start each tracker on a separate worker + % SETUP.tracker.job{i} = batch(@startPointGrey,0,... + % {SETUP.tracker.cfgFleNme{i},SETUP.tracker.bufFleNme{i}}); + end +end + +%% Init naming scheme for keyboard answers +if(strcmpi(SETUP.io.interface, 'Keyboard')) + disp(' --> Init naming scheme for keyboard answers ...'); + KbName('UnifyKeyNames'); + disp(' done!'); +end + +%% Call PsychStartup finally +try + PsychStartup; +catch + disp(' ================================================================'); + disp(' Call of PsychStartup didn''t work out! You can use the Open '); + disp(' Toolbox for Behavioral Research anyway, but problems may occur!'); + disp(' Please install gstreamer on your system to use the Psychophysics'); + disp(' Toolbox completely!'); + disp(' ================================================================'); +end + +%% Clear needless variables +clear names counter bioPath i j needless pos startPos interface curDir len +clear tmp0 k ans year tmp2 tmp1 pc psychPath foundHW tmpVer + +%% Done +% If we reach this point everything went probably fine ;) +SETUP.initDone = 1; +disp(' ... done'); diff --git a/Examples/SkinnerBox_SimpleExample/myHardwareSetup.m b/Examples/SkinnerBox_SimpleExample/myHardwareSetup.m new file mode 100644 index 0000000000000000000000000000000000000000..1596eddd7592e6f18a25765092d9fa1322cd4af7 --- /dev/null +++ b/Examples/SkinnerBox_SimpleExample/myHardwareSetup.m @@ -0,0 +1,193 @@ +function myHardwareSetup +% This function initializes the OTBR Toolbox and sets all basic parameters +% of the hardware in your setup. This function needs to be stored on each +% experimental computer. All required hardware definitions are stored in +% the global variable SETUP created by this function. Edit this function when +% building a new setup. Description of specific field names and values can +% be found in the function.. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://sourceforge.net/projects/biopsytoolbox/ +% +% See also: start + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.0 +% Last Change: 05.06.2018 +% +% 05.06.2018, Tobias: first draft + +%% Init global struct +global SETUP + +%% (1a) Please enter path to the BiopsyToolbox folder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SETUP.toolboxPath = 'D:\Matlab\IKNToolbox\OTBRToolbox'; + +%% (1b) Please enter path to the Psychophysics Toolbox folder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SETUP.psychToolboxPath = 'D:\Matlab\PsychToolboxOrig\PsychToolbox3Orig_GitHub_Branches\Psychtoolbox-3.0.15\Psychtoolbox'; + +%% (1c) Please specify platform of your experimental computer +% This version of the Biopsy Toolbox supports these platforms: +% 'Windows64Bit' --> Computer with a 64 bit Windows operating system +% installed and a 64 bit MATLAB +% 'RaspberryPi3' --> Raspberry Pi with Octave +SETUP.platform = 'Windows64Bit'; + +%% (2) Please choose the IO device +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% The BiopsyToolbox provides nativ access to two different IO devices: the +% IO-Warrior 40 and a self made parallel port Interface. Please choose the +% interface you want to use. +% For training purposes the keyboard can be used as well. +% 'IOWarrior' -> Grants access to the IO-Warrior 40 (WINDOWS ONLY) +% 'Parallel' -> Grants access to the self made parallel port interface (WINDOWS ONLY) +% 'WarriorBox' -> Grants access to the self made IO-Warrior interface (WINDOWS ONLY) +% 'FBIScience' -> Grants access to the FBI-Science IO device (WINDOWS ONLY) +% 'NetworkBox' -> Grants access to the Odroid network IO device (Biopsychology) +% 'NetworkBoxACN'-> Grants access to the Odroid network IO device (ACN) +% 'Keyboard' -> For testing your program in your office using the keyboard +% 'RaspberryPi3' -> Grants access to the IO pins of the Raspberry 3B+ +SETUP.io.interface = 'Keyboard'; + +% Add IP address of NetworkBox as string here. E.g. '192.168.0.1' +% Otherwise leave this field empty! +SETUP.networkDIO.odroidIP = []; + +%% (3a) Please specify the OUTPUT devices +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each device is connected to a separate pin/output at the output device. +% Please specify the pin numbers for the devices used in the setup. Unused +% outputs should contain an empty matrix. +SETUP.io.houseLight = []; +SETUP.io.feeder = 4; +SETUP.io.feederLight = 5; +SETUP.io.feederPower = []; +SETUP.io.punishment = []; + +%% (3b) Please specify the feeder +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This section defines if the amount of food given to a subject is defined +% in time (seconds) or in amount of food (e.g. number of pellets) +% Define time in seconds --> 'time' +% Define amount in exact numbers --> 'amount' +SETUP.io.feederType = 'time'; + +% Manual shaping (WINDOWS ONLY) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Set this value to one, if you want to manually reward by pressing the 'f' +SETUP.manualShaping.status = 0; + +%% (4) Please specify the number(s) of the INPUT pins +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each input device (e.g. real switch or pecking key) +% must be specified here. Physicsal switches are connected to a single +% pin/input to the IO device. Please be aware that the first pin number +% corresponds to the first input key (doesn't matter on which input pin the +% key is connected). That means that the first response key could be +% connected to pin 7 and the second response key to pin 3. That would +% result in [7 3]. +% If you are using touch screens only and no external input device you can +% leave this entry empty: SETUP.io.inputKeys = []; +SETUP.io.inputKeys = [1 2 3 4 5 6 7 8 9]; + +%% (5) Please specify monitor dimensions +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Screen width and height in centimeters +SETUP.screen.ScreenWidth = 16.5; +SETUP.screen.ScreenHeight = 12.8; + +%% (6) TOUCH SCREEN SETTINGS (WINDOWS ONLY) +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Set this value to one, if you want to use a touch screen +SETUP.touchScreen.on = 0; + +% Set to one to plot a red dot to the recognised touch location +% Not recommended during experiments! +SETUP.touchScreen.display = 0; + +% Enter minimum time between two touches (in seconds) +SETUP.touchScreen.deltaT = 0.01; + +% Enter minimum distance between two touches (in centimeters) +% After call of initWindow the relative value is computed and replaced +SETUP.touchScreen.deltaP = 0.5; + +%% (7) Set Psych toolbox verbosity +% Set the verbosity of the psychophysics toolbox +% 0 - Disable all output - Same as using the SuppressAllWarnings flag. +% 1 - Only output critical errors. +% 2 - Output warnings as well. +% 3 - Output startup information and a bit of additional information. +% This is the default. +% 4 - Be pretty verbose about information and hints to optimize your code +% and system. +% 5 - Levels 5 and higher enable very verbose debugging output, mostly +% useful for debugging PTB itself, not generally useful for end-users. +% +% For normal use 1 is fine! +SETUP.psychToolbox.verbosity = 1; + +%% (8) UDP communication for remote status display +% contact details of the remote listener +SETUP.udp.ip = [];'192.168.2.1'; % remote ip +SETUP.udp.port = 5006; % remote port +SETUP.udp.lclPort = 5005; % local port + +%% (9) EPHYS SETTINGS +% %%%%%%%%%%%%%%%%%%%% +% Enter values for the white bar at the top of the screen. This is useful +% to get the exact timing when the stimulus was presented on the monitor. +% The coordinates are entered with reference to the left lower corner of +% the screen. +% The values are entered in centimeters and have the following meaning: +% 1. left lower corner, X coordinate +% 2. left lower corner, Y coordinate +% 3. width of bar +% 4. height of bar +% +% Defaults should be okay! + +% White bar at the top of the screen, stretched over the whole screen +% [left, bottom, width, height] +SETUP.ephys.photodiode = [0, SETUP.screen.ScreenHeight-2, SETUP.screen.ScreenWidth, SETUP.screen.ScreenHeight]; + +%% (10) Raspberry 3B+ SETTINGS +% %%%%%%%%%%%%%%%%%%%%%%%%%%% +% Enter the IP addresses of your Raspberry 3B+ as string systems here. +% The order of the IP addresses is important, because the system that is +% entered first is known as system one, the second system entered is known +% as system two, ... +% e.g. SETUP.raspberry.ip{1} = '192.168.0.20'; +% e.g. SETUP.raspberry.ip{2} = '192.168.0.21'; +SETUP.raspberry.ip{1} = []; + +%% (11) memory mapped file(s) for gaze tracker +% ---> more information needed +SETUP.tracker.bufFleNme = {}; % match fileNames in videoTracker.m +SETUP.tracker.cfgFleNme = {}; % configuration file of tracker +SETUP.tracker.bufFle = {}; % generate it using tracker.preview +SETUP.tracker.frameRate = 150; + +%% (12) Select trigger counter for MRI experiments +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% With this entry the program for recording the triggers is selected. +% The program is part of the OTBR Toolbox and will be started automatically +% if desired. +% Two options are available: +% * 'Parallel' --> to detect triggers at the parallel port +% * 'Network' --> to detect triggers with a network IO device +% * [] --> Leave empty if you want to start the program manually! +SETUP.MRI.triggerCounter = []; + +% Name of the application to be started. +% Please make sure that the application is located in the +% "Current Directory". If necessary, it must be copied from the folder +% <OTBR-Toolbox>\Tools\MRI-Counter into the "Current Directory" +% Leave empty if you want to start the program manually! +% e.g. SETUP.MRI.triggerCounterName = 'triggerCounter2_MCR9-2.exe'; +SETUP.MRI.triggerCounterName = []; diff --git a/Examples/SkinnerBox_SimpleExample/myParadigmSetup.m b/Examples/SkinnerBox_SimpleExample/myParadigmSetup.m new file mode 100644 index 0000000000000000000000000000000000000000..9bccd8f05d6f7fd0ef6d97d7ef1fc7be497f11ef --- /dev/null +++ b/Examples/SkinnerBox_SimpleExample/myParadigmSetup.m @@ -0,0 +1,79 @@ +function myParadigmSetup +% This function initializes the OTBR Toolbox and sets all basic parameters +% of the SOFTWARE in your setup. This function needs to be stored on each +% experimental computer. All required hardware definitions are stored in +% the global variable SETUP created by this function. Edit this function when +% building a new setup. Description of specific field names and values can +% be found in the function.. +% +% Copyright 2007-2018 +% CC-GNU GPL by attribution +% Please cite the BioPsychology Toolbox where this function is used. +% http://sourceforge.net/projects/biopsytoolbox/ +% +% See also: initOTBR, myHardwareSetup + +% VERSION HISTORY: +% Author: Tobias Otto, Jonas Rose +% Version: 1.2 +% Last Change: 18.07.2019 +% +% 05.06.2018, Tobias: first draft +% 06.02.2019, Tobias: type punishment +% 18.07.2019, Tobias: typo in switch position comments + +%% Init global struct +global SETUP + +%% (1) Switch/Image positions in centimeters +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Specify the position of the left upper corner of the switch with +% reference to the left upper corner of the screen. +% REMEMBER for the TOUCH SCREEN that the first number entered in the +% following vectors corresponds to index 1 in the keyBuffer output. The +% second value to index 2 and so on. But only for touchscreens! +SETUP.screen.X = [0.001, 5.5, 11, 0.001, 5.5, 11, 0.001, 5.5, 11]; +SETUP.screen.Y = [0.1 0.1 0.1 4.2 4.2 4.2 8.3 8.3 8.3]; + +%% (2) Width and height of the switches/ images in centimeters +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Specify the width and height of the switches in centimeters +SETUP.screen.width = [5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.5]; +SETUP.screen.height = [4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2 4.2]; + +%% (3) Background color +% Change to background color from black to any another color using short +% names (pre defined in MATLAB) or a RGB triplet ranged from 0 to 1 +% +% [1 1 0] --> yellow +% [1 0 1] --> magenta +% [0 1 1] --> cyan +% [1 0 0] --> red +% [0 1 0] --> green +% [0 0 1] --> blue +% [1 1 1] --> white +% [0 0 0] --> black +% +% or anything between zero and one ... +SETUP.screen.bgColor = [0 0 0]; + +%% (4) Transparency +% Set this value to 1, if you want to enable transparency for your stimuli. +% This is a global setting and transparency has to be enabled for each +% stimulus individually during call of showStimuli +SETUP.screen.transparency = 1; + +%% (5) Sound support +% %%%%%%%%%%%%%%%%%%% +% Set this value to one to enable sound support. This should always be the +% case, because some functions use sound options. If this is disabled the +% keyBuffer will not work with the optional argument 'sound' and an error +% occurs. +SETUP.sound.enable = 0; + +%% (6) Image to be used as negative feedback +% If specified the given image will be shown during punishment! +% Specify the path to the image as string or leave empty otherwise! +% Image position is full screen by default: [0 0 1 1] +SETUP.punishment.img = []; +SETUP.punishment.imgPos = [0 0 1 1]; %[left, bottom, width, height] figure coordinate diff --git a/Examples/SkinnerBox_SimpleExample/simpleExample.m b/Examples/SkinnerBox_SimpleExample/simpleExample.m new file mode 100644 index 0000000000000000000000000000000000000000..7775f4431ef58412c3fb2b2363ca5d2099a88943 --- /dev/null +++ b/Examples/SkinnerBox_SimpleExample/simpleExample.m @@ -0,0 +1,98 @@ +function simpleExample +% Example paradigm for the OTBR Toolbox on Windows. +% Answer on one stimulus provides reward +% +% Make sure to define the screen befor you start this example: +% par.screenNum = 0; +% 0 = one screen only +% 1 = first windows screen +% 2 = second windows screen + +% Tobias Otto +% 1.0 +% 11.03.2021 + +% 11.03.2021, Tobias: first draft + +%% Init toolbox +initOTBR; + +%% Init variables for paradigm +par.screenNum = 2; % 0 = one screen only | 1= first screen | 2 = second screen +par.subject = 'ThisIsJustATest'; + +par.numTrials = 10; +par.iti = 3; % Iti in seconds +par.feeding = 2; % Feeding time in seconds +par.ansTime = 4; % Time in seconds to answer +par.numPecks = 1; % Number of pecks needed +par.stimPos = 5; % Position of stimulus (Positions are defined in myParadigmSetup.m) + +par.savePath = fullfile(pwd, 'Result'); +par.imPath = fullfile(pwd, 'Stimuli'); + +% Technical information +par.info = getBasicInformation; +par.infoText = 'Example autoshaping program'; + +%% Compute buffer for random stimulus positions +% In this example we have 9 different stimulus positions +buffer.good = randomOrder(9,par.numTrials); +buffer.bad = mod(buffer.good + randomOrder(7, par.numTrials), 9)+1; + +%% Start experiment +try + %% Init toolbox + initWindow(par.screenNum); + + %% Load stimuli + im.good = loadImage(fullfile(par.imPath, 'stim1.png')); + im.bad = loadImage(fullfile(par.imPath, 'stim2.png')); + + %% Start main loop + % turn off all devices, clear screen and turn on the house light + startExperiment; + + % Start loop + for i=1:par.numTrials + %% Start ITI + showStimuli; + WaitSecs(par.iti); + + %% Show stimulus + showStimuli([im.good im.bad], [buffer.good(i) buffer.bad(i)]); + + %% Wait for an answer + % Save response to get performance + out(i).ans = keyBuffer(par.ansTime, ... + 'goodKey', buffer.good(i), par.numPecks, ... + 'badKey', buffer.bad(i), 1); + + % Save good and bad key as well + out(i).goodKey = buffer.good(i); + out(i).badKey = buffer.bad(i); + + %% Start feeding + if(out(i).ans.goodKey == 1) + feeding(par.feeding); + else + punishment(par.feeding); + end + end + + %% Clean up and save + % Close window and cleans up + stopExperiment + + % Save + save2File(par, out, 'subject', par.subject, 'path', par.savePath) + + % User info + res = [out.ans]; + disp(['Performance of subject ' par.subject ': ' num2str(sum([res.goodKey])/par.numTrials*100) '%']); + +catch + closeWindow; + errorSave; +end + diff --git a/Screen/showExtraStimuli.m b/Screen/showExtraStimuli.m index 3287c30ab9e52cfa4d0351b4e6632eb5755cbe17..08729f7a2daab8179f5950067b4591c0db0370e2 100644 --- a/Screen/showExtraStimuli.m +++ b/Screen/showExtraStimuli.m @@ -18,10 +18,6 @@ function rect = showExtraStimuli(im, screenNum, varargin) % drawing of the texture % * transparency : Specifies the transparency of an image in percent. % From 0 (fully opaque) to 100 (fully transparent) -% * ephys : displays a white bar (usually on top of the monitor) -% to trigger a photo diode. This is useful to get the -% exact timing when the stimulus was presented on the -% monitor. % * rect : Specifies the position of the stimulus in pixel % * dontFlip : Don't flip buffers (advanced users) % diff --git a/drafts/runArena.m b/drafts/runArena.m deleted file mode 100644 index 550161cd35f782e80f04d83b81b2f450e3866d31..0000000000000000000000000000000000000000 --- a/drafts/runArena.m +++ /dev/null @@ -1,111 +0,0 @@ -function [par] = runArena - -% lass ihm mehr Zeit vorm kopieren? - -%% Init variables (in nested functions) -global SETUP -% hosts = []; - -par.funName = ''; -par.funPath = ''; -par.subjName = ''; - -%% open the figure -fig.hand = figure('menubar','none'); -fig.ctrlPnl.hand = uipanel('Title','ArenaControl','FontSize',10,... - 'BackgroundColor','white',... - 'Position',[.01 0 .4 1]); -fig.ctrlPnl.mainBtn = uicontrol('Parent',fig.ctrlPnl.hand,... - 'String','Select paradigm','units','normalized',... - 'FontSize',12,... - 'Position',[.1 .1 .8 .2], 'callback',{@selParadigm}); - -%% =========== callback functions - function [] = selParadigm(~, ~) - % get the paradigm - [par.funName, par.funPath] = uigetfile('Select paradigm'); - par.funHand = str2func(par.funName(1:end-2)); - fig.ctrlPnl.mainBtn.Visible = 'off'; - - % get the subject - fig.ctrlPnl.info1 = uicontrol('Parent',fig.ctrlPnl.hand, ... - 'Style','text', 'String','Enter bird name','FontSize',10,... - 'units', 'normalized', 'Position',[.1 .9 1 .05],... - 'HorizontalAlignment','left', 'BackgroundColor','white'); - uicontrol('Parent',fig.ctrlPnl.hand, ... - 'Style','text', 'String','Name:','FontSize',10,... - 'units', 'normalized', 'Position',[.1 .8 .5 .05],... - 'HorizontalAlignment','left', 'BackgroundColor','white'); - fig.ctrlPnl.subj = uicontrol('Parent',fig.ctrlPnl.hand, ... - 'Style','edit', 'FontSize',12, 'HorizontalAlignment','left', ... - 'units', 'normalized', 'Position',[.4 .8 .5 .05], ... - 'callback',@selSubj, 'BackgroundColor',[.8 .8 .8]); - - % info - fig.ctrlPnl.info2 = uicontrol('Parent',fig.ctrlPnl.hand, ... - 'Style','text', 'String',... - 'I''m copying and initializing. Meanwhile you can enter the bird''s name.',... - 'FontSize',8, 'HorizontalAlignment','left', ... - 'units', 'normalized', 'Position',[.1 .4 .8 .1],... - 'BackgroundColor','white'); - fig.ctrlPnl.info3 = uicontrol('Parent',fig.ctrlPnl.hand, ... - 'Style','text', 'String','','FontSize',10,... - 'units', 'normalized', 'Position',[.1 .7 .9 .05],... - 'HorizontalAlignment','left', 'BackgroundColor','white'); - - - % initalizes remote hosts - initHosts(par); - % wait until the subject name is provided - uiwait(gcf); - % update info - fig.ctrlPnl.info2.String = 'Now you can put the bird in the areana. Then you can run the paradigm'; - - % get ready to start the paradigm - fig.ctrlPnl.mainBtn.Visible = 'on'; - fig.ctrlPnl.mainBtn.String = ['RUN: ' par.funName]; - fig.ctrlPnl.mainBtn.Callback = @runParadigm; - end - - function [] = selSubj(~,~) - fig.ctrlPnl.subj.Enable = 'off'; - fig.ctrlPnl.info1.String = ''; - uiresume(gcbf); - end - - function [] = runParadigm(~,~) - % start the paradigm - cd(par.funPath) - par.funHand(par, fig, hosts); - % update info - fig.ctrlPnl.info1.String = ['Running: ' par.funName(1:end-2)]; - fig.ctrlPnl.info2.String = ''; - fig.ctrlPnl.info3.String = ['Started: ' datestr(datetime('now'))]; - % update the main button - fig.ctrlPnl.mainBtn.String = 'Disconnect'; - fig.ctrlPnl.mainBtn.Callback = @shutdownHost; - fig.ctrlPnl.mainBtn.Visible = 'off'; - end - - function [] = shutdownHost(~,~) - disp('disconnectFromHost') -% disconnectFromHost('reboot') - disp('resetFigure') - end -end -function [hosts] = initHosts(par) -% Start the local toolbox -% disp('start'); -% disp('sendExperiment2Host'); -% disp('initWindow'); -% disp('showstimuli'); -start; -for i=1:length(SETUP.raspberry.ip) - % send code and stimuli of this experiment to all raspberry pi - sendExperiment2Host(SETUP.raspberry.ip{i}, fullfile(par.funPath,'rpiFiles')) -end -hosts = connect2Host; -sendCommand2Host(hosts, 'initWindow(0)', 1); -sendCommand2Host(par.remoteSetups, 'showStimuli;', 1); - -end diff --git a/drafts/stimulusChoice.m b/drafts/stimulusChoice.m deleted file mode 100644 index cdaf3c478a02aa8a7d62ea3b0e5a3f91895960ea..0000000000000000000000000000000000000000 --- a/drafts/stimulusChoice.m +++ /dev/null @@ -1,34 +0,0 @@ -function [response] = stimulusChoice(imgs, waitDur, goodKey, badKey, numPeck) -% function [response] = stimulusChoice(imgs, waitDur, goodKey, badKey, numPeck) -% - paradigm snippet - -% Shows stimuli for choosing. Correct choices will be rewarded. -% -% INPUT -% imgs : stimuli, previsouly loaded (see loadImage, showStimuli) -% : ! first image is the good image ! -% waitDur : time to wait for a response (seconds) -% goodKey : responding to this position will be rewarded -% badKey : responding to this key will be punished -% numPeck : number of responses - -% Version 1.1 -% Date 08.03.2018 -% Author Jonas - -% reward light or punishment -fBackDur = 2; - -% first image is good image -pos = [goodKey(:); badKey(:)]; - -% display the images -showStimuli(imgs, pos); -% wait for response -response = keyBuffer(waitDur, 'goodKey', goodKey, numPeck, ... - 'badKey', badKey, numPeck, 'remoteStop', 'sendNetID'); -% clear the images -showStimuli; -% feed on correct response -if response.goodKey >= numPeck - feeding(1, 'lightdur', fBackDur); -end \ No newline at end of file diff --git a/drafts/stimulusOutcome.m b/drafts/stimulusOutcome.m deleted file mode 100644 index bd77fa102db5f763230700ef2a487e05c8362c7b..0000000000000000000000000000000000000000 --- a/drafts/stimulusOutcome.m +++ /dev/null @@ -1,25 +0,0 @@ -function [response] = stimulusOutcome(im, pos, waitDur, numPeck) -% function [response] = stimulusOutcome(im, pos, waitDur, numPeck) -% - paradigm snippet - -% shows one stimulus, waits for and rewards correct response -% -% INPUT -% im : image handle (see loadImage, showStimuli) -% pos : position on screen todisplay image (see showStimuli) -% waitDur : time to wait for a response (seconds) -% numPeck : number of responses required to receive outcome - -% Version 1.0 -% Date 08.03.2018 -% Author Jonas - -% display the image -showStimuli(im, pos); -% wait for response -response = keyBuffer(waitDur, 'goodKey', pos, numPeck, 'remoteStop', 'sendNetID'); -% clear the image -showStimuli; -% feed on correct response -if response.goodKey >= numPeck - feeding(1, 'lightdur', 2); -end \ No newline at end of file diff --git a/drafts/stimulusResponse.m b/drafts/stimulusResponse.m deleted file mode 100644 index 28734a15ef70e673c7df46e0fb32ddc0550306c3..0000000000000000000000000000000000000000 --- a/drafts/stimulusResponse.m +++ /dev/null @@ -1,24 +0,0 @@ -function [response] = stimulusResponse(im, pos, waitDur, numPeck) -% function [response] = stimulusResponse(im, pos, waitDur, numPeck) -% - paradigm snippet - -% shows one stimulus, waits for response -% -% INPUT -% im : image handle (see loadImage, showStimuli) -% pos : position on screen todisplay image (see showStimuli) -% waitDur : time to wait for a response (seconds) -% numPeck : number of responses required to receive outcome - -% Version 1.0 -% Date 08.03.2018 -% Author Jonas - -%% Show one stimulus and wait for a response -% This function serves as trial-snippet to run on remote setup - -% display the image -showStimuli(im, pos); -% wait for response -response = keyBuffer(waitDur, 'goodKey', pos, numPeck, 'remoteStop', 'sendNetID'); -% clear the image -showStimuli; diff --git a/drafts/stimulusReward.m b/drafts/stimulusReward.m deleted file mode 100644 index ec2180e919749de2282955e60908509ee4bb8e00..0000000000000000000000000000000000000000 --- a/drafts/stimulusReward.m +++ /dev/null @@ -1,26 +0,0 @@ -function [response] = stimulusReward(im, pos, waitDur, numPeck) -% function [response] = stimulusReward(im, pos, waitDur, numPeck) -% - paradigm snippet - -% shows one stimulus, waits for response and rewards (autoshaping) -% -% INPUT -% im : image handle (see loadImage, showStimuli) -% pos : position on screen todisplay image (see showStimuli) -% waitDur : time to wait for a response (seconds) -% numPeck : number of responses required to receive outcome - -% Version 1.0 -% Date 08.03.2018 -% Author Jonas - -%% Show one stimulus and wait for a response -% This function serves as trial-snippet to run on remote setup - -% display the image -showStimuli(im, pos); -% wait for response -response = keyBuffer(waitDur, 'goodKey', pos, numPeck, 'remoteStop', 'sendNetID'); -% clear the image -showStimuli; -% reward after response or when time is up -feeding(1, 'lightdur', 2); diff --git a/drafts/tst.m b/drafts/tst.m deleted file mode 100644 index 49623bdb34cec15920d17fbc5badf080c869b424..0000000000000000000000000000000000000000 --- a/drafts/tst.m +++ /dev/null @@ -1,5 +0,0 @@ -function [par] = tst(par,fig) -par,fig, - -disp('nargin: ....') -disp(nargin)