def test_inverted_sync_sweep_spectrum(): FFTLEN = 1024 sweep = SyncSweep(10, 1000, 2, 2000) ispec = InvertedSyncSweepSpectrum.from_sweep(sweep, fftlen=FFTLEN) assume(ispec.fftlen == FFTLEN) assume(len(ispec.freq) == (FFTLEN // 2 + 1)) assume(len(ispec.spectrum) == (FFTLEN // 2 + 1)) assume(ispec.spectrum[0] == 0j) expected_invspec = np.zeros_like(ispec.spectrum) expected_invspec[0] = 0j expected_invspec[1:] = ( 2 * np.sqrt(ispec.freq[1:] / sweep.sweepperiod) * np.exp(-2j * np.pi * ispec.freq[1:] * sweep.sweepperiod * (1 - np.log(ispec.freq[1:] / sweep.startfreq)) + 1j * np.pi / 4)) assume(np.allclose(ispec.spectrum, expected_invspec)) ispec.fftlen = 2 * FFTLEN assume(ispec.fftlen == 2 * FFTLEN) assume(len(ispec.freq) == (2 * FFTLEN // 2 + 1)) assume(len(ispec.spectrum) == (2 * FFTLEN // 2 + 1)) assume(len(ispec) == len(ispec.spectrum)) assume(np.all(ispec.spectrum[::-1] == ispec[::-1])) assume(np.array(ispec, dtype='complex64').dtype == np.complex64) assume(ispec.__repr__().startswith('InvertedSyncSweepSpectrum('))
def test_linear_model(): sweep = SyncSweep(startfreq=16, stopfreq=16000, durationappr=10, samplerate=96000) def nonlinear_system(sig): return 0.5 * sig + 0.25 * sig**2 + 0.125 * sig**3 outsweep = nonlinear_system(np.array(sweep)) hhir = HigherHarmonicImpulseResponse.from_sweeps(sweep, outsweep) lm = LinearModel.from_higher_harmonic_impulse_response(hhir, length=1024) x = np.array([1] + 127 * [0]) y = lm.filter(x) with pytest.raises(ValueError): hm = LinearModel.from_higher_harmonic_impulse_response(hhir, 96e4, delay=0)
def test_higher_harmonic_impulse_response(): sweep = SyncSweep(10, 10000, 5, 20000) hhir = HigherHarmonicImpulseResponse.from_sweeps(sweep, sweep) assume(np.all(np.array(hhir) == hhir.hhir)) hir = hhir.harmonic_impulse_response(order=1, length=1024, delay=0, window=True) assume(type(hir) == np.ndarray) assume(len(hir) == 1024) assume(np.argmax(hir) == 512) hir = hhir.harmonic_impulse_response(order=1, length=1024, delay=0, window=np.linspace(0, 1, 1024)) assume(type(hir) == np.ndarray) assume(len(hir) == 1024) assume(np.argmax(hir) == 512)
def test_sync_sweep_instantiation(): SyncSweep(20, 20000, 4, 44100) SyncSweep(16, 16000, 5, 48000) with pytest.raises(TypeError): # must not be callable without arguments. SyncSweep()
def test_sync_sweep_instantiation_errors(startfreq, stopfreq, durationappr, samplerate): with pytest.raises(ValueError): SyncSweep(startfreq, stopfreq, durationappr, samplerate)
class Measurement(object): def __init__(self, config): self._config = config or DEFAULTS self._sweep = None def save_config(self, path): with open(_pathlib.Path(path)/_cfg.CONFIGFILENAME, 'w') as fp: return _yaml.dump(fp, self._config) @classmethod def from_config_file(cls, filepath): config = _cfg.load_config(filepath) return cls(**config) def run_adaptive_delay_and_decay_measurement(self): cfg = { **self._config, **self._config['measurement'], **self._config['sounddevice'] } stream_kwargs = {k: cfg[k] for k in ( 'samplerate', 'device', 'channels', 'dtype', 'blocksize')} callback = SounddeviceQueueCallback() with _sd.Stream( callback=callback, **stream_kwargs): noise, data, delay, decay = estimate_adaptive_delay_and_decay( input_queue=callback.input_queue, output_queue=callback.output_queue, noise_level_dbfs=cfg['noise']['level_dbfs'], noise_duration=cfg['noise']['duration'], **cfg) return noise, data, delay, decay def run_sweep_measurement(self): cfg = { **self._config, **self._config['sweep'], **self._config['measurement'], **self._config['sounddevice']} if not self._sweep: self._sweep = SyncSweep( startfreq=cfg['startfreq'], stopfreq=cfg['stopfreq'], durationappr=cfg['durationappr'], samplerate=cfg['samplerate'], ) sweep_playback_signal = self._sweep.get_windowed_signal( left=cfg['flanksamples'], right=cfg['flanksamples'], pausestart=cfg['pausestart'], pausestop=cfg['pausestop'], amplitude=10**(cfg['sweep']['level_dbfs']/20)) measured_sweep = _sd.playrec( sweep_playback_signal, samplerate=cfg['samplerate'], device=cfg['device'], input_mapping=cfg['channelmap_input'], output_mapping=cfg['channelmap_output'], blocking=True) return sweep_playback_signal, measured_sweep def run(self): cfg = { **self._config, **self._config['measurement']} auto = cfg['auto'] delays = cfg['delays'] irlength = cfg['irlength'] pausestart = cfg['pausestart'] pausestop = cfg['pausestop'] window = cfg['window'] _time.sleep(cfg['wait_before_start_sec']) if auto: (noise, measured_noises, delays, decay) = self.run_adaptive_delay_and_decay_measurement() pausestop = max(pausestop, decay) self._config['measurement']['pausestop'] = pausestop irlength = max(irlength, decay) else: measured_noises = None sweep, measured_sweeps = self.run_sweep_measurement() delays_total = _np.array(delays) + pausestart delays_total[:] = _np.median(delays_total).astype(_np.int) linear_models = estimate_lm_from_sweeps(self._sweep, measured_sweeps, irlength, delays_total, window) rirs = _np.array([lm.kernel.ir for lm in linear_models]).transpose() res = Result( uid=_uuid.uuid4().hex, config=self._config, input_sweep=sweep, input_noise=noise, measured_sweeps=measured_sweeps, measured_noises=measured_noises, delays=delays, delays_total=delays_total, samplerate=self._config['measurement']['samplerate'], rirs=rirs) return res
[1.0, -1.8996, 0.9025], [1.0, -1.9075, 0.9409], [1.0, -1.8471, 0.8649], ] B = [ [1.0, -1.9027, 0.9409], [1.0, -1.8959, 0.9025], [0.5, -0.9176, 0.4512], ] orders = [1, 2, 3] kernels_theo = [IirFilterKernel(*ba) for ba in zip(B, A)] hm_theo = HammersteinModel(kernels_theo, orders) # system identification of the theoretical system sweep = SyncSweep(f1, f2, dursec, samplerate) sweep_sig = sweep.get_windowed_signal(1024, 1024, pausestart=0, pausestop=512) outsweep = hm_theo.filter(sweep_sig) hhir = HigherHarmonicImpulseResponse.from_sweeps(sweep, outsweep, regularize=False) hm_identified = HammersteinModel.from_higher_harmonic_impulse_response( hhir=hhir, length=nfft, orders=orders, delay=0, window=True, ) # bode diagram of the theoretical and identification results figure() for theo, kernel, order in zip(hm_theo.kernels, hm_identified.kernels, orders): freq = kernel.freq
import numpy as np from syncsweptsine import SyncSweep from syncsweptsine import HigherHarmonicImpulseResponse from syncsweptsine import HammersteinModel import matplotlib.pyplot as plt sweep = SyncSweep(startfreq=16, stopfreq=16000, durationappr=10, samplerate=96000) def nonlinear_system(sig): return 1.0 * sig + 0.25 * sig**2 + 0.125 * sig**3 outsweep = nonlinear_system(np.array(sweep)) # hhir = HigherHarmonicImpulseResponse.from_sweeps(sweep, outsweep) #hm = HammersteinModel.from_higher_harmonic_impulse_response( # hhir, 2048, orders=(1, 2, 3), delay=0) hm = HammersteinModel.from_sweeps(sweep, outsweep, orders=(1, 2, 3), regularize=False) for kernel, order in zip(hm.kernels, hm.orders): plt.plot(abs(kernel.frf)) print('Coefficient estimate of nonlinear system:', np.round(np.max(abs(kernel.frf[500:1000])), 3), 'Order', order) plt.show()