Lab048: accelerate BPSK receiver to real-time
This commit is contained in:
@@ -176,6 +176,30 @@
|
||||
`0.030964473262429237` не менялся, второй RF-сеанс не выполнялся.
|
||||
- **Журнал:** записи 033-034 ([`PROJECT_LOG.md`](../PROJECT_LOG.md)).
|
||||
|
||||
## Lab048
|
||||
|
||||
- **Цель:** профилирование и ускорение доказанного автономного BPSK-приёмника
|
||||
без изменения детектора, PHY, FEC, CRC и порога.
|
||||
- **Статус:** завершена успешно только на сохранённой RX-записи Lab047;
|
||||
обработка ускорена с 37,423910 до 13,966058 с, в 2,679633 раза. Для 15,0 с
|
||||
эфира отношение времени обработки равно 0,931071:
|
||||
`REAL-TIME CAPABLE = YES`.
|
||||
- **Результат:** сохранены 36/36 кадров, FEC PASS 36/36, CRC PASS/FAIL 36/0,
|
||||
полный JPEG, точный SHA-256, 0 ошибочных байтов и битов. Порог
|
||||
`0.030964473262429237` не изменён.
|
||||
- **Ограничение:** приблизительный пик памяти остаётся около 1,44 ГиБ из-за
|
||||
16-миллионного блока грубого поиска; запас реального времени около 1,03 с.
|
||||
- **Основной эксперимент:**
|
||||
[`experiments/lab048_receiver_performance.py`](../experiments/lab048_receiver_performance.py).
|
||||
- **Проверки:**
|
||||
[`tests/test_lab048_receiver_performance.py`](../tests/test_lab048_receiver_performance.py).
|
||||
- **Документ состояния:**
|
||||
[`docs/lab048_current_state.md`](lab048_current_state.md).
|
||||
- **Обработанные данные:** машинный результат и восстановленный JPEG находятся
|
||||
в [`data/processed/lab048`](../data/processed/lab048).
|
||||
- **RF:** новый радиосеанс не выполнялся, `RF TX = 0`.
|
||||
- **Журнал:** запись 035 ([`PROJECT_LOG.md`](../PROJECT_LOG.md)).
|
||||
|
||||
## Правило хранения данных
|
||||
|
||||
Большие записи IQ, потоки `cs8`, бинарные объекты и другие аппаратные исходные
|
||||
|
||||
206
docs/lab048_current_state.md
Normal file
206
docs/lab048_current_state.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# 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 не запускать.
|
||||
Reference in New Issue
Block a user