Перейти к содержанию

Лабораторная 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-модемом.