annotate matlab/bmm/carfac/Carfac.py @ 471:40cc1c0f20a8

Added a calculation (last plot) of the group delay. The previous check in fixed a couple of bugs.
author alan.strelzoff
date Sun, 11 Mar 2012 22:45:36 +0000
parents 8e8381785b2b
children
rev   line source
alan@461 1 # Carfac.py - Cochlear filter model based on Dick Lyons work. This material taken from his Hearing book (to be published)
alan@470 2 # Author: Al Strelzoff: strelz@mit.edu
alan@461 3
alan@470 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.
alan@470 5 # The file has been written and tested for python 2.7
alan@461 6
alan@471 7 from numpy import cos, sin, pi, e, real,imag,arctan2,log10
alan@470 8
alan@470 9
alan@470 10 from pylab import figure, plot,loglog, title, axis, show
alan@461 11
alan@461 12 fs = 22050.0 # sampling rate
alan@461 13
alan@461 14
alan@461 15 # given a frequency f, return the ERB
alan@461 16 def ERB_Hz(f):
alan@461 17 # Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138
alan@461 18 return 24.7 * (1.0 + 4.37 * f / 1000.0)
alan@461 19
alan@461 20
alan@461 21 # ERB parameters
alan@461 22 ERB_Q = 1000.0/(24.7*4.37) # 9.2645
alan@461 23 ERB_break_freq = 1000/4.37 # 228.833
alan@461 24
alan@461 25 ERB_per_step = 0.3333
alan@461 26
alan@461 27 # set up channels
alan@461 28
alan@461 29 first_pole_theta = .78 * pi # We start at the top frequency.
alan@461 30 pole_Hz = first_pole_theta * fs / (2.0*pi) # frequency of top pole
alan@461 31 min_pole_Hz = 40.0 # bottom frequency
alan@461 32
alan@461 33 # set up the pole frequencies according to the above parameters
alan@461 34 pole_freqs = [] # empty list of pole frequencies to fill, zeroth will be the top
alan@461 35 while pole_Hz > min_pole_Hz:
alan@461 36 pole_Hz = pole_Hz - ERB_per_step * ERB_Hz(pole_Hz)
alan@461 37 pole_freqs.append(pole_Hz)
alan@461 38
alan@461 39 n_ch = len(pole_freqs) # n_ch is the number of channels or frequency steps
alan@461 40 print('num channels',n_ch)
alan@461 41
alan@461 42 # Now we have n_ch, the number of channels, so can make the array of filters by instantiating the filter class (see below)
alan@461 43
alan@461 44 # before we make the filters, let's plot the position of the frequencies and the values of ERB at each.
alan@461 45
alan@461 46 fscale = []
alan@461 47 erbs = []
alan@461 48
alan@470 49 figure(1)
alan@461 50 for i in range(n_ch):
alan@461 51
alan@461 52 f = pole_freqs[i] # the frequencies from the list
alan@461 53 ERB = ERB_Hz(f) # the ERB value at each frequency
alan@461 54 fscale.append(f)
alan@461 55 erbs.append(ERB)
alan@461 56
alan@461 57 # plot a verticle hash at each frequency:
alan@461 58 u = []
alan@461 59 v = []
alan@461 60 for j in range(5):
alan@461 61 u.append(f)
alan@461 62 v.append(10.0 + float(j))
alan@461 63
alan@461 64 plot(u,v)
alan@461 65
alan@461 66 loglog(fscale,erbs)
alan@461 67
alan@461 68 title('ERB scale')
alan@461 69
alan@461 70
alan@461 71
alan@461 72 # This filter class includes some methods useful only in design. They will not be used in run time implementation.
alan@461 73 # From figure 14.3 in Dick Lyon's book.
alan@470 74 # When translating to C++, this class will become a struct, and all the methods will be moved outside.
alan@461 75
alan@461 76
alan@461 77 #########################################################The Carfac filter class#################################################################################
alan@461 78
alan@461 79 # fixed parameters
alan@461 80 min_zeta = 0.12
alan@461 81
alan@461 82 class carfac():
alan@461 83
alan@461 84
alan@461 85 # instantiate the class (in C++, the constructor)
alan@461 86 def __init__(self,f):
alan@461 87
alan@461 88 self.frequency = f
alan@461 89
alan@461 90 theta = 2.0 * pi * f/fs
alan@461 91 r = 1.0 - sin(theta) * min_zeta
alan@461 92 a = r * cos(theta)
alan@461 93 c = r * sin(theta)
alan@470 94 h = sin(theta)
alan@470 95 g = (1.0 - 2.0 * a + r ** 2)/(1.0 - 2.0 * a + h * c + r ** 2)
alan@470 96
alan@470 97
alan@461 98
alan@470 99 self.gh = g*h # no need to repeat in real time
alan@461 100
alan@461 101 # make all parameters properties of the class
alan@461 102 self.a = a
alan@461 103 self.c = c
alan@461 104 self.r = r
alan@461 105 self.theta = theta
alan@461 106 self.h = h
alan@461 107 self.g = g
alan@461 108
alan@461 109
alan@461 110 # the two storage elements. Referring to diagram 14.3 on p.263, z2 is the upper storage register, z1, the lower
alan@461 111 self.z1 = 0.0
alan@461 112 self.z2 = 0.0
alan@461 113
alan@461 114
alan@461 115 # frequency response of this filter
alan@461 116 self.H = []
alan@461 117
alan@461 118
alan@461 119
alan@461 120 # the total frequency magnitude of this filter including all the filters in front of this one
alan@461 121 self.HT = [] # this list will be filled by multiplying all the H's ahead of it together with its own (H)
alan@461 122
alan@461 123
alan@461 124
alan@461 125 # execute one clock tick. Take in one input and output one result. Execution semantics taken from fig. 14.3
alan@461 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.
alan@461 127 def input(self,X):
alan@461 128
alan@461 129 # recover the class definitions of these variables. These statements below take up zero time at execution since they are just compiler declarations.
alan@470 130 # computation below is organized as some loads, followed by 3 2x2 multiply accumulates
alan@470 131 # Note: this function is not exercised in this file and is here only for reference
alan@461 132
alan@461 133 a = self.a
alan@461 134 c = self.c
alan@461 135 g = self.g
alan@470 136 gh = self.gh
alan@461 137 z1 = self.z1 # z1 is the lower storage in fig. 14.3
alan@461 138 z2 = self.z2
alan@461 139
alan@461 140 # calculate what the next value of z1 will be, but don't overwrite current value yet.
alan@470 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.
alan@461 142 # the output Y
alan@470 143 Y = g * X + gh * next_z1 # Note: organized as a 2 element multiply accumulate.
alan@461 144
alan@461 145 #stores
alan@470 146 self.z2 = X + (a * z2) - (c * z1) #Note: this is a 2 element multiply accumulate
alan@470 147 self.z1 = next_z1
alan@461 148
alan@461 149 return Y # The output
alan@461 150
alan@461 151 # complex frequency response of this filter at frequency w. That is, what it contributes to the cascade
alan@461 152 # this method is used for test only. It finds the frequency magnitude. Not included in run time filter class.
alan@461 153 def Hw(self,w):
alan@461 154
alan@461 155 a = self.a
alan@461 156 c = self.c
alan@461 157 g = self.g
alan@461 158 h = self.h
alan@461 159 r = self.r
alan@461 160 z = e ** (complex(0,w)) # w is in radians so this is z = exp(jw)
alan@470 161 return g * (1.0 + (h*c*z)/(z**2 - 2.0*a*z + r**2 ))
alan@461 162
alan@461 163
alan@461 164
alan@461 165 ######################################################End of Carfac filter class########################################################################
alan@471 166
alan@471 167
alan@461 168 # instantiate the filters
alan@461 169
alan@461 170 # n_ch is the number of filters as determined above
alan@461 171
alan@461 172 Filters = [] # the list of all filters, the zeroth is the top frequency
alan@461 173 for i in range(n_ch):
alan@461 174 f = pole_freqs[i]
alan@461 175 filter = carfac(f) # note: get the correct parameters for r and h from Dick's matlab script. Load them here from a table.
alan@461 176 Filters.append(filter)
alan@461 177
alan@461 178
alan@461 179
alan@461 180 # sweep parameters
alan@461 181 steps = 1000
alan@461 182
alan@470 183 figure(2)
alan@470 184 title('CarFac individual filter frequency response')
alan@470 185 # note: the scales are linear, not logrithmic as in the book
alan@461 186
alan@461 187
alan@461 188 for i in range(n_ch):
alan@461 189 filter = Filters[i]
alan@461 190 # plotting arrays
alan@461 191 u = []
alan@461 192 v = []
alan@461 193 # calculate the frequency magnitude by stepping the frequency in radians
alan@461 194 for j in range(steps):
alan@461 195
alan@470 196 w = pi * float(j)/steps
alan@461 197 u.append(w)
alan@461 198 mag = filter.Hw(w) # freq mag at freq w
alan@461 199 filter.H.append(mag) # save for later use
alan@461 200 filter.HT.append(mag) # will be total response of cascade to this point after we do the multiplication in a step below
alan@470 201 v.append(abs(mag)) # y plotting axis
alan@461 202
alan@461 203
alan@461 204 plot(u,v)
alan@461 205
alan@461 206
alan@461 207 # calculate the phase response of the same group of filters
alan@461 208 figure(3)
alan@470 209 title('Carfac individual filter Phase lag')
alan@461 210
alan@461 211
alan@461 212 for i in range(n_ch):
alan@461 213 filter = Filters[i]
alan@461 214
alan@461 215 u = []
alan@461 216 v = []
alan@461 217 for j in range(steps):
alan@470 218 x = pi * float(j)/steps
alan@461 219
alan@461 220 u.append(x)
alan@461 221
alan@461 222 mag = filter.H[j]
alan@470 223 phase = arctan2(-imag(mag),-real(mag)) - pi # this formula used to avoid wrap around
alan@461 224
alan@461 225 v.append(phase) # y plotting axis
alan@461 226
alan@461 227 plot(u,v)
alan@470 228 axis([0.0,pi,-3.0,0.05])
alan@461 229
alan@461 230
alan@470 231 # calulate and plot cascaded frequency response
alan@461 232
alan@461 233 figure(4)
alan@470 234 title('CarFac cascaded filter frequency response')
alan@461 235
alan@461 236
alan@461 237 for i in range(n_ch-1):
alan@461 238
alan@461 239 filter = Filters[i]
alan@461 240 next = Filters[i+1]
alan@461 241
alan@461 242
alan@461 243 u = []
alan@461 244 v = []
alan@461 245 for j in range(steps):
alan@470 246 w = pi * float(j)/steps
alan@470 247 u.append(w)
alan@461 248 mag = filter.HT[j] * next.HT[j]
alan@470 249 next.HT[j] = mag
alan@470 250 v.append(abs(mag))
alan@461 251
alan@461 252
alan@461 253 plot(u,v)
alan@461 254
alan@461 255
alan@461 256
alan@461 257 # calculate and plot the phase responses of the cascaded filters
alan@461 258
alan@470 259 figure(5)
alan@470 260 title('Carfac cascaded filter Phase lag')
alan@461 261
alan@461 262 for i in range(n_ch):
alan@470 263
alan@461 264 filter = Filters[i]
alan@461 265
alan@461 266
alan@461 267 u = []
alan@470 268 v = [] # store list of phases
alan@470 269 w = [] # second copy of phases needed for phase unwrapping
alan@461 270 for j in range(steps):
alan@470 271 x = pi * float(j)/steps
alan@461 272
alan@461 273 u.append(x)
alan@461 274 mag = filter.HT[j]
alan@470 275 a = imag(mag)
alan@470 276 b = real(mag)
alan@470 277 phase = arctan2(a,b)
alan@461 278
alan@470 279 v.append(phase)
alan@470 280 w.append(phase)
alan@461 281
alan@470 282 # unwrap the phase
alan@461 283
alan@461 284
alan@470 285 for i in range(1,len(v)):
alan@470 286 diff = v[i]-v[i-1]
alan@470 287 if diff > pi:
alan@470 288 for j in range(i,len(w)):
alan@470 289 w[j] -= 2.0 * pi
alan@470 290 elif diff < -pi:
alan@470 291 for j in range(i,len(w)):
alan@470 292 w[j] += 2.0 * pi
alan@470 293
alan@470 294 else: continue
alan@471 295
alan@471 296 # convert delay to cycles
alan@471 297 for i in range(len(w)):
alan@471 298 w[i] /= 2.0 * pi
alan@471 299
alan@470 300
alan@470 301 plot(u,w)
alan@471 302 axis([0.0,pi,-5.0,0.05])
alan@471 303
alan@471 304 # calculate group delay as cascaded lag/filter center frequency
alan@471 305
alan@471 306 figure(6)
alan@471 307 title('Carfac group delay')
alan@471 308
alan@471 309 for i in range(n_ch):
alan@471 310
alan@471 311 filter = Filters[i]
alan@471 312
alan@471 313
alan@471 314 u = []
alan@471 315 v = [] # store list of phases
alan@471 316 w = [] # second copy of phases needed for phase unwrapping
alan@471 317 for j in range(1,steps):
alan@471 318 x = pi * float(j)/steps
alan@471 319
alan@471 320 u.append(x)
alan@471 321 mag = filter.HT[j]
alan@471 322 a = imag(mag)
alan@471 323 b = real(mag)
alan@471 324 phase = arctan2(a,b)
alan@471 325
alan@471 326 v.append(phase)
alan@471 327 w.append(phase)
alan@471 328
alan@471 329
alan@471 330 for i in range(1,len(v)):
alan@471 331 diff = v[i]-v[i-1]
alan@471 332 if diff > pi:
alan@471 333 for j in range(i,len(w)):
alan@471 334 w[j] -= 2.0 * pi
alan@471 335 elif diff < -pi:
alan@471 336 for j in range(i,len(w)):
alan@471 337 w[j] += 2.0 * pi
alan@471 338
alan@471 339 else: continue
alan@471 340
alan@471 341 # calculate the group delay from: GroupDelay(n)=-[((ph(n+1)-ph(n-1))/(w(n+1)-w(n-1))]/2pi # w in radians
alan@471 342 # or gd[n] = - ((delta ph)/(delta w))/2pi
alan@471 343 # delta ph = w[n+1] - w[n-1]
alan@471 344 # delta w = pi* fs/steps # we divide up the frequency range fs into fractions of pi radians.
alan@471 345 # gd[n] = - steps(w[n+1] - w[n-1])/fs
alan@471 346
alan@471 347 A = -(float(steps))/(2 *fs) # note. could have calculated this once outside the filter loop
alan@471 348
alan@471 349 # gd[n] = A *(w[n+1] - w[n-1])
alan@471 350
alan@471 351 gd = [] # in radians
alan@471 352 gd.append(0.0) # pad to equalize size of u array, end points are then meaningless
alan@471 353 for n in range(1,len(w)-1):
alan@471 354 gd.append(A * (w[n+1] - w[n-1]))
alan@471 355
alan@471 356 gd.append(0.0)
alan@471 357
alan@471 358 plot(u,gd)
alan@471 359 axis([0.1,pi,-0.01,0.05])
alan@471 360
alan@461 361 show()
alan@461 362