# 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/_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 не добавлять.