ARQ включён и для group-FEC. Паритет и ретрансмит перестали быть альтернативами:
GF гасит одиночные потери без задержки, ARQ добирает то, что паритету не по силам
(>2 стираний/группу). Дыру, закрытую паритетом, RX снимает с NACK сам — лишнего
трафика нет.
receiver.c:
- group_route: кадр ложится в кольцо ДО любого bypass. Кольцо — единственный
источник порядка и живёт независимо от сборки групп; иначе ретрансмит закрытой
группы не занял бы свой слот и next_out встал бы на нём навсегда. Паритет
получает слот нулевой длины: тратит seq, но в файл не идёт.
- group_route: bypass для кадра уже закрытой группы (base < g.base, а также
base == g.base при !active — группа финализирована по END). Без него ретрансмит
преждевременно flush-ал активную группу, сбрасывая её стирания, либо «воскрешал»
закрытую с одним элементом.
- group_flush: под FDD пишет в кольцо ТОЛЬКО восстановленные паритетом кадры
(g.fixed) и снимает их seq с NACK — принятые уже в кольце. Симплекс сохраняет
прямой вывод группы: ретрансмитов там нет, переупорядочивать нечего.
- process_end: досрочный group_flush последней группы. Штатно группа закрывается
первым кадром следующей — для последней его не будет, и её восстановление
случилось бы только при выходе потока C, то есть уже после COMPLETE.
- process_end/miss_rebuild: NACK пересобирается из кольца на каждом END. Находка
стресса 3b: miss_add при переполнении MISS_CAP молча терял seq, и вернуть его
было некому (детектор дыр по нему второй раз не срабатывает) — приём заклинивало
насмерть. Кольцо знает точно, каких кадров нет.
- arq_check_complete: критерий единый (next_out == total_seq), временный gf_seen
убран.
transmitter.c: g_arq_on = 1 безусловно при -F; arq_service в GF-цикле как в legacy.
Раньше A после EOF молча закрывал буфер и рапортовал успех независимо от
того, что доехало. Теперь передача завершается подтверждением от B.
- common.h: HDR_SIG_END_1 0xE7 — третий тип кадра (данные F0 AA, END F0 E7,
STATUS F0 55). Тип задаёт сигнатура, в payload не кодируется.
- feedback.{h,c}: fb_end_t{total_seq,total_bytes} + pack/unpack, 12 Б BE.
Чистая логика без liquid/iio, как и STATUS.
- transmitter.c: fb_callback публикует STATUS снимком под мьютексом
(latest-wins — STATUS кумулятивен, очередь не нужна); TX-буфером владеет
только main. end_phase(): END каждые 100 мс + опрос снимка, до 10 с;
COMPLETE → exit 0, таймаут → exit 2. Симплекс идёт прежним путём.
- receiver.c: process_end() ветвится по сигнатуре ДО seq-детектора (у END
свой счётчик seq) и делает backfill хвостовых дыр в NACK — иначе потеря
последних кадров молчалива: нет следующего seq, по которому видна дыра.
arq_check_complete() поднимает FB_FLAG_COMPLETE, держит 600 мс (≥6 STATUS
в тихом эфире END-фазы) и завершает приём штатным stop.
- scripts/test_arq.sh: проверяет протокол, а не только md5 — сторожевой kill
приёмника = провал (B обязан выйти сам), код возврата A штатный отчёт,
режим NEGATIVE=1.
Прогон 1 МБ GROUPFEC=0 PAUSE=4000, кабель:
END 98 отправлено → 98 принято (100%); backfill «не хватает 3» точно равен
«Потери seq: 3»; A честно exit 2 (ретрансмитов ещё нет — 3b).
Негатив FBGAIN=-89: доставка STATUS 0%, A exit 2 за 10.0 с — OK.
Регрессия симплекса: Кадров 1024 ровно, END не просочился; потери 5/7/10/6
на 4 прогонах — уровень базы §12.8 (3/5/14).
Групповой FEC над GF(256), RAID-6-стиль: 16 кадров данных + P(⊕) + Q(Σgⁱ·dᵢ)
закрывают до 2 стёртых кадров на группу. Потеря (пропуск seq или битый CRC) —
это стирание с известной позицией, поэтому 2 паритета оптимальны (overhead 12.5%,
~99% передач 10 МБ байт-в-байт при PER 0.26%).
- src/group_fec.{c,h}: GF(256) polynom 0x11d, gf_encode/gf_recover + self-test
(make gftest, нативный cc). Восстановление 1 стирания через P или Q, 2 — P+Q.
- transmitter.c: флаг -G, вынесен tx_frame(); копит группу, шлёт k данных +
P/Q с флагами в hdr[11]. Мета паритета: k′ + длина хвостового кадра.
- receiver.c: групповой ассемблер в потоке C, автодетект по hdr[11], финализация
группы на смене базы seq и на выходе потока. Счётчики Rec/GrpF в статистике.
- hdr[11] (был резерв): bit7 режим, bit6 паритет, bit5 Q, bits4-0 idx. RS/CRC
слой прозрачен — геометрия кадра не тронута, PHY-бенчмарк не требуется.
- test_file_link.sh: GROUPFEC=1 по умолчанию (TX -G, RX автодетект).
README §12.7 + перенумерация роадмапа — отдельным коммитом после верификации.
- src: transmitter, receiver, ofdm_bench, common (libiio local: + liquid-dsp)
- scripts: кросс-сборка зависимостей, деплой scp -O, e2e-тест с md5
- docs: бенчмарк CPU (выбор rate 1.92 MSPS), грабли FDD/регуляторика
- все текстовые файлы нормализованы в LF (.gitattributes принудительно):
CRLF в toolchain.env/makefile ломал source и make на Linux
- .vscode: IntelliSense в режиме linux-gcc-arm, заголовки из .xarm/include
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>