207 lines
12 KiB
Markdown
207 lines
12 KiB
Markdown
# 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 не запускать.
|