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

391 lines
34 KiB
Markdown
Raw Permalink 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.
# 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 в решении не участвуют.
На меняющемся фоне и в слабых положительных регрессиях дополнительные потери
не наблюдались; универсальная чувствительность при любом нестационарном канале
этим конечным исследованием не доказана.
![Распределения обнаружителя](../data/processed/lab052/detector_distributions.png)
## Выбранное правило
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.
## Файлы и воспроизведение
- [Обнаружитель и потоковый адаптер](../experiments/lab052_candidate_detection.py)
- [Генератор сравнений, измерений и регрессий](../experiments/lab052_detector_study.py)
- [Статистика и график](../experiments/lab052_detection_statistics.py)
- [Целевые тесты](../tests/test_lab052_candidate_detection.py)
- [Профиль старого RX](../data/processed/lab052/baseline_profile.json)
- [Старый RX без наблюдателя на том же источнике](../data/processed/lab052/baseline_stream.json)
- [Сравнение правил](../data/processed/lab052/rule_comparison.json)
- [Статистика после решений](../data/processed/lab052/statistics.json)
- [Итоговый полный поток](../data/processed/lab052/stream.json)
- [Длинное состояние](../data/processed/lab052/memory.json)
- [Измеренные массивы и tracemalloc](../data/processed/lab052/allocations.json)
- [Слабые сигналы](../data/processed/lab052/sensitivity.json)
- [Отдельный профиль CPU](../data/processed/lab052/receiver_cpu.txt)
- [Старая запись 20 ксимв/с](../data/processed/lab052/regression20k/result.json)
- [Сверка критериев и отрицательного результата скорости](../data/processed/lab052/acceptance.json)
```powershell
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.