* matrix.cpp - matrix class implementation
*
* Copyright (C) 2003-2009 Stefan Jahn <stefan@lkcc.org>
* Copyright (C) 2012-2017 Qucs Team
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this package; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* $Id$
*
*/
\brief Dense matrix class implementation
References:
[1] Power Waves and the Scattering Matrix
Kurokawa, K.
Microwave Theory and Techniques, IEEE Transactions on,
Vol.13, Iss.2, Mar 1965
Pages: 194- 202
[2] A Rigorous Technique for Measuring the Scattering Matrix of
a Multiport Device with a 2-Port Network Analyzer
John C. TIPPET, Ross A. SPECIALE
Microwave Theory and Techniques, IEEE Transactions on,
Vol.82, Iss.5, May 1982
Pages: 661- 666
[3] Comments on "A Rigorous Technique for Measuring the Scattering
Matrix of a Multiport Device with a Two-Port Network Analyzer"
Dropkin, H.
Microwave Theory and Techniques, IEEE Transactions on,
Vol. 83, Iss.1, Jan 1983
Pages: 79 - 81
[4] Arbitrary Impedance
"Accurate Measurements In Almost Any
Impedance Environment"
in Scropion Application note
Anritsu
online (2016-02-21)
https://dl.cdn-anritsu.com/en-us/test-measurement/files/Application-Notes/Application-Note/11410-00284B.pdf
[5] Conversions between S, Z, Y, H, ABCD, and T parameters
which are valid for complex source and load impedances
Frickey, D.A.
Microwave Theory and Techniques, IEEE Transactions on
Vol. 42, Iss. 2, Feb 1994
pages: 205 - 211
doi: 10.1109/22.275248
[6] Comments on "Conversions between S, Z, Y, h, ABCD,
and T parameters which are valid for complex source and load impedances" [and reply]
Marks, R.B.; Williams, D.F.; Frickey, D.A.
Microwave Theory and Techniques, IEEE Transactions on,
Vol.43, Iss.4, Apr 1995
Pages: 914- 915
doi: 10.1109/22.375247
[7] Wave Techniques for Noise Modeling and Measurement
S. W. Wedge and D. B. Rutledge,
IEEE Transactions on Microwave Theory and Techniques,
vol. 40, no. 11, Nov. 1992.
pages 2004-2012,
doi: 10.1109/22.168757
Author copy online (2007/07/31)
http://authors.library.caltech.edu/6226/01/WEDieeetmtt92.pdf
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <cstdlib>
#include <string.h>
#include <cmath>
#include "logging.h"
#include "object.h"
#include "complex.h"
#include "vector.h"
#include "matrix.h"
namespace qucs {
Constructor creates an unnamed instance of the matrix class.
*/
matrix::matrix () {
rows = 0;
cols = 0;
data = NULL;
}
Constructor creates an unnamed instance of the matrix class with a
certain number of rows and columns. Particularly creates a square matrix.
\param[in] s number of rows or columns of square matrix
\todo Why not s const?
*/
matrix::matrix (int s) {
rows = cols = s;
data = (s > 0) ? new nr_complex_t[s * s] : NULL;
}
Constructor creates an unnamed instance of the matrix class with a
certain number of rows and columns.
\param[in] r number of rows
\param[in] c number of column
\todo Why not r and c constant
\todo Assert r >= 0 and c >= 0
*/
matrix::matrix (int r, int c) {
rows = r;
cols = c;
data = (r > 0 && c > 0) ? new nr_complex_t[r * c] : NULL;
}
The copy constructor creates a new instance based on the given
matrix object.
\todo Add assert tests
*/
matrix::matrix (const matrix & m) {
rows = m.rows;
cols = m.cols;
data = NULL;
if (rows > 0 && cols > 0) {
data = new nr_complex_t[rows * cols];
memcpy (data, m.data, sizeof (nr_complex_t) * rows * cols);
}
}
The assignment copy constructor creates a new instance based on the
given matrix object.
\param[in] m object to copy
\return assigned object
\note m = m is safe
*/
const matrix& matrix::operator=(const matrix & m) {
if (&m != this) {
rows = m.rows;
cols = m.cols;
if (data) {
delete[] data;
data = NULL;
}
if (rows > 0 && cols > 0) {
data = new nr_complex_t[rows * cols];
memcpy (data, m.data, sizeof (nr_complex_t) * rows * cols);
}
}
return *this;
}
Destructor deletes a matrix object.
*/
matrix::~matrix () {
delete[] data;
}
\param[in] r row number
\param[in] c column number
\todo Why not inline and synonymous of ()
\todo c and r const
*/
nr_complex_t matrix::get (int r, int c) {
return data[r * cols + c];
}
\param[in] r row number
\param[in] c column number
\param[in] z complex number to assign
\todo Why not inline and synonymous of ()
\todo r c and z const
*/
void matrix::set (int r, int c, nr_complex_t z) {
data[r * cols + c] = z;
}
#ifdef DEBUG
void matrix::print (void) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
fprintf (stderr, "%+.2e,%+.2e ", (double) real (get (r, c)),
(double) imag (get (r, c)));
}
fprintf (stderr, "\n");
}
}
#endif
\param[a] first matrix
\param[b] second matrix
\note assert same size
\todo a and b are const
*/
matrix operator + (matrix a, matrix b) {
assert (a.getRows () == b.getRows () && a.getCols () == b.getCols ());
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) + b.get (r, c));
return res;
}
\param[in] a matrix to add
\note assert same size
\todo a is const
*/
matrix matrix::operator += (matrix a) {
assert (a.getRows () == rows && a.getCols () == cols);
int r, c, i;
for (i = 0, r = 0; r < a.getRows (); r++)
for (c = 0; c < a.getCols (); c++, i++)
data[i] += a.get (r, c);
return *this;
}
\param[a] first matrix
\param[b] second matrix
\note assert same size
\todo a and b are const
*/
matrix operator - (matrix a, matrix b) {
assert (a.getRows () == b.getRows () && a.getCols () == b.getCols ());
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) - b.get (r, c));
return res;
}
matrix matrix::operator - () {
matrix res (getRows (), getCols ());
int r, c, i;
for (i = 0, r = 0; r < getRows (); r++)
for (c = 0; c < getCols (); c++, i++)
res.set (r, c, -data[i]);
return res;
}
\param[in] a matrix to substract
\note assert same size
*/
matrix matrix::operator -= (matrix a) {
assert (a.getRows () == rows && a.getCols () == cols);
int r, c, i;
for (i = 0, r = 0; r < a.getRows (); r++)
for (c = 0; c < a.getCols (); c++, i++)
data[i] -= a.get (r, c);
return *this;
}
\param[in] a matrix to scale
\param[in] z scaling complex
\return Scaled matrix
\todo Why not a and z const
*/
matrix operator * (matrix a, nr_complex_t z) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) * z);
return res;
}
\param[in] a matrix to scale
\param[in] z scaling complex
\return Scaled matrix
\todo Why not a and z const
\todo Why not inline
*/
matrix operator * (nr_complex_t z, matrix a) {
return a * z;
}
\param[in] a matrix to scale
\param[in] d scaling real
\return Scaled matrix
\todo Why not d and a const
*/
matrix operator * (matrix a, nr_double_t d) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) * d);
return res;
}
\param[in] a matrix to scale
\param[in] d scaling real
\return Scaled matrix
\todo Why not inline?
\todo Why not d and a const
*/
matrix operator * (nr_double_t d, matrix a) {
return a * d;
}
\param[in] a matrix to scale
\param[in] z scaling complex
\return Scaled matrix
\todo Why not a and z const
*/
matrix operator / (matrix a, nr_complex_t z) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) / z);
return res;
}
\param[in] a matrix to scale
\param[in] d scaling real
\return Scaled matrix
\todo Why not a and d const
*/
matrix operator / (matrix a, nr_double_t d) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) / d);
return res;
}
Dumb and not optimized matrix multiplication
\param[a] first matrix
\param[b] second matrix
\note assert compatibility
\todo a and b are const
*/
matrix operator * (matrix a, matrix b) {
assert (a.getCols () == b.getRows ());
int r, c, i, n = a.getCols ();
nr_complex_t z;
matrix res (a.getRows (), b.getCols ());
for (r = 0; r < a.getRows (); r++) {
for (c = 0; c < b.getCols (); c++) {
for (i = 0, z = 0; i < n; i++) z += a.get (r, i) * b.get (i, c);
res.set (r, c, z);
}
}
return res;
}
\param[in] a matrix
\param[in] z complex to add
\todo Move near other +
\todo a and z are const
*/
matrix operator + (matrix a, nr_complex_t z) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) + z);
return res;
}
\param[in] a matrix
\param[in] z complex to add
\todo Move near other +
\todo a and z are const
\todo Why not inline
*/
matrix operator + (nr_complex_t z, matrix a) {
return a + z;
}
\param[in] a matrix
\param[in] d real to add
\todo Move near other +
\todo a and d are const
*/
matrix operator + (matrix a, nr_double_t d) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, a.get (r, c) + d);
return res;
}
\param[in] a matrix
\param[in] d real to add
\todo Move near other +
\todo a and d are const
\todo Why not inline
*/
matrix operator + (nr_double_t d, matrix a) {
return a + d;
}
\param[in] a matrix
\param[in] z complex to add
\todo Move near other +
\todo a and z are const
\todo Why not inline
*/
matrix operator - (matrix a, nr_complex_t z) {
return -z + a;
}
\param[in] a matrix
\param[in] z complex to add
\todo Move near other +
\todo a and z are const
\todo Why not inline
*/
matrix operator - (nr_complex_t z, matrix a) {
return -a + z;
}
\param[in] a matrix
\param[in] z real to add
\todo Move near other +
\todo a and z are const
\todo Why not inline
*/
matrix operator - (matrix a, nr_double_t d) {
return -d + a;
}
\param[in] a matrix
\param[in] z real to add
\todo Move near other +
\todo a and z are const
\todo Why not inline
*/
matrix operator - (nr_double_t d, matrix a) {
return -a + d;
}
\param[in] a Matrix to transpose
\todo add transpose in place
\todo a is const
*/
matrix transpose (matrix a) {
matrix res (a.getCols (), a.getRows ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (c, r, a.get (r, c));
return res;
}
\param[in] a Matrix to conjugate
\todo add conj in place
\todo a is const
*/
matrix conj (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, conj (a.get (r, c)));
return res;
}
The function returns the adjoint complex matrix. This is also
called the adjugate or transpose conjugate.
\param[in] a Matrix to transpose
\todo add adjoint in place
\todo Do not lazy and avoid conj and transpose copy
\todo a is const
*/
matrix adjoint (matrix a) {
return transpose (conj (a));
}
\param[in] a matrix
\todo add abs in place
\todo a is const
*/
matrix abs (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, abs (a.get (r, c)));
return res;
}
\param[in] a matrix
*/
matrix dB (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, dB (a.get (r, c)));
return res;
}
\param[in] a matrix
\todo add arg in place
\todo a is const
*/
matrix arg (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, arg (a.get (r, c)));
return res;
}
\param[in] a matrix
\todo add real in place
\todo a is const
*/
matrix real (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, real (a.get (r, c)));
return res;
}
\param[in] a matrix
\todo add imag in place
\todo a is const
*/
matrix imag (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, imag (a.get (r, c)));
return res;
}
\param[in] a matrix
*/
matrix sqr (matrix a) {
return a * a;
}
\param[in] rs row number
\param[in] cs column number
\todo Avoid res.get*
\todo Use memset
\todo rs, cs are const
*/
matrix eye (int rs, int cs) {
matrix res (rs, cs);
for (int r = 0; r < res.getRows (); r++)
for (int c = 0; c < res.getCols (); c++)
if (r == c) res.set (r, c, 1);
return res;
}
\param[in] s row or column number of square matrix
\todo Do not by lazy and implement it
\todo s is const
*/
matrix eye (int s) {
return eye (s, s);
}
\param[in] diag vector to write on the diagonal
\todo diag is const
*/
matrix diagonal (qucs::vector diag) {
int size = diag.getSize ();
matrix res (size);
for (int i = 0; i < size; i++) res (i, i) = diag (i);
return res;
}
matrix pow (matrix a, int n) {
matrix res;
if (n == 0) {
res = eye (a.getRows (), a.getCols ());
}
else {
res = a = n < 0 ? inverse (a) : a;
for (int i = 1; i < std::abs (n); i++)
res = res * a;
}
return res;
}
The cofactor is the determinant obtained by deleting the row and column
of a given element of a matrix or determinant.
The cofactor is preceded by a + or - sign depending of the sign
of \f$(-1)^(u+v)\f$
\bug This algorithm is recursive! Stack overfull!
\todo ((u + v) & 1) is cryptic use (u + v)% 2
\todo #ifdef 0
\todo static?
*/
nr_complex_t cofactor (matrix a, int u, int v) {
matrix res (a.getRows () - 1, a.getCols () - 1);
int r, c, ra, ca;
for (ra = r = 0; r < res.getRows (); r++, ra++) {
if (ra == u) ra++;
for (ca = c = 0; c < res.getCols (); c++, ca++) {
if (ca == v) ca++;
res.set (r, c, a.get (ra, ca));
}
}
nr_complex_t z = detLaplace (res);
return ((u + v) & 1) ? -z : z;
}
The Laplace expansion of the determinant of
an n by n square matrix a expresses
the determinant of a as a sum of n determinants of (n-1) by (n-1)
sub-matrices of a. There are 2n such expressions, one for each row
and column of a.
See Wikipedia http://en.wikipedia.org/wiki/Laplace_expansion
\param[in] a matrix
\bug This algorithm is recursive! Stack overfull!
\note assert square matrix
\todo #ifdef 0
\todo static ?
*/
nr_complex_t detLaplace (matrix a) {
assert (a.getRows () == a.getCols ());
int s = a.getRows ();
nr_complex_t res = 0;
if (s > 1) {
use the row or column with most zeros in it */
int r = 0;
for (int i = 0; i < s; i++) {
res += a.get (r, i) * cofactor (a, r, i);
}
return res;
}
else if (s == 1) {
return a (0, 0);
}
return 1;
}
Compute determinant of the given matrix using the Gaussian
algorithm. This means to triangulate the matrix and multiply all
the diagonal elements.
\param[in] a matrix
\note assert square matrix
\todo static ?
\todo a const?
*/
nr_complex_t detGauss (matrix a) {
assert (a.getRows () == a.getCols ());
nr_double_t MaxPivot;
nr_complex_t f, res;
matrix b;
int i, c, r, pivot, n = a.getCols ();
if (n == 0) return 1;
if (n == 1) return a (0, 0);
b = matrix (a);
for (res = 1, i = 0; i < n; i++) {
for (MaxPivot = 0, pivot = r = i; r < n; r++) {
if (abs (b.get (r, i)) > MaxPivot) {
MaxPivot = abs (b.get (r, i));
pivot = r;
}
}
assert (MaxPivot != 0);
if (i != pivot) { b.exchangeRows (i, pivot); res = -res; }
for (r = i + 1; r < n; r++) {
f = b.get (r, i) / b.get (i, i);
for (c = i + 1; c < n; c++) {
b.set (r, c, b.get (r, c) - f * b.get (i, c));
}
}
}
for (i = 0; i < n; i++) res *= b.get (i, i);
return res;
}
\param[in] a matrix
\return Complex determinant
\todo a const?
*/
nr_complex_t det (matrix a) {
#if 0
return detLaplace (a);
#else
return detGauss (a);
#endif
}
Compute inverse matrix of the given matrix using Laplace expansion.
\param[in] a matrix to invert
\todo Static?
\bug recursive! Stack overflow
\todo a const?
\todo #ifdef 0
*/
matrix inverseLaplace (matrix a) {
matrix res (a.getRows (), a.getCols ());
nr_complex_t d = detLaplace (a);
assert (abs (d) != 0);
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, cofactor (a, c, r) / d);
return res;
}
Compute inverse matrix of the given matrix by Gauss-Jordan
elimination.
\todo a const?
\todo static?
\note assert non singulat matrix
\param[in] a matrix to invert
*/
matrix inverseGaussJordan (matrix a) {
nr_double_t MaxPivot;
nr_complex_t f;
matrix b, e;
int i, c, r, pivot, n = a.getCols ();
b = matrix (a);
e = eye (n);
for (i = 0; i < n; i++) {
for (MaxPivot = 0, pivot = r = i; r < n; r++) {
if (abs (b (r, i)) > MaxPivot) {
MaxPivot = abs (b (r, i));
pivot = r;
}
}
assert (MaxPivot != 0);
if (i != pivot) {
b.exchangeRows (i, pivot);
e.exchangeRows (i, pivot);
}
for (f = b (i, i), c = 0; c < n; c++) {
b (i, c) /= f;
e (i, c) /= f;
}
for (r = 0; r < n; r++) {
if (r != i) {
for (f = b (r, i), c = 0; c < n; c++) {
b (r, c) -= f * b (i, c);
e (r, c) -= f * e (i, c);
}
}
}
}
return e;
}
\param[in] a matrix to invert
\todo a is const
*/
matrix inverse (matrix a) {
#if 0
return inverseLaplace (a);
#else
return inverseGaussJordan (a);
#endif
}
Convert scattering parameters with the reference impedance 'zref'
to scattering parameters with the reference impedance 'z0'.
Details are given in [1], under equation (32). Brief summary:
The new scattering matrix \f$S'\f$ is:
\f[
S'=A^{-1}(S-\Gamma^+)(I-\Gamma S)^{-1}A^+
\f]
where \f$I\f$ is the identity matrix, \f$\Gamma\f$ a diagonal matrix with
diagonal components \f$\Gamma_i=r_i\f$ and \f$A\f$ a diagonal matrix with
diagonal components
\f[
A_i = \frac{(1-r_i^*)\sqrt{|1-r_ir_i^*|}}{|1-r_i|}
\f]
and \f$r_i\f$ is the power wave reflexion coefficient of \f$Z_i'\f$ with
respect to \f$Z_i^*\f$ (where \f$Z_i'\f$ is the new impedance and
\f$Z_i\f$ is the old impedance), i.e.:
\f[
r_i = \frac{Z_i'-Z_i}{Z_i'+Z_i^*}
\f]
(\f$x^*\f$ denotes the complex conjugate of \f$x\f$ and
\f$A^+\f$ the complex conjugate transpose (Hermitian adjoint) of \f$A\f$)
\param[in] s original S matrix
\param[in] zref original reference impedance
\param[in] z0 new reference impedance
\bug The function implements only the real zref and z0 case!
\todo Correct documentation about standing waves [1-4]
\todo Implement Speciale implementation [2-3] if applicable
\return Renormalized scattering matrix
\todo s, zref and z0 const
*/
matrix stos (matrix s, qucs::vector zref, qucs::vector z0) {
int d = s.getRows ();
matrix e, r;
qucs::vector a;
assert (d == s.getCols () && d == z0.getSize () && d == zref.getSize ());
e = eye (d);
r = diagonal ((z0 - zref) / (z0 + zref));
a = sqrt (z0 / zref) * 2 * zref / (z0 + zref);
return diagonal (1 / a) * (s - r) * inverse (e - r * s) * diagonal (a);
}
\param[in] s original S matrix
\param[in] zref original reference impedance
\param[in] z0 new reference impedance
\todo Why not inline
\return Renormalized scattering matrix
\todo s, zref and z0 const
*/
matrix stos (matrix s, nr_complex_t zref, nr_complex_t z0) {
int d = s.getRows ();
return stos (s, qucs::vector (d, zref), qucs::vector (d, z0));
}
\param[in] s original S matrix
\param[in] zref original reference impedance
\param[in] z0 new reference impedance
\todo Why not inline
\return Renormalized scattering matrix
\todo s, zref and z0 const
*/
matrix stos (matrix s, nr_double_t zref, nr_double_t z0) {
return stos (s, nr_complex_t (zref, 0), nr_complex_t (z0, 0));
}
\param[in] s original S matrix
\param[in] zref original reference impedance
\param[in] z0 new reference impedance
\todo Why not inline
\return Renormalized scattering matrix
\todo s, zref and z0 const
*/
matrix stos (matrix s, qucs::vector zref, nr_complex_t z0) {
return stos (s, zref, qucs::vector (zref.getSize (), z0));
}
\param[in] s original S matrix
\param[in] zref original reference impedance
\param[in] z0 new reference impedance
\todo Why not inline
\todo s, zref and z0 const
\return Renormalized scattering matrix
*/
matrix stos (matrix s, nr_complex_t zref, qucs::vector z0) {
return stos (s, qucs::vector (z0.getSize (), zref), z0);
}
Convert scattering parameters to impedance matrix.
According to [1] eq (19):
\f[
Z=F^{-1} (I- S)^{-1} (SG + G^+) F
\f]
Where \f$S\f$ is the scattering matrix, \f$x^+\f$
is the adjoint of x, I the identity matrix. The matrix
F and G are diagonal matrix defined by:
\f{align*}
F_i&=\frac{1}{2\sqrt{\Re\text{e}\; Z_i}} \\
G_i&=Z_i
\f}
\param[in] s Scattering matrix
\param[in] z0 Normalisation impedance
\note We could safely drop the \f$1/2\f$ in \f$F\f$ because we compute
\f$FXF^{-1}\f$ and therefore \f$1/2\f$ will simplify.
\bug not correct if zref is complex
\todo s, z0 const
\return Impedance matrix
*/
matrix stoz (matrix s, qucs::vector z0) {
int d = s.getRows ();
matrix e, zref, gref;
assert (d == s.getCols () && d == z0.getSize ());
e = eye (d);
zref = diagonal (z0);
gref = diagonal (sqrt (real (1 / z0)));
return inverse (gref) * inverse (e - s) * (s * zref + zref) * gref;
}
\param[in] s Scattering matrix
\param[in] z0 Normalisation impedance
\return Impedance matrix
\todo Why not inline?
\todo s and z0 const?
*/
matrix stoz (matrix s, nr_complex_t z0) {
return stoz (s, qucs::vector (s.getRows (), z0));
}
Convert scattering parameters to impedance matrix.
According to [1] eq (18):
\f[
S=F(Z-G^+)(Z+G)^{-1} F^{-1}
\f]
Where \f$Z\f$ is the scattering matrix, \f$x^+\f$
is the adjoint of x, I the identity matrix. The matrix
F and G are diagonal matrix defined by:
\f{align*}
F_i&=\frac{1}{2\sqrt{\Re\text{e}\; Z_i}} \\
G_i&=Z_i
\f}
\param[in] Z Impedance matrix
\param[in] z0 Normalisation impedance
\return Scattering matrix
\note We could safely drop the \f$1/2\f$ in \f$F\f$ because we compute
\f$FXF^{-1}\f$ and therefore \f$1/2\f$ will simplify.
\bug not correct if zref is complex
\todo z and z0 const?
*/
matrix ztos (matrix z, qucs::vector z0) {
int d = z.getRows ();
matrix e, zref, gref;
assert (d == z.getCols () && d == z0.getSize ());
e = eye (d);
zref = diagonal (z0);
gref = diagonal (sqrt (real (1 / z0)));
return gref * (z - zref) * inverse (z + zref) * inverse (gref);
}
\param[in] Z Impedance matrix
\param[in] z0 Normalisation impedance
\return Scattering matrix
\todo Why not inline
\todo z and z0 const
*/
matrix ztos (matrix z, nr_complex_t z0) {
return ztos (z, qucs::vector (z.getRows (), z0));
}
Convert impedance matrix to admittance matrix. By definition
\f$Y=Z^{-1}\f$
\param[in] z impedance matrix
\return Admittance matrix
\todo Why not inline
\todo z const
*/
matrix ztoy (matrix z) {
assert (z.getRows () == z.getCols ());
return inverse (z);
}
Convert scattering parameters to admittance matrix.
According to [1] eq (19):
\f[
Z=F^{-1} (I- S)^{-1} (SG + G^+) F
\f]
Where \f$S\f$ is the scattering matrix, \f$x^+\f$
is the adjoint of x, I the identity matrix. The matrix
F and G are diagonal matrix defined by:
\f{align*}
F_i&=\frac{1}{2\sqrt{\Re\text{e}\; Z_i}} \\
G_i&=Z_i
\f}
Using the well know formula \f$(AB)^{-1}=B^{1}A^{1}\f$,
we derivate:
\f[
Y=F^{-1} (SG+G^+)^{-1} (I-S) F
\f]
\param[in] s Scattering matrix
\param[in] z0 Normalisation impedance
\note We could safely drop the \f$1/2\f$ in \f$F\f$ because we compute
\f$FXF^{-1}\f$ and therefore \f$1/2\f$ will simplify.
\bug not correct if zref is complex
\todo s and z0 const
\return Admittance matrix
*/
matrix stoy (matrix s, qucs::vector z0) {
int d = s.getRows ();
matrix e, zref, gref;
assert (d == s.getCols () && d == z0.getSize ());
e = eye (d);
zref = diagonal (z0);
gref = diagonal (sqrt (real (1 / z0)));
return inverse (gref) * inverse (s * zref + zref) * (e - s) * gref;
}
\param[in] S Scattering matrix
\param[in] z0 Normalisation impedance
\return Admittance matrix
\todo Why not inline
\todo s and z0 const
*/
matrix stoy (matrix s, nr_complex_t z0) {
return stoy (s, qucs::vector (s.getRows (), z0));
}
Convert admittance matrix to scattering parameters.
Using the same methodology as [1] eq (16-19), but writing
(16) as \f$i=Yv\f$, ie
\f[
S=F(I-G^+Y)(I-GY)^{-1}F^{-1}
\f]
Where \f$S\f$ is the scattering matrix, \f$x^+\f$
is the adjoint of x, I the identity matrix. The matrix
F and G are diagonal matrix defined by:
\f{align*}
F_i&=\frac{1}{2\sqrt{\Re\text{e}\; Z_i}} \\
G_i&=Z_i
\f}
Using the well know formula \f$(AB)^{-1}=B^{1}A^{1}\f$,
we derivate:
\f[
Y=F^{-1} (SG+G^+)^{-1} (I-S) F
\f]
\param[in] y admittance matrix
\param[in] z0 Normalisation impedance
\note We could safely drop the \f$1/2\f$ in \f$F\f$ because we compute
\f$FXF^{-1}\f$ and therefore \f$1/2\f$ will simplify.
\bug not correct if zref is complex
\todo why not y and z0 const
\return Scattering matrix
*/
matrix ytos (matrix y, qucs::vector z0) {
int d = y.getRows ();
matrix e, zref, gref;
assert (d == y.getCols () && d == z0.getSize ());
e = eye (d);
zref = diagonal (z0);
gref = diagonal (sqrt (real (1 / z0)));
return gref * (e - zref * y) * inverse (e + zref * y) * inverse (gref);
}
\param[in] y Admittance matrix
\param[in] z0 Normalisation impedance
\return Scattering matrix
\todo Why not inline
\todo y and z0 const
*/
matrix ytos (matrix y, nr_complex_t z0) {
return ytos (y, qucs::vector (y.getRows (), z0));
}
Converts scattering parameters to chain matrix.
Formulae are given by [5] tab 1. and are remembered here:
\f{align*}
A&=\frac{(Z_{01}^*+S_{11}Z_{01})(1-S_{22})
+S_{12}S_{21}Z_{01}}{\Delta} \\
B&=\frac{(Z_{01}^*+S_{11}Z_{01})(Z_{02}^*+S_{22}Z_{02})
-S_{12}S_{21}Z_{01}Z_{02}}{\Delta} \\
C&=\frac{(1-S_{11})(1-S_{22})
-S_{12}S_{21}}{\Delta} \\
D&=\frac{(1-S_{11})(Z_{02}^*+S_{22}Z_{02})
+S_{12}S_{21}Z_{02}}{\Delta}
\f}
Where:
\f[
\Delta = 2 S_{21}\sqrt{\Re\text{e}\;Z_{01}\Re\text{e}\;Z_{02}}
\f]
\bug Do not need fabs
\param[in] s Scattering matrix
\param[in] z1 impedance at input 1
\param[in] z2 impedance at input 2
\return Chain matrix
\note Assert 2 by 2 matrix
\todo Why not s,z1,z2 const
*/
matrix stoa (matrix s, nr_complex_t z1, nr_complex_t z2) {
nr_complex_t d = s (0, 0) * s (1, 1) - s (0, 1) * s (1, 0);
nr_complex_t n = 2.0 * s (1, 0) * sqrt (fabs (real (z1) * real (z2)));
matrix a (2);
assert (s.getRows () >= 2 && s.getCols () >= 2);
a.set (0, 0, (conj (z1) + z1 * s (0, 0) -
conj (z1) * s (1, 1) - z1 * d) / n);
a.set (0, 1, (conj (z1) * conj (z2) + z1 * conj (z2) * s (0, 0) +
conj (z1) * z2 * s (1, 1) + z1 * z2 * d) / n);
a.set (1, 0, (1.0 - s (0, 0) - s (1, 1) + d) / n);
a.set (1, 1, (conj (z2) - conj (z2) * s (0, 0) +
z2 * s (1, 1) - z2 * d) / n);
return a;
}
Converts chain matrix to scattering parameters
Formulae are given by [5] and are remembered here:
\f{align*}
S_{11}&=\frac{AZ_{02}+B-CZ_{01}^*Z_{02}-DZ_{01}^*}{\Delta} \\
S_{12}&=\frac{2(AD-BC)
(\Re\text{e}\;Z_{01}\Re\text{e}\;Z_{02})^{1/2}}
{\Delta}\\
S_{21}&=\frac{2(\Re\text{e}\;Z_{01}\Re\text{e}\;Z_{02})^{1/2}}{\Delta}\\
S_{22}&=\frac{-AZ_{02}^*+B-CZ_{01}^*Z_{02}+DZ_{01}}{\Delta}
\f}
Where:
\f[
\Delta =AZ_{02}+B+CZ_{01}Z_{02}-DZ_{01}
\f]
\param[in] a Chain matrix
\param[in] z1 impedance at input 1
\param[in] z2 impedance at input 2
\return Scattering matrix
\bug Do not use fabs
\todo a, z1, z2 const
*/
matrix atos (matrix a, nr_complex_t z1, nr_complex_t z2) {
nr_complex_t d = 2.0 * sqrt (fabs (real (z1) * real (z2)));
nr_complex_t n = a (0, 0) * z2 + a (0, 1) +
a (1, 0) * z1 * z2 + a (1, 1) * z1;
matrix s (2);
assert (a.getRows () >= 2 && a.getCols () >= 2);
s.set (0, 0, (a (0, 0) * z2 + a (0, 1)
- a (1, 0) * conj (z1) * z2 - a (1, 1) * conj (z1)) / n);
s.set (0, 1, (a (0, 0) * a (1, 1) -
a (0, 1) * a (1, 0)) * d / n);
s.set (1, 0, d / n);
s.set (1, 1, (a (1, 1) * z1 - a (0, 0) * conj (z2) +
a (0, 1) - a (1, 0) * z1 * conj (z2)) / n);
return s;
}
Converts chain matrix to scattering parameters
Formulae are given by [5] and are remembered here:
\f{align*}
h_{11}&=\frac{(Z_{01}^*+S_{11}Z_{01})
(Z_{02}^*+S_{22}Z_{02})
-S_{12}S_{21}Z_{01}Z_{02}}{\Delta}\\
h_{12}&=\frac{2S_{12}
(\Re\text{e}\;Z_{01} \Re\text{e}\;Z_{02})^\frac{1}{2}}
{\Delta} \\
h_{21}&=\frac{-2S_{21}
(\Re\text{e}\;Z_{01} \Re\text{e}\;Z_{02})^\frac{1}{2}}
{\Delta} \\
h_{22}&=\frac{(1-S_{11})(1-S_{22})-S_{12}S_{21}}{\Delta}
\f}
Where \f$\Delta\f$ is:
\f[
\Delta=(1-S_{11})(Z_{02}^*+S_{22}Z_{02})+S_{12}S_{21}Z_{02}
\f]
\bug{Programmed formulae are valid only for Z real}
\param[in] s Scattering matrix
\param[in] z1 impedance at input 1
\param[in] z2 impedance at input 2
\return hybrid matrix
\note Assert 2 by 2 matrix
\todo Why not s,z1,z2 const
*/
matrix stoh (matrix s, nr_complex_t z1, nr_complex_t z2) {
nr_complex_t n = s (0, 1) * s (1, 0);
nr_complex_t d = (1.0 - s (0, 0)) * (1.0 + s (1, 1)) + n;
matrix h (2);
assert (s.getRows () >= 2 && s.getCols () >= 2);
h.set (0, 0, ((1.0 + s (0, 0)) * (1.0 + s (1, 1)) - n) * z1 / d);
h.set (0, 1, +2.0 * s (0, 1) / d);
h.set (1, 0, -2.0 * s (1, 0) / d);
h.set (1, 1, ((1.0 - s (0, 0)) * (1.0 - s (1, 1)) - n) / z2 / d);
return h;
}
Formulae are given by [5] and are remembered here:
\f{align*}
S_{11}&=\frac{(h_{11}-Z_{01}^*)(1+h_{22}Z_{02})-h_{12}h_{21}Z_{02}}
{\Delta}\\
S_{12}&=\frac{-2h_{12}(\Re\text{e}\;Z_{01}\Re\text{e}\;Z_{02})^\frac{1}{2}}
{\Delta}\\
S_{21}&=\frac{-2h_{21}(\Re\text{e}\;Z_{01}\Re\text{e}\;Z_{02})^\frac{1}{2}}
{\Delta}\\
S_{22}&=\frac{(h_{11}+Z_{01})(1-h_{22}Z_{02}^*)-h_{12}h_{21}Z_{02}^*}
{\Delta}
\f}
Where \f$\Delta\f$ is:
\f[
\Delta=(Z_{01}+h_{11})(1+h_{22}Z_{02})-h_{12}h_{21}Z_{02}
\f]
\param[in] h hybrid matrix
\param[in] z1 impedance at input 1
\param[in] z2 impedance at input 2
\return scattering matrix
\note Assert 2 by 2 matrix
\todo Why not h,z1,z2 const
*/
matrix htos (matrix h, nr_complex_t z1, nr_complex_t z2) {
nr_complex_t n = h (0, 1) * h (1, 0);
nr_complex_t d = (1.0 + h (0, 0) / z1) * (1.0 + z2 * h (1, 1)) - n;
matrix s (2);
assert (h.getRows () >= 2 && h.getCols () >= 2);
s.set (0, 0, ((h (0, 0) / z1 - 1.0) * (1.0 + z2 * h (1, 1)) - n) / d);
s.set (0, 1, +2.0 * h (0, 1) / d);
s.set (1, 0, -2.0 * h (1, 0) / d);
s.set (1, 1, ((1.0 + h (0, 0) / z1) * (1.0 - z2 * h (1, 1)) + n) / d);
return s;
}
\bug{Programmed formulae are valid only for Z real}
\bug{Not documented and references}
\param[in] s Scattering matrix
\param[in] z1 impedance at input 1
\param[in] z2 impedance at input 2
\return second hybrid matrix
\note Assert 2 by 2 matrix
\todo Why not s,z1,z2 const
*/
matrix stog (matrix s, nr_complex_t z1, nr_complex_t z2) {
nr_complex_t n = s (0, 1) * s (1, 0);
nr_complex_t d = (1.0 + s (0, 0)) * (1.0 - s (1, 1)) + n;
matrix g (2);
assert (s.getRows () >= 2 && s.getCols () >= 2);
g.set (0, 0, ((1.0 - s (0, 0)) * (1.0 - s (1, 1)) - n) / z1 / d);
g.set (0, 1, -2.0 * s (0, 1) / d);
g.set (1, 0, +2.0 * s (1, 0) / d);
g.set (1, 1, ((1.0 + s (0, 0)) * (1.0 + s (1, 1)) - n) * z2 / d);
return g;
}
\bug{Programmed formulae are valid only for Z real}
\bug{Not documented and references}
\param[in] g second hybrid matrix
\param[in] z1 impedance at input 1
\param[in] z2 impedance at input 2
\return scattering matrix
\note Assert 2 by 2 matrix
\todo Why not g,z1,z2 const
*/
matrix gtos (matrix g, nr_complex_t z1, nr_complex_t z2) {
nr_complex_t n = g (0, 1) * g (1, 0);
nr_complex_t d = (1.0 + g (0, 0) * z1) * (1.0 + g (1, 1) / z2) - n;
matrix s (2);
assert (g.getRows () >= 2 && g.getCols () >= 2);
s.set (0, 0, ((1.0 - g (0, 0) * z1) * (1.0 + g (1, 1) / z2) + n) / d);
s.set (0, 1, -2.0 * g (0, 1) / d);
s.set (1, 0, +2.0 * g (1, 0) / d);
s.set (1, 1, ((g (0, 0) * z1 + 1.0) * (g (1, 1) / z2 - 1.0) - n) / d);
return s;
}
Convert \f$Y\f$ matrix to \f$Z\f$ matrix using well known relation
\f$Z=Y^{-1}\f$
\param[in] y admittance matrix
\return Impedance matrix
\note Check if y matrix is a square matrix
\todo Why not inline
\todo y const
\todo move near ztoy()
*/
matrix ytoz (matrix y) {
assert (y.getRows () == y.getCols ());
return inverse (y);
}
correlation matrix
Converts admittance noise correlation matrix to S-parameter noise
correlation matrix. According to [7] fig 2:
\f[
C_s=\frac{1}{4}(I+S)C_y(I+S)^+
\f]
Where \f$C_s\f$ is the scattering noise correlation matrix,
\f$C_y\f$ the admittance noise correlation matrix, \f$I\f$
the identity matrix and \f$S\f$ the scattering matrix
of device. \f$x^+\f$ is the adjoint of \f$x\f$
\warning cy matrix and s matrix are assumed to be normalized
\param[in] cy Admittance noise correlation
\param[in] s S parameter matrix of device
\return S-parameter noise correlation matrix
\note Assert compatibility of matrix
\todo cy s const
*/
matrix cytocs (matrix cy, matrix s) {
matrix e = eye (s.getRows ());
assert (cy.getRows () == cy.getCols () && s.getRows () == s.getCols () &&
cy.getRows () == s.getRows ());
return (e + s) * cy * adjoint (e + s) / 4;
}
correlation matrix.
According to [7] fig 2:
\f[
C_y=(I+Y)C_s(I+Y)^+
\f]
Where \f$C_s\f$ is the scattering noise correlation matrix,
\f$C_y\f$ the admittance noise correlation matrix, \f$I\f$
the identity matrix and \f$S\f$ the scattering matrix
of device. \f$x^+\f$ is the adjoint of \f$x\f$
\warning cs matrix and y matrix are assumed to be normalized
\param[in] cs S parameter noise correlation
\param[in] y Admittance matrix of device
\return admittance noise correlation matrix
\todo cs, y const
*/
matrix cstocy (matrix cs, matrix y) {
matrix e = eye (y.getRows ());
assert (cs.getRows () == cs.getCols () && y.getRows () == y.getCols () &&
cs.getRows () == y.getRows ());
return (e + y) * cs * adjoint (e + y);
}
correlation matrix.
According to [7] fig 2:
\f[
C_s=\frac{1}{4}(I-S)C_z(I-S)
\f]
Where \f$C_s\f$ is the scattering noise correlation matrix,
\f$C_z\f$ the impedance noise correlation matrix, \f$I\f$
the identity matrix and \f$S\f$ the scattering matrix
of device. \f$x^+\f$ is the adjoint of \f$x\f$
\warning Cz matrix and s matrix are assumed to be normalized
\param[in] cz Impedance noise correlation
\param[in] s S parameter matrix of device
\return S-parameter noise correlation matrix
\note Assert compatibility of matrix
\todo cz, s const
*/
matrix cztocs (matrix cz, matrix s) {
matrix e = eye (s.getRows ());
assert (cz.getRows () == cz.getCols () && s.getRows () == s.getCols () &&
cz.getRows () == s.getRows ());
return (e - s) * cz * adjoint (e - s) / 4;
}
correlation matrix.
According to [7] fig 2:
\f[
C_z=(I+Z)C_s(I+Z)^+
\f]
Where \f$C_s\f$ is the scattering noise correlation matrix,
\f$C_z\f$ the impedance noise correlation matrix, \f$I\f$
the identity matrix and \f$S\f$ the scattering matrix
of device. \f$x^+\f$ is the adjoint of \f$x\f$
\warning cs matrix and y matrix are assumed to be normalized
\param[in] cs S parameter noise correlation
\param[in] z Impedance matrix of device
\return Impedance noise correlation matrix
\todo cs, z const
*/
matrix cstocz (matrix cs, matrix z) {
assert (cs.getRows () == cs.getCols () && z.getRows () == z.getCols () &&
cs.getRows () == z.getRows ());
matrix e = eye (z.getRows ());
return (e + z) * cs * adjoint (e + z);
}
correlation matrix.
According to [7] fig 2:
\f[
C_y=YC_zY^+
\f]
Where \f$C_z\f$ is the impedance correlation matrix,
\f$I\f$ the identity matrix and \f$C_y\f$ the admittance noise
correlation matrix.
\f$x^+\f$ is the adjoint of \f$x\f$
\warning cz matrix and y matrix are assumed to be normalized
\param[in] cz impedance noise correlation
\param[in] y Admittance matrix of device
\return admittance noise correlation matrix
\todo cs, y const
*/
matrix cztocy (matrix cz, matrix y) {
assert (cz.getRows () == cz.getCols () && y.getRows () == y.getCols () &&
cz.getRows () == y.getRows ());
return y * cz * adjoint (y);
}
correlation matrix.
According to [7] fig 2:
\f[
C_z=ZC_yZ^+
\f]
Where \f$C_z\f$ is the impedance correlation matrix,
\f$I\f$ the identity matrix and \f$C_y\f$ the admittance noise
correlation matrix.
\f$x^+\f$ is the adjoint of \f$x\f$
\warning cy matrix and z matrix are assumed to be normalized
\param[in] cy Admittance noise correlation
\param[in] z Impedance matrix of device
\return Impedance noise correlation matrix
\todo cs, z const
*/
matrix cytocz (matrix cy, matrix z) {
assert (cy.getRows () == cy.getCols () && z.getRows () == z.getCols () &&
cy.getRows () == z.getRows ());
return z * cy * adjoint (z);
}
\param[in] r1 source row
\param[in] r2 destination row
\note assert not out of bound r1 and r2
\todo r1 and r2 const
*/
void matrix::exchangeRows (int r1, int r2) {
nr_complex_t * s = new nr_complex_t[cols];
int len = sizeof (nr_complex_t) * cols;
assert (r1 >= 0 && r2 >= 0 && r1 < rows && r2 < rows);
memcpy (s, &data[r1 * cols], len);
memcpy (&data[r1 * cols], &data[r2 * cols], len);
memcpy (&data[r2 * cols], s, len);
delete[] s;
}
\param[in] c1 source column
\param[in] c2 destination column
\note assert not out of bound r1 and r2
\todo c1 and c2 const
*/
void matrix::exchangeCols (int c1, int c2) {
nr_complex_t s;
assert (c1 >= 0 && c2 >= 0 && c1 < cols && c2 < cols);
for (int r = 0; r < rows * cols; r += cols) {
s = data[r + c1];
data[r + c1] = data[r + c2];
data[r + c2] = s;
}
}
Extract a block of size (p,q), starting at (i,j)
\param[in] i start row
\param[in] j start column
\param[in] p number of rows for the submatrix
\param[in] q number of columns for the submatrix
\todo check that (p+i)<rows and (q+j)<cols?
*/
matrix matrix::getBlock(int i, int j, int p, int q) {
matrix bm = matrix(p, q);
for (int r = 0; r < p; r++) {
for (int c = 0; c < q; c++) {
bm.set(r, c, data[(r + i) * cols + c + j]);
}
}
return bm;
}
Extract a block of size p by q flush against the top-left corner
\param[in] p number of rows for the submatrix
\param[in] q number of columns for the submatrix
*/
matrix matrix::getTopLeftCorner(int p, int q) {
return getBlock(0, 0, p, q);
}
Extract a block of size p by q flush against the bottom-left corner
\param[in] p number of rows for the submatrix
\param[in] q number of columns for the submatrix
*/
matrix matrix::getBottomLeftCorner(int p, int q) {
return getBlock(getRows()-p, 0, p, q);
}
Extract a block of size p by q flush against the top-right corner
\param[in] p number of rows for the submatrix
\param[in] q number of columns for the submatrix
*/
matrix matrix::getTopRightCorner(int p, int q) {
return getBlock(0, getCols()-q, p, q);
}
Extract a block of size p by q flush against the bottom-right corner
\param[in] p number of rows for the submatrix
\param[in] q number of columns for the submatrix
*/
matrix matrix::getBottomRightCorner(int p, int q) {
return getBlock(getRows()-p, getCols()-q, p, q);
}
Assign a block of size (p,q), starting at (i,j)
\param[in] m matrix block to assign
\param[in] i start row
\param[in] j start column
\param[in] p number of rows to assign
\param[in] q number of columns to assign
\return matrix with assigned submatrix portion
\todo check that (p+i)<rows and (q+j)<cols?
*/
void matrix::setBlock(matrix m, int i, int j, int p, int q) {
for (int r = 0; r < p; r++) {
for (int c = 0; c < q; c++) {
data[(r + i) * cols + c + j] = m.get(r, c);
}
}
}
Assign a block of size p by q, starting at the top-left corner
\param[in] m matrix block to assign
\param[in] p number of rows to assign
\param[in] q number of columns to assign
\return matrix with assigned submatrix portion
*/
void matrix::setTopLeftCorner(matrix m, int p, int q) {
setBlock(m, 0, 0, p, q);
}
Assign a block of size p by q, starting at the bottom-left corner
\param[in] m matrix block to assign
\param[in] p number of rows to assign
\param[in] q number of columns to assign
\return matrix with assigned submatrix portion
*/
void matrix::setBottomLeftCorner(matrix m, int p, int q) {
setBlock(m, getRows()-p, 0, p, q);
}
Assign a block of size p by q, starting at the top-right corner
\param[in] m matrix block to assign
\param[in] p number of rows to assign
\param[in] q number of columns to assign
\return matrix with assigned submatrix portion
*/
void matrix::setTopRightCorner(matrix m, int p, int q) {
setBlock(m, 0, getCols()-q, p, q);
}
Assign a block of size p by q, starting at the bottom-right corner
\param[in] m matrix block to assign
\param[in] p number of rows to assign
\param[in] q number of columns to assign
\return matrix with assigned submatrix portion
*/
void matrix::setBottomRightCorner(matrix m, int p, int q) {
setBlock(m, getRows()-p, getCols()-q, p, q);
}
This function converts 2x2 matrices from any of the matrix forms Y,
Z, H, G and A to any other. Also converts S<->(A, T, H, Y and Z)
matrices.
Conversion assumed:
Y->Y, Y->Z, Y->H, Y->G, Y->A, Y->S,
Z->Y, Z->Z, Z->H, Z->G, Z->A, Z->S,
H->Y, H->Z, H->H, H->G, H->A, H->S,
G->Y, G->Z, G->H, G->G, G->A, G->S,
A->Y, A->Z, A->H, A->G, A->A, A->S,
S->Y, S->Z, S->H, S->G, S->A, S->S,
S->T,T->T,T->S
\note assert 2x2 matrix
\param[in] m base matrix
\param[in] in matrix
\param[in] out matrix
\return matrix given by format out
\todo m, in, out const
*/
matrix twoport (matrix m, char in, char out) {
assert (m.getRows () >= 2 && m.getCols () >= 2);
nr_complex_t d;
matrix res (2);
switch (in) {
case 'Y':
switch (out) {
case 'Y':
res = m;
break;
case 'Z':
d = m (0, 0) * m (1, 1) - m (0, 1) * m (1, 0);
res.set (0, 0, m (1, 1) / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, m (0, 0) / d);
break;
case 'H':
d = m (0, 0);
res.set (0, 0, 1.0 / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, m (1, 0) / d);
res.set (1, 1, m (1, 1) - m (0, 1) * m (1, 0) / d);
break;
case 'G':
d = m (1, 1);
res.set (0, 0, m (0, 0) - m (0, 1) * m (1, 0) / d);
res.set (0, 1, m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, 1.0 / d);
break;
case 'A':
d = m (1, 0);
res.set (0, 0, -m (1, 1) / d);
res.set (0, 1, -1.0 / d);
res.set (1, 0, m (0, 1) - m (1, 1) * m (0, 0) / d);
res.set (1, 1, -m (0, 0) / d);
break;
case 'S':
res = ytos (m);
break;
}
break;
case 'Z':
switch (out) {
case 'Y':
d = m (0, 0) * m (1, 1) - m (0, 1) * m (1, 0);
res.set (0, 0, m (1, 1) / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, m (0, 0) / d);
break;
case 'Z':
res = m;
break;
case 'H':
d = m (1, 1);
res.set (0, 0, m (0, 0) - m (0, 1) * m (1, 0) / d);
res.set (0, 1, m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, 1.0 / d);
break;
case 'G':
d = m (0, 0);
res.set (0, 0, 1.0 / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, m (1, 0) / d);
res.set (1, 1, m (1, 1) - m (0, 1) * m (1, 0) / d);
break;
case 'A':
d = m (1, 0);
res.set (0, 0, m (0, 0) / d);
res.set (0, 1, m (0, 0) * m (1, 1) / d - m (0, 1));
res.set (1, 0, 1.0 / d);
res.set (1, 1, m (1, 1) / d);
break;
case 'S':
res = ztos (m);
break;
}
break;
case 'H':
switch (out) {
case 'Y':
d = m (0, 0);
res.set (0, 0, 1.0 / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, m (1, 0) / d);
res.set (1, 1, m (1, 1) - m (0, 1) * m.get(2, 1) / d);
break;
case 'Z':
d = m (1, 1);
res.set (0, 0, m (0, 0) - m (0, 1) * m (1, 0) / d);
res.set (0, 1, m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, 1.0 / d);
break;
case 'H':
res = m;
break;
case 'G':
d = m (0, 0) * m (1, 1) - m (0, 1) * m (1, 0);
res.set (0, 0, m (1, 1) / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, m (0, 0) / d);
break;
case 'A':
d = m (1, 0);
res.set (0, 0, m (0, 1) - m (0, 0) * m (1, 1) / d);
res.set (0, 1, -m (0, 0) / d);
res.set (1, 0, -m (1, 1) / d);
res.set (1, 1, -1.0 / d);
break;
case 'S':
res = htos (m);
break;
}
break;
case 'G':
switch (out) {
case 'Y':
d = m (1, 1);
res.set (0, 0, m (0, 0) - m (0, 1) * m (1, 0) / d);
res.set (0, 1, m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, 1.0 / d);
break;
case 'Z':
d = m (0, 0);
res.set (0, 0, 1.0 / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, m (1, 0) / d);
res.set (1, 1, m (1, 1) - m (0, 1) * m (1, 0) / d);
break;
case 'H':
d = m (0, 0) * m (1, 1) - m (0, 1) * m (1, 0);
res.set (0, 0, m (1, 1) / d);
res.set (0, 1, -m (0, 1) / d);
res.set (1, 0, -m (1, 0) / d);
res.set (1, 1, m (0, 0) / d);
break;
case 'G':
res = m;
break;
case 'A':
d = m (1, 0);
res.set (0, 0, 1.0 / d);
res.set (0, 1, m (1, 1) / d);
res.set (1, 0, m (0, 0) / d);
res.set (1, 1, m (0, 0) * m (1, 1) / d - m (0, 1));
break;
case 'S':
res = gtos (m);
break;
}
break;
case 'A':
switch (out) {
case 'Y':
d = m (0, 1);
res.set (0, 0, m (1, 1) / d);
res.set (0, 1, m (1, 0) - m (0, 0) * m (1, 1) / d);
res.set (1, 0, -1.0 / d);
res.set (1, 1, m (0, 0) / d);
break;
case 'Z':
d = m (1, 0);
res.set (0, 0, m (0, 0) / d);
res.set (0, 1, m (0, 0) * m (1, 1) / d - m (0, 1));
res.set (1, 0, 1.0 / d);
res.set (1, 1, m (1, 1) / d);
break;
case 'H':
d = m (1, 1);
res.set (0, 0, m (0, 1) / d);
res.set (0, 1, m (0, 0) - m (0, 1) * m (1, 0) / d);
res.set (1, 0, -1.0 / d);
res.set (1, 1, m (1, 0) / d);
break;
case 'G':
d = m (0, 0);
res.set (0, 0, m (1, 0) / d);
res.set (0, 1, m (1, 0) * m (0, 1) / d - m (1, 1));
res.set (1, 0, 1.0 / d);
res.set (1, 1, m (0, 1) / d);
break;
case 'A':
res = m;
break;
case 'S':
res = atos (m);
break;
}
break;
case 'S':
switch (out) {
case 'S':
res = m;
break;
case 'T':
d = m (1, 0);
res.set (0, 0, m (0, 1) - m (0, 0) * m (1, 1) / d);
res.set (0, 1, m (0, 0) / d);
res.set (1, 0, -m (1, 1) / d);
res.set (1, 1, 1.0 / d);
break;
case 'A':
res = stoa (m);
break;
case 'H':
res = stoh (m);
break;
case 'G':
res = stog (m);
break;
case 'Y':
res = stoy (m);
break;
case 'Z':
res = stoz (m);
break;
}
break;
case 'T':
switch (out) {
case 'S':
d = m (1, 1);
res.set (0, 0, m (0, 1) / d);
res.set (0, 1, m (0, 0) - m (0, 1) * m (1, 0) / d);
res.set (1, 0, 1.0 / d);
res.set (1, 1, -m (1, 0) / d);
break;
case 'T':
res = m;
break;
}
break;
}
return res;
}
The function returns the Rollet stability factor (\f$K\f) of the given
S-parameter matrix:
\[
K=\frac{1-|S_{11}|^2-|S_{22}|^2+|\delta|^2}{2|S_{12}S_{21}|}
\]
Where:
\[
\Delta=S_{11}S_{22}-S_{12}S_{21}
\]
\param[in] m S parameter matrix
\return Rollet factor
\note Assert 2x2 matrix
\todo m const?
\todo Rewrite with abs and expand det. It is cleaner.
*/
nr_double_t rollet (matrix m) {
assert (m.getRows () >= 2 && m.getCols () >= 2);
nr_double_t res;
res = (1 - norm (m (0, 0)) - norm (m (1, 1)) + norm (det (m))) /
2 / abs (m (0, 1) * m (1, 0));
return res;
}
nr_double_t b1 (matrix m) {
assert (m.getRows () >= 2 && m.getCols () >= 2);
nr_double_t res;
res = 1 + norm (m (0, 0)) - norm (m (1, 1)) - norm (det (m));
return res;
}
matrix rad2deg (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, rad2deg (a.get (r, c)));
return res;
}
matrix deg2rad (matrix a) {
matrix res (a.getRows (), a.getCols ());
for (int r = 0; r < a.getRows (); r++)
for (int c = 0; c < a.getCols (); c++)
res.set (r, c, deg2rad (a.get (r, c)));
return res;
}
}