diff options
Diffstat (limited to 'CTB')
-rw-r--r-- | CTB/AddElement2Port.m | 83 | ||||
-rw-r--r-- | CTB/ApplyCurrent2Port.m | 69 | ||||
-rw-r--r-- | CTB/ApplyRFPower2Port.m | 43 | ||||
-rw-r--r-- | CTB/InitNetwork.m | 53 | ||||
-rw-r--r-- | CTB/Makefile | 14 | ||||
-rw-r--r-- | CTB/SetPortTermination.m | 45 | ||||
-rw-r--r-- | CTB/a2s.m | 31 | ||||
-rw-r--r-- | CTB/a_PI.m | 15 | ||||
-rw-r--r-- | CTB/a_T.m | 14 | ||||
-rw-r--r-- | CTB/a_mul.m | 28 | ||||
-rw-r--r-- | CTB/a_series.m | 22 | ||||
-rw-r--r-- | CTB/a_shunt.m | 22 | ||||
-rw-r--r-- | CTB/license.txt | 22 | ||||
-rw-r--r-- | CTB/read_touchstone.m | 150 | ||||
-rw-r--r-- | CTB/readme.txt | 29 | ||||
-rw-r--r-- | CTB/s2a.m | 31 | ||||
-rw-r--r-- | CTB/s2y.m | 43 | ||||
-rw-r--r-- | CTB/s2z.m | 43 | ||||
-rw-r--r-- | CTB/s_renorm.m | 19 | ||||
-rw-r--r-- | CTB/write_touchstone.m | 87 | ||||
-rw-r--r-- | CTB/y2s.m | 43 | ||||
-rw-r--r-- | CTB/y2z.m | 22 | ||||
-rw-r--r-- | CTB/z2s.m | 44 | ||||
-rw-r--r-- | CTB/z2y.m | 18 |
24 files changed, 990 insertions, 0 deletions
diff --git a/CTB/AddElement2Port.m b/CTB/AddElement2Port.m new file mode 100644 index 0000000..36019b2 --- /dev/null +++ b/CTB/AddElement2Port.m @@ -0,0 +1,83 @@ +function net = AddElement2Port(net, port, type, value, varargin) +% function net = AddElement2Port(net, port, type, value, varargin) +% +% Add a linear network element to the given ports +% +% arguments: +% port: number of ports to apply the element +% type: type of network element: +% 'C_se' : series capacity +% 'C_sh' : shunt capacity +% 'L_se' : series inductance +% 'L_sh' : shunt inductance +% 'R_se' : series resistance +% 'R_sh' : shunt resistance +% 'Z_se' : series impedance +% 'Y_se' : shunt addmitance +% +% See also: InitNetwork, SetPortTermination, ApplyCurrent2Port, +% ApplyRFPower2Port +% +% ------ +% Cuicuit Toolbox (https://github.com/thliebig/CTB) +% (c) Thorsten Liebig, 2013 + +if (numel(port)>1) + if (numel(value)==1) + value = value*ones(size(port)); + end + for n=1:numel(port) + net = AddElement2Port(net, port(n), type, value(n), varargin); + end + return; +end + +if ((port<1) || (port>net.numPorts)) + error 'invalid port number' +end + +if iscell(type) + for n=1:numel(type) + net = AddElement2Port(net, port, type{n}, value(n), varargin{:}); + end + return +end + +Y = []; +Z = []; + +if (strcmpi(type, 'C_se')) + Z = 1./(2j*pi*net.f*value); +elseif (strcmpi(type, 'C_sh')) + Y = 2j*pi*net.f*value; +elseif (strcmpi(type, 'L_se')) + Z = 2j*pi*net.f*value; +elseif (strcmpi(type, 'L_sh')) + Y = 1./(2j*pi*net.f*value); +elseif (strcmpi(type, 'R_se')) + Z = value*ones(size(net.f)); +elseif (strcmpi(type, 'R_sh')) + Y = 1/value*ones(size(net.f)); +elseif (strcmpi(type, 'Z_se')) + Z = value; +elseif (strcmpi(type, 'Y_sh')) + Y = value; +else + error 'unknown element type' +end + +if (~isempty(Y)) + net.ABCD{port} = a_mul(net.ABCD{port}, a_shunt(Y)); + Y = reshape(Y,1,1,numel(net.f)); + net.y(port,port,:) = net.y(port,port,:) + Y(1,1,:); + net.s = y2s(net.y, net.Z0); + net.z = y2z(net.y); +elseif (~isempty(Z)) + net.ABCD{port} = a_mul(net.ABCD{port}, a_series(Z)); + Z = reshape(Z,1,1,numel(net.f)); + net.z(port,port,:) = net.z(port,port,:) + Z(1,1,:); + net.s = z2s(net.z, net.Z0); + net.y = z2y(net.z); +else + error 'internal error' +end diff --git a/CTB/ApplyCurrent2Port.m b/CTB/ApplyCurrent2Port.m new file mode 100644 index 0000000..6628714 --- /dev/null +++ b/CTB/ApplyCurrent2Port.m @@ -0,0 +1,69 @@ +function [I_port I_orig_port] = ApplyCurrent2Port(net, port, current, varargin) +% function I_port = ApplyCurrent2Port(net, port, current, varargin) +% +% Apply a total current to a given port of your network +% +% arguments: +% port: number of ports to apply the power to +% current: applied total current +% +% output: +% I_port: Total currents going into the ports +% +% See also: InitNetwork, SetPortTermination, AddElement2Port, +% ApplyRFPower2Port +% +% ------ +% Cuicuit Toolbox (https://github.com/thliebig/CTB) +% (c) Thorsten Liebig, 2013 + +if (numel(port)>1) + I_port = zeros(net.numPorts,net.numFreq); + I_orig_port = zeros(net.numPorts,net.numFreq); + for n=1:numel(port) + [I_p I_op] = ApplyCurrent2Port(net, port(n), current(n), varargin{:}); + I_port = I_port + I_p; + I_orig_port = I_orig_port + I_op; + end + return +end + +if ((port<1) || (port>net.numPorts)) + error 'invalid port number' +end + +if (numel(current)==1) + current = current*ones(size(net.f)) +end + +if (numel(net.Z0)==1) + Z0 = net.Z0*ones(net.numPorts,1); +else + Z0 = net.Z0; +end + +z_term = net.z; +for n=1:net.numPorts + z_term(n,n,:) = z_term(n,n,:) + Z0(n); +end + +z_mat = z_term; +z_mat(port,:,:) = []; +z_mat(:,port,:) = []; + +port_other = 1:net.numPorts; +port_other(port) = []; + +I_port(port,:) = current; + +for fn = 1:numel(net.f) + z_port_vec = -1*squeeze(z_term(port_other,port,fn)); + I_port(port_other,fn) = squeeze(z_mat(:,:,fn))\z_port_vec * current(fn); +end + +I_orig_port=I_port*0; +for n=1:net.numPorts + C = reshape(net.ABCD{n}(2,1,:),1,numel(net.f)); + D = reshape(net.ABCD{n}(2,2,:),1,numel(net.f)); + I_orig_port(n,:) = (Z0(n)*C + D) .* I_port(n,:); +end diff --git a/CTB/ApplyRFPower2Port.m b/CTB/ApplyRFPower2Port.m new file mode 100644 index 0000000..2464038 --- /dev/null +++ b/CTB/ApplyRFPower2Port.m @@ -0,0 +1,43 @@ +function [I_port I_orig_port] = ApplyRFPower2Port(net, port, power, varargin) +% function I_port = ApplyRFPower2Port(net, port, power, varargin) +% +% Apply a RF power to a given port of your network +% +% arguments: +% port: number of ports to apply the power to +% power: applied power in W +% +% output: +% I_port: Total currents going into the ports +% +% See also: InitNetwork, SetPortTermination, AddElement2Port, +% ApplyCurrent2Port +% +% ------ +% Cuicuit Toolbox (https://github.com/thliebig/CTB) +% (c) Thorsten Liebig, 2013 + +if (numel(port)>1) + I_port = zeros(net.numPorts,net.numFreq); + I_orig_port = zeros(net.numPorts,net.numFreq); + for n=1:numel(port) + [I_p I_op] = ApplyRFPower2Port(net, port(n), power(n), varargin{:}); + I_port = I_port + I_p; + I_orig_port = I_orig_port + I_op; + end + return +end + +if ((port<1) || (port>net.numPorts)) + error 'invalid port number' +end + +if (numel(net.Z0)==1) + Z0 = net.Z0*ones(net.numPorts,1); +else + Z0 = net.Z0; +end + +I_tot = sqrt(2*abs(power)/Z0(port)) * (1-squeeze(net.s(port,port,:))) * exp(1j*angle(power)); + +[I_port I_orig_port] = ApplyCurrent2Port(net, port, I_tot, varargin); diff --git a/CTB/InitNetwork.m b/CTB/InitNetwork.m new file mode 100644 index 0000000..960f24e --- /dev/null +++ b/CTB/InitNetwork.m @@ -0,0 +1,53 @@ +function net = InitNetwork(freq, s_para, varargin) +% function net = InitNetwork(freq, s_para, varargin) +% +% Init a new network with a given frequncy vector and scattering parameter set +% +% arguments: +% freq: frequncy vector +% s_para: scattering parameter +% +% example: +% % read some touchstone s-parameter file +% [type,freq,data,ref]=read_touchstone('test.s3p'); +% +% % init network +% net = InitNetwork(freq, data); +% % attach a series capacity to port 2 +% net = AddElement2Port(net, 2, 'C_se', 1e-12); +% % terminate all ports with 50 Ohms +% net = AddElement2Port(net, 1:3, 'R_se', 50); +% % apply 1mW to port 1 and get port currents +% I_tot = ApplyRFPower2Port(net, 1, 1e-3); +% +% See also AddElement2Port, ApplyCurrent2Port, ApplyRFPower2Port, +% read_touchstone, write_touchstone +% +% ------ +% Cuicuit Toolbox (https://github.com/thliebig/CTB) +% (c) Thorsten Liebig, 2013 + +net.Z0 = 50; +net.numFreq = numel(freq); +net.numPorts = size(s_para, 1); +net.f = freq; +net.s = s_para; +net.orig.s = s_para; +net.z = s2z(net.s, net.Z0); +net.y = s2y(net.s, net.Z0); + +ABCD(1,1,1:net.numFreq) = 1; +ABCD(2,2,:) = 1; + +% networks attached to each port +for n=1:net.numPorts + net.ABCD{n} = ABCD; +end + +if (size(s_para, 1) ~= size(s_para, 2)) + error 'n unequal m' +end + +if (size(s_para, 3) ~= net.numFreq) + error 'number of frequencies do not match' +end diff --git a/CTB/Makefile b/CTB/Makefile new file mode 100644 index 0000000..f11dbe8 --- /dev/null +++ b/CTB/Makefile @@ -0,0 +1,14 @@ + +####### Compiler, tools and options +MKDIR = mkdir -p +COPY = cp -at + +# default install path +PREFIX = /usr/local + +INSTALL_PATH = $(PREFIX)/share/CTB + +all: + +install: + $(MKDIR) "$(INSTALL_PATH)/matlab" && $(COPY) "$(INSTALL_PATH)/matlab" *.m && $(COPY) "$(INSTALL_PATH)" license.txt readme.txt diff --git a/CTB/SetPortTermination.m b/CTB/SetPortTermination.m new file mode 100644 index 0000000..f66e9a6 --- /dev/null +++ b/CTB/SetPortTermination.m @@ -0,0 +1,45 @@ +function net = SetPortTermination(net, port, Z0, varargin) +% function net = SetPortTermination(net, port, Z0, varargin) +% +% Set the given port impedance/termination +% +% arguments: +% port: number of ports to apply the termination +% Z0: port impedance +% +% See also: InitNetwork, AddElement2Port, ApplyCurrent2Port, +% ApplyRFPower2Port +% +% ------ +% Cuicuit Toolbox (https://github.com/thliebig/CTB) +% (c) Thorsten Liebig, 2013 + +if (numel(port)>1) + if (numel(Z0)==1) + Z0 = Z0*ones(1,size(port)); + end + for n=1:numel(port) + net = SetPortTermination(net, port(n), Z0(n), varargin); + end + return; +end + +if ((port<1) || (port>net.numPorts)) + error 'invalid port number' +end + +if (numel(net.Z0)==1) + if (net.Z0==Z0) + % nothing needs to be done + return + end + net.Z0 = net.Z0*ones(1,net.numPorts); +end + +if (net.Z0(port)==Z0) + % nothing needs to be done + return +end + +net.Z0(port)=Z0; +net.s = z2s(net.z, net.Z0); diff --git a/CTB/a2s.m b/CTB/a2s.m new file mode 100644 index 0000000..5e55ae4 --- /dev/null +++ b/CTB/a2s.m @@ -0,0 +1,31 @@ +function s = a2s(a,ref)
+% s = a2s(a [,ref])
+%
+% ABCD to Scattering Matrix transformation (only for 2x2xf matrices)
+%
+% input:
+% a: ABCD matrix 2x2xf (f: number of frequencies)
+% ref: (optional) reference impedance (default 50 Ohm)
+%
+% output:
+% s: S-matrix 2x2xf normalized to ref Ohm
+%
+% Reference: David M. Pozar "Microwave Engineering"
+%
+% Sebastian Held <sebastian.held@gmx.de>
+% Sep 1 2010
+
+if nargin < 2
+ Z0 = 50;
+else
+ Z0 = ref;
+end
+
+A = squeeze(a(1,1,:));
+B = squeeze(a(1,2,:));
+C = squeeze(a(2,1,:));
+D = squeeze(a(2,2,:));
+s(1,1,:) = (A + B./Z0 - C.*Z0 - D) ./ (A + B./Z0 + C.*Z0 + D);
+s(1,2,:) = 2*(A.*D - B.*C) ./ (A + B./Z0 + C.*Z0 + D);
+s(2,1,:) = 2 ./ (A + B./Z0 + C.*Z0 + D);
+s(2,2,:) = (-A + B./Z0 - C.*Z0 + D) ./ (A + B./Z0 + C.*Z0 + D);
diff --git a/CTB/a_PI.m b/CTB/a_PI.m new file mode 100644 index 0000000..3ae4db8 --- /dev/null +++ b/CTB/a_PI.m @@ -0,0 +1,15 @@ +function a = a_PI(Y1,Y2,Y3) +% a = a_PI(Y1,Y2,Y3) +% +% calculate the ABCD matrix from a PI-circuit +% +% Reference: David M. Pozar "Microwave Engineering" +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% Feb. 2013 + + +a(1,1,:) = 1+Y2./Y3; +a(1,2,:) = 1./Y3; +a(2,1,:) = Y1+Y2+Y1.Y2./Y3; +a(2,2,:) = 1+Y1./Y3; diff --git a/CTB/a_T.m b/CTB/a_T.m new file mode 100644 index 0000000..49a2d30 --- /dev/null +++ b/CTB/a_T.m @@ -0,0 +1,14 @@ +function a = a_T(Z1,Z2,Z3) +% a = a_T(Z1,Z2,Z3) +% +% calculate the ABCD matrix from a T-circuit +% +% Reference: David M. Pozar "Microwave Engineering" +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% Feb. 2013 + +a(1,1,:) = 1+Z1./Z2; +a(1,2,:) = Z1+Z2+Z1.*Z2./Z3; +a(2,1,:) = 1./Z3; +a(2,2,:) = 1+Z2./Z3; diff --git a/CTB/a_mul.m b/CTB/a_mul.m new file mode 100644 index 0000000..c1d2130 --- /dev/null +++ b/CTB/a_mul.m @@ -0,0 +1,28 @@ +function a = a_mul(varargin) +% a = a_mul(varargin) +% +% multiply a number of ABCD matricies +% +% example: +% +% a = a_mul(a1,a2); which is a = a1 * a2; +% +% a = a_mul(a1,a2,a3,a4); which is a = a1 * a2 * a3 * a4; +% +% Reference: David M. Pozar "Microwave Engineering" +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% Feb. 2013 + +if nargin<1 + error 'no argument is given' +end + +a = varargin{1}; + +for n=2:numel(varargin) + a2 = varargin{n}; + for f=1:size(a,3) + a(:,:,f) = a(:,:,f)*a2(:,:,f); + end +end diff --git a/CTB/a_series.m b/CTB/a_series.m new file mode 100644 index 0000000..b8a2de1 --- /dev/null +++ b/CTB/a_series.m @@ -0,0 +1,22 @@ +function a = a_series(Z) +% a = a_series(Z) +% +% create the ABCD matrix of a series impedance +% +% example: +% C = 1e-12; aka 1pF +% f = linspace(0,2e9,201); +% Z = 2j*pi*f*C; +% +% %ABCD matrix of a series capacitor +% a = a_series(Z); +% +% Reference: David M. Pozar "Microwave Engineering" +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% Feb. 2013 + +a(1,2,:) = Z; +a(1,1,:) = 1; +a(2,1,:) = 0; +a(2,2,:) = 1; diff --git a/CTB/a_shunt.m b/CTB/a_shunt.m new file mode 100644 index 0000000..13bc993 --- /dev/null +++ b/CTB/a_shunt.m @@ -0,0 +1,22 @@ +function a = a_shunt(Y) +% a = a_shunt(Y) +% +% create the ABCD matrix of a shunt admittance +% +% example: +% C = 1e-12; aka 1pF +% f = linspace(0,2e9,201); +% Y = 1./(2j*pi*f*C); +% +% %ABCD matrix of a shunt capacitor +% a = a_shunt(Y); +% +% Reference: David M. Pozar "Microwave Engineering" +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% Feb. 2013 + +a(2,1,:) = Y; +a(1,2,:) = 0; +a(1,1,:) = 1; +a(2,2,:) = 1; diff --git a/CTB/license.txt b/CTB/license.txt new file mode 100644 index 0000000..266f365 --- /dev/null +++ b/CTB/license.txt @@ -0,0 +1,22 @@ +Copyright (c) 2006-2013 Sebastian Held, Thorsten Liebig. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. diff --git a/CTB/read_touchstone.m b/CTB/read_touchstone.m new file mode 100644 index 0000000..dd30548 --- /dev/null +++ b/CTB/read_touchstone.m @@ -0,0 +1,150 @@ +function [type,freq,data,ref,comment]=read_touchstone( filename ) +% [type,freq,data,ref]=read_touchstone( filename ) - read touchstone-file +% type (lowercase) 'z' for Z-Parameter touchstone-file +% freq vector of frequencies (in Hz) +% data (numports x numports x numfreq) matrix +% filename filename of the file to read +% ref reference impedance +% comment cell array of comment lines +% +% Version 16. Jun 2010 +% 18. Aug 2010: comment lines +% 1. Sep 2010: BUGFIX: two-port files had S12 and S21 swapped!!!!! +% ATTENTION: higher than two-ports must be validated again!!!!!!!!!1 + +[fid, message] = fopen( filename, 'rt' ); +if fid == -1 + error( message ); +end + +comment = {}; + +% constants +DB = 0; +MA = 1; +RI = 2; + +% number of ports (extract extension) +temp = lower(filename); +k = strfind(temp,'.'); +if isempty(k) + error( 'filename has no extension' ); +end +last_dot = k(length(k)); +if length(temp) <= k + error( 'filename ends with "."' ); +end +extension = temp(last_dot+1:length(temp)); +if (lower(extension(1)) ~= 's') && (lower(extension(length(extension))) ~= 'p') + error( 'filename must have an extension like .s2p' ); +end +num_ports = str2double(extension(2:length(extension)-1)); + +parameters_found = 0; +freq_nr = 1; +tline = fgetl(fid); +while ischar(tline) + tline = strtrim(tline); + if isempty(tline) || (tline(1) == '!') + % comment + if ~isempty(tline) + comment{end+1} = tline; + end + tline = fgetl(fid); + continue + end + if tline(1) == '#' + % found parameters + if parameters_found == 1 + warning( 'additional parameter line found; expect garbage!' ); + end + parameters_found = 1; + % parse parameters + [~,remain] = strtok(tline); % token is now '#' + [token,remain] = strtok(remain); % token is now the unit + unit = lower(token); + if strcmp(unit,'hz') + freq_mul = 1; + elseif strcmp(unit,'khz') + freq_mul = 1e3; + elseif strcmp(unit,'mhz') + freq_mul = 1e6; + elseif strcmp(unit,'ghz') + freq_mul = 1e9; + else + error( ['wrong unit: "' token '"'] ); + end + [token,remain] = strtok(remain); % token is now the parameter type + type = lower(token); + [token,remain] = strtok(remain); % token is now the complex format + format = lower(token); + if strcmp(format,'ri') + format = RI; + elseif strcmp(format,'ma') + format = MA; + elseif strcmp(format,'db') + format = DB; + else + error( ['wrong complex format: "' format '"'] ); + end + [token,remain] = strtok(remain); % token must be 'R' + if lower(token) ~= 'r' + error( 'incorrect file format' ); + end + token = strtok(remain); % token is reference impedance + ref = str2double(token); + tline = fgetl(fid); + continue + end + % read data + clear 'cell_array'; + cell_array = split( tline ); + while length(cell_array) < 2*num_ports^2 + 1 + temp_tline = fgetl(fid); %FIXME comments? empty lines? + cell_array = [cell_array,split( temp_tline )]; + end + % now we have (at least) num_values values in cell_array + temp_freq = str2double(cell_array(1)) * freq_mul; + temp_data = zeros(num_ports); + for source = 1:num_ports + for dest = 1:num_ports + if num_ports ~= 2 + pos = ((source-1)*num_ports + dest)*2; + else + % special file format for two-ports + pos = ((source-1)*2 + dest)*2; + end + if format == RI + temp_data(dest,source) = str2double(cell_array(pos)) + 1i*str2double(cell_array(pos+1)); + elseif format == MA + temp_data(dest,source) = str2double(cell_array(pos)) * exp(1i*str2double(cell_array(pos+1))/180*pi); + %disp( [tline ' ### freq: ' num2str(temp_freq/1e9) ' S' num2str(source) num2str(dest) ' ' num2str(temp_data(dest,source))] ); + %disp( ['pos: ' num2str(pos) ' cell_array: ' char(cell_array(pos))] ); + elseif format == DB + temp_data(dest,source) = 10^(str2double(cell_array(pos))/20) * exp(1i*str2double(cell_array(pos+1))/180*pi); + end + end + end + % values are parsed, insert them into the freq and data arrays + freq(freq_nr) = temp_freq; + data(:,:,freq_nr) = temp_data; + freq_nr = freq_nr + 1; + +% disp( ['frequency f=' num2str(temp_freq/1e9)] ); + tline = fgetl(fid); +end + +fclose( fid ); + +end %read_touchstone() + + + +function cell_array=split( str ) + cell_array = cell(0); + [token,remain] = strtok(str); + while ~isempty(token) + cell_array = [cell_array token]; + [token,remain] = strtok(remain); + end +end diff --git a/CTB/readme.txt b/CTB/readme.txt new file mode 100644 index 0000000..8b8cef9 --- /dev/null +++ b/CTB/readme.txt @@ -0,0 +1,29 @@ +Circuit Toolbox for Matlab/Octave +--------------------------------- + +Small collection of useful functions to convert n-port network parameter +such as scattering parameter, Z- or Y-parameter. + +Read/Write to touchstone file format to read with Qucs: http://qucs.sourceforge.net + +--- +Examples: +addpath('path/to/CTB'); +% create ABCD matrix of a series C, shunt R and shunt L +f = linspace(0,1e9,201); % frequency vector +a = a_series(1./(2j*pi*f*10e-12)); % create ABCD matrix for a series capacity of 10pF +a = a_mul(a, a_shunt(1/50*ones(size(f)))); % append a 50 Ohms shunt resistance +a = a_mul(a, a_series(2j*pi*f*20e-9)); % append a 20nH series inductance +s = a2s(a); % convert to s-parameter + +figure +plot(f,20*log10(squeeze(abs(s(1,1,:))))); +hold on; +grid on; +plot(f,20*log10(squeeze(abs(s(2,1,:)))),'r--'); +ylim([-20 0]); +legend('s11','s21'); + +--- +Copyright (c) 2006-2013 Sebastian Held, Thorsten Liebig. +All rights reserved. diff --git a/CTB/s2a.m b/CTB/s2a.m new file mode 100644 index 0000000..4f795ef --- /dev/null +++ b/CTB/s2a.m @@ -0,0 +1,31 @@ +function a = s2a(s,ref)
+% a = s2a(s [,ref])
+%
+% Scattering to ABCD transformation (only for 2x2xf matrices)
+%
+% input:
+% s: S-matrix matrix 2x2xf (f: number of frequencies)
+% ref: (optional) reference impedance (default 50 Ohm)
+%
+% output:
+% a: ABCD 2x2xf
+%
+% Reference: David M. Pozar "Microwave Engineering"
+%
+% Sebastian Held <sebastian.held@gmx.de>
+% Sep 1 2010
+
+if nargin < 2
+ Z0 = 50;
+else
+ Z0 = ref;
+end
+
+S11 = squeeze(s(1,1,:));
+S12 = squeeze(s(1,2,:));
+S21 = squeeze(s(2,1,:));
+S22 = squeeze(s(2,2,:));
+a(1,1,:) = ((1+S11).*(1-S22) + S12.*S21) ./ (2*S21);
+a(1,2,:) = Z0 .* ((1+S11).*(1+S22) - S12.*S21) ./ (2*S21);
+a(2,1,:) = 1./Z0 .* ((1-S11).*(1-S22) - S12.*S21) ./ (2*S21);
+a(2,2,:) = ((1-S11).*(1+S22) + S12.*S21) ./ (2*S21);
diff --git a/CTB/s2y.m b/CTB/s2y.m new file mode 100644 index 0000000..c3cbd23 --- /dev/null +++ b/CTB/s2y.m @@ -0,0 +1,43 @@ +function y = s2y(s,ref)
+% y = s2y(s [,ref])
+%
+% Scattering to Y transformation
+%
+% input:
+% s: S-matrix matrix nxnxf (f: number of frequencies)
+% ref: (optional) reference impedance (default 50 Ohm)
+%
+% output:
+% y: Y-matrix nxnxf
+%
+% Reference: http://qucs.sourceforge.net/tech/node98.html
+%
+% Thorsten Liebig <thorsten.liebig@gmx.de>
+% Feb. 2013
+
+if nargin < 2
+ Z0 = 50;
+else
+ Z0 = ref;
+end
+
+N = size(s,1);
+Nf = size(s,3);
+
+if (numel(Z0)==1)
+ Z0 = Z0*ones(N,1);
+end
+
+E = eye(N);
+Zref = zeros(N,N);
+G = Zref;
+for n=1:N
+ Zref(n,n) = Z0(n);
+ G(n,n) = 1/sqrt(real(Z0(n)));
+end
+
+y=zeros(N,N,Nf); %preallocate
+
+for f=1:Nf
+ y(:,:,f) = G\(Zref\((s(:,:,f)+E)\(E-s(:,:,f))))*G;
+end
diff --git a/CTB/s2z.m b/CTB/s2z.m new file mode 100644 index 0000000..0a76f09 --- /dev/null +++ b/CTB/s2z.m @@ -0,0 +1,43 @@ +function z = s2z(s,ref)
+% z = s2z(s [,ref])
+%
+% Scattering to Z transformation
+%
+% input:
+% s: S-matrix matrix nxnxf (f: number of frequencies)
+% ref: (optional) reference impedance (default 50 Ohm)
+%
+% output:
+% z: Z-matrix nxnxf
+%
+% Reference: http://qucs.sourceforge.net/tech/node98.html
+%
+% Thorsten Liebig <thorsten.liebig@gmx.de>
+% Feb. 2013
+
+if nargin < 2
+ Z0 = 50;
+else
+ Z0 = ref;
+end
+
+N = size(s,1);
+Nf = size(s,3);
+
+if (numel(Z0)==1)
+ Z0 = Z0*ones(N,1);
+end
+
+E = eye(N);
+Zref = zeros(N,N);
+G = Zref;
+for n=1:N
+ Zref(n,n) = Z0(n);
+ G(n,n) = 1/sqrt(real(Z0(n)));
+end
+
+z=zeros(N,N,Nf); %preallocate
+
+for f=1:Nf
+ z(:,:,f) = G\ ((E-s(:,:,f))\(s(:,:,f)+E))*Zref*G;
+end
diff --git a/CTB/s_renorm.m b/CTB/s_renorm.m new file mode 100644 index 0000000..2d887e0 --- /dev/null +++ b/CTB/s_renorm.m @@ -0,0 +1,19 @@ +function s_new = s_renorm(s, Z, Z_new) +% s_new = s_renorm(s, Z, Z_new) +% +% scattering matrix renormalization +% +% input: +% s: scattering matrix nxnxf (f: number of frequencies) +% Z: old reference impedance +% Z_new: new reference impedance +% +% output: +% s: renormalized scattering-matrix nxnxf +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% (c) Jan. 2014 + +z = s2z(s, Z); +s_new = z2s(z, Z_new); + diff --git a/CTB/write_touchstone.m b/CTB/write_touchstone.m new file mode 100644 index 0000000..c695669 --- /dev/null +++ b/CTB/write_touchstone.m @@ -0,0 +1,87 @@ +function write_touchstone( type, freq, data, filename, ref, comment ) +% write_touchstone( type, freq, data, filename [,ref [,comment]] ) - write touchstone-file +% for ADS +% type 'z' for Z-Parameter touchstone-file +% 'y' for Y-Parameter touchstone-file +% 's' for S-Parameter touchstone-file +% freq vector of frequencies (in Hz) +% data (numports x numports x numfreq) matrix +% filename filename of the file to create +% ref (optional) reference impedance +% comment (optional) written in the header +% +% (C) Sebastian Held <sebastian.held@gmx.de> +% Version 21. Feb 2006 + +if lower(type) == 'z' + par_type = 'Z'; + if nargin < 5; ref = 1; end +elseif lower(type) == 'y' + par_type = 'Y'; + if nargin < 5; ref = 1; end +elseif lower(type) == 's' + par_type = 'S'; + if nargin < 5; ref = 50; end +else + error( 'only z-, y- and s-parameters supported by now' ); +end + +if nargin < 6 + comment = ''; +end + +num_rows = size( data, 1 ); +num_columns = size( data, 2 ); +if num_rows ~= num_columns + error( 'number of rows and columns do not agree' ); +end + +[fid, message] = fopen( filename, 'wt' ); +if fid == -1 + error( message ); +end + +fprintf( fid, '%s\n', ['# Hz ' par_type ' RI R ' num2str(ref)] ); +fprintf( fid, '%s\n', ['! ' comment] ); + +if size( data, 1 ) == 1 || size( data, 1 ) >= 3 + write_data_1(data, fid, freq); +elseif size( data, 1 ) == 2 + write_data_2(data, fid, freq); +else + error( 'FIXME - unhandled dimension' ); +end + +fclose( fid ); +return; +end %write_touchstone() + +function write_data_1(data, fid, freq) +num_rows = size( data, 1 ); +num_columns = size( data, 2 ); +% for more than 3-port devices +for f=1:length(freq) + fprintf( fid, '%e ', freq(f) ); %Frequenz + for row = 1:num_rows + column = 1; + while column <= num_columns + for z=1:min( 4, num_columns-column+1 ); % nur maximal 4 Parameter in einer Zeile! (ADS-Begrenzung) + fprintf( fid, '% e % e ', real( data(row,column,f) ), imag( data(row,column,f) ) ); + column = column + 1; + end + fprintf( fid, '\n ' ); % Zeilenende + end + end + fprintf( fid, '\n' ); +end +end %write_data_1() + +function write_data_2(data, fid, freq) +% for 2-port devices +% ADS uses different format (what a mess) +for f=1:length(freq) + fprintf( fid, '%e % e % e % e % e % e % e % e % e\n', freq(f), ... + real(data(1,1,f)), imag(data(1,1,f)), real(data(2,1,f)), imag(data(2,1,f)), ... + real(data(1,2,f)), imag(data(1,2,f)), real(data(2,2,f)), imag(data(2,2,f)) ); %Frequenz +end +end %write_data_2() diff --git a/CTB/y2s.m b/CTB/y2s.m new file mode 100644 index 0000000..f2b3cac --- /dev/null +++ b/CTB/y2s.m @@ -0,0 +1,43 @@ +function s = y2s(y,ref)
+% s = y2s(y [,ref])
+%
+% Y-matrix to scattering transformation
+%
+% input:
+% y: Y-matrix matrix nxnxf (f: number of frequencies)
+% ref: (optional) reference impedance (default 50 Ohm)
+%
+% output:
+% s: S-matrix nxnxf
+%
+% Reference: http://qucs.sourceforge.net/tech/node98.html
+%
+% Thorsten Liebig <thorsten.liebig@gmx.de>
+% Feb. 2013
+
+if nargin < 2
+ Z0 = 50;
+else
+ Z0 = ref;
+end
+
+N = size(y,1);
+Nf = size(y,3);
+
+if (numel(Z0)==1)
+ Z0 = Z0*ones(N,1);
+end
+
+E = eye(N);
+Zref = zeros(N,N);
+G = Zref;
+for n=1:N
+ Zref(n,n) = Z0(n);
+ G(n,n) = 1/sqrt(real(Z0(n)));
+end
+
+s=zeros(N,N,Nf); %preallocate
+
+for f=1:Nf
+ s(:,:,f) = G*((E-Zref*y(:,:,f))/(E+Zref*y(:,:,f)))/G;
+end
diff --git a/CTB/y2z.m b/CTB/y2z.m new file mode 100644 index 0000000..b7b54ac --- /dev/null +++ b/CTB/y2z.m @@ -0,0 +1,22 @@ +function z = y2z(y) +% z = y2z(y) +% +% Y-matrix to Z-matrix +% +% input: +% y: Y-matrix matrix nxnxf (f: number of frequencies) +% +% output: +% z: Z-matrix nxnxf +% +% Reference: http://qucs.sourceforge.net/tech/node98.html +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% (c) 2013 + +Nf = size(y,3); + +z = y*0; % init +for f=1:Nf + z(:,:,f) = inv(y(:,:,f)); +end diff --git a/CTB/z2s.m b/CTB/z2s.m new file mode 100644 index 0000000..9f2e165 --- /dev/null +++ b/CTB/z2s.m @@ -0,0 +1,44 @@ +function s = z2s(z,ref)
+% s = z2s(z [,ref])
+%
+% Z-matrix to scattering transformation
+%
+% input:
+% z: Z-matrix matrix nxnxf (f: number of frequencies)
+% ref: (optional) reference impedance (default 50 Ohm)
+%
+% output:
+% s: S-matrix nxnxf
+%
+% Reference: http://qucs.sourceforge.net/tech/node98.html
+%
+% Thorsten Liebig <thorsten.liebig@gmx.de>
+% Feb. 2013
+
+if nargin < 2
+ Z0 = 50;
+else
+ Z0 = ref;
+end
+
+N = size(z,1);
+Nf = size(z,3);
+
+if (numel(Z0)==1)
+ Z0 = Z0*ones(N,1);
+end
+
+E = eye(N);
+Zref = zeros(N,N);
+G = Zref;
+
+for n=1:N
+ Zref(n,n) = Z0(n);
+ G(n,n) = 1/sqrt(real(Z0(n)));
+end
+
+s=zeros(N,N,Nf); %preallocate
+
+for f=1:Nf
+ s(:,:,f) = G*(z(:,:,f)-Zref)/(z(:,:,f)+Zref)/G;
+end
diff --git a/CTB/z2y.m b/CTB/z2y.m new file mode 100644 index 0000000..f8666a3 --- /dev/null +++ b/CTB/z2y.m @@ -0,0 +1,18 @@ +function y = z2y(z) +% y = z2y(z) +% +% Z-matrix to Y-matrix +% +% input: +% z: Z-matrix matrix nxnxf (f: number of frequencies) +% +% output: +% y: Y-matrix nxnxf +% +% Reference: http://qucs.sourceforge.net/tech/node98.html +% +% Thorsten Liebig <thorsten.liebig@gmx.de> +% (c) 2013 + +% y2z is just doing the invertion ... +y = y2z(z); |