mirror of
https://github.com/njchorda/MATLAB-Touchstone-Reader.git
synced 2026-02-25 11:00:45 -05:00
Add files via upload
This commit is contained in:
717
SPARAMS.m
Normal file
717
SPARAMS.m
Normal file
@@ -0,0 +1,717 @@
|
|||||||
|
classdef SPARAMS < handle
|
||||||
|
% Author: Nathan Chordas-Ewell
|
||||||
|
% Written: 2020
|
||||||
|
%Works on up to 4 port measurements in RI, MA, dBA formats (Som
|
||||||
|
%Usage:
|
||||||
|
% s = SPARAMS('filename')
|
||||||
|
% s.setZ0(Z0)
|
||||||
|
% If you do not want to associate a file
|
||||||
|
% s = SPARAMS()
|
||||||
|
% s.setNumPorts(N)
|
||||||
|
% s.f = freq; s.S11 = s11; ....
|
||||||
|
%Copy an object to a new object (without referencing a pointer)
|
||||||
|
% snew = s.copyobj();
|
||||||
|
%Get some value:
|
||||||
|
% s11 = s.S11 in R + jI format
|
||||||
|
%Plot any trace in dB:
|
||||||
|
% s.plotdB('S11')
|
||||||
|
% or for multiple, put arguments in cell: s.plotdB({'S11', 'S21'})
|
||||||
|
%Plot all:
|
||||||
|
% s.plotAlldB
|
||||||
|
%To get the S matrix at a specific frequency:
|
||||||
|
% s.toMat(freq)
|
||||||
|
%To renormalize to a different system impedance
|
||||||
|
% s.renorm(Z0new)
|
||||||
|
% This will also automatically reset Z0 to the new value
|
||||||
|
%To cascade N times
|
||||||
|
% s.cascade(N)
|
||||||
|
%Plot dispersion diagram when s is a unit cell
|
||||||
|
% s.plotDispersion
|
||||||
|
%Export a csv file of the S parameter data
|
||||||
|
% s.writeCSV('filename.csv')
|
||||||
|
%Deembed S parameters to the DUT. Returns an SPARAMS object of the DUT
|
||||||
|
% P1 <--err1--DUT--err2--> P2
|
||||||
|
% DUT = SPARAMS.deembed(err1, measured, err2)
|
||||||
|
|
||||||
|
properties
|
||||||
|
f;
|
||||||
|
S11;S12;S13;S14;S21;S22;S23;S24;S31;S32;S33;S34;S41;S42;S43;S44;
|
||||||
|
Z0;
|
||||||
|
numPorts;
|
||||||
|
end
|
||||||
|
properties(Access = private)
|
||||||
|
sparams;
|
||||||
|
rawData; data; form; filename; formStr;
|
||||||
|
freqScale;
|
||||||
|
end
|
||||||
|
|
||||||
|
methods
|
||||||
|
function obj = SPARAMS(filename)
|
||||||
|
switch nargin
|
||||||
|
case 1
|
||||||
|
obj.setFile(filename);
|
||||||
|
case 0
|
||||||
|
warning('No data set. Once data is assigned you will need to call setNumPorts() to set the number of ports.');
|
||||||
|
end
|
||||||
|
obj.setFreqUnits('Hz');
|
||||||
|
end
|
||||||
|
|
||||||
|
function setFile(obj, fname)
|
||||||
|
%If you wish to set a filename after creating the object. Also
|
||||||
|
%called when initializing with a sNp file.
|
||||||
|
obj.filename = fname;
|
||||||
|
obj.txt2data();
|
||||||
|
obj.getFormat();
|
||||||
|
obj.parseData();
|
||||||
|
end
|
||||||
|
|
||||||
|
function s2 = copyobj(obj)
|
||||||
|
%Copy an object without creating a pointer
|
||||||
|
% s2 = s2.copyobj();
|
||||||
|
%Note that this does not copy items such as the original
|
||||||
|
%filename, raw data, and original s2p file format
|
||||||
|
s2 = eval(class(obj));
|
||||||
|
for p = properties(obj).'
|
||||||
|
try
|
||||||
|
s2.(p{1}) = obj.(p{1});
|
||||||
|
catch
|
||||||
|
warning('Failed to copy property: %s', p);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function setNumPorts(obj, numPorts)
|
||||||
|
%To set the number of ports if object is created without a file
|
||||||
|
%s.setNumPorts(N);
|
||||||
|
%Where N = 1, 2, 3, or 4
|
||||||
|
obj.numPorts = numPorts;
|
||||||
|
end
|
||||||
|
|
||||||
|
function setZ0(obj, sysZ0)
|
||||||
|
%Set system Z0 if file does not have the information or object
|
||||||
|
%is not created with a file
|
||||||
|
%s.setZ0(z0)
|
||||||
|
%Where Z0 usually = 50, 75
|
||||||
|
obj.Z0 = sysZ0;
|
||||||
|
end
|
||||||
|
|
||||||
|
function setFreqUnits(obj, scale)
|
||||||
|
%Sets the frequency scale (Hz, kHz, MHz, GHz) for plotting.
|
||||||
|
%Does not change any S-parameter or frequency data
|
||||||
|
%s.setFreqUnits('GHz')
|
||||||
|
if strcmpi(scale, 'GHz')
|
||||||
|
obj.freqScale = {10^9, 'GHz'};
|
||||||
|
elseif strcmpi(scale, 'MHz')
|
||||||
|
obj.freqScale = {10^6, 'MHz'};
|
||||||
|
elseif strcmpi(scale, 'kHz')
|
||||||
|
obj.freqScale = {10^3, 'kHz'};
|
||||||
|
elseif strcmpi(scale, 'Hz')
|
||||||
|
obj.freqScale = {1, 'Hz'};
|
||||||
|
else
|
||||||
|
warning('Frequency scale not set. Defaulting to Hz');
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function txt2data(obj)
|
||||||
|
[~, ~, fExt] = fileparts(obj.filename);
|
||||||
|
obj.numPorts = str2num(fExt(3));
|
||||||
|
txt = fileread(obj.filename);
|
||||||
|
key = '#';
|
||||||
|
|
||||||
|
txt = regexprep(txt, '![\s\S]*?!|([^:]|^)!.*$', '', 'lineanchors', 'dotexceptnewline');
|
||||||
|
txt = regexprep(txt, '\t+', ' '); %Remove all tabs and replace with s single space (dealt with in next line)
|
||||||
|
txt = strtrim(txt); %Remove trailing whitespace
|
||||||
|
|
||||||
|
hashIndex = strfind(txt,key);
|
||||||
|
newLineIndex = regexp(txt(hashIndex:end), '[\r]');
|
||||||
|
newLineIndex = newLineIndex(1);
|
||||||
|
obj.form = txt(hashIndex:hashIndex+newLineIndex);
|
||||||
|
|
||||||
|
dataTxt = txt(hashIndex+newLineIndex:end);
|
||||||
|
%Remove comments (!)
|
||||||
|
dataTxt = regexprep(dataTxt, '![\s\S]*?!|([^:]|^)!.*$', '', 'lineanchors', 'dotexceptnewline');
|
||||||
|
dataTxt = regexprep(dataTxt, '\t+', ' '); %Remove all tabs and replace with s single space (dealt with in next line
|
||||||
|
dataTxt = strtrim(dataTxt); %Remove trailing whitespace
|
||||||
|
for j = 1:100 %Not a very efficient way of doing it, but I'm getting tired of this shit
|
||||||
|
len = 101 - j;
|
||||||
|
for k = 1:len
|
||||||
|
regStr(k) = ' ';
|
||||||
|
end
|
||||||
|
dataTxt = regexprep(dataTxt, regStr, ',');
|
||||||
|
clear regStr
|
||||||
|
end
|
||||||
|
obj.rawData = cell2mat(textscan(dataTxt, '', 'delimiter', ',', 'headerlines', 0, 'emptyvalue', nan, 'collectoutput', 1));
|
||||||
|
obj.formatMatrix();
|
||||||
|
obj.f = obj.data(:, 1);
|
||||||
|
if contains(obj.form, 'MHz', 'IgnoreCase', true)
|
||||||
|
obj.f = obj.f.*1e6;
|
||||||
|
elseif contains(obj.form, 'GHz', 'IgnoreCase', true)
|
||||||
|
obj.f = obj.f.*1e9;
|
||||||
|
elseif contains(obj.form, 'kHz', 'IgnoreCase', true)
|
||||||
|
obj.f = obj.f.*1e3;
|
||||||
|
end
|
||||||
|
obj.sparams = obj.data(:, 2:end);
|
||||||
|
end
|
||||||
|
|
||||||
|
function formatMatrix(obj)
|
||||||
|
[rows, ~] = size(obj.rawData);
|
||||||
|
if(obj.numPorts == 1 || obj.numPorts == 2)
|
||||||
|
obj.data = obj.rawData;
|
||||||
|
elseif(obj.numPorts == 3)
|
||||||
|
%Do the stuff for s3p
|
||||||
|
rr = 1;
|
||||||
|
for r = 1:3:rows-2
|
||||||
|
obj.data(rr, :) = [obj.rawData(r, 1:end) obj.rawData(r+1, 1:end-1) obj.rawData(r+2, 1:end-1)];
|
||||||
|
rr = rr + 1;
|
||||||
|
end
|
||||||
|
elseif(obj.numPorts == 4)
|
||||||
|
rr = 1;
|
||||||
|
for r = 1:4:rows-3
|
||||||
|
obj.data(rr, :) = [obj.rawData(r, 1:end-1) obj.rawData(r+1, 2:end-1) obj.rawData(r+2, 2:end-1) obj.rawData(r+3, 2:end-1)];
|
||||||
|
rr = rr + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function getFormat(obj)
|
||||||
|
obj.formStr = string(obj.form);
|
||||||
|
if contains(obj.formStr, 'f', 'IgnoreCase', true) == 0 && contains(obj.formStr, 'Hz', 'IgnoreCase', true) == 0
|
||||||
|
error('Looks like this is a time domain file, or not formatted properly.')
|
||||||
|
end
|
||||||
|
if(contains(obj.formStr, 'R', 'IgnoreCase', true))
|
||||||
|
RIndex = strfind(obj.formStr, 'R');
|
||||||
|
tempZ0str = char(obj.formStr);
|
||||||
|
tempZ0str = strtrim(tempZ0str);
|
||||||
|
Z0temp = str2double(tempZ0str(RIndex+2:end));
|
||||||
|
obj.setZ0(Z0temp);
|
||||||
|
else
|
||||||
|
warning('System Z0 not set. You may do so manually with obj.setZ0()')
|
||||||
|
end
|
||||||
|
if contains(obj.formStr, 'dB', 'IgnoreCase', true)
|
||||||
|
obj.form = 'dB';
|
||||||
|
elseif contains(obj.formStr, 'MA', 'IgnoreCase', true)
|
||||||
|
obj.form = 'MA';
|
||||||
|
elseif contains(obj.formStr, 'RI', 'IgnoreCase', true)
|
||||||
|
obj.form = 'RI';
|
||||||
|
else
|
||||||
|
error('Format unknown.');
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function S = formatCorrectly(obj, c1, c2)
|
||||||
|
%Make S parameter in RI no matter orignal format
|
||||||
|
switch obj.form
|
||||||
|
case "MA"
|
||||||
|
S = c1.*exp(j*deg2rad(c2));
|
||||||
|
case "RI"
|
||||||
|
S = c1 + j*c2;
|
||||||
|
case "dB"
|
||||||
|
S = 10.^(c1/20).*exp(j*deg2rad(c2));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parseData(obj)
|
||||||
|
switch obj.numPorts
|
||||||
|
case 1
|
||||||
|
obj.S11 = obj.formatCorrectly(obj.sparams(:, 1), obj.sparams(:, 1+1));
|
||||||
|
case 2
|
||||||
|
obj.S11 = obj.formatCorrectly(obj.sparams(:, 1), obj.sparams(:, 2));
|
||||||
|
obj.S21 = obj.formatCorrectly(obj.sparams(:, 3), obj.sparams(:, 4));
|
||||||
|
obj.S12 = obj.formatCorrectly(obj.sparams(:, 5), obj.sparams(:, 6));
|
||||||
|
obj.S22 = obj.formatCorrectly(obj.sparams(:, 7), obj.sparams(:, 8));
|
||||||
|
case 3
|
||||||
|
obj.S11 = obj.formatCorrectly(obj.sparams(:, 1), obj.sparams(:, 2));
|
||||||
|
obj.S12 = obj.formatCorrectly(obj.sparams(:, 3), obj.sparams(:, 4));
|
||||||
|
obj.S13 = obj.formatCorrectly(obj.sparams(:, 5), obj.sparams(:, 6));
|
||||||
|
obj.S21 = obj.formatCorrectly(obj.sparams(:, 7), obj.sparams(:, 8));
|
||||||
|
obj.S22 = obj.formatCorrectly(obj.sparams(:, 9), obj.sparams(:, 10));
|
||||||
|
obj.S23 = obj.formatCorrectly(obj.sparams(:, 11), obj.sparams(:, 12));
|
||||||
|
obj.S31 = obj.formatCorrectly(obj.sparams(:, 13), obj.sparams(:, 14));
|
||||||
|
obj.S32 = obj.formatCorrectly(obj.sparams(:, 15), obj.sparams(:, 16));
|
||||||
|
obj.S33 = obj.formatCorrectly(obj.sparams(:, 17), obj.sparams(:, 18));
|
||||||
|
case 4
|
||||||
|
obj.S11 = obj.formatCorrectly(obj.sparams(:, 1), obj.sparams(:, 2));
|
||||||
|
obj.S12 = obj.formatCorrectly(obj.sparams(:, 3), obj.sparams(:, 4));
|
||||||
|
obj.S13 = obj.formatCorrectly(obj.sparams(:, 5), obj.sparams(:, 6));
|
||||||
|
obj.S14 = obj.formatCorrectly(obj.sparams(:, 7), obj.sparams(:, 8));
|
||||||
|
obj.S21 = obj.formatCorrectly(obj.sparams(:, 9), obj.sparams(:, 10));
|
||||||
|
obj.S22 = obj.formatCorrectly(obj.sparams(:, 11), obj.sparams(:, 12));
|
||||||
|
obj.S23 = obj.formatCorrectly(obj.sparams(:, 13), obj.sparams(:, 14));
|
||||||
|
obj.S24 = obj.formatCorrectly(obj.sparams(:, 15), obj.sparams(:, 16));
|
||||||
|
obj.S31 = obj.formatCorrectly(obj.sparams(:, 17), obj.sparams(:, 18));
|
||||||
|
obj.S32 = obj.formatCorrectly(obj.sparams(:, 19), obj.sparams(:, 20));
|
||||||
|
obj.S33 = obj.formatCorrectly(obj.sparams(:, 21), obj.sparams(:, 22));
|
||||||
|
obj.S34 = obj.formatCorrectly(obj.sparams(:, 23), obj.sparams(:, 24));
|
||||||
|
obj.S41 = obj.formatCorrectly(obj.sparams(:, 25), obj.sparams(:, 26));
|
||||||
|
obj.S42 = obj.formatCorrectly(obj.sparams(:, 27), obj.sparams(:, 28));
|
||||||
|
obj.S43 = obj.formatCorrectly(obj.sparams(:, 29), obj.sparams(:, 30));
|
||||||
|
obj.S44 = obj.formatCorrectly(obj.sparams(:, 31), obj.sparams(:, 32));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function [M, fAct] = toMat(obj, freq)
|
||||||
|
%Gives S matrix at a specified frequency.
|
||||||
|
%If frequency is not found, gives data at closest frequency
|
||||||
|
%[S, fActual] = s.toMat(freq);
|
||||||
|
%Where S is a numPort x numPort matrix at the specified
|
||||||
|
%frequency (or the closest)
|
||||||
|
[~, ind] = min(abs(obj.f-freq));
|
||||||
|
fAct = obj.f(ind);
|
||||||
|
if obj.f(ind) ~= freq
|
||||||
|
warning('Not a frequency in list, using %d', obj.f(ind));
|
||||||
|
end
|
||||||
|
switch obj.numPorts
|
||||||
|
case 1
|
||||||
|
M = obj.S11(ind);
|
||||||
|
case 2
|
||||||
|
M = [obj.S11(ind), obj.S12(ind);
|
||||||
|
obj.S21(ind), obj.S22(ind)];
|
||||||
|
case 3
|
||||||
|
M = [obj.S11(ind), obj.S12(ind), obj.S13(ind);
|
||||||
|
obj.S21(ind), obj.S22(ind), obj.S23(ind);
|
||||||
|
obj.S31(ind), obj.S32(ind), obj.S33(ind)];
|
||||||
|
case 4
|
||||||
|
M = [obj.S11(ind) obj.S12(ind) obj.S13(ind) obj.S14(ind);
|
||||||
|
obj.S21(ind) obj.S22(ind) obj.S23(ind) obj.S24(ind);
|
||||||
|
obj.S31(ind) obj.S32(ind) obj.S33(ind) obj.S34(ind);
|
||||||
|
obj.S41(ind) obj.S42(ind) obj.S43(ind) obj.S44(ind)];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sxx = todB(obj, s)
|
||||||
|
Sxx = 20*log10(abs(eval(['obj.' s])));
|
||||||
|
end
|
||||||
|
|
||||||
|
function plotAlldB(obj)
|
||||||
|
%Plot all S-parameters in dB format
|
||||||
|
%s.plotAlldB();
|
||||||
|
hold on
|
||||||
|
switch obj.numPorts
|
||||||
|
case 1
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S11)));
|
||||||
|
leg = {'S11'};
|
||||||
|
case 2
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S11)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S12)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S21)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S22)));
|
||||||
|
leg = {'S11', 'S12', 'S21', 'S22'};
|
||||||
|
case 3
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S11)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S12)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S13)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S21)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S22)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S23)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S31)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S32)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S33)));
|
||||||
|
leg = {'S11', 'S12', 'S13', 'S21', 'S22', 'S23', 'S31', 'S32', 'S33'};
|
||||||
|
case 4
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S11)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S12)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S13)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S14)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S21)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S22)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S23)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S24)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S31)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S32)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S33)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S34)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S41)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S42)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S43)));
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(obj.S44)));
|
||||||
|
leg = {'S11','S12','S13','S14','S21','S22','S23','S24','S31','S32','S33','S34','S41','S42','S43','S44'};
|
||||||
|
end
|
||||||
|
xlabel(['f (' obj.freqScale{2} ')']);
|
||||||
|
ylabel('S-parameters (dB)');
|
||||||
|
legend(leg);
|
||||||
|
end
|
||||||
|
|
||||||
|
function plotdB(obj, splt)
|
||||||
|
%Plots any speciified S-parameter(s) in dB format
|
||||||
|
%Say you wish to plot only S11 and S21
|
||||||
|
%s.plotdB({'S11' 'S21'})
|
||||||
|
hold on
|
||||||
|
switch class(splt)
|
||||||
|
case 'cell'
|
||||||
|
leg = cell(size(length(splt), 1));
|
||||||
|
for i = 1:length(splt)
|
||||||
|
stringToPlot = ['obj.' splt{i}];
|
||||||
|
StoPlot = eval(stringToPlot);
|
||||||
|
if isempty(StoPlot)
|
||||||
|
error('%s not found', stringToPlot)
|
||||||
|
end
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(StoPlot)));
|
||||||
|
leg{i} = splt{i};
|
||||||
|
end
|
||||||
|
legend(leg);
|
||||||
|
case 'char'
|
||||||
|
stringToPlot = ['obj.' splt];
|
||||||
|
StoPlot = eval(stringToPlot);
|
||||||
|
if isempty(StoPlot)
|
||||||
|
error('%s not found', stringToPlot)
|
||||||
|
end
|
||||||
|
plot(obj.f/obj.freqScale{1}, 20*log10(abs(StoPlot)));
|
||||||
|
end
|
||||||
|
xlabel(['f (' obj.freqScale{2} ')']);
|
||||||
|
ylabel('dB');
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function plotPolar(obj, splt)
|
||||||
|
%Plots any speciified S-parameter(s) in polar format
|
||||||
|
%Say you wish to plot only S11 and S21
|
||||||
|
%s.plotPolar({'S11' 'S21'})
|
||||||
|
switch class(splt)
|
||||||
|
case 'cell'
|
||||||
|
leg = cell(size(length(splt), 1));
|
||||||
|
for i = 1:length(splt)
|
||||||
|
stringToPlot = ['obj.' splt{i}];
|
||||||
|
StoPlot = eval(stringToPlot);
|
||||||
|
polarplot(angle(StoPlot), abs(StoPlot));
|
||||||
|
hold on
|
||||||
|
leg{i} = splt{i};
|
||||||
|
end
|
||||||
|
legend(leg);
|
||||||
|
case 'char'
|
||||||
|
stringToPlot = ['obj.' splt];
|
||||||
|
StoPlot = eval(stringToPlot);
|
||||||
|
polarplot(angle(StoPlot), abs(StoPlot));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function plotSmith(obj, splt)
|
||||||
|
%Plots any speciified S-parameter(s) in polar format
|
||||||
|
%Say you wish to plot only S11 and S21
|
||||||
|
%s.plotPolar({'S11' 'S21'})
|
||||||
|
|
||||||
|
% Draw Smith chart axes
|
||||||
|
% Draw outer circle
|
||||||
|
t = linspace(0, 2*pi, 100);
|
||||||
|
x = cos(t);
|
||||||
|
y = sin(t);
|
||||||
|
plot(x, y, 'Color','black'); axis equal;
|
||||||
|
% Place title and remove ticks from axes
|
||||||
|
title(' Smith Chart ')
|
||||||
|
set(gca,'xticklabel',{[]});
|
||||||
|
set(gca,'yticklabel',{[]});
|
||||||
|
hold on
|
||||||
|
|
||||||
|
% Draw circles along horizontal axis
|
||||||
|
k = [.25 .5 .75 ];
|
||||||
|
for i = 1 : length(k)
|
||||||
|
x(i,:) = k(i) + (1 - k(i)) * cos(t);
|
||||||
|
y(i,:) = (1 - k(i)) * sin(t);
|
||||||
|
plot(x(i,:), y(i,:), 'k')
|
||||||
|
end
|
||||||
|
|
||||||
|
line ([-1 1],[0 0], 'color', 'black')
|
||||||
|
|
||||||
|
% Draw partial circles along vertical axis
|
||||||
|
kt = [0 2.5 pi 3.79 4.22];
|
||||||
|
k = [0 .5 1 2 4 ];
|
||||||
|
for i = 1 : length(kt)
|
||||||
|
t = linspace(kt(i), 1.5*pi, 50);
|
||||||
|
a(i,:) = 1 + k(i) * cos(t);
|
||||||
|
b(i,:) = k(i) + k(i) * sin(t);
|
||||||
|
plot(a(i,:), b(i,:),'k', a(i,:), -b(i,:),'k' )
|
||||||
|
end
|
||||||
|
hold on
|
||||||
|
switch class(splt)
|
||||||
|
case 'cell'
|
||||||
|
leg = cell(size(length(splt), 1));
|
||||||
|
for i = 1:length(splt)
|
||||||
|
stringToPlot = ['obj.' splt{i}];
|
||||||
|
StoPlot = eval(stringToPlot);
|
||||||
|
[x, y] = pol2cart(angle(StoPlot), abs(StoPlot));
|
||||||
|
plot(x, y);
|
||||||
|
hold on
|
||||||
|
leg{i} = splt{i};
|
||||||
|
end
|
||||||
|
legend(leg);
|
||||||
|
case 'char'
|
||||||
|
stringToPlot = ['obj.' splt];
|
||||||
|
StoPlot = eval(stringToPlot);
|
||||||
|
[x, y] = pol2cart(angle(StoPlot), abs(StoPlot));
|
||||||
|
plot(x, y);
|
||||||
|
end
|
||||||
|
xlim([-1.05 1.05]); ylim([-1.05 1.05])
|
||||||
|
end
|
||||||
|
|
||||||
|
function [Zin1, Zin2] = toInputImpedance(obj)
|
||||||
|
%Converts to port 1 and port 2 input impedance (assuming other
|
||||||
|
%port is terminated in Z0)
|
||||||
|
%[Zin1, Zin2] = s.toInputImpedance();
|
||||||
|
Zin1 = obj.Z0.*(1 + obj.S11)./(1 - obj.S11);
|
||||||
|
Zin2 = obj.Z0.*(1 + obj.S22)./(1 - obj.S22);
|
||||||
|
end
|
||||||
|
|
||||||
|
function [A, B, C, D] = toABCD(obj, freq)
|
||||||
|
%Calculates the ABCD parameters from S-parameters
|
||||||
|
%[A, B, C, D] = s.toABCD(); for all frequencies (s.f)
|
||||||
|
%[A, B, C, D] = s.toABCD(freq); for a specified frequency
|
||||||
|
if obj.numPorts ~= 2
|
||||||
|
warning('Only for 2 port measurements');
|
||||||
|
end
|
||||||
|
switch nargin
|
||||||
|
case 1
|
||||||
|
fS11 = obj.S11; fS12 = obj.S12; fS21 = obj.S21; fS22 = obj.S22;
|
||||||
|
case 2
|
||||||
|
S = obj.toMat(freq);
|
||||||
|
fS11 = S(1, 1); fS12 = S(1, 2); fS21 = S(2, 1); fS22 = S(2, 2);
|
||||||
|
end
|
||||||
|
A = ((1 + fS11).*(1 - fS22) + fS12.*fS21)./(2.*fS21);
|
||||||
|
B = obj.Z0*((1 + fS11).*(1 + fS22) - fS12.*fS21)./(2.*fS21);
|
||||||
|
C = (1/obj.Z0)*((1 - fS11).*(1 - fS22) - fS12.*fS21)./(2*fS21);
|
||||||
|
D = ((1 - fS11).*(1 + fS22) + fS12.*fS21)./(2*fS21);
|
||||||
|
end
|
||||||
|
|
||||||
|
function [T11, T12, T21, T22] = toTParams(obj, freq)
|
||||||
|
%Calculates T parametres from S-parameters
|
||||||
|
%[T11, T12, T21, T22] = s.toTParams(); for all frequencies
|
||||||
|
%(s.f)
|
||||||
|
%[T11, T12, T21, T22] = s.toTParams(freq); for a specified frequency
|
||||||
|
switch nargin
|
||||||
|
case 1
|
||||||
|
fS11 = obj.S11; fS12 = obj.S12; fS21 = obj.S21; fS22 = obj.S22;
|
||||||
|
case 2
|
||||||
|
S = obj.toMat(freq);
|
||||||
|
fS11 = S(1, 1); fS12 = S(1, 2); fS21 = S(2, 1); fS22 = S(2, 2);
|
||||||
|
end
|
||||||
|
T11 = -(fS11.*fS22 - fS12.*fS21)./fS21;
|
||||||
|
T12 = fS11./fS21;
|
||||||
|
T21 = -fS22./fS21;
|
||||||
|
T22 = 1./fS21;
|
||||||
|
end
|
||||||
|
|
||||||
|
function renorm(obj, Z0new)
|
||||||
|
%Renormalize to a new characteristic impedance
|
||||||
|
%s.renorm(newZ0);
|
||||||
|
%http://qucs.sourceforge.net/tech/node98.html
|
||||||
|
%NOT FINISHED FOR 3 OR 4 PORTS
|
||||||
|
Z0old = obj.Z0;
|
||||||
|
for i = 1:length(obj.f)
|
||||||
|
S = obj.toMat(obj.f(i));
|
||||||
|
R = ((Z0new - Z0old)./(Z0new + Z0old)).*eye(obj.numPorts);
|
||||||
|
A = (sqrt(Z0new/Z0old)./(Z0new + Z0old)).*eye(obj.numPorts);
|
||||||
|
Sn = A^(-1) * (S - R) * (eye(obj.numPorts) - R * S)^(-1) * A;
|
||||||
|
% Sn = s2s(S, Z0old, Z0new);
|
||||||
|
switch obj.numPorts
|
||||||
|
case 1
|
||||||
|
S11n(i) = Sn(1, 1);
|
||||||
|
case 2
|
||||||
|
S11n(i) = Sn(1, 1); S12n(i) = Sn(1, 2); S21n(i) = Sn(2, 1); S22n(i) = Sn(2, 2);
|
||||||
|
end
|
||||||
|
obj.Z0 = Z0new;
|
||||||
|
end
|
||||||
|
switch obj.numPorts
|
||||||
|
case 1
|
||||||
|
obj.S11 = S11n;
|
||||||
|
case 2
|
||||||
|
obj.S11 = S11n; obj.S12 = S12n; obj.S21 = S21n; obj.S22 = S22n;
|
||||||
|
end
|
||||||
|
obj.setZ0(Z0new);
|
||||||
|
end
|
||||||
|
|
||||||
|
function [Z11, Z12, Z21, Z22] = toZparams(obj, freq)
|
||||||
|
%Calculate Z parameters from S-parameters
|
||||||
|
%[Z11, Z12, Z21, Z22] = toZparams(); for all frequencies (s.f)
|
||||||
|
%[Z11, Z12, Z21, Z22] = toZparams(freq); for a specified frequency
|
||||||
|
if obj.numPorts ~= 2
|
||||||
|
warning('Only for 2 port measurements');
|
||||||
|
end
|
||||||
|
switch nargin
|
||||||
|
case 2
|
||||||
|
fS11 = obj.S11; fS12 = obj.S12; fS21 = obj.S21; fS22 = obj.S22;
|
||||||
|
case 3
|
||||||
|
S = obj.toMat(freq);
|
||||||
|
fS11 = S(1, 1); fS12 = S(1, 2); fS21 = S(2, 1); fS22 = S(2, 2);
|
||||||
|
end
|
||||||
|
Z11 = obj.Z0*((1 + fS11).*(1 - fS22) + fS12.*fS21)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z12 = obj.Z0*(2.*fS12)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z21 = obj.Z0*(2*fS21)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z22 = obj.Z0*((1 - fS11).*(1 + fS22) + fS12.*fS21)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
end
|
||||||
|
|
||||||
|
function [Y11, Y12, Y21, Y22] = toYparams(obj)
|
||||||
|
%Calculate Y parameters from S-parameters
|
||||||
|
%[Y11, Y12, Y21, Y22] = toYparams(); for all frequencies (s.f)
|
||||||
|
%[Y11, Y12, Y21, Y22] = toYparams(freq); for a specified frequency
|
||||||
|
if obj.numPorts ~= 2
|
||||||
|
warning('Only for 2 port measurements');
|
||||||
|
end
|
||||||
|
switch nargin
|
||||||
|
case 2
|
||||||
|
fS11 = obj.S11; fS12 = obj.S12; fS21 = obj.S21; fS22 = obj.S22;
|
||||||
|
case 3
|
||||||
|
S = obj.toMat(freq);
|
||||||
|
fS11 = S(1, 1); fS12 = S(1, 2); fS21 = S(2, 1); fS22 = S(2, 2);
|
||||||
|
end
|
||||||
|
delS = (1 + fS11).*(1 + fS22) - fS12.*fS21;
|
||||||
|
Y11 = ((1 - fS11).*(1 + fS22) + fS12.*fS21)./delS./obj.Z0;
|
||||||
|
Y12 = -2.*fS12./delS./obj.Z0;
|
||||||
|
Y21 = -2.*fS21./delS./obj.Z0;
|
||||||
|
Y22 = ((1 + fS11).*(1 - fS22) + fS12.*fS21)./delS./obj.Z0;
|
||||||
|
end
|
||||||
|
|
||||||
|
function writeCSV(obj, filename)
|
||||||
|
%Write a CSV file in the format
|
||||||
|
%f, S11 - for 1 port
|
||||||
|
%f, S11, S21, S12, S22 - for 2 port
|
||||||
|
%f, S11, S12, S13, S21, S22, S23, S31, S32, S33 - for 3 port
|
||||||
|
%And so on
|
||||||
|
switch nargin
|
||||||
|
case 1
|
||||||
|
[~, csvname, ~] = fileparts(obj.filename);
|
||||||
|
csvwrite(strcat(csvname, '.csv'), obj.data);
|
||||||
|
case 2
|
||||||
|
csvwrite(filename, obj.data);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
%Work in progress
|
||||||
|
% function writeSNP(obj, filenameOut)
|
||||||
|
% fileExt = ['.s' num2str(obj.numPorts), 'p'];
|
||||||
|
%
|
||||||
|
% dlmwrite(filenameOut, obj.data, ' ');
|
||||||
|
% end
|
||||||
|
|
||||||
|
function cascade(obj, N)
|
||||||
|
%Calculates S parameters when cascaded N times. Only valid for
|
||||||
|
%2 port unit cells.
|
||||||
|
%This function will overwrite the original S-parameters, so it
|
||||||
|
%may be advisable to copy the original unit cell first using
|
||||||
|
%copyobj()
|
||||||
|
[Au, Bu, Cu, Du] = obj.toABCD;
|
||||||
|
At = Au; Bt = Bu; Ct = Cu; Dt = Du;
|
||||||
|
for i = 1:N-1
|
||||||
|
Attemp = At; Bttemp = Bt; Cttemp = Ct; Dttemp = Dt;
|
||||||
|
At = Attemp.*Au + Bttemp.*Cu;
|
||||||
|
Bt = Attemp.*Bu + Bttemp.*Du;
|
||||||
|
Ct = Cttemp.*Au + Dttemp.*Cu;
|
||||||
|
Dt = Cttemp.*Bu + Dttemp.*Du;
|
||||||
|
end
|
||||||
|
obj.S11 = (At + Bt/obj.Z0 - Ct.*obj.Z0 - Dt)./(At + Bt/obj.Z0 + Ct.*obj.Z0 + Dt);
|
||||||
|
obj.S12 = (2.*(At.*Dt - Bt.*Ct))./(At + Bt/obj.Z0 + Ct.*obj.Z0 + Dt);
|
||||||
|
obj.S21 = 2./(At + Bt/obj.Z0 + Ct.*obj.Z0 + Dt);
|
||||||
|
obj.S22 = (-At + Bt./obj.Z0 - Ct.*obj.Z0 + Dt)./(At + Bt/obj.Z0 + Ct.*obj.Z0 + Dt);
|
||||||
|
end
|
||||||
|
|
||||||
|
function plotDispersion(obj, flip)
|
||||||
|
%Plots the dispersion diagram when s is a unit cell.
|
||||||
|
%s.plotDispersion();
|
||||||
|
Beta_p = real(acosd((1 - obj.S11.*obj.S22 + obj.S12.*obj.S21)./(2.*obj.S21)));
|
||||||
|
%Beta_p = acos((1 - S(1,1)*S(2,2) + S(1,2)*S(2,1))/(2*S(2,1)))
|
||||||
|
switch nargin
|
||||||
|
case 1
|
||||||
|
plot(Beta_p, obj.f/obj.freqScale{1}, 'k');
|
||||||
|
ylabel(['f (' obj.freqScale{2} ')'])
|
||||||
|
xlabel('\beta p (deg)')
|
||||||
|
case 2
|
||||||
|
if ~strcmpi(flip, 'flip')
|
||||||
|
plot(Beta_p, obj.f/obj.freqScale{1}, 'k');
|
||||||
|
ylabel(['f (' obj.freqScale{2} ')'])
|
||||||
|
xlabel('\beta p (deg)')
|
||||||
|
else
|
||||||
|
plot(obj.f/obj.freqScale{1}, Beta_p, 'k');
|
||||||
|
xlabel(['f (' obj.freqScale{2} ')'])
|
||||||
|
ylabel('\beta p (deg)')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
grid on
|
||||||
|
end %
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
methods(Static)
|
||||||
|
|
||||||
|
function sDUT = deembed(err1, sALL, err2)
|
||||||
|
%Deembed S-parameters under the following conditions
|
||||||
|
%Measurement: sALL = P1 <-- err1 <--> DUT <--> err2 --> P2
|
||||||
|
%sDUT = SPARAMS.deembed(err1, sALL, err2);
|
||||||
|
%Will return a new sparameter object that represents the DUT
|
||||||
|
%CURRENTLY UNDER TESTINNG
|
||||||
|
if err1.f ~= err2.f | err1.f ~= sALL.f
|
||||||
|
error('frequencies unequal');
|
||||||
|
end
|
||||||
|
len = length(sALL.f);
|
||||||
|
|
||||||
|
Tt11 = zeros(1, len); Tt12 = zeros(1, len); Tt21 = zeros(1, len); Tt22 = zeros(1, len);
|
||||||
|
for i = 1:len
|
||||||
|
f = sALL.f(i);
|
||||||
|
[e1T11, e1T12, e1T21, e1T22] = err1.toTParams(f);
|
||||||
|
[dT11, dT12, dT21, dT22] = sALL.toTParams(f);
|
||||||
|
[e2T11, e2T12, e2T21, e2T22] = err2.toTParams(f);
|
||||||
|
tempT = [e1T11 e1T12;e1T21 e1T22]\[dT11 dT12;dT21 dT22]*inv([e2T11 e2T12;e2T21 e2T22]);
|
||||||
|
Tt11(i) = tempT(1, 1);
|
||||||
|
Tt12(i) = tempT(1, 2);
|
||||||
|
Tt21(i) = tempT(2, 1);
|
||||||
|
Tt22(i) = tempT(2, 2);
|
||||||
|
end
|
||||||
|
|
||||||
|
warning('off', 'all')
|
||||||
|
sDUT = SPARAMS();
|
||||||
|
sDUT.setNumPorts(2);
|
||||||
|
warning('on', 'all')
|
||||||
|
sDUT.S11 = Tt12./Tt22;
|
||||||
|
sDUT.S12 = (Tt11.*Tt22 - Tt12.*Tt21)./Tt22;
|
||||||
|
sDUT.S21 = 1./Tt22;
|
||||||
|
sDUT.S22 = -Tt21./Tt22;
|
||||||
|
sDUT.f = sALL.f;
|
||||||
|
sDUT.setZ0(sALL.Z0);
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function plotAnydB(ob1, splt1, ob2, splt2)
|
||||||
|
%Plot any S-parameters from any 2 objects in dB
|
||||||
|
%SPARAMS.plotAnydB(obj1, {'S11', 'S21'}, obj2, {'S11', 'S21'});
|
||||||
|
if class(splt1) == 'char' | class(splt2) == 'char'
|
||||||
|
error('Input name of parameters in cell format')
|
||||||
|
end
|
||||||
|
|
||||||
|
figure();
|
||||||
|
hold on;
|
||||||
|
splt1 = cell(splt1); splt2 = cell(splt2);
|
||||||
|
for i = 1:length(splt1)
|
||||||
|
stringToPlot = ['ob1.' splt1{i}];
|
||||||
|
stoPlot = eval(stringToPlot);
|
||||||
|
plot(ob1.f, 20*log10(abs(stoPlot)));
|
||||||
|
end
|
||||||
|
for i = 1:length(splt2)
|
||||||
|
stringToPlot = ['ob2.' splt2{i}];
|
||||||
|
stoPlot = eval(stringToPlot);
|
||||||
|
plot(ob2.f, 20*log10(abs(stoPlot)));
|
||||||
|
end
|
||||||
|
xlabel('f (Hz)');
|
||||||
|
ylabel('S-parameters (dB)');
|
||||||
|
end
|
||||||
|
|
||||||
|
function Z = s2z(S, Z0)
|
||||||
|
%General conversion from S to Z
|
||||||
|
fS11 = S(1, 1); fS12 = S(1, 2); fS21 = S(2, 1); fS22 = S(2, 2);
|
||||||
|
Z11 = Z0*((1 + fS11).*(1 - fS22) + fS12.*fS21)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z12 = Z0*(2.*fS12)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z21 = Z0*(2*fS21)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z22 = Z0*((1 - fS11).*(1 + fS22) + fS12.*fS21)./((1 - fS11).*(1 - fS22) - fS12.*fS21);
|
||||||
|
Z = [Z11 Z12;Z21 Z22];
|
||||||
|
end
|
||||||
|
|
||||||
|
function S = z2s(Z, Z0)
|
||||||
|
%General conversion from Z to S
|
||||||
|
Z11 = Z(1, 1); Z12 = Z(1, 2); Z21 = Z(2, 1); Z22 = Z(2, 2);
|
||||||
|
dZ = (Z11 + Z0).*(Z22 + Z0) - Z12.*Z21;
|
||||||
|
S11 = ((Z11 - Z0).*(Z22 + Z0) - Z12.*Z21)/dZ;
|
||||||
|
S12 = 2*Z12.*Z0./dZ;
|
||||||
|
S21 = 2*Z21.*Z0./dZ;
|
||||||
|
S22 = ((Z11 + Z0).*(Z22 - Z0) - Z12.*Z21)/dZ;s
|
||||||
|
S = [S11 S12;S21 S22];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user