diff --git a/@MyDaq/MyDaq.asv b/@MyDaq/MyDaq.asv deleted file mode 100644 index 008d4fc..0000000 --- a/@MyDaq/MyDaq.asv +++ /dev/null @@ -1,988 +0,0 @@ -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/@MyInstrument/MyInstrument.asv b/@MyInstrument/MyInstrument.asv deleted file mode 100644 index 8f70233..0000000 --- a/@MyInstrument/MyInstrument.asv +++ /dev/null @@ -1,457 +0,0 @@ -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 = 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 - - %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 - 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 2700bd3..cc1d3cd 100644 --- a/@MyInstrument/MyInstrument.m +++ b/@MyInstrument/MyInstrument.m @@ -1,462 +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 %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); + 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) + 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 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.asv b/@MyRsa/MyRsa.asv deleted file mode 100644 index 8ab4a58..0000000 --- a/@MyRsa/MyRsa.asv +++ /dev/null @@ -1,106 +0,0 @@ -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 have arguments - addCommand(this, 'abort_acq',':ABORt', 'access','w',... - 'str_spec',''); - addCommand(this, 'init_acq','INITiate', 'access','w',... - 'str_spec',''); - - - % Number of points in trace - addCommand(this, 'point_no','DPSA:POIN:COUN',... - 'default',10401, 'val_list',{801,2401,4001,10401},... - 'str_spec','P%i'); - % Continuous triggering - addCommand(this, 'init_cont','INIT:CONT','default',true,... - 'str_spec','%b'); - % 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'); - end - end - end - - %% Public functions including callbacks - methods (Access=public) - - function readSingle(this, n_channel) - fetch_cmd = sprintf('fetch:dpsa:res:trace%i?', n_channel); - openDevice(this); - fwrite(this.Device, fetch_cmd); - data = binblockread(this.Device,'float'); - closeDevice(this); - - 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; - - %Trigger acquired data event (inherited from MyInstrument) - triggerNewData(this); - end - end -end - diff --git a/@MyTrace/MyTrace.m b/@MyTrace/MyTrace.m index a3db643..af2ad33 100644 --- a/@MyTrace/MyTrace.m +++ b/@MyTrace/MyTrace.m @@ -1,429 +1,423 @@ classdef MyTrace < handle & matlab.mixin.Copyable properties (Access=public) x=[]; y=[]; name_x='x'; name_y='y'; unit_x=''; unit_y=''; filename='placeholder'; save_dir=''; load_path=''; save_pres=15; overwrite_flag %Cell that contains handles the trace is plotted in hlines={}; end properties (Access=private) Parser; end properties (Dependent=true) label_x; label_y; end methods (Access=private) %Creates the input parser for the class. Includes default values %for all optional parameters. function createParser(this) p=inputParser; addParameter(p,'filename','placeholder'); addParameter(p,'x',[]); addParameter(p,'y',[]); addParameter(p,'unit_x','x'); addParameter(p,'unit_y','y'); addParameter(p,'name_x','x'); addParameter(p,'name_y','y'); %Default save folder is the current directory upon %instantiation addParameter(p,'save_dir',pwd); addParameter(p,'load_path',''); addParameter(p,'save_pres',15); addParameter(p,'overwrite_flag',false); this.Parser=p; end %Sets the class variables to the inputs from the inputParser. Can %be used to reset class to default values if default_flag=true. function parseInputs(this, default_flag) for i=1:length(this.Parser.Parameters) %Sets the value if there was an input or if the default %flag is on. The default flag is used to reset the class to %its default values. if default_flag || ~any(ismember(this.Parser.Parameters{i},... this.Parser.UsingDefaults)) this.(this.Parser.Parameters{i})=... this.Parser.Results.(this.Parser.Parameters{i}); end end end end methods (Access=public) function this=MyTrace(varargin) createParser(this); parse(this.Parser,varargin{:}); parseInputs(this,true); if ~ismember('load_path',this.Parser.UsingDefaults) loadTrace(this,this.load_path); end end %Defines the save function for the class. Saves the data with %column headers as label_x and label_y function save(this,varargin) %Allows all options of the class as inputs for the save %function, to change the name or save directory. parse(this.Parser,varargin{:}); - parseInputs(this,false); - addParameter(p,'save_dir',pwd); - addParameter(p,'Color','b'); - addParameter(p,'Marker','none'); - addParameter(p,'LineStyle','-'); - addParameter(p,'MarkerSize',6); - + parseInputs(this,false); %Adds the \ at the end if it was not added by the user. if ~strcmp(this.save_dir(end),'\') this.save_dir(end+1)='\'; end %Creates save directory if it does not exist if ~exist(this.save_dir,'dir') mkdir(this.save_dir) end %Creates a file name out of the name of the class and the save %directory fullfilename=[this.save_dir,this.filename,'.txt']; if exist(fullfilename,'file') && ~this.overwrite_flag switch questdlg('Would you like to overwrite?',... 'File already exists', 'Yes', 'No', 'No') case 'Yes' this.overwrite_flag=1; otherwise warning('No file written as %s already exists',... fullfilename); return end end %Creates the file fileID=fopen(fullfilename,'w'); %MATLAB returns -1 for the fileID if the file could not be %opened if fileID==-1 errordlg(sprintf('File %s could not be created.',... fullfilename),'File error'); return end %Finds appropriate column width cw=max([length(this.label_y),length(this.label_x),... this.save_pres+7]); %Pads the vectors if they are not equal length diff=length(this.x)-length(this.y); if diff<0 this.x=[this.x;zeros(-diff,1)]; warning(['Zero padded x vector as the saved vectors are',... ' not of the same length']); elseif diff>0 this.y=[this.y;zeros(diff,1)]; warning(['Zero padded y vector as the saved vectors are',... ' not of the same length']); end %Makes a format string with the correct column width. %% makes %a % symbol in sprintf, thus if cw=18, below is %18s\t%18s\r\n. %\r\n prints a carriage return, ensuring linebreak in NotePad. title_format_str=sprintf('%%%is\t%%%is\r\n',cw,cw); fprintf(fileID,title_format_str,... this.label_x, this.label_y); %Saves in scientific notation with correct column width defined %above. Again if cw=20, we get %14.10e\t%14.10e\r\n data_format_str=sprintf('%%%i.%ie\t%%%i.%ie\r\n',... cw,this.save_pres,cw,this.save_pres); fprintf(fileID,data_format_str,[this.x, this.y]'); fclose(fileID); end function clearData(this) this.x=[]; this.y=[]; end function loadTrace(this, file_path) if ~exist(file_path,'file') error('File does not exist, please choose a different load path') end read_opts=detectImportOptions(file_path); DataTable=readtable(file_path,read_opts); data_labels=DataTable.Properties.VariableNames; %Finds where the unit is specified, within parantheses. %Forces indices to be in cells for later. ind_start=strfind(data_labels, '(','ForceCellOutput',true); ind_stop=strfind(data_labels, ')','ForceCellOutput',true); col_name={'x','y'}; for i=1:length(ind_start) if ~isempty(ind_start{i}) && ~isempty(ind_stop{i}) %Extracts the data labels from the file this.(sprintf('unit_%s',col_name{i}))=... data_labels{i}((ind_start{i}+4):(ind_stop{i}-1)); this.(sprintf('name_%s',col_name{i}))=... data_labels{i}(1:(ind_start{i}-2)); end %Loads the data into the trace this.(col_name{i})=DataTable.(data_labels{i}); end this.load_path=file_path; end %Allows setting of multiple properties in one command. function setTrace(this, varargin) parse(this.Parser,varargin{:}) parseInputs(this, false); end %Plots the trace on the given axes, using the class variables to %define colors, markers, lines and labels. Takes all optional %parameters of the class as inputs. function plotTrace(this,plot_axes,varargin) %Checks that there are axes to plot assert(exist('plot_axes','var') && ... (isa(plot_axes,'matlab.graphics.axis.Axes')||... isa(plot_axes,'matlab.ui.control.UIAxes')),... 'Please input axes to plot in.') %Checks that x and y are the same size assert(validatePlot(this),... 'The length of x and y must be identical to make a plot') %Parses inputs p=inputParser(); validateColor=@(x) assert(iscolor(x),... 'Input must be a valid color. See iscolor function'); addParameter(p,'Color','b',validateColor); validateMarker=@(x) assert(ismarker(x),... 'Input must be a valid marker. See ismarker function'); addParameter(p,'Marker','none',validateMarker); validateLine=@(x) assert(isline(x),... 'Input must be a valid linestyle. See isline function'); addParameter(p,'LineStyle','-',validateLine); addParameter(p,'MarkerSize',6,... @(x) validateattributes(x,{'numeric'},{'positive'})); addParameter(p,'make_labels',false,@islogical); interpreters={'none','tex','latex'}; validateInterpreter=@(x) assert(contains(x,interpreters),... 'Interpreter must be none, tex or latex'); addParameter(p,'Interpreter','latex',validateInterpreter); parse(p,varargin{:}); ind=findLineInd(this, plot_axes); if ~isempty(ind) && any(ind) set(this.hlines{ind},'XData',this.x,'YData',this.y); else this.hlines{end+1}=plot(plot_axes,this.x,this.y); ind=length(this.hlines); end %Sets the correct color and label options set(this.hlines{ind},'Color',p.Results.Color,'LineStyle',... p.Results.LineStyle,'Marker',p.Results.Marker,... 'MarkerSize',p.Results.MarkerSize); if p.Results.make_labels interpreter=p.Results.Interpreter; xlabel(plot_axes,this.label_x,'Interpreter',interpreter); ylabel(plot_axes,this.label_y,'Interpreter',interpreter); set(plot_axes,'TickLabelInterpreter',interpreter); end end %If there is a line object from the trace in the figure, this sets %it to the appropriate visible setting. function setVisible(this, plot_axes, bool) if bool vis='on'; else vis='off'; end ind=findLineInd(this, plot_axes); if ~isempty(ind) && any(ind) set(this.hlines{ind},'Visible',vis) end end %Defines addition of two MyTrace objects function sum=plus(a,b) checkArithmetic(a,b); sum=MyTrace('x',a.x,'y',a.y+b.y,'unit_x',a.unit_x,... 'unit_y',a.unit_y,'name_x',a.name_x,'name_y',a.name_y); end %Defines subtraction of two MyTrace objects function sum=minus(a,b) checkArithmetic(a,b); sum=MyTrace('x',a.x,'y',a.y-b.y,'unit_x',a.unit_x,... 'unit_y',a.unit_y,'name_x',a.name_x,'name_y',a.name_y); end function [max_val,max_x]=max(this) assert(validatePlot(this),['MyTrace object must contain',... ' nonempty data vectors of equal length to find the max']) [max_val,max_ind]=max(this.y); max_x=this.x(max_ind); end function fwhm=calcFwhm(this) assert(validatePlot(this),['MyTrace object must contain',... ' nonempty data vectors of equal length to find the fwhm']) [max_val,~]=max(this); ind1=find(this.y>max_val/2,1,'first'); ind2=find(this.y>max_val/2,1,'last'); fwhm=this.x(ind2)-this.x(ind1); end %Integrates the trace numerically function area=integrate(this,varargin) assert(validatePlot(this),['MyTrace object must contain',... ' nonempty data vectors of equal length to integrate']) %Input parser for optional inputs p=inputParser; %Default is to use all the data in the trace addOptional(p,'ind',true(1,length(this.x))); parse(p,varargin{:}); ind=p.Results.ind; %Integrates the data contained in the indexed part. area=trapz(this.x(ind),this.y(ind)); end %Checks if the object is empty function bool=isempty(this) bool=isempty(this.x) && isempty(this.y); end %Checks if the data can be plotted function bool=validatePlot(this) bool=~isempty(this.x) && ~isempty(this.y)... && length(this.x)==length(this.y); end function hline=getLineHandle(this,ax) ind=findLineInd(this,ax); if ~isempty(ind) hline=this.hlines{ind}; else hline=[]; end end end methods (Access=private) %Checks if arithmetic can be done with MyTrace objects. function checkArithmetic(a,b) assert(isa(a,'MyTrace') && isa(b,'MyTrace'),... ['Both objects must be of type MyTrace to add,',... 'here they are type %s and %s'],class(a),class(b)); assert(strcmp(a.unit_x, b.unit_x) && strcmp(a.unit_y,b.unit_y),... 'The MyTrace classes must have the same units for arithmetic'); assert(length(a.x)==length(a.y) && length(a.x)==length(a.y),... 'The length of x and y must be equal for arithmetic'); assert(all(a.x==b.x),... 'The MyTrace objects must have identical x-axis for arithmetic') end %Finds the hline handle that is plotted in the specified axes function ind=findLineInd(this, plot_axes) if ~isempty(this.hlines) ind=cellfun(@(x) ismember(x,findall(plot_axes,... 'Type','Line')),this.hlines); else ind=[]; end end end %Set and get methods methods %Set function for x, checks if it is a vector of doubles. function set.x(this, x) assert(isnumeric(x),... 'Data must be of class double'); this.x=x(:); end %Set function for y, checks if it is a vector of doubles. function set.y(this, y) assert(isnumeric(y),... 'Data must be of class double'); this.y=y(:); end %Set function for name, checks if input is a string. function set.filename(this, name) assert(ischar(name),'Name must be a string, not a %s',... class(name)); this.filename=name; end %Set function for unit_x, checks if input is a string. function set.unit_x(this, unit_x) assert(ischar(unit_x),'Unit must be a char, not a %s',... class(unit_x)); this.unit_x=unit_x; end %Set function for unit_y, checks if input is a string function set.unit_y(this, unit_y) assert(ischar(unit_y),'Unit must be a char, not a %s',... class(unit_y)); this.unit_y=unit_y; end %Set function for name_x, checks if input is a string function set.name_x(this, name_x) assert(ischar(name_x),'Name must be a char, not a %s',... class(name_x)); this.name_x=name_x; end %Set function for name_y, checks if input is a string function set.name_y(this, name_y) assert(ischar(name_y),'Name must be a char, not a %s',... class(name_y)); this.name_y=name_y; end function set.load_path(this, load_path) assert(ischar(load_path),'File path must be a char, not a %s',... class(load_path)); this.load_path=load_path; end %Get function for label_x, creates label from name_x and unit_x. function label_x=get.label_x(this) label_x=sprintf('%s (%s)', this.name_x, this.unit_x); end %Get function for label_y, creates label from name_y and unit_y. function label_y=get.label_y(this) label_y=sprintf('%s (%s)', this.name_y, this.unit_y); end end end diff --git a/Local/InstrumentList.mat b/Local/InstrumentList.mat new file mode 100644 index 0000000..d27bafe Binary files /dev/null and b/Local/InstrumentList.mat differ diff --git a/Utility functions/App utilities/linkControlElement.asv b/Utility functions/App utilities/linkControlElement.asv deleted file mode 100644 index 7f4bd79..0000000 --- a/Utility functions/App utilities/linkControlElement.asv +++ /dev/null @@ -1,69 +0,0 @@ -function linkControlElement(app, elem, prop_tag, varargin) - p=inputParser(); - % GUI control element - addRequired(p,'elem'); - % Instrument command to be linked to the GUI element - addRequired(p,'prop_tag',@ischar); - % If input_presc is given, the value assigned to the instrument propery - % is related to the value x displayed in GUI as x/input_presc. - addParameter(p,'input_presc',1,@isnumeric); - % Add an arbitrary function for processing the value, read from the - % device before outputting it. - addParameter(p,'out_proc_fcn',@(x)x,@(f)isa(f,'function_handle')); - addParameter(p,'create_callback_fcn',@(x)0,@(f)isa(f,'function_handle')); - % For drop-down menues initializes entries automatically based on the - % list of values. Ignored for all the other control elements. - addParameter(p,'auto_val_list',false,@islogical); - parse(p,elem,prop_tag,varargin{:}); - - % The property-control link is established by assigning the tag - % and adding the control to the list of linked elements - elem.Tag = prop_tag; - app.linked_elem_list = [app.linked_elem_list, elem]; - - % If the create_callback_fcn is set, assign it to the - % ValueChangedFcn which passes the field input to the instument - if ~ismember('create_callback_fcn',p.UsingDefaults) - elem.ValueChangedFcn = feval(p.Results.create_callback_fcn); - end - - % If prescaler is given, add it to the element as a new property - if p.Results.input_presc ~= 1 - if isprop(elem, 'InputPrescaler') - warning(['The InputPrescaler property already exists',... - ' in the control element']); - else - addprop(elem,'InputPrescaler'); - end - elem.InputPrescaler = p.Results.input_presc; - end - - % Add an arbitrary function for output processing - if ~ismember('out_proc_fcn',p.UsingDefaults) - if isprop(elem, 'OutputProcessingFcn') - warning(['The OutputProcessingFcn property already exists',... - ' in the control element']); - else - addprop(elem,'OutputProcessingFcn'); - end - elem.OutputProcessingFcn = p.Results.out_proc_fcn; - end - - % Auto initialization of entries, for dropdown menus only - if p.Results.auto_val_list && isequal(elem.Type, 'uidropdown') - try - cmd_val_list = app.Instr.CommandList.(prop_tag).val_list; - str_list={}; - for i=1:length(cmd_val_list) - - end - elem.Items=cellfun(@num2str,cmd_val_list,... - 'UniformOutput',false); - elem.ItemsData = cmd_val_list; - catch - warning(['Could not automatically assign values',... - ' when linking the ',prop_tag,' property']); - end - end -end - diff --git a/Utility functions/App utilities/updateGui.m b/Utility functions/App utilities/updateGui.m index 7c13430..28602fd 100644 --- a/Utility functions/App utilities/updateGui.m +++ b/Utility functions/App utilities/updateGui.m @@ -1,18 +1,22 @@ % update all the linked control elements according to their counterpart properties function updateGui(app) % app.Instr is a MyInstrument object for i=1:length(app.linked_elem_list) tmpelem = app.linked_elem_list(i); if isprop(app.Instr, tmpelem.Tag) + % update the element value tmpval = app.Instr.(tmpelem.Tag); % scale the value if the control element has a prescaler if isprop(tmpelem, 'OutputProcessingFcn') tmpval = tmpelem.OutputProcessingFcn(tmpval); elseif isprop(tmpelem, 'InputPrescaler') tmpval = tmpval*tmpelem.InputPrescaler; end tmpelem.Value = tmpval; + else + % disable the control element otherwise + tmpelem.Enable = 'off'; end end end diff --git a/Utility functions/FindInstrumentDlg.mlapp b/Utility functions/FindInstrumentDlg.mlapp index d53f073..d46c070 100644 Binary files a/Utility functions/FindInstrumentDlg.mlapp and b/Utility functions/FindInstrumentDlg.mlapp differ diff --git a/Utility functions/InstrListManager.mlapp b/Utility functions/InstrListManager.mlapp deleted file mode 100644 index 2615859..0000000 Binary files a/Utility functions/InstrListManager.mlapp and /dev/null differ diff --git a/Utility functions/InstrumentManager.mlapp b/Utility functions/InstrumentManager.mlapp new file mode 100644 index 0000000..bfa4762 Binary files /dev/null and b/Utility functions/InstrumentManager.mlapp differ diff --git a/Utility functions/findTcpipInstruments.m b/Utility functions/findTcpipInstruments.m new file mode 100644 index 0000000..1a6b7f6 --- /dev/null +++ b/Utility functions/findTcpipInstruments.m @@ -0,0 +1,34 @@ +% Pings all the IP addresses assuming the subnet mask 255.255.255.000 +% Local IP is a string of dot-separated decimals like '192.168.1.8' +% Caution: very slow, use the 'ind' option for partial search +function rsc_list = findTcpipInstruments(local_ip, varargin) + p=inputParser(); + addParameter(p,'ind',[1,254], @(x) validateattributes(x, {'numeric'},... + {'integer','nondecreasing','>',1,'<',254})); + addParameter(p,'visa_adaptor','ni',@ischar); + parse(p,varargin{:}); + v_ad = p.Results.visa_adaptor; + + a = sscanf(local_ip,'%i.%i.%i.%i'); + rsc_list = {}; + % Do full search. 0 and 255 are not valid as host names + disp('Found TCPIP-VISA instruments:'); + for i=p.Results.ind(1):p.Results.ind(2) + ip = sprintf('%i.%i.%i.%i',a(1),a(2),a(3),i); + rsc_name = sprintf('TCPIP0::%s::inst0::INSTR',ip); + % Try to connect to the device and open it + tmp_dev = visa(v_ad,rsc_name); %#ok + try + fopen(tmp_dev); + fclose(tmp_dev); + % If the fopen operation was successful, add the IP to ip_list + rsc_list = [rsc_list,sprintf('visa(''%s'',''%s'');',... + v_ad,rsc_name)]; %#ok + disp(rsc_name); + catch + % If error - do nothing + end + delete(tmp_dev); + end +end + diff --git a/Utility functions/getLocalIP.m b/Utility functions/getLocalIP.m new file mode 100644 index 0000000..4faa7b6 --- /dev/null +++ b/Utility functions/getLocalIP.m @@ -0,0 +1,31 @@ +% get the IP addresses of this computer on local networks +function ip_list = getLocalIP() + % Regex to match IP address, e.g 192.168.1.8 + % Each of the 4 blocks match dot-separated numbers and read as + % ('25x', where x=0 to 5) or ('2xy' where x=0 to 4 and y=0 to 9) or + % (zyx where z=0,1 or nothing; y=0 to 9; x=0 to 9 or nothing) + % The 4 blocks are peceeded and followed by non-numeric characters + ip_regex = ['(\D)',... + '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.',... + '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.',... + '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.',... + '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\D)']; + instr_info = instrhwinfo('tcpip'); + ip_list = cell(1,length(instr_info.LocalHost)); + for i=1:length(instr_info.LocalHost) + tmp_ip=regexp([' ',instr_info.LocalHost{i},' '],ip_regex,'match'); + if length(tmp_ip)>=1 + % if a match is found, assign it to the output disregarding the + % first and the last separating characters + ip_list{i}=tmp_ip{1}(2:end-1); + if length(tmp_ip)>1 + warning('Multiple IP''s are matched by regex') + end + else + ip_list{i}=''; + end + end + % Discard all the empty cells + ip_list = ip_list(~cellfun(@isempty,ip_list)); +end +