def coupling_N(F, P, E, eps, eps_R): """Function to evaluate the (N,N) coupling matrix.""" F = s_to_w(F); P = s_to_w(P); E = s_to_w(E) nfz = len(P) - 1 const_mult = np.conjugate(eps) / eps * (-1) ** nfz EF_plus = E + F / eps_R EF_plus_conj = const_mult * EF_plus.conj() EF_minus = E - F / eps_R EF_minus_conj = const_mult * EF_minus.conj() y11_Num = 1j * (EF_minus + EF_minus_conj) y21_Num = 2j * P / eps y_Den = EF_plus - EF_plus_conj # The function "signal.residue" takes only 1D arrays! resid11, poles11, const11 = signal.residue(y11_Num[:, 0], y_Den[:, 0]) resid21, poles21, const21 = signal.residue(y21_Num[:, 0], y_Den[:, 0]) # Gramm_Schmidt orthonormalization T1k = np.sqrt(resid11); lambdk = -poles11; TNk = resid21 / T1k RS_L1 = sum(T1k ** 2); RL_LN = sum(TNk ** 2) T = np.eye(len(T1k), len(T1k)) T[0, :] = T1k; T[1, :] = TNk np.set_printoptions(precision=6, suppress=True) T = Gramm_Schmidt(T) # "normalizing of T1k, TNk" is done in this step # swapping the second and last rows after normalization is finished temp = np.copy(T[1, :]); T[1, :] = T[-1, :]; T[-1, :] = temp Lamb = np.diag(lambdk) # diagonal eigenvalue matrix M = np.dot(T, np.dot(Lamb, T.T)) # (N,N) coupling matrix return M, RS_L1, RL_LN
def coupling_N2(F, P, E, eps, eps_R): r""" Function to evaluate the (N+2,N+2) coupling matrix. :param F: Polynomial F, i.e., numerator of S11 (in s-domain) :param P: Polynomial P, i.e., numerator of S21 (in s-domain) :param E: polynomial E is the denominator of S11 and S21 (in s-domain) :param eps: Constant term associated with S21 :param eps_R: Constant term associated with S11 :rtype: Returns (N+2,N+2) coupling matrix """ F = s_to_w(F); P = s_to_w(P); E = s_to_w(E) nfz = len(P) - 1; N = len(E) - 1 const_mult = np.conjugate(eps) / eps * (-1) ** nfz EF_plus = E + F / eps_R EF_plus_conj = const_mult * EF_plus.conj() EF_minus = E - F / eps_R EF_minus_conj = const_mult * EF_minus.conj() y11_Num = 1j * (EF_minus + EF_minus_conj) y21_Num = -2j * P / eps y_Den = EF_plus - EF_plus_conj # The function "signal.residue" takes only 1D arrays! resid11, poles11, const11 = signal.residue(y11_Num[:, 0], y_Den[:, 0]) resid21, poles21, const21 = signal.residue(y21_Num[:, 0], y_Den[:, 0]) MSk = np.sqrt(resid11); lambdk = -poles11; MLk = resid21 / MSk JSL = -const21 M = np.zeros((N + 2, N + 2), dtype=complex) M[0, 1:N + 1] = np.reshape(MSk, (-1, N)) M[-1, 1:N + 1] = np.reshape(MLk, (-1, N)) M[:, 0] = M[0, :]; M[:, -1] = M[-1, :] M[0, -1] = JSL[0]; M[-1, 0] = JSL[0] diag1 = np.diag(lambdk); M[1:N + 1, 1:N + 1] = diag1 return M
def coupling_N(F, P, E, eps, eps_R): r""" Function to evaluate the (N,N) coupling matrix. :param F: Polynomial F, i.e., numerator of S11 (in s-domain) :param P: Polynomial P, i.e., numerator of S21 (in s-domain) :param E: polynomial E is the denominator of S11 and S21 (in s-domain) :param eps: Constant term associated with S21 :param eps_R: Constant term associated with S11 :rtype: [M, RS_L1, RL_LN] ... M is the NbyN coupling matrix and RS_L1 and RL_LN are the ratios of RS/L1 and RL/LN. """ N = len(F) nfz = len(P) if (((N + nfz) % 2 == 0) and (abs(eps.real) > 0)): warnings.warn( "'eps' value should be pure imaginary when (N+nfz) is an even number" ) elif (((N + nfz + 1) % 2 == 0) and (abs(eps.imag) > 0)): warnings.warn( "'eps' value should be pure real when (N+nfz) is an odd number") F = s_to_w(F) P = s_to_w(P) E = s_to_w(E) nfz = len(P) - 1 const_mult = np.conjugate(eps) / eps * (-1)**nfz EF_plus = E + F / eps_R EF_plus_conj = const_mult * EF_plus.conj() EF_minus = E - F / eps_R EF_minus_conj = const_mult * EF_minus.conj() y11_Num = 1j * (EF_minus + EF_minus_conj) y21_Num = 2j * P / eps y_Den = EF_plus - EF_plus_conj # The function "signal.residue" takes only 1D arrays! resid11, poles11, const11 = signal.residue(y11_Num[:, 0], y_Den[:, 0]) resid21, poles21, const21 = signal.residue(y21_Num[:, 0], y_Den[:, 0]) # Gramm_Schmidt orthonormalization T1k = np.sqrt(resid11) lambdk = -poles11 TNk = resid21 / T1k RS_L1 = sum(T1k**2) RL_LN = sum(TNk**2) T = np.eye(len(T1k), len(T1k), dtype='complex') T[0, :] = T1k T[1, :] = TNk np.set_printoptions(precision=6, suppress=True) T = Gramm_Schmidt(T) # "normalizing of T1k, TNk" is done in this step # swapping the second and last rows after normalization is finished temp = np.copy(T[1, :]) T[1, :] = T[-1, :] T[-1, :] = temp Lamb = np.diag(lambdk) # diagonal eigenvalue matrix M = np.dot(T, np.dot(Lamb, T.T)) # (N,N) coupling matrix return M, RS_L1, RL_LN
def coupling_N(F, P, E, eps, eps_R): r""" Function to evaluate the (N,N) coupling matrix. :param F: Polynomial F, i.e., numerator of S11 (in s-domain) :param P: Polynomial P, i.e., numerator of S21 (in s-domain) :param E: polynomial E is the denominator of S11 and S21 (in s-domain) :param eps: Constant term associated with S21 :param eps_R: Constant term associated with S11 :rtype: [M, RS_L1, RL_LN] ... M is the NbyN coupling matrix and RS_L1 and RL_LN are the ratios of RS/L1 and RL/LN. """ N = len(F) nfz = len(P) if ((N + nfz) % 2 == 0) and (abs(eps.real) > 0): warnings.warn("'eps' value should be pure imaginary when (N+nfz) is an even number") elif ((N + nfz + 1) % 2 == 0) and (abs(eps.imag) > 0): warnings.warn("'eps' value should be pure real when (N+nfz) is an odd number") F = s_to_w(F) P = s_to_w(P) E = s_to_w(E) nfz = len(P) - 1 const_mult = np.conjugate(eps) / eps * (-1) ** nfz EF_plus = E + F / eps_R EF_plus_conj = const_mult * EF_plus.conj() EF_minus = E - F / eps_R EF_minus_conj = const_mult * EF_minus.conj() y11_Num = 1j * (EF_minus + EF_minus_conj) y21_Num = 2j * P / eps y_Den = EF_plus - EF_plus_conj # The function "signal.residue" takes only 1D arrays! resid11, poles11, const11 = signal.residue(y11_Num[:, 0], y_Den[:, 0]) resid21, poles21, const21 = signal.residue(y21_Num[:, 0], y_Den[:, 0]) # Gramm_Schmidt orthonormalization T1k = np.sqrt(resid11) lambdk = -poles11 TNk = resid21 / T1k RS_L1 = sum(T1k ** 2) RL_LN = sum(TNk ** 2) T = np.eye(len(T1k), len(T1k), dtype="complex") T[0, :] = T1k T[1, :] = TNk np.set_printoptions(precision=6, suppress=True) T = Gramm_Schmidt(T) # "normalizing of T1k, TNk" is done in this step # swapping the second and last rows after normalization is finished temp = np.copy(T[1, :]) T[1, :] = T[-1, :] T[-1, :] = temp Lamb = np.diag(lambdk) # diagonal eigenvalue matrix M = np.dot(T, np.dot(Lamb, T.T)) # (N,N) coupling matrix return M, RS_L1, RL_LN
def _get_coeff(self, b, a): # multiply by 1/s (step) a = np.append(a, 0) # do partial fraction expansion # r: Residues # p: Poles # k: Coefficients of the direct polynomial term r, p, _ = signal.residue(b, a) d_s = np.array([]) d_d_real = np.array([]) d_d_imag = np.array([]) d_i = np.array([]) poles_real = np.array([]) poles_imag = np.array([]) integrador = 0 i = 0 for i in range(np.size(p)): if (p[i] == 0): if (integrador): d_i = np.append(d_i, r[i].real) else: d_s = np.append(d_s, r[i].real) integrador += 1 else: d_d_real = np.append(d_d_real, r[i].real) d_d_imag = np.append(d_d_imag, r[i].imag) poles_real = np.append(poles_real, p[i].real) poles_imag = np.append(poles_imag, p[i].imag) if (d_i.size == 0): d_i = np.append(d_i, 0) return d_s, d_d_real, d_d_imag, d_i, poles_real, poles_imag
def _get_coeff(self, b, a): # multiply by 1/s (step) a = np.append(a, 0) # do partial fraction expansion r, p, k = signal.residue(b, a) # r: Residues # p: Poles # k: Coefficients of the direct polynomial term d_s = np.array([]) # d_d = np.array([]) d_d = np.zeros(self.na) d_i = np.array([]) poles = np.zeros(self.na) integrador = 0 i = 0 for i in range(np.size(p)): if (p[i] == 0): if (integrador): d_i = np.append(d_i, r[i]) else: d_s = np.append(d_s, r[i]) integrador += 1 else: d_d[i] = r[i] poles[i] = p[i] i += 1 if (d_i.size == 0): d_i = np.append(d_i, 0) return d_s, d_d, d_i, poles
def __init__(self, b, a, Ts=1.): # multiple roots are not supported if ( np.any( sig.unique_roots( np.roots(a) )[1] > 1 ) ): raise("Roots with multiplicity > 1 not supported") self.b = b; self.a = a; # Partial Fraction Decomposition (r, p, k) = sig.residue(b,a) if ( k != 0. ): raise("System with direct term not supported ATM") # roots in half-plane with imag-part >= 0 posIP = np.imag(p) >= 0.0 r = r[posIP] p = p[posIP] # going into the discrete domain P = np.exp(p*Ts) self.PS = list() for rp in zip(r,P): self.PS.append( firstOrderSys(rp) ) # must make sure polynomial computation doesn't overflow # NOTE: if there are multiple channels then all must be normalized # by the *same* number maxResp = self.maxPolyResponse() if self.maxPolyResponse() > 1.0: raise RuntimeError("Polynomial too large (would overflow); reduce numerator by", maxResp)
def residue(self, tol=1e-3, verbose=0): """from scipy.signal.residue: Compute residues/partial-fraction expansion of b(s) / a(s). If M = len(b) and N = len(a) b(s) b[0] s**(M-1) + b[1] s**(M-2) + ... + b[M-1] H(s) = ------ = ---------------------------------------------- a(s) a[0] s**(N-1) + a[1] s**(N-2) + ... + a[N-1] r[0] r[1] r[-1] = -------- + -------- + ... + --------- + k(s) (s-p[0]) (s-p[1]) (s-p[-1]) If there are any repeated roots (closer than tol), then the partial fraction expansion has terms like r[i] r[i+1] r[i+n-1] -------- + ----------- + ... + ----------- (s-p[i]) (s-p[i])**2 (s-p[i])**n returns r, p, k """ r,p,k = signal.residue(self.num, self.den, tol=tol) if verbose>0: print('r='+str(r)) print('') print('p='+str(p)) print('') print('k='+str(k)) return r, p, k
def impinvar(b_in, a_in, fs=1, tol=0.0001): """ # Copyright (c) 2007 R.G.H. Eschauzier <*****@*****.**> # Copyright (c) 2011 Carnë Draug <*****@*****.**> # Copyright (c) 2011 Juan Pablo Carbajal <*****@*****.**> # Copyright (c) 2016 Sascha Eichstädt <*****@*****.**> # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, see <https://www.gnu.org/licenses/>. """ ts = 1 / fs # we should be using sampling frequencies to be compatible with Matlab [r_in, p_in, k_in] = dsp.residue(b_in, a_in) # partial fraction expansion n = len(r_in) # Number of poles/residues if len(k_in ) > 0: # Greater than zero means we cannot do impulse invariance if abs(k_in) > 0: raise ValueError("Order numerator >= order denominator") r_out = np.zeros(n, dtype=complex) # Residues of H(z) p_out = np.zeros(n, dtype=complex) # Poles of H(z) k_out = np.zeros(1) # Constant term of H(z) i = 0 while i < n: m = 0 first_pole = p_in[i] # Pole in the s-domain while (i < (n - 1) and np.abs(first_pole - p_in[i + 1]) < tol ): # Multiple poles at p(i) i += 1 # Next residue m += 1 # Next multiplicity [r, p, k] = z_res(r_in[i - m:i + 1], first_pole, ts) # Find z-domain residues k_out += k # Add direct term to output p_out[i - m:i + 1] = p # Copy z-domain pole(s) to output r_out[i - m:i + 1] = r # Copy z-domain residue(s) to output i += 1 # Next s-domain residue/pole [b_out, a_out] = dsp.invres(r_out, p_out, k_out, tol=tol) a_out = to_real(a_out) # Get rid of spurious imaginary part b_out = to_real(b_out) # Shift results right to account for calculating in z instead of z^-1 b_out = b_out[:-1] return b_out, a_out
def impinvar(b_in, a_in, fs = 1, tol = 0.0001): """ ## Copyright (c) 2007 R.G.H. Eschauzier <*****@*****.**> ## Copyright (c) 2011 Carnë Draug <*****@*****.**> ## Copyright (c) 2011 Juan Pablo Carbajal <*****@*****.**> ## Copyright (c) 2016 Sascha Eichstaedt <*****@*****.**> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see <http://www.gnu.org/licenses/>. """ ts = 1 / fs # we should be using sampling frequencies to be compatible with Matlab [r_in, p_in, k_in] = dsp.residue(b_in, a_in) # partial fraction expansion n = len(r_in) # Number of poles/residues if (len(k_in) > 0): # Greater than zero means we cannot do impulse invariance if abs(k_in)>0: raise ValueError("Order numerator >= order denominator") r_out = np.zeros(n, dtype = complex) # Residues of H(z) p_out = np.zeros(n, dtype = complex) # Poles of H(z) k_out = np.zeros(n) # Constant term of H(z) i = 0 while (i < n): m = 0 first_pole = p_in[i] # Pole in the s-domain while (i < (n-1) and np.abs(first_pole - p_in[i + 1]) < tol): # Multiple poles at p(i) i += 1 # Next residue m += 1 # Next multiplicity [r, p, k] = z_res(r_in[i - m:i+1], first_pole, ts) # Find z-domain residues k_out += k # Add direct term to output p_out[i - m:i+1] = p # Copy z-domain pole(s) to output r_out[i - m:i+1] = r # Copy z-domain residue(s) to output i += 1 # Next s-domain residue/pole [b_out, a_out] = dsp.invres(r_out, p_out, k_out, tol=tol) a_out = to_real(a_out) # Get rid of spurious imaginary part b_out = to_real(b_out) ## Shift results right to account for calculating in z instead of z^-1 b_out = b_out[:-1] return b_out, a_out
def coupling_N2(F, P, E, eps, eps_R): r""" Function to evaluate the (N+2,N+2) coupling matrix. :param F: Polynomial F, i.e., numerator of S11 (in s-domain) :param P: Polynomial P, i.e., numerator of S21 (in s-domain) :param E: polynomial E is the denominator of S11 and S21 (in s-domain) :param eps: Constant term associated with S21 :param eps_R: Constant term associated with S11 :rtype: Returns (N+2,N+2) coupling matrix """ F = s_to_w(F) P = s_to_w(P) E = s_to_w(E) nfz = len(P) - 1 N = len(E) - 1 const_mult = np.conjugate(eps) / eps * (-1) ** nfz EF_plus = E + F / eps_R EF_plus_conj = const_mult * EF_plus.conj() EF_minus = E - F / eps_R EF_minus_conj = const_mult * EF_minus.conj() y11_Num = 1j * (EF_minus + EF_minus_conj) y21_Num = -2j * P / eps y_Den = EF_plus - EF_plus_conj # The function "signal.residue" takes only 1D arrays! resid11, poles11, const11 = signal.residue(y11_Num[:, 0], y_Den[:, 0]) resid21, poles21, const21 = signal.residue(y21_Num[:, 0], y_Den[:, 0]) MSk = np.sqrt(resid11) lambdk = -poles11 MLk = resid21 / MSk JSL = -const21 M = np.zeros((N + 2, N + 2), dtype=complex) M[0, 1 : N + 1] = np.reshape(MSk, (-1, N)) M[-1, 1 : N + 1] = np.reshape(MLk, (-1, N)) M[:, 0] = M[0, :] M[:, -1] = M[-1, :] M[0, -1] = JSL[0] M[-1, 0] = JSL[0] diag1 = np.diag(lambdk) M[1 : N + 1, 1 : N + 1] = diag1 return M
def green_components(self): mult = {} simple_decomp_coeffs, poles, k = residue(self.Q, self.P) components = [] for i, p in enumerate(poles): if p in mult: mult[p] += 1 else: mult[p] = 1 m = mult[p] components.append((simple_decomp_coeffs[i], m, p)) return components
def gpa_72aa_pf(alf): ''' This code computes the residues (weights) and poles of the partial fraction decomposition of the global Pad\'e approximant, type (7,2) for \alpha = \beta. Signature: gpa_72aa_pf(alf) Parameters: alf positive number in (0, 1) Returns: r : ndarray Residues (or Weights). p : ndarray Poles The partial fraction decomposition can be assumed to take the form r[0] r[1] r[2] r[3] -------- + -------- + --------- + --------- (s-p[0]) (s-p[1]) (s-p[2]) (s-p[3]) ''' a = gamma(-alf)/gamma(alf) b = gamma(-alf)/gamma(2*alf) c = gamma(-alf)/gamma(3*alf) d = gamma(-alf)/gamma(4*alf) e = gamma(-alf)/gamma(5*alf) f = gamma(-alf)/gamma(-2*alf) p2 = a*(a**4 - 2*a**2*c - a**2*d*f + 2*a*b**2 + 2*a*b*c*f - b**3*f - b*d + c**2)/(a**3*e - 2*a**2*b*d - a**2*c**2 + 3*a*b**2*c - a*c*e + a*d**2 - b**4 + b**2*e - 2*b*c*d + c**3) p3 = (-a*b*e + a*c*d - a*(a**2*d - 2*a*b*c + b**3) + b**2*d - b*c**2 + f*(-a**3*e + 2*a**2*b*d + a**2*c**2 - 3*a*b**2*c + b**4))/(a**3*e - 2*a**2*b*d - a**2*c**2 + 3*a*b**2*c - a*c*e + a*d**2 - b**4 + b**2*e - 2*b*c*d + c**3) q0 = (a**2*c - a*b**2 - a*(a**3 - a*c + b**2) + b*d - c**2 + f*(a**2*d - 2*a*b*c + b**3))/(a**3*e - 2*a**2*b*d - a**2*c**2 + 3*a*b**2*c - a*c*e + a*d**2 - b**4 + b**2*e - 2*b*c*d + c**3) q1 = (-a**3*b + a**2*d + a**2*e*f - a*b*d*f - a*c**2*f - b**3 + b**2*c*f + b*e - c*d)/(a**3*e - 2*a**2*b*d - a**2*c**2 + 3*a*b**2*c - a*c*e + a*d**2 - b**4 + b**2*e - 2*b*c*d + c**3) q2 = (-a**2*e + 2*a*b*d + a*(a**2*c - a*b**2 + b*d - c**2) - b**2*c + c*e - d**2 + f*(a*b*e - a*c*d - b**2*d + b*c**2))/(a**3*e - 2*a**2*b*d - a**2*c**2 + 3*a*b**2*c - a*c*e + a*d**2 - b**4 + b**2*e - 2*b*c*d + c**3) q3 = (-a*b*e + a*c*d - a*(a**2*d - 2*a*b*c + b**3) + b**2*d - b*c**2 + f*(-a*c*e + a*d**2 + b**2*e - 2*b*c*d + c**3))/(a**3*e - 2*a**2*b*d - a**2*c**2 + 3*a*b**2*c - a*c*e + a*d**2 - b**4 + b**2*e - 2*b*c*d + c**3) return residue([0,0,1,p3,p2],[-gamma(-alf), -gamma(-alf)*q3, -gamma(-alf)*q2, -gamma(-alf)*q1, -gamma(-alf)*q0])
def gpa8_13_4_ml_pf(alf, bet): coeff = gpa8_13_4_ml_coff(alf, bet) num = [ 1, coeff[6, 0], coeff[5, 0], coeff[4, 0], coeff[3, 0], coeff[2, 0], coeff[1, 0], coeff[0, 0] ] denum = gamma(bet - alf) * np.array([ 1, coeff[14, 0], coeff[13, 0], coeff[12, 0], coeff[11, 0], coeff[10, 0], coeff[9, 0], coeff[8, 0], coeff[7, 0] ]) return residue(num, denum)
def _order(self): H = self.H na = np.zeros((self.ny, self.nu), dtype='int') for i in range(self.ny): for j in range(self.nu): try: b = H[i, j].num a = H[i, j].den _, p, _ = signal.residue(b, a) na[i, j] = (p != 0).sum() except: na[i, j] = 0 return na
def LinearRecurrenceSequence(name, size, a, b, c=[1], d=[]): print('Name: {}\n'.format(name)) print( 'Type: {}'.format('non-homogeneous' if len(d) > 0 else 'homogeneous')) print('Degree: {}'.format(len(a))) print() print('Recurrence coefficients') print('a: ', a) print('Initial conditions') print('b: ', b) print() if len(d) != 0: print('Non-homogeneous rational polynomials') print('QI:') print(np.poly1d(c)) print('PI:') print(np.poly1d(d)) print() P, Q = CreateG(a, b, c, d) print('Generating function') print('P:') print(P) print('Q:') print(Q) r, p, h = signal.residue(P.c, Q.c) print() print('Partial fraction expantion') print('residue (r) : ') print(r) print('poles: (eta) : ') print(p) print('remainder (h): ') print(h) print() if len(d) > 0 and len(h) == 1 and h[0] == 0: print('Can be reduce to homogeneous type') print('Recurrence coefficients') print('a: ', [-x for x in Q.c[-2::-1]]) print('Initial conditions') print( 'b: ', [EvalCloseForm(r, p, h, n, format='real') for n in range(len(p))]) print() print('Sequence ({})'.format(size)) for n in range(size): print(EvalCloseForm(r, p, h, n), end=', ') print() print() print('------------------------') print()
def coupling_N2(F, P, E, eps, eps_R): """Function to evaluate the (N+2,N+2) coupling matrix.""" F = s_to_w(F); P = s_to_w(P); E = s_to_w(E) nfz = len(P) - 1; N = len(E) - 1 const_mult = np.conjugate(eps) / eps * (-1) ** nfz EF_plus = E + F / eps_R EF_plus_conj = const_mult * EF_plus.conj() EF_minus = E - F / eps_R EF_minus_conj = const_mult * EF_minus.conj() y11_Num = 1j * (EF_minus + EF_minus_conj) y21_Num = -2j * P / eps y_Den = EF_plus - EF_plus_conj # The function "signal.residue" takes only 1D arrays! resid11, poles11, const11 = signal.residue(y11_Num[:, 0], y_Den[:, 0]) resid21, poles21, const21 = signal.residue(y21_Num[:, 0], y_Den[:, 0]) MSk = np.sqrt(resid11); lambdk = -poles11; MLk = resid21 / MSk JSL = -const21 M = np.zeros((N + 2, N + 2), dtype=complex) M[0, 1:N + 1] = np.reshape(MSk, (-1, N)) M[-1, 1:N + 1] = np.reshape(MLk, (-1, N)) M[:, 0] = M[0, :]; M[:, -1] = M[-1, :] M[0, -1] = JSL[0]; M[-1, 0] = JSL[0] diag1 = np.diag(lambdk); M[1:N + 1, 1:N + 1] = diag1 return M
def find_exp_decay(t, data, n, p0=None, t0=None, removebg=True): background = 0 if removebg: background = np.median(data[-1000:]) data = data - background if p0 is None: p0 = 1 / (t[np.argmin(abs(data - max(data) / 2))] - t[0]) print('p0 : %.4e' % p0) if t0 is None: t0 = t[0] dis = Calculate_di(t - t0, data, p0, n) bis = Calculate_bi(dis) ais = Calculate_ai(bis, dis) A, gamma, _ = signal.residue(np.flip(ais), np.concatenate((np.flip(bis), [1]))) gamma = abs(gamma) - p0 # error = np.sum((decay(t,A,gamma,t0,background)-(data+background))**2/(data+background)) error = R2(data + background, decay(t, A, gamma, t0, background)) return A, gamma, t0, background, error
def debug_plot(self, h_step, dt, ps, zs, scale): t = np.array(range(len(h_step))) * dt zs = zs[np.isfinite(zs)] ps = ps[np.isfinite(ps)] from scipy.signal import residue r, p, k = residue(np.poly(zs), np.poly(ps)) h_step_est = np.zeros(h_step.shape) for coef, pole in zip(r, p): h_step_est = h_step_est + scale * coef * np.exp(pole*t) print('Added coef', scale*coef, 'for pole', pole) plt.figure() plt.plot(t, h_step, '--+') plt.plot(t, h_step_est, '-+') plt.legend(['Step response', 'Fit']) plt.grid() plt.show() pass
print(fs) cutoff_freq = 4000.0 Wn = 1.5 * cutoff_freq / sampl_freq b, a = signal.butter(order, Wn, 'low') output_signal = signal.filtfilt(b, a, input_signal) sf.write('Sound_with_Reduced_Noise.wav', output_signal, fs) input_signal, ft = sf.read('Sound_with_Reduced_Noise.wav') print(ft) r, p, k = signal.residue(b, a, tol=0.001, rtype='avg') print(r) print(p) print(k) print(b) x = range(500) plt.xlim(0, 500) hn = [0] for j in range(500): t = 0 for i in range(4): t += r[i] * p[i]**j hn.append(t) plt.plot(hn)
def compute_RationalApproximation_AAA_new(alpha, beta=None, MaxOrder=100, tol = 1.e-12, nPoints = 1000, verbose=False): # deal with divisions by zero np.seterr(divide='ignore', invalid='ignore') if beta: func = lambda x: x**(1-alpha) * (x-1)**(-beta) else: func = lambda x: x**(1-alpha) M = nPoints Z = np.linspace(1+1.e-3, 1000, M).reshape([M,1]) M = Z.size F = func(Z) SF = scipy.sparse.spdiags(F.flatten(),0,M,M) J = np.arange(M) z = np.array([]).reshape([0,1]) f = np.array([]).reshape([0,1]) C = np.array([]).reshape([M,0]) errvec = np.array([]) R = np.mean(F)*np.ones_like(F) for m in range(MaxOrder): j = np.argmax(np.abs(F-R)) z = np.vstack([z, Z[j]]) f = np.vstack([f, F[j]]) J = J[J!=j] C = np.hstack([ C, 1/(Z-Z[j]) ]) Sf = np.diag(f.flatten()) A = SF @ C - C @ Sf U,S,V = scipy.linalg.svd(A[J,:]) w = V[m,:].reshape([-1,1]) N = C @ (w*f) D = C @ w R[:] = F R[J] = N[J]/D[J] err = np.linalg.norm(F-R, ord=np.inf) errvec = np.append(errvec, err) if verbose: print(err) if err <= tol: break if verbose: print('degree',m) m = w.size B = np.eye(m+1) B[0,0] = 0 E = np.block([ [ 0, w.reshape([1,m]) ], [ np.ones([m,1]), np.diag(z.flatten()) ] ]) pol = scipy.linalg.eig(E,B, left=False, right=False) pol = pol[~np.isinf(pol)] E = np.block([ [ 0, (w*f).reshape([1,m]) ], [ np.ones([m,1]), np.diag(z.flatten()) ] ]) zer = scipy.linalg.eig(E,B, left=False, right=False) zer = zer[~np.isinf(zer)] assert( np.all(np.isclose(np.imag(zer),0)) ) assert( np.all(np.isclose(np.imag(pol),0)) ) pc0 = np.sum(w*f) pd0 = np.sum(w) pc = np.poly(zer) pd = np.poly(pol) pd = np.append(pd, 0) c, d, k = residue(pc, pd) assert(all(k==0)) d = -d c *= pc0/pd0 # x = np.linspace(100, 1000, 10000) # plt.plot(x, [ np.sum(c/(z+d)) - func(z)/z for z in x] ) # plt.show() return c, d
plt.plot(t,y) plt.grid(True) plt.ylabel('y(t)') plt.title('Hand Solved y(t)') plt.subplot(2,1,2) plt.plot(tout, yout) plt.grid(True) plt.ylabel('y(t)') plt.title('y(t) using sig.step()') plt.show(); denH = [1,10,24,0] print('Part 1:') r,p,_ = sig.residue(numH, denH, tol=1e-3) print('r='+str(r)) print('') print('p='+str(p)) ##-----------------------------Part 2 -----------------------------------## print('') print('Part 2:') numH = [0,0,0,0,0,25250] denH = [1,18,218,2036,9085,25250,0] r,p,_ = sig.residue(numH, denH, tol=1e-3) print('r='+str(r)) print('')
input_signal , fs = sf.read('Sound_Noise.wav') sampl_freq = fs order = 4 cutoff_freq = 4000.0 Wn = 2*cutoff_freq/sampl_freq b,a = signal.butter(order,Wn,'low') output_signal = signal.filtfilt(b,a,input_signal) b = b[::-1] a = a[::-1] r, p,k = signal.residue(b,a) n = np.linspace(0,30) h = [] for i in range(0,30): s = 0 for j in range(0,order): s = s - r[j]/(p[j]**(i+1)) if (i==0): s=s+k[0] h.append(np.real(s)) y1=np.convolve(input_signal,h) y = [] for i in range(0,input_signal.size): s = 0
tout, yout = sig.step((num, den), T=t) plt.figure(figsize=(10, 7)) plt.plot(tout, yout) plt.grid() plt.xlabel('t [s]') plt.ylabel('x(t)') plt.title('Python sig.step') #%% Part 1 Task 3 num = [1, 6, 12] den = [1, 10, 24, 0] [R, P, _] = sig.residue(num, den) print("R1 =", R, "\nP1 = ", P) #%% Part 2 Task 1 num = [25250] den = [1, 18, 218, 2036, 9085, 25250, 0] [R, P, _] = sig.residue(num, den) print("\nR2 =", R, "\nP2 = ", P) #%% Part 2 Task 2 def cos2_method(R, P, t): y = 0
plt.subplot(2,1,2) plt.plot(tout, yout) plt.grid() plt.ylabel('y(t), python') plt.ylim([0,1]) plt.xlabel('Time') plt.show() num = [1,6,12] den = [1, 10,24,0] R, P, _ = sig.residue(num,den) print (R,P) #%%Part 2 num = [25250] den = [1,18,218,2036,9085,25250,0] R, P, _ = sig.residue(num,den) print (R,P)
def partial_fraction_expansion(b, a): "Return the pole, residue, and polynomial of a partial fraction expansion" return signal.residue(b, a)
plt.xlabel('time (s)') plt.show() tout2, yout2 = sig.step((num, den), T=t) myFigSize = (10, 10) plt.figure(figsize=myFigSize) plt.plot(tout2, yout2) plt.grid(True) plt.ylabel('Coded Step Response') plt.title('Part 1') plt.xlabel('time (s)') plt.show() num2 = [1, 6, 12] den2 = [1, 10, 24, 0] [R, P, _] = sig.residue(num2, den2) #partial fraction expansion print(R) print(P) #---------------------------Part 2-------------------------------------------# num3 = [25250] den3 = [1, 18, 218, 2036, 9085, 25250, 0] [R, P, _] = sig.residue(num3, den3) print(R) print(P) def cosine(R, P, t): y = np.zeros(t.shape) for i in range(len(R)): #goes through entire range of fraction expansion a = P[i].real #real part using alpha variable
def find_sysd_impulse(Ts,sys): #Find the discrete-time transfer function for the impulse invariant method #Partial Fraction Decomposition N = sys.num[0][0].tolist() D = sys.den[0][0].tolist() R,P,k = signal.residue(N,D) valc = abs(sys.horner(0)[0][0]) r = [] p = [] for l in range(len(P)): r.append(R[l]) p.append(P[l]) sysd = 0 #Construct transfer function z = Symbol('z') k = 0 poles = [] while k < len(p): b = p[k] a = exp(b*Ts) number = p.count(b) for t in range(number): poles.append(a) if number == 1: c = r[k] new = (Ts*c*z)/(z-a) sysd = sysd + new if number == 2: c1 = r[k] c2 = r[k+1] new = (Ts*c1*z)/(z-a) + ((Ts**2)*c2*a*z)/(z**2 -2*a*z+a**2) sysd = sysd + new if number == 3: c1 = r[k] c2 = r[k+1] c3 = r[k+2] new = (Ts*c1*z)/(z-a) + ((Ts**2)*c2*a*z)/(z**2 -2*a*z+a**2) + ((Ts**3)*c3*a*(z**2)*(1/2) + (Ts**3)*(1/2)*c3*(a**2)*z)/((z**3)-a*(z**2)+(a**2)*z-(a**3)) sysd = sysd + new if number == 4: c1 = r[k] c2 = r[k+1] c3 = r[k+2] c4 = r[k+3] new = (Ts*c1*z)/(z-a) + ((Ts**2)*c2*a*z)/(z**2 -2*a*z+a**2) + ((Ts**3)*c3*a*(z**2)*(1/2) + (Ts**3)*(1/2)*c3*(a**2)*z)/((z**3)-a*(z**2)+(a**2)*z-(a**3)) + ((Ts**4)*(1/6)*c4*a*(z**3)+4*(Ts**4)*(1/6)*c4*(a**2)*(z**2)+(Ts**4)*(1/6)*c4*(a**3)*z)/((z**4)-4*a*(z**3)+6*(a**2)*(z**2)-4*z*(a**3)+(a**4)) sysd = sysd + new k = k + number func = sysd gfun = sysd #Compute gain for n in range(len(poles)): func = func * (z-poles[n]) zeros = solve(func, z) num,den = signal.zpk2tf(zeros,poles,1) num = num.tolist() den = den.tolist() for k in range(len(num)): try: num[k] = float(num[k]) except: num[k] = num[k] for k in range(len(den)): try: den[k] = float(den[k]) except: den[k] = den[k] sysd = TransferFunction(num,den) gain = gfun.subs(z,1)/sysd.horner(1)[0][0] #Construct final version num,den = signal.zpk2tf(zeros,poles,gain) num = num.tolist() den = den.tolist() for k in range(len(num)): try: num[k] = float(num[k]) except: num[k] = num[k] for k in range(len(den)): try: den[k] = float(den[k]) except: den[k] = den[k] sysd = TransferFunction(num,den,Ts) return sysd
def rfp(frf, omega, denom_order, numer_order): """Computes estimates for the modal parameters of the given FRF, in the frequency range given by omega, utilizing polynomials of order "numer_order" and "denom_order", and following the RFP method as described by Richardson and Formenti [1]. Arguments: frf (numpy complex array): - Frequency Response Function vector (receptance). omega (numpy array): - Angular velocity range (rad/s) - denom_order (int): - Order of the denominator polynomial. - numer_order (int): - Order of the numerator polynomial. Returns: modal_params (array list): - Modal parameter for the estimated FRF Modal parameter list: [freq_n, xi_n, modal_mag_n, modal_ang_n] alpha (numpy complex array): - Estimated receptance FRF in the given frequency range. [1] Richardson, M. H. & Formenti D. L. "Parameter estimation from frequency response measurements using rational fraction polynomials", 1st IMAC Conference, Orlando, FL, 1986. """ omega_norm = omega / np.max(omega) # omega normalization m = numer_order # number of polynomial terms in numerator n = denom_order # number of polynomial terms in denominator d = np.zeros(n + 1) # Orthogonal denominator polynomial coefficients # computation of Forsythe orthogonal polynomials Phi, Gamma_phi = forsythe_polys_rfp(omega_norm, np.ones(len(omega_norm)), m) Theta, Gamma_theta = forsythe_polys_rfp(omega_norm, np.abs(frf)**2, n) T = np.diag(frf) @ Theta[:, :-1] W = frf * Theta[:, -1] X = -2 * np.real(Phi.T.conj() @ T) H = 2 * np.real(Phi.T.conj() @ W) d[:-1] = -np.linalg.inv(np.eye(X.shape[1]) - X.T @ X) @ X.T @ H d[-1] = 1 c = H - X @ d[:-1] # Orthogonal numerator polynomial coefficients # calculation of the estimated FRF (alpha) numer = Phi @ c denom = Theta @ d alpha = numer / denom a = np.flipud(Gamma_phi @ c) # Standard polynomial numerator coefficients b = np.flipud( Gamma_theta @ d) # Standard polynomial denominator coefficients # Calculation of the poles and residues res, pol, _ = signal.residue(a, b) residues = res[::2] * np.max(omega) poles = pol[::2] * np.max(omega) freq_n = np.abs(poles) / 2 / np.pi # Natural frequencies (rad/sec) xi_n = -np.real(poles) / np.abs(poles) # Damping ratios modal_const = 1j * 2 * residues * np.imag(poles) modal_mag_n = np.abs(modal_const) # Modal constant magnitude modal_ang_n = np.angle(modal_const) # Modal constant phase modal_params = [freq_n, xi_n, modal_mag_n, modal_ang_n] # Modal Parameters return modal_params, alpha
def grfp_parameters(frf, omega, denom, denom_coeff, numer_order): """Computes an estimate of the modal parameters for each of the FRFs given by the columns of "frf", which correspond to the frequency range given by omega, following the GRFP method described by Richardson and Formenti [1]. It is assumed that "numer_order" is the order of the numerator polynomial, and that the common denominator polynomial shared by all the FRFs is given by "denom", formed by the coefficients "denom_coeff". Arguments: - frf (numpy complex array): - Frequency Response Function matrix. Each column contains the FRF for a particular DOF, with all of them corresponding the reference or input DOF. - omega (numpy array): - Angular velocity range (rad/s). - denom (numpy array): - Common denominator polynomial shared by all the FRFs. - denom_coeff (numpy array): - Coefficients that form the "denom" polynomial. - numer_order (int): - Order of the numerator polynomial. Returns: modal_params (array list): - Modal parameter for the estimated FRF Modal parameter list: [freq_n, xi_n, modal_mag_n, modal_ang_n] [1] Richardson, M. H. & Formenti, D. L. "Global curve fitting of frequency response measurements using the Rational Fraction Polynomial method", 3rd IMAC Conference, Orlando, FL, 1985. """ m = numer_order n_dof = frf.shape[1] # number of frf measurements (degrees of freedom) w_norm = omega / np.max(omega) # normalized angular frequency range w_j = 1j * w_norm # complex normalized angular frequency range total_poles = len( denom_coeff ) - 1 # number of residues and poles expected in the solution c = np.zeros( (m + 1, n_dof)) # orthogonal numerator polynomial coefficients # standard numerator polynomial coefficients numer_coef = np.zeros((m + 1, n_dof), dtype=complex) numer = np.zeros((len(w_norm), n_dof), dtype=complex) # numerator polynomials alpha = np.zeros((len(w_norm), n_dof), dtype=complex) # FRF estimations residues_norm = np.zeros((total_poles, n_dof), dtype=complex) # frecuency-normalized residues poles_norm = np.zeros((total_poles, n_dof), dtype=complex) # frequency-normalized poles Z, Gamma_phi = forsythe_polys_rfp(w_norm, np.abs(1 / denom)**2, m) X = np.diag(1 / denom) @ Z for dof in range(n_dof): c[:, dof] = 2 * np.real(X.conj().T @ frf[:, dof]) numer_coef[:, dof] = np.flipud(Gamma_phi @ c[:, dof]) numer[:, dof] = np.polyval(numer_coef[:, dof], w_j) alpha[:, dof] = numer[:, dof] / denom residues_norm[:, dof], poles_norm[:, dof], _ = signal.residue( numer_coef[:, dof], denom_coeff) xi_n_raw = -np.real(poles_norm * np.max(omega)) / np.abs( poles_norm * np.max(omega)) # modes with unitary damping coefficient are discarded physical_modes_idx = (abs(xi_n_raw[:, 0]) != 1) residues = residues_norm[physical_modes_idx, :][::2] * np.max(omega) poles = poles_norm[physical_modes_idx, :][::2] * np.max(omega) freq_n = np.abs(poles[:, 0]) / 2 / np.pi xi_n = -np.real(poles[:, 0]) / np.abs(poles[:, 0]) modal_const = 1j * 2 * residues * np.imag(poles) modal_mag_n = np.abs(modal_const) # Modal constant magnitude modal_ang_n = np.angle(modal_const) # Modal constant phase modal_params = [freq_n, xi_n, modal_mag_n, modal_ang_n] return modal_params, alpha
plt.grid(True) plt.ylabel('h(t)') plt.title('Step response of system') plt.subplot(2, 1, 2) plt.plot(t, y2) plt.grid(True) plt.ylabel('sig.step') #plt.title('') plt.show() H = ([1, 6, 12], [1, 10, 24, 0]) #partial fraction expansion of H (r, p, k) = sig.residue(H[0], H[1]) for i in range(len(r)): print('\n', '\nr[', i, ']=', r[i], '\np[', i, ']=', p[i], '\n') #Part 2 t = np.arange(0, 4.5 + stepsize, stepsize) d = [1, 18, 218, 2036, 9085, 25250] n = [25250] (Tr, y2) = sig.step((n, d), T=t) #plt.figure(figsize=fullsize_figure) plt.subplots_adjust(top=2, bottom=0)
#!/usr/bin/env python # -*- coding: utf-8 -*- from scipy.signal import residue print residue([2,5,3,6], [1,6,11,6])
# -*- coding: utf-8 -*- """ Created on Sat Dec 09 23:55:31 2017 @author: Rocky """ from scipy.signal import residue b = [2,0.8,0.5,0.3] a = [1,0.8,0.2] r,p,k=residue(b,a) x = [1,0,0,0,0,0,0,0,0,0,0] print ('Residues=', r) print ('Poles=',p) print ('Coefficients of the direct polynomial term=',k)
# print apart(Zs, Zs, s) # # #print ilt(apart(Xs, s), s, t) # #print ilt(apart(Ys, s), s, t) # #print ilt(apart(Zs, s), s, t) # # print ilt(Xs, s, t) x_num = [2, 1] x_den = [1, 5, 6] y_num = [5, 3, 2] y_den = [1, 3, 9, 7] z_num = [1] z_den = [2, 5, 0] x_residues, x_poles, _ = residue(x_num, x_den) y_residues, y_poles, _ = residue(y_num, y_den) z_residues, z_poles, _ = residue(z_num, z_den) print "Residues X: ", x_residues print "Poles X: ", x_poles print "Residues Y: ", y_residues print "Poles Y: ", y_poles print "Residues Z: ", z_residues print "Poles Z: ", z_poles tLen = 5 t = np.arange(-1, tLen, tStep) x = np.zeros(t.size) y = np.zeros(t.size) z = np.zeros(t.size)
def RFP(rec, omega, N): x = np.shape(omega) if x == 1: omega = np.transpose(omega) x = np.shape(rec) if x == 1: rec = np.transpose(rec) # normalizacja danych nom_omega = np.max(omega) omega = omega / nom_omega m = 2 * N - 1 # licznik n = 2 * N # mianownik # stworzenie wielomanów ortogonalnych [phimatrix, coeff_a] = orthogonal(rec, omega, 1, m) [thetamatrix, coeff_b] = orthogonal(rec, omega, 2, n) [x, y] = np.shape(phimatrix) Phi = phimatrix[:, 0:x] [x, y] = np.shape(thetamatrix) Theta = thetamatrix[:, 0:x] T = np.diag(rec) @ thetamatrix[:, 0:y - 1] W = rec * thetamatrix[:, y - 1] X = -2.0 * np.real(np.conj(np.transpose(Phi)) @ T) G = 2.0 * np.real(np.conj(np.transpose(Phi)) @ W) [x, y] = np.shape(X) d = -1.0 * np.linalg.inv(np.eye(x, y) - np.transpose(X) @ X) @ np.transpose(X) @ G C = G - X @ d D = np.append(d, 1.0) #dodanie jedynki na koniec append? alfa = np.zeros(len(omega), dtype=complex) # obliczenie aproksymowanej charakterystyki for i in range(len(omega)): numer = np.sum(np.transpose(C) * Phi[i, :]) denom = np.sum(np.transpose(D) * Theta[i, :]) alfa[i] = np.true_divide(numer, denom) A = coeff_a @ C A = np.transpose(A[::-1]) # standaryzowane wspolczynniki licznika B = coeff_b @ D B = np.transpose((B[::-1])) # standaryzowane wspolczynniki mianownika [R, P, K] = residue(A, B) # obliczenie reszt i biegunow # odrzucenie biegunow lezacych w zakresie ujemnych czestosci x = int(len(R)) ii = 0 Resi = np.zeros((int(x), 1), dtype=complex) Pole = np.copy(Resi) for i in range(int(x)): if P[i].imag >= 0: Resi[ii] = R[i] Pole[ii] = P[i] ii += 1 for i in range(int(x) - 1, -1, -1): if np.abs(Pole[i]) == 0: Resi = np.delete(Resi, i) Pole = np.delete(Pole, i) # denormalizacja danych Resi = Resi * nom_omega Pole = Pole * nom_omega return Resi, Pole, alfa
y = np.array([-1, 2, 2, -2, 2, 2]) solve = np.linalg.solve(X, y) print(solve) print(X @ solve) solve = slau(X, y) print(solve) print(X @ solve) fun = lambda x: (2.7 * x**2 + 3 * x + 3.7) / (6 * x**4 - 7 * x**2 + 2 * x + 12) t = np.arange(start=-10, stop=10) plt.plot(t, fun(t)) plt.xlabel('$X$') plt.ylabel('$y$') plt.title('$ \\frac{2.7x^2 + 3x + 3.7}{6x^4 - 7x^2 + 2x + 12}$') plt.show() ftt = np.fft.fft(fun(t)) plt.plot(t, np.real(ftt)) plt.plot(t, np.abs(ftt)) plt.plot(t, np.imag(ftt)) plt.legend(['real', 'abs', 'imag']) plt.show() up = np.poly1d([1, 0.652, 0.652, 1]) down = np.poly1d([1, -1.145, 0.727, -0.1205]) up_root = np.roots(up) down_root = np.roots(down) print(residue(down_root, up_root))