Source code for skrf.media.freespace

"""
freespace (:mod:`skrf.media.freespace`)
========================================

A plane-wave (TEM Mode) in Freespace.

Represents a plane-wave in a homogeneous freespace, defined by
the space's relative permittivity and relative permeability.

.. autosummary::
    :toctree: generated/

    Freespace


"""
from __future__ import annotations

from typing import TYPE_CHECKING

from numpy import ones, real, sqrt
from scipy.constants import epsilon_0, mu_0

from ..constants import NumberLike
from ..data import materials
from .media import Media

if TYPE_CHECKING:
    from ..frequency import Frequency


[docs] class Freespace(Media): r""" A plane-wave (TEM Mode) in Freespace. A Freespace media can be constructed in two ways: * from complex, relative permativity and permeability OR * from real relative permativity and permeability with loss tangents. There is also a method to initialize from a existing distributed circuit, appropriately named :func:`Freespace.from_distributed_circuit` Parameters ---------- frequency : :class:`~skrf.frequency.Frequency` object frequency band of this transmission line medium z0_port : number, array-like, or None `z0_port` is the port impedance for networks generated by the media. If `z0_port` is not None, the networks generated by the media are renormalized (or in other words embedded) from the characteristic impedance z0 of the media to `z0_port`. Else if `z0_port` is None, the networks port impedances will be the raw characteristic impedance z0 of the media. (Default is None) z0_override : number, array-like, or None `z0_override` override the characteristic impedance for the media. If `z0_override` is not None, the networks generated by the media have their characteristic impedance `z0` overrided by `z0_override`. (Default is None) z0 : number, array-like, or None deprecated parameter, alias to `z0_override` if `z0_override` is None. Emmit a deprecation warning. ep_r : number, array-like complex relative permittivity. negative imaginary is lossy. mu_r : number, array-like complex relative permeability. negative imaginary is lossy. ep_loss_tan : None, number, array-like electric loss tangent (of the permativity). If not None, imag(ep_r) is ignored. mu_loss_tan : None, number, array-like magnetic loss tangent (of the permeability). If not None, imag(mu_r) is ignored. rho : number, array-like, string or None resistivity (ohm-m) of the conductor walls. If array-like must be same length as frequency. if str, it must be a key in :data:`skrf.data.materials`. Default is None (lossless). \*args, \*\*kwargs : arguments and keyword arguments Examples -------- >>> from skrf.media.freespace import Freespace >>> from skrf.frequency import Frequency >>> f = Frequency(75,110,101,'ghz') >>> Freespace(frequency=f, ep_r=11.9) >>> Freespace(frequency=f, ep_r=11.9-1.1j) >>> Freespace(frequency=f, ep_r=11.9, ep_loss_tan=.1) >>> Freespace(frequency=f, ep_r=11.9-1.1j, mu_r = 1.1-.1j) """
[docs] def __init__(self, frequency: Frequency | None = None, z0_port: NumberLike | None = None, z0_override: NumberLike | None = None, z0: NumberLike | None = None, ep_r: NumberLike = 1+0j, mu_r: NumberLike = 1+0j, ep_loss_tan: NumberLike | None = None, mu_loss_tan: NumberLike | None = None, rho: NumberLike | str | None = None, *args, **kwargs): Media.__init__(self, frequency = frequency, z0_port = z0_port, z0_override = z0_override, z0 = z0) self.ep_r = ep_r self.mu_r = mu_r self.rho = rho self.ep_loss_tan = ep_loss_tan self.mu_loss_tan = mu_loss_tan
def __str__(self) -> str: f = self.frequency output = 'Freespace Media. %i-%i %s. %i points'%\ (f.f_scaled[0], f.f_scaled[-1], f.unit, f.npoints) return output def __repr__(self) -> str: return self.__str__() @property def ep(self) -> NumberLike: r""" Complex dielectric permittivity. If :math:`\tan\delta_e` is not defined: .. math:: \varepsilon = \varepsilon_0 \varepsilon_r otherwise, .. math:: \varepsilon = \varepsilon_0 \Re[\varepsilon_r] (1 - j\tan\delta_e) where :math:`\tan\delta_e` is the electric loss tangent. Returns ------- ep : number or array-like Complex dielectric permittivity in F/m. """ if self.ep_loss_tan is not None: ep_r = real(self.ep_r)*(1 - 1j*self.ep_loss_tan) else: ep_r = self.ep_r return ep_r*epsilon_0 @property def mu(self) -> NumberLike: r""" Complex dielectric permeability. If :math:`\tan\delta_m` is not defined: .. math:: \mu = \mu_0 \mu_r otherwise, .. math:: \mu = \mu_0 \Re[\mu_r] (1 - j\tan\delta_m) where :math:`\tan\delta_m` is the magnetic loss tangent. Returns ------- mu : number Complex permeability in H/m. """ if self.mu_loss_tan is not None: mu_r = real(self.mu_r)*(1 -1j*self.mu_loss_tan) else: mu_r = self.mu_r return mu_r*mu_0
[docs] @classmethod def from_distributed_circuit(cls, dc, *args, **kwargs) -> Media: r""" Initialize a freespace from :class:`~skrf.media.distributedCircuit.DistributedCircuit`. Parameters ---------- dc: :class:`~skrf.media.distributedCircuit.DistributedCircuit` a DistributedCircuit object \*args, \*\*kwargs : passed to `Freespace.__init__ Notes ----- Here are the details:: w = dc.frequency.w z= dc.Z/(w*mu_0) y= dc.Y/(w*epsilon_0) ep_r = -1j*y mu_r = -1j*z See Also -------- skrf.media.distributedCircuit.DistributedCircuit """ w = dc.frequency.w z= dc.Z/(w*mu_0) y= dc.Y/(w*epsilon_0) kw={} kw['ep_r'] = -1j*y kw['mu_r'] = -1j*z kwargs.update(kw) return cls(frequency=dc.frequency, **kwargs)
@property def rho(self) -> NumberLike: """ Conductivity in ohm*m. Parameters ---------- val : float, array-like or str the resistivity in ohm*m. If array-like must be same length as self.frequency. if str, it must be a key in :data:`~skrf.data.materials`. Examples -------- >>> wg.rho = 2.8e-8 >>> wg.rho = 2.8e-8 * ones(len(wg.frequency)) >>> wg.rho = 'al' >>> wg.rho = 'aluminum' """ return self._rho @rho.setter def rho(self, val): if isinstance(val, str): self._rho = materials[val.lower()]['resistivity(ohm*m)'] else: self._rho=val @property def ep_with_rho(self) -> NumberLike: r""" Complex permittivity with resistivity absorbed into its imaginary component. .. math:: \varepsilon - j \frac{1}{\rho\omega} See Also -------- rho ep """ if self.rho is not None: return self.ep -1j/(self.rho*self.frequency.w) else: return self.ep @property def gamma(self) -> NumberLike: r""" Propagation Constant, :math:`\gamma`. Defined as, .. math:: \gamma = \sqrt{ Z^{'} Y^{'}} Returns ------- gamma : np.ndarray Propagation Constant, Note ---- The components of propagation constant are interpreted as follows: * positive real(gamma) = attenuation * positive imag(gamma) = forward propagation """ ep = self.ep_with_rho return 1j*self.frequency.w * sqrt(ep*self.mu) @property def z0_characteristic(self) -> NumberLike: r""" Characteristic Impedance, :math:`z_0`. .. math:: Z_0 = \sqrt{ \frac{Z^{'}}{Y^{'}}} Returns ------- z0_characteristic : np.ndarray Characteristic Impedance in units of ohms """ ep = self.ep_with_rho return sqrt(self.mu/ep)*ones(len(self))
[docs] def plot_ep(self): """ Plot the real and imaginary part of the complex permittivity. """ self.plot(self.ep_r.real, label=r'ep_r real') self.plot(self.ep_r.imag, label=r'ep_r imag')
[docs] def plot_mu(self): """ Plot the real and imaginary part of the complex permeability. """ self.plot(self.mu_r.real, label=r'mu_r real') self.plot(self.mu_r.imag, label=r'mu_r imag')
[docs] def plot_ep_mu(self): """ Plot the real and imaginary part of the complex permittivity with resistivity. """ self.plot_ep() self.plot_mu()