# Lab048: профилирование и ускорение автономного BPSK-приёмника ## Состояние Lab048 завершена успешно только на сохранённой аппаратной RX-записи Lab047. Новый радиосеанс не выполнялся: `RF TX = 0`, HackRF не запускался. Научные критерии обнаружения и принятия кадра, BPSK, PRBS7, PHY, FEC, CRC, формат пакета и порог `0.030964473262429237` не изменены. Итоговая обработка 15,0 с эфира занимает 13,966058 с. Это в 2,679633 раза быстрее исходных 37,423910 с и впервые быстрее длительности самой записи: отношение времени обработки к времени эфира равно 0,931071. ## Исходная контрольная точка Использована та же запись: `C:\Users\user\Documents\SDR_Rover_Lab047_raw\20260903_115747\rx_stream.cs8`. Размер — 240000000 байт, 120000000 комплексных отсчётов, длительность 15,0 с, SHA-256 `8521bf1b574cd1fe4e0a4f980c00c8664f8bb51551a241e46449e1eb2c77e635`. Принятые до оптимизации метрики Lab047: - полное время: 37,423910 с; - строгая ступень: 32,459969 с; - среднее время строгой проверки: 0,470434 с; - строгих кандидатов: 69; - приблизительный пик памяти: 1545755352 байта, около 1,44 ГиБ; - действительных кадров: 36; - CRC PASS/FAIL: `36/0`; - полный JPEG, SHA-256 совпадает, ошибок байтов и битов нет. ## Базовое профилирование До изменения кода выполнен один полный проход под `cProfile`. Он сохранил 185828 локальных максимумов, 32414 кандидатов первого каскада, 69 строгих проверок, 36 действительных кадров, CRC PASS/FAIL `36/0` и точный JPEG. Накладные расходы профилировщика увеличили полное время до 45,947287 с, поэтому коэффициент ускорения считается относительно принятого измерения без профилировщика 37,423910 с. Десять функций по суммарному времени: | Функция | Вызовы | Суммарно, с | |---|---:|---:| | `_run_with_fixed_candidate_rule` | 1 | 45,945 | | `stream_process_capture` | 1 | 45,930 | | `decode_candidate_from_memmap` | 69 | 40,912 | | `_extract_soft_window` | 105 | 25,697 | | `_matched_waveform` | 105 | 21,199 | | `fftconvolve` | 252 | 13,660 | | `_freq_domain_conv` | 252 | 12,811 | | `__ua_function__` FFT | 756 | 11,889 | | `root_raised_cosine_taps` | 175 | 11,885 | | `_execute_nD` FFT | 756 | 11,881 | Десять функций по собственному времени: | Функция | Вызовы | Собственное время, с | |---|---:|---:| | `scipy.fft c2c` | 756 | 11,222 | | `_matched_waveform` | 105 | 5,704 | | `numpy.isclose` | 1400175 | 5,407 | | `upfirdn.apply_filter` | 9 | 3,171 | | `_timing_offsets` | 105 | 2,859 | | `root_raised_cosine_taps` | 175 | 2,306 | | `_scale_and_carrier` | 70 | 1,801 | | генератор `numpy.isclose` | 7000875 | 1,160 | | `_cs8_window` | 113 | 1,105 | | `_freq_domain_conv` | 252 | 0,917 | Профиль показал повторное формирование одной опорной преамбулы 70 раз и одинаковых RRC-коэффициентов 175 раз. Согласованный фильтр вызывался 105 раз: 69 коротких окон для PHY и 36 полных окон. Временная синхронизация выполняла 1601 Python-итерацию для каждого окна. Витерби занимал лишь 0,875 с на все 36 кадров, CFO/фаза — 0,054 с на 105 вызовов, PHY-заголовок — 0,006 с. Поэтому FEC, CFO, фаза, PHY и CRC не оптимизировались. ## Выполненные оптимизации Изменены только вычислительные детали приёмника: 1. Опорная PRBS7-преамбула формируется один раз, кэшируется и защищается от записи. 2. RRC-коэффициенты формируются один раз, хранятся как неизменяемый `float32`. 3. Та же полная линейная RRC-свёртка вычисляется методом overlap-add (`oaconvolve`) вместо единой FFT (`fftconvolve`). 4. Неизменяемые векторы переноса несущей кэшируются по началу и длине окна; кэш ограничен четырьмя записями. 5. Рабочие массивы согласованного фильтра переведены с `complex128` на `complex64`. Максимальное абсолютное расхождение с прежним фильтром на проверочном шумовом массиве равно `1,263e-6`, менее `5e-7` от пика. 6. Преамбульная временная синхронизация считает те же 1601 нормированные оценки группами по 400 фазам и не вычисляет второй идентичный набор оценок. Прямой тест прежнего `fftconvolve/complex128` и нового `oaconvolve/complex64` требует абсолютное расхождение не более `2e-6` и отношение максимального расхождения к пику менее `5e-7`. Отдельные тесты подтверждают точность и неизменяемость кэшей, ограничение их размера, тот же timing offset и отсутствие TX/expected-frame параметров у независимого API. Существующие сквозные тесты защищают те же PHY, FEC, CRC и полезные данные. ## Единственный итоговый полный проход После программного шлюза выполнен один итоговый полный проход той же записи. Первый вызов нового командного файла завершился до импорта проекта из-за отсутствующего загрузочного пути: RX не открывался и файл-свидетель не был создан. После исправления загрузочного блока выполнен ровно один фактический полный проход; автоматического повтора нет. Независимая стадия не получала ожидаемое число кадров, TX offsets, номера TX-фрагментов, переданный JPEG, пакеты или кодированные биты. Только после `completed = true` и `OBJECT COMPLETE = YES` выполнена диагностическая сверка с TX. Результат: - полное время: 13,966058 с; - грубый поиск: 4,776677 с; - строгая ступень: 9,171629 с; - среднее время строгой проверки: 0,132922 с; - ускорение полного прохода: 2,679633 раза; - отношение обработки к длительности эфира: 0,931071; - `REAL-TIME CAPABLE = YES`; - локальных максимумов: 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`. Только после независимого вердикта подтверждены 36/36 переданных кадров, 19 ошибок кодированных бит до FEC, 0 ошибок протокольных бит после FEC и 0 ложных CRC PASS. Восстановленный JPEG имеет 8724 байта, RGB 320 x 240, декодируется Pillow и имеет SHA-256 `e913dab9ac32795250880a4be67fbbbf89a4f96536842476ab591d1acaafc02a`. SHA-256 переданного файла совпадает; ошибочных байтов 0, ошибочных битов 0. ## Время стадий после оптимизации Измерения вложенных функций пересекаются по времени: | Стадия | Вызовы | Суммарно, с | |---|---:|---:| | преобразование CS8 в `complex64` | 113 | 1,487798 | | уточнение начала | 69 | 3,121533 | | согласованный фильтр | 105 | 3,282575 | | временная синхронизация | 105 | 1,270783 | | CFO, фаза и мягкие решения | 105 | 0,042236 | | PHY-заголовок | 105 | 0,004111 | | FEC, разбор пакета и CRC | 36 | 0,645559 | | Витерби | 36 | 0,627109 | Следующее узкое место верхнего уровня — грубый поиск, 4,776677 с. Внутри строгой ступени сопоставимы согласованный фильтр, 3,282575 с, и уточнение начала, 3,121533 с. Запас реального времени пока мал: около 1,03 с на 15 с эфира. ## Память Консервативная пиковая оценка осталась 1545755352 байта, около 1,44 ГиБ, поскольку максимум создаёт неизменённая обработка 16-миллионного блока грубого поиска. Крупнейшие явно управляемые массивы: | Назначение | dtype | shape | MiB | |---|---|---:|---:| | окно грубого поиска | `complex64` | 16000000 | 122,070 | | прореженный поток | `complex64` | 800000 | 6,104 | | вектор переноса грубого поиска | `complex64` | 800000 | 6,104 | | оценки корреляции | `float32` | 773561 | 2,951 | | полное строгое окно | `complex64` | 2306400 | 17,596 | | кэшированный вектор несущей | `complex64` | 2306400 | 17,596 | | основная полоса строгого окна | `complex64` | 2306400 | 17,596 | | выход согласованного фильтра | `complex64` | 2310400 | 17,627 | | RRC-коэффициенты | `float32` | 4001 | 0,015 | Переход строгой ступени с `complex128` на `complex64` уменьшил её рабочие массивы, но не общий пик всего процесса. Уменьшение блока грубого поиска в Lab048 не выполнялось, чтобы не менять блочные границы и результаты детектора. ## Артефакты - `experiments/lab048_receiver_performance.py`; - `tests/test_lab048_receiver_performance.py`; - `data/processed/lab048/final_processing_attempt.json`; - `data/processed/lab048/result.json`; - `data/processed/lab048/recovered_image.jpg`. Файл `cProfile` сохранён только как временный локальный профиль вне проекта: `C:\Users\user\AppData\Local\Temp\lab048_baseline_20260903_6fb7e4e.pstats`. ## Проверки - связанные тесты после поэтапных изменений: 169 passed; - `python tools/quick_gate.py`: 547 тестов и 11 функциональных проверок, PASS; - `python -m pytest tests/ -q`: 547 passed, 0 failed; - `git diff --check`: PASS. ## Следующий этап STOP. Не переходить к Lab049, видео, QPSK, повышению символьной скорости или новому RF-сеансу без отдельной постановки. `RF TX = 0`; HackRF не запускать.