171 lines
14 KiB
Markdown
171 lines
14 KiB
Markdown
# 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).
|