Этап 3c — гибрид GF+ARQ, пересборка NACK, приёмка. Техдолг #6 закрыт

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.
This commit is contained in:
Maxim
2026-07-16 15:29:25 +03:00
parent 6294a472cd
commit ab1e87a17e
6 changed files with 224 additions and 49 deletions

View File

@@ -10,7 +10,9 @@
#
# Использование: scripts/test_arq.sh [ipA] [ipB] [размер_КБ]
# SIZE_KB=10240 — длинный прогон (или третьим позиционным аргументом)
# TXGAIN=-45 — стресс: высокий PER, ARQ обязан вытянуть md5
# TXGAIN=-33 — стресс: высокий PER, ARQ обязан вытянуть md5. Клифф на
# кабеле резкий: 30 ≈ PER 0.4%, 38 и глубже — CRC 0
# (канала нет вовсе, проверять там нечего)
# NEGATIVE=1 FBGAIN=-89 — обратный канал заглушён: успех = A НЕ подтвердил
# FBGAIN (деф. 15) — середина рабочего окна 20…10 (README §12.8)
# FBFREQ, PAUSE, TXGAIN, FEC, GROUPFEC — как в test_fdd_fwd.sh
@@ -134,7 +136,8 @@ else
echo " - md5 разошёлся при коде A=0 → COMPLETE поднят рано (проверь miss/next_out)."
echo " - код A=2 при целом md5 → COMPLETE не долетел: подними FBGAIN к 10."
echo " - размер 0 → RX не декодит: остаточный процесс на B или самоглушение."
echo " - GROUPFEC=1: ARQ там пока выключен (этап 3c) → потеря кадра даёт код A=2."
echo " - CRC: 0 при живых 'Заголовках' → канал за клиффом (TXGAIN ≤ 38 на кабеле):"
echo " заголовок BPSK ещё держится, payload QPSK — нет. Это не стресс ARQ, а обрыв."
echo " - 'NACK вне окна' > 0 на B → дыра пережила TX-окно (2048 кадров ≈ 21 с)."
echo "--- хвост rx_arq.log (B) ---"
"${SSH[@]}" "root@$B" "tail -8 /tmp/rx_arq.log" || true