diff --git a/@MyDaq/MyDaq.m b/@MyDaq/MyDaq.asv similarity index 97% copy from @MyDaq/MyDaq.m copy to @MyDaq/MyDaq.asv index bab7091..008d4fc 100644 --- a/@MyDaq/MyDaq.m +++ b/@MyDaq/MyDaq.asv @@ -1,990 +1,988 @@ -classdef MyDaq < handle - properties - %Contains GUI handles - Gui; - %Contains Reference trace (MyTrace object) - Ref=MyTrace(); - %Contains Data trace (MyTrace object) - Data=MyTrace(); - %Contains Background trace (MyTrace object) - Background=MyTrace(); - %Struct containing available instruments - InstrList=struct(); - %Struct containing MyInstrument objects - Instruments=struct() - %Struct containing apps for interfacing with instruments - InstrApps=struct(); - %Struct containing Cursor objects - Cursors=struct(); - %Struct containing Cursor labels - CrsLabels=struct(); - %Struct containing MyFit objects - Fits=struct(); - %Input parser - Parser; - %Struct for listeners - Listeners=struct(); - - %Sets the colors of fits, data and reference - fit_color='k'; - data_color='b'; - ref_color='r'; - bg_color='c'; - - %Properties for saving files - base_dir; - session_name; - file_name; - - %Flag for enabling the GUI - enable_gui; - end - - properties (Dependent=true) - save_dir; - main_plot; - open_fits; - open_instrs; - open_crs; - instr_tags; - instr_names; - savefile; - end - - methods (Access=public) - %% Class functions - %Constructor function - function this=MyDaq(varargin) - createParser(this); - parse(this.Parser,varargin{:}); - parseInputs(this); - if this.enable_gui - this.Gui=guihandles(eval('GuiDaq')); - initGui(this); - hold(this.main_plot,'on'); - end - initDaq(this) - end - - function delete(this) - %Deletes the MyFit objects and their listeners - cellfun(@(x) deleteListeners(this,x), this.open_fits); - structfun(@(x) delete(x), this.Fits); - - - %Deletes the InstrApp objects and their listeners - cellfun(@(x) deleteListeners(this,x), fieldnames(this.InstrApps)); - structfun(@(x) delete(x), this.InstrApps); - - %Deletes the MyInstrument objects and their listeners - cellfun(@(x) deleteListeners(this,x), this.open_instrs); - structfun(@(x) delete(x), this.Instruments); - - - - if this.enable_gui - this.Gui.figure1.CloseRequestFcn=''; - %Deletes the figure - delete(this.Gui.figure1); - %Removes the figure handle to prevent memory leaks - this.Gui=[]; - end - end - end - - methods (Access=private) - %Creates parser for constructor function - function createParser(this) - p=inputParser; - addParameter(p,'enable_gui',1); - this.Parser=p; - end - - %Sets the class variables to the inputs from the inputParser. - function parseInputs(this) - for i=1:length(this.Parser.Parameters) - %Takes the value from the inputParser to the appropriate - %property. - if isprop(this,this.Parser.Parameters{i}) - this.(this.Parser.Parameters{i})=... - this.Parser.Results.(this.Parser.Parameters{i}); - end - end - end - - %Initializes the class depending on the computer name - function initDaq(this) - computer_name=getenv('computername'); - switch computer_name - case 'LPQM1PCLAB2' - initRt(this); - case 'LPQM1PC18' - initUhq(this); - case 'LPQM1PC2' - %Test case for testing on Nils' computer. - otherwise - warning('Please create an initialization function for this computer') - end - - %Initializes empty trace objects - this.Ref=MyTrace; - this.Data=MyTrace; - this.Background=MyTrace; - end - - %Sets callback functions for the GUI - initGui(this) - - %Executes when the GUI is closed - function closeFigure(this,~,~) - delete(this); - end - - %Adds an instrument to InstrList. Used by initialization functions. - function addInstr(this,tag,name,type,interface,address) - %Usage: addInstr(this,tag,name,type,interface,address) - if ~ismember(tag,this.instr_tags) - this.InstrList.(tag).name=name; - this.InstrList.(tag).type=type; - this.InstrList.(tag).interface=interface; - this.InstrList.(tag).address=address; - else - error(['%s is already defined as an instrument. ',... - 'Please choose a different tag'],tag); - end - end - - %Gets the tag corresponding to an instrument name - function tag=getTag(this,instr_name) - ind=cellfun(@(x) strcmp(this.InstrList.(x).name,instr_name),... - this.instr_tags); - tag=this.instr_tags{ind}; - end - - %Opens the correct instrument - function openInstrument(this,tag) - instr_type=this.InstrList.(tag).type; - %Collects the correct inputs for creating the MyInstrument - %class - input_cell={this.InstrList.(tag).interface,... - this.InstrList.(tag).address}; - - switch instr_type - case 'RSA' - this.Instruments.(tag)=MyRsa(input_cell{:},... - 'gui','GuiRsa','name',this.InstrList.(tag).name); - case 'Scope' - this.InstrApps.(tag)=GuiScope(input_cell{:},... - 'name',this.InstrList.(tag).name); - this.Instruments.(tag)=this.InstrApps.(tag).Instr; - case 'NA' - this.Instruments.(tag)=MyNa(input_cell{:},... - 'gui','GuiNa','name',this.InstrList.(tag).name); - end - - %Adds listeners for new data and deletion of the instrument. - %These call plot functions and delete functions respectively. - this.Listeners.(tag).NewData=... - addlistener(this.Instruments.(tag),'NewData',... - @(src, eventdata) acquireNewData(this, src, eventdata)); - this.Listeners.(tag).Deletion=... - addlistener(this.Instruments.(tag),'ObjectBeingDestroyed',... - @(src, eventdata) deleteInstrument(this, src, eventdata)); - end - - %Updates fits - function updateFits(this) - %Pushes data into fits in the form of MyTrace objects, so that - %units etc follow. Also updates user supplised parameters. - for i=1:length(this.open_fits) - switch this.open_fits{i} - case {'Linear','Quadratic','Gaussian',... - 'Exponential','Beta'} - this.Fits.(this.open_fits{i}).Data=... - getFitData(this,'VertData'); - case {'Lorentzian','DoubleLorentzian'} - this.Fits.(this.open_fits{i}).Data=... - getFitData(this,'VertData'); - if isfield(this.Cursors,'VertRef') - ind=findCursorData(this,'Data','VertRef'); - this.Fits.(this.open_fits{i}).CalVals.line_spacing=... - range(this.Data.x(ind)); - end - case {'G0'} - this.Fits.G0.MechTrace=getFitData(this,'VertData'); - this.Fits.G0.CalTrace=getFitData(this,'VertRef'); - end - end - end - - % If vertical cursors are on, takes only data - %within cursors. - %If the cursor is not open, it takes all the data from the selected - %trace in the analysis trace selection dropdown - function Trace=getFitData(this,varargin) - %Parses varargin input - p=inputParser; - addOptional(p,'name','',@ischar); - parse(p,varargin{:}) - name=p.Results.name; - - %Finds out which trace the user wants to fit. - trc_opts=this.Gui.SelTrace.String; - trc_str=trc_opts{this.Gui.SelTrace.Value}; - % Note the use of copy here! This is a handle - %class, so if normal assignment is used, this.Fits.Data and - %this.(trace_str) will refer to the same object, causing roblems. - %Name input is the name of the cursor to be used to extract data. - Trace=copy(this.(trc_str)); - if isfield(this.Cursors,name) - ind=findCursorData(this, trc_str, name); - Trace.x=this.(trc_str).x(ind); - Trace.y=this.(trc_str).y(ind); - end - end - - %Finds data between named cursors in the given trace - function ind=findCursorData(this, trc_str, name) - crs_pos=sort([this.Cursors.(name){1}.Location,... - this.Cursors.(name){2}.Location]); - ind=(this.(trc_str).x>crs_pos(1) & this.(trc_str).x - %Prints the figure to the clipboard - print(newFig,'-clipboard','-dbitmap'); - %Deletes the figure - delete(newFig); - end - - function updateAxis(this) - axis(this.main_plot,'tight'); - end - end - - methods (Access=public) - %% Callbacks - - %Callback for copying the plot to clipboard - function copyPlotCallback(this,~,~) - copyPlot(this); - end - - %Callback for centering cursors - function centerCursorsCallback(this, ~, ~) - if ~this.Gui.LogX.Value - x_pos=mean(this.main_plot.XLim); - else - x_pos=10^(mean(log10(this.main_plot.XLim))); - end - - if ~this.Gui.LogY.Value - y_pos=mean(this.main_plot.YLim); - else - y_pos=10^(mean(log10(this.main_plot.YLim))); - end - - for i=1:length(this.open_crs) - switch this.Cursors.(this.open_crs{i}){1}.Orientation - case 'horizontal' - pos=y_pos; - case 'vertical' - pos=x_pos; - end - - %Centers the position - cellfun(@(x) set(x,'Location',pos), ... - this.Cursors.(this.open_crs{i})); - %Triggers the UpdateCursorBar event, which triggers - %listener callback to reposition text - cellfun(@(x) notify(x,'UpdateCursorBar'),... - this.Cursors.(this.open_crs{i})); - cellfun(@(x) notify(x,'EndDrag'),... - this.Cursors.(this.open_crs{i})); - end - end - - %Callback for creating vertical data cursors - function cursorButtonCallback(this, hObject, ~) - name=erase(hObject.Tag,'Button'); - %Gets the first four characters of the tag (Vert or Horz) - type=name(1:4); - - if hObject.Value - hObject.BackgroundColor=[0,1,0.2]; - createCursors(this,name,type); - else - hObject.BackgroundColor=[0.941,0.941,0.941]; - deleteCursors(this,name); - end - end - - %Callback for the instrument menu - function instrMenuCallback(this,hObject,~) - val=hObject.Value; - names=hObject.String; - %Finds the correct instrument tag as long as an instrument is - %selected - if val~=1 - try - tag=getTag(this,names(val)); - catch - error('Invalid instrument selected: %s',names{val}); - end - end - - %If instrument is valid and not open, opens it. If it is valid - %and open it changes focus to the instrument control window. - if (ismember(tag,this.instr_tags) && ... - ~ismember(tag,this.open_instrs)) - openInstrument(this,tag); - elseif ismember(tag,this.open_instrs) - if isfield(this.InstrApps,tag) - prop_names=properties(this.InstrApps.(tag)); - figure_ind=cellfun(@(x) isa(this.InstrApps.(tag).(x),... - 'matlab.ui.Figure'),prop_names); - fig_handle=this.InstrApps.(tag).(prop_names{figure_ind}); - fig_handle.Visible='off'; - fig_handle.Visible='on'; - elseif isprop(this.Instruments.(tag),'Gui') - InstrGui=this.Instruments.(tag).Gui; - ind=structfun(@(x) isa(x,'matlab.ui.Figure'),InstrGui); - names=fieldnames(InstrGui); - figure(InstrGui.(names{ind})); - else - error(['Instrument %s does not have a GUI but is',... - ' registered as open'],tag) - end - - - end - end - - %Select trace callback - function selTraceCallback(this, ~, ~) - updateFits(this) - end - - %Saves the data if the save data button is pressed. - function saveDataCallback(this, ~, ~) - if this.Data.validatePlot - save(this.Data,'save_dir',this.save_dir,'name',... - this.savefile) - else - errdlg('Data trace was empty, could not save'); - end - end - - %Saves the reference if the save ref button is pressed. - function saveRefCallback(this, ~, ~) - if this.Data.validatePlot - save(this.Ref,'save_dir',this.save_dir,'name',... - this.savefile) - else - errdlg('Reference trace was empty, could not save') - end - end - - %Toggle button callback for showing the data trace. - function showDataCallback(this, hObject, ~) - if hObject.Value - hObject.BackgroundColor=[0,1,0.2]; - setVisible(this.Data,this.main_plot,1); - updateAxis(this); - else - hObject.BackgroundColor=[0.941,0.941,0.941]; - setVisible(this.Data,this.main_plot,0); - updateAxis(this); - end - end - - %Toggle button callback for showing the ref trace - function showRefCallback(this, hObject, ~) - if hObject.Value - hObject.BackgroundColor=[0,1,0.2]; - setVisible(this.Ref,this.main_plot,1); - updateAxis(this); - else - hObject.BackgroundColor=[0.941,0.941,0.941]; - setVisible(this.Ref,this.main_plot,0); - updateAxis(this); - end - end - - %Callback for moving the data to reference. - function dataToRefCallback(this, ~, ~) - if this.Data.validatePlot - this.Ref.x=this.Data.x; - this.Ref.y=this.Data.y; - this.Ref.plotTrace(this.main_plot,'Color',this.ref_color,... - 'make_labels',true); - this.Ref.setVisible(this.main_plot,1); - updateFits(this); - this.Gui.ShowRef.Value=1; - this.Gui.ShowRef.BackgroundColor=[0,1,0.2]; - else - warning('Data trace was empty, could not move to reference') - end - end - - %Callback for ref to bg button. Sends the reference to background - function refToBgCallback(this, ~, ~) - if this.Ref.validatePlot - this.Background.x=this.Ref.x; - this.Background.y=this.Ref.y; - this.Background.plotTrace(this.main_plot,... - 'Color',this.bg_color,'make_labels',true); - this.Background.setVisible(this.main_plot,1); - else - warning('Reference trace was empty, could not move to background') - end - end - - %Callback for data to bg button. Sends the data to background - function dataToBgCallback(this, ~, ~) - if this.Data.validatePlot - this.Background.x=this.Data.x; - this.Background.y=this.Data.y; - this.Background.plotTrace(this.main_plot,... - 'Color',this.bg_color,'make_labels',true); - this.Background.setVisible(this.main_plot,1); - else - warning('Data trace was empty, could not move to background') - end - end - - %Callback for clear background button. Clears the background - function clearBgCallback(this, ~, ~) - this.Background.x=[]; - this.Background.y=[]; - this.Background.setVisible(this.main_plot,0); - end - - %Callback for LogY button. Sets the YScale to log/lin - function logYCallback(this, hObject, ~) - if hObject.Value - this.main_plot.YScale='Log'; - hObject.BackgroundColor=[0,1,0.2]; - else - this.main_plot.YScale='Linear'; - hObject.BackgroundColor=[0.941,0.941,0.941]; - end - updateAxis(this); - updateCursors(this); - end - - %Callback for LogX button. Sets the XScale to log/lin. Updates the - %axis and cursors afterwards. - function logXCallback(this, hObject, ~) - if get(hObject,'Value') - set(this.main_plot,'XScale','Log'); - set(hObject, 'BackgroundColor',[0,1,0.2]); - else - set(this.main_plot,'XScale','Linear'); - set(hObject, 'BackgroundColor',[0.941,0.941,0.941]); - end - updateAxis(this); - updateCursors(this); - end - - %Base directory callback. Sets the base directory. Also - %updates fit objects with the new save directory. - function baseDirCallback(this, hObject, ~) - this.base_dir=hObject.String; - for i=1:length(this.open_fits) - this.Fits.(this.open_fits{i}).save_dir=this.save_dir; - end - end - - %Callback for session name edit box. Sets the session name. Also - %updates fit objects with the new save directory. - function sessionNameCallback(this, hObject, ~) - this.session_name=hObject.String; - for i=1:length(this.open_fits) - this.Fits.(this.open_fits{i}).save_dir=this.save_dir; - end - end - - %Callback for filename edit box. Sets the file name. Also - %updates fit objects with the new file name. - function fileNameCallback(this, hObject,~) - this.file_name=hObject.String; - for i=1:length(this.open_fits) - this.Fits.(this.open_fits{i}).save_name=this.file_name; - end - end - - %Callback for the analyze menu (popup menu for selecting fits). - %Opens the correct MyFit object. - function analyzeMenuCallback(this, hObject, ~) - analyze_ind=hObject.Value; - %Finds the correct fit name by erasing spaces and other - %superfluous strings - analyze_name=hObject.String{analyze_ind}; - analyze_name=erase(analyze_name,'Fit'); - analyze_name=erase(analyze_name,'Calibration'); - analyze_name=erase(analyze_name,' '); - - switch analyze_name - case {'Linear','Quadratic','Lorentzian','Gaussian',... - 'DoubleLorentzian'} - openMyFit(this,analyze_name); - case 'g0' - openMyG(this); - case 'Beta' - openMyBeta(this); - end - end - - function openMyFit(this,fit_name) - %Sees if the MyFit object is already open, if it is, changes the - %focus to it, if not, opens it. - if ismember(fit_name,fieldnames(this.Fits)) - %Changes focus to the relevant fit window - figure(this.Fits.(fit_name).Gui.Window); - else - %Gets the data for the fit using the getFitData function - %with the vertical cursors - DataTrace=getFitData(this,'VertData'); - %Makes an instance of MyFit with correct parameters. - this.Fits.(fit_name)=MyFit(... - 'fit_name',fit_name,... - 'enable_plot',1,... - 'plot_handle',this.main_plot,... - 'Data',DataTrace,... - 'save_dir',this.save_dir,... - 'save_name',this.file_name); - - updateFits(this); - %Sets up a listener for the Deletion event, which - %removes the MyFit object from the Fits structure if it is - %deleted. - this.Listeners.(fit_name).Deletion=... - addlistener(this.Fits.(fit_name),'ObjectBeingDestroyed',... - @(src, eventdata) deleteFit(this, src, eventdata)); - - %Sets up a listener for the NewFit. Callback plots the fit - %on the main plot. - this.Listeners.(fit_name).NewFit=... - addlistener(this.Fits.(fit_name),'NewFit',... - @(src, eventdata) plotNewFit(this, src, eventdata)); - - %Sets up a listener for NewInitVal - this.Listeners.(fit_name).NewInitVal=... - addlistener(this.Fits.(fit_name),'NewInitVal',... - @(~,~) updateCursors(this)); - end - end - - %Opens MyG class if it is not open. - function openMyG(this) - if ismember('G0',this.open_fits) - figure(this.Fits.G0.Gui.figure1); - else - MechTrace=getFitData(this,'VertData'); - CalTrace=getFitData(this,'VertRef'); - this.Fits.G0=MyG('MechTrace',MechTrace,'CalTrace',CalTrace,... - 'name','G0'); - - %Adds listener for object being destroyed - this.Listeners.G0.Deletion=addlistener(this.Fits.G0,... - 'ObjectBeingDestroyed',... - @(~,~) deleteObj(this,'G0')); - end - end - - %Opens MyBeta class if it is not open. - function openMyBeta(this) - if ismember('Beta', this.open_fits) - figure(this.Fits.Beta.Gui.figure1); - else - DataTrace=getFitData(this); - this.Fits.Beta=MyBeta('Data',DataTrace); - - %Adds listener for object being destroyed, to perform cleanup - this.Listeners.Beta.Deletion=addlistener(this.Fits.Beta,... - 'ObjectBeingDestroyed',... - @(~,~) deleteObj(this,'Beta')); - end - end - - %Callback for load data button - function loadDataCallback(this, ~, ~) - if isempty(this.base_dir) - warning('Please input a valid folder name for loading a trace'); - this.base_dir=pwd; - end - - try - [load_name,path_name]=uigetfile('.txt','Select the trace',... - this.base_dir); - load_path=[path_name,load_name]; - dest_trc=this.Gui.DestTrc.String{this.Gui.DestTrc.Value}; - loadTrace(this.(dest_trc),load_path); - this.(dest_trc).plotTrace(this.main_plot,... - 'Color',this.(sprintf('%s_color',lower(dest_trc))),... - 'make_labels',true); - updateAxis(this); - updateCursors(this); - catch - error('Please select a valid file'); - end - end - end - - methods (Access=public) - %% Listener functions - %Callback function for NewFit listener. Plots the fit in the - %window using the plotFit function of the MyFit object - function plotNewFit(this, src, ~) - src.plotFit('Color',this.fit_color); - updateAxis(this); - updateCursors(this); - end - - %Callback function for the NewData listener - function acquireNewData(this, src, ~) - hline=getLineHandle(this.Data,this.main_plot); - this.Data=copy(src.Trace); - if ~isempty(hline); this.Data.hlines{1}=hline; end - clearData(src.Trace); - this.Data.plotTrace(this.main_plot,'Color',this.data_color,... - 'make_labels',true) - updateAxis(this); - updateCursors(this); - updateFits(this); - end - - %Callback function for MyInstrument ObjectBeingDestroyed listener. - %Removes the relevant field from the Instruments struct and deletes - %the listeners from the object - function deleteInstrument(this, src, ~) - %Deletes the object from the Instruments struct - tag=getTag(this, src.name); - if ismember(tag, this.open_instrs) - this.Instruments=rmfield(this.Instruments,tag); - end - - %Deletes the listeners from the Listeners struct - deleteListeners(this, tag); - end - - %Callback function for MyFit ObjectBeingDestroyed listener. - %Removes the relevant field from the Fits struct and deletes the - %listeners from the object. - function deleteFit(this, src, ~) - %Deletes the object from the Fits struct and deletes listeners - deleteObj(this,src.fit_name); - - %Clears the fits - src.clearFit; - - %Updates cursors since the fits are removed from the plot - updateCursors(this); - end - - %Callback function for other analysis method deletion listeners. - %Does the same as above. - function deleteObj(this,name) - if ismember(name,this.open_fits) - this.Fits=rmfield(this.Fits,name); - end - deleteListeners(this, name); - end - - %Listener update function for vertical cursor - function vertCursorUpdate(this, src) - %Finds the index of the cursor. All cursors are tagged - %(name)1, (name)2, e.g. VertData2, ind is the number. - ind=str2double(src.Tag(end)); - tag=src.Tag(1:(end-1)); - %Moves the cursor labels - set(this.CrsLabels.(tag){ind},'Position',[src.Location,... - this.CrsLabels.(tag){ind}.Position(2),0]); - if strcmp(tag,'VertData') - %Sets the edit box displaying the location of the cursor - this.Gui.(sprintf('EditV%d',ind)).String=... - num2str(src.Location); - %Sets the edit box displaying the difference in locations - this.Gui.EditV2V1.String=... - num2str(this.Cursors.VertData{2}.Location-... - this.Cursors.VertData{1}.Location); - end - end - - %Listener update function for horizontal cursor - function horzCursorUpdate(this, src) - %Finds the index of the cursor. All cursors are tagged - %(name)1, (name)2, e.g. VertData2, ind is the number. - ind=str2double(src.Tag(end)); - tag=src.Tag(1:(end-1)); - %Moves the cursor labels - set(this.CrsLabels.(tag){ind},'Position',... - [this.CrsLabels.(tag){ind}.Position(1),... - src.Location,0]); - if strcmp(tag,'HorzData') - %Sets the edit box displaying the location of the cursor - this.Gui.(sprintf('EditH%d',ind)).String=... - num2str(src.Location); - %Sets the edit box displaying the difference in locations - this.Gui.EditH2H1.String=... - num2str(this.Cursors.HorzData{2}.Location-... - this.Cursors.HorzData{1}.Location); - end - end - - %Function that deletes listeners from the listeners struct, - %corresponding to an object of name obj_name - function deleteListeners(this, obj_name) - %Finds if the object has listeners in the listeners structure - if ismember(obj_name, fieldnames(this.Listeners)) - %Grabs the fieldnames of the object's listeners structure - names=fieldnames(this.Listeners.(obj_name)); - for i=1:length(names) - %Deletes the listeners - delete(this.Listeners.(obj_name).(names{i})); - %Removes the field from the structure - this.Listeners.(obj_name)=... - rmfield(this.Listeners.(obj_name),names{i}); - end - %Removes the object's field from the structure - this.Listeners=rmfield(this.Listeners, obj_name); - end - end - end - - methods - - %% Set functions - function set.base_dir(this,base_dir) - if ~strcmp(base_dir(end),'\') - base_dir(end+1)='\'; - end - this.base_dir=base_dir; - end - - %% Get functions - - %Get function from save directory - function save_dir=get.save_dir(this) - save_dir=[this.base_dir,datestr(now,'yyyy-mm-dd '),... - this.session_name,'\']; - end - - %Get function for the plot handles - function main_plot=get.main_plot(this) - if this.enable_gui - main_plot=this.Gui.figure1.CurrentAxes; - else - main_plot=[]; - end - end - - %Get function for available instrument tags - function instr_tags=get.instr_tags(this) - instr_tags=fieldnames(this.InstrList); - end - - %Get function for open fits - function open_fits=get.open_fits(this) - open_fits=fieldnames(this.Fits); - end - - %Get function for open instrument tags - function open_instrs=get.open_instrs(this) - open_instrs=fieldnames(this.Instruments); - end - - %Get function for instrument names - function instr_names=get.instr_names(this) - %Cell of strings is output, so UniformOutput must be 0. - instr_names=cellfun(@(x) this.InstrList.(x).name, ... - this.instr_tags,'UniformOutput',0); - end - - %Generates appropriate file name for the save file. - function savefile=get.savefile(this) - if get(this.Gui.AutoName,'Value') - date_time = datestr(now,'yyyy-mm-dd_HHMMSS'); - else - date_time=''; - end - - savefile=[this.file_name,date_time]; - end - - %Get function that displays names of open cursors - function open_crs=get.open_crs(this) - open_crs=fieldnames(this.Cursors); - end - end +classdef MyDaq < handle + properties + %Contains GUI handles + Gui; + %Contains Reference trace (MyTrace object) + Ref=MyTrace(); + %Contains Data trace (MyTrace object) + Data=MyTrace(); + %Contains Background trace (MyTrace object) + Background=MyTrace(); + %Struct containing available instruments + InstrList=struct(); + %Struct containing MyInstrument objects + Instruments=struct() + %Struct containing apps for interfacing with instruments + InstrApps=struct(); + %Struct containing Cursor objects + Cursors=struct(); + %Struct containing Cursor labels + CrsLabels=struct(); + %Struct containing MyFit objects + Fits=struct(); + %Input parser + Parser; + %Struct for listeners + Listeners=struct(); + + %Sets the colors of fits, data and reference + fit_color='k'; + data_color='b'; + ref_color='r'; + bg_color='c'; + + %Properties for saving files + base_dir; + session_name; + file_name; + + %Flag for enabling the GUI + enable_gui; + end + + properties (Dependent=true) + save_dir; + main_plot; + open_fits; + open_instrs; + open_crs; + instr_tags; + instr_names; + savefile; + end + + methods (Access=public) + %% Class functions + %Constructor function + function this=MyDaq(varargin) + createParser(this); + parse(this.Parser,varargin{:}); + parseInputs(this); + if this.enable_gui + this.Gui=guihandles(eval('GuiDaq')); + initGui(this); + hold(this.main_plot,'on'); + end + initDaq(this) + end + + function delete(this) + %Deletes the MyFit objects and their listeners + cellfun(@(x) deleteListeners(this,x), this.open_fits); + structfun(@(x) delete(x), this.Fits); + + %Deletes the InstrApp objects and their listeners + cellfun(@(x) deleteListeners(this,x), fieldnames(this.InstrApps)); + structfun(@(x) delete(x), this.InstrApps); + + %Deletes the MyInstrument objects and their listeners + cellfun(@(x) deleteListeners(this,x), this.open_instrs); + structfun(@(x) delete(x), this.Instruments); + + if this.enable_gui + this.Gui.figure1.CloseRequestFcn=''; + %Deletes the figure + delete(this.Gui.figure1); + %Removes the figure handle to prevent memory leaks + this.Gui=[]; + end + end + end + + methods (Access=private) + %Creates parser for constructor function + function createParser(this) + p=inputParser; + addParameter(p,'enable_gui',1); + this.Parser=p; + end + + %Sets the class variables to the inputs from the inputParser. + function parseInputs(this) + for i=1:length(this.Parser.Parameters) + %Takes the value from the inputParser to the appropriate + %property. + if isprop(this,this.Parser.Parameters{i}) + this.(this.Parser.Parameters{i})=... + this.Parser.Results.(this.Parser.Parameters{i}); + end + end + end + + %Initializes the class depending on the computer name + function initDaq(this) + computer_name=getenv('computername'); + switch computer_name + case 'LPQM1PCLAB2' + initRt(this); + case 'LPQM1PC18' + initUhq(this); + case 'LPQM1PC2' + %Test case for testing on Nils' computer. + otherwise + warning('Please create an initialization function for this computer') + end + + %Initializes empty trace objects + this.Ref=MyTrace(); + this.Data=MyTrace(); + this.Background=MyTrace(); + end + + %Sets callback functions for the GUI + initGui(this) + + %Executes when the GUI is closed + function closeFigure(this,~,~) + delete(this); + end + + %Adds an instrument to InstrList. Used by initialization functions. + function addInstr(this,tag,name,type,interface,address) + %Usage: addInstr(this,tag,name,type,interface,address) + if ~ismember(tag,this.instr_tags) + this.InstrList.(tag).name=name; + this.InstrList.(tag).type=type; + this.InstrList.(tag).interface=interface; + this.InstrList.(tag).address=address; + else + error(['%s is already defined as an instrument. ',... + 'Please choose a different tag'],tag); + end + end + + %Gets the tag corresponding to an instrument name + function tag=getTag(this,instr_name) + ind=cellfun(@(x) strcmp(this.InstrList.(x).name,instr_name),... + this.instr_tags); + tag=this.instr_tags{ind}; + end + + %Opens the correct instrument + function openInstrument(this,tag) + instr_type=this.InstrList.(tag).type; + %Collects the correct inputs for creating the MyInstrument + %class + input_cell={this.InstrList.(tag).interface,... + this.InstrList.(tag).address}; + + switch instr_type + case 'RSA' + this.Instruments.(tag)=MyRsa(input_cell{:},... + 'gui','GuiRsa','name',this.InstrList.(tag).name); + this.InstrApps. + case 'DPO' + this.InstrApps.(tag)=GuiScope(input_cell{:},... + 'name',this.InstrList.(tag).name); + this.Instruments.(tag)=this.InstrApps.(tag).Instr; + case 'NA' + this.Instruments.(tag)=MyNa(input_cell{:},... + 'gui','GuiNa','name',this.InstrList.(tag).name); + end + + %Adds listeners for new data and deletion of the instrument. + %These call plot functions and delete functions respectively. + this.Listeners.(tag).NewData=... + addlistener(this.Instruments.(tag),'NewData',... + @(src, eventdata) acquireNewData(this, src, eventdata)); + this.Listeners.(tag).Deletion=... + addlistener(this.Instruments.(tag),'ObjectBeingDestroyed',... + @(src, eventdata) deleteInstrument(this, src, eventdata)); + end + + %Updates fits + function updateFits(this) + %Pushes data into fits in the form of MyTrace objects, so that + %units etc follow. Also updates user supplised parameters. + for i=1:length(this.open_fits) + switch this.open_fits{i} + case {'Linear','Quadratic','Gaussian',... + 'Exponential','Beta'} + this.Fits.(this.open_fits{i}).Data=... + getFitData(this,'VertData'); + case {'Lorentzian','DoubleLorentzian'} + this.Fits.(this.open_fits{i}).Data=... + getFitData(this,'VertData'); + if isfield(this.Cursors,'VertRef') + ind=findCursorData(this,'Data','VertRef'); + this.Fits.(this.open_fits{i}).CalVals.line_spacing=... + range(this.Data.x(ind)); + end + case {'G0'} + this.Fits.G0.MechTrace=getFitData(this,'VertData'); + this.Fits.G0.CalTrace=getFitData(this,'VertRef'); + end + end + end + + % If vertical cursors are on, takes only data + %within cursors. + %If the cursor is not open, it takes all the data from the selected + %trace in the analysis trace selection dropdown + function Trace=getFitData(this,varargin) + %Parses varargin input + p=inputParser; + addOptional(p,'name','',@ischar); + parse(p,varargin{:}) + name=p.Results.name; + + %Finds out which trace the user wants to fit. + trc_opts=this.Gui.SelTrace.String; + trc_str=trc_opts{this.Gui.SelTrace.Value}; + % Note the use of copy here! This is a handle + %class, so if normal assignment is used, this.Fits.Data and + %this.(trace_str) will refer to the same object, causing roblems. + %Name input is the name of the cursor to be used to extract data. + Trace=copy(this.(trc_str)); + if isfield(this.Cursors,name) + ind=findCursorData(this, trc_str, name); + Trace.x=this.(trc_str).x(ind); + Trace.y=this.(trc_str).y(ind); + end + end + + %Finds data between named cursors in the given trace + function ind=findCursorData(this, trc_str, name) + crs_pos=sort([this.Cursors.(name){1}.Location,... + this.Cursors.(name){2}.Location]); + ind=(this.(trc_str).x>crs_pos(1) & this.(trc_str).x + %Prints the figure to the clipboard + print(newFig,'-clipboard','-dbitmap'); + %Deletes the figure + delete(newFig); + end + + function updateAxis(this) + axis(this.main_plot,'tight'); + end + end + + methods (Access=public) + %% Callbacks + + %Callback for copying the plot to clipboard + function copyPlotCallback(this,~,~) + copyPlot(this); + end + + %Callback for centering cursors + function centerCursorsCallback(this, ~, ~) + if ~this.Gui.LogX.Value + x_pos=mean(this.main_plot.XLim); + else + x_pos=10^(mean(log10(this.main_plot.XLim))); + end + + if ~this.Gui.LogY.Value + y_pos=mean(this.main_plot.YLim); + else + y_pos=10^(mean(log10(this.main_plot.YLim))); + end + + for i=1:length(this.open_crs) + switch this.Cursors.(this.open_crs{i}){1}.Orientation + case 'horizontal' + pos=y_pos; + case 'vertical' + pos=x_pos; + end + + %Centers the position + cellfun(@(x) set(x,'Location',pos), ... + this.Cursors.(this.open_crs{i})); + %Triggers the UpdateCursorBar event, which triggers + %listener callback to reposition text + cellfun(@(x) notify(x,'UpdateCursorBar'),... + this.Cursors.(this.open_crs{i})); + cellfun(@(x) notify(x,'EndDrag'),... + this.Cursors.(this.open_crs{i})); + end + end + + %Callback for creating vertical data cursors + function cursorButtonCallback(this, hObject, ~) + name=erase(hObject.Tag,'Button'); + %Gets the first four characters of the tag (Vert or Horz) + type=name(1:4); + + if hObject.Value + hObject.BackgroundColor=[0,1,0.2]; + createCursors(this,name,type); + else + hObject.BackgroundColor=[0.941,0.941,0.941]; + deleteCursors(this,name); + end + end + + %Callback for the instrument menu + function instrMenuCallback(this,hObject,~) + val=hObject.Value; + names=hObject.String; + %Finds the correct instrument tag as long as an instrument is + %selected + if val~=1 + try + tag=getTag(this,names(val)); + catch + error('Invalid instrument selected: %s',names{val}); + end + end + + %If instrument is valid and not open, opens it. If it is valid + %and open it changes focus to the instrument control window. + if (ismember(tag,this.instr_tags) && ... + ~ismember(tag,this.open_instrs)) + openInstrument(this,tag); + elseif ismember(tag,this.open_instrs) + if isfield(this.InstrApps,tag) + prop_names=properties(this.InstrApps.(tag)); + figure_ind=cellfun(@(x) isa(this.InstrApps.(tag).(x),... + 'matlab.ui.Figure'),prop_names); + fig_handle=this.InstrApps.(tag).(prop_names{figure_ind}); + fig_handle.Visible='off'; + fig_handle.Visible='on'; + elseif isprop(this.Instruments.(tag),'Gui') + InstrGui=this.Instruments.(tag).Gui; + ind=structfun(@(x) isa(x,'matlab.ui.Figure'),InstrGui); + names=fieldnames(InstrGui); + figure(InstrGui.(names{ind})); + else + error(['Instrument %s does not have a GUI but is',... + ' registered as open'],tag) + end + + + end + end + + %Select trace callback + function selTraceCallback(this, ~, ~) + updateFits(this) + end + + %Saves the data if the save data button is pressed. + function saveDataCallback(this, ~, ~) + if this.Data.validatePlot + save(this.Data,'save_dir',this.save_dir,'name',... + this.savefile) + else + errdlg('Data trace was empty, could not save'); + end + end + + %Saves the reference if the save ref button is pressed. + function saveRefCallback(this, ~, ~) + if this.Data.validatePlot + save(this.Ref,'save_dir',this.save_dir,'name',... + this.savefile) + else + errdlg('Reference trace was empty, could not save') + end + end + + %Toggle button callback for showing the data trace. + function showDataCallback(this, hObject, ~) + if hObject.Value + hObject.BackgroundColor=[0,1,0.2]; + setVisible(this.Data,this.main_plot,1); + updateAxis(this); + else + hObject.BackgroundColor=[0.941,0.941,0.941]; + setVisible(this.Data,this.main_plot,0); + updateAxis(this); + end + end + + %Toggle button callback for showing the ref trace + function showRefCallback(this, hObject, ~) + if hObject.Value + hObject.BackgroundColor=[0,1,0.2]; + setVisible(this.Ref,this.main_plot,1); + updateAxis(this); + else + hObject.BackgroundColor=[0.941,0.941,0.941]; + setVisible(this.Ref,this.main_plot,0); + updateAxis(this); + end + end + + %Callback for moving the data to reference. + function dataToRefCallback(this, ~, ~) + if this.Data.validatePlot + this.Ref.x=this.Data.x; + this.Ref.y=this.Data.y; + this.Ref.plotTrace(this.main_plot,'Color',this.ref_color,... + 'make_labels',true); + this.Ref.setVisible(this.main_plot,1); + updateFits(this); + this.Gui.ShowRef.Value=1; + this.Gui.ShowRef.BackgroundColor=[0,1,0.2]; + else + warning('Data trace was empty, could not move to reference') + end + end + + %Callback for ref to bg button. Sends the reference to background + function refToBgCallback(this, ~, ~) + if this.Ref.validatePlot + this.Background.x=this.Ref.x; + this.Background.y=this.Ref.y; + this.Background.plotTrace(this.main_plot,... + 'Color',this.bg_color,'make_labels',true); + this.Background.setVisible(this.main_plot,1); + else + warning('Reference trace was empty, could not move to background') + end + end + + %Callback for data to bg button. Sends the data to background + function dataToBgCallback(this, ~, ~) + if this.Data.validatePlot + this.Background.x=this.Data.x; + this.Background.y=this.Data.y; + this.Background.plotTrace(this.main_plot,... + 'Color',this.bg_color,'make_labels',true); + this.Background.setVisible(this.main_plot,1); + else + warning('Data trace was empty, could not move to background') + end + end + + %Callback for clear background button. Clears the background + function clearBgCallback(this, ~, ~) + this.Background.x=[]; + this.Background.y=[]; + this.Background.setVisible(this.main_plot,0); + end + + %Callback for LogY button. Sets the YScale to log/lin + function logYCallback(this, hObject, ~) + if hObject.Value + this.main_plot.YScale='Log'; + hObject.BackgroundColor=[0,1,0.2]; + else + this.main_plot.YScale='Linear'; + hObject.BackgroundColor=[0.941,0.941,0.941]; + end + updateAxis(this); + updateCursors(this); + end + + %Callback for LogX button. Sets the XScale to log/lin. Updates the + %axis and cursors afterwards. + function logXCallback(this, hObject, ~) + if get(hObject,'Value') + set(this.main_plot,'XScale','Log'); + set(hObject, 'BackgroundColor',[0,1,0.2]); + else + set(this.main_plot,'XScale','Linear'); + set(hObject, 'BackgroundColor',[0.941,0.941,0.941]); + end + updateAxis(this); + updateCursors(this); + end + + %Base directory callback. Sets the base directory. Also + %updates fit objects with the new save directory. + function baseDirCallback(this, hObject, ~) + this.base_dir=hObject.String; + for i=1:length(this.open_fits) + this.Fits.(this.open_fits{i}).save_dir=this.save_dir; + end + end + + %Callback for session name edit box. Sets the session name. Also + %updates fit objects with the new save directory. + function sessionNameCallback(this, hObject, ~) + this.session_name=hObject.String; + for i=1:length(this.open_fits) + this.Fits.(this.open_fits{i}).save_dir=this.save_dir; + end + end + + %Callback for filename edit box. Sets the file name. Also + %updates fit objects with the new file name. + function fileNameCallback(this, hObject,~) + this.file_name=hObject.String; + for i=1:length(this.open_fits) + this.Fits.(this.open_fits{i}).save_name=this.file_name; + end + end + + %Callback for the analyze menu (popup menu for selecting fits). + %Opens the correct MyFit object. + function analyzeMenuCallback(this, hObject, ~) + analyze_ind=hObject.Value; + %Finds the correct fit name by erasing spaces and other + %superfluous strings + analyze_name=hObject.String{analyze_ind}; + analyze_name=erase(analyze_name,'Fit'); + analyze_name=erase(analyze_name,'Calibration'); + analyze_name=erase(analyze_name,' '); + + switch analyze_name + case {'Linear','Quadratic','Lorentzian','Gaussian',... + 'DoubleLorentzian'} + openMyFit(this,analyze_name); + case 'g0' + openMyG(this); + case 'Beta' + openMyBeta(this); + end + end + + function openMyFit(this,fit_name) + %Sees if the MyFit object is already open, if it is, changes the + %focus to it, if not, opens it. + if ismember(fit_name,fieldnames(this.Fits)) + %Changes focus to the relevant fit window + figure(this.Fits.(fit_name).Gui.Window); + else + %Gets the data for the fit using the getFitData function + %with the vertical cursors + DataTrace=getFitData(this,'VertData'); + %Makes an instance of MyFit with correct parameters. + this.Fits.(fit_name)=MyFit(... + 'fit_name',fit_name,... + 'enable_plot',1,... + 'plot_handle',this.main_plot,... + 'Data',DataTrace,... + 'save_dir',this.save_dir,... + 'save_name',this.file_name); + + updateFits(this); + %Sets up a listener for the Deletion event, which + %removes the MyFit object from the Fits structure if it is + %deleted. + this.Listeners.(fit_name).Deletion=... + addlistener(this.Fits.(fit_name),'ObjectBeingDestroyed',... + @(src, eventdata) deleteFit(this, src, eventdata)); + + %Sets up a listener for the NewFit. Callback plots the fit + %on the main plot. + this.Listeners.(fit_name).NewFit=... + addlistener(this.Fits.(fit_name),'NewFit',... + @(src, eventdata) plotNewFit(this, src, eventdata)); + + %Sets up a listener for NewInitVal + this.Listeners.(fit_name).NewInitVal=... + addlistener(this.Fits.(fit_name),'NewInitVal',... + @(~,~) updateCursors(this)); + end + end + + %Opens MyG class if it is not open. + function openMyG(this) + if ismember('G0',this.open_fits) + figure(this.Fits.G0.Gui.figure1); + else + MechTrace=getFitData(this,'VertData'); + CalTrace=getFitData(this,'VertRef'); + this.Fits.G0=MyG('MechTrace',MechTrace,'CalTrace',CalTrace,... + 'name','G0'); + + %Adds listener for object being destroyed + this.Listeners.G0.Deletion=addlistener(this.Fits.G0,... + 'ObjectBeingDestroyed',... + @(~,~) deleteObj(this,'G0')); + end + end + + %Opens MyBeta class if it is not open. + function openMyBeta(this) + if ismember('Beta', this.open_fits) + figure(this.Fits.Beta.Gui.figure1); + else + DataTrace=getFitData(this); + this.Fits.Beta=MyBeta('Data',DataTrace); + + %Adds listener for object being destroyed, to perform cleanup + this.Listeners.Beta.Deletion=addlistener(this.Fits.Beta,... + 'ObjectBeingDestroyed',... + @(~,~) deleteObj(this,'Beta')); + end + end + + %Callback for load data button + function loadDataCallback(this, ~, ~) + if isempty(this.base_dir) + warning('Please input a valid folder name for loading a trace'); + this.base_dir=pwd; + end + + try + [load_name,path_name]=uigetfile('.txt','Select the trace',... + this.base_dir); + load_path=[path_name,load_name]; + dest_trc=this.Gui.DestTrc.String{this.Gui.DestTrc.Value}; + loadTrace(this.(dest_trc),load_path); + this.(dest_trc).plotTrace(this.main_plot,... + 'Color',this.(sprintf('%s_color',lower(dest_trc))),... + 'make_labels',true); + updateAxis(this); + updateCursors(this); + catch + error('Please select a valid file'); + end + end + end + + methods (Access=public) + %% Listener functions + %Callback function for NewFit listener. Plots the fit in the + %window using the plotFit function of the MyFit object + function plotNewFit(this, src, ~) + src.plotFit('Color',this.fit_color); + updateAxis(this); + updateCursors(this); + end + + %Callback function for the NewData listener + function acquireNewData(this, src, ~) + hline=getLineHandle(this.Data,this.main_plot); + this.Data=copy(src.Trace); + if ~isempty(hline); this.Data.hlines{1}=hline; end + clearData(src.Trace); + this.Data.plotTrace(this.main_plot,'Color',this.data_color,... + 'make_labels',true) + updateAxis(this); + updateCursors(this); + updateFits(this); + end + + %Callback function for MyInstrument ObjectBeingDestroyed listener. + %Removes the relevant field from the Instruments struct and deletes + %the listeners from the object + function deleteInstrument(this, src, ~) + %Deletes the object from the Instruments struct + tag=getTag(this, src.name); + if ismember(tag, this.open_instrs) + this.Instruments=rmfield(this.Instruments,tag); + end + + %Deletes the listeners from the Listeners struct + deleteListeners(this, tag); + end + + %Callback function for MyFit ObjectBeingDestroyed listener. + %Removes the relevant field from the Fits struct and deletes the + %listeners from the object. + function deleteFit(this, src, ~) + %Deletes the object from the Fits struct and deletes listeners + deleteObj(this,src.fit_name); + + %Clears the fits + src.clearFit; + + %Updates cursors since the fits are removed from the plot + updateCursors(this); + end + + %Callback function for other analysis method deletion listeners. + %Does the same as above. + function deleteObj(this,name) + if ismember(name,this.open_fits) + this.Fits=rmfield(this.Fits,name); + end + deleteListeners(this, name); + end + + %Listener update function for vertical cursor + function vertCursorUpdate(this, src) + %Finds the index of the cursor. All cursors are tagged + %(name)1, (name)2, e.g. VertData2, ind is the number. + ind=str2double(src.Tag(end)); + tag=src.Tag(1:(end-1)); + %Moves the cursor labels + set(this.CrsLabels.(tag){ind},'Position',[src.Location,... + this.CrsLabels.(tag){ind}.Position(2),0]); + if strcmp(tag,'VertData') + %Sets the edit box displaying the location of the cursor + this.Gui.(sprintf('EditV%d',ind)).String=... + num2str(src.Location); + %Sets the edit box displaying the difference in locations + this.Gui.EditV2V1.String=... + num2str(this.Cursors.VertData{2}.Location-... + this.Cursors.VertData{1}.Location); + end + end + + %Listener update function for horizontal cursor + function horzCursorUpdate(this, src) + %Finds the index of the cursor. All cursors are tagged + %(name)1, (name)2, e.g. VertData2, ind is the number. + ind=str2double(src.Tag(end)); + tag=src.Tag(1:(end-1)); + %Moves the cursor labels + set(this.CrsLabels.(tag){ind},'Position',... + [this.CrsLabels.(tag){ind}.Position(1),... + src.Location,0]); + if strcmp(tag,'HorzData') + %Sets the edit box displaying the location of the cursor + this.Gui.(sprintf('EditH%d',ind)).String=... + num2str(src.Location); + %Sets the edit box displaying the difference in locations + this.Gui.EditH2H1.String=... + num2str(this.Cursors.HorzData{2}.Location-... + this.Cursors.HorzData{1}.Location); + end + end + + %Function that deletes listeners from the listeners struct, + %corresponding to an object of name obj_name + function deleteListeners(this, obj_name) + %Finds if the object has listeners in the listeners structure + if ismember(obj_name, fieldnames(this.Listeners)) + %Grabs the fieldnames of the object's listeners structure + names=fieldnames(this.Listeners.(obj_name)); + for i=1:length(names) + %Deletes the listeners + delete(this.Listeners.(obj_name).(names{i})); + %Removes the field from the structure + this.Listeners.(obj_name)=... + rmfield(this.Listeners.(obj_name),names{i}); + end + %Removes the object's field from the structure + this.Listeners=rmfield(this.Listeners, obj_name); + end + end + end + + methods + + %% Set functions + function set.base_dir(this,base_dir) + if ~strcmp(base_dir(end),'\') + base_dir(end+1)='\'; + end + this.base_dir=base_dir; + end + + %% Get functions + + %Get function from save directory + function save_dir=get.save_dir(this) + save_dir=[this.base_dir,datestr(now,'yyyy-mm-dd '),... + this.session_name,'\']; + end + + %Get function for the plot handles + function main_plot=get.main_plot(this) + if this.enable_gui + main_plot=this.Gui.figure1.CurrentAxes; + else + main_plot=[]; + end + end + + %Get function for available instrument tags + function instr_tags=get.instr_tags(this) + instr_tags=fieldnames(this.InstrList); + end + + %Get function for open fits + function open_fits=get.open_fits(this) + open_fits=fieldnames(this.Fits); + end + + %Get function for open instrument tags + function open_instrs=get.open_instrs(this) + open_instrs=fieldnames(this.Instruments); + end + + %Get function for instrument names + function instr_names=get.instr_names(this) + %Cell of strings is output, so UniformOutput must be 0. + instr_names=cellfun(@(x) this.InstrList.(x).name, ... + this.instr_tags,'UniformOutput',0); + end + + %Generates appropriate file name for the save file. + function savefile=get.savefile(this) + if get(this.Gui.AutoName,'Value') + date_time = datestr(now,'yyyy-mm-dd_HHMMSS'); + else + date_time=''; + end + + savefile=[this.file_name,date_time]; + end + + %Get function that displays names of open cursors + function open_crs=get.open_crs(this) + open_crs=fieldnames(this.Cursors); + end + end end \ No newline at end of file diff --git a/@MyDaq/MyDaq.m b/@MyDaq/MyDaq.m index bab7091..1a96410 100644 --- a/@MyDaq/MyDaq.m +++ b/@MyDaq/MyDaq.m @@ -1,990 +1,987 @@ classdef MyDaq < handle properties %Contains GUI handles Gui; %Contains Reference trace (MyTrace object) Ref=MyTrace(); %Contains Data trace (MyTrace object) Data=MyTrace(); %Contains Background trace (MyTrace object) Background=MyTrace(); %Struct containing available instruments InstrList=struct(); %Struct containing MyInstrument objects Instruments=struct() %Struct containing apps for interfacing with instruments InstrApps=struct(); %Struct containing Cursor objects Cursors=struct(); %Struct containing Cursor labels CrsLabels=struct(); %Struct containing MyFit objects Fits=struct(); %Input parser Parser; %Struct for listeners Listeners=struct(); %Sets the colors of fits, data and reference fit_color='k'; data_color='b'; ref_color='r'; bg_color='c'; %Properties for saving files base_dir; session_name; file_name; %Flag for enabling the GUI enable_gui; end properties (Dependent=true) save_dir; main_plot; open_fits; open_instrs; open_crs; instr_tags; instr_names; savefile; end methods (Access=public) %% Class functions %Constructor function function this=MyDaq(varargin) createParser(this); parse(this.Parser,varargin{:}); parseInputs(this); if this.enable_gui this.Gui=guihandles(eval('GuiDaq')); initGui(this); hold(this.main_plot,'on'); end initDaq(this) end function delete(this) %Deletes the MyFit objects and their listeners cellfun(@(x) deleteListeners(this,x), this.open_fits); structfun(@(x) delete(x), this.Fits); - %Deletes the InstrApp objects and their listeners cellfun(@(x) deleteListeners(this,x), fieldnames(this.InstrApps)); structfun(@(x) delete(x), this.InstrApps); %Deletes the MyInstrument objects and their listeners cellfun(@(x) deleteListeners(this,x), this.open_instrs); structfun(@(x) delete(x), this.Instruments); - - if this.enable_gui this.Gui.figure1.CloseRequestFcn=''; %Deletes the figure delete(this.Gui.figure1); %Removes the figure handle to prevent memory leaks this.Gui=[]; end end end methods (Access=private) %Creates parser for constructor function function createParser(this) p=inputParser; addParameter(p,'enable_gui',1); this.Parser=p; end %Sets the class variables to the inputs from the inputParser. function parseInputs(this) for i=1:length(this.Parser.Parameters) %Takes the value from the inputParser to the appropriate %property. if isprop(this,this.Parser.Parameters{i}) this.(this.Parser.Parameters{i})=... this.Parser.Results.(this.Parser.Parameters{i}); end end end %Initializes the class depending on the computer name function initDaq(this) computer_name=getenv('computername'); switch computer_name case 'LPQM1PCLAB2' initRt(this); case 'LPQM1PC18' initUhq(this); case 'LPQM1PC2' %Test case for testing on Nils' computer. otherwise warning('Please create an initialization function for this computer') end %Initializes empty trace objects - this.Ref=MyTrace; - this.Data=MyTrace; - this.Background=MyTrace; + this.Ref=MyTrace(); + this.Data=MyTrace(); + this.Background=MyTrace(); end %Sets callback functions for the GUI initGui(this) %Executes when the GUI is closed function closeFigure(this,~,~) delete(this); end %Adds an instrument to InstrList. Used by initialization functions. function addInstr(this,tag,name,type,interface,address) %Usage: addInstr(this,tag,name,type,interface,address) if ~ismember(tag,this.instr_tags) this.InstrList.(tag).name=name; this.InstrList.(tag).type=type; this.InstrList.(tag).interface=interface; this.InstrList.(tag).address=address; else error(['%s is already defined as an instrument. ',... 'Please choose a different tag'],tag); end end %Gets the tag corresponding to an instrument name function tag=getTag(this,instr_name) ind=cellfun(@(x) strcmp(this.InstrList.(x).name,instr_name),... this.instr_tags); tag=this.instr_tags{ind}; end %Opens the correct instrument function openInstrument(this,tag) instr_type=this.InstrList.(tag).type; %Collects the correct inputs for creating the MyInstrument %class input_cell={this.InstrList.(tag).interface,... this.InstrList.(tag).address}; switch instr_type case 'RSA' - this.Instruments.(tag)=MyRsa(input_cell{:},... - 'gui','GuiRsa','name',this.InstrList.(tag).name); - case 'Scope' - this.InstrApps.(tag)=GuiScope(input_cell{:},... + this.InstrApps.(tag)=GuiRsa(input_cell{:},'name',this.InstrList.(tag).name); + this.Instruments.(tag)=this.InstrApps.(tag).Instr; + case 'DPO' + this.InstrApps.(tag)=GuiDpo(input_cell{:},... 'name',this.InstrList.(tag).name); this.Instruments.(tag)=this.InstrApps.(tag).Instr; case 'NA' this.Instruments.(tag)=MyNa(input_cell{:},... 'gui','GuiNa','name',this.InstrList.(tag).name); end %Adds listeners for new data and deletion of the instrument. %These call plot functions and delete functions respectively. this.Listeners.(tag).NewData=... addlistener(this.Instruments.(tag),'NewData',... @(src, eventdata) acquireNewData(this, src, eventdata)); this.Listeners.(tag).Deletion=... addlistener(this.Instruments.(tag),'ObjectBeingDestroyed',... @(src, eventdata) deleteInstrument(this, src, eventdata)); end %Updates fits function updateFits(this) %Pushes data into fits in the form of MyTrace objects, so that %units etc follow. Also updates user supplised parameters. for i=1:length(this.open_fits) switch this.open_fits{i} case {'Linear','Quadratic','Gaussian',... 'Exponential','Beta'} this.Fits.(this.open_fits{i}).Data=... getFitData(this,'VertData'); case {'Lorentzian','DoubleLorentzian'} this.Fits.(this.open_fits{i}).Data=... getFitData(this,'VertData'); if isfield(this.Cursors,'VertRef') ind=findCursorData(this,'Data','VertRef'); this.Fits.(this.open_fits{i}).CalVals.line_spacing=... range(this.Data.x(ind)); end case {'G0'} this.Fits.G0.MechTrace=getFitData(this,'VertData'); this.Fits.G0.CalTrace=getFitData(this,'VertRef'); end end end % If vertical cursors are on, takes only data %within cursors. %If the cursor is not open, it takes all the data from the selected %trace in the analysis trace selection dropdown function Trace=getFitData(this,varargin) %Parses varargin input p=inputParser; addOptional(p,'name','',@ischar); parse(p,varargin{:}) name=p.Results.name; %Finds out which trace the user wants to fit. trc_opts=this.Gui.SelTrace.String; trc_str=trc_opts{this.Gui.SelTrace.Value}; % Note the use of copy here! This is a handle %class, so if normal assignment is used, this.Fits.Data and %this.(trace_str) will refer to the same object, causing roblems. %Name input is the name of the cursor to be used to extract data. Trace=copy(this.(trc_str)); if isfield(this.Cursors,name) ind=findCursorData(this, trc_str, name); Trace.x=this.(trc_str).x(ind); Trace.y=this.(trc_str).y(ind); end end %Finds data between named cursors in the given trace function ind=findCursorData(this, trc_str, name) crs_pos=sort([this.Cursors.(name){1}.Location,... this.Cursors.(name){2}.Location]); ind=(this.(trc_str).x>crs_pos(1) & this.(trc_str).x %Prints the figure to the clipboard print(newFig,'-clipboard','-dbitmap'); %Deletes the figure delete(newFig); end function updateAxis(this) axis(this.main_plot,'tight'); end end methods (Access=public) %% Callbacks %Callback for copying the plot to clipboard function copyPlotCallback(this,~,~) copyPlot(this); end %Callback for centering cursors function centerCursorsCallback(this, ~, ~) if ~this.Gui.LogX.Value x_pos=mean(this.main_plot.XLim); else x_pos=10^(mean(log10(this.main_plot.XLim))); end if ~this.Gui.LogY.Value y_pos=mean(this.main_plot.YLim); else y_pos=10^(mean(log10(this.main_plot.YLim))); end for i=1:length(this.open_crs) switch this.Cursors.(this.open_crs{i}){1}.Orientation case 'horizontal' pos=y_pos; case 'vertical' pos=x_pos; end %Centers the position cellfun(@(x) set(x,'Location',pos), ... this.Cursors.(this.open_crs{i})); %Triggers the UpdateCursorBar event, which triggers %listener callback to reposition text cellfun(@(x) notify(x,'UpdateCursorBar'),... this.Cursors.(this.open_crs{i})); cellfun(@(x) notify(x,'EndDrag'),... this.Cursors.(this.open_crs{i})); end end %Callback for creating vertical data cursors function cursorButtonCallback(this, hObject, ~) name=erase(hObject.Tag,'Button'); %Gets the first four characters of the tag (Vert or Horz) type=name(1:4); if hObject.Value hObject.BackgroundColor=[0,1,0.2]; createCursors(this,name,type); else hObject.BackgroundColor=[0.941,0.941,0.941]; deleteCursors(this,name); end end %Callback for the instrument menu function instrMenuCallback(this,hObject,~) val=hObject.Value; names=hObject.String; %Finds the correct instrument tag as long as an instrument is %selected if val~=1 try tag=getTag(this,names(val)); catch error('Invalid instrument selected: %s',names{val}); end end %If instrument is valid and not open, opens it. If it is valid %and open it changes focus to the instrument control window. if (ismember(tag,this.instr_tags) && ... ~ismember(tag,this.open_instrs)) openInstrument(this,tag); elseif ismember(tag,this.open_instrs) if isfield(this.InstrApps,tag) prop_names=properties(this.InstrApps.(tag)); figure_ind=cellfun(@(x) isa(this.InstrApps.(tag).(x),... 'matlab.ui.Figure'),prop_names); fig_handle=this.InstrApps.(tag).(prop_names{figure_ind}); fig_handle.Visible='off'; fig_handle.Visible='on'; elseif isprop(this.Instruments.(tag),'Gui') InstrGui=this.Instruments.(tag).Gui; ind=structfun(@(x) isa(x,'matlab.ui.Figure'),InstrGui); names=fieldnames(InstrGui); figure(InstrGui.(names{ind})); else error(['Instrument %s does not have a GUI but is',... ' registered as open'],tag) end end end %Select trace callback function selTraceCallback(this, ~, ~) updateFits(this) end %Saves the data if the save data button is pressed. function saveDataCallback(this, ~, ~) if this.Data.validatePlot save(this.Data,'save_dir',this.save_dir,'name',... this.savefile) else errdlg('Data trace was empty, could not save'); end end %Saves the reference if the save ref button is pressed. function saveRefCallback(this, ~, ~) if this.Data.validatePlot save(this.Ref,'save_dir',this.save_dir,'name',... this.savefile) else errdlg('Reference trace was empty, could not save') end end %Toggle button callback for showing the data trace. function showDataCallback(this, hObject, ~) if hObject.Value hObject.BackgroundColor=[0,1,0.2]; setVisible(this.Data,this.main_plot,1); updateAxis(this); else hObject.BackgroundColor=[0.941,0.941,0.941]; setVisible(this.Data,this.main_plot,0); updateAxis(this); end end %Toggle button callback for showing the ref trace function showRefCallback(this, hObject, ~) if hObject.Value hObject.BackgroundColor=[0,1,0.2]; setVisible(this.Ref,this.main_plot,1); updateAxis(this); else hObject.BackgroundColor=[0.941,0.941,0.941]; setVisible(this.Ref,this.main_plot,0); updateAxis(this); end end %Callback for moving the data to reference. function dataToRefCallback(this, ~, ~) if this.Data.validatePlot this.Ref.x=this.Data.x; this.Ref.y=this.Data.y; this.Ref.plotTrace(this.main_plot,'Color',this.ref_color,... 'make_labels',true); this.Ref.setVisible(this.main_plot,1); updateFits(this); this.Gui.ShowRef.Value=1; this.Gui.ShowRef.BackgroundColor=[0,1,0.2]; else warning('Data trace was empty, could not move to reference') end end %Callback for ref to bg button. Sends the reference to background function refToBgCallback(this, ~, ~) if this.Ref.validatePlot this.Background.x=this.Ref.x; this.Background.y=this.Ref.y; this.Background.plotTrace(this.main_plot,... 'Color',this.bg_color,'make_labels',true); this.Background.setVisible(this.main_plot,1); else warning('Reference trace was empty, could not move to background') end end %Callback for data to bg button. Sends the data to background function dataToBgCallback(this, ~, ~) if this.Data.validatePlot this.Background.x=this.Data.x; this.Background.y=this.Data.y; this.Background.plotTrace(this.main_plot,... 'Color',this.bg_color,'make_labels',true); this.Background.setVisible(this.main_plot,1); else warning('Data trace was empty, could not move to background') end end %Callback for clear background button. Clears the background function clearBgCallback(this, ~, ~) this.Background.x=[]; this.Background.y=[]; this.Background.setVisible(this.main_plot,0); end %Callback for LogY button. Sets the YScale to log/lin function logYCallback(this, hObject, ~) if hObject.Value this.main_plot.YScale='Log'; hObject.BackgroundColor=[0,1,0.2]; else this.main_plot.YScale='Linear'; hObject.BackgroundColor=[0.941,0.941,0.941]; end updateAxis(this); updateCursors(this); end %Callback for LogX button. Sets the XScale to log/lin. Updates the %axis and cursors afterwards. function logXCallback(this, hObject, ~) if get(hObject,'Value') set(this.main_plot,'XScale','Log'); set(hObject, 'BackgroundColor',[0,1,0.2]); else set(this.main_plot,'XScale','Linear'); set(hObject, 'BackgroundColor',[0.941,0.941,0.941]); end updateAxis(this); updateCursors(this); end %Base directory callback. Sets the base directory. Also %updates fit objects with the new save directory. function baseDirCallback(this, hObject, ~) this.base_dir=hObject.String; for i=1:length(this.open_fits) this.Fits.(this.open_fits{i}).save_dir=this.save_dir; end end %Callback for session name edit box. Sets the session name. Also %updates fit objects with the new save directory. function sessionNameCallback(this, hObject, ~) this.session_name=hObject.String; for i=1:length(this.open_fits) this.Fits.(this.open_fits{i}).save_dir=this.save_dir; end end %Callback for filename edit box. Sets the file name. Also %updates fit objects with the new file name. function fileNameCallback(this, hObject,~) this.file_name=hObject.String; for i=1:length(this.open_fits) this.Fits.(this.open_fits{i}).save_name=this.file_name; end end %Callback for the analyze menu (popup menu for selecting fits). %Opens the correct MyFit object. function analyzeMenuCallback(this, hObject, ~) analyze_ind=hObject.Value; %Finds the correct fit name by erasing spaces and other %superfluous strings analyze_name=hObject.String{analyze_ind}; analyze_name=erase(analyze_name,'Fit'); analyze_name=erase(analyze_name,'Calibration'); analyze_name=erase(analyze_name,' '); switch analyze_name case {'Linear','Quadratic','Lorentzian','Gaussian',... 'DoubleLorentzian'} openMyFit(this,analyze_name); case 'g0' openMyG(this); case 'Beta' openMyBeta(this); end end function openMyFit(this,fit_name) %Sees if the MyFit object is already open, if it is, changes the %focus to it, if not, opens it. if ismember(fit_name,fieldnames(this.Fits)) %Changes focus to the relevant fit window figure(this.Fits.(fit_name).Gui.Window); else %Gets the data for the fit using the getFitData function %with the vertical cursors DataTrace=getFitData(this,'VertData'); %Makes an instance of MyFit with correct parameters. this.Fits.(fit_name)=MyFit(... 'fit_name',fit_name,... 'enable_plot',1,... 'plot_handle',this.main_plot,... 'Data',DataTrace,... 'save_dir',this.save_dir,... 'save_name',this.file_name); updateFits(this); %Sets up a listener for the Deletion event, which %removes the MyFit object from the Fits structure if it is %deleted. this.Listeners.(fit_name).Deletion=... addlistener(this.Fits.(fit_name),'ObjectBeingDestroyed',... @(src, eventdata) deleteFit(this, src, eventdata)); %Sets up a listener for the NewFit. Callback plots the fit %on the main plot. this.Listeners.(fit_name).NewFit=... addlistener(this.Fits.(fit_name),'NewFit',... @(src, eventdata) plotNewFit(this, src, eventdata)); %Sets up a listener for NewInitVal this.Listeners.(fit_name).NewInitVal=... addlistener(this.Fits.(fit_name),'NewInitVal',... @(~,~) updateCursors(this)); end end %Opens MyG class if it is not open. function openMyG(this) if ismember('G0',this.open_fits) figure(this.Fits.G0.Gui.figure1); else MechTrace=getFitData(this,'VertData'); CalTrace=getFitData(this,'VertRef'); this.Fits.G0=MyG('MechTrace',MechTrace,'CalTrace',CalTrace,... 'name','G0'); %Adds listener for object being destroyed this.Listeners.G0.Deletion=addlistener(this.Fits.G0,... 'ObjectBeingDestroyed',... @(~,~) deleteObj(this,'G0')); end end %Opens MyBeta class if it is not open. function openMyBeta(this) if ismember('Beta', this.open_fits) figure(this.Fits.Beta.Gui.figure1); else DataTrace=getFitData(this); this.Fits.Beta=MyBeta('Data',DataTrace); %Adds listener for object being destroyed, to perform cleanup this.Listeners.Beta.Deletion=addlistener(this.Fits.Beta,... 'ObjectBeingDestroyed',... @(~,~) deleteObj(this,'Beta')); end end %Callback for load data button function loadDataCallback(this, ~, ~) if isempty(this.base_dir) warning('Please input a valid folder name for loading a trace'); this.base_dir=pwd; end try [load_name,path_name]=uigetfile('.txt','Select the trace',... this.base_dir); load_path=[path_name,load_name]; dest_trc=this.Gui.DestTrc.String{this.Gui.DestTrc.Value}; loadTrace(this.(dest_trc),load_path); this.(dest_trc).plotTrace(this.main_plot,... 'Color',this.(sprintf('%s_color',lower(dest_trc))),... 'make_labels',true); updateAxis(this); updateCursors(this); catch error('Please select a valid file'); end end end methods (Access=public) %% Listener functions %Callback function for NewFit listener. Plots the fit in the %window using the plotFit function of the MyFit object function plotNewFit(this, src, ~) src.plotFit('Color',this.fit_color); updateAxis(this); updateCursors(this); end %Callback function for the NewData listener function acquireNewData(this, src, ~) hline=getLineHandle(this.Data,this.main_plot); this.Data=copy(src.Trace); if ~isempty(hline); this.Data.hlines{1}=hline; end clearData(src.Trace); this.Data.plotTrace(this.main_plot,'Color',this.data_color,... 'make_labels',true) updateAxis(this); updateCursors(this); updateFits(this); end %Callback function for MyInstrument ObjectBeingDestroyed listener. %Removes the relevant field from the Instruments struct and deletes %the listeners from the object function deleteInstrument(this, src, ~) %Deletes the object from the Instruments struct tag=getTag(this, src.name); if ismember(tag, this.open_instrs) this.Instruments=rmfield(this.Instruments,tag); end %Deletes the listeners from the Listeners struct deleteListeners(this, tag); end %Callback function for MyFit ObjectBeingDestroyed listener. %Removes the relevant field from the Fits struct and deletes the %listeners from the object. function deleteFit(this, src, ~) %Deletes the object from the Fits struct and deletes listeners deleteObj(this,src.fit_name); %Clears the fits src.clearFit; %Updates cursors since the fits are removed from the plot updateCursors(this); end %Callback function for other analysis method deletion listeners. %Does the same as above. function deleteObj(this,name) if ismember(name,this.open_fits) this.Fits=rmfield(this.Fits,name); end deleteListeners(this, name); end %Listener update function for vertical cursor function vertCursorUpdate(this, src) %Finds the index of the cursor. All cursors are tagged %(name)1, (name)2, e.g. VertData2, ind is the number. ind=str2double(src.Tag(end)); tag=src.Tag(1:(end-1)); %Moves the cursor labels set(this.CrsLabels.(tag){ind},'Position',[src.Location,... this.CrsLabels.(tag){ind}.Position(2),0]); if strcmp(tag,'VertData') %Sets the edit box displaying the location of the cursor this.Gui.(sprintf('EditV%d',ind)).String=... num2str(src.Location); %Sets the edit box displaying the difference in locations this.Gui.EditV2V1.String=... num2str(this.Cursors.VertData{2}.Location-... this.Cursors.VertData{1}.Location); end end %Listener update function for horizontal cursor function horzCursorUpdate(this, src) %Finds the index of the cursor. All cursors are tagged %(name)1, (name)2, e.g. VertData2, ind is the number. ind=str2double(src.Tag(end)); tag=src.Tag(1:(end-1)); %Moves the cursor labels set(this.CrsLabels.(tag){ind},'Position',... [this.CrsLabels.(tag){ind}.Position(1),... src.Location,0]); if strcmp(tag,'HorzData') %Sets the edit box displaying the location of the cursor this.Gui.(sprintf('EditH%d',ind)).String=... num2str(src.Location); %Sets the edit box displaying the difference in locations this.Gui.EditH2H1.String=... num2str(this.Cursors.HorzData{2}.Location-... this.Cursors.HorzData{1}.Location); end end %Function that deletes listeners from the listeners struct, %corresponding to an object of name obj_name function deleteListeners(this, obj_name) %Finds if the object has listeners in the listeners structure if ismember(obj_name, fieldnames(this.Listeners)) %Grabs the fieldnames of the object's listeners structure names=fieldnames(this.Listeners.(obj_name)); for i=1:length(names) %Deletes the listeners delete(this.Listeners.(obj_name).(names{i})); %Removes the field from the structure this.Listeners.(obj_name)=... rmfield(this.Listeners.(obj_name),names{i}); end %Removes the object's field from the structure this.Listeners=rmfield(this.Listeners, obj_name); end end end methods %% Set functions function set.base_dir(this,base_dir) if ~strcmp(base_dir(end),'\') base_dir(end+1)='\'; end this.base_dir=base_dir; end %% Get functions %Get function from save directory function save_dir=get.save_dir(this) save_dir=[this.base_dir,datestr(now,'yyyy-mm-dd '),... this.session_name,'\']; end %Get function for the plot handles function main_plot=get.main_plot(this) if this.enable_gui main_plot=this.Gui.figure1.CurrentAxes; else main_plot=[]; end end %Get function for available instrument tags function instr_tags=get.instr_tags(this) instr_tags=fieldnames(this.InstrList); end %Get function for open fits function open_fits=get.open_fits(this) open_fits=fieldnames(this.Fits); end %Get function for open instrument tags function open_instrs=get.open_instrs(this) open_instrs=fieldnames(this.Instruments); end %Get function for instrument names function instr_names=get.instr_names(this) %Cell of strings is output, so UniformOutput must be 0. instr_names=cellfun(@(x) this.InstrList.(x).name, ... this.instr_tags,'UniformOutput',0); end %Generates appropriate file name for the save file. function savefile=get.savefile(this) if get(this.Gui.AutoName,'Value') date_time = datestr(now,'yyyy-mm-dd_HHMMSS'); else date_time=''; end savefile=[this.file_name,date_time]; end %Get function that displays names of open cursors function open_crs=get.open_crs(this) open_crs=fieldnames(this.Cursors); end end end \ No newline at end of file diff --git a/@MyDaq/initRt.m b/@MyDaq/initRt.m index 628d67a..82d0ca0 100644 --- a/@MyDaq/initRt.m +++ b/@MyDaq/initRt.m @@ -1,16 +1,16 @@ function initRt(this) this.base_dir='M:\Measurement Campaigns\'; %addInstr(this, tag, name, type, interface, address) -addInstr(this,'RtOsc1','RT Oscilloscope 1 (Tektronix DPO 4034)',... - 'Scope','USB','0x0699::0x0413::C013397'); +addInstr(this,'RtOsc1','RT Oscilloscope 1 (Tektronix DPO 3034)',... + 'DPO','USB','0x0699::0x0413::C013397'); addInstr(this,'Rsa5103','RSA 5103','RSA','TCPIP','192.168.1.3'); addInstr(this,'Rsa5106','RSA 5106','RSA','TCPIP','192.168.1.5'); addInstr(this,'AgNa','Agilent NA','NA','TCPIP','192.168.1.4'); if this.enable_gui set(this.Gui.InstrMenu,'String',[{'Select the device'};... this.instr_names]); set(this.Gui.BaseDir,'String',this.base_dir); end end \ No newline at end of file diff --git a/@MyInstrument/MyInstrument.asv b/@MyInstrument/MyInstrument.asv index 8abc51d..8f70233 100644 --- a/@MyInstrument/MyInstrument.asv +++ b/@MyInstrument/MyInstrument.asv @@ -1,436 +1,457 @@ classdef MyInstrument < dynamicprops properties (SetAccess=protected, GetAccess=public) name=''; interface=''; address=''; %Contains the device object. struct() is a dummy, as Device %needs to always support properties for consistency. Device=struct(); %Input parser for class constructor Parser; %Contains a list of the commands available for the instrument as %well as the default values and input requirements CommandList=struct(); %Parses commands using an inputParser object CommandParser; %Trace object for storing data Trace=MyTrace(); end properties (Constant=true) % Default parameters for VISA connection DEFAULT_INP_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_OUT_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_TIMEOUT = 10; % Timeout in s DEFAULT_VISA_BRAND = 'ni'; end properties (Dependent=true) command_names; command_no; write_commands; read_commands; end events NewData; end methods (Access=private) function createParser(this) p=inputParser(); addRequired(p,'interface',@ischar); addRequired(p,'address',@ischar); addParameter(p,'name','',@ischar); addParameter(p,'visa_brand',this.DEFAULT_VISA_BRAND,@ischar); this.Parser=p; end end methods (Access=public) function this=MyInstrument(interface, address, varargin) createParser(this); parse(this.Parser,interface,address,varargin{:}); %Loads parsed variables into class properties this.name=this.Parser.Results.name; this.interface=this.Parser.Results.interface; this.address=this.Parser.Results.address; end function delete(this) %Closes the connection to the device closeDevice(this); %Deletes the device object delete(this.Device); clear('this.Device'); - end - - %Clears data from trace to save memory. - function clearData(this) - this.Trace.x=[]; - this.Trace.y=[]; - end + end + %% Read and write commands %Writes properties to device. Can take multiple inputs. With the %option all, the function writes default to all the %available writeable parameters. function writeProperty(this, varargin) %Parses the inputs using the CommandParser parse(this.CommandParser, varargin{:}); if this.CommandParser.Results.all % If the 'all' is true, write all the commands exec=this.write_commands; else % Check which commands were passed values ind_val=cellfun(@(x)... (~ismember(x, this.CommandParser.UsingDefaults)),... this.write_commands); exec=this.write_commands(ind_val); end for i=1:length(exec) %Creates the write command using the right string spec write_command=[this.CommandList.(exec{i}).command,... ' ',this.CommandList.(exec{i}).str_spec]; %Gets the value to write to the device this.(exec{i})=this.CommandParser.Results.(exec{i}); command=sprintf(write_command, this.(exec{i})); %Sends command to device fprintf(this.Device, command); end end % Wrapper for writeProperty that opens and closes the device function writePropertyHedged(this, varargin) openDevice(this); try writeProperty(this, varargin{:}); catch warning('Error while writing the properties:'); disp(varargin); end readProperty(this, 'all'); closeDevice(this); end function result=readProperty(this, varargin) result = struct(); - read_all_flag = any(strcmp('all',varargin)); - + read_all_flag = any(strcmp('all',varargin)); if read_all_flag % Read all the commands with read access exec=this.read_commands; else ind_r=ismember(varargin,this.read_commands); exec=varargin(ind_r); if any(~ind_r) % Issue warnings for commands not in the command_names warning('The following are not valid read commands:'); disp(varargin(~ind_r)); end end - % + % concatenate all commands in one string read_command=join(cellfun(... @(cmd)this.CommandList.(cmd).command,exec,... 'UniformOutput',false),'?;:'); read_command=[read_command{1},'?;']; res_str = query(this.Device,read_command); % drop the end-of-the-string symbol and split res_str = split(res_str(1:end-1),';'); if length(exec)==length(res_str) for i=1:length(exec) result.(exec{i})=sscanf(res_str{i},... this.CommandList.(exec{i}).str_spec); %Assign the values to the MyInstrument properties this.(exec{i})=result.(exec{i}); end else warning(['Not all the properties could be read, ',... 'no instrument class values are not updated']); end end % Wrapper for readProperty that opens and closes the device function result=readPropertyHedged(this, varargin) openDevice(this); try result = readProperty(this, varargin{:}); catch warning('Error while reading the properties:'); disp(varargin); end closeDevice(this); end + %Triggers event for acquired data + function triggerNewData(this) + notify(this,'NewData') + end + + %% Processing of the class variable values % Extend the property value based on val_list function std_val = standardizeValue(this, cmd, varargin) if ~ismember(cmd,this.command_names) warning('%s is not a valid command',cmd); std_val = ''; return end vlist = this.CommandList.(cmd).val_list; % The value to normalize can be explicitly passed as % varargin{1}, otherwise use the property value if isempty(varargin) val = this.(cmd); else val = varargin{1}; end % find matching commands ismatch = false(1,length(vlist)); for i=1:length(vlist) n = min([length(val), length(vlist{i})]); % compare first n symbols disregarding case ismatch(i) = strncmpi(val, vlist{i},n); end % out of matching names pick the longest if any(ismatch) mvlist = vlist(ismatch); str = mvlist{1}; for i=1:length(mvlist) if length(mvlist{i})>length(str) str = mvlist{i}; end end std_val = str; % set the property if value was not given explicitly if isempty(varargin) this.(cmd) = std_val; end else std_val = val; end end + % Create a string of property values + function par_str = getParametersString(this) + % Try to find out the device name + if ~isempty(this.name) + name_str = this.name; + else + try + openDevice(this) + close + catch + end + end + par_str = sprintf('Instrument name: %s',this.name); + + rcmds=this.read_commands; + for i=1:length(rcmds) + new_str = sprintf(['\t',rcmds{i},'\t',... + this.CommandList.(rcmds{i}).str_spec,'\n'],... + this.(rcmds{i})); + par_str = [par_str, new_str]; + end + end + + %% Connect, open, configure and close the device % Connects to the device function connectDevice(this, interface, address) try % visa brand, DEFAULT_VISA_BRAND if not specified vb = this.Parser.Results.visa_brand; switch lower(interface) case 'constructor' % in this case the 'address' is a command % (ObjectConstructorName) as returned by the % instrhwinfo this.Device=eval(address); case 'visa' this.Device=visa(vb, address); case 'tcpip' this.Device= visa(vb, sprintf(... 'TCPIP0::%s::inst0::INSTR',this.address)); case 'usb' this.Device=visa(vb, sprintf(... 'USB0::%s::INSTR',address)); otherwise warning('Device is not connected: unknown interface'); end configureDeviceDefault(this); catch warning('Device is not connected'); end end % Opens the device if it is not open function openDevice(this) if ~isopen(this) try fopen(this.Device); catch % try to find and close all the devices with the same % VISA resource name try instr_list=instrfind('RsrcName',this.Device.RsrcName); fclose(instr_list); fopen(this.Device); warning('Multiple instrument objects of address %s exist',... this.address); catch error('Could not open device') end end end end % Closes the connection to the device function closeDevice(this) if isopen(this) fclose(this.Device); end end function configureDeviceDefault(this) dev_prop_list = properties(this.Device); if ismember('OutputBufferSize',dev_prop_list) this.Device.OutputBufferSize = this.DEFAULT_OUT_BUFF_SIZE; end if ismember('InputBufferSize',dev_prop_list) this.Device.InputBufferSize = this.DEFAULT_INP_BUFF_SIZE; end if ismember('Timeout',dev_prop_list) this.Device.Timeout = this.DEFAULT_TIMEOUT; end end - %Triggers event for acquired data - function triggerNewData(this) - notify(this,'NewData') - end - %Checks if the connection to the device is open function bool=isopen(this) try bool=strcmp(this.Device.Status, 'open'); catch warning('Cannot verify device Status property'); bool=false; end end - + + %% addCommand %Adds a command to the CommandList function addCommand(this, tag, command, varargin) p=inputParser(); addRequired(p,'tag',@ischar); addRequired(p,'command',@ischar); addParameter(p,'default','placeholder'); addParameter(p,'classes',{},@iscell); addParameter(p,'attributes',{},@iscell); addParameter(p,'str_spec','%e',@ischar); % list of the values the variable can take, {} means no % restriction addParameter(p,'val_list',{},@iscell); addParameter(p,'access','rw',@ischar); parse(p,tag,command,varargin{:}); %Adds the command to be sent to the device this.CommandList.(tag).command=command; this.CommandList.(tag).access=p.Results.access; this.CommandList.(tag).write_flag=contains(p.Results.access,'w'); this.CommandList.(tag).read_flag=contains(p.Results.access,'r'); this.CommandList.(tag).default=p.Results.default; this.CommandList.(tag).val_list=p.Results.val_list; % Adds the string specifier to the list. if the format % specifier is not given explicitly, try to infer if ismember('str_spec', p.UsingDefaults) this.CommandList.(tag).str_spec=... formatSpecFromAttributes(this,p.Results.classes... ,p.Results.attributes); elseif strcmp(p.Results.str_spec,'%b') % b is a non-system specifier to represent the % logical type this.CommandList.(tag).str_spec='%i'; else this.CommandList.(tag).str_spec=p.Results.str_spec; end % Adds the attributes for the input to the command. If not % given explicitly, infer from the format specifier if ismember('classes',p.UsingDefaults) [this.CommandList.(tag).classes,... this.CommandList.(tag).attributes]=... AttributesFromFormatSpec(this, p.Results.str_spec); else this.CommandList.(tag).classes=p.Results.classes; this.CommandList.(tag).attributes=p.Results.attributes; end % Adds a property to the class corresponding to the tag if ~isprop(this,tag) addprop(this,tag); end this.(tag)=p.Results.default; end %Creates inputParser using the command list function createCommandParser(this) %Use input parser %Requires input of the appropriate class p=inputParser; p.StructExpand=0; %Flag for whether the command should initialize the device with %defaults addParameter(p, 'all',false,@islogical); for i=1:length(this.write_commands) %Adds optional inputs for each command, with the %appropriate default value from the command list and the %required attributes for the command input. tag=this.write_commands{i}; % Create validation function based on properties: % class, attributes and list of values if ~isempty(this.CommandList.(tag).val_list) v_func = @(x) any(cellfun(@(y) isequal(y, x),... this.CommandList.(tag).val_list)); else v_func = @(x) validateattributes(x,... this.CommandList.(tag).classes,... this.CommandList.(tag).attributes); end addParameter(p, tag,... this.CommandList.(tag).default, v_func); end this.CommandParser=p; end + %% Auxiliary functions for auto format assignment to commands function str_spec=formatSpecFromAttributes(~,classes,attributes) if ismember('char',classes) str_spec='%s'; elseif ismember('logical',classes)||... (ismember('numeric',classes)&&... ismember('integer',attributes)) str_spec='%i'; else %assign default value, i.e. double str_spec='%e'; end end function [class,attribute]=AttributesFromFormatSpec(~, str_spec) % find index of the first letter after the % sign ind_p=strfind(str_spec,'%'); ind=ind_p+find(isletter(str_spec(ind_p:end)),1)-1; str_spec_letter=str_spec(ind); switch str_spec_letter case {'d','f','e','g'} class={'numeric'}; attribute={}; case 'i' class={'numeric'}; attribute={'integer'}; case 's' class={'char'}; attribute={}; case 'b' class={'logical'}; attribute={}; otherwise % Any of the above classes will pass class={'numeric','char','logical'}; attribute={}; end end end - % Get functions + %% Get functions methods function command_names=get.command_names(this) command_names=fieldnames(this.CommandList); end function write_commands=get.write_commands(this) ind_w=structfun(@(x) x.write_flag, this.CommandList); write_commands=this.command_names(ind_w); end function read_commands=get.read_commands(this) ind_r=structfun(@(x) x.read_flag, this.CommandList); read_commands=this.command_names(ind_r); end function command_no=get.command_no(this) command_no=length(this.command_names); end end end \ No newline at end of file diff --git a/@MyInstrument/MyInstrument.m b/@MyInstrument/MyInstrument.m index a3ec1d3..2700bd3 100644 --- a/@MyInstrument/MyInstrument.m +++ b/@MyInstrument/MyInstrument.m @@ -1,429 +1,462 @@ classdef MyInstrument < dynamicprops properties (SetAccess=protected, GetAccess=public) name=''; interface=''; address=''; %Contains the device object. struct() is a dummy, as Device %needs to always support properties for consistency. Device=struct(); %Input parser for class constructor Parser; %Contains a list of the commands available for the instrument as %well as the default values and input requirements CommandList=struct(); %Parses commands using an inputParser object CommandParser; %Trace object for storing data Trace=MyTrace(); end properties (Constant=true) % Default parameters for VISA connection DEFAULT_INP_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_OUT_BUFF_SIZE = 1e7; % buffer size bytes DEFAULT_TIMEOUT = 10; % Timeout in s DEFAULT_VISA_BRAND = 'ni'; end properties (Dependent=true) command_names; command_no; write_commands; read_commands; end events NewData; end methods (Access=private) function createParser(this) p=inputParser(); addRequired(p,'interface',@ischar); addRequired(p,'address',@ischar); addParameter(p,'name','',@ischar); addParameter(p,'visa_brand',this.DEFAULT_VISA_BRAND,@ischar); this.Parser=p; end end methods (Access=public) function this=MyInstrument(interface, address, varargin) createParser(this); parse(this.Parser,interface,address,varargin{:}); %Loads parsed variables into class properties this.name=this.Parser.Results.name; this.interface=this.Parser.Results.interface; this.address=this.Parser.Results.address; end function delete(this) %Closes the connection to the device closeDevice(this); %Deletes the device object delete(this.Device); clear('this.Device'); end + %% Read and write commands %Writes properties to device. Can take multiple inputs. With the %option all, the function writes default to all the %available writeable parameters. function writeProperty(this, varargin) %Parses the inputs using the CommandParser parse(this.CommandParser, varargin{:}); if this.CommandParser.Results.all % If the 'all' is true, write all the commands exec=this.write_commands; else % Check which commands were passed values ind_val=cellfun(@(x)... (~ismember(x, this.CommandParser.UsingDefaults)),... this.write_commands); exec=this.write_commands(ind_val); end for i=1:length(exec) %Creates the write command using the right string spec write_command=[this.CommandList.(exec{i}).command,... ' ',this.CommandList.(exec{i}).str_spec]; %Gets the value to write to the device this.(exec{i})=this.CommandParser.Results.(exec{i}); command=sprintf(write_command, this.(exec{i})); %Sends command to device fprintf(this.Device, command); end end % Wrapper for writeProperty that opens and closes the device function writePropertyHedged(this, varargin) openDevice(this); try writeProperty(this, varargin{:}); catch warning('Error while writing the properties:'); disp(varargin); end readProperty(this, 'all'); closeDevice(this); end function result=readProperty(this, varargin) result = struct(); read_all_flag = any(strcmp('all',varargin)); if read_all_flag % Read all the commands with read access exec=this.read_commands; else ind_r=ismember(varargin,this.read_commands); exec=varargin(ind_r); if any(~ind_r) % Issue warnings for commands not in the command_names warning('The following are not valid read commands:'); disp(varargin(~ind_r)); end end % concatenate all commands in one string read_command=join(cellfun(... @(cmd)this.CommandList.(cmd).command,exec,... 'UniformOutput',false),'?;:'); read_command=[read_command{1},'?;']; res_str = query(this.Device,read_command); % drop the end-of-the-string symbol and split res_str = split(res_str(1:end-1),';'); if length(exec)==length(res_str) for i=1:length(exec) result.(exec{i})=sscanf(res_str{i},... this.CommandList.(exec{i}).str_spec); %Assign the values to the MyInstrument properties this.(exec{i})=result.(exec{i}); end else warning(['Not all the properties could be read, ',... 'no instrument class values are not updated']); end end % Wrapper for readProperty that opens and closes the device function result=readPropertyHedged(this, varargin) openDevice(this); try result = readProperty(this, varargin{:}); catch warning('Error while reading the properties:'); disp(varargin); end closeDevice(this); end + %Triggers event for acquired data + function triggerNewData(this) + notify(this,'NewData') + end + + %% Processing of the class variable values % Extend the property value based on val_list function std_val = standardizeValue(this, cmd, varargin) if ~ismember(cmd,this.command_names) warning('%s is not a valid command',cmd); std_val = ''; return end vlist = this.CommandList.(cmd).val_list; % The value to normalize can be explicitly passed as % varargin{1}, otherwise use the property value if isempty(varargin) val = this.(cmd); else val = varargin{1}; end % find matching commands ismatch = false(1,length(vlist)); for i=1:length(vlist) n = min([length(val), length(vlist{i})]); % compare first n symbols disregarding case ismatch(i) = strncmpi(val, vlist{i},n); end % out of matching names pick the longest if any(ismatch) mvlist = vlist(ismatch); str = mvlist{1}; for i=1:length(mvlist) if length(mvlist{i})>length(str) str = mvlist{i}; end end std_val = str; % set the property if value was not given explicitly if isempty(varargin) this.(cmd) = std_val; end else std_val = val; end end + % Create a string of property values + function par_str = getConfigString(this) + % Try to find out the device name + if ~isempty(this.name) + name_str = this.name; + else + try + openDevice(this); + name_str = query(this.Device,'*IDN?'); + closeDevice(this); + % Remove the new line end symbol + name_str=name_str(1:end-1); + catch + warning('Could not get the device name'); + name_str = ''; + end + end + par_str = sprintf('Instrument name: %s\n',name_str); + % Append the values of all the commands + rcmds=this.read_commands; + for i=1:length(rcmds) + new_str = sprintf(['\t',rcmds{i},'\t',... + this.CommandList.(rcmds{i}).str_spec,'\n'],... + this.(rcmds{i})); + par_str = [par_str, new_str]; + end + end + + %% Connect, open, configure and close the device % Connects to the device function connectDevice(this, interface, address) try % visa brand, DEFAULT_VISA_BRAND if not specified vb = this.Parser.Results.visa_brand; switch lower(interface) case 'constructor' % in this case the 'address' is a command % (ObjectConstructorName) as returned by the % instrhwinfo this.Device=eval(address); case 'visa' this.Device=visa(vb, address); case 'tcpip' this.Device= visa(vb, sprintf(... 'TCPIP0::%s::inst0::INSTR',this.address)); case 'usb' this.Device=visa(vb, sprintf(... 'USB0::%s::INSTR',address)); otherwise warning('Device is not connected: unknown interface'); end configureDeviceDefault(this); catch warning('Device is not connected'); end end % Opens the device if it is not open function openDevice(this) if ~isopen(this) try fopen(this.Device); catch % try to find and close all the devices with the same % VISA resource name try instr_list=instrfind('RsrcName',this.Device.RsrcName); fclose(instr_list); fopen(this.Device); warning('Multiple instrument objects of address %s exist',... this.address); catch error('Could not open device') end end end end % Closes the connection to the device function closeDevice(this) if isopen(this) fclose(this.Device); end end function configureDeviceDefault(this) dev_prop_list = properties(this.Device); if ismember('OutputBufferSize',dev_prop_list) this.Device.OutputBufferSize = this.DEFAULT_OUT_BUFF_SIZE; end if ismember('InputBufferSize',dev_prop_list) this.Device.InputBufferSize = this.DEFAULT_INP_BUFF_SIZE; end if ismember('Timeout',dev_prop_list) this.Device.Timeout = this.DEFAULT_TIMEOUT; end end - %Triggers event for acquired data - function triggerNewData(this) - notify(this,'NewData') - end - %Checks if the connection to the device is open function bool=isopen(this) try bool=strcmp(this.Device.Status, 'open'); catch warning('Cannot verify device Status property'); bool=false; end end - + + %% addCommand %Adds a command to the CommandList function addCommand(this, tag, command, varargin) p=inputParser(); addRequired(p,'tag',@ischar); addRequired(p,'command',@ischar); addParameter(p,'default','placeholder'); addParameter(p,'classes',{},@iscell); addParameter(p,'attributes',{},@iscell); addParameter(p,'str_spec','%e',@ischar); % list of the values the variable can take, {} means no % restriction addParameter(p,'val_list',{},@iscell); addParameter(p,'access','rw',@ischar); parse(p,tag,command,varargin{:}); %Adds the command to be sent to the device this.CommandList.(tag).command=command; this.CommandList.(tag).access=p.Results.access; this.CommandList.(tag).write_flag=contains(p.Results.access,'w'); this.CommandList.(tag).read_flag=contains(p.Results.access,'r'); this.CommandList.(tag).default=p.Results.default; this.CommandList.(tag).val_list=p.Results.val_list; % Adds the string specifier to the list. if the format % specifier is not given explicitly, try to infer if ismember('str_spec', p.UsingDefaults) this.CommandList.(tag).str_spec=... formatSpecFromAttributes(this,p.Results.classes... ,p.Results.attributes); elseif strcmp(p.Results.str_spec,'%b') % b is a non-system specifier to represent the % logical type this.CommandList.(tag).str_spec='%i'; else this.CommandList.(tag).str_spec=p.Results.str_spec; end % Adds the attributes for the input to the command. If not % given explicitly, infer from the format specifier if ismember('classes',p.UsingDefaults) [this.CommandList.(tag).classes,... this.CommandList.(tag).attributes]=... AttributesFromFormatSpec(this, p.Results.str_spec); else this.CommandList.(tag).classes=p.Results.classes; this.CommandList.(tag).attributes=p.Results.attributes; end % Adds a property to the class corresponding to the tag if ~isprop(this,tag) addprop(this,tag); end this.(tag)=p.Results.default; end %Creates inputParser using the command list function createCommandParser(this) %Use input parser %Requires input of the appropriate class p=inputParser; p.StructExpand=0; %Flag for whether the command should initialize the device with %defaults addParameter(p, 'all',false,@islogical); for i=1:length(this.write_commands) %Adds optional inputs for each command, with the %appropriate default value from the command list and the %required attributes for the command input. tag=this.write_commands{i}; % Create validation function based on properties: % class, attributes and list of values if ~isempty(this.CommandList.(tag).val_list) v_func = @(x) any(cellfun(@(y) isequal(y, x),... this.CommandList.(tag).val_list)); else v_func = @(x) validateattributes(x,... this.CommandList.(tag).classes,... this.CommandList.(tag).attributes); end addParameter(p, tag,... this.CommandList.(tag).default, v_func); end this.CommandParser=p; end + %% Auxiliary functions for auto format assignment to commands function str_spec=formatSpecFromAttributes(~,classes,attributes) if ismember('char',classes) str_spec='%s'; elseif ismember('logical',classes)||... (ismember('numeric',classes)&&... ismember('integer',attributes)) str_spec='%i'; else %assign default value, i.e. double str_spec='%e'; end end function [class,attribute]=AttributesFromFormatSpec(~, str_spec) % find index of the first letter after the % sign ind_p=strfind(str_spec,'%'); ind=ind_p+find(isletter(str_spec(ind_p:end)),1)-1; str_spec_letter=str_spec(ind); switch str_spec_letter case {'d','f','e','g'} class={'numeric'}; attribute={}; case 'i' class={'numeric'}; attribute={'integer'}; case 's' class={'char'}; attribute={}; case 'b' class={'logical'}; attribute={}; otherwise % Any of the above classes will pass class={'numeric','char','logical'}; attribute={}; end end end - % Get functions + %% Get functions methods function command_names=get.command_names(this) command_names=fieldnames(this.CommandList); end function write_commands=get.write_commands(this) ind_w=structfun(@(x) x.write_flag, this.CommandList); write_commands=this.command_names(ind_w); end function read_commands=get.read_commands(this) ind_r=structfun(@(x) x.read_flag, this.CommandList); read_commands=this.command_names(ind_r); end function command_no=get.command_no(this) command_no=length(this.command_names); end end end \ No newline at end of file diff --git a/@MyRsa/MyRsa.m b/@MyRsa/MyRsa.m index 63da9a8..f27a13e 100644 --- a/@MyRsa/MyRsa.m +++ b/@MyRsa/MyRsa.m @@ -1,106 +1,106 @@ classdef MyRsa < MyInstrument %% Constructor and destructor methods (Access=public) function this=MyRsa(interface, address,varargin) this@MyInstrument(interface, address, varargin{:}); createCommandList(this); createCommandParser(this); connectDevice(this, interface, address); this.Trace.unit_x='Hz'; this.Trace.unit_y='$\mathrm{V}^2/\mathrm{Hz}$'; this.Trace.name_y='Power'; this.Trace.name_x='Frequency'; end end %% Private functions methods (Access=private) function createCommandList(this) % Resolution bandwidth (Hz) addCommand(this, 'rbw','DPSA:BAND:RES',... 'default',1e3,'str_spec','%e'); addCommand(this, 'span', 'DPSA:FREQ:SPAN',... 'default',1e6,'str_spec','%e'); addCommand(this, 'start_freq','DPSA:FREQ:STAR',... 'default',1e6,'str_spec','%e'); addCommand(this, 'stop_freq','DPSA:FREQ:STOP',... 'default',2e6,'str_spec','%e'); addCommand(this, 'cent_freq','DPSA:FREQ:CENT',... 'default',1.5e6,'str_spec','%e'); % Initiate and abort data acquisition, don't take arguments addCommand(this, 'abort_acq','ABORt', 'access','w',... 'str_spec',''); addCommand(this, 'init_acq','INIT', 'access','w',... 'str_spec',''); % Continuous triggering addCommand(this, 'init_cont','INIT:CONT','default',true,... 'str_spec','%b'); % Number of points in trace addCommand(this, 'point_no','DPSA:POIN:COUN',... 'default',10401, 'val_list',{801,2401,4001,10401},... 'str_spec','P%i'); % Reference level (dB) addCommand(this, 'ref_level','INPut:RLEVel','default',0,... 'str_spec','%e'); % Display scale per division (dBm/div) addCommand(this, 'disp_y_scale','DISPlay:DPX:Y:PDIVision',... 'default',10,'str_spec','%e'); % Display vertical offset (dBm) addCommand(this, 'disp_y_offset','DISPLAY:DPX:Y:OFFSET',... 'default',0,'str_spec','%e'); % Parametric commands for i = 1:3 i_str = num2str(i); % Display trace addCommand(this, ['disp_trace',i_str],... ['TRAC',i_str,':DPX'],... 'default',false,'str_spec','%b'); % Trace Detection addCommand(this, ['det_trace',i_str],... ['TRAC',i_str,':DPX:DETection'],... 'val_list',{'AVER','AVERage','NEG','NEGative',... 'POS','POSitive'},... 'default','AVER','str_spec','%s'); % Trace Function addCommand(this, ['func_trace',i_str],... ['TRAC',i_str,':DPX:FUNCtion'],... 'val_list',{'AVER','AVERage','HOLD','NORM','NORMal'},... 'default','AVER','str_spec','%s'); % Number of averages addCommand(this, ['average_no',i_str],... ['TRAC',i_str,':DPX:AVER:COUN'],... 'default',1,'str_spec','%i'); % Count completed averages addCommand(this, ['cnt_trace',i_str],... ['TRACe',i_str,':DPX:COUNt:ENABle'],... 'default',false,'str_spec','%b'); end end end %% Public functions including callbacks methods (Access=public) function readSingle(this, n_trace) fetch_cmd = sprintf('fetch:dpsa:res:trace%i?', n_trace); fwrite(this.Device, fetch_cmd); data = binblockread(this.Device,'float'); - readProperty('start_freq','stop_freq','point_no'); + readProperty(this, 'start_freq','stop_freq','point_no'); x_vec=linspace(this.start_freq,this.stop_freq,... this.point_no); %Calculates the power spectrum from the data, which is in dBm. %Output is in V^2/Hz power_spectrum = (10.^(data/10))/this.rbw*50*0.001; %Trace object is created containing the data and its units - this.Trace.x=x_vec; - this.Trace.y=power_spectrum; + this.Trace.x = x_vec; + this.Trace.y = power_spectrum; %Trigger acquired data event (inherited from MyInstrument) triggerNewData(this); end end end diff --git a/GUIs/GuiDpo.mlapp b/GUIs/GuiDpo.mlapp index 8ea9842..1ba3d60 100644 Binary files a/GUIs/GuiDpo.mlapp and b/GUIs/GuiDpo.mlapp differ diff --git a/GUIs/GuiRsa.mlapp b/GUIs/GuiRsa.mlapp index 60a948a..d297653 100644 Binary files a/GUIs/GuiRsa.mlapp and b/GUIs/GuiRsa.mlapp differ diff --git a/Local/InstrAddressList.mat b/Local/InstrAddressList.mat index 4249bac..7a438d1 100644 Binary files a/Local/InstrAddressList.mat and b/Local/InstrAddressList.mat differ diff --git a/Local/RunRSA5103.m b/Local/RunRSA5103.m new file mode 100644 index 0000000..f836b15 --- /dev/null +++ b/Local/RunRSA5103.m @@ -0,0 +1 @@ +runGui('RSA5103', 'GuiRsa') \ No newline at end of file diff --git a/Local/RunRSA5106.m b/Local/RunRSA5106.m new file mode 100644 index 0000000..30fe7bb --- /dev/null +++ b/Local/RunRSA5106.m @@ -0,0 +1 @@ +runGui('RSA5106', 'GuiRsa') \ No newline at end of file diff --git a/Utility functions/runGui.m b/Utility functions/runGui.m index 077cd10..f50eff3 100644 --- a/Utility functions/runGui.m +++ b/Utility functions/runGui.m @@ -1,29 +1,33 @@ function runGui(instr_name, gui_name) % load the InstrAddressList structure - load('InstrAddressList.mat','InstrAddressList') + load('InstrAddressList.mat','InstrAddressList'); - % Find out if Gui is running already - name_exist = ~exist(instr_name, 'var'); + prog_name=[gui_name,'_',instr_name]; + % Find out if the same Gui with the same device is running already + name_exist = ~exist(prog_name, 'var'); if name_exist try - gui_running = evalin('base',sprintf('isvalid(%s)',instr_name)); + prog_running = evalin('base',sprintf('isvalid(%s)',prog_name)); catch - gui_running = false; + prog_running = false; end else - gui_running = false; + prog_running = false; end % Start the instrument Gui if not running already - if ~gui_running + if ~prog_running + % Replacement ' -> '' in the string + constructor_name = replace(InstrAddressList.(instr_name),... + '''',''''''); eval_str = sprintf(... - '%s=%s(''constructor'',InstrAddressList.%s,''name'',''%s'');',... - instr_name, gui_name, instr_name, instr_name); + '%s=%s(''constructor'',''%s'',''name'',''%s'');',... + prog_name, gui_name, constructor_name, instr_name); % Evaluate in the Matlab base workspace to create a variable named % instr_name evalin('base', eval_str); else - warning('GUI for the instrument %s is already running', instr_name); + warning('%s is already running', prog_name); end end