Lab 11.42 — Последние проценты: ложный захват frame-sync¶
Цель¶
После починки DC-блокера (Lab 11.41) двухплатный канал под Gardner чист на ~92%. Понять, что за оставшиеся ~6–8% кадров, и закрыть их — либо доказать, что без RTL-изменения закрыть нельзя.
Отказы бимодальны¶
Первый шаг — не крутить ручки, а посмотреть, что вообще представляют собой грязные кадры. Разбивка залоченных кадров по числу ошибок payload:
- ~92% декодируются идеально;
- ~2–3% несут один неверный бит — обычный шум канала, разбросан по случайным индексам;
- ~2–3% несут ~117 из 256 бит неверно — 46%.
46% — это не маргинальный SNR. Кадр у порога шума теряет горстку бит, а не половину. Половина бит — это дискретное событие: весь бёрст декодирован в неверном повороте созвездия. Две популяции ошибок требуют разных объяснений; усреднение их в один «BER» прячет ту, что важна.
Где сидят грубые ошибки¶
Поворот на 90° и проскок несущей в середине оба дают ~50% BER, поэтому число ошибок их не различит.
А позиция — различит, и телеметрия gp_ctrl[15] её сообщает: какой квартиль отказывает первым и
индекс первой ошибки. По грубым кадрам:
- квартиль 0 чист лишь в ~1 из 17 грубых кадров;
- первая ошибка в битах 0–31 в ~16 из 17.
Порча начинается с бита 0 и охватывает весь кадр. Это исключает проскок в середине (он оставил бы ранний чистый участок) и фиксирует механизм как разворот всего бёрста с самого первого символа.
Каждый рантайм-рычаг — отвергнут¶
Приёмник уже разрешает 90-градусную неоднозначность QPSK четырёхветочным frame-sync, значит неверный поворот означает, что это разрешение промахнулось. До RTL каждая рантайм-гипотеза проверена и убита данными:
| Гипотеза | Рычаг | Результат |
|---|---|---|
| межплатный CFO путает захват | coarse CFO gp_ctrl[13] |
хуже — gross 5% → 16%, lock 100% → 84% |
| реакквизиция с нуля каждый бёрст | costas_hold_phase gp_ctrl[11] |
без эффекта (3.0% → 3.3%) |
| плохая фаза выборки | свип start_offset 0–7 |
плоско; прежняя «корреляция» была шумом |
| не хватает SNR | TX −38…−22 dBm | U-образно, минимум ~3% на −30 (ниже шум, выше компрессия) |
| рабочая точка RX | RX gain 30–70 | U-образно, минимум ~3% на gain 30–50 |
Обе оси мощности U-образны с полом на оптимуме, и ни один гейт его не убирает. Пол — не условие канала и не тюнинг; он структурный.
Причина — арифметическая¶
Арбитраж frame-sync (qpsk_ber_counter) — first-past-the-post по скользящей 24-битной корреляции:
первая ветка, нашедшая окно с промахом преамбулы ≤ LOCK_ERR_TOL, забирает бёрст, и у ветки A
приоритет. Комментарий модуля предполагал, что неверный квадрант «промахивается на ~12 из 24 бит
преамбулы, далеко за LOCK_ERR_TOL, поэтому захватывает ровно одна ветка». Это верно в истинной
позиции кадра. И игнорирует скользящий поиск по остальной части бёрста.
Неверный квадрант совпадает по каждому биту преамбулы с вероятностью ~½, поэтому на любой из ~140
скользящих позиций шанс окна с ≤ LOCK_ERR_TOL промахами равен C(24, ≤tol) / 2²⁴. По бёрсту:
python lab_11_42_ber_floor_lock_tolerance.py --predict
LOCK_ERR_TOL=3: per-position 1.4e-4 -> per-burst 1.9e-2
LOCK_ERR_TOL=2: per-position 1.8e-5 -> per-burst 2.5e-3
LOCK_ERR_TOL=1: per-position 1.5e-6 -> per-burst 2.1e-4
LOCK_ERR_TOL=0: per-position 6.0e-8 -> per-burst 8.3e-6
При отгруженном LOCK_ERR_TOL=3 это ~2% на бёрст — ложный захват, и он совпадает с измеренным
полом. Грубые отказы — это ветка A, скользящая по шумной части бёрста и лочащаяся на случайном окне
раньше, чем приходит истинный кадр.
Правка, и почему она не стоит lock rate¶
Ужесточить LOCK_ERR_TOL до 1: случайное окно падает ~100× до ~0.02% на бёрст. Очевидное опасение —
другая сторона компромисса: истинная преамбула с парой шумовых ошибок теперь не залочится и бёрст
потеряется. Здесь это безопасно по двум причинам:
- истинная преамбула совпадает почти идеально (у чистых кадров ноль ошибок преамбулы), поэтому лочится с запасом при tol=1;
- проверено симуляцией на реальных self-OTA захватах — и
tb_qpsk_rx_costas, и, что важно,tb_qpsk_costas_stress(который добавляет 3000 отсчётов шума перед кадром — ровно сценарий ложного захвата) декодируют при BER 0/280 сLOCK_ERR_TOL=1. Ужесточённый захват отвергает шум и всё равно берёт истинный кадр.
Полный пакет блока 5: 35/35. Правка — один параметр в инстанциации моста, но за ней арифметика выше и стресс-бенч, а не догадка.
Проверка на железе¶
Пересобрано при LOCK_ERR_TOL=1, передеплоено холодной загрузкой и прогнано через A TX1 → 30 дБ →
B RX1 (1200 бёрстов за два прогона):
| tol=3 (baseline) | tol=1 (эта сборка) | |
|---|---|---|
| чистых кадров | ~92% | 98.9% (1182/1195) |
| грубых (разворот всего бёрста) | ~3% | 0.75% (9/1195) |
| однобитный шум | ~2% | ~0.3% |
| lock rate | ~100% | 99.6% (5 потеряно из 1200) |
Пол упал в ~3–4×, чистота выросла с ~92% до ~99%. Пять потерянных кадров — намеренный размен: бёрст, у которого истинная преамбула несла ≥2 шумовых ошибки, теперь отвергается, а не декодируется криво, и потерянный кадр даёт ноль ошибок там, где грубый давал ~117.
Но арифметика обещала ~100×, а железо дало ~4× — значит модель была неполной, и честнее сказать
почему, а не округлить. Компонента скользящего ложного захвата (~2%) упала как предсказано (до
~0.02%); осталась другая причина неверного поворота, которую LOCK_ERR_TOL не трогает. Проверены
две:
- самоподобие преамбулы под поворотом — отвергнуто офлайн: 24-битная преамбула промахивается по своим 90° и 270° поворотам ровно на 12/24, по 180° на 24/24, то есть не легче случайного;
- бёрсты с маргинальной несущей — остаточная частота гуляла 0.3%→1.0% между прогонами, отслеживая медленный дрейф межплатного CFO, что указывает на бёрсты, где Costas садится у 45°-границы: преамбула лочится на верном повороте, но payload разворачивается. Это подлинное событие захвата, не ложный лок, и ужесточение корреляции на него не влияет.
Так что честный итог — устойчивое, понятое сокращение в ~4× до пола ~1%, а не 100× из
одномеханизменной модели. Пробить ниже ~1% потребует другого инструмента — дифференциального
кодирования или проверки согласованности payload, а не очередного шага lock-толеранса. Результат:
docs/assets/lab1142_ber_floor_live.json.
О чём эта лаба на самом деле¶
Баг DC-блокера — один блок доверял измерению, снятому не в том месте. Этот — один комментарий доверял допущению в одной точке («неверная ветка не может залочиться»), которое держалось в истинной позиции кадра и рушилось везде ещё, куда смотрел коррелятор. Оба закрыты одинаково: перестать усреднять, разделить популяцию отказов и идти за той, что несёт сигнал. Здесь арифметика скользящего коррелятора предсказала точный пол ещё до того, как повернули хоть одну ручку.