docs: group-FEC 16+2 задокументирован, рабочая точка соука -p 4000 (§12.7)

- README §12.7 — журнал этапа (расчёт 16+2, таблица прогонов, находка overrun)
- README §10 — перенумерация: п.6=group-FEC, ARQ/FDD→п.7, туннель→п.8, видео→п.9
- README §9 — техдолг #6 обновлён (group-FEC закрывает симплекс-потери)
- docs/BENCHMARK.md, CLAUDE.md, test_file_link.sh — соук-точка -p 4000
- transmitter.c — снят -Wdiscarded-qualifiers на crc_generate_key (const-каст)
This commit is contained in:
Maxim
2026-07-15 09:48:26 +03:00
parent 23b60c6fa2
commit 4bf1a6df7b
5 changed files with 95 additions and 16 deletions

View File

@@ -30,6 +30,10 @@ PHY: OFDM через liquid-dsp 1.8.0 + FFTW/NEON, FEC RS(255,223).
- FEC RS(255,223), payload ≤ 1024 Б исходных (≤ 5 блоков, 1275 Б в эфире)
- Симплекс-тест: обе платы 915 МГц. FDD (этап 2): A TX915/RX868, B зеркально
- RX gain 50 дБ manual; TX gain: кабель 40, антенны от 20
- Пауза TX: короткие ≤1 МБ — `-p 3000`; длинные/group-FEC — `-p 4000` (§12.7).
НА 10 МБ-СОУКЕ `-p 3000` СРЫВАЕТ ядро 0 (~60 Overrun) — только `-p 4000`.
- Защита информации (шифрование/аутентификация) — отложена, требования ОКР нет.
Формат кадра с резервом; при появлении требования — лёгкий AEAD, +16 Б/кадр.
## Формат кадра (заголовок 12 Б)
0-1: 0xF0 0xAA | 2-5: seq u32 BE | 6-7: len u16 BE | 8-9: nblocks |

View File

@@ -235,7 +235,7 @@ ssh root@192.168.2.1 '/tmp/ofdm_bench 1.92'
| 3 | `fec_decode()` liquid не сообщает о неисправимых RS-блоках → счётчик `rs_saved` фиктивен. Достоверный критерий — только CRC/md5 поверх данных | ✅ исправлено: CRC32 поверх данных до RS на TX, сверка после декода на RX; `rs_saved`/`rs_fail` достоверны (подтверждено loopback'ом, §12.2) |
| 4 | Внешний NCO-CFO в receiver: `set_phase(0)` на границах буферов рвёт фазу; ofdmflexframesync и так компенсирует CFO сам | ✅ исправлено: внешний NCO удалён, CFO оставлен только как телеметрия |
| 5 | TX пушит весь буфер 16384 сэмпла при кадре ~10300 → ~35% эфира впустую + паузы `-p` | ✅ исправлено: `iio_buffer_push_partial(idx)` + дренаж хвоста DMA перед закрытием |
| 6 | Нет ARQ: потерянный кадр = молчаливая дыра в файле. seq в заголовке есть, но RX его игнорирует | ⚙️ частично: добавлен детектор пропусков seq (лог `[ПОТЕРЯ]` + счётчик «Потери seq»); ARQ ещё нет |
| 6 | Нет ARQ: потерянный кадр = молчаливая дыра в файле. seq в заголовке есть, но RX его игнорирует | ⚙️ частично: детектор пропусков seq (лог `[ПОТЕРЯ]` + счётчик) + **групповой стирающий FEC 16+2 закрывает ≤2 потери/группу на симплексе без обратного канала (§12.7, п.6)**; полный ARQ — на дуплексе (п.7) |
## 10. Дорожная карта
@@ -247,17 +247,23 @@ ssh root@192.168.2.1 '/tmp/ofdm_bench 1.92'
✅ первый OTA-приём — 10 КБ байт-в-байт, EVM 22.7 дБ (§12.3)
✅ пауза `-p 6000` убрала throughput-потери (100 КБ — 100/100);
на 1 МБ остаточный PER ≈ 0.2% (2 кадра). Побайтовый md5 на объёме отложен
на ARQ/дуплекс (п.6) — решение §12.3; веха симплекса = PHY-линия + PER
на ARQ/дуплекс (п.7) — решение §12.3; веха симплекса = PHY-линия + PER
4. ✅ антенны на столе — PER 10 МБ ≈ 0.36%, свип TX gain (§12.4),
рабочая точка 20 дБ; линия SNR-ограничена, целостность 100%
5. ✅ **3-стадийный RX-конвейер** (§12.6): ядро 0 — только
`ofdmflexframesync_execute`; ядро 1 — refill+конвертация **и RS-декод**.
Рабочая пауза `-p 6000` → `-p 3000`, throughput 90 → 120 КБ/с (**×1.36**),
FQ drops 0 (RS не узкое место). `-p 0` на этом PHY недостижим — осознанное
ограничение; побайтовый лосслесс — через ARQ (п.6).
6. FDD 868/915 двусторонняя + ARQ с обратным каналом + меры из docs/NOTES.md
7. UDP-туннель поверх линка; затем web-настройка (libmicrohttpd)
8. Видео (raw UDP, пакеты ≤1472 Б) — при устойчивом PER
ограничение; побайтовый лосслесс на симплексе — групповым FEC (п.6, §12.7).
6. ✅ **Групповой стирающий FEC 16+2** (§12.7): восстановление молчаливых
потерь кадров БЕЗ обратного канала (RAID-6-стиль P+Q над GF(256): 16 кадров
данных + 2 паритета, закрывает ≤2 стёртых кадра/группу). Соук 10 МБ
байт-в-байт на рабочей точке `-p 4000`. Защита информации (лёгкий AEAD,
+16 Б/кадр, <1% CPU) — отложена, требования ОКР «R-Link» нет; формат
с резервом под неё.
7. FDD 868/915 двусторонняя + ARQ с обратным каналом + меры из docs/NOTES.md
8. UDP-туннель поверх линка; затем web-настройка (libmicrohttpd)
9. Видео (raw UDP, пакеты ≤1472 Б) — при устойчивом PER
## 11. Диагностика (шпаргалка)
@@ -340,13 +346,13 @@ TX паузой `-p 6000` возвращает систематическую п
Дальнейшие рычаги: (1) **устойчивая передача файла** требует избыточности —
на симплексе это forward-redundancy (TX гонит файл N раз, RX пишет кадры по
`seq` в нужное смещение, дыры заполняются за проходы), полноценный ARQ — на этапе
дуплекса (§10 п.6); (2) двухпоточный конвейер RX (BENCHMARK §«резервы» п.2) —
дуплекса (§10 п.7); (2) двухпоточный конвейер RX (BENCHMARK §«резервы» п.2) —
поднимет throughput и позволит убрать паузу. `test_file_link.sh` теперь с
параметрами `PAUSE`/`FEC`/`TXGAIN`. **Рычаг (2) реализован и опровергнут —
см. §12.5.**
**Решение (2026-07-14):** побайтовый лосслесс на объёме отложен на этап дуплекса —
там ставится полноценный ARQ с обратным каналом (§10 п.6). На текущем симплекс-
там ставится полноценный ARQ с обратным каналом (§10 п.7). На текущем симплекс-
этапе forward-redundancy не внедряем; веха этапа — работоспособная PHY-линия и
измеренный PER.
@@ -375,7 +381,8 @@ TX паузой `-p 6000` возвращает систематическую п
**Итог симплекс-этапа:** PHY OTA-линия работает и охарактеризована по мощности,
целостность 100% (мусор в файл не попадает), PER ≈ 0.10.3% в рабочей зоне.
Остаток к «надёжной побайтовой» передаче = ARQ на этапе дуплекса (§10 п.6).
Остаток к «надёжной побайтовой» передаче на симплексе закрыт групповым FEC
(§12.7, §10 п.6); полный ARQ — на этапе дуплекса (§10 п.7).
### 12.5 Двухпоточный RX и диагностика паузы (2026-07-14)
@@ -436,8 +443,67 @@ docs/BENCHMARK.md «Разбивка стоимости по стадиям»):
~90 КБ/с на `-p 6000` = **×1.36** (не ×45 — эфир кадра 5.33 мс доминирует над
паузой; прежняя оценка исправлена).
- `-p 0` по-прежнему недостижим (sync сам 0.86× realtime, §12.5) — осознанное
ограничение PHY, лосслесс на объёме — через ARQ (§10 п.6).
ограничение PHY, лосслесс на объёме — групповым FEC (§12.7) или ARQ (§10 п.7).
### 12.7 Групповой стирающий FEC 16+2 (§10 п.6) — 2026-07-15
Симплекс закрывает молчаливые потери кадров БЕЗ обратного канала (ARQ требует
дуплекса, п.7). Потери одиночные и с известной позицией (из seq) — это
*стирания*, поэтому оптимально стирающее кодирование, а не дублирование.
**Метод (по расчёту).** Группа = 16 кадров данных + 2 паритета над GF(256)
(полином 0x11d, генератор g=2, RAID-6-стиль): P = ⊕dᵢ, Q = ⊕gⁱ·dᵢ. Позиции
стираний известны → 2 паритета закрывают ≤2 стёртых кадра на группу.
| Вариант | Overhead | Остаток на 10 МБ (PER 0.26%) |
|---|---|---|
| Дублирование 2× | 100% | ~0.07 кадра |
| XOR 16+1 | 6.25% | ~0.6 группы — НЕ лосслесс |
| **16+2 (выбрано)** | **12.5%** | **~0.009 группы** → ~99% передач байт-в-байт |
Формат: hdr[11] (был резерв) = флаги `[bit7 GF_MODE][bit6 GF_PARITY][bit5 GF_Q]
[bits4-0 idx 0..17]`; hdr[11]=0 = legacy (RX автодетект по первому кадру, старый
путь не тронут). Паритетный кадр несёт 1026 Б (2 Б меты `k`/`tail_len`
+ 1024 Б паритета) → те же 5 RS-блоков (1275 Б в эфире), что и данные →
геометрия PHY не меняется, бенчмарк §2 в силе (правило №6 не срабатывает).
GF-математика самопроверена офлайн: `make gftest` — **774400 кейсов** (все
`k=1..16`, все пары стираний, все комбинации наличия P/Q), **0 ошибок**.
Стоимость GF-кодека < 0.2% ядра, ложится в поток C (RS).
**Прогоны OTA (2026-07-15, TXGAIN 20, симплекс 915 МГц):**
| Тест | Размер | `-p` | Потери seq | Overrun | GF восст | GF провал | md5 |
|---|---|---|---|---|---|---|---|
| Регрессия legacy (`GROUPFEC=0`) | 1 МБ | 3000 | 0 | 0 | — | — | ✅ |
| Group | 1 МБ | 3000 | 4 | 0 | 4 | 0 | ✅ |
| Group стресс (29 дБ) | 1 МБ | 3000 | 3 | 0 | 3 | 0 | ✅ |
| **Соук** | 10 МБ | 3000 | 208 | **62** | 38 | **44** | ✗ |
| Диагностика legacy (`GROUPFEC=0`) | 10 МБ | 3000 | 213 | 63 | — | — | ✗ |
| **Соук** | 10 МБ | 4000 | 33 | **0** | 31 | **0** | ✅ **байт-в-байт** |
**Ключевая находка — overrun рвёт стирающий FEC на длинной дистанции.**
Соук на `-p 3000` дал **44 провала группы** против ~6 по независимой модели
(λ=0.42 стирания/группу, Пуассон) = **×7 сверх ожидания** → потери
**кластеризуются в пачки**, а не рассыпаны. Источник — 62 overrun ядра 0:
каждый роняет подряд идущие seq = подряд идущие idx одной группы, пробивая
бюджет ≤2. Диагностика (`GROUPFEC=0`, тот же 10 МБ) дала те же 63 overrun и
2.7% потерь → **срыв ядра 0 присущ sustained-соуку сам по себе, group-FEC ни
при чём**; рабочая точка `-p 3000` из §12.6 снята на коротких прогонах и на
10 МБ-соук не переносится (поток B не держит realtime на непрерывном потоке
дольше).
**`-p 4000` даёт ядру 0 запас → Overrun 0 → чистый floor** (33 стирания на
640 групп = λ=0.05, независимые одиночные) → 16+2 закрыл всё с запасом
(GF провал 0), **10 МБ байт-в-байт**. Интерливер (разнос кадров группы против
пачек) оказался **не нужен**: убрав overrun паузой, получили редкие
независимые стирания, где у 16+2 колоссальный margin.
**Рабочая точка group-FEC / длинных передач: `-p 4000`** (~95 КБ/с; цена
против `-p 3000` — ~12 КБ/с за Overrun 0 и лосслесс). Короткие передачи
(≤1 МБ) остаются рабочими на `-p 3000` (§12.6). Защита информации (лёгкий
AEAD, +16 Б/кадр, <1% CPU) отложена, требования ОКР «R-Link» нет; формат
кадра с резервом под неё.
---
*Ввод в строй: 14-07-2026. Замеры CPU и базовые решения10-07-2026.
*Ввод в строй: 1415-07-2026. Замеры CPU и базовые решения 10-07-2026.
Прошивка v0.38, liquid-dsp 1.8.0.*

View File

@@ -137,8 +137,13 @@ sync-only **1.649 vs 1.652** без него = 0.2%. Гипотеза кэш-к
Эмпирическое колено ~2500 мкс выше нуль-запасной модели 1333 из-за джиттера
планировщика (запас ~2×). Throughput 90 120 КБ/с = **×1.36** (не ×45:
эфир кадра 5.33 мс доминирует над паузой). Абсолютный `-p 0` недостижим
sync сам 0.86× realtime; лосслесс на объёме через ARQ или дешевле PHY
(M=32/CP=8, резерв п.1).
sync сам 0.86× realtime; лосслесс на объёме групповым FEC (README §12.7),
ARQ или дешевле PHY (M=32/CP=8, резерв п.1).
> ⚠️ **Соук-точка (README §12.7, 2026-07-15):** `-p 3000` держит Overrun 0 на
> коротких прогонах (≤1 МБ), но на 10 МБ-соуке ядро 0 всё же срывается
> (~60 Overrun, потери 2.7%) — рабочая точка §12.6 снята на коротких прогонах.
> Для длинных передач / группового FEC — **`-p 4000`** (Overrun 0, ~95 КБ/с).
## Неисследованные резервы (если понадобится > 1.92 MSPS)

View File

@@ -16,8 +16,10 @@ RATE=1920000 # НЕ поднимать: бюджет CPU, docs/BE
BW=1500000
TXGAIN="${TXGAIN:--30}" # OTA: переопредели, напр. TXGAIN=-20 scripts/test_file_link.sh
FEC="${FEC:-rs8}" # rs8 | none — фаза 3.2 гоняет сначала none, потом rs8
PAUSE="${PAUSE:-3000}" # пауза TX между кадрами, мкс. Рабочая точка 3-стадийного
# RX (§12.6): чисто, ~120 КБ/с. Ниже ~2500 — Overrun на ядре 0
PAUSE="${PAUSE:-3000}" # пауза TX между кадрами, мкс. §12.6: `-p 3000` чист на
# коротких (≤1 МБ). §12.7: на 10 МБ-соуке ядро 0 срывается
# (~60 Overrun) — для длинных/group-FEC ставь PAUSE=4000.
# Ниже ~2500 — Overrun на ядре 0 сразу
GROUPFEC="${GROUPFEC:-1}" # 1 = групповой стирающий FEC 16+2 (§12.7): TX шлёт -G,
# RX автодетектит по hdr[11]. 0 = legacy (потери = дыры)
[ "$GROUPFEC" = 1 ] && GFLAG="-G" || GFLAG=""

View File

@@ -33,7 +33,9 @@ static void tx_frame(ofdmflexframegen fg, struct iio_buffer *buf, fec enc,
// неисправимых блоках — единственный надёжный критерий на RX (техдолг #3).
uint8_t framed[GF_PAR_PAY + 4]; // ≥ данные+4 и ≥ паритет 1026+4
memcpy(framed, pl, n);
unsigned int crc = crc_generate_key(LIQUID_CRC_32, pl, n);
// liquid crc_generate_key() не помечает вход const (API-огрех), хотя
// только читает буфер — снимаем const кастом, чтобы -Wextra был чист.
unsigned int crc = crc_generate_key(LIQUID_CRC_32, (unsigned char *)pl, n);
framed[n] = (crc >> 24) & 0xFF;
framed[n + 1] = (crc >> 16) & 0xFF;
framed[n + 2] = (crc >> 8) & 0xFF;