Commit 308691fd authored by Jesse Heckman's avatar Jesse Heckman
Browse files

Cleared up and structured code for vPrime

parent 6c421a0b
function handles = pb_createdir(handles)
% PB_CREATEDIR()
% PB_CREATEDIR(HANDLES)
%
% PB_CREATEDIR() ...
% PB_CREATEDIR(HANDLES) checks if intended data directory exists, and,
% if not, makes directory. Furthermore, a quick check is done to see if
% there is already existing data in this folder to prevent loss of data.
%
% See also ...
% See also PB_VPRIME, PB_VPRIME, PB_VRUNEXP
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
% Creates directory and sets parameters for data storage
cfg = handles.cfg;
path = [cfg.dname filesep 'trial'];
......@@ -19,8 +20,10 @@ function handles = pb_createdir(handles)
while exist(fname,'file')
% Check for existing recordings
quest = 'Recording already exists. Indicate how to proceed?';
answer = questdlg(quest,'Choices','Overwrite', 'Increment','Stop','Stop');
switch answer
case 'Overwrite'
break;
......@@ -30,6 +33,7 @@ function handles = pb_createdir(handles)
case 'Stop'
error('Run stopped.');
end
fname = [cfg.expInitials '-' cfg.subjectid '-' cfg.datestring '-' cfg.recording '-0001.vc' ];
end
handles.cfg = cfg;
......
function handles = pb_getblock(handles)
% PB_GETBLOCK()
% PB_GETBLOCK(HANDLES)
%
% PB_GETBLOCK() ...
% PB_GETBLOCK(HANDLES) retracts block info provided by the exp file.
%
% See also ...
% See also PB_VPRIME, PB_VPRIMEGUI, PB_VRUNEXP.
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
cfg = handles.cfg;
[block, cfg] = pb_vReadExp(cfg);
block = pb_vPrimeZ(block,cfg);
handles.block = block;
......
function handles = pb_getcfgdefaults(handles)
% PB_GETCFGDEFAULTS()
% PB_GETCFGDEFAULTS(HANDLES)
%
% PB_GETCFGDEFAULTS() ...
% PB_GETCFGDEFAULTS(HANDLES) initiaties default parameters into handles.
%
% See also ...
% See also PB_VPRIME, PB_VPRIMEGUI.
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
......@@ -21,7 +21,7 @@ function handles = pb_getcfgdefaults(handles)
d = datestr(now,formatOut);
cfg.date = d;
cfg.datestring = d;
cfg.trialnumber = 1;
cfg.trialnumber = [1 1]; % format: [Tblock Texp]
cfg.blocknumber = 1;
%% TDT and PLC defaults
......
function handles = pb_gethandles(handles)
% PB_GETHANDLES()
% PB_GETHANDLES(HANDLES)
%
% PB_GETHANDLES() ...
% PB_GETHANDLES(HANDLES) extracts the set parameters from the GUI and stores
% them into your handles.
%
% See also ...
% See also PB_VPRIME, PB_VPRIMEGUI, PB_VRUNEXP
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
......@@ -22,21 +23,21 @@ function handles = pb_gethandles(handles)
% get exp and cfg files
str = [cfg.expdir filesep '*.exp'];
d = dir(str); % default exp folder
d = dir(str); % default exp folder
cfg.expfiles = {d.name};
if isempty(cfg.expfiles)
end
%set(handles.popupmenu_exp,'String',cfg.expfiles)
%expfileIdx = get(handles.popupmenu_exp,'Value');
%cfg.expfname = cfg.expfiles{expfileIdx};
if isempty(cfg.expfiles); end
% set(handles.popupmenu_exp,'String',cfg.expfiles)
% expfileIdx = get(handles.popupmenu_exp,'Value');
% cfg.expfname = cfg.expfiles{expfileIdx};
cfg.expfname = get(handles.editLoad,'String');
% d = dir([cfg.expdir filesep '*.cfg']);
% cfg.cfgfiles = {d.name};
% d = dir([cfg.expdir filesep '*.cfg']);
% cfg.cfgfiles = {d.name};
% set(handles.popupmenu_cfg,'String',cfg.cfgfiles);
% cfgfileIdx = get(handles.popupmenu_cfg,'Value');
% cfg.cfgfname = cfg.cfgfiles{cfgfileIdx};
handles.cfg = cfg;
end
......
function pb_setupShow(handles)
% PB_SETUPSHOW()
% PB_SETUPSHOW(HANDLES)
%
% PB_SETUPSHOW() ...
% PB_SETUPSHOW(HANDLES) sets up axes for vPrime GUI.
%
% See also ...
% See also PB_VPRIMEGUI, PB_VPRIME, PB_VRUNEXP
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
axes(handles.signals);
cla; hold on;
axis([0 120 -50 50]);
box off
axes(handles.signals);
cla; hold on;
axis([0 120 -50 50]);
box off
axes(handles.hTrace);
cla; hold on;
axis([0 2.5 0 300]);
box off
axes(handles.hTrace);
cla; hold on;
axis([0 2.5 0 300]);
box off
axes(handles.eTrace);
cla; hold on;
axis([0 2.5 0 300]);
box off
axes(handles.eTrace);
cla; hold on;
axis([0 2.5 0 300]);
box off
end
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
......
function block = pb_vPrimeZ(block,cfg)
% PB_VPRIMEZ()
% PB_VPRIMEZ(BLOCK, CFG)
%
% PB_VPRIMEZ() ...
% PB_VPRIMEZ(BLOCK, CFG) adds additional fields to 'block' in order to
% allow for TDT control of correct LSCs during experimentation.
%
% See also ...
% See also PB_VPRIME, PB_VPRIME, PB_VRUNEXP, PB_GETBLOCK, PB_VREADEXP
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
cfg;
for iBlck = 1:cfg.Blocks
ntrls = length(block(iBlck).trial);
for iTrl = 1:ntrls
nstms = length(block(iBlck).trial(iTrl).stim);
for iStm = 1:nstms
X = block(iBlck).trial(iTrl).stim(iStm);
if ~isempty(X)
ZI = cfg.interpolant(block(iBlck).trial(iTrl).stim(iStm).azimuth,block(iBlck).trial(iTrl).stim(iStm).elevation);
block(iBlck).trial(iTrl).stim(iStm).Z = ZI;
block(iBlck).trial(iTrl).stim(iStm).azimuth = cfg.lookup(ZI+1,4);
block(iBlck).trial(iTrl).stim(iStm).elevation = cfg.lookup(ZI+1,5);
end
end
end
......
function D = pb_vCreateSignal(N, dur, SR, freq, type, varargin)
% PB_VCREATESIGNAL()
% PB_VCREATESIGNAL(N, DUR, SR, FREQ, TYPE, VARARGIN)
%
% PB_VCREATESIGNAL() ...
% PB_VCREATESIGNAL(N, DUR, SR, FREQ, TYPE, VARARGIN) creates an unscaled
% signal for the vestibular chair.
%
% See also ...
% See also PB_VPRIME, PB_VPRIMEGUI, PB_VRUNEXP, PB_VSIGNALVC.
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
dshow = pb_keyval('dshow',varargin,false);
D(N) = struct;
for i=1:N
for i = 1:N
switch type
case 'noise'
[D(i).x,D(i).t] = VC_noisesignal(0, dur, SR);
......@@ -24,9 +25,10 @@ function D = pb_vCreateSignal(N, dur, SR, freq, type, varargin)
error('False type specification');
end
end
if dshow; figure(); hold on; for i=1:N; plot(D(i).t,D(i).x); pb_nicegraph; end; end
if dshow; figure; hold on; for i=1:N; plot(D(i).t,D(i).x); pb_nicegraph; end; end
end
%-- Specific signal functions --%
function [x,t] = VC_noisesignal(mu, dur, SR)
% function generates a noise signal with mean mu, length dur and frequency freq.
......@@ -49,11 +51,9 @@ function [x,t] = VC_noisesignal(mu, dur, SR)
% tukey window
L = length(t);
x = x.* tukeywin(L,0.25);
end
function [x,t] = VC_sinesignal(dur, SR, freq)
% function generates a sine signal
w = freq*2*pi;
......@@ -62,14 +62,10 @@ function [x,t] = VC_sinesignal(dur, SR, freq)
L = length(t);
x = x.* tukeywin(L,0.25)';
end
function [x,t] = VC_predictedsine(dur, SR, freq)
% function generates a sine signal that in theory should generete an
% perfect sine output: y(t)=sin(wt).
function [x,t] = VC_predictedsine(dur, SR, freq) %%% <--- TO DO: FIX THE TRANSFER FUNCTION!!
% function generates a perfect sine output
a = 1.39;
b = 1.4;
......@@ -80,11 +76,9 @@ function [x,t] = VC_predictedsine(dur, SR, freq)
L = length(t);
x = x.* tukeywin(L,0.25)';
end
function [x,t] = VC_turnsignal(dur, SR)
% function will create turn signal of length dur
t = 0:1/SR:dur;
......
function signal = pb_vSafety(signal)
% PB_VSAFETY()
% PB_VSAFETY(SIGNAL)
%
% PB_VSAFETY() ...
% PB_VSAFETY(SIGNAL) checks the signal parameters for the vestibular chair
% to see wether they are within a 'safe' range. Note that if not, the values
% are fixed to safe maximum.
%
% See also ...
% See also PB_VPRIME, PB_VPRIMEGUI, PB_VRUNEXP, PB_VSIGNALVC.
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
if ~strcmp(signal(2).type,'none')
if signal(2).duration > 300
warning('Unsafe signal: Horizontal duration too long. Duration reduced to 300.' );
signal(2).duration = 300;
end
if signal(2).amplitude > 20
warning('Unsafe signal: Horizontal amplitude too large. Amplitude reduced to 20');
signal(2).amplitude = 20;
end
end
if ~strcmp(signal(1).type,'none')
if signal(1).duration > 300
warning('Unsafe signal: Vertical duration too long!');
signal(1).duration = 300;
end
if signal(1).amplitude > 40
warning('Unsafe signal: Horizontal amplitude too large!');
signal(2).amplitude = 40;
vAx = {'Vertical','Horizontal'};
for iAx = 1:2
if ~strcmp(signal(iAx).type,'none')
if signal(iAx).duration > 300
warning(['Detected unsafe signal. ' vAx{iAx} ' duration too long!']);
signal(iAx).duration = 300;
end
if signal(iAx).amplitude > 40/iAx
warning(['Detected unsafe signal. ' vAx{iAx} ' amplitude too large!']);
signal(iAx).amplitude = 40/iAx;
end
if signal(iAx).frequency > .3
warning(['Detected unsafe signal. ' vAx{iAx} ' frequency too high!']);
signal(iAx).frequency = .3;
end
end
end
end
......
function [Dat,profile,dur] = pb_vSignalVC(handles,block,iBlock)
% PB_VSIGNALVC()
function [Dat,profile,dur] = pb_vSignalVC(handles)
% PB_VSIGNALVC(HANDLES)
%
% PB_VSIGNALVC() ...
% PB_VSIGNALVC(HANDLES) reads vestibular signal from handles, writes
% profile, and feeds back the signal to the axes in GUI.
%
% See also ...
% See also PB_VPRIME, PB_VPRIMEGUI, PB_VRUNEXP
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
block = handles.block;
bnumber = handles.cfg.blocknumber;
%% READ SIGNAL PARAMETERS
signal(1) = block(iBlock).signal.ver;
signal(2) = block(iBlock).signal.hor;
signal(1) = block(bnumber).signal.ver;
signal(2) = block(bnumber).signal.hor;
signal = pb_vSafety(signal);
%% CREATE BASIC SIGNAL
vSignal = pb_vCreateSignal(1, signal(1).duration, 10, .1, signal(1).type);
hSignal = pb_vCreateSignal(1, signal(2).duration, 10, .1, signal(2).type);
vSignal = pb_vCreateSignal(1, signal(1).duration, 10, signal(1).frequency, signal(1).type);
hSignal = pb_vCreateSignal(1, signal(2).duration, 10, signal(2).frequency, signal(2).type);
%% FINALIZE SIGNAL
Dat.v.x = vSignal.x .* signal(1).amplitude;
profile.v = Dat.v.x;
Dat.v.t = (0:1:length(Dat.v.x)-1)/10;
Dat.h.x = hSignal.x .* signal(2).amplitude; profile.h = Dat.h.x;
Dat.h.t = (0:1:length(Dat.h.x)-1)/10;
Dat.v.x = vSignal.x .* signal(1).amplitude;
profile.v = Dat.v.x;
Dat.v.t = (0:1:length(Dat.v.x)-1)/10;
Dat.h.x = hSignal.x .* signal(2).amplitude; profile.h = Dat.h.x;
Dat.h.t = (0:1:length(Dat.h.x)-1)/10;
Dat.v.amplitude = signal(1).amplitude;
Dat.h.amplitude = signal(2).amplitude;
......@@ -30,7 +34,7 @@ function [Dat,profile,dur] = pb_vSignalVC(handles,block,iBlock)
dur = max([signal(1).duration signal(2).duration])+5; % add 5 extra seconds for delay of the system
%% FEEDBACK GUI
updateBlock(handles,iBlock,signal);
updateBlock(handles,bnumber,signal);
handles = pb_gethandles(handles);
......@@ -45,10 +49,10 @@ function [Dat,profile,dur] = pb_vSignalVC(handles,block,iBlock)
plot(Dat.h.t,Dat.h.x,'b');
end
function updateBlock(handles, iBlock, signal)
function updateBlock(handles, bnumber, signal)
% Updates the block information to the GUI
bn = pb_sentenceCase(num2str(iBlock,'%03d')); % count block
bn = pb_sentenceCase(num2str(bnumber,'%03d')); % count block
set(handles.Bn,'string',bn);
vs = ['V = ' pb_sentenceCase(signal(1).type) ... % VC stim
......
function handles = pb_vInitialize(handles, initialize)
% PB_VREADYEXP(h,initialize)
% PB_VINITIALIZE(HANDLES, INITIALIZE)
%
% PB_VREADYEXP() interacts with GUI during check in/out experiment.
% PB_VINITIALIZE(HANDLES, INITIALIZE) interacts with GUI and command window
% during check in/out of experiment.
%
% See also PB_VRUNEXP
% See also PB_VPRIME, PB_VPRIME, PB_VRUNEXP.
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
if initialize; initialize = 'off'; else; initialize = 'on'; end
set(handles.buttonClose, 'Enable', initialize) % avoid closing GUI during executing run function
set(handles.buttonRun, 'Enable', initialize)
set(handles.buttonLoad, 'Enable', initialize)
if strcmp(initialize,'off')
% test
else
disp([newline newline 'Experiment finished!']);
if initialize
buttonPress = 'off'; clc;
fprintf(['<strong>Experiment has started.</strong>' newline newline]);
else
buttonPress = 'on';
fprintf([newline newline '<strong>Experiment has finished.</strong>' newline newline]);
handles.cfg.recording = num2str(str2double(handles.cfg.recording)+1,'%04d');
set(handles.editRec,'string',handles.cfg.recording)
end
set(handles.buttonClose, 'Enable', buttonPress) % avoid closing GUI during executing run function
set(handles.buttonRun, 'Enable', buttonPress)
set(handles.buttonLoad, 'Enable', buttonPress)
end
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
......
function [block,cfg] = pb_vReadExp(cfg)
% PB_VREADEXP(expfile)
% PB_VREADEXP(CFG)
%
% PB_VREADEXP() reads experimental data from expfile and loads BLOCK and
% CFG parameters.
% PB_VREADEXP(CFG) reads experimental data from expfile and loads it in
% 'block' and 'cfg'.
%
% See also PB_VRUNEXP
% See also PB_VPRIME, PB_VPRIMEGUI, PB_VRUNEXP, PB_GETBLOCK
% PBToolbox (2018): JJH: j.heckman@donders.ru.nl
......@@ -22,15 +22,17 @@ function [block,cfg] = pb_vReadExp(cfg)
%% EXP
block = struct([]);
block(1).signal = [];
block(1).trial = [];
block = struct([]);
block(1).signal = [];
block(1).trial = [];
bn = 0;
while ~feof(fid)
tline = fgetl(fid);
firstCell = sscanf(tline,'%s',1);
tline = fgetl(fid);
firstCell = sscanf(tline,'%s',1);
nchar = length(firstCell);
if strcmp(firstCell,'%'); firstCell = sscanf(tline,'%s',2); firstCell = firstCell(2:end-1); end
switch upper(firstCell)
......@@ -89,8 +91,7 @@ function [block,cfg] = pb_vReadExp(cfg)
block(bn).trial(tn).stim(sn).ondelay = par(5);
block(bn).trial(tn).stim(sn).offevent = par(6);
block(bn).trial(tn).stim(sn).offdelay = par(7);
case 'TRG0'
% Updates count and writes stimulus
sn = sn+1;
......@@ -106,6 +107,7 @@ function [block,cfg] = pb_vReadExp(cfg)
block(bn).trial(tn).stim(sn).onevent = par(3);
block(bn).trial(tn).stim(sn).ondelay = par(4);
block(bn).trial(tn).stim(sn).event = par(5);
case 'ACQ'
% Updates count and writes stimulus
sn = sn+1;
......@@ -115,56 +117,66 @@ function [block,cfg] = pb_vReadExp(cfg)
block(bn).trial(tn).stim(sn).onevent = par(1);
block(bn).trial(tn).stim(sn).ondelay = par(2);
end
end
cfg = pb_vLookup(cfg);
%% EXTRA: Azimuth Elevation
for iBlck = 1:cfg.Blocks
ntrials = length(block(iBlck).trial);
for iTrl = 1:ntrials % for every trial
s = block(iBlck).trial(iTrl).stim;
s = block(iBlck).trial(iTrl).stim;
block(iBlck).trial(iTrl).nstim = numel(s); % number of stimuli per trial
for stmIdx = 1:block(iBlck).trial(iTrl).nstim % for every stimulus in a trial
X = block(iBlck).trial(iTrl).stim(stmIdx).X;
Y = block(iBlck).trial(iTrl).stim(stmIdx).Y;
mod = block(iBlck).trial(iTrl).stim(stmIdx).modality;
for iStm = 1:block(iBlck).trial(iTrl).nstim % for every stimulus in a trial
X = block(iBlck).trial(iTrl).stim(iStm).X;
Y = block(iBlck).trial(iTrl).stim(iStm).Y;
mod = block(iBlck).trial(iTrl).stim(iStm).modality;
if ~isempty(X) % for every stimulus that has an X and Y parameter, determine azimuth and elevation
if cfg.Lab==1 % Hoop lab
if strcmpi(mod,'sky')
[Az,El] = hoopsky2azel(X,Y);
else
[Az,El] = hoopXY2azel(X,Y);
end
elseif ismember(cfg.Lab,[2 3]) % Sphere lab
channel = cfg.interpolant(X,Y);
Az = cfg.lookup(channel+1,5);
El = cfg.lookup(channel+1,6);
elseif ismember(cfg.Lab,4) % SphereMinor lab
channel = cfg.interpolant(X,Y);
Az = cfg.lookup(channel+1,4);
El = cfg.lookup(channel+1,5);
elseif ismember(cfg.Lab,5) % vPrime lab
channel = cfg.interpolant(X,Y);
Az = cfg.lookup(channel+1,4);
El = cfg.lookup(channel+1,5);
end
block(iBlck).trial(iTrl).stim(stmIdx).azimuth = Az;
block(iBlck).trial(iTrl).stim(stmIdx).elevation = El;
block(iBlck).trial(iTrl).stim(iStm).azimuth = Az;
block(iBlck).trial(iTrl).stim(iStm).elevation = El;
end
end
end
end
%% CHECK OUT
fclose(fid);
end
function comment = checkcomment(fid)
% reads comments from the expfile
isComment = true;
cnt = 0; % counter
comment = cell(1);
while isComment % do this for every line that starts with '%'
position = ftell(fid); % find the position in the file
str = fscanf(fid,'%s',1); % read the string (moving the position in the file)
......@@ -179,14 +191,18 @@ function comment = checkcomment(fid)
end
function cfg = hread(fid,cfg)
% reads the header from the expfile
cnt = 0;
isBody = false;
header = cell(1);