Files
SDR-Rover/docs/lab046_current_state.md
2026-09-01 11:39:45 +03:00

171 lines
14 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.
# Lab046: текущее состояние
## Статус
**SOFTWARE VALIDATED. RF TX = 0.
HARDWARE VALIDATION = NOT PERFORMED.**
Lab046 — только программная лаба. HackRF и `hackrf_transfer` не
запускались, эфирной передачи нет. Ни JPEG, ни видео, ни QPSK.
## Назначение
Lab046 переходит от одиночной 256-байтной полезной нагрузки к передаче
произвольного бинарного объекта последовательностью независимо защищённых
радиокадров. Два уровня строго разделены и не смешиваются:
- **Уровень 1 (внутри одного кадра) — Lab045.** Свёрточный код R=1/2 K=7,
перемежитель, мягкий декодер Витерби, CRC пакета. Исправляет неизвестные
битовые ошибки внутри принятого кадра.
- **Уровень 2 (большой объект) — существующий `protocol/image_fragments`.**
Фрагментация, нумерация фрагментов, идентификатор объекта, сборка. Собирает
объект из нескольких независимо принятых кадров.
Новый код в `experiments/lab046_bpsk_object_transport.py` минимален: он
связывает два готовых уровня и добавляет fail-closed сборщик объекта.
Отдельный модуль формата фрагмента НЕ создавался: переиспользован 12-байтовый
заголовок `image_fragments` (`>IHHI`: object_id u32, fragment_index u16,
fragment_count u16, original_size u32).
## Аудит: что переиспользовано, чего не хватало, минимальный новый слой
1. **Переиспользовано.** `protocol/image_fragments` уже даёт нужную семантику
фрагмента: идентификатор объекта, индекс, число фрагментов, общий размер,
а также fail-closed `reassemble_image` (несовпадение id/count/size,
конфликтующие дубликаты, недостающие фрагменты, финальная проверка длины).
Слой Lab045 даёт кадр целиком: `build_transmission_plan`,
`analyze_post_preamble_soft`, `simulate_soft_channel`, диагностику pre/post-FEC.
2. **Чего не хватало.** Не было (а) упаковки фрагмента в 256-байтную нагрузку
Lab045 с обрезкой добивки на приёме по длине из заголовка и (б) сборщика,
который принимает по одному независимо декодированному кадру и **никогда не
бросает** на плохом входе, возвращая `object_complete = False` вместо
фиктивного объекта.
3. **Минимальный новый слой.** `recover_fragment_from_payload` (fail-closed
декод + обрезка добивки) и класс `ObjectAssembler` (offer/missing/complete/
recovered). Erasure-код 12+3 GF(256) (`protocol/packet_erasure_fec`) НЕ
использован и НЕ изменён — он остаётся будущим расширением для потери целых
фрагментов, а не для битовых ошибок (это работа свёрточного кода).
## Детерминированный объект
- Размер **16384 байта (16 KiB)**. Это не JPEG, а воспроизводимый
псевдослучайный поток `numpy.random.default_rng(seed).integers(0,256,...)`.
- **Выбор размера.** 16 KiB — компромисс: достаточно велик, чтобы кадров было
много (68) и учёт фрагментации/сборки был содержательным, но достаточно мал,
чтобы программный прогон 68 кадров с мягким Витерби считался секунды. Ровный
размер (не кратный 244) специально оставляет короткий последний фрагмент.
- Seed объекта по умолчанию `46`, идентификатор объекта `0x4C303436` («L046»).
- SHA-256 объекта по умолчанию:
`748013b5f670c82bd52650be5454555355dbd8528538753df4558f1c3408d515`.
- Объект не сохраняется в Git (крупный генерируемый артефакт); при
необходимости он детерминированно восстанавливается по seed.
## Раскладка нагрузки пакета
Полезная нагрузка Lab045 остаётся 256 байт (доказанная рабочая точка):
[12 байт метаданных фрагмента][244 байта данных фрагмента]
`FRAGMENT_DATA_SIZE = 256 − 12 = 244`. Последний фрагмент короче; его нагрузка
добивается нулями до 256 байт перед Lab045 и обрезается на приёме по длине,
вычисленной **только из полей заголовка** (`expected_fragment_data_length`).
Для 16384 байт последний, 68-й фрагмент несёт 36 байт данных (16384 − 67·244).
## Fail-closed сборка (без оракула)
Приёмник независим: переданный объект и TX-биты не используются ни для приёма
пакета, ни для FEC, ни для восстановления метаданных, ни для решения о
завершении объекта. Сравнение с оригиналом делается только в диагностике,
**после** независимого CRC-решения и независимой сборки.
`ObjectAssembler.offer(fragment)` возвращает `accepted/duplicate/conflicting/
rejected` и никогда не бросает. Отвергаются: чужой object_id, несогласованные
count/original_size, невозможный индекс, конфликтующий дубликат (тот же индекс,
другие данные), malformed/усечённые метаданные. Идентичный дубликат
игнорируется. Пока не собраны все индексы, `object_complete()` = False, а
`recovered_object()` = None — фиктивный объект не создаётся. Поле original_size
проверяется на обе границы (`(count−1)·244 < original_size ≤ count·244`), чтобы
абсурдное значение не управляло размером аллокаций.
## Программные сценарии (seed фиксирован)
| Сценарий | Условие | Ожидалось | Получено |
|---|---|---|---|
| A. Идеальный канал | шума нет | объект точен | complete=True, sha совпал, 0 байт-ошибок, CRC 68/0 |
| B. Распределённые битовые ошибки | гауссов шум sigma=0.40 | pre-FEC>0, post-FEC=0, все CRC PASS, объект точен | pre-FEC 1805, post-FEC 0, CRC 68/0, complete=True, sha совпал |
| C. Приём не по порядку | обратный порядок кадров | объект точен | complete=True, sha совпал |
| D. Дубликаты фрагментов | повтор кадров 0,2,2 | объект точен, дубликаты учтены | duplicates≥1, complete=True, sha совпал |
| E. Один потерянный фрагмент | кадр 1 не дошёл | OBJECT COMPLETE = NO | complete=False, 1 в missing, recovered SHA = None |
| F. Повреждённый пакет | инверсия coded-потока кадра 0 | CRC FAIL, объекта нет | crc_fail≥1, complete=False, recovered SHA = None |
Сценарий B — честная точка: уровень sigma выбран так, чтобы мягкий Витерби
исправлял **все** 68 кадров (при sigma=0.45+ отдельный кадр не декодируется, и
без erasure-FEC объект оказывается неполным — это по сути сценарий E, а не
подгон). Числа не подкручивались ради прохождения: до FEC присутствуют 1805
реальных ошибок кодированных бит, после мягкого Витерби — ноль, объект
побитово точен.
## Метрики (объект 16384 байта, идеальный канал)
| Величина | Значение |
|---|---:|
| Размер объекта | 16384 байта |
| SHA-256 объекта | `748013b5…3408d515` |
| Размер метаданных фрагмента | 12 байт/пакет |
| Размер данных фрагмента | 244 байта (последний 36) |
| Число фрагментов | 68 |
| Размер протокольного пакета | 268 байт (256 нагрузка + 8 header + 4 CRC) |
| Число BPSK-кадров | 68 |
| Всего информационных BPSK-символов | 389 232 (5724 × 68) |
| Всего отсчётов формы сигнала | 156 835 200 (2 306 400 × 68) |
| Оценка эфирного времени | 19,60 с |
| Полезный goodput | 835,7 байт/с = 6,69 кбит/с |
| Полезных бит на символ | ≈ 0,337 |
Учёт кадра совпадает с Lab045: 2144 информационных бита, 6 tail-бит, 4300
кодированных бит (R=1/2), перемежитель 4300, PHY header 48→144 бит (×3),
преамбула PRBS7 1280 бит, итого 5724 информационных символа кадра. Форма
сигнала добавляет 42 символо-времени (32 нулевых guard + 10 хвост ФРК,
`shape_frame_like_lab042`, режим `full`): 5766 символо-времён = 2 306 400
отсчётов = 288,3 мс на кадр при 8 MS/s.
### Разбор накладных расходов
- **Фрагментация (уровень 2):** 12 байт метаданных на пакет × 68 = 816 байт.
- **Протокольный header+CRC (уровень 1):** 12 байт на пакет × 68 = 816 байт.
- **Свёрточный код R=1/2:** удваивает число кодированных бит (2144→4300 с tail).
- **PHY преамбула и защищённый header:** 1280 + 144 = 1424 бита на кадр.
- **Guard/фильтр:** 42 символо-времени на кадр (32 guard + 10 хвост ФРК).
- **Добивка последнего фрагмента:** 208 нулевых байт (только 68-й кадр).
Goodput 6,69 кбит/с — это честная baseline при 20 ксимв/с с полной
преамбулой и guard на каждый кадр; в реальной серии кадров преамбула на кадр
доминирует в накладных расходах, и это направление для будущей оптимизации,
а не дефект.
## Фактические программные результаты
- Новый набор `tests/test_lab046_bpsk_object_transport.py`: все проверки
проходят (фрагмент туда-обратно, короткий последний фрагмент, чужой
object_id, невозможный индекс, несогласованный счётчик, усечённые и абсурдные
метаданные без крупной аллокации, полный тракт PHY для сценариев A–F,
отсутствие оракула, детерминизм и SHA объекта).
- Полный тракт `объект → фрагменты → кадр Lab045 → канал → приём → сборка`
проверен для всех шести сценариев значениями выше.
## Software gate
- `python tools/quick_gate.py`: PASS; 438 тестов ядра, 6 функциональных
проверок лабораторий выполнено, 13 штатно пропущено.
- `python -m pytest tests/ -q`: 438 passed, 0 failed, 0 errors, 0 skipped.
Единственное предупреждение связано с отсутствием прав на создание
`.pytest_cache`; на выполнение и результат тестов оно не повлияло.
- `git diff --check`: чист.
## Ограничения и следующий шаг
RF TX = 0, аппаратная проверка не проводилась. Erasure-код 12+3 GF(256) не
задействован (будущее восстановление потери целых фрагментов). Оптимизация
преамбулы/guard на серию кадров, QPSK, JPEG и видео в этой лабе не делались.
Следующий этап определяется после анализа (Lab046 первым смотрит ChatGPT).