Files
SDR-Rover/docs/lab052_current_state.md
2026-09-08 17:29:52 +03:00

34 KiB
Raw Blame History

Lab052 — снижение числа ложных кандидатов при 500 ксимв/с

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

Результат

Ложные строгие проверки сокращены с 146765 до 15, в 9784,33 раза (99,9898%). Сохранены все 240 уникальных физических пакетов, получены 30/30 JPEG с точными байтами. Программное исследование завершено.

Реальное время и 5 свежих изображений/с не достигнуты. Медианный из трёх полных проходов: 7,369795 с обработки / 6,044416 с IQ = 1,219273. Возраст декодированных изображений: 234,520 / 834,077 / 1549,595 мс (min / median / max). При MAX_VIDEO_AGE=200 мс все 30 просрочены, DISPLAYED=FRESH DISPLAYED=0, FRESH DISPLAY FPS=0.

В этих условиях ложные проверки уже не определяют скорость: они занимают 28,884 мс за весь опыт, 0,392% полной обработки. Дальнейшее удаление только этих проверок не закроет оставшееся отставание.

Рабочая точка: BPSK, 500000 символов/с, Fs=8000000 отсчётов/с, 16 отсчётов/символ. JPEG C: 128×96 RGB, quality 25, 1740–1893 байта. PRBS7, PHY, FEC, CRC, интерливер, формат пакета и фрагмента не менялись. Файлы protocol не изменены. Управление RadioMaster → ExpressLRS → штатный ELRS RX → CRSF → контроллер остаётся отдельным физическим каналом; в SDR-поток управление не добавлялось. Временные подмены старых функций рассчитаны на один последовательный RX-worker; потокобезопасность и одновременная работа нескольких таких приёмников не реализованы.

Git и аудит

Проверены root/main/HEAD/status, diff stat/check, отчёт Lab051, запись 040, индекс лабораторных, три новых вычислительных модуля, тесты и итоговые JSON. До новой реализации quick_gate PASS; pytest 688 passed, 0 failed. Verifier независимо подтвердил состав и согласованность данных.

По прямому разрешению пункта 0 задания показаны staged stat/status и создан:

9f33607d3ae82992c595de37dd64e99d7e4dea73

Lab051: accelerate 500 ksymbol/s streaming receiver

Ровно 13 файлов, 9695 добавлений: код/тесты/отчёт Lab051, журнал и индекс, baseline_profile.json, benchmark.json, stream.json, memory.json, replay.json, regression20k/result.json. IQ-окна, промежуточные замеры, JPEG и посторонние файлы не добавлялись. Push не выполнялся. Это committed HEAD всей Lab052.

Что означают исходные 147 тысяч

В исходном JSON Lab051 147082 — все строгие кандидаты, включая 240 принятых; ложных было 146842. Число 12239 — новые soft/timing-окна, включая настоящие. Исторический processing/RF=6,391045, fresh FPS=0. Называть 147082 ложными неверно.

Lab051 не сохраняла измеренные encode_seconds каждого JPEG. Её длинная временная сетка немного менялась между запусками. Поэтому в Lab052 тот же генератор, noise seed=50050, coded-bit Eb/N0=7 дБ, CFO=+10 Гц, фаза 0,73, чередование полярности и CS8-квантование используют однократно измеренную и сохранённую готовность JPEG в source_fixture.json. Проверяются SHA всех 30 JPEG. Все новые варианты получают одну и ту же последовательность IQ; сравнение потерь и кандидатов ниже относится к этой общей сетке. Историческая сетка Lab051 не объявляется побитно воспроизведённой.

Свежий профиль прежнего Lab051, после независимых решений:

Метрика Значение
IQ / Fs 6,044416 с / 8 Мвыб/с
TOTAL LOCAL PEAKS 1675842
FIRST-STAGE CANDIDATES 392785
STRICT CANDIDATES 147005
TRUE CANDIDATES 240
FALSE CANDIDATES 146765
CANDIDATES / RF SECOND 24320,795
FALSE CANDIDATES / RF SECOND 24281,089
Уникальных уточнённых позиций ложных кандидатов 11387
Повторных уточнённых позиций 135378

Отдельный свежий проход старого Lab051 без наблюдателя, на этой же зафиксированной сетке: 41,633312 с, processing/RF=6,887897, 240/240 пакетов и 30/30 точных JPEG. Сравнение с итоговыми 7,369795 с даёт около 5,65× ускорения всего приёмника. Относительно исторических 38,630132 с — 5,24×. Инструментированный профиль и чистый контроль хранятся раздельно; настольная нагрузка и прогрев влияют на временные числа.

Сначала детектор получает каждый локальный максимум выше низкого фиксированного порога. Только затем пропускает область уже принятого пакета. На продолжительном шуме число проб растёт с числом входных отсчётов. Нормировка на энергию означает, что малая абсолютная мощность шума сама по себе не уменьшает корреляционную статистику. Более 135 тысяч проверок вновь приходят к уже встречавшимся позициям.

PRBS7 повторяется через 127 символов, то есть через 2032 отсчёта в рабочем режиме. Преамбула содержит 1280 символов. Поэтому рядом с ней возможны сильные боковые корреляционные пики, помимо случайных шумовых максимумов. Только проверка структуры и последующий PHY/FEC/CRC различают их надёжнее одного большого пика.

Распределения и фон

Истинные главные пики определены после CRC PASS: берётся максимум в прежней области уточнения около восстановленного старта. Это диагностическая метрика, не дополнительный вход алгоритма. Числа истинных кадров также сверены по множеству всех 240 уникальных уточнённых стартов, а не только по счётчику.

Корреляционная статистика min median p99 max
Главный пик настоящей преамбулы, 240 0,830055 0,841691 0,848389 0,849945
Ложные строгие кандидаты, 146765 0,030964 0,035464 0,055803 0,805951
Меньшая корреляция половин у принятых кандидатов 0,825038 0,837340 0,845914 0,846226

Отдельно после приёма выделены 357 блоков, чьи корреляционные окна не пересекают ни одного декодированного физического кадра. Медиана блочных медиан =0,016353, медиана MAD =0,006184, медиана квантилей 99% =0,041875. Диапазон медиан 0,006451–0,017422 показывает, что фон не идеально стационарен. MAD — медиана абсолютного отклонения от медианы; здесь это мера разброса. Полные min/median/MAD/p90/p99/p999/max по блокам сохранены в JSON.

Адаптацию порога по median/MAD рассмотрели, но не выбрали: она не устраняет повторные структурные пики и потребовала бы отдельно доказывать чувствительность при смене фона. Основное правило использует локальную энергию каждой половины наблюдаемого IQ. Глобальный median/second или TX reference в решении не участвуют. На меняющемся фоне и в слабых положительных регрессиях дополнительные потери не наблюдались; универсальная чувствительность при любом нестационарном канале этим конечным исследованием не доказана.

Распределения обнаружителя

Выбранное правило

  1. Прежняя грубая PRBS7-корреляция на двух отсчётах/символ и прежний первый порог 0.030964473262429237. Все локальные пики считаются одинаково с baseline.
  2. На уже децимированном общем входе блока считаются корреляции двух половин опорной формы. Полная форма содержит 2644 отсчёта, каждая половина — 1322. Для каждой позиции обе величины нормируются своими энергиями IQ и опоры. Требуются обе корреляции ≥0.08474038063392896. Одиночная сильная половина этого условия не выполняет. Неизвестная фаза/полярность сохраняется через модуль; согласование фаз половин, сужающее CFO, не вводилось.
  3. Прошедшие пики объединяются в группы с полной шириной ≤20480 отсчётов от первого пика, без продления по цепочке следующих соседей. На границе блока группа обрывается: это может добавить работу, но не скрывает следующий пик.
  4. Представитель — максимум нормированной корреляции; при равенстве правый пик. Если он не проходит строгий тракт, проверяется следующая альтернатива группы. Только реальный CRC PASS разрешает прежний пропуск принятого физического кадра.
  5. VALID остаётся исключительно прежним PHY → FEC → CRC PASS.

Корреляции частей — общие FFT на блок, не новое timing-окно для каждого пика. Это дополнительные согласованные фильтры частей преамбулы на децимированном сигнале. Повторно использовать полный RRC-выход для предварительного решения не потребовалось: его создание на шумовых блоках дороже этой проверки. Полный RRC/timing и все 1601 прежних смещения остались в строгом тракте.

Порог половины выбран до положительных сравнений из номинальной шумовой модели |rho|² ~ Beta(1,N−1), N=640, alpha=0,01 на часть: sqrt(1−alpha**(1/(N−1))). Это инженерная исходная настройка, не доказанная вероятность ложного принятия после фильтрации и выбора максимумов. Корреляция отсчётов и отбор пиков нарушают независимость; фактические отказы измерены отдельно. Ни порог, ни ширина не выбираются по TX offsets.

Обоснование ширины для двух разных последовательных физических кадров:

MIN_FRAME_SAMPLES=(1280+144+2*(12*8+6)+32+10)*16=26720.

Точность прежнего локального уточнения ±2000 плюс сетка децимации 8: 20480 < 26720−2*2008 = 22704. Поэтому два центральных кандидата неперекрывающихся минимальных кадров, локализованных в этом диапазоне, не попадают в одну группу. Это обоснование для последовательного потока, не обещание разрешить любые радиоколлизии.

Почему первый вариант был отвергнут

Изначальное подавление всех меньших пиков в радиусе одной преамбулы дало 0 ложных строгих проверок на основном опыте. Adversary воспроизвёл потерю: ложная сильная преамбула на отсчёте 10000 без правильного заголовка подавляла настоящий слабый пакет на 30000. Lab051 его принимала, новый вариант — нет. Одного обоснования минимальной дистанцией двух настоящих кадров недостаточно для случая сильного ложного пика. Этот вариант отвергнут.

Исправление: дешёвая проверка всех локальных кандидатов до выбора представителя, ограниченная полная ширина группы и сохранение альтернатив после FAIL. Контрпример, его сдвиги через границу IQ-блока и сильный пакет с плохой CRC перепроверены независимо. Корректный слабый пакет теперь сохраняется. Итог Lab052 — 15, а не предварительные 0 ложных проверок.

Сравнение числа частей

Во всех четырёх вариантах сохранены те же 240 уникальных стартов и 30 точных JPEG. Наблюдатель собирал полные распределения; время этих проходов не является основным измерением производительности.

Правило Порог части Ложные строгие проверки Сокращение Потеря настоящих
Только группы, с альтернативами после FAIL — 145863 1,006× 0
Одна часть, контроль скалярного порога 0,059951 332 442,06× 0
Две части, основной вариант 0,084740 15 9784,33× 0
Три части 0,103813 6 24460,83× 0

Одна часть — только сравнительный контроль, не принятый ответ «поднять порог». Третья часть убирает ещё девять проверок, но увеличивает стоимость общего этапа: 0,531 → 0,819 с в инструментированных проходах. Две части — минимальный вариант с проверкой распределённой структуры и большим запасом к ориентиру 50×. Группировка сама по себе с корректным возвратом альтернатив почти не помогает на распределённом шуме: отказы нельзя использовать для безусловного удаления соседей.

Окончательные вычисления и свежесть

Три отдельных полных прохода без наблюдателя: processing/RF 1,257688 / 1,219199 / 1,219273, в каждом 240/240 пакетов, 30/30 JPEG, 15 ложных проверок и fresh FPS=0. Выбран целиком третий проход — медианный по суммарному времени, без смешивания временных отметок разных запусков.

Стадия / счётчик Итог
TOTAL LOCAL PEAKS 1675842
FIRST-STAGE / CHEAP CHECKED 392785
CHEAP REJECTED 388407
CHEAP RETAINED 4378
CLUSTERS, включая группы внутри уже принятых кадров 384
REPRESENTATIVE / ALTERNATIVE STRICT CHECKS 243 / 12
Пропущено внутри принятого физического кадра 4123
STRICT FALSE / TRUE 15 / 240
STRICT CHECKS / RF SECOND 42,187699
FALSE CHECKS / RF SECOND 2,481629
Новые soft/timing-окна 488
Грубая корреляция и децимация 1,790240 с
Общие проверки частей 0,446616 с
Группировка 0,003113 с
Строгие настоящие / ложные 3,996005 / 0,028884 с
Среднее настоящего кандидата 16,650 мс
Суммарно RX / сборка / JPEG decode 7,358399 / 0,006008 / 0,005388 с
Полная обработка 7,369795 с
PROCESSING / RF 1,219273
INPUT FPS / FRAMES 5 / 30
RX PACKETS / IMAGES 240 / 30
DROPPED BEFORE TX / STALE AFTER DECODE 0 / 30
DISPLAYED / FRESH DISPLAYED / FRESH FPS 0 / 0 / 0
RAW RECEIVED FPS в первые 6 с шкалы работника 4
Возраст декодированных JPEG min / median / max 234,520 / 834,077 / 1549,595 мс
MAX_VIDEO_AGE 200 мс

После устранения основной массы ложных кандидатов обнаружена лишняя подготовка: старый адаптер очищал кэш и 734 раза строил ту же опорную форму. В начальном cProfile это занимало 3,544 с. В Lab052 три неизменных массива — опора, RRC taps и PRBS7 bits — хранятся в самом приёмнике. Они побитно сравнены со старым расчётом; функции возвращаются в finally до выхода старого контекста режима. Это не новая PHY и не передача данных TX в RX.

Финальный отдельный cProfile только вокруг push/finish показывает следующие узкие места: FFT 1,650 с собственного времени, FIR-децимация 1,295 с, Viterbi 1,397 с; уточнение 1,084 с и matched 0,865 с включают свои FFT, поэтому эти строки нельзя складывать повторно. Копирование ndarray — 0,313 с, прочие расходы push — также измерены. Данные cProfile не заменяют чистый замер.

Теперь основное ограничение — настоящие пакеты вместе с общей обработкой IQ. 40 пакетов/с ×16,650 мс требуют около 666 мс обработки в секунду; к ним добавляются децимация/корреляция, общий промежуточный этап, копирование и организация блоков. Во время JPEG-пачки пакет приходит каждые 11,532 мс, быстрее средней строгой обработки настоящего пакета. Нужны и дальнейшее снижение общей стоимости, и проверка задержки пачки. Одного среднего processing/RF<1 недостаточно для доказательства MAX_VIDEO_AGE=200 мс при неравномерной нагрузке.

Событийная шкала повторно использует Lab051: worker_start=max(previous_worker_end, block_arrival); worker_end=start+measured_push. Сборка/JPEG decode добавляют измеренные затраты. RF и CPU перекрываются; стоимость RX не заменяется нулём. Генерация IQ, сравнение SHA и JSON I/O исключены. Восстановленные байты сверяются с TX только после принятия, сборки и JPEG decode.

Возраст относится к декодированным, затем отброшенным изображениям. Камера и физический экран не измерялись, их задержки UNKNOWN. Capture lookup используется после вердикта для отчётной модели вывода. В неизменном формате фрагмента нет capture timestamp; независимый live-контроль возраста не реализован. Максимальное отставание 1,426779 с соответствует примерно 91313848 байтам внешнего complex64 IQ. Этот backlog вычислен, но не хранится офлайн-источником. REAL-TIME CAPABLE=NO; 5 FPS SOFTWARE REAL-TIME TARGET=FAIL.

Память и длительное состояние

  • Пик удерживаемого IQ: 3177728 байт = 3,031 МиБ; проверяемый предел 3398912.
  • Инженерная оценка рабочих массивов: 142775456 байт = 136,161 МиБ. Это прежний запас Lab051, включающий буферы; не доказанный универсальный предел C-аллокаций и не отдельная величина для сложения с RSS.
  • Оценка массивов именно обнаружителя в опыте: 1480664 байта; постоянные новые шаблоны — 171140 байт. Временные FFT отдельно целиком не перечислены, они покрываются общей инженерной оценкой с запасом.
  • RSS выбранного полного прохода: 149393408 байт после потока; process RSS peak 168804352 байта = 160,984 МиБ. Это весь процесс Python, включая библиотеки, генератор и аллокатор, а не только массивы RX.
  • В основном опыте одновременно максимум 23 прошедших дешёвый этап пика и 3 группы на блок; между вызовами и после finish состояние кандидатов =0.
  • Предел числа кандидатов задаётся числом локальных максимумов ограниченного IQ-окна. Списки групп и альтернатив уничтожаются на выходе, не копятся в сессии.

Дополнительно измерен отдельный RX replay длительностью 0,063184 с IQ с одним независимо восстановленным пакетом. Наблюдаются 6754 ndarray, из них 6681 владеют данными; суммарные байты наблюдаемых владельцев за проход — 37378942 (не пик). tracemalloc peak=11586792 байта, после выхода остаётся 6461 байт отслеживаемых выделений. Входной IQ подготовлен до tracing. Это измерение отслеживаемой памяти RX, включая Python, и нижняя граница числа массивов в локальных переменных; временные C/SciPy-аллокации полностью не перечислены. Короткий replay не заменяет RSS полного опыта. Подсчёт ndarray и tracemalloc выполняются в разных проходах; tracemalloc измеряет следующий прогретый проход. Его tracing-время не используется для оценки скорости, а пик не объявляется верхней границей холодного RX.

Длинный тест: 2048 блоков, 16,777216 с распределённого шума с амплитудными масштабами 0,002 / 0,2 / 20 / 0,02. Получены 4783206 локальных пиков, 1068038 кандидатов первого каскада; 1068037 отклонены дешёво, один дошёл до строгого отказа. Ни одного принятого шумового пакета. После прогрева в контрольных точках IQ=2653440 байт, децимация=397216 байт, сохранённых кандидатов=0. RSS сначала растёт при прогреве кэшей, затем стабилизируется; последние четыре точки 140533760–140550144 байта. После finish оба IQ-буфера освобождены, константы ограниченного размера остаются.

Полная диагностическая история NPZ/JSON принадлежит конечному эксперименту, не приёмнику. Она выключена в окончательном измерении скорости и длинном тесте. Ограниченный RX не подменяет отсутствующую живую входную очередь с overflow/gap policy: аппаратный драйвер и переполнения в этой лабораторной не реализованы.

Регрессии и проверка

45 дополнительных слабых сценариев: Eb/N0=0/1/2/3/7 дБ, CFO=−100/+10/+100 Гц, три seed, масштабы амплитуды 0,002/0,2/1 и разные фазы/полярности. Старый и новый RX правильно приняли 37/45; 0 потерянных пакетов baseline. Восемь случаев не принимаются обоими, поэтому это не объявляется 45/45 успешным декодированием. Основной поток отдельно даёт 240/240 и 30/30 SHA MATCH.

30 быстрых целевых тестов покрывают шумовые группы, равные пики, отсутствие цепочного расширения, два минимальных соседних кадра, слабый кадр, одну сильную ложную половину, фазу/масштаб, распределённый и меняющийся шум, границы блоков, освобождение состояния, эквивалентность общих корреляций отдельным vdot, ложную преамбулу и восстановление функций. Verifier и adversary независимо подтвердили исправление воспроизведённой потери; обхода PHY/FEC/CRC не найдено.

Сохранённая Lab047-запись 20 ксимв/с повторно разобрана прежним Lab048: 36/36, CRC PASS, JPEG SHA MATCH, 19 ошибок до FEC, 0 после, 0 байтовых ошибок. SHA: e913dab9ac32795250880a4be67fbbbf89a4f96536842476ab591d1acaafc02a. Обработка 15 с старого IQ заняла 13,213346 с. Новый адаптер принимает только 500 ксимв/с; режим 20 ксимв/с, его порог и вычислительный тракт не менялись.

Итоговый quick_gate PASS: 718 тестов, 20 функциональных проверок, 13 штатных пропусков. Полный pytest: 718 passed за 77,15 с, 0 failed; одно предупреждение доступа к .pytest_cache не влияет на результат тестов. Дополнительный helper измерения массивов выполнен отдельно, все три модуля после его добавления проверены py_compile и своими run_functional_tests. git diff --check PASS. Машинная сверка итоговых JSON — в acceptance.json: обязательные регрессии PASS, real-time и 5-fps target явно FAIL.

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

python experiments/lab052_detector_study.py --stage profile
python experiments/lab052_detector_study.py --stage baseline
python experiments/lab052_detector_study.py --stage rules
python experiments/lab052_detector_study.py --stage stream
python experiments/lab052_detector_study.py --stage cpu
python experiments/lab052_detector_study.py --stage memory
python experiments/lab052_detector_study.py --stage sensitivity
python experiments/lab052_detection_statistics.py
python -m pytest tests/test_lab052_candidate_detection.py -q -p no:cacheprovider
python tools/quick_gate.py
python -m pytest tests/ -q
git diff --check

Старый IQ: команда Lab048 с параметрами --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/lab052/regression20k.

Первый запуск фиксирует измеренную готовность JPEG; последующие используют source_fixture.json. Между запусками меняются фактические CPU timings и RSS, поэтому временные числа могут отличаться. Основной приём и набор кандидатов на зафиксированном источнике воспроизводятся. Большой новый IQ-файл не создаётся.

Lab052 git add/commit и push не выполнялись. RF TX=0; HackRF и другое оборудование не перечислялись и не запускались. QPSK, 180 МГц, аппаратные 500 ксимв/с и переход к 10 fps не выполнялись. Следующий рекомендуемый этап — отдельная оптимизация истинных пакетов/общего IQ и задержки JPEG-пачки. STOP.