Files
Pluto-SDR/CLAUDE.md
Maxim ab1e87a17e Этап 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.
2026-07-16 15:29:25 +03:00

69 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. Порядок отладки: кабель+аттенюатор 3040 дБ (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.