Files
SDR-Rover/docs/lab051_current_state.md
2026-09-08 16:04:09 +03:00

24 KiB
Raw Blame History

Lab051 — вычислительная архитектура потокового BPSK-приёмника

Дата: 8 сентября 2026 года. SOFTWARE ONLY. RF TX = 0.

Результат и граница вывода

Программный эксперимент завершён; цели реального времени и свежести не достигнуты. В полном шестисекундном потоке получены 240/240 физических пакетов и 30/30 JPEG с точным содержимым. Обработка 6,044416 с синтетического IQ заняла 38,630132 с, отношение 6,391045. При MAX_VIDEO_AGE=200 мс FRESH DISPLAY FPS=0; не выполнен даже минимальный критерий >0.

Короткий шумный сценарий Lab050 ускорен примерно в 48,21 раза относительно исторического замера: 55,157553 с → медиана 1,144176 с трёх проходов. Его processing/RF = 3,534193, поэтому и этот вариант не работает в реальном времени. Среднее успешного кандидата — 17,7675 мс; полная стоимость короткого опыта — 47,674 мс/пакет, а длинного потока — 160,959 мс/пакет. Последние величины включают обработку ложных кандидатов и фона.

Модуляция BPSK, PRBS7, PHY-заголовок, фрагментация, интерливер, FEC R=1/2 K=7, 171/133 octal, soft-метрика и CRC не менялись. GPU и multiprocessing не использовались. Новых зависимостей нет. Аппаратура не перечислялась и не запускалась.

Git и аудит до реализации

Исходный main: cdf5396e807b83f82a264632dc46f71428fcd616. Проверены HEAD/status/diff stat/diff check, затем показан точный staged scope. По прямому разрешению пункта 0 задания создан коммит:

60691a09f193f36983c7174c5252ed0897c559e9

Lab050: model fresh-frame SDR video stream

В него вошли ровно девять файлов: PROJECT_LOG.md, docs/LABS_INDEX.md, experiments/lab046c_offline_recovery.py, protocol/video_frame_scheduler.py, docs/lab050_current_state.md, experiments/lab050_streaming_receiver.py, experiments/lab050_video_stream.py, tests/test_lab050_video_stream.py, data/processed/lab050/receiver_benchmarks.json. 1769 добавлений, 12 удалений. Большой result.json, профили JPEG и посторонние локальные файлы не добавлялись.

Lab051 начата от этого HEAD. Изучены Lab046B/C, Lab047, Lab048, Lab050, Lab043/044/045 и свёрточный декодер. Ядро protocol в Lab051 не менялось. Планировщик LATEST_ONLY и ObjectAssembler переиспользованы. Новая видеочередь не создана.

Профиль до оптимизации

Рабочая точка: 500000 символов/с, Fs=8000000 отсчётов/с, 16 отсчётов/символ. Профиль C: 128×96 RGB, JPEG quality 25. Короткий контроль: три изображения, 24 пакета, seed=50050, coded-bit Eb/N0=7 дБ, CFO=+10 Гц, фаза 0,73 рад, чередование полярности, исходное CS8-квантование. Генератор точно прежний, из Lab050.

Путь кандидата: IQ → грубая нормированная корреляция → уточнение старта → согласованный RRC-фильтр → timing → оценка частотного рассогласования CFO → компенсация CFO и фазы → мягкие решения → PHY header → deinterleave → soft Viterbi → protocol parser → CRC. Только полный PHY/FEC/CRC PASS выдаёт пакет. TX-границы, ожидаемое число кадров и содержимое не входят в RX API.

Историческая база Lab050: 55,157553 / 0,323745 = 170,373647. Свежий инструментированный baseline Lab051: 49,614327 с, processing/RF=153,251430. Настольная нагрузка и инструментация влияют на время; эти замеры не подменяют друг друга. Все 2374 кандидата: 24 valid, 2350 false. Средние времена — 72,967 / 20,099 мс.

Главная причина замедления — fallback timing. Номинальное положение равно 416, а диапазон остался −800…+800 отсчётов. Начало выходит за ноль, и прежний быстрый путь возвращается к Python-перебору 1601 смещения. У ложных кандидатов timing занял 41,833 с из 47,232 с строгой обработки. Их matched filter — 0,934 с, уточнение — 2,797 с. Прежняя реализация уже останавливала кандидата при PHY FAIL: тело, Viterbi и CRC на таких входах не запускались.

Для преамбулы и заголовка достаточно 23456 входных отсчётов и 23616 отсчётов после RRC. Полный физический пакет с payload 256 байт требует 92256 входных отсчётов. Длина тела определяется только проверенным PHY-заголовком.

Изменения вычислений

  1. Децимация сохраняет внутреннюю часть конечного импульсного отклика FIR. При сдвиге буфера заново считаются левый нулевой край и новые правые отсчёты. Коэффициенты resample_poly/kaiser(8) прежние; сравнение с исходной функцией побитно точное. Среднее по всему прежнему окну сохраняется. Корреляция считается только для принадлежащих блоку стартов плюс правый сосед максимума.
  2. Корреляция известного prefix для уточнения считается один раз для общего участка. Кандидаты используют срезы, с прежней поправкой локального DC. Постоянный probe и неразделимые FFT-пики сохраняют исходный выбор первого максимума; при необходимости вызывается старое уточнение.
  3. Компенсация известного +50 кГц и RRC выполняются для общего участка. Локальное вычитание среднего и оба нулевых края изолированного окна восстановлены отдельно. Без этих поправок общая фильтрация меняла бы алгоритм. Тело за пределами общего участка фильтруется локально только после PHY PASS.
  4. Извлечение символов кэшируется по уточнённому положению и длине внутри одного блока. Усечённое окно не подменяется ранее сохранённым полным окном. Кэш уничтожается вместе с блоком; история сессии не сохраняется.
  5. Timing сохраняет весь прежний диапазон 1601 смещения. Корреляция с разреженной известной преамбулой и разности накопленных энергий заменяют Python-перебор. Тело за последним нужным отсчётом не участвует в этом расчёте. При пиках, неразделимых с точностью float32, возвращается исходный расчёт и его tie-правило.
  6. CFO по прежним преамбульным символам не изменён. Векторы переноса кэшируются; вектор остаточной частоты строится на символах локального окна. Gain/фаза для probe и полного кадра не объединены: у них различается локальное среднее IQ.
  7. Viterbi после устранения главного повтора остался существенной долей принятого пакета. Предвычислены четыре различные пары евклидовых branch metrics; две ветви сравниваются непосредственно, с прежним выбором branch 0 при равенстве. Решётка, старт/финиш 0, шесть хвостовых битов и декодированные биты сохранены.

Дополнительный порог преамбулы/новый критерий отсева не введён. Фиксированный порог первого каскада 0.030964473262429237 сохранён. В коротком опыте совпали число кандидатов и все причины отказа с исходным RX. Параллелизм и CUDA не добавлялись.

Профиль стадий

Ячейка: число вызовов / суммарные миллисекунды по соответствующей группе. Это не длительность одного вызова. Общая работа front-end учитывается в том кандидате, который впервые запросил её. Фаза показана без вложенного CFO; время body pipeline не складывается повторно со временем Viterbi/CRC.

Стадия Baseline valid (24) Optimized valid (24) Baseline false (2350) Optimized false (2350)
Точное уточнение 24 / 28.974 24 / 93.878 2350 / 2797.404 2350 / 119.743
Согласованная RRC-фильтрация 48 / 190.656 48 / 102.875 2336 / 933.965 174 / 26.502
Timing: весь диапазон 48 / 914.632 48 / 43.535 2336 / 41833.166 174 / 138.588
CFO и комплексное усиление 48 / 10.838 48 / 8.442 2336 / 485.772 174 / 26.385
Фаза/полярность и диагностика 48 / 8.994 48 / 8.324 2336 / 424.756 174 / 28.438
PHY header 48 / 1.711 48 / 1.452 2298 / 67.136 2298 / 32.044
Viterbi 24 / 484.381 24 / 132.119 0 / 0 0 / 0
Protocol parser / CRC 24 / 0.270 24 / 0.211 0 / 0 0 / 0

Грубая локализация — общая стадия: 36 вызовов, baseline 498,040 мс, optimized 89,183 мс. На один false candidate среднее 0,187317 мс, медиана 0,060500 мс; это включает cache hits. Холодное новое окно дороже.

Массивы и память

Число всех аллокаций внутри NumPy/SciPy C не измерено. Line tracer наблюдает массивы в локальных переменных проектного Python: число ниже — нижняя граница, включающая views. Владельцы данных выделены отдельно. Временные выражения и внутренние C-массивы не перечислены. Отдельный tracemalloc-проход измеряет пик отслеживаемой памяти. Эти медленные проходы не включены в CPU benchmark.

Кандидат / алгоритм Наблюдаемые ndarray Из них владельцы Суммарные байты наблюдаемых владельцев Пик tracemalloc, байт
false / baseline 2888 61 6612268 2104129
false / optimized 76 61 5906101 5524392
valid / baseline 14361 8704 16681890 5885299
valid / optimized 6594 6560 12345244 7771675

Пик холодного кандидата вырос из-за общих промежуточных данных: ускорение не объявляется уменьшением каждого временного массива. В рабочем потоке:

  • Пик удерживаемых IQ: 3177728 байт = 3,031 МиБ.
  • Проверяемый предел IQ: 3398912 байт; кэш децимации хранится отдельно.
  • Инженерная оценка рабочих массивов: 136,161 МиБ. Использован запас прежних 22 размеров IQ-окна плюс 20 размеров на новые массивы и кэши. Это оценка для данного опыта, не доказанный универсальный предел всех аллокаций и не RSS.
  • Windows process RSS после потока: 154,105 МиБ; peak RSS процесса — 178,004 МиБ. В него входят Python, библиотеки, генератор, предыдущие замеры и аллокатор.
  • Текущий объект ограничен 65536 байтами / 269 фрагментами; C фактически ≤1893 байт.

Длинный тест: 4096 блоков, 33,554432 с модельного времени. Фон нулевой; шум отдельно проверен в основном опыте. После прогрева удерживаются 2653440 байт IQ и 397216 байт децимированных данных; значения не растут. RSS контрольных точек: 156,199–156,250 МиБ, без роста вместе с длительностью. После finish эти буферы освобождены. История JSON относится к отчёту, не к RX.

Непрерывный поток 5 изображений/с

30 воспроизводимых сдвигов исходного data/raw/lab011_scene.jpg, профиль C, 1740–1893 байта. Это искусственная последовательность, не видео камеры. Видеопланировщик LATEST_ONLY использует время capture и завершает активное JPEG. Передатчик знает нижнюю задержку радио/буфера; он не получает будущую стоимость CPU или результаты приёма. Приёмник получает лишь IQ-блоки по 65536 отсчётов.

Шум, фаза, CFO и полярность заданы, но готовность JPEG берётся из измеренного encode. Поэтому точное размещение пакетов и число фоновых кандидатов длинного опыта могут немного меняться между запусками. Короткий baseline использует точно прежний детерминированный сценарий Lab050.

Время CPU-работника для каждого блока: start=max(previous_end, block_arrival); end=start+measured_push_time. Сборка/JPEG decode добавляют своё измеренное время. Генерация TX/IQ, SHA-сравнение с передатчиком и запись JSON исключены. Длительность RX не заменена нулём. Все данные реально прошли независимый PHY/FEC/CRC и JPEG decode.

Метрика Итог
INPUT FPS / FRAMES 5 / 30
TX IMAGES / PHYSICAL PACKETS 30 / 240
DROPPED BEFORE TX 0
MAX WAITING VIDEO FRAMES 1
RX PHYSICAL PACKETS / IMAGES 240 / 30
JPEG SHA MATCH 30 / 30
DROPPED STALE AT DISPLAY 30
DISPLAYED / FRESH DISPLAYED 0 / 0
RAW RECEIVED FPS в первых 6 с CPU-шкалы 0.833333
RAW DISPLAY FPS / FRESH DISPLAY FPS 0 / 0
Возраст декодированных JPEG: min / median / max 276.515 / 15691.203 / 31150.009 мс
MAX_VIDEO_AGE 200 мс
Строгих кандидатов 147082
Полное время обработки / IQ 38.630132 / 6.044416 с
PROCESSING / RF TIME 6.391045

Возраст относится к декодированным, затем отброшенным JPEG. Показанных изображений нет. RAW DISPLAY и FRESH DISPLAY совпадают, потому что политика не выводит просроченные изображения; RAW RECEIVED приведён отдельно.

Физическое время одного JPEG C: 8×5766/500000 = 92,256 мс. Очередь ожидающих JPEG в этом режиме не задерживает готовые изображения. Время RX за опыт: 38,619085 с; сборка 5,451 мс; JPEG decode 5,596 мс суммарно на 30 JPEG. RF и CPU перекрываются по событийной шкале: эти суммы нельзя просто сложить и назвать измеренной задержкой камеры→экрана. Camera capture latency и display latency UNKNOWN; экран в отчётной модели мгновенный.

Capture lookup используется только после независимой сборки JPEG для метрики возраста/решения модели вывода. В существующем fragment format нет времени съёмки. Готовый независимый аппаратный контроль возраста этим не доказан; формат в Lab051 не расширялся. ID монотонны в одной конечной сессии; wrap/restart не реализованы.

Внешняя IQ-очередь также не реализована. Офлайн-источник подаёт следующий блок по запросу и физически не хранит многосекундный backlog. Если бы вход шёл непрерывно, максимальное отставание 32,632404 с потребовало бы около 2088473848 байт дополнительного complex64 IQ. Поэтому bounded RX state нельзя выдавать за готовую ограниченную live-систему: при таких затратах потребуются потери/переполнение. REAL-TIME CAPABLE = NO.

В длительном опыте встречено значительно больше фоновых кандидатов, чем в коротком: 147082 против 2374. Кэш дал 135057 повторных использований, но осталось 12239 новых окон синхронизации. Следующее ограничение — стоимость этих фоновых событий. Одного ускорения Viterbi недостаточно; подгонять отсев по TX запрещено.

Регрессии и независимая проверка

38 целевых тестов: весь диапазон timing, ненулевые и нулевые ties, локальные DC/края общего RRC, точная децимация при движении окна, Viterbi noisy/tie/invalid, совпадение старого/нового блочного приёма, усечение, буферы и finally. Verifier нашёл два случая округления FFT (timing и refine); оба исправлены, воспроизведены повторно и закреплены тестами. Adversary отдельно проверил malformed PHY (0 body/Viterbi), дубликаты и старые ID, лимиты объекта, усечение и восстановление глобальных подмен.

Сохранённая Lab047-запись 20 ксимв/с повторно обработана штатным Lab048: 36/36, FEC/CRC PASS, JPEG SHA MATCH, 19 ошибок до FEC, 0 после. Исходный путь и SHA JPEG сохранены в regression20k/result.json. Старый режим работает прежним декодером; адаптер Lab051 принимает только 500 ксимв/с и не подменяет аппаратную проверку этого режима.

Итог: quick_gate PASS — 688 тестов, 17 функциональных проверок, 13 штатных пропусков. Полный pytest — 688 passed за 72,92 с, 0 failed; одно предупреждение доступа к .pytest_cache. git diff --check PASS. Подробности — в записи 040 журнала. Управление ELRS/CRSF, локальная защита и ядро protocol не менялись.

Файлы и воспроизведение

python experiments/lab051_receiver_profile.py
python experiments/lab051_receiver_performance.py --stage all
python -m pytest tests/test_lab051_streaming_receiver.py -q -p no:cacheprovider
python tools/quick_gate.py
python -m pytest tests/ -q
git diff --check

Старая запись:

python experiments/lab048_receiver_performance.py --run-saved-capture --capture 'C:\Users\user\Documents\SDR_Rover_Lab047_raw\20260903_115747\rx_stream.cs8' --original-result 'C:\Users\user\Documents\SDR_Rover_Lab047_raw\20260903_115747\independent_result.json' --tx-image data/processed/lab047/tx_image.jpg --output-directory data/processed/lab051/regression20k

Локальные replay .npy — ограниченные окна, не полная IQ-запись; в Git не добавлены. Новый полный CS8/IQ файл не создавался. Lab051 git add/commit и push не выполнялись. Следующий рекомендуемый этап — отдельное программное исследование стоимости уникальных фоновых кандидатов в полном режиме 5 кадров/с с тем же PHY/FEC/CRC. RF, QPSK, новый FEC/кодек, 180 МГц и аппаратные 500 ксимв/с не выполнялись. STOP.