def playrec(self, y, send_signal=True): """Simulatenously play through secondary path while recording the result. Parameters ---------- y : (blocklength,) ndarray Control signal. send_signal : bool, optional If `False`, turn of the disturbance. Can be used to 'measure' the secondary path. Returns ------- x, e, u, d : (blocklength,) ndarray Reference signal, error signal, control signal at error microphone, primary at error microphone. """ y = np.atleast_1d(y) if send_signal: x = np.atleast_1d(next(self.signal)) # reference signal else: x = np.zeros((self.blocklength, *self._orig_signal.shape[1:2])) subscripts_sec = ( "nlm"[: self._orig_h_pri.ndim - 1] + "," + "nm"[: y.ndim] + "->n" ) subscripts_pri = ( "nlk"[: self._orig_h_pri.ndim - 1] + "," + "nk"[: self._orig_signal.ndim] + "->n" ) if self._orig_h_sec.ndim - 1 > 1: subscripts_sec += "l" subscripts_pri += "l" d, self._zi_pri = olafilt( next(self.h_pri), x, subscripts_pri, zi=self._zi_pri ) # primary path signal at error mic u, self._zi_sec = olafilt( next(self.h_sec), y, subscripts_sec, zi=self._zi_sec ) # secondary path signal at error mic e = d + u # error signal if self.noise is not None: e += next(self.noise) return x, e, u, d
def __call__(self, x): """Filter signal x. Parameters ---------- x : array_like Input signal Returns ------- y : numpy.ndarray Output signal. Has same length as `x`. """ y, self.zi = olafilt(self.w, x, zi=self.zi) return y
def filt(self, x): """Filter signal x. Parameters ---------- x : array_like Input signal Returns ------- y : numpy.ndarray Output signal with `y.shape[0] == x.shape[0]`. """ y, self.zi = olafilt(self.w, x, subscripts=self.subscripts, zi=self.zi) return y
def filt(self, x): """Filter reference signal. Parameters ---------- x : (blocklength, Nin) array_like Reference signal. Returns ------- y : (blocklength, Nout) numpy.ndarray Filter output. """ x = atleast_2d(x) assert x.shape[0] == self.blocklength assert x.shape[1] == self.Nin # NOTE: filtering could also be done in FD. When is each one better? # NOTE: give olafilt the FFT of w? y, self.zifilt = olafilt(self.w, x, "nmk,nk->nm", zi=self.zifilt) return y
def __call__(self, x, d, sec_path_coeff=None, sec_path_est=None): """Compute filter output, error, and coefficients. Parameters ---------- x : (N,) array_like Reference signal. `N` must be divisable by filter blocklength. d : (N,) array_like Desired signal. `N` must be divisable by filter blocklength. sec_path_coeff : array_like or None, optional Coefficients of the secondary path filter model. sec_path_est : None, optional Estimate of secondary path filter model. Returns ------- y : (N,) numpy.ndarray Filter output. u : (N,) numpy.ndarray Filter output at error signal. e : (N,) numpy.ndarray Error signal. w : (N, length) Filter coefficients at each block. """ x = np.atleast_1d(x) d = np.atleast_1d(d) assert x.ndim == 1 assert x.shape[0] % self.blocklength == 0 assert x.shape == d.shape if sec_path_est is not None: fx = olafilt(sec_path_est, x) # filtered reference signal else: fx = x x = x.reshape((-1, self.blocklength)) d = d.reshape((-1, self.blocklength)) n_blocks = x.shape[0] w = np.zeros((n_blocks, self.length)) # filter history e = np.zeros((n_blocks, self.blocklength)) # error signal y = np.zeros((n_blocks, self.blocklength)) # filtered output signal u = np.zeros( (n_blocks, self.blocklength)) # control signal at error mic fx = fx.reshape((-1, self.blocklength)) if sec_path_coeff is not None: zi = 0 for n in range(n_blocks): w[n] = self.w y[n] = self.filt(x[n]) # control signal at error sensor if sec_path_coeff is not None: u[n], zi = olafilt(sec_path_coeff, y[n], zi=zi) else: u[n] = y[n] # error signal e[n] = d[n] + u[n] self.adapt(fx[n], e[n]) return y.reshape(-1), u.reshape(-1), e.reshape(-1), w
signal = np.random.normal(0, 1, size=n_buffers * blocklength) # the adaptive filter filt = FastBlockLMSFilter(length, blocklength, stepsize=0.1, leakage=0.9999) # secondary path estimate has to account for block size plant_model = SimpleFilter(np.concatenate((np.zeros(blocklength), h_sec))) # simulates an audio interface with primary and secondary paths and 40 dB SNR noise # at the error sensor sim = FakeInterface( blocklength, signal, h_pri=h_pri, h_sec=h_sec, noise=wgn(olafilt(h_pri, signal), 40, "dB"), ) elog = [] y = np.zeros(blocklength) # control signal is zero for first block for i in range(n_buffers): # record reference signal x and error signal e while playing back y x, e, _, _ = sim.playrec(y) # filter the reference signal fx = plant_model(x) # adapt filter filt.adapt(fx, e)