def wiener_deconvolve(y, x, length=None, noise_variance=1., let_n_points=15, let_div_base=2): ''' Deconvolve an excitation signal from an impulse response We use Wiener filter Parameters ---------- y : ndarray The recording x : ndarray The excitation signal length: int, optional the length of the impulse response to deconvolve noise_variance : float, optional estimate of the noise variance let_n_points: int number of points to use in the LET approximation let_div_base: float the divider used for the LET grid ''' # FFT length including zero padding n = y.shape[0] + x.shape[0] - 1 # let FFT size be even for convenience if n % 2 != 0: n += 1 # when unknown, pick the filter size as size of test signal if length is None: length = n # Forward transforms Y = fft.rfft(np.array(y, dtype=np.float32), n=n) / np.sqrt(n) # recording X = fft.rfft(np.array(x, dtype=np.float32), n=n) / np.sqrt(n) # test signal # Squared amplitude of test signal X_sqm = np.abs(X)**2 # approximate SNR SNR_hat = np.maximum(1e-7, ((np.linalg.norm(Y)**2 / np.linalg.norm(X)**2) - noise_variance)) / noise_variance dividers = let_div_base**np.linspace(-let_n_points/2, let_n_points, let_n_points) SNR_grid = SNR_hat / dividers # compute candidate points G = (X_sqm[:,None] / (X_sqm[:,None] + 1./SNR_grid[None,:])) * Y[:,None] H_candidates = G / X[:,None] # find the best linear combination of the candidates weights = np.linalg.lstsq(G, Y)[0] # compute the estimated filter H = np.squeeze(np.dot(H_candidates, weights)) h = fft.irfft(H, n=n) return h[:length]
def deconvolve(y, s, noise, periodogram_len=64): ''' Wiener deconvolution of an excitation signal from an impulse response Parameters ---------- y : ndarray The recording s : ndarray The excitation signal noise: ndarray The noise reference signal Returns ------- h_len : int The length of the impulse response ''' # FFT length including zero padding n = y.shape[0] + s.shape[0] - 1 # let FFT size be even for convenience if n % 2 != 0: n += 1 # Compute the periodogram of the noise signal N = periodogram(noise, periodogram_len, n=n) # Forward transforms Y = fft.rfft(y, n=n, axis=0) / np.sqrt(n) S = fft.rfft(s, n=n, axis=0) / np.sqrt(n) # Wiener deconvolution if Y.ndim > 1: S = S[:, None] H = Y * np.conj(S) / (np.abs(S)**2 + N) # Inverse transform h = fft.irfft(H, n=n, axis=0) return h
def deconvolve(y, s, length=None, thresh=0.0): ''' Deconvolve an excitation signal from an impulse response Parameters ---------- y : ndarray The recording s : ndarray The excitation signal length: int, optional the length of the impulse response to deconvolve thresh : float, optional ignore frequency bins with power lower than this ''' # FFT length including zero padding n = y.shape[0] + s.shape[0] - 1 # let FFT size be even for convenience if n % 2 != 0: n += 1 # when unknown, pick the filter size as size of test signal if length is None: length = n # Forward transforms Y = fft.rfft(np.array(y, dtype=np.float32), n=n) / np.sqrt(n) S = fft.rfft(np.array(s, dtype=np.float32), n=n) / np.sqrt(n) # Only do the division where S is large enough H = np.zeros(*Y.shape, dtype=Y.dtype) I = np.where(np.abs(S) > thresh) H[I] = Y[I] / S[I] # Inverse transform h = fft.irfft(H, n=n) return h[:length]
def synthesis(self, X=None): """ Perform time synthesis of frequency domain to real signal using the inverse DFT. Parameters ---------- X: numpy array (complex64) Real input signal in time domain. Must be of size (N/2+1,D). Default is previously computed DFT. plot_time: bool Whether or not to plot time waveform. Returns ------- Time domain signal, numpy array of size (N,D) and of type float32. """ # check for valid input if X is not None: if self.D != 1: if X.shape != (self.nbin, self.D): raise ValueError('Invalid input dimensions.') elif self.D == 1: if X.ndim != 1 and X.shape[0] != self.nbin: raise ValueError('Invalid input dimensions.') self.X[:, ] = X # inverse DFT if self.transform == 'fftw': self.b[:] = self.X self.x[:, ] = self.backward() elif self.transform == 'mkl': self.x[:, ] = mkl_fft.irfft(self.X, axis=0) else: self.x[:, ] = irfft(self.X, axis=0) # apply window if needed if self.synthesis_window is not None: np.multiply(self.synthesis_window, self.x[:, ], self.x[:, ]) return self.x
def correlate(x1, x2, interp=1, phat=False): """ Compute the cross-correlation between x1 and x2 Parameters ---------- x1,x2: array_like The data arrays interp: int, optional The interpolation factor for the output array, default 1. phat: bool, optional Apply the PHAT weighting (default False) Returns ------- The cross-correlation between the two arrays """ N1 = x1.shape[0] N2 = x2.shape[0] N = N1 + N2 - 1 X1 = fft.rfft(x1, n=N) X2 = fft.rfft(x2, n=N) if phat: eps1 = np.mean(np.abs(X1)) * 1e-10 X1 /= np.abs(X1) + eps1 eps2 = np.mean(np.abs(X2)) * 1e-10 X2 /= np.abs(X2) + eps2 m = np.minimum(N1, N2) out = fft.irfft(X1 * np.conj(X2), n=int(N * interp)) return np.concatenate([out[-interp * (N2 - 1):], out[:(interp * N1)]])