

'''
.. module:: skrf.media.cpw

========================================
cpw (:mod:`skrf.media.cpw`)
========================================

Coplanar waveguide class

This class was made from the technical documentation [#]_ provided
by the qucs project [#]_ .
The variables  and properties of this class are coincident with
their derivations.

.. [#] http://qucs.sourceforge.net/docs/technical.pdf
.. [#] http://www.qucs.sourceforge.net/

.. autosummary::
   :toctree: generated/

   CPW

'''
from scipy.constants import  epsilon_0, mu_0
from scipy.special import ellipk
from numpy import real, imag,pi,sqrt,log,zeros, ones
from .media import Media
from ..tlineFunctions import skin_depth, surface_resistivity

class CPW(Media):
    '''
    Coplanar Waveguide Media

    Parameters
    -------------
    frequency : :class:`~skrf.frequency.Frequency` object, optional
        frequency band of the media. The default is None.
    z0 : number, array-like, optional
        the port impedance for media. The default is None.
        Only needed if  its different from the characterisitc impedance 
        of the transmission.
    w : number, or array-like, optional
            width of center conductor, in m. Default is 70.
    s : number, or array-like
            width of gap, in m. Default is 4.
    ep_r : number, or array-like, optional
            relative permativity of substrate. Default is 3.
    t : number, or array-like, optional
            conductor thickness, in m. Default is None (metalization thickness neglected)
    rho: number, or array-like, optional
            resistivity of conductor. Default is None
      
    '''
    def __init__(self, frequency=None, z0=None, w=70, s=4,
                 ep_r=3, t=None, rho=None,  *args, **kwargs):
        Media.__init__(self, frequency=frequency,z0=z0)

        self.w, self.s, self.ep_r, self.t, self.rho =\
                w, s, ep_r, t, rho


    def __str__(self):
        f=self.frequency
        output =  \
                'Coplanar Waveguide Media.  %i-%i %s.  %i points'%\
                (f.f_scaled[0],f.f_scaled[-1],f.unit, f.npoints) + \
                '\n W= %.2em, S= %.2em'% \
                (self.w,self.s)
        return output

    def __repr__(self):
        return self.__str__()

    @property
    def ep_re(self):
        '''
        Effective permittivity of the CPW (also known as Keff).
        
        If the thickness of the dielectric susbtrate is large, 
        the effective dielectric constant of the even mode can be approx as:
        
        .. math::

                \epsilon_{eff} = \\frac{\epsilon_r + 1}{2} 
        
        The effective permittivity can be defined as in the case of a 
        microstripa line, that is as the square of ratio 
        of the capacitance per unit length to the phase velocity. 
        '''
        return (self.ep_r+1)/2.0

    @property
    def k1(self):
        '''
        Intermediary parameter. see qucs docs on cpw lines.
        
        Defined as:
            
        .. math::
                
                k = \\frac{w}{w + 2s}
        
        '''
        return self.w/(self.w +2*self.s)

    @property
    def K_ratio(self):
        '''
        intermediary parameter. see qucs docs on cpw lines.
        K_ratio is the ratio of two elliptic integrals
        '''
        k1 = self.k1
        # k prime
        k_p = sqrt(1 - k1**2)

        if (0 <= k1 <= 1/sqrt(2)):
            return pi/log(2*(1 + sqrt(k1))/(1 - sqrt(k1)))
        elif (1/sqrt(2) < k1 <= 1):
            return (log(2*(1 + sqrt(k_p))/(1 - sqrt(k_p)) ))/pi




    @property
    def alpha_conductor(self):
        '''
        Losses due to conductor resistivity

        Returns
        --------
        alpha_conductor : array-like
                lossyness due to conductor losses
        See Also
        ----------
        surface_resistivity : calculates surface resistivity
        '''
        if self.rho is None or self.t is None:
            raise(AttributeError('must provide values conductivity and conductor thickness to calculate this. see initializer help'))

        t, k1, ep_re = self.t, self.k1,self.ep_re
        r_s = surface_resistivity(f=self.frequency.f, rho=self.rho, \
                mu_r=1)
        a = self.w/2.
        b = self.s+self.w/2.
        K = ellipk      # complete elliptical integral of first kind
        K_p = lambda x: ellipk(sqrt(1-x**2)) # ellipk's compliment

        return ((r_s * sqrt(ep_re)/(480*pi*K(k1)*K_p(k1)*(1-k1**2) ))*\
                (1./a * (pi+log((8*pi*a*(1-k1))/(t*(1+k1)))) +\
                 1./b * (pi+log((8*pi*b*(1-k1))/(t*(1+k1))))))



    @property
    def Z0(self):
        '''
        Characterisitc impedance
        '''
        return (30.*pi / sqrt(self.ep_re) * self.K_ratio)*ones(len(self.frequency.f), dtype='complex')

    @property
    def gamma(self):
        '''
        Propagation constant


        See Also
        --------
        alpha_conductor : calculates losses to conductors
        '''
        beta = 1j*2*pi*self.frequency.f*sqrt(self.ep_re*epsilon_0*mu_0)
        alpha = zeros(len(beta))
        if self.rho is not None and self.t is not None:
            alpha = self.alpha_conductor

        return beta+alpha
