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.
69 lines
5.4 KiB
Markdown
69 lines
5.4 KiB
Markdown
# CLAUDE.md — конституция проекта pluto-link
|
||
|
||
Прочитай этот файл первым. Полная документация — README.md, docs/.
|
||
|
||
## Контекст
|
||
Радиолиния точка-точка на двух Pluto+ (клон ADALM-Pluto: Zynq-7020,
|
||
2 × Cortex-A9 @ 667 МГц, NEON, 1 ГБ RAM, прошивка v0.38).
|
||
Весь сигнальный тракт исполняется НА ARM ВНУТРИ Pluto (libiio, контекст
|
||
`local:`). Хост (Raspberry Pi, aarch64) — только кросс-сборка и деплой.
|
||
PHY: OFDM через liquid-dsp 1.8.0 + FFTW/NEON, FEC RS(255,223).
|
||
|
||
## Жёсткие правила
|
||
1. Язык — C. НИКОГДА не предлагай Python, GNU Radio или обработку на хосте.
|
||
2. libiio только `local:` в тракте. `ip:` — только для хост-утилит отладки.
|
||
3. Статическая линковка, флаги обязательны:
|
||
`-O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -ffast-math -static`
|
||
4. Никаких тяжёлых зависимостей (FFmpeg, cJSON, curl). JSON — руками.
|
||
5. Бюджет CPU: на DSP доступно ОДНО ядро 667 МГц (второе — IIO/FEC/IO).
|
||
Замерено: ofdmflexframesync тянет 2.34 Msps на кадрах, 5.32 Msps на шуме.
|
||
Поэтому sample rate = 1 920 000. НИКОГДА не поднимай до 3 840 000 —
|
||
система развалится под нагрузкой (0.61× от realtime).
|
||
6. Изменил PHY-параметры (M/CP/модуляция/rate) → сначала прогони
|
||
src/ofdm_bench.c на плате, обнови docs/BENCHMARK.md в том же коммите.
|
||
7. Порядок отладки: кабель+аттенюатор 30–40 дБ (TX gain −40) → антенны
|
||
на столе → дистанция. Не отлаживать новый код сразу в эфире.
|
||
8. Изменения показывай в виде diff с объяснением каждой правки.
|
||
|
||
## Параметры тракта (текущие)
|
||
- rate 1 920 000, bw 1 500 000, OFDM M=64 CP=16 taper=4, QPSK, CRC32
|
||
- 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 |
|
||
10: last_block_bytes | 11: group-FEC флаги (§10 п.6)
|
||
Байт 11: bit7 GF_MODE (группа активна), bit6 GF_PARITY (P/Q-кадр),
|
||
bit5 GF_Q (Q иначе P), bits4-0 idx кадра в группе (0..17). 0 = legacy-кадр.
|
||
Group-FEC 16+2: 16 кадров данных + P(⊕) + Q(Σgⁱ) над GF(256), закрывает ≤2
|
||
стёртых кадра/группу без обратного канала (src/group_fec.c). RX автодетект.
|
||
Сигнатура F0 E7 — END (этап 3): payload 12 Б, total_seq u32 + total_bytes u64,
|
||
маркер конца передачи для хендшейка END/COMPLETE (feedback.h, §12.9).
|
||
КРИТИЧНО: liquid по умолчанию передаёт 8 Б заголовка. Обязательно
|
||
`ofdmflexframegen_set_header_len(fg,12)` и
|
||
`ofdmflexframesync_set_header_len(fs,12)` на обеих сторонах.
|
||
|
||
## IIO-имена (первые грабли)
|
||
PHY: `ad9361-phy`; RX DMA: `cf-ad9361-lpc`; TX DMA: `cf-ad9361-dds-core-lpc`.
|
||
TX LO: канал `altvoltage1` (output); RX LO: `altvoltage0` (output).
|
||
Каналы данных: voltage0/voltage1 (I/Q, int16, диапазон ±2047 — 12 бит).
|
||
|
||
## Известный техдолг (README §9)
|
||
#1 header 12Б (см. выше) · #2 лимит nblocks ≤ 18, не 100 (стек recovered[4096])
|
||
· #3 fec_decode() liquid не сообщает об ошибках — rs_saved фиктивен
|
||
· #4 внешний NCO в receiver вреден, убрать · #5 TX пушит лишние сэмплы
|
||
· #6 ✅ закрыт на FDD: ARQ (NACK→ретрансмит) + END/COMPLETE, §12.9. На симплексе
|
||
ARQ невозможен — там детектор [ПОТЕРЯ] + group-FEC · #7 reciever.c → receiver.c
|
||
|
||
## Команды
|
||
Сборка: `make` · Деплой: `make deploy` (scp -O! sftp на плате нет)
|
||
Бенчмарк: `make bench && scripts/deploy.sh bench && ssh root@<pluto> '/tmp/ofdm_bench 1.92'`
|
||
E2E-тест: `scripts/test_file_link.sh`
|
||
Пароль root на Pluto: `analog`. rootfs в RAM — /tmp очищается при ребуте.
|
||
Offline CI на push (2 self-hosted runner'а, host+armhf, НЕ эмуляция ARM
|
||
— кросс-компиляция): `.gitea/workflows/`, разбор нюансов в docs/CI.md. |