Files
SDR-Rover/docs/lab044_current_state.md
LittleSam129 72f933a32b Lab044: validate arbitrary BPSK packet reception
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-31 14:39:12 +03:00

226 lines
15 KiB
Markdown
Raw 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.
# Lab044: текущее состояние
## Статус
Lab044 hardware experiment ВЫПОЛНЕН один раз. RF TX count = 1.
Результат: **RESULT C** (PHY sync восстановлен независимо, но protocol packet
не восстановлен, CRC NOT_REACHED). CRC **не** PASS — это честный
отрицательный, но научно полезный результат.
Реализация подготовлена поверх рабочей точки Lab043 (checkpoint `4cee6e9`,
"Lab043: establish reproducible HackRF BPSK working point") и не меняет её
RF-параметры, gain, PHY sync, PRBS7-преамбулу, CRC или независимый детектор.
## Фактический аппаратный результат (31 августа 2026 года)
Каталог артефактов:
`C:\Users\user\Documents\SDR_Rover_Lab044_raw\20260831_140142_bpsk_packet\`
- **RF TX count = 1** (ровно одна передача, `TransmitGuard`, без retry).
- **Common clock = PASS**: MASTER reg29 = 0x26, SLAVE reg29 = 0x26,
SLAVE CLKIN = DETECTED за 69 мс (1 опрос), bounded polling 0.05/0.05/0.5 с.
- **PHY SYNC = PASS**: независимый PRBS7-детектор преамбулы,
peak/median +38.3 dB, peak/second +11.6 dB, payload не использовался.
- **FRAME HEADER**: PHY SYNC 0xD391 декодирован верно, но поле PHY LENGTH
искажено битовой ошибкой — декодировано 264 вместо 268 байт. Структурно
`frame_header_pass=true` (sync совпал, длина в границах 1..1024), но длина
фактически неверна.
- **PROTOCOL HEADER = FAIL** (`PAYLOAD_LENGTH_MISMATCH`): protocol SYNC
0xAA55, version 1, message type 2 (TELEMETRY), sequence 44 декодированы
верно, но payload_length = 2304 вместо 256 (битовая ошибка в старшем
байте длины). `expected_packet_bytes = 2316 ≠ 264`.
- **CRC = NOT_REACHED** (packet не собран корректно, до CRC decode не дошло).
- **Classification = C**.
### Радиометрика
- independent preamble detected: **да**
- preamble errors: **8 / 1280** (physical-layer BER ≈ **0.625 %**,
согласуется с ~0.4586 % Lab043)
- CFO: **0.101 Hz** (общий опорный генератор ⇒ почти нулевой сдвиг несущей)
- SNR: **7.19 dB**
- separation: **4.574**
- timing offset: **−3 samples**
- ADC peak: peak_complex **5.10** code, peak_component **5.0** code
- ADC p99.9: **3.606** code (headroom 122 code до предела int8)
- clipping: **0** (fraction 0.0), near-int8: 0
- RX overruns: **0**, TX underruns: **0** (оба лога чистые)
### Ошибки данных (только ПОСЛЕ независимого CRC decision)
CRC-решение было NOT_REACHED, поэтому packet/payload BER **не вычисляются
как «после успешного decode»**. Диагностическое сравнение с TX-артефактами
показало:
- packet: `comparable=false` — RX 264 байт против TX 268 байт (длины не
совпадают из-за искажения PHY LENGTH), побитовый BER не определён.
- payload: `comparable=false` — `rx_payload.bin` не создан (protocol header
не прошёл), payload не извлечён.
Первичный вывод: канал по-прежнему даёт ~0.6 % BER (как в Lab043), и в этом
единственном кадре ошибки попали именно в поля длины (PHY LENGTH и protocol
payload_length), что структурно сорвало разбор пакета раньше CRC. Это не
новый дефект, а известная чувствительность незащищённого кадра к одиночным
битовым ошибкам в критических полях — прямой довод в пользу FEC/защиты
заголовка на следующем этапе.
### BEFORE / DURING / AFTER
Границы построены по независимо найденному preamble start (sample 6 122 876)
и декодированной PHY LENGTH.
| Регион | Длительность | RMS (после DC) | p95 | p99 | p99.9 | peak | clipping |
|---|---|---|---|---|---|---|---|
| BEFORE | 0.765 с | 0.776 | 3.162 | 3.606 | 3.606 | 5.00 | 0 |
| DURING | 0.171 с | 0.833 | 3.162 | 3.606 | 3.606 | 4.47 | 0 |
| AFTER | 3.063 с | 0.782 | 3.162 | 3.606 | 3.606 | 5.10 | 0 |
DURING отличается от фоновых BEFORE/AFTER повышенным RMS после удаления DC
(0.833 против 0.776/0.782) — то есть на интервале кадра действительно
присутствует переданный сигнал над фоном; хвосты амплитуды (p95/p99/p99.9)
и clipping идентичны, перегрузки АЦП нет.
### Артефакты и SHA-256 (пересчитаны независимо, совпали с metadata.json)
- `tx_payload.bin` (256 B): `aae1ee74…e27109`
- `tx_packet.bin` (268 B): `cc179e93…e4554b`
- `tx_bpsk_cs8.iq` (2 798 400 B): `15260dbc…f42e7`
- `rx_cs8.iq` (64 000 000 B): `8d5f4cd1…1019f1` (в Git не добавляется)
- `rx_packet.bin` (264 B): `b6ebbce3…c694e1`
- `rx_payload.bin`: **не создан** (SHA-256 = null, фиктивный файл не создаётся)
### Cleanup
MASTER CLKOUT OFF (SLAVE CLKIN = "no clock signal detected"),
SLAVE CLKOUT OFF, зависших `hackrf_transfer.exe` нет.
## Цель Lab044
Впервые передать по реальному RF BPSK-пакет с произвольным payload, который
приёмник заранее не знает: `arbitrary bytes → protocol packet → PHY frame →
BPSK → RF → independent synchronization → demodulation → frame parsing →
CRC verification`. CRC PASS желателен, но не обязателен — CRC FAIL при
BER около 0.4586% (унаследованном из Lab043) является нормальным научно
полезным результатом (RESULT B).
## Формат (подтверждён по фактическому коду, не принят на веру)
- `protocol/packet.py`: `HEADER_FORMAT = ">HBBHH"` (8 байт: SYNC 0xAA55,
version, message type, sequence number, payload length), CRC-32
(`zlib.crc32`, big-endian uint32, 4 байта), CRC покрывает header+payload.
- Payload = **256 bytes**, protocol packet = **268 bytes**
(8 + 256 + 4), сгенерирован через `secrets.token_bytes` — системный
источник случайности, один раз.
- PHY frame = `[1280-bit PRBS7 preamble Lab043][PHY SYNC 0xD391 + PHY LENGTH
uint16, ">HH", big-endian][protocol packet]` = **3456 BPSK-символов**
(1280 + 32 + 268×8). Преамбула — та же аппаратно проверенная
1280-битная PRBS7 (`pilot_prbs7`, `PILOT_INITIAL_STATE=0x7F`) из
`experiments/lab043_hackrf_bpsk.py`, не generic-маркер из
`protocol/bpsk_radio.build_radio_frame()`.
- Waveform = **1,399,200 сэмплов** при 8 MS/s ⇒ длительность
**≈ 174.9 мс** (после RRC-формирования Lab042: rolloff 0.35,
span 10 символов, guard 16 символов).
- Bit order: MSB-first, явно (`np.unpackbits(..., bitorder="big")`).
- MAX_PACKET_BYTES = 1024 (fail-closed предел для PHY LENGTH).
## RF-параметры (без изменений от Lab043)
RF center 1080 MHz, sample rate 8 MS/s, symbol rate 20 ksym/s, digital
carrier offset +50 kHz, TX VGA = 10 dB, RX LNA = 24 dB, RX VGA = 24 dB,
RF amp OFF, common clock: JYEC TCXO 10 MHz MASTER → MASTER CLKOUT → SLAVE
CLKIN, bounded common-clock preflight (initial_settle=0.05s,
poll_interval=0.05s, timeout=0.5s), reg29 = 0x26 на обеих платах.
## Пробелы аудита A/B/C — закрыты
**A. TRUNCATED PROTOCOL PACKET TEST — FIXED.** Добавлен
`test_truncated_protocol_packet_is_flagged_without_exception`
(`tests/test_lab044_hackrf_bpsk_packet.py`): синтетический capture с
полной преамбулой и полным PHY header, но физически обрезанным (не
дополненным нулями) телом protocol packet. Ожидаемое и фактическое
поведение: `phy_sync_pass=True`, `frame_header_pass=False`,
`packet_received=False`, `frame_header["reason"]=="TRUNCATED_PACKET"`,
`crc=="NOT_REACHED"`, без исключений, без изменения рабочей
классификации. Исходный код `analyze_capture_independently` этот путь уже
предусматривал — не хватало только теста, подтверждающего его
достижимость и корректность.
**B. BEFORE/DURING/AFTER DIAGNOSTICS — FIXED.** Добавлена функция
`_capture_diagnostics()` в `experiments/lab044_hackrf_bpsk_packet.py`,
переиспользующая `lab043._capture_intervals()` и `lab043.raw_statistics()`
без изменения самого Lab043 (совместимый набор полей: mean I/Q,
RMS после DC removal, p95/p99/p99.9 magnitude, peak, clipping
count/fraction — плюс добавленные `sample_range` и
`duration_seconds` на каждый регион). Граница DURING строится только по
`detection["refined_start_sample"]` (независимый preamble) и по PHY
LENGTH, если она уже декодирована; если PHY header ещё не восстановлен —
по максимально возможной длине кадра Lab044 (MAX_PACKET_BYTES), payload
никогда не используется. Если независимый preamble не найден вовсе,
DURING остаётся `None` с явной пометкой в поле `note`, сохраняется только
`whole_capture`. Результат добавлен как `analysis["capture_intervals"]` во
всех пяти точках возврата `analyze_capture_independently` (RESULT A/B/C/D).
Тесты: `test_capture_intervals_present_for_independently_decoded_frame`,
`test_capture_intervals_undefined_when_no_independent_start`.
**C. SHA-256 ДЛЯ ВСЕХ BINARY ARTIFACTS — FIXED.** Добавлена функция
`_artifact_sha256()`, переиспользующая `lab043._sha256_file()`. После
`run_hardware_experiment()` в `metadata.json` появляется поле
`artifact_sha256` с хэшем каждого РЕАЛЬНО существующего файла:
`tx_payload.bin`, `tx_packet.bin`, `tx_bpsk_cs8.iq`, `rx_cs8.iq`,
`rx_packet.bin`, `rx_payload.bin`. Для `rx_packet.bin`/`rx_payload.bin`,
если decode не дошёл до их создания, значение — `None`; фиктивный файл не
создаётся (это поведение уже было в коде и не менялось). Тесты:
`test_artifact_sha256_recorded_for_every_real_rx_and_tx_file`,
`test_no_fake_rx_artifacts_or_hashes_when_packet_not_decoded`.
Ничего из RF-параметров, gain, packet format, PHY sync, PRBS7-преамбулы,
payload size, CRC, независимого детектора или порогов Lab043 не менялось.
FEC/QPSK/JPEG/video не добавлялись.
## Что реально проверено в этой сессии
Эта сессия работает без `device_bash` на машине пользователя и без доступа
к сети из своей песочницы (нет pip/apt-репозитория), поэтому команда
`pytest` в буквальном смысле **NOT EXECUTED IN THIS SESSION**.
Вместо этого фактически выполнено (все результаты — реальный вывод
интерпретатора, не предположение):
- `ast.parse()` по `experiments/lab044_hackrf_bpsk_packet.py` и
`tests/test_lab044_hackrf_bpsk_packet.py` — синтаксис корректен.
- Полный импорт `experiments.lab044_hackrf_bpsk_packet` со всей цепочкой
зависимостей (`protocol.*`, `experiments.lab043_*`) — импортируется без
ошибок.
- Все 22 функции `test_*` из `tests/test_lab044_hackrf_bpsk_packet.py`
(17 существовавших + 5 новых для A/B/C) вызваны напрямую, как обычные
Python-функции, с самодельными подстановками фикстур `tmp_path` и
`monkeypatch` (реальный pytest недоступен для установки в этой
песочнице — нет сетевого доступа к PyPI/apt) — **22/22 PASS**. Это
честная проверка логики, но не замена запуску `pytest` как CLI-команды.
- `python tools/quick_gate.py` запущен реально: синтаксис и импорт
`protocol/*` прошли, единственная сообщённая ошибка — сам pytest не
установлен (`No module named pytest`), что ожидаемо и не является
дефектом кода.
- Аппаратный RF-эксперимент не запускался и не имитировался.
## Что должен сделать локальный агент перед RF
1. `python tools/quick_gate.py` — уже в этой сессии проходит синтаксис и
импорт; локально должен пройти целиком, включая `pytest tests -q`.
2. `python -m pytest tests/ -q` — полный реальный прогон (в этой сессии
недоступен физически, см. выше). Ожидается: все прежние тесты Lab043
зелёные, все 22 теста `test_lab044_hackrf_bpsk_packet.py` зелёные.
3. `git diff --check` — конфликт-маркеры/whitespace.
4. Только после полного PASS — `python experiments/lab044_hackrf_bpsk_packet.py --hardware --i-understand-hardware`.
5. STOP conditions: common-clock preflight FAIL, RX завершился до TX,
TX/RX вернули ненулевой код, log содержит overrun/underrun, любое
исключение — во всех случаях RF TX должен остаться равным 0 (гарантия
`TransmitGuard`, покрыта тестами `test_common_clock_failure_means_zero_tx`,
`test_frame_validation_failure_means_zero_tx`,
`test_rx_startup_failure_means_zero_tx`).
6. Artifacts появятся в `~/Documents/SDR_Rover_Lab044_raw/<timestamp>_bpsk_packet/`:
`tx_payload.bin`, `tx_packet.bin`, `tx_bpsk_cs8.iq`, `rx_cs8.iq`,
`rx_packet.bin` (если извлечён), `rx_payload.bin` (если извлечён),
`metadata.json`, `analysis.json`.
7. Raw IQ (`rx_cs8.iq`, `tx_bpsk_cs8.iq`) в Git не добавлять.