# Source code for skrf.tlineFunctions

# -*- coding: utf-8 -*-
r"""
.. module:: skrf.tlineFunctions
===============================================
tlineFunctions (:mod:skrf.tlineFunctions)
===============================================

This module provides functions related to transmission line theory.

Impedance and Reflection Coefficient
--------------------------------------
These functions relate basic transmission line quantities such as
characteristic impedance, input impedance, reflection coefficient, etc.
Each function has two names. One is a long-winded but readable name and
the other is a short-hand variable-like names. Below is a table relating
these two names with each other as well as common mathematical symbols.

====================  ======================  ================================
Symbol                Variable Name           Long Name
====================  ======================  ================================
:math:Z_l           z_l                     load_impedance
:math:Z_{in}        z_in                    input_impedance
:math:\Gamma_0      Gamma_0                 reflection_coefficient
:math:\Gamma_{in}   Gamma_in                reflection_coefficient_at_theta
:math:\theta        theta                   electrical_length
====================  ======================  ================================

There may be a bit of confusion about the difference between the load
impedance the input impedance. This is because the load impedance **is**
the input impedance at the load. An illustration may provide some
useful reference.

Below is a (bad) illustration of a section of uniform transmission line
of characteristic impedance :math:Z_0, and electrical length
:math:\theta. The line is terminated on the right with some
load impedance, :math:Z_l. The input impedance :math:Z_{in} and
input reflection coefficient :math:\Gamma_{in} are
looking in towards the load from the distance :math:\theta from the

.. math::
Z_0, \theta

\text{o===============o=}[Z_l]

So, to clarify the confusion,

.. math::
\Gamma_{in}=\Gamma_l \text{ at }  \theta=0

Short names
+++++++++++++
.. autosummary::
:toctree: generated/

theta

zl_2_Gamma0
zl_2_zin
zl_2_Gamma_in
zl_2_swr
zl_2_total_loss

Gamma0_2_zl
Gamma0_2_Gamma_in
Gamma0_2_zin
Gamma0_2_swr

Long-names
++++++++++++++
.. autosummary::
:toctree: generated/

electrical_length

distance_2_electrical_length
electrical_length_2_distance

reflection_coefficient_at_theta
reflection_coefficient_2_input_impedance
reflection_coefficient_2_input_impedance_at_theta
reflection_coefficient_2_propagation_constant

input_impedance_at_theta

voltage_current_propagation

Distributed Circuit and Wave Quantities
----------------------------------------
.. autosummary::
:toctree: generated/

distributed_circuit_2_propagation_impedance
propagation_impedance_2_distributed_circuit

Transmission Line Physics
---------------------------------
.. autosummary::
:toctree: generated/

skin_depth
surface_resistivity
"""

from . constants import NumberLike, INF, ONE
import numpy as npy
from numpy import pi, sqrt, exp, array, imag, real

from scipy.constants import mu_0
from . import mathFunctions as mf

[docs]def skin_depth(f: NumberLike, rho: float, mu_r: float):
r"""
Skin depth for a material.

The skin depth is calculated as:

.. math::

\delta = \sqrt{\frac{ \rho }{ \pi f \mu_r \mu_0 }}

Parameters
----------
f : number or array-like
frequency, in Hz
rho : number of array-like
bulk resistivity of material, in ohm*m
mu_r : number or array-like
relative permeability of material

Returns
-------
skin depth : number or array-like
the skin depth, in meter

References
----------
.. [#] https://www.microwaves101.com/encyclopedias/skin-depth
.. [#] http://en.wikipedia.org/wiki/Skin_effect

--------
surface_resistivity

"""
return sqrt(rho/(pi*f*mu_r*mu_0))

[docs]def surface_resistivity(f: NumberLike, rho: float, mu_r: float):
r"""
Surface resistivity.

The surface resistivity is calculated as:

.. math::

\frac{ \rho }{ \delta }

where :math:\delta is the skin depth from :func:skin_depth.

Parameters
----------
f : number or array-like
frequency, in Hz
rho : number or array-like
bulk resistivity of material, in ohm*m
mu_r : number or array-like
relative permeability of material

Returns
-------
surface resistivity : number of array-like
Surface resistivity in ohms/square

References
----------
.. [#] https://www.microwaves101.com/encyclopedias/sheet-resistance
.. [#] https://en.wikipedia.org/wiki/Sheet_resistance

--------
skin_depth
"""
return rho/skin_depth(rho=rho, f=f, mu_r=mu_r)

distributed_impedance: NumberLike):
r"""
Convert distributed circuit values to wave quantities.

This converts complex distributed impedance and admittance to
propagation constant and characteristic impedance. The relation is

.. math::
Z_0 = \sqrt{ \frac{Z^{'}}{Y^{'}}}
\gamma = \sqrt{ Z^{'}  Y^{'}}

Parameters
----------
distributed_impedance :  number, array-like
distributed impedance

Returns
-------
propagation_constant : number, array-like
distributed impedance
characteristic_impedance : number, array-like
distributed impedance

--------
propagation_impedance_2_distributed_circuit : opposite conversion
"""
propagation_constant = \
characteristic_impedance = \
return (propagation_constant, characteristic_impedance)

[docs]def propagation_impedance_2_distributed_circuit(propagation_constant: NumberLike,
characteristic_impedance: NumberLike):
r"""
Convert wave quantities to distributed circuit values.

Convert complex propagation constant and characteristic impedance
to distributed impedance and admittance. The relation is,

.. math::
Y^{'} = \frac{\gamma}{Z_0}

Parameters
----------
propagation_constant : number, array-like
distributed impedance
characteristic_impedance : number, array-like
distributed impedance

Returns
-------
distributed_impedance :  number, array-like
distributed impedance

--------
distributed_circuit_2_propagation_impedance : opposite conversion
"""
distributed_impedance = propagation_constant*characteristic_impedance

[docs]def electrical_length(gamma: NumberLike, f: NumberLike, d: NumberLike, deg: bool = False):
r"""
Electrical length of a section of transmission line.

.. math::
\theta = \gamma(f) \cdot d

Parameters
----------
gamma : number, array-like or function
propagation constant. See Notes.
If passed as a function, takes frequency in Hz as a sole argument.
f : number or array-like
frequency at which to calculate
d : number or array-like
length of line, in meters
deg : Boolean
return in degrees or not.

Returns
-------
theta : number or array-like
electrical length in radians or degrees, depending on  value of deg.

--------
electrical_length_2_distance : opposite conversion

Note
----
The convention has been chosen that forward propagation is
represented by the positive imaginary part of the value returned by
the gamma function.
"""
# if gamma is not a function, create a dummy function which return gamma
if not callable(gamma):
_gamma = gamma
def gamma(f0): return _gamma

# typecast to a 1D array
f = array(f, dtype=float).reshape(-1)
d = array(d, dtype=float).reshape(-1)

if deg == False:
return  gamma(f)*d
elif deg == True:

[docs]def electrical_length_2_distance(theta: NumberLike, gamma: NumberLike, f0: NumberLike, deg: bool = True):
r"""
Convert electrical length to a physical distance.

.. math::
d = \frac{\theta}{\gamma(f_0)}

Parameters
----------
theta : number or array-like
electrical length. units depend on deg option
gamma : number, array-like or function
propagation constant. See Notes.
If passed as a function, takes frequency in Hz as a sole argument.
f0 : number or array-like
frequency at which to calculate gamma
deg : Boolean
return in degrees or not.

Returns
-------
d : number or array-like (real)
physical distance in m

Note
----
The convention has been chosen that forward propagation is
represented by the positive imaginary part of the value returned by
the gamma function.

--------
distance_2_electrical_length: opposite conversion
"""
# if gamma is not a function, create a dummy function which return gamma
if not callable(gamma):
_gamma = gamma
def gamma(f0): return _gamma

if deg:
return real(theta / gamma(f0))

r"""
Reflection coefficient from a load impedance.

Return the reflection coefficient for a given load impedance, and
characteristic impedance.

For a transmission line of characteristic impedance :math:Z_0
terminated with load impedance :math:Z_l, the complex reflection
coefficient is given by,

.. math::
\Gamma = \frac {Z_l - Z_0}{Z_l + Z_0}

Parameters
----------
z0 : number or array-like
characteristic impedance
zl : number or array-like

Returns
-------
gamma : number or array-like
reflection coefficient

--------
Gamma0_2_zl : reflection coefficient to load impedance

Note
----
Inputs are typecasted to 1D complex array.
"""
# typecast to a complex 1D array. this makes everything easier
z0 = array(z0, dtype=complex).reshape(-1)
zl = array(zl, dtype=complex).reshape(-1)

# handle singularity  by numerically representing inf as big number
zl[(zl == npy.inf)] = INF

return ((zl - z0)/(zl + z0))

[docs]def reflection_coefficient_2_input_impedance(z0: NumberLike, Gamma: NumberLike):
r"""
Input impedance from a load reflection coefficient.

Calculate the input impedance given a reflection coefficient and
characteristic impedance.

.. math::
Z_0 \left(\frac {1 + \Gamma}{1-\Gamma} \right)

Parameters
----------
Gamma : number or array-like
complex reflection coefficient
z0 : number or array-like
characteristic impedance

Returns
-------
zin : number or array-like
input impedance

"""
# typecast to a complex 1D array. this makes everything easier
Gamma = array(Gamma, dtype=complex).reshape(-1)
z0 = array(z0, dtype=complex).reshape(-1)

# handle singularity by numerically representing inf as close to 1
Gamma[(Gamma == 1)] = ONE

return z0*((1.0 + Gamma)/(1.0 - Gamma))

[docs]def reflection_coefficient_at_theta(Gamma0: NumberLike, theta: NumberLike):
r"""
Reflection coefficient at a given electrical length.

.. math::
\Gamma_{in} = \Gamma_0 e^{-2 \theta}

Parameters
----------
Gamma0 : number or array-like
reflection coefficient at theta=0
theta : number or array-like
electrical length (may be complex)

Returns
-------
Gamma_in : number or array-like
input reflection coefficient

"""
Gamma0 = array(Gamma0, dtype=complex).reshape(-1)
theta = array(theta, dtype=complex).reshape(-1)
return Gamma0 * exp(-2*theta)

[docs]def input_impedance_at_theta(z0: NumberLike, zl: NumberLike, theta: NumberLike):
"""
Input impedance from load impedance at a given electrical length.

Input impedance of load impedance zl at a given electrical length,
given characteristic impedance z0.

Parameters
----------
z0 : number or array-like
characteristic impedance
zl : number or array-like
theta : number or array-like
electrical length of the line (may be complex)

Returns
-------
zin : number or array-like
input impedance at theta

"""
Gamma_in = reflection_coefficient_at_theta(Gamma0=Gamma0, theta=theta)
return reflection_coefficient_2_input_impedance(z0=z0, Gamma=Gamma_in)

[docs]def load_impedance_2_reflection_coefficient_at_theta(z0: NumberLike, zl: NumberLike, theta: NumberLike):
"""
Reflection coefficient of load at a given electrical length.

Reflection coefficient of load impedance zl at a given electrical length,
given characteristic impedance z0.

Parameters
----------
z0 : number or array-like
characteristic impedance.
zl : number or array-like
theta : number or array-like
electrical length of the line (may be complex).

Returns
-------
Gamma_in : number or array-like
input reflection coefficient at theta

"""
Gamma_in = reflection_coefficient_at_theta(Gamma0=Gamma0, theta=theta)
return Gamma_in

[docs]def reflection_coefficient_2_input_impedance_at_theta(z0: NumberLike, Gamma0: NumberLike, theta: NumberLike):
"""
Input impedance from load reflection coefficient at a given electrical length.

Calculate the input impedance at electrical length theta, given a
reflection coefficient and characteristic impedance of the medium.

Parameters
----------
z0 : number or array-like
characteristic impedance.
Gamma: number or array-like
reflection coefficient
theta: number or array-like
electrical length of the line, (may be complex)

Returns
-------
zin: number or array-like
input impedance at theta

"""
Gamma_in = reflection_coefficient_at_theta(Gamma0=Gamma0, theta=theta)
zin = reflection_coefficient_2_input_impedance(z0=z0, Gamma=Gamma_in)
return zin

[docs]def reflection_coefficient_2_propagation_constant(Gamma_in: NumberLike, Gamma_l: NumberLike, d: NumberLike):
r"""
Propagation constant from line input and load reflection coefficients.

Calculate the propagation constant of a line of length d, given the
reflection coefficient and characteristic impedance of the medium.

.. math::
\Gamma_{in} = \Gamma_l e^{-2 j \gamma \cdot d}
\to \gamma = -\frac{1}{2 d} \ln \left ( \frac{ \Gamma_{in} }{ \Gamma_l } \right )

Parameters
----------
Gamma_in : number or array-like
input reflection coefficient
Gamma_l :  number or array-like
d : number or array-like
length of line, in meters

Returns
-------
gamma : number (complex) or array-like
propagation constant (see notes)

Note
----
The convention has been chosen that forward propagation is
represented by the positive imaginary part of gamma.

"""
gamma = -1/(2*d) * npy.log(Gamma_in/Gamma_l)
# the imaginary part of gamma (=beta) cannot be negative with the given
# definition of gamma. Thus one should take the first modulo positive value
gamma.imag = gamma.imag % (pi/d)

return gamma

[docs]def Gamma0_2_swr(Gamma0: NumberLike):
r"""
Standing Wave Ratio (SWR) for a given reflection coefficient.

Standing Wave Ratio value is defined by:

.. math::
VSWR = \frac{1 + |\Gamma_0|}{1 - |\Gamma_0|}

Parameters
----------
Gamma0 : number or array-like
Reflection coefficient

Returns
-------
swr : number or array-like
Standing Wave Ratio.

"""
return (1 + npy.abs(Gamma0)) / (1 - npy.abs(Gamma0))

[docs]def zl_2_swr(z0: NumberLike, zl: NumberLike):
r"""
Standing Wave Ratio (SWR) for a given load impedance.

Standing Wave Ratio value is defined by:

.. math::
VSWR = \frac{1 + |\Gamma|}{1 - |\Gamma|}

where

.. math::
\Gamma = \frac{Z_L - Z_0}{Z_L + Z_0}

Parameters
----------
z0 : number or array-like
line characteristic impedance [Ohm]
zl : number or array-like

Returns
-------
swr : number or array-like
Standing Wave Ratio.

"""
return Gamma0_2_swr(Gamma0)

[docs]def voltage_current_propagation(v1: NumberLike, i1: NumberLike, z0: NumberLike, theta: NumberLike):
"""
Voltages and currents calculated on electrical length theta of a transmission line.

Give voltage v2 and current i1 at theta, given voltage v1
and current i1 at theta=0 and given characteristic parameters gamma and z0.

::

i1                          i2
○-->---------------------->--○

v1         gamma,z0         v2

○----------------------------○

<------------ d ------------->

theta=0                   theta

Uses (inverse) ABCD parameters of a transmission line.

Parameters
----------
v1 : array-like (nfreqs,)
total voltage at z=0
i1 : array-like (nfreqs,)
total current at z=0, directed toward the transmission line
z0: array-like (nfreqs,)
characteristic impedance
theta : number or array-like (nfreq, ntheta)
electrical length of the line (may be complex).

Return
------
v2 : array-like (nfreqs, ntheta)
total voltage at z=d
i2 : array-like (nfreqs, ndtheta
total current at z=d, directed outward the transmission line
"""
# outer product by broadcasting of the electrical length
# theta = gamma[:, npy.newaxis] * d  # (nbfreqs x nbd)
# ABCD parameters of a transmission line (gamma, z0)
A = npy.cosh(theta)
B = z0*npy.sinh(theta)
C = npy.sinh(theta)/z0
D = npy.cosh(theta)
# transpose and de-transpose operations are necessary
# for linalg.inv to inverse square matrices
ABCD = npy.array([[A, B],[C, D]]).transpose()
inv_ABCD = npy.linalg.inv(ABCD).transpose()

v2 = inv_ABCD[0,0] * v1 + inv_ABCD[0,1] * i1
i2 = inv_ABCD[1,0] * v1 + inv_ABCD[1,1] * i1
return v2, i2

[docs]def zl_2_total_loss(z0: NumberLike, zl: NumberLike, theta: NumberLike):
r"""
Total loss of a terminated transmission line (in natural unit).

The total loss expressed in terms of the load impedance is [#]_ :

.. math::
TL = \frac{R_{in}}{R_L} \left| \cosh \theta  + \frac{Z_L}{Z_0} \sinh\theta \right|^2

Parameters
----------
z0 : number or array-like
characteristic impedance.
zl : number or array-like
theta : number or array-like
electrical length of the line (may be complex).

Returns
-------
total_loss: number or array-like
total loss in natural unit

References
----------
.. [#] Steve Stearns (K6OIK), Transmission Line Power Paradox and Its Resolution.
ARRL PacificonAntenna Seminar, Santa Clara, CA, October 10-12, 2014.

"""
Rin = npy.real(zl_2_zin(z0, zl, theta))
total_loss = Rin/npy.real(zl)*npy.abs(npy.cosh(theta) + zl/z0*npy.sinh(theta))**2

# short hand convenience.