Оконные функции и утечка спектра
Разбираем, почему реальные спектры «грязнее» идеальных и как окна наводят порядок.
Утечка спектра (spectral leakage) — размазывание энергии тона по соседним частотам, когда его частота не попадает точно на бин ДПФ. Оконная функция плавно «гасит» края сигнала, уменьшая утечку.
В учебных примерах тоны магически давали идеальные одиночные линии. В жизни так не бывает: частота сигнала почти никогда не совпадает точно с бином ДПФ, и энергия «растекается» по соседям. Это утечка спектра — главный источник ошибок при спектральном анализе. Лечится она оконными функциями.
Откуда берётся утечка
ДПФ неявно считает, что ваш кусок сигнала повторяется бесконечно. Если на окне укладывается целое число периодов тона — стык гладкий, спектр чистый. Если нет — на стыке возникает «разрыв», который и порождает ложные частоты. Сравним тон точно на бине и тон между бинами.
import math, cmath
def dft(x):
N = len(x)
return [sum(x[n] * cmath.exp(-2j * math.pi * k * n / N) for n in range(N))
for k in range(N)]
def mag(sig):
X = dft(sig); N = len(sig)
return [round(abs(X[k]) / (N / 2), 3) for k in range(N // 2 + 1)]
N, fs = 16, 16.0
on_bin = [math.sin(2 * math.pi * 3.0 * n / fs) for n in range(N)] # ровно на бине
off_bin = [math.sin(2 * math.pi * 3.5 * n / fs) for n in range(N)] # между бинами
print("3.0 Гц (на бине): ", mag(on_bin))
print("3.5 Гц (между бин.):", mag(off_bin))
Вывод:
3.0 Гц (на бине): [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0] 3.5 Гц (между бин.): [0.152, 0.168, 0.239, 0.654, 0.628, 0.212, 0.136, 0.11, 0.103]
Тон 3.0 Гц дал чистую линию. Тон 3.5 Гц «размазался» на все бины — энергия утекла повсюду. А ведь это один-единственный чистый синус! Без обработки спектр вводит в заблуждение.
Оконные функции: гасим края
Окно — это набор весов, плавно спадающих к нулю на краях. Умножив сигнал на окно, мы убираем «разрыв» на стыке и резко уменьшаем утечку. Популярны окна Ханна и Хэмминга.
import math
N = 8
hann = [round(0.5 - 0.5 * math.cos(2 * math.pi * n / (N - 1)), 3) for n in range(N)]
hamming = [round(0.54 - 0.46 * math.cos(2 * math.pi * n / (N - 1)), 3) for n in range(N)]
print("Ханна: ", hann)
print("Хэмминга:", hamming)
Вывод:
Ханна: [0.0, 0.188, 0.611, 0.95, 0.95, 0.611, 0.188, 0.0] Хэмминга: [0.08, 0.253, 0.642, 0.954, 0.954, 0.642, 0.253, 0.08]
Окно Ханна спадает к нулю на краях; Хэмминга оставляет небольшой «пьедестал» (0.08). Применим окно Ханна к нашему «утекающему» тону 3.5 Гц.
import math, cmath
def dft(x):
N = len(x)
return [sum(x[n] * cmath.exp(-2j * math.pi * k * n / N) for n in range(N))
for k in range(N)]
def mag(sig):
X = dft(sig); N = len(sig)
return [round(abs(X[k]) / (N / 2), 3) for k in range(N // 2 + 1)]
N, fs = 16, 16.0
off = [math.sin(2 * math.pi * 3.5 * n / fs) for n in range(N)]
w = [0.5 - 0.5 * math.cos(2 * math.pi * n / (N - 1)) for n in range(N)]
off_win = [off[n] * w[n] for n in range(N)]
print("3.5 Гц с окном Ханна:", mag(off_win))
Вывод:
3.5 Гц с окном Ханна: [0.005, 0.013, 0.104, 0.406, 0.406, 0.104, 0.012, 0.003, 0.002]
Энергия на далёких бинах упала в десятки раз (с 0.15 до 0.005): утечка собралась вокруг истинной частоты между бинами 3 и 4. Спектр стал честнее.
Разрешение по частоте
Разрешение по частоте — минимальная разница частот, которую ДПФ способно различить, равная fs/N. Хотите различать тоны через 1 Гц при fs = 1000 — нужно минимум N = 1000 отсчётов (1 секунда записи). Здесь компромисс: окно улучшает «чистоту» (меньше утечка), но слегка расширяет главный лепесток — то есть платит небольшим ухудшением разрешения за резкое снижение боковых лепестков.
Как работает под капотом
Умножение сигнала на окно во временной области эквивалентно свёртке спектра со спектром окна в частотной. У прямоугольного «окна» (то есть без окна) спектр — функция sinc с высокими боковыми лепестками: они и есть утечка. У окна Ханна боковые лепестки гораздо ниже, поэтому свёртка с ним «приглушает» растекание. Платой служит более широкий главный лепесток — отсюда ухудшение разрешения. Выбор окна — всегда баланс: прямоугольное даёт лучшее разрешение, но худшую утечку; Ханна/Хэмминга/Блэкмана — наоборот. Универсального окна нет, его подбирают под задачу.
Частые ошибки
- Анализировать спектр без окна. Для произвольного сигнала утечка исказит картину; почти всегда применяют окно.
- Ждать, что окно повысит разрешение. Наоборот, оно чуть ухудшает разрешение ради подавления утечки.
- Брать слишком короткий
N. Разрешениеfs/Nгрубое; близкие тоны сольются. Нужно больше отсчётов.
Итог
- Утечка спектра возникает, когда частота тона не попадает на бин ДПФ.
- Оконные функции (Ханна, Хэмминга) гасят края сигнала и уменьшают утечку.
- Разрешение по частоте равно
fs/N: больше отсчётов — тоньше различение. - Окно — компромисс: меньше боковых лепестков ценой чуть худшего разрешения.