diff --git a/GUIs/GuiTekMso.mlapp b/GUIs/GuiTekMso.mlapp index 6dfb2ae..af3f61f 100644 Binary files a/GUIs/GuiTekMso.mlapp and b/GUIs/GuiTekMso.mlapp differ diff --git a/Instrument classes/@MyTekMso/MyTekMso.m b/Instrument classes/@MyTekMso/MyTekMso.m index c8e6249..9bc1c6d 100644 --- a/Instrument classes/@MyTekMso/MyTekMso.m +++ b/Instrument classes/@MyTekMso/MyTekMso.m @@ -1,121 +1,244 @@ % Class for controlling 4-channel Tektronix MSO scopes. classdef MyTekMso < MyTekScope methods (Access = public) function this = MyTekMso(varargin) this.gui_name = 'GuiTekMso'; P = MyClassParser(this); processInputs(P, this, varargin{:}); this.knob_list = lower({'GPKNOB1', 'GPKNOB2', 'HORZPos', ... 'HORZScale', 'TRIGLevel', 'PANKNOB1', 'ZOOM', ... 'VERTPOS1', 'VERTPOS2', 'VERTPOS3', 'VERTPOS4', ... 'VERTSCALE1', 'VERTSCALE2', 'VERTSCALE3', 'VERTSCALE4'}); % Create communication object connect(this); % 2e7 is the maximum trace size of DPO4034-3034 %(10 mln point of 2-byte integers) this.Comm.InputBufferSize = 2.1e7; %byte createCommandList(this); end end methods (Access = protected) function createCommandList(this) addCommand(this, 'channel',':DATa:SOUrce', ... 'format', 'CH%i', ... 'info', ['Channel from which the trace ' ... 'is transferred'], ... 'value_list', {1, 2, 3, 4}); %%%% Not implemented in UI. What's selected on display %%%% is not relevant. addCommand(this, 'ctrl_channel', ':DISplay:SELect:SOUrce', ... 'format', 'CH%i', ... 'info', ['Channel currently selected in ' ... 'the scope display'], ... 'value_list', {1, 2, 3, 4}); %Changed to any number input addCommand(this, 'point_no', ':HORizontal:RECOrdlength', ... 'format', '%i', ... 'info', 'Numbers of points in the scope trace (1k-62.5M)'); addCommand(this, 'time_scale', ':HORizontal:SCAle', ... 'format', '%e', ... 'info', 'Time scale (s/div)'); %Added EITHER option addCommand(this, 'trig_slope', ':TRIGger:A:EDGE:SLOpe', ... 'format', '%s', ... 'value_list', {'RISe','FALL','EITHER'}); %Deleted EXT, AUX, does not seem to support on MSO addCommand(this, 'trig_source', ':TRIGger:A:EDGE:SOUrce', ... 'format', '%s', ... 'value_list', {'CH1','CH2','CH3','CH4','LINE'}); % addCommand(this, 'trig_mode', ':TRIGger:A:MODe', ... 'format', '%s', ... 'value_list', {'AUTO', 'NORMal'}); addCommand(this, 'acq_state', ':ACQuire:STATE', ... 'format', '%b',... 'info', 'State of data acquisition by the scope'); addCommand(this, 'acq_mode', ':ACQuire:MODe', ... 'format', '%s', ... 'info', ['Acquisition mode: sample, peak ' ... 'detect, high resolution, average or envelope'], ... 'value_list', {'SAMple', 'PEAKdetect', 'HIRes', ... 'AVErage', 'ENVelope'}); - + + addCommand(this, 'avg_point_no', ':ACQuire:NUMAVg', ... + 'format', '%i', ... + 'info', 'Numbers of averages (2-10240)'); + + % Spectrum view + addCommand(this, 'span', ':SV:SPAN', ... + 'format', '%e', ... + 'info', '(Hz)'); + + addCommand(this, 'rbw', ':SV:RBW', ... + 'format', '%e', ... + 'info', '(Hz))'); + + addCommand(this, 'rbwratio', ':SV:SPANRBWRatio', ... + 'format', '%e', ... + 'info', 'span / rbw'); + + addCommand(this, 'auto_rbw', ':SV:RBWMode', ... + 'format', '%s',... + 'info', 'Adjust Span|RBW based on Ratio',... + 'value_list', {'AUTOMATIC','MANUAL'}); + % Parametric commands for i = 1:this.channel_no i_str = num2str(i); %%%% removed GND, not supported by MSO addCommand(this,... ['cpl',i_str],[':CH',i_str,':COUP'], ... 'format', '%s', ... 'info', 'Channel coupling: AC or DC', ... 'value_list', {'DC','AC'}); % impedances, 1MOhm or 50 Ohm, changed string value list addCommand(this,... ['imp', i_str], [':CH', i_str, ':TERmination'],... 'format', '%s', ... 'info', 'Channel impedance: 1 MOhm or 50 Ohm', ... 'value_list', {'1.0000E+6', '50.0000'}); % Offset Max/Min +-10V addCommand(this, ... ['offset',i_str], [':CH',i_str,':OFFSet'], ... 'format', '%e', ... 'info', '(V)'); % Scale Max 10V/div, Min 0.0005V/div addCommand(this, ... ['scale',i_str], [':CH',i_str,':SCAle'], ... 'format', '%e', ... 'info', 'Channel y scale (V/div)'); addCommand(this,... ['enable',i_str], [':SEL:CH',i_str], ... 'format', '%b',... 'info', 'Channel enabled'); %%%% MSO can specify the trigger level to any channel addCommand(this,... ['trig_lev',i_str], [':TRIGger:A:LEVel:CH',i_str],... 'format', '%e', ... 'info', '(V)'); + addCommand(this,... + ['start_freq',i_str],[':CH',i_str,':SV:STARTFrequency'],... + 'format', '%e', ... + 'info', '(Hz)'); + + addCommand(this,... + ['stop_freq',i_str],[':CH',i_str,':SV:STOPFrequency'],... + 'format', '%e', ... + 'info', '(Hz)'); + + addCommand(this,... + ['center_freq',i_str],[':CH',i_str,':SV:CENTERFrequency'],... + 'format', '%e', ... + 'info', '(Hz)'); + + addCommand(this,... + ['sv_state',i_str],[':CH',i_str,':SV:STATE'],... + 'format', '%b', ... + 'info', 'Set SV on | off'); + + addCommand(this,... + ['sv_norm',i_str],[':SV:CH',i_str,':SELect:RF_NORMal'],... + 'format', '%b', ... + 'info', 'Set SV_normal on | off'); + + addCommand(this,... + ['sv_avg',i_str],[':SV:CH',i_str,':SELect:RF_AVErage'],... + 'format', '%b', ... + 'info', 'Set SV__average on | off'); + + addCommand(this,... + ['sv_avg_no',i_str],[':SV:CH',i_str,':RF_AVErage:NUMAVg'],... + 'format', '%i', ... + 'info', 'Number of average for SV (2~2^9)'); end end end + + methods (Access = public) + %%%% spectrum data aquisition + function readTrace_SV(this,arg1,arg2) + % Read units, offsets and steps for the scales + % Moved the parm query before the data aquisition + % because it seems that MSO has a problem responding + % to query after data aquisition + + % set the aquisition source to spectrum trace + writeStrings(this,... + sprintf(':DATa:SOUrce CH%i_SV_%s',arg2,arg1)); + parms = queryStrings(this, ... + ':WFMOutpre:XUNit?', ... + ':WFMOutpre:YUNit?', ... + ':WFMOutpre:XINcr?', ... + ':WFMOutpre:YMUlt?', ... + ':WFMOutpre:XZEro?', ... + ':WFMOutpre:YZEro?', ... + ':WFMOutpre:YOFf?'); + % Read raw y data + + % Configure data transfer: binary format and two bytes per + % point. Then query the trace. + this.Comm.ByteOrder = 'bigEndian'; + + writeStrings(this, ... + ':DATA:ENCDG RIBinary', ... + ':DATA:WIDTH 2', ... + ':DATA:STARt 1', ... + ':DATA:STOP 1000', ... + ':CURVe?'); + + y_data = double(binblockread(this.Comm, 'int16')); + + % read off the terminating character + % which can not be read by the binblockread + if this.Comm.BytesAvailable == 1 || this.Comm.BytesAvailable == 2 + fread(this.Comm,this.Comm.BytesAvailable,'uint8'); + end + + % For some reason MDO3000 scope needs to have an explicit pause + % between data reading and any other communication + pause(0.01); + + num_params = str2doubleHedged(parms); + [unit_x, unit_y, step_x, step_y, x_zero, ... + y_zero, y_offset] = num_params{:}; + + % Calculating the y data + y = (y_data-y_offset)*step_y+y_zero; + n_points = length(y); + + % Calculating the x data + x = linspace(x_zero, x_zero + step_x*(n_points-1), n_points); + + this.Trace.x = x; + this.Trace.y = y; + + % Discard "" when assiging the Trace labels + this.Trace.unit_x = unit_x(2:end-1); + this.Trace.unit_y = unit_y(2:end-1); + writeStrings(this,... + sprintf(':DATa:SOUrce CH%i',arg2)); + triggerNewData(this); + end + end end \ No newline at end of file diff --git a/Instrument classes/@MyTekScope/MyTekScope.m b/Instrument classes/@MyTekScope/MyTekScope.m index 8aa761a..091421f 100644 --- a/Instrument classes/@MyTekScope/MyTekScope.m +++ b/Instrument classes/@MyTekScope/MyTekScope.m @@ -1,110 +1,116 @@ % Generic class for controlling Tektronix scopes classdef MyTekScope < MyScpiInstrument & MyDataSource & MyCommCont ... & MyGuiCont properties (GetAccess = public, SetAccess={?MyClassParser,?MyTekScope}) % number of channels channel_no = 4 % List of the physical knobs, which can be rotated programmatically knob_list = {} end methods (Access = public) function this = MyTekScope(varargin) % Set default GUI name this.gui_name = 'GuiTekScope'; this.Trace.name_x = 'Time'; this.Trace.name_y = 'Voltage'; end function readTrace(this) % Read units, offsets and steps for the scales % Moved the parm query before the data aquisition % because it seems that MSO has a problem responding % to query after data aquisition parms = queryStrings(this, ... ':WFMOutpre:XUNit?', ... ':WFMOutpre:YUNit?', ... ':WFMOutpre:XINcr?', ... ':WFMOutpre:YMUlt?', ... ':WFMOutpre:XZEro?', ... ':WFMOutpre:YZEro?', ... ':WFMOutpre:YOFf?'); % Read raw y data y_data = readY(this); num_params = str2doubleHedged(parms); [unit_x, unit_y, step_x, step_y, x_zero, ... y_zero, y_offset] = num_params{:}; % Calculating the y data y = (y_data-y_offset)*step_y+y_zero; n_points = length(y); % Calculating the x data x = linspace(x_zero, x_zero + step_x*(n_points-1), n_points); this.Trace.x = x; this.Trace.y = y; % Discard "" when assiging the Trace labels this.Trace.unit_x = unit_x(2:end-1); this.Trace.unit_y = unit_y(2:end-1); triggerNewData(this); end function acquireContinuous(this) writeStrings(this, ... ':ACQuire:STOPAfter RUNSTop', ... ':ACQuire:STATE ON'); end function acquireSingle(this) writeStrings(this, ... ':ACQuire:STOPAfter SEQuence', ... ':ACQuire:STATE ON'); end function turnKnob(this, knob, nturns) writeString(this, sprintf(':FPAnel:TURN %s,%i', knob, nturns)); end end methods (Access = protected) % The default version of this method works for DPO3034-4034 scopes function y_data = readY(this) % Configure data transfer: binary format and two bytes per % point. Then query the trace. this.Comm.ByteOrder = 'bigEndian'; writeStrings(this, ... ':DATA:ENCDG RIBinary', ... ':DATA:WIDTH 2', ... ':DATA:STARt 1', ... sprintf(':DATA:STOP %i', this.point_no), ... ':CURVe?'); y_data = double(binblockread(this.Comm, 'int16')); + % read off the terminating character + % which can not be read by the binblockread + if this.Comm.BytesAvailable == 1 || this.Comm.BytesAvailable == 2 + fread(this.Comm,this.Comm.BytesAvailable,'uint8'); + end + % For some reason MDO3000 scope needs to have an explicit pause % between data reading and any other communication pause(0.01); end end methods function set.knob_list(this, val) assert(iscellstr(val), ['Value must be a cell array of ' ... 'character strings.']) %#ok this.knob_list = val; end end end