annotate cortical_model.py @ 5:d7b2784ff5a3

* Fixed some miscalculations from the way python does float division * Made data files more precise
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Sun, 23 Feb 2014 16:58:00 +0000
parents 29cd3e735c4c
children 546dfcd45281
rev   line source
c@0 1 """
c@0 2 A module used for auditory analysis.
c@0 3
c@0 4 Models currently implemented:
c@0 5 * Frequency-modulation analysis model based on the human auditory system.
c@0 6
c@0 7 Model implementations in progress:
c@0 8 * Glasberg and Moore model
c@0 9
c@0 10 Packaged dependencies:
c@0 11 * utils.py and/or utils.pyc
c@0 12 * erb.dat
c@0 13 * outMidFir.dat
c@1 14 * tq.dat
c@0 15
c@0 16 External dependencies:
c@0 17 * scipy
c@0 18 * numpy
c@0 19 * copy
c@0 20 * matplotlib
c@0 21 """
c@0 22
c@5 23 import sys
c@0 24 import utils
c@0 25 import scipy.signal as sp
c@1 26 import scipy.fftpack as fft
c@0 27 import numpy as np
c@0 28 from copy import deepcopy
c@0 29 import matplotlib.pyplot as plt
c@0 30
c@5 31 def get_specific_loudness_noise(exc_i, inc_loud_region = False):
c@5 32
c@5 33
c@5 34
c@5 35 return
c@5 36
c@1 37 def get_specific_loudness(exc_i, inc_loud_region = False):
c@0 38 """
c@3 39 A function to calculate the specific loudness of the excitation patterns at each ERB. Specific loudness is calculated for three regions of
c@3 40 signal intensity, low level, mid level and high level. The high level processing is optional, and by default, ignored. When ignored the high level
c@3 41 region is processed equivalent to that of the mid region.
c@3 42
c@3 43 Parameters:
c@3 44 * exc_i (type: array-like of floats) - an array of shape (39, lenSig) of the excitation intensity of the signal. The outer dimension determines the
c@3 45 ERB channel. (Required)
c@3 46 * inc_loud_region (type: boolean) - Specifies whether to process the high levels differently to the mid levels. (Optional; Default = False)
c@0 47
c@0 48 TO DO
c@0 49 """
c@3 50 exc_i = np.array(exc_i)
c@3 51
c@1 52 lenSig = np.shape(exc_i)[-1] #length signal
c@3 53 tq, G, A, alpha = get_specific_loudness_parameters(lenSig)
c@1 54 specific_loudness = exc_low = exc_mid = exc_high = np.zeros(np.shape(exc_i))
c@0 55
c@1 56 less_than_tq = get_less_than_tq(exc_i, tq) #boolean array of elements less than or equal to the threshold of quiet
c@1 57 greater_than_tq = ~less_than_tq #boolean array of elements greater than the threshold of quiet
c@1 58 greater_than_tl = get_greater_than_tl(exc_i) #boolean array of elements greater than the loud threshold
c@1 59 gttq_lttl = greater_than_tq[~greater_than_tl] #boolean array of elements greater than the threshold of quiet but less than the threshold of loud
c@0 60
c@1 61 exc_low = exc_i[less_than_tq]
c@1 62 specific_loudness[less_than_tq] = get_specific_loudness_low(exc_low, G[less_than_tq], tq[less_than_tq], A[less_than_tq], alpha[less_than_tq])
c@1 63
c@1 64 if(inc_loud_region):
c@1 65 exc_mid = exc_i[gttq_lttl]
c@1 66 exc_high = exc_i[greater_than_tl]
c@1 67 specific_loudness[gttq_lttl] = get_specific_loudness_mid(exc_mid, G[gttq_lttl], A[gttq_lttl], alpha[gttq_lttl])
c@1 68 specific_loudness[greater_than_tl] = get_specific_loudness_high(exc_high)
c@1 69 else:
c@1 70 exc_mid = exc_i[greater_than_tq]
c@1 71 specific_loudness[greater_than_tq] = get_specific_loudness_mid(exc_mid, G[greater_than_tq], A[greater_than_tq], alpha[greater_than_tq])
c@1 72
c@1 73 return specific_loudness
c@0 74
c@3 75 def get_specific_loudness_parameters(lenSig = 1):
c@3 76 """
c@3 77 Loads and returns the specific loudness values. If lenSig is specified, the parameters are shaped equal to the excitation signal to allow for
c@3 78 matrix elementwise operations between the parameters and signal. (Assumes excitation signal shape is [39, lenSig]).
c@3 79
c@3 80 Parameters:
c@3 81 * lenSig (type: numerical int) - the length of the excitation signal to be analysed. (Optional; Default = 1)
c@3 82
c@3 83 Returns:
c@3 84 * tq (type: numpy array of floats) - the threshold of quiet for each centre frequency of the ERBs
c@3 85 * G (type: numpy array of floats) - the frequency dependent gain values for each centre frequency of the ERBs
c@3 86 * A (type: numpy array of floats) - the frequency dependent A values for each centre frequency of the ERBs
c@3 87 * alpha (type: numpy array of floats) - the frequency dependent alpha values for each centre frequency of the ERBs
c@3 88 """
c@3 89
c@3 90 tq_dB, A, alpha = utils.load_sl_parameters() #load tq, A and alpha parameters
c@3 91 tq_dB = np.transpose(np.tile(tq_dB, (lenSig,1)))
c@3 92 tq = 10**(tq_dB / 10)
c@3 93 A = np.transpose(np.tile(A, (lenSig,1)))
c@3 94 alpha = np.transpose(np.tile(alpha, (lenSig,1)))
c@3 95 tq500_dB = 3.73 #threshold of quiet at 500Hz reference
c@3 96 G = 10**((tq500_dB - tq_dB)/10) #gain parameters
c@3 97
c@3 98 return tq, G, A, alpha
c@3 99
c@1 100 def get_specific_loudness_low(exc_low, G, tq, A, alpha):
c@3 101 """
c@3 102 Returns the specific loudness of the low level parts of the signal. Use get_specific_loudness_parameters() and specify lenSig
c@3 103 to obtain the correct values for G, tq, A and alpha. Use get_less_than_tq() to find the indexes of the samples with lower level
c@3 104 excitations.
c@3 105
c@3 106 e.g.,
c@3 107
c@3 108 # exc_i is the excitation intensity
c@3 109 lenSig = np.shape(exc_i)[-1] # find the length of the signal
c@3 110 tq, G, A, alpha = get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
c@3 111 less_than_tq = get_less_than_tq(exc_i, tq) # find which samples are lower level
c@3 112 specific_loudness[less_than_tq] = get_specific_loudness_low(exc_low[less_than_tq], G[less_than_tq], tq[less_than_tq], A[less_than_tq], alpha[less_than_tq])
c@3 113 # only process the low level part of the signal
c@3 114
c@3 115 Parameters:
c@3 116 * exc_low (type: array-like matrix of floats) - the lower level (less than or equal to tq) excitation pattern for each ERB
c@3 117 (Required)
c@3 118 * G (type: array-like matrix of floats) - the frequency dependent loudness gain parameters for each ERB, must be same shape
c@3 119 as exc_low. (Required)
c@3 120 * tq (type: array-like matrix of floats) - the frequency dependent threshold of quiet parameters for each ERB. Must be same
c@3 121 shape as exc_low. (Required)
c@3 122 * A (type: array-like matrix of floats) - the frequency dependent A parameters for each ERB. Must be same shape as exc_low.
c@3 123 * alpha (type: array-like matrix of floats) - the frequency dependent alpha parameters for each ERB. Must be same shape as
c@3 124 exc_low. (Required)
c@3 125
c@3 126 Returns:
c@3 127 * specific_loudness_low (type: array-like matrix of floats) - An array with dimensions equal to the exc_low containing the
c@3 128 specific loudness of the signal at levels below the threshold of quiet.
c@3 129 """
c@1 130
c@5 131 C = 0.046871 #constant parameter
c@5 132 specific_loudness_low = C * ((2.0*exc_low)/(0.0+exc_low+tq))**1.5 * ((G * exc_low + A)**alpha - A**alpha)
c@1 133
c@1 134 return specific_loudness_low
c@1 135
c@1 136 def get_specific_loudness_mid(exc_mid, G, A, alpha):
c@3 137 """
c@3 138 Returns the specific loudness of the mid level parts of the signal.
c@3 139
c@3 140 e.g.,
c@3 141
c@3 142 # exc_i is the excitation intensity
c@3 143 lenSig = np.shape(exc_i)[-1] # find the length of the signal
c@3 144 tq, G, A, alpha = get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
c@3 145 less_than_tq = get_less_than_tq(exc_i, tq) # find which samples are lower level
c@3 146 greater_than_tq = ~less_than_tq # find which samples are greater than the threshold of quiet
c@3 147 greater_than_tl = get_greater_than_tl(exc_i) # find which samples are greater than the loud threshold
c@3 148 gttq_lttl = greater_than_tq[~greater_than_tl] # find which samples are mid level
c@3 149 specific_loudness[gttq_lttl] = get_specific_loudness_low(exc_low[gttq_lttl], G[gttq_lttl], tq[gttq_lttl], A[gttq_lttl], alpha[gttq_lttl])
c@3 150 # only process the mid level part of the signal
c@3 151
c@3 152 NOTE: The above is an example of use assuming the higher level processing IS NOT IGNORED. Use variable greater_than_tq if processing
c@3 153 higher levels equivalent to the mid.
c@3 154
c@3 155
c@3 156 Parameters:
c@3 157 * exc_mid (type: array-like matrix of floats) - the mid level (larger than tq (and, optionally less than high level threshold))
c@3 158 excitation pattern for each ERB (Required)
c@3 159 * G (type: array-like matrix of floats) - the frequency dependent loudness gain parameters for each ERB, must be same shape
c@3 160 as exc_low. (Required)
c@3 161 * A (type: array-like matrix of floats) - the frequency dependent A parameters for each ERB. Must be same shape as exc_low.
c@3 162 * alpha (type: array-like matrix of floats) - the frequency dependent alpha parameters for each ERB. Must be same shape as
c@3 163 exc_low. (Required)
c@3 164
c@3 165 Returns:
c@3 166 * specific_loudness_mid (type: array-like matrix of floats) - An array with dimensions equal to the exc_mid containing the
c@3 167 specific loudness of the signal at mid levels.
c@3 168 """
c@1 169
c@1 170 C = 0.047 #constant parameter
c@5 171 specific_loudness_mid = C * ((G * exc_mid + A)**alpha - (A**alpha))
c@1 172
c@1 173 return specific_loudness_mid
c@1 174
c@1 175 def get_specific_loudness_high(exc_high):
c@1 176 """
c@3 177 Returns the specific loudness of the high level parts of the signal.
c@3 178
c@3 179 e.g.,
c@3 180
c@3 181 # exc_i is the excitation intensity
c@3 182 lenSig = np.shape(exc_i)[-1] # find the length of the signal
c@3 183 tq, G, A, alpha = get_specific_loudness_parameters(lenSig) # get the shaped loudness parameters
c@3 184 greater_than_tl = get_greater_than_tl(exc_i) # find which samples are greater than the loud threshold
c@3 185 specific_loudness[greater_than_tl] = get_specific_loudness_low(exc_low[greater_than_tl], G[greater_than_tl], tq[greater_than_tl], A[greater_than_tl], alpha[greater_than_tl])
c@3 186 # only process the mid level part of the signal
c@3 187
c@3 188 Parameters:
c@3 189 * exc_high (type: array-like matrix of floats) - the high level (larger than the threshold of high level) excitation pattern
c@3 190 for each ERB (Required)
c@3 191
c@3 192 Returns:
c@3 193 * specific_loudness_high (type: array-like matrix of floats) - An array with dimensions equal to the exc_high containing the
c@3 194 specific loudness of the signal at high levels.
c@1 195 """
c@1 196
c@1 197 C = 0.047 #constant parameter
c@1 198 specific_loudness_high = C * (exc_high / (1.04 * 10**6))**0.5
c@1 199
c@1 200 return specific_loudness_high
c@1 201
c@1 202 def get_greater_than_tl(exc_i):
c@1 203 """
c@1 204 A function to return if each element of the excitation intensity is greater than the threshold of loud.
c@1 205
c@1 206 Parameters:
c@1 207 * exc_i (type: array-like matrix of floats) - the input excitation intensity
c@1 208
c@1 209 Returns:
c@3 210 * le_tq (type: array-like matrix of booleans) - a boolean array with dimensions equal to the input
c@3 211 specifying if the excitation intensity is greater than the threshold of loud
c@1 212 """
c@1 213
c@1 214 lenSig = np.shape(exc_i)[-1]
c@1 215 g_tl = exc_i[:,:]>np.transpose(np.tile(10**10,(lenSig,39)))
c@1 216
c@1 217 return g_tl
c@1 218
c@1 219 def get_less_than_tq(exc_i, tq):
c@1 220 """
c@1 221 A function to return if each element of the excitation intensity is less than the threshold of quiet.
c@1 222
c@1 223 Parameters:
c@1 224 * exc_i (type: array-like matrix of floats) - the input excitation intensity
c@1 225 * tq (type: array-like matrix of floats) - the threshold of quiet for each ERB.
c@1 226
c@1 227 Returns:
c@1 228 * le_tq (type: array-like matrix of booleans) - a boolean array with dimensions equal to the input
c@1 229 specifying if the excitation intensity is less than the threshold of quiet
c@1 230 """
c@1 231
c@1 232 if (np.shape(exc_i)!=np.shape(tq)):
c@1 233 np.transpose(np.tile(exc_i,(np.shape(exc_i)[-1],1)))
c@1 234
c@1 235 le_tq = exc_i<=tq
c@1 236
c@1 237 return le_tq
c@1 238
c@1 239 def get_excitation_i(input, fs, SPL, rectify=False, verbose = False):
c@0 240 """
c@0 241 A function to calculate the excitation intensity of the input signal.
c@0 242
c@0 243 Parameters:
c@0 244 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 245 * fs (type: numerical) - sample frequency of the signal, input. (Required)
c@0 246 * SPL (type: double) - the sound pressure level (SPL) at 0 dBFS, e.g., the SPL of a sine
c@0 247 wave with peaks at amplitude 1 and troughs at amplitude -1. (Required)
c@0 248 * rectify (type: boolean) - Specifies whether to include half wave rectification, modelling the direction
c@0 249 of that the cochlear nerves vibrate.
c@0 250 True to include, False to ignore. (Optional; Default = False)
c@0 251
c@0 252 Returns:
c@0 253 * gtfs (type: numpy array of floats) - array with size ((39,) + np.shape(input)) containing the excitation
c@0 254 pattern (in sound intensity) for each ERB of input signal. The excitation pattern for the nth ERB can
c@0 255 be accessed with gtfs[n].
c@0 256 """
c@0 257
c@0 258 input = np.array(input)
c@5 259
c@5 260 inputPa = v_Pascal(input, SPL)
c@5 261
c@5 262 inputOMFir = outMidFir(inputPa)
c@5 263
c@5 264 b = gamma_tone_filter(fs, normalise = True)
c@5 265 gtfs = decomposition(inputOMFir, b, verbose = verbose)
c@5 266
c@0 267 if (rectify):
c@0 268 gtfs = half_rectification(gtfs)
c@5 269
c@5 270 gtfsI = pa_i(gtfs)
c@5 271 gtfsN = at_normalise(gtfsI)
c@0 272
c@5 273 b1,a1 = exp_smoothing(fs)
c@5 274 gtfsF = np.zeros(np.shape(gtfsI))
c@5 275
c@5 276 for i in range(39):
c@5 277 gtfsF[i] = sp.lfilter(b1,a1,gtfsN[i])
c@5 278
c@5 279 return gtfsF
c@0 280
c@4 281 def plot_excitation_response(input = None, fs = 44100, outMidFilt = True, gammatone = True, xscale = 'log', yscale = 'log'):
c@0 282 """
c@0 283 A function that plots the transfer function of the outer middle ear and each gammatone filter.
c@0 284
c@0 285 Parameters:
c@0 286 * fs (type: numerical) - the sampling frequency of the signal. (Optional; Default = 44100)
c@0 287 * outMidFilt (type: boolean) - filter the signal by the outer and middle ear FIR filter. (Optional; Default = True)
c@0 288 * xscale (type: string) - the scale of the frequency axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
c@0 289 * yscale (type: string) - the scale of the amplitude axis. Values are 'log' or 'linear'. (Optional; Default = 'log')
c@0 290 """
c@1 291
c@4 292 if input == None:
c@4 293 input = np.zeros((np.ceil(fs)))
c@1 294 input[0] = 1
c@4 295
c@0 296 if(outMidFilt): input = outMidFir(input)
c@4 297 if(gammatone):
c@5 298 b = gamma_tone_filter(fs, normalise = True)
c@4 299 input = decomposition(input, b)
c@4 300 #input = holdsworthGamma(input,fs)
c@4 301 numPlot = range(np.shape(input)[0])
c@4 302 else:
c@4 303 numPlot = (0,)
c@4 304 input = input.reshape(1,len(input))
c@4 305
c@4 306 for i in numPlot:
c@4 307 utils.plot_fft(input[i],xscale, yscale, False)
c@0 308
c@0 309 plt.show()
c@0 310
c@4 311 return
c@0 312
c@0 313 def get_modulation_i(input, fs):
c@0 314 """
c@0 315 A function to calculate the modulation intensity of the input intensity signal. The function implements a
c@0 316 filter bank of bandpass filters with cut off frequencies ranging from 0.25 to 16 Hz.
c@0 317
c@0 318 Parameters:
c@0 319 * input (type: array-like matrix of floats) - the input intensity signal.
c@0 320 E.g., use get_excitation_i() to obtain excitation intensity and use as input.
c@0 321 * fs (type: numerical) - sampling frequency of input signal
c@0 322
c@0 323 Returns:
c@0 324 * y (type: numpy array of floats) - array with size ((10,) + np.shape(input)) containing the
c@0 325 modulation intensity of the signal at each modulation filter. The modulation intensity for the nth filter can
c@0 326 be accessed with y[n].
c@0 327 """
c@0 328
c@0 329 input = np.array(input)
c@0 330 b = fir_antialias(fs)
c@0 331 input_lp = sp.lfilter(b,(1),input_fr)
c@0 332 input_ds = downsample(input_lp, fs)
c@0 333 fc = np.array(utils.exp_sequence(-2,4,10))
c@0 334 bw = fc/2
c@0 335 y = decomposition(input_ds, fs, fc, bw)
c@0 336
c@0 337 return y
c@0 338
c@0 339 def outMidFir(input):
c@0 340 """
c@0 341 A function to filter the input signal with the response of the outer and middle ear.
c@0 342
c@0 343 Parameters:
c@0 344 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 345
c@0 346 Returns:
c@0 347 * y (type: numpy array of floats) - array with dimensions equal to the input signal filtered by the response of
c@0 348 the outer and middle ear.
c@0 349 """
c@0 350
c@0 351 input = np.array(input)
c@0 352 b = utils.load_outMidFir_coeff()
c@0 353 y = sp.lfilter(b, (1), input)
c@0 354
c@0 355 return y
c@0 356
c@5 357 def gamma_tone_filter(fs, normalise = False):
c@0 358 """
c@0 359 A function to filter to decompose the input signal into 39 different gammatones filtered signals modelling the ERBs.
c@0 360
c@0 361 Parameters:
c@0 362 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 363 * fs (type: numerical) - sample frequency of the signal, input. (Required)
c@0 364
c@0 365 Returns:
c@0 366 * y (type: numpy array of floats) - array with size ((39),np.shape(input)) containing the impulse response of the
c@0 367 signal at each gammatone filter. The response at the nth gammatone filter can be accessed by y[n].
c@0 368 """
c@0 369
c@4 370 nerbs = 39
c@4 371 erbs = np.array(range(1,nerbs+1))
c@4 372 fc = (10**(erbs/21.366)-1)/0.004368
c@4 373 bw = 24.673 * (1 + 0.004368*fc)
c@4 374 N = 4
c@4 375 filterLength = 4096
c@4 376 t = 1.0*np.array(range(filterLength))/fs
c@4 377
c@4 378 gain=((1.019*bw*(2.0*np.pi)/float(fs))**4)/6.0
c@4 379
c@4 380 PI = N * np.arctan(1)
c@4 381
c@4 382 b = np.zeros((nerbs,filterLength))
c@4 383
c@4 384 for i in range(39):
c@4 385 b[i] = gain[i] * t**(N-1) * fs**(N-1) * np.exp(-2*np.pi*bw[i]*t) * np.cos(2*np.pi*fc[i]*t)
c@4 386
c@5 387 if normalise:
c@5 388 fftbmax = (np.abs(fft.fft(b, np.shape(b)[-1]))).max();
c@5 389 b = b/fftbmax;
c@5 390
c@4 391 return b
c@4 392
c@4 393 def holdsworthGamma(input, fs):
c@4 394 """
c@4 395
c@4 396 """
c@0 397 input = np.array(input)
c@4 398 input = input + 0j
c@4 399
c@4 400 T = 1.0/fs
c@4 401
c@4 402 ERBs = np.array(range(1,40))
c@4 403 f0 = (10**(ERBs/21.4)-1)/4.37e-3
c@4 404
c@4 405 inLen = len(input)
c@4 406 b = 24.673 * (1 + 0.004368*f0)
c@4 407 k = np.array(range(inLen)) + 0j
c@4 408 out = np.zeros((39,inLen))
c@4 409
c@4 410 for erb in range(39):
c@4 411
c@4 412 zArr = input*np.exp(-2*np.pi*1j*f0[erb]*k*T)
c@4 413 wArr = np.zeros((inLen+1))
c@4 414
c@4 415 for i in range(1,inLen+1):
c@4 416 wArr[i] = wArr[i-1] + (1 - np.exp(-2*np.pi*b[erb]*T))*(zArr[i-1] - wArr[i-1])
c@4 417
c@4 418 out[erb] = (wArr[1:]*np.exp(2*np.pi*1j*f0[erb]*k*T)).real
c@4 419
c@4 420 return out
c@4 421
c@0 422
c@0 423 def v_Pascal(input, SPL):
c@0 424 """
c@0 425 A function to convert a signal, normalised to an amplitude range of -1 to 1, to a signal represented in pressure (units: Pascal).
c@0 426
c@0 427 Parameters:
c@0 428 * input (type: array-like matrix of floats) - signal normalised to an amplitude range of -1 to 1. (Required)
c@0 429 * SPL (type: double) - the sound pressure level (SPL) at 0 dBFS, e.g., the SPL of a sine
c@0 430 wave with peaks at amplitude 1 and troughs at amplitude -1. (Required)
c@0 431
c@0 432 Returns:
c@0 433 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input represented
c@0 434 as a pressure signal.
c@0 435 """
c@0 436
c@0 437 input = np.array(input)
c@5 438 y = np.sign(input)*(0.00002*10**(np.log10(np.abs(input))+SPL/20.0))
c@0 439
c@0 440 return y
c@0 441
c@0 442 def pa_i(input, C=406):
c@0 443 """
c@0 444 A function to convert a pressure signal (unit: Pascal) to an intensity signal.
c@0 445
c@0 446 Parameters:
c@0 447 * input (type: array-like matrix of floats) - pressure signal (unit: Pascal) (Required)
c@0 448 * C (type: double) - the acoustic impedance of the air (Optional; Default = 406)
c@0 449
c@0 450 Returns:
c@0 451 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input represented
c@0 452 as a pressure signal.
c@0 453 """
c@0 454
c@0 455 input = np.array(input)
c@0 456 y = (input**2) / C
c@0 457
c@0 458 return y
c@0 459
c@0 460 def at_normalise(input):
c@0 461 """
c@0 462 A function to normalise an intensity signal with the audibility threshold.
c@0 463
c@0 464 Parameters:
c@0 465 * input (type: array-like matrix of floats) - intensity signal (unit: Pascal) (Required)
c@0 466
c@0 467 Returns:
c@0 468 * y (type: numpy array of floats) - array with dimensions equal to the input signal containing the input normalised
c@0 469 with the audibility threshold.
c@0 470 """
c@0 471
c@0 472 input = np.array(input)
c@5 473 y = input / (1*(10**-12))
c@0 474
c@1 475 return y
c@0 476
c@0 477 def downsample(input, factor=100):
c@0 478 """
c@0 479 A function to downsample a signal, input, with sampling frequency, fs, by a downsample factor of factor.
c@0 480
c@0 481 NOTE: It is advised to use the fir_antialias() function before downsampling to remove any high frequencies
c@0 482 which would otherwise represented as low frequencies due to aliasing.
c@0 483
c@0 484 Parameters:
c@0 485 * input (type: array-like matrix of floats) - input signal. (Required)
c@0 486 * factor - downsample factor (Optional; Default = 100)
c@0 487
c@0 488 Returns:
c@0 489 * output (type: numpy array of floats) - array with outer dimensions equivalent to the to the input, and
c@0 490 inner dimension equal to np.floor(lenIn / factor) where lenIn is the length of the inner dimension.
c@0 491 """
c@0 492
c@0 493 input = np.array(input)
c@0 494 shapeIn = np.shape(input)
c@0 495 nDim = np.shape(shapeIn)
c@0 496 lenIn = shapeIn[nDim[0]-1]
c@0 497 lenOut = np.floor(lenIn / factor)
c@0 498 n = np.linspace(0,lenIn,lenOut, endpoint=False).astype(np.int)
c@0 499 output = input[...,n]
c@0 500
c@0 501 return output
c@0 502
c@0 503 def half_rectification(input):
c@0 504 """
c@0 505 A function which performs a half-wave rectification on the input signal.
c@0 506
c@0 507 Parameters:
c@0 508 * input (type: array-like matrix of floats) - input signal. (Required)
c@0 509
c@0 510 Returns:
c@0 511 * y (type: numpy array of floats) - an array with dimensions of input containing the half-wave rectification of
c@0 512 input.
c@0 513 """
c@0 514
c@0 515
c@0 516 y = np.array(input)
c@0 517 y[y<0] = 0
c@0 518
c@0 519 return y
c@0 520
c@4 521 def decomposition(input, b, a = None, verbose = False):
c@0 522 """
c@4 523 A function to run the input through a bandpass filter bank with parameters defined by the b and a coefficints.
c@0 524
c@0 525 Parameters:
c@0 526 * input (type: array-like matrix of floats) - input signal. (Required)
c@4 527 * b (type: array-like matrix of floats) - the b coefficients of each filter in shape b[numOfFilters][numOfCoeffs]. (Required)
c@4 528 * a (type: array-like matrix of floats) - the a coefficients of each filter in shape a[numOfFilters][numOfCoeffs].
c@4 529 If not specified, the filter is treated as a FIR filter. (Optional; Default = 1)
c@4 530 * verbose (type: boolean) - determines whether to display the current subroutine/progress of the procedure.
c@4 531 (Optional; Default = False)
c@4 532
c@4 533 Returns:
c@4 534 * y (type: numpy array of floats) - an array with inner dimensions equal to that of the input and outer dimension equal to
c@4 535 the length of fc (i.e. the number of bandpass filters in the bank) containing the outputs to each filter. The output
c@4 536 signal of the nth filter can be accessed using y[n].
c@4 537 """
c@4 538
c@4 539 input = np.array(input)
c@4 540 bShape = np.shape(b)
c@4 541 nFilters = bShape[0]
c@4 542
c@4 543 if a == None:
c@4 544 a = np.ones(nFilters)
c@4 545
c@4 546 shape = (nFilters,) + np.shape(input)
c@4 547 shape = shape[0:]
c@4 548 y = np.zeros(shape)
c@4 549
c@5 550 if(verbose): sys.stdout.write("Running decomposition.\n")
c@4 551 for i in range(nFilters):
c@5 552 if(verbose):
c@5 553 sys.stdout.write("\r" + str(int(np.round(100.0*i/nFilters))) + "% complete.")
c@5 554 sys.stdout.flush()
c@4 555 x = deepcopy(input)
c@4 556 y[i] = sp.lfilter(b[i],a[i],x)
c@4 557
c@4 558 return y
c@4 559
c@4 560 def get_Modulation_filter_bank_coefficients(fs, fc, bw, order = 1024, verbose = False):
c@4 561 """
c@4 562 A function which returns the b and a coefficients of the modulation filter bank of length equal to the length of fc. Each
c@4 563 bandpass filter is designed by defining the centre frequencies, fc, and bandwidths, bw.
c@4 564
c@4 565 Parameters:
c@0 566 * fs (type: numerical) - the sampling frequency of the input signal. (Required)
c@0 567 * fc (type: array-like vector of floats) - the centre off frequencies (unnormalised) of each bandpass filter.
c@0 568 The length of this vector determines the number of filters in the bank.
c@0 569 * bw (type: array-like vector of floats) - the bandwidths (unnormalised) of each bandpass filter. Must be equal
c@0 570 to or more than the length of fc. If the length is more, all elements exceeding the length of fc - 1 will be
c@0 571 ignored.
c@0 572 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 1024)
c@0 573 * verbose (type: boolean) - determines whether to display the current subroutine/progress of the procedure.
c@0 574 (Optional; Default = False)
c@0 575
c@0 576 Returns:
c@4 577 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap)
c@0 578 """
c@4 579
c@4 580
c@0 581 nFreqs = len(fc)
c@4 582
c@5 583 if(verbose): sys.stdout.write("Running frequency decomposition.")
c@4 584
c@0 585 for i in range(nFreqs):
c@5 586 if(verbose):
c@5 587 sys.stdout.write("\r" + str(int(np.round(100.0*i/nFreqs))) + "% complete.")
c@5 588 sys.stdout.flush()
c@0 589 low = fc[i]-bw[i]/2;
c@0 590 high = fc[i]+bw[i]/2;
c@5 591 if(verbose): sys.stdout.write("\r Low: " + str(low) + "; High: " + str(high))
c@0 592 b = fir_bandpass(low, high, fs, order, verbose)
c@4 593
c@4 594 return b
c@0 595
c@3 596 def exp_smoothing(fs, tc = 0.01):
c@3 597 """
c@3 598 Designs an exponential filter, y[n] = alpha*x[n] - -(1-alpha)*y[n-1], where alpha = T/(tc+T), where T
c@3 599 is the inverse of the sampling frequency and time constant, tc, is specified.
c@3 600
c@3 601 Parameters:
c@3 602 * fs (type: numerical) - sampling frequency of the signal to be filtered (Required)
c@3 603 * tc (type: numerical) - the time constant of the filter. (Optional; Default = 0.01)
c@3 604
c@3 605 Returns:
c@3 606 * b (type: numerical) - the coefficient of x[n]. Equal to alpha.
c@3 607 * a (type: numpy array of floats) - an array of size 2 of the coefficients of y[n] (a[0] = 1) and y[n-1]
c@3 608 """
c@3 609
c@3 610 T = 1.0/fs
c@3 611 alpha = T/(tc + T)
c@3 612 b = [alpha]
c@3 613 a = [1, -(1-alpha)]
c@3 614
c@3 615 return b, a
c@3 616
c@3 617 def antialias_fir(fs, fc=100, order=64):
c@3 618 """
c@3 619 A function which returns the b coefficients for a lowpass fir filter with specified requirements.
c@3 620 Made specifically to remove aliasing when downsampling, but can be used for any application that
c@3 621 requires a lowpass filter.
c@3 622
c@3 623 Parameters:
c@3 624 * fs (type: numerical) - sampling frequency of the signal to be filtered (Required)
c@3 625 * fc (type: numerical) - unnormalised cut off frequency of the filter (Optional; Default = 100)
c@3 626 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 64)
c@3 627
c@3 628 Returns:
c@3 629 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap)
c@3 630 """
c@3 631
c@3 632 nyquist = 0.5*fs
c@3 633 fcNorm = fc/nyquist
c@3 634 b = sp.firwin(order+1, fcNorm)
c@3 635
c@3 636 return b
c@3 637
c@4 638 def fir_bandpass(low, high, fs, order = 4096, verbose = False):
c@0 639 """
c@0 640 A function which returns the b coefficients for a bandpass fir filter with specified requirements.
c@0 641
c@0 642 Parameters:
c@0 643 * low - the lower cutoff frequency of the filter. (Required)
c@0 644 * high - the upper cutoff frequency of the filter. (Required)
c@0 645 * fs (type: numerical) - sampling frequency of the signal to be filtered. (Required)
c@0 646 * order (type: numerical int) - the order of the filter (number of taps minus 1) (Optional; Default = 1024)
c@0 647 * verbose (type: boolean) - determines whether to display the current progress (or info on the current subroutine)
c@0 648 of the procedure. (Optional; Default = False)
c@0 649
c@0 650 Returns:
c@0 651 * b (type: numpy array of floats) - an array of size order + 1 (i.e. a coefficient for each tap).
c@0 652 """
c@0 653
c@0 654 nyquist = 0.5*fs
c@0 655 lowNorm = low/nyquist
c@0 656 highNorm = high/nyquist
c@5 657 if(verbose): sys.stdout.write("Low: " + str(lowNorm) + "; High: " + str(highNorm))
c@0 658 b = sp.firwin(order+1, [lowNorm, highNorm], pass_zero=False)
c@0 659
c@0 660 return b