"""Synthetic test signals shared by the test modules.""" import numpy as np from scipy.signal import butter, hilbert, lfilter FS = 32000.0 def _noise(x, snr_db, rng): p = float(np.mean(np.abs(x) ** 2)) n = np.sqrt(p / (2 * 10 ** (snr_db / 10.0))) return (x + n * (rng.standard_normal(x.size) + 1j * rng.standard_normal(x.size))).astype(np.complex64) def voice(n, fs=FS, seed=0): """Band-limited noise with a syllabic envelope -- a good speech stand-in.""" rng = np.random.default_rng(seed) b, a = butter(4, [300 / (fs / 2), 2700 / (fs / 2)], btype="band") v = lfilter(b, a, rng.standard_normal(n)) v /= max(np.abs(v).max(), 1e-9) t = np.arange(n) / fs return v * (0.4 + 0.6 * np.abs(np.sin(2 * np.pi * 1.7 * t))) def make(kind, n=64000, fs=FS, snr_db=30.0, seed=3): rng = np.random.default_rng(seed) t = np.arange(n) / fs v = voice(n, fs, seed) if kind == "nfm": msg = v + 0.15 * np.sin(2 * np.pi * 100.0 * t) x = np.exp(1j * np.cumsum(2 * np.pi * 2500 * msg / fs)) elif kind == "wfm": x = np.exp(1j * np.cumsum(2 * np.pi * 3000 * v / fs)) elif kind == "am": x = ((1 + 0.6 * v) * np.exp(2j * np.pi * 30 * t)) elif kind == "usb": x = 0.5 * hilbert(v) elif kind == "lsb": x = 0.5 * np.conj(hilbert(v)) elif kind == "carrier": x = np.exp(2j * np.pi * 137 * t) elif kind == "cw": dot = 0.08 pat = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0] key = np.zeros(n) i = 0 while i < n: for b in pat: m = int(dot * fs) if i + m > n: break key[i:i + m] = b i += m env = np.convolve(key, np.hanning(int(0.005 * fs)), "same") env /= max(env.max(), 1e-9) x = env * np.exp(2j * np.pi * 300 * t) elif kind.startswith("fsk"): levels = int(kind[3]) baud, dev = (1200.0, 2400.0) if levels == 2 else (4800.0, 1800.0) sp = int(fs / baud) sym = rng.integers(0, levels, n // sp + 1) lv = (sym - (levels - 1) / 2) / max(1, (levels - 1) / 2) f = np.resize(np.repeat(lv, sp) * dev, n) x = np.exp(1j * np.cumsum(2 * np.pi * f / fs)) elif kind.startswith("psk"): m = int(kind[3]) sp = int(fs / 4800.0) sym = rng.integers(0, m, n // sp + 1) x = np.exp(1j * np.resize(np.repeat(2 * np.pi * sym / m, sp), n)) elif kind == "noise": return (0.01 * (rng.standard_normal(n) + 1j * rng.standard_normal(n))).astype(np.complex64) else: raise ValueError(kind) return _noise(np.asarray(x, dtype=np.complex128), snr_db, rng)