diff --git a/@MyTrace/MyTrace.m b/@MyTrace/MyTrace.m index 8139612..344eaf5 100644 --- a/@MyTrace/MyTrace.m +++ b/@MyTrace/MyTrace.m @@ -1,452 +1,471 @@ % Class for XY data representation with labelling, plotting and % saving/loading functionality % If instantiated as MyTrace(load_path) then % the content is loaded from file classdef MyTrace < handle & matlab.mixin.Copyable & matlab.mixin.SetGet properties (Access=public) x=[]; y=[]; name_x='x'; name_y='y'; unit_x=''; unit_y=''; % MyMetadata storing information about how the trace was taken MeasHeaders file_name=''; uid=''; % Data column and line separators column_sep = '\t' line_sep='\r\n' %Cell that contains handles the trace is plotted in hlines={}; end properties (Dependent=true) %MyMetadata containing the MeasHeaders and %information about the trace Metadata label_x label_y end methods (Access=public) function this=MyTrace(varargin) P=MyClassParser(this); % options for MeasHeaders addParameter(P, 'metadata_opts',{},@iscell); if mod(length(varargin),2)==1 % odd number of elements in varargin - interpret the first % element as file name and the rest as name-value pairs load_path=varargin{1}; assert(ischar(load_path)&&isvector(load_path),... '''file_name'' must be a vector of characters'); processInputs(P, this, varargin{2:end}); this.file_name=load_path; else % Parse varargin as a list of name-value pairs processInputs(P, this, varargin{:}); load_path=[]; end this.MeasHeaders=MyMetadata(P.Results.metadata_opts{:}); if ~isempty(load_path) load(this, load_path); end end %Defines the save function for the class. Note that this is only %used when we want to write only the data with its associated %trace, rather than just the trace. To write just the trace with %fewer input checks, use the writeData function. function save(this, varargin) %Parse inputs for saving p=inputParser; addParameter(p,'save_prec',15); addParameter(p,'overwrite',false); if mod(length(varargin),2)==1 % odd number of elements in varargin - interpret the first % element as file name and the rest as name-value pairs fname=varargin{1}; assert(ischar(fname)&&isvector(fname),... '''filename'' must be a vector of characters'); this.file_name=fname; parse(p,varargin{2:end}); else % Parse varargin as a list of name-value pairs and take % file name from the class property fname=this.file_name; parse(p,varargin{:}); end %Creates the file in the given folder stat=createFile(fname, 'overwrite', p.Results.overwrite); %Returns if the file is not created for some reason if ~stat error('File not created, returned write_flag %i',stat); end %We now write the data to the file writeData(this, fname, 'save_prec', p.Results.save_prec); end %Writes the data to a file. This is separated so that other %programs can write to the file from the outside. We circumvent the %checks for the existence of the file here, assuming it is done %outside. function writeData(this, fullfilename, varargin) p=inputParser; addRequired(p,'fullfilename',@ischar); addParameter(p,'save_prec',15); parse(p,fullfilename,varargin{:}); fullfilename=p.Results.fullfilename; save_prec=p.Results.save_prec; %Writes the metadata header save(this.Metadata,fullfilename); fileID=fopen(fullfilename,'a'); %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 %Save in the more compact of fixed point and scientific %notation with trailing zeros removed %If save_prec=15, we get %.15g\t%.15g\r\n %Formatting without column padding may looks ugly but makes %files significantly smaller data_format_str=sprintf(['%%.%ig',this.column_sep,'%%.%ig',... this.line_sep],save_prec,save_prec); fprintf(fileID, data_format_str,[this.x, this.y]'); fclose(fileID); end function clearData(this) this.x=[]; this.y=[]; end function load(this, file_path, varargin) p=inputParser; addParameter(p,'hdr_spec',... this.MeasHeaders.hdr_spec,@ischar); addParameter(p,'end_header',... this.MeasHeaders.end_header,@ischar); parse(p,varargin{:}); this.MeasHeaders.hdr_spec=p.Results.hdr_spec; this.MeasHeaders.end_header=p.Results.end_header; if ~exist(file_path,'file') error('File does not exist, please choose a different load path') end %Read metadata. We get the line number we want to read %the main data from as an output. end_line_no=load(this.MeasHeaders, file_path); %Tries to assign units and names and then delete the Info field %from MeasHeaders try this.unit_x=this.MeasHeaders.Info.Unit1.value; this.unit_y=this.MeasHeaders.Info.Unit2.value; this.name_x=this.MeasHeaders.Info.Name1.value; this.name_y=this.MeasHeaders.Info.Name2.value; deleteField(this.MeasHeaders,'Info'); catch warning(['No metadata found. No units or labels assigned',... ' when loading trace from %s'],file_path) this.name_x='x'; this.name_y='y'; this.unit_x='x'; this.unit_y='y'; end %Reads x and y data data_array=dlmread(file_path, this.column_sep, ... end_line_no,0); this.x=data_array(:,1); this.y=data_array(:,2); this.file_name=file_path; 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 plot(this, varargin) %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(); % Axes in which log should be plotted addOptional(p, 'plot_axes', [], @(x)assert( ... isa(x,'matlab.graphics.axis.Axes')||... isa(x,'matlab.ui.control.UIAxes'),... 'Argument must be axes or uiaxes.')); 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{:}); %If axes are not supplied get current if ~isempty(p.Results.plot_axes) plot_axes=p.Results.plot_axes; else plot_axes=gca(); end 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']) [~,~,fwhm,~]=findPeaks(this.y,this.x,'NPeaks',1); 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 + % Picks every n-th element from the trace, + % performing a running average first if opt=='avg' + function downsample(this, n, opt) + n0=ceil(n/2); + if nargin()==3 && (strcmpi(opt,'average') || strcmpi(opt,'vg')) + % Compute moving average with 'shrink' option so that the + % total number of samples is preserved. Endpoints will be + % discarded by starting the indexing from n0. + tmpy=movmean(this.y, 'Endpoints', 'shrink'); + + this.x=this.x(n0:n:end); + this.y=tmpy(n0:n:end); + else + % Downsample without averaging + this.x=this.x(n0:n:end); + this.y=this.y(n0:n:end); + end + 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 MeasHeaders function set.MeasHeaders(this, MeasHeaders) assert(isa(MeasHeaders,'MyMetadata'),... ['MeasHeaders must be an instance of MyMetadata, ',... 'it is %s'],class(MeasHeaders)); this.MeasHeaders=MeasHeaders; end %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 and %generates a new UID for the trace function set.y(this, y) assert(isnumeric(y),... 'Data must be of class double'); this.y=y(:); this.uid=genUid(); %#ok 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.file_name(this, file_name) assert(ischar(file_name),'File path must be a char, not a %s',... class(file_name)); this.file_name=file_name; end function set.uid(this, uid) assert(ischar(uid),'UID must be a char, not a %s',... class(uid)); this.uid=uid; 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 %Generates the full metadata of the trace function Metadata=get.Metadata(this) %First we update the trace information Metadata=MyMetadata(); addField(Metadata,'Info'); addParam(Metadata,'Info','uid',this.uid); addParam(Metadata,'Info','Name1',this.name_x); addParam(Metadata,'Info','Name2',this.name_y); addParam(Metadata,'Info','Unit1',this.unit_x); addParam(Metadata,'Info','Unit2',this.unit_y); addMetadata(Metadata,this.MeasHeaders); end end end diff --git a/@MyZiRingdown/MyZiRingdown.m b/@MyZiRingdown/MyZiRingdown.m index 3adfd43..110cc47 100644 --- a/@MyZiRingdown/MyZiRingdown.m +++ b/@MyZiRingdown/MyZiRingdown.m @@ -1,443 +1,461 @@ % Class for performing ringdown measurements of mechanical oscillators % using Zurich Instruments UHF or MF lock-in. % % Operation: sweep the driving tone (drive_osc) using the sweep module % in LabOne web user interface, when the magnitude of the demodulator % signal exceeds trig_threshold switch off the driving tone and start % recording the demodulated signal for the duration of record_time. classdef MyZiRingdown < handle properties (Access=public) % Ringdown is recorded if the signal in the triggering demodulation % channel exceeds this value trig_threshold=1e-3 % V % Duration of the recorded ringdown record_time=1 % s % If enable_acq is true, then the drive is on andthe acquisition % of record is triggered when signal exceeds trig_threshold enable_acq=false + + % Downsample the measurement record to reduce the amount of data + % while keeping the large demodulation bandwidth + downsample_t=1e-3 % (s), averaging time end % The properties which are read or set only once during the class % initialization properties (GetAccess=public, SetAccess={?MyClassParser,?MyZiRingdown}) name='ziRingdown' dev_serial='dev4090' % enumeration for demodulators, oscillators and output starts from 1 demod=1 % demodulator used for both triggering and measurement % Enumeration in the node structure starts from 0, so, for example, % the default path to the trigger demodulator refers to the % demodulator #1 demod_path='/dev4090/demods/0' drive_osc=1 meas_osc=2 % Signal input, integers above 1 correspond to main inputs, aux % input etc. (see the user interface for device-specific details) signal_in=1 drive_out=1 % signal output used for driving % Device clock frequency, i.e. the number of timestamps per second clockbase % The string that specifies the device name as appears % in the server's node tree. Can be the same as dev_serial. dev_id % Device information string containing the data returned by % ziDAQ('discoveryGet', ... idn_str % Poll duration of 1 ms practically means that ziDAQ('poll', ... % returns immediately with the data accumulated since the % previous function call. poll_duration = 0.001; % s poll_timeout = 50; % ms end % Internal variables properties (GetAccess=public, SetAccess=protected) recording=false % true if a ringdown is being recorded % Reference timestamp at the beginning of measurement record. % Stored as uint64. t0 elapsed_t % Time elapsed since the last recording was started Trace % MyTrace object storing the ringdown end % Setting or reading these properties automatically invokes % communication with the device properties (Dependent=true) drive_osc_freq meas_osc_freq drive_on % true when the dirive output is on current_osc % demodulator sampling rate (as transferred to the computer) demod_rate + % downsampling factor calculated from downsample_t + % (not a device property) + downsample_n + % The properties below are only used within the program to display % the information about device state. drive_amp % (V), peak-to-peak amplitude of the driving tone lowpass_order % low-pass filter order lowpass_bw % low-pass filter bandwidth end properties (Access=private) PollTimer end events % Event for communication with Daq that signals the acquisition of % a new ringdown - NewData + NewData + + NewDemodSample % New demodulator samples received end methods (Access=public) %% Constructor and destructor function this = MyZiRingdown(dev_serial, varargin) P=MyClassParser(this); % Poll timer period addPArameter(P,'poll_period',0.1,@isnumeric); processInputs(P, varargin{:}); % Create and configure the trace object this.Trace=MyTrace(... 'name_x','Time',... 'unit_x','s',... 'name_y','Magnitude r',... 'unit_y','V'); % Set up the poll timer. Using a timer for anyncronous % data readout allows to use the wait time for execution % of other programs. % Fixed spacing is preferred as it is the most robust mode of % operation when the precision of timing is less of a concern. this.PollTimer=timer(... 'ExecutionMode','fixedSpacing',... 'Period',P.Results.poll_period,... 'TimerFcn',@this.pollTimerCallback); % Check the ziDAQ MEX (DLL) and Utility functions can be found in Matlab's path. if ~(exist('ziDAQ', 'file') == 3) && ~(exist('ziCreateAPISession', 'file') == 2) fprintf('Failed to either find the ziDAQ mex file or ziDevices() utility.\n') fprintf('Please configure your path using the ziDAQ function ziAddPath().\n') fprintf('This can be found in the API subfolder of your LabOne installation.\n'); fprintf('On Windows this is typically:\n'); fprintf('C:\\Program Files\\Zurich Instruments\\LabOne\\API\\MATLAB2012\\\n'); return end % Create an API session and connect to the correct Data Server. % This is a high level function that uses ziDAQ('connect',.. % and ziDAQ('connectDevice', ... when necessary apilevel=6; [this.dev_id,~]=ziCreateAPISession(dev_serial, apilevel); % Read the divice clock frequency this.clockbase = ... double(ziDAQ('getInt',['/',this.dev_id,'/clockbase'])); end function delete(this) % delete function should never throw errors, so protect % statements with try-catch try stopPoll(this) catch warning(['Could not usubscribe from the demodulator ', ... 'or stop the poll timer.']) end try delete(this.PollTimer) catch warning('Could not delete the poll timer.') end end %% Other methods function startPoll(this) % Configure the oscillators, demodulator and driving output % -1 accounts for the difference in enumeration conventions % in the software names (starting from 1) and node numbers % (starting from 0) this.demod_path = sprintf('/%s/demods/%i', ... this.dev_id, this.demod-1); % Set the data transfer rate so that it satisfies the Nyquist % criterion (>x2 the bandwidth of interest) this.demod_rate=3*this.lowpass_bw; % Configure the demodulator. Signal input: ziDAQ('setInt', ... [this.demod_path,'/adcselect'], this.signal_in-1); % Oscillator: ziDAQ('setInt', ... [this.demod_path,'/oscselect'], this.drive_osc-1); % Enable data transfer from the demodulator to the computer ziDAQ('setInt', [this.demod_path,'/enable'], 1); % Configure the driving output - disable all the oscillator % contributions including the driving tone since we start % form 'enable_acq=false' state path = sprintf('/%s/sigouts/%i/enables/*', ... this.dev_id, this.drive_out-1); ziDAQ('setInt', path, 0); % Subscribe to continuously receive samples from the % demodulator. Samples accumulated between timer callbacks % will be read out using ziDAQ('poll', ... ziDAQ('subscribe',[this.demod_path,'/sample']); % Start continuous polling start(this.PollTimer) end function stopPoll(this) stop(this.PollTimer) ziDAQ('unsubscribe',[this.demod_path,'/sample']); end % Main function that polls data drom the device demodulator function pollTimerCallback(this) % ziDAQ('poll', ... with short poll_duration returns % immediately with the data accumulated since the last timer % callback Data = ziDAQ('poll', this.poll_duration, this.poll_timeout); if ziCheckPathInData(Data, [this.demod_path,'/sample']) % Demodulator returns data DemodSample= ... Data.(this.dev_id).demods(this.demod).sample; if this.recording % If recording is under way, append the new samples to % the trace appendSamples(this, DemodSample) % Check if recording should be stopped if this.Trace.x(end)>=this.record_time % stop recording this.recording=false; % Switch the oscillator this.current_osc=this.drive_osc; % Do not enable acquisition after a ringdown is % recorded to prevent possible overwriting this.enable_acq=false; - + + % Downsample the trace to reduce the amount of data + downsample(this.Trace, this.downsample_n, 'avg'); + triggerNewData(this); else % Update elapsed time this.elapsed_t=this.Trace.x(end); end else rmax=max(sqrt(DemodSample.x^2+DemodSample.y^2)); if this.enable_acq && rmax>this.threshold % Start acquisition of a new trace if the maximum % of the signal exceeds threshold clearData(this.Trace); this.recording=true; this.t0=DemodSample.timestamp(1); this.elapsed_t=0; % Switch the drive off this.drive_on=false; % Set the measurement oscillator frequency to be % the frequency at which triggering occurred this.meas_osc_freq=this.drive_osc_freq; % Switch the oscillator this.current_osc=this.meas_osc; end end + notify(this,'NewDemodSample') end end function appendSamples(this, DemodSample) r=sqrt(DemodSample.x^2+DemodSample.y^2); % Subtract the reference time, convert timestamps to seconds % and append the new data to the trace. this.Trace.x=[this.Trace.x, ... double(DemodSample.timestamp-this.t0)/this.clockbase]; this.Trace.y=[this.Trace.y, r]; end function str=idn(this) DevProp=ziDAQ('discoveryGet', this.dev_id); str=this.dev_id; if isfield(DevProp, 'devicetype') str=[str,'; device type: ', DevProp.devicetype]; end if isfield(DevProp, 'options') % Print options from the list as comma-separated values and % discard the last comma. opt_str=sprintf('%s,',DevProp.options{:}); str=[str,'; options: ', opt_str(1:end-1)]; end if isfield(DevProp, 'serverversion') str=[str,'; server version: ', DevProp.serverversion]; end this.idn_str=str; end function triggerNewData(this) notify(this,'NewData') end function Hdr=readHeader(this) Hdr=MyMetadata(); % Generate valid field name from instrument name if present and % class name otherwise if ~isempty(this.name) field_name=genvarname(this.name); else field_name=class(this); end addField(Hdr, field_name); % Add identification string addParam(Hdr, field_name, 'idn', this.idn_str); % Add the measurement configuration addParam(Hdr, field_name, 'demod', this.demod, ... 'comment', 'Demodulator number (starting from 1)'); addParam(Hdr, field_name, 'drive_osc', this.drive_osc, ... 'comment', 'Swept oscillator number'); addParam(Hdr, field_name, 'meas_osc', this.meas_osc, ... 'comment', 'Measurement oscillator number'); addParam(Hdr, field_name, 'signal_in', this.signal_in, ... 'comment', 'Singnal input number'); addParam(Hdr, field_name, 'drive_out', this.drive_out, ... 'comment', 'Driving output number'); addParam(Hdr, field_name, 'clockbase', this.clockbase, ... 'comment', ['Device clock frequency, i.e. the number ', ... 'of timestamps per second']); addParam(Hdr, field_name, 'drive_amp', this.drive_amp, ... 'comment', '(V) peak to peak'); addParam(Hdr, field_name, 'meas_osc_freq', ... this.meas_osc_freq, 'comment', '(Hz)'); addParam(Hdr, field_name, 'trig_threshold', ... this.drive_threshold, 'comment', '(V)'); addParam(Hdr, field_name, 'record_time', ... this.record_time, 'comment', '(s)'); addParam(Hdr, field_name, 'lowpass_order', ... this.lowpass_order, 'comment', ... 'Order of the demodulator low-pass filter'); addParam(Hdr, field_name, 'lowpass_bw', this.lowpass_bw, ... 'comment', ['(Hz), 3 dB bandwidth of the demodulator ', ... 'low-pass filter']); addParam(Hdr, field_name, 'demod_rate', this.demod_rate, ... 'comment', '(samples/s), demodulator data transfer rate'); addParam(Hdr, field_name, 'poll_duration', ... this.poll_duration, 'comment', '(s)'); addParam(Hdr, field_name, 'poll_timeout', ... this.poll_timeout, 'comment', '(ms)'); end end %% Set and get methods methods function freq=get.drive_osc_freq(this) path=sprintf('/%s/oscs/%i/freq', this.dev_id, this.drive_osc-1); freq=ziDAQ('getDouble', path); end function set.drive_osc_freq(this, val) assert(isfloat(val), ... 'Oscillator frequency must be a floating point number') path=sprintf('/%s/oscs/%i/freq', this.dev_id, this.drive_osc-1); ziDAQ('setDouble', path, val); end function freq=get.meas_osc_freq(this) path=sprintf('/%s/oscs/%i/freq', this.dev_id, this.meas_osc-1); freq=ziDAQ('getDouble', path); end function set.meas_osc_freq(this, val) assert(isfloat(val), ... 'Oscillator frequency must be a floating point number') path=sprintf('/%s/oscs/%i/freq', this.dev_id, this.meas_osc-1); ziDAQ('setDouble', path, val); end function set.drive_on(this, val) path=sprintf('/%s/sigouts/%i/on',this.dev_id,this.drive_out-1); % Use double() to convert from logical type ziDAQ('setInt', path, double(val)); end function bool=get.drive_on(this) path=sprintf('/%s/sigouts/%i/on',this.dev_id,this.drive_out-1); bool=logical(ziDAQ('getInt', path)); end function set.current_osc(this, val) assert((val==this.drive_osc) && (val==this.meas_osc), ... ['The number of current oscillator must be that of ', ... 'the drive or measurement oscillator']) ziDAQ('setInt', [this.demod_path,'/oscselect'], val-1); end function osc_num=get.current_osc(this) osc_num=ziDAQ('getInt', [this.demod_path,'/oscselect']); end function amp=get.drive_amp(this) path=sprintf('/%s/sigouts/%i/amplitudes/%i', ... this.dev_id, this.drive_out-1, this.drive_osc-1); amp=ziDAQ('getDouble', path); end function set.drive_amp(this, val) path=sprintf('/%s/sigouts/%i/amplitudes/%i', ... this.dev_id, this.drive_out-1, this.drive_osc-1); ziDAQ('setDouble', path, val); end function set.lowpass_order(this, val) assert(any(val==[1,2,3,4,5,6,7,8]), ['Low-pass filter ', ... 'order must be an integer between 1 and 8']) ziDAQ('setInt', [this.demod_path,'/order'], val); end function n=get.lowpass_order(this) n=ziDAQ('getInt', [this.demod_path,'/order']); end function bw=get.lowpass_bw(this) tc=ziDAQ('getDouble', [this.demod_path,'/timeconstant']); bw=ziBW2TC(tc, this.lowpass_order); end function set.lowpass_bw(this, val) tc=ziBW2TC(val, this.lowpass_order); ziDAQ('setDouble', [this.demod_path,'/timeconstant'], tc); end function rate=get.demod_rate(this) rate=ziDAQ('getDouble', [this.demod_path,'/rate']); end function set.demod_rate(this, val) ziDAQ('setDouble', [this.demod_path,'/rate'], val); end + + function val=get.downsample_n(this) + val=ceil(this.downsample_t*this.demod_rate); + end end end