classdef RigExpert < hgsetget
%RigExpert AA230-PRO Driver V2.4 for MATLAB.
%----------------------------------------------------------------------------------
% Copyright (C) MSc. Gergely Ollos, February 23, 2013. (gergely.ollos@gmail.com)
% All rights reserved.
%----------------------------------------------------------------------------------
% LICENSE:
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
%
%----------------------------------------------------------------------------------
% Basic description:
% This module controls the rig expert complex impedance meter. Generally you
% have to create the object first, then open the port and make your various
% measurements or generate signals etc. After you are done, close the
% communication channel and finally delete the object.
%
%----------------------------------------------------------------------------------
% EXAMPLE(1): Measuring complex impedance from 300KHz to 200MHz of 1000 points
% AA=RigExpert('COM13');
% serOpen(AA);
% X=Measure(AA,300E3,200E6,1000);
% serClose(AA);
%
% Where X is a [n by 3] matrix, where the first column is the frequency in Hz, the
% second column is the resistance and the third comulm is the reactance, both in
% ohms.
%
%----------------------------------------------------------------------------------
% EXAMPLE(2): Measuring and plotting the complex impedance from 300KHz to 200MHz
% AA=RigExpert('COM13');
% serOpen(AA);
% X=Measure(AA,300E3,100E6,300,'plot');
% serClose(AA);
%
% Where X is a [n by 3] matrix, where the first column is the frequency in Hz, the
% second column is the resistance and the third comulm is the reactance, both in
% ohms.
%
%----------------------------------------------------------------------------------
% EXAMPLE(3): Generate 1 sec long 10dBm (max amplitude) 10MHz signal
% AA=RigExpert();
% serOpen(AA,'COM13'); %The com port can be specifies here as well
% setGen(AA,1E6,1);
% genOn(AA);
% pause(1);
% genOff(AA);
% serClose(AA);
%
%----------------------------------------------------------------------------------
% EXAMPLE(4): Generating the following pattern:
% 1 second long 1MHz signal of 0.7 amplitude
% 0.5 second long silence
% 2 second long 1MHz signal of 0.2 amplitude
% 1sec long 5MHz signal of 1 amplitude
% -------------------------------------------------
% AA=RigExpert('COM13');
% serOpen(AA); %Returns "1" if succesful else "0"
% setGen(AA,1E6,0.7);
% genOn(AA);
% pause(1); %1sec 1MHZ signal
% genOff(AA);
% pause(0.5); %0.5sec silence
% setAmp(AA,0.2);
% genOn(AA);
% pause(2); %2sec 1MHz signal
% setGen(AA,5E6,1);
% pause(1); %1sec 5MHz signal
% genOff(AA);
% serClose(AA);
%
%----------------------------------------------------------------------------------
% EXAMPLE(5): Frequency and amplitude modulation demo
% AA=RigExpert('COM13');
% serOpen(AA);
% setGen(AA,1E6,0.5);
% genOn(AA);
%
% for i=1:0.1:10
% setAmp(AA,abs(sin(i))); %Amplitude modulation
% end
% for i=1:0.1:30
% setGen(AA,3E6+(2.7E6)*sin(i),1); %Frequency modulation
% end
% for i=1:0.1:30
% setGen(AA,3E6+(2.7E6)*sin(i),abs(sin(i))); %Both
% end
%
% genOff(AA);
% serClose(AA);
%
%----------------------------------------------------------------------------------
% EXAMPLE (6): Getting the current screen of the RigExpert
% AA=RigExpert('COM13');
% img=screenShot(AA);
% imshow(img);
%
% Where img is a [64x128] binary matrix that stores the image. No need to close
% the channel, since it is automatically closed.
%
%----------------------------------------------------------------------------------
properties (SetAccess=private)
% The serial COM object
ComObj;
% Scan/Generator default frequency
genFreq=1E6;
% Default scanner frequency
genWidth=100E3;
% Default reference phase
phased=90;
% Default measurement delay
mdelay=4000;
% default number of returned samples
nsamp=10;
% Default generator amplitude (max 10dBm when genAmp=1)
genAmp=0.5;
% Default serial port
defSerPrt='COM13';
% This variable signalizes active serial communication
activeSer=0;
end
methods
%% CONSTRUCTOR
function Eobj = RigExpert(serPrt)
% Creating the serial port object
if exist('serPrt','var')
Eobj.ComObj=serial(serPrt,'BaudRate',38400,'InputBufferSize',4096);
end
end
%% OPENING THE PORT
function ret=serOpen(obj,serPrt)
% Checking if there is an active communication channel already established
if obj.activeSer
fprintf(2,'First you have to close the active channel!\n');
return;
end
% Creating a new serial object if a new parameter is given
if exist('serPrt','var')
obj.ComObj=serial(serPrt,'BaudRate',38400,'InputBufferSize',4096);
end
% Checking if there is a serial object, if not, creating a default
if ~isobject(obj.ComObj)
fprintf(['There was no com port specified! '...
'Opening the default %s port.\n'],obj.defSerPrt);
obj.ComObj=serial(obj.defSerPrt,'BaudRate',38400,'InputBufferSize',4096);
end
% Attempting to open the serial port
ret=1;
try
fopen(obj.ComObj);
catch err %#ok<NASGU>
ret=0;
fprintf('Unable to open the specified port!\n');
return
end
% Making a communication attempt
fprintf(obj.ComObj,'VER\n');
if ~waitForIt(obj,23,1)
fclose(obj.ComObj);
ret=0;
fprintf('The rig expert is not responding (make sure it is in PC mode)!\n');
return;
end
resp = fread(obj.ComObj, obj.ComObj.BytesAvailable);
strings = regexp(char(resp)','\r\n','split'); %#ok<FREAD>
if (length(strings) < 3) || (~strcmp(strings{2},'AA-230PRO 312'))
ret=0;
fclose(obj.ComObj);
reply = input('Wrong firmware! Continue anyway? (Y/N) [Y]: ', 's');
if ~(isempty(reply) || strcmpi(reply,'Y'))
return
end
end
% Initializing basic parameters
cmdseq=updateCmds(obj);
for i=[1 2 3 4 5]
fprintf(obj.ComObj,cmdseq{i}); waitForIt(obj,6,1);
if ~strcmp(strtrim(char(fread(obj.ComObj, ...
obj.ComObj.BytesAvailable))'),'OK') %#ok<FREAD>
ret=0;
fprintf('Unable to configure the device! Channel left open!\n');
return
end
end
% Indicating the active channel
obj.activeSer=ret;
end
%% TURNING ON THE GENERATOR
function genOn(obj)
% Checking if there is an active channel
if ~obj.activeSer
fprintf(2,'First you have to connect to the analyser!\n');
return;
end
cmdseq=updateCmds(obj);
% Turning on the generator
for i=[8 1]
fprintf(obj.ComObj,cmdseq{i}); waitForIt(obj,6,1); %ON
if ~strcmp(strtrim(char(fread(obj.ComObj, ...
obj.ComObj.BytesAvailable))'),'OK') %#ok<FREAD>
fprintf('Unable to configure the device!\n');
end
end
serialFlush(obj);
end
%% TURNING OFF THE GENERATOR
function genOff(obj)
% Checking if there is an active channel
if ~obj.activeSer
fprintf(2,'First you have to connect to the analyzer!\n');
return;
end
cmdseq=updateCmds(obj);
% Turning off the generator
fprintf(obj.ComObj,cmdseq{7}); waitForIt(obj,6,1); %OFF
if ~strcmp(strtrim(char(fread(obj.ComObj, ...
obj.ComObj.BytesAvailable))'),'OK') %#ok<FREAD>
fprintf('Unable to configure the device!\n');
end
end
%% SETTING THE GENERATOR AMPLITUDE
function setAmp(obj,amp)
% Checking if there is an active channel
if ~obj.activeSer
fprintf(2,'First you have to connect to the analyzer!\n');
return;
end
% Checking the range
if ((amp < 0) || (amp > 1))
fprintf(2,'Wrong amplitude!\n');
return;
end
% Refreshing basic parameters
obj.genAmp=amp;
cmdseq=updateCmds(obj);
fprintf(obj.ComObj,cmdseq{1}); waitForIt(obj,6,1);
if ~strcmp(strtrim(char(fread(obj.ComObj, ...
obj.ComObj.BytesAvailable))'),'OK') %#ok<FREAD>
fprintf('Unable to configure the device!\n');
return
end
serialFlush(obj);
end
%% MAKING A MEASUREMENT
function dta=Measure(obj,ffrom,fto,nums,plotit)
% Checking if there is an active channel
if ~obj.activeSer
fprintf(2,'First you have to connect to the analyzer!\n');
return;
end
% Checking the parameters
dta=NaN;
if (ffrom >= fto) || (ffrom < 300E3) || (ffrom > 230E6) || ...
(fto < 300E3) || (fto > 230E6) || (nums > 10000) || (nums < 2)
fprintf(2,'Wrong parameters (freqency is in Hz)!\n');
return;
end
% Initializing theRigExpert variables
updateVec=[];
if (obj.genFreq ~= round((ffrom+fto)/2))
obj.genFreq=round((ffrom+fto)/2);
updateVec(end+1)=2;
end
if (obj.genWidth ~= fto-ffrom)
obj.genWidth=fto-ffrom;
updateVec(end+1)=3;
end
obj.nsamp=nums-1; %RigExpert always returns one more sample
cmdseq=updateCmds(obj);
% Updating the device
for i=updateVec
fprintf(obj.ComObj,cmdseq{i}); waitForIt(obj,6,1);
if ~strcmp(strtrim(char(fread(obj.ComObj, ...
obj.ComObj.BytesAvailable))'),'OK') %#ok<FREAD>
fprintf('Unable to configure the device!\n');
return
end
end
serialFlush(obj);
% Asking for samples
fprintf(obj.ComObj,cmdseq{6}); %FRX
% Collecting the data without delay as it coms
data=[]; tim=tic;
while(toc(tim) < 60)
if (obj.ComObj.BytesAvailable > 0)
data=[data fread(obj.ComObj, obj.ComObj.BytesAvailable)';]; %#ok<AGROW>
end
if ((length(data) >= 6) && all(data(end-5:end)==[13 10 79 75 13 10]))
% This is the end pointer
break;
end
end
if (toc(tim) >= 60)
fprintf(2,'Unable to recive the data!\n');
return;
end
serialFlush(obj);
% Processing the strings
strings = regexp(char(data),'\r\n','split');
% Filtering the strings
nstr={};
for i=1:length(strings)
if length(strings{i}) > 5
nstr{end+1}=strings{i}; %#ok<AGROW>
end
end
strings=nstr;
% Converting the sub-strings
for i=1:length(strings)
dtas=regexp(strings{i},',','split');
fr=str2double(dtas{1})*1E6;
RR=str2double(dtas{2});
XX=str2double(dtas{3});
if isnan(dta)
dta=[fr RR XX];
else
dta=[dta; [fr RR XX]]; %#ok<AGROW>
end
end
% Turning off the generator
genOff(obj);
%Plotting the results if requested
if (exist('plotit','var') && strcmpi(plotit,'plot'))
% Create figure
sc = get(0,'ScreenSize');
figure1=figure('Name','Complex Impedance','NumberTitle','off','Resize',...
'off','Color',[1 1 1],'Position',[sc(3)/2-375,sc(4)-465,719,379]);
% Create axes
axes1 = axes('Parent',figure1,'YMinorTick','on','YMinorGrid','off',...
'YGrid','off',...
'XMinorTick','on',...
'XGrid','off',...
'FontSize',16);
hold(axes1,'all');
% Create multiple lines using matrix input to plot
if max(max(abs(dta(:,2:3)))) > 1E3
RScale=1E3;
else
RScale=1;
end
if max(dta(:,1)) > 1E6
FScale=1E6;
elseif max(dta(:,1)) > 1E3
FScale=1E3;
else
FScale=1;
end
XData=dta(:,1)./FScale;
RData=dta(:,2)./RScale;
CData=dta(:,3)./RScale;
% If smothing the data is acceptable (presuming not)
% RData = pchip(XData,RData,linspace(min(XData),max(XData),1000))';
% CData = pchip(XData,CData,linspace(min(XData),max(XData),1000))';
% XData=linspace(min(XData),max(XData),1000)';
plot1 = plot(XData,[RData CData],'Parent',axes1,'LineWidth',1);
set(plot1(1),'DisplayName','R');
set(plot1(2),'LineStyle','-','DisplayName','X');
if max(dta(:,1)) > 1E6
xlabel('frequency [MHz]','FontSize',16);
elseif max(dta(:,1)) > 1E3
xlabel('frequency [KHz]','FontSize',16);
else
xlabel('frequency [Hz]','FontSize',16);
end
% Create ylabel
if max(max(abs(dta(:,2:3)))) > 1E3
ylabel('reactance [Kohm]','FontSize',16);
else
ylabel('reactance [Ohm]','FontSize',16);
end
% Create title
title('complex impedance','FontSize',16);
% Create legend
legend1 = legend(axes1,'show');
set(legend1,'EdgeColor',[1 1 1],'YColor',[1 1 1],'XColor',[1 1 1]);
legend('boxoff');
set(gca, 'Position', get(gca, 'OuterPosition') - ...
get(gca, 'TightInset') * [-1 0 1 0; 0 -1 0 1; 0 0 1 0; 0 0 0 1]);
axis tight;
end
end
%% SETTING THE GENERATOR
function setGen(obj, freq, amp)
% Checking if there is an active channel
if ~obj.activeSer
fprintf(2,'First you have to connect to the analyzer!\n');
return;
end
% Checking the range
if ((amp < 0) || (amp > 1) || (freq < 300E3) || (freq > 230E6))
fprintf(2,'Wrong amplitude (0..1) or frequency (300E3..230E6)!\n');
return;
end
% Initializing the basic parameters (only those that were changed)
updateVec=[];
if (obj.genAmp ~= amp)
obj.genAmp=amp;
updateVec(end+1)=1;
end
if (obj.genFreq ~= freq)
obj.genFreq = freq;
updateVec(end+1)=2;
end
cmdseq=updateCmds(obj);
for i=updateVec
fprintf(obj.ComObj,cmdseq{i}); waitForIt(obj,6,1);
if ~strcmp(strtrim(char(fread(obj.ComObj, ...
obj.ComObj.BytesAvailable))'),'OK') %#ok<FREAD>
fprintf('Unable to configure the device!\n');
return
end
end
serialFlush(obj);
end
%% GETTING THE SCREENSHOT FROM THE RIGEXPERT
function binIm=screenShot(obj,serPrt)
% Checking if there is an active channel
if obj.activeSer
fprintf(2,['First close the existing connection '...
'end exit computer mode on the RigExpert!\n']);
return;
end
% Trying to open the port
if exist('serPrt','var')
obj.defSerPrt=serPrt;
end
CObj=serial(obj.defSerPrt,'BaudRate',38400,'InputBufferSize',4096);
fprintf('Please press F+6 on RigExpert when ready!\n');
% Attempting to open the serial port
try
fopen(CObj);
catch err %#ok<NASGU>
fprintf('Unable to open the port!\n');
return
end
% Waiting for the data to arrive
while(CObj.BytesAvailable < 2064); end;
% This is the raw data from the device
rawdata=fread(CObj, CObj.BytesAvailable)';
%This is the payload part
hexdat=rawdata(15:(14+2048));
% Convering the hexadecimal string to bytes
dscreen=NaN*ones(1,1024);
for i=1:length(dscreen)
% These are the hexadecimal pairs of data that has to be converted to bytes
dscreen(i)=hex2dec(char(hexdat([((i-1)*2+1) ((i-1)*2+2)])));
end
% Convering the byte streams into binary pixels
binIm=NaN*ones(64,128);
for y=1:size(binIm,1)
for x=1:size(binIm,2)
%Getting the address of the data inside the linear byte vector
byteLinearAddr=x+(ceil(y/8)-1)*128;
%Extracting the data byte from the linear byte vector
dataByte=uint8(dscreen(byteLinearAddr));
%Getting the address of the bit inside the dataByte
bitAddr=mod(y-1,8)+1;
%Getting the binary bit position mask which flags the desired bit
bitMask=bitshift(1,bitAddr-1,'uint8');
%Getting the bit from the dataByte on the flagged position by the mask
binIm(y,x)=~logical(bitand(dataByte,bitMask,'uint8'));
end
end;
% Closing the communication channel
serialFlush(obj);
fclose(CObj);
end
%% CLOSING THE SERIAL PORT
function serClose(obj)
% Checking if there is an active channel
if ~obj.activeSer
fprintf(2,'First you have to open a channel before closing it!\n');
return;
end
fclose(obj.ComObj);
obj.activeSer=0;
end
end
end
%% FLUSHING THE SERIAL PORT BUFFER
function serialFlush(obj)
while (obj.ComObj.BytesAvailable > 0)
fread(obj.ComObj, obj.ComObj.BytesAvailable);
fprintf(2,'There were bytes to flush!\n');
end
end
%% WAITF FOR THE SERIAL DATA TO ARRIVE
function ret=waitForIt(obj,dnum,dela)
ret=1; t1=tic;
while ((obj.ComObj.BytesAvailable < dnum) && (toc(t1) < dela)); end;
if (toc(t1) >= dela)
ret=0;
fprintf(2,'Communication error (time out)!\n');
end
end
%% THIS FUNCTION UPDATES THE CMD VECTOR WITH ACTUAL PARAMETERS
function cmdseq=updateCmds(obj)
cmdseq{1}=sprintf('am%.f',round(obj.genAmp*1023));
cmdseq{2}=sprintf('fq%.f',obj.genFreq);
cmdseq{3}=sprintf('sw%.f',obj.genWidth);
cmdseq{4}=sprintf('ph%.f',obj.phased);
cmdseq{5}=sprintf('de%.f',obj.mdelay);
cmdseq{6}=sprintf('frx%.f',obj.nsamp);
cmdseq{7}=sprintf('off');
cmdseq{8}=sprintf('on');
cmdseq{9}=sprintf('ver');
end