comparison trunk/matlab/bmm/carfac/Carfac.py @ 531:acd08b2ff774

(none)
author alan.strelzoff
date Sun, 11 Mar 2012 00:49:12 +0000
parents c3c85000f804
children 9b478420cbe2
comparison
equal deleted inserted replaced
530:fb60ea429bb8 531:acd08b2ff774
1 # Carfac.py - Cochlear filter model based on Dick Lyons work. This material taken from his Hearing book (to be published) 1 # Carfac.py - Cochlear filter model based on Dick Lyons work. This material taken from his Hearing book (to be published)
2 # Author: Al Strelzoff 2 # Author: Al Strelzoff: strelz@mit.edu
3 3
4 from numpy import cos, sin, tan, sinh, arctan, pi, e, real,imag,arccos,arcsin,arctan2,log10,log 4 # The point of this file is to extract some of the formulas from the book and practice with them so as to better understand the filters.
5 5 # The file has been written and tested for python 2.7
6 from pylab import figure, clf, plot,loglog, xlabel, ylabel, xlim, ylim, title, grid, axes, axis, show 6
7 from numpy import cos, sin, pi, e, real,imag,arctan2
8
9
10 from pylab import figure, plot,loglog, title, axis, show
7 11
8 fs = 22050.0 # sampling rate 12 fs = 22050.0 # sampling rate
9 Nyq = fs/2.0 # nyquist frequency
10 13
11 14
12 # given a frequency f, return the ERB 15 # given a frequency f, return the ERB
13 def ERB_Hz(f): 16 def ERB_Hz(f):
14 # Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138 17 # Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138
41 # before we make the filters, let's plot the position of the frequencies and the values of ERB at each. 44 # before we make the filters, let's plot the position of the frequencies and the values of ERB at each.
42 45
43 fscale = [] 46 fscale = []
44 erbs = [] 47 erbs = []
45 48
46 figure(0) 49 figure(1)
47 for i in range(n_ch): 50 for i in range(n_ch):
48 51
49 f = pole_freqs[i] # the frequencies from the list 52 f = pole_freqs[i] # the frequencies from the list
50 ERB = ERB_Hz(f) # the ERB value at each frequency 53 ERB = ERB_Hz(f) # the ERB value at each frequency
51 fscale.append(f) 54 fscale.append(f)
66 69
67 70
68 71
69 # This filter class includes some methods useful only in design. They will not be used in run time implementation. 72 # This filter class includes some methods useful only in design. They will not be used in run time implementation.
70 # From figure 14.3 in Dick Lyon's book. 73 # From figure 14.3 in Dick Lyon's book.
74 # When translating to C++, this class will become a struct, and all the methods will be moved outside.
71 75
72 76
73 #########################################################The Carfac filter class################################################################################# 77 #########################################################The Carfac filter class#################################################################################
74 78
75 # fixed parameters 79 # fixed parameters
82 def __init__(self,f): 86 def __init__(self,f):
83 87
84 self.frequency = f 88 self.frequency = f
85 89
86 theta = 2.0 * pi * f/fs 90 theta = 2.0 * pi * f/fs
87
88 r = 1.0 - sin(theta) * min_zeta 91 r = 1.0 - sin(theta) * min_zeta
89
90
91 a = r * cos(theta) 92 a = r * cos(theta)
92 c = r * sin(theta) 93 c = r * sin(theta)
93 94 h = sin(theta)
95 g = (1.0 - 2.0 * a + r ** 2)/(1.0 - 2.0 * a + h * c + r ** 2)
94 96
95 h = c 97
96 98
97 g = 1.0/(1.0 + h * r * sin(theta) / (1.0 - 2.0 * r * cos(theta) + r ** 2)) 99 self.gh = g*h # no need to repeat in real time
98 100
99 # make all parameters properties of the class 101 # make all parameters properties of the class
100 self.a = a 102 self.a = a
101 self.c = c 103 self.c = c
102 self.r = r 104 self.r = r
116 118
117 119
118 # the total frequency magnitude of this filter including all the filters in front of this one 120 # the total frequency magnitude of this filter including all the filters in front of this one
119 self.HT = [] # this list will be filled by multiplying all the H's ahead of it together with its own (H) 121 self.HT = [] # this list will be filled by multiplying all the H's ahead of it together with its own (H)
120 122
121 123
122
123
124
125 124
126 # execute one clock tick. Take in one input and output one result. Execution semantics taken from fig. 14.3 125 # execute one clock tick. Take in one input and output one result. Execution semantics taken from fig. 14.3
127 # This execution model is not tested in this file. Here for reference. See the file Exec.py for testing this execution model. This is the main run time method. 126 # This execution model is not tested in this file. Here for reference. See the file Exec.py for testing this execution model. This is the main run time method.
128 def input(self,X): 127 def input(self,X):
129 128
130 # recover the class definitions of these variables. These statements below take up zero time at execution since they are just compiler declarations. 129 # recover the class definitions of these variables. These statements below take up zero time at execution since they are just compiler declarations.
131 130 # computation below is organized as some loads, followed by 3 2x2 multiply accumulates
131 # Note: this function is not exercised in this file and is here only for reference
132 132
133 a = self.a 133 a = self.a
134 c = self.c 134 c = self.c
135 h = self.h
136 g = self.g 135 g = self.g
136 gh = self.gh
137 z1 = self.z1 # z1 is the lower storage in fig. 14.3 137 z1 = self.z1 # z1 is the lower storage in fig. 14.3
138 z2 = self.z2 138 z2 = self.z2
139 139
140 # calculate what the next value of z1 will be, but don't overwrite current value yet. 140 # calculate what the next value of z1 will be, but don't overwrite current value yet.
141 next_z1 = (a * z1) - (c * z2) # Note: view this as next_z1 = a*z1 + (-c*z2) so that it is a 2 element multiply accumulate 141 next_z1 = (a * z1) + (c * z2) # Note: it is a 2 element multiply accumulate. compute first so as not to have to do twice.
142 # the output Y 142 # the output Y
143 Y = g * (X + h * next_z1) # Note: reorganize this as Y = g*X + (g*h) * next_z1 g*h is a precomputed constant so then the form is a 2 element multiply accumulate. 143 Y = g * X + gh * next_z1 # Note: organized as a 2 element multiply accumulate.
144 144
145 #stores 145 #stores
146 z2 = (a * z2) + (c * z1) #Note: this is a 2 element multiply accumulate 146 self.z2 = X + (a * z2) - (c * z1) #Note: this is a 2 element multiply accumulate
147 z1 = next_z1 147 self.z1 = next_z1
148 148
149 return Y # The output 149 return Y # The output
150 150
151 # complex frequency response of this filter at frequency w. That is, what it contributes to the cascade 151 # complex frequency response of this filter at frequency w. That is, what it contributes to the cascade
152 # this method is used for test only. It finds the frequency magnitude. Not included in run time filter class. 152 # this method is used for test only. It finds the frequency magnitude. Not included in run time filter class.
156 c = self.c 156 c = self.c
157 g = self.g 157 g = self.g
158 h = self.h 158 h = self.h
159 r = self.r 159 r = self.r
160 z = e ** (complex(0,w)) # w is in radians so this is z = exp(jw) 160 z = e ** (complex(0,w)) # w is in radians so this is z = exp(jw)
161 return g * (1.0 + (h*c*z)/(z**2 - 2.0*a*z + r**2 )) # from page ?? of Lyon's book. 161 return g * (1.0 + (h*c*z)/(z**2 - 2.0*a*z + r**2 ))
162 162
163
164 # Note: to get the complex frequency response of this filter at frequency w, get Hw(w) and then compute arctan2(-imag(Hw(w))/-real(Hw(w)) + pi
165
166
167
168 163
169 164
170 ######################################################End of Carfac filter class######################################################################## 165 ######################################################End of Carfac filter class########################################################################
171 166
172 # instantiate the filters 167 # instantiate the filters
182 177
183 178
184 # sweep parameters 179 # sweep parameters
185 steps = 1000 180 steps = 1000
186 181
187 sum = [] # array to hold the magnitude sum 182 figure(2)
188 for i in range(steps): sum.append( 0.0 ) 183 title('CarFac individual filter frequency response')
189 184 # note: the scales are linear, not logrithmic as in the book
190 figure(1) 185
191 title('CarFac frequency response')
192 186
193 for i in range(n_ch): 187 for i in range(n_ch):
194 filter = Filters[i] 188 filter = Filters[i]
195 # plotting arrays 189 # plotting arrays
196 u = [] 190 u = []
197 v = [] 191 v = []
198 # calculate the frequency magnitude by stepping the frequency in radians 192 # calculate the frequency magnitude by stepping the frequency in radians
199 for j in range(steps): 193 for j in range(steps):
200 194
201 w = pi * float(j)/steps 195 w = pi * float(j)/steps
202 u.append(w) 196 u.append(w)
203 mag = filter.Hw(w) # freq mag at freq w 197 mag = filter.Hw(w) # freq mag at freq w
204 filter.H.append(mag) # save for later use 198 filter.H.append(mag) # save for later use
205 filter.HT.append(mag) # will be total response of cascade to this point after we do the multiplication in a step below 199 filter.HT.append(mag) # will be total response of cascade to this point after we do the multiplication in a step below
206 v.append(real(mag)) # y plotting axis 200 v.append(abs(mag)) # y plotting axis
207 sum[j]+= mag
208 201
209 202
210 plot(u,v) 203 plot(u,v)
211 204
212
213
214 figure(2)
215 title('Summed frequency magnitudes')
216 for i in range(steps): sum[i] = abs(sum[i])/n_ch
217 plot(u,sum)
218 205
219 # calculate the phase response of the same group of filters 206 # calculate the phase response of the same group of filters
220 figure(3) 207 figure(3)
221 title('Filter Phase') 208 title('Carfac individual filter Phase lag')
222 209
223 210
224 for i in range(n_ch): 211 for i in range(n_ch):
225 filter = Filters[i] 212 filter = Filters[i]
226 213
227 u = [] 214 u = []
228 v = [] 215 v = []
229 for j in range(steps): 216 for j in range(steps):
230 x = float(j)/Nyq 217 x = pi * float(j)/steps
231 218
232 u.append(x) 219 u.append(x)
233 220
234 mag = filter.H[j] 221 mag = filter.H[j]
235 phase = arctan2(-imag(mag),-real(mag)) + pi # this formula used to avoid wrap around 222 phase = arctan2(-imag(mag),-real(mag)) - pi # this formula used to avoid wrap around
236 223
237 v.append(phase) # y plotting axis 224 v.append(phase) # y plotting axis
238 225
239 plot(u,v) 226 plot(u,v)
240 227 axis([0.0,pi,-3.0,0.05])
241 228
242 229
243 # calulate and plot cascaded frequency response and summed magnitude 230 # calulate and plot cascaded frequency response
244 sum = [] # array to hold the magnitude sum
245 for i in range(steps): sum.append( 0.0 )
246
247 231
248 figure(4) 232 figure(4)
249 title('CarFac Cascaded frequency response') 233 title('CarFac cascaded filter frequency response')
250 234
251 235
252 for i in range(n_ch-1): 236 for i in range(n_ch-1):
253 237
254 filter = Filters[i] 238 filter = Filters[i]
256 240
257 241
258 u = [] 242 u = []
259 v = [] 243 v = []
260 for j in range(steps): 244 for j in range(steps):
261 u.append(float(j)/Nyq) 245 w = pi * float(j)/steps
246 u.append(w)
262 mag = filter.HT[j] * next.HT[j] 247 mag = filter.HT[j] * next.HT[j]
263 filter.HT[j] = mag 248 next.HT[j] = mag
264 v.append(real(mag)) 249 v.append(abs(mag))
265 sum[j]+= mag
266 250
267 251
268 plot(u,v) 252 plot(u,v)
269 253
270 254
271 255
256 # calculate and plot the phase responses of the cascaded filters
257
272 figure(5) 258 figure(5)
273 title('Summed cascaded frequency magnitudes') 259 title('Carfac cascaded filter Phase lag')
274 for i in range(steps): sum[i] = abs(sum[i])/n_ch 260
275 plot(u,sum) 261
276 262 for i in range(n_ch):
277 # calculate and plot the phase responses of the cascaded filters 263
278
279 figure(6)
280 title('Filter cascaded Phase')
281
282
283 for i in range(n_ch):
284 filter = Filters[i] 264 filter = Filters[i]
285 265
286 266
287 u = [] 267 u = []
288 v = [] 268 v = [] # store list of phases
269 w = [] # second copy of phases needed for phase unwrapping
289 for j in range(steps): 270 for j in range(steps):
290 x = float(j)/Nyq 271 x = pi * float(j)/steps
291 272
292 u.append(x) 273 u.append(x)
293 mag = filter.HT[j] 274 mag = filter.HT[j]
294 phase = arctan2(-imag(mag),-real(mag)) + pi 275 a = imag(mag)
295 276 b = real(mag)
296 v.append(phase) # y plotting axis 277 phase = arctan2(a,b)
297 278
298 279 v.append(phase)
299 280 w.append(phase)
300 281
301 plot(u,v) 282 # unwrap the phase
302 283
284
285 for i in range(1,len(v)):
286 diff = v[i]-v[i-1]
287 if diff > pi:
288 for j in range(i,len(w)):
289 w[j] -= 2.0 * pi
290 elif diff < -pi:
291 for j in range(i,len(w)):
292 w[j] += 2.0 * pi
293
294 else: continue
295
296
297 plot(u,w)
298 axis([0.0,pi,-25.0,0.05])
303 show() 299 show()
304 300