annotate matlab/bmm/carfac/Carfac.py @ 464:7b57ab0d0126

Clean up AGC FIR smoothing coeffs code
author dicklyon@google.com
date Wed, 07 Mar 2012 19:45:39 +0000
parents af9d803c0f2b
children 8e8381785b2b
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@461 2 # Author: Al Strelzoff
alan@461 3
alan@461 4 from numpy import cos, sin, tan, sinh, arctan, pi, e, real,imag,arccos,arcsin,arctan2,log10,log
alan@461 5
alan@461 6 from pylab import figure, clf, plot,loglog, xlabel, ylabel, xlim, ylim, title, grid, axes, axis, show
alan@461 7
alan@461 8 fs = 22050.0 # sampling rate
alan@461 9 Nyq = fs/2.0 # nyquist frequency
alan@461 10
alan@461 11
alan@461 12 # given a frequency f, return the ERB
alan@461 13 def ERB_Hz(f):
alan@461 14 # Ref: Glasberg and Moore: Hearing Research, 47 (1990), 103-138
alan@461 15 return 24.7 * (1.0 + 4.37 * f / 1000.0)
alan@461 16
alan@461 17
alan@461 18 # ERB parameters
alan@461 19 ERB_Q = 1000.0/(24.7*4.37) # 9.2645
alan@461 20 ERB_break_freq = 1000/4.37 # 228.833
alan@461 21
alan@461 22 ERB_per_step = 0.3333
alan@461 23
alan@461 24 # set up channels
alan@461 25
alan@461 26 first_pole_theta = .78 * pi # We start at the top frequency.
alan@461 27 pole_Hz = first_pole_theta * fs / (2.0*pi) # frequency of top pole
alan@461 28 min_pole_Hz = 40.0 # bottom frequency
alan@461 29
alan@461 30 # set up the pole frequencies according to the above parameters
alan@461 31 pole_freqs = [] # empty list of pole frequencies to fill, zeroth will be the top
alan@461 32 while pole_Hz > min_pole_Hz:
alan@461 33 pole_Hz = pole_Hz - ERB_per_step * ERB_Hz(pole_Hz)
alan@461 34 pole_freqs.append(pole_Hz)
alan@461 35
alan@461 36 n_ch = len(pole_freqs) # n_ch is the number of channels or frequency steps
alan@461 37 print('num channels',n_ch)
alan@461 38
alan@461 39 # 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 40
alan@461 41 # before we make the filters, let's plot the position of the frequencies and the values of ERB at each.
alan@461 42
alan@461 43 fscale = []
alan@461 44 erbs = []
alan@461 45
alan@461 46 figure(0)
alan@461 47 for i in range(n_ch):
alan@461 48
alan@461 49 f = pole_freqs[i] # the frequencies from the list
alan@461 50 ERB = ERB_Hz(f) # the ERB value at each frequency
alan@461 51 fscale.append(f)
alan@461 52 erbs.append(ERB)
alan@461 53
alan@461 54 # plot a verticle hash at each frequency:
alan@461 55 u = []
alan@461 56 v = []
alan@461 57 for j in range(5):
alan@461 58 u.append(f)
alan@461 59 v.append(10.0 + float(j))
alan@461 60
alan@461 61 plot(u,v)
alan@461 62
alan@461 63 loglog(fscale,erbs)
alan@461 64
alan@461 65 title('ERB scale')
alan@461 66
alan@461 67
alan@461 68
alan@461 69 # This filter class includes some methods useful only in design. They will not be used in run time implementation.
alan@461 70 # From figure 14.3 in Dick Lyon's book.
alan@461 71
alan@461 72
alan@461 73 #########################################################The Carfac filter class#################################################################################
alan@461 74
alan@461 75 # fixed parameters
alan@461 76 min_zeta = 0.12
alan@461 77
alan@461 78 class carfac():
alan@461 79
alan@461 80
alan@461 81 # instantiate the class (in C++, the constructor)
alan@461 82 def __init__(self,f):
alan@461 83
alan@461 84 self.frequency = f
alan@461 85
alan@461 86 theta = 2.0 * pi * f/fs
alan@461 87
alan@461 88 r = 1.0 - sin(theta) * min_zeta
alan@461 89
alan@461 90
alan@461 91 a = r * cos(theta)
alan@461 92 c = r * sin(theta)
alan@461 93
alan@461 94
alan@461 95 h = c
alan@461 96
alan@461 97 g = 1.0/(1.0 + h * r * sin(theta) / (1.0 - 2.0 * r * cos(theta) + r ** 2))
alan@461 98
alan@461 99 # make all parameters properties of the class
alan@461 100 self.a = a
alan@461 101 self.c = c
alan@461 102 self.r = r
alan@461 103 self.theta = theta
alan@461 104 self.h = h
alan@461 105 self.g = g
alan@461 106
alan@461 107
alan@461 108 # the two storage elements. Referring to diagram 14.3 on p.263, z2 is the upper storage register, z1, the lower
alan@461 109 self.z1 = 0.0
alan@461 110 self.z2 = 0.0
alan@461 111
alan@461 112
alan@461 113 # frequency response of this filter
alan@461 114 self.H = []
alan@461 115
alan@461 116
alan@461 117
alan@461 118 # the total frequency magnitude of this filter including all the filters in front of this one
alan@461 119 self.HT = [] # this list will be filled by multiplying all the H's ahead of it together with its own (H)
alan@461 120
alan@461 121
alan@461 122
alan@461 123
alan@461 124
alan@461 125
alan@461 126 # execute one clock tick. Take in one input and output one result. Execution semantics taken from fig. 14.3
alan@461 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.
alan@461 128 def input(self,X):
alan@461 129
alan@461 130 # recover the class definitions of these variables. These statements below take up zero time at execution since they are just compiler declarations.
alan@461 131
alan@461 132
alan@461 133 a = self.a
alan@461 134 c = self.c
alan@461 135 h = self.h
alan@461 136 g = self.g
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@461 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
alan@461 142 # the output Y
alan@461 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.
alan@461 144
alan@461 145 #stores
alan@461 146 z2 = (a * z2) + (c * z1) #Note: this is a 2 element multiply accumulate
alan@461 147 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@461 161 return g * (1.0 + (h*c*z)/(z**2 - 2.0*a*z + r**2 )) # from page ?? of Lyon's book.
alan@461 162
alan@461 163
alan@461 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
alan@461 165
alan@461 166
alan@461 167
alan@461 168
alan@461 169
alan@461 170 ######################################################End of Carfac filter class########################################################################
alan@461 171
alan@461 172 # instantiate the filters
alan@461 173
alan@461 174 # n_ch is the number of filters as determined above
alan@461 175
alan@461 176 Filters = [] # the list of all filters, the zeroth is the top frequency
alan@461 177 for i in range(n_ch):
alan@461 178 f = pole_freqs[i]
alan@461 179 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 180 Filters.append(filter)
alan@461 181
alan@461 182
alan@461 183
alan@461 184 # sweep parameters
alan@461 185 steps = 1000
alan@461 186
alan@461 187 sum = [] # array to hold the magnitude sum
alan@461 188 for i in range(steps): sum.append( 0.0 )
alan@461 189
alan@461 190 figure(1)
alan@461 191 title('CarFac frequency response')
alan@461 192
alan@461 193 for i in range(n_ch):
alan@461 194 filter = Filters[i]
alan@461 195 # plotting arrays
alan@461 196 u = []
alan@461 197 v = []
alan@461 198 # calculate the frequency magnitude by stepping the frequency in radians
alan@461 199 for j in range(steps):
alan@461 200
alan@461 201 w = pi * float(j)/steps
alan@461 202 u.append(w)
alan@461 203 mag = filter.Hw(w) # freq mag at freq w
alan@461 204 filter.H.append(mag) # save for later use
alan@461 205 filter.HT.append(mag) # will be total response of cascade to this point after we do the multiplication in a step below
alan@461 206 v.append(real(mag)) # y plotting axis
alan@461 207 sum[j]+= mag
alan@461 208
alan@461 209
alan@461 210 plot(u,v)
alan@461 211
alan@461 212
alan@461 213
alan@461 214 figure(2)
alan@461 215 title('Summed frequency magnitudes')
alan@461 216 for i in range(steps): sum[i] = abs(sum[i])/n_ch
alan@461 217 plot(u,sum)
alan@461 218
alan@461 219 # calculate the phase response of the same group of filters
alan@461 220 figure(3)
alan@461 221 title('Filter Phase')
alan@461 222
alan@461 223
alan@461 224 for i in range(n_ch):
alan@461 225 filter = Filters[i]
alan@461 226
alan@461 227 u = []
alan@461 228 v = []
alan@461 229 for j in range(steps):
alan@461 230 x = float(j)/Nyq
alan@461 231
alan@461 232 u.append(x)
alan@461 233
alan@461 234 mag = filter.H[j]
alan@461 235 phase = arctan2(-imag(mag),-real(mag)) + pi # this formula used to avoid wrap around
alan@461 236
alan@461 237 v.append(phase) # y plotting axis
alan@461 238
alan@461 239 plot(u,v)
alan@461 240
alan@461 241
alan@461 242
alan@461 243 # calulate and plot cascaded frequency response and summed magnitude
alan@461 244 sum = [] # array to hold the magnitude sum
alan@461 245 for i in range(steps): sum.append( 0.0 )
alan@461 246
alan@461 247
alan@461 248 figure(4)
alan@461 249 title('CarFac Cascaded frequency response')
alan@461 250
alan@461 251
alan@461 252 for i in range(n_ch-1):
alan@461 253
alan@461 254 filter = Filters[i]
alan@461 255 next = Filters[i+1]
alan@461 256
alan@461 257
alan@461 258 u = []
alan@461 259 v = []
alan@461 260 for j in range(steps):
alan@461 261 u.append(float(j)/Nyq)
alan@461 262 mag = filter.HT[j] * next.HT[j]
alan@461 263 filter.HT[j] = mag
alan@461 264 v.append(real(mag))
alan@461 265 sum[j]+= mag
alan@461 266
alan@461 267
alan@461 268 plot(u,v)
alan@461 269
alan@461 270
alan@461 271
alan@461 272 figure(5)
alan@461 273 title('Summed cascaded frequency magnitudes')
alan@461 274 for i in range(steps): sum[i] = abs(sum[i])/n_ch
alan@461 275 plot(u,sum)
alan@461 276
alan@461 277 # calculate and plot the phase responses of the cascaded filters
alan@461 278
alan@461 279 figure(6)
alan@461 280 title('Filter cascaded Phase')
alan@461 281
alan@461 282
alan@461 283 for i in range(n_ch):
alan@461 284 filter = Filters[i]
alan@461 285
alan@461 286
alan@461 287 u = []
alan@461 288 v = []
alan@461 289 for j in range(steps):
alan@461 290 x = float(j)/Nyq
alan@461 291
alan@461 292 u.append(x)
alan@461 293 mag = filter.HT[j]
alan@461 294 phase = arctan2(-imag(mag),-real(mag)) + pi
alan@461 295
alan@461 296 v.append(phase) # y plotting axis
alan@461 297
alan@461 298
alan@461 299
alan@461 300
alan@461 301 plot(u,v)
alan@461 302
alan@461 303 show()
alan@461 304