From 6d1a140bb7dc84cbdcfb07fa66a8c30977e1457a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Wed, 29 Jun 2016 11:12:06 -0400 Subject: [PATCH 01/30] Added to .gitignore folders Calibration Files, Data, and Protocols --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ec3507 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +Calibration Files/** +Data/** +Protocols/** \ No newline at end of file From fb4c4ebd8767e694ab52c4085f94e8257fb4db57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Wed, 29 Jun 2016 11:21:08 -0400 Subject: [PATCH 02/30] Added file .gitmodules to .gitignore. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7ec3507..94f72a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ Calibration Files/** Data/** -Protocols/** \ No newline at end of file +Protocols/** +.gitmodules \ No newline at end of file From b7e799205c3e10edbfdd9bfe116524befa7e812f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Thu, 30 Jun 2016 15:22:59 -0400 Subject: [PATCH 03/30] Added uicontrol styles 'togglebutton' and 'pushbutton' to GUI --- .../Plugins/ParameterGUI/BpodParameterGUI.m | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index c36081b..ae9262e 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -113,8 +113,16 @@ case 'popupmenu' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 4; BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'togglebutton' % INCOMPLETE + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 5; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'pushbutton' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 6; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'pushbutton', 'String', ThisParamString,... + 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12,... + 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center','Callback',Meta.OdorSettings.Callback); otherwise - error('Invalid parameter style specified. Valid parameters are: ''edit'', ''text'', ''checkbox'', ''popupmenu'''); + error('Invalid parameter style specified. Valid parameters are: ''edit'', ''text'', ''checkbox'', ''popupmenu'', ''togglebutton'', ''pushbutton'''); end InPanelPos = InPanelPos + 35; ParamNum = ParamNum + 1; @@ -124,7 +132,7 @@ if p < nPanels NextPanelParams = Panels.(PanelNames{p+1}); NextPanelSize = (length(NextPanelParams)*45) + 5; - if VPos + NextPanelSize > GUIHeight + if VPos + ThisPanelHeight + 10 + NextPanelSize > GUIHeight Wrap = 1; end end @@ -179,6 +187,13 @@ elseif Params.GUI.(ThisParamName) ~= ThisParamLastValue set(ThisParamHandle, 'Value', GUIParam); end + case 6 + GUIParam = get(ThisParamHandle, 'Value'); + if GUIParam ~= ThisParamLastValue + Params.GUI.(ThisParamName) = GUIParam; + elseif Params.GUI.(ThisParamName) ~= ThisParamLastValue + set(ThisParamHandle, 'Value', GUIParam); + end end BpodSystem.GUIData.ParameterGUI.LastParamValues{p} = GUIParam; end From bffd7438f3bb64d6f00470ab3fb44957bfa08a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Thu, 30 Jun 2016 20:14:32 -0400 Subject: [PATCH 04/30] Work in progress --- .../Plugins/ParameterGUI/BpodParameterGUI.m | 170 ++++++++++-------- 1 file changed, 92 insertions(+), 78 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index ae9262e..6d03b50 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -18,7 +18,7 @@ along with this program. If not, see . %} -function varargout = ParameterGUI(varargin) +function varargout = BpodParameterGUI(varargin) % EnhancedParameterGUI('init', ParamStruct) - initializes a GUI with edit boxes for every field in subfield ParamStruct.GUI % EnhancedParameterGUI('sync', ParamStruct) - updates the GUI with fields of @@ -49,13 +49,22 @@ if isfield(Params, 'GUIPanels') Panels = Params.GUIPanels; PanelNames = fieldnames(Panels); - nPanels = length(PanelNames); else Panels = struct; Panels.Parameters = ParamNames; PanelNames = {'Parameters'}; - nPanels = 1; end + if isfield(Params, 'GUITabs') + Tabs = Params.GUITabs; + TabNames = fieldnames(Tabs); + nTabs = length(TabNames); + else + Tabs = struct; + Tabs.Panels = PanelNames; + TabNames = {'Parameters'}; + nTabs = 1; + end + Params = Params.GUI; PanelNames = PanelNames(end:-1:1); GUIHeight = 650; @@ -63,89 +72,94 @@ HPos = 10; MaxVPos = 0; BpodSystem.ProtocolFigures.ParameterGUI = figure('Position', [50 50 450 GUIHeight],'name','Parameter GUI','numbertitle','off', 'MenuBar', 'none', 'Resize', 'on'); - ParamNum = 1; - for p = 1:nPanels - ThisPanelParamNames = Panels.(PanelNames{p}); - ThisPanelParamNames = ThisPanelParamNames(end:-1:1); - nParams = length(ThisPanelParamNames); - ThisPanelHeight = (45*nParams)+5; - uipanel('title', PanelNames{p},'FontSize',12, 'FontWeight', 'Bold', 'BackgroundColor','white','Units','Pixels', 'Position',[HPos VPos 430 ThisPanelHeight]); - InPanelPos = 10; - for i = 1:nParams - ThisParamName = ThisPanelParamNames{i}; - ThisParam = Params.(ThisParamName); - BpodSystem.GUIData.ParameterGUI.ParamNames{ParamNum} = ThisParamName; - if ischar(ThisParam) - BpodSystem.GUIData.ParameterGUI.LastParamValues{ParamNum} = NaN; - else - BpodSystem.GUIData.ParameterGUI.LastParamValues{ParamNum} = ThisParam; - end - if isfield(Meta, ThisParamName) - if isstruct(Meta.(ThisParamName)) - if isfield(Meta.(ThisParamName), 'Style') - ThisParamStyle = Meta.(ThisParamName).Style; - if isfield(Meta.(ThisParamName), 'String') - ThisParamString = Meta.(ThisParamName).String; + for t = 1:nTabs + ThisTabPanelNames = Tabs.(TabNames{t}); + nPanels = length(ThisTabPanelNames); + uitab('title', TabNames{t}); + ParamNum = 1; + for p = 1:nPanels + ThisPanelParamNames = Panels.(ThisTabPanelNames{p}); + ThisPanelParamNames = ThisPanelParamNames(end:-1:1); + nParams = length(ThisPanelParamNames); + ThisPanelHeight = (45*nParams)+5; + uipanel('title', PanelNames{p},'FontSize',12, 'FontWeight', 'Bold', 'BackgroundColor','white','Units','Pixels', 'Position',[HPos VPos 430 ThisPanelHeight]); + InPanelPos = 10; + for i = 1:nParams + ThisParamName = ThisPanelParamNames{i}; + ThisParam = Params.(ThisParamName); + BpodSystem.GUIData.ParameterGUI.ParamNames{ParamNum} = ThisParamName; + if ischar(ThisParam) + BpodSystem.GUIData.ParameterGUI.LastParamValues{ParamNum} = NaN; + else + BpodSystem.GUIData.ParameterGUI.LastParamValues{ParamNum} = ThisParam; + end + if isfield(Meta, ThisParamName) + if isstruct(Meta.(ThisParamName)) + if isfield(Meta.(ThisParamName), 'Style') + ThisParamStyle = Meta.(ThisParamName).Style; + if isfield(Meta.(ThisParamName), 'String') + ThisParamString = Meta.(ThisParamName).String; + else + ThisParamString = ''; + end else - ThisParamString = ''; + error(['Style not specified for parameter ' ThisParamName '.']) end else - error(['Style not specified for parameter ' ThisParamName '.']) + error(['GUIMeta entry for ' ThisParamName ' must be a struct.']) end else - error(['GUIMeta entry for ' ThisParamName ' must be a struct.']) + ThisParamStyle = 'edit'; + ThisParamValue = NaN; end - else - ThisParamStyle = 'edit'; - ThisParamValue = NaN; - end - BpodSystem.GUIHandles.ParameterGUI.Labels(ParamNum) = uicontrol('Style', 'text', 'String', ThisParamName, 'Position', [HPos+5 VPos+InPanelPos 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); - switch lower(ThisParamStyle) - case 'edit' - BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 1; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); - case 'text' - BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 2; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); - case 'checkbox' - BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 3; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'checkbox', 'Value', ThisParam, 'String', ' (check to activate)', 'Position', [HPos+220 VPos+InPanelPos+4 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); - case 'popupmenu' - BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 4; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); - case 'togglebutton' % INCOMPLETE - BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 5; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); - case 'pushbutton' - BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 6; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'pushbutton', 'String', ThisParamString,... - 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12,... - 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center','Callback',Meta.OdorSettings.Callback); - otherwise - error('Invalid parameter style specified. Valid parameters are: ''edit'', ''text'', ''checkbox'', ''popupmenu'', ''togglebutton'', ''pushbutton'''); - end - InPanelPos = InPanelPos + 35; - ParamNum = ParamNum + 1; - end - % Check next panel to see if it will fit, otherwise start new column - Wrap = 0; - if p < nPanels - NextPanelParams = Panels.(PanelNames{p+1}); - NextPanelSize = (length(NextPanelParams)*45) + 5; - if VPos + ThisPanelHeight + 10 + NextPanelSize > GUIHeight - Wrap = 1; + BpodSystem.GUIHandles.ParameterGUI.Labels(ParamNum) = uicontrol('Style', 'text', 'String', ThisParamName, 'Position', [HPos+5 VPos+InPanelPos 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + switch lower(ThisParamStyle) + case 'edit' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 1; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'text' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 2; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'checkbox' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 3; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'checkbox', 'Value', ThisParam, 'String', ' (check to activate)', 'Position', [HPos+220 VPos+InPanelPos+4 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'popupmenu' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 4; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'togglebutton' % INCOMPLETE + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 5; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'pushbutton' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 6; + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'pushbutton', 'String', ThisParamString,... + 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12,... + 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center','Callback',Meta.OdorSettings.Callback); + otherwise + error('Invalid parameter style specified. Valid parameters are: ''edit'', ''text'', ''checkbox'', ''popupmenu'', ''togglebutton'', ''pushbutton'''); + end + InPanelPos = InPanelPos + 35; + ParamNum = ParamNum + 1; end - end - VPos = VPos + ThisPanelHeight + 10; - if Wrap - HPos = HPos + 450; - if VPos > MaxVPos - MaxVPos = VPos; + % Check next panel to see if it will fit, otherwise start new column + Wrap = 0; + if p < nPanels + NextPanelParams = Panels.(PanelNames{p+1}); + NextPanelSize = (length(NextPanelParams)*45) + 5; + if VPos + ThisPanelHeight + 10 + NextPanelSize > GUIHeight + Wrap = 1; + end end - VPos = 10; - else - if VPos > MaxVPos - MaxVPos = VPos; + VPos = VPos + ThisPanelHeight + 10; + if Wrap + HPos = HPos + 450; + if VPos > MaxVPos + MaxVPos = VPos; + end + VPos = 10; + else + if VPos > MaxVPos + MaxVPos = VPos; + end end end end From d138406e3f9ac1878d42e813fee93c9daad8c8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Tue, 5 Jul 2016 10:59:16 -0400 Subject: [PATCH 05/30] Tabs working. Now adding uitable. --- .../Plugins/ParameterGUI/BpodParameterGUI.m | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index 6d03b50..762a937 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -68,21 +68,24 @@ Params = Params.GUI; PanelNames = PanelNames(end:-1:1); GUIHeight = 650; - VPos = 10; - HPos = 10; MaxVPos = 0; + MaxHPos = 0; + ParamNum = 1; BpodSystem.ProtocolFigures.ParameterGUI = figure('Position', [50 50 450 GUIHeight],'name','Parameter GUI','numbertitle','off', 'MenuBar', 'none', 'Resize', 'on'); + BpodSystem.GUIHandles.ParameterGUI.TabGroup = uitabgroup(BpodSystem.ProtocolFigures.ParameterGUI); for t = 1:nTabs + VPos = 10; + HPos = 10; ThisTabPanelNames = Tabs.(TabNames{t}); nPanels = length(ThisTabPanelNames); - uitab('title', TabNames{t}); - ParamNum = 1; + htab = uitab('title', TabNames{t}); + BpodSystem.GUIHandles.ParameterGUI.Tab{t} = htab; for p = 1:nPanels ThisPanelParamNames = Panels.(ThisTabPanelNames{p}); ThisPanelParamNames = ThisPanelParamNames(end:-1:1); nParams = length(ThisPanelParamNames); ThisPanelHeight = (45*nParams)+5; - uipanel('title', PanelNames{p},'FontSize',12, 'FontWeight', 'Bold', 'BackgroundColor','white','Units','Pixels', 'Position',[HPos VPos 430 ThisPanelHeight]); + uipanel(htab,'title', ThisTabPanelNames{p},'FontSize',12, 'FontWeight', 'Bold', 'BackgroundColor','white','Units','Pixels', 'Position',[HPos VPos 430 ThisPanelHeight]); InPanelPos = 10; for i = 1:nParams ThisParamName = ThisPanelParamNames{i}; @@ -112,26 +115,26 @@ ThisParamStyle = 'edit'; ThisParamValue = NaN; end - BpodSystem.GUIHandles.ParameterGUI.Labels(ParamNum) = uicontrol('Style', 'text', 'String', ThisParamName, 'Position', [HPos+5 VPos+InPanelPos 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Labels(ParamNum) = uicontrol(htab,'Style', 'text', 'String', ThisParamName, 'Position', [HPos+5 VPos+InPanelPos 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); switch lower(ThisParamStyle) case 'edit' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 1; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'text' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 2; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'checkbox' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 3; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'checkbox', 'Value', ThisParam, 'String', ' (check to activate)', 'Position', [HPos+220 VPos+InPanelPos+4 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'checkbox', 'Value', ThisParam, 'String', ' (check to activate)', 'Position', [HPos+220 VPos+InPanelPos+4 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'popupmenu' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 4; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'togglebutton' % INCOMPLETE BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 5; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'pushbutton' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 6; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol('Style', 'pushbutton', 'String', ThisParamString,... + BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'pushbutton', 'String', ThisParamString,... 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12,... 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center','Callback',Meta.OdorSettings.Callback); otherwise @@ -143,9 +146,9 @@ % Check next panel to see if it will fit, otherwise start new column Wrap = 0; if p < nPanels - NextPanelParams = Panels.(PanelNames{p+1}); + NextPanelParams = Panels.(ThisTabPanelNames{p+1}); NextPanelSize = (length(NextPanelParams)*45) + 5; - if VPos + ThisPanelHeight + 10 + NextPanelSize > GUIHeight + if VPos + ThisPanelHeight + 45 + NextPanelSize > GUIHeight Wrap = 1; end end @@ -161,9 +164,12 @@ MaxVPos = VPos; end end - end - end - set(BpodSystem.ProtocolFigures.ParameterGUI, 'Position', [50 50 HPos+450 MaxVPos+10]); + if HPos > MaxHPos + MaxHPos = HPos; + end + set(BpodSystem.ProtocolFigures.ParameterGUI, 'Position', [50 50 MaxHPos+450 MaxVPos+45]); + end + end case 'sync' ParamNames = BpodSystem.GUIData.ParameterGUI.ParamNames; nParams = BpodSystem.GUIData.ParameterGUI.nParams; From 53f84260947f6f812cc73f5cffb6a99e1a5cbdeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Tue, 5 Jul 2016 15:46:15 -0400 Subject: [PATCH 06/30] Accepts meta.style uitables. Stores handles in Matlab 2016. --- .../Plugins/ParameterGUI/BpodParameterGUI.m | 59 +++++++++++++++---- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index 762a937..d45606b 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -39,7 +39,7 @@ BpodSystem.GUIData.ParameterGUI.ParamNames = cell(1,nParams); BpodSystem.GUIData.ParameterGUI.nParams = nParams; BpodSystem.GUIHandles.ParameterGUI.Labels = zeros(1,nParams); - BpodSystem.GUIHandles.ParameterGUI.Params = zeros(1,nParams); + BpodSystem.GUIHandles.ParameterGUI.Params = cell(1,nParams); BpodSystem.GUIData.ParameterGUI.LastParamValues = cell(1,nParams); if isfield(Params, 'GUIMeta') Meta = Params.GUIMeta; @@ -72,20 +72,20 @@ MaxHPos = 0; ParamNum = 1; BpodSystem.ProtocolFigures.ParameterGUI = figure('Position', [50 50 450 GUIHeight],'name','Parameter GUI','numbertitle','off', 'MenuBar', 'none', 'Resize', 'on'); - BpodSystem.GUIHandles.ParameterGUI.TabGroup = uitabgroup(BpodSystem.ProtocolFigures.ParameterGUI); + BpodSystem.GUIHandles.ParameterGUI.Tabs.TabGroup = uitabgroup(BpodSystem.ProtocolFigures.ParameterGUI); for t = 1:nTabs VPos = 10; HPos = 10; ThisTabPanelNames = Tabs.(TabNames{t}); nPanels = length(ThisTabPanelNames); - htab = uitab('title', TabNames{t}); - BpodSystem.GUIHandles.ParameterGUI.Tab{t} = htab; + BpodSystem.GUIHandles.ParameterGUI.Tabs.(TabNames{t}) = uitab('title', TabNames{t}); + htab = BpodSystem.GUIHandles.ParameterGUI.Tabs.(TabNames{t}); for p = 1:nPanels ThisPanelParamNames = Panels.(ThisTabPanelNames{p}); ThisPanelParamNames = ThisPanelParamNames(end:-1:1); nParams = length(ThisPanelParamNames); ThisPanelHeight = (45*nParams)+5; - uipanel(htab,'title', ThisTabPanelNames{p},'FontSize',12, 'FontWeight', 'Bold', 'BackgroundColor','white','Units','Pixels', 'Position',[HPos VPos 430 ThisPanelHeight]); + BpodSystem.GUIHandles.ParameterGUI.Panels.(ThisTabPanelNames{p}) = uipanel(htab,'title', ThisTabPanelNames{p},'FontSize',12, 'FontWeight', 'Bold', 'BackgroundColor','white','Units','Pixels', 'Position',[HPos VPos 430 ThisPanelHeight]); InPanelPos = 10; for i = 1:nParams ThisParamName = ThisPanelParamNames{i}; @@ -119,24 +119,45 @@ switch lower(ThisParamStyle) case 'edit' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 1; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'text' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 2; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'checkbox' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 3; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'checkbox', 'Value', ThisParam, 'String', ' (check to activate)', 'Position', [HPos+220 VPos+InPanelPos+4 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'checkbox', 'Value', ThisParam, 'String', ' (check to activate)', 'Position', [HPos+220 VPos+InPanelPos+4 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'popupmenu' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 4; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'popupmenu', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'togglebutton' % INCOMPLETE BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 5; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'togglebutton', 'String', ThisParamString, 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'pushbutton' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 6; - BpodSystem.GUIHandles.ParameterGUI.Params(ParamNum) = uicontrol(htab,'Style', 'pushbutton', 'String', ThisParamString,... + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'pushbutton', 'String', ThisParamString,... 'Value', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12,... 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center','Callback',Meta.OdorSettings.Callback); + case 'table' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 7; + columnNames = fieldnames(Params.(ThisParamName)); + if isfield(Meta.(ThisParamName),'ColumnLabel') + columnLabel = Meta.(ThisParamName).ColumnLabel; + else + columnLabel = columnNames; + end + tableData = []; + for iTableCol = 1:numel(columnNames) + tableData = [tableData, Params.(ThisParamName).(columnNames{iTableCol})]; + end +% tableData(:,2) = tableData(:,2)/sum(tableData(:,2)); + htable = uitable(htab,'data',tableData,'columnname',columnLabel,... + 'ColumnEditable',[true true], 'FontSize', 12); + htable.Position([3 4]) = htable.Extent([3 4]); + htable.Position([1 2]) = [HPos+220 VPos+InPanelPos+2]; + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = htable; + ThisPanelHeight = ThisPanelHeight + (htable.Position(4)-25); + BpodSystem.GUIHandles.ParameterGUI.Panels.(ThisTabPanelNames{p}).Position(4) = ThisPanelHeight; + BpodSystem.GUIData.ParameterGUI.LastParamValues{ParamNum} = htable.Data; otherwise error('Invalid parameter style specified. Valid parameters are: ''edit'', ''text'', ''checkbox'', ''popupmenu'', ''togglebutton'', ''pushbutton'''); end @@ -176,7 +197,7 @@ for p = 1:nParams ThisParamName = ParamNames{p}; ThisParamStyle = BpodSystem.GUIData.ParameterGUI.Styles(p); - ThisParamHandle = BpodSystem.GUIHandles.ParameterGUI.Params(p); + ThisParamHandle = BpodSystem.GUIHandles.ParameterGUI.Params{p}; ThisParamLastValue = BpodSystem.GUIData.ParameterGUI.LastParamValues{p}; switch ThisParamStyle case 1 % Edit @@ -214,6 +235,20 @@ elseif Params.GUI.(ThisParamName) ~= ThisParamLastValue set(ThisParamHandle, 'Value', GUIParam); end + case 7 + GUIParam = ThisParamHandle.Data; + columnNames = fieldnames(Params.GUI.(ThisParamName)); + argData = []; + for iColumn = 1:numel(columnNames) + argData = [argData, Params.GUI.(ThisParamName).(columnNames{iColumn})]; + end + if any(GUIParam(:) ~= ThisParamLastValue(:)) % Change originated in the GUI propagates to TaskParameters + for iColumn = 1:numel(columnNames) + Params.GUI.(ThisParamName).(columnNames{iColumn}) = GUIParam(:,iColumn); + end + elseif any(argData(:) ~= ThisParamLastValue(:)) % Change originated in TaskParameters propagates to the GUI + ThisParamHandle.Data = argData; + end end BpodSystem.GUIData.ParameterGUI.LastParamValues{p} = GUIParam; end From a1a82cafda875f21fd6ee17b2c4f612668106878 Mon Sep 17 00:00:00 2001 From: lclclclclclclc Date: Fri, 15 Jul 2016 14:51:47 -0400 Subject: [PATCH 07/30] Added purpose of Kepecs Bpod fork --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d1ca2a..c72b2c5 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ -# Bpod \ No newline at end of file +# Bpod + +Kepecs Lab workspace, forked from Sanworks. + +Used to modify Bpod code for use in Kepecs Lab. +General improvements will be contributed to Sanworks' repo. From 0186480bec4545f937bea6a48d48074baaa2ca34 Mon Sep 17 00:00:00 2001 From: Fitz Sturgill Date: Thu, 4 Aug 2016 12:16:52 -0400 Subject: [PATCH 08/30] Bpod Settings, Calibration Files, and Protocols are saved in the user-specific home directory (e.g. /Users/JoeUser/BpodUser/). This BpodUser directory is created automatically if not found. BpodInputConfig copied to this new directory automatically. Calibration files and protocols and data (if desired) must be copied manually. I've tested this on PC but not OSX or Unix !!! Related to #2 and #4 --- Functions/Internal Functions/BpodObject.m | 44 +++++++++++++++---- .../SaveBpodSystemSettings.m | 2 +- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Functions/Internal Functions/BpodObject.m b/Functions/Internal Functions/BpodObject.m index 997bde8..d567839 100644 --- a/Functions/Internal Functions/BpodObject.m +++ b/Functions/Internal Functions/BpodObject.m @@ -32,6 +32,7 @@ ProtocolSettings Data BpodPath + BpodUserPath % FS MOD SettingsPath DataPath ProtocolPath @@ -117,15 +118,29 @@ obj.HostOS = system_dependent('getos'); obj.BpodPath = BpodPath; - dir_calfiles = dir( fullfile(obj.BpodPath,'Calibration Files') ); +%% FS MOD + if ispc + import java.lang.*; + S.BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); + if ~isdir(S.BpodUserPath) + mkdir(S.BpodUserPath); + disp('*** Bpod User Directories Not Found, Creating them ***'); + end + elseif ~isdir(fullfile('~', 'BpodUser')) % tilde works on UNIX and MAC platforms to specify user home directory + mkdir(fullfile('~', 'BpodUser')); + disp('*** Bpod User Directories Not Found, Creating them ***'); + end + obj.BpodUserPath = S.BpodUserPath; + %% + dir_calfiles = dir(fullfile(obj.BpodUserPath,'Calibration Files') ); % FS MOD if length(dir_calfiles) == 0, %then Cal Folder didn't exist. - mkdir(fullfile(obj.BpodPath,'Calibration Files')); + mkdir(fullfile(obj.BpodUserPath,'Calibration Files')); % FS MOD obj.CalibrationTables.LiquidCal = []; obj.CalibrationTables.SoundCal = []; else % Liquid try - LiquidCalibrationFilePath = fullfile(obj.BpodPath, 'Calibration Files', 'LiquidCalibration.mat'); + LiquidCalibrationFilePath = fullfile(obj.BpodUserPath, 'Calibration Files', 'LiquidCalibration.mat'); % FS MOD load(LiquidCalibrationFilePath); obj.CalibrationTables.LiquidCal = LiquidCal; catch @@ -133,7 +148,7 @@ end % Sound try - SoundCalibrationFilePath = fullfile(obj.BpodPath, 'Calibration Files', 'SoundCalibration.mat'); + SoundCalibrationFilePath = fullfile(obj.BpodUserPath, 'Calibration Files', 'SoundCalibration.mat'); % FS MOD load(SoundCalibrationFilePath); obj.CalibrationTables.SoundCal = SoundCal; catch @@ -141,8 +156,14 @@ end end % Load input channel settings - obj.InputConfigPath = fullfile(obj.BpodPath, 'Settings Files', 'BpodInputConfig.mat'); - load(obj.InputConfigPath); + obj.InputConfigPath = fullfile(obj.BpodUserPath, 'Settings Files', 'BpodInputConfig.mat'); % FS MOD + try + load(obj.InputConfigPath); + catch + mkdir(fullfile(obj.BpodUserPath,'Settings Files')); % FS MOD + copyfile(fullfile(obj.BpodPath, 'Settings Files', 'BpodInputConfig.mat'), fullfile(obj.BpodUserPath, 'Settings Files')); + load(obj.InputConfigPath); + end obj.InputsEnabled = BpodInputConfig; % Determine if PsychToolbox is installed. If so, serial communication @@ -155,9 +176,9 @@ obj.UsesPsychToolbox = 0; end %Check for Data folder - dir_data = dir(fullfile(obj.BpodPath,'Data')); + dir_data = dir(fullfile(obj.BpodUserPath,'Data')); % FS MOD if length(dir_data) == 0, %then Data didn't exist. - mkdir(fullfile(obj.BpodPath, 'Data')); + mkdir(fullfile(obj.BpodUserPath, 'Data')); end end function obj = InitializeHardware(obj, portString) @@ -425,7 +446,12 @@ set(obj.GUIHandles.MainFig,'handlevisibility','off'); % Load protocols into selector - ProtocolPath = fullfile(obj.BpodPath,'Protocols'); + %% FS MOD + ProtocolPath = fullfile(obj.BpodUserPath,'Protocols'); + if ~isdir(ProtocolPath) + mkdir(ProtocolPath) + end +%% Candidates = dir(ProtocolPath); ProtocolNames = cell(1); nCandidates = length(Candidates)-2; diff --git a/Functions/Internal Functions/SaveBpodSystemSettings.m b/Functions/Internal Functions/SaveBpodSystemSettings.m index 40130f8..55f93a1 100644 --- a/Functions/Internal Functions/SaveBpodSystemSettings.m +++ b/Functions/Internal Functions/SaveBpodSystemSettings.m @@ -1,4 +1,4 @@ function SaveBpodSystemSettings global BpodSystem BpodSystemSettings = BpodSystem.SystemSettings; -save(fullfile(BpodSystem.BpodPath, 'Settings Files', 'BpodSystemSettings.mat'), 'BpodSystemSettings'); \ No newline at end of file +save(fullfile(BpodSystem.BpodUserPath, 'Settings Files', 'BpodSystemSettings.mat'), 'BpodSystemSettings'); % FS MOD \ No newline at end of file From c4160598a858cfec5dd350a126552450fb8ff045 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Fri, 5 Aug 2016 17:29:29 -0400 Subject: [PATCH 09/30] checks for calibration and settings folders in BpodUserPath first and copies whole folder, if they don't exist. removes unecessary logical cses. --- Functions/Internal Functions/BpodObject.m | 50 +++++++++++------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/Functions/Internal Functions/BpodObject.m b/Functions/Internal Functions/BpodObject.m index d567839..037dcaa 100644 --- a/Functions/Internal Functions/BpodObject.m +++ b/Functions/Internal Functions/BpodObject.m @@ -132,38 +132,34 @@ end obj.BpodUserPath = S.BpodUserPath; %% - dir_calfiles = dir(fullfile(obj.BpodUserPath,'Calibration Files') ); % FS MOD - if length(dir_calfiles) == 0, %then Cal Folder didn't exist. - mkdir(fullfile(obj.BpodUserPath,'Calibration Files')); % FS MOD + %setting up costum folders + if ~isdir(fullfile(obj.BpodUserPath,'Calibration Files')) %then Cal Folder didn't exist. + copyfile(fullfile(obj.BpodPath, 'Calibration Files'),fullfile(obj.BpodUserPath,'Calibration Files')); % FS MOD + end + if ~isdir(fullfile(obj.BpodUserPath,'Settings Files')) + copyfile(fullfile(obj.BpodPath, 'Settings Files'), fullfile(obj.BpodUserPath, 'Settings Files')); + end + + % Liquid + try + LiquidCalibrationFilePath = fullfile(obj.BpodUserPath, 'Calibration Files', 'LiquidCalibration.mat'); % FS MOD + load(LiquidCalibrationFilePath); + obj.CalibrationTables.LiquidCal = LiquidCal; + catch obj.CalibrationTables.LiquidCal = []; - obj.CalibrationTables.SoundCal = []; - else - % Liquid - try - LiquidCalibrationFilePath = fullfile(obj.BpodUserPath, 'Calibration Files', 'LiquidCalibration.mat'); % FS MOD - load(LiquidCalibrationFilePath); - obj.CalibrationTables.LiquidCal = LiquidCal; - catch - obj.CalibrationTables.LiquidCal = []; - end - % Sound - try - SoundCalibrationFilePath = fullfile(obj.BpodUserPath, 'Calibration Files', 'SoundCalibration.mat'); % FS MOD - load(SoundCalibrationFilePath); - obj.CalibrationTables.SoundCal = SoundCal; - catch - obj.CalibrationTables.SoundCal = []; - end end - % Load input channel settings - obj.InputConfigPath = fullfile(obj.BpodUserPath, 'Settings Files', 'BpodInputConfig.mat'); % FS MOD + % Sound try - load(obj.InputConfigPath); + SoundCalibrationFilePath = fullfile(obj.BpodUserPath, 'Calibration Files', 'SoundCalibration.mat'); % FS MOD + load(SoundCalibrationFilePath); + obj.CalibrationTables.SoundCal = SoundCal; catch - mkdir(fullfile(obj.BpodUserPath,'Settings Files')); % FS MOD - copyfile(fullfile(obj.BpodPath, 'Settings Files', 'BpodInputConfig.mat'), fullfile(obj.BpodUserPath, 'Settings Files')); - load(obj.InputConfigPath); + obj.CalibrationTables.SoundCal = []; end + + % Load input channel settings + obj.InputConfigPath = fullfile(obj.BpodUserPath, 'Settings Files', 'BpodInputConfig.mat'); % FS MOD + load(obj.InputConfigPath); obj.InputsEnabled = BpodInputConfig; % Determine if PsychToolbox is installed. If so, serial communication From 62246eed0845ff81ccd0c243be10565db1adf4fd Mon Sep 17 00:00:00 2001 From: Fitz Sturgill Date: Mon, 8 Aug 2016 19:11:11 -0400 Subject: [PATCH 10/30] In progress, still need to finish updating instances of BpodPath --- Functions/Launch manager/LaunchManager.m | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Functions/Launch manager/LaunchManager.m b/Functions/Launch manager/LaunchManager.m index 1239bd8..6c9df4c 100644 --- a/Functions/Launch manager/LaunchManager.m +++ b/Functions/Launch manager/LaunchManager.m @@ -91,9 +91,9 @@ function LaunchManager_OpeningFcn(hObject, eventdata, handles, varargin) % Check to see if a folder for this protocol's name exists, and if not, make one % along with default files for the default test subject named "Test Session" -DataPath = fullfile(BpodSystem.BpodPath,'Data',DummySubjectString); +DataPath = fullfile(BpodSystem.BpodUserPath,'Data',DummySubjectString); ProtocolName = BpodSystem.CurrentProtocolName; -ProtocolPath = fullfile(BpodSystem.BpodPath,'Protocols',ProtocolName); +ProtocolPath = fullfile(BpodSystem.BpodUserPath,'Protocols',ProtocolName); SettingsPath = fullfile(DataPath,ProtocolName,'Session Settings'); DefaultSettingsPath = fullfile(ProtocolPath,'SessionSettings.mat'); @@ -113,7 +113,7 @@ function LaunchManager_OpeningFcn(hObject, eventdata, handles, varargin) % Make a list of the names of all subjects who already have a folder for this % protocol. -DataPath = fullfile(BpodSystem.BpodPath,'Data'); +DataPath = fullfile(BpodSystem.BpodUserPath,'Data'); CandidateSubjects = dir(DataPath); SubjectNames = cell(1); nSubjects = 1; @@ -133,7 +133,7 @@ function LaunchManager_OpeningFcn(hObject, eventdata, handles, varargin) end set(handles.listbox1,'String',SubjectNames); -SettingsPath = fullfile(BpodSystem.BpodPath,'Data',DummySubjectString, ProtocolName,'Session Settings'); +SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',DummySubjectString, ProtocolName,'Session Settings'); Candidates = dir(SettingsPath); nSettingsFiles = 0; SettingsFileNames = cell(1); @@ -211,7 +211,7 @@ function listbox1_Callback(hObject, eventdata, handles) else SelectedName = NameList; end -SettingsPath = fullfile(BpodSystem.BpodPath,'Data',SelectedName,ProtocolName,'Session Settings'); +SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SelectedName,ProtocolName,'Session Settings'); Candidates = dir(SettingsPath); nSettingsFiles = 0; SettingsFileNames = cell(1); @@ -268,7 +268,7 @@ function pushbutton1_Callback(hObject, eventdata, handles) if ~isempty(SettingsFileName) && ~isempty(NameList) ProtocolName = BpodSystem.CurrentProtocolName; FormattedDate = [datestr(now, 3) datestr(now, 7) '_' datestr(now, 10)]; - DataFolder = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName, 'Session Data'); + DataFolder = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName, 'Session Data'); Candidates = dir(DataFolder); nSessionsToday = 0; for x = 1:length(Candidates) @@ -278,11 +278,11 @@ function pushbutton1_Callback(hObject, eventdata, handles) end end end - DataPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName,'Session Data',[SubjectName '_' ProtocolName '_' FormattedDate '_Session' num2str(nSessionsToday+1) '.mat']); - SettingsPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName, 'Session Settings',[SettingsFileName '.mat']); + DataPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName,'Session Data',[SubjectName '_' ProtocolName '_' FormattedDate '_Session' num2str(nSessionsToday+1) '.mat']); + SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName, 'Session Settings',[SettingsFileName '.mat']); BpodSystem.DataPath = DataPath; BpodSystem.SettingsPath = SettingsPath; - ProtocolPath = fullfile(BpodSystem.BpodPath,'Protocols',ProtocolName,[ProtocolName '.m']); + ProtocolPath = fullfile(BpodSystem.BpodUserPath,'Protocols',ProtocolName,[ProtocolName '.m']); close(LaunchManager) BpodSystem.Live = 1; BpodSystem.GUIData.ProtocolName = ProtocolName; @@ -331,9 +331,9 @@ function pushbutton2_Callback(hObject, eventdata, handles) % ------------------ NewName = Spaces2Underscores(NewName); % Check to see if subject already exists ProtocolName = BpodSystem.CurrentProtocolName; -Testpath = fullfile(BpodSystem.BpodPath,'Data',NewName); +Testpath = fullfile(BpodSystem.BpodUserPath,'Data',NewName); Testpath2 = fullfile(Testpath,ProtocolName); -ProtocolPath = fullfile(BpodSystem.BpodPath,'Protocols',ProtocolName); +ProtocolPath = fullfile(BpodSystem.BpodUserPath,'Protocols',ProtocolName); NewAnimal = 0; if exist(Testpath) ~= 7 mkdir(Testpath); @@ -397,7 +397,7 @@ function pushbutton3_Callback(hObject, eventdata, handles) catch end if ((OkToDelete == 1) && (~isempty(SelectedName))) - DeletePath = fullfile(BpodSystem.BpodPath,'Data',SelectedName); + DeletePath = fullfile(BpodSystem.BpodUserPath,'Data',SelectedName); rmdir(DeletePath,'s') BpodErrorSound; msgbox([' Entry for ' SelectedName ' deleted!'], 'Modal'); @@ -459,7 +459,7 @@ function pushbutton4_Callback(hObject, eventdata, handles) SubjectName = SubjectNameList{SubjectNameValue}; % Check to see if subject already exists ProtocolName = BpodSystem.CurrentProtocolName; -Testpath = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName,'Session Settings',[NewSettingsName '.mat' ]); +Testpath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName,'Session Settings',[NewSettingsName '.mat' ]); if exist(Testpath) == 0 SettingsPath = Testpath; ProtocolSettings = struct; @@ -502,13 +502,13 @@ function pushbutton5_Callback(hObject, eventdata, handles) Selected = get(handles.listbox2, 'Value'); SettingsFileName = NameList{Selected}; ProtocolName = BpodSystem.CurrentProtocolName; -SettingsPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName,'Session Settings',[ SettingsFileName '.mat']); +SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName,'Session Settings',[ SettingsFileName '.mat']); delete(SettingsPath); BpodErrorSound; msgbox(['Settings file ' SettingsFileName ' deleted!'], 'Modal'); set(handles.listbox2, 'Value', 1); % Update UI with new settings -SettingsPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName, ProtocolName,'Session Settings'); +SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName, ProtocolName,'Session Settings'); Candidates = dir(SettingsPath); nSettingsFiles = 0; SettingsFileNames = cell(1); @@ -549,7 +549,7 @@ function pushbutton6_Callback(hObject, eventdata, handles) Selected = get(handles.listbox2, 'Value'); SettingsFileName = NameList{Selected}; ProtocolName = BpodSystem.CurrentProtocolName; -SettingsPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName,'Session Settings',[ SettingsFileName '.mat']); +SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName,'Session Settings',[ SettingsFileName '.mat']); BpodSystem.SettingsPath = SettingsPath; evalin('base', ['load(''' SettingsPath ''')']) clc From eda212840d0b89d37015534c08e841fc7f74f2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Mon, 8 Aug 2016 22:27:14 -0400 Subject: [PATCH 11/30] Removes reference to undefined variable S. Moves redundant bits of code out of if clause. --- Functions/Internal Functions/BpodObject.m | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Functions/Internal Functions/BpodObject.m b/Functions/Internal Functions/BpodObject.m index 037dcaa..5093815 100644 --- a/Functions/Internal Functions/BpodObject.m +++ b/Functions/Internal Functions/BpodObject.m @@ -121,17 +121,14 @@ %% FS MOD if ispc import java.lang.*; - S.BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); - if ~isdir(S.BpodUserPath) - mkdir(S.BpodUserPath); - disp('*** Bpod User Directories Not Found, Creating them ***'); + obj.BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); + else + obj.BpodUserPath = fullfile('~', 'BpodUser'); end - elseif ~isdir(fullfile('~', 'BpodUser')) % tilde works on UNIX and MAC platforms to specify user home directory - mkdir(fullfile('~', 'BpodUser')); - disp('*** Bpod User Directories Not Found, Creating them ***'); + if ~isdir(obj.BpodUserPath) + mkdir(obj.BpodUserPath); + warning(['Bpod user directory not found. Directory created at ' obj.BpodUserPath]); end - obj.BpodUserPath = S.BpodUserPath; - %% %setting up costum folders if ~isdir(fullfile(obj.BpodUserPath,'Calibration Files')) %then Cal Folder didn't exist. copyfile(fullfile(obj.BpodPath, 'Calibration Files'),fullfile(obj.BpodUserPath,'Calibration Files')); % FS MOD From a988ae505fd83d7a2b8e0e7a88bb8670fb6a7f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Gouve=CC=82a?= Date: Tue, 9 Aug 2016 17:18:33 -0400 Subject: [PATCH 12/30] Fixes bug with unspecified tabs --- Functions/Plugins/ParameterGUI/BpodParameterGUI.m | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index d45606b..6abf061 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -55,16 +55,14 @@ PanelNames = {'Parameters'}; end if isfield(Params, 'GUITabs') - Tabs = Params.GUITabs; - TabNames = fieldnames(Tabs); - nTabs = length(TabNames); + Tabs = Params.GUITabs; else Tabs = struct; - Tabs.Panels = PanelNames; - TabNames = {'Parameters'}; - nTabs = 1; + Tabs.Parameters = PanelNames; end - + TabNames = fieldnames(Tabs); + nTabs = length(TabNames); + Params = Params.GUI; PanelNames = PanelNames(end:-1:1); GUIHeight = 650; From ce7b9acbe35ec12ea24bd2b747f9f05d0387f1c2 Mon Sep 17 00:00:00 2001 From: Fitz Sturgill Date: Tue, 6 Sep 2016 16:14:42 -0400 Subject: [PATCH 13/30] Updated LaunchManager, RunProtocol, BpodLiquidCalibration, and Pipeline to utilize BpodSystem.BpodUserPath where appropriate. Still need to test these changes on a rig. --- Functions/Launch manager/LaunchManager.m | 6 +++--- Functions/Launch manager/RunProtocol.m | 4 ++-- Functions/LiquidCalibrator/BpodLiquidCalibration.m | 10 +++++----- Functions/Plugins/Pipeline/Pipeline.m | 4 ++-- .../Plugins/RechiaOlfactometer/OlfactometerConfig.m | 3 +++ 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Functions/Launch manager/LaunchManager.m b/Functions/Launch manager/LaunchManager.m index 6c9df4c..74a6aba 100644 --- a/Functions/Launch manager/LaunchManager.m +++ b/Functions/Launch manager/LaunchManager.m @@ -567,7 +567,7 @@ function pushbutton7_Callback(hObject, eventdata, handles) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) global BpodSystem; -SearchStartPath = fullfile(BpodSystem.BpodPath, 'Data'); +SearchStartPath = fullfile(BpodSystem.BpodUserPath, 'Data'); [Filename Pathname Junk] = uigetfile('*.mat', 'Select settings to import', SearchStartPath); SettingsName = Filename(1:(length(Filename)-4)); TargetSettingsPath = [Pathname Filename]; @@ -582,7 +582,7 @@ function pushbutton7_Callback(hObject, eventdata, handles) Selected = get(handles.listbox1, 'Value'); SubjectName = NameList{Selected}; ProtocolName = BpodSystem.CurrentProtocolName; -DestinationSettingsPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName,ProtocolName,'Session Settings',[ SettingsName '.mat']); +DestinationSettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName,ProtocolName,'Session Settings',[ SettingsName '.mat']); if (exist(DestinationSettingsPath) == 2) msgbox(['"' SettingsName '"' ' already exists in the target folder. Import aborted.']) @@ -593,7 +593,7 @@ function pushbutton7_Callback(hObject, eventdata, handles) copyfile(TargetSettingsPath, DestinationSettingsPath); % Update UI with new settings -SettingsPath = fullfile(BpodSystem.BpodPath,'Data',SubjectName, ProtocolName,'Session Settings'); +SettingsPath = fullfile(BpodSystem.BpodUserPath,'Data',SubjectName, ProtocolName,'Session Settings'); Candidates = dir(SettingsPath); nSettingsFiles = 0; SettingsFileNames = cell(1); diff --git a/Functions/Launch manager/RunProtocol.m b/Functions/Launch manager/RunProtocol.m index 6f5a2ef..380e24a 100644 --- a/Functions/Launch manager/RunProtocol.m +++ b/Functions/Launch manager/RunProtocol.m @@ -27,7 +27,7 @@ function RunProtocol(Opstring) SelectedProtocol = get(BpodSystem.GUIHandles.ProtocolSelector, 'Value'); SelectedProtocolName = ProtocolNames{SelectedProtocol}; BpodSystem.CurrentProtocolName = SelectedProtocolName; - addpath(fullfile(BpodSystem.BpodPath, 'Protocols', SelectedProtocolName)); + addpath(fullfile(BpodSystem.BpodUserPath, 'Protocols', SelectedProtocolName)); LaunchManager; else if BpodSystem.Pause == 0 @@ -45,7 +45,7 @@ function RunProtocol(Opstring) disp(' ') disp([BpodSystem.CurrentProtocolName ' ended.']) end - rmpath(fullfile(BpodSystem.BpodPath, 'Protocols', BpodSystem.CurrentProtocolName)); + rmpath(fullfile(BpodSystem.BpodUserPath, 'Protocols', BpodSystem.CurrentProtocolName)); BpodSystem.BeingUsed = 0; BpodSystem.CurrentProtocolName = ''; BpodSystem.SettingsPath = ''; diff --git a/Functions/LiquidCalibrator/BpodLiquidCalibration.m b/Functions/LiquidCalibrator/BpodLiquidCalibration.m index a0294d1..f21f933 100644 --- a/Functions/LiquidCalibrator/BpodLiquidCalibration.m +++ b/Functions/LiquidCalibrator/BpodLiquidCalibration.m @@ -40,7 +40,7 @@ % Setup calibration BpodSystem.PluginObjects.LiquidCal.PendingMeasurements = cell(1,8); - CalibrationFilePath = fullfile(BpodSystem.BpodPath, 'Calibration Files', 'LiquidCalibration.mat'); + CalibrationFilePath = fullfile(BpodSystem.BpodUserPath, 'Calibration Files', 'LiquidCalibration.mat'); if exist(CalibrationFilePath) == 2 load(CalibrationFilePath); else @@ -329,11 +329,11 @@ function RemoveMeasurement(varargin) BpodSystem.PluginObjects.LiquidCal.CalData(CurrentValve).Coeffs = []; end % Save file - TestSavePath = fullfile(BpodSystem.BpodPath, 'Calibration Files'); + TestSavePath = fullfile(BpodSystem.BpodUserPath, 'Calibration Files'); if exist(TestSavePath) ~= 7 mkdir(TestSavePath); end - SavePath = fullfile(BpodSystem.BpodPath, 'Calibration Files', 'LiquidCalibration.mat'); + SavePath = fullfile(BpodSystem.BpodUserPath, 'Calibration Files', 'LiquidCalibration.mat'); LiquidCal = BpodSystem.PluginObjects.LiquidCal.CalData; LiquidCal(1).LastDateModified = now; save(SavePath, 'LiquidCal'); @@ -696,11 +696,11 @@ function AddCalMeasurements(varargin) % LiquidCalibrationManager to reflect the new pending measurements vector % Save file - TestSavePath = fullfile(BpodSystem.BpodPath, 'Calibration Files'); + TestSavePath = fullfile(BpodSystem.BpodUserPath, 'Calibration Files'); if exist(TestSavePath) ~= 7 mkdir(TestSavePath); end - SavePath = fullfile(BpodSystem.BpodPath, 'Calibration Files', 'LiquidCalibration.mat'); + SavePath = fullfile(BpodSystem.BpodUserPath, 'Calibration Files', 'LiquidCalibration.mat'); LiquidCal = BpodSystem.PluginObjects.LiquidCal.CalData; LiquidCal(1).LastDateModified = now; save(SavePath, 'LiquidCal'); diff --git a/Functions/Plugins/Pipeline/Pipeline.m b/Functions/Plugins/Pipeline/Pipeline.m index dc89cda..74886cd 100644 --- a/Functions/Plugins/Pipeline/Pipeline.m +++ b/Functions/Plugins/Pipeline/Pipeline.m @@ -61,7 +61,7 @@ catch end FormattedDate = [datestr(now, 3) datestr(now, 7) '_' datestr(now, 10)]; - DataFolder = fullfile(BpodSystem.BpodPath,'Data',currentAnimalName,BpodSystem.CurrentProtocolName, 'Session Data'); + DataFolder = fullfile(BpodSystem.BpodUserPath,'Data',currentAnimalName,BpodSystem.CurrentProtocolName, 'Session Data'); Candidates = dir(DataFolder); nSessionsToday = 0; for x = 1:length(Candidates) @@ -71,7 +71,7 @@ end end end - DataPath = fullfile(BpodSystem.BpodPath,'Data',currentAnimalName,BpodSystem.CurrentProtocolName,'Session Data',[BpodSystem.GUIData.SubjectName '_' BpodSystem.CurrentProtocolName '_' FormattedDate '_Session' num2str(nSessionsToday+1) '.mat']); + DataPath = fullfile(BpodSystem.BpodUserPath,'Data',currentAnimalName,BpodSystem.CurrentProtocolName,'Session Data',[BpodSystem.GUIData.SubjectName '_' BpodSystem.CurrentProtocolName '_' FormattedDate '_Session' num2str(nSessionsToday+1) '.mat']); BpodSystem.DataPath = DataPath; BpodSystem.Data = struct; BpodSystem.Data.TrialTypes = []; diff --git a/Functions/Plugins/RechiaOlfactometer/OlfactometerConfig.m b/Functions/Plugins/RechiaOlfactometer/OlfactometerConfig.m index 079621f..737a98d 100644 --- a/Functions/Plugins/RechiaOlfactometer/OlfactometerConfig.m +++ b/Functions/Plugins/RechiaOlfactometer/OlfactometerConfig.m @@ -51,10 +51,13 @@ function OlfactometerConfig_OpeningFcn(hObject, eventdata, handles, varargin) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to OlfactometerConfig (see VARARGIN) +global BpodSystem ha = axes('units','normalized', 'position',[0 0 1 1]); uistack(ha,'bottom'); BG = imread('OlfControlPanel.bmp'); image(BG); axis off; +% FS NOTE: This function not updated to utilize BpodUserPath +% loadBpodPath; if exist(fullfile(BpodPath,'Bpod System Files','OlfConfig.mat')) load OlfConfig From 9b45e63608478d3deffdc847ed7b637e0fa4c365 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Tue, 6 Sep 2016 19:38:02 -0400 Subject: [PATCH 14/30] adds mat file containing string to BpodUserPath in Bpod home directory. uses string for BpodUserPath. default is home directory. --- Functions/Internal Functions/BpodObject.m | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Functions/Internal Functions/BpodObject.m b/Functions/Internal Functions/BpodObject.m index 5093815..426fd6e 100644 --- a/Functions/Internal Functions/BpodObject.m +++ b/Functions/Internal Functions/BpodObject.m @@ -118,13 +118,19 @@ obj.HostOS = system_dependent('getos'); obj.BpodPath = BpodPath; -%% FS MOD - if ispc - import java.lang.*; - obj.BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); + %% FS MOD + if exist(fullfile(obj.BpodPath,'BpodUserPath.mat'),'file')==2 + load(fullfile(obj.BpodPath,'BpodUserPath.mat')); else - obj.BpodUserPath = fullfile('~', 'BpodUser'); + if ispc + import java.lang.*; + BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); + else + BpodUserPath = fullfile('~', 'BpodUser'); end + save(fullfile(obj.BpodPath,'BpodUserPath.mat'),'BpodUserPath') + end + obj.BpodUserPath = BpodUserPath; if ~isdir(obj.BpodUserPath) mkdir(obj.BpodUserPath); warning(['Bpod user directory not found. Directory created at ' obj.BpodUserPath]); From 0b27cf37c7a37b4019a2650be6406cef9b3b3e11 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Wed, 7 Sep 2016 11:40:43 -0400 Subject: [PATCH 15/30] changed mat file to txt file --- Functions/Internal Functions/BpodObject.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Functions/Internal Functions/BpodObject.m b/Functions/Internal Functions/BpodObject.m index 426fd6e..d982134 100644 --- a/Functions/Internal Functions/BpodObject.m +++ b/Functions/Internal Functions/BpodObject.m @@ -119,8 +119,10 @@ obj.HostOS = system_dependent('getos'); obj.BpodPath = BpodPath; %% FS MOD - if exist(fullfile(obj.BpodPath,'BpodUserPath.mat'),'file')==2 - load(fullfile(obj.BpodPath,'BpodUserPath.mat')); + if exist(fullfile(obj.BpodPath,'BpodUserPath.txt'),'file') == 2 + UserFile = fopen(fullfile(obj.BpodPath,'BpodUserPath.txt'),'r'); + BpodUserPath = fscanf(UserFile,'%s'); + fclose(UserFile); else if ispc import java.lang.*; @@ -128,7 +130,9 @@ else BpodUserPath = fullfile('~', 'BpodUser'); end - save(fullfile(obj.BpodPath,'BpodUserPath.mat'),'BpodUserPath') + UserFile = fopen(fullfile(obj.BpodPath,'BpodUserPath.txt'),'w'); + fprintf(UserFile,'%s',BpodUserPath); + fclose(UserFile); end obj.BpodUserPath = BpodUserPath; if ~isdir(obj.BpodUserPath) From a847ac661e1f3b85765d1905d422b42181ed6fd1 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Thu, 15 Sep 2016 20:36:37 -0400 Subject: [PATCH 16/30] Added menu bar to Parameter GUI allowing to save current TaskParameters to current settings file (save) or to new settings file (save as). --- .../Plugins/ParameterGUI/BpodParameterGUI.m | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index 6abf061..0c026e1 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -71,6 +71,9 @@ ParamNum = 1; BpodSystem.ProtocolFigures.ParameterGUI = figure('Position', [50 50 450 GUIHeight],'name','Parameter GUI','numbertitle','off', 'MenuBar', 'none', 'Resize', 'on'); BpodSystem.GUIHandles.ParameterGUI.Tabs.TabGroup = uitabgroup(BpodSystem.ProtocolFigures.ParameterGUI); + Menu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label','File'); + MenuSave = uimenu(Menu,'Label','Save','Callback',{@MenuSave_Callback}); + MenuSaveAs = uimenu(Menu,'Label','Save as...','Callback',{@MenuSaveAs_Callback}); for t = 1:nTabs VPos = 10; HPos = 10; @@ -254,3 +257,19 @@ error('ParameterGUI must be called with a valid op code: ''init'' or ''sync'''); end varargout{1} = Params; + +function MenuSave_Callback(hObject, eventdata, handles) +global BpodSystem +global TaskParameters +TaskParameters = BpodParameterGUI('sync', TaskParameters); +ProtocolSettings = TaskParameters; +save(BpodSystem.SettingsPath,'ProtocolSettings') + +function MenuSaveAs_Callback(hObject, eventdata, handles) +global BpodSystem +global TaskParameters +TaskParameters = BpodParameterGUI('sync', TaskParameters); +ProtocolSettings = TaskParameters; +[file,path] = uiputfile('*.mat','Select a Bpod ProtocolSettings file.',BpodSystem.SettingsPath); +save(fullfile(path,file),'ProtocolSettings') + From 284ca61b622764af6009d7cb3e9600ce6635b4bc Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Thu, 15 Sep 2016 20:48:40 -0400 Subject: [PATCH 17/30] prevents protocol from crashing when you hit "cancel" while saving. --- Functions/Plugins/ParameterGUI/BpodParameterGUI.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index 0c026e1..b27b939 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -271,5 +271,7 @@ function MenuSaveAs_Callback(hObject, eventdata, handles) TaskParameters = BpodParameterGUI('sync', TaskParameters); ProtocolSettings = TaskParameters; [file,path] = uiputfile('*.mat','Select a Bpod ProtocolSettings file.',BpodSystem.SettingsPath); -save(fullfile(path,file),'ProtocolSettings') +if file>0 + save(fullfile(path,file),'ProtocolSettings') +end From 55a970962247b920ec31c6f2d11103e4ab433753 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Fri, 16 Sep 2016 13:35:17 -0400 Subject: [PATCH 18/30] Solves sync-issue by adding a 'get' option to BpodParameterGUI. Shows current settings, subject, protocol. --- .../Plugins/ParameterGUI/BpodParameterGUI.m | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index b27b939..2ede30c 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -71,9 +71,15 @@ ParamNum = 1; BpodSystem.ProtocolFigures.ParameterGUI = figure('Position', [50 50 450 GUIHeight],'name','Parameter GUI','numbertitle','off', 'MenuBar', 'none', 'Resize', 'on'); BpodSystem.GUIHandles.ParameterGUI.Tabs.TabGroup = uitabgroup(BpodSystem.ProtocolFigures.ParameterGUI); - Menu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label','File'); - MenuSave = uimenu(Menu,'Label','Save','Callback',{@MenuSave_Callback}); - MenuSaveAs = uimenu(Menu,'Label','Save as...','Callback',{@MenuSaveAs_Callback}); + SettingsMenu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label','Settings:'); + [~, SettingsFile] = fileparts(BpodSystem.SettingsPath); + SettingsNameMenu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',strcat(SettingsFile,'.')); + uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',['Protocol: ', BpodSystem.CurrentProtocolName,'.']); + [subpath1, ~] = fileparts(BpodSystem.DataPath); [subpath2, ~] = fileparts(subpath1); [subpath3, ~] = fileparts(subpath2); + [~, subject] = fileparts(subpath3); + uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',['Subject: ', subject,'.']); + SettingsMenuSave = uimenu(SettingsMenu,'Label','Save','Callback',{@SettingsMenuSave_Callback}); + SettingsMenuSaveAs = uimenu(SettingsMenu,'Label','Save as...','Callback',{@SettingsMenuSaveAs_Callback,SettingsNameMenu}); for t = 1:nTabs VPos = 10; HPos = 10; @@ -229,14 +235,14 @@ elseif Params.GUI.(ThisParamName) ~= ThisParamLastValue set(ThisParamHandle, 'Value', GUIParam); end - case 6 + case 6 %Pushbutton GUIParam = get(ThisParamHandle, 'Value'); if GUIParam ~= ThisParamLastValue Params.GUI.(ThisParamName) = GUIParam; elseif Params.GUI.(ThisParamName) ~= ThisParamLastValue set(ThisParamHandle, 'Value', GUIParam); end - case 7 + case 7 %Table GUIParam = ThisParamHandle.Data; columnNames = fieldnames(Params.GUI.(ThisParamName)); argData = []; @@ -253,25 +259,54 @@ end BpodSystem.GUIData.ParameterGUI.LastParamValues{p} = GUIParam; end + case 'get' + ParamNames = BpodSystem.GUIData.ParameterGUI.ParamNames; + nParams = BpodSystem.GUIData.ParameterGUI.nParams; + for p = 1:nParams + ThisParamName = ParamNames{p}; + ThisParamStyle = BpodSystem.GUIData.ParameterGUI.Styles(p); + ThisParamHandle = BpodSystem.GUIHandles.ParameterGUI.Params{p}; + switch ThisParamStyle + case 1 % Edit + GUIParam = str2double(get(ThisParamHandle, 'String')); + Params.GUI.(ThisParamName) = GUIParam; + case 2 % Text + GUIParam = get(ThisParamHandle, 'String'); + Params.GUI.(ThisParamName) = GUIParam; + case 3 % Checkbox + GUIParam = get(ThisParamHandle, 'Value'); + Params.GUI.(ThisParamName) = GUIParam; + case 4 % Popupmenu + GUIParam = get(ThisParamHandle, 'Value'); + Params.GUI.(ThisParamName) = GUIParam; + case 6 % Pushbutton + GUIParam = get(ThisParamHandle, 'Value'); + Params.GUI.(ThisParamName) = GUIParam; + case 7 % Table + GUIParam = ThisParamHandle.Data; + Params.GUI.(ThisParamName) = GUIParam; + end + end otherwise error('ParameterGUI must be called with a valid op code: ''init'' or ''sync'''); end varargout{1} = Params; -function MenuSave_Callback(hObject, eventdata, handles) +function SettingsMenuSave_Callback(~, ~, ~) global BpodSystem global TaskParameters -TaskParameters = BpodParameterGUI('sync', TaskParameters); -ProtocolSettings = TaskParameters; +ProtocolSettings = BpodParameterGUI('sync',TaskParameters); save(BpodSystem.SettingsPath,'ProtocolSettings') -function MenuSaveAs_Callback(hObject, eventdata, handles) +function SettingsMenuSaveAs_Callback(~, ~, SettingsMenuHandle) global BpodSystem global TaskParameters -TaskParameters = BpodParameterGUI('sync', TaskParameters); -ProtocolSettings = TaskParameters; +ProtocolSettings = BpodParameterGUI('get',TaskParameters); [file,path] = uiputfile('*.mat','Select a Bpod ProtocolSettings file.',BpodSystem.SettingsPath); if file>0 save(fullfile(path,file),'ProtocolSettings') + BpodSystem.SettingsPath = fullfile(path,file); + [~,SettingsName] = fileparts(file); + set(SettingsMenuHandle,'Label',strcat(SettingsName,'.')); end From 5c29a52af149c75e4caf984dccf471d98b24971d Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Mon, 19 Sep 2016 14:05:12 -0400 Subject: [PATCH 19/30] more consistent appearance of menus --- Functions/Plugins/ParameterGUI/BpodParameterGUI.m | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index 2ede30c..fd1fcd6 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -71,15 +71,14 @@ ParamNum = 1; BpodSystem.ProtocolFigures.ParameterGUI = figure('Position', [50 50 450 GUIHeight],'name','Parameter GUI','numbertitle','off', 'MenuBar', 'none', 'Resize', 'on'); BpodSystem.GUIHandles.ParameterGUI.Tabs.TabGroup = uitabgroup(BpodSystem.ProtocolFigures.ParameterGUI); - SettingsMenu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label','Settings:'); [~, SettingsFile] = fileparts(BpodSystem.SettingsPath); - SettingsNameMenu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',strcat(SettingsFile,'.')); + SettingsMenu = uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',['Settings: ',SettingsFile,'.']); uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',['Protocol: ', BpodSystem.CurrentProtocolName,'.']); [subpath1, ~] = fileparts(BpodSystem.DataPath); [subpath2, ~] = fileparts(subpath1); [subpath3, ~] = fileparts(subpath2); [~, subject] = fileparts(subpath3); uimenu(BpodSystem.ProtocolFigures.ParameterGUI,'Label',['Subject: ', subject,'.']); - SettingsMenuSave = uimenu(SettingsMenu,'Label','Save','Callback',{@SettingsMenuSave_Callback}); - SettingsMenuSaveAs = uimenu(SettingsMenu,'Label','Save as...','Callback',{@SettingsMenuSaveAs_Callback,SettingsNameMenu}); + uimenu(SettingsMenu,'Label','Save','Callback',{@SettingsMenuSave_Callback}); + uimenu(SettingsMenu,'Label','Save as...','Callback',{@SettingsMenuSaveAs_Callback,SettingsMenu}); for t = 1:nTabs VPos = 10; HPos = 10; @@ -307,6 +306,6 @@ function SettingsMenuSaveAs_Callback(~, ~, SettingsMenuHandle) save(fullfile(path,file),'ProtocolSettings') BpodSystem.SettingsPath = fullfile(path,file); [~,SettingsName] = fileparts(file); - set(SettingsMenuHandle,'Label',strcat(SettingsName,'.')); + set(SettingsMenuHandle,'Label',['Settings: ',SettingsName,'.']); end From fb79a586cdf3b6854713ddc5553ee31ac14b0c07 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Wed, 21 Sep 2016 17:44:33 -0400 Subject: [PATCH 20/30] corrected save menu callback function. --- Functions/Plugins/ParameterGUI/BpodParameterGUI.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index fd1fcd6..09f3672 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -294,7 +294,7 @@ function SettingsMenuSave_Callback(~, ~, ~) global BpodSystem global TaskParameters -ProtocolSettings = BpodParameterGUI('sync',TaskParameters); +ProtocolSettings = BpodParameterGUI('get',TaskParameters); save(BpodSystem.SettingsPath,'ProtocolSettings') function SettingsMenuSaveAs_Callback(~, ~, SettingsMenuHandle) From 7a9319122d8536e8be49d345a4a33d7c6fa8b680 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Thu, 22 Sep 2016 12:03:05 -0400 Subject: [PATCH 21/30] corrects error in 'get'. --- Functions/Plugins/ParameterGUI/BpodParameterGUI.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index 09f3672..b08c8eb 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -271,6 +271,7 @@ Params.GUI.(ThisParamName) = GUIParam; case 2 % Text GUIParam = get(ThisParamHandle, 'String'); + GUIParam = str2double(GUIParam); Params.GUI.(ThisParamName) = GUIParam; case 3 % Checkbox GUIParam = get(ThisParamHandle, 'Value'); @@ -283,7 +284,10 @@ Params.GUI.(ThisParamName) = GUIParam; case 7 % Table GUIParam = ThisParamHandle.Data; - Params.GUI.(ThisParamName) = GUIParam; + columnNames = fieldnames(Params.GUI.(ThisParamName)); + for iColumn = 1:numel(columnNames) + Params.GUI.(ThisParamName).(columnNames{iColumn}) = GUIParam(:,iColumn); + end end end otherwise From 4671f5c4ba4b2ba3c309a2efd9fff7614282e4d0 Mon Sep 17 00:00:00 2001 From: elsie Date: Fri, 23 Sep 2016 11:48:22 -0400 Subject: [PATCH 22/30] Replaced with latest port breakout board design from Sanworks (LED adjust, not IR adjust) --- .../MouseBox_PortBreakout_r1 GERBER.zip | Bin 21731 -> 0 bytes .../MouseBox_PortBreakout_r1.brd | 603 ---- .../MouseBox_PortBreakout_r1.sch | 2443 ----------------- .../MouseBox_PortBreakout_r1.zip | Bin 0 -> 24143 bytes 4 files changed, 3046 deletions(-) delete mode 100644 CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1 GERBER.zip delete mode 100644 CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.brd delete mode 100644 CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.sch create mode 100644 CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.zip diff --git a/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1 GERBER.zip b/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1 GERBER.zip deleted file mode 100644 index fe0efbdf98b00eb36fe9e8437e10d4f3de8e67c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21731 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 diff --git a/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.brd b/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.brd deleted file mode 100644 index c89198a..0000000 --- a/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.brd +++ /dev/null @@ -1,603 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Bpod port interface -rev 1S -LED -Emitter -Sensor -+ -- -GND -GND -+V - - - - - - - - - - - -IR emitter -current -adjust - - - - -SIG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - -<b>Bourns Trimmers and Potentiometers</b><p> -Trim pots, potentiometers, poly-fuses and other components by Bourns. -<h4><i>Created by Bob Starr (rtzaudio@mindspring.com)<br> -Updated 01/08/2005</i></h4> - - -1/4" Square Trimmer - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE -3 -1 - - - - -<h3>SparkFun Electronics' preferred foot prints</h3> -In this library you'll find resistors, capacitors, inductors, test points, jumper pads, etc.<br><br> -We've spent an enormous amount of time creating and checking these footprints and parts, but it is the end user's responsibility to ensure correctness and suitablity for a given componet or application. If you enjoy using this library, please buy one of our products at www.sparkfun.com. -<br><br> -<b>Licensing:</b> Creative Commons ShareAlike 4.0 International - https://creativecommons.org/licenses/by-sa/4.0/ -<br><br> -You are welcome to use this library for commercial purposes. For attribution, we ask that when you begin to sell your device using our footprint, you email us with a link to the product being sold. We want bragging rights that we helped (in a very small part) to create your 8th world wonder. We would like the opportunity to feature your device on our homepage. - - - - - - - - - - - - ->Name ->Value - - - - - - - - - - - - - -<b>EAGLE Design Rules</b> -<p> -Die Standard-Design-Rules sind so gewählt, dass sie für -die meisten Anwendungen passen. Sollte ihre Platine -besondere Anforderungen haben, treffen Sie die erforderlichen -Einstellungen hier und speichern die Design Rules unter -einem neuen Namen ab. -<b>EAGLE Design Rules</b> -<p> -The default Design Rules have been set to cover -a wide range of applications. Your particular design -may have different requirements, so please make the -necessary adjustments and save your customized -design rules under a new name. -<b>Seeed Studio EAGLE Design Rules</b> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.sch b/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.sch deleted file mode 100644 index bc6d2c4..0000000 --- a/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.sch +++ /dev/null @@ -1,2443 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - - - - - - - -This footprint was designed to help hold the alignment of a through-hole component (i.e. 6-pin header) while soldering it into place. -You may notice that each hole has been shifted either up or down by 0.005 of an inch from it's more standard position (which is a perfectly straight line). -This slight alteration caused the pins (the squares in the middle) to touch the edges of the holes. Because they are alternating, it causes a "brace" -to hold the component in place. 0.005 has proven to be the perfect amount of "off-center" position when using our standard breakaway headers. -Although looks a little odd when you look at the bare footprint, once you have a header in there, the alteration is very hard to notice. Also, -if you push a header all the way into place, it is covered up entirely on the bottom side. This idea of altering the position of holes to aid alignment -will be further integrated into the Sparkfun Library for other footprints. It can help hold any component with 3 or more connection pins. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - -This footprint was designed to help hold the alignment of a through-hole component (i.e. 6-pin header) while soldering it into place. -You may notice that each hole has been shifted either up or down by 0.005 of an inch from it's more standard position (which is a perfectly straight line). -This slight alteration caused the pins (the squares in the middle) to touch the edges of the holes. Because they are alternating, it causes a "brace" -to hold the component in place. 0.005 has proven to be the perfect amount of "off-center" position when using our standard breakaway headers. -Although looks a little odd when you look at the bare footprint, once you have a header in there, the alteration is very hard to notice. Also, -if you push a header all the way into place, it is covered up entirely on the bottom side. This idea of altering the position of holes to aid alignment -will be further integrated into the Sparkfun Library for other footprints. It can help hold any component with 3 or more connection pins. - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - -This footprint was designed to help hold the alignment of a through-hole component (i.e. 6-pin header) while soldering it into place. -You may notice that each hole has been shifted either up or down by 0.005 of an inch from it's more standard position (which is a perfectly straight line). -This slight alteration caused the pins (the squares in the middle) to touch the edges of the holes. Because they are alternating, it causes a "brace" -to hold the component in place. 0.005 has proven to be the perfect amount of "off-center" position when using our standard breakaway headers. -Although looks a little odd when you look at the bare footprint, once you have a header in there, the alteration is very hard to notice. Also, -if you push a header all the way into place, it is covered up entirely on the bottom side. This idea of altering the position of holes to aid alignment -will be further integrated into the Sparkfun Library for other footprints. It can help hold any component with 3 or more connection pins. - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - -This footprint was designed to help hold the alignment of a through-hole component (i.e. 6-pin header) while soldering it into place. -You may notice that each hole has been shifted either up or down by 0.005 of an inch from it's more standard position (which is a perfectly straight line). -This slight alteration caused the pins (the squares in the middle) to touch the edges of the holes. Because they are alternating, it causes a "brace" -to hold the component in place. 0.005 has proven to be the perfect amount of "off-center" position when using our standard breakaway headers. -Although looks a little odd when you look at the bare footprint, once you have a header in there, the alteration is very hard to notice. Also, -if you push a header all the way into place, it is covered up entirely on the bottom side. This idea of altering the position of holes to aid alignment -will be further integrated into the Sparkfun Library for other footprints. It can help hold any component with 3 or more connection pins. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - -thru-hole vertical Female Header -used as an SMD part -(placed horizontally, at board's edge) - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Package for 4UCONN part #19686 *UNPROVEN* - - - - - - - - - - ->Value ->Name - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - ->Name ->VALUE - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - -2mm SMD side-entry connector. tDocu layer indicates the actual physical plastic housing. +/- indicate SparkFun standard batteries and wiring. - - - - - - - - - - - - - ->Name ->Value -+ -- - - - - - - - - - - - - - - - - - - - - - - - - - ->Name ->Value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - -This footprint was designed to help hold the alignment of a through-hole component (i.e. 6-pin header) while soldering it into place. -You may notice that each hole has been shifted either up or down by 0.005 of an inch from it's more standard position (which is a perfectly straight line). -This slight alteration caused the pins (the squares in the middle) to touch the edges of the holes. Because they are alternating, it causes a "brace" -to hold the component in place. 0.005 has proven to be the perfect amount of "off-center" position when using our standard breakaway headers. -Although looks a little odd when you look at the bare footprint, once you have a header in there, the alteration is very hard to notice. Also, -if you push a header all the way into place, it is covered up entirely on the bottom side. This idea of altering the position of holes to aid alignment -will be further integrated into the Sparkfun Library for other footprints. It can help hold any component with 3 or more connection pins. - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - ->Name ->Value -+ -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - ->Name ->Value -+ -- - - - - - - - - - - - ->Name ->Value -+ -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->Name ->Value - - - - - - - - - - - - - - - - - - - - ->VALUE ->NAME - - - - - - - - - - - - - - ->VALUE ->NAME - - - - - - -<b>RJ45 Jack</b> -Simple RJ45, 8-pin connection - connector for common Cat5, Cat5e, and Cat6 Ethernet cables. Footprint not yet proven in production. Connector sku is PRT-00643; Breakout PCB sku is BOB-00716. - - - - - - - - - - - - - - - - - - - - - - -<b>Header 6</b> -Standard 6-pin 0.1" header. Use with straight break away headers (SKU : PRT-00116), right angle break away headers (PRT-00553), swiss pins (PRT-00743), machine pins (PRT-00117), and female headers (PRT-00115). Molex polarized connector foot print use with SKU : PRT-08094 with associated crimp pins and housings. - -NOTES ON THE VARIANTS LOCK and LOCK_LONGPADS... -This footprint was designed to help hold the alignment of a through-hole component (i.e. 6-pin header) while soldering it into place. -You may notice that each hole has been shifted either up or down by 0.005 of an inch from it's more standard position (which is a perfectly straight line). -This slight alteration caused the pins (the squares in the middle) to touch the edges of the holes. Because they are alternating, it causes a "brace" -to hold the component in place. 0.005 has proven to be the perfect amount of "off-center" position when using our standard breakaway headers. -Although looks a little odd when you look at the bare footprint, once you have a header in there, the alteration is very hard to notice. Also, -if you push a header all the way into place, it is covered up entirely on the bottom side. This idea of altering the position of holes to aid alignment -will be further integrated into the Sparkfun Library for other footprints. It can help hold any component with 3 or more connection pins. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<b>Header 2</b> -Standard 2-pin 0.1" header. Use with straight break away headers (SKU : PRT-00116), right angle break away headers (PRT-00553), swiss pins (PRT-00743), machine pins (PRT-00117), and female headers (PRT-00115). Molex polarized connector foot print use with SKU : PRT-08233 with associated crimp pins and housings. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<b>Bourns Trimmers and Potentiometers</b><p> -Trim pots, potentiometers, poly-fuses and other components by Bourns. -<h4><i>Created by Bob Starr (rtzaudio@mindspring.com)<br> -Updated 01/08/2005</i></h4> - - -1/4" Square Trimmer - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE -3 -1 - - -1/4" Square Trimmer - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE -3 -1 - - -1/4" Square Trimmer - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE -3 -1 - - -1/4" Square Trimmer - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->NAME ->VALUE -3 -1 - - - - - - - - - - - - - - - - ->NAME ->VALUE -1 -3 - - - - - - - -<b>3362 Series</b><p> -1/4" Square Trimmer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<h3>SparkFun Electronics' preferred foot prints</h3> -In this library you'll find resistors, capacitors, inductors, test points, jumper pads, etc.<br><br> -We've spent an enormous amount of time creating and checking these footprints and parts, but it is the end user's responsibility to ensure correctness and suitablity for a given componet or application. If you enjoy using this library, please buy one of our products at www.sparkfun.com. -<br><br> -<b>Licensing:</b> Creative Commons ShareAlike 4.0 International - https://creativecommons.org/licenses/by-sa/4.0/ -<br><br> -You are welcome to use this library for commercial purposes. For attribution, we ask that when you begin to sell your device using our footprint, you email us with a link to the product being sold. We want bragging rights that we helped (in a very small part) to create your 8th world wonder. We would like the opportunity to feature your device on our homepage. - - - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - ->Name ->Value - - -<b>RESISTOR</b><p> -chip - - - - - - - - - - ->NAME ->VALUE - - - - - - - - ->NAME ->VALUE - - - - - - - - - - ->NAME ->VALUE - - - - - - -1/6W Thru-hole Resistor - *UNPROVEN* - - - - - - ->NAME ->VALUE - - - - - - ->NAME ->VALUE - - - - -1/4W Resistor, 0.4" wide<p> - -Yageo CFR series <a href="http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf">http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf</a> - - - - - - ->Name ->Value - - -1/2W Resistor, 0.5" wide<p> - -Yageo CFR series <a href="http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf">http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf</a> - - - - - - ->Name ->Value - - -1W Resistor, 0.6" wide<p> - -Yageo CFR series <a href="http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf">http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf</a> - - - - - - ->Name ->Value - - -2W Resistor, 0.8" wide<p> - -Yageo CFR series <a href="http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf">http://www.yageo.com/pdf/yageo/Leaded-R_CFR_2008.pdf</a> - - - - - - ->Name ->Value - - -<h3>AXIAL-0.3-KIT</h3> - -Commonly used for 1/4W through-hole resistors. 0.3" pitch between holes.<br> -<br> - -<b>Warning:</b> This is the KIT version of the AXIAL-0.3 package. This package has a smaller diameter top stop mask, which doesn't cover the diameter of the pad. This means only the bottom side of the pads' copper will be exposed. You'll only be able to solder to the bottom side. - - - - - - - - - - ->Name ->Value - - - - - - - - - - - - - - - - - - - - - - - - - - -This is the "EZ" version of the standard .3" spaced resistor package.<br> -It has a reduced top mask to make it harder to install upside-down. - - - - - - - - - - ->Name ->Value - - - - - - - - - - - - ->Name ->Value - - - - - - - - - - - - - ->Name ->Value - - -<b>CAPACITOR</b><p> -chip - - - - - - - - ->NAME ->VALUE - - - - - - - - - - - - - - - - - ->NAME ->VALUE - - - - - - -<b>Resistor</b> -Basic schematic elements and footprints for 0603, 1206, and PTH resistors. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.zip b/CAD/PCB/MouseBoxPortBreakout/MouseBox_PortBreakout_r1.zip new file mode 100644 index 0000000000000000000000000000000000000000..1dffbda7b3f908110a9300e78a1baea16ac2433c GIT binary patch literal 24143 zcmeFYS6mZOyY?+2ptOhuLXC)mNCyF_5mB%qC<;i2sFct{Z=pz$DuRO2MFgb_p%;-7 zqy&%>dg!5t7D56^-f_SC;M+(0{e8#J0mHTKb+4Jpy0T{0{8z@{A>*kFCr+F=eL~&m z_+EjYfdqy3#ED4P6DI_KsGh4A;?-?eUn>Jw56{~kuWaD1UY=GSa?-lDAH`dYmx60J zf@B6->@81<3^c2MtGj1#Nh|E2gG5Ju6_8elny>Xb*M3lEdM&1?FbSDXMYqs5Yd)>e zp~vL){?+VNG-Rcwg@z|(w|qQ!!AIY*LQ;u%NUjaHEXu4u{DR!m zclc1>LK8uXFBQN|Tu3`fLo!x>_21ISgYSB~oIp=$C0*DBmJi7Cb9C!g@MfwE%ip(O z+4vV)nQN{OII{lb6`qeCD7#}Ow7EyQnxFXWY>$_f33u_Rt8KtZr3q!Z%Y@d0WA1NN zi#!x|IPv%Ht4cn-ab@&lRgTajW+XxOCJ>TEJy}$X&YOM5Pxh8M= zn2*IKYsGe}&i6RszILuo?)`iwa}!MT&cp*3-f~4SKeg59gE#zy(x;E_yTdo-d3y$Y z^jo_XTpzzWUCl7$O?;t?QMLROqPNMm`C#=nTb!}z-c{GMn%CbxMV^Rz6@D+$TWY7V zPV(1;ZI(+~o|H=}^8nTh>c~*+GxP9kzD%pzOwx-|Z%oK@mm0T(!Ml;H)#-1ST#bHx z>=NG%c5+dEt^3F`_fGMV_f$=Tx4f=))^(|pJD=B2(N-rM?|zP8VcKu=ea_dLtA2Gk zyRrYyP@S&Ig22yI6|vdu4VAaT8|hs-k8hTBoqa32{p@9YFI(05PgmSSRql8XS!#9O z*9*}cWcrcGSU^VS;g#nsI&qB7c%0OTg9YD`HhF zwg`DSi0m||O3${cIz;7lEhN z)kpJ0V1zBItcSGEN((Pnfn47dUi=1JmnVn7oZ^%DDjNrjNbSX;PJmEa-hCv<`|=TLO_=yro@!@$V})bGF-)|F=bkM>Rm ziTKS^O{~%UJqk9nP*Ys2A1f^{zI#)&%a^|%G7Ft}^ayv$^z}t{d}cUV{T6h>e5!Oo zK;kWXDBZqjo2FE*9q)X~rt4^=)`F?!R@%z#wjR6kj1#)ik$yvEM#?_=#$H^M5=Z~YhblD%QfFw=riNDAERV`3^{+%I6K`kMzyvT znK(tyt$tJOdZJUQKx!M}m_BiP@`_UNKe;}^OwU!S*bF^*=45x0D$Ad)dT`M!qZOIf zhna=;R`dMx${p~kqbW62Us&e!e|wdh_{Fo8xEU5ozf{u^gw-+gA$L9!Wq%b^hv#Kwl{C3Q=&O=2}hjR$IB~ShWYua z_YjP98#w%;!)u{pR!VN8=|#XU&yhFaz2Fa_N1XvxtQg^d`<9%CVgb$ahoBD}taS#K zCXq%uSuw>)OcCCflO;mw2ICeEOuSZSs8K5?ZE}VH%WQhBH!}#_>_FuD;K|_Up8l`w zKqe&Pe}>q;0=#+1ouymYmzx5X`@duB+6_iE@#=)jT>C>#=Z!mR?>pPI9X@b&0?w|w zHk>Y-KCK3fE)Kvkg4=(MuJ+k?$g|wLAY1<~=sknmG%bFvUC>1IJLG`Qo8#~|N{f&y zQ^NGxgqt$#dx_6&X%&vWP@{M<`iC6dL~0pQ)Nu?WrXk;PoguUEroThJd%_BcGC=qP z&Esskpk4NnfPe$ybks4d0LXm;BD2H`?t(nd3Dd_^mLYe5%IHR*$=lOp7L`rld=hWd z+*O9z+K$6B+`FLO@njYoVY+Pbp|>!-Tt${HYYO~*mZO)WY^J`dyOE=IyO$u{hN;MF z1PfYjz~U}w`e7<^C9C0B`&HW# zLsMADUTG%MM1ItJs>1phWeB5ku7!3uf#T6k7UFral1BlXkKvV%@tAP?V zaoU(}U^c%7aYUF?wVjiJFb9GNs`LVmnH~*@OhE9*0;1zz1n92H&FLeit;weTfWVH3 zpvCl9Nnw~3^m~|I>9AqO8sIFwn^{j=ZH=CVi4ZlsaoEojE`L0L@tmOm> zAqXKKWTH<)UL0L06?!6hX2BpXFn%bPT6f-d%4tjLs!yR*>A?4IVp&}1Cv5fd@Sy)lT=N}&FhmF8F6n=vpEESJNN z*VqV^BHpcs>}5VT5tYxH+?UXVmQTE1=~0Qu4u79^E7s;-<$)J`NJRkT*)Ld@As}E9 zm8S3=wXJBd$Aa~DS_K;-^arH zdfh*v*JkW+fAGI==(1#Izo=0+T4=n$|Hcm+w^u5Nzj}VEA7ybjB$*{f#nQnFJ)}ps z0B4y==;WTsl5j7sQ0=0A4cYlgxwxDE$Nw>SS06K>p;U%?5*@8R%@cG%zIDQAeoA?6 z;zCcY(q_85Gs3DmO*YPb?D12R*dwhMy%CBT#u#V&2u15kEhoh~0!n8L*Ab}2o2mho z{m9WD{$V8fx2nrG=0fdN?9mMz=^Xf_&ksH9GACT}$9Vcvd;~wp zEyiYC{aY_w_9KupWvXsl0Ncm}fB$ex>Vjji?c(hn$+&)WA@ADgk_^ZxWq{Nf@^SnH z^!ge)rSdUU$eHw(sa;pQ*D5-ON>o)`VJ zjH~-QY}NQhJ0?sTz(l0mFBcx|ykp?9N{fr0mru7yaSxdj}efMyXZWrZ*rSRWGxY z)l4rSs_t3Yp}jcm2p_mVpCbQ0MwlRXE>d45(ByTIpYmFIv32hDgUt97xK;UMB84eP$%4%$3$+lMh&VDdcCkZVJ=jr4wfU# zxH!iR=5#UENqi6}`&zvu`=>R~s;}o*;9fRr@-6H$>cK1nEP+L3ME*(Z}{P5SZp z3#sOKE@@nif{2gnLxiY0H0O#GE-k)*d?QPDZ2Vl7E;tg;4=ZqMmJ=VUmsOXkX!fFef#U1u<%CB0E{eQ2pk0C5Z3xL&9Ld$XY@qo~9x4@!&Bg}uUrIwQ z9Ob3*Giw?ibb~vQf#_7(LdJwv#rKw%ko@66mi_#GvWt%-7E})dQDTKnN$OU|@RS=2 z-|$7wI+6>`M52^a*pwO0xEOn@Cxq|K-znA3>Yvr4zhtg~Ov5K~cGv2IJ$I|-nyxJc zNf@+@XgHcIWCTLfWcUBn&nUBFACh&CxR)ufKk_7gP-m11OvDVS7j1*EN|R|~plJN& z;z$bNxqyDbaDo6qOpY>@r!sFjGv>29tG3iVP54W7OB*W5bL~r{tu==EnZ8tqiyh73 zKTx+3UBNf0BUCp^(b3$#F{y2}Y%6NWY8q6~QVl6==?oag@gvlO7BseJe|cE`g3dVv zkJn_9{@ArXR=GwN&@YEIT0Q`mrC!qxB$2@&g0`VccE9%`+{a!FukA83XqM)4;~?He z(1`xaYs%}~EE_RM7LHW`xdn=nxDM5y;Sw#yb**jpLL4yu^zCRby6 zbyK0{a^`(I7e(R}3qs<9d#c$BDF%c@-NTjDboeQX5uZ&{%8z(NlYdS7j_U&N-2?Cz zOGGktDpAo|hU)8rfJj%1wFPmbw*5E1`z)ozVdpH7}D(LgQiDI}bhdWa~tdYd2HeX=k+Q1gN`D3aT zvB}P_Sly<2M~W|Tp(ft>-o`@n-IQYvYMspr2~`#g^5o>v9d7sEnvef^CDqdeJH<-* zDC)Iz?uEo6^HC9eKK?rbu~FUjGHANEK6Dj5E7 zL-Vf6JCqIP1z8P|jTrszL%JpE%054BWJu(^&gNtmoVf9pq&6uJ;=M3fk|(o_ zQ7!AqqTZq^&g9W%(iT?v32<}Q z2t;hU%Kwp+w=ZIm$MTZfezw@sq9TUa- zHfbzp%Wex<@31Xj1O^dngGij;1AaMu2dQNZpq!;TPk!kCCy~9)Phzyk#V^Z7aP=xw zo}GOA$Hl{^d;T7|F!jYs8!%|l=zxF7`;`YfeqLfdcIP?Jru&+(Wn~)Q0u(4`+oX|9 zta<}trbgWSJ%jAMVdTym1ixje`+Io|WL-yjp~DGDpX1EwzNhO!Z5e&i9;984^}`^} z4$ypnY_`_t#AbWYgNMY<;>@Mn)}T1M65AFK}egP^1cH@q9o^ zzz-?E(R9PV<{l?Sy$eIE`hKQz;KKi?bh_!__z$XQlzWNOpUb{o(oXnjyU);9-_l3$ z>lXt#CB8Fu!98YoYWTx2aqu*?J;8diL}ub3>Lau2SQtU6*;;MkyDRp0<0ucl$q%$% zOFE4!yhBzn`v5Y8xbf{hqriZ+@)!RCXx}Dg$n`Nf%aXZ`=jAYU8Z!wDYUxpw%ty?aZwtn@ z<2CDER(0(C&|VBv4C}>h3Yg5fYMA4C3D%a`<~bX?)6~CDa2L!9UH{gI-YgqZxxQBR z_vy~GWd1nprxMoK&%nK(qs`}xACD#w#_3kzJ0BH$FT{+}OXir+mjR?qVnZ_P0=*W6?M91vsu*HLmv8)y59{ z*7`%ItKU50T=qQHNU(0USRxD*PtOuUt_P>T-ZX_$KiUTOudftX89bU?YHVwK@v^Tk zeMwEFqQFOuDtZc+%iY_ODjk$rc5Qb#z3oR}C7yD9d3m52Q>{i(m%4GP{oZauqV;Gg z&%u)-0>U{=5YEpCd$ME3*!-z`m($!K@68ThAb;>4>r0UyWv9nVF3q}|veAGO+D4!dJFJ;b$5+Y30 zO7Ubbc2bi|?80U*No&g2Y=*;g|C7~SN|)=lKe;479C=84Z_}09M+oq39<%(?IE9m% z_o<#dqS;Ao+jUfWFCY)2Y-+`$do8m0qeoRRZ1nL=xOYVeQE$unO6QcaXga}x z?D{B+;D%PoI_hkfJ??zDXCXn4KQkD(Gdvysv)LEaE!)WAr4bzfBl$0-ziMR3cTa9$ zc?F&cpAZN?3@}i0pV{2fdST@QVznx(BN^L!yS|ujkd2wft%tevN!OiS8@+W@ys1NI zeLY!IRw`(#+rau$$#Nxt`my>BbhA7#j>UhgywZXqZE7hOD52Lv)R3(%VCvs4E;~wi zF%zIZK@{u^NGBl&qLg}Vbk(YCIMwh>{L8Y=SRUMYPmcks#UGn1645l8-(k#m*LUBT zeV~7z^E~Zc9ufG6+fnixonpluG$YDCS~nh8D+lc|uNEd2_w{+mq^%moG@Gqi&nQ0fnF)fgxFSv^m>WqSdD6Zlg*7 z^<@s)pf{*bebH6%p`z`0P*5BpnK#na>tf>E+t7f#zVdxN3Ul>PDq6JV-4lV)Q93+| z5c$cwxwoh+=ZRASFMDI0w++&5oqp2bf2oU~% zmFYbI1sqn?fLu2%hLBkwD0#H9GK!(q@g_MBl^B0^eL>yC$0-hCYsXl97&T@Zl1UYj z#Qde>19=v9tb^u0`AN2kcFJjtJ%$&+0#BG#duMZ`#j%`U=21%DlsE|&%Vbf5olvT= z;m3BL*ZusPr`jGIX*U`N7A-fjnr-#h?VX8N_4)OZw|bc^dCvXkiAis6$Nk@&&TXp2 z8k2JVo0I%fXijS`WlO?ffO?{mwc6-yfx-RCUwigumXy#S-5P~;krR~@10vc2%9Az8 zCpv9aG|E|GJ5WscbazPjiS(hZbN%uia#KEDMMXnu)@}2rNG3z@XjvEKq85!4=gSOx zsjA#H2X}e{La!?!GDU)j^O`| zKq&N?Y9st4F_ZVyu<~yL_9j+1NJPHB=OQ!wOT?RQWnjO4WZRfSO(vx9VUR()eDZ1?#e8MRxfmKRo_Ns9 zK*=4Gce0(zWr3MDe1_#fU|wcDz&3uz`usl31R^e4dr*hdWP1LFaH|v?ZHS6xSHyFLr$t=jQV3jgSXXfRhX!= zijpqc@p~~xC#bX28-Z;7Kcir^1j~K zG}cCVns^HE2+u*`LbdV(7TFFj7Ak6-YxmQ0ya5>0^YB4DRF@_0WcW=2Z^x`3wbRC` zbMF@Pi!o8MtnrTio{r}B4Dv7acZP^>c<`yz6{6IyIE2Y9Xjr<*yY!50rr z?u)JhOeP0q!NV!NcC@ZGJlGtwl>R-ohoB z38BG-PUEI3U0#Vw-ube{it8fxsjc2EVni-8jqGao5)*U67@JW8i((djPbvqz!cz7c z?A5+8oidRtn!2Q4c5clTl&DP|^KN-S8gR8G1~RWjIibsO3B)AZcj4#Vb0C!+dm&LN zA+}QOM7$Y6rH7%{(fH~bs%*C;^HUB^v<`c9)?MntWa=&GvTySX#ey6NBD%w?aOaOZ zBnOgQkXbhO!bG4g$gV~mhU|5i5+Bp0tZpja0h0pv;8X_Cs%{Kcd`<{Gue1_oJHz&@ z;=_W|<^%U;bbChU3(*k;g%xy=vXJlVo>2Cc#I6tQvpZei<08|REbZr-#s$yJ2?eMA z@OTGpL9FILp72xtL=bHiuLvU6P~X^D-7S;<0%myU-~aCJPzae}i_y{#=)V?;V^@}T zsDS~IB7g3I=LCn8rHzMTs2(qh~rhWhGA?RxQ zJTxUGhHkKXou)mR2L#%Hz>QeCfrk{3QG1v;^n-5jREDOVZE+kGb7u!qU`)@o1L_iu zkE5Ofj1J4%tLnv4iUM45bzF&lfA>PwZy8szCC4oRa5ICPqubp~}GfssK zBF&|Nb5P-@qL;k8>kW=||Hfbg8iNb&(rtRm_nWQc6ssIYdt z5T{DNX%5t_A1}CNA_YqK65G6J?!X~0>{;f-^a&U*LwrCPK!?sAFJ$rJA*5+HnmZz7 zFEL#Lh-S)Tn*!p4F7Yw(S9(!e*jE397X=u>4u?~>2!jT3TGq40cTsQC6xjZbC-T$ zs$lA%latBsOXuN5d*590W<1A1R1womu(*g^?pM^L;cKAANl<7Ybpb(kB_C?hC}=1$ zh&HpLD?-K7n}d!hiL}i!*D}i99?q(zT#`?Q7-1E2T$;^V)7(P0v@$*N6e{vAD?FIU{bi;V0iqOBl)(8!Gl(BLP&i6*l$$e#^*QNt*_T(a{JG5b$~~^*#wG>+V`hT z^I5_A9m~G5Td&dQ%k!J7{t7wOWJWjD?$QcAuh0X6w3!#OK<*ZDvS8Skua@{Ys=jJ+5&y%g-d{65mn``>2` zdlYkd$}2NcJVP}keR`Ht7M_aEW{-qfTiYp$fQQ$by$w<)v(Y9krbY8?jNcTcgl}mM zOqAufnJz?|GH-uyzc1Cr(y8)143w75;*sobQo^pjYL4YA_Im7eelt!@Z5X{YrfFS* zkh?g0AMeChJ38Zh$6^2_Vt>Lu0v5DAm^G*cJ(V8AQ8l?~{RVC)$G)G}{{wyo*v^2I zZb9ouldR+5{p3+OVf2wISrWXWYh|%%bhjZtKyv>R{0Wf0XAmc0d*H%O8T7%DtOpXC z$2Trn72Y7e3J-p_++>{t`AQT95A!<+3U0h*P^@93o~7-e^dEg!wqo-R;@D%#($!n| z{cHS=VbJSB=q{haD3jbhJCw}%b z?JqQ&g*KAF1Fx%wOa_^SNNInWF$ZJk!-Efb-=>#GW#-qO+No41V4}>`X|a(|0+)wh z_A9!d*^m_L;5#Uv%KsydPFxRBME}A#>d5+9HhG(*($oh)WzTn(-o|2YNG&oJQO|F& zC?wk}SNXEweQraOC*iQl4G!-7vMUF5g-x$$5kqTCzIOcDN>5BC$n3OX0xwn>AV z>I$rd?+5x6iu?_nc{OKh)L>e{eZxoDQ9T~PbC!KS{PmOK=fJpmGa)Qi(5atrZr9bT zQk!bx-+Y^7;95*>R9j<_X)B+p27EpnzUiF<$vXt5ByU0jrN%}OShZ%SUpIX2e#r`o za2>%j6O}#_>p6VQ&)p?C1DV-sMeP^%VV*_}tSXva{fzst0RuWK<+c-eOYKJ|?>dZq z>2as(`=Xj91MLO`KJ}PU=>5Z(C5JYR>Itd;b_g&6r2sg@2wQ88`xqoTka*-WJZKls z;8O@nyy!FQo+wTVOY6*jT@~T|~4*=dgz!_yS;3 zT}ZI+md9Mmg1G^-nEO=t=jzW~>;!hKw0#Y5BQpZV(8`aI8`xhgrbp&l&vI92FrJcG-goNeOw8OF}p!+Gq$S$p~G;PkyM2N@P@AP+H^{7!-eAb{Cp3f|8 zv~aU6Xf2)=9`JwviIX6Y>gcmcM zL-!#Vu3WmCH1HcB#N!ocMmmrpz)b<+>j#iG#Ug179%3_P(r8;ca0fj~>=_5n>E9Rs zDV?io-Y*faR&sDYJtl(Gw+G^S)@A@T%|f20ulOKt(r2^qkjsq@G-11{3)<8bKU!-F z9`eW!D5ot$et+6Tr;}L{)#M*Hi1Au7IFhPV*2he%rFb-)V-Ie z2SmmDp-R9+$J&TZ3MMiEw4G+eL$Ev-;(pjp=1Ue1KLj0Y|y;KAH};c_9DCgOg! z{^L8uWq!H3gFQ2+Rpm2WFj#GfAnO^SrqNY>6rJ>huM@Xi!Y@~;W-N)OjZSvA{LlH) z|2v-1d-NYX^B+9(A3XCPJo6ts^B+9(A3XCPJoEnp&u9gO*aboSt|AuFtJ@PW` zOUO{>Y?LW?t*8StYC}`*2sQ~_F}V~P-`5dL=Q(rt=}^b(NoMs0$F=j&nBsF$duYCF z8np~+l!Qbc9UP_Z;&xY+$p@qsU?C`PuwRFQqX*Gxo37NYegc6)?6JZ%|CHo&CC1}o zj!UxHYMPP91 zRcnN1`FA5{^>?qLOU~ol2O;xjnfrTz;!908+8uE$MUh0>LnoPEqSSNeNkU_mEx)Hy zDZg7{pFmAAR}_ff$8M=PvY6Ry%El>K8)8eFPAmk=iuzmUtu*(RdshXOPpetO#H-z; zAPu6zf!38?9Rqf6qixyYOH7$NFa zyI>)NCq>XQsb3$NbS$tU~a4;P*)Z%h_ZBoj|OLTH;at6i^mb!G=wbb7W4wL2P% z9x5NJ`!hPRYdqY4Rc=#DDE@5RVDzhgLT*bg>faOQ^BZ&{mN+SWxdTMev^k?(QN+gz zu1(L))85rAHjk!x9LMp^mx5_7O?my?;mkyL*vRRF?X(Bm*)Xe)0=&YHi=^_(V$?0* z;qofS+?1rUV{4F9)RhR~Zy({eWvuN~#8*!Sm~4IQ6G!Ji`UNdwYW*(=P&g8_nIl~T zO{@q;8Flb5KZ2Xy4hhUbSv`zGb#kHDXC)Tio4y0ZA%2{K$U;Gjq6_7wya4hpRWw=8 z7!#e~_3{2k0fK&+;%%@0O2&-ea4Tt=jIPgt>kgb4U`11A^EC3p|1SlonQT3c#n=K6 zCi^+|9I$PIIE4gzmWZ^1u~~a1u-geFuiY220E4NYD2efYNHSSbzZ(UoqQ2DQ_>X-=g@D%61_8}UpY5SmwY+Ak! zPm|eJcjV(?#)y7iQ_ya@ues_cyM5C^lZ~081*`1508j?w1}2rqLjVE@H+n)BrTnZ1 z{Q1m>KSR`t9dVfzGM9|H1&vP6l3@TPdAzX(2Z*B9bJ)UONL9t5>bAl;WgC#%L-#ZDc6N{1_S+EQ+*@#! zJB5F;;PdCkUObLb+_0&8m~5B;!7{)TLNiC=thXEeH_N4|emTH{)O?SKwB=CMJ5cd~ zek~-;{gl)I2!Bqc`AnJzr`$>u;=CLH(7^wcpfgZ&XW~40xVG|Eka>a>BbJZHcmQ}( zy$Dc%fLOpBGr*jf#7gzgm#?3u2#-B;%9$R#=D_oN!Unt&)%CG-Dc;$G-L#q!=(A*7ML&41EVH%yDYdNMRPZoZ#U=-f$wZW{Mn5DtGb3{72p>%Sop zSfr_3STk6Rb^f*R{#v~&BNa@YdU5p|fNClyo_FnN1qGk>C^uOI`cA1GXc;E1{;!IRc$DQ22c6q zC1}FgYqf)Xi)$GZ10L1!wBIV!!#sV)30Q==2w0?DYMv(ZS=(F=7)8Hx zfKl`Zcxw-l%2ptN52M(iE`f)u0GoX6YQ41@IJBwdrWdVl8JB#kV+JrMo|Q5u&gg`#5~Xe$q2DIYPZVATTp2p&_A1Cv!($x zyk1&ZOb)QRmvBD>pdtVRxv~O)l{@#TQ33!eS-kX=F3geyK%0qS5?Eqo(g@ZpOt?EaGKWur!fbP zaRuNl7C`D(fCc$^132c&Q+fq_3)+qyMn(q!*)agsi3TdJ=zOQ&4D*-WfZmBSq*nku z=+r|hy2G>z*iJJZC9bm_K(KZj(5;5Kra!Q=6j0)GfW~|SxXTzoSo*I4oaQ4?PykV* z@H#+E6hK(&0K(G$2Y@hJ|2U2MKE#p@Ncjvv7_1S%X_&R?b0|K5)2sq1o4{UFE(4tA zD!^$l|2T~yz-iimy_oX=oThyMX};BdG_qu}0dy&Vid6UkPO~Ysc6$vPW~4r|u%l3N z5USVl`)HXQb@~Uu)};EOZ;<q~%GSI7ONMBAMf;R=k<48cbq#?I6jdOzCfR(PkH)Nn!ff^2=2I~k2J)jXb zI1PDI7pOwq2L%3~Fa$(Y1R1@g3h<%F?SOa;h=dM6*a8C635WwO;G3FI=m#pfITk>T z{_?tO3ZLvU_V<3%RPtGgaSV3Lx@N!ga>@MOo}*R+xw}++^lZ9J`HtFm)gf5-2p-xUch1+B|TRb?Qjc%HN}3tqS{ zlI_L0N?(?2j)ig-v)46o@y&C7U_?>slDmY8sxqWv{&s>Ty9NoRTr(iOXcpu?@3Z+- zGULHE!5aCU2>qXo2Y#6#KXLxV32w9hb<(9DWZv^b>Q8*8^1fA-*tcu*f-yEIkP?T| z^VzrSz-!wd{5Q-WEUADu*1huI-Oe2lNjajy`deruBo)R(BeWdTNVsnL#}--y{g8yD z@72(&cI^0fk#s^LG?-p2-LPuOy6#s!XQd0tl&wM#r+q#q9ap7)yOCdqF&?LAn&NV#&t1R z18Zb}T9a742!1EWklkw)RazjODRcMRmP8>T`~-h`67k}FtrQzDDt+U^782fL|qL?OMDhyID8Yd-s!Q>tyJZ861b zB!>01f>W9*cHiH+NLN6#8a!@bWX|m-1FJXRu_=gNmJAtAq7`So!6vc>}fm zQx$rjI2XRd!1UK@K94->pNJ1OmDn|c#qa`G2YIhnQ`zkZz)$4*ff9K7t3x)aG{@yKOhe399y zI>1%RR;xvED@c7MOxo46VWUb2wKrZilsW#MV`Ui6t0G7_l*qKHwKBRo?udBizXcU2spW(!q=B%01Isc2flX^|={M zd@h-k1G&acgkL3;3==uHy`IdlFnd574b+0?d+y;Il;R7@{(Y>!>gTC5FLSH_o!xVK z>}lo>rz*GSi>uQQm4ar_A;GN`-xQ-)j{g*y7QJcWjU-_!avt^t^*h#~-RnYmy!^JA z`P1UcLBsv!e#J<}OhIklWTU^Ywvzl&b>0){Fh=;J1woUtPjMliq(e!1b_*TU`hU6= ztJiJ~PUy^T|hhNydU~il;}+Q^DMhd~LV0bS#1wpVu2e(lF5+ zVH3Z7(HK{QggLVTlaw_MD-e+%k#$G;@eeUwYaeX}9%(pBcAgZ!PGZ4S4tQ9vtm$@D4 zb+bn4VfT>pZ74N&{SQchO(|^vtyT@MUY!x!;p2V@b}b+2r3Q(+1K@(W`MDefRDQG${Wc|5nY=eJZ>WMb}8>)YFF6RY=vpX)4A zwWQauRcO&T8|Qn8_vWV9*$A6d^YthD!WJ?a{1E>y5@JIYOibE1S=G1(J)DqGTK8#H zev1s7LiY9_&G=LNHzO%cf1Ukg64pybvg&VgkEZWhp4*r=$)tUC=?wDS?7C)3&`~Y%(PunTiImUa@p9*77*bU`jND9QU6Kz8OiLl2aG4 zlCiE&oR!hq(pSx!LX(XK#*Z&$l^wk28am?cpDK52j%4DU5JMC9AG7q@zR7wO3(m^# z_;v|vi(yP2yWqJ)yxv)ETz6e$#ZLb6H~9g2m^u2!>!^k$6BE~``f1HgI=qn))Ta#u zqgWI4aRoLw>s6lN`zb6jMY?OmXEI#TX7VWyBm;gk((B6}^CR3l5XLZr2QThal(gvm z-8s*^%^>nLvzUcsUltCcj}Rz0HGmb4_5fjBT(l`a!sw zgt2x1CAsxQPmnD@VWwO;nX4jsGmOH!?9Ss=4d!;5bwB3NZnznVUteeHU7VMcU9mNh zX@I`kS4!sacU`KpLSGwxqp%8GOJ!3k`cXBe20Um3JC}+gYq#brxSQ(b-kYRW;5~q= z3J1Qd2S z->rk2IkKg3@n@UqnYne`N9yQuU3J4|W8n>Li_}|oCMF=S?@v;nd;gW$zGS z^C>tKxMxZHyJyu~OC-kCTcj$OHGb)agFsf zmQ0&#ZGKPi6?SCnPPOF`DP3OeZ*M`C(t=su?(Cbrcb^<_o}a9Vo~w(O*Z*jS99`0i z^4=l3xoYvCzuLa_UJa-&n9|grDYLnTm0NW7d}W@H<2j`0x2jakTr4>-?kiG#<1=t) z7GL(<{nve*t>YK-}=&kyD%2AGvuO#cEM9o}cQ7}|)Ej_n-6L{7^$ui-_zGt$)r zwu9eZiyUVaSdJ*Q_EeK!H||~a9vtrub9K8=G%U>O@~N+k$x{Yyx4TeAUdSVyeQEmYYwm87QP+!qkTIy}Tujj)Wws=2`7Prm3y+SCOVoMBfTW!t zvD=YBX>rj_D`?(@Z-}G&&w|G8R@`lkiYaA5%?|ruqQA#=!A))=7UIX@SIp|9yBbCa z4xmCSRN1p)9Vad4Rj{6kYmDoFice6RVq9}U75Qs`^+dl>VT-^*d`nNEmQK5;D{$CL z3wkZe4g2>Y{9r+j{B-;`k$8S{w%a8wi$a6;w+e7-*IeV%wyUp+6x*e(2&^hi{94p; zTGMoN<~4hpIv`y&a=>Mn$yjD%AFvQ#e{Gr@65~DIiiw=_I}9GwcZMhvj5jID0#jda z+mY%-LTs9*KDrhE-E1w2=SS2_Tjj3!LyGUYpe4UFeh5FQqu!ya!@ICK*Ap>vUdeG; zP;C#B<@d@JWZ4!L+!_0+f2rCl`&YWOlDOlC*&am4_2-35QGw2rtdIR;$1LUY;1WZq zgPfxu+VpQ@1itNbwhl+=Vdr2=$8WPPKAbHZ?qffVQ^8^_HaH$HeEUua_S z=g6ioycAS7-}?-0Rt|dyli?-C)BX61c4ora7nPi(X!k>{Y9_-Bx;z zlD6JB@!In&@}?0Vf!T>)WOUGvzV$OMeqNpW@dl+AAQAM2`jYu%){Jf^kvARF0S=^|iT$uf3 zGqo{afqdr?0eGN_^Zc#GnAr`sHynCZRgAz{LnqA$g_RRItyDKN6oY*4H>cx*O^M|@ zYI&wlI?G)TSjY7HGp5gC^n&uZQ;Ia7xzk*lJLi~IhQ-P{S38G&dU6*R0`+b|o7k|7 za@Kl&%2Oi?o^CRUB|J0vL^bwehK66R z#=>z?b!U>l^P~0o)zVKwx;%?Zo%dwBAVJUP1P^~#e!ZM_epfd&pdBn+*7&2BNpoh& z=Uki?ZKZ*2y7Zoy%(cKiEogl(sSCWA_;{C9`TON>0U})xs^>V17OfmB;bKfs;Ja&C zwj6pa*G}LKKbBhzmW{+6{n4l|pJdMC6W^qZZ((CD9gA;)qrIse$bt=}jGcTYduXq*jZ z|HS{ZC5N4?^j@wD68<@Z5OVXz6&=~g>H=X;!QGQEe;(k+P+!l_aIZXLUVe$quP#P} z9?u0x%#vP*yPsZdH(IzQpq!q-C^zW@J-r*1$yIbleqZv%)$;5t^4L3QuX3kO-I}&Y zV9Sr@&g%@Blq-he?8NC%0^9OJ>RO0mTgYQVFR_t1ZD_8kig*b*X7S3xrC}^nh<_n; zhT1cw{7uf*sSCpW@bHk^`W%88=%TS3+ftpjW0!#FdS~ddjhuT%#LhFAUFgxAT=k>H z5fgJ^q;GHOJE(efO&ZUenh1dtb@Pai#*gPDPm{wdu7V$*rhLe&wK&~q2z=y>>NlyJ zo%IsRMw~V)aF4`mQ;EX(fZrN?^Zf-*iyulq^cXg>%{QZ4D z7qC(lIjkJvsz?SzJcy`V1)_vYgTSCDNRz2TL5p1V07Os>NR=EYhe1(JLl7`Xh?str2aL!{m9%m0NeBbMH_#*;g!Gy+bTNUw*E9 z;b20<467{sqk;m*?3fGe&&HAk2amRT9k5;UI=SPNRsG>>D@F0yT>8^{N6wgEN`fP{ zq3MKdoO#7wZr0VEsVUnMlVbAJE%vm5aq|_I?_rf|9W6)jxWahxW`P=!R48JbC9R{h zBz6|-@Q|9KtD61pH7#zNH)d43EF4R2(D*e2U3G3=?RNxb&p92uHInM2%cxvSvAj~A zW;&nQvfy)uw6u|ZLX_3ORTVRcZxSYZ@IN~Dx78HGA&XEPvU_G0q*thpqf9-klU$aU zFQtZ}dkBVFBctCd_iXBhR>h8Lu6F`v?n zT#&Sx%w3!)XRz0d#JSqFySpZ|bH zg*DWqOPz{h%T^G$p>(oqlMo4?+tVOcZ^T9AGQ{d4;GXIMaE@{CF=OIvIB0M`R49T* z^BqDYEoX$&I7tbjlbat35$`DZ#W={_qS4+-iZKUG8JncU(Bs8@@Q#5p@c>bNcsrd; z>H}`NkSA{32dSKRH4C+r&+?m94$jUTVS0-YsO^oIr2z+J{|*rM*k5;%#-MyhA#(nN z5P1TjTZuUv*3=Rs>2*&s4kikb3gDucz(ws*a8cpFMeR{=Q9R(H_H2R{9&k~xs33rg zvfEU8chU}$!U#hqI(!Hsx#x9eSXs4q?|NNDE6P-?sW`bI6I!!@i|WT}eJ>vb{EN5* z=qp3OzlcU4?kfZyL{J|HX=;>ROPP0;*^!W;78do?nT$U6h*U!*7Ys1_4F(-Po3SoPL27}xj_Se!;asp=11ik`HeJ+PP1tA7pgyPG&eC^CPhkhD#X&N7kY~#yZWC-pOP()cn})yF*3k&ze*e_s$p5T z_e{)ZOOuuga(#VV%5nXfk|r&|yrjhS$?~tm)o#`{@*<9YGjV&Au3fcS;Dzh*tok(K|_`dh!dY&UyyN?ZZl?i?zn{!0R{HZ!w)_onG$f;OZoIi;hi{I!fh5Gesb(5Wb7Y`Eus?L<%#XV+QnhZLYH*JCG8}* zL{W+#K6qy{NqRx?ZjhK*eFOayu}Z)2J681Cl@EQ(MS_hG9 zWvhYS5QQ&_#;6+g#!kF9Hfm=&*?j@j&4U~1$B{~g{?>xt2M_YN)D1bF*c^vU{+%|F zFp{-&(m_9EbcNTu-US|CG7S7vY;Wg>^Zfk|jB%(nBK>z4EW$1XEDzqV9cb5eNpH~M z)}uq$_4XUSz|CS4I8B-62YK(RS-UzdOfMg>pkvh=!+!JVG0yGVd&GN$Mm+sCro7mr zC5ZWWEB;=3>HJ1@S4UN~FH}BV>Ki_~imD3KaQO2vRpD=CC*^+_C=A{^t<{~fgt&{c zlk#`g4^DZ@rgYk+?DXk5wU|!jWH5<@ literal 0 HcmV?d00001 From 5e1a304c017bd00ea89264d096cc5fd7203368a8 Mon Sep 17 00:00:00 2001 From: Torben Ott Date: Wed, 28 Sep 2016 12:28:44 -0400 Subject: [PATCH 23/30] When stopping a Bpod protocol the custom user script UserKillScript.m located in the current protocol folder is executed, if it exists. Allows for custom actions at session end (eg email figures, etc). --- Functions/Launch manager/RunProtocol.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Functions/Launch manager/RunProtocol.m b/Functions/Launch manager/RunProtocol.m index 380e24a..3aa370e 100644 --- a/Functions/Launch manager/RunProtocol.m +++ b/Functions/Launch manager/RunProtocol.m @@ -41,6 +41,12 @@ function RunProtocol(Opstring) end end case 'Stop' + %execute user kill script + try + [~,Protocol]=fileparts(fileparts(fileparts(BpodSystem.DataPath))); + run(fullfile(BpodSystem.BpodUserPath,'Protocols',Protocol,'UserKillScript.m')); + catch + end if ~isempty(BpodSystem.CurrentProtocolName) disp(' ') disp([BpodSystem.CurrentProtocolName ' ended.']) From 076d02a8162731313a99de893e4aa11a8b218b89 Mon Sep 17 00:00:00 2001 From: torbencshl Date: Mon, 24 Oct 2016 16:41:43 -0400 Subject: [PATCH 24/30] Custom location for BpodUser (#8) Adds .txt file containing string to BpodUserPath in Bpod home directory. Default is home directory. --- Functions/Internal Functions/BpodObject.m | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Functions/Internal Functions/BpodObject.m b/Functions/Internal Functions/BpodObject.m index 5093815..d982134 100644 --- a/Functions/Internal Functions/BpodObject.m +++ b/Functions/Internal Functions/BpodObject.m @@ -118,13 +118,23 @@ obj.HostOS = system_dependent('getos'); obj.BpodPath = BpodPath; -%% FS MOD - if ispc - import java.lang.*; - obj.BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); + %% FS MOD + if exist(fullfile(obj.BpodPath,'BpodUserPath.txt'),'file') == 2 + UserFile = fopen(fullfile(obj.BpodPath,'BpodUserPath.txt'),'r'); + BpodUserPath = fscanf(UserFile,'%s'); + fclose(UserFile); else - obj.BpodUserPath = fullfile('~', 'BpodUser'); + if ispc + import java.lang.*; + BpodUserPath = fullfile(char(System.getProperty('user.home')), 'BpodUser'); + else + BpodUserPath = fullfile('~', 'BpodUser'); end + UserFile = fopen(fullfile(obj.BpodPath,'BpodUserPath.txt'),'w'); + fprintf(UserFile,'%s',BpodUserPath); + fclose(UserFile); + end + obj.BpodUserPath = BpodUserPath; if ~isdir(obj.BpodUserPath) mkdir(obj.BpodUserPath); warning(['Bpod user directory not found. Directory created at ' obj.BpodUserPath]); From ffd003bf135097496e3c06147cde493ddd96d978 Mon Sep 17 00:00:00 2001 From: Fitz Sturgill Date: Thu, 3 Nov 2016 15:04:51 -0400 Subject: [PATCH 25/30] Very minor: 1) ignore BpodUserPath.txt 2) fix SaveBpodProtocolSettings so it works with more recent matlab versions. I've tested these changes. Sorry for not doing a pull request!!! --- .gitignore | 3 ++- Functions/Launch manager/SaveBpodProtocolSettings.m | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 94f72a9..41d8c63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ Calibration Files/** Data/** Protocols/** -.gitmodules \ No newline at end of file +.gitmodules +*BpodUserPath.txt \ No newline at end of file diff --git a/Functions/Launch manager/SaveBpodProtocolSettings.m b/Functions/Launch manager/SaveBpodProtocolSettings.m index 33a477d..fe6cd26 100644 --- a/Functions/Launch manager/SaveBpodProtocolSettings.m +++ b/Functions/Launch manager/SaveBpodProtocolSettings.m @@ -19,4 +19,8 @@ %} function SaveBpodProtocolSettings global BpodSystem -save(BpodSystem.SettingsPath, 'BpodSystem.ProtocolSettings'); \ No newline at end of file + +% now compatible with recent matlab versions in which you can't save a +% structure field directly (FS MOD) +ProtocolSettings = BpodSystem.ProtocolSettings; +save(BpodSystem.SettingsPath, 'ProtocolSettings'); \ No newline at end of file From bc1ae7fb1f48bcc4c31711bb3a3e8885439d1c22 Mon Sep 17 00:00:00 2001 From: Fitz Sturgill Date: Thu, 30 Mar 2017 16:39:53 -0400 Subject: [PATCH 26/30] added edit text/string option to parameter GUI --- Functions/Plugins/ParameterGUI/BpodParameterGUI.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m index b08c8eb..2724d41 100644 --- a/Functions/Plugins/ParameterGUI/BpodParameterGUI.m +++ b/Functions/Plugins/ParameterGUI/BpodParameterGUI.m @@ -126,6 +126,9 @@ case 'edit' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 1; BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'edit', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); + case 'edittext' + BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 8; + BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'edit', 'String', ThisParam, 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); case 'text' BpodSystem.GUIData.ParameterGUI.Styles(ParamNum) = 2; BpodSystem.GUIHandles.ParameterGUI.Params{ParamNum} = uicontrol(htab,'Style', 'text', 'String', num2str(ThisParam), 'Position', [HPos+220 VPos+InPanelPos+2 200 25], 'FontWeight', 'normal', 'FontSize', 12, 'BackgroundColor','white', 'FontName', 'Arial','HorizontalAlignment','Center'); @@ -213,6 +216,13 @@ elseif Params.GUI.(ThisParamName) ~= ThisParamLastValue set(ThisParamHandle, 'String', num2str(GUIParam)); end + case 8 % Edit Text + GUIParam = get(ThisParamHandle, 'String'); + if ~strcmpi(GUIParam, ThisParamLastValue) + Params.GUI.(ThisParamName) = GUIParam; + elseif ~strcmpi(Params.GUI.(ThisParamName), ThisParamLastValue) + set(ThisParamHandle, 'String', GUIParam); + end case 2 % Text GUIParam = Params.GUI.(ThisParamName); Text = GUIParam; @@ -269,6 +279,9 @@ case 1 % Edit GUIParam = str2double(get(ThisParamHandle, 'String')); Params.GUI.(ThisParamName) = GUIParam; + case 8 % Edit Text + GUIParam = get(ThisParamHandle, 'String'); + Params.GUI.(ThisParamName) = GUIParam; case 2 % Text GUIParam = get(ThisParamHandle, 'String'); GUIParam = str2double(GUIParam); From 2a7b51fc2251e9ebb66b080a82c7a738ffc56894 Mon Sep 17 00:00:00 2001 From: Fitz Sturgill Date: Sat, 8 Apr 2017 16:34:44 -0400 Subject: [PATCH 27/30] testing changes to deal with TrialTypes intialized as vector of NaNs --- Functions/Plugins/Plots/TrialTypeOutcomePlot.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Functions/Plugins/Plots/TrialTypeOutcomePlot.m b/Functions/Plugins/Plots/TrialTypeOutcomePlot.m index f98b64f..729dc15 100644 --- a/Functions/Plugins/Plots/TrialTypeOutcomePlot.m +++ b/Functions/Plugins/Plots/TrialTypeOutcomePlot.m @@ -64,6 +64,9 @@ function TrialTypeOutcomePlot(AxesHandle, Action, varargin) end axes(AxesHandle); MaxTrialType = max(TrialTypeList); + if isnan(MaxTrialType) + MaxTrialType = 1; % for NaN filled TrialTypes, might occur if you determine trial type on the fly + end %plot in specified axes Xdata = 1:nTrialsToShow; Ydata = -TrialTypeList(Xdata); BpodSystem.GUIHandles.FutureTrialLine = line([Xdata,Xdata],[Ydata,Ydata],'LineStyle','none','Marker','o','MarkerEdge','b','MarkerFace','b', 'MarkerSize',6); @@ -84,6 +87,9 @@ function TrialTypeOutcomePlot(AxesHandle, Action, varargin) TrialTypeList = varargin{2}; OutcomeRecord = varargin{3}; MaxTrialType = max(TrialTypeList); + if isnan(MaxTrialType) + MaxTrialType = 1; % for NaN filled TrialTypes, might occur if you determine trial type on the fly + end set(AxesHandle,'YLim',[-MaxTrialType-.5, -.5], 'YTick', -MaxTrialType:1:-1,'YTickLabel', strsplit(num2str(MaxTrialType:-1:-1))); if CurrentTrial<1 CurrentTrial = 1; From 54beae0654a95c291f86784f0902d7cb4bdb2fa9 Mon Sep 17 00:00:00 2001 From: marionbosc Date: Tue, 16 May 2017 17:10:31 -0400 Subject: [PATCH 28/30] As the olfactometer IP address is often a string, turning it into a string makes an error --- Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m index 8e011e3..0ec6b04 100644 --- a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m +++ b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m @@ -1,5 +1,11 @@ function SetBankFlowRate(OlfIP, Bank, FlowRate) -IPString = [num2str(OlfIP(1)) '.' num2str(OlfIP(2)) '.' num2str(OlfIP(3)) '.' num2str(OlfIP(4))]; + +if ~isstring(OlfIP) % in case OlfIP is a number instead of a string + IPString = [num2str(OlfIP(1)) '.' num2str(OlfIP(2)) '.' num2str(OlfIP(3)) '.' num2str(OlfIP(4))]; +else + IPString = OlfIP; +end + try TCPWrite(IPString, 3336, ['WRITE BankFlow' num2str(Bank) '_Actuator ' num2str(FlowRate)]); catch From ef92eed0cb4703ba8377242fc5fc04e98c54b8df Mon Sep 17 00:00:00 2001 From: "ELENA-RIG\\Marion" Date: Thu, 18 May 2017 16:38:18 -0400 Subject: [PATCH 29/30] As the olfactometer IP address is often a string, turning it into a string makes an error. I added a line to test wheter it is a number or not. --- .../Plugins/RechiaOlfactometer/SetBankFlowRate.asv | 13 +++++++++++++ .../Plugins/RechiaOlfactometer/SetBankFlowRate.m | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv diff --git a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv new file mode 100644 index 0000000..059db6f --- /dev/null +++ b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv @@ -0,0 +1,13 @@ +function SetBankFlowRate(OlfIP, Bank, FlowRate) + +if ~ischar(OlfIP) % in case OlfIP is a number instead of a string + IPString = [num2str(OlfIP(1)) '.' num2str(OlfIP(2)) '.' num2str(OlfIP(3)) '.' num2str(OlfIP(4))]; +else + IPString = OlfIP; +end + +try + TCPWrite(IPString, 3336, ['WRITE BankFlow' num2str(Bank) '_Actuator ' num2str(FlowRate)]); +catch + error('Connection Error!') +end \ No newline at end of file diff --git a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m index 0ec6b04..fbd027b 100644 --- a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m +++ b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.m @@ -1,6 +1,6 @@ function SetBankFlowRate(OlfIP, Bank, FlowRate) -if ~isstring(OlfIP) % in case OlfIP is a number instead of a string +if isnumeric(OlfIP) % in case OlfIP is a number instead of a string IPString = [num2str(OlfIP(1)) '.' num2str(OlfIP(2)) '.' num2str(OlfIP(3)) '.' num2str(OlfIP(4))]; else IPString = OlfIP; From 1d7fa8e5c46b847d1fc645bea9bf2dca45a36bb3 Mon Sep 17 00:00:00 2001 From: "ELENA-RIG\\Marion" Date: Thu, 18 May 2017 17:12:14 -0400 Subject: [PATCH 30/30] As the olfactometer IP address is often a string, turning it into a string makes an error - debug --- .../Plugins/RechiaOlfactometer/SetBankFlowRate.asv | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv diff --git a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv b/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv deleted file mode 100644 index 059db6f..0000000 --- a/Functions/Plugins/RechiaOlfactometer/SetBankFlowRate.asv +++ /dev/null @@ -1,13 +0,0 @@ -function SetBankFlowRate(OlfIP, Bank, FlowRate) - -if ~ischar(OlfIP) % in case OlfIP is a number instead of a string - IPString = [num2str(OlfIP(1)) '.' num2str(OlfIP(2)) '.' num2str(OlfIP(3)) '.' num2str(OlfIP(4))]; -else - IPString = OlfIP; -end - -try - TCPWrite(IPString, 3336, ['WRITE BankFlow' num2str(Bank) '_Actuator ' num2str(FlowRate)]); -catch - error('Connection Error!') -end \ No newline at end of file