Files
SDR-Rover/docs/lab046_current_state.md
2026-09-02 16:18:40 +03:00

43 KiB
Raw Blame History

Lab046: текущее состояние

Статус

LAB046 SOFTWARE OBJECT TRANSPORT = COMPLETE.

CONTINUOUS STREAM VALIDATION = PASS (NORMALIZED SOFT-SYMBOL LEVEL).

ПРОГРАММНАЯ ПРОВЕРКА НЕПРЕРЫВНОГО СЫРОГО IQ-ПОТОКА = PASS.

LAB046C HARDWARE RESULT = A (FULL 1400 MHZ RF OBJECT TRANSPORT).

FULL RF OBJECT TRANSPORT = PASS.

RF TX PROCESS COUNT FOR THIS RESULT = 1.

VALID FRAMES = 68/68. CRC PASS = 68. CRC FAIL = 0.

PRE-FEC CODED BIT ERRORS = 787. POST-FEC PROTOCOL BIT ERRORS = 0.

OBJECT COMPLETE = YES. OBJECT SIZE = 16384 BYTES.

SHA MATCH = YES. OBJECT BYTE ERRORS = 0. OBJECT BIT ERRORS = 0.

HISTORICAL FIRST LAB046C AT 1080 MHZ = RESULT C.

THE HISTORICAL 1080 MHZ RESULT WAS NOT RECLASSIFIED.

INDEPENDENT 1400 MHZ THRESHOLD = 0.033527640625834465.

CURRENT SOFTWARE GATE = PASS: quick_gate 512, pytest 512 passed, git diff --check clean.

Lab046A/B остаются только программными лабами. В истории Lab046C отдельно сохранены первый полный аппаратный результат C на 1080 МГц и новый полный аппаратный результат A на 1400 МГц. В новом опыте выполнен ровно один заранее разрешённый непрерывный процесс передачи без повтора. Ни JPEG, ни видео, ни QPSK не запускались.

Lab046C: полный аппаратный результат 1400 МГц, RESULT A

2 сентября 2026 года через два HackRF передан новый криптографически случайный объект размером 16384 байта. Он был разбит существующей архитектурой Lab046 на 68 фрагментов и передан 68 последовательными защищёнными BPSK-кадрами одним непрерывным процессом. Частота равна 1400 МГц, частота дискретизации 8 МГц, символьная скорость 20 ксимв/с, цифровое смещение несущей +50 кГц. Усиления не менялись: TX VGA 10 дБ, RX LNA/VGA 24/24 дБ, RF amplifier и Bias-Tee выключены.

Общий опорный генератор прошёл аварийно-безопасную проверку: MASTER reg29 0x26, SLAVE CLKIN DETECTED, SLAVE reg29 0x26. Приём начался на 0.7525116 с раньше передачи и закончился на 3.6341200 с позже неё. Файл передачи содержит 156 835 200 комплексных отсчётов, 313 670 400 байт и 19,6044 с сигнала; процесс передачи занял 19,6408497 с. Приём содержит 192 000 000 комплексных отсчётов, 384 000 000 байт и 24,0 с сигнала; процесс приёма занял 24,0274813 с. Переполнения приёмника, опустошения передатчика, ограничение АЦП и приближение к пределу отсутствуют.

До полного декодирования по фону до передачи зафиксировано правило первого каскада Q99.9 локальных максимумов + 4 * MAD: Q99.9 0.023181289434432983, MAD 0.0025865877978503704, порог 0.033527640625834465. После начала декодирования порог не менялся. На независимом фоне после передачи максимум равен 0.030997175723314285, кандидатов выше порога нет. Исторический строгий порог IQ_PREAMBLE_THRESHOLD = 0.65, Lab045, PHY и FEC не менялись.

Независимый приёмник обработал весь 24-секундный файл. Он не использовал смещения и период кадров передатчика, ожидаемое число 68, пакеты, кодированные биты или исходный объект. Первый каскад получил 295469 локальных максимумов и 35009 кандидатов. Строгий каскад обработал 95 неперекрывающихся гипотез: PHY-заголовок PASS/FAIL 68/27, FEC attempts/PASS/FAIL 68/68/0, CRC PASS/FAIL 68/0, действительных кадров 68. Поиск завершился по концу записи и списка кандидатов, а не по достижении известного количества кадров.

ObjectAssembler принял 68 уникальных фрагментов. Дубликатов, конфликтующих дубликатов и пропусков нет. Независимый вердикт: OBJECT COMPLETE = YES. Восстановленный объект размером 16384 байта был записан до открытия опорных данных передатчика. Только после этого выполнено сравнение: все 68 кадров совпали с 68 переданными, пропущенных кадров и ложных CRC PASS нет. Ошибки до FEC составили 787 кодированных бит, после FEC 0 протокольных бит. Ошибки объекта равны 0 байт и 0 бит. SHA-256 исходного и восстановленного объектов: b5a27caaa3f03dfc468e71a857a06f18cf0259ca6ea21d3c42ccfde7168d1dfe.

Для 68 принятых кадров SNR min/median/max равен 8.302870/8.909278/9.837726 дБ, ошибки преамбулы 0/3/9 из 1280, CFO -0.214661/0.016493/0.281551 Гц. Медианный SNR по третям сеанса плавно снижался 9.29 -> 8.86 -> 8.57 дБ, но все кадры прошли FEC и CRC. Ошибки начала кадра находились в пределах от -4 до +3 отсчётов.

Фактические внешние артефакты сохранены в C:\Users\user\Documents\SDR_Rover_Lab046_raw\20260902_151819_1400MHz_full. Большие tx_stream.cs8, rx_stream.cs8, tx_object.bin и recovered_object.bin в Git не добавляются. После результата оба CLKOUT выключены, процессов hackrf_transfer нет. Независимая проверка сохранённых JSON, размеров, SHA-256, временного покрытия и счётчиков расхождений не нашла.

Lab046C: подготовка аппаратного непрерывного объекта

Добавлен experiments/lab046c_hackrf_bpsk_object.py, который не меняет протокол и только связывает готовые Lab043--Lab046B. Подготовительный режим создаёт новый криптографически случайный объект 16384 байта вне Git, разбивает его существующим Lab046 API на 68 фрагментов и последовательно записывает 68 полных Lab045 waveform в один tx_stream.cs8. Полные 313 670 400 байт не удерживаются в ОЗУ одновременно; SHA-256 объекта и потока сохраняются в tx_metadata.json.

24-секундный RX был запланирован как 192 000 000 complex cs8 samples, 384 000 000 байт. Такой capture даёт 0,75 с до передачи и около 3,65 с после потока длительностью 19,6044 с. Приёмный API получает только путь к сырому cs8 и PHY safety limits. Он читает файл через memmap окнами complex64, ищет локальные PRBS7-кандидаты без обязательного global peak/second gate, получает длину кадра из принятого PHY header и передаёт прошедшие CRC payload в ObjectAssembler. TX start, границы 68 waveform, ожидаемое число фрагментов, пакеты, coded bits и исходный объект приёмнику не передаются.

Аппаратный runner фиксирует доказанные serial и PHY-параметры, запускает RX раньше TX, содержит один TransmitGuard.register_transmit() и cleanup в finally. --preflight-only не регистрирует передачу и выполняет только inventory/common-clock gate с последующим CLKOUT OFF. RF-режим требует два отдельных CLI-ключа. После отдельной точной авторизации он был выполнен один раз; повторный запуск не выполнялся и не разрешён.

Фактическая подготовка 1 сентября 2026 года создала внешний каталог C:\Users\user\Documents\SDR_Rover_Lab046_raw\20260901_145339_bpsk_object:

  • tx_object.bin: 16384 байта, SHA-256 f7568a5d1a6dc5dd907fa1e971a171578315de740bd6788136f447e409fc6bf8;
  • tx_stream.cs8: 313670400 байт, 156835200 complex samples, 19,6044 с, SHA-256 8941eb231f1f2f99cde51f3cc5426472dbedf1a61675543e7774faec4b935f5e;
  • tx_metadata.json: LAB046C_PREPARATION_ONLY, rf_tx_count = 0.

Software gate подготовки: quick gate PASS с 482 тестами и 8 функциональными лабами; полный pytest = 482 passed. Безопасный common-clock preflight также PASS: MASTER reg29 0x26, SLAVE CLKIN DETECTED после 141,51 мс, SLAVE reg29 0x26; после проверки оба CLKOUT выключены, процессов hackrf_transfer нет, RF TX = 0 на этапе подготовки.

Lab046C: первый аппаратный результат 1080 МГц, исторический RESULT C

Выполнен ровно один непрерывный RF TX всего tx_stream.cs8; автоматического или ручного повтора не было. MASTER/TX 000000000000000097c063dc323b97d3, SLAVE/RX 000000000000000097c063dc2e5327d3, RF 1080 МГц, Fs 8 MS/s, BPSK 20 ксимв/с, TX VGA 10 дБ, RX LNA/VGA 24/24 дБ, RF amp и bias OFF. Common clock прошёл fail-closed gate: MASTER reg29 0x26, SLAVE CLKIN DETECTED, SLAVE reg29 0x26. Передача длилась 19,60632 с и полностью находилась внутри RX capture длительностью 24,00386 с. USB-журналы чистые: TX underruns 0, RX overruns 0.

Независимый приёмник без TX oracle вернул официальный RESULT C:

  • PREAMBLE CANDIDATES = 0;
  • VALID FRAMES = 0;
  • PHY HEADER PASS = 0;
  • CRC PASS = 0;
  • OBJECT COMPLETE = NO;
  • восстановленный объект не создавался.

Рабочий порог PRBS7 оставался 0.65; максимальная независимая корреляция равна 0.126562. Фоновая медиана мощности -34.467535 dBFS, кратковременный максимум -34.307678 dBFS; максимальный средний подъём на интервале 19,6 с всего 0.012049 dB. Обычный power/PSD-анализ не показывает заметной передачи.

После завершения и фиксации независимого RESULT C выполнена разрешённая read-only диагностика с TX reference. Она обнаружила следы всех 68 waveform с точным периодом 2 306 400 samples. Нормированная корреляция в выровненных позициях: min 0.065784, median 0.089991, max 0.122379; контроль со смещением: median 0.004275, max 0.010075. Медианное преимущество 26,46 дБ, все 68 aligned значений выше максимума контроля.

Эта post-run диагностика использует знание TX только после официального решения и не превращает опыт в PASS: OBJECT TRANSPORT PASS и LAB046C COMPLETE SUCCESS не заявляются. TX-файл, его спектр около +50 кГц, первый/средний/последний self-decode и runner проверены; дефекта формирования или управления, объясняющего результат, не найдено. Сохранённый официальный исход остаётся LAB046C HARDWARE RESULT = C, RF TX COUNT = 1, OBJECT COMPLETE = NO.

Lab046C: автономная слабосигнальная обработка исходного RX capture

После аппаратного RESULT C реализован отдельный offline-модуль experiments/lab046c_offline_recovery.py. Lab045 и строгий многокадровый порог Lab046B IQ_PREAMBLE_THRESHOLD = 0.65 не изменялись. Новый режим не содержит HackRF-команд и читает только ранее сохранённый rx_stream.cs8 через np.memmap.

Первый каскад был зафиксирован до полного прогона по калибровочному фону 0.0--0.5 s: порог равен 99,9-му процентилю локальных PRBS7-максимумов. Получено threshold = 0.025011632591485977, correlation median 0.00838765874505043, MAD 0.003039387986063957, 5193 локальных максимума, 6 кандидатов, 12.0 candidates/s. После фиксации правило проверено на отдельном контрольном фоне 21.0--24.0 s и больше не менялось: 35501 локальный максимум, 301 кандидат, 100.333333 candidates/s. Временные границы TX использовались только для выбора участков, где передачи заведомо не было, и не передавались поиску кадров.

Выполнен ровно один независимый полный offline-прогон 192 000 000 samples без tx_stream.cs8, tx_object.bin, TX offsets, ожидаемого числа 68, fragment order, packets или coded bits. Первый каскад насчитал 291446 локальных максимумов и 3025 кандидатов выше порога. Все 3025 прошли refinement, timing и оценку carrier/CFO/phase, но все 3025 были отвергнуты как PHY_HEADER_MAGIC_MISMATCH. PHY HEADER PASS = 0, FEC ATTEMPTS = 0, CRC PASS = 0, VALID FRAMES = 0, VALID FRAGMENTS = 0, OBJECT COMPLETE = NO. recovered_object.bin не создавался.

Только после завершённого independent verdict была открыта TX reference. Диагностика показала, что каждый из 68 настоящих стартов имел first-stage кандидат в пределах +-40 samples; корреляция на этих кандидатах min/median/max 0.059379209/0.088661019/0.126557365, то есть все они были выше зафиксированного порога. FALSE CRC PASS = 0, но совпавших independently valid frames также 0, пропущены все 68 TX frames. На известных только post-verdict позициях локальная физическая экстракция прошла для 68/68, однако ошибки min/median/max составили 341/430/554 из 1280 бит преамбулы и 30/54/81 из 144 бит PHY header. Оценочный SNR min/median/max -19.282130/-12.829480/-9.195846 dB; все заголовки дали MAGIC_MISMATCH. Следовательно, новый first-stage detector обнаружил слабые следы, но качество демодуляции оказалось недостаточным даже для защищённого PHY header.

Производительность: calibration 0.269617 s, control 1.031204 s, coarse search 7.721316 s, strict candidate processing 836.340048 s, в среднем 0.276476 s/candidate, полный прогон 845.427589 s. Capture длительностью 24 s обработан за 35.226150 его длительности. RX остаётся memmap; самый большой coarse complex64 block содержит 16 000 000 samples (128 000 000 байт), консервативная оценка временных массивов SciPy 1 545 705 764 байта. Максимальное strict local window: 594400 samples, 4 755 200 байт. Полный 192M-sample complex64 массив не создавался.

Артефакты результата находятся в data/processed/lab046c_offline_recovery/: independent_result.json, post_verdict_diagnostics.json, tx_reference_stage_diagnostics.json. Правильная итоговая формулировка: ORIGINAL LAB046C HARDWARE RESULT = C; OFFLINE REPROCESSING OF ORIGINAL RX CAPTURE = FAIL; OBJECT RECOVERED = NO; ORIGINAL HARDWARE RESULT CHANGED = NO. Второй независимый прогон с новым порогом не выполнялся.

Финальный software gate после результата: python tools/quick_gate.py PASS, 506 проверок и 9 функциональных лабораторий; python -m pytest tests/ -q = 506 passed. Единственное предупреждение связано с недоступным .pytest_cache и не является отказом. git diff --check чист, кроме информационных LF/CRLF notices.

Назначение

Lab046 переходит от одиночной 256-байтной полезной нагрузки к передаче произвольного бинарного объекта последовательностью независимо защищённых радиокадров. Два уровня строго разделены и не смешиваются:

  • Уровень 1 (внутри одного кадра) — Lab045. Свёрточный код R=1/2 K=7, перемежитель, мягкий декодер Витерби, CRC пакета. Исправляет неизвестные битовые ошибки внутри принятого кадра.
  • Уровень 2 (большой объект) — существующий protocol/image_fragments. Фрагментация, нумерация фрагментов, идентификатор объекта, сборка. Собирает объект из нескольких независимо принятых кадров.

Новый код в experiments/lab046_bpsk_object_transport.py минимален: он связывает два готовых уровня и добавляет fail-closed сборщик объекта. Отдельный модуль формата фрагмента НЕ создавался: переиспользован 12-байтовый заголовок image_fragments (>IHHI: object_id u32, fragment_index u16, fragment_count u16, original_size u32).

Аудит: что переиспользовано, чего не хватало, минимальный новый слой

  1. Переиспользовано. protocol/image_fragments уже даёт нужную семантику фрагмента: идентификатор объекта, индекс, число фрагментов, общий размер, а также fail-closed reassemble_image (несовпадение id/count/size, конфликтующие дубликаты, недостающие фрагменты, финальная проверка длины). Слой Lab045 даёт кадр целиком: build_transmission_plan, analyze_post_preamble_soft, simulate_soft_channel, диагностику pre/post-FEC.
  2. Чего не хватало. Не было (а) упаковки фрагмента в 256-байтную нагрузку Lab045 с обрезкой добивки на приёме по длине из заголовка и (б) сборщика, который принимает по одному независимо декодированному кадру и никогда не бросает на плохом входе, возвращая object_complete = False вместо фиктивного объекта.
  3. Минимальный новый слой. recover_fragment_from_payload (fail-closed декод + обрезка добивки) и класс ObjectAssembler (offer/missing/complete/ recovered). Erasure-код 12+3 GF(256) (protocol/packet_erasure_fec) НЕ использован и НЕ изменён — он остаётся будущим расширением для потери целых фрагментов, а не для битовых ошибок (это работа свёрточного кода).

После этого Lab046A закрыла оставшийся пробел: до неё Lab046 передавала каждый кадр отдельным вызовом и выбирала его из TX-плана по известному индексу. Функции поиска нескольких кадров в одном потоке не было. Теперь scan_continuous_soft_stream получает только один массив soft-символов, самостоятельно ищет PRBS7 после монотонного cursor, проверяет защищённый PHY header, вычисляет длину coded body из принятого packet length, выполняет готовые FEC/CRC Lab045 и передаёт принятый fragment в ObjectAssembler.

TX offsets, список длин кадров, ожидаемое число 68, порядок/индексы TX, исходный объект, TX packets и coded bits scanner-у не передаются. TX-side frame_starts остаётся только в генераторе потока и используется тестом для сравнения уже после независимого receive.

Граница доказательства Lab046A

Lab046A работает на уровне одного непрерывного нормализованного soft BPSK stream (float32). Это разрешённый интерфейс после уже проверенного в Lab045 sample-level front-end: PRBS7 detection, matched filtering, timing, CFO и phase/polarity recovery. Внутри нового длинного теста эти sample-level операции заново не моделируются для каждого кадра. Поэтому результат доказывает самостоятельный поиск границ, header-driven длину, FEC/CRC и сборку объекта в непрерывном soft stream, но не является аппаратной или непрерывной IQ-проверкой.

Каждый фрагмент содержит полный активный Lab045 PHY frame: 1280 бит PRBS7, 144 бита защищённого header и 4300 coded bits. Точная полная sample-level форма с guard и хвостом ФРК учитывается как 2 306 400 отсчётов на кадр, однако все 156 835 200 IQ-отсчётов одновременно в памяти не создаются. Это намеренное ограничение памяти, а не использование известных границ приёмником.

Scanner fail-closed:

  • cursor всегда растёт, после принятого кадра переносится за вычисленный конец;
  • все PRBS7 candidates выше порога рассматриваются в порядке stream; после принятого кадра cursor переносится за вычисленный конец и не даёт декодировать этот кадр повторно;
  • плохой header, усечённое body, ошибка FEC/CRC отвергают candidate, но поиск следующего продолжается;
  • MAX_FRAMES и MAX_SEARCH_ITERATIONS дают явную причину остановки, а не частичный объект, объявленный полным;
  • scan продолжается до конца capture и не останавливается на числе 68.

Сценарии continuous stream

Сценарий Получено
Идеальный, кадры подряд candidates 68, valid headers 68, FEC PASS 68, CRC PASS 68/68, fragments 68, complete=True, SHA совпал, 0 байт-ошибок
Фон до/между/после 37/37/43 soft-символа, valid frames 68, complete=True, SHA совпал
Непрерывный AWGN, sigma=0.40, seed=46001 frames 68, pre-FEC 1791, post-FEC 0, CRC 68/0, complete=True, SHA совпал
Valid-looking preamble + bad protected header candidates 71, rejected 3, последующие valid frames 68, complete=True, SHA совпал

Фон между кадрами добавлен только после кадров 7, 23 и 51: 11, 19 и 7 soft-символов, всего 37. В baseline дополнительного inter-frame gap нет. Отдельный тест с валидным header для 12-байтного packet и повреждённым body проверяет, что scanner берёт длину именно из принятого header, отвергает этот candidate и затем находит настоящий кадр.

Verifier нашёл опасный вариант прежнего глобального non-maximum suppression: более сильный ложный PRBS7 peak мог удалить более поздний настоящий старт кадра. Поэтому suppression убран из списка кандидатов, а добавленный регрессионный тест проверяет, что ложный peak не скрывает настоящий кадр.

Lab046B: непрерывный сырой IQ-поток

Lab046B закрывает только программный sample-level пробел Lab046A. Новый experiments/lab046b_continuous_iq.py формирует пять полных Lab045 waveform с PRBS7, защищённым PHY header, interleaved convolutional body, BPSK, RRC и guards, размещает их в одном complex64 массиве и передаёт scanner-у только этот массив. TX start offsets, длины TX waveform, номера кадров, payload, packet и coded bits в scan_continuous_iq не передаются.

Переиспользованы существующие функции Lab043-Lab045: BPSK/RRC waveform, PRBS7 reference, normalized sliding correlation, локальное уточнение sample start, matched filter, preamble-only timing, non-data-aided CFO и комплексный gain, normalized.real, защищённый PHY header, deinterleave, soft Viterbi и protocol CRC. Новый слой только перечисляет локальные IQ-кандидаты, вызывает этот тракт на окне кандидата и монотонно продолжает поиск.

Глобальное требование «главный пик значительно выше второго» не используется. В идеальном пятикадровом опыте peak/second для настоящих кадров лежит около 0 dB, поскольку вторым пиком является другой настоящий кадр; тем не менее все пять кадров приняты. Все локальные максимумы выше порога сохраняются, а после CRC PASS cursor переносится за вычисленный по принятому header конец кадра. После плохого кандидата cursor также строго растёт, но следующий кандидат не скрывается. Выход ограничен MAX_FRAMES и MAX_SEARCH_ITERATIONS.

Фактические сценарии Lab046B

Сценарий Получено
Идеальный continuous IQ PRBS7 candidates 32, processed 5, accepted 5, rejected 0, PHY/FEC/CRC 5/5/5, CRC FAIL 0, sample offset error 0 для всех
CFO + phase + amplitude внесено 0.2 Hz / 0.7 rad / 0.65; медианная оценка 0.200003768 Hz / 0.699634288 rad; CRC 5/0
Комплексный AWGN sigma=0.17, seed=46002; accepted 5, pre-FEC 0, post-FEC 0, CRC 5/0
PRBS7 + неверный PHY header перед настоящим кадром candidates 11, processed 8, rejected 7, следующий настоящий кадр найден, CRC 1/0

Начало первого waveform: sample 137, то есть не sample 0 и не граница сетки 400 samples/symbol. Дополнительные gaps: 211/353/587/809 samples. Найденные фазы символьной выборки: 137/348/301/88/97 samples modulo 400. Точные TX offsets используются только постфактум после CRC PASS; ошибки обнаруженного start во всех пяти случаях равны нулю.

Шумовая точка зафиксирована честно. Первый разумный однокадровый запуск при sigma=0.12 дал CRC PASS, но 0 pre-FEC ошибок. Выполнена одна разрешённая обоснованная корректировка до sigma=0.17; она также дала 0 pre-FEC ошибок и CRC PASS. Дальнейший подбор не выполнялся. Поэтому этот сценарий доказывает устойчивый многокадровый IQ receive с комплексным детерминированным шумом, но не является новым доказательством реального исправления ошибок FEC; такое доказательство уже относится к аппаратной Lab045.

Основной массив содержит 11 534 330 complex64 отсчётов и занимает 92 274 640 байт (88.0 MiB). Пять waveform занимают 11 532 000 samples, остальное — leading/gaps/trailing. Полные CFO/phase-corrected копии не создаются: первичный поиск работает с decimated stream, а matched filtering и soft extraction выполняются на локальном окне одного кандидата. Это размер именно основного IQ-массива, а не peak RSS процесса: RRC/FFT, локальный matched stream и временные массивы SciPy требуют дополнительную рабочую память.

Память continuous stream

Основной 68-кадровый soft stream содержит 389 232 значения float32 и занимает 1 556 928 байт (1,485 MiB). Массив correlation scores занимает 1 551 812 байт, накопленная энергия float64 — 3 113 864 байта. FFT и denominator создают временное рабочее пространство, размер которого зависит от SciPy, но стратегия остается в пределах десятков MiB, а не нескольких GiB. Для сравнения, один материализованный continuous complex64 IQ stream из 156 835 200 отсчётов занял бы около 1,17 GiB ещё до временных копий.

Детерминированный объект

  • Размер 16384 байта (16 KiB). Это не JPEG, а воспроизводимый псевдослучайный поток numpy.random.default_rng(seed).integers(0,256,...).
  • Выбор размера. 16 KiB — компромисс: достаточно велик, чтобы кадров было много (68) и учёт фрагментации/сборки был содержательным, но достаточно мал, чтобы программный прогон 68 кадров с мягким Витерби считался секунды. Ровный размер (не кратный 244) специально оставляет короткий последний фрагмент.
  • Seed объекта по умолчанию 46, идентификатор объекта 0x4C303436 («L046»).
  • SHA-256 объекта по умолчанию: 748013b5f670c82bd52650be5454555355dbd8528538753df4558f1c3408d515.
  • Объект не сохраняется в Git (крупный генерируемый артефакт); при необходимости он детерминированно восстанавливается по seed.

Раскладка нагрузки пакета

Полезная нагрузка Lab045 остаётся 256 байт (доказанная рабочая точка):

[12 байт метаданных фрагмента][244 байта данных фрагмента]

FRAGMENT_DATA_SIZE = 256 − 12 = 244. Последний фрагмент короче; его нагрузка добивается нулями до 256 байт перед Lab045 и обрезается на приёме по длине, вычисленной только из полей заголовка (expected_fragment_data_length). Для 16384 байт последний, 68-й фрагмент несёт 36 байт данных (16384 − 67·244).

Fail-closed сборка (без оракула)

Приёмник независим: переданный объект и TX-биты не используются ни для приёма пакета, ни для FEC, ни для восстановления метаданных, ни для решения о завершении объекта. Сравнение с оригиналом делается только в диагностике, после независимого CRC-решения и независимой сборки.

ObjectAssembler.offer(fragment) возвращает accepted/duplicate/conflicting/ rejected и никогда не бросает. Отвергаются: чужой object_id, несогласованные count/original_size, невозможный индекс, конфликтующий дубликат (тот же индекс, другие данные), malformed/усечённые метаданные. Идентичный дубликат игнорируется. Пока не собраны все индексы, object_complete() = False, а recovered_object() = None — фиктивный объект не создаётся. Поле original_size проверяется на обе границы ((count−1)·244 < original_size ≤ count·244), чтобы абсурдное значение не управляло размером аллокаций.

Программные сценарии (seed фиксирован)

Сценарий Условие Ожидалось Получено
A. Идеальный канал шума нет объект точен complete=True, sha совпал, 0 байт-ошибок, CRC 68/0
B. Распределённые битовые ошибки гауссов шум sigma=0.40 pre-FEC>0, post-FEC=0, все CRC PASS, объект точен pre-FEC 1805, post-FEC 0, CRC 68/0, complete=True, sha совпал
C. Приём не по порядку обратный порядок кадров объект точен complete=True, sha совпал
D. Дубликаты фрагментов повтор кадров 0,2,2 объект точен, дубликаты учтены duplicates≥1, complete=True, sha совпал
E. Один потерянный фрагмент кадр 1 не дошёл OBJECT COMPLETE = NO complete=False, 1 в missing, recovered SHA = None
F. Повреждённый пакет инверсия coded-потока кадра 0 CRC FAIL, объекта нет crc_fail≥1, complete=False, recovered SHA = None

Сценарий B — честная точка: уровень sigma выбран так, чтобы мягкий Витерби исправлял все 68 кадров (при sigma=0.45+ отдельный кадр не декодируется, и без erasure-FEC объект оказывается неполным — это по сути сценарий E, а не подгон). Числа не подкручивались ради прохождения: до FEC присутствуют 1805 реальных ошибок кодированных бит, после мягкого Витерби — ноль, объект побитово точен.

Метрики (объект 16384 байта, идеальный канал)

Величина Значение
Размер объекта 16384 байта
SHA-256 объекта 748013b5…3408d515
Размер метаданных фрагмента 12 байт/пакет
Размер данных фрагмента 244 байта (последний 36)
Число фрагментов 68
Размер протокольного пакета 268 байт (256 нагрузка + 8 header + 4 CRC)
Число BPSK-кадров 68
Всего информационных BPSK-символов 389 232 (5724 × 68)
Всего отсчётов формы сигнала 156 835 200 (2 306 400 × 68)
Baseline: дополнительный inter-frame gap 0 отсчётов
Baseline: полная длительность 19,604400 с
Baseline: полезный goodput 835,7 байт/с = 6,685846 кбит/с
Фоновый тест: leading/inter-frame/trailing 14 800 / 14 800 / 17 200 отсчётов
Фоновый тест: полная длина и длительность 156 882 000 отсчётов, 19,610250 с
Фоновый тест: фактический goodput 6,683852 кбит/с
Полезных бит на символ ≈ 0,337

Учёт кадра совпадает с Lab045: 2144 информационных бита, 6 tail-бит, 4300 кодированных бит (R=1/2), перемежитель 4300, PHY header 48→144 бит (×3), преамбула PRBS7 1280 бит, итого 5724 информационных символа кадра. Форма сигнала добавляет 42 символо-времени (32 нулевых guard + 10 хвост ФРК, shape_frame_like_lab042, режим full): 5766 символо-времён = 2 306 400 отсчётов = 288,3 мс на кадр при 8 MS/s.

Разбор накладных расходов

  • Фрагментация (уровень 2): 12 байт метаданных на пакет × 68 = 816 байт.
  • Протокольный header+CRC (уровень 1): 12 байт на пакет × 68 = 816 байт.
  • Свёрточный код R=1/2: удваивает число кодированных бит (2144→4300 с tail).
  • PHY преамбула и защищённый header: 1280 + 144 = 1424 бита на кадр.
  • Guard/фильтр: 42 символо-времени на кадр (32 guard + 10 хвост ФРК).
  • Добивка последнего фрагмента: 208 нулевых байт (только 68-й кадр).

Goodput 6,69 кбит/с — это честная baseline при 20 ксимв/с с полной преамбулой и guard на каждый кадр; в реальной серии кадров преамбула на кадр доминирует в накладных расходах, и это направление для будущей оптимизации, а не дефект.

Фактические программные результаты

  • Новый набор tests/test_lab046_bpsk_object_transport.py: все проверки проходят (фрагмент туда-обратно, короткий последний фрагмент, чужой object_id, невозможный индекс, несогласованный счётчик, усечённые и абсурдные метаданные без крупной аллокации, полный тракт PHY для сценариев A–F, отсутствие оракула, последовательный continuous scan, rejected candidate, MAX_FRAMES/MAX_SEARCH_ITERATIONS, регрессия против удаления настоящего кадра ложным PRBS7 peak, детерминизм и SHA объекта).
  • Полный тракт объект → фрагменты → кадр Lab045 → канал → приём → сборка проверен для всех шести сценариев значениями выше.

Software gate Lab046B на момент его фиксации

  • python tools/quick_gate.py: PASS; 468 тестов ядра, 7 функциональных проверок лабораторий выполнено, 13 штатно пропущено.
  • python -m pytest tests/ -q: 468 passed, 0 failed, 0 errors, 0 skipped. Единственное предупреждение связано с отсутствием прав на создание .pytest_cache; на выполнение и результат тестов оно не повлияло.
  • git diff --check: чист.

Актуальный общий gate после добавления Lab046C указан в начале документа.

Ограничения и следующий шаг

Для Lab046A/B RF TX = 0. Первый полный аппаратный Lab046C на 1080 МГц сохранён как исторический RESULT C. Новый полный аппаратный Lab046C на 1400 МГц завершён как RESULT A: 68/68 кадров, объект 16384 байта восстановлен без ошибок. Erasure-код 12+3 GF(256) не задействован, Lab045, PHY и FEC не менялись. Следующий этап определяется отдельной постановкой. Новый RF-сеанс, JPEG, видео, QPSK и переход к другой FEC автоматически не разрешены.