"""
taper (:mod:`skrf.taper`)
=========================
Taper Objects
Tapered transformers, or tapers in short, are used to match
one impedance to another (from Z1 to Z2) [#]_.
By tapering a transmission line, a very broadband impedance match (low VSWR)
can be realized over a wide bandwidth, the longer the taper, the wider the frequency band.
References
----------
.. [#] https://www.microwaves101.com/encyclopedias/tapered-transformers
.. autosummary::
:toctree: generated/
Taper1D
Linear
Exponential
SmoothStep
Klopfenstein
"""
from __future__ import annotations
from numbers import Number
from typing import Callable
from numpy import exp, linspace, log
from .network import cascade_list
[docs]
class Taper1D:
"""
Generic 1D Taper Object
"""
[docs]
def __init__(self, med, start: Number, stop: Number, n_sections: int, f: Callable,
length: Number, length_unit: str = 'm', param: str = 'z0', f_is_normed: bool = True,
med_kw: dict = None, f_kw: dict = None):
"""
Generic 1D Taper Constructor.
Parameters
----------
med : :class:`~skrf.media.media.Media`
A :class:`~skrf.media.media.Media` object or a `@classmethod` `__init__`,
used to generate the transmission line.
See `med_kw` for arguments.
Examples:
* `skrf.media.RectangularWaveguide` # a class
* `skrf.media.RectangularWaveguide.from_z0` # an init
start : number
starting value for `param`
stop : number
stop value for `param`
n_sections : int
number of sections in taper
f : Callable
function defining the taper transition. must take either
no arguments or take `(x,length, start, stop)`.
see `f_is_normed` arguments
length : number
physical length of the taper (in `length_unit`)
length_unit : str
unit of length variable. see `skrf.to_meters`
param : str
name of the parameter of `med` that varies along the taper
f_is_normed: bool
is `f` scalable and normalized. ie can f just be scaled
to fit different start/stop conditions? if so then f is
called with no arguments, and must have domain and raings
of [0,1], and [0,1]
f_kw : dict
passed to `f()` when called
med_kw : dict
passed to `med.__init__` when an instance is created
Note
----
The default behaviour should is to taper based on impedance.
To do this we inspect the `med` class for a `from_z0`
init method, and if it exists, we assign it to `med` attribute,
in `__init__`.
Admittedly having `med` be a class or a method is abuse,
it makes for a intuitive operation.
Examples
--------
Create a linear taper from 100 to 1000mil
>>> from skrf import Frequency, RectangularWaveguide, Taper1D, mil, inch
>>> taper = Taper1D(med=RectangularWaveguide,
param='a',
start=100*mil,
stop=1000*mil,
length=1*inch,
n_sections=20,
f=lambda x: x,
f_is_normed=True,
med_kw={'frequency':Frequency(75,110,101,'ghz')})
"""
if f_kw is None:
f_kw = {}
if med_kw is None:
med_kw = {}
self.med = med
self.param = param
self.start = start
self.stop = stop
self.f = f
self.f_is_normed = f_is_normed
self.length = length
self.length_unit = length_unit
self.n_sections = n_sections
self.med_kw = med_kw
self.f_kw = f_kw
# the default behaviour should be to taper based on impedance.
# to do this we inspect the media class for a `from_z0`
# init method, and if it exists, we assign it to `med` attribute
# admittedly having `med` be a class or a method is abuse,
# it makes for a intuitive operation
if param == 'z0':
if hasattr(self.med, 'from_z0'):
self.med = self.med.from_z0
def __str__(self) -> str:
return 'Taper: {classname}: {param} from {start}-{stop}'
@property
def section_length(self) -> Number:
"""
Section length.
Returns
-------
l : number
"""
return 1.0*self.length/self.n_sections
@property
def value_vector(self):
if self.f_is_normed:
x = linspace(0,1,self.n_sections)
y = self.f(x, **self.f_kw)*(self.stop-self.start) + self.start
else:
x = linspace(0,self.length,self.n_sections)
y = self.f(x,self.length, self.start, self.stop, **self.f_kw)
return y
[docs]
def section_at(self, val: Number):
"""
Create a single section of the taper with parameter value `val`.
Parameters
----------
val : number
parameter value
Returns
-------
media : :class:`~skrf.network.Network`
Network instance for the section of the taper
for the given parameter value
"""
return self.media_at(val).line(self.section_length,
unit=self.length_unit)
@property
def medias(self) -> list:
"""
List of medias.
Returns
-------
medias : list of :class:`~skrf.media.media.Media`
"""
return [self.media_at(k) for k in self.value_vector]
@property
def sections(self) -> list:
"""
List of sections.
Returns
-------
sections : list of :class:`~skrf.network.Network`
"""
return [self.section_at(k) for k in self.value_vector]
@property
def network(self):
"""
Resulting Network
Returns
-------
ntwk : :class:`~skrf.network.Network`
"""
return cascade_list(self.sections)
[docs]
class Linear(Taper1D):
"""
A linear Taper.
Defined by :math:`f(x)=x`
"""
[docs]
def __init__(self, **kw):
opts = dict(f=lambda x:x, f_is_normed=True)
kw.update(opts)
super().__init__(**kw)
[docs]
class Exponential(Taper1D):
r"""
An Exponential Taper.
Defined by :math:`f(x) = f_0 \exp\left[ \frac{x}{x_1} \ln\left( \frac{f_1}{f_0} \right) \right]`
where:
* :math:`f_0`: start param value
* :math:`f_1`: stop param value
* :math:`x`: independent variable (position along taper)
* :math:`x_1`: length of taper
"""
[docs]
def __init__(self, **kw):
"""
Exponential Taper Constructor
"""
def f(x, length, start, stop):
return start*exp(x/length*(log(stop/start)))
opts = dict(f=f, f_is_normed=False)
kw.update(opts)
super().__init__(**kw)
[docs]
class SmoothStep(Taper1D):
"""
A smoothstep Taper.
There is no analytical basis for this in the EE world that I know
of. it is just a reasonable smooth curve, that is easy to implement.
:math:`f(x) = (3 x^2 - 2x^3)`
References
----------
https://en.wikipedia.org/wiki/Smoothstep
"""
[docs]
def __init__(self, **kw):
"""
Smoothstep Taper Constructor.
"""
def f(x):
return 3 * x ** 2 - 2 * x ** 3
opts = dict(f=f, f_is_normed=True)
kw.update(opts)
super().__init__(**kw)
[docs]
class Klopfenstein(Taper1D):
"""
Klopfenstein Taper.
This impedance taper was first described by R. W. Klopfenstein [#microwaves101]_ in a paper titled
"A Transmission Line Taper of Improved Design", published in 1956 [#Klopfenstein]_ .
A correction to Klopfenstein's math was published by in May 1973 by Darko Kajfez and Jame Prewit [#Kajfez]_ .
References
----------
.. [#microwaves101] https://www.microwaves101.com/encyclopedias/klopfenstein-taper
.. [#Klopfenstein] R. W. Klopfenstein Proc. IRE. vol. 44 pp. 31-35 Jan. 1956.
doi: 10.1109/JRPROC.1956.274847
https://ieeexplore.ieee.org/document/4051841
.. [#Kajfez] D. Kajfez and J. O. Prewitt, "Correction to "A Transmission Line Taper of Improved Design" (Letters),"
in IEEE Transactions on Microwave Theory and Techniques, vol. 21, no. 5, pp. 364-364, May 1973,
doi: 10.1109/TMTT.1973.1128003.
https://ieeexplore.ieee.org/document/1128003
"""
[docs]
def __init__(self, **kw):
raise NotImplementedError('Not Yet Implemented')