{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import numpy.polynomial.polynomial as np_pp\n", "#np.seterr(divide='ignore', invalid='ignore')\n", "from IPython.display import display, Markdown, Latex\n", "#import scipy.special as ss\n", "import sympy as sp\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Synthesis of Combline ~~and Capacitively Loaded Interdigital~~ Bandpass Filters of Arbitrary Bandwidth\n", "\n", "https://ieeexplore.ieee.org/abstract/document/1127609\n", " \n", "#### IV. Combline Filter Synthesis Example \n", "\n", "1) passband: ${f}_{1}=1.20\\;GHz$, ${f}_{2}=1.80\\;GHz$, $0.1\\;dB$ ripple $({\\epsilon}=0.1526)$; \n", "2) attenuation: ${A}_{H}=30\\;dB$ for ${f}_{H}\\ge 2.4\\;GHz$; ${A}_{L}=20\\;dB$ for ${f}_{L}\\le 0.5\\;GHz$; \n", "3) no spurious responses through the fourth harmonic; let ${f}_{S}=7.418\\;GHz$. " ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "#IV. Combline Filter Synthesis Example \n", "#1)\n", "f1 = 1.2e9\n", "f2 = 1.8e9\n", "ripple = 0.1 #dB\n", "#2)\n", "AH = 30 #High side attenuation in dB\n", "fH = 2.4e9 #High side attenuation frequency \n", "AL = 20 #Los side attenuation in dB\n", "fL = 0.5e9 #Low side attenuation frequency\n", "#3)\n", "fS = 7.418e9 #Stop band frequency\n", "#In the paper, fS = 7.148GHz is incorrect" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "def Comb_Req_Order(f1, f2, ripple, AL, fL, AH, fH, fS):\n", " \"\"\"\n", " This function takes in various filter specifications and returns the needed\n", " order, and other parameters\n", " \n", " f1: lower passband edge frequency\n", " f2: upper passband edge frequency\n", " ripple: inband ripple in dB\n", " AL: lowside rejection requirement\n", " fL: lowside rejection frequency\n", " AH: highside rejection requirement\n", " fH: highide rejection frequency\n", " fS: frequency to hold the highside rejection\n", " \n", " Table I of...\n", " \"Synthesis of Combline and Capacitively Loaded Inter... of Arbitrary Bandwidth\" \n", " \"\"\"\n", " e = np.sqrt(10**(ripple/10.0)-1)\n", " fo = (fH+fS)/2.0\n", " omega1 = np.tan(np.pi*f1/(2*fo))\n", " omega2 = np.tan(np.pi*f2/(2*fo))\n", " omegaL = np.tan(np.pi*fL/(2*fo))\n", " omegaH = np.tan(np.pi*fH/(2*fo))\n", "\n", " ZL = np.sqrt((omega2**2-omegaL**2)/(omega1**2-omegaL**2))\n", " ZH = np.sqrt((omegaH**2-omega2**2)/(omegaH**2-omega1**2))\n", "\n", " NL = 0.5*(1+(AL/10.0-np.log10(abs((ZL+omega2/omega1)/(ZL-omega2/omega1))) \\\n", " +np.log10(4.0/pow(e,2)))/np.log10(abs((ZL+1)/(ZL-1))))\n", " NH = 0.5*(1+(AH/10.0-np.log10(abs((ZH+omega2/omega1)/(ZH-omega2/omega1))) \\\n", " +np.log10(4.0/pow(e,2)))/np.log10(abs((ZH+1)/(ZH-1))))\n", "\n", " N_Order = int(np.ceil(max(NL,NH)))\n", " \n", " return N_Order, omega1, omega2, e, fo" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "#Equations for combline filters from Table I.\n", "N_Order, omega1, omega2, e, fo = Comb_Req_Order(f1, f2, ripple, AL, fL, AH, fH, fS)\n", "\n", "# print('N: ', N_Order)\n", "# print('Omega1: ', omega1)\n", "# print('Omega2: ', omega2)\n", "# print('e: ', e)\n", "# print('fo: ', fo)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$Z_{i} = 1$, for poles at ${\\infty}$. $\\;\\;\\;\\;\\;\\;\\;$ See equations(3)-(4) \n", "$Z_{i} = \\displaystyle\\frac{\\Omega_2}{\\Omega_1}$, for poles at DC. $\\;\\;\\;$See equations(3)-(4) \n", " \n", "Form the following polynomial for a combline filter: \n", "$\\mathbf{E}+Z\\mathbf{F} = \\displaystyle\\prod_{i = 0}^{2N-1}(Z+Z_i) = (Z+1)^{2N-1}\\left[Z+\\frac{\\Omega_2}{\\Omega_1}\\right]$ $\\;\\;\\;\\;$See equation(5) \n", " \n", "$^*$All indices are starting from zero to match with python's convention " ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "def Comb_EpZF(N_Order, omega1, omega2):\n", " #EQN(5): Form the polynomial E+ZF\n", " lp_poly = np.array([1, 1])\n", " EpZF = np.array([1, omega2/omega1]) #EpZF will always have only 1 high pass pole \n", " for i in range(2*N_Order-1):\n", " EpZF = np.polymul(EpZF, lp_poly)\n", " \n", " return EpZF" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "#EQN(5): Form the polynomial E+ZF \n", "EpZF = Comb_EpZF(N_Order, omega1, omega2)\n", "#print('E + ZF: \\n',EpZF)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Extract $\\mathbf{E}$ from $\\mathbf{E}+Z\\mathbf{F}$. $\\mathbf{E}$ is just the even order parts of $\\mathbf{E}+Z\\mathbf{F}$. \n", "\n", "Form the following polynomial:\n", "\n", "$\\mathbf{E} + \\displaystyle\\frac{Z\\mathbf{F}}{\\sqrt{1+{\\epsilon}^{2}}}$ $\\;\\;\\;\\;$See equation(6)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def EpZF_rf(EpZF, e): \n", " #EQN(6): Extract polynomial E and form the polynomial E+ZF/sqr(1+e^2)\n", " #Indicies in the multiplication in EQN(6) are shifted to 0 -> N_order-1\n", " E = np.copy(EpZF)\n", " EpZF_rf = np.copy(EpZF)\n", " # for i in range(1, 2*N_Order, 2):\n", " # E[i] = 0\n", " # EpZF_rf[i] = EpZF_rf[i]/np.sqrt(1+e**2)\n", " E[1::2] =0\n", " EpZF_rf[1::2] = EpZF_rf[1::2]/np.sqrt(1+e**2)\n", " \n", " return E, EpZF_rf" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "#EQN(6): Extract polynomial E and form the polynomial E+ZF/sqr(1+e^2)\n", "E, EpZF_rf = EpZF_rf(EpZF, e) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Find the roots of $\\mathbf{E} + \\displaystyle\\frac{Z\\mathbf{F}}{\\sqrt{1+{\\epsilon}^{2}}} = 0$. \n", " \n", "Form the product of second order polynomials from the roots. \n", " \n", "$\\mathbf{E} + \\displaystyle\\frac{Z\\mathbf{F}}{\\sqrt{1+\\epsilon^2}} = \\displaystyle\\prod_{i = 0}^{N-1}(Z^2+m_iZ+n_i)$ $\\;\\;\\;\\;$See equation(6) \n", " \n", "$^*$All indices are starting from zero to match with python's convention " ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "def EpZF_rf_Poly2(EpZF_rf):\n", " #EQN(6): Find the factored quadratics\n", " #Indicies in the multiplication in EQN(6) are shifted to 0 -> N_order-1\n", " EpZF_rf_fact = [] #list to store the factored quadratics\n", " EpZF_rf_roots = np.roots(EpZF_rf) #The roots should come in complex conjugate pairs\n", " \n", " N_Order = len(EpZF_rf)//2\n", " \n", " for i in range(N_Order):\n", " #assume the resulting roots are listed in ordered pairs of conjugate roots\n", " EpZF_rf_fact.append(np.real(np.polymul([1, -1*EpZF_rf_roots[2*i]], \\\n", " [1, -1*EpZF_rf_roots[2*i+1]])))\n", " \n", " return EpZF_rf_fact" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "#EQN(6): Find the factored quadratics\n", "EpZF_rf_fact = EpZF_rf_Poly2(EpZF_rf)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "After obtaining the values of $m_i$ and $n_i$, find $p_i$, $q_i$, and $r_i$. See equation(7)\n", "\n", "$M_i=2n_i-m_i^2$ \n", " \n", "$N_i=n_i^2$ \n", " \n", "$R_i=\\sqrt{1+M_i+N_i}$ \n", " \n", "$T_i=\\sqrt{\\Omega_2^4+\\Omega_1^2\\Omega_2^2M_i+\\Omega_1^4N_i}$ \n", "$\\;$ \n", "$\\;$ \n", "$p_i=\\displaystyle\\frac{\\Omega_2^2+\\Omega_1^2(1+M_i)}{T_i+\\Omega_1^2R_i}$ \n", " \n", "$r_i=\\displaystyle\\frac{M_i\\Omega_2^2+(\\Omega_1^2+\\Omega_2^2)N_i}{T_i+\\Omega_2^2R_i}$ \n", " \n", "$q_i=\\displaystyle\\sqrt{\\frac{r_i(r_i-M_ip_i)+N_ip_i^2}{T_iR_i}}$" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "def p_q_r_Array(EpZF_rf_fact, omega1, omega2): \n", " #EQN(7): Generate coefficients p, q, r(lower case gamma from paper)\n", " #Indicies in the multiplication are shifted to 0-N_order-1\n", " M = np.array([])\n", " N = np.array([])\n", " R = np.array([])\n", " T = np.array([])\n", " p = np.array([])\n", " q = np.array([])\n", " r = np.array([])\n", " \n", " N_Order = len(EpZF_rf_fact)\n", " \n", " for i in range(N_Order):\n", " M = np.append(M, 2*EpZF_rf_fact[i][2] - EpZF_rf_fact[i][1]**2)\n", " N = np.append(N, EpZF_rf_fact[i][2]**2)\n", " R = np.append(R, np.sqrt(1 + M[i]+N[i]))\n", " T = np.append(T, np.sqrt(omega2**4 + (omega1**2)*(omega2**2)*M[i] \\\n", " + (omega1**4)*N[i]))\n", " p = np.append(p, (omega2**2 + (omega1**2)*(1+M[i])) \\\n", " /(T[i] + (omega1**2)*R[i]))\n", " r = np.append(r, (M[i]*(omega2**2) + (omega1**2 + omega2**2)*N[i]) \\\n", " /(T[i] + (omega2**2)*R[i])) \n", " q = np.append(q, np.sqrt((r[i]*(r[i]-M[i]*p[i]) + N[i]*(p[i]**2))/(T[i]*R[i])))\n", " \n", " return p, q, r" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "#EQN(7): Generate coefficients p, q, r(lower case gamma from paper)\n", "p, q, r = p_q_r_Array(EpZF_rf_fact, omega1, omega2) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Find the polynomials $\\mathbf{A}$ and $\\mathbf{B}$. \n", "\n", "$\\mathbf{A}+\\mathbf{B}\\sqrt{(Z^2-1)(\\Omega_2^2-\\Omega_1^2Z^2)} = \\sqrt{1+\\epsilon^2}\\displaystyle\\prod_{{i=0}}^{N-1}\\left(p_iZ^2+q_i\\sqrt{(Z^2-1)(\\Omega_2^2-\\Omega_1^2Z^2)}+r_i\\right)$ $\\;\\;\\;\\;$See equation(8) \n", " \n", "Due to the $\\sqrt{(Z^2-1)(\\Omega_2^2-\\Omega_1^2Z^2)}$ factor, the procedure for finding polynomials $\\mathbf{A}$ and $\\mathbf{B}$ is not straight forward. \n", "$\\;$ \n", "$\\;$ \n", "The method implemented here for evaluating $\\mathbf{A}$ and $\\mathbf{B}$ is the following: \n", "1. Using the function __prm()__, the permutations of the products of $p_i+q_i+r_i$ are formed. \n", "For example, if $N=3$, the output of __prm(3)__ would be [['$p$', 2, '$p$', 1, '$p$' ,0], ['$q$', 2, '$p$', 1, '$p$', 0], ...] \n", "2. Using __Sorted_Combo()__, each item from the output of __prm()__ is placed in a dictionary of lists where the keys correspond to the number of $q$'s in the item. \n", "For example, {0: [['$p$', 1, '$p$', 1, '$p$', 0, ...]], 1: [['$q$', 2, '$p$', 1, '$p$', 0, ...]], 2: [['$q$', 2, '$q$', 1, '$p$', 0, ...]], 3: [['$q$', 2, '$q$', 1, '$q$', 0]]}\n", "3. Using __ApBsqrt_fact()__, $\\mathbf{A}$ is formed by using the even keys from the output of __Sorted_Combo()__. $\\mathbf{B}$ is formed using the odd keys since the odd combinations of $q$'s will always contain the previously mentioned $\\sqrt{(Z^2-1)(\\Omega_2^2-\\Omega_1^2Z^2)}$ factor. \n", " \n", "$^*$All indices are starting from zero to match with python's convention " ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "def prm(order):\n", " \"\"\"\n", " Generate the permutations of the coefficients of \n", " (p_0+q_0+r_0)(p_1+q_1+r_1)...(p_(order-1)+q_(order-1)+r_(order-1))\n", " \n", " This is useful in formulating eqn(8) from the Wenzel paper:\n", " \"Synthesis of Combline and Capacitively Loaded Inter... of Arbitrary Bandwidth\" \n", " \"\"\"\n", " if order==1:\n", " return [['p',0], ['q',0], ['r',0]]\n", " temp = []\n", " perm_list = prm(order-1)\n", " for item in perm_list:\n", " #print('item',item)\n", " for pqr in ('p', 'q', 'r'):\n", " item_new = item.copy()\n", " item_new.insert(0, order-1)\n", " item_new.insert(0, pqr)\n", " temp.append(item_new) \n", " return temp\n", "\n", "\n", "def form_poly(order, p, q, r, combo, omega1, omega2):\n", " \"\"\"\n", " This function takes a a string in combo and forms a polynomial\n", " \n", " order: order of the filter\n", " p, q, r: numpy arrays of the coefficients p, q, and r\n", " combo: combination of p, q, and r. ie 'p0q1r2' or order=3\n", " omega1, omega2: upper and lower passband frequencies\n", " \n", " This is useful in formulating eqn(8) from the Wenzel paper:\n", " \"Synthesis of Combline and Capacitively Loaded Inter... of Arbitrary Bandwidth\" \n", " \"\"\"\n", " #form the polynomial of z^2\n", " p_poly = np.array([1, 0, 0])\n", " #form the polynomial of (Z^2-1)(omega2^2-omega1^2Z^2)\n", " q_poly = np.polymul([1, 0, -1], [-1*omega1**2, 0, omega2**2])\n", " \n", " #initialize coefficient and order of p's and q's\n", " coef = 1 \n", " p_order = 0\n", " q_order = 0\n", " \n", " #order count and coefficient multiplication\n", " for i in range(0, 2*order, 2):\n", " if combo[i]=='p':\n", " p_order += 1\n", " coef *= p[int(combo[i+1])]\n", " if combo[i]=='q':\n", " q_order += 1\n", " coef *= q[int(combo[i+1])]\n", " if combo[i]=='r':\n", " coef *= r[int(combo[i+1])]\n", " \n", " q_order = q_order//2 \n", " #q_order is halved each q corresponds to sqrt of of q_poly\n", " #integer division will result in correct order if q_order is odd\n", "\n", " #form the polynomial of the combination of combo \n", " polynom = np.array([coef])\n", " for i in range(p_order):\n", " polynom = np.polymul(polynom, p_poly)\n", " for i in range(q_order):\n", " polynom = np.polymul(polynom, q_poly)\n", " \n", " return polynom\n", "\n", "\n", "def Sorted_Combo(N_Order):\n", " '''\n", " form the following list of lists:\n", " [ [ combos with no q ], [ combos with 1 q's ], \n", " [ combos with 2 q's ], ...,[q(N_Order-1)q(N_Order-2)..q0] ]\n", " ''' \n", " #Helper func to generate polynomials A and B, EQN(8)\n", " combo_list = prm(N_Order)\n", " \n", " #Generate lists in a list where the indices of \n", " #the lists correspond to the order of q's\n", " sorted_combo = {}\n", " for key in range(N_Order+1):\n", " sorted_combo[key] = []\n", "\n", " #counts the q's in each permutation and places \n", " #each permutation into the corresponding lists \n", " for combo in combo_list:\n", " count=0\n", " for letter in range(0, len(combo), 2):\n", " if combo[letter]=='q':\n", " count += 1\n", " sorted_combo[count].append(combo)\n", " \n", " return sorted_combo\n", " \n", "\n", "def ApBsqrt_fact(N_Order, e, p, q, r, omega1, omega2, sorted_combo): \n", " #EQN(8): Extract A and B from A+Bsqrt((Z^2-1)(omega^2-(omega1^2)(Z^2)))\n", " #Form the polynomial A\n", " A = np.array([])\n", " #Using only even order of q's\n", " for q_ord in range(0, len(sorted_combo), 2):\n", " for combination in sorted_combo[q_ord]:\n", " A = np.polyadd(A, form_poly(N_Order, p, q, r, combination, omega1, omega2))\n", " A = A*np.sqrt(1+e**2) \n", "\n", " #Form the polynomial B\n", " B = np.array([])\n", " #Using only odd order of q's\n", " for q_ord in range(1, len(sorted_combo), 2):\n", " for combination in sorted_combo[q_ord]:\n", " B = np.polyadd(B, form_poly(N_Order, p, q, r, combination, omega1, omega2))\n", " B = B*np.sqrt(1+e**2)\n", " \n", " return A, B" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "#EQN(8): Extract A and B from A+Bsqrt((Z^2-1)(omega^2-(omega1^2)(Z^2))) \n", "sorted_combo = Sorted_Combo(N_Order)\n", "A, B = ApBsqrt_fact(N_Order, e, p, q, r, omega1, omega2, sorted_combo)\n", "#print(A)\n", "#print(B)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With polynomials $\\mathbf{A}$, $\\mathbf{B}$, and $\\mathbf{E}$, form the following polynomial: \n", "\n", "$\\mathbf{Y}(Z^2)=\\displaystyle\\frac{\\mathbf{A}+{\\epsilon}\\mathbf{E}}{\\mathbf{B}(\\Omega_2^2-\\Omega_1^2Z^2)}$ $\\;\\;\\;\\;$See equation(9) " ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "def YofZ2(A, E, B, e, omega1, omega2):\n", " #EQN(9):Form polynomial Y(Z^2)'s numerator Yn=A+eE \n", " #Form Y(Z^2)'s denominator Yd=B(omega2^2-omega1^2Z^2)\n", " YZn = np.polyadd(A, e*E)\n", " YZd = np.polymul(B, [-1*(omega1**2), 0, omega2**2])\n", "\n", " return YZn, YZd" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "#EQN(9):Form polynomial Y(Z^2)'s numerator Yn=A+eE \n", "YZn, YZd = YofZ2(A, E, B, e, omega1, omega2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert from Z to S domain by the following substitution: \n", "\n", "$Z^2=\\displaystyle\\frac{S^2+\\Omega_2^2}{S^2+\\Omega_1^2}$ $\\;\\;\\;\\;$See equation(2) \n", " \n", "Find $\\mathbf{Y}(S)$ using the following relationship: \n", "\n", "$\\displaystyle\\frac{\\mathbf{Y}(S)}{S}=\\mathbf{Y}(Z^2)$ $\\;\\;\\;\\;$See equation(9) " ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "def YofS(YZn, YZd, omega1, omega2):\n", " #Convert Y(Z^2) to Y(S)/S by using EQN(2)\n", " Z2n = np.array([1, 0, omega2**2])\n", " Z2d = np.array([1, 0, omega1**2])\n", "\n", " #Numerator and denominator of Y(Z^2) will have the same even order\n", " YSn = np.array([])\n", " YSd = np.array([])\n", " \n", " N_Order = len(YZd)//2\n", " for i in range(N_Order + 1):\n", " temp = np.polymul(np_pp.polypow(Z2n, N_Order - i), np_pp.polypow(Z2d, i)) \n", " YSn = np.polyadd(YSn, YZn[2*i]*temp) \n", " YSd = np.polyadd(YSd, YZd[2*i]*temp)\n", "\n", " #Ysd constant term is always zero and therefore..\n", " YSd = YSd[:-1]\n", " \n", " return YSn, YSd" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "#Convert Y(Z^2) to Y(S)/S by using EQN(2)\n", "YSn, YSd = YofS(YZn, YZd, omega1, omega2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once $\\mathbf{Y}(S)$ is obtained, the filter elements can then be extracted from $\\mathbf{Y}(S)$. \n", " \n", "The element extraction process for a network with equal shunt capacitors is described in the paragraphs after equation(9). It is further elaborated below: \n", "\n", "__Step 1__. A shunt capacitor $C_1$ is extracted from $\\mathbf{Y}(S)$ leaving $\\mathbf{Y}'(S)$ \n", " \n", "__Step 2__. Shunt inductor $L_T$ is extracted from $\\mathbf{Y}'(S)$ leaving $\\mathbf{Y}''(S)$ \n", " \n", "__Step 3__. A series inductor $L_{C1}'$ is removed from $\\mathbf{Y}''(S)$ leaving $\\displaystyle\\frac{1}{\\mathbf{Y}'''(S)}$ or $\\mathbf{Z}'''(S)$ \n", " \n", "__Step 4__. Shunt capcitor $C_2'$ is evaluated but not revomed from $\\mathbf{Z}'''(S)$ \n", " \n", "__Step 5__. Find the redundant circuit elements $L_1$ and $L_{C1}$ (notice the $'$ has disappeared) where \n", " \n", "$\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;$ $L_1=\\displaystyle\\frac{L_T L_{C1}'}{L_{C1}'+\\left(1-\\sqrt{\\displaystyle\\frac{C_1}{C_2'}}\\right)L_{T}}$ \n", "\n", "$\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;$ $L_{C1}=L_{C1}'\\sqrt{\\displaystyle\\frac{C_2'}{C_1}}$ \n", " \n", " \n", "$\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;$ The factor $\\sqrt{\\displaystyle\\frac{C_1}{C_2'}}$ is to keep all shunt capacitors equal in the final nonredundant network \n", "\n", "Steps 1-5 is implemented by the function __Comb_Non_Red_Extract()__ \n", " \n", "__Step 6__. Go back to step 2 and perform the following steps starting from $\\mathbf{Y}'(S)$: \n", "\n", "$\\;\\;\\;\\;\\;\\;\\;$__Step 2__. Shunt inductor $L_1$ is partially extracted from $\\mathbf{Y}'(S)$ leaving $\\mathbf{Y}''(S)$ \n", "$\\;\\;\\;\\;\\;\\;\\;$__Step 3__. A series inductor $L_{C1}$ is removed from $\\mathbf{Y}''(S)$ leaving $\\displaystyle\\frac{1}{\\mathbf{Y}'''(S)}$ or $\\mathbf{Z}'''(S)$ \n", "Step 6 is implemented by the function __Comb_Red_Extract()__ \n", "${\\mathbf{Y}'''(S)}$ becomes ${\\mathbf{Y}(S)}$ and steps 1-6 is repeated $N-1$ times until only a shunt inductor and a shunt capacitor is left \n", " \n", "__Step 7__. ${\\mathbf{Y}'''(S)}$ becomes ${\\mathbf{Y}(S)}$ and go back to step 1: \n", " \n", "$\\;\\;\\;\\;\\;\\;\\;$__Step 1__. Shunt inductor $L_N$ is extracted from $\\mathbf{Y}(S)$ leaving $\\mathbf{Y}'(S)$ \n", "$\\;\\;\\;\\;\\;\\;\\;$__Step 2__. Shunt capacitor $C_N$ is extracted from $\\mathbf{Y}'(S)$" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "def Comb_Non_Red_Extract(Yn, Yd, digs = 12):\n", " #Step 1: A shunt capacitor C1 is totally removed from Y(s) leaving Y'(s)\n", " #Yn/Yd\n", " Yn_div_Yd = np.polydiv(Yn, Yd)\n", " C1 = Yn_div_Yd[0][0]\n", " #Remainder function\n", " Yn_p = np.trim_zeros(np.round(np.copy(Yn_div_Yd[1]), digs), 'f')\n", " Yd_p = np.copy(Yd)\n", " \n", " #Step 2: The shunt inductor LT is totally removed from Y'(s) leaving Y''(s)\n", " #See Saal & Ulbrich extraction Figure 4\n", " #LT = 1/(sY'(s)), s->0\n", " LT = np.polyval(Yd_p[:-1], 0)/np.polyval(Yn_p, 0)\n", " #Remainder function\n", " Yn_pp = np.polysub(Yn_p, 1/LT*Yd_p[:-1])\n", " Yd_pp = np.copy(Yd_p)\n", " \n", " #Step 3: A series inductor LC1' is totally removed from Z''(S) leaving Z'''(S)\n", " Zn_pp = np.copy(Yd_pp)\n", " Zd_pp = np.trim_zeros(np.round(np.copy(Yn_pp), digs), 'f')\n", " LC1_p = Zn_pp[0]/Zd_pp[0]\n", " Zn_ppp = np.polysub(Zn_pp, np.polymul(Zd_pp, [LC1_p, 0]))\n", " Zd_ppp = np.copy(Zd_pp)\n", " \n", " #Step 4: The next shunt capacitor C2' is evaluated from Y'''(S)\n", " Yn_pppp = np.copy(Zd_ppp)\n", " Yd_pppp = np.trim_zeros(np.round(np.copy(Zn_ppp), digs), 'f')\n", " Yn_pppp_div_Yd_pppp = np.polydiv(Yn_pppp, Yd_pppp)\n", " C2_p = Yn_pppp_div_Yd_pppp[0][0]\n", "\n", " #Step 5: The redundant network element values are then C1, L1, LC1, where \n", " L1 = LT*LC1_p/(LC1_p+(1-np.sqrt(C1/C2_p))*LT)\n", " LC1 = LC1_p*np.sqrt(C2_p/C1)\n", " #n = np.sqrt(C1/C2_p)\n", " \n", " return C1, L1, LC1, Yn_p, Yd_p\n", "\n", " \n", "def Comb_Red_Extract(L1, LC1, Yn_p, Yd_p, digs = 12):\n", " #Step 6:\n", " #Repeat step 2: A shunt inductor L1 is partially removed from Y'(S) leaving Y''(S)\n", " #Remainder function. See Saal & Ulbrich extraction Figure 4.\n", " Yn_pp = np.polysub(Yn_p, 1/L1*Yd_p[:-1])\n", " Yd_pp = np.copy(Yd_p)\n", " #print('Red ',Yn_pp, Yd_pp) \n", " \n", " #Repeat step 3: A series inductor LC1 is partially removed from Z''(S) leaving Z'''(S)\n", " Zn_pp = np.copy(Yd_pp)\n", " Zd_pp = np.trim_zeros(np.round(np.copy(Yn_pp), digs), 'f')\n", " #Remainder function.\n", " Zn_ppp = np.polysub(Zn_pp, np.polymul(Zd_pp, [LC1, 0]))\n", " Zd_ppp = np.copy(Zd_pp)\n", " #Convert to Y'''(S)\n", " Yn_ppp = np.copy(Zd_ppp)\n", " Yd_ppp = np.trim_zeros(np.round(np.copy(Zn_ppp), digs-2), 'f')\n", " #print('Red ',Yn_ppp, Yd_ppp) \n", " \n", " return Yn_ppp, Yd_ppp\n", " \n", "\n", "def Comb_CL_Extract(Yn, Yd, digs = 12):\n", " #Step 7:\n", " #Repeat step 1: A shunt capacitor C1 is totally removed from Y(s) leaving Y'(s)\n", " #Yn/Yd\n", " Yn_div_Yd = np.polydiv(Yn, Yd)\n", " C1 = Yn_div_Yd[0][0]\n", " #Remainder function\n", " Yn_p = np.trim_zeros(np.round(np.copy(Yn_div_Yd[1]), digs), 'f')\n", " Yd_p = np.copy(Yd)\n", " \n", " #Repeat step 2: The shunt inductor LT is totally removed from Y'(s) leaving Y''(s)\n", " #See Saal & Ulbrich extraction Figure 4\n", " #LT = 1/(sY'(s)), s->0\n", " LT = np.polyval(Yd_p[:-1], 0)/np.polyval(Yn_p, 0)\n", " #Remainder function\n", " Yn_pp = np.polysub(Yn_p, 1/LT*Yd_p[:-1])\n", " Yd_pp = np.copy(Yd_p)\n", " \n", " return C1, LT, Yn_pp, Yd_pp \n", "def Comb_Element_Extract(Yn, Yd, N_Order): \n", " #Create empty component arrays\n", " cap_array = np.array([])\n", " ind_array = np.array([])\n", "\n", " #Extract components\n", " for ext in range(N_Order - 1):\n", " C, L, LC, Yn, Yd = Comb_Non_Red_Extract(Yn, Yd)\n", " \n", " #Update element array\n", " cap_array = np.append(cap_array, [C, np.inf])\n", " ind_array = np.append(ind_array, [L, LC])\n", " \n", " Yn, Yd = Comb_Red_Extract(L, LC, Yn, Yd) \n", "\n", " #Extract remaining shunt L||C resonator \n", " C, L, _, _ = Comb_CL_Extract(Yn, Yd) \n", "\n", " #Update element array\n", " cap_array = np.append(cap_array, C)\n", " ind_array = np.append(ind_array, L)\n", " \n", " return cap_array, ind_array" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "N: 3\n", "Shunt Caps: [4.2073454 4.2073454 4.2073454]\n", "Shunt Inds: [1.30028089 3.0501708 1.30028089]\n", "Series Inds: [1.99973049 1.99973049]\n" ] } ], "source": [ "#Extract capacitors and inductors for equal capacitor values\n", "cap_array, ind_array = Comb_Element_Extract(YSn, YSd, N_Order)\n", "\n", "print('N: ',N_Order)\n", "print('Shunt Caps: ',cap_array[::2])\n", "print('Shunt Inds: ',ind_array[::2])\n", "print('Series Inds: ',ind_array[1::2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "___" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Simulation of combline filter \n", " \n", "The extracted inductors and capacitor values are normalized to 1${\\Omega}$ termination. Also, they represent impedances of short and open commensurate length transmission lines. To simulate the filter response, the components are treated as lumped elements and lambdified to ${\\omega}$ with __SAPLad()__. ${\\omega}$ is then mapped to the interested frequency with Richards' transform, \n", "\n", "$S=j{\\omega}=j\\;tan\\displaystyle\\frac{\\pi f}{2f_o}$ $\\;\\;\\;$See equation(1)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "def SAPLad(E, First_Element = 'shnt'):\n", " \n", " \"\"\"\n", " S-parameter All Pole Ladder(SAPLad)\n", " Function for computing the S-Parameters of an all pole\n", " doubly terminated ladder network\n", " \n", " E = an array of network elements including the terminations\n", " First_Element = 'shnt'(shunt) or 'srs'(series)\n", " \"\"\"\n", " \n", " w = sp.symbols('w')\n", " Rs = E[0][0]\n", " Rl = E[0][1]\n", " orientation = First_Element\n", " abcd = np.matrix([[1, 0], [0, 1]])\n", " \n", " for k in range(1, len(E)):\n", " if orientation == 'shnt':\n", " abcd = np.dot(abcd, np.matrix([[1, 0], [(1j*w*E[k][0]), 1]]))\n", " abcd = np.dot(abcd, np.matrix([[1, 0], [(1j*w*E[k][1])**-1, 1]]))\n", " orientation = 'srs'\n", " else:\n", " abcd = np.dot(abcd, np.matrix([[1, (1j*w*E[k][0])**-1], [0, 1]]))\n", " abcd = np.dot(abcd, np.matrix([[1, (1j*w*E[k][1])], [0, 1]]))\n", " orientation = 'shnt'\n", "\n", " abcd = np.dot(abcd, np.matrix([[np.sqrt(Rl/Rs), 0], [0, 1/np.sqrt(Rl/Rs)]]))\n", " \n", " A = abcd[0,0]\n", " B = abcd[0,1]\n", " C = abcd[1,0]\n", " D = abcd[1,1]\n", " \n", " denom = A+B/Rs+C*Rs+D\n", " S11_sym = (A+B/Rs-C*Rs-D)/denom\n", " S21_sym = 2/denom\n", " S12_sym = 2**(A*D-B*C)/denom\n", " S22_sym = (-A+B/Rs-C*Rs+D)/denom\n", " \n", " S11 = sp.lambdify(w, S11_sym, 'numpy')\n", " S21 = sp.lambdify(w, S21_sym, 'numpy')\n", " S12 = sp.lambdify(w, S12_sym, 'numpy')\n", " S22 = sp.lambdify(w, S22_sym, 'numpy')\n", " \n", " return np.array([S11, S21, S12, S22])\n", "\n", "\n", "def Eval_Elements(cap_array, ind_array, w, terms=np.array([1,1])):\n", " '''\n", " Outputs the S21 and S11 in dB given abs(K(lambda))^2\n", " FnF: numerator of abs(K(lambda))^2\n", " PnF: denominator of abs(K(lambda))^2\n", " w: vector of omega for evaluation (rad)\n", " '''\n", " \n", " EE = np.array([[terms[0], terms[1]]])\n", " for i in range(len(cap_array)):\n", " EE = np.append(EE, [[cap_array[i], ind_array[i]]], axis = 0)\n", " #print(EE) \n", "\n", " Sparm = SAPLad(EE)\n", " S11 = Sparm[0]\n", " S21 = Sparm[1]\n", " \n", " S11_dB = 20*np.log10(abs(S11(w)) + 1e-10) #1e-8 is added to prevent log(0) condition\n", " S21_dB = 20*np.log10(abs(S21(w)) + 1e-10) #1e-8 is added to prevent log(0) condition\n", " \n", " return S11_dB, S21_dB\n", " \n", "\n", "def Plot_S(S11_dB, S21_dB, f, fs, rej_dB, title = '???', fig = 1):\n", " #test plot of transfer function \n", " \n", " #plt.clf()\n", " #plt.close(fig)\n", " plt.figure(fig, (10, 6))\n", " plt.plot(f, S21_dB, label = '$|S_{21}|^2$')\n", " plt.plot(f, S11_dB, label = '$|S_{11}|^2$')\n", " plt.legend(loc = 'lower right', shadow=False, fontsize = 'large')\n", " \n", " #Y Limit\n", " As = -1*rej_dB\n", " \n", " #Limit the lower y-axis to 20dB below the rejection rounded to x10. E.g. 43->50\n", " plt.axis([0, f[-1], np.floor(As/10)*10-20, 0])\n", " plt.xticks(np.arange(0, f[-1]+1, 0.5))\n", " \n", " plt.xlabel('freq(GHz)', fontsize = 'large')\n", " plt.ylabel('(dB)', fontsize = 'large')\n", " plt.title(title, fontsize = 'large')\n", " \n", " plt.grid(b = bool)\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "#Simulation frequency range in Hz\n", "f = np.arange(20e6, 4e9, 20e6)\n", "\n", "#Applying Richard's Transform\n", "w = np.tan(np.pi/2*f/fo)\n", "\n", "#Simulate using ABCD \n", "S11_dB, S21_dB = Eval_Elements(cap_array, ind_array, w)\n", "\n", "#Normalize frequency to GHz and plot\n", "fplot = f/1e9\n", "Plot_S(S11_dB, S21_dB, fplot, fH/1e9, AH, 'Combline Example')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### As the number of resonators increases, the lambdification method of simulation becomes extremely time consuming. Also, symmetry of the extracted values starts to deviate. To be continued..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 2 }