diff --git a/@MyCommCont/MyCommCont.m b/@MyCommCont/MyCommCont.m index bd44b02..bcfd211 100644 --- a/@MyCommCont/MyCommCont.m +++ b/@MyCommCont/MyCommCont.m @@ -1,167 +1,167 @@ % Communicator container. % This class provides extended functionality for communication using VISA, % tcpip and serial objects or any other objects that have a similar usage. classdef MyCommCont < handle % Giving explicit set access to this class makes properties protected % instead of private properties (GetAccess=public, SetAccess={?MyClassParser,?MyCommCont}) interface = 'serial' address = 'placeholder' end properties (GetAccess = public, SetAccess = protected) Comm % Communication object end methods (Access = public) %% Constructor and destructor function this = MyCommCont(varargin) P = MyClassParser(this); processInputs(P, this, varargin{:}); try connect(this); catch ME warning(ME.message); % Create a dummy this.Comm = serial('placeholder'); end configureCommDefault(this); end function delete(this) % Close the connection to the device try closeComm(this); catch warning('Connection could not be closed.'); end % Delete the device object try delete(this.Comm); catch warning('Communication object could not be deleted.'); end end %% Set up communication % Create an interface object function connect(this) switch lower(this.interface) % Use 'constructor' interface to create an object with % more that one parameter passed to the constructor case 'constructor' % In this case 'address' is a MATLAB command that % creates communication object when executed. % Such commands, for example, are returned by % instrhwinfo as ObjectConstructorName. this.Comm=eval(this.address); case 'visa' % visa brand is 'ni' by default this.Comm=visa('ni', this.address); case 'tcpip' % Works only with default socket. Use 'constructor' % if socket or other options need to be specified this.Comm=tcpip(this.address); case 'serial' this.Comm=serial(this.address); otherwise error(['Unknown interface ''' this.interface ... ''', a communication object is not created.' ... ' Valid interfaces are ',... '''constructor'', ''visa'', ''tcpip'' and ''serial''']) end end % Set larger buffer sizes and longer timeout than the MATLAB default function configureCommDefault(this) comm_props = properties(this.Comm); if ismember('OutputBufferSize',comm_props) this.Comm.OutputBufferSize = 1e7; % bytes end if ismember('InputBufferSize',comm_props) this.Comm.InputBufferSize = 1e7; % bytes end if ismember('Timeout',comm_props) this.Comm.Timeout = 10; % s end end function bool = isopen(this) try bool = strcmp(this.Comm.Status, 'open'); catch warning('Cannot access the communicator Status property'); bool = false; end end % Opens the device if it is not open. Does not throw error if % device is already open for communication with another object, but % tries to close existing connections instead. function openComm(this) try fopen(this.Comm); catch % try to find and close all the devices with the same % VISA resource name - instr_list=instrfind('RsrcName',this.Comm.RsrcName); + instr_list = instrfind('RsrcName',this.Comm.RsrcName); fclose(instr_list); fopen(this.Comm); warning(['Multiple instrument objects of ' ... 'address %s exist'], this.address); end end function closeComm(this) fclose(this.Comm); end %% Communication % Write textual command function writeString(this, cmd) try fprintf(this.Comm, cmd); catch ME try % Attempt re-opening communication openComm(this); fprintf(this.Comm, cmd); catch rethrow(ME); end end end % Query textual command function result = queryString(this, cmd) try result = query(this.Comm, cmd); catch ME try % Attempt re-opening communication openComm(this); result = query(this.Comm, cmd); catch rethrow(ME); end end end end end diff --git a/@MyRsa/MyRsa.m b/@MyRsa/MyRsa.m index 7d347a1..5ef373d 100644 --- a/@MyRsa/MyRsa.m +++ b/@MyRsa/MyRsa.m @@ -1,178 +1,178 @@ % Class for controlling Tektronix RSA5103 and RSA5106 spectrum analyzers classdef MyRsa < MyScpiInstrument & MyDataSource & MyCommCont properties (SetAccess = protected, GetAccess = public) acq_trace = [] % Last read trace end methods (Access = public) function this = MyRsa(varargin) P = MyClassParser(this); processInputs(P, this, varargin{:}); 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 methods (Access = protected) function createCommandList(this) addCommand(this, 'rbw', ':DPX:BAND:RES', ... 'format', '%e', ... 'info', 'Resolution bandwidth (Hz)', ... 'default', 1e3); addCommand(this, 'auto_rbw', ':DPX:BAND:RES:AUTO', ... 'format', '%b', ... 'default', true); addCommand(this, 'span', ':DPX:FREQ:SPAN', ... 'format', '%e', ... 'info', '(Hz)', ... 'default', 1e6); - addCommand(this, 'start_freq',':DPX:FREQ:STAR',... + addCommand(this, 'start_freq', ':DPX:FREQ:STAR',... 'format', '%e', ... 'info', '(Hz)', ... 'default', 1e6); - addCommand(this, 'stop_freq',':DPX:FREQ:STOP',... + addCommand(this, 'stop_freq', ':DPX:FREQ:STOP',... 'format', '%e', ... 'info', '(Hz)', ... 'default', 2e6); - addCommand(this, 'cent_freq',':DPX:FREQ:CENT',... + addCommand(this, 'cent_freq', ':DPX:FREQ:CENT',... 'format', '%e', ... 'info', '(Hz)', ... 'default', 1.5e6); % Continuous triggering addCommand(this, 'init_cont', ':INIT:CONT', ... 'format', '%b',... 'info', 'Continuous triggering on/off', ... 'default', true); % Number of points in trace addCommand(this, 'point_no', ':DPSA:POIN:COUN', ... 'format', 'P%i', ... 'value_list', {801, 2401, 4001, 10401}, ... 'default', 10401); % Reference level (dB) addCommand(this, 'ref_level',':INPut:RLEVel', ... 'format', '%e',... 'info', '(dB)', ... 'default', 0); % Display scale per division (dBm/div) addCommand(this, 'disp_y_scale', ':DISPlay:DPX:Y:PDIVision',... 'format', '%e', ... 'info', '(dBm/div)', ... 'default', 10); % Display vertical offset (dBm) addCommand(this, 'disp_y_offset', ':DISPLAY:DPX:Y:OFFSET', ... 'format', '%e', ... 'info', '(dBm)', ... 'default', 0); % Parametric commands for i = 1:3 i_str = num2str(i); % Display trace addCommand(this, ['disp_trace',i_str], ... [':TRAC',i_str,':DPX'], ... 'format', '%b', ... 'info', 'on/off', ... 'default', false); % Trace Detection addCommand(this, ['det_trace',i_str],... [':TRAC',i_str,':DPX:DETection'],... 'format', '%s', ... 'value_list', {'AVERage', 'NEGative', 'POSitive'},... 'default', 'average'); % Trace Function addCommand(this, ['func_trace',i_str], ... [':TRAC',i_str,':DPX:FUNCtion'], ... 'format', '%s', ... 'value_list', {'AVERage', 'HOLD', 'NORMal'},... 'default', 'average'); % Number of averages addCommand(this, ['average_no',i_str], ... [':TRAC',i_str,':DPX:AVER:COUN'], ... 'format', '%i', ... 'default', 1); % Count completed averages addCommand(this, ['cnt_trace',i_str], ... [':TRACe',i_str,':DPX:COUNt:ENABle'], ... 'format', '%b', ... 'info', 'Count completed averages', ... 'default', false); end end end 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(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.acq_trace = n_trace; %Trigger acquired data event (inherited from MyInstrument) triggerNewData(this); end % Abort data acquisition function abortAcq(this) writeCommand(this, ':ABORt'); end % Initiate data acquisition function initAcq(this) writeCommand(this, ':INIT'); end % Wait for the current operation to be completed function val = opc(this) val = queryCommand(this, '*OPC?'); if ~isempty(val) val = val{1}; end end % Extend readHeader function function Hdr = readHeader(this) %Call parent class method and then append parameters Hdr = readHeader@MyScpiInstrument(this); %Hdr should contain single field addParam(Hdr, Hdr.field_names{1}, ... 'acq_trace', this.acq_trace, ... 'comment', 'Last read trace'); end end end diff --git a/@MyTekScope/MyTekScope.m b/@MyTekScope/MyTekScope.m new file mode 100644 index 0000000..fd71098 --- /dev/null +++ b/@MyTekScope/MyTekScope.m @@ -0,0 +1,124 @@ +classdef MyTekScope < MyScpiInstrument & MyDataSource & MyCommCont + properties (SetAccess = protected, GetAccess = public) + + % List of the physical knobs, which can be rotated programmatically + knob_list = {'GPKNOB1','GPKNOB2','HORZPos','HORZScale',... + 'TRIGLevel','PANKNOB1','VERTPOS','VERTSCALE','ZOOM'}; + end + + properties (SetAccess = immutable, GetAccess = public) + channel_no = 4; % number of channels + end + + methods (Access = public) + function this = MyTekScope(varargin) + end + + function acquireContinuous(this) + writeCommand(this, ... + ':ACQuire:STOPAfter RUNSTop', ... + ':ACQuire:STATE ON'); + end + + function acquireSingle(this) + writeCommand(this, ... + ':ACQuire:STOPAfter SEQuence', ... + ':ACQuire:STATE ON'); + end + + function turnKnob(this, knob, nturns) + writeCommand(this, ... + sprintf(':FPAnel:TURN %s,%i', knob, nturns)); + end + end + + methods (Access = protected) + function createCommandList(this) + addCommand(this,'channel',':DATa:SOUrce',... + 'default',1,... + 'format','CH%i',... + 'info','Channel from which the data is transferred'); + + addCommand(this, 'ctrl_channel', ':SELect:CONTROl',... + 'format','CH%i',... + 'info','Channel currently selected in the scope display', ... + 'default',1); + + addCommand(this, 'point_no', ':HORizontal:RECOrdlength', ... + 'format', '%i', ... + 'info', 'Numbers of points in the scope trace', ... + 'value_list', {1000, 10000, 100000, 1000000, 10000000}, ... + 'default', 100000); + + addCommand(this, 'time_scale', ':HORizontal:SCAle', ... + 'format', '%e', ... + 'info', 'Time scale (s/div)', ... + 'default', 10E-3); + + addCommand(this, 'trig_lev', ':TRIGger:A:LEVel',... + 'format', '%e', ... + 'info', '(V)', ... + 'default', 1); + + addCommand(this, 'trig_slope', ':TRIGger:A:EDGE:SLOpe',... + 'default', 'RISe', 'value_list',{'RISe','RIS','FALL'},... + 'format','%s'); + + addCommand(this, 'trig_source', ':TRIGger:A:EDGE:SOUrce',... + 'default', 'AUX', 'value_list', {'CH1','CH2','CH3','CH4',... + 'AUX','EXT','LINE'},... + 'format','%s'); + + addCommand(this, 'trig_mode', ':TRIGger:A:MODe',... + 'default', 'AUTO', 'value_list',{'AUTO','NORMal','NORM'},... + 'format','%s'); + + addCommand(this, 'acq_state', ':ACQuire:STATE',... + 'default',true,... + 'format','%b',... + 'info','State of data acquisition by the scope'); + + addCommand(this, 'acq_mode', ':ACQuire:MODe',... + 'default', 'HIR', 'value_list',{'SAMple', ... + 'PEAKdetect','HIRes','AVErage','ENVelope'},... + 'format','%s',... + 'info',['Acquisition mode: sample, peak detect, ',... + 'high resolution, average or envelope']); + + % Parametric commands + for i = 1:this.channel_no + i_str = num2str(i); + + addCommand(this,... + ['cpl',i_str],[':CH',i_str,':COUP'],... + 'default','DC', 'value_list', {'AC','DC','GND'},... + 'format','%s',... + 'info','Channel coupling: AC, DC or GND'); + + % impedances, 1MOhm or 50 Ohm + addCommand(this,... + ['imp',i_str],[':CH',i_str,':IMPedance'],... + 'default','MEG', 'value_list', {'FIFty','MEG'},... + 'format','%s',... + 'info','Channel impedance: 1 MOhm or 50 Ohm'); + + % Offset + addCommand(this,... + ['offset',i_str],[':CH',i_str,':OFFSet'],'default',0,... + 'format','%e',... + 'info','(V)'); + + addCommand(this,... + ['scale',i_str],[':CH',i_str,':SCAle'],'default',1,... + 'format','%e',... + 'info','Channel y scale (V/div)'); + + addCommand(this,... + ['enable',i_str],[':SEL:CH',i_str],'default',true,... + 'format','%b',... + 'info','Channel enabled'); + end + end + end +end +