From 4bf1a6df7b20d9d9711643c578951fee642ca3d3 Mon Sep 17 00:00:00 2001 From: Maxim Date: Wed, 15 Jul 2026 09:48:26 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20group-FEC=2016+2=20=D0=B7=D0=B0=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD,=20=D1=80=D0=B0=D0=B1=D0=BE=D1=87=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=82=D0=BE=D1=87=D0=BA=D0=B0=20=D1=81=D0=BE=D1=83?= =?UTF-8?q?=D0=BA=D0=B0=20-p=204000=20(=C2=A712.7)=20-=20README=20=C2=A712?= =?UTF-8?q?.7=20=E2=80=94=20=D0=B6=D1=83=D1=80=D0=BD=D0=B0=D0=BB=20=D1=8D?= =?UTF-8?q?=D1=82=D0=B0=D0=BF=D0=B0=20(=D1=80=D0=B0=D1=81=D1=87=D1=91?= =?UTF-8?q?=D1=82=2016+2,=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D0=B0=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B3=D0=BE=D0=BD=D0=BE=D0=B2,=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=85=D0=BE=D0=B4=D0=BA=D0=B0=20overrun)=20-=20README=20?= =?UTF-8?q?=C2=A710=20=E2=80=94=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D1=83?= =?UTF-8?q?=D0=BC=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F:=20=D0=BF.6=3Dgroup-?= =?UTF-8?q?FEC,=20ARQ/FDD=E2=86=92=D0=BF.7,=20=D1=82=D1=83=D0=BD=D0=BD?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=E2=86=92=D0=BF.8,=20=D0=B2=D0=B8=D0=B4=D0=B5?= =?UTF-8?q?=D0=BE=E2=86=92=D0=BF.9=20-=20README=20=C2=A79=20=E2=80=94=20?= =?UTF-8?q?=D1=82=D0=B5=D1=85=D0=B4=D0=BE=D0=BB=D0=B3=20#6=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D1=91=D0=BD=20(group-FEC=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BA=D1=80=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20=D1=81=D0=B8?= =?UTF-8?q?=D0=BC=D0=BF=D0=BB=D0=B5=D0=BA=D1=81-=D0=BF=D0=BE=D1=82=D0=B5?= =?UTF-8?q?=D1=80=D0=B8)=20-=20docs/BENCHMARK.md,=20CLAUDE.md,=20test=5Ffi?= =?UTF-8?q?le=5Flink.sh=20=E2=80=94=20=D1=81=D0=BE=D1=83=D0=BA-=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BA=D0=B0=20-p=204000=20-=20transmitter.c=20?= =?UTF-8?q?=E2=80=94=20=D1=81=D0=BD=D1=8F=D1=82=20-Wdiscarded-qualifiers?= =?UTF-8?q?=20=D0=BD=D0=B0=20crc=5Fgenerate=5Fkey=20(const-=D0=BA=D0=B0?= =?UTF-8?q?=D1=81=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 4 ++ README.md | 88 ++++++++++++++++++++++++++++++++++----- docs/BENCHMARK.md | 9 +++- scripts/test_file_link.sh | 6 ++- src/transmitter.c | 4 +- 5 files changed, 95 insertions(+), 16 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 133d1cf..91d972e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 | diff --git a/README.md b/README.md index ab38466..2518138 100644 --- a/README.md +++ b/README.md @@ -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.1–0.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** (не ×4–5 — эфир кадра 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. +*Ввод в строй: 14–15-07-2026. Замеры CPU и базовые решения — 10-07-2026. Прошивка v0.38, liquid-dsp 1.8.0.* \ No newline at end of file diff --git a/docs/BENCHMARK.md b/docs/BENCHMARK.md index 19a90c1..ebdac48 100644 --- a/docs/BENCHMARK.md +++ b/docs/BENCHMARK.md @@ -137,8 +137,13 @@ sync-only **1.649 vs 1.652** без него = 0.2%. Гипотеза кэш-к Эмпирическое колено ~2500 мкс — выше нуль-запасной модели 1333 из-за джиттера планировщика (запас ~2×). Throughput 90 → 120 КБ/с = **×1.36** (не ×4–5: эфир кадра 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) diff --git a/scripts/test_file_link.sh b/scripts/test_file_link.sh index 2419f32..596703d 100755 --- a/scripts/test_file_link.sh +++ b/scripts/test_file_link.sh @@ -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="" diff --git a/src/transmitter.c b/src/transmitter.c index b7e3ede..1e1abb7 100644 --- a/src/transmitter.c +++ b/src/transmitter.c @@ -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;