summaryrefslogtreecommitdiff
path: root/CTB
diff options
context:
space:
mode:
Diffstat (limited to 'CTB')
-rw-r--r--CTB/AddElement2Port.m83
-rw-r--r--CTB/ApplyCurrent2Port.m69
-rw-r--r--CTB/ApplyRFPower2Port.m43
-rw-r--r--CTB/InitNetwork.m53
-rw-r--r--CTB/Makefile14
-rw-r--r--CTB/SetPortTermination.m45
-rw-r--r--CTB/a2s.m31
-rw-r--r--CTB/a_PI.m15
-rw-r--r--CTB/a_T.m14
-rw-r--r--CTB/a_mul.m28
-rw-r--r--CTB/a_series.m22
-rw-r--r--CTB/a_shunt.m22
-rw-r--r--CTB/license.txt22
-rw-r--r--CTB/read_touchstone.m150
-rw-r--r--CTB/readme.txt29
-rw-r--r--CTB/s2a.m31
-rw-r--r--CTB/s2y.m43
-rw-r--r--CTB/s2z.m43
-rw-r--r--CTB/s_renorm.m19
-rw-r--r--CTB/write_touchstone.m87
-rw-r--r--CTB/y2s.m43
-rw-r--r--CTB/y2z.m22
-rw-r--r--CTB/z2s.m44
-rw-r--r--CTB/z2y.m18
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);