Лабораторная 8.5 — Мини-цепочка OFDM (CP, пилоты, синхронизация, эквализация)¶
Цель¶
Построить компактную OFDM-цепочку, включающую:
- обработку циклического префикса (CP);
- пилотные поднесущие;
- грубую синхронизацию кадра и оценку CFO (Schmidl & Cox);
- этап уточнения, снимающий неоднозначность грубой оценки;
- оценку канала по известной преамбуле и посимвольное отслеживание фазы по пилотам;
- отчёт по BER/EVM.
Почему это важно¶
Главный трюк OFDM это превратить частотно-селективный многолучевой канал в множество узких, плоских по замиранию каналов: данные распределяются по N поднесущим, так что каждая из них видит канал примерно постоянным на своей (гораздо более узкой) полосе. Циклический префикс (CP) это копия хвоста символа, добавленная перед ним это делает это точным, а не приближённым: пока CP не короче задержки многолучевости канала, линейная свёртка с каналом становится циклической внутри окна FFT, а FFT превращает её в поэлементное умножение на каждой поднесущей: Y[k] = H[k]·X[k]. Именно из-за этого единственного уравнения эквализация в OFDM это «поделить на одно комплексное число на каждой поднесущей», а не полноценный эквалайзер во временной области.
Это уравнение работает только если окно FFT приёмника начинается в нужном отсчёте, а несущие частоты совпадают. Ошибись в любом из двух, и каждая поднесущая получит наводку от соседей (межканальная интерференция) вместо чистого H[k]·X[k]. Поэтому прежде чем начать эквализацию, приёмник должен решить две задачи с нуля, без априорного знания о положении кадра: где начинается символ и какое остаточное смещение несущей частоты (CFO).
Преамбула Schmidl & Cox¶
build_preamble() строит один OFDM-символ, несущий BPSK-значения только на чётных используемых поднесущих, с нулями на нечётных. Размещение энергии лишь на каждой второй поднесущей делает временной сигнал преамбулы точно периодичным с половиной длины FFT: x[n] = x[n + N/2] для n из первой половины. Именно это свойство делает преамбулу самосинхронизирующейся: приёмнику не нужно знать переданные значения, достаточно знать, что это свойство повторяемости существует.
schmidl_metric() реализует классическую оценку Schmidl & Cox прямо из этого свойства, скользя окном длины l_half = fft_size // 2 по принятому сигналу:
P(d) = sum_{m=0}^{l_half-1} conj(x[d+m]) * x[d+m+l_half] -- корреляция двух половин
R(d) = sum_{m=0}^{l_half-1} |x[d+m+l_half]|^2 -- нормировка по энергии
M(d) = |P(d)|^2 / R(d)^2
M(d) близко к 1 там, где окно полностью содержит преамбулу с повторяющейся половиной (обе половины тогда почти идентичны с точностью до фазового наклона от CFO), и падает за пределами этой области. Из одного этого расчёта получаются сразу две вещи:
- Грубый тайминг: положение пика,
d_peak = argmax(M), приблизительно указывает, где лежит преамбула. - Грубый CFO: остаточный CFO величиной
fГц поворачивает вторую половину относительно первой наangle(P) = 2*pi*f*l_half/fs, поэтомуf_est = angle(P(d_peak)) * fs / (2*pi*l_half)восстанавливает его напрямую без перебора. Однозначный диапазон:±fs/(2*l_half), поскольку сама функцияangle()заворачивается на±pi.
Запустите лабораторную и сравните истинный и оценённый CFO, выведенные в конце; при штатном смещении 1250 Гц оценка обычно попадает в пределы нескольких герц: этого достаточно, чтобы убрать основную часть поворота до начала всей остальной обработки.
Почему одного пика недостаточно: проблема плато¶
M(d) не имеет резкого одноотсчётного пика: она остаётся близкой к максимуму на плато шириной примерно cp_len отсчётов, потому что свойство повторяемости половин выполняется для любого положения окна, при котором всё окно ещё лежит внутри области CP+символ. argmax выбирает какую-то точку этого плато, не обязательно истинную. В штатном запуске этой лабораторной coarse_start_sample обычно оказывается примерно на длину CP раньше истинной границы кадра: достаточно, чтобы понять, где искать, но недостаточно, чтобы поставить окно FFT.
refine_frame_start() решает это напрямую: после устранения грубого CFO функция коррелирует (уже скорректированный по CFO) сигнал с реальной известной формой преамбулы (а не только с её свойством повторяемости) в небольшом окне поиска вокруг грубой оценки, и оставляет смещение с наибольшей нормированной корреляцией (fine_sync_score, близкой к 1.0 при чистом совпадении). Это возможно только потому, что содержимое преамбулы известно приёмнику; это та же идея согласованного фильтра для синхронизации кадра, что используется в других местах курса, применённая здесь для снятия неоднозначности, которую энергетическая метрика Schmidl оставляет нерешённой.
Оценка канала: от одной преамбулы ко всем поднесущим¶
Как только окно FFT установлено правильно, FFT преамбулы даёт по одному уравнению на каждую чётную поднесущую: H[k] = Y_pre[k] / X_pre[k], поскольку X_pre[k] известен. Это покрывает только половину используемых поднесущих (те, где была энергия преамбулы): нечётные передавались нулём и не несут информации о H. interpolate_channel() заполняет пробел линейной интерполяцией действительной и мнимой частей по индексу поднесущей, что является разумной оценкой, пока частотная характеристика канала не меняется быстрее, чем расстояние между известными точками. Постройте график lab85_ofdm_channel_estimate.png, вы должны увидеть, как |H[k]| повторяет частотную характеристику трёхлучевого канала многолучевости: провалы там, где лучи интерферируют деструктивно, а не плоскую линию.
Посимвольная эквализация и отслеживание фазы по пилотам¶
Каждый последующий OFDM-символ данных декодируется одинаково: FFT, деление на интерполированное H[k] (одноотводная эквализация), затем вторая, меньшая коррекция. Оценка канала/CFO по преамбуле не идеальна в штатном запуске остаточная ошибка CFO порядка 10 Гц, мала по сравнению со смещением 1250 Гц, но не нулевая, и этот остаток поворачивает созвездие каждого последующего символа на небольшой, медленно накапливающийся угол. Четыре пилотные поднесущие (k = -21, -7, 7, 21, фиксированные опорные значения {+1, +1, +1, -1}) передаются в каждом символе данных именно для того, чтобы приёмник мог измерить этот остаток фазы напрямую (pilot_phase = angle(vdot(pilot_ref, y_eq[pilots]))) и убрать его перед декодированием поднесущих данных. Это стандартная причина, по которой реальные OFDM-системы (802.11, DVB) несут пилоты на протяжении всего бёрста, а не только в преамбуле: преамбула даёт стартовую оценку, а пилоты поддерживают её точность на протяжении всего кадра.
Цепочка обработки¶
flowchart LR
BITS[биты] --> MAP[QPSK mapper]
MAP --> OFDM[OFDM символ + CP]
OFDM --> CH[многолучевость + CFO + шум]
CH --> SYNC[грубая синхронизация + коррекция CFO]
SYNC --> FFT[FFT + коррекция фазы по пилотам]
FFT --> EQ[эквализация канала]
EQ --> DEC[жёсткие решения]
DEC --> METRICS[BER и EVM]
Исполняемый файл¶
| Файл | Назначение |
|---|---|
blocks/block_08_modulation_and_synchronization/python/lab_8_5_ofdm_mini_link.py |
OFDM TX/RX мини-линия с метриками |
Запуск из корня репозитория:
python blocks/block_08_modulation_and_synchronization/python/lab_8_5_ofdm_mini_link.py
Генерируемые артефакты¶
docs/assets/lab85_ofdm_sync_metric.png
docs/assets/lab85_ofdm_equalized_constellation.png
docs/assets/lab85_ofdm_channel_estimate.png
docs/assets/lab85_ofdm_metrics.json
Чего ожидать¶
При параметрах по умолчанию (FFT=64, CP=16, 24 OFDM-символа, CFO = 1250 Гц, SNR = 20 дБ, детерминированный seed) корректный запуск печатает:
Frame start coarse/refined/true: 410 / 420 / 420
Fine synchronization score: 0.9514
CFO true/estimated/error: 1250.000 / 1260.336 / 10.336 Hz
BER: 0.000000e+00 (0/2304)
EVM: 11.676% (-18.65 dB)
Читайте эти цифры вместе, а не по отдельности:
- Грубое начало на 10 отсчётов раньше истинного (на длину CP короче границы) это проблема плато выше, а не баг; уточнённое начало попадает точно на истинную границу, ради чего и нужен этап уточнения.
- Ошибка CFO (~10 Гц) мала, но не нулевая: это ровно тот остаток, для устранения которого посимвольно существует коррекция фазы по пилотам; если отключить эту коррекцию в модифицированном запуске, ожидайте роста EVM на протяжении бёрста по мере накопления некомпенсированной фазы.
- BER = 0 при ненулевом EVM (11,7 %, что вполне укладывается в ограниченную шумом область для QPSK при SNR = 20 дБ) это ожидаемый результат, а не противоречие: BER падает только тогда, когда ошибки решения реально пересекают границу символа, тогда как EVM уже отражает накопленную ошибку синхронизации, оценки и шума. Тот же урок: «BER может быть безупречным, пока EVM уже показывает деградацию»: повторяется и усиливается в Лабораторной 8.10.
- График оценки канала должен показывать изменение
|H[k]|в зависимости от индекса поднесущей (частотную характеристику трёхлучевой многолучевости), а не плоскую линию; плоская оценка обычно означает, что преамбула или шаг интерполяции не подключены к реальному каналу.
Следующий шаг: от модели к RTL¶
Эта Python-модель это эталон для синтезируемого OFDM-тракта, реализованного и самопроверяемого в Лабораторной 8.14: OFDM RTL: от mapper до эквализированного loopback: QPSK mapper, распределитель/экстрактор поднесущих, потоковый radix-2 IFFT/FFT, вставка/удаление циклического префикса и одноотводный эквалайзер, каждый со своим самопроверяющим тестбенчем на Icarus Verilog, сверенным побитово с Q1.15 fixed-point моделью той же цепочки.
Что включить в отчёт¶
- График метрики синхронизации и оценённый стартовый отсчёт.
- Оценку CFO и остаточную ошибку.
- Оценку канала и эквализированное созвездие.
- BER и EVM.
- Объяснение упрощений по сравнению с полным OFDM-модемом.