Оконные функции и утечка спектра

Разбираем, почему реальные спектры «грязнее» идеальных и как окна наводят порядок.

Утечка спектра (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: больше отсчётов — тоньше различение.
  • Окно — компромисс: меньше боковых лепестков ценой чуть худшего разрешения.
Проверьте себя
1. Что такое утечка спектра?
AПотеря отсчётов при дискретизации
BРазмазывание энергии тона по соседним бинам, когда его частота не на бине
CШум квантования
DАлиасинг
2. Зачем применяют оконные функции (Ханна, Хэмминга)?
AЧтобы усилить сигнал
BЧтобы плавно погасить края и уменьшить утечку спектра
CЧтобы повысить частоту дискретизации
DЧтобы ускорить БПФ
3. Чему равно разрешение ДПФ по частоте?
Afs
Bfs/2
Cfs/N
DN/fs
4. Какова плата за применение окна?
AСпектр становится бесполезным
BНемного ухудшается разрешение (шире главный лепесток)
CПоявляется алиасинг
DПлаты нет