Lab047: transmit JPEG over real RF link
This commit is contained in:
244
docs/lab047_current_state.md
Normal file
244
docs/lab047_current_state.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# Lab047: аппаратная передача и восстановление одного JPEG
|
||||
|
||||
## Состояние
|
||||
|
||||
Программная подготовка и единственный разрешённый аппаратный сеанс завершены.
|
||||
Первоначальный независимый приём остался незавершённым из-за вычислительного
|
||||
ограничения `MAX_CANDIDATES_REACHED`, до PHY/FEC/CRC он не дошёл. Это
|
||||
историческое состояние сохранено и не переименовано в успешный приём.
|
||||
|
||||
После исправления только порядка обработки кандидатов выполнен ровно один
|
||||
полный автономный повторный анализ исходной RX-записи без нового RF. Он собрал
|
||||
JPEG полностью; последующая диагностическая сверка подтвердила точное
|
||||
совпадение с переданным файлом. На этапе повторной обработки `RF TX = 0`,
|
||||
HackRF не запускался.
|
||||
|
||||
## Аудит транспорта
|
||||
|
||||
Существующий транспорт Lab046 принимает произвольный непустой двоичный объект.
|
||||
Размер 16384 байта был параметром Lab046C, а не ограничением протокола.
|
||||
Число фрагментов вычисляется из фактического размера объекта, длина последнего
|
||||
фрагмента восстанавливается из его собственных метаданных.
|
||||
|
||||
JPEG поэтому передаётся как обычная последовательность байтов. Изменения в
|
||||
`protocol/`, Lab045, FEC и физическом уровне не потребовались и не выполнялись.
|
||||
|
||||
## Демонстрационный JPEG
|
||||
|
||||
Выбрана существующая настоящая фотография
|
||||
`data/raw/lab011_scene.jpg`: поле, грунтовая дорога и деревья. Исходный файл не
|
||||
изменялся. Подготовка полностью фиксирована:
|
||||
|
||||
- цветовой режим RGB;
|
||||
- размер 320 x 240 пикселей;
|
||||
- фильтр уменьшения LANCZOS;
|
||||
- JPEG quality 35;
|
||||
- цветовая субдискретизация 4:2:0;
|
||||
- `optimize = false`;
|
||||
- `progressive = false`.
|
||||
|
||||
Полученный файл `data/processed/lab047/tx_image.jpg` имеет 8724 байта,
|
||||
формат JPEG, режим RGB, размер 320 x 240 и SHA-256
|
||||
`e913dab9ac32795250880a4be67fbbbf89a4f96536842476ab591d1acaafc02a`.
|
||||
Pillow полностью читает файл и не обнаруживает усечения.
|
||||
|
||||
## Программный сквозной проход
|
||||
|
||||
Выполнена цепочка:
|
||||
|
||||
`tx_image.jpg -> bytes -> Lab046 fragmentation -> Lab045 FEC/BPSK ->`
|
||||
`идеальный канал -> PHY/FEC/CRC -> ObjectAssembler -> recovered_image.jpg`.
|
||||
|
||||
Фактический результат:
|
||||
|
||||
- фрагментов: 36;
|
||||
- защищённых BPSK-кадров: 36;
|
||||
- CRC PASS / FAIL: 36 / 0;
|
||||
- действительных фрагментов: 36;
|
||||
- пропущенных фрагментов: 0;
|
||||
- `OBJECT COMPLETE = YES`;
|
||||
- SHA-256 восстановленного файла:
|
||||
`e913dab9ac32795250880a4be67fbbbf89a4f96536842476ab591d1acaafc02a`;
|
||||
- `SHA MATCH = YES`;
|
||||
- ошибочных байтов: 0;
|
||||
- ошибочных битов: 0;
|
||||
- Pillow распознаёт восстановленный файл как JPEG 320 x 240 RGB;
|
||||
- `IMAGE DECODE = PASS`.
|
||||
|
||||
## Учёт будущего радиопотока
|
||||
|
||||
Каждый полный кадр Lab045 содержит 2 306 400 комплексных отсчётов. Для
|
||||
фактических 8724 байт JPEG рассчитано:
|
||||
|
||||
- число отсчётов TX: 83 030 400;
|
||||
- размер будущего `tx_stream.cs8`: 166 060 800 байт;
|
||||
- расчётная длительность TX: 10,3788 с;
|
||||
- полезная скорость по данным JPEG: 6724,48 бит/с;
|
||||
- плановая длительность RX: 15,0 с;
|
||||
- объём RX: 120 000 000 комплексных отсчётов, 240 000 000 байт;
|
||||
- упреждение запуска RX: 0,75 с.
|
||||
|
||||
Большие `tx_stream.cs8`, `rx_stream.cs8` и сырые IQ в Git не создавались и не
|
||||
добавлялись.
|
||||
|
||||
## Подготовленный аппаратный запуск
|
||||
|
||||
`experiments/lab047_jpeg_object_transport.py` содержит отдельные режимы для
|
||||
формирования будущего TX-потока без RF, одного аппаратного захвата и одного
|
||||
независимого полного анализа. Аппаратный режим требует одновременно три явных
|
||||
ключа командной строки и обязательный новый признак разрешения внутри функции.
|
||||
До попытки создаётся одноразовый файл-свидетель; автоматического повтора нет.
|
||||
|
||||
Параметры будущего опыта зафиксированы:
|
||||
|
||||
- MASTER/TX: `000000000000000097c063dc323b97d3`;
|
||||
- SLAVE/RX: `000000000000000097c063dc2e5327d3`;
|
||||
- центральная частота: 1 400 000 000 Гц;
|
||||
- частота дискретизации: 8 МГц;
|
||||
- BPSK: 20 ксимв/с, 400 отсчётов на символ;
|
||||
- цифровое смещение несущей: +50 кГц;
|
||||
- TX VGA: 10 дБ;
|
||||
- RX LNA/VGA: 24/24 дБ;
|
||||
- RF amplifier и Bias-Tee: выключены на обоих устройствах;
|
||||
- общий генератор: TCXO 10 МГц -> MASTER -> CLKOUT -> SLAVE CLKIN;
|
||||
- ожидаемый регистр 29 обоих устройств после проверки: `0x26`.
|
||||
|
||||
## Независимое обнаружение
|
||||
|
||||
Для каждой новой записи первый порог вычисляется заново только по фону до TX:
|
||||
|
||||
`threshold = Q99.9 локальных максимумов + 4 x MAD локальных максимумов`.
|
||||
|
||||
Интервал калибровки равен 0,0-0,5 с записи, затем порог фиксируется до полного
|
||||
декодирования. Независимый контрольный фон расположен в интервале 12,0-15,0 с.
|
||||
Строгий каскад остаётся прежним:
|
||||
|
||||
`точная синхронизация -> CFO -> фаза -> PHY header -> FEC -> CRC`.
|
||||
|
||||
Кадр принимается только при `CRC PASS`. Исторический
|
||||
`IQ_PREAMBLE_THRESHOLD = 0.65` не изменён. Интерфейс независимого приёмника
|
||||
получает только путь к RX-записи и выходной каталог. Он не получает смещения
|
||||
TX, ожидаемое число кадров, индексы фрагментов передатчика, исходный JPEG,
|
||||
пакеты или кодированные биты. Сравнение с TX-файлом вынесено в отдельную
|
||||
функцию, которая открывается только после сохранённого полного независимого
|
||||
вердикта.
|
||||
|
||||
## Единственный аппаратный сеанс
|
||||
|
||||
3 сентября 2026 года выполнен один непрерывный RF TX без повторов. Частота
|
||||
равна 1400 МГц, фактическая длительность TX — 10,38030 с. RX продолжался
|
||||
15,00875 с и сохранил 120000000 комплексных отсчётов, или 240000000 байт.
|
||||
RX overruns 0, TX underruns 0. Проверка общего опорного генератора прошла.
|
||||
Повторная передача не выполнялась: `RF TX COUNT = 1` за аппаратный сеанс и
|
||||
`RF TX = 0` на этапе автономной обработки.
|
||||
|
||||
По фону до передачи до декодирования зафиксированы:
|
||||
|
||||
- Q99.9 локальных максимумов: `0.020810436457395554`;
|
||||
- MAD локальных максимумов: `0.002538509201258421`;
|
||||
- порог `Q99.9 + 4 * MAD`: `0.030964473262429237`.
|
||||
|
||||
На независимом контрольном фоне после передачи maximum равен
|
||||
`0.026494961231946945`, кандидатов 0. Порог после аппаратного результата и
|
||||
при повторной обработке не менялся.
|
||||
|
||||
Первоначальный приём остановился после 71113 локальных максимумов и заполнения
|
||||
промежуточного списка из 10000 кандидатов. Сработало
|
||||
`MAX_CANDIDATES_REACHED`; строгая ступень не запускалась. Поэтому сохранённый
|
||||
исторический результат сформулирован точно:
|
||||
`ORIGINAL HARDWARE RECEIVE = INCOMPLETE DUE TO MAX_CANDIDATES_REACHED`, а не
|
||||
PHY FAIL, FEC FAIL, CRC FAIL или отказ передачи JPEG.
|
||||
|
||||
## Потоковая обработка кандидатов
|
||||
|
||||
В `lab046c_offline_recovery.py` устранено предварительное накопление всех
|
||||
кандидатов. Блоки IQ и локальные максимумы идут по возрастанию смещения, а
|
||||
каждый кандидат сразу передаётся строгой ступени. Защитный предел сохранён и
|
||||
теперь ограничивает число фактически выполненных строгих проверок; его
|
||||
значение 100000 не зависит от числа кадров JPEG.
|
||||
|
||||
После принятого по CRC кадра позиция перескакивает за принадлежащую ему область.
|
||||
После плохого кандидата она продвигается на один отсчёт, поэтому близкий
|
||||
настоящий кадр не скрывается. Точный повтор одного смещения подавляется;
|
||||
произвольного радиуса подавления и глобального отношения первого пика ко
|
||||
второму нет. Перекрытия блоков имеют однозначное владение и не породили
|
||||
дубликатов.
|
||||
|
||||
Для списка кандидатов измерено около 108 байт на запись; при потоковой
|
||||
обработке пиковый объём такого списка составил 49588 байт. Основной временный
|
||||
объём определяет обработка блока IQ: приблизительно 1545755352 байта
|
||||
(около 1,44 ГиБ).
|
||||
|
||||
## Автономный результат исходной RX-записи
|
||||
|
||||
После программного шлюза выполнен ровно один полный независимый проход всей
|
||||
15-секундной записи. Передаточные смещения, период кадров, ожидаемое число
|
||||
кадров, номера фрагментов, исходный JPEG, пакеты и кодированные биты не
|
||||
использовались. Получено:
|
||||
|
||||
- всего локальных максимумов: 185828;
|
||||
- кандидатов первого каскада: 32414;
|
||||
- строгих проверок: 69;
|
||||
- кандидатов, пропущенных как принадлежащие уже принятому локальному событию:
|
||||
32345;
|
||||
- точных дубликатов на перекрытиях: 0;
|
||||
- PHY-заголовок PASS/FAIL: `36/33`;
|
||||
- FEC attempts/PASS/FAIL: `36/36/0`;
|
||||
- CRC PASS/FAIL: `36/0`;
|
||||
- действительных кадров и фрагментов: `36/36`;
|
||||
- отсутствующих фрагментов: 0;
|
||||
- `OBJECT COMPLETE = YES`.
|
||||
|
||||
Все восемь блоков оценки были исчерпаны, защитные пределы не сработали. Полное
|
||||
время равно 37,42391 с, из них строгая ступень заняла 32,459969 с; среднее
|
||||
время одной строгой проверки — 0,470434 с. Первоначально поле `completed`
|
||||
осталось ложным из-за отдельной ошибки учёта границы: последнее допустимое
|
||||
начало корреляции было принято за конец обработанной области IQ. Исходный JSON
|
||||
результата не изменён, полный поиск не повторяли. В отдельном исправленном JSON
|
||||
после проверки исчерпания всех восьми блоков граница изменена с 119471220 на
|
||||
120000000 и `completed` на `true`; порог не менялся,
|
||||
`full_decoder_rerun = false`.
|
||||
|
||||
Только после сохранения завершённого независимого вердикта открыты опорные
|
||||
данные передатчика. Совпали 36 из 36 кадров, пропущено 0, ложных CRC PASS 0.
|
||||
До FEC обнаружено 19 ошибок кодированных бит, после FEC — 0 ошибок
|
||||
протокольных бит. В независимый результат кадры не добавлялись.
|
||||
|
||||
Восстановленный `recovered_image.jpg` имеет 8724 байта. Его SHA-256 и SHA-256
|
||||
переданного файла одинаковы:
|
||||
`e913dab9ac32795250880a4be67fbbbf89a4f96536842476ab591d1acaafc02a`.
|
||||
Ошибочных байтов 0, ошибочных битов 0. Pillow полностью декодирует JPEG как
|
||||
изображение RGB 320 x 240. Итог:
|
||||
`LAB047 OFFLINE RECOVERY OF HARDWARE CAPTURE = PASS`; единственный RF-сеанс
|
||||
фактически передал JPEG успешно, а первоначальную обработку остановил
|
||||
вычислительный предел.
|
||||
|
||||
## Артефакты
|
||||
|
||||
- `data/processed/lab047/tx_image.jpg`;
|
||||
- `data/processed/lab047/recovered_image.jpg`;
|
||||
- `data/processed/lab047/result.json`;
|
||||
- `data/processed/lab047/result.txt`.
|
||||
|
||||
Большая RX-запись и результаты аппаратного сеанса оставлены вне Git в
|
||||
`C:\Users\user\Documents\SDR_Rover_Lab047_raw\20260903_115747`. Исходный
|
||||
`independent_reprocess_result.json` сохранён неизменным; исправление только
|
||||
признака полного покрытия находится в
|
||||
`independent_reprocess_result_corrected.json`, а разрешённая послерезультатная
|
||||
сверка — в `post_verdict_diagnostics.json`.
|
||||
|
||||
## Проверки
|
||||
|
||||
- `python tools/quick_gate.py`: PASS, 540 проверок pytest и 10 быстрых
|
||||
функциональных проверок лабораторных;
|
||||
- `python -m pytest tests/ -q`: 540 passed, 0 failed;
|
||||
- `git diff --check`: PASS.
|
||||
|
||||
Предупреждение pytest о невозможности создать служебный `.pytest_cache` не
|
||||
относится к тестам и не изменило код возврата.
|
||||
|
||||
## Следующий этап
|
||||
|
||||
STOP. Разрешение на единственный аппаратный процесс Lab047 израсходовано.
|
||||
Новый RF, повтор передачи, изменение порога, PHY, FEC, CRC или Lab045 без
|
||||
отдельной новой постановки не выполнять. `RF TX = 0`; HackRF не запускать.
|
||||
Reference in New Issue
Block a user