Compare commits
6 Commits
25cc89f0c7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af7c05f2da | ||
|
|
b27a5cf00e | ||
|
|
dacf04f2d0 | ||
|
|
cff226e0bf | ||
|
|
4437917465 | ||
|
|
f8ae6c6bfc |
340
PROJECT_LOG.md
@@ -2661,3 +2661,343 @@ READY_BEHIND_EXPLICIT_MODE до отдельного контролируемо
|
||||
500K-пакетами. Оборудование не опрашивалось, RF TX=0. Lab061 фиксируется этим
|
||||
отдельным коммитом; push выполняется пользователем отдельно. Следующий этап:
|
||||
STOP; Lab062 автоматически не начинать.
|
||||
|
||||
# Запись 056
|
||||
|
||||
## Дата
|
||||
|
||||
14 сентября 2026 года
|
||||
|
||||
## Тема
|
||||
|
||||
Lab062 — подготовка полного пакета 500K и аппаратный RX-only preflight
|
||||
|
||||
## Выполнено
|
||||
|
||||
От HEAD 25cc89f0c734f77141632d19dbcdb6b2f066d60b подготовлен один waveform
|
||||
из16 полных PHY-пакетов по256 байт payload:1476096 отсчётов,0,184512с,
|
||||
SHA-256 e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5.
|
||||
Строгий программный roundtrip legacy/T2 дал16/16 HEADER/FEC/CRC и точных
|
||||
payload в исходном порядке. Quick gate:962 теста ядра,35 функциональных
|
||||
проверок,13 пропусков; pytest:962 passed,0 failed.
|
||||
|
||||
Фактическая схема:1400МГц,500ксимв/с, Fs8Мвыб/с, расстояние примерно1,2м,
|
||||
LOS в помещении; TX возле окна, RX на столе, одинаковая поляризация. Антенны,
|
||||
кабели и USB-схема неподвижны. Усиления сохранены: TX VGA10дБ, RX LNA/VGA
|
||||
24/24дБ, RF amplifier и Bias-Tee OFF. Общая опора:10МГц TCXO → MASTER →
|
||||
MASTER CLKOUT → SLAVE CLKIN, длинный кабель сохранён.
|
||||
|
||||
Один аппаратный RX-only preflight подтвердил MASTER/SLAVE reg29=0x26 и SLAVE
|
||||
CLKIN=DETECTED на втором опросе через133,5795мс. Записано800000 отсчётов фона:
|
||||
полный RMS2,314058, AC RMS0,985483 кода, max|I|/|Q|=6/8, clipping0/800000,
|
||||
RX overruns0, неожиданно сильного сигнала нет. Отсутствие аналоговой компрессии
|
||||
этим не доказано. CLKOUT выключены без ошибок, процессы до/после CLEAR.
|
||||
|
||||
RF TX=0, TX-команд не было, усиления не менялись, preflight не повторялся.
|
||||
Lab062 не коммитировалась, push не выполнялся. Следующий этап: STOP и ожидание
|
||||
отдельного явного разрешения на единственный Lab062 TX; Lab063 не начинать.
|
||||
|
||||
# Запись 057
|
||||
|
||||
## Дата
|
||||
|
||||
14 сентября 2026 года
|
||||
|
||||
## Тема
|
||||
|
||||
Lab062 — единственный RF-сеанс полного пакета 500K и сравнение legacy/T2
|
||||
|
||||
## Выполнено
|
||||
|
||||
После отдельного разрешения пользователя выполнен один TX-сеанс подготовленного потока из 16
|
||||
полных PHY-пакетов. Перед TX повторная проверка подтвердила MASTER/SLAVE reg29=0x26 и SLAVE
|
||||
CLKIN=DETECTED. Автоматических и ручных повторов не было. RX сохранил 24 000 000 комплексных
|
||||
отсчётов за 3,000 с, 48 000 000 байт, SHA-256
|
||||
ce0bd603e4ca356af67d504ee11c9e9f38171f31c7427fbe61216a28126b2b32; overruns0 и clipping0.
|
||||
|
||||
В промежуточной строке TX-журнала зарегистрирован один underrun длиной до16672 байт, хотя
|
||||
итоговая строка утилиты сообщила0. Ненулевое наблюдение сохранено, сеанс признан
|
||||
`INVALID_TX_UNDERRUN`, повтор запрещён и не выполнялся. Cleanup прошёл без ошибок, SDR-процессы
|
||||
после сеанса CLEAR, RF TX=0.
|
||||
|
||||
Одна и та же RX-запись независимо обработана строгими режимами legacy и T2 без fallback. Оба
|
||||
получили144 строгих кандидата,3 прохода PHY header,3 запуска и прохода FEC,3 запуска CRC,0 CRC
|
||||
PASS и0/16 пакетов. T2 gain равен0. Диагностические границы всех16 пакетов найдены только после
|
||||
строгого вердикта и не передавались приёмнику. По сопоставимой методике Lab058 средняя доля
|
||||
ошибочных символов равна0,099287, EVM1,220401, matched/residual−1,730051дБ, медианная
|
||||
неравномерность полосы p95–p05 равна6,660341дБ. Эти значения хуже Lab056 B, но их нельзя
|
||||
отделить от TX underrun.
|
||||
|
||||
Lab062 не коммитировалась, push не выполнялся, Lab063 не создана. Следующий этап: RF TX=0 и
|
||||
отдельно согласовать только офлайн-локализацию underrun относительно границ пакетов.
|
||||
|
||||
# Запись 058
|
||||
|
||||
## Дата
|
||||
|
||||
14 сентября 2026 года
|
||||
|
||||
## Тема
|
||||
|
||||
Lab063: офлайн-локализация TX underrun и глобального ухудшения Lab062
|
||||
|
||||
## Выполнено
|
||||
|
||||
Lab062 зафиксирована отдельным коммитом
|
||||
f8ae6c6bfceaa21b6c08dcf55d2bff996dd0dae5 с сообщением
|
||||
`Lab062: record invalidated 500 ksymbol/s RF packet trial`; push не выполнялся. Статус
|
||||
`RF_SESSION_INVALID_TX_UNDERRUN`, результаты legacy/T2 0/16 и запрет повтора сохранены.
|
||||
|
||||
По сохранённым TX/RX без аппаратных команд установлено, что 16 waveform по 92 256 отсчётов
|
||||
записаны вплотную: 92 256 × 16 = 1 476 096. Дополнительная межпакетная пауза равна нулю.
|
||||
`prepare()` не передаёт параметр паузы и не пишет нули между packet waveform, поэтому отсутствие
|
||||
обещанных 5 мс признано ошибкой реализации подготовки Lab062. Внутренние защитные участки и
|
||||
хвосты RRC каждого пакета сохранены.
|
||||
|
||||
Строка `longest 16672 bytes` пересчитана в 8 336 комплексных отсчётов, 1,042 мс, 521 символ и
|
||||
9,035727% одного packet waveform. Это масштаб сообщения, а не доказанное число потерянных или
|
||||
повторённых RF-отсчётов. Локальное сопоставление 512-символьными окнами с шагом 256 символов
|
||||
дало 221 надёжное окно из 359. Задержка в них равна 0…1 отсчёту, скачков нет; место underrun по
|
||||
RX не локализовано. В 22 дополнительных внутренних окнах пакетов3,4,12,13 корреляция ниже0,15,
|
||||
поэтому задержка там не установлена. Края, повторяющаяся PRBS-преамбула и низкокорреляционные
|
||||
окна исключены из delay-verdict, но сохранены в CSV. Корреляционные копии преамбулы имеют период
|
||||
2 032 отсчёта.
|
||||
|
||||
Все 16 пакетов хуже Lab056 B по SER, EVM и matched/residual. SER составляет
|
||||
7,580920…16,013629%, EVM 1,063657…1,663418, matched/residual от −4,420028 до
|
||||
−0,536031дБ. Пакеты 3,4,12,13 выделяются всплесками AC-мощности и остатка при стабильной
|
||||
медиане локального |h| 0,004488…0,004805. Пакет14 не имеет общего с ними физического признака.
|
||||
Результат классифицирован как `GLOBAL_RF_DEGRADATION`; один локальный underrun недостаточен для
|
||||
объяснения качества всего сеанса.
|
||||
|
||||
AC RMS по пакетам равен0,760816…2,032438 кода, max|I|/|Q| лежат в диапазонах3…6/4…7,
|
||||
DC стабилен. Сильная аналоговая перегрузка по цифровым уровням менее вероятна, но без прибора не
|
||||
исключена. Локального исходного файла или документации, объясняющей расхождение промежуточного и
|
||||
итогового счётчиков `hackrf_transfer`, не найдено; `COUNTER SEMANTICS=NOT_ESTABLISHED`.
|
||||
|
||||
Созданы постановка, отчёт, JSON, два CSV, восемь графиков, анализатор и тесты. Lab063 выполнена
|
||||
полностью офлайн, RF TX=0, оборудование не опрашивалось. Lab063 не коммитировалась, push не
|
||||
выполнялся. Следующий этап: STOP и отдельно согласовать исследование уровня RX, усилений,
|
||||
геометрии и возможной аналоговой компрессии или слабого полезного сигнала; Lab064 автоматически
|
||||
не создавать.
|
||||
|
||||
# Запись 059
|
||||
|
||||
## Дата
|
||||
|
||||
14 сентября 2026 года
|
||||
|
||||
## Тема
|
||||
|
||||
Lab064: подготовка измерения уровня и линейности, RX-only preflight
|
||||
|
||||
## Выполнено
|
||||
|
||||
После фиксации Lab063 коммитом 4437917465274257ff0ca5d3c269029da45de3ec
|
||||
подготовлен один измерительный PRBS7/BPSK/RRC waveform без полного PHY-пакета.
|
||||
Параметры: 1400 МГц, 20000 символов/с, Fs 8000000 комплексных отсчётов/с,
|
||||
цифровая несущая +50000 Гц, TX VGA 10 дБ, RX LNA/VGA 24/24 дБ, RF amplifier и
|
||||
Bias-Tee выключены. Геометрия сохранена: примерно 1,2 м, LOS в помещении, TX
|
||||
возле окна, RX на столе, одинаковая поляризация.
|
||||
|
||||
TX-файл содержит 100 мс начальных нулей, четыре одинаковых 100-мс участка с
|
||||
цифровыми уровнями −18/−12/−6/0 дБ, три нулевых промежутка по 50 мс и 100 мс
|
||||
конечных нулей. Полная длина равна 6000000 отсчётов и 0,75 с. RMS участков:
|
||||
5,556137/11,056978/22,056892/44,000457 кода; фактические шаги мощности:
|
||||
+5,977270/+5,998157/+5,998257 дБ. Максимумы |I|/|Q| не превышают 69/69,
|
||||
цифрового clipping нет. SHA-256 TX:
|
||||
fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9.
|
||||
|
||||
Подготовлены RX-first runner и офлайн-анализатор согласованной составляющей,
|
||||
остатка, комплексного h и линейности. Полные stdout/stderr и все промежуточные
|
||||
и финальные счётчики потерь сохраняются. Независимая read-only проверка нашла
|
||||
и помогла закрыть fail-closed ошибку: промежуточный underrun с финальным нулём
|
||||
теперь всегда даёт `INVALID_CAPTURE`, диагностические метрики не превращаются
|
||||
в A/B/C. Целевые тесты:13 passed. Quick gate:984 теста ядра,37 функциональных
|
||||
проверок,13 пропусков; pytest:984 passed,0 failed; diff-check PASS.
|
||||
|
||||
Один разрешённый RX-only preflight подтвердил MASTER/SLAVE reg29=0x26 и SLAVE
|
||||
CLKIN=DETECTED при первом опросе через66,793мс. Записано800000 отсчётов фона:
|
||||
полный RMS2,193193, AC RMS0,832038 кода, средние I/Q 0,933738/1,801650,
|
||||
max|I|/|Q|=4/5, clipping0, overruns0, неожиданно сильного сигнала нет. SHA-256
|
||||
RX-only записи:02d87adcbb621027290a77d5f8fd3190c299d13e89f887cd294d4af352a6ef52.
|
||||
CLKOUT выключены без ошибок, процессы до и после CLEAR.
|
||||
|
||||
Вызовов TX не было, `RF TX=0`. Lab064 не коммитируется до аппаратного
|
||||
результата, push не выполняется. Следующий этап: STOP и ожидание отдельного
|
||||
явного разрешения на единственный Lab064 TX; Lab065 не начинать.
|
||||
|
||||
После отдельного прямого разрешения пользователя выполнен ровно один RF-сеанс
|
||||
подготовленного Lab064 waveform. Перед TX MASTER/SLAVE reg29 равнялись0x26,
|
||||
SLAVE CLKIN был обнаружен при втором опросе через139,042мс. RX стартовал раньше
|
||||
TX. Автоматических и ручных повторов не было. Все две строки TX underrun и все
|
||||
четыре строки RX overrun равны нулю. RX clipping отсутствует. Записано16000000
|
||||
комплексных отсчётов за2,000000с, SHA-256
|
||||
02015891948aaee52ff81e60fdfeb391bb9b76cd9192b75972b8fedbc8690d86.
|
||||
После отключения CLKOUT процессы CLEAR, ошибок очистки нет, RF TX=0.
|
||||
|
||||
Пять окон цифрового нуля дали AC RMS0,718264…0,728217 кода; разброс фоновой
|
||||
AC-мощности равен0,119530дБ, фон стабилен. Согласованная мощность L0/L1/L2/L3
|
||||
равна0,000750751/0,002961235/0,011333436/0,045331864код². Ошибки трёх ступеней
|
||||
относительно цифрового TX равны−0,017501/−0,169270/+0,022162дБ. Общий рост
|
||||
L3/L0 равен17,809077дБ при цифровых17,973685дБ; ошибка−0,164609дБ. |h|
|
||||
составляет0,004882853/0,004872939/0,004778930/0,004791153. Верхняя ступень не
|
||||
показывает компрессии.
|
||||
|
||||
Matched/residual для L0…L3 равен−32,086493/−25,577463/−16,691669/−12,133336дБ.
|
||||
По заранее заданным критериям результат `LINEAR_BUT_WEAK`: тракт линейно
|
||||
следует цифровому уровню, но полезная согласованная составляющая даже на L3
|
||||
слабее остатка. Абсолютная RF-мощность и разделение вклада TX/эфира/RX без
|
||||
измерительного прибора не установлены.
|
||||
|
||||
Quick gate после сеанса прошёл:986 тестов ядра,37 функциональных проверок,13
|
||||
пропусков. Полный pytest:986 passed,0 failed. Lab064 не коммитировалась, push
|
||||
не выполнялся. Следующий отдельный шаг рекомендуется только после согласования:
|
||||
повтор той же лестницы при TX VGA20дБ и остальных неизменных условиях. Lab065
|
||||
не создана.
|
||||
|
||||
Независимая read-only проверка повторно вычислила SHA, положение сегментов,
|
||||
пять окон фона, matched/residual, комплексные h, ступени мощности и амплитуды,
|
||||
полное сравнение L3/L0 и двенадцать спектральных значений непосредственно из
|
||||
CS8. Расхождений нет, вердикт `чисто`. Оборудование при проверке не опрашивалось,
|
||||
RF TX остаётся равным нулю.
|
||||
|
||||
# Запись 060
|
||||
|
||||
## Дата
|
||||
|
||||
15 сентября 2026 года
|
||||
|
||||
## Тема
|
||||
|
||||
Lab064 зафиксирована; подготовка Lab065 при TX VGA 20 дБ и RX-only preflight
|
||||
|
||||
## Выполнено
|
||||
|
||||
После проверки уже подготовленного индекса по прямому разрешению пользователя
|
||||
создан коммит `cff226e0bf9e36a627829ea11a4ca3292ae20953` с сообщением
|
||||
`Lab064: measure OTA level linearity at TX VGA 10 dB`. В коммит вошли только
|
||||
12 файлов и фрагментов Lab064; изменения Lab054 и Lab058 и сырые IQ не вошли.
|
||||
Push не выполнялся.
|
||||
|
||||
Пользователь подтвердил неизменность геометрии, антенн, поляризации, USB,
|
||||
clock-кабеля и RF-кабелей. Для Lab065 TX VGA изменён с 10 до 20 дБ. Частота
|
||||
1400000000 Гц, RX LNA/VGA 24/24 дБ, RF amplifier и Bias-Tee OFF, PRBS7/BPSK/RRC,
|
||||
20000 символов/с, Fs 8000000 отсчётов/с, несущая +50000 Гц и лестница
|
||||
−18/−12/−6/0 дБ сохранены. TX-файл побайтово совпадает с Lab064: 6000000
|
||||
отсчётов, 12000000 байт, 0,75 с, SHA-256
|
||||
fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9.
|
||||
|
||||
Подготовлены RX-first runner без повторов, fail-closed проверка максимальных
|
||||
счётчиков и анализ двух видов линейности: внутри Lab065 и для одинаковых
|
||||
уровней Lab064/Lab065. До RF численно заданы границы пяти классификаций.
|
||||
Software gate и 12 целевых тестов прошли.
|
||||
|
||||
RX-only preflight прошёл. Обе HackRF One r9 и роли подтверждены,
|
||||
MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED на третьем опросе через201,986мс.
|
||||
RX записал800000 отсчётов фона: полный RMS2,333068, AC RMS1,056565 кода,
|
||||
max|I|/|Q|=6/8, clipping0, overruns0, неожиданно сильного сигнала нет.
|
||||
Процессы до и после CLEAR, cleanup без ошибок.
|
||||
|
||||
После отдельного прямого разрешения выполнен ровно один OTA-сеанс Lab065.
|
||||
Перед TX MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED на втором опросе через
|
||||
136,897мс. RX стартовал раньше TX. TX underrun maximum0, RX overrun maximum0,
|
||||
clipping0, cleanup без ошибок; процессы после CLEAR. RX:16000000 отсчётов,
|
||||
2,0с, SHA-256 845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e.
|
||||
Автоматических и ручных повторов не было.
|
||||
|
||||
Внутренние ошибки ступеней Lab065 равны−0,077363/+0,034934/−0,013858дБ;
|
||||
компрессии L2/L3 нет. Относительно Lab064 matched power вырос на
|
||||
11,179328/11,119466/11,323670/11,287650дБ. Медианный прирост+11,233489дБ,
|
||||
медианное изменение остатка+0,517004дБ, улучшение matched/residual+10,734495дБ.
|
||||
Фон стабилен: разброс1,002840дБ при границе3дБ. Остаточные полосы50/100/150кГц
|
||||
на L3 выросли на7,304593/6,432281/4,996232дБ, то есть слабее matched power.
|
||||
Классификация `LINEAR_GAIN_IMPROVEMENT`.
|
||||
|
||||
Независимая read-only проверка пересчитала RX SHA и размер, все счётчики,
|
||||
локализацию, фон, matched/residual, комплексный h, CFO, внутренние ступени,
|
||||
сравнение с Lab064 и полосы50/100/150кГц. Расхождений нет. Adversary подтвердил
|
||||
одноразовость runner и нашёл неоднозначную подпись текущего состояния RF после
|
||||
сеанса; она исправлена на `RF TX NOW=0` и закрыта отдельным тестом. Финальные
|
||||
шлюзы: quick gate1000/38/13 PASS, pytest1000 passed, diff check PASS.
|
||||
|
||||
RF TX=0. Lab065 не коммитировалась, push не выполнялся, Lab066 не создана.
|
||||
Следующий этап: STOP, рассмотрение результата и отдельное разрешение на коммит
|
||||
Lab065; повторная передача или изменение усилений запрещены.
|
||||
|
||||
# Запись 061
|
||||
|
||||
## Дата
|
||||
|
||||
15 сентября 2026 года
|
||||
|
||||
## Тема
|
||||
|
||||
Lab065 зафиксирована; подготовка исправленного Lab066 и RX-only preflight
|
||||
|
||||
## Выполнено
|
||||
|
||||
По прямому разрешению пользователя создан локальный коммит Lab065
|
||||
`dacf04f2d0a15c0c1b56a7fb527db61fc2e38462` с сообщением
|
||||
`Lab065: validate OTA gain improvement at TX VGA 20 dB`. Push не выполнялся.
|
||||
|
||||
Lab066 подготовлена как исправленный повтор цели Lab062. Исторический результат
|
||||
Lab062 сохранён: `RF_SESSION_INVALID_TX_UNDERRUN`, legacy/T2 0/16. В Lab066
|
||||
использованы16 штатных полных PHY-пакетов по256 байт при500ксимв/с, Fs8Мвыб/с,
|
||||
цифровой несущей+50кГц, RF1400МГц, TX VGA20дБ и RX LNA/VGA24/24дБ. RF amplifier
|
||||
и Bias-Tee выключены; геометрия около1,2м, LOS, антенны и поляризация прежние.
|
||||
|
||||
Каждый packet waveform содержит92256 отсчётов. Между пакетами записаны15
|
||||
фактических нулевых пауз по40000 отсчётов, то есть по5мс. Добавлены начальный и
|
||||
конечный guards по800000 отсчётов, или по100мс. Длина без guards равна2076096,
|
||||
полная длина3676096 отсчётов,7352192 байта,0,459512с. SHA-256 TX:
|
||||
621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199.
|
||||
|
||||
Весь конечный TX-файл независимо обработан legacy и T2. Оба режима дали16/16
|
||||
HEADER, FEC, CRC, packet output и точных payload в правильном порядке. Позиции
|
||||
пакетов совпали с разметкой пауз. Между режимами fallback отсутствует; T2 для
|
||||
каждого пакета заново оценивает состояние по его собственной преамбуле.
|
||||
|
||||
Программные шлюзы: quick gate1013 тестов ядра/39 функциональных/13 аппаратных
|
||||
пропусков PASS; pytest1013 passed; diff check PASS. Затем выполнен один RX-only
|
||||
preflight. Обе HackRF One r9 и роли подтверждены. MASTER/SLAVE reg29=0x26,
|
||||
SLAVE CLKIN=DETECTED на втором опросе через132,113мс. Фон:800000 отсчётов,
|
||||
полный RMS2,225641, AC RMS0,827637, max|I|/|Q|=4/5, clipping0, maximum
|
||||
overruns0, сильного сигнала нет. Cleanup без ошибок, процессы после CLEAR.
|
||||
|
||||
Независимый verifier пересчитал SHA, длину,15 нулевых пауз, оба guards,
|
||||
16 канонических packet waveform, payload и RX-only метрики; расхождений нет.
|
||||
Read-only adversary проверил запрет TX, одноразовость, связь preflight/capture
|
||||
с конфигурацией, максимум счётчиков, live-фон перед TX и границу диагностик;
|
||||
после исправлений открытых замечаний нет.
|
||||
|
||||
RF TX=0. Lab066 не коммитировалась, push не выполнялся, Lab067 не создавалась.
|
||||
Следующий этап: STOP и ожидание отдельного прямого разрешения на один TX Lab066.
|
||||
|
||||
После отдельного прямого разрешения выполнен ровно один OTA RF-сеанс Lab066.
|
||||
Передача всего потока запущена одним вызовом, автоматических повторов не было.
|
||||
Перед TX MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED на втором опросе через
|
||||
131,291мс. Live RX-фон: AC RMS1,173567, max|I|/|Q|=4/5, clipping0. TX underrun
|
||||
observations0/0, RX overrun observations0/0/0/0; максимумы0/0. RX записал
|
||||
16000000 отсчётов за2,0с, SHA-256
|
||||
72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0.
|
||||
Cleanup без ошибок, процессы после CLEAR; аппаратная запись валидна.
|
||||
|
||||
Одна RX-запись независимо обработана legacy и T2 без fallback. Оба режима дали
|
||||
145 кандидатов,10 HEADER/FEC/CRC PASS,10 packet output и10 точных payload.
|
||||
Оба приняли пакеты4–8 и10–14; пакеты1–3,9,15,16 остановились на PHY header.
|
||||
Выигрыш T2 равен нулю.
|
||||
|
||||
Диагностика всех16 пакетов после строгого результата дала SER0,0110466,
|
||||
RMS-отклонение0,528374, matched/residual+5,542117дБ и медианную неравномерность
|
||||
полосы2,870070дБ. Это лучше Lab056 B на0,0239123 SER,0,316976 RMS,
|
||||
4,077271дБ matched/residual и2,601312дБ неравномерности. Относительно Lab062
|
||||
улучшения составляют0,0882400 SER,0,692027 RMS,7,272168дБ matched/residual и
|
||||
3,790271дБ неравномерности; сравнение с Lab062 контекстное из-за её underrun.
|
||||
|
||||
Классификация `PARTIAL_PACKET_RECOVERY_NO_T2_GAIN`: аппаратно чистый сеанс
|
||||
существенно улучшил физическое качество, но не достиг16/16, а T2 не изменил
|
||||
строгий результат. RF TX=0, повтор не выполнялся. Lab066 не коммитировалась,
|
||||
push не выполнялся, Lab067 не создавалась. Следующий возможный одиночный шаг —
|
||||
отдельно разрешённый офлайн-анализ PHY header не прошедших пакетов по сохранённой
|
||||
IQ-записи; новый TX не нужен.
|
||||
|
||||
130
data/processed/lab062/capture_result.json
Normal file
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"lab": "Lab062",
|
||||
"stage": "SINGLE_RF_CAPTURE",
|
||||
"passed": false,
|
||||
"rf_tx_count": 1,
|
||||
"automatic_tx_repeats": 0,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 500000,
|
||||
"samples_per_symbol": 16,
|
||||
"digital_carrier_hz": 50000.0,
|
||||
"packets": 16,
|
||||
"payload_bytes": 256,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
|
||||
},
|
||||
"tx_sha256": "e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5",
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 304,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 2,
|
||||
"time_to_detect_ms": 140.9547000657767,
|
||||
"elapsed_ms": 140.9547000657767,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.31830002553761,
|
||||
"end_ms": 67.1174000017345,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 117.15810000896454,
|
||||
"end_ms": 140.9547000657767,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_level_before_tx": {
|
||||
"samples": 65536,
|
||||
"mean_i": 0.957977294921875,
|
||||
"mean_q": 1.857269287109375,
|
||||
"power_code2": 4.886199951171875,
|
||||
"ac_power_code2": 0.5190302487462768,
|
||||
"rms_code": 2.2104750510177387,
|
||||
"peak_magnitude_code": 4.123105625617661,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.414776863475277,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||
},
|
||||
"rx_samples": 24000000,
|
||||
"rx_bytes": 48000000,
|
||||
"rx_duration_seconds": 3.0,
|
||||
"error": "TX underrun или неоднозначный счётчик; повтор запрещён",
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 304,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
2043
data/processed/lab062/legacy_rf_decode.json
Normal file
17
data/processed/lab062/packet_comparison.csv
Normal file
@@ -0,0 +1,17 @@
|
||||
packet_index,legacy_header,legacy_fec,legacy_crc,legacy_output,t2_header,t2_fec,t2_crc,t2_output,preamble_errors_t2,header_errors_t2,payload_errors_before_fec_t2,initial_frequency_hz_t2,t2_final_frequency_hz,t2_lock_status
|
||||
1,False,False,False,False,False,False,False,False,108,20,367,-2.511306789128502,1.938828000443245,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
2,False,False,False,False,False,False,False,False,135,11,377,16.850274814266026,-2.223133252684196,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
3,False,False,False,False,False,False,False,False,466,115,2229,-266.23811405950204,-265.79617151697045,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
4,False,False,False,False,False,False,False,False,515,104,2266,297.2495839963796,293.7996556301242,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
5,False,False,False,False,False,False,False,False,108,27,369,-1.7121731230326125,-0.8347122576256625,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
6,True,True,False,False,True,True,False,False,112,10,345,4.67554618043703,-3.16390424095876,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
7,False,False,False,False,False,False,False,False,113,7,344,2.1125086049646824,1.0944814837028547,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
8,False,False,False,False,False,False,False,False,91,19,368,0.9638879273500407,0.35355297825700155,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
9,False,False,False,False,False,False,False,False,105,21,367,-2.6793109690685877,0.19088110622128074,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
10,False,False,False,False,False,False,False,False,116,7,345,-0.29035656707501434,0.9472030503555074,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
11,False,False,False,False,True,True,False,False,109,9,392,0.7573187277726318,-0.14196556023732618,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
12,False,False,False,False,False,False,False,False,297,110,2181,-223.04966205995262,-222.38930722105994,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
13,False,False,False,False,False,False,False,False,304,36,524,-0.16099769754669116,-2.081420319755734,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
14,True,True,False,False,False,False,False,False,314,127,2091,228.20916012396785,224.92813768839025,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
15,True,True,False,False,True,True,False,False,131,7,395,-9.95965988857566,2.2844456653638283,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
16,False,False,False,False,False,False,False,False,100,23,355,13.156526986310709,-0.8684121381823278,NOT_DEFINED_BY_IMPLEMENTATION
|
||||
|
144
data/processed/lab062/preflight_result.json
Normal file
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"lab": "Lab062",
|
||||
"stage": "RX_ONLY_PREFLIGHT",
|
||||
"passed": true,
|
||||
"rf_tx_count": 0,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 500000,
|
||||
"samples_per_symbol": 16,
|
||||
"digital_carrier_hz": 50000.0,
|
||||
"packets": 16,
|
||||
"payload_bytes": 256,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
|
||||
},
|
||||
"tx_sha256": "e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5",
|
||||
"stop_before_tx": true,
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 308,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 2,
|
||||
"time_to_detect_ms": 133.57950001955032,
|
||||
"elapsed_ms": 133.57950001955032,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.528000108897686,
|
||||
"end_ms": 67.34060007147491,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 117.69610014744103,
|
||||
"end_ms": 133.57950001955032,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_process": {
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "call hackrf_set_sample_rate(8000000 Hz/8.000 MHz)\ncall hackrf_set_hw_sync_mode(0)\ncall hackrf_set_freq(1400000000 Hz/1400.000 MHz)\ncall hackrf_set_amp_enable(0)\ncall hackrf_set_antenna_enable(0)\nsamples_to_xfer 800000/0Mio\nStop with Ctrl-C\n 1.8 MiB / 0.115 sec = 15.9 MiB/second, average power -34.5 dBfs, 9120 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11549 s\nhackrf_stop_rx() done\nTransfer statistics:\n1847680 bytes transferred by M0\n1835008 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n"
|
||||
},
|
||||
"rx_level": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.9660625,
|
||||
"mean_q": 1.85752875,
|
||||
"power_code2": 5.35486625,
|
||||
"ac_power_code2": 0.9711764390171878,
|
||||
"rms_code": 2.3140583938180987,
|
||||
"peak_magnitude_code": 8.246211251235321,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 8.0,
|
||||
"papr_db": 11.037602848982825,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
|
||||
"ac_rms_code": 0.9854828456229909,
|
||||
"unexpected_strong_signal": false,
|
||||
"strong_signal_rule": "AC RMS >= 20.0 code or max |I|/|Q| >= 100.0 code",
|
||||
"symbol_energy": "NOT_APPLICABLE_TO_BACKGROUND"
|
||||
},
|
||||
"rx_sha256": "4cc0ceef5a95f84e5aa0c232d14ff1123efd307494b5c8a5646854be912fdbca",
|
||||
"rx_overruns": 0,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 308,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": [],
|
||||
"processed_correction": {
|
||||
"source": "data/raw/lab062/preflight_result.json",
|
||||
"removed_field": "rx_level.symbol_energy_code2_seconds",
|
||||
"removed_value": 0.00026774331249999997,
|
||||
"reason": "inherited Lab056 20 ksymbol/s normalization is not applicable to RX-only background",
|
||||
"preflight_pass_affected": false
|
||||
}
|
||||
}
|
||||
80
data/processed/lab062/preparation.json
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"lab": "Lab062",
|
||||
"stage": "PREPARATION_ONLY",
|
||||
"rf_tx_count": 0,
|
||||
"head": "25cc89f0c734f77141632d19dbcdb6b2f066d60b",
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 500000,
|
||||
"samples_per_symbol": 16,
|
||||
"digital_carrier_hz": 50000.0,
|
||||
"packets": 16,
|
||||
"payload_bytes": 256,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
|
||||
},
|
||||
"tx_samples": 1476096,
|
||||
"tx_bytes": 2952192,
|
||||
"tx_duration_seconds": 0.184512,
|
||||
"tx_sha256": "e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5",
|
||||
"rx_samples": 24000000,
|
||||
"rx_duration_seconds": 3,
|
||||
"automatic_retry": false,
|
||||
"tx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc323b97d3",
|
||||
"-t",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab062\\tx_stream.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"1476096",
|
||||
"-x",
|
||||
"10",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
],
|
||||
"rx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"-r",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab062\\rx_stream.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"24000000",
|
||||
"-l",
|
||||
"24",
|
||||
"-g",
|
||||
"24",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
]
|
||||
}
|
||||
2986
data/processed/lab062/rf_analysis.json
Normal file
25
data/processed/lab062/software_gate.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"lab": "Lab062",
|
||||
"stage": "SOFTWARE_GATE",
|
||||
"rf_tx_count": 0,
|
||||
"tx_sha256": "e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5",
|
||||
"legacy": {
|
||||
"mode": "legacy",
|
||||
"packets": 16,
|
||||
"header_pass": 16,
|
||||
"fec_pass": 16,
|
||||
"crc_pass": 16,
|
||||
"exact_ordered_payloads": true,
|
||||
"passed": true
|
||||
},
|
||||
"t2": {
|
||||
"mode": "t2",
|
||||
"packets": 16,
|
||||
"header_pass": 16,
|
||||
"fec_pass": 16,
|
||||
"crc_pass": 16,
|
||||
"exact_ordered_payloads": true,
|
||||
"passed": true
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
2043
data/processed/lab062/t2_rf_decode.json
Normal file
BIN
data/processed/lab063/gain_vs_time.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
data/processed/lab063/lab056_b_vs_lab062_packets.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
360
data/processed/lab063/local_alignment.csv
Normal file
@@ -0,0 +1,360 @@
|
||||
window_index,tx_sample,rx_sample,time_seconds,local_delay_samples,packet_index,position_inside_packet_samples,inside_reliable_region,correlation_above_threshold,reliable_for_delay,normalized_correlation,gain,gain_amplitude,phase_rad,residual_power,rx_ac_power
|
||||
0,0,4256092,0.0,1,1,0,False,True,False,0.2723983005613592,(0.0014400764308659896+0.004547916824745963j),0.004770468275918322,1.264139438138503,0.5320521433901737,0.5746949911117554
|
||||
1,4096,4258156,0.000512,-2031,1,4096,False,True,False,0.2729793659247979,(-0.004663053599288344+0.00040199323732778125j),0.004680349071671177,3.0555971153877457,0.5307691750552395,0.5735055059194565
|
||||
2,8192,4258188,0.001024,-6095,1,8192,False,True,False,0.2744171645442273,(0.004046229816782595+0.0023981760914401954j),0.004703533171539868,0.5350301142527935,0.5298967126207345,0.5730501264333725
|
||||
3,12288,4258220,0.001536,-10159,1,12288,False,True,False,0.2745940021638059,(-0.001877375284743114-0.004308549206226646j),0.004699801508812957,-1.9817223367149237,0.5290641319544691,0.572209820151329
|
||||
4,16384,4272476,0.002048,1,1,16384,False,True,False,0.26655475961180447,(0.0015909658512084958+0.0043219233704984905j),0.004605452633582567,1.2180752315139343,0.5385030300563245,0.5796909034252167
|
||||
5,20480,4276572,0.00256,1,1,20480,False,True,False,0.2704674132861876,(0.0015020188131524086+0.004503663352078344j),0.004747530316271536,1.2488863714827263,0.5517759339741067,0.5953255593776703
|
||||
6,24576,4280667,0.003072,0,1,24576,True,True,True,0.2779743002913568,(0.001574055297828197+0.004628794269849324j),0.004889108965160313,1.2430064552849862,0.5566272125305693,0.6032393425703049
|
||||
7,28672,4284763,0.003584,0,1,28672,True,True,True,0.2720544529364392,(0.0016932309240155242+0.004457847887134059j),0.004768588758413548,1.2077965884614033,0.5530613403203055,0.5972672551870346
|
||||
8,32768,4288859,0.004096,0,1,32768,True,True,True,0.2649913203278537,(0.0018990710284393965+0.004188594569993716j),0.004598999374085488,1.1451260339739677,0.5459171758273589,0.5871468633413315
|
||||
9,36864,4292955,0.004608,0,1,36864,True,True,True,0.2721722386513534,(0.0018268451981139167+0.004317763275913575j),0.004688330522126165,1.1705361837258939,0.5365336736680916,0.5794586539268494
|
||||
10,40960,4297051,0.00512,0,1,40960,True,True,True,0.2721968822811185,(0.001724738414167657+0.004364487642303184j),0.004692917533594935,1.1944559420309822,0.5372551467224218,0.5802462548017502
|
||||
11,45056,4301147,0.005632,0,1,45056,True,True,True,0.27115565572908557,(0.0018536312646620433+0.0043132285698386785j),0.004694666075559072,1.1649051100363104,0.5411645818582298,0.5841116160154343
|
||||
12,49152,4305244,0.006144,1,1,49152,True,True,True,0.26695565820866574,(0.0015749959490531304+0.004372541401953098j),0.004647551027296822,1.2250623805453835,0.5459189026441111,0.5878093242645264
|
||||
13,53248,4309339,0.006656,0,1,53248,True,True,True,0.2556440262627536,(0.0016446481954502357+0.004141139532609057j),0.0044557720223700045,1.1927503782494664,0.5527965436310334,0.5914500951766968
|
||||
14,57344,4313435,0.007168,0,1,57344,True,True,True,0.2654944778350678,(0.001579553863468991+0.004353550882949614j),0.004631241269684746,1.2227468320256794,0.551303941123149,0.5931107252836227
|
||||
15,61440,4317531,0.00768,0,1,61440,True,True,True,0.2819287365366617,(0.0016676229288336369+0.0046224170689160154j),0.004914031521243816,1.2245604961967766,0.5432338343896431,0.5901404470205307
|
||||
16,65536,4321628,0.008192,1,1,65536,True,True,True,0.2849343081441107,(0.0015551300976373065+0.004760709738123959j),0.005008271840789566,1.255064382840333,0.5515766203313819,0.6003147065639496
|
||||
17,69632,4325724,0.008704,1,1,69632,True,True,True,0.2668779433455796,(0.0015400349773235437+0.0044066605083271275j),0.004668015056427071,1.234585792156039,0.5544426689160701,0.5969604849815369
|
||||
18,73728,4329819,0.009216,0,1,73728,True,True,True,0.26731850919222144,(0.0015998970853807015+0.004351612962721423j),0.004636400118748897,1.2184796304589962,0.544979127072188,0.5869199484586716
|
||||
19,77824,4333915,0.009728,0,1,77824,True,True,True,0.2757493751671597,(0.0015028961676935508+0.0045326408799782995j),0.004775304203683616,1.2506319351828965,0.5405722579331813,0.58505879342556
|
||||
20,81920,4338011,0.01024,0,1,81920,True,True,True,0.2747031051630683,(0.001434360817300166+0.004554302124498627j),0.004774835996703836,1.2656843127617807,0.5449793922392892,0.5894611924886703
|
||||
21,86016,4342106,0.010752,-1,1,86016,False,True,False,0.262832641274786,(0.0017063341695680602+0.004412668558523586j),0.004731090794480502,1.201816607513573,0.5405771973365503,0.5806919783353806
|
||||
22,90112,4346203,0.011264,0,1,90112,False,True,False,0.24463720210977444,(0.001540875532155414+0.004130615883847389j),0.004408660225679404,1.2137471203289174,0.5462349738883019,0.5810066908597946
|
||||
23,94208,4360459,0.011776,10160,2,1952,False,True,False,0.2716641040460329,(-0.0013022645358288175-0.004525075678879153j),0.00470873686043944,-1.8510125610584165,0.5428179407596163,0.5860707759857178
|
||||
24,98304,4360491,0.012288,6096,2,6048,False,True,False,0.2718898121257277,(-0.0016149804689081098+0.004431209064076905j),0.0047163307437574796,1.920291000004672,0.5434848560970025,0.5868685692548752
|
||||
25,102400,4360523,0.0128,2032,2,10144,False,True,False,0.2725240587031942,(0.00391949046182433-0.0026463739117019856j),0.004729238877543486,-0.5938754774412633,0.5434089720090052,0.5870054960250854
|
||||
26,106496,4362587,0.013312,0,2,14240,False,True,False,0.2760592697755412,(0.0013920156722528913+0.004580095816437278j),0.0047869599245809365,1.2757405072063803,0.5415059501043626,0.5861778110265732
|
||||
27,110592,4366683,0.013824,0,2,18336,False,True,False,0.2724376874597982,(0.0014133537140496781+0.0045130584690135705j),0.004729192897921709,1.2673013462261535,0.5413853024567086,0.5847897380590439
|
||||
28,114688,4370780,0.014336,1,2,22432,False,True,False,0.2719029335917029,(0.0011697644964912767+0.004560116010595991j),0.0047077602962922165,1.3196899415543735,0.5406078471906972,0.5837664008140564
|
||||
29,118784,4374876,0.014848,1,2,26528,True,True,True,0.2700707220369181,(0.0013350388080086953+0.004474286128071312j),0.004669214599345446,1.2808261632328335,0.5394193308566163,0.5818590819835663
|
||||
30,122880,4378971,0.01536,0,2,30624,True,True,True,0.2686195464655446,(0.0012246606908276484+0.0044642820496554455j),0.004629212462885483,1.3030584917227905,0.5373847039400281,0.5791759938001633
|
||||
31,126976,4383067,0.015872,0,2,34720,True,True,True,0.2633530926845802,(0.0011406041785767163+0.004404294536372791j),0.004549592097684131,1.3173881793080358,0.5396795929719889,0.5798983573913574
|
||||
32,131072,4387163,0.016384,0,2,38816,True,True,True,0.25637788480815954,(0.0012829050950408745+0.004270558977812529j),0.004459094018503873,1.278966333401463,0.5485951175865063,0.5871909558773041
|
||||
33,135168,4391260,0.016896,1,2,42912,True,True,True,0.26000719134870665,(0.001208701026115259+0.00434386307850457j),0.004508891728055496,1.2994066208398154,0.5469509736502136,0.5866078585386276
|
||||
34,139264,4395355,0.017408,0,2,47008,True,True,True,0.2539878786554866,(0.001204104650759226+0.0042140376803331j),0.004382691134593809,1.2924760702562699,0.5432254548985715,0.5806853771209717
|
||||
35,143360,4399451,0.01792,0,2,51104,True,True,True,0.26248777906333653,(0.001090192569531215+0.004413735444813682j),0.004546381024008624,1.3286431982708542,0.544464620474242,0.584754079580307
|
||||
36,147456,4403547,0.018432,0,2,55200,True,True,True,0.268908662501336,(0.0011983970061596977+0.004461156216980074j),0.0046193149033890825,1.3083625534812198,0.5334410404305294,0.5750219523906708
|
||||
37,151552,4407644,0.018944,1,2,59296,True,True,True,0.2584294308489172,(0.0009705941914629901+0.004321800607292927j),0.004429448450281277,1.3498807548369551,0.5329131149684843,0.5710512101650238
|
||||
38,155648,4411739,0.019456,0,2,63392,True,True,True,0.2589804129517903,(0.0011773086855389008+0.004351617974012928j),0.004508063301773581,1.306576549991733,0.5504235283232656,0.5899949967861176
|
||||
39,159744,4415835,0.019968,0,2,67488,True,True,True,0.26707042084269383,(0.0012836628929886265+0.004466770988566209j),0.004647562101482096,1.2909568817815837,0.5478066109478578,0.5898808091878891
|
||||
40,163840,4419932,0.02048,1,2,71584,True,True,True,0.26685819356609625,(0.0011948923329598014+0.004432616311602053j),0.004590844699316968,1.3074870108552974,0.5338153340239872,0.5747448056936264
|
||||
41,167936,4424027,0.020992,0,2,75680,True,True,True,0.2602019922356652,(0.0012135424294046583+0.004275450290739874j),0.004444340267863503,1.294230653530701,0.5290908955689677,0.5675145089626312
|
||||
42,172032,4428123,0.021504,0,2,79776,True,True,True,0.2648953034335269,(0.0010080749497805373+0.004436298157786467j),0.0045493907778025216,1.3473571568083174,0.5346305944052281,0.5749764144420624
|
||||
43,176128,4432219,0.022016,0,2,83872,True,True,True,0.27271964951391364,(0.0010679789510904913+0.004638600937917362j),0.004759957741537227,1.3445025739068763,0.5379263101079028,0.5811499208211899
|
||||
44,180224,4436316,0.022528,1,2,87968,False,True,False,0.26621296963966334,(0.0014238192462404508+0.004578690891323345j),0.004794963141073359,1.2693091820913467,0.5398449030192329,0.5810215175151825
|
||||
45,184320,4440411,0.02304,0,2,92064,False,True,False,0.2580468087430128,(0.0016982791241270806+0.004292585999959374j),0.004616323921746941,1.1940621888188008,0.5458059940350145,0.5847429484128952
|
||||
46,188416,4452635,0.023552,8128,3,3904,False,True,False,0.2649939974461844,(0.004547469531258444+0.0002454033015846323j),0.004554086287956402,0.053912509828619726,0.5349567727385085,0.5753595679998398
|
||||
47,192512,4452667,0.024064,4064,3,8000,False,True,False,0.2652695563170298,(-0.00352196396834277-0.00288831530557835j),0.004554843081681619,-2.4547232497156846,0.5348699867647786,0.5753566473722458
|
||||
48,196608,4450668,0.024576,-2031,3,12096,False,True,False,0.2655804119817936,(-0.00455017405199118-0.0005553942299958869j),0.00458394444273996,-3.0201334712223153,0.5399761574427078,0.580952450633049
|
||||
49,200704,4456795,0.025088,0,3,16192,False,True,False,0.27329487809545694,(0.0010001317651298055+0.004609230865953922j),0.004716489449080111,1.3571240999141405,0.5367884244476238,0.5801174491643906
|
||||
50,204800,4460891,0.0256,0,3,20288,False,True,False,0.2748035784048019,(0.0009020685528086647+0.004696528423964232j),0.0047823746100729364,1.3810359628033242,0.5447007004166863,0.5891949385404587
|
||||
51,208896,4464987,0.026112,0,3,24384,True,True,True,0.2733714317503713,(0.001196082844537941+0.0046138615886948545j),0.0047663752402251945,1.3171432489691388,0.5430379318122193,0.5868979543447495
|
||||
52,212992,4469084,0.026624,1,3,28480,True,True,True,0.26980098289370985,(0.000891303823734098+0.00462177943251277j),0.0047069382435932896,1.3802864468658624,0.5452336561152853,0.5880384892225266
|
||||
53,217088,4473180,0.027136,1,3,32576,True,True,True,0.2593488284007324,(0.000701164278632974+0.004429229337331668j),0.004484384446979364,1.4137952643367864,0.5418432466726182,0.5809167623519897
|
||||
54,221184,4477275,0.027648,0,3,36672,True,True,True,0.24083000237735547,(0.0010787855072950715+0.003956623747567151j),0.004101054699776901,1.3046133691002941,0.5316101788297216,0.5643414705991745
|
||||
55,225280,4481371,0.02816,0,3,40768,True,True,True,0.24884057677466154,(0.0012529409123619626+0.004099663105570891j),0.004286851817948644,1.274191159290718,0.5432383289368947,0.5790969580411911
|
||||
56,229376,4485467,0.028672,0,3,44864,True,True,True,0.26424577088305007,(0.001419557704160117+0.004367512865639938j),0.004592419047405269,1.2565404007957168,0.5470104412221599,0.5880731344223022
|
||||
57,233472,4489563,0.029184,0,3,48960,True,True,True,0.25762850178040403,(0.0013096625676030976+0.004235848862247899j),0.004433692775191755,1.2709340205678785,0.5390744902777722,0.5773977935314178
|
||||
58,237568,4493659,0.029696,0,3,53056,True,True,True,0.2679321562033791,(0.0013369756290009971+0.0043821287442785415j),0.004581545172098038,1.2746696997240843,0.5273775856410188,0.5681647956371307
|
||||
59,241664,4497755,0.030208,0,3,57152,True,True,True,0.2711470944273147,(0.001351328684164128+0.004419603802068811j),0.004621578407850054,1.2740654815604695,0.5230486505702867,0.5645551681518555
|
||||
60,245760,4501852,0.03072,1,3,61248,True,True,True,0.2672883844824355,(0.0010068247184626777+0.004467841124027755j),0.004579879946380805,1.3491494727972868,0.5312288236824456,0.5721015185117722
|
||||
61,249856,4505947,0.031232,0,3,65344,True,True,True,0.27107441392986664,(0.0013357978398945835+0.0044942838974314555j),0.004688597191034725,1.2818905523246318,0.5404706646150161,0.583334892988205
|
||||
62,253952,4510043,0.031744,0,3,69440,True,False,False,0.13153433429624267,(0.001554183671647779+0.004426566695093525j),0.004691479467217968,1.2331386492766252,2.4339817589794546,2.476834163069725
|
||||
63,258048,4514139,0.032256,0,3,73536,True,False,False,0.08888879507126439,(0.00169980331450993+0.004389042425002968j),0.004706699981568283,1.2013003657755736,5.416685224653776,5.459824487566948
|
||||
64,262144,4518235,0.032768,0,3,77632,True,False,False,0.07990273751817419,(0.001699250897969519+0.004324783344646435j),0.004646633683902942,1.1964168666678643,6.53860352546403,6.580617129802704
|
||||
65,266240,4522332,0.03328,1,3,81728,True,False,False,0.07824125225628915,(0.0013933691504081321+0.004356594463416111j),0.004573990916910198,1.2612476939877006,6.624367159902822,6.665169283747673
|
||||
66,270336,4526428,0.033792,1,3,85824,True,False,False,0.07587440188611191,(0.0013041751210003604+0.004457185452549003j),0.004644068787674313,1.286141724316014,6.655995644921719,6.694535583257675
|
||||
67,274432,4530523,0.034304,0,3,89920,False,False,False,0.0769175854864118,(0.0015678214075495489+0.004396212075719992j),0.004667413050146401,1.2282272423410476,6.554379697834005,6.593388259410858
|
||||
68,278528,4542747,0.034816,8128,4,1760,False,False,False,0.08597985441315165,(0.005066283573465239+0.00034999030444959325j),0.005078358244548373,0.0689726756303229,6.758426343340691,6.808760344982147
|
||||
69,282624,4542779,0.035328,4064,4,5856,False,False,False,0.0857009006288708,(-0.0038966589911976885-0.003238719072000978j),0.005066877985606587,-2.4481426241304436,6.759721722902659,6.80973681807518
|
||||
70,286720,4544843,0.03584,2032,4,9952,False,False,False,0.08525779282376747,(0.004325880698897092-0.0025736448588474193j),0.005033576430388451,-0.536691526326966,6.741963816067408,6.791329249739647
|
||||
71,290816,4546907,0.036352,0,4,14048,False,False,False,0.0818033690015016,(0.0011822849366477169+0.004670129356209848j),0.004817458445607716,1.3228467728695121,6.720242903354096,6.765516325831413
|
||||
72,294912,4551003,0.036864,0,4,18144,False,False,False,0.07658097028160148,(0.0008517722304822827+0.004438565003908057j),0.0045195547597676146,1.3811986992356107,6.7219641213364305,6.761618614196777
|
||||
73,299008,4555099,0.037376,0,4,22240,False,False,False,0.08613404389567611,(0.0011849698335850026+0.0049152028039830455j),0.005056023349509896,1.334227966715792,6.6581370011692105,6.707903429865837
|
||||
74,303104,4559195,0.037888,0,4,26336,True,False,False,0.08726659571551212,(0.0016595927555988928+0.004842576482874993j),0.005119061936227209,1.2406318687205053,6.6524727994418935,6.703523203730583
|
||||
75,307200,4563291,0.0384,0,4,30432,True,False,False,0.07303287114951702,(0.001432858640864123+0.004032332716192873j),0.0042793446950179625,1.229370113401519,6.647078068400921,6.682722374796867
|
||||
76,311296,4567387,0.038912,0,4,34528,True,False,False,0.07923837125521409,(0.001686126196189353+0.004315979331039012j),0.004633648577032135,1.198358325356545,6.621968636371651,6.663808822631836
|
||||
77,315392,4571483,0.039424,0,4,38624,True,False,False,0.08766369642748506,(0.0017031705964680188+0.004852024258675652j),0.0051422689046227675,1.2332107176608704,6.6245479222476575,6.675851330161095
|
||||
78,319488,4575579,0.039936,0,4,42720,True,False,False,0.08111151553103035,(0.0017214618264675924+0.004433371066833172j),0.004755860577878481,1.2004198351270303,6.642704557350704,6.686696857213974
|
||||
79,323584,4579675,0.040448,0,4,46816,True,False,False,0.10671176630261674,(0.0013274729713343197+0.004468864548466894j),0.0046618595905676295,1.2820488137155435,3.674980498404129,3.7173110097646713
|
||||
80,327680,4583771,0.04096,0,4,50912,True,True,True,0.17364185979381344,(0.0011104452242883332+0.0048122481560667054j),0.0049387064006389545,1.3440119960319097,1.5273621675226863,1.5748461335897446
|
||||
81,331776,4587867,0.041472,0,4,55008,True,False,False,0.13566484987135102,(0.0011900787687552166+0.004564289775742337j),0.004716887600185949,1.3157381520156248,2.309129150389409,2.3524254262447357
|
||||
82,335872,4591963,0.041984,0,4,59104,True,True,True,0.1663171407536924,(0.0012317423905629345+0.004361708579217488j),0.004532294236557146,1.2955643672094288,1.3985216467121702,1.4383072257041931
|
||||
83,339968,4596059,0.042496,0,4,63200,True,True,True,0.26471751311013225,(0.001558865952620833+0.00430077474184848j),0.004574573907847846,1.2230632263032186,0.5405374832466419,0.5812702029943466
|
||||
84,344064,4600155,0.043008,0,4,67296,True,True,True,0.2792268825008271,(0.0016212932750898708+0.004545583027784552j),0.004826066405089617,1.2281878779094144,0.5361519398429078,0.5814892947673798
|
||||
85,348160,4604251,0.04352,0,4,71392,True,True,True,0.2832122191263414,(0.001568572376109022+0.004689580014148107j),0.004944955005678986,1.2480138106379053,0.546382714975119,0.5940293073654175
|
||||
86,352256,4608347,0.044032,0,4,75488,True,True,True,0.2751351562277797,(0.0014124280165170157+0.004572151352605746j),0.004785344386037095,1.2711764645789363,0.5418399951586526,0.5862161815166473
|
||||
87,356352,4612443,0.044544,0,4,79584,True,True,True,0.2711253433830607,(0.0012792764994174382+0.004507944933506286j),0.0046859487711120714,1.2942834417500173,0.539963691491252,0.5828050822019577
|
||||
88,360448,4616539,0.045056,0,4,83680,True,True,True,0.2780326176311392,(0.0014125086541028251+0.004613805346804851j),0.004825181911194686,1.2737084804157615,0.5414278402357562,0.5867877900600433
|
||||
89,364544,4620635,0.045568,0,4,87776,False,True,False,0.2717667108050532,(0.0013978470154112287+0.004703990842016978j),0.004907291118353758,1.281945292248661,0.5413104739012351,0.5844783782958984
|
||||
90,368640,4626764,0.04608,2033,4,91872,False,True,False,0.2662074290976547,(0.004115316763378964-0.002495104285846531j),0.004812626877309069,-0.5450368010824937,0.5440730211399605,0.5855702757835388
|
||||
91,372736,4626795,0.046592,-2032,5,3712,False,True,False,0.282542960509231,(-0.004899029038211988-8.122809335022387e-05j),0.004899702391002294,-3.125013725308483,0.5400234934244572,0.5868739485740662
|
||||
92,376832,4626827,0.047104,-6096,5,7808,False,True,False,0.2825020973176275,(0.003908930591380348+0.002952507348220343j),0.004898677169351359,0.646899486603501,0.5395280432357176,0.5863208025693893
|
||||
93,380928,4626859,0.047616,-10160,5,11904,False,True,False,0.2807600356970303,(-0.001420950169111977-0.004657543716583497j),0.004869477677840383,-1.8669123818056594,0.5399243687694288,0.5861264914274216
|
||||
94,385024,4641116,0.048128,1,5,16000,False,True,False,0.2650614809565532,(0.0010542710537117725+0.004575124896595126j),0.004695024523273451,1.344314387574751,0.5685874798206638,0.6115537732839584
|
||||
95,389120,4645211,0.04864,0,5,20096,False,True,False,0.2667422739644077,(0.0010977299055325784+0.004457196663720133j),0.004590382668643046,1.3293194581197063,0.534952635962959,0.5759309530258179
|
||||
96,393216,4649307,0.049152,0,5,24192,True,True,True,0.26466251848509825,(0.0010486869362193254+0.004394194972639804j),0.0045175982277942785,1.336525561985254,0.5277840580651971,0.5675379633903503
|
||||
97,397312,4653403,0.049664,0,5,28288,True,True,True,0.25854313841229054,(0.001198925223421213+0.004261898495125296j),0.004427324301890123,1.2965709791550253,0.5339610295058146,0.5722101628780365
|
||||
98,401408,4657499,0.050176,0,5,32384,True,True,True,0.2537591241878097,(0.001220992856318022+0.004200952361654078j),0.004374794200881525,1.287942494915078,0.5425047534699575,0.5798429846763611
|
||||
99,405504,4661596,0.050688,1,5,36480,True,True,True,0.2590655534926447,(0.0011303389414594528+0.004347174614996451j),0.004491724975535481,1.3164124223967255,0.5442177708614893,0.5833706706762314
|
||||
100,409600,4665692,0.0512,1,5,40576,True,True,True,0.27545885714841517,(0.001241740275123457+0.004628777994975647j),0.004792442450111889,1.3087020787717447,0.5450552239659276,0.589808464050293
|
||||
101,413696,4669788,0.051712,1,5,44672,True,True,True,0.2779047219199058,(0.0013383854927980384+0.004623275702312929j),0.004813102320430073,1.289010720505806,0.5374273640261628,0.5824072808027267
|
||||
102,417792,4673883,0.052224,0,5,48768,True,True,True,0.28072829168306407,(0.0014892805370576398+0.004618240714550299j),0.004852432772907704,1.258847275319138,0.5363134186037127,0.5821952819824219
|
||||
103,421888,4677979,0.052736,0,5,52864,True,True,True,0.27647214381674806,(0.0013533142580344057+0.004639687152038339j),0.004833027658703067,1.2869878818229048,0.5499437435476484,0.5954587310552597
|
||||
104,425984,4682075,0.053248,0,5,56960,True,True,True,0.2715921696331709,(0.001087912656261071+0.004603111202176992j),0.004729924596360954,1.338712126802001,0.5463894941519993,0.5899020284414291
|
||||
105,430080,4686171,0.05376,0,5,61056,True,True,True,0.2669719744416374,(0.001055495801123365+0.004535850831207893j),0.004657039204168071,1.342164417057586,0.5496048207813974,0.5917836278676987
|
||||
106,434176,4690267,0.054272,0,5,65152,True,True,True,0.25933246973065593,(0.0011738677237808808+0.004384331485952915j),0.00453875842182117,1.3091906786672414,0.5567507899047981,0.5968938916921616
|
||||
107,438272,4694363,0.054784,0,5,69248,True,True,True,0.26592635147561616,(0.0014396359888415133+0.004449124742918487j),0.004676244514395839,1.257851704672455,0.5601043060592626,0.6027272641658783
|
||||
108,442368,4698459,0.055296,0,5,73344,True,True,True,0.27365159790299287,(0.0015497665053611947+0.004564716338565147j),0.004820623556419133,1.243497177656846,0.5584733039849059,0.6036799997091293
|
||||
109,446464,4702555,0.055808,0,5,77440,True,True,True,0.2633210162250354,(0.0013221811004029498+0.004381795354342359j),0.004576930564209931,1.2777401668138901,0.5471677345553811,0.5879338681697845
|
||||
110,450560,4706651,0.05632,0,5,81536,True,True,True,0.26824181335626013,(0.0012740111219966598+0.004465711961654281j),0.0046438871286276235,1.29289142838727,0.5420455105218226,0.5840716063976288
|
||||
111,454656,4710747,0.056832,0,5,85632,True,True,True,0.26388173742294235,(0.0014123473641952727+0.004534563825781714j),0.004749420403295272,1.2688567766150023,0.5375280882444592,0.5777595490217209
|
||||
112,458752,4714843,0.057344,0,5,89728,False,True,False,0.2522196006429852,(0.0013772325045995537+0.004293720322152726j),0.004509191011322637,1.2604086118008029,0.5350556534434683,0.5714054554700851
|
||||
113,462848,4725035,0.057856,6096,6,1568,False,True,False,0.2771232078693172,(-0.0020002876001643417+0.004297049846018963j),0.004739808842405231,2.0064671134474126,0.5274695198960893,0.5713474452495575
|
||||
114,466944,4725067,0.058368,2032,6,5664,False,True,False,0.27702128795645814,(0.00413210728460306-0.002319090529045502j),0.004738406007654707,-0.5114293343015762,0.5277817565190994,0.5716506838798523
|
||||
115,471040,4725099,0.05888,-2032,6,9760,False,True,False,0.276592565978206,(-0.004702814275236275-0.000545451646585368j),0.004734340461577387,-3.0261244877072064,0.5278071537418947,0.5715312659740448
|
||||
116,475136,4731227,0.059392,0,6,13856,False,True,False,0.261387587164352,(0.0011452191894361628+0.004324249063830631j),0.0044733272804359965,1.3119033303989793,0.5314278199197381,0.5703994929790497
|
||||
117,479232,4735323,0.059904,0,6,17952,False,True,False,0.26600518395588735,(0.0011882035152338427+0.00440899760787155j),0.004566299103194086,1.307555089993816,0.5333333931089501,0.5739450305700302
|
||||
118,483328,4739419,0.060416,0,6,22048,False,True,False,0.26354711468027764,(0.0012722185803642125+0.004339840170588295j),0.00452247198139201,1.285636904251594,0.5330185708165498,0.5728038549423218
|
||||
119,487424,4743516,0.060928,1,6,26144,True,True,True,0.2655258469985231,(0.0010413614298705306+0.004482633907505584j),0.004602003952230026,1.3425349502168569,0.5439972900983086,0.5852604806423187
|
||||
120,491520,4747612,0.06144,1,6,30240,True,True,True,0.27026658341105503,(0.0006871157719793929+0.004660496024741168j),0.0047108758507024015,1.424416812159449,0.5483031597970439,0.5915093868970871
|
||||
121,495616,4751707,0.061952,0,6,34336,True,True,True,0.27212727343914983,(0.00103874048233643+0.004593169010476414j),0.004709159516139308,1.3483885391116444,0.5392289521801527,0.5823541730642319
|
||||
122,499712,4755803,0.062464,0,6,38432,True,True,True,0.28136708133251975,(0.0015478134989297052+0.004631816125900667j),0.00488358958673049,1.248293057797807,0.5387114553079869,0.5850265026092529
|
||||
123,503808,4759899,0.062976,0,6,42528,True,True,True,0.27890087509243017,(0.0013600301944214322+0.00462867624870359j),0.004824347204031933,1.2850123849861281,0.5356196312800044,0.5807973593473434
|
||||
124,507904,4763995,0.063488,0,6,46624,True,True,True,0.26336035378327605,(0.001049261617941425+0.0043808875864818145j),0.004504789227952348,1.3357157400217448,0.5320584679757843,0.5717116296291351
|
||||
125,512000,4768091,0.064,0,6,50720,True,True,True,0.26497822213724925,(0.001099080196697002+0.00443634202713449j),0.004570460355422775,1.3279414286903835,0.5397274050774757,0.5804852843284607
|
||||
126,516096,4772188,0.064512,1,6,54816,True,True,True,0.2588557900323732,(0.0013698338171910028+0.004291962654778482j),0.004505262269472581,1.2618532123786583,0.5491224961879504,0.5885597169399261
|
||||
127,520192,4776284,0.065024,1,6,58912,True,True,True,0.26525822500006635,(0.0014099236859939925+0.004358861980797595j),0.004581218458878557,1.2579566494377912,0.5409942829315486,0.5819407552480698
|
||||
128,524288,4780379,0.065536,0,6,63008,True,True,True,0.2663899652999382,(0.0013118458205982157+0.004388013938848249j),0.004579913294435554,1.2802928513905263,0.5342598690886164,0.5750688314437866
|
||||
129,528384,4784475,0.066048,0,6,67104,True,True,True,0.25666966629660665,(0.0012463167346545275+0.004264542171658961j),0.004442929814541037,1.286463776193324,0.5445433095922607,0.5829474925994873
|
||||
130,532480,4788571,0.06656,0,6,71200,True,True,True,0.2606059923710226,(0.001271455909544945+0.004320777588694721j),0.00450396704038599,1.2846087811950069,0.54270473673413,0.5822484195232391
|
||||
131,536576,4792667,0.067072,0,6,75296,True,True,True,0.2670879834822261,(0.001595899119666262+0.004329908780645395j),0.00461465102135163,1.2176697888301709,0.5365249042267309,0.5777384489774704
|
||||
132,540672,4796763,0.067584,0,6,79392,True,True,True,0.26593394499633477,(0.001477311710559645+0.004342725822956805j),0.004587125185073165,1.2428958119988545,0.5347466339577565,0.5754424184560776
|
||||
133,544768,4800859,0.068096,0,6,83488,True,True,True,0.2652553665245194,(0.0013141328612987792+0.004389330045641679j),0.004581829702937256,1.2798968884696806,0.5400880761578909,0.5809650123119354
|
||||
134,548864,4804955,0.068608,0,6,87584,False,True,False,0.25548083595374993,(0.0015057567714245848+0.004342790980108081j),0.004596426541521042,1.2370415480903751,0.542590490303864,0.5804785937070847
|
||||
135,552960,4817180,0.06912,8129,6,91680,False,True,False,0.26036916131092225,(0.004673620949138175+0.00024960406064367444j),0.0046802815047081335,0.05335630613581867,0.5395512157685356,0.5787885040044785
|
||||
136,557056,4817212,0.069632,4065,7,3520,False,True,False,0.27341327144153854,(-0.0035304699699410433-0.003130835945304359j),0.004718723548277156,-2.416116014289601,0.5372784161036481,0.5806876122951508
|
||||
137,561152,4817244,0.070144,1,7,7616,False,True,False,0.27403920750408217,(0.001010539602555097+0.004616804647374057j),0.0047261057373218915,1.355311797069995,0.5376433134511249,0.5812972784042358
|
||||
138,565248,4817276,0.070656,-4063,7,11712,False,True,False,0.2746479667516686,(0.0018912570769004213-0.004346463071505326j),0.004740104910535782,-1.1603806139411221,0.5378884612654133,0.5817724317312241
|
||||
139,569344,4825435,0.071168,0,7,15808,False,True,False,0.25937216918957146,(0.0011938454665440122+0.004362559508218352j),0.004522962796744413,1.3036790097834017,0.5521339003347727,0.5919571816921234
|
||||
140,573440,4829531,0.07168,0,7,19904,False,True,False,0.26774346245706554,(0.0009418111768576637+0.004568398639175929j),0.004664469360953987,1.3674868432051928,0.5443368442013263,0.5863718241453171
|
||||
141,577536,4833627,0.072192,0,7,24000,True,True,True,0.26587676777880576,(0.001102610189571028+0.004463079859444132j),0.0045972634318604925,1.3285947230861475,0.5408037074042943,0.5819414108991623
|
||||
142,581632,4837724,0.072704,1,7,28096,True,True,True,0.2619208785763954,(0.0012054341276794263+0.004364128878079499j),0.004527548155532052,1.3013016855057298,0.5421426589707972,0.582074448466301
|
||||
143,585728,4841820,0.073216,1,7,32192,True,True,True,0.26004799444738186,(0.0012996243478831987+0.004280701998500875j),0.004473637563055396,1.2760393722352807,0.5373060159033105,0.5762767046689987
|
||||
144,589824,4845916,0.073728,1,7,36288,True,True,True,0.26352682248810066,(0.0013929034499800838+0.004376577528341034j),0.004592886987782971,1.2626696720599786,0.5511815324075017,0.5923157185316086
|
||||
145,593920,4850011,0.07424,0,7,40384,True,True,True,0.27321560983225973,(0.0014136076722512999+0.004603422423890218j),0.004815577292892621,1.2728590831142508,0.560267982433693,0.605463907122612
|
||||
146,598016,4854107,0.074752,0,7,44480,True,True,True,0.2697569569525196,(0.0012948903346004814+0.004510190476376705j),0.00469239375072477,1.2912130045767223,0.5456335561563538,0.5884547084569931
|
||||
147,602112,4858203,0.075264,0,7,48576,True,True,True,0.26704062643742627,(0.0014034649430516995+0.004351442985962747j),0.004572173433549901,1.258801319004848,0.5305424443764983,0.5712808817625046
|
||||
148,606208,4862299,0.075776,0,7,52672,True,True,True,0.26110270903330773,(0.001204834540491995+0.004328357525056808j),0.004492917218765382,1.299310555739649,0.5375498470396575,0.5768783092498779
|
||||
149,610304,4866396,0.076288,1,7,56768,True,True,True,0.24788848233138613,(0.000885925455637422+0.004191896025296573j),0.004284490191358078,1.3625187593746237,0.5466587176655056,0.5824494808912277
|
||||
150,614400,4870491,0.0768,0,7,60864,True,True,True,0.2601141176285434,(0.0013182434384379415+0.004314238663366558j),0.004511144090745888,1.2742497022015684,0.5462691178352976,0.5859115123748779
|
||||
151,618496,4874587,0.077312,0,7,64960,True,True,True,0.27010121357136685,(0.0014941599628287964+0.004411796556139344j),0.004657946204850752,1.2442470656108626,0.5370899949431283,0.579356774687767
|
||||
152,622592,4878683,0.077824,0,7,69056,True,True,True,0.2758319253957052,(0.0012682927921888675+0.004576544230142604j),0.004749033975154294,1.3004519486229928,0.5343123720322084,0.5783122479915619
|
||||
153,626688,4882779,0.078336,0,7,73152,True,True,True,0.27404653150617597,(0.0011890329932240313+0.004588957075942728j),0.004740498550133744,1.3172644783717473,0.5393673486206108,0.5831638276576996
|
||||
154,630784,4886875,0.078848,0,7,77248,True,True,True,0.27119602861279707,(0.001236306317670613+0.004529799008937786j),0.004695479993833008,1.3043580041913996,0.5387397575259696,0.5815081000328064
|
||||
155,634880,4890971,0.07936,0,7,81344,True,True,True,0.27342762369718,(0.0012399300344019713+0.004586782940726171j),0.004751421285841605,1.3067799874345216,0.5439590769382545,0.5879130214452744
|
||||
156,638976,4895067,0.079872,0,7,85440,True,True,True,0.25670283077288303,(0.0014843755553559303+0.004419445796424099j),0.004662067345810103,1.246761198916183,0.5523321337147029,0.5912964046001434
|
||||
157,643072,4899163,0.080384,0,7,89536,False,True,False,0.25712482738830733,(0.0016360898784780716+0.004385459434551182j),0.00468070983319329,1.213717477491946,0.5544384543875003,0.5936891287565231
|
||||
158,647168,4913419,0.080896,10160,8,1376,False,True,False,0.2738845838199934,(-0.0015876886917470013-0.004421074468700674j),0.004697515815800758,-1.9155711352157767,0.531179403244459,0.5742559283971786
|
||||
159,651264,4913451,0.081408,6096,8,5472,False,True,False,0.2747492677176624,(-0.0013158162001726363+0.004526012931705732j),0.00471340273375873,1.853720561422047,0.5310687131217184,0.5744308680295944
|
||||
160,655360,4913483,0.08192,2032,8,9568,False,True,False,0.2747634003920657,(0.003737797470948664-0.0028737000744164056j),0.004714793956423873,-0.6554386217012346,0.5309266415904615,0.5742820203304291
|
||||
161,659456,4915547,0.082432,0,8,13664,False,True,False,0.2760058670060885,(0.0016051542012133788+0.00447398890371161j),0.004753219616239878,1.2263258552386604,0.5340496829226165,0.5780879855155945
|
||||
162,663552,4919643,0.082944,0,8,17760,False,True,False,0.27497714732101164,(0.0016332720032709552+0.004516072649607772j),0.00480234209664452,1.2237740985103753,0.5476249916746951,0.5924192517995834
|
||||
163,667648,4923739,0.083456,0,8,21856,False,True,False,0.2733575276500988,(0.0016469296756453885+0.004519756643704177j),0.004810465411457552,1.221364639004181,0.556940211069477,0.6019181460142136
|
||||
164,671744,4927835,0.083968,0,8,25952,True,True,True,0.2655826518710453,(0.0013094385268856785+0.004465689287358343j),0.004653709280447115,1.2855693847150558,0.5558054338160154,0.5979837030172348
|
||||
165,675840,4931932,0.08448,1,8,30048,True,True,True,0.2620066553332512,(0.0010008056079044434+0.0044481756529488265j),0.00455937259985403,1.3494891413311099,0.5494416678685756,0.589939534664154
|
||||
166,679936,4936027,0.084992,0,8,34144,True,True,True,0.25745610810815944,(0.0012829361577189977+0.004307185143174958j),0.004494192813216837,1.281304418017169,0.5547843706430848,0.5941679924726486
|
||||
167,684032,4940123,0.085504,0,8,38240,True,True,True,0.26383925616404263,(0.0012050656839874409+0.004426963193058868j),0.004588048214156221,1.3050253747795577,0.5481423154096086,0.5891540050506592
|
||||
168,688128,4944220,0.086016,1,8,42336,True,True,True,0.2699244067190662,(0.0008380194484942735+0.004581934378772789j),0.004657939377818761,1.389899279461403,0.5367680805238122,0.578949898481369
|
||||
169,692224,4948315,0.086528,0,8,46432,True,True,True,0.2691021030184952,(0.00121419546525523+0.0044533788553063475j),0.00461593477604873,1.304620022784614,0.53117277336095,0.5726411193609238
|
||||
170,696320,4952411,0.08704,0,8,50528,True,True,True,0.26370857056758684,(0.0015938477998613623+0.004269459998787447j),0.00455726228018193,1.213504897991644,0.5379725842213715,0.5781805366277695
|
||||
171,700416,4956507,0.087552,0,8,54624,True,True,True,0.2568596341044175,(0.001446965486859506+0.004214076097215652j),0.004455574763516697,1.240044764929582,0.5458980072328209,0.5844587683677673
|
||||
172,704512,4960604,0.088064,1,8,58720,True,True,True,0.26834124851878693,(0.0008934368200219986+0.0046038240991153915j),0.0046897148833342376,1.3791148572475473,0.5527223312153192,0.5956104695796967
|
||||
173,708608,4964700,0.088576,1,8,62816,True,True,True,0.2773340662906,(0.0009627857840257513+0.004797044213699565j),0.004892707803876148,1.372723971241416,0.560669449801557,0.60738605260849
|
||||
174,712704,4968795,0.089088,0,8,66912,True,True,True,0.2678611827980943,(0.0010743244283594811+0.0045672352206597025j),0.004691887737169807,1.3397718736689235,0.5551330463819606,0.5980423539876938
|
||||
175,716800,4972891,0.0896,0,8,71008,True,True,True,0.26521123625627346,(0.0010548052056041765+0.004463329559517832j),0.004586275698062134,1.3387272036829811,0.5424371911275525,0.5834772288799286
|
||||
176,720896,4976987,0.090112,0,8,75104,True,True,True,0.25737022850050784,(0.0011324525934055022+0.004313776409934131j),0.004459945716173578,1.314069283511442,0.5453875555595226,0.5840764492750168
|
||||
177,724992,4981083,0.090624,0,8,79200,True,True,True,0.26016421509739096,(0.0013264669173897253+0.00434301814084294j),0.004541070474526934,1.2743698025520087,0.554166359432325,0.5943984687328339
|
||||
178,729088,4985180,0.091136,1,8,83296,True,True,True,0.2783413309071134,(0.0013001352711514097+0.00466852028999491j),0.004846177196655742,1.299188582266864,0.545488141480179,0.5912983268499374
|
||||
179,733184,4989275,0.091648,0,8,87392,False,True,False,0.2669927542714062,(0.0010690162338773935+0.0047141621899569105j),0.004833851555593401,1.347800703208135,0.54517560929071,0.5870215147733688
|
||||
180,737280,4999467,0.09216,6096,8,91488,False,True,False,0.24816357519383125,(-0.0014479165006434653+0.0043169281492356215j),0.004553276934088076,1.8944096410174938,0.5657326760074871,0.6028598994016647
|
||||
181,741376,5001531,0.092672,4064,9,3328,False,True,False,0.2740896007100458,(-0.0037899232005933095-0.002977920345328503j),0.004819909485614524,-2.475603261145279,0.5575766126187574,0.602867066860199
|
||||
182,745472,5001563,0.093184,0,9,7424,False,True,False,0.2743042616798551,(0.00130453694455819+0.004636674957322557j),0.004816697135961462,1.296534520595517,0.5566299001124788,0.6019200682640076
|
||||
183,749568,5001595,0.093696,-4064,9,11520,False,True,False,0.27343656950933776,(0.0016448575650682739-0.0045140736020721755j),0.00480441639476505,-1.2213648928016885,0.5571284056004779,0.6021496653556824
|
||||
184,753664,5009755,0.094208,0,9,15616,False,True,False,0.24644837794190042,(0.0014424054288444065+0.004024460919176389j),0.004275139659840118,1.226649391143599,0.5499930625137114,0.5855579823255539
|
||||
185,757760,5013851,0.09472,0,9,19712,False,True,False,0.24879306161420675,(0.0012449191670893779+0.004102749387320414j),0.0042874673488837835,1.2761908291392956,0.5429787220660903,0.5788056254386902
|
||||
186,761856,5017947,0.095232,0,9,23808,False,True,False,0.26695567150173954,(0.0011847130863471237+0.004528474155278807j),0.004680878472251788,1.3149168858210374,0.5535382083577316,0.5960132926702499
|
||||
187,765952,5022043,0.095744,0,9,27904,True,True,True,0.2637983930091585,(0.0012155635620488801+0.004491220067611713j),0.004652811243656798,1.3064756759036986,0.5633993665169301,0.605538547039032
|
||||
188,770048,5026139,0.096256,0,9,32000,True,True,True,0.2601781313069146,(0.0014610321221578382+0.004345406927177649j),0.004584449391665322,1.2464460140416111,0.5636380757930741,0.604562520980835
|
||||
189,774144,5030236,0.096768,1,9,36096,True,True,True,0.2574682516876231,(0.001298756614798111+0.004280571292086525j),0.004473260481026915,1.276216500083633,0.5491678509292242,0.5881567001342773
|
||||
190,778240,5034331,0.09728,0,9,40192,True,True,True,0.2557230534224733,(0.001108738527750533+0.004290301714831174j),0.004431251508005581,1.3179003234342883,0.5472720788091381,0.5855646580457687
|
||||
191,782336,5038427,0.097792,0,9,44288,True,True,True,0.2591844276449496,(0.0011696742442483666+0.004405803800622378j),0.004558425711496962,1.3112975975206218,0.5608249286130765,0.6012123078107834
|
||||
192,786432,5042523,0.098304,0,9,48384,True,True,True,0.26868207485726564,(0.0011177235910650781+0.004592068204744255j),0.004726139695676223,1.3320360923306238,0.5598665867529256,0.6034280955791473
|
||||
193,790528,5046619,0.098816,0,9,52480,True,True,True,0.26639007139566734,(0.0012616732316124942+0.004464204940363573j),0.004639067265403041,1.295359831326025,0.5482883426646452,0.5901688933372498
|
||||
194,794624,5050715,0.099328,0,9,56576,True,True,True,0.2536069365130275,(0.0014990938223051057+0.004108733154170838j),0.0043736678452136685,1.220949003984714,0.5422185106395125,0.579489216208458
|
||||
195,798720,5054811,0.09984,0,9,60672,True,True,True,0.26324630262348936,(0.0014473933824976843+0.004342594477702816j),0.004577452828973017,1.2490743958972919,0.5487804343548445,0.5896417945623398
|
||||
196,802816,5058907,0.100352,0,9,64768,True,True,True,0.26746161674679997,(0.0013964631045754207+0.004455196331173598j),0.00466892745196829,1.2670499289100425,0.5509006238761466,0.5933460593223572
|
||||
197,806912,5063003,0.100864,0,9,68864,True,True,True,0.25574049545490446,(0.0013486357963794198+0.004207392214630718j),0.004418253926497562,1.2606039860007627,0.5440070331470497,0.5820767134428024
|
||||
198,811008,5067100,0.101376,1,9,72960,True,True,True,0.2629884708976966,(0.001045123129219692+0.0044137623372614765j),0.004535810878449172,1.3382912235991724,0.540570451212055,0.5807358473539352
|
||||
199,815104,5071195,0.101888,0,9,77056,True,True,True,0.2873942479114494,(0.0010623899727609538+0.004878621166164128j),0.0049929567329557,1.3563794191279657,0.538916393710259,0.5874359309673309
|
||||
200,819200,5075291,0.1024,0,9,81152,True,True,True,0.2725851962025746,(0.0010468577792493766+0.0046323056860140535j),0.004749122779909261,1.3485391685098471,0.5472674857602668,0.5911948531866074
|
||||
201,823296,5079387,0.102912,0,9,85248,True,True,True,0.2513843183766455,(0.001134941201452397+0.00439649283533871j),0.004540621188993728,1.3181647689098932,0.5467931267368271,0.583678126335144
|
||||
202,827392,5083483,0.103424,0,9,89344,False,True,False,0.26256421755141157,(0.0010627634370894722+0.0046536944780845866j),0.004773503788473327,1.3462767516536025,0.5515670240928654,0.5924075841903687
|
||||
203,831488,5091643,0.103936,4064,10,1184,False,True,False,0.27258876397618037,(-0.0038680063793312966-0.00277723489387529j),0.004761775614863231,-2.5188869491625456,0.5512196226945838,0.5954654663801193
|
||||
204,835584,5091675,0.104448,0,10,5280,False,True,False,0.27240588233562446,(0.001487311386628569+0.0045247471758307605j),0.0047629226495906314,1.2532161691794101,0.55156569647999,0.5957751721143723
|
||||
205,839680,5091707,0.10496,-4064,10,9376,False,True,False,0.2721740620175623,(0.0014356126180578048-0.00453346011489686j),0.004755338495048216,-1.264116616301169,0.5513755050294287,0.5954885333776474
|
||||
206,843776,5099867,0.105472,0,10,13472,False,True,False,0.2594300166800149,(0.0014767794994913843+0.004273745334716329j),0.004521700661933252,1.2380941194824366,0.5523710983458887,0.5922305434942245
|
||||
207,847872,5103963,0.105984,0,10,17568,False,True,False,0.2635039470208177,(0.0012883266803848526+0.004463702338228557j),0.004645904023943945,1.2898096493804307,0.5612177508783607,0.6030931174755096
|
||||
208,851968,5108059,0.106496,0,10,21664,False,True,False,0.26676893350381925,(0.0011085188613918987+0.0045316800695588055j),0.00466529080753795,1.330891881128166,0.5533470249568841,0.5957435071468353
|
||||
209,856064,5112155,0.107008,0,10,25760,True,True,True,0.26769649861033307,(0.0009767804087360958+0.004581372966701971j),0.004684343948400701,1.360734656079032,0.5538200423149755,0.5965711772441864
|
||||
210,860160,5116251,0.10752,0,10,29856,True,True,True,0.256131266937885,(0.0009048122182806056+0.00439301727572447j),0.004485229752773376,1.367670860405023,0.5571939318707251,0.5963140577077866
|
||||
211,864256,5120347,0.108032,0,10,33952,True,True,True,0.25301897115559957,(0.0010068931640645764+0.004322075987329293j),0.0044378119252722345,1.341913576472646,0.5601182547167346,0.5984288305044174
|
||||
212,868352,5124443,0.108544,0,10,38048,True,True,True,0.26696855591367613,(0.0012309215601142838+0.004492112724254102j),0.004657708086018251,1.3033429265307364,0.5505673324772662,0.5928188413381577
|
||||
213,872448,5128539,0.109056,0,10,42144,True,True,True,0.26061560705088677,(0.0014043112701381327+0.004290370017869034j),0.004514351009133707,1.2544701620679264,0.5449885148501848,0.5847017467021942
|
||||
214,876544,5132635,0.109568,0,10,46240,True,True,True,0.26336949602200077,(0.0015175076975572559+0.004303237990655464j),0.004562969079049955,1.231768689979611,0.5437184754310922,0.584243655204773
|
||||
215,880640,5136731,0.11008,0,10,50336,True,True,True,0.27464480256794577,(0.0016993485450312845+0.004501897630615793j),0.004811950514504906,1.2098585843646474,0.551037136036287,0.5959927290678024
|
||||
216,884736,5140827,0.110592,0,10,54432,True,True,True,0.27500097293793924,(0.0016943086540589032+0.004499716621939183j),0.004808131809026731,1.21067905365459,0.5508883384485461,0.5959579795598984
|
||||
217,888832,5144923,0.111104,0,10,58528,True,True,True,0.27728841349715994,(0.0014592047535882462+0.004608045616475609j),0.004833566273096357,1.264121918909197,0.5470731589989841,0.5926406234502792
|
||||
218,892928,5149019,0.111616,0,10,62624,True,True,True,0.2808954442284297,(0.001321540028381485+0.004706366969990381j),0.00488839013406571,1.2970474776506113,0.5431006966767402,0.5896233022212982
|
||||
219,897024,5153115,0.112128,0,10,66720,True,True,True,0.26688871885297244,(0.0012163594036441242+0.004456810196717522j),0.004619814642212257,1.3043634891796592,0.542377872830745,0.5839741080999374
|
||||
220,901120,5157211,0.11264,0,10,70816,True,True,True,0.2584040243452016,(0.0012297490842230225+0.00434190848764283j),0.004512698984555939,1.2947970233139183,0.5552511155037323,0.594979465007782
|
||||
221,905216,5161307,0.113152,0,10,74912,True,True,True,0.26956783335469564,(0.0014425355859659845+0.004480879786438754j),0.004707355157335554,1.2593424341663264,0.549948134696654,0.5930426567792892
|
||||
222,909312,5165403,0.113664,0,10,79008,True,True,True,0.2860522821392337,(0.0014710913905272292+0.004750496996945799j),0.004973060586527616,1.2704907946331834,0.5395488067357961,0.5876323580741882
|
||||
223,913408,5169500,0.114176,1,10,83104,True,True,True,0.28140672063874517,(0.0010721642674111451+0.00477187328708063j),0.0048908394866594195,1.3497825405392807,0.5418614433934189,0.5884615629911423
|
||||
224,917504,5173596,0.114688,1,10,87200,False,True,False,0.25867954021012346,(0.0009839169384996689+0.00453073974476785j),0.004636345023473296,1.3569519895502433,0.5357271852248144,0.5741462409496307
|
||||
225,921600,5177692,0.1152,1,10,91296,False,True,False,0.26237223676573584,(0.0011429895995709038+0.00454483339878272j),0.004686356350878383,1.3244138479151768,0.5322185693016872,0.5715646147727966
|
||||
226,925696,5181787,0.115712,0,11,3136,False,True,False,0.2701034954758276,(0.0013597724802481925+0.0044589495582259085j),0.004661674845036207,1.2748012130593906,0.5383609316350877,0.5807285010814667
|
||||
227,929792,5181819,0.116224,-4064,11,7232,False,True,False,0.26852876195676684,(0.0015255994291846465-0.00438126032616622j),0.004639277493744758,-1.235716894414013,0.5394909994438597,0.5814155340194702
|
||||
228,933888,5181851,0.116736,-8128,11,11328,False,True,False,0.26892700058895613,(-0.003820842233905499+0.0026459066180629393j),0.004647543136744965,2.535922712155432,0.5400452058880975,0.582147091627121
|
||||
229,937984,5194076,0.117248,1,11,15424,False,True,False,0.2691833879073944,(0.0010258072970440611+0.0047867337971681965j),0.004895416331183805,1.3596872888997011,0.5980976072263786,0.6448211520910263
|
||||
230,942080,5198171,0.11776,0,11,19520,False,True,False,0.27073871499175883,(0.0013708190441480024+0.004736808820083946j),0.0049311765989288915,1.289095063901871,0.5949415767398565,0.6419998109340668
|
||||
231,946176,5202267,0.118272,0,11,23616,False,True,False,0.25498334889723284,(0.0013814387836797268+0.004397449823860593j),0.0046093316290353945,1.2664131183099794,0.5954814634201167,0.6368898153305054
|
||||
232,950272,5206364,0.118784,1,11,27712,True,True,True,0.2520701625705865,(0.0009223825889995769+0.004332380078972095j),0.004429481548574711,1.3610240803390832,0.5634445223882787,0.6016745418310165
|
||||
233,954368,5210460,0.119296,1,11,31808,True,True,True,0.26164867311179046,(0.0008087107334952197+0.004453052814775219j),0.004525891340017774,1.391146134456171,0.5444633618706969,0.5844766497612
|
||||
234,958464,5214555,0.119808,0,11,35904,True,True,True,0.26854883778171956,(0.0012413561194861147+0.004505624623853556j),0.004673501713539979,1.3019536667625724,0.5487865994516878,0.5914403796195984
|
||||
235,962560,5218651,0.12032,0,11,40000,True,True,True,0.2726733173974843,(0.001378589420936903+0.004526878296162865j),0.004732138617981256,1.2751848920096938,0.5424629878076264,0.5860351324081421
|
||||
236,966656,5222747,0.120832,0,11,44096,True,True,True,0.26774424452520174,(0.0013053061679331418+0.0044299709635238094j),0.004618275319825397,1.2842518255973607,0.5379742199872226,0.579518124461174
|
||||
237,970752,5226844,0.121344,1,11,48192,True,True,True,0.2529277516354949,(0.0009143590480539627+0.004254492347760584j),0.0043516384966942635,1.359100328612116,0.5398657003944332,0.5767626166343689
|
||||
238,974848,5230940,0.121856,1,11,52288,True,True,True,0.2678507653416655,(0.0010920771787949514+0.004531790102875468j),0.0046615184329749015,1.3343235913479703,0.5483664187313746,0.5907491445541382
|
||||
239,978944,5235035,0.122368,0,11,56384,True,True,True,0.27166443355630754,(0.0013576998340728897+0.004544220909553324j),0.004742709406474656,1.2804637172622124,0.5497189797711337,0.5935218185186386
|
||||
240,983040,5239132,0.12288,1,11,60480,True,True,True,0.24298761555141254,(0.0009359177755966125+0.00412392315729343j),0.004228792296854827,1.3476281418310532,0.5537337934408951,0.5884793698787689
|
||||
241,987136,5243228,0.123392,1,11,64576,True,True,True,0.2594503125405808,(0.000872214900954877+0.004443342101533314j),0.004528139558881353,1.3769638279001095,0.5535284658088118,0.5934781283140182
|
||||
242,991232,5247323,0.123904,0,11,68672,True,True,True,0.2728972761722316,(0.0010649047350872493+0.00460220143954971j),0.004723799337927541,1.343407899747667,0.5392853326323054,0.5826791524887085
|
||||
243,995328,5251420,0.124416,1,11,72768,True,True,True,0.2677790176885852,(0.0010331007579770734+0.004533070798005724j),0.004649304037795878,1.346720423115713,0.5431229706384719,0.5850762128829956
|
||||
244,999424,5255516,0.124928,1,11,76864,True,True,True,0.27273585576465686,(0.001190185230456936+0.004606812727866582j),0.004758073600989309,1.317971325646566,0.5488263894264966,0.5929315090179443
|
||||
245,1003520,5259611,0.12544,0,11,80960,True,True,True,0.277553334130381,(0.001377950261444966+0.004616611618738488j),0.00481786776087591,1.2807377876274038,0.5415111722890744,0.5867087841033936
|
||||
246,1007616,5263707,0.125952,0,11,85056,True,True,True,0.2655589509320627,(0.0011514306146589667+0.004635249098410475j),0.004776120461702055,1.3273170531356284,0.5375551530033255,0.5783406347036362
|
||||
247,1011712,5267804,0.126464,1,11,89152,False,True,False,0.25538412849058145,(0.0009827635050397186+0.004463016645785106j),0.004569938915061435,1.3540537158216441,0.535083797776337,0.5724174678325653
|
||||
248,1015808,5280027,0.126976,8128,12,992,False,True,False,0.2812606928866462,(0.0048793151638969095+8.05644236800617e-05j),0.004879980235103132,0.016509920106764792,0.5399580759163802,0.5863421857357025
|
||||
249,1019904,5278028,0.127488,2033,12,5088,False,True,False,0.2816763742277087,(0.0041030436954555246-0.002639239528791662j),0.004878581029065087,-0.5716079390475177,0.5385550842790271,0.584967315196991
|
||||
250,1024000,5278060,0.128,-2031,12,9184,False,True,False,0.28242789552891234,(-0.004878754419270177-0.0002662660909589648j),0.004886014972832446,-3.0870700906873467,0.5376792656874811,0.5842850655317307
|
||||
251,1028096,5278091,0.128512,-6096,12,13280,False,True,False,0.2681008918313915,(0.0038474847466956337+0.0025998928860442062j),0.004643552723396047,0.5942567128822575,0.5426132080040174,0.5846357047557831
|
||||
252,1032192,5288283,0.129024,0,12,17376,False,True,False,0.24375932875272263,(0.0011357620006579774+0.0040703688113794805j),0.0042258558402753655,1.2986857372531115,0.5460942745071787,0.5805922597646713
|
||||
253,1036288,5292379,0.129536,0,12,21472,False,True,False,0.25538271104520355,(0.0011127166312888783+0.004253423429781606j),0.004396561062303354,1.3149255298493328,0.5379460291956822,0.5754789561033249
|
||||
254,1040384,5296476,0.130048,1,12,25568,True,True,True,0.2632105657612795,(0.0011581742548442148+0.0043932573571891466j),0.004543355347216472,1.313035430034399,0.5401483179505394,0.5803552120923996
|
||||
255,1044480,5300571,0.13056,0,12,29664,True,True,True,0.26986947258717187,(0.0013068274108764114+0.004486378559068552j),0.004672835376630335,1.2873514021821055,0.541962749115588,0.5845340937376022
|
||||
256,1048576,5304667,0.131072,0,12,33760,True,True,True,0.2716474398335631,(0.0014068769556635232+0.0045219013663756165j),0.004735704249169993,1.2691645381137087,0.5479715361191466,0.5916292369365692
|
||||
257,1052672,5308763,0.131584,0,12,37856,True,True,True,0.2602069168691974,(0.0014259509292018208+0.004322987510848465j),0.004552093701962135,1.2521812254494566,0.553095674227995,0.5932641923427582
|
||||
258,1056768,5312859,0.132096,0,12,41952,True,True,True,0.26067296967171716,(0.001243339607338989+0.004388924606338943j),0.004561639242564598,1.294739177254704,0.5553010262917414,0.5957848429679871
|
||||
259,1060864,5316955,0.132608,0,12,46048,True,True,True,0.2763320728115187,(0.001091005945684809+0.004723962722877519j),0.004848310817249239,1.3438244988245012,0.5541697016465323,0.5999841392040253
|
||||
260,1064960,5321051,0.13312,0,12,50144,True,True,True,0.2754645271489226,(0.0013234191848957258+0.0046175039478779465j),0.004803413478727236,1.2916694754434184,0.5464955195365455,0.591369017958641
|
||||
261,1069056,5325147,0.133632,0,12,54240,True,True,True,0.26852039912809356,(0.0015103311655884739+0.00438350483122627j),0.004636401064956728,1.2389860736762326,0.5387882672532496,0.5806553810834885
|
||||
262,1073152,5329243,0.134144,0,12,58336,True,False,False,0.12222192020312277,(0.0016917557631161454+0.0040214872795306614j),0.004362842823373725,1.172591181605333,2.4462056325581023,2.4833016842603683
|
||||
263,1077248,5333339,0.134656,0,12,62432,True,False,False,0.08371985216496043,(0.001522863605885863+0.004194885739388139j),0.004462754746639531,1.2225621889916407,5.489172343866355,5.527917593717575
|
||||
264,1081344,5337436,0.135168,1,12,66528,True,False,False,0.07678297321464417,(0.000837113473129972+0.004424337184075886j),0.004502834494548102,1.383800318183157,6.664573420147217,6.704098269343376
|
||||
265,1085440,5341532,0.13568,1,12,70624,True,False,False,0.075825225755414,(0.0010644786672962556+0.0043263854821034625j),0.0044554153760210085,1.329544959208023,6.688882765976795,6.72756265103817
|
||||
266,1089536,5345627,0.136192,0,12,74720,True,False,False,0.0777915075019332,(0.0013604196351404264+0.004361358503073744j),0.00456860915115413,1.2684347849139403,6.676964480041531,6.717616260051727
|
||||
267,1093632,5349723,0.136704,0,12,78816,True,False,False,0.0828610428066847,(0.0015486982431894794+0.004585583083988033j),0.004840045296132608,1.2450921911444341,6.60539333945425,6.651059195399284
|
||||
268,1097728,5353821,0.137216,2,12,82912,True,False,False,0.08195769883417732,(0.0011937036958979035+0.004685608387128368j),0.004835271912842961,1.3213430856688024,6.74911831950998,6.794759154319763
|
||||
269,1101824,5357917,0.137728,2,12,87008,False,False,False,0.0716654800554192,(0.0005531018437636853+0.004432103696542682j),0.004466482377272085,1.4466437471388893,6.906468290897046,6.942122623324394
|
||||
270,1105920,5366076,0.13824,4065,12,91104,False,False,False,0.07377076771462239,(-0.004091817902133142-0.0020419652606560356j),0.004573029178776726,-2.6787163810812644,6.832708365234196,6.870096296072006
|
||||
271,1110016,5374235,0.138752,8128,13,2944,False,False,False,0.08087575586757796,(0.004779806391792624+8.861341029904627e-05j),0.004780627728604942,0.018536997739119303,6.768167156707149,6.812728449702263
|
||||
272,1114112,5374267,0.139264,4064,13,7040,False,False,False,0.0800133242828991,(-0.003767808252151954-0.0028533788785221463j),0.004726325216209805,-2.4934329082951177,6.763541054221683,6.807121142745018
|
||||
273,1118208,5374299,0.139776,0,13,11136,False,False,False,0.07956224503645665,(0.0013792993209312803+0.004496803629903398j),0.004703584750234004,1.273177782321964,6.7703096256518736,6.813439726829529
|
||||
274,1122304,5378395,0.140288,0,13,15232,False,False,False,0.07909923134694494,(0.0015060370202961348+0.004398239469498638j),0.004648941593261645,1.2408918421229516,6.697100535491124,6.739266023039818
|
||||
275,1126400,5382492,0.1408,1,13,19328,False,False,False,0.07780223075074265,(0.001290172857201479+0.0043936989878511305j),0.0045792070052907054,1.2851830749053166,6.6471269868946585,6.687608331441879
|
||||
276,1130496,5386588,0.141312,1,13,23424,False,False,False,0.07864006701111693,(0.0010540703263440517+0.004463093516908906j),0.004585877014656342,1.3388713181506051,6.581861800468286,6.622819036245346
|
||||
277,1134592,5390682,0.141824,-1,13,27520,True,False,False,0.08301430893626999,(0.0015449788316282908+0.004589310350730047j),0.004842388778846393,1.2460661944160356,6.588775629410941,6.634496435523033
|
||||
278,1138688,5394779,0.142336,0,13,31616,True,False,False,0.07878097899540573,(0.001481786310700397+0.004374651467605647j),0.004618794878927114,1.24420473670389,6.651206343796035,6.692744478583336
|
||||
279,1142784,5398876,0.142848,1,13,35712,True,False,False,0.09576023729412096,(0.0010937122441491563+0.004038675938138141j),0.004184149830765837,1.3063300491750123,3.679667399162275,3.7137223184108734
|
||||
280,1146880,5402972,0.14336,1,13,39808,True,True,True,0.2383815900640343,(0.0012815346971280827+0.004158815962114253j),0.004351790595453722,1.2718803162847057,0.6127624549315758,0.6496810913085938
|
||||
281,1150976,5407067,0.143872,0,13,43904,True,True,True,0.26364871875573537,(0.0013686838317304665+0.004418645928249998j),0.004625767749301804,1.2704170494974214,0.5558803660749743,0.5974064767360687
|
||||
282,1155072,5411164,0.144384,1,13,48000,True,True,True,0.26715586396351754,(0.0012130199951223312+0.004499399727171521j),0.004660044572044107,1.3074611161349192,0.5495655838572667,0.5918039679527283
|
||||
283,1159168,5415260,0.144896,1,13,52096,True,True,True,0.25698590459791865,(0.001158258764511068+0.004307109301455813j),0.004460129359139025,1.3080933467821958,0.5475274608915194,0.5862440466880798
|
||||
284,1163264,5419356,0.145408,1,13,56192,True,True,True,0.2581379731067278,(0.001248193819057321+0.004339140607048203j),0.004515100111588618,1.2906994427415837,0.55376852676701,0.5933034271001816
|
||||
285,1167360,5423452,0.14592,1,13,60288,True,True,True,0.2530657803756539,(0.0013788577757052885+0.00419175764528281j),0.00441271808780142,1.2530004458654427,0.5521800321637115,0.5899625867605209
|
||||
286,1171456,5427547,0.146432,0,13,64384,True,True,True,0.25392783983290507,(0.001401972804627126+0.004166755137027025j),0.00439629117744209,1.246228731526822,0.5473219261437456,0.585045263171196
|
||||
287,1175552,5431643,0.146944,0,13,68480,True,True,True,0.2652653510843641,(0.0014373715911341833+0.004373403795019587j),0.004603552741665001,1.253255857245764,0.5448719137584159,0.5861142575740814
|
||||
288,1179648,5435739,0.147456,0,13,72576,True,True,True,0.26566180677487866,(0.00145395835175196+0.004343914276516279j),0.00458078444486872,1.2478058759437545,0.5376427555518017,0.5784688889980316
|
||||
289,1183744,5439835,0.147968,0,13,76672,True,True,True,0.2634471009230045,(0.0014112452051848665+0.004300735879135284j),0.004526360804359151,1.2537266677052141,0.5335782988276216,0.5733728855848312
|
||||
290,1187840,5443931,0.14848,0,13,80768,True,True,True,0.25121895961789686,(0.0013545192402851529+0.004096059693806466j),0.004314212255734364,1.2514281391921533,0.5380727033377047,0.5743184983730316
|
||||
291,1191936,5448027,0.148992,0,13,84864,True,True,True,0.24618455862048508,(0.001239173099831446+0.004247836216677644j),0.004424891241043571,1.286954298856598,0.5431489357370782,0.5781912803649902
|
||||
292,1196032,5452123,0.149504,0,13,88960,False,True,False,0.25460500594550434,(0.001456645781360352+0.004342461175011724j),0.004580260471724191,1.2471487770574143,0.5413396313714424,0.578863725066185
|
||||
293,1200128,5464347,0.150016,8128,14,800,False,True,False,0.2664892367017101,(0.004595258533890168-0.00025639798128029975j),0.004602405992314784,-0.05573840974724589,0.5403135139541736,0.581617996096611
|
||||
294,1204224,5464379,0.150528,4064,14,4896,False,True,False,0.2677067102634058,(-0.0039035142522089628-0.002474915637314424j),0.004621972623136355,-2.576531583209091,0.5393652625029224,0.5810040086507797
|
||||
295,1208320,5464411,0.15104,0,14,8992,False,True,False,0.2676224417366838,(0.0017066501910134538+0.0042936749487946044j),0.004620421998085522,1.1924641946640253,0.5397369743995677,0.5813761651515961
|
||||
296,1212416,5468507,0.151552,0,14,13088,False,True,False,0.2540449166869206,(0.0012582258203098654+0.004159380751831945j),0.004345524186286909,1.2770446405022853,0.5336992416461102,0.5705199241638184
|
||||
297,1216512,5472603,0.152064,0,14,17184,False,True,False,0.24859482152857068,(0.0012690168352510153+0.004147650472783568j),0.004337442584349937,1.2738802772540043,0.553916127141515,0.5904026478528976
|
||||
298,1220608,5476700,0.152576,1,14,21280,False,True,False,0.2558611321685786,(0.0011615097203271432+0.004337193105057625j),0.0044900277127178,1.309134118890352,0.5548674484680924,0.5937363505363464
|
||||
299,1224704,5480796,0.153088,1,14,25376,True,True,True,0.24902334455788566,(0.0011255067621460822+0.004187091354160079j),0.004335723639684458,1.3081995949229324,0.5533850609771982,0.5899706929922104
|
||||
300,1228800,5484891,0.1536,0,14,29472,True,True,True,0.2524803523265553,(0.0013228128281321033+0.004215829556643872j),0.004418489858418024,1.266752277708672,0.5565978879327824,0.5944947451353073
|
||||
301,1232896,5488987,0.154112,0,14,33568,True,True,True,0.25659362997876295,(0.001523597078719511+0.004232821106107312j),0.004498680092492703,1.2252864490022244,0.559860953567165,0.5993203818798065
|
||||
302,1236992,5493083,0.154624,0,14,37664,True,True,True,0.2701015466384452,(0.0015930439218050786+0.004434456215466593j),0.004711920082481281,1.2259117421093622,0.5500180173758105,0.5933022946119308
|
||||
303,1241088,5497179,0.155136,0,14,41760,True,True,True,0.2654883946307786,(0.0015101082344784968+0.004325150491303857j),0.0045811956574966055,1.2348826680926182,0.5382364009561323,0.5790502279996872
|
||||
304,1245184,5501275,0.155648,0,14,45856,True,True,True,0.2642378873941613,(0.0014789350860992378+0.004294330327625845j),0.004541863268710591,1.239125775840792,0.5361206526207132,0.5763632953166962
|
||||
305,1249280,5505371,0.15616,0,14,49952,True,True,True,0.2704631814372041,(0.0014297452012599925+0.004460898789483908j),0.004684419852078267,1.2606343751470939,0.5420946543757261,0.5848787277936935
|
||||
306,1253376,5509467,0.156672,0,14,54048,True,True,True,0.25862368887790793,(0.0014114606716766828+0.004290730646771298j),0.0045169227036592555,1.2529907254827484,0.5547799785112384,0.5945469737052917
|
||||
307,1257472,5513563,0.157184,0,14,58144,True,True,True,0.2537946459711735,(0.0014512878585148262+0.004152496169937046j),0.004398802210785839,1.2345690612285416,0.5482861363456426,0.5860335677862167
|
||||
308,1261568,5517659,0.157696,0,14,62240,True,True,True,0.25880367193626036,(0.0013953166604228435+0.004247498083004329j),0.004470810726029341,1.253399184203357,0.5424842444975086,0.5814279019832611
|
||||
309,1265664,5521755,0.158208,0,14,66336,True,True,True,0.25852433212459397,(0.0012096553631778682+0.004317856823054481j),0.004484100092779276,1.297646864720312,0.5464240381960734,0.5855598300695419
|
||||
310,1269760,5525851,0.15872,0,14,70432,True,True,True,0.26478919294097997,(0.0012756367113437668+0.004410535775679678j),0.004591304264354332,1.2892540359027742,0.5455173013175918,0.5866492241621017
|
||||
311,1273856,5529947,0.159232,0,14,74528,True,True,True,0.26738258733865083,(0.0014459702809501217+0.004439384043162406j),0.004668935717706549,1.2559186226601515,0.551789156057867,0.5942759960889816
|
||||
312,1277952,5534043,0.159744,0,14,78624,True,True,True,0.25761703065587765,(0.0013013793274676412+0.004291751269303245j),0.004484720405056056,1.276380664774593,0.5515944654460578,0.5908040851354599
|
||||
313,1282048,5538139,0.160256,0,14,82720,True,True,True,0.2594854532357755,(0.001014695960330343+0.004360357591237143j),0.004476865668676032,1.3421562446445414,0.539021821863747,0.5779357999563217
|
||||
314,1286144,5542236,0.160768,1,14,86816,False,True,False,0.25534036090986695,(0.0010348878449817414+0.004494382765061006j),0.004611991900531523,1.3444786345884008,0.5423552649187608,0.5801824033260345
|
||||
315,1290240,5546331,0.16128,0,14,90912,False,True,False,0.24958348378421272,(0.0015809884356665748+0.004215168793332553j),0.004501907638989949,1.2119632322931633,0.5466115493167396,0.5829229354858398
|
||||
316,1294336,5552459,0.161792,2032,15,2752,False,True,False,0.2653891462384448,(0.0037756615455502513-0.0024862762863175044j),0.004520751030354544,-0.5823280105725755,0.5254411813925184,0.5652527213096619
|
||||
317,1298432,5552491,0.162304,-2032,15,6848,False,True,False,0.2657521082769784,(-0.004521714802829172-0.00017802384287181412j),0.0045252179225707245,-3.1022421077858477,0.5259034994737588,0.5658674240112305
|
||||
318,1302528,5552523,0.162816,-6096,15,10944,False,True,False,0.2648650947094851,(0.0035480581267110073+0.00278413095489372j),0.004510000182319003,0.665336521026977,0.5263289531903287,0.5660385489463806
|
||||
319,1306624,5562715,0.163328,0,15,15040,False,True,False,0.2620496140457573,(0.001531028169564713+0.004261039621478605j),0.004527748437337394,1.2258529584701106,0.5415241879914068,0.5814525336027145
|
||||
320,1310720,5566812,0.16384,1,15,19136,False,True,False,0.26453769584680464,(0.0011282515653286331+0.00448029271963071j),0.0046201703916893,1.32410036700869,0.547386468762953,0.5885750651359558
|
||||
321,1314816,5570908,0.164352,1,15,23232,False,True,False,0.2602922116740193,(0.0010637771205495672+0.004353568314823194j),0.004481649119915256,1.3311460884082906,0.5387595665150288,0.5779144465923309
|
||||
322,1318912,5575003,0.164864,0,15,27328,True,True,True,0.2646714154148712,(0.001350060593372117+0.004299112239390732j),0.004506110257489876,1.2665159901569734,0.5260034771882566,0.5656261295080185
|
||||
323,1323008,5579099,0.165376,0,15,31424,True,True,True,0.2675197135922362,(0.001229299289273+0.004407258670649587j),0.004575489671349184,1.2987837668896933,0.5257819233489514,0.5663109868764877
|
||||
324,1327104,5583195,0.165888,0,15,35520,True,True,True,0.2527301679389408,(0.0011438527326352401+0.004195144440594359j),0.00434829115301711,1.3046058624948298,0.5373194205840789,0.5739810466766357
|
||||
325,1331200,5587291,0.1664,0,15,39616,True,True,True,0.24252120082883175,(0.0013404286467247842+0.003997623218501677j),0.004216365775648994,1.2472710654732522,0.5534920044503024,0.588080883026123
|
||||
326,1335296,5591387,0.166912,0,15,43712,True,True,True,0.2571775457631954,(0.0015098749844689487+0.004185489604321386j),0.0044494994883253446,1.2245854919847523,0.5440344222728948,0.5825654715299606
|
||||
327,1339392,5595483,0.167424,0,15,47808,True,True,True,0.26432524091011667,(0.0014117994702806471+0.004327530007223564j),0.004551998869475376,1.2554462062864538,0.5386840960050098,0.5791479051113129
|
||||
328,1343488,5599579,0.167936,0,15,51904,True,True,True,0.2621376516183847,(0.0013181958784353406+0.004343836003591089j),0.004539443974763663,1.2761650037093621,0.544478221527759,0.5846533477306366
|
||||
329,1347584,5603675,0.168448,0,15,56000,True,True,True,0.2602155447030736,(0.0014567732237749647+0.004242818646846353j),0.004485944526577967,1.2400577194321012,0.5383806447420117,0.5774832665920258
|
||||
330,1351680,5607772,0.16896,1,15,60096,True,True,True,0.26420496559852147,(0.0012353303830398529+0.004402993922998593j),0.00457300739571061,1.2972628513414242,0.5426832718703825,0.5834076106548309
|
||||
331,1355776,5611867,0.169472,0,15,64192,True,True,True,0.2645956652886718,(0.0013486633031770535+0.004399815742174559j),0.004601876929082688,1.2733620636596512,0.5481978807485378,0.5894669741392136
|
||||
332,1359872,5615963,0.169984,0,15,68288,True,True,True,0.27635966275564405,(0.001538395327785914+0.004518762326779053j),0.004773455053781399,1.2426579370597932,0.5369393073322216,0.5813388675451279
|
||||
333,1363968,5620060,0.170496,1,15,72384,True,True,True,0.28065909499300684,(0.0013899322538439272+0.004688437803033093j),0.004890128904557162,1.2825907901516267,0.5446156770475633,0.5911828726530075
|
||||
334,1368064,5624155,0.171008,0,15,76480,True,True,True,0.2787902846792729,(0.001505395943641435+0.004657137912448599j),0.0048943999103769785,1.2581526999750539,0.5536297896520709,0.6002864688634872
|
||||
335,1372160,5628251,0.17152,0,15,80576,True,True,True,0.2782403905799784,(0.0013851585328944899+0.004671326949604034j),0.004872366943421579,1.2825319635019046,0.549059815247206,0.5951337069272995
|
||||
336,1376256,5632347,0.172032,0,15,84672,True,True,True,0.25835564575952363,(0.0014878867435889247+0.004418020064904829j),0.004661835288343993,1.2459498434659404,0.5424659481705443,0.5812639445066452
|
||||
337,1380352,5636443,0.172544,0,15,88768,False,True,False,0.2661307059301699,(0.001742326334063677+0.0044784482607504j),0.00480543441101739,1.1997678069313928,0.5420525528553599,0.5833700597286224
|
||||
338,1384448,5650699,0.173056,10160,16,608,False,True,False,0.2807983627799369,(-0.0014132177560300736-0.004678753858631776j),0.0048875271964072394,-1.864132822427261,0.5440899108503512,0.5906622856855392
|
||||
339,1388544,5650731,0.173568,6096,16,4704,False,True,False,0.28175733500795064,(-0.0016292651991435913+0.00462401096559049j),0.004902650558528672,1.909562177118072,0.5438451356478441,0.5907425284385681
|
||||
340,1392640,5650763,0.17408,2032,16,8800,False,True,False,0.2810896310741443,(0.004032377747532641-0.0027645120137144447j),0.00488902822376471,-0.6009814789944031,0.5438568421206966,0.5905141830444336
|
||||
341,1396736,5652827,0.174592,0,16,12896,False,True,False,0.27995892582994847,(0.0013644086381278274+0.004682448638769451j),0.00487718527293271,1.287259194961097,0.5457013503526119,0.5921090841293335
|
||||
342,1400832,5656923,0.175104,0,16,16992,False,True,False,0.2694191676957321,(0.0012987608029786238+0.004501592194187054j),0.004685201373059593,1.289912543456448,0.5464975984850089,0.5892708152532578
|
||||
343,1404928,5661020,0.175616,1,16,21088,False,True,False,0.2724290212387798,(0.0013557614569333096+0.004514417086428908j),0.004713602736585594,1.2790476630851628,0.5394092203654874,0.5826522558927536
|
||||
344,1409024,5665116,0.176128,1,16,25184,True,True,True,0.2703439033029992,(0.0015023504482026882+0.004452830832832162j),0.004699442445124271,1.2453972397154547,0.5445303148943486,0.5874657332897186
|
||||
345,1413120,5669212,0.17664,1,16,29280,True,True,True,0.26084853462192703,(0.0015064276691071788+0.0042858697602228745j),0.004542906990446158,1.2327973663021916,0.5488662670965079,0.5889388173818588
|
||||
346,1417216,5673307,0.177152,0,16,33376,True,True,True,0.26680859121689254,(0.0017233605136990984+0.004346323777514884j),0.004675521557983502,1.1933022514222584,0.5563818107259524,0.5990244597196579
|
||||
347,1421312,5677403,0.177664,0,16,37472,True,True,True,0.28118617001285623,(0.0017648563265804532+0.004599738199242859j),0.004926693551972255,1.2044321407102385,0.5511238823654319,0.5984399318695068
|
||||
348,1425408,5681499,0.178176,0,16,41568,True,True,True,0.2765548104730267,(0.0017933494122994718+0.004409004550129239j),0.004759771343001174,1.1844871287004388,0.5314170018017368,0.5754271447658539
|
||||
349,1429504,5685595,0.178688,0,16,45664,True,True,True,0.2665701091804682,(0.0016608712100729577+0.004276843269699999j),0.00458801498799071,1.200381524244915,0.5360344693741199,0.5770386159420013
|
||||
350,1433600,5689691,0.1792,0,16,49760,True,True,True,0.27603880819109367,(0.001647519716432292+0.00448414294414518j),0.004777222954767775,1.218696301508401,0.5400274148415931,0.5845701545476913
|
||||
351,1437696,5693787,0.179712,0,16,53856,True,True,True,0.27423717356605604,(0.0018811367461085812+0.004391660175502362j),0.004777588780405176,1.1660975677898966,0.5460581921807042,0.5904646962881088
|
||||
352,1441792,5697883,0.180224,0,16,57952,True,True,True,0.26939315416499676,(0.001895787593627749+0.004261155598661815j),0.004663845798926014,1.1521917534315318,0.5405786387936542,0.5828797817230225
|
||||
353,1445888,5701979,0.180736,0,16,62048,True,True,True,0.2704970887722574,(0.0018084369666119368+0.004265944201606953j),0.004633435463393503,1.1698372476397976,0.5303428144210389,0.5722107142210007
|
||||
354,1449984,5706075,0.181248,0,16,66144,True,True,True,0.2631123469104774,(0.0014536311531551688+0.004261454370101405j),0.004502558925531079,1.2420618201093678,0.532675277665522,0.5722941160202026
|
||||
355,1454080,5710171,0.18176,0,16,70240,True,True,True,0.266366325753132,(0.0013174392407077908+0.004407645976290434j),0.004600324902141802,1.2803502910288584,0.5411182266002784,0.5824431627988815
|
||||
356,1458176,5714267,0.182272,0,16,74336,True,True,True,0.27057933254212896,(0.0013928641345691333+0.004495997771867698j),0.004706810646500274,1.2703723153640838,0.5461706870280945,0.5893164128065109
|
||||
357,1462272,5718363,0.182784,0,16,78432,True,True,True,0.2670809726675467,(0.0012795950443333676+0.004403007790597894j),0.004585176232332653,1.2879685794606828,0.5334450177163121,0.5744196623563766
|
||||
358,1466368,5722459,0.183296,0,16,82528,True,True,True,0.26574562793667467,(0.0016406126164279962+0.004275302399884901j),0.004579281643188625,1.2043836478611014,0.5377712923688862,0.5786349177360535
|
||||
|
BIN
data/processed/lab063/local_delay_vs_time.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
data/processed/lab063/matched_residual_by_packet.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
data/processed/lab063/normalized_correlation_vs_time.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
17
data/processed/lab063/packet_quality.csv
Normal file
@@ -0,0 +1,17 @@
|
||||
packet_index,raw_power_code2,rms_full_iq_code,mean_i,mean_q,ac_power_code2,ac_rms_code,max_abs_i,max_abs_q,max_complex_magnitude,background_ac_power_code2,background_subtracted_useful_power_code2,symbol_error_rate,symbol_rms_deviation,matched_component_power,residual_power,matched_residual_db,band_gain_nonuniformity_db,initial_frequency_hz,diagnostic_residual_phase_error_rad,local_delay_median_samples,local_delay_min_samples,local_delay_max_samples,local_correlation_median,local_correlation_min,local_gain_amplitude_median,local_phase_unwrapped_span_rad,ser_minus_lab056_b,evm_minus_lab056_b,matched_residual_minus_lab056_b_db,ripple_minus_lab056_b_db
|
||||
1,4.900591831425598,2.2137280391740983,0.9692702913631633,1.8366285119667014,0.5879026427373475,0.7667480960115568,3.0,4.0,4.4721360206604,0.5252822403066744,0.06262040243067313,0.08517887563884156,1.088322316906538,0.7075133679013462,0.8380110003749788,-0.7351506962803888,6.642520146946069,1.0073017677042053,5.396055347590577,0.0,0,1,0.2720544529364392,0.2556440262627536,0.004692917533594935,0.120558278787813,0.05021991563884157,0.24297231690653787,-2.199996696280389,1.1711381469460687
|
||||
2,4.865146982310094,2.2057078188894588,0.9677527748872702,1.8294528269164065,0.5817039030956508,0.7626951573831126,3.0,4.0,4.4721360206604,0.5252822403066744,0.05642166278897642,0.09795570698466781,1.104921348985033,0.6708739973434846,0.8190373162813612,-0.8666272994726886,6.598551357893342,1.6416990453424165,0.9116196756938657,0.0,0,1,0.2633530926845802,0.2539878786554866,0.0045493907778025216,0.0709144214354922,0.06299674698466781,0.25957134898503287,-2.3314732994726888,1.127169357893342
|
||||
3,6.015186004162331,2.4525876139625127,0.9700940860215054,1.8342655220256676,1.7095734631363353,1.3075065824447445,6.0,7.0,7.211102485656738,0.5252822403066744,1.184291222829661,0.10221465076660988,1.3578439728375573,0.6181275875589448,1.1396667156434608,-2.6569973778482696,7.370968795797246,-0.9619877262845884,0.9174219316762064,0.0,0,1,0.2672883844824355,0.24083000237735547,0.004581545172098038,0.15725486354106955,0.06725569076660988,0.5124939728375573,-4.12184337784827,1.899586795797246
|
||||
4,8.462127124523066,2.9089735517056643,0.9603494623655914,1.846362296219216,4.130802305757289,2.032437528131502,6.0,7.0,7.6157732009887695,0.5252822403066744,3.6055200654506145,0.15587734241908008,1.6634180909816034,0.694252592647496,1.920968976998595,-4.420028409350613,10.617549420778396,2.5845496302438504,5.388297118381418,0.0,0,0,0.2731302498054202,0.1663171407536924,0.004805263148615891,0.12094876972869106,0.12091838241908008,0.8180680909816034,-5.884874409350614,5.146167420778395
|
||||
5,4.954615417967395,2.2258965425121167,0.9645117932708983,1.8537439299340963,0.5879658608412441,0.7667893197229889,3.0,4.0,4.242640495300293,0.5252822403066744,0.06268362053456977,0.08986371379897785,1.0991964913376657,0.7021817411712009,0.8483991001186207,-0.8215066687796163,6.409725568268307,-1.2358463102217572,0.8342172701371073,0.0,0,1,0.26644916295862675,0.2537591241878097,0.0046666418592819555,0.09866723940073996,0.05490475379897786,0.2538464913376657,-2.2863526687796165,0.9383435682683068
|
||||
6,4.919647502601457,2.218027840808464,0.968934269857787,1.8444437218175511,0.5788412403444356,0.7608161672470135,3.0,4.0,4.4721360206604,0.5252822403066744,0.05355900003776126,0.075809199318569,1.1024024281507157,0.6444662106996041,0.783214058873921,-0.8468032228257232,6.3642932090088,-1.7795429995488667,5.318131944986163,0.0,0,1,0.2655258469985231,0.25666966629660665,0.004581829702937256,0.20674702332927808,0.040850239318569,0.2570524281507156,-2.3116492228257233,0.8929112090087994
|
||||
7,4.958322493929934,2.226729102053039,0.9635145681581686,1.856150277488727,0.5846683182554333,0.7646360691567154,3.0,4.0,4.4721360206604,0.5252822403066744,0.05938607794875894,0.08390119250425894,1.1001519668540132,0.6819353341348239,0.8253697594999119,-0.8290535905764504,6.236828217134885,-0.2484094971075878,1.4617535496008514,0.0,0,1,0.266458697108116,0.24788848233138613,0.004627604818355622,0.11827169376376112,0.04894223250425894,0.25480196685401313,-2.2938995905764505,0.765446217134885
|
||||
8,4.913306451612903,2.2165979454138505,0.9589186611168922,1.8453433922996878,0.5884892174705705,0.7671305087601265,3.0,4.0,4.4721360206604,0.5252822403066744,0.06320697716389612,0.08262350936967632,1.1492572709962479,0.6381478875618168,0.8428608001594871,-1.2083452015652267,6.714920606410644,-1.0783628659018663,0.78177232042639,0.0,0,1,0.26521123625627346,0.2568596341044175,0.004588048214156221,0.17639438146975883,0.04766454936967632,0.3039072709962478,-2.673191201565227,1.2435386064106435
|
||||
9,4.8869558619493585,2.2106460281893523,0.9541276448144294,1.840042924037461,0.5908383370498989,0.7686600920107007,3.0,4.0,4.4721360206604,0.5252822403066744,0.06555609674322449,0.08943781942078365,1.141830995657666,0.6581222468886276,0.858045321706861,-1.1520365600725753,6.809131520732102,0.8322819250764986,1.0738651986809553,0.0,0,1,0.2629884708976966,0.2513843183766455,0.004577452828973017,0.13543041514325171,0.05447885942078365,0.29648099565766595,-2.6168825600725754,1.3377495207321015
|
||||
10,4.915355098855359,2.2170600124614035,0.9478299514394728,1.850437911897329,0.5928530162226577,0.769969490449237,3.0,4.0,4.242640495300293,0.5252822403066744,0.06757077591598337,0.08347529812606473,1.0923957700987015,0.6971368095977797,0.8319132362097249,-0.7676001965024029,6.678161163217084,-1.3378733037888852,0.7703154441715622,0.0,0,1,0.26769649861033307,0.25301897115559957,0.004684343948400701,0.1578122760403755,0.048516338126064734,0.24704577009870143,-2.232446196502403,1.2067791632170835
|
||||
11,4.919918487686438,2.218088926911281,0.9430389351370101,1.852898456468956,0.5973633645170526,0.7728928544869933,3.0,4.0,4.4721360206604,0.5252822403066744,0.07208112421037827,0.08049403747870527,1.0853439130113745,0.6882509611422404,0.8107399547939167,-0.7113474983787614,6.630624104636567,1.1396956047405093,0.8869440636679229,1.0,0,1,0.2677790176885852,0.24298761555141254,0.0046615184329749015,0.11596124244647732,0.04553507747870528,0.2399939130113744,-2.1761934983787614,1.1592421046365669
|
||||
12,6.8007067291016305,2.607816467679739,0.9549947970863684,1.8480749219562955,2.4733107494758295,1.5726763015559908,6.0,7.0,8.062257766723633,0.5252822403066744,1.9480285091691552,0.12819420783645655,1.4566037803957919,0.6313306634190563,1.3394908423846723,-3.2668286553130113,7.945031576922354,-0.15870982770940278,1.0850946010670803,0.0,0,1,0.2691949358576327,0.2602069168691974,0.004654618220793532,0.10483842514826858,0.09323524783645655,0.6112537803957918,-4.731674655313011,2.4736495769223534
|
||||
13,7.57159425945196,2.751653004913948,0.9523066250433576,1.8468283905653833,3.253931247152167,1.8038656399943336,6.0,7.0,7.810249328613281,0.5252822403066744,2.7286490068454925,0.16013628620102216,1.542471770787923,0.6706715317796331,1.5956745609431342,-3.764344492637608,9.871896166053979,-3.768747766207012,1.4778689507229945,0.0,0,1,0.25756193885232326,0.2383815900640343,0.004487614735363821,0.0618646152553739,0.12517732620102218,0.6971217707879229,-5.229190492637608,4.400514166053979
|
||||
14,4.931039715574054,2.220594450946425,0.9521657127991675,1.8542859001040584,0.5860439716189889,0.7655350884309542,3.0,4.0,4.4721360206604,0.5252822403066744,0.06076173131231455,0.09454855195911414,1.1250423245117716,0.6318534852320092,0.7997497398817571,-1.0233772215014318,6.450865575756282,0.07157023489205856,0.8500145465030344,0.0,0,1,0.25880367193626036,0.24902334455788566,0.004498680092492703,0.11686979564231703,0.059589591959114147,0.2796923245117715,-2.488223221501432,0.9794835757562819
|
||||
15,4.914639698231009,2.2168986666582233,0.9496509712105445,1.8523890045091918,0.5814577070833247,0.7625337416031664,3.0,4.0,4.4721360206604,0.5252822403066744,0.05617546677665031,0.09284497444633731,1.1566134639773282,0.6485174305492952,0.8675572440266134,-1.2637648711797032,7.550266963349408,0.6368695507363243,5.326810530921518,0.0,0,1,0.26432524091011667,0.24252120082883175,0.00457300739571061,0.08002037051007749,0.057886014446337314,0.3112634639773282,-2.7286108711797032,2.0788849633494078
|
||||
16,4.976760319112036,2.2308653744930544,0.956902532084634,1.864615851543531,0.5843055893746447,0.7643988418192722,3.0,4.0,4.4721360206604,0.5252822403066744,0.059023349067970354,0.08603066439522998,1.063656865382626,0.7029467258520877,0.7952899743189897,-0.5360309519366353,6.3916733554839595,1.3365576309608829,0.7414410771269146,0.0,0,1,0.26939315416499676,0.26084853462192703,0.004663845798926014,0.13577682602915098,0.05107170439522998,0.2183068653826259,-2.000876951936635,0.9202913554839594
|
||||
|
BIN
data/processed/lab063/phase_vs_time.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
data/processed/lab063/received_power_by_packet.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
951
data/processed/lab063/summary.json
Normal file
@@ -0,0 +1,951 @@
|
||||
{
|
||||
"lab": "Lab063",
|
||||
"status": "COMPLETED_OFFLINE",
|
||||
"rf_tx": 0,
|
||||
"hardware_queries": 0,
|
||||
"lab062_status_preserved": "RF_SESSION_INVALID_TX_UNDERRUN",
|
||||
"lab062_commit": "f8ae6c6bfceaa21b6c08dcf55d2bff996dd0dae5",
|
||||
"physical_device": "Auron HackRF Plus",
|
||||
"api_firmware_identification": "HackRF One r9",
|
||||
"integrity": {
|
||||
"tx_sha256": "e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5",
|
||||
"rx_sha256": "ce0bd603e4ca356af67d504ee11c9e9f38171f31c7427fbe61216a28126b2b32",
|
||||
"tx_samples": 1476096,
|
||||
"rx_samples": 24000000
|
||||
},
|
||||
"actual_inter_packet_gap": {
|
||||
"tx_samples": 1476096,
|
||||
"packets": 16,
|
||||
"packet_waveform_samples": 92256,
|
||||
"packet_product_samples": 1476096,
|
||||
"identity_confirmed": true,
|
||||
"additional_inter_packet_gap_samples": 0,
|
||||
"additional_inter_packet_gap_seconds": 0.0,
|
||||
"actual": "0 additional samples (no planned 5 ms gaps)",
|
||||
"reason": "IMPLEMENTATION_OMISSION: prepare() writes the 16 complete packet waveforms back-to-back and has no gap parameter or zero-fill operation",
|
||||
"packet_internal_note": "Each packet waveform still contains its own guards and RRC tails"
|
||||
},
|
||||
"tx_underrun_scale": {
|
||||
"bytes": 16672,
|
||||
"complex_samples": 8336,
|
||||
"duration_seconds": 0.001042,
|
||||
"duration_milliseconds": 1.042,
|
||||
"symbols": 521.0,
|
||||
"fraction_of_packet": 0.09035726673603885,
|
||||
"percent_of_packet": 9.035726673603884,
|
||||
"interpretation": "Scale only; RX does not prove that this many RF samples were lost or repeated"
|
||||
},
|
||||
"tx_underrun_log": {
|
||||
"binary_path": "C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"binary_exists": true,
|
||||
"local_source_files": [],
|
||||
"progress_line": "1 underruns, longest 16672 bytes",
|
||||
"final_line": "0 underruns, longest 0 bytes",
|
||||
"counter_semantics": "NOT_ESTABLISHED",
|
||||
"note": "No local source or documentation was found that explains the reset between progress and final counters"
|
||||
},
|
||||
"background": {
|
||||
"before": {
|
||||
"raw_power_code2": 4.867889351370101,
|
||||
"rms_full_iq_code": 2.2063293841514464,
|
||||
"mean_i": 0.9631677072493929,
|
||||
"mean_q": 1.8471644120707595,
|
||||
"ac_power_code2": 0.5281809538613338,
|
||||
"ac_rms_code": 0.7267605890947402,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.123105525970459
|
||||
},
|
||||
"after": {
|
||||
"raw_power_code2": 4.852031304197017,
|
||||
"rms_full_iq_code": 2.202732690136735,
|
||||
"mean_i": 0.9442421089143254,
|
||||
"mean_q": 1.8541991848768644,
|
||||
"ac_power_code2": 0.5223835267520149,
|
||||
"ac_rms_code": 0.7227610440194013,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604
|
||||
},
|
||||
"ac_power_code2_used": 0.5252822403066744,
|
||||
"preflight_ac_rms_code": 0.9854828456229909
|
||||
},
|
||||
"local_alignment": {
|
||||
"valid_windows": 221,
|
||||
"total_windows": 359,
|
||||
"minimum_correlation": 0.0716654800554192,
|
||||
"median_correlation": 0.2655258469985231,
|
||||
"maximum_correlation": 0.2873942479114494,
|
||||
"delay_min_samples": 0,
|
||||
"delay_median_samples": 0.0,
|
||||
"delay_max_samples": 1,
|
||||
"jump_candidates": [],
|
||||
"local_delay_discontinuity": false,
|
||||
"local_delay_discontinuity_scope": "NO_IN_221_RELIABLE_WINDOWS; UNDETERMINED_IN_22_INTERNAL_LOW_CORRELATION_WINDOWS",
|
||||
"underrun_location": "NOT_LOCALIZED_FROM_RX",
|
||||
"reliable_region_inside_packet_samples": [
|
||||
24000,
|
||||
86000
|
||||
],
|
||||
"correlation_threshold": 0.15,
|
||||
"internal_low_correlation_windows": {
|
||||
"count": 22,
|
||||
"packet_counts": {
|
||||
"3": 5,
|
||||
"4": 7,
|
||||
"12": 7,
|
||||
"13": 3
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"window_index": 62,
|
||||
"packet_index": 3,
|
||||
"tx_sample": 253952,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4510043,
|
||||
"position_inside_packet_samples": 69440,
|
||||
"normalized_correlation": 0.13153433429624267
|
||||
},
|
||||
{
|
||||
"window_index": 63,
|
||||
"packet_index": 3,
|
||||
"tx_sample": 258048,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4514139,
|
||||
"position_inside_packet_samples": 73536,
|
||||
"normalized_correlation": 0.08888879507126439
|
||||
},
|
||||
{
|
||||
"window_index": 64,
|
||||
"packet_index": 3,
|
||||
"tx_sample": 262144,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4518235,
|
||||
"position_inside_packet_samples": 77632,
|
||||
"normalized_correlation": 0.07990273751817419
|
||||
},
|
||||
{
|
||||
"window_index": 65,
|
||||
"packet_index": 3,
|
||||
"tx_sample": 266240,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4522332,
|
||||
"position_inside_packet_samples": 81728,
|
||||
"normalized_correlation": 0.07824125225628915
|
||||
},
|
||||
{
|
||||
"window_index": 66,
|
||||
"packet_index": 3,
|
||||
"tx_sample": 270336,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4526428,
|
||||
"position_inside_packet_samples": 85824,
|
||||
"normalized_correlation": 0.07587440188611191
|
||||
},
|
||||
{
|
||||
"window_index": 74,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 303104,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4559195,
|
||||
"position_inside_packet_samples": 26336,
|
||||
"normalized_correlation": 0.08726659571551212
|
||||
},
|
||||
{
|
||||
"window_index": 75,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 307200,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4563291,
|
||||
"position_inside_packet_samples": 30432,
|
||||
"normalized_correlation": 0.07303287114951702
|
||||
},
|
||||
{
|
||||
"window_index": 76,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 311296,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4567387,
|
||||
"position_inside_packet_samples": 34528,
|
||||
"normalized_correlation": 0.07923837125521409
|
||||
},
|
||||
{
|
||||
"window_index": 77,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 315392,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4571483,
|
||||
"position_inside_packet_samples": 38624,
|
||||
"normalized_correlation": 0.08766369642748506
|
||||
},
|
||||
{
|
||||
"window_index": 78,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 319488,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4575579,
|
||||
"position_inside_packet_samples": 42720,
|
||||
"normalized_correlation": 0.08111151553103035
|
||||
},
|
||||
{
|
||||
"window_index": 79,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 323584,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4579675,
|
||||
"position_inside_packet_samples": 46816,
|
||||
"normalized_correlation": 0.10671176630261674
|
||||
},
|
||||
{
|
||||
"window_index": 81,
|
||||
"packet_index": 4,
|
||||
"tx_sample": 331776,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 4587867,
|
||||
"position_inside_packet_samples": 55008,
|
||||
"normalized_correlation": 0.13566484987135102
|
||||
},
|
||||
{
|
||||
"window_index": 262,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1073152,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5329243,
|
||||
"position_inside_packet_samples": 58336,
|
||||
"normalized_correlation": 0.12222192020312277
|
||||
},
|
||||
{
|
||||
"window_index": 263,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1077248,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5333339,
|
||||
"position_inside_packet_samples": 62432,
|
||||
"normalized_correlation": 0.08371985216496043
|
||||
},
|
||||
{
|
||||
"window_index": 264,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1081344,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5337436,
|
||||
"position_inside_packet_samples": 66528,
|
||||
"normalized_correlation": 0.07678297321464417
|
||||
},
|
||||
{
|
||||
"window_index": 265,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1085440,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5341532,
|
||||
"position_inside_packet_samples": 70624,
|
||||
"normalized_correlation": 0.075825225755414
|
||||
},
|
||||
{
|
||||
"window_index": 266,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1089536,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5345627,
|
||||
"position_inside_packet_samples": 74720,
|
||||
"normalized_correlation": 0.0777915075019332
|
||||
},
|
||||
{
|
||||
"window_index": 267,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1093632,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5349723,
|
||||
"position_inside_packet_samples": 78816,
|
||||
"normalized_correlation": 0.0828610428066847
|
||||
},
|
||||
{
|
||||
"window_index": 268,
|
||||
"packet_index": 12,
|
||||
"tx_sample": 1097728,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5353821,
|
||||
"position_inside_packet_samples": 82912,
|
||||
"normalized_correlation": 0.08195769883417732
|
||||
},
|
||||
{
|
||||
"window_index": 277,
|
||||
"packet_index": 13,
|
||||
"tx_sample": 1134592,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5390682,
|
||||
"position_inside_packet_samples": 27520,
|
||||
"normalized_correlation": 0.08301430893626999
|
||||
},
|
||||
{
|
||||
"window_index": 278,
|
||||
"packet_index": 13,
|
||||
"tx_sample": 1138688,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5394779,
|
||||
"position_inside_packet_samples": 31616,
|
||||
"normalized_correlation": 0.07878097899540573
|
||||
},
|
||||
{
|
||||
"window_index": 279,
|
||||
"packet_index": 13,
|
||||
"tx_sample": 1142784,
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": 5398876,
|
||||
"position_inside_packet_samples": 35712,
|
||||
"normalized_correlation": 0.09576023729412096
|
||||
}
|
||||
],
|
||||
"interpretation": "Delay is not established in these windows; low correlation is itself a local anomaly, but it does not identify an underrun without a reliable timing discontinuity"
|
||||
},
|
||||
"excluded_region_reason": "Packet edges and repeated PRBS preamble have correlation aliases spaced by 2032 samples; 22 additional internal windows are below the 0.15 correlation threshold. All are retained in CSV, and delay is not asserted for any excluded window"
|
||||
},
|
||||
"packet_quality": [
|
||||
{
|
||||
"packet_index": 1,
|
||||
"raw_power_code2": 4.900591831425598,
|
||||
"rms_full_iq_code": 2.2137280391740983,
|
||||
"mean_i": 0.9692702913631633,
|
||||
"mean_q": 1.8366285119667014,
|
||||
"ac_power_code2": 0.5879026427373475,
|
||||
"ac_rms_code": 0.7667480960115568,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.06262040243067313,
|
||||
"symbol_error_rate": 0.08517887563884156,
|
||||
"symbol_rms_deviation": 1.088322316906538,
|
||||
"matched_component_power": 0.7075133679013462,
|
||||
"residual_power": 0.8380110003749788,
|
||||
"matched_residual_db": -0.7351506962803888,
|
||||
"band_gain_nonuniformity_db": 6.642520146946069,
|
||||
"initial_frequency_hz": 1.0073017677042053,
|
||||
"diagnostic_residual_phase_error_rad": 5.396055347590577,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2720544529364392,
|
||||
"local_correlation_min": 0.2556440262627536,
|
||||
"local_gain_amplitude_median": 0.004692917533594935,
|
||||
"local_phase_unwrapped_span_rad": 0.120558278787813,
|
||||
"ser_minus_lab056_b": 0.05021991563884157,
|
||||
"evm_minus_lab056_b": 0.24297231690653787,
|
||||
"matched_residual_minus_lab056_b_db": -2.199996696280389,
|
||||
"ripple_minus_lab056_b_db": 1.1711381469460687
|
||||
},
|
||||
{
|
||||
"packet_index": 2,
|
||||
"raw_power_code2": 4.865146982310094,
|
||||
"rms_full_iq_code": 2.2057078188894588,
|
||||
"mean_i": 0.9677527748872702,
|
||||
"mean_q": 1.8294528269164065,
|
||||
"ac_power_code2": 0.5817039030956508,
|
||||
"ac_rms_code": 0.7626951573831126,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.05642166278897642,
|
||||
"symbol_error_rate": 0.09795570698466781,
|
||||
"symbol_rms_deviation": 1.104921348985033,
|
||||
"matched_component_power": 0.6708739973434846,
|
||||
"residual_power": 0.8190373162813612,
|
||||
"matched_residual_db": -0.8666272994726886,
|
||||
"band_gain_nonuniformity_db": 6.598551357893342,
|
||||
"initial_frequency_hz": 1.6416990453424165,
|
||||
"diagnostic_residual_phase_error_rad": 0.9116196756938657,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2633530926845802,
|
||||
"local_correlation_min": 0.2539878786554866,
|
||||
"local_gain_amplitude_median": 0.0045493907778025216,
|
||||
"local_phase_unwrapped_span_rad": 0.0709144214354922,
|
||||
"ser_minus_lab056_b": 0.06299674698466781,
|
||||
"evm_minus_lab056_b": 0.25957134898503287,
|
||||
"matched_residual_minus_lab056_b_db": -2.3314732994726888,
|
||||
"ripple_minus_lab056_b_db": 1.127169357893342
|
||||
},
|
||||
{
|
||||
"packet_index": 3,
|
||||
"raw_power_code2": 6.015186004162331,
|
||||
"rms_full_iq_code": 2.4525876139625127,
|
||||
"mean_i": 0.9700940860215054,
|
||||
"mean_q": 1.8342655220256676,
|
||||
"ac_power_code2": 1.7095734631363353,
|
||||
"ac_rms_code": 1.3075065824447445,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"max_complex_magnitude": 7.211102485656738,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 1.184291222829661,
|
||||
"symbol_error_rate": 0.10221465076660988,
|
||||
"symbol_rms_deviation": 1.3578439728375573,
|
||||
"matched_component_power": 0.6181275875589448,
|
||||
"residual_power": 1.1396667156434608,
|
||||
"matched_residual_db": -2.6569973778482696,
|
||||
"band_gain_nonuniformity_db": 7.370968795797246,
|
||||
"initial_frequency_hz": -0.9619877262845884,
|
||||
"diagnostic_residual_phase_error_rad": 0.9174219316762064,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2672883844824355,
|
||||
"local_correlation_min": 0.24083000237735547,
|
||||
"local_gain_amplitude_median": 0.004581545172098038,
|
||||
"local_phase_unwrapped_span_rad": 0.15725486354106955,
|
||||
"ser_minus_lab056_b": 0.06725569076660988,
|
||||
"evm_minus_lab056_b": 0.5124939728375573,
|
||||
"matched_residual_minus_lab056_b_db": -4.12184337784827,
|
||||
"ripple_minus_lab056_b_db": 1.899586795797246
|
||||
},
|
||||
{
|
||||
"packet_index": 4,
|
||||
"raw_power_code2": 8.462127124523066,
|
||||
"rms_full_iq_code": 2.9089735517056643,
|
||||
"mean_i": 0.9603494623655914,
|
||||
"mean_q": 1.846362296219216,
|
||||
"ac_power_code2": 4.130802305757289,
|
||||
"ac_rms_code": 2.032437528131502,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"max_complex_magnitude": 7.6157732009887695,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 3.6055200654506145,
|
||||
"symbol_error_rate": 0.15587734241908008,
|
||||
"symbol_rms_deviation": 1.6634180909816034,
|
||||
"matched_component_power": 0.694252592647496,
|
||||
"residual_power": 1.920968976998595,
|
||||
"matched_residual_db": -4.420028409350613,
|
||||
"band_gain_nonuniformity_db": 10.617549420778396,
|
||||
"initial_frequency_hz": 2.5845496302438504,
|
||||
"diagnostic_residual_phase_error_rad": 5.388297118381418,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 0,
|
||||
"local_correlation_median": 0.2731302498054202,
|
||||
"local_correlation_min": 0.1663171407536924,
|
||||
"local_gain_amplitude_median": 0.004805263148615891,
|
||||
"local_phase_unwrapped_span_rad": 0.12094876972869106,
|
||||
"ser_minus_lab056_b": 0.12091838241908008,
|
||||
"evm_minus_lab056_b": 0.8180680909816034,
|
||||
"matched_residual_minus_lab056_b_db": -5.884874409350614,
|
||||
"ripple_minus_lab056_b_db": 5.146167420778395
|
||||
},
|
||||
{
|
||||
"packet_index": 5,
|
||||
"raw_power_code2": 4.954615417967395,
|
||||
"rms_full_iq_code": 2.2258965425121167,
|
||||
"mean_i": 0.9645117932708983,
|
||||
"mean_q": 1.8537439299340963,
|
||||
"ac_power_code2": 0.5879658608412441,
|
||||
"ac_rms_code": 0.7667893197229889,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.242640495300293,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.06268362053456977,
|
||||
"symbol_error_rate": 0.08986371379897785,
|
||||
"symbol_rms_deviation": 1.0991964913376657,
|
||||
"matched_component_power": 0.7021817411712009,
|
||||
"residual_power": 0.8483991001186207,
|
||||
"matched_residual_db": -0.8215066687796163,
|
||||
"band_gain_nonuniformity_db": 6.409725568268307,
|
||||
"initial_frequency_hz": -1.2358463102217572,
|
||||
"diagnostic_residual_phase_error_rad": 0.8342172701371073,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.26644916295862675,
|
||||
"local_correlation_min": 0.2537591241878097,
|
||||
"local_gain_amplitude_median": 0.0046666418592819555,
|
||||
"local_phase_unwrapped_span_rad": 0.09866723940073996,
|
||||
"ser_minus_lab056_b": 0.05490475379897786,
|
||||
"evm_minus_lab056_b": 0.2538464913376657,
|
||||
"matched_residual_minus_lab056_b_db": -2.2863526687796165,
|
||||
"ripple_minus_lab056_b_db": 0.9383435682683068
|
||||
},
|
||||
{
|
||||
"packet_index": 6,
|
||||
"raw_power_code2": 4.919647502601457,
|
||||
"rms_full_iq_code": 2.218027840808464,
|
||||
"mean_i": 0.968934269857787,
|
||||
"mean_q": 1.8444437218175511,
|
||||
"ac_power_code2": 0.5788412403444356,
|
||||
"ac_rms_code": 0.7608161672470135,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.05355900003776126,
|
||||
"symbol_error_rate": 0.075809199318569,
|
||||
"symbol_rms_deviation": 1.1024024281507157,
|
||||
"matched_component_power": 0.6444662106996041,
|
||||
"residual_power": 0.783214058873921,
|
||||
"matched_residual_db": -0.8468032228257232,
|
||||
"band_gain_nonuniformity_db": 6.3642932090088,
|
||||
"initial_frequency_hz": -1.7795429995488667,
|
||||
"diagnostic_residual_phase_error_rad": 5.318131944986163,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2655258469985231,
|
||||
"local_correlation_min": 0.25666966629660665,
|
||||
"local_gain_amplitude_median": 0.004581829702937256,
|
||||
"local_phase_unwrapped_span_rad": 0.20674702332927808,
|
||||
"ser_minus_lab056_b": 0.040850239318569,
|
||||
"evm_minus_lab056_b": 0.2570524281507156,
|
||||
"matched_residual_minus_lab056_b_db": -2.3116492228257233,
|
||||
"ripple_minus_lab056_b_db": 0.8929112090087994
|
||||
},
|
||||
{
|
||||
"packet_index": 7,
|
||||
"raw_power_code2": 4.958322493929934,
|
||||
"rms_full_iq_code": 2.226729102053039,
|
||||
"mean_i": 0.9635145681581686,
|
||||
"mean_q": 1.856150277488727,
|
||||
"ac_power_code2": 0.5846683182554333,
|
||||
"ac_rms_code": 0.7646360691567154,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.05938607794875894,
|
||||
"symbol_error_rate": 0.08390119250425894,
|
||||
"symbol_rms_deviation": 1.1001519668540132,
|
||||
"matched_component_power": 0.6819353341348239,
|
||||
"residual_power": 0.8253697594999119,
|
||||
"matched_residual_db": -0.8290535905764504,
|
||||
"band_gain_nonuniformity_db": 6.236828217134885,
|
||||
"initial_frequency_hz": -0.2484094971075878,
|
||||
"diagnostic_residual_phase_error_rad": 1.4617535496008514,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.266458697108116,
|
||||
"local_correlation_min": 0.24788848233138613,
|
||||
"local_gain_amplitude_median": 0.004627604818355622,
|
||||
"local_phase_unwrapped_span_rad": 0.11827169376376112,
|
||||
"ser_minus_lab056_b": 0.04894223250425894,
|
||||
"evm_minus_lab056_b": 0.25480196685401313,
|
||||
"matched_residual_minus_lab056_b_db": -2.2938995905764505,
|
||||
"ripple_minus_lab056_b_db": 0.765446217134885
|
||||
},
|
||||
{
|
||||
"packet_index": 8,
|
||||
"raw_power_code2": 4.913306451612903,
|
||||
"rms_full_iq_code": 2.2165979454138505,
|
||||
"mean_i": 0.9589186611168922,
|
||||
"mean_q": 1.8453433922996878,
|
||||
"ac_power_code2": 0.5884892174705705,
|
||||
"ac_rms_code": 0.7671305087601265,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.06320697716389612,
|
||||
"symbol_error_rate": 0.08262350936967632,
|
||||
"symbol_rms_deviation": 1.1492572709962479,
|
||||
"matched_component_power": 0.6381478875618168,
|
||||
"residual_power": 0.8428608001594871,
|
||||
"matched_residual_db": -1.2083452015652267,
|
||||
"band_gain_nonuniformity_db": 6.714920606410644,
|
||||
"initial_frequency_hz": -1.0783628659018663,
|
||||
"diagnostic_residual_phase_error_rad": 0.78177232042639,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.26521123625627346,
|
||||
"local_correlation_min": 0.2568596341044175,
|
||||
"local_gain_amplitude_median": 0.004588048214156221,
|
||||
"local_phase_unwrapped_span_rad": 0.17639438146975883,
|
||||
"ser_minus_lab056_b": 0.04766454936967632,
|
||||
"evm_minus_lab056_b": 0.3039072709962478,
|
||||
"matched_residual_minus_lab056_b_db": -2.673191201565227,
|
||||
"ripple_minus_lab056_b_db": 1.2435386064106435
|
||||
},
|
||||
{
|
||||
"packet_index": 9,
|
||||
"raw_power_code2": 4.8869558619493585,
|
||||
"rms_full_iq_code": 2.2106460281893523,
|
||||
"mean_i": 0.9541276448144294,
|
||||
"mean_q": 1.840042924037461,
|
||||
"ac_power_code2": 0.5908383370498989,
|
||||
"ac_rms_code": 0.7686600920107007,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.06555609674322449,
|
||||
"symbol_error_rate": 0.08943781942078365,
|
||||
"symbol_rms_deviation": 1.141830995657666,
|
||||
"matched_component_power": 0.6581222468886276,
|
||||
"residual_power": 0.858045321706861,
|
||||
"matched_residual_db": -1.1520365600725753,
|
||||
"band_gain_nonuniformity_db": 6.809131520732102,
|
||||
"initial_frequency_hz": 0.8322819250764986,
|
||||
"diagnostic_residual_phase_error_rad": 1.0738651986809553,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2629884708976966,
|
||||
"local_correlation_min": 0.2513843183766455,
|
||||
"local_gain_amplitude_median": 0.004577452828973017,
|
||||
"local_phase_unwrapped_span_rad": 0.13543041514325171,
|
||||
"ser_minus_lab056_b": 0.05447885942078365,
|
||||
"evm_minus_lab056_b": 0.29648099565766595,
|
||||
"matched_residual_minus_lab056_b_db": -2.6168825600725754,
|
||||
"ripple_minus_lab056_b_db": 1.3377495207321015
|
||||
},
|
||||
{
|
||||
"packet_index": 10,
|
||||
"raw_power_code2": 4.915355098855359,
|
||||
"rms_full_iq_code": 2.2170600124614035,
|
||||
"mean_i": 0.9478299514394728,
|
||||
"mean_q": 1.850437911897329,
|
||||
"ac_power_code2": 0.5928530162226577,
|
||||
"ac_rms_code": 0.769969490449237,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.242640495300293,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.06757077591598337,
|
||||
"symbol_error_rate": 0.08347529812606473,
|
||||
"symbol_rms_deviation": 1.0923957700987015,
|
||||
"matched_component_power": 0.6971368095977797,
|
||||
"residual_power": 0.8319132362097249,
|
||||
"matched_residual_db": -0.7676001965024029,
|
||||
"band_gain_nonuniformity_db": 6.678161163217084,
|
||||
"initial_frequency_hz": -1.3378733037888852,
|
||||
"diagnostic_residual_phase_error_rad": 0.7703154441715622,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.26769649861033307,
|
||||
"local_correlation_min": 0.25301897115559957,
|
||||
"local_gain_amplitude_median": 0.004684343948400701,
|
||||
"local_phase_unwrapped_span_rad": 0.1578122760403755,
|
||||
"ser_minus_lab056_b": 0.048516338126064734,
|
||||
"evm_minus_lab056_b": 0.24704577009870143,
|
||||
"matched_residual_minus_lab056_b_db": -2.232446196502403,
|
||||
"ripple_minus_lab056_b_db": 1.2067791632170835
|
||||
},
|
||||
{
|
||||
"packet_index": 11,
|
||||
"raw_power_code2": 4.919918487686438,
|
||||
"rms_full_iq_code": 2.218088926911281,
|
||||
"mean_i": 0.9430389351370101,
|
||||
"mean_q": 1.852898456468956,
|
||||
"ac_power_code2": 0.5973633645170526,
|
||||
"ac_rms_code": 0.7728928544869933,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.07208112421037827,
|
||||
"symbol_error_rate": 0.08049403747870527,
|
||||
"symbol_rms_deviation": 1.0853439130113745,
|
||||
"matched_component_power": 0.6882509611422404,
|
||||
"residual_power": 0.8107399547939167,
|
||||
"matched_residual_db": -0.7113474983787614,
|
||||
"band_gain_nonuniformity_db": 6.630624104636567,
|
||||
"initial_frequency_hz": 1.1396956047405093,
|
||||
"diagnostic_residual_phase_error_rad": 0.8869440636679229,
|
||||
"local_delay_median_samples": 1.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2677790176885852,
|
||||
"local_correlation_min": 0.24298761555141254,
|
||||
"local_gain_amplitude_median": 0.0046615184329749015,
|
||||
"local_phase_unwrapped_span_rad": 0.11596124244647732,
|
||||
"ser_minus_lab056_b": 0.04553507747870528,
|
||||
"evm_minus_lab056_b": 0.2399939130113744,
|
||||
"matched_residual_minus_lab056_b_db": -2.1761934983787614,
|
||||
"ripple_minus_lab056_b_db": 1.1592421046365669
|
||||
},
|
||||
{
|
||||
"packet_index": 12,
|
||||
"raw_power_code2": 6.8007067291016305,
|
||||
"rms_full_iq_code": 2.607816467679739,
|
||||
"mean_i": 0.9549947970863684,
|
||||
"mean_q": 1.8480749219562955,
|
||||
"ac_power_code2": 2.4733107494758295,
|
||||
"ac_rms_code": 1.5726763015559908,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"max_complex_magnitude": 8.062257766723633,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 1.9480285091691552,
|
||||
"symbol_error_rate": 0.12819420783645655,
|
||||
"symbol_rms_deviation": 1.4566037803957919,
|
||||
"matched_component_power": 0.6313306634190563,
|
||||
"residual_power": 1.3394908423846723,
|
||||
"matched_residual_db": -3.2668286553130113,
|
||||
"band_gain_nonuniformity_db": 7.945031576922354,
|
||||
"initial_frequency_hz": -0.15870982770940278,
|
||||
"diagnostic_residual_phase_error_rad": 1.0850946010670803,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.2691949358576327,
|
||||
"local_correlation_min": 0.2602069168691974,
|
||||
"local_gain_amplitude_median": 0.004654618220793532,
|
||||
"local_phase_unwrapped_span_rad": 0.10483842514826858,
|
||||
"ser_minus_lab056_b": 0.09323524783645655,
|
||||
"evm_minus_lab056_b": 0.6112537803957918,
|
||||
"matched_residual_minus_lab056_b_db": -4.731674655313011,
|
||||
"ripple_minus_lab056_b_db": 2.4736495769223534
|
||||
},
|
||||
{
|
||||
"packet_index": 13,
|
||||
"raw_power_code2": 7.57159425945196,
|
||||
"rms_full_iq_code": 2.751653004913948,
|
||||
"mean_i": 0.9523066250433576,
|
||||
"mean_q": 1.8468283905653833,
|
||||
"ac_power_code2": 3.253931247152167,
|
||||
"ac_rms_code": 1.8038656399943336,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"max_complex_magnitude": 7.810249328613281,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 2.7286490068454925,
|
||||
"symbol_error_rate": 0.16013628620102216,
|
||||
"symbol_rms_deviation": 1.542471770787923,
|
||||
"matched_component_power": 0.6706715317796331,
|
||||
"residual_power": 1.5956745609431342,
|
||||
"matched_residual_db": -3.764344492637608,
|
||||
"band_gain_nonuniformity_db": 9.871896166053979,
|
||||
"initial_frequency_hz": -3.768747766207012,
|
||||
"diagnostic_residual_phase_error_rad": 1.4778689507229945,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.25756193885232326,
|
||||
"local_correlation_min": 0.2383815900640343,
|
||||
"local_gain_amplitude_median": 0.004487614735363821,
|
||||
"local_phase_unwrapped_span_rad": 0.0618646152553739,
|
||||
"ser_minus_lab056_b": 0.12517732620102218,
|
||||
"evm_minus_lab056_b": 0.6971217707879229,
|
||||
"matched_residual_minus_lab056_b_db": -5.229190492637608,
|
||||
"ripple_minus_lab056_b_db": 4.400514166053979
|
||||
},
|
||||
{
|
||||
"packet_index": 14,
|
||||
"raw_power_code2": 4.931039715574054,
|
||||
"rms_full_iq_code": 2.220594450946425,
|
||||
"mean_i": 0.9521657127991675,
|
||||
"mean_q": 1.8542859001040584,
|
||||
"ac_power_code2": 0.5860439716189889,
|
||||
"ac_rms_code": 0.7655350884309542,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.06076173131231455,
|
||||
"symbol_error_rate": 0.09454855195911414,
|
||||
"symbol_rms_deviation": 1.1250423245117716,
|
||||
"matched_component_power": 0.6318534852320092,
|
||||
"residual_power": 0.7997497398817571,
|
||||
"matched_residual_db": -1.0233772215014318,
|
||||
"band_gain_nonuniformity_db": 6.450865575756282,
|
||||
"initial_frequency_hz": 0.07157023489205856,
|
||||
"diagnostic_residual_phase_error_rad": 0.8500145465030344,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.25880367193626036,
|
||||
"local_correlation_min": 0.24902334455788566,
|
||||
"local_gain_amplitude_median": 0.004498680092492703,
|
||||
"local_phase_unwrapped_span_rad": 0.11686979564231703,
|
||||
"ser_minus_lab056_b": 0.059589591959114147,
|
||||
"evm_minus_lab056_b": 0.2796923245117715,
|
||||
"matched_residual_minus_lab056_b_db": -2.488223221501432,
|
||||
"ripple_minus_lab056_b_db": 0.9794835757562819
|
||||
},
|
||||
{
|
||||
"packet_index": 15,
|
||||
"raw_power_code2": 4.914639698231009,
|
||||
"rms_full_iq_code": 2.2168986666582233,
|
||||
"mean_i": 0.9496509712105445,
|
||||
"mean_q": 1.8523890045091918,
|
||||
"ac_power_code2": 0.5814577070833247,
|
||||
"ac_rms_code": 0.7625337416031664,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.05617546677665031,
|
||||
"symbol_error_rate": 0.09284497444633731,
|
||||
"symbol_rms_deviation": 1.1566134639773282,
|
||||
"matched_component_power": 0.6485174305492952,
|
||||
"residual_power": 0.8675572440266134,
|
||||
"matched_residual_db": -1.2637648711797032,
|
||||
"band_gain_nonuniformity_db": 7.550266963349408,
|
||||
"initial_frequency_hz": 0.6368695507363243,
|
||||
"diagnostic_residual_phase_error_rad": 5.326810530921518,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.26432524091011667,
|
||||
"local_correlation_min": 0.24252120082883175,
|
||||
"local_gain_amplitude_median": 0.00457300739571061,
|
||||
"local_phase_unwrapped_span_rad": 0.08002037051007749,
|
||||
"ser_minus_lab056_b": 0.057886014446337314,
|
||||
"evm_minus_lab056_b": 0.3112634639773282,
|
||||
"matched_residual_minus_lab056_b_db": -2.7286108711797032,
|
||||
"ripple_minus_lab056_b_db": 2.0788849633494078
|
||||
},
|
||||
{
|
||||
"packet_index": 16,
|
||||
"raw_power_code2": 4.976760319112036,
|
||||
"rms_full_iq_code": 2.2308653744930544,
|
||||
"mean_i": 0.956902532084634,
|
||||
"mean_q": 1.864615851543531,
|
||||
"ac_power_code2": 0.5843055893746447,
|
||||
"ac_rms_code": 0.7643988418192722,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"max_complex_magnitude": 4.4721360206604,
|
||||
"background_ac_power_code2": 0.5252822403066744,
|
||||
"background_subtracted_useful_power_code2": 0.059023349067970354,
|
||||
"symbol_error_rate": 0.08603066439522998,
|
||||
"symbol_rms_deviation": 1.063656865382626,
|
||||
"matched_component_power": 0.7029467258520877,
|
||||
"residual_power": 0.7952899743189897,
|
||||
"matched_residual_db": -0.5360309519366353,
|
||||
"band_gain_nonuniformity_db": 6.3916733554839595,
|
||||
"initial_frequency_hz": 1.3365576309608829,
|
||||
"diagnostic_residual_phase_error_rad": 0.7414410771269146,
|
||||
"local_delay_median_samples": 0.0,
|
||||
"local_delay_min_samples": 0,
|
||||
"local_delay_max_samples": 1,
|
||||
"local_correlation_median": 0.26939315416499676,
|
||||
"local_correlation_min": 0.26084853462192703,
|
||||
"local_gain_amplitude_median": 0.004663845798926014,
|
||||
"local_phase_unwrapped_span_rad": 0.13577682602915098,
|
||||
"ser_minus_lab056_b": 0.05107170439522998,
|
||||
"evm_minus_lab056_b": 0.2183068653826259,
|
||||
"matched_residual_minus_lab056_b_db": -2.000876951936635,
|
||||
"ripple_minus_lab056_b_db": 0.9202913554839594
|
||||
}
|
||||
],
|
||||
"group_trends": [
|
||||
{
|
||||
"packets": "1-4",
|
||||
"useful_power_mean": 1.2272133383749813,
|
||||
"symbol_error_rate_mean": 0.11030664395229983,
|
||||
"symbol_rms_deviation_mean": 1.303626432427683,
|
||||
"matched_component_power_mean": 0.672691886362818,
|
||||
"residual_power_mean": 1.179421002324599,
|
||||
"matched_residual_db_mean": -2.16970094573799,
|
||||
"initial_frequency_hz_mean": 1.067890679251471,
|
||||
"diagnostic_residual_phase_error_rad_mean": 3.153348518335517,
|
||||
"local_phase_unwrapped_span_rad_mean": 0.11741908337326645,
|
||||
"local_gain_amplitude_median": 0.004637231352846487
|
||||
},
|
||||
{
|
||||
"packets": "5-8",
|
||||
"useful_power_mean": 0.05970891892124652,
|
||||
"symbol_error_rate_mean": 0.08304940374787052,
|
||||
"symbol_rms_deviation_mean": 1.1127520393346606,
|
||||
"matched_component_power_mean": 0.6666827933918614,
|
||||
"residual_power_mean": 0.8249609296629852,
|
||||
"matched_residual_db_mean": -0.9264271709367542,
|
||||
"initial_frequency_hz_mean": -1.0855404181950195,
|
||||
"diagnostic_residual_phase_error_rad_mean": 2.0989687712876277,
|
||||
"local_phase_unwrapped_span_rad_mean": 0.1500200844908845,
|
||||
"local_gain_amplitude_median": 0.004607826516255922
|
||||
},
|
||||
{
|
||||
"packets": "9-12",
|
||||
"useful_power_mean": 0.5383091265096853,
|
||||
"symbol_error_rate_mean": 0.09540034071550255,
|
||||
"symbol_rms_deviation_mean": 1.1940436147908833,
|
||||
"matched_component_power_mean": 0.668710170261926,
|
||||
"residual_power_mean": 0.9600473387737937,
|
||||
"matched_residual_db_mean": -1.4744532275666877,
|
||||
"initial_frequency_hz_mean": 0.11884859957967997,
|
||||
"diagnostic_residual_phase_error_rad_mean": 0.9540548268968803,
|
||||
"local_phase_unwrapped_span_rad_mean": 0.12851058969459328,
|
||||
"local_gain_amplitude_median": 0.004658068326884217
|
||||
},
|
||||
{
|
||||
"packets": "13-16",
|
||||
"useful_power_mean": 0.7261523885006069,
|
||||
"symbol_error_rate_mean": 0.10839011925042591,
|
||||
"symbol_rms_deviation_mean": 1.2219461061649122,
|
||||
"matched_component_power_mean": 0.6634972933532564,
|
||||
"residual_power_mean": 1.0145678797926236,
|
||||
"matched_residual_db_mean": -1.6468793843138447,
|
||||
"initial_frequency_hz_mean": -0.4309375874044365,
|
||||
"diagnostic_residual_phase_error_rad_mean": 2.0990337763186155,
|
||||
"local_phase_unwrapped_span_rad_mean": 0.09863290185922985,
|
||||
"local_gain_amplitude_median": 0.0045358437441016565
|
||||
}
|
||||
],
|
||||
"special_packets_3_4_12_14": {
|
||||
"packets": [
|
||||
3,
|
||||
4,
|
||||
12,
|
||||
14
|
||||
],
|
||||
"all_have_power_drop_below_median": false,
|
||||
"all_have_correlation_below_median": false,
|
||||
"all_have_large_delay_change": false,
|
||||
"interpretation": "No common underrun signature unless one of all three booleans is true with localized alignment evidence"
|
||||
},
|
||||
"packets_most_affected": [
|
||||
13,
|
||||
4,
|
||||
12,
|
||||
3
|
||||
],
|
||||
"active_rx_level": {
|
||||
"ac_rms_min": 0.7608161672470135,
|
||||
"ac_rms_max": 2.032437528131502,
|
||||
"max_abs_i_min": 3.0,
|
||||
"max_abs_i_max": 6.0,
|
||||
"max_abs_q_min": 4.0,
|
||||
"max_abs_q_max": 7.0,
|
||||
"mean_i_min": 0.9430389351370101,
|
||||
"mean_i_max": 0.9700940860215054,
|
||||
"mean_q_min": 1.8294528269164065,
|
||||
"mean_q_max": 1.864615851543531,
|
||||
"local_gain_amplitude_median_min": 0.004487614735363821,
|
||||
"local_gain_amplitude_median_max": 0.004805263148615891,
|
||||
"analog_overload": "LESS_LIKELY_FROM_DIGITAL_LEVEL_BUT_NOT_EXCLUDED_WITHOUT_INSTRUMENT"
|
||||
},
|
||||
"lab056_comparison": {
|
||||
"reference": {
|
||||
"symbol_error_rate": 0.03495896,
|
||||
"evm_rms": 0.84535,
|
||||
"matched_residual_db": 1.464846,
|
||||
"band_ripple_db": 5.471382
|
||||
},
|
||||
"packets_worse_on_all_three_core_metrics": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16
|
||||
]
|
||||
},
|
||||
"quality_distribution": "GLOBAL",
|
||||
"primary_cause_status": "PARTIALLY_LOCALIZED",
|
||||
"packets_worse_than_lab056_on_ser_evm_and_matched_residual": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16
|
||||
],
|
||||
"global_degradation_rule": "at least 12/16 packets worse than Lab056 B on SER, EVM and matched/residual",
|
||||
"global_degradation": true,
|
||||
"local_delay_discontinuity": false,
|
||||
"next_single_step": "Before another TX, investigate the low active RX level, gain/geometry and possible analogue compression; also change TX streaming/logging so any underrun is unambiguous. Do not change RF parameters yet.",
|
||||
"lab063_commit": "NOT_PERFORMED",
|
||||
"push": "NOT_PERFORMED",
|
||||
"lab064": "NOT_CREATED"
|
||||
}
|
||||
BIN
data/processed/lab063/symbol_error_rate_by_packet.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
230
data/processed/lab064/capture_result.json
Normal file
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"lab": "Lab064",
|
||||
"stage": "SINGLE_RF_LEVEL_CAPTURE",
|
||||
"passed": true,
|
||||
"rf_tx_count": 1,
|
||||
"automatic_tx_repeats": 0,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 20000,
|
||||
"samples_per_symbol": 400,
|
||||
"digital_carrier_hz": 50000,
|
||||
"rrc_rolloff": 0.35,
|
||||
"rrc_span_symbols": 10,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": false,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [
|
||||
-18.0,
|
||||
-12.0,
|
||||
-6.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 333,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 2,
|
||||
"time_to_detect_ms": 139.0420999377966,
|
||||
"elapsed_ms": 139.0420999377966,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.13589980080724,
|
||||
"end_ms": 67.04779993742704,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 117.27159982547164,
|
||||
"end_ms": 139.0420999377966,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_level_before_tx": {
|
||||
"samples": 65536,
|
||||
"mean_i": 0.9547576904296875,
|
||||
"mean_q": 1.83575439453125,
|
||||
"power_code2": 4.8329315185546875,
|
||||
"ac_power_code2": 0.5513750740792602,
|
||||
"rms_code": 2.1983929399801774,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.710618637663133,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||
},
|
||||
"tx_process": {
|
||||
"returncode": 0,
|
||||
"stdout_file": "tx_stdout.txt",
|
||||
"stderr_file": "tx_stderr.txt"
|
||||
},
|
||||
"tx_counter_summary": {
|
||||
"expected_kind": "underrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "underrun",
|
||||
"text": "0 underruns",
|
||||
"character_offset": 337
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "underrun",
|
||||
"text": "0 underruns",
|
||||
"character_offset": 509
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_samples": 16000000,
|
||||
"rx_bytes": 32000000,
|
||||
"rx_duration_seconds": 2.0,
|
||||
"rx_counter_summary": {
|
||||
"expected_kind": "overrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 336
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 458
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 580
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 751
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_sha256": "02015891948aaee52ff81e60fdfeb391bb9b76cd9192b75972b8fedbc8690d86",
|
||||
"rx_level": {
|
||||
"samples": 16000000,
|
||||
"mean_i": 0.9548163125,
|
||||
"mean_q": 1.83674125,
|
||||
"power_code2": 5.2568038125,
|
||||
"ac_power_code2": 0.9715112024323407,
|
||||
"rms_code": 2.2927720803647276,
|
||||
"peak_magnitude_code": 8.602325267042627,
|
||||
"max_abs_i": 7.0,
|
||||
"max_abs_q": 8.0,
|
||||
"papr_db": 11.485099505760791,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||
},
|
||||
"rx_clipping": false,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 333,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
164
data/processed/lab064/preflight_result.json
Normal file
@@ -0,0 +1,164 @@
|
||||
{
|
||||
"lab": "Lab064",
|
||||
"stage": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT",
|
||||
"passed": true,
|
||||
"rf_tx_count": 0,
|
||||
"stop_before_tx": true,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 20000,
|
||||
"samples_per_symbol": 400,
|
||||
"digital_carrier_hz": 50000,
|
||||
"rrc_rolloff": 0.35,
|
||||
"rrc_span_symbols": 10,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": false,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [
|
||||
-18.0,
|
||||
-12.0,
|
||||
-6.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 329,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 1,
|
||||
"time_to_detect_ms": 66.79299985989928,
|
||||
"elapsed_ms": 66.79299985989928,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.07890006527305,
|
||||
"end_ms": 66.79299985989928,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_process": {
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "call hackrf_set_sample_rate(8000000 Hz/8.000 MHz)\ncall hackrf_set_hw_sync_mode(0)\ncall hackrf_set_freq(1400000000 Hz/1400.000 MHz)\ncall hackrf_set_amp_enable(0)\ncall hackrf_set_antenna_enable(0)\nsamples_to_xfer 800000/0Mio\nStop with Ctrl-C\n 1.8 MiB / 0.117 sec = 15.6 MiB/second, average power -35.3 dBfs, 9792 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11842 s\nhackrf_stop_rx() done\nTransfer statistics:\n1892448 bytes transferred by M0\n1867776 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n"
|
||||
},
|
||||
"rx_counter_summary": {
|
||||
"expected_kind": "overrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 333
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 502
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_level": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.9337375,
|
||||
"mean_q": 1.80165,
|
||||
"power_code2": 4.810095,
|
||||
"ac_power_code2": 0.6922865585937501,
|
||||
"rms_code": 2.1931928779749397,
|
||||
"peak_magnitude_code": 5.385164807134504,
|
||||
"max_abs_i": 4.0,
|
||||
"max_abs_q": 5.0,
|
||||
"papr_db": 7.802443440680863,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
|
||||
"symbol_energy": "NOT_APPLICABLE_TO_BACKGROUND",
|
||||
"ac_rms_code": 0.8320375944593791,
|
||||
"unexpected_strong_signal": false,
|
||||
"strong_signal_rule": "AC RMS >= 20.0 code or max |I|/|Q| >= 100.0 code"
|
||||
},
|
||||
"rx_sha256": "02d87adcbb621027290a77d5f8fd3190c299d13e89f887cd294d4af352a6ef52",
|
||||
"rx_overruns": 0,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 329,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
221
data/processed/lab064/preparation.json
Normal file
@@ -0,0 +1,221 @@
|
||||
{
|
||||
"lab": "Lab064",
|
||||
"stage": "PREPARATION_ONLY",
|
||||
"rf_tx_count": 0,
|
||||
"head": "4437917465274257ff0ca5d3c269029da45de3ec",
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 20000,
|
||||
"samples_per_symbol": 400,
|
||||
"digital_carrier_hz": 50000,
|
||||
"rrc_rolloff": 0.35,
|
||||
"rrc_span_symbols": 10,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": false,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [
|
||||
-18.0,
|
||||
-12.0,
|
||||
-6.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"segments": [
|
||||
{
|
||||
"label": "L0",
|
||||
"level_db": -18.0,
|
||||
"start_sample": 800000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 808400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": -9.625e-05,
|
||||
"mean_q": 2.75e-05,
|
||||
"power_code2": 30.87065625,
|
||||
"ac_power_code2": 30.870656239979688,
|
||||
"rms_code": 5.556136809870686,
|
||||
"peak_magnitude_code": 9.219544457292887,
|
||||
"max_abs_i": 9.0,
|
||||
"max_abs_q": 9.0,
|
||||
"papr_db": 4.398730638796093,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0015435328125,
|
||||
"requested_relative_power_db": -18.0,
|
||||
"tx_rms_code": 5.556136809870686,
|
||||
"tx_peak_abs_i_code": 9.0,
|
||||
"tx_peak_abs_q_code": 9.0,
|
||||
"tx_peak_magnitude_code": 9.219544457292887,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": -17.97368512020099
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L1",
|
||||
"level_db": -12.0,
|
||||
"start_sample": 2000000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 2008400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": -5e-05,
|
||||
"mean_q": 5e-05,
|
||||
"power_code2": 122.2567675,
|
||||
"ac_power_code2": 122.25676749499999,
|
||||
"rms_code": 11.056978226441435,
|
||||
"peak_magnitude_code": 17.804493814764857,
|
||||
"max_abs_i": 17.0,
|
||||
"max_abs_q": 17.0,
|
||||
"papr_db": 4.1378635346865025,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.006112838375,
|
||||
"requested_relative_power_db": -12.0,
|
||||
"tx_rms_code": 11.056978226441435,
|
||||
"tx_peak_abs_i_code": 17.0,
|
||||
"tx_peak_abs_q_code": 17.0,
|
||||
"tx_peak_magnitude_code": 17.804493814764857,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": -11.99641465105681
|
||||
},
|
||||
"tx_input_difference_from_previous_db": 5.9772704691441785
|
||||
},
|
||||
{
|
||||
"label": "L2",
|
||||
"level_db": -6.0,
|
||||
"start_sample": 3200000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 3208400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": 5.625e-05,
|
||||
"mean_q": 2.625e-05,
|
||||
"power_code2": 486.5064875,
|
||||
"ac_power_code2": 486.50648749614686,
|
||||
"rms_code": 22.05689206347984,
|
||||
"peak_magnitude_code": 35.12833614050059,
|
||||
"max_abs_i": 34.0,
|
||||
"max_abs_q": 35.0,
|
||||
"papr_db": 4.0422652379446395,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.024325324375,
|
||||
"requested_relative_power_db": -6.0,
|
||||
"tx_rms_code": 22.05689206347984,
|
||||
"tx_peak_abs_i_code": 34.0,
|
||||
"tx_peak_abs_q_code": 35.0,
|
||||
"tx_peak_magnitude_code": 35.12833614050059,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": -5.998257379520232
|
||||
},
|
||||
"tx_input_difference_from_previous_db": 5.998157271536577
|
||||
},
|
||||
{
|
||||
"label": "L3",
|
||||
"level_db": 0.0,
|
||||
"start_sample": 4400000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 4408400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": 8.25e-05,
|
||||
"mean_q": 0.00011125,
|
||||
"power_code2": 1936.04021375,
|
||||
"ac_power_code2": 1936.0402137308172,
|
||||
"rms_code": 44.00045697205883,
|
||||
"peak_magnitude_code": 69.6419413859206,
|
||||
"max_abs_i": 69.0,
|
||||
"max_abs_q": 69.0,
|
||||
"papr_db": 3.9882736474748155,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0968020106875,
|
||||
"requested_relative_power_db": 0.0,
|
||||
"tx_rms_code": 44.00045697205883,
|
||||
"tx_peak_abs_i_code": 69.0,
|
||||
"tx_peak_abs_q_code": 69.0,
|
||||
"tx_peak_magnitude_code": 69.6419413859206,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": 0.0
|
||||
},
|
||||
"tx_input_difference_from_previous_db": 5.998257379520232
|
||||
}
|
||||
],
|
||||
"leading_zero_samples": 800000,
|
||||
"gap_samples": 400000,
|
||||
"trailing_zero_samples": 800000,
|
||||
"tx_samples": 6000000,
|
||||
"tx_bytes": 12000000,
|
||||
"tx_duration_seconds": 0.75,
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"rx_samples": 16000000,
|
||||
"rx_duration_seconds": 2.0,
|
||||
"rx_lead_seconds": 0.5,
|
||||
"automatic_retry": false,
|
||||
"tx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc323b97d3",
|
||||
"-t",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab064\\tx_level_staircase.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"6000000",
|
||||
"-x",
|
||||
"10",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
],
|
||||
"rx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"-r",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab064\\rx_stream.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"16000000",
|
||||
"-l",
|
||||
"24",
|
||||
"-g",
|
||||
"24",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
]
|
||||
}
|
||||
425
data/processed/lab064/rf_result.json
Normal file
@@ -0,0 +1,425 @@
|
||||
{
|
||||
"lab": "Lab064",
|
||||
"stage": "OFFLINE_RF_LEVEL_LINEARITY_ANALYSIS",
|
||||
"packet_receiver_used": false,
|
||||
"location": {
|
||||
"start_sample": 4123520,
|
||||
"correlation": 0.0888256100450642,
|
||||
"coarse_cfo_hz": 0.0,
|
||||
"stride": 32
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"label": "L0",
|
||||
"level_db": -18.0,
|
||||
"rx_start_sample": 4923521,
|
||||
"alignment_adjustment_samples": 1,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.94972375,
|
||||
"mean_q": 1.8355175,
|
||||
"power_code2": 5.18564125,
|
||||
"ac_power_code2": 0.9145415558796861,
|
||||
"rms_code": 2.2772003095907043,
|
||||
"peak_magnitude_code": 7.0710678118654755,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 9.841675359702911,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00025928206249999997,
|
||||
"ac_rms_code": 0.9563166608815754
|
||||
},
|
||||
"background_label": "INITIAL_GUARD",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.940225,
|
||||
"mean_q": 1.8409875,
|
||||
"power_code2": 4.7947875,
|
||||
"ac_power_code2": 0.5215294742187488,
|
||||
"rms_code": 2.189700321961889,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.7450314065030845,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000239739375,
|
||||
"ac_rms_code": 0.7221699759881665
|
||||
},
|
||||
"background_window_start_sample": 4443520,
|
||||
"matched_signal_power": 0.0007507510158784873,
|
||||
"matched_signal_amplitude_rms": 0.027399836055686307,
|
||||
"residual_power": 1.2137948040249413,
|
||||
"residual_to_matched_amplitude_ratio": 40.209129659782626,
|
||||
"matched_to_residual_db": -32.08649345507262,
|
||||
"complex_gain_h": {
|
||||
"real": 0.0018760642981310563,
|
||||
"imag": -0.0045080629571906915,
|
||||
"phase_rad_at_active_start": -1.1764391150184978
|
||||
},
|
||||
"gain_abs": 0.00488285253481168,
|
||||
"gain_power_abs_h_squared": 2.384224887671685e-05,
|
||||
"cfo_hz": 0.0957717781274782,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.0029441230361582733,
|
||||
"residual_band_h2_power_code2": 0.002517548539085255,
|
||||
"residual_band_h3_power_code2": 0.0017878979213816356,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L1",
|
||||
"level_db": -12.0,
|
||||
"rx_start_sample": 6123541,
|
||||
"alignment_adjustment_samples": 21,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.9560375,
|
||||
"mean_q": 1.8325725,
|
||||
"power_code2": 5.2504775,
|
||||
"ac_power_code2": 0.9781478308375009,
|
||||
"rms_code": 2.2913920441513276,
|
||||
"peak_magnitude_code": 7.211102550927978,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 9.958045419078847,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000262523875,
|
||||
"ac_rms_code": 0.9890135645366553
|
||||
},
|
||||
"background_label": "GAP_L0_L1",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.95144375,
|
||||
"mean_q": 1.8329875,
|
||||
"power_code2": 4.78826875,
|
||||
"ac_power_code2": 0.5231803654296865,
|
||||
"rms_code": 2.1882113129220406,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.7509398714368505,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00023941343750000002,
|
||||
"ac_rms_code": 0.7233120802459243
|
||||
},
|
||||
"background_window_start_sample": 5883520,
|
||||
"matched_signal_power": 0.0029612351609719875,
|
||||
"matched_signal_amplitude_rms": 0.054417232206094304,
|
||||
"residual_power": 1.0695946032022712,
|
||||
"residual_to_matched_amplitude_ratio": 19.005231005856622,
|
||||
"matched_to_residual_db": -25.577463055166668,
|
||||
"complex_gain_h": {
|
||||
"real": 0.004471119012843702,
|
||||
"imag": -0.001937685853593986,
|
||||
"phase_rad_at_active_start": -0.40894566669212534
|
||||
},
|
||||
"gain_abs": 0.0048729387123409105,
|
||||
"gain_power_abs_h_squared": 2.374553169423069e-05,
|
||||
"cfo_hz": -0.16857111258676097,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.0037442904303572424,
|
||||
"residual_band_h2_power_code2": 0.0029946268848091966,
|
||||
"residual_band_h3_power_code2": 0.0022268690579244723,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L2",
|
||||
"level_db": -6.0,
|
||||
"rx_start_sample": 7323533,
|
||||
"alignment_adjustment_samples": 13,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.9631825,
|
||||
"mean_q": 1.84173875,
|
||||
"power_code2": 5.17589875,
|
||||
"ac_power_code2": 0.856176598442187,
|
||||
"rms_code": 2.275060164039624,
|
||||
"peak_magnitude_code": 7.280109889280518,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 10.102900974475599,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002587949375,
|
||||
"ac_rms_code": 0.925298113281437
|
||||
},
|
||||
"background_label": "GAP_L1_L2",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.9560375,
|
||||
"mean_q": 1.8575625,
|
||||
"power_code2": 4.88045,
|
||||
"ac_power_code2": 0.5159038571874985,
|
||||
"rms_code": 2.209174053803819,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.668126373000443,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002440225,
|
||||
"ac_rms_code": 0.7182644757939087
|
||||
},
|
||||
"background_window_start_sample": 7083520,
|
||||
"matched_signal_power": 0.011333436190702588,
|
||||
"matched_signal_amplitude_rms": 0.10645861257175292,
|
||||
"residual_power": 0.5290886880136152,
|
||||
"residual_to_matched_amplitude_ratio": 6.832559719073439,
|
||||
"matched_to_residual_db": -16.691668725481204,
|
||||
"complex_gain_h": {
|
||||
"real": 0.0032772531007889995,
|
||||
"imag": -0.003478187284280655,
|
||||
"phase_rad_at_active_start": -0.8151334504253348
|
||||
},
|
||||
"gain_abs": 0.00477893028523777,
|
||||
"gain_power_abs_h_squared": 2.283817467116275e-05,
|
||||
"cfo_hz": 0.006742170898886185,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.004466260208758569,
|
||||
"residual_band_h2_power_code2": 0.0030543665029441423,
|
||||
"residual_band_h3_power_code2": 0.0020345488796523733,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L3",
|
||||
"level_db": 0.0,
|
||||
"rx_start_sample": 8523534,
|
||||
"alignment_adjustment_samples": 14,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.9575575,
|
||||
"mean_q": 1.8561875,
|
||||
"power_code2": 5.48103,
|
||||
"ac_power_code2": 1.1186815990375,
|
||||
"rms_code": 2.3411599689043037,
|
||||
"peak_magnitude_code": 7.280109889280518,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 9.85413690444334,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002740515,
|
||||
"ac_rms_code": 1.0576774551050523
|
||||
},
|
||||
"background_label": "GAP_L2_L3",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.97165,
|
||||
"mean_q": 1.8424125,
|
||||
"power_code2": 4.8607875,
|
||||
"ac_power_code2": 0.5221999573437506,
|
||||
"rms_code": 2.204719369897221,
|
||||
"peak_magnitude_code": 4.47213595499958,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 6.14323360311441,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00024303937499999998,
|
||||
"ac_rms_code": 0.7226340410911671
|
||||
},
|
||||
"background_window_start_sample": 8283520,
|
||||
"matched_signal_power": 0.0453318643989967,
|
||||
"matched_signal_amplitude_rms": 0.21291280938214285,
|
||||
"residual_power": 0.740861773845582,
|
||||
"residual_to_matched_amplitude_ratio": 4.042656143280289,
|
||||
"matched_to_residual_db": -12.133336061267785,
|
||||
"complex_gain_h": {
|
||||
"real": 0.003384387504328541,
|
||||
"imag": -0.003391322182689881,
|
||||
"phase_rad_at_active_start": -0.7864216245934512
|
||||
},
|
||||
"gain_abs": 0.004791152776343041,
|
||||
"gain_power_abs_h_squared": 2.295514492625963e-05,
|
||||
"cfo_hz": -0.07606695286129402,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.009576142863317505,
|
||||
"residual_band_h2_power_code2": 0.004468597337512337,
|
||||
"residual_band_h3_power_code2": 0.002725169628283891,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
}
|
||||
],
|
||||
"background_windows": [
|
||||
{
|
||||
"label": "INITIAL_GUARD",
|
||||
"tx_relative_start_sample": 320000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 4443520,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.940225,
|
||||
"mean_q": 1.8409875,
|
||||
"power_code2": 4.7947875,
|
||||
"ac_power_code2": 0.5215294742187488,
|
||||
"rms_code": 2.189700321961889,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.7450314065030845,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000239739375,
|
||||
"ac_rms_code": 0.7221699759881665
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "GAP_L0_L1",
|
||||
"tx_relative_start_sample": 1760000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 5883520,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.95144375,
|
||||
"mean_q": 1.8329875,
|
||||
"power_code2": 4.78826875,
|
||||
"ac_power_code2": 0.5231803654296865,
|
||||
"rms_code": 2.1882113129220406,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.7509398714368505,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00023941343750000002,
|
||||
"ac_rms_code": 0.7233120802459243
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "GAP_L1_L2",
|
||||
"tx_relative_start_sample": 2960000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 7083520,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.9560375,
|
||||
"mean_q": 1.8575625,
|
||||
"power_code2": 4.88045,
|
||||
"ac_power_code2": 0.5159038571874985,
|
||||
"rms_code": 2.209174053803819,
|
||||
"peak_magnitude_code": 4.242640687119285,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 5.668126373000443,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002440225,
|
||||
"ac_rms_code": 0.7182644757939087
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "GAP_L2_L3",
|
||||
"tx_relative_start_sample": 4160000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 8283520,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.97165,
|
||||
"mean_q": 1.8424125,
|
||||
"power_code2": 4.8607875,
|
||||
"ac_power_code2": 0.5221999573437506,
|
||||
"rms_code": 2.204719369897221,
|
||||
"peak_magnitude_code": 4.47213595499958,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 6.14323360311441,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00024303937499999998,
|
||||
"ac_rms_code": 0.7226340410911671
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "FINAL_GUARD",
|
||||
"tx_relative_start_sample": 5440000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 9563520,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.958375,
|
||||
"mean_q": 1.83606875,
|
||||
"power_code2": 4.81993125,
|
||||
"ac_power_code2": 0.5303001546484385,
|
||||
"rms_code": 2.195434182570728,
|
||||
"peak_magnitude_code": 4.47213595499958,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 6.179891520223454,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002409965625,
|
||||
"ac_rms_code": 0.7282171068084288
|
||||
}
|
||||
}
|
||||
],
|
||||
"background_summary": {
|
||||
"ac_power_min_code2": 0.5159038571874985,
|
||||
"ac_power_max_code2": 0.5303001546484385,
|
||||
"ac_rms_min_code": 0.7182644757939087,
|
||||
"ac_rms_max_code": 0.7282171068084288,
|
||||
"maximum_to_minimum_ac_power_db": 0.11952978854087751,
|
||||
"stability_rule_db": 3.0,
|
||||
"stable": true
|
||||
},
|
||||
"comparisons": [
|
||||
{
|
||||
"comparison": "L1/L0",
|
||||
"tx_input_difference_db": 5.9772704691441785,
|
||||
"rx_matched_power_difference_db": 5.959769689089129,
|
||||
"linearity_error_db": -0.01750078005504907,
|
||||
"tx_amplitude_ratio": 1.9900478704552953,
|
||||
"rx_matched_amplitude_ratio": 1.9860422557090833
|
||||
},
|
||||
{
|
||||
"comparison": "L2/L1",
|
||||
"tx_input_difference_db": 5.998157271536577,
|
||||
"rx_matched_power_difference_db": 5.828887063637578,
|
||||
"linearity_error_db": -0.16927020789899938,
|
||||
"tx_amplitude_ratio": 1.9948390610676463,
|
||||
"rx_matched_amplitude_ratio": 1.9563400830193338
|
||||
},
|
||||
{
|
||||
"comparison": "L3/L2",
|
||||
"tx_input_difference_db": 5.998257379520232,
|
||||
"rx_matched_power_difference_db": 6.020419771848732,
|
||||
"linearity_error_db": 0.022162392328500147,
|
||||
"tx_amplitude_ratio": 1.9948620524335567,
|
||||
"rx_matched_amplitude_ratio": 1.9999585213327857
|
||||
}
|
||||
],
|
||||
"total_comparison": {
|
||||
"comparison": "L3/L0",
|
||||
"tx_digital_power_difference_db": 17.97368512020099,
|
||||
"rx_matched_power_difference_db": 17.80907652457544,
|
||||
"total_linearity_error_db": -0.16460859562555186,
|
||||
"tx_amplitude_ratio": 7.9192536969014835,
|
||||
"rx_matched_amplitude_ratio": 7.770586982689514
|
||||
},
|
||||
"classification": {
|
||||
"case": "LINEAR_BUT_WEAK",
|
||||
"rules_fixed_before_rf": {
|
||||
"linear_max_abs_error_db": 1.0,
|
||||
"compression_upper_step_error_at_most_db": -1.5,
|
||||
"weak_l3_matched_to_residual_below_db": 3.0,
|
||||
"healthy_l3_matched_to_residual_at_least_db": 6.0
|
||||
}
|
||||
},
|
||||
"capture_valid": true,
|
||||
"capture_invalid_reasons": [],
|
||||
"tx_underruns_maximum": 0,
|
||||
"tx_underruns_final": 0,
|
||||
"rx_overruns_maximum": 0,
|
||||
"rx_overruns_final": 0,
|
||||
"rx_clipping": false
|
||||
}
|
||||
39
data/processed/lab064/software_gate.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"lab": "Lab064",
|
||||
"stage": "SOFTWARE_LINEARITY_GATE",
|
||||
"rf_tx_count": 0,
|
||||
"channel_model": "known linear complex gain plus DC and deterministic Gaussian noise",
|
||||
"rows": [
|
||||
{
|
||||
"label": "L0",
|
||||
"matched_signal_power": 4.310961695783257,
|
||||
"residual_power": 0.0018007645625517528,
|
||||
"gain_abs": 0.37000937351312335
|
||||
},
|
||||
{
|
||||
"label": "L1",
|
||||
"matched_signal_power": 17.072568748673206,
|
||||
"residual_power": 0.0017975392617719525,
|
||||
"gain_abs": 0.3700018841553234
|
||||
},
|
||||
{
|
||||
"label": "L2",
|
||||
"matched_signal_power": 67.93699349861977,
|
||||
"residual_power": 0.0017977879292746589,
|
||||
"gain_abs": 0.37000113388875977
|
||||
},
|
||||
{
|
||||
"label": "L3",
|
||||
"matched_signal_power": 270.35275600931345,
|
||||
"residual_power": 0.001803678589230678,
|
||||
"gain_abs": 0.3700016234349186
|
||||
}
|
||||
],
|
||||
"linearity_errors_db": [
|
||||
-2.3407130973929213e-05,
|
||||
-8.27380804224731e-05,
|
||||
-1.2603446491787906e-05
|
||||
],
|
||||
"passed": true,
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9"
|
||||
}
|
||||
232
data/processed/lab065/capture_result.json
Normal file
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"lab": "Lab065",
|
||||
"stage": "SINGLE_RF_LEVEL_CAPTURE",
|
||||
"passed": true,
|
||||
"rf_tx_count": 1,
|
||||
"automatic_tx_repeats": 0,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 20000,
|
||||
"samples_per_symbol": 400,
|
||||
"digital_carrier_hz": 50000,
|
||||
"rrc_rolloff": 0.35,
|
||||
"rrc_span_symbols": 10,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": false,
|
||||
"tx_vga_db": 20,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [
|
||||
-18.0,
|
||||
-12.0,
|
||||
-6.0,
|
||||
0.0
|
||||
],
|
||||
"geometry_unchanged_from_lab064": true,
|
||||
"waveform_source": "Lab064 exact CS8 copy"
|
||||
},
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 292,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 2,
|
||||
"time_to_detect_ms": 136.89709990285337,
|
||||
"elapsed_ms": 136.89709990285337,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.08170008659363,
|
||||
"end_ms": 67.4550998955965,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 117.92290001176298,
|
||||
"end_ms": 136.89709990285337,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_level_before_tx": {
|
||||
"samples": 65536,
|
||||
"mean_i": 0.9449310302734375,
|
||||
"mean_q": 1.8423919677734375,
|
||||
"power_code2": 4.87310791015625,
|
||||
"ac_power_code2": 0.5858050952665499,
|
||||
"rms_code": 2.2075117010236323,
|
||||
"peak_magnitude_code": 4.47213595499958,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 6.132239671543243,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||
},
|
||||
"tx_process": {
|
||||
"returncode": 0,
|
||||
"stdout_file": "tx_stdout.txt",
|
||||
"stderr_file": "tx_stderr.txt"
|
||||
},
|
||||
"tx_counter_summary": {
|
||||
"expected_kind": "underrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "underrun",
|
||||
"text": "0 underruns",
|
||||
"character_offset": 337
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "underrun",
|
||||
"text": "0 underruns",
|
||||
"character_offset": 509
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_samples": 16000000,
|
||||
"rx_bytes": 32000000,
|
||||
"rx_duration_seconds": 2.0,
|
||||
"rx_counter_summary": {
|
||||
"expected_kind": "overrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 337
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 459
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 581
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 752
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_sha256": "845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e",
|
||||
"rx_level": {
|
||||
"samples": 16000000,
|
||||
"mean_i": 0.942780625,
|
||||
"mean_q": 1.8349653125,
|
||||
"power_code2": 5.6163274375,
|
||||
"ac_power_code2": 1.3603944325463857,
|
||||
"rms_code": 2.3698792031451728,
|
||||
"peak_magnitude_code": 10.816653826391969,
|
||||
"max_abs_i": 10.0,
|
||||
"max_abs_q": 10.0,
|
||||
"papr_db": 13.187334420841685,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||
},
|
||||
"rx_clipping": false,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 292,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
181
data/processed/lab065/preflight_result.json
Normal file
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"lab": "Lab065",
|
||||
"stage": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT",
|
||||
"passed": true,
|
||||
"rf_tx_count": 0,
|
||||
"stop_before_tx": true,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 20000,
|
||||
"samples_per_symbol": 400,
|
||||
"digital_carrier_hz": 50000,
|
||||
"rrc_rolloff": 0.35,
|
||||
"rrc_span_symbols": 10,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": false,
|
||||
"tx_vga_db": 20,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [
|
||||
-18.0,
|
||||
-12.0,
|
||||
-6.0,
|
||||
0.0
|
||||
],
|
||||
"geometry_unchanged_from_lab064": true,
|
||||
"waveform_source": "Lab064 exact CS8 copy"
|
||||
},
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 289,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 3,
|
||||
"time_to_detect_ms": 201.98559993878007,
|
||||
"elapsed_ms": 201.98559993878007,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.617999862879515,
|
||||
"end_ms": 66.89479993656278,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 117.23309988155961,
|
||||
"end_ms": 136.1208998132497,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 3,
|
||||
"start_ms": 187.1324998792261,
|
||||
"end_ms": 201.98559993878007,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_process": {
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "call hackrf_set_sample_rate(8000000 Hz/8.000 MHz)\ncall hackrf_set_hw_sync_mode(0)\ncall hackrf_set_freq(1400000000 Hz/1400.000 MHz)\ncall hackrf_set_amp_enable(0)\ncall hackrf_set_antenna_enable(0)\nsamples_to_xfer 800000/0Mio\nStop with Ctrl-C\n 1.8 MiB / 0.116 sec = 15.9 MiB/second, average power -34.8 dBfs, 9024 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11556 s\nhackrf_stop_rx() done\nTransfer statistics:\n1847520 bytes transferred by M0\n1835008 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n"
|
||||
},
|
||||
"rx_counter_summary": {
|
||||
"expected_kind": "overrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 333
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 502
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_level": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.94889625,
|
||||
"mean_q": 1.85107375,
|
||||
"power_code2": 5.4432075,
|
||||
"ac_power_code2": 1.1163293787968742,
|
||||
"rms_code": 2.333068258752838,
|
||||
"peak_magnitude_code": 8.06225774829855,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 8.0,
|
||||
"papr_db": 10.770584663112091,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
|
||||
"symbol_energy": "NOT_APPLICABLE_TO_BACKGROUND",
|
||||
"ac_rms_code": 1.0565648956864289,
|
||||
"unexpected_strong_signal": false
|
||||
},
|
||||
"rx_sha256": "be3c61dfa1bc40d1a9f6777689026804a1ccfc340b82680d3ec5304ddd68f78c",
|
||||
"rx_overruns": 0,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 289,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
219
data/processed/lab065/preparation.json
Normal file
@@ -0,0 +1,219 @@
|
||||
{
|
||||
"lab": "Lab065",
|
||||
"stage": "PREPARATION_ONLY",
|
||||
"rf_tx_count": 0,
|
||||
"head": "cff226e0bf9e36a627829ea11a4ca3292ae20953",
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 20000,
|
||||
"samples_per_symbol": 400,
|
||||
"digital_carrier_hz": 50000,
|
||||
"rrc_rolloff": 0.35,
|
||||
"rrc_span_symbols": 10,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": false,
|
||||
"tx_vga_db": 20,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [
|
||||
-18.0,
|
||||
-12.0,
|
||||
-6.0,
|
||||
0.0
|
||||
],
|
||||
"geometry_unchanged_from_lab064": true,
|
||||
"waveform_source": "Lab064 exact CS8 copy"
|
||||
},
|
||||
"segments": [
|
||||
{
|
||||
"label": "L0",
|
||||
"level_db": -18.0,
|
||||
"start_sample": 800000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 808400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": -9.625e-05,
|
||||
"mean_q": 2.75e-05,
|
||||
"power_code2": 30.87065625,
|
||||
"ac_power_code2": 30.870656239979688,
|
||||
"rms_code": 5.556136809870686,
|
||||
"peak_magnitude_code": 9.219544457292887,
|
||||
"max_abs_i": 9.0,
|
||||
"max_abs_q": 9.0,
|
||||
"papr_db": 4.398730638796093,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0015435328125,
|
||||
"requested_relative_power_db": -18.0,
|
||||
"tx_rms_code": 5.556136809870686,
|
||||
"tx_peak_abs_i_code": 9.0,
|
||||
"tx_peak_abs_q_code": 9.0,
|
||||
"tx_peak_magnitude_code": 9.219544457292887,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": -17.97368512020099
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L1",
|
||||
"level_db": -12.0,
|
||||
"start_sample": 2000000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 2008400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": -5e-05,
|
||||
"mean_q": 5e-05,
|
||||
"power_code2": 122.2567675,
|
||||
"ac_power_code2": 122.25676749499999,
|
||||
"rms_code": 11.056978226441435,
|
||||
"peak_magnitude_code": 17.804493814764857,
|
||||
"max_abs_i": 17.0,
|
||||
"max_abs_q": 17.0,
|
||||
"papr_db": 4.1378635346865025,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.006112838375,
|
||||
"requested_relative_power_db": -12.0,
|
||||
"tx_rms_code": 11.056978226441435,
|
||||
"tx_peak_abs_i_code": 17.0,
|
||||
"tx_peak_abs_q_code": 17.0,
|
||||
"tx_peak_magnitude_code": 17.804493814764857,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": -11.99641465105681
|
||||
},
|
||||
"tx_input_difference_from_previous_db": 5.9772704691441785
|
||||
},
|
||||
{
|
||||
"label": "L2",
|
||||
"level_db": -6.0,
|
||||
"start_sample": 3200000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 3208400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": 5.625e-05,
|
||||
"mean_q": 2.625e-05,
|
||||
"power_code2": 486.5064875,
|
||||
"ac_power_code2": 486.50648749614686,
|
||||
"rms_code": 22.05689206347984,
|
||||
"peak_magnitude_code": 35.12833614050059,
|
||||
"max_abs_i": 34.0,
|
||||
"max_abs_q": 35.0,
|
||||
"papr_db": 4.0422652379446395,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.024325324375,
|
||||
"requested_relative_power_db": -6.0,
|
||||
"tx_rms_code": 22.05689206347984,
|
||||
"tx_peak_abs_i_code": 34.0,
|
||||
"tx_peak_abs_q_code": 35.0,
|
||||
"tx_peak_magnitude_code": 35.12833614050059,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": -5.998257379520232
|
||||
},
|
||||
"tx_input_difference_from_previous_db": 5.998157271536577
|
||||
},
|
||||
{
|
||||
"label": "L3",
|
||||
"level_db": 0.0,
|
||||
"start_sample": 4400000,
|
||||
"samples": 800000,
|
||||
"duration_seconds": 0.1,
|
||||
"analysis_start_sample": 4408400,
|
||||
"analysis_samples": 783200,
|
||||
"metrics": {
|
||||
"samples": 800000,
|
||||
"mean_i": 8.25e-05,
|
||||
"mean_q": 0.00011125,
|
||||
"power_code2": 1936.04021375,
|
||||
"ac_power_code2": 1936.0402137308172,
|
||||
"rms_code": 44.00045697205883,
|
||||
"peak_magnitude_code": 69.6419413859206,
|
||||
"max_abs_i": 69.0,
|
||||
"max_abs_q": 69.0,
|
||||
"papr_db": 3.9882736474748155,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0968020106875,
|
||||
"requested_relative_power_db": 0.0,
|
||||
"tx_rms_code": 44.00045697205883,
|
||||
"tx_peak_abs_i_code": 69.0,
|
||||
"tx_peak_abs_q_code": 69.0,
|
||||
"tx_peak_magnitude_code": 69.6419413859206,
|
||||
"digital_clipping": false,
|
||||
"actual_relative_power_db": 0.0
|
||||
},
|
||||
"tx_input_difference_from_previous_db": 5.998257379520232
|
||||
}
|
||||
],
|
||||
"tx_samples": 6000000,
|
||||
"tx_bytes": 12000000,
|
||||
"tx_duration_seconds": 0.75,
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"source_tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"waveform_unchanged_from_lab064": true,
|
||||
"automatic_retry": false,
|
||||
"tx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc323b97d3",
|
||||
"-t",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab065\\tx_level_staircase.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"6000000",
|
||||
"-x",
|
||||
"20",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
],
|
||||
"rx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"-r",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab065\\rx_stream.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"16000000",
|
||||
"-l",
|
||||
"24",
|
||||
"-g",
|
||||
"24",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
]
|
||||
}
|
||||
515
data/processed/lab065/rf_result.json
Normal file
@@ -0,0 +1,515 @@
|
||||
{
|
||||
"lab": "Lab065",
|
||||
"stage": "OFFLINE_RF_LEVEL_LINEARITY_ANALYSIS",
|
||||
"packet_receiver_used": false,
|
||||
"location": {
|
||||
"start_sample": 4217216,
|
||||
"correlation": 0.28566040369708917,
|
||||
"coarse_cfo_hz": 0.0,
|
||||
"stride": 32
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"label": "L0",
|
||||
"level_db": -18.0,
|
||||
"rx_start_sample": 5017215,
|
||||
"alignment_adjustment_samples": -1,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.9456825,
|
||||
"mean_q": 1.849345,
|
||||
"power_code2": 5.91858,
|
||||
"ac_power_code2": 1.6041876801687494,
|
||||
"rms_code": 2.4328131864160882,
|
||||
"peak_magnitude_code": 7.280109889280518,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 9.520583473606806,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000295929,
|
||||
"ac_rms_code": 1.2665653082919766
|
||||
},
|
||||
"background_label": "INITIAL_GUARD",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.9375625,
|
||||
"mean_q": 1.83864375,
|
||||
"power_code2": 4.84290625,
|
||||
"ac_power_code2": 0.5832719691796884,
|
||||
"rms_code": 2.200660412239926,
|
||||
"peak_magnitude_code": 4.47213595499958,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 6.1592393370019245,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00024214531250000003,
|
||||
"ac_rms_code": 0.7637224425009969
|
||||
},
|
||||
"matched_signal_power": 0.009849829325472823,
|
||||
"residual_power": 1.147671646182205,
|
||||
"matched_to_residual_db": -20.663889470509574,
|
||||
"complex_gain_h": {
|
||||
"real": -0.0069147093491316635,
|
||||
"imag": 0.016278708105995708,
|
||||
"phase_rad_at_active_start": 1.9724722764994853
|
||||
},
|
||||
"gain_abs": 0.01768642256034779,
|
||||
"gain_power_abs_h_squared": 0.00031280954298317925,
|
||||
"cfo_hz": 0.051385857393477286,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.005700729949335216,
|
||||
"residual_band_h2_power_code2": 0.004639110794845057,
|
||||
"residual_band_h3_power_code2": 0.003007515042889266,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L1",
|
||||
"level_db": -12.0,
|
||||
"rx_start_sample": 6217220,
|
||||
"alignment_adjustment_samples": 4,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.943035,
|
||||
"mean_q": 1.84948125,
|
||||
"power_code2": 5.45620125,
|
||||
"ac_power_code2": 1.1463053446734381,
|
||||
"rms_code": 2.335851290215197,
|
||||
"peak_magnitude_code": 7.810249675906654,
|
||||
"max_abs_i": 7.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 10.48439454272797,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00027281006250000003,
|
||||
"ac_rms_code": 1.0706565017191265
|
||||
},
|
||||
"background_label": "GAP_L0_L1",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.94049375,
|
||||
"mean_q": 1.8457,
|
||||
"power_code2": 4.89708125,
|
||||
"ac_power_code2": 0.6059442662109387,
|
||||
"rms_code": 2.2129349854887286,
|
||||
"peak_magnitude_code": 5.0990195135927845,
|
||||
"max_abs_i": 4.0,
|
||||
"max_abs_q": 5.0,
|
||||
"papr_db": 7.250360382891476,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002448540625,
|
||||
"ac_rms_code": 0.7784242199539648
|
||||
},
|
||||
"matched_signal_power": 0.038319468321711674,
|
||||
"residual_power": 0.6202840436902609,
|
||||
"matched_to_residual_db": -12.091711347697867,
|
||||
"complex_gain_h": {
|
||||
"real": -0.010179637303215595,
|
||||
"imag": 0.014270629544800722,
|
||||
"phase_rad_at_active_start": 2.1904112551609387
|
||||
},
|
||||
"gain_abs": 0.017529286443833295,
|
||||
"gain_power_abs_h_squared": 0.00030727588322995773,
|
||||
"cfo_hz": -0.015456037277818255,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.0075272705585461884,
|
||||
"residual_band_h2_power_code2": 0.005178438128918921,
|
||||
"residual_band_h3_power_code2": 0.0030044589971250603,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L2",
|
||||
"level_db": -6.0,
|
||||
"rx_start_sample": 7417219,
|
||||
"alignment_adjustment_samples": 3,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.94256,
|
||||
"mean_q": 1.85174125,
|
||||
"power_code2": 5.55749625,
|
||||
"ac_power_code2": 1.2401312394484378,
|
||||
"rms_code": 2.3574342514691686,
|
||||
"peak_magnitude_code": 7.280109889280518,
|
||||
"max_abs_i": 6.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 9.793966912760688,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002778748125,
|
||||
"ac_rms_code": 1.1136117992588073
|
||||
},
|
||||
"background_label": "GAP_L1_L2",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.94511875,
|
||||
"mean_q": 1.8435375,
|
||||
"power_code2": 4.89561875,
|
||||
"ac_power_code2": 0.6037387844921858,
|
||||
"rms_code": 2.2126045173053406,
|
||||
"peak_magnitude_code": 8.06225774829855,
|
||||
"max_abs_i": 7.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 11.231057671986083,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002447809375,
|
||||
"ac_rms_code": 0.7770062963015073
|
||||
},
|
||||
"matched_signal_power": 0.15371936701352645,
|
||||
"residual_power": 0.7099975700355287,
|
||||
"matched_to_residual_db": -6.645282748943733,
|
||||
"complex_gain_h": {
|
||||
"real": -0.00913356439809709,
|
||||
"imag": 0.015044605957089683,
|
||||
"phase_rad_at_active_start": 2.1164192954656
|
||||
},
|
||||
"gain_abs": 0.01760006156291179,
|
||||
"gain_power_abs_h_squared": 0.000309762167018285,
|
||||
"cfo_hz": -0.02238057704898943,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.016197144376941752,
|
||||
"residual_band_h2_power_code2": 0.007835153285271243,
|
||||
"residual_band_h3_power_code2": 0.003956469302302745,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L3",
|
||||
"level_db": 0.0,
|
||||
"rx_start_sample": 8617220,
|
||||
"alignment_adjustment_samples": 4,
|
||||
"full_iq": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.941795,
|
||||
"mean_q": 1.85647375,
|
||||
"power_code2": 6.38914875,
|
||||
"ac_power_code2": 2.055676143535938,
|
||||
"rms_code": 2.52767655169723,
|
||||
"peak_magnitude_code": 8.602325267042627,
|
||||
"max_abs_i": 7.0,
|
||||
"max_abs_q": 8.0,
|
||||
"papr_db": 10.637887203839098,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00031945743750000003,
|
||||
"ac_rms_code": 1.4337629314276255
|
||||
},
|
||||
"background_label": "GAP_L2_L3",
|
||||
"background": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.9481,
|
||||
"mean_q": 1.8589625,
|
||||
"power_code2": 4.9655625,
|
||||
"ac_power_code2": 0.6109273135937494,
|
||||
"rms_code": 2.228354213315289,
|
||||
"peak_magnitude_code": 6.324555320336759,
|
||||
"max_abs_i": 5.0,
|
||||
"max_abs_q": 6.0,
|
||||
"papr_db": 9.060915387364618,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000248278125,
|
||||
"ac_rms_code": 0.7816183938430246
|
||||
},
|
||||
"matched_signal_power": 0.609773488771332,
|
||||
"residual_power": 1.8217771923731447,
|
||||
"matched_to_residual_db": -4.753267221075902,
|
||||
"complex_gain_h": {
|
||||
"real": -0.009107358713761582,
|
||||
"imag": 0.015027742846344474,
|
||||
"phase_rad_at_active_start": 2.1156425161969685
|
||||
},
|
||||
"gain_abs": 0.017572052748526183,
|
||||
"gain_power_abs_h_squared": 0.0003087770377969866,
|
||||
"cfo_hz": -0.006443832004222744,
|
||||
"residual_spectrum": {
|
||||
"residual_band_h1_power_code2": 0.05148134451389538,
|
||||
"residual_band_h2_power_code2": 0.019651663054535452,
|
||||
"residual_band_h3_power_code2": 0.008610269373925542,
|
||||
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
}
|
||||
}
|
||||
],
|
||||
"background_windows": [
|
||||
{
|
||||
"label": "INITIAL_GUARD",
|
||||
"tx_relative_start_sample": 320000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 4537216,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.9375625,
|
||||
"mean_q": 1.83864375,
|
||||
"power_code2": 4.84290625,
|
||||
"ac_power_code2": 0.5832719691796884,
|
||||
"rms_code": 2.200660412239926,
|
||||
"peak_magnitude_code": 4.47213595499958,
|
||||
"max_abs_i": 3.0,
|
||||
"max_abs_q": 4.0,
|
||||
"papr_db": 6.1592393370019245,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.00024214531250000003,
|
||||
"ac_rms_code": 0.7637224425009969
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "GAP_L0_L1",
|
||||
"tx_relative_start_sample": 1760000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 5977216,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.94049375,
|
||||
"mean_q": 1.8457,
|
||||
"power_code2": 4.89708125,
|
||||
"ac_power_code2": 0.6059442662109387,
|
||||
"rms_code": 2.2129349854887286,
|
||||
"peak_magnitude_code": 5.0990195135927845,
|
||||
"max_abs_i": 4.0,
|
||||
"max_abs_q": 5.0,
|
||||
"papr_db": 7.250360382891476,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002448540625,
|
||||
"ac_rms_code": 0.7784242199539648
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "GAP_L1_L2",
|
||||
"tx_relative_start_sample": 2960000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 7177216,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.94511875,
|
||||
"mean_q": 1.8435375,
|
||||
"power_code2": 4.89561875,
|
||||
"ac_power_code2": 0.6037387844921858,
|
||||
"rms_code": 2.2126045173053406,
|
||||
"peak_magnitude_code": 8.06225774829855,
|
||||
"max_abs_i": 7.0,
|
||||
"max_abs_q": 7.0,
|
||||
"papr_db": 11.231057671986083,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002447809375,
|
||||
"ac_rms_code": 0.7770062963015073
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "GAP_L2_L3",
|
||||
"tx_relative_start_sample": 4160000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 8377216,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.9481,
|
||||
"mean_q": 1.8589625,
|
||||
"power_code2": 4.9655625,
|
||||
"ac_power_code2": 0.6109273135937494,
|
||||
"rms_code": 2.228354213315289,
|
||||
"peak_magnitude_code": 6.324555320336759,
|
||||
"max_abs_i": 5.0,
|
||||
"max_abs_q": 6.0,
|
||||
"papr_db": 9.060915387364618,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000248278125,
|
||||
"ac_rms_code": 0.7816183938430246
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "FINAL_GUARD",
|
||||
"tx_relative_start_sample": 5440000,
|
||||
"samples": 160000,
|
||||
"duration_seconds": 0.02,
|
||||
"rx_start_sample": 9657216,
|
||||
"metrics": {
|
||||
"samples": 160000,
|
||||
"mean_i": 0.94553125,
|
||||
"mean_q": 1.8361,
|
||||
"power_code2": 5.00006875,
|
||||
"ac_power_code2": 0.7347761952734375,
|
||||
"rms_code": 2.2360833504142907,
|
||||
"peak_magnitude_code": 9.486832980505138,
|
||||
"max_abs_i": 9.0,
|
||||
"max_abs_q": 9.0,
|
||||
"papr_db": 12.55266533595234,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.0002500034375,
|
||||
"ac_rms_code": 0.8571908744692966
|
||||
}
|
||||
}
|
||||
],
|
||||
"background_summary": {
|
||||
"ac_power_min_code2": 0.5832719691796884,
|
||||
"ac_power_max_code2": 0.7347761952734375,
|
||||
"ac_rms_min_code": 0.7637224425009969,
|
||||
"ac_rms_max_code": 0.8571908744692966,
|
||||
"maximum_to_minimum_ac_power_db": 1.0028397226073626,
|
||||
"stability_rule_db": 3.0,
|
||||
"stable": true
|
||||
},
|
||||
"comparisons": [
|
||||
{
|
||||
"comparison": "L1/L0",
|
||||
"tx_input_difference_db": 5.9772704691441785,
|
||||
"rx_matched_power_difference_db": 5.899907694072936,
|
||||
"linearity_error_db": -0.07736277507124267
|
||||
},
|
||||
{
|
||||
"comparison": "L2/L1",
|
||||
"tx_input_difference_db": 5.998157271536577,
|
||||
"rx_matched_power_difference_db": 6.033091127942342,
|
||||
"linearity_error_db": 0.03493385640576463
|
||||
},
|
||||
{
|
||||
"comparison": "L3/L2",
|
||||
"tx_input_difference_db": 5.998257379520232,
|
||||
"rx_matched_power_difference_db": 5.9843995108689985,
|
||||
"linearity_error_db": -0.013857868651233396
|
||||
}
|
||||
],
|
||||
"capture_valid": true,
|
||||
"capture_invalid_reasons": [],
|
||||
"tx_underruns_maximum": 0,
|
||||
"rx_overruns_maximum": 0,
|
||||
"rx_clipping": false,
|
||||
"lab064_to_lab065": {
|
||||
"rows": [
|
||||
{
|
||||
"label": "L0",
|
||||
"tx_vga_nominal_difference_db": 10.0,
|
||||
"rx_matched_power_difference_db": 11.179327767173566,
|
||||
"gain_power_abs_h_squared_difference_db": 11.179327767173568,
|
||||
"residual_power_difference_db": -0.24327621738947727,
|
||||
"matched_to_residual_improvement_db": 11.422603984563047,
|
||||
"complex_gain_h_old": {
|
||||
"real": 0.0018760642981310563,
|
||||
"imag": -0.0045080629571906915,
|
||||
"phase_rad_at_active_start": -1.1764391150184978
|
||||
},
|
||||
"complex_gain_h_new": {
|
||||
"real": -0.0069147093491316635,
|
||||
"imag": 0.016278708105995708,
|
||||
"phase_rad_at_active_start": 1.9724722764994853
|
||||
},
|
||||
"complex_gain_phase_change_degrees": -179.5806672053524,
|
||||
"residual_spectral_band_difference_db": {
|
||||
"h1_50000_hz": 2.869745130254167,
|
||||
"h2_100000_hz": 2.6545689216728405,
|
||||
"h3_150000_hz": 2.25865088789477
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L1",
|
||||
"tx_vga_nominal_difference_db": 10.0,
|
||||
"rx_matched_power_difference_db": 11.119465772157373,
|
||||
"gain_power_abs_h_squared_difference_db": 11.119465772157373,
|
||||
"residual_power_difference_db": -2.3662859353114283,
|
||||
"matched_to_residual_improvement_db": 13.4857517074688,
|
||||
"complex_gain_h_old": {
|
||||
"real": 0.004471119012843702,
|
||||
"imag": -0.001937685853593986,
|
||||
"phase_rad_at_active_start": -0.40894566669212534
|
||||
},
|
||||
"complex_gain_h_new": {
|
||||
"real": -0.010179637303215595,
|
||||
"imag": 0.014270629544800722,
|
||||
"phase_rad_at_active_start": 2.1904112551609387
|
||||
},
|
||||
"complex_gain_phase_change_degrees": 148.9321810702975,
|
||||
"residual_spectral_band_difference_db": {
|
||||
"h1_50000_hz": 3.0326799855695237,
|
||||
"h2_100000_hz": 2.378560725133286,
|
||||
"h3_150000_hz": 1.3007160057992286
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L2",
|
||||
"tx_vga_nominal_difference_db": 10.0,
|
||||
"rx_matched_power_difference_db": 11.323669836462136,
|
||||
"gain_power_abs_h_squared_difference_db": 11.323669836462136,
|
||||
"residual_power_difference_db": 1.2772838599246652,
|
||||
"matched_to_residual_improvement_db": 10.046385976537472,
|
||||
"complex_gain_h_old": {
|
||||
"real": 0.0032772531007889995,
|
||||
"imag": -0.003478187284280655,
|
||||
"phase_rad_at_active_start": -0.8151334504253348
|
||||
},
|
||||
"complex_gain_h_new": {
|
||||
"real": -0.00913356439809709,
|
||||
"imag": 0.015044605957089683,
|
||||
"phase_rad_at_active_start": 2.1164192954656
|
||||
},
|
||||
"complex_gain_phase_change_degrees": 167.96559975953804,
|
||||
"residual_spectral_band_difference_db": {
|
||||
"h1_50000_hz": 5.594944314904455,
|
||||
"h2_100000_hz": 4.091263491385863,
|
||||
"h3_150000_hz": 2.888396722883724
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "L3",
|
||||
"tx_vga_nominal_difference_db": 10.0,
|
||||
"rx_matched_power_difference_db": 11.287649575482401,
|
||||
"gain_power_abs_h_squared_difference_db": 11.287649575482403,
|
||||
"residual_power_difference_db": 3.907580735290519,
|
||||
"matched_to_residual_improvement_db": 7.380068840191884,
|
||||
"complex_gain_h_old": {
|
||||
"real": 0.003384387504328541,
|
||||
"imag": -0.003391322182689881,
|
||||
"phase_rad_at_active_start": -0.7864216245934512
|
||||
},
|
||||
"complex_gain_h_new": {
|
||||
"real": -0.009107358713761582,
|
||||
"imag": 0.015027742846344474,
|
||||
"phase_rad_at_active_start": 2.1156425161969685
|
||||
},
|
||||
"complex_gain_phase_change_degrees": 166.2760271435506,
|
||||
"residual_spectral_band_difference_db": {
|
||||
"h1_50000_hz": 7.304592641009381,
|
||||
"h2_100000_hz": 6.432280867349566,
|
||||
"h3_150000_hz": 4.996231985221501
|
||||
}
|
||||
}
|
||||
],
|
||||
"rules_fixed_before_rf": {
|
||||
"internal_linear_max_abs_error_db": 1.0,
|
||||
"compression_upper_step_error_at_most_db": -1.5,
|
||||
"meaningful_matched_gain_median_at_least_db": 3.0,
|
||||
"meaningful_ratio_improvement_median_at_least_db": 3.0,
|
||||
"maximum_level_to_level_gain_spread_db": 3.0,
|
||||
"residual_tracks_if_within_matched_gain_db": 3.0
|
||||
}
|
||||
},
|
||||
"classification": {
|
||||
"case": "LINEAR_GAIN_IMPROVEMENT",
|
||||
"summary": {
|
||||
"median_matched_gain_db": 11.233488671327983,
|
||||
"median_residual_gain_db": 0.5170038212675939,
|
||||
"median_matched_to_residual_improvement_db": 10.73449498055026,
|
||||
"matched_gain_level_spread_db": 0.20420406430476312,
|
||||
"maximum_internal_linearity_error_db": 0.07736277507124267,
|
||||
"upper_step_linearity_error_db": -0.013857868651233396
|
||||
},
|
||||
"rules_fixed_before_rf": {
|
||||
"internal_linear_max_abs_error_db": 1.0,
|
||||
"compression_upper_step_error_at_most_db": -1.5,
|
||||
"meaningful_matched_gain_median_at_least_db": 3.0,
|
||||
"meaningful_ratio_improvement_median_at_least_db": 3.0,
|
||||
"maximum_level_to_level_gain_spread_db": 3.0,
|
||||
"residual_tracks_if_within_matched_gain_db": 3.0
|
||||
}
|
||||
}
|
||||
}
|
||||
17
data/processed/lab065/software_gate.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"lab": "Lab065",
|
||||
"stage": "SOFTWARE_GATE",
|
||||
"rf_tx_count": 0,
|
||||
"passed": true,
|
||||
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
|
||||
"waveform_unchanged_from_lab064": true,
|
||||
"lab064_baseline_classification": "LINEAR_BUT_WEAK",
|
||||
"classification_rules": {
|
||||
"internal_linear_max_abs_error_db": 1.0,
|
||||
"compression_upper_step_error_at_most_db": -1.5,
|
||||
"meaningful_matched_gain_median_at_least_db": 3.0,
|
||||
"meaningful_ratio_improvement_median_at_least_db": 3.0,
|
||||
"maximum_level_to_level_gain_spread_db": 3.0,
|
||||
"residual_tracks_if_within_matched_gain_db": 3.0
|
||||
}
|
||||
}
|
||||
224
data/processed/lab066/capture_result.json
Normal file
@@ -0,0 +1,224 @@
|
||||
{
|
||||
"lab": "Lab066",
|
||||
"stage": "SINGLE_RF_CAPTURE",
|
||||
"passed": true,
|
||||
"rf_tx_count": 1,
|
||||
"automatic_tx_repeats": 0,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 500000,
|
||||
"samples_per_symbol": 16,
|
||||
"digital_carrier_hz": 50000.0,
|
||||
"packets": 16,
|
||||
"payload_bytes": 256,
|
||||
"tx_vga_db": 20,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"geometry_unchanged": true,
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
|
||||
},
|
||||
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 300,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 2,
|
||||
"time_to_detect_ms": 131.2907999381423,
|
||||
"elapsed_ms": 131.2907999381423,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.125200068578124,
|
||||
"end_ms": 66.83470006100833,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 116.86519999057055,
|
||||
"end_ms": 131.2907999381423,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_level_before_tx": {
|
||||
"samples": 65536,
|
||||
"mean_i": 0.952423095703125,
|
||||
"mean_q": 1.83642578125,
|
||||
"power_code2": 5.656829833984375,
|
||||
"ac_power_code2": 1.377260430715979,
|
||||
"rms_code": 2.3784090972716143,
|
||||
"peak_magnitude_code": 5.385164807134504,
|
||||
"max_abs_i": 4.0,
|
||||
"max_abs_q": 5.0,
|
||||
"papr_db": 7.0982488319834305,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
|
||||
"ac_rms_code": 1.173567395046394,
|
||||
"unexpected_strong_signal": false
|
||||
},
|
||||
"tx_process": {
|
||||
"returncode": 0,
|
||||
"stdout_file": "tx_stdout.txt",
|
||||
"stderr_file": "tx_stderr.txt"
|
||||
},
|
||||
"tx_counter_summary": {
|
||||
"expected_kind": "underrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "underrun",
|
||||
"text": "0 underruns",
|
||||
"character_offset": 336
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "underrun",
|
||||
"text": "0 underruns",
|
||||
"character_offset": 506
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_counter_summary": {
|
||||
"expected_kind": "overrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 337
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 459
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 580
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 751
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_samples": 16000000,
|
||||
"rx_sha256": "72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0",
|
||||
"rx_level": {
|
||||
"samples": 16000000,
|
||||
"mean_i": 0.9510684375,
|
||||
"mean_q": 1.844211625,
|
||||
"power_code2": 5.0153848125,
|
||||
"ac_power_code2": 0.7097371219061666,
|
||||
"rms_code": 2.239505483918269,
|
||||
"peak_magnitude_code": 5.830951894845301,
|
||||
"max_abs_i": 4.0,
|
||||
"max_abs_q": 5.0,
|
||||
"papr_db": 8.311746565484663,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"symbol_energy_code2_seconds": 0.000250769240625,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||
},
|
||||
"rx_clipping": false,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 300,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
17
data/processed/lab066/packet_comparison.csv
Normal file
@@ -0,0 +1,17 @@
|
||||
packet_index,legacy_header,legacy_fec,legacy_crc,legacy_packet_output,t2_header,t2_fec,t2_crc,t2_packet_output
|
||||
1,False,False,False,False,False,False,False,False
|
||||
2,False,False,False,False,False,False,False,False
|
||||
3,False,False,False,False,False,False,False,False
|
||||
4,True,True,True,True,True,True,True,True
|
||||
5,True,True,True,True,True,True,True,True
|
||||
6,True,True,True,True,True,True,True,True
|
||||
7,True,True,True,True,True,True,True,True
|
||||
8,True,True,True,True,True,True,True,True
|
||||
9,False,False,False,False,False,False,False,False
|
||||
10,True,True,True,True,True,True,True,True
|
||||
11,True,True,True,True,True,True,True,True
|
||||
12,True,True,True,True,True,True,True,True
|
||||
13,True,True,True,True,True,True,True,True
|
||||
14,True,True,True,True,True,True,True,True
|
||||
15,False,False,False,False,False,False,False,False
|
||||
16,False,False,False,False,False,False,False,False
|
||||
|
164
data/processed/lab066/preflight_result.json
Normal file
@@ -0,0 +1,164 @@
|
||||
{
|
||||
"lab": "Lab066",
|
||||
"stage": "RX_ONLY_PREFLIGHT",
|
||||
"passed": true,
|
||||
"rf_tx_count": 0,
|
||||
"point": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 500000,
|
||||
"samples_per_symbol": 16,
|
||||
"digital_carrier_hz": 50000.0,
|
||||
"packets": 16,
|
||||
"payload_bytes": 256,
|
||||
"tx_vga_db": 20,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"geometry_unchanged": true,
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
|
||||
},
|
||||
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
|
||||
"stop_before_tx": true,
|
||||
"process_before": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 294,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"inventory": {
|
||||
"passed": true,
|
||||
"serials": [
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"000000000000000097c063dc323b97d3"
|
||||
],
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3",
|
||||
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
|
||||
},
|
||||
"clock": {
|
||||
"passed": true,
|
||||
"master_reg29": 38,
|
||||
"slave_reg29": 38,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
"slave_clock_wait": {
|
||||
"state": "DETECTED",
|
||||
"queries": 2,
|
||||
"time_to_detect_ms": 132.11270002648234,
|
||||
"elapsed_ms": 132.11270002648234,
|
||||
"polls": [
|
||||
{
|
||||
"query": 1,
|
||||
"start_ms": 50.09360006079078,
|
||||
"end_ms": 66.02549995295703,
|
||||
"raw": "CLKIN status: no clock signal detected",
|
||||
"state": "NOT_DETECTED",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"query": 2,
|
||||
"start_ms": 116.46570009179413,
|
||||
"end_ms": 132.11270002648234,
|
||||
"raw": "CLKIN status: clock signal detected",
|
||||
"state": "DETECTED",
|
||||
"exit_code": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"master_serial": "000000000000000097c063dc323b97d3",
|
||||
"slave_serial": "000000000000000097c063dc2e5327d3"
|
||||
},
|
||||
"rx_process": {
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "call hackrf_set_sample_rate(8000000 Hz/8.000 MHz)\ncall hackrf_set_hw_sync_mode(0)\ncall hackrf_set_freq(1400000000 Hz/1400.000 MHz)\ncall hackrf_set_amp_enable(0)\ncall hackrf_set_antenna_enable(0)\nsamples_to_xfer 800000/0Mio\nStop with Ctrl-C\n 1.8 MiB / 0.115 sec = 15.9 MiB/second, average power -35.2 dBfs, 11712 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11546 s\nhackrf_stop_rx() done\nTransfer statistics:\n1850016 bytes transferred by M0\n1835008 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n"
|
||||
},
|
||||
"rx_counter_summary": {
|
||||
"expected_kind": "overrun",
|
||||
"observations": [
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 334
|
||||
},
|
||||
{
|
||||
"count": 0,
|
||||
"role": "UNKNOWN",
|
||||
"kind": "overrun",
|
||||
"text": "0 overruns",
|
||||
"character_offset": 503
|
||||
}
|
||||
],
|
||||
"selected_counts": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"final_count": 0,
|
||||
"maximum_count": 0,
|
||||
"unparsed_loss_text": false,
|
||||
"clean": true
|
||||
},
|
||||
"rx_level": {
|
||||
"samples": 800000,
|
||||
"mean_i": 0.94753,
|
||||
"mean_q": 1.83594125,
|
||||
"power_code2": 4.95347625,
|
||||
"ac_power_code2": 0.6849828756484371,
|
||||
"rms_code": 2.2256406381084974,
|
||||
"peak_magnitude_code": 5.385164807134504,
|
||||
"max_abs_i": 4.0,
|
||||
"max_abs_q": 5.0,
|
||||
"papr_db": 7.674879128392212,
|
||||
"clipped_components_fraction": 0.0,
|
||||
"clipped_samples_fraction": 0.0,
|
||||
"saturation_detected": false,
|
||||
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
|
||||
"symbol_energy": "NOT_APPLICABLE_TO_BACKGROUND",
|
||||
"ac_rms_code": 0.8276369225985735,
|
||||
"unexpected_strong_signal": false
|
||||
},
|
||||
"rx_sha256": "ff817582e6a8fc03b489bf89f46322a9db8a9493335a4360a1b0f9bedad7c4d5",
|
||||
"rx_overruns": 0,
|
||||
"process_after": {
|
||||
"state": "CLEAR",
|
||||
"attempts": [
|
||||
{
|
||||
"state": "CLEAR",
|
||||
"mechanism": "psutil",
|
||||
"matches": [],
|
||||
"inspected": 294,
|
||||
"errors": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanup_errors": []
|
||||
}
|
||||
217
data/processed/lab066/preparation.json
Normal file
@@ -0,0 +1,217 @@
|
||||
{
|
||||
"lab": "Lab066",
|
||||
"stage": "PREPARATION_ONLY",
|
||||
"rf_tx_count": 0,
|
||||
"head": "dacf04f2d0a15c0c1b56a7fb527db61fc2e38462",
|
||||
"parameters": {
|
||||
"rf_frequency_hz": 1400000000,
|
||||
"sample_rate_hz": 8000000,
|
||||
"symbol_rate": 500000,
|
||||
"samples_per_symbol": 16,
|
||||
"digital_carrier_hz": 50000.0,
|
||||
"packets": 16,
|
||||
"payload_bytes": 256,
|
||||
"tx_vga_db": 20,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": false,
|
||||
"bias_tee": false,
|
||||
"geometry": {
|
||||
"frequency_hz": 1400000000,
|
||||
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
"path_confirmed": true,
|
||||
"tx_antenna": "existing 1400 MHz antenna near window",
|
||||
"rx_antenna": "existing 1400 MHz antenna on desk",
|
||||
"distance_m": 1.2,
|
||||
"orientation": "same polarization; fixed during preflight and RF session",
|
||||
"visibility": "LOS indoor"
|
||||
},
|
||||
"geometry_unchanged": true,
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
|
||||
},
|
||||
"packet_samples": 92256,
|
||||
"inter_packet_gap_seconds": 0.005,
|
||||
"inter_packet_gap_samples": 40000,
|
||||
"number_of_gaps": 15,
|
||||
"zero_guard_before_samples": 800000,
|
||||
"zero_guard_after_samples": 800000,
|
||||
"base_tx_samples": 2076096,
|
||||
"expected_tx_samples": 3676096,
|
||||
"tx_samples": 3676096,
|
||||
"tx_bytes": 7352192,
|
||||
"tx_duration_seconds": 0.459512,
|
||||
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
|
||||
"layout": [
|
||||
{
|
||||
"packet_index": 1,
|
||||
"start_sample": 800000,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "414dee46d0555da2caabe0d4e446eb52d22be2f380ed4529fd7f192420d06465",
|
||||
"following_gap_start_sample": 892256,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 2,
|
||||
"start_sample": 932256,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "f5304773cc626858c4ad1cfe509bfe40418cfee6d9c65718c7c271c664465f2d",
|
||||
"following_gap_start_sample": 1024512,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 3,
|
||||
"start_sample": 1064512,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "e5604108e4a0a0441b962cf65822838d817a228d5df899dfd64dab958f32a59c",
|
||||
"following_gap_start_sample": 1156768,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 4,
|
||||
"start_sample": 1196768,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "43a7b5866bcf826920aae650830abfd2fa75b6e5460078c1bbde3ec7b461703f",
|
||||
"following_gap_start_sample": 1289024,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 5,
|
||||
"start_sample": 1329024,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "46836a613f080ddeba9c1b23bc8d8dd981db24975115074fa01b31699099efb8",
|
||||
"following_gap_start_sample": 1421280,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 6,
|
||||
"start_sample": 1461280,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "6ff8c9b941d37c39834f8cc5be3fdf563b3e55c51fc0c7d7344d4e046a15d13c",
|
||||
"following_gap_start_sample": 1553536,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 7,
|
||||
"start_sample": 1593536,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "273543a396d7499a1e1b325415b38555c72bb2665a227cd88aff5b797e18875d",
|
||||
"following_gap_start_sample": 1685792,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 8,
|
||||
"start_sample": 1725792,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "20051b24e17274d3b0cf3c170ea09559409c18ff429a3020ff28cb83d9b7bb3b",
|
||||
"following_gap_start_sample": 1818048,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 9,
|
||||
"start_sample": 1858048,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "9239a9fdb3fc94354cc8372dfa06867a18500ad13f867ea3a4cd4468e76bb9f0",
|
||||
"following_gap_start_sample": 1950304,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 10,
|
||||
"start_sample": 1990304,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "997be4e3b7a26708a48afd8ac0932460dbb34d67a359f1770dd76214f810e658",
|
||||
"following_gap_start_sample": 2082560,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 11,
|
||||
"start_sample": 2122560,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "fb1d718a4c2fa358f24ba34197b3100384b0df4229efae846944949894526903",
|
||||
"following_gap_start_sample": 2214816,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 12,
|
||||
"start_sample": 2254816,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "778de948620ddcf8c70f639f72ed042f97915234a57055421af9cbeb16c47e1f",
|
||||
"following_gap_start_sample": 2347072,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 13,
|
||||
"start_sample": 2387072,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "cabcd273073d70d686900e180be3c59e970cb3633bb370ba2198e64540727190",
|
||||
"following_gap_start_sample": 2479328,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 14,
|
||||
"start_sample": 2519328,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "3d6fda80bdd274d781399b1d1831cb4cb4a7c17bcd69930330564bf0ed6eb125",
|
||||
"following_gap_start_sample": 2611584,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 15,
|
||||
"start_sample": 2651584,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "ece74586bedf93fc3f5e07f41f64eea4383425d43424fe209ca30d911279c6ff",
|
||||
"following_gap_start_sample": 2743840,
|
||||
"following_gap_samples": 40000
|
||||
},
|
||||
{
|
||||
"packet_index": 16,
|
||||
"start_sample": 2783840,
|
||||
"samples": 92256,
|
||||
"payload_sha256": "bba4133aea193e327938abc18d2a0215f1df0e821d378d530dc0b47498af9a5c"
|
||||
}
|
||||
],
|
||||
"rx_samples": 16000000,
|
||||
"rx_duration_seconds": 2,
|
||||
"automatic_retry": false,
|
||||
"tx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc323b97d3",
|
||||
"-t",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab066\\tx_stream.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"3676096",
|
||||
"-x",
|
||||
"20",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
],
|
||||
"rx_command_prepared_not_run": [
|
||||
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||
"-d",
|
||||
"000000000000000097c063dc2e5327d3",
|
||||
"-r",
|
||||
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab066\\rx_stream.cs8",
|
||||
"-f",
|
||||
"1400000000",
|
||||
"-s",
|
||||
"8000000",
|
||||
"-n",
|
||||
"16000000",
|
||||
"-l",
|
||||
"24",
|
||||
"-g",
|
||||
"24",
|
||||
"-a",
|
||||
"0",
|
||||
"-p",
|
||||
"0",
|
||||
"-B"
|
||||
]
|
||||
}
|
||||
3788
data/processed/lab066/rf_result.json
Normal file
382
data/processed/lab066/rf_summary.json
Normal file
@@ -0,0 +1,382 @@
|
||||
{
|
||||
"lab": "Lab066",
|
||||
"capture_valid": true,
|
||||
"legacy": {
|
||||
"candidates": 145,
|
||||
"header_pass": 10,
|
||||
"fec_started": 10,
|
||||
"fec_pass": 10,
|
||||
"crc_started": 10,
|
||||
"crc_pass": 10,
|
||||
"packet_output": 10,
|
||||
"exact_payload_match": 10,
|
||||
"packet_indexes": [
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14
|
||||
]
|
||||
},
|
||||
"t2": {
|
||||
"candidates": 145,
|
||||
"header_pass": 10,
|
||||
"fec_started": 10,
|
||||
"fec_pass": 10,
|
||||
"crc_started": 10,
|
||||
"crc_pass": 10,
|
||||
"packet_output": 10,
|
||||
"exact_payload_match": 10,
|
||||
"packet_indexes": [
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14
|
||||
]
|
||||
},
|
||||
"passed_by_both": [
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14
|
||||
],
|
||||
"passed_only_by_legacy": [],
|
||||
"passed_only_by_t2": [],
|
||||
"failed_by_both": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
9,
|
||||
15,
|
||||
16
|
||||
],
|
||||
"packet_rows": [
|
||||
{
|
||||
"packet_index": 1,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 2,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 3,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 4,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 5,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 6,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 7,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 8,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 9,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 10,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 11,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 12,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 13,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 14,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": true,
|
||||
"fec": true,
|
||||
"crc": true,
|
||||
"packet_output": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 15,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"packet_index": 16,
|
||||
"legacy": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
},
|
||||
"t2": {
|
||||
"candidate_at_expected_packet": true,
|
||||
"header": false,
|
||||
"fec": false,
|
||||
"crc": false,
|
||||
"packet_output": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"physical": {
|
||||
"symbol_error_rate": 0.011046635434412266,
|
||||
"rms_symbol_deviation": 0.5283737542111951,
|
||||
"matched_component_power": 8.359397909690596,
|
||||
"residual_power": 2.333261015636848,
|
||||
"matched_residual_db": 5.542116733950265,
|
||||
"band_gain_nonuniformity_p95_p05_db_median": 2.8700698316435833,
|
||||
"band_gain_nonuniformity_p95_p05_db_min": 2.7249057267348604,
|
||||
"band_gain_nonuniformity_p95_p05_db_max": 3.1116789732634516
|
||||
},
|
||||
"lab056_B": {
|
||||
"symbol_error_rate": 0.03495896464646465,
|
||||
"rms_symbol_deviation": 0.8453497751219741,
|
||||
"matched_residual_db": 1.4648455366051558,
|
||||
"band_gain_nonuniformity_db": 5.471381501203027
|
||||
},
|
||||
"difference_from_lab056_B": {
|
||||
"symbol_error_rate_absolute": -0.023912329212052382,
|
||||
"rms_symbol_deviation_absolute": -0.3169760209107789,
|
||||
"matched_residual_db": 4.077271197345109,
|
||||
"band_gain_nonuniformity_db": -2.6013116695594434
|
||||
},
|
||||
"lab062": {
|
||||
"symbol_error_rate": 0.0992866269165247,
|
||||
"rms_symbol_deviation": 1.2204009809742542,
|
||||
"matched_residual_db": -1.7300509612417754,
|
||||
"band_gain_nonuniformity_db": 6.660340655081576,
|
||||
"capture_status": "RF_SESSION_INVALID_TX_UNDERRUN"
|
||||
},
|
||||
"difference_from_lab062": {
|
||||
"symbol_error_rate_absolute": -0.08823999148211244,
|
||||
"rms_symbol_deviation_absolute": -0.6920272267630591,
|
||||
"matched_residual_db": 7.27216769519204,
|
||||
"band_gain_nonuniformity_db": -3.790270823437993
|
||||
},
|
||||
"classification": "PARTIAL_PACKET_RECOVERY_NO_T2_GAIN",
|
||||
"primary_result": "Аппаратно валидный Lab066 дал одинаковые 10/16 точных пакетов в legacy и T2; физическое качество лучше Lab056 B и невалидной Lab062.",
|
||||
"recommended_next_single_step": "Отдельно разрешить офлайн-сравнение header-ошибок пакетов 1-3, 9, 15-16 с принятыми пакетами по сохранённому RX; новый TX не нужен."
|
||||
}
|
||||
507
data/processed/lab066/software_gate.json
Normal file
@@ -0,0 +1,507 @@
|
||||
{
|
||||
"lab": "Lab066",
|
||||
"stage": "SOFTWARE_GATE",
|
||||
"rf_tx_count": 0,
|
||||
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
|
||||
"legacy": {
|
||||
"mode": "legacy",
|
||||
"candidates": 16,
|
||||
"header_pass": 16,
|
||||
"fec_started": 16,
|
||||
"fec_pass": 16,
|
||||
"crc_started": 16,
|
||||
"crc_pass": 16,
|
||||
"packet_output": 16,
|
||||
"exact_payload_match": 16,
|
||||
"packet_indexes": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16
|
||||
],
|
||||
"exact_ordered_payloads": true,
|
||||
"t2_state_scope": "NOT_APPLICABLE",
|
||||
"fallback_between_modes": false,
|
||||
"passed": true,
|
||||
"rows": [
|
||||
{
|
||||
"absolute_refined": 800000,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 1,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 932256,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 2,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1064512,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 3,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1196768,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 4,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1329024,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 5,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1461280,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 6,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1593536,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 7,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1725792,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 8,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1858048,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 9,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1990304,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 10,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2122560,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 11,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2254816,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 12,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2387072,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 13,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2519328,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 14,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2651584,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 15,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2783840,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 16,
|
||||
"t2_tracking_runs_for_candidate": 0
|
||||
}
|
||||
],
|
||||
"detector_statistics": {
|
||||
"total_local_peaks": 80004,
|
||||
"first_stage_candidates": 21193,
|
||||
"cheap_stage_checked": 21193,
|
||||
"cheap_stage_rejected": 20800,
|
||||
"clusters": 37,
|
||||
"representative_checks": 16,
|
||||
"inside_accepted_frame": 377
|
||||
},
|
||||
"candidate_overflow": 0
|
||||
},
|
||||
"t2": {
|
||||
"mode": "t2",
|
||||
"candidates": 16,
|
||||
"header_pass": 16,
|
||||
"fec_started": 16,
|
||||
"fec_pass": 16,
|
||||
"crc_started": 16,
|
||||
"crc_pass": 16,
|
||||
"packet_output": 16,
|
||||
"exact_payload_match": 16,
|
||||
"packet_indexes": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16
|
||||
],
|
||||
"exact_ordered_payloads": true,
|
||||
"t2_state_scope": "NEW_PREAMBLE_ESTIMATE_PER_PACKET",
|
||||
"fallback_between_modes": false,
|
||||
"passed": true,
|
||||
"rows": [
|
||||
{
|
||||
"absolute_refined": 800000,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 1,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 932256,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 2,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1064512,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 3,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1196768,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 4,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1329024,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 5,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1461280,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 6,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1593536,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 7,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1725792,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 8,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1858048,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 9,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 1990304,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 10,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2122560,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 11,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2254816,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 12,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2387072,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 13,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2519328,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 14,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2651584,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 15,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
},
|
||||
{
|
||||
"absolute_refined": 2783840,
|
||||
"reason": "CRC_PASS",
|
||||
"header": true,
|
||||
"fec_started": true,
|
||||
"fec": true,
|
||||
"crc_started": true,
|
||||
"crc": true,
|
||||
"packet_output": true,
|
||||
"packet_index_after_verdict": 16,
|
||||
"t2_tracking_runs_for_candidate": 2
|
||||
}
|
||||
],
|
||||
"detector_statistics": {
|
||||
"total_local_peaks": 80004,
|
||||
"first_stage_candidates": 21193,
|
||||
"cheap_stage_checked": 21193,
|
||||
"cheap_stage_rejected": 20800,
|
||||
"clusters": 37,
|
||||
"representative_checks": 16,
|
||||
"inside_accepted_frame": 377
|
||||
},
|
||||
"candidate_overflow": 0,
|
||||
"t2_state_reset_evidence": {
|
||||
"estimate_source": "each candidate's own preamble",
|
||||
"context_scope": "new PacketMeasure context for every candidate",
|
||||
"tracking_runs_per_accepted_packet": [
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
"all_accepted_packets_have_fresh_tracking_runs": true,
|
||||
"persistent_tracking_state_in_packet_processor": false
|
||||
}
|
||||
},
|
||||
"gaps_verified_zero": true,
|
||||
"t2_state_reset_verified_by_fresh_preamble_per_packet": true,
|
||||
"passed": true
|
||||
}
|
||||
146
data/processed/lab067/candidate_analysis.csv
Normal file
@@ -0,0 +1,146 @@
|
||||
candidate_number,position,nearest_packet,difference_samples,category,strict_reason
|
||||
1,4902630,1,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
2,4900598,1,-2032,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
3,4904662,1,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
4,4898566,1,-4064,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
5,4896534,1,-6096,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
6,4906694,1,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
7,4894502,1,-8128,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
8,4908726,1,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
9,4910758,1,8128,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
10,4902630,1,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
11,4900598,1,-2032,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
12,4904662,1,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
13,4898566,1,-4064,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
14,4906694,1,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
15,4902630,1,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
16,4900598,1,-2032,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
17,4904662,1,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
18,4968617,1,65987,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
19,4958408,1,55778,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
20,4982295,2,-52591,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
|
||||
21,5034886,2,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
22,5032854,2,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
23,5036918,2,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
24,5030822,2,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
25,5028790,2,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
26,5038949,2,4063,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
27,5026758,2,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
28,5040981,2,6095,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
29,5043013,2,8127,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
30,5034886,2,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
31,5032854,2,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
32,5034886,2,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
33,5036918,2,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
34,5032854,2,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
35,5036918,2,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
36,5030822,2,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
37,5167142,3,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
|
||||
38,5165110,3,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
39,5169174,3,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
40,5163078,3,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
41,5161046,3,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
42,5171206,3,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
43,5159014,3,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
44,5173238,3,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
45,5175270,3,8128,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
46,5167142,3,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
|
||||
47,5169174,3,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
48,5165110,3,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
49,5171206,3,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
50,5163078,3,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
51,5167142,3,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
|
||||
52,5169174,3,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
53,5165110,3,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
54,5198234,3,31092,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
55,5196202,3,29060,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
56,5194172,3,27030,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
57,5228601,3,61459,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
58,5230634,3,63492,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
59,5232665,3,65523,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
60,5220198,3,53056,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
61,5299398,4,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
62,5431654,5,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
63,5563910,6,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
64,5696166,7,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
65,5828422,8,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
66,5960678,9,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
67,5958646,9,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
68,5962710,9,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
69,5956614,9,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
70,5954582,9,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
71,5952550,9,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
72,5960678,9,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
73,5958646,9,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
74,5962710,9,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
75,5956614,9,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
76,5960678,9,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
|
||||
77,5958646,9,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
78,5962710,9,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
79,5964742,9,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
80,5966774,9,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
81,5968806,9,8128,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
82,5964742,9,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
83,6017761,9,57083,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
84,6015727,9,55049,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
85,6092934,10,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
86,6225190,11,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
87,6355414,12,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
88,6353382,12,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
89,6351350,12,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
90,6349317,12,-8129,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
91,6355414,12,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
92,6353382,12,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
93,6351350,12,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
94,6355414,12,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
95,6357446,12,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
96,6487670,13,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
97,6485638,13,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
98,6483606,13,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
99,6481574,13,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
100,6487670,13,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
101,6485638,13,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
102,6483606,13,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
103,6487670,13,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
104,6485638,13,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
105,6489702,13,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
106,6617894,14,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
107,6615862,14,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
108,6613830,14,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
109,6617894,14,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
110,6621958,14,0,TRUE_PREAMBLE_POSITION,CRC_PASS
|
||||
111,6750150,15,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
112,6748118,15,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
113,6746086,15,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
114,6750150,15,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
115,6748118,15,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
116,6750150,15,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
117,6754214,15,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
|
||||
118,6752182,15,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
119,6756246,15,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
120,6758278,15,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
121,6760309,15,6095,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
122,6762341,15,8127,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
123,6754214,15,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
|
||||
124,6752182,15,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
125,6756246,15,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
126,6758278,15,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
127,6754214,15,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
|
||||
128,6752182,15,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
129,6756246,15,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
130,6880374,16,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
131,6878342,16,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
132,6886470,16,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_VERSION_MISMATCH
|
||||
133,6884438,16,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
134,6888502,16,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
135,6882406,16,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
136,6890534,16,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
137,6892566,16,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
138,6894597,16,8127,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
139,6888502,16,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
140,6886470,16,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_VERSION_MISMATCH
|
||||
141,6892566,16,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
142,6890534,16,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
143,6884438,16,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
|
||||
144,6888502,16,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
145,6902724,16,16254,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
|
||||
|
BIN
data/processed/lab067/candidate_checks_by_packet.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
data/processed/lab067/gap_background_by_packet.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
2305
data/processed/lab067/header_bit_errors.csv
Normal file
BIN
data/processed/lab067/header_error_position_map.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
data/processed/lab067/header_errors_by_packet.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
17
data/processed/lab067/header_packet_comparison.csv
Normal file
@@ -0,0 +1,17 @@
|
||||
packet,strict_result,candidate_position,expected_packet_position,candidate_difference_samples,preamble_errors,preamble_error_rate,preamble_correlation,header_raw_errors,header_voted_errors,header_copy_1_errors,header_copy_2_errors,header_copy_3_errors,magic_errors,version_errors,length_errors,checksum_errors,expected_header_bits,received_raw_header_bits,voted_header_bits,header_accepted,header_reason,legacy_header_bits,t2_header_bits,legacy_initial_frequency_hz,t2_initial_frequency_hz,reference_frequency_hz,legacy_frequency_error_vs_reference_hz,initial_phase_error_rad,timing_offset_samples,fractional_timing_offset,reference_timing_offset_samples,reference_fractional_timing_offset,local_matched_power,local_residual_power,local_matched_residual_db,rms_symbol_deviation,legacy_t2_header_differences,classification
|
||||
1,HEADER_FAIL,4902630,4902630,0,15,0.01171875,0.8981139142983712,3,1,1,2,0,0,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000,110100111001000100000001000000010000110100001000,False,CHECKSUM_MISMATCH,111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000,111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000,-2.662272181646709,-5.3879680108224575,-2.6542601970280364,-0.008011984618672585,1.8341164291785854,0,0.0,0,0.0,8.982336070665001,2.7583799759789063,5.127352085281537,0.5541564541425134,0,HEADER_SPECIFIC_FAILURE
|
||||
2,HEADER_FAIL,5034886,5034886,0,10,0.0078125,0.892574719565451,6,1,3,2,1,0,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000001000001110000111111000000000000000000111000000000,110100111001000100000001000000010010110000001000,False,CHECKSUM_MISMATCH,111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000001000001110000111111000000000000000000111000000000,111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000011000001110000111111000000000000000000111000000000,-11.085402212024045,-5.423727145761877,2.664169158774504,-13.749571370798549,1.8865220093820032,0,0.0,0,0.0,8.566883580374462,3.867819679092585,3.4535664637802532,0.6719263565992358,1,HEADER_SPECIFIC_FAILURE
|
||||
3,HEADER_FAIL,5167142,5167142,0,11,0.00859375,0.8832445602414427,3,1,1,1,1,1,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100110001000100000001000000010000110000001000,False,MAGIC_MISMATCH,111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-2.14390103272374,2.717358680061899,4.3756668288999,-6.51956786162364,1.837976468233186,0,0.0,0,0.0,8.391114739306301,2.917171371178907,4.5885771684300085,0.589618554298201,0,HEADER_SPECIFIC_FAILURE
|
||||
4,PASS,5299398,5299398,0,12,0.009375,0.8702559090330103,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-4.969843852460903,0.5011874511379912,3.928841196279306,-8.898685048740209,1.823004794704275,0,0.0,0,0.0,8.221366239802954,2.9179318124892393,4.498688563977461,0.5957520861601466,0,PASSED
|
||||
5,PASS,5431654,5431654,0,15,0.01171875,0.882298526549992,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,3.163024030078619,-1.2710648335872299,1.1365290837564324,2.026494946322187,1.8355890323417587,0,0.0,0,0.0,8.188755217773341,1.2708156134102264,8.091353471792932,0.39394203715952975,0,PASSED
|
||||
6,PASS,5563910,5563910,0,17,0.01328125,0.8834263035683648,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,5.595453900367469,1.2623874159193273,-4.567512776067651,10.162966676435119,1.7721238280090583,0,0.0,0,0.0,8.667865149110309,1.6073728447094802,7.317955191422993,0.43062797582960244,0,PASSED
|
||||
7,PASS,5696166,5696166,0,7,0.00546875,0.8957036694801145,1,0,0,0,1,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000001,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000001,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-1.2654548420073324,-1.236432022604831,-6.203901481459032,4.9384466394516995,1.8155098517430892,0,0.0,0,0.0,8.65014337171221,2.0185187625245264,6.319905151171046,0.4830640770103008,1,PASSED
|
||||
8,PASS,5828422,5828422,0,10,0.0078125,0.8935154947988603,3,0,1,2,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000,110100111001000100000001000000010000110000001000,True,,111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000,111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000,9.131379183391202,-0.4710188398180419,4.02876530540373,5.102613877987472,1.6973544391851634,0,0.0,0,0.0,8.476859162285322,2.688967210304384,4.986494612866651,0.5632163702340075,0,PASSED
|
||||
9,HEADER_FAIL,5960678,5960678,0,13,0.01015625,0.8831012072365596,4,1,1,2,1,0,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000,110100111001000100000001000000011000110000001000,False,CHECKSUM_MISMATCH,111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000,6.363378164483905,-1.9815016298670118,8.696281322717777,-2.332903158233872,1.7240388753166553,0,0.0,0,0.0,8.379835253157635,3.513421286396085,3.7750525233897125,0.6475113327264761,0,HEADER_SPECIFIC_FAILURE
|
||||
10,PASS,6092934,6092934,0,14,0.0109375,0.8756981622368548,1,0,0,0,1,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,10.521050631968299,4.784422094967777,8.894608707236657,1.6264419247316422,1.6172834220436263,0,0.0,0,0.0,7.7745725932128815,2.478953865410118,4.964080786327893,0.5646716196549459,0,PASSED
|
||||
11,PASS,6225190,6225190,0,14,0.0109375,0.8739448578846127,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-4.3377788034794795,5.576222280700207,6.2889992427567645,-10.626778046236243,1.8042319285279784,0,0.0,0,0.0,8.281301835417366,2.613504672826743,5.008753330856255,0.5617749030480063,0,PASSED
|
||||
12,PASS,6357446,6357446,0,15,0.01171875,0.8691086828109845,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-14.662329907046818,-12.429475985972452,-7.481009787452366,-7.181320119594452,1.9299825595605988,0,0.0,0,0.0,8.105161843192903,1.785984725783043,6.568839508606049,0.4694161471804715,0,PASSED
|
||||
13,PASS,6489702,6489702,0,19,0.01484375,0.8870876180609537,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,2.005711398433546,-3.8713317585287843,-9.48236864846436,11.488080046897906,1.7755837460769197,0,0.0,0,0.0,8.379598937532618,1.5074086416620165,7.44992232360151,0.42413477685697976,0,PASSED
|
||||
14,PASS,6621958,6621958,0,13,0.01015625,0.8979602974476922,1,0,0,0,1,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000110000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000110000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,7.210729633183354,3.326192426336044,-3.8425458753995345,11.053275508582889,1.6778789052916618,0,0.0,0,0.0,8.575750726813316,2.4203963781579967,5.493856544116208,0.5312600671059894,1,PASSED
|
||||
15,HEADER_FAIL,6754214,6754214,0,12,0.009375,0.8887378180983065,7,2,2,2,3,1,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000,110101111001000100000001001000010000110000001000,False,MAGIC_MISMATCH,111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000,111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000,-5.7315977256001345,-3.5200403113930196,0.10139388237069231,-5.832991607970826,1.7811965898235738,0,0.0,0,0.0,7.968245167205011,3.1321489434833123,4.055202820682332,0.6269600363881329,0,HEADER_SPECIFIC_FAILURE
|
||||
16,HEADER_FAIL,6886470,6886470,0,16,0.0125,0.8805097957519517,5,2,1,2,2,0,1,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,101111000111000000111111111000000111000000000111000000000011000000000111000000000000001100000111000000000000111111000000000000000000111000000000,110100111001000100010001000001010000110000001000,False,VERSION_MISMATCH,101111000111000000111111111000000111000000000111000000000011000000000111000000000000001100000111000000000000111111000000000000000000111000000000,000111000000111111100000000101011110110001101000011101111000001111000110111101111011100000111001010010111111000000111111111111111011000111111111,4.7344866536019055,228.10491338016192,10.381403191597384,-5.646916537995478,1.6829380426891485,0,0.0,0,0.0,8.084601213892752,3.04749463363358,4.237156529516785,0.613962963260252,107,HEADER_SPECIFIC_FAILURE
|
||||
|
BIN
data/processed/lab067/local_signal_quality_by_packet.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
data/processed/lab067/preamble_errors_by_packet.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
data/processed/lab067/preamble_header_transition.png
Normal file
|
After Width: | Height: | Size: 838 KiB |
1724
data/processed/lab067/summary.json
Normal file
BIN
data/processed/lab067/timing_and_cfo_by_packet.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
49
data/processed/lab067/transition_metrics.csv
Normal file
@@ -0,0 +1,49 @@
|
||||
packet,region,symbols,decision_errors,magnitude_median,magnitude_minimum,phase_error_rms_rad,imaginary_rms,timing_offset_samples
|
||||
1,preamble_tail,128,0,1.0988442307313235,0.43466351049701235,0.36545432588063453,0.3677449037710503,0
|
||||
1,header,144,3,1.0815752733984212,0.5778378379912865,0.6187439503436525,0.551502400297575,0
|
||||
1,postheader,128,2,1.1114278978759744,0.4714742841146855,0.5693534448896019,0.49591231338100494,0
|
||||
2,preamble_tail,128,0,1.074345037947809,0.5715381413183729,0.5169701760998907,0.4918784327994302,0
|
||||
2,header,144,6,1.0888449278310801,0.4753465266196904,0.7542889272180515,0.6186207192551644,0
|
||||
2,postheader,128,0,1.1317475637994039,0.6993167478293558,0.28345944315453764,0.2904885233790264,0
|
||||
3,preamble_tail,128,0,1.0919580830559625,0.4931835747157359,0.577421183092019,0.5161252295458062,0
|
||||
3,header,144,4,1.152609811941216,0.6541218073505881,0.5437712455230955,0.433109513511742,0
|
||||
3,postheader,128,0,1.0857706086674181,0.3396593308737327,0.2976136824815135,0.3003548051915534,0
|
||||
4,preamble_tail,128,0,1.1087294828126495,0.5024192881228802,0.6792582574914069,0.6647511818530001,0
|
||||
4,header,144,0,1.1507766975854852,0.42562892480326897,0.3156584316410723,0.33839321792646554,0
|
||||
4,postheader,128,0,1.1449814637127051,0.5937437256113064,0.29460252451816493,0.31949283720140587,0
|
||||
5,preamble_tail,128,0,1.1434636342884739,0.6047967729049536,0.35172140313369066,0.3535407042323131,0
|
||||
5,header,144,0,1.1363877434085625,0.6007022877000862,0.2522418948656545,0.27555358339271957,0
|
||||
5,postheader,128,5,1.0966485328526976,0.41749527315450785,0.6464901825183261,0.5318845761533794,0
|
||||
6,preamble_tail,128,0,1.1318097006554524,0.6221411610236426,0.2794053247219388,0.30212984078304644,0
|
||||
6,header,144,0,1.118944116022563,0.604506255970942,0.4000248179092374,0.40617877592291496,0
|
||||
6,postheader,128,3,1.1203373379450718,0.5733983281027794,0.6433269203677763,0.5585770337413266,0
|
||||
7,preamble_tail,128,0,1.098335506453933,0.685298350349321,0.31267846128306165,0.3400209740040822,0
|
||||
7,header,144,1,1.0859515485682385,0.4918795883455653,0.49770271058274784,0.48404835853198735,0
|
||||
7,postheader,128,3,1.078419996569039,0.44698965664144213,0.5990105590866479,0.5112821191437632,0
|
||||
8,preamble_tail,128,0,1.0950824513758386,0.5415446078858628,0.37054349208551474,0.38513707581929985,0
|
||||
8,header,144,3,1.0904644019616199,0.43076194377790394,0.6094384014423195,0.5588536614362375,0
|
||||
8,postheader,128,2,1.0935087479377619,0.63642176553416,0.4463244127331142,0.43911459515487505,0
|
||||
9,preamble_tail,128,2,1.093901338967227,0.4388973792742324,0.5923392165896195,0.5322335060592657,0
|
||||
9,header,144,4,1.1195324105485278,0.49998913621143454,0.611185029044535,0.5659527404173302,0
|
||||
9,postheader,128,0,1.1053805607840526,0.640256963735712,0.28052596266122526,0.30630431218636367,0
|
||||
10,preamble_tail,128,3,1.1419841905626784,0.5060536577715014,0.5971065417005673,0.5330612346503504,0
|
||||
10,header,144,1,1.1147443336597755,0.6501217642416475,0.39358947394490806,0.40010760065192014,0
|
||||
10,postheader,128,0,1.1787629415552199,0.42900718390148934,0.2926514547716184,0.3306690896219249,0
|
||||
11,preamble_tail,128,3,1.1704570892176922,0.6573740193329803,0.6151666707460389,0.6107317885152251,0
|
||||
11,header,144,0,1.12981219997058,0.5282686497447672,0.324658042677611,0.3384614869201178,0
|
||||
11,postheader,128,0,1.1070044356208588,0.5046571226194821,0.33630657442006734,0.36220237044356646,0
|
||||
12,preamble_tail,128,4,1.1293212243971127,0.5975355368621857,0.5056848722345468,0.41499438743931705,0
|
||||
12,header,144,0,1.1346606346022847,0.37281496808234904,0.2862848068844218,0.3172306512537894,0
|
||||
12,postheader,128,1,1.1452864327334882,0.4013942634977783,0.4893751135204076,0.4862028130220328,0
|
||||
13,preamble_tail,128,0,1.1020405947443312,0.5476951608093663,0.26825405646139866,0.299908757841907,0
|
||||
13,header,144,0,1.1246852022329477,0.5448659176501547,0.3819108989758959,0.3972670513029744,0
|
||||
13,postheader,128,2,1.0925865889420296,0.5942311935828164,0.5526657411269684,0.47037502313805846,0
|
||||
14,preamble_tail,128,0,1.0986885517578937,0.4240058786452209,0.307170345418273,0.30833136259021016,0
|
||||
14,header,144,1,1.1117645619461933,0.5698477702591676,0.5701021956757241,0.5678681526072823,0
|
||||
14,postheader,128,3,1.106509873733971,0.5370530426198264,0.594466571272217,0.5534673376824929,0
|
||||
15,preamble_tail,128,1,1.0605799496659238,0.5096523999436898,0.4395937997414911,0.41506255958111626,0
|
||||
15,header,144,7,1.0998344881084918,0.562650314086593,0.6887166853647746,0.5814901000470972,0
|
||||
15,postheader,128,2,1.1227074410764701,0.5161407533490777,0.5946319825247028,0.5701686238399535,0
|
||||
16,preamble_tail,128,2,1.1150803467611397,0.5398340206811254,0.5646446219736847,0.48272300998033535,0
|
||||
16,header,144,5,1.1024025293008193,0.41214915514462663,0.6355591984079474,0.49483023185734204,0
|
||||
16,postheader,128,0,1.1777412846771014,0.3344663773895672,0.3015102248779219,0.3386110581722476,0
|
||||
|
38730
data/processed/lab067/transition_symbols.json
Normal file
@@ -570,3 +570,174 @@
|
||||
[synthetic CSV](../data/processed/lab061/synthetic_packet_results.csv).
|
||||
- **STOP:** Lab061 фиксируется этим отдельным коммитом; push выполняется
|
||||
пользователем отдельно, Lab062 не создана.
|
||||
|
||||
## Lab062 — полный 500K PHY-пакет, подготовка и RX-only preflight
|
||||
|
||||
- **Запись 056; RF TX=0.** Подготовлены16 пакетов по256 байт payload,
|
||||
0,184512с. Legacy/T2 дали16/16 строгих HEADER/FEC/CRC и точных payload.
|
||||
- **Геометрия:**1400МГц, примерно1,2м, LOS в помещении; TX возле окна, RX на
|
||||
столе, одинаковая поляризация, антенны и кабели неподвижны.
|
||||
- **Аппаратный preflight PASS:** MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED
|
||||
через133,5795мс. RX-only:800000 отсчётов, RMS2,314058, AC RMS0,985483,
|
||||
max|I|/|Q|=6/8, clipping0, overruns0, сильного фона нет.
|
||||
- **Проверки:** quick gate962/35/13 PASS, pytest962 passed. Усиления10/24/24дБ
|
||||
сохранены; аналоговая компрессия без прибора остаётся неизвестной.
|
||||
- [План](lab062_current_state.md), [отчёт](lab062_rx_preflight.md),
|
||||
[software](../data/processed/lab062/software_gate.json),
|
||||
[preflight](../data/processed/lab062/preflight_result.json).
|
||||
- **STOP:** технически готово к одному TX, но отдельного разрешения ещё нет.
|
||||
Lab062 не коммитировалась, push не выполнялся, Lab063 не создана.
|
||||
- **Единственный RF-сеанс:** выполнен один TX без повторов. RX сохранён полностью: 24 000 000
|
||||
комплексных отсчётов, 3,000 с, overruns 0, clipping 0. В TX-журнале зарегистрирован
|
||||
промежуточный `1 underruns, longest 16672 bytes`, поэтому сеанс признан невалидным для чистого
|
||||
A/B-сравнения. Legacy/T2 дали одинаковый пакетный итог 0/16, T2 gain 0.
|
||||
- [RF-отчёт](lab062_rf_result.md),
|
||||
[JSON](../data/processed/lab062/rf_analysis.json),
|
||||
[покадровый CSV](../data/processed/lab062/packet_comparison.csv).
|
||||
- **Текущее состояние:** `RF TX=0`; повтор не выполнялся, Lab062 не закоммичена, push не
|
||||
выполнялся, Lab063 не создана. Следующий шаг требует отдельного согласования.
|
||||
|
||||
## Lab063: офлайн-локализация TX underrun и ухудшения Lab062
|
||||
|
||||
- **Запись 058; только сохранённые TX/RX, RF TX=0.** Lab062 перед анализом зафиксирована
|
||||
коммитом `f8ae6c6bfceaa21b6c08dcf55d2bff996dd0dae5`; её статус
|
||||
`RF_SESSION_INVALID_TX_UNDERRUN` сохранён.
|
||||
- **Фактические паузы:** 92 256 × 16 = 1 476 096 отсчётов, поэтому дополнительных пауз между
|
||||
полными packet waveform нет. Причина: ошибка реализации `prepare()`, пакеты записаны подряд
|
||||
без параметра паузы и без вставки нулей.
|
||||
- **Масштаб журнала:** 16 672 байта cs8 = 8 336 комплексных отсчётов = 1,042 мс = 521 символ =
|
||||
9,035727% одного waveform. RX не подтверждает потерю или повтор именно такого числа отсчётов.
|
||||
- **Локальное сопоставление:** 221 надёжное окно из 359, задержка 0…1 отсчёт, скачков нет.
|
||||
Ещё 22 внутренних окна пакетов3,4,12,13 имеют корреляцию ниже0,15; задержка там не
|
||||
установлена. `UNDERRUN LOCATION=NOT_LOCALIZED_FROM_RX`. Повторяющаяся преамбула с
|
||||
корреляционными копиями и низкокорреляционные окна исключены из решения о разрыве.
|
||||
- **Качество:** все 16 пакетов хуже Lab056 B по SER, EVM и matched/residual. Пакеты 3,4,12,13
|
||||
имеют дополнительные всплески AC-мощности и остатка при почти постоянном локальном |h|;
|
||||
пакет14 общего признака с ними не показывает. Итог: `GLOBAL_RF_DEGRADATION`, причина
|
||||
`PARTIALLY_LOCALIZED`.
|
||||
- **RX-уровень:** AC RMS0,760816…2,032438 кода, max|I|=3…6, max|Q|=4…7. Сильная аналоговая
|
||||
перегрузка менее вероятна, но без прибора не исключена. Значение расхождения счётчиков
|
||||
`hackrf_transfer` локально не установлено: `COUNTER SEMANTICS=NOT_ESTABLISHED`.
|
||||
- [Текущее состояние](lab063_current_state.md), [отчёт](lab063_offline_result.md),
|
||||
[JSON](../data/processed/lab063/summary.json),
|
||||
[пакеты CSV](../data/processed/lab063/packet_quality.csv),
|
||||
[локальное сопоставление CSV](../data/processed/lab063/local_alignment.csv).
|
||||
- **STOP:** Lab063 не коммитировалась, push не выполнялся, Lab064 не создана. Следующий шаг
|
||||
требует отдельного согласования; RF TX=0.
|
||||
|
||||
## Lab064: измерение уровня и линейности RF-тракта
|
||||
|
||||
- **Запись 059; подготовка и RX-only, RF TX=0.** Создан один 0,75-секундный
|
||||
PRBS7/BPSK/RRC waveform с четырьмя 100-мс участками −18/−12/−6/0 дБ,
|
||||
промежутками по 50 мс и нулями по 100 мс в начале и конце.
|
||||
- **Цифровые уровни:** RMS 5,556137/11,056978/22,056892/44,000457 кода; шаги
|
||||
+5,977270/+5,998157/+5,998257 дБ; max |I|/|Q| не выше 69/69, clipping0.
|
||||
- **Методика:** пакетный приёмник, FEC, CRC, T2 и JPEG не используются. Главная
|
||||
величина — мощность согласованной с известным TX составляющей. Анализатор
|
||||
выдаёт `INVALID_CAPTURE` без A/B/C при любом промежуточном underrun, overrun
|
||||
или clipping.
|
||||
- **Аппаратный preflight PASS:** MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED
|
||||
при первом опросе через66,793мс. RX-only:800000 отсчётов, полный RMS2,193193,
|
||||
AC RMS0,832038, max|I|/|Q|=4/5, clipping0, overruns0, сильного фона нет.
|
||||
- **Проверки:** quick gate984/37/13 PASS, pytest984 passed, целевые13 passed,
|
||||
независимая read-only проверка чистая.
|
||||
- [Текущее состояние](lab064_current_state.md), [отчёт](lab064_rx_preflight.md),
|
||||
[подготовка](../data/processed/lab064/preparation.json),
|
||||
[software gate](../data/processed/lab064/software_gate.json),
|
||||
[preflight](../data/processed/lab064/preflight_result.json).
|
||||
- **STOP:** технически готово к одному TX, но отдельного разрешения ещё нет.
|
||||
Lab064 не коммитируется, push не выполняется, Lab065 не начинается.
|
||||
- **Единственный RF-сеанс:** один TX, повторов0, underruns0, overruns0,
|
||||
clipping0; RX16000000 отсчётов за2,0с. Фон стабилен: AC RMS
|
||||
0,718264…0,728217 кода, разброс0,119530дБ.
|
||||
- **Линейность:** L0→L1/L1→L2/L2→L3 дали ошибки
|
||||
−0,017501/−0,169270/+0,022162дБ. Полный рост TX/RX равен
|
||||
17,973685/17,809077дБ, общая ошибка −0,164609дБ. Признака компрессии нет.
|
||||
- **Итог `LINEAR_BUT_WEAK`:** L3 matched/residual равен −12,133336дБ. Тракт
|
||||
следует цифровым ступеням, но согласованная полезная составляющая остаётся
|
||||
слабее остатка. [RF-отчёт](lab064_rf_result.md),
|
||||
[RF JSON](../data/processed/lab064/rf_result.json),
|
||||
[capture JSON](../data/processed/lab064/capture_result.json).
|
||||
- **Текущее состояние:** `RF TX=0`, процессы CLEAR; второй TX не выполнялся,
|
||||
Lab064 не коммитировалась, push не выполнялся, Lab065 не создана.
|
||||
- **Независимая проверка:** прямой пересчёт из TX/RX CS8 подтвердил все SHA,
|
||||
счётчики, фоны, matched/residual/`h`, ступени и спектральные полосы;
|
||||
расхождений нет. Quick gate и pytest после RF:986 passed,0 failed.
|
||||
|
||||
## Lab065: измерение уровня при TX VGA 20 дБ
|
||||
|
||||
- **База:** Lab064 зафиксирована коммитом
|
||||
`cff226e0bf9e36a627829ea11a4ca3292ae20953`; push не выполнялся.
|
||||
- **Единственное изменение параметра:** TX VGA 10 → 20 дБ. Частота 1400 МГц,
|
||||
RX LNA/VGA 24/24 дБ, усилители, геометрия, антенны и кабели сохранены.
|
||||
- **Waveform:** точная копия Lab064, 6000000 отсчётов, 0,75 с; SHA-256
|
||||
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
|
||||
- **Подготовлено:** RX-first runner, максимальные счётчики потерь, сравнение
|
||||
линейности внутри Lab065 и по одинаковым уровням Lab064/Lab065, численные
|
||||
правила классификации. Software gate PASS.
|
||||
- **RX-only preflight:** обе HackRF и роли подтверждены, reg29=0x26 у MASTER и
|
||||
SLAVE, CLKIN обнаружен на третьем опросе через201,986мс. Фон:800000 отсчётов,
|
||||
AC RMS1,056565 кода, max|I|/|Q|=6/8, clipping0, overruns0. Cleanup PASS.
|
||||
- **Граница:** `RF TX=0`. Lab065 не коммитировалась. Нужны рассмотрение
|
||||
подготовки и отдельное прямое разрешение на один TX; Lab066 не начинать.
|
||||
- [Текущее состояние](lab065_current_state.md),
|
||||
[RX-only отчёт](lab065_rx_preflight.md),
|
||||
[подготовка](../data/processed/lab065/preparation.json),
|
||||
[software gate](../data/processed/lab065/software_gate.json),
|
||||
[preflight](../data/processed/lab065/preflight_result.json).
|
||||
- **Единственный RF-сеанс:** TX1, автоматических повторов0; underrun/overrun/
|
||||
clipping0, cleanup PASS, процессы CLEAR. RX16000000 отсчётов, SHA-256
|
||||
`845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e`.
|
||||
- **Линейность:** ошибки ступеней −0,077363/+0,034934/−0,013858дБ, компрессии
|
||||
L2/L3 нет. Медианный прирост matched power+11,233489дБ, изменение остатка
|
||||
+0,517004дБ, улучшение matched/residual+10,734495дБ.
|
||||
- **Результат:** `LINEAR_GAIN_IMPROVEMENT`. Фон стабилен; диагностические полосы
|
||||
остатка растут слабее matched power. [RF-отчёт](lab065_rf_result.md),
|
||||
[RF JSON](../data/processed/lab065/rf_result.json),
|
||||
[capture JSON](../data/processed/lab065/capture_result.json).
|
||||
- **STOP:** `RF TX=0`; повторов и изменения усилений не было. Lab065 не
|
||||
коммитировалась, push отсутствует, Lab066 не создана.
|
||||
- **Независимая проверка:** прямой пересчёт CS8 подтвердил SHA, счётчики,
|
||||
локализацию, фон, matched/residual, `h`, CFO и спектральные полосы. Расхождений
|
||||
нет. Quick gate1000/38/13 PASS, pytest1000 passed, diff check PASS.
|
||||
- **Фиксация:** Lab065 зафиксирована локальным коммитом
|
||||
`dacf04f2d0a15c0c1b56a7fb527db61fc2e38462`; push не выполнялся.
|
||||
|
||||
## Lab066 — исправленный повтор полного 500K PHY-сеанса
|
||||
|
||||
- **Основание:** Lab062 остаётся `RF_SESSION_INVALID_TX_UNDERRUN`, legacy/T2
|
||||
0/16. Lab066 исправляет отсутствовавшие межпакетные паузы и использует
|
||||
максимальный наблюдавшийся счётчик потерь.
|
||||
- **TX:** 16 полных PHY-пакетов по256 байт, packet waveform92256 отсчётов;
|
||||
15 нулевых пауз по40000 отсчётов/5мс и guards по800000 отсчётов/100мс.
|
||||
Всего3676096 отсчётов,0,459512с; SHA-256
|
||||
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`.
|
||||
- **Параметры:** 1400МГц, Fs8Мвыб/с,500ксимв/с, несущая+50кГц,
|
||||
TX VGA20дБ, RX LNA/VGA24/24дБ, amplifier/Bias-Tee OFF; прежняя геометрия.
|
||||
- **Software gate:** legacy и T2 независимо дали16/16 HEADER/FEC/CRC,
|
||||
packet output и точных упорядоченных payload. Fallback отсутствует; T2
|
||||
инициализируется по собственной преамбуле каждого пакета.
|
||||
- **RX-only preflight:** MASTER/SLAVE reg29=0x26, CLKIN DETECTED через132,113мс;
|
||||
AC RMS0,827637, max|I|/|Q|=4/5, clipping0, maximum overruns0. Cleanup PASS,
|
||||
процессы CLEAR.
|
||||
- **Проверки:** quick gate1013/39/13 PASS, pytest1013 passed, diff check PASS;
|
||||
verifier и adversary не оставили открытых замечаний.
|
||||
- [Состояние](lab066_current_state.md), [RX-only отчёт](lab066_rx_preflight.md),
|
||||
[подготовка](../data/processed/lab066/preparation.json),
|
||||
[software gate](../data/processed/lab066/software_gate.json),
|
||||
[preflight](../data/processed/lab066/preflight_result.json).
|
||||
- **STOP:** RF TX=0; требуется отдельное прямое разрешение на один TX. Lab066
|
||||
не коммитировалась, push не выполнялся, Lab067 не создавалась.
|
||||
- **Единственный RF-сеанс:** один TX, повторов0; maximum underrun/overrun0/0,
|
||||
clipping0, cleanup PASS, процессы CLEAR. RX16000000 отсчётов, SHA-256
|
||||
`72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0`.
|
||||
- **Строгий результат:** legacy и T2 совпали — по10/16 точных пакетов;
|
||||
приняты4–8 и10–14, отказали на header пакеты1–3,9,15,16. Выигрыша T2 нет.
|
||||
- **Физическое качество:** SER0,011047, RMS0,528374, matched/residual
|
||||
+5,542117дБ, медианная неравномерность2,870070дБ. Все четыре показателя лучше
|
||||
Lab056 B и невалидной Lab062.
|
||||
- **Итог:** `PARTIAL_PACKET_RECOVERY_NO_T2_GAIN`. [RF-отчёт](lab066_rf_result.md),
|
||||
[RF summary](../data/processed/lab066/rf_summary.json),
|
||||
[packet CSV](../data/processed/lab066/packet_comparison.csv).
|
||||
- **После сеанса:** RF TX=0, второй TX не выполнялся. Lab066 не
|
||||
коммитировалась, push не выполнялся, Lab067 не создавалась.
|
||||
|
||||
48
docs/lab062_current_state.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Lab062 — полный пакет 500K, подготовка к одному RF-сеансу
|
||||
|
||||
Lab062 проверяет T2 на полном штатном PHY-пакете в одном будущем коротком
|
||||
эфирном сеансе. На текущем этапе разрешены подготовка, программный шлюз,
|
||||
аппаратная проверка общей опоры и RX-only запись фона. Передача запрещена до
|
||||
отдельного разрешения пользователя.
|
||||
|
||||
## Неподвижная физическая схема
|
||||
|
||||
| Условие | Фактическое значение |
|
||||
|---|---|
|
||||
| Частота | 1400000000 Гц, только для Lab062 |
|
||||
| Расстояние TX–RX | примерно 1,2 м |
|
||||
| Геометрия | прямая видимость, помещение |
|
||||
| TX | плата и антенна возле окна |
|
||||
| RX | плата и антенна на столе пользователя |
|
||||
| Антенны | имеющиеся антенны на 1400 МГц, неподвижны |
|
||||
| Поляризация | одинаковая |
|
||||
| Тактирование | 10 МГц TCXO → MASTER → MASTER CLKOUT → SLAVE CLKIN |
|
||||
|
||||
Длинный кабель общей опоры, USB-подключение, антенны и остальные кабели не
|
||||
перемещаются. Пользователь по возможности не проходит между антеннами. Во время
|
||||
будущего сеанса не меняются частота, усиления, ориентация и PHY.
|
||||
|
||||
## Параметры
|
||||
|
||||
- 16 полных пакетов, payload каждого — 256 байт;
|
||||
- BPSK, 500000 символов/с, Fs 8000000 выборок/с, 16 выборок/символ;
|
||||
- центральная частота 1400 МГц, цифровая несущая +50 кГц;
|
||||
- TX VGA 10 дБ, RX LNA/VGA 24/24 дБ;
|
||||
- RF amplifier и Bias-Tee выключены;
|
||||
- один конечный TX-waveform и одна будущая RX-запись;
|
||||
- режим приёмника задаётся заранее: `legacy` или `t2`, без CRC-fallback.
|
||||
|
||||
RX-only preflight не меняет усиления. Он проверяет rail-коды, overruns,
|
||||
максимальные |I|/|Q|, полный и AC RMS, сильный фон и ошибки аппаратных команд.
|
||||
Эвристический STOP для неожиданно сильного цифрового фона: AC RMS не менее 20
|
||||
кодов или максимум |I|/|Q| не менее 100 кодов. Отсутствие цифрового клиппинга
|
||||
не доказывает отсутствие аналоговой компрессии.
|
||||
|
||||
Обязательная аппаратная точка: MASTER reg29=0x26, SLAVE reg29=0x26 и SLAVE
|
||||
CLKIN=DETECTED. Любой отказ означает `RF TX = 0` и STOP.
|
||||
|
||||
Lab062 не коммитируется. Push не выполняется. Lab063 не создаётся автоматически.
|
||||
|
||||
## Фактический RF-результат
|
||||
|
||||
14 сентября 2026 года выполнен ровно один разрешённый TX-сеанс. Полная RX-запись сохранена, но в промежуточной строке `hackrf_transfer` зарегистрирован `1 underruns, longest 16672 bytes`. Повтор не выполнялся. Строгие `legacy` и `t2` на одной записи дали 0/16 CRC и пакетов; T2 gain равен 0. Подробности и покадровая таблица приведены в [отчёте](lab062_rf_result.md). Текущий статус: `RF TX = 0`, Lab062 не закоммичена, push не выполнялся, Lab063 не создана.
|
||||
108
docs/lab062_rf_result.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Lab062 — результат единственного RF-сеанса
|
||||
|
||||
Передача была запущена один раз. Приёмник сохранил полную трёхсекундную IQ-запись без переполнений и цифрового клиппинга. Однако `hackrf_transfer` во время передачи сообщил об одном кратком underrun: буфер передатчика один раз опустел на интервал до 16 672 байт. В итоговой строке утилита напечатала ноль underrun, но промежуточную ненулевую строку игнорировать нельзя. Поэтому этот сеанс нельзя считать чистым сравнением `legacy` и `t2`.
|
||||
|
||||
Оба строгих варианта приёмника обработали одну и ту же запись и выдали 0 из 16 пакетов. T2 не дал выигрыша в пакетах. Установлено, что передача состоялась, общий тактовый сигнал перед ней был исправен, RX-файл полный, а строгий результат равен 0/16 в обоих режимах. Нельзя установить, каким был бы результат без underrun, и нельзя приписывать все ошибки самому T2 или эфирному каналу. Аналоговая компрессия также остаётся неизвестной без измерительного прибора.
|
||||
|
||||
## Условия
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---:|
|
||||
| RF | 1 400 000 000 Гц |
|
||||
| Символьная скорость | 500 000 символов/с |
|
||||
| Частота дискретизации | 8 000 000 комплексных отсчётов/с |
|
||||
| Цифровая несущая | +50 000 Гц |
|
||||
| Пакеты | 16 полных PHY-пакетов по 256 байт payload |
|
||||
| TX | 1 476 096 отсчётов, 0,184512 с, VGA 10 дБ |
|
||||
| RX | LNA/VGA 24/24 дБ, 3 с |
|
||||
| RF amplifier / Bias-Tee | OFF / OFF |
|
||||
| Геометрия | около 1,2 м, LOS, помещение; TX возле окна, RX на столе |
|
||||
| Антенны | имеющиеся антенны на 1400 МГц, одинаковая поляризация, неподвижны |
|
||||
| Опора | 10 МГц TCXO → MASTER → MASTER CLKOUT → SLAVE CLKIN |
|
||||
|
||||
SHA-256 передаваемого файла: `e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5`.
|
||||
|
||||
## Аппаратный результат
|
||||
|
||||
- перед TX повторно получены `MASTER reg29 = 0x26`, `SLAVE reg29 = 0x26`, `SLAVE CLKIN = DETECTED`;
|
||||
- выполнен один TX-сеанс, автоматических повторов нет;
|
||||
- RX: 24 000 000 комплексных отсчётов, 48 000 000 байт, 3,000 с;
|
||||
- SHA-256 RX: `ce0bd603e4ca356af67d504ee11c9e9f38171f31c7427fbe61216a28126b2b32`;
|
||||
- RX overruns: 0 во всех пяти строках журнала;
|
||||
- RX clipping: 0;
|
||||
- TX underruns: промежуточный максимум 1, `longest 16672 bytes`; финальная строка — 0;
|
||||
- cleanup: оба CLKOUT выключены без зарегистрированных ошибок, SDR-процессы после сеанса — `CLEAR`;
|
||||
- текущее состояние: `RF TX = 0`.
|
||||
|
||||
Из-за ненулевой промежуточной строки underrun статус сеанса: `INVALID_TX_UNDERRUN`. Полученные файлы сохранены, повтор не выполнялся.
|
||||
|
||||
## Строгое декодирование одной записи
|
||||
|
||||
| Стадия | legacy | T2 |
|
||||
|---|---:|---:|
|
||||
| CANDIDATES | 144 | 144 |
|
||||
| TRUE PACKETS FOUND | 0 | 0 |
|
||||
| HEADER ACCEPTED | 3 | 3 |
|
||||
| FEC STARTED | 3 | 3 |
|
||||
| FEC PASS | 3 | 3 |
|
||||
| CRC STARTED | 3 | 3 |
|
||||
| CRC PASS | 0 | 0 |
|
||||
| PACKET OUTPUT | 0 | 0 |
|
||||
| EXACT PAYLOAD MATCH | 0 | 0 |
|
||||
|
||||
Детектор в обоих запусках получил 209 956 первичных кандидатов, дешёвая ступень отклонила 209 812, после группировки строгий декодер проверил 144 кандидата. Переполнения очереди кандидатов не было. Никакого fallback между `legacy` и `t2` не выполнялось.
|
||||
|
||||
- `PASSED BY BOTH = []`
|
||||
- `PASSED ONLY BY LEGACY = []`
|
||||
- `PASSED ONLY BY T2 = []`
|
||||
- `FAILED BY BOTH = [1, 2, …, 16]`
|
||||
|
||||
## Таблица 16 фактических границ
|
||||
|
||||
Начало потока найдено только после строгого вердикта корреляцией с первым TX-пакетом: отсчёт 4 256 091, отношение пика к медиане 21,739. Знание TX не передавалось детектору и не использовалось для повторной настройки приёмника. Во всех строках `H/F/C/O` означают header/FEC/CRC/output; `1` — проход, `0` — непроход. Ошибки символов и оценки частоты — диагностические, по известной границе и известным переданным символам.
|
||||
|
||||
| № | legacy H/F/C/O | T2 H/F/C/O | Preamble errors | Header errors | Payload errors до FEC | CFO нач., Гц | T2 фин., Гц | T2 lock |
|
||||
|---:|:---:|:---:|---:|---:|---:|---:|---:|:---:|
|
||||
| 1 | 0/0/0/0 | 0/0/0/0 | 108 | 20 | 367 | -2.511 | +1.939 | не определён |
|
||||
| 2 | 0/0/0/0 | 0/0/0/0 | 135 | 11 | 377 | +16.850 | -2.223 | не определён |
|
||||
| 3 | 0/0/0/0 | 0/0/0/0 | 466 | 115 | 2229 | -266.238 | -265.796 | не определён |
|
||||
| 4 | 0/0/0/0 | 0/0/0/0 | 515 | 104 | 2266 | +297.250 | +293.800 | не определён |
|
||||
| 5 | 0/0/0/0 | 0/0/0/0 | 108 | 27 | 369 | -1.712 | -0.835 | не определён |
|
||||
| 6 | 1/1/0/0 | 1/1/0/0 | 112 | 10 | 345 | +4.676 | -3.164 | не определён |
|
||||
| 7 | 0/0/0/0 | 0/0/0/0 | 113 | 7 | 344 | +2.113 | +1.094 | не определён |
|
||||
| 8 | 0/0/0/0 | 0/0/0/0 | 91 | 19 | 368 | +0.964 | +0.354 | не определён |
|
||||
| 9 | 0/0/0/0 | 0/0/0/0 | 105 | 21 | 367 | -2.679 | +0.191 | не определён |
|
||||
| 10 | 0/0/0/0 | 0/0/0/0 | 116 | 7 | 345 | -0.290 | +0.947 | не определён |
|
||||
| 11 | 0/0/0/0 | 1/1/0/0 | 109 | 9 | 392 | +0.757 | -0.142 | не определён |
|
||||
| 12 | 0/0/0/0 | 0/0/0/0 | 297 | 110 | 2181 | -223.050 | -222.389 | не определён |
|
||||
| 13 | 0/0/0/0 | 0/0/0/0 | 304 | 36 | 524 | -0.161 | -2.081 | не определён |
|
||||
| 14 | 1/1/0/0 | 0/0/0/0 | 314 | 127 | 2091 | +228.209 | +224.928 | не определён |
|
||||
| 15 | 1/1/0/0 | 1/1/0/0 | 131 | 7 | 395 | -9.960 | +2.284 | не определён |
|
||||
| 16 | 0/0/0/0 | 0/0/0/0 | 100 | 23 | 355 | +13.157 | -0.868 | не определён |
|
||||
|
||||
В реализации T2 нет отдельного детектора lock, поэтому поле нельзя честно заменить на `PASS` или `FAIL`. Сохранены начальное состояние частоты и конечное состояние петли.
|
||||
|
||||
## Физическое качество по методике Lab058
|
||||
|
||||
Функции `analyze_values` и `channel_metrics` из Lab058 применены отдельно к каждому полному пакету. Для оценки использованы диагностические границы и чередующиеся обучающие/проверочные участки с защитными краями. Это измерение сделано после строгого результата и не меняет 0/16.
|
||||
|
||||
| Метрика | Lab062 | Lab056, 500K | Разница по смыслу |
|
||||
|---|---:|---:|---|
|
||||
| Диагностическая доля ошибочных символов | 0,099287 | 0,034959 | Lab062 хуже |
|
||||
| Нормированное RMS-отклонение, EVM | 1,220401 | 0,845350 | Lab062 хуже |
|
||||
| Matched/residual | -1,730051 дБ | +1,464846 дБ | Lab062 хуже на 3,194897 дБ |
|
||||
| Неравномерность усиления, p95–p05 | медиана 6,660341 дБ | среднее 5,471382 дБ | Lab062 больше |
|
||||
|
||||
Для Lab062 разброс неравномерности между пакетами составил 6,236828…10,617549 дБ. Остаток включает шум, искажения канала и возможное влияние underrun; называть его чистым шумом нельзя. Отдельная диагностика именно выходных символов T2 дала SER 0,186626 и RMS 1,699461; это другой срез данных, поэтому он не подменяет приведённые выше сопоставимые метрики Lab058.
|
||||
|
||||
## Инженерный вывод
|
||||
|
||||
Чистое сравнение `legacy` и T2 не состоялось из-за зафиксированного TX underrun. Фактический строгий результат существующей записи — 0/16 для обоих режимов, выигрыш T2 равен нулю. В записи видны неоднородные повреждения пакетов и заметно худшее качество символов, чем у 500K-сегментов Lab056, но эти наблюдения нельзя отделить от аппаратной ошибки передачи.
|
||||
|
||||
Следующий одиночный шаг: без RF локализовать момент underrun относительно границ пакетов и проверить, совместимы ли провалы качества с его положением. Новый RF-сеанс, Lab063, изменение PHY или усилений этим отчётом не разрешаются.
|
||||
|
||||
`LAB062 COMMIT = NOT PERFORMED`
|
||||
|
||||
`PUSH = NOT PERFORMED`
|
||||
|
||||
`RF TX = 0`
|
||||
108
docs/lab062_rx_preflight.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Lab062 — подготовка и RX-only preflight
|
||||
|
||||
14 сентября 2026 года выполнена подготовка полного 500K PHY-сеанса и одна
|
||||
аппаратная проверка при выключенном передатчике. Результат preflight — PASS.
|
||||
Передача не запускалась: `RF TX = 0`.
|
||||
|
||||
## Условия
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Центральная частота | 1400000000 Гц |
|
||||
| Скорость символов | 500000 символов/с |
|
||||
| Частота дискретизации | 8000000 выборок/с |
|
||||
| Пакеты | 16 полных PHY-пакетов |
|
||||
| Payload | 256 байт в каждом пакете |
|
||||
| TX VGA | 10 дБ |
|
||||
| RX LNA / VGA | 24 / 24 дБ |
|
||||
| RF amplifier / Bias-Tee | выключены |
|
||||
| Расстояние TX–RX | примерно 1,2 м |
|
||||
| Геометрия | прямая видимость, помещение |
|
||||
| TX | плата и антенна возле окна |
|
||||
| RX | плата и антенна на столе пользователя |
|
||||
| Антенны | имеющиеся антенны на 1400 МГц, неподвижны |
|
||||
| Поляризация | одинаковая |
|
||||
| Общая опора | 10 МГц TCXO → MASTER → MASTER CLKOUT → SLAVE CLKIN |
|
||||
|
||||
Длинный clock-кабель и USB-схема не менялись. Антенны, кабели, ориентация,
|
||||
частота, усиления и PHY должны остаться неизменными до будущего разрешённого
|
||||
сеанса.
|
||||
|
||||
## Программная проверка
|
||||
|
||||
Создан один waveform из 16 пакетов. Его длина — 1476096 комплексных отсчётов,
|
||||
2952192 байта и 0,184512 с. SHA-256:
|
||||
`e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5`.
|
||||
|
||||
Строгий приёмник в заранее выбранных режимах дал:
|
||||
|
||||
| Режим | Header | FEC | CRC | Точный payload и порядок |
|
||||
|---|---:|---:|---:|---:|
|
||||
| legacy | 16/16 | 16/16 | 16/16 | 16/16 |
|
||||
| T2 | 16/16 | 16/16 | 16/16 | 16/16 |
|
||||
|
||||
Автоматического повторного декодирования по результату CRC нет.
|
||||
|
||||
## Общая опора и RX-only фон
|
||||
|
||||
Свежая инвентаризация подтвердила обе HackRF One r9 с firmware/libhackrf
|
||||
2024.02.1. Роли сохранены:
|
||||
|
||||
- MASTER `000000000000000097c063dc323b97d3`;
|
||||
- SLAVE `000000000000000097c063dc2e5327d3`.
|
||||
|
||||
Аппаратная проверка получила:
|
||||
|
||||
```text
|
||||
MASTER reg29 = 0x26
|
||||
SLAVE reg29 = 0x26
|
||||
SLAVE CLKIN = DETECTED
|
||||
CLKIN query = 2
|
||||
time to detect = 133.5795 ms
|
||||
```
|
||||
|
||||
Первый опрос SLAVE ещё показывал отсутствие опоры, второй штатный опрос её
|
||||
обнаружил. После этого SLAVE записал 800000 комплексных отсчётов, 0,1 с, при
|
||||
1400 МГц и RX LNA/VGA 24/24 дБ.
|
||||
|
||||
| Метрика RX-only записи | Значение |
|
||||
|---|---:|
|
||||
| Полный RMS IQ | 2,314058 кода |
|
||||
| AC RMS после удаления среднего | 0,985483 кода |
|
||||
| Средние I / Q | 0,966063 / 1,857529 кода |
|
||||
| Максимум \|I\| / \|Q\| | 6 / 8 кодов |
|
||||
| Максимальная комплексная амплитуда | 8,246211 кода |
|
||||
| Цифровой clipping | 0 / 800000 отсчётов |
|
||||
| RX overruns | 0 |
|
||||
| Неожиданно сильный сигнал | нет |
|
||||
|
||||
Сильным цифровым фоном считался бы AC RMS не менее 20 кодов или максимум
|
||||
`|I|/|Q|` не менее 100 кодов. Оба показателя значительно ниже порога.
|
||||
|
||||
Отсутствие rail-кодов не доказывает отсутствия аналоговой компрессии при
|
||||
будущем TX. Абсолютная RF-мощность без измерительного прибора также неизвестна.
|
||||
Усиления по результату preflight не менялись.
|
||||
|
||||
Унаследованное от Lab056 поле энергии на символ исключено из обработанной
|
||||
сводки: у фоновой записи нет символов, поэтому такая величина к ней неприменима.
|
||||
Сырой аппаратный JSON сохранён без переписывания; это поле не участвовало в
|
||||
решении PASS.
|
||||
|
||||
SHA-256 RX-only записи:
|
||||
`4cc0ceef5a95f84e5aa0c232d14ff1123efd307494b5c8a5646854be912fdbca`.
|
||||
|
||||
После записи оба CLKOUT выключены без ошибок. Проверка процессов до и после
|
||||
дала `CLEAR`. Вызовов `hackrf_transfer -t` не было. Эксклюзивный маркер
|
||||
preflight запрещает его автоматический повтор.
|
||||
|
||||
Артефакты:
|
||||
|
||||
- [подготовка](../data/processed/lab062/preparation.json);
|
||||
- [программный шлюз](../data/processed/lab062/software_gate.json);
|
||||
- [результат preflight](../data/processed/lab062/preflight_result.json);
|
||||
- исходные TX и RX-only CS8 находятся в `data/raw/lab062/` и в Git не входят.
|
||||
|
||||
`READY FOR SINGLE LAB062 TX = YES` означает только техническую готовность.
|
||||
Отдельного разрешения на передачу ещё нет. `RF TX = 0`. STOP.
|
||||
|
||||
Lab062 не коммитируется. Push не выполняется. Lab063 не создаётся автоматически.
|
||||
78
docs/lab063_current_state.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Lab063: текущее состояние
|
||||
|
||||
## Статус
|
||||
|
||||
Lab063 выполнена полностью офлайн по сохранённым файлам единственного RF-сеанса Lab062. Новых передач, запросов к HackRF и аппаратных команд не было.
|
||||
|
||||
- `LAB063 STATUS = COMPLETED_OFFLINE`
|
||||
- `RF TX = 0`
|
||||
- `LAB063 COMMIT = NOT PERFORMED`
|
||||
- `PUSH = NOT PERFORMED`
|
||||
- `LAB064 = NOT CREATED`
|
||||
|
||||
Lab062 перед началом анализа зафиксирована отдельным коммитом:
|
||||
|
||||
- `LAB062 COMMIT = f8ae6c6bfceaa21b6c08dcf55d2bff996dd0dae5`
|
||||
- `NEW HEAD = f8ae6c6bfceaa21b6c08dcf55d2bff996dd0dae5`
|
||||
- сообщение: `Lab062: record invalidated 500 ksymbol/s RF packet trial`
|
||||
|
||||
Статус аппаратного опыта сохранён без изменения: `RF_SESSION_INVALID_TX_UNDERRUN`. Результат `legacy = 0/16` и `T2 = 0/16` не переобъявлялся как валидное сравнение приёмников.
|
||||
|
||||
## Исходные данные
|
||||
|
||||
Анализ использует только сохранённые файлы Lab062:
|
||||
|
||||
| Объект | Значение |
|
||||
|---|---|
|
||||
| TX | `data/raw/lab062/tx_stream.cs8` |
|
||||
| TX SHA-256 | `e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5` |
|
||||
| TX | 1 476 096 комплексных отсчётов |
|
||||
| RX | `data/raw/lab062/rx_stream.cs8` |
|
||||
| RX SHA-256 | `ce0bd603e4ca356af67d504ee11c9e9f38171f31c7427fbe61216a28126b2b32` |
|
||||
| RX | 24 000 000 комплексных отсчётов |
|
||||
| Начало TX в RX | отсчёт 4 256 091 |
|
||||
| Частота дискретизации | 8 Мвыб/с |
|
||||
| Символьная скорость | 500 ксимв/с |
|
||||
| Пакетов | 16 |
|
||||
| Длина одного полного waveform | 92 256 отсчётов |
|
||||
|
||||
Физическое устройство и ответ программного интерфейса записаны отдельно:
|
||||
|
||||
- `PHYSICAL DEVICE = Auron HackRF Plus`
|
||||
- `API/FIRMWARE IDENTIFICATION = HackRF One r9`
|
||||
|
||||
## Зафиксированный результат
|
||||
|
||||
Фактический TX-файл содержит пакеты вплотную: `92 256 × 16 = 1 476 096`. Дополнительных пауз между полными waveform нет. Внутренние защитные участки и хвосты RRC каждого пакета сохранены.
|
||||
|
||||
`INTER-PACKET GAP ACTUAL = 0 additional samples = 0 s`
|
||||
|
||||
Причина установлена по `prepare()` в `experiments/lab062_full_packet_rf_preflight.py`: шестнадцать результатов `build_parameterized_tx_cs8(...)` записываются подряд, параметра паузы и записи нулей между ними нет. Это ошибка реализации подготовленного потока Lab062, а не доказанное ограничение генератора PHY.
|
||||
|
||||
Записанный `longest = 16 672 bytes` для cs8 соответствует 8 336 комплексным отсчётам, 1,042 мс, 521 символу и 9,035727 % waveform одного пакета. Это оценка масштаба сообщения журнала. RX-запись не доказывает, что именно столько эфирных отсчётов было потеряно, повторено или заменено.
|
||||
|
||||
Локальное сопоставление 512-символьными окнами с шагом 256 символов дало 359 окон. В 221 надёжном внутреннем окне задержка равна 0…1 отсчёту, скачков не найдено. Ещё 22 внутренних окна в пакетах 3, 4, 12 и 13 имеют корреляцию ниже порога 0,15; задержка в них не установлена:
|
||||
|
||||
- `UNDERRUN LOCATION = NOT_LOCALIZED_FROM_RX`
|
||||
- `LOCAL DELAY DISCONTINUITY = NO IN 221 RELIABLE WINDOWS; UNDETERMINED IN 22 INTERNAL LOW-CORRELATION WINDOWS`
|
||||
|
||||
Участки на краях пакетов и повторяющаяся PRBS-преамбула сохранены в CSV, но исключены из решения о разрыве: там возникают корреляционные копии с шагом 2 032 отсчёта. Те же ограничения применены к 22 внутренним окнам с низкой корреляцией. Низкая корреляция сама по себе отмечает локальную аномалию, но без надёжной временной привязки не определяет underrun.
|
||||
|
||||
Ухудшение относительно Lab056 B наблюдается у всех 16 пакетов по трём общим показателям: доле ошибочных символов, EVM и отношению согласованной составляющей к остатку. При этом пакеты 3, 4, 12 и 13 дополнительно выделяются всплесками AC-мощности и остатка. Пакет 14 такого общего физического признака не имеет.
|
||||
|
||||
- `PACKETS MOST AFFECTED = 13, 4, 12, 3`
|
||||
- `LAB062 QUALITY DISTRIBUTION = GLOBAL`
|
||||
- `PRIMARY CAUSE STATUS = PARTIALLY_LOCALIZED`
|
||||
|
||||
Установлено глобальное ухудшение принятого сигнала и отсутствие наблюдаемого временного скачка в надёжных окнах. Точная физическая причина ухудшения и смысл расхождения промежуточного и итогового счётчиков `hackrf_transfer` не установлены.
|
||||
|
||||
## Артефакты
|
||||
|
||||
- `experiments/lab063_tx_underrun_localization.py`
|
||||
- `tests/test_lab063_tx_underrun_localization.py`
|
||||
- `data/processed/lab063/summary.json`
|
||||
- `data/processed/lab063/packet_quality.csv`
|
||||
- `data/processed/lab063/local_alignment.csv`
|
||||
- `docs/lab063_offline_result.md`
|
||||
|
||||
Следующий один шаг до нового TX: отдельно согласовать исследование уровня RX, усилений, геометрии и возможной аналоговой компрессии или слишком слабого полезного сигнала. Для будущего повтора также потребуется однозначное журналирование TX underrun и явное добавление запланированных межпакетных пауз. RF-параметры в Lab063 не менялись.
|
||||
212
docs/lab063_offline_result.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Lab063: офлайн-локализация TX underrun и ухудшения пакетов Lab062
|
||||
|
||||
## Что установлено
|
||||
|
||||
Lab062 остаётся аппаратно невалидным опытом: во время единственной передачи журнал показал `1 underruns, longest 16672 bytes`, хотя итоговая строка показала ноль. Офлайн-анализ не нашёл в RX-записи временного скачка, пропуска или повтора ожидаемого масштаба в 221 надёжном внутреннем окне. Локальная задержка в них составляет 0…1 отсчёт. Ещё в 22 внутренних окнах корреляция ниже 0,15, поэтому задержку там установить нельзя.
|
||||
|
||||
Качество сигнала при этом хуже Lab056 B на протяжении всего потока. Все 16 пакетов имеют большую долю ошибочных символов и EVM, а отношение согласованной полезной составляющей к остатку у всех ниже. Пакеты 3, 4, 12 и 13 содержат дополнительные всплески AC-мощности и остатка при почти неизменной TX-согласованной амплитуде. Это указывает на добавочную помеху или искажение, а не на провал полезной несущей в этих пакетах. По сохранённой записи нельзя установить конкретный аппаратный источник.
|
||||
|
||||
Цифровые значения далеки от пределов cs8: по пакетам `max |I| = 3…6`, `max |Q| = 4…7`. Сильная аналоговая перегрузка поэтому менее вероятна. Отсутствие rail clipping не является измерением аналоговой линейности, и без отдельного прибора аналоговую компрессию исключить нельзя.
|
||||
|
||||
Итоговый выбор: **B. `GLOBAL_RF_DEGRADATION`**. Единичный локальный underrun недостаточен для объяснения всего результата, а его положение по RX не локализовано.
|
||||
|
||||
## Целостность и границы анализа
|
||||
|
||||
Анализ выполнен без RF и без опроса аппаратуры:
|
||||
|
||||
- TX SHA-256: `e84bbce227426c0aac13a48ded1286ca4a44fc6e3efce01f6e5a513217c87ff5`;
|
||||
- RX SHA-256: `ce0bd603e4ca356af67d504ee11c9e9f38171f31c7427fbe61216a28126b2b32`;
|
||||
- TX: 1 476 096 комплексных отсчётов;
|
||||
- RX: 24 000 000 комплексных отсчётов;
|
||||
- сохранённое начало TX в RX: отсчёт 4 256 091;
|
||||
- `RF TX = 0`;
|
||||
- аппаратных запросов: 0.
|
||||
|
||||
Поиск использует известный TX только после строгого результата `0/16`; это диагностическая привязка, которая не превращает пакет в принятый и не меняет verdict `legacy` или T2.
|
||||
|
||||
## Фактические паузы между пакетами
|
||||
|
||||
Проверка длины даёт точное равенство:
|
||||
|
||||
`92 256 отсчётов × 16 пакетов = 1 476 096 отсчётов`.
|
||||
|
||||
Следовательно:
|
||||
|
||||
`ACTUAL INTER-PACKET GAP = 0 additional samples = 0 s`.
|
||||
|
||||
Запланированных пауз 5 мс в TX-файле нет. Внутренние защитные интервалы и хвосты RRC, входящие в каждый 92 256-отсчётный waveform, присутствуют.
|
||||
|
||||
Код `prepare()` в `experiments/lab062_full_packet_rf_preflight.py` записывает шестнадцать результатов `build_parameterized_tx_cs8(...)` непосредственно друг за другом. Между вызовами нет параметра паузы, массива нулей или другой операции, которая добавила бы 5 мс. Причина: **ошибка реализации подготовки Lab062**. Возможность сформировать паузы в других генераторах проекта означает, что это не установленное ограничение PHY-генератора.
|
||||
|
||||
## Масштаб сообщения TX underrun
|
||||
|
||||
Для cs8 один комплексный отсчёт занимает два байта:
|
||||
|
||||
| Величина | Значение |
|
||||
|---|---:|
|
||||
| Журнал, bytes | 16 672 |
|
||||
| Комплексные отсчёты | 8 336 |
|
||||
| Длительность при 8 Мвыб/с | 1,042 мс |
|
||||
| Символы при 500 ксимв/с | 521 |
|
||||
| Доля waveform одного пакета | 9,035727 % |
|
||||
|
||||
Это только пересчёт масштаба строки журнала. Он не доказывает фактическую потерю, повтор или замену 8 336 отсчётов в эфире.
|
||||
|
||||
## Локальное сопоставление TX и RX
|
||||
|
||||
TX разбит на окна по 512 символов, то есть по 8 192 отсчёта. Шаг равен 256 символам, или 4 096 отсчётам. Для каждого окна независимо искалось положение в RX в пределах ±12 000 отсчётов, затем вычислялись нормированная корреляция, комплексный коэффициент `h`, его амплитуда и фаза, а также остаточная мощность.
|
||||
|
||||
Повторяющаяся PRBS-преамбула создаёт ложные корреляционные максимумы с периодом 2 032 отсчёта. Поэтому решение о временном скачке использует внутреннюю часть каждого пакета от отсчёта 24 000 до 86 000 и требует корреляцию не ниже 0,15. Края, преамбула и внутренние окна ниже порога остаются в `local_alignment.csv` для проверки, но их выбранная задержка не участвует в verdict.
|
||||
|
||||
| Показатель | Значение |
|
||||
|---|---:|
|
||||
| Всего окон | 359 |
|
||||
| Надёжных окон | 221 |
|
||||
| Внутренних окон с корреляцией ниже 0,15 | 22 |
|
||||
| Корреляция, минимум по всем окнам | 0,071665 |
|
||||
| Корреляция, медиана по всем окнам | 0,265526 |
|
||||
| Корреляция, максимум по всем окнам | 0,287394 |
|
||||
| Локальная задержка в надёжных окнах | 0…1 отсчёт |
|
||||
| Кандидаты на скачок | 0 |
|
||||
|
||||
- `UNDERRUN LOCATION = NOT_LOCALIZED_FROM_RX`
|
||||
- `LOCAL DELAY DISCONTINUITY = NO IN 221 RELIABLE WINDOWS; UNDETERMINED IN 22 INTERNAL LOW-CORRELATION WINDOWS`
|
||||
|
||||
Внутренние окна с низкой корреляцией распределены так:
|
||||
|
||||
- пакет 3: 5 окон, позиции 69 440…85 824 отсчёта внутри пакета;
|
||||
- пакет 4: 7 окон, позиции 26 336…46 816 и 55 008 отсчётов;
|
||||
- пакет 12: 7 окон, позиции 58 336…82 912 отсчётов;
|
||||
- пакет 13: 3 окна, позиции 27 520…35 712 отсчёта.
|
||||
|
||||
Низкая корреляция совпадает с пакетами, где выросли AC-мощность и остаток. Она подтверждает локальные участки сильного искажения. Без надёжной оценки задержки эти участки не доказывают пропуск или повтор отсчётов и не локализуют TX underrun.
|
||||
|
||||
Поиск не подгонялся под ожидаемые 8 336 отсчётов. Он способен обнаружить синтетически вставленный скачок и проверяет переходы между пакетами; оба свойства закрыты отдельными тестами.
|
||||
|
||||
## Качество пакетов 1–16
|
||||
|
||||
`raw` означает полную принятую мощность вместе с DC. `useful` в таблице означает AC-мощность пакета после вычитания средней фоновой AC-мощности до и после потока. Это избыточная над фоном мощность, а не доказанная мощность только полезного TX-сигнала. `M/R` означает отношение TX-согласованной составляющей к остатку. Остаток включает шум, помехи, искажения канала и возможное влияние underrun.
|
||||
|
||||
| № | raw, код² | useful, код² | SER, % | EVM RMS | matched / residual, код² | M/R, дБ | ripple, дБ | CFO, Гц | phase, рад |
|
||||
|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 1 | 4,900592 | 0,062620 | 8,517888 | 1,088322 | 0,707513 / 0,838011 | -0,735151 | 6,642520 | +1,007302 | 5,396055 |
|
||||
| 2 | 4,865147 | 0,056422 | 9,795571 | 1,104921 | 0,670874 / 0,819037 | -0,866627 | 6,598551 | +1,641699 | 0,911620 |
|
||||
| 3 | 6,015186 | 1,184291 | 10,221465 | 1,357844 | 0,618128 / 1,139667 | -2,656997 | 7,370969 | -0,961988 | 0,917422 |
|
||||
| 4 | 8,462127 | 3,605520 | 15,587734 | 1,663418 | 0,694253 / 1,920969 | -4,420028 | 10,617549 | +2,584550 | 5,388297 |
|
||||
| 5 | 4,954615 | 0,062684 | 8,986371 | 1,099196 | 0,702182 / 0,848399 | -0,821507 | 6,409726 | -1,235846 | 0,834217 |
|
||||
| 6 | 4,919648 | 0,053559 | 7,580920 | 1,102402 | 0,644466 / 0,783214 | -0,846803 | 6,364293 | -1,779543 | 5,318132 |
|
||||
| 7 | 4,958322 | 0,059386 | 8,390119 | 1,100152 | 0,681935 / 0,825370 | -0,829054 | 6,236828 | -0,248409 | 1,461754 |
|
||||
| 8 | 4,913306 | 0,063207 | 8,262351 | 1,149257 | 0,638148 / 0,842861 | -1,208345 | 6,714921 | -1,078363 | 0,781772 |
|
||||
| 9 | 4,886956 | 0,065556 | 8,943782 | 1,141831 | 0,658122 / 0,858045 | -1,152037 | 6,809132 | +0,832282 | 1,073865 |
|
||||
| 10 | 4,915355 | 0,067571 | 8,347530 | 1,092396 | 0,697137 / 0,831913 | -0,767600 | 6,678161 | -1,337873 | 0,770315 |
|
||||
| 11 | 4,919918 | 0,072081 | 8,049404 | 1,085344 | 0,688251 / 0,810740 | -0,711347 | 6,630624 | +1,139696 | 0,886944 |
|
||||
| 12 | 6,800707 | 1,948029 | 12,819421 | 1,456604 | 0,631331 / 1,339491 | -3,266829 | 7,945032 | -0,158710 | 1,085095 |
|
||||
| 13 | 7,571594 | 2,728649 | 16,013629 | 1,542472 | 0,670672 / 1,595675 | -3,764344 | 9,871896 | -3,768748 | 1,477869 |
|
||||
| 14 | 4,931040 | 0,060762 | 9,454855 | 1,125042 | 0,631853 / 0,799750 | -1,023377 | 6,450866 | +0,071570 | 0,850015 |
|
||||
| 15 | 4,914640 | 0,056175 | 9,284497 | 1,156613 | 0,648517 / 0,867557 | -1,263765 | 7,550267 | +0,636870 | 5,326811 |
|
||||
| 16 | 4,976760 | 0,059023 | 8,603066 | 1,063657 | 0,702947 / 0,795290 | -0,536031 | 6,391673 | +1,336558 | 0,741441 |
|
||||
|
||||
Полные значения, включая локальную задержку, корреляцию, `|h|`, DC и разности относительно Lab056 B, находятся в `data/processed/lab063/packet_quality.csv`.
|
||||
|
||||
## Уровень RX и DC во время передачи
|
||||
|
||||
Средняя фоновая AC-мощность соседних окон равна 0,525282 код², что соответствует AC RMS 0,724764 кода. На RX-only preflight было 0,985483 кода AC RMS.
|
||||
|
||||
| № | RMS full | AC RMS | mean I | mean Q | max \|I\| | max \|Q\| | max \|IQ\| |
|
||||
|---:|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 1 | 2,213728 | 0,766748 | 0,969270 | 1,836629 | 3 | 4 | 4,472136 |
|
||||
| 2 | 2,205708 | 0,762695 | 0,967753 | 1,829453 | 3 | 4 | 4,472136 |
|
||||
| 3 | 2,452588 | 1,307507 | 0,970094 | 1,834266 | 6 | 7 | 7,211102 |
|
||||
| 4 | 2,908974 | 2,032438 | 0,960349 | 1,846362 | 6 | 7 | 7,615773 |
|
||||
| 5 | 2,225897 | 0,766789 | 0,964512 | 1,853744 | 3 | 4 | 4,242640 |
|
||||
| 6 | 2,218028 | 0,760816 | 0,968934 | 1,844444 | 3 | 4 | 4,472136 |
|
||||
| 7 | 2,226729 | 0,764636 | 0,963515 | 1,856150 | 3 | 4 | 4,472136 |
|
||||
| 8 | 2,216598 | 0,767131 | 0,958919 | 1,845343 | 3 | 4 | 4,472136 |
|
||||
| 9 | 2,210646 | 0,768660 | 0,954128 | 1,840043 | 3 | 4 | 4,472136 |
|
||||
| 10 | 2,217060 | 0,769969 | 0,947830 | 1,850438 | 3 | 4 | 4,242640 |
|
||||
| 11 | 2,218089 | 0,772893 | 0,943039 | 1,852898 | 3 | 4 | 4,472136 |
|
||||
| 12 | 2,607816 | 1,572676 | 0,954995 | 1,848075 | 6 | 7 | 8,062258 |
|
||||
| 13 | 2,751653 | 1,803866 | 0,952307 | 1,846828 | 6 | 7 | 7,810249 |
|
||||
| 14 | 2,220594 | 0,765535 | 0,952166 | 1,854286 | 3 | 4 | 4,472136 |
|
||||
| 15 | 2,216899 | 0,762534 | 0,949651 | 1,852389 | 3 | 4 | 4,472136 |
|
||||
| 16 | 2,230865 | 0,764399 | 0,956903 | 1,864616 | 3 | 4 | 4,472136 |
|
||||
|
||||
DC стабилен: `mean I = 0,943039…0,970094`, `mean Q = 1,829453…1,864616`. Ступени AC-амплитуды есть в пакетах 3, 4, 12 и 13. При этом медиана локального `|h|` по пакетам меняется только от 0,004488 до 0,004805. Рост полной AC-мощности в этих пакетах создаёт главным образом остаток, а не согласованный полезный компонент.
|
||||
|
||||
## Пакеты 3, 4, 12 и 14
|
||||
|
||||
У четырёх ранее отмеченных пакетов нет общего признака underrun:
|
||||
|
||||
- у всех четырёх нет скачка задержки;
|
||||
- у них нет общего провала мощности;
|
||||
- у них нет общей потери локальной корреляции;
|
||||
- пакет 14 близок к обычным пакетам по AC RMS, SER, EVM и неравномерности полосы;
|
||||
- пакеты 3, 4 и 12 имеют всплески AC-мощности и остатка; аналогичный, ещё более выраженный всплеск есть у пакета 13.
|
||||
|
||||
Поэтому связывать набор `3, 4, 12, 14` с одним TX underrun оснований нет. По сводному ранжированию наиболее повреждены пакеты `13, 4, 12, 3`.
|
||||
|
||||
## Тренд по четвертям потока
|
||||
|
||||
| Пакеты | useful, среднее код² | SER, % | EVM RMS | matched, код² | residual, код² | M/R, дБ | CFO, Гц | phase error, рад | local phase span, рад | медиана \|h\| |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 1–4 | 1,227213 | 11,030664 | 1,303626 | 0,672692 | 1,179421 | -2,169701 | +1,067891 | 3,153349 | 0,117419 | 0,004637 |
|
||||
| 5–8 | 0,059709 | 8,304940 | 1,112752 | 0,666683 | 0,824961 | -0,926427 | -1,085540 | 2,098969 | 0,150020 | 0,004608 |
|
||||
| 9–12 | 0,538309 | 9,540034 | 1,194044 | 0,668710 | 0,960047 | -1,474453 | +0,118849 | 0,954055 | 0,128511 | 0,004658 |
|
||||
| 13–16 | 0,726152 | 10,839012 | 1,221946 | 0,663497 | 1,014568 | -1,646879 | -0,430938 | 2,099034 | 0,098633 | 0,004536 |
|
||||
|
||||
Согласованная составляющая и локальный коэффициент передачи стабильны по четвертям. Средний локальный фазовый размах лежит в пределах 0,098633…0,150020 рад; последовательного роста к концу потока нет. Остаток и SER меняются вместе со всплесками избыточной мощности. Базовое качество всех четвертей хуже Lab056 B.
|
||||
|
||||
## Сравнение с Lab056 B
|
||||
|
||||
| Метрика | Lab056 B | Lab062, диапазон пакетов | Результат |
|
||||
|---|---:|---:|---|
|
||||
| SER | 3,495896 % | 7,580920…16,013629 % | все 16 хуже |
|
||||
| EVM RMS | 0,845350 | 1,063657…1,663418 | все 16 хуже |
|
||||
| Matched/residual | +1,464846 дБ | -4,420028…-0,536031 дБ | все 16 хуже |
|
||||
| Неравномерность полосы | 5,471382 дБ | 6,236828…10,617549 дБ | все 16 хуже |
|
||||
|
||||
Ухудшение не ограничено одним пакетом или коротким участком. Локальный TX-согласованный gain не показывает потери длиной около 8 336 отсчётов. Поэтому наблюдавшийся TX underrun остаётся достаточной причиной признать Lab062 невалидной, но не является достаточным объяснением глобально худшего качества.
|
||||
|
||||
## Смысл счётчиков `hackrf_transfer`
|
||||
|
||||
Сохранены обе строки:
|
||||
|
||||
- промежуточная: `1 underruns, longest 16672 bytes`;
|
||||
- итоговая: `0 underruns, longest 0 bytes`.
|
||||
|
||||
В локальной установке исходный файл `hackrf_transfer.c` и документация с описанием сброса этих счётчиков не найдены. В бинарном файле обнаружены только строки формата статистики. Поэтому:
|
||||
|
||||
`COUNTER SEMANTICS = NOT_ESTABLISHED`.
|
||||
|
||||
Нулевая итоговая строка не отменяет ненулевое промежуточное наблюдение. Причину расхождения без исходного кода используемой версии придумывать нельзя.
|
||||
|
||||
## Графики и машинные результаты
|
||||
|
||||
- [Локальная задержка](../data/processed/lab063/local_delay_vs_time.png)
|
||||
- [Нормированная корреляция](../data/processed/lab063/normalized_correlation_vs_time.png)
|
||||
- [Амплитуда коэффициента передачи](../data/processed/lab063/gain_vs_time.png)
|
||||
- [Фаза коэффициента передачи](../data/processed/lab063/phase_vs_time.png)
|
||||
- [Принятая мощность по пакетам](../data/processed/lab063/received_power_by_packet.png)
|
||||
- [SER по пакетам](../data/processed/lab063/symbol_error_rate_by_packet.png)
|
||||
- [Matched/residual по пакетам](../data/processed/lab063/matched_residual_by_packet.png)
|
||||
- [Сравнение Lab056 B и Lab062](../data/processed/lab063/lab056_b_vs_lab062_packets.png)
|
||||
- [Сводка JSON](../data/processed/lab063/summary.json)
|
||||
- [Качество пакетов CSV](../data/processed/lab063/packet_quality.csv)
|
||||
- [Локальное сопоставление CSV](../data/processed/lab063/local_alignment.csv)
|
||||
|
||||
## Инженерный вывод
|
||||
|
||||
`LAB062 QUALITY DISTRIBUTION = GLOBAL`.
|
||||
|
||||
`PRIMARY CAUSE STATUS = PARTIALLY_LOCALIZED`.
|
||||
|
||||
Найдена ошибка подготовки TX: обещанные межпакетные паузы не были записаны. Подтверждено глобальное ухудшение относительно Lab056 B и отдельные всплески помехи или искажения. Положение underrun и точная физическая причина общего ухудшения не установлены.
|
||||
|
||||
`NEXT SINGLE STEP = до нового TX отдельно исследовать уровень RX, усиления, геометрию и возможную аналоговую компрессию либо слишком слабый полезный сигнал; будущий TX-процесс должен иметь явные паузы и однозначное журналирование underrun`.
|
||||
|
||||
Новый RF-сеанс требует отдельного разрешения. В рамках Lab063 параметры PHY, приёмник, усиления и частота не менялись.
|
||||
|
||||
- `RF TX = 0`
|
||||
- `LAB063 COMMIT = NOT PERFORMED`
|
||||
- `PUSH = NOT PERFORMED`
|
||||
- `LAB064 = NOT CREATED`
|
||||
141
docs/lab064_current_state.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Lab064: измерение уровня и линейности RF-тракта
|
||||
|
||||
Lab064 проверяет, следует ли мощность согласованной принятой составляющей
|
||||
заранее заданной цифровой лестнице TX. Это измерительный опыт с известным
|
||||
PRBS7/BPSK-сигналом, а не пакетный тест. FEC, CRC, T2, JPEG и packet output в
|
||||
нём не используются.
|
||||
|
||||
На текущем этапе сформирован сигнал, подготовлены RX-first runner, офлайн-
|
||||
анализатор и полный учёт сообщений overruns/underruns. Перед аппаратным
|
||||
preflight и будущим RF-сеансом исходные параметры не меняются.
|
||||
|
||||
## Физические условия
|
||||
|
||||
| Условие | Значение |
|
||||
|---|---|
|
||||
| Центральная частота | 1400000000 Гц |
|
||||
| Расстояние TX–RX | примерно 1,2 м |
|
||||
| Геометрия | прямая видимость, помещение |
|
||||
| TX | плата и антенна возле окна |
|
||||
| RX | плата и антенна на столе пользователя |
|
||||
| Антенны | имеющиеся антенны на 1400 МГц |
|
||||
| Поляризация | одинаковая |
|
||||
| Тактирование | 10 МГц TCXO → MASTER → MASTER CLKOUT → SLAVE CLKIN |
|
||||
|
||||
Антенны, платы, длинный кабель общей опоры, USB-подключение и остальные
|
||||
кабели не перемещаются. Во время опыта не меняются частота, усиления,
|
||||
ориентация и форма сигнала.
|
||||
|
||||
## Подготовленный измерительный сигнал
|
||||
|
||||
Параметры сигнала: BPSK/PRBS7, 20000 символов/с, Fs 8000000 комплексных
|
||||
отсчётов/с, 400 отсчётов/символ, цифровая несущая +50000 Гц и существующий RRC
|
||||
с roll-off 0,35 и span 10 символов. TX VGA равен 10 дБ. RX LNA/VGA равны
|
||||
24/24 дБ. RF amplifier и Bias-Tee выключены.
|
||||
|
||||
Один файл содержит 100 мс нулей, четыре одинаковых по содержанию участка по
|
||||
100 мс, три нулевых промежутка по 50 мс и 100 мс нулей в конце. Общая длина:
|
||||
6000000 комплексных отсчётов, 12000000 байт, 0,75 с. Нулевые участки являются
|
||||
цифровыми нулями IQ; они не означают аппаратного выключения TX посреди файла.
|
||||
|
||||
SHA-256 подготовленного CS8:
|
||||
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
|
||||
|
||||
| Уровень | Задано, дБ | Фактически, дБ | TX RMS, код | max \|I\| / \|Q\|, код | Шаг мощности, дБ | Clipping |
|
||||
|---|---:|---:|---:|---:|---:|---|
|
||||
| L0 | −18 | −17,973685 | 5,556137 | 9 / 9 | — | нет |
|
||||
| L1 | −12 | −11,996415 | 11,056978 | 17 / 17 | +5,977270 | нет |
|
||||
| L2 | −6 | −5,998257 | 22,056892 | 34 / 35 | +5,998157 | нет |
|
||||
| L3 | 0 | 0 | 44,000457 | 69 / 69 | +5,998257 | нет |
|
||||
|
||||
Небольшое отклонение L0 от −18 дБ вызвано квантованием CS8. Каждый шаг
|
||||
укладывается в заранее установленный допуск ±0,1 дБ.
|
||||
|
||||
## Анализ будущей записи
|
||||
|
||||
Для каждого L0…L3 анализатор отдельно выдаёт полный и AC RMS, средние I/Q,
|
||||
max |I|, max |Q| и максимальную комплексную амплитуду. По соседнему нулевому
|
||||
окну оценивается фон. По известному TX оцениваются комплексный коэффициент
|
||||
передачи `h`, `|h|²`, мощность согласованной составляющей, мощность остатка и
|
||||
их отношение.
|
||||
|
||||
Для каждой соседней пары вычисляются:
|
||||
|
||||
```text
|
||||
TX INPUT DIFFERENCE
|
||||
RX MATCHED POWER DIFFERENCE
|
||||
LINEARITY ERROR = RX difference − TX difference
|
||||
```
|
||||
|
||||
Критерии зафиксированы до RF:
|
||||
|
||||
- линейный участок: максимальная абсолютная ошибка шага не более 1,0 дБ;
|
||||
- признак компрессии: ошибка верхнего шага не более −1,5 дБ;
|
||||
- `LINEAR_BUT_WEAK`: шаги линейны, фон стабилен, а L3 ниже 3 дБ над остатком;
|
||||
- `HEALTHY_LINEAR_RANGE`: шаги линейны, фон стабилен, а L3 не менее 6 дБ над
|
||||
остатком;
|
||||
- неоднозначный результат получает статус `NOT_LOCALIZED`.
|
||||
|
||||
Эти границы являются правилами данного опыта, а не абсолютной калибровкой
|
||||
RF-мощности. Абсолютную выходную и принятую мощность без измерительного прибора
|
||||
установить нельзя.
|
||||
|
||||
Синтетическая проверка известного линейного комплексного канала с DC и шумом
|
||||
восстановила три цифровых шага с максимальной ошибкой меньше 0,0001 дБ. Это
|
||||
проверяет вычисления анализатора, но не подтверждает аппаратные свойства
|
||||
HackRF или эфирного канала.
|
||||
|
||||
## Управление аппаратным сеансом
|
||||
|
||||
Preflight допускает только RX-команды. Он проверяет MASTER/SLAVE reg29=0x26,
|
||||
SLAVE CLKIN=DETECTED, процессы, инвентаризацию, 0,1 с фона, rail-коды,
|
||||
overruns, AC RMS и максимумы |I|/|Q|. Усиления автоматически не меняются.
|
||||
|
||||
Будущий runner запускает RX раньше TX. Передача требует отдельного явного
|
||||
флага, создаёт одноразовый маркер до обращения к SDR и допускает ровно один
|
||||
TX-вызов. Полные stdout и stderr сохраняются раздельно. В итог попадают все
|
||||
промежуточные и финальные счётчики потерь, их максимум и последнее значение.
|
||||
Любой underrun, overrun, clipping или аппаратная ошибка завершает опыт без
|
||||
повтора. Анализатор при этом может сохранить диагностические метрики, но
|
||||
выдаёт только `INVALID_CAPTURE`: варианты A/B/C запрещены. Проверка использует
|
||||
все строки счётчиков и их максимум, поэтому последний ноль не может скрыть
|
||||
предыдущее ненулевое значение.
|
||||
|
||||
После исправления строгой проверки счётчиков quick gate прошёл: 984 теста
|
||||
ядра, 37 функциональных проверок выполнены, 13 пропущены. Полный pytest:
|
||||
984 passed, 0 failed. Независимая read-only проверка подтвердила подготовленный
|
||||
CS8, программные метрики и запрет классификации невалидной записи.
|
||||
|
||||
## Аппаратный preflight
|
||||
|
||||
14 сентября 2026 года выполнена одна разрешённая аппаратная проверка при
|
||||
выключенном передатчике. MASTER/SLAVE reg29 равны `0x26`, SLAVE CLKIN имеет
|
||||
состояние `DETECTED` при первом опросе через 66,793 мс. RX-only запись длиной
|
||||
800000 отсчётов дала AC RMS 0,832038 кода, max |I|/|Q| 4/5 кодов, clipping 0 и
|
||||
overruns 0. Неожиданно сильного сигнала нет. После отключения CLKOUT процессы
|
||||
имеют состояние `CLEAR`, ошибок очистки нет. Подробности приведены в
|
||||
[отчёте preflight](lab064_rx_preflight.md).
|
||||
|
||||
`RF TX = 0`. На этом этапе Lab064 не коммитируется. Push не выполняется.
|
||||
Lab065 автоматически не начинается.
|
||||
|
||||
## Фактический RF-результат
|
||||
|
||||
14 сентября 2026 года выполнен один разрешённый RF-сеанс без повторов. TX
|
||||
underruns 0, RX overruns 0, clipping 0; RX содержит 16000000 комплексных
|
||||
отсчётов за 2,0 с. Согласованная мощность выросла на 17,809077 дБ при цифровом
|
||||
росте 17,973685 дБ. Общая ошибка линейности равна −0,164609 дБ. Верхняя ступень
|
||||
дала +6,020420 дБ при ожидаемых +5,998257 дБ, признака компрессии нет.
|
||||
|
||||
Фон стабилен: AC RMS 0,718264…0,728217 кода, разброс мощности 0,119530 дБ.
|
||||
Даже L3 остаётся на −12,133336 дБ относительно остатка. Итог по заранее
|
||||
заданным критериям: `LINEAR_BUT_WEAK`. Подробные значения приведены в
|
||||
[RF-отчёте](lab064_rf_result.md).
|
||||
|
||||
После сеанса `RF TX = 0`, процессы `CLEAR`. Усиления не менялись, второй TX не
|
||||
выполнялся. Lab064 не коммитировалась, push не выполнялся, Lab065 не создана.
|
||||
|
||||
Независимый пересчёт непосредственно по TX/RX CS8 подтвердил SHA, аппаратные
|
||||
счётчики, все четыре matched/residual/`h`, пять окон фона, три ступени и общий
|
||||
результат L3/L0. Расхождений нет, вердикт проверки: `чисто`. После сеанса quick
|
||||
gate и полный pytest дали 986 passed, 0 failed; `git diff --check` прошёл.
|
||||
216
docs/lab064_rf_result.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Lab064: результат измерения уровня и линейности RF-тракта
|
||||
|
||||
В одном коротком OTA-сеансе цифровая мощность TX была увеличена почти на 18 дБ,
|
||||
то есть цифровая амплитуда выросла примерно в 7,92 раза. Согласованная с
|
||||
известным TX амплитуда на RX выросла в 7,77 раза, а её мощность на 17,81 дБ.
|
||||
Коэффициент передачи `|h|` почти не менялся. Признака компрессии на верхнем
|
||||
уровне не обнаружено.
|
||||
|
||||
При этом полезная согласованная составляющая остаётся слабее остатка даже на
|
||||
L3: −12,13 дБ. Фон во всех пяти нулевых окнах стабилен. По заранее заданным
|
||||
критериям результат классифицирован как `LINEAR_BUT_WEAK`.
|
||||
|
||||
Опыт установил относительную линейность именно этой цифровой лестницы в
|
||||
текущей OTA-геометрии. Он не измеряет абсолютную мощность в ваттах или дБм и не
|
||||
разделяет вклад TX, антенн, эфирного канала и RX. Для этого нужен измерительный
|
||||
прибор или отдельный проведённый тест по кабелю.
|
||||
|
||||
## Сеанс и аппаратная целостность
|
||||
|
||||
14 сентября 2026 года выполнен ровно один разрешённый TX. RX стартовал раньше
|
||||
TX. Повторных передач не было.
|
||||
|
||||
| Проверка | Результат |
|
||||
|---|---:|
|
||||
| TX waveform SHA-256 | `fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9` |
|
||||
| RF / Fs / symbol rate | 1400000000 Гц / 8000000 отсчётов/с / 20000 символов/с |
|
||||
| TX VGA / RX LNA / RX VGA | 10 / 24 / 24 дБ |
|
||||
| RF amplifier / Bias-Tee | выключены |
|
||||
| MASTER / SLAVE reg29 | `0x26` / `0x26` |
|
||||
| SLAVE CLKIN | `DETECTED`, второй опрос, 139,042 мс |
|
||||
| Выполненные TX-сеансы | 1 |
|
||||
| Автоматические повторы | 0 |
|
||||
| TX underrun, максимум / финал | 0 / 0 |
|
||||
| RX overrun, максимум / финал | 0 / 0 |
|
||||
| RX clipping | 0 |
|
||||
| RX длина | 16000000 комплексных отсчётов, 2,000000 с |
|
||||
| RX SHA-256 | `02015891948aaee52ff81e60fdfeb391bb9b76cd9192b75972b8fedbc8690d86` |
|
||||
| Процессы до / после | `CLEAR` / `CLEAR` |
|
||||
| Ошибки очистки | 0 |
|
||||
|
||||
В TX-журнале сохранены две строки `0 underruns`; в RX-журнале сохранены четыре
|
||||
строки `0 overruns`. Анализ использует все наблюдения и их максимум. Финальный
|
||||
нулевой счётчик не мог бы скрыть промежуточное ненулевое значение.
|
||||
|
||||
## Фон
|
||||
|
||||
Каждое окно имеет длину 160000 отсчётов, или 20 мс, и лежит внутри цифрового
|
||||
нулевого участка вдали от активной границы.
|
||||
|
||||
| Окно | AC power, код² | AC RMS, код |
|
||||
|---|---:|---:|
|
||||
| Начальный guard | 0,521529 | 0,722170 |
|
||||
| Gap L0–L1 | 0,523180 | 0,723312 |
|
||||
| Gap L1–L2 | 0,515904 | 0,718264 |
|
||||
| Gap L2–L3 | 0,522200 | 0,722634 |
|
||||
| Конечный guard | 0,530300 | 0,728217 |
|
||||
|
||||
Диапазон AC RMS равен 0,718264…0,728217 кода. Отношение максимальной AC-мощности
|
||||
к минимальной составляет 0,119530 дБ при заранее заданной границе стабильности
|
||||
3 дБ. Фон считается стабильным.
|
||||
|
||||
## Метрики участков
|
||||
|
||||
Мощности приведены в квадрате цифрового кода RX. Это относительные цифровые
|
||||
величины, а не ватты и не дБм.
|
||||
|
||||
### L0, −18 дБ
|
||||
|
||||
```text
|
||||
FULL IQ RMS = 2.277200 code
|
||||
AC RMS = 0.956317 code
|
||||
MEAN I = 0.949724 code
|
||||
MEAN Q = 1.835518 code
|
||||
MAX |I| = 6 code
|
||||
MAX |Q| = 7 code
|
||||
MAX COMPLEX MAGNITUDE = 7.071068 code
|
||||
BACKGROUND AC POWER = 0.521529 code^2
|
||||
MATCHED SIGNAL POWER = 0.000750751 code^2
|
||||
RESIDUAL POWER = 1.213794804 code^2
|
||||
MATCHED / RESIDUAL = -32.086493 dB
|
||||
h = 0.001876064 - j0.004508063
|
||||
|h| = 0.004882853
|
||||
|h|^2 = 0.000023842249
|
||||
CFO = +0.095772 Hz
|
||||
```
|
||||
|
||||
### L1, −12 дБ
|
||||
|
||||
```text
|
||||
FULL IQ RMS = 2.291392 code
|
||||
AC RMS = 0.989014 code
|
||||
MEAN I = 0.956038 code
|
||||
MEAN Q = 1.832573 code
|
||||
MAX |I| = 6 code
|
||||
MAX |Q| = 7 code
|
||||
MAX COMPLEX MAGNITUDE = 7.211103 code
|
||||
BACKGROUND AC POWER = 0.523180 code^2
|
||||
MATCHED SIGNAL POWER = 0.002961235 code^2
|
||||
RESIDUAL POWER = 1.069594603 code^2
|
||||
MATCHED / RESIDUAL = -25.577463 dB
|
||||
h = 0.004471119 - j0.001937686
|
||||
|h| = 0.004872939
|
||||
|h|^2 = 0.000023745532
|
||||
CFO = -0.168571 Hz
|
||||
```
|
||||
|
||||
### L2, −6 дБ
|
||||
|
||||
```text
|
||||
FULL IQ RMS = 2.275060 code
|
||||
AC RMS = 0.925298 code
|
||||
MEAN I = 0.963183 code
|
||||
MEAN Q = 1.841739 code
|
||||
MAX |I| = 6 code
|
||||
MAX |Q| = 7 code
|
||||
MAX COMPLEX MAGNITUDE = 7.280110 code
|
||||
BACKGROUND AC POWER = 0.515904 code^2
|
||||
MATCHED SIGNAL POWER = 0.011333436 code^2
|
||||
RESIDUAL POWER = 0.529088688 code^2
|
||||
MATCHED / RESIDUAL = -16.691669 dB
|
||||
h = 0.003277253 - j0.003478187
|
||||
|h| = 0.004778930
|
||||
|h|^2 = 0.000022838175
|
||||
CFO = +0.006742 Hz
|
||||
```
|
||||
|
||||
### L3, 0 дБ
|
||||
|
||||
```text
|
||||
FULL IQ RMS = 2.341160 code
|
||||
AC RMS = 1.057677 code
|
||||
MEAN I = 0.957558 code
|
||||
MEAN Q = 1.856188 code
|
||||
MAX |I| = 6 code
|
||||
MAX |Q| = 7 code
|
||||
MAX COMPLEX MAGNITUDE = 7.280110 code
|
||||
BACKGROUND AC POWER = 0.522200 code^2
|
||||
MATCHED SIGNAL POWER = 0.045331864 code^2
|
||||
RESIDUAL POWER = 0.740861774 code^2
|
||||
MATCHED / RESIDUAL = -12.133336 dB
|
||||
h = 0.003384388 - j0.003391322
|
||||
|h| = 0.004791153
|
||||
|h|^2 = 0.000022955145
|
||||
CFO = -0.076067 Hz
|
||||
```
|
||||
|
||||
Максимумы активных участков равны `|I|=6` и `|Q|=7`. Полная двухсекундная
|
||||
запись также далека от rail-кодов: `|I|=7`, `|Q|=8`. Это исключает цифровой
|
||||
clipping в записи, но не является самостоятельным доказательством идеальной
|
||||
линейности аналогового тракта.
|
||||
|
||||
## Линейность мощности и амплитуды
|
||||
|
||||
| Пара | TX, дБ | RX matched, дБ | Ошибка, дБ | TX amplitude ratio | RX matched amplitude ratio |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| L0 → L1 | +5,977270 | +5,959770 | −0,017501 | 1,990048 | 1,986042 |
|
||||
| L1 → L2 | +5,998157 | +5,828887 | −0,169270 | 1,994839 | 1,956340 |
|
||||
| L2 → L3 | +5,998257 | +6,020420 | +0,022162 | 1,994862 | 1,999959 |
|
||||
| L0 → L3 | +17,973685 | +17,809077 | −0,164609 | 7,919254 | 7,770587 |
|
||||
|
||||
Максимальная абсолютная ошибка отдельной ступени равна 0,169270 дБ и заметно
|
||||
меньше заранее установленной границы линейности 1,0 дБ. Верхняя ступень не
|
||||
отстаёт от TX: она выросла на 6,020420 дБ при ожидаемых 5,998257 дБ. Порог
|
||||
признака компрессии составлял −1,5 дБ ошибки верхней ступени; фактическая
|
||||
ошибка равна +0,022162 дБ.
|
||||
|
||||
`|h|` изменился от 0,004882853 на L0 до 0,004791153 на L3. Это соответствует
|
||||
−0,164609 дБ по мощности и совпадает с общей ошибкой линейности. Систематического
|
||||
падения коэффициента передачи на верхнем уровне нет.
|
||||
|
||||
Остаточная мощность при переходе L2 → L3 выросла примерно на 1,46 дБ, тогда
|
||||
как согласованная полезная мощность выросла на 6,02 дБ. Отношение
|
||||
matched/residual улучшилось с −16,69 до −12,13 дБ. Ухудшения формы сигнала на
|
||||
L3 относительно L2 по этому показателю нет.
|
||||
|
||||
Диагностические полосы остатка около 50/100/150 кГц на L3 равны
|
||||
0,009576/0,004469/0,002725 код². Они не растут быстрее согласованной мощности
|
||||
при переходе L2 → L3. Эти полосы не являются калиброванным измерением
|
||||
RF-гармоник, поэтому абсолютный вывод о спектральной чистоте потребовал бы
|
||||
анализатора спектра.
|
||||
|
||||
## Вывод
|
||||
|
||||
```text
|
||||
CLASSIFICATION = LINEAR_BUT_WEAK
|
||||
```
|
||||
|
||||
Текущий тракт воспроизводит цифровые ступени почти один к одному по дБ. Данных
|
||||
за компрессию при TX VGA 10 дБ и RX LNA/VGA 24/24 дБ нет. Главная проблема
|
||||
этого сеанса состоит в низкой согласованной полезной мощности относительно
|
||||
остатка: даже на L3 запас равен −12,13 дБ.
|
||||
|
||||
Практически это означает, что простое объяснение Lab062 аналоговой перегрузкой
|
||||
текущего тракта не подтверждается. Слабое отношение полезной составляющей к
|
||||
остатку остаётся вероятным фактором плохого 500K-приёма, хотя Lab064 использует
|
||||
узкий сигнал 20 ksymbol/s и сама по себе не объясняет все широкополосные
|
||||
искажения Lab062.
|
||||
|
||||
Рекомендуемый следующий отдельный шаг: согласовать повтор той же лестницы при
|
||||
TX VGA 20 дБ, сохранив RX LNA/VGA 24/24 дБ, частоту, геометрию, waveform и
|
||||
общую опору. Это проверит, улучшается ли matched/residual при сохранении
|
||||
линейности. В рамках Lab064 усиления не менялись и второй TX не выполнялся.
|
||||
|
||||
## Независимая проверка
|
||||
|
||||
Проверяющий агент работал только с сохранёнными файлами и не обращался к
|
||||
оборудованию. Он независимо пересчитал SHA-256, положение четырёх сегментов,
|
||||
пять окон фона, matched/residual, комплексные `h`, отношения мощности и
|
||||
амплитуды, общий результат L3/L0 и все двенадцать спектральных значений.
|
||||
Подтверждённых расхождений нет. Целевые тесты дали 15 passed; итоговый вердикт:
|
||||
`чисто`.
|
||||
|
||||
После RF-сеанса quick gate прошёл: 986 тестов ядра, 37 функциональных проверок,
|
||||
13 пропусков. Полный pytest: 986 passed, 0 failed. `git diff --check`: PASS.
|
||||
|
||||
`RF TX = 0`. Lab064 не коммитировалась, push не выполнялся, Lab065 не создана.
|
||||
120
docs/lab064_rx_preflight.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Lab064: подготовка и RX-only preflight
|
||||
|
||||
14 сентября 2026 года завершена программная подготовка измерения уровня и
|
||||
линейности текущего OTA-тракта. Затем выполнена одна аппаратная проверка общей
|
||||
опоры и RX-фона. Preflight прошёл. Передатчик не запускался: `RF TX = 0`.
|
||||
|
||||
## Условия
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Центральная частота | 1400000000 Гц |
|
||||
| Скорость символов | 20000 символов/с |
|
||||
| Частота дискретизации | 8000000 комплексных отсчётов/с |
|
||||
| Цифровая несущая | +50000 Гц |
|
||||
| TX VGA | 10 дБ |
|
||||
| RX LNA / VGA | 24 / 24 дБ |
|
||||
| RF amplifier / Bias-Tee | выключены |
|
||||
| Расстояние TX–RX | примерно 1,2 м |
|
||||
| Геометрия | прямая видимость, помещение |
|
||||
| TX | плата и антенна возле окна |
|
||||
| RX | плата и антенна на столе пользователя |
|
||||
| Антенны | имеющиеся антенны на 1400 МГц |
|
||||
| Поляризация | одинаковая |
|
||||
| Общая опора | 10 МГц TCXO → MASTER → MASTER CLKOUT → SLAVE CLKIN |
|
||||
|
||||
Антенны, платы, длинный кабель общей опоры, USB-подключение и остальные
|
||||
кабели не перемещались. Усиления не менялись.
|
||||
|
||||
## Измерительный TX-файл
|
||||
|
||||
Подготовлен один детерминированный PRBS7/BPSK/RRC-сигнал с уровнями −18, −12,
|
||||
−6 и 0 дБ относительно номинального waveform. Он состоит из 100 мс начальных
|
||||
нулей, четырёх участков по 100 мс, трёх промежутков по 50 мс и 100 мс конечных
|
||||
нулей. Полная длительность равна 0,75 с, длина составляет 6000000 комплексных
|
||||
отсчётов и 12000000 байт.
|
||||
|
||||
| Уровень | TX RMS, код | max \|I\| / \|Q\|, код | Фактический уровень, дБ | Шаг, дБ |
|
||||
|---|---:|---:|---:|---:|
|
||||
| L0 | 5,556137 | 9 / 9 | −17,973685 | — |
|
||||
| L1 | 11,056978 | 17 / 17 | −11,996415 | +5,977270 |
|
||||
| L2 | 22,056892 | 34 / 35 | −5,998257 | +5,998157 |
|
||||
| L3 | 44,000457 | 69 / 69 | 0 | +5,998257 |
|
||||
|
||||
Цифрового clipping нет. SHA-256:
|
||||
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
|
||||
|
||||
Синтетический линейный канал с известным комплексным коэффициентом, DC и
|
||||
шумом прошёл software gate. После независимой проверки анализатор изменён так,
|
||||
чтобы промежуточный ненулевой счётчик нельзя было скрыть финальным нулём. При
|
||||
любой потере сохраняются диагностические метрики, но A/B/C-классификация
|
||||
запрещается и результат получает статус `INVALID_CAPTURE`.
|
||||
|
||||
## Common clock
|
||||
|
||||
Свежая инвентаризация подтвердила две HackRF One r9 с firmware/libhackrf
|
||||
2024.02.1. Роли сохранены:
|
||||
|
||||
- MASTER `000000000000000097c063dc323b97d3`;
|
||||
- SLAVE `000000000000000097c063dc2e5327d3`.
|
||||
|
||||
Аппаратная проверка получила:
|
||||
|
||||
```text
|
||||
MASTER reg29 = 0x26
|
||||
SLAVE reg29 = 0x26
|
||||
SLAVE CLKIN = DETECTED
|
||||
CLKIN query = 1
|
||||
time to detect = 66.793 ms
|
||||
```
|
||||
|
||||
## RX-only фон
|
||||
|
||||
SLAVE записал 800000 комплексных отсчётов, 0,1 с, при 1400 МГц и RX LNA/VGA
|
||||
24/24 дБ.
|
||||
|
||||
| Метрика | Значение |
|
||||
|---|---:|
|
||||
| Полный RMS IQ | 2,193193 кода |
|
||||
| AC RMS после удаления среднего | 0,832038 кода |
|
||||
| Средние I / Q | 0,933738 / 1,801650 кода |
|
||||
| max \|I\| / \|Q\| | 4 / 5 кодов |
|
||||
| Максимальная комплексная амплитуда | 5,385165 кода |
|
||||
| Clipping | 0 / 800000 отсчётов |
|
||||
| RX overruns | 0, максимум и финальное значение |
|
||||
| Неожиданно сильный сигнал | нет |
|
||||
|
||||
Порог STOP для неожиданно сильного цифрового фона: AC RMS не менее 20 кодов
|
||||
или max |I|/|Q| не менее 100 кодов. Измеренные значения значительно ниже.
|
||||
Отсутствие rail-кодов не доказывает отсутствие аналоговой компрессии при
|
||||
будущей передаче. Абсолютная RF-мощность без измерительного прибора неизвестна.
|
||||
|
||||
SHA-256 RX-only записи:
|
||||
`02d87adcbb621027290a77d5f8fd3190c299d13e89f887cd294d4af352a6ef52`.
|
||||
|
||||
Процессы до и после preflight имели состояние `CLEAR`. Оба CLKOUT выключены,
|
||||
ошибок очистки нет. Preflight сохранил все две строки счётчика RX overruns;
|
||||
обе равны нулю. Вызовов `hackrf_transfer -t` не было, `rf_tx_count = 0`.
|
||||
|
||||
## Проверки и артефакты
|
||||
|
||||
- quick gate: 984 теста ядра, 37 функциональных проверок, 13 пропусков;
|
||||
- pytest: 984 passed, 0 failed;
|
||||
- целевые тесты Lab064: 13 passed;
|
||||
- `git diff --check`: PASS;
|
||||
- независимая read-only проверка: чисто.
|
||||
|
||||
Артефакты:
|
||||
|
||||
- [подготовка](../data/processed/lab064/preparation.json);
|
||||
- [software gate](../data/processed/lab064/software_gate.json);
|
||||
- [preflight](../data/processed/lab064/preflight_result.json);
|
||||
- CS8 и исходные аппаратные журналы находятся в `data/raw/lab064/` и в Git не
|
||||
входят.
|
||||
|
||||
`READY FOR SINGLE LAB064 TX = YES` означает техническую готовность только этой
|
||||
конфигурации. Передача требует отдельного явного разрешения пользователя.
|
||||
`RF TX = 0`. STOP.
|
||||
|
||||
Lab064 не коммитируется до аппаратного результата. Push не выполняется.
|
||||
Lab065 автоматически не начинается.
|
||||
70
docs/lab065_current_state.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Lab065: подготовка измерения при TX VGA 20 дБ
|
||||
|
||||
Lab065 повторяет измерение Lab064 с одним изменением аппаратного параметра:
|
||||
TX VGA повышен с 10 до 20 дБ. Частота 1400000000 Гц, RX LNA/VGA 24/24 дБ,
|
||||
геометрия, антенны, кабели, общая опора и цифровой waveform сохранены.
|
||||
|
||||
TX-файл Lab065 является побайтовой копией Lab064. Его SHA-256 должен быть равен
|
||||
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
|
||||
Длина остаётся равной 6000000 комплексных отсчётов и 0,75 с. Ступени
|
||||
−18/−12/−6/0 дБ, PRBS7, BPSK, RRC, 20000 символов/с, Fs 8000000 отсчётов/с и
|
||||
цифровая несущая +50000 Гц не меняются.
|
||||
|
||||
## Сравнение с Lab064
|
||||
|
||||
Для L0, L1, L2 и L3 анализатор выдаёт изменение согласованной мощности,
|
||||
`|h|²`, остаточной мощности и matched/residual. Номинальное изменение TX VGA
|
||||
равно +10 дБ и приводится как справочная величина, а не как условие PASS.
|
||||
|
||||
До RF зафиксированы численные правила:
|
||||
|
||||
- линейность внутри Lab065: максимальная абсолютная ошибка шага не более 1 дБ;
|
||||
- компрессия: ошибка верхнего шага не более −1,5 дБ;
|
||||
- заметный прирост: медиана прироста matched power не менее 3 дБ;
|
||||
- заметное улучшение matched/residual: медиана не менее 3 дБ;
|
||||
- согласованность прироста четырёх уровней: разброс не более 3 дБ;
|
||||
- остаток считается растущим почти как сигнал, если его медианный прирост
|
||||
отстаёт от медианного прироста matched power не более чем на 3 дБ.
|
||||
|
||||
Эти пороги нужны для воспроизводимой классификации конкретного опыта. Они не
|
||||
заменяют измерение абсолютной RF-мощности.
|
||||
|
||||
## Аппаратная граница
|
||||
|
||||
Подготовка допускает программные проверки, common-clock preflight и RX-only
|
||||
запись фона. Любая команда с `-t` внутри preflight запрещена. Передача Lab065
|
||||
требует нового прямого разрешения пользователя и в текущий этап не входит.
|
||||
Автоматических повторов и изменения усилений после ошибки нет.
|
||||
|
||||
`RF TX = 0`.
|
||||
|
||||
## Результат подготовки
|
||||
|
||||
Подготовка выполнена от HEAD
|
||||
`cff226e0bf9e36a627829ea11a4ca3292ae20953`. Software gate прошёл. Побайтовая
|
||||
копия waveform имеет требуемый SHA-256, подготовленная TX-команда содержит
|
||||
TX VGA 20 дБ, а RX-команда сохраняет LNA/VGA 24/24 дБ.
|
||||
|
||||
Common-clock и RX-only preflight прошёл. Обе HackRF One r9 обнаружены с
|
||||
прежними ролями. MASTER и SLAVE имеют reg29 `0x26`; SLAVE CLKIN обнаружен на
|
||||
третьем опросе через 201,986 мс. В RX-фоне из 800000 отсчётов AC RMS равен
|
||||
1,056565 кода, max |I|/|Q| равны 6/8, clipping и overruns отсутствуют.
|
||||
Процессы до и после проверки имеют состояние CLEAR, ошибок очистки нет.
|
||||
|
||||
Lab065 готова к рассмотрению перед одним RF-сеансом. Текущая подготовка такого
|
||||
разрешения не содержит. `RF TX = 0`.
|
||||
|
||||
## Единственный RF-сеанс
|
||||
|
||||
После отдельного прямого разрешения выполнен один TX. Общая опора прошла:
|
||||
MASTER/SLAVE reg29 `0x26`, SLAVE CLKIN обнаружен на втором опросе через
|
||||
136,897 мс. TX underrun, RX overrun, clipping и ошибки очистки отсутствуют.
|
||||
Повторов не было. RX содержит 16000000 отсчётов, SHA-256
|
||||
`845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e`.
|
||||
|
||||
Медианный прирост matched power относительно Lab064 равен +11,233489 дБ,
|
||||
медианное изменение остатка +0,517004 дБ, улучшение matched/residual
|
||||
+10,734495 дБ. Ошибки ступеней равны −0,077363/+0,034934/−0,013858 дБ.
|
||||
Признаков компрессии нет. Классификация: `LINEAR_GAIN_IMPROVEMENT`.
|
||||
|
||||
[Полный RF-отчёт](lab065_rf_result.md). `RF TX = 0`.
|
||||
114
docs/lab065_rf_result.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Lab065: один OTA-сеанс при TX VGA 20 дБ
|
||||
|
||||
15 сентября 2026 года выполнен один разрешённый OTA-сеанс Lab065 на частоте
|
||||
1400000000 Гц. Передача использовала точную копию waveform Lab064 с SHA-256
|
||||
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
|
||||
Автоматических и ручных повторов не было.
|
||||
|
||||
## Аппаратная валидность
|
||||
|
||||
Перед TX обе HackRF One r9 и их роли подтверждены. MASTER и SLAVE имели reg29
|
||||
`0x26`; SLAVE CLKIN обнаружен на втором опросе через 136,897 мс. RX стартовал
|
||||
раньше TX. Результат сеанса:
|
||||
|
||||
```text
|
||||
COMPLETED TX SESSIONS = 1
|
||||
AUTOMATIC TX REPEATS = 0
|
||||
TX UNDERRUN MAXIMUM = 0
|
||||
RX OVERRUN MAXIMUM = 0
|
||||
RX CLIPPING = NO
|
||||
CLEANUP ERRORS = []
|
||||
SDR PROCESSES = CLEAR
|
||||
```
|
||||
|
||||
RX содержит 16000000 комплексных отсчётов, 32000000 байт и 2,0 с. SHA-256:
|
||||
`845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e`.
|
||||
Максимумы всей записи равны `|I|=10` и `|Q|=10`; rail-кодов нет.
|
||||
|
||||
## Линейность внутри Lab065
|
||||
|
||||
| Переход | TX, дБ | RX matched, дБ | Ошибка, дБ |
|
||||
|---|---:|---:|---:|
|
||||
| L0 → L1 | +5,977270 | +5,899908 | −0,077363 |
|
||||
| L1 → L2 | +5,998157 | +6,033091 | +0,034934 |
|
||||
| L2 → L3 | +5,998257 | +5,984400 | −0,013858 |
|
||||
|
||||
Максимальная абсолютная ошибка равна 0,077363 дБ при границе 1 дБ. Ошибка
|
||||
верхней ступени равна −0,013858 дБ при пороге компрессии −1,5 дБ. Согласованная
|
||||
мощность L3 выросла относительно L2 на 5,984400 дБ. Признаков компрессии на
|
||||
L2/L3 нет.
|
||||
|
||||
## Сравнение Lab064 и Lab065
|
||||
|
||||
| Уровень | Matched, дБ | Остаток, дБ | Улучшение matched/residual, дБ | Изменение \|h\|², дБ |
|
||||
|---|---:|---:|---:|---:|
|
||||
| L0 | +11,179328 | −0,243276 | +11,422604 | +11,179328 |
|
||||
| L1 | +11,119466 | −2,366286 | +13,485752 | +11,119466 |
|
||||
| L2 | +11,323670 | +1,277284 | +10,046386 | +11,323670 |
|
||||
| L3 | +11,287650 | +3,907581 | +7,380069 | +11,287650 |
|
||||
|
||||
Медианный прирост согласованной мощности равен +11,233489 дБ. Его разброс по
|
||||
четырём уровням составляет 0,204204 дБ. Медианное изменение остатка равно
|
||||
+0,517004 дБ, а медианное улучшение matched/residual равно +10,734495 дБ.
|
||||
|
||||
При TX VGA 20 дБ matched/residual для L0/L1/L2/L3 равен
|
||||
−20,663889/−12,091711/−6,645283/−4,753267 дБ. Отношение улучшилось на каждом
|
||||
уровне, хотя даже на L3 согласованная составляющая остаётся слабее остатка.
|
||||
|
||||
## Комплексный коэффициент передачи
|
||||
|
||||
| Уровень | h Lab064 | h Lab065 | \|h\| Lab064 | \|h\| Lab065 | Изменение фазы, градусы |
|
||||
|---|---|---|---:|---:|---:|
|
||||
| L0 | 0,001876 − j0,004508 | −0,006915 + j0,016279 | 0,004883 | 0,017686 | −179,581 |
|
||||
| L1 | 0,004471 − j0,001938 | −0,010180 + j0,014271 | 0,004873 | 0,017529 | +148,932 |
|
||||
| L2 | 0,003277 − j0,003478 | −0,009134 + j0,015045 | 0,004779 | 0,017600 | +167,966 |
|
||||
| L3 | 0,003384 − j0,003391 | −0,009107 + j0,015028 | 0,004791 | 0,017572 | +166,276 |
|
||||
|
||||
Модуль `h` внутри Lab065 почти постоянен: 0,017529…0,017686. Его мощность
|
||||
относительно Lab064 выросла на 11,119…11,324 дБ, что согласуется с matched
|
||||
power. Фаза между отдельными сеансами не имеет непрерывной временной привязки,
|
||||
поэтому приведённый поворот является диагностикой конкретных записей, а не
|
||||
калиброванной фазовой характеристикой тракта.
|
||||
|
||||
## Фон и остаточные полосы
|
||||
|
||||
Пять нулевых окон Lab065 имеют AC RMS 0,763722…0,857191 кода. Разброс фоновой
|
||||
AC-мощности равен 1,002840 дБ при границе 3 дБ, поэтому фон классифицирован как
|
||||
стабильный. В Lab064 разброс составлял 0,119530 дБ; фон Lab065 стабильный, но
|
||||
межоконный разброс стал больше.
|
||||
|
||||
Изменение диагностических полос остатка относительно Lab064:
|
||||
|
||||
| Уровень | 50 кГц, дБ | 100 кГц, дБ | 150 кГц, дБ |
|
||||
|---|---:|---:|---:|
|
||||
| L0 | +2,869745 | +2,654569 | +2,258651 |
|
||||
| L1 | +3,032680 | +2,378561 | +1,300716 |
|
||||
| L2 | +5,594944 | +4,091263 | +2,888397 |
|
||||
| L3 | +7,304593 | +6,432281 | +4,996232 |
|
||||
|
||||
На верхних уровнях остаточные полосы выросли, особенно около 50 кГц. На L3 их
|
||||
рост остаётся ниже прироста matched power +11,287650 дБ, а matched/residual
|
||||
улучшился на 7,380069 дБ. Эти цифровые полосы не являются калиброванным
|
||||
измерением RF-гармоник.
|
||||
|
||||
## Итог
|
||||
|
||||
```text
|
||||
CLASSIFICATION = LINEAR_GAIN_IMPROVEMENT
|
||||
```
|
||||
|
||||
Повышение TX VGA с 10 до 20 дБ дало устойчивый прирост согласованной принятой
|
||||
мощности примерно на 11,2 дБ без потери линейности. Остаток вырос существенно
|
||||
меньше, поэтому отношение полезной составляющей к остатку улучшилось на всех
|
||||
четырёх уровнях. Верхняя ступень не показывает компрессии.
|
||||
|
||||
Результат остаётся относительным измерением всего OTA-тракта. Он не определяет
|
||||
абсолютную RF-мощность и не доказывает прохождение полного PHY-пакета, FEC, CRC,
|
||||
T2 или JPEG.
|
||||
|
||||
Независимая проверка пересчитала сохранённый CS8, локализацию, фон, matched,
|
||||
residual, `h`, CFO, внутреннюю линейность и сравнение с Lab064. Расхождений нет.
|
||||
Рекомендуемый следующий одиночный шаг: рассмотреть scope Lab065 и отдельно
|
||||
разрешить локальный коммит. Новую RF-передачу для этого выполнять не требуется.
|
||||
|
||||
`RF TX = 0`. Lab065 не коммитировалась, push не выполнялся, Lab066 не создана.
|
||||
83
docs/lab065_rx_preflight.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Lab065: подготовка и RX-only preflight
|
||||
|
||||
15 сентября 2026 года подготовлена Lab065 с TX VGA 20 дБ. Пользователь
|
||||
подтвердил неизменность расстояния около 1,2 м, прямой видимости, положения TX
|
||||
возле окна и RX на столе, антенн, поляризации, USB, общей опоры и RF-кабелей.
|
||||
|
||||
## Неизменность сигнала
|
||||
|
||||
TX-файл Lab065 является побайтовой копией файла Lab064:
|
||||
|
||||
```text
|
||||
TX samples = 6000000
|
||||
TX bytes = 12000000
|
||||
TX duration = 0.750000 s
|
||||
TX SHA-256 = fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9
|
||||
WAVEFORM UNCHANGED FROM LAB064 = YES
|
||||
```
|
||||
|
||||
Сохранены PRBS7, BPSK, RRC, ступени −18/−12/−6/0 дБ, интервалы и нулевые
|
||||
защитные участки, 20000 символов/с, Fs 8000000 комплексных отсчётов/с и
|
||||
цифровая несущая +50000 Гц. Из аппаратных параметров изменён только TX VGA:
|
||||
10 → 20 дБ. RX LNA/VGA остаются равными 24/24 дБ; RF amplifier и Bias-Tee
|
||||
выключены.
|
||||
|
||||
Software gate прошёл. Он подтвердил SHA, пригодность результата Lab064 как
|
||||
базы сравнения, синтетическую проверку линейности и наличие численных правил
|
||||
классификации, зафиксированных до RF.
|
||||
|
||||
## Common clock
|
||||
|
||||
Перед аппаратными командами проверка процессов через psutil осмотрела 289
|
||||
процессов и получила CLEAR. Обнаружены обе HackRF One r9 с firmware и
|
||||
libhackrf 2024.02.1. Роли совпали с Lab064:
|
||||
|
||||
- MASTER `000000000000000097c063dc323b97d3`;
|
||||
- SLAVE `000000000000000097c063dc2e5327d3`.
|
||||
|
||||
Результат общей опоры:
|
||||
|
||||
```text
|
||||
MASTER reg29 = 0x26
|
||||
SLAVE reg29 = 0x26
|
||||
SLAVE CLKIN = DETECTED
|
||||
CLKIN query = 3
|
||||
time to detect = 201.986 ms
|
||||
```
|
||||
|
||||
Первые два опроса не обнаружили CLKIN, третий опрос прошёл в пределах штатного
|
||||
окна ожидания. Итоговый common-clock gate имеет статус PASS.
|
||||
|
||||
## RX-only фон
|
||||
|
||||
SLAVE записал 800000 комплексных отсчётов при 1400000000 Гц и RX LNA/VGA
|
||||
24/24 дБ.
|
||||
|
||||
| Метрика | Значение |
|
||||
|---|---:|
|
||||
| Полный RMS IQ | 2,333068 кода |
|
||||
| AC RMS после удаления среднего | 1,056565 кода |
|
||||
| Средние I / Q | 0,948896 / 1,851074 кода |
|
||||
| max \|I\| / \|Q\| | 6 / 8 кодов |
|
||||
| Максимальная комплексная амплитуда | 8,062258 кода |
|
||||
| Clipping | 0 / 800000 отсчётов |
|
||||
| RX overruns, максимум / финал | 0 / 0 |
|
||||
| Неожиданно сильный сигнал | нет |
|
||||
|
||||
SHA-256 RX-фона:
|
||||
`be3c61dfa1bc40d1a9f6777689026804a1ccfc340b82680d3ec5304ddd68f78c`.
|
||||
|
||||
Оба CLKOUT выключены без ошибок. Повторная проверка процессов получила CLEAR.
|
||||
Команд передачи не было.
|
||||
|
||||
```text
|
||||
LAB065 PREPARATION = PASS
|
||||
COMMON CLOCK = PASS
|
||||
RX PREFLIGHT = PASS
|
||||
RF TX = 0
|
||||
READY FOR SINGLE LAB065 TX = YES
|
||||
WAITING FOR EXPLICIT RF AUTHORIZATION = YES
|
||||
```
|
||||
|
||||
Готовность означает, что программные и аппаратные входные проверки пройдены.
|
||||
Она не разрешает передачу и не предсказывает результат измерения.
|
||||
92
docs/lab066_current_state.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Lab066 — исправленный повтор полного 500K PHY-сеанса
|
||||
|
||||
## Цель и границы
|
||||
|
||||
Lab066 повторяет исходную цель Lab062 после двух установленных дефектов того
|
||||
сеанса: пакеты Lab062 шли вплотную, без заявленных пауз 5 мс, а в TX-журнале
|
||||
был промежуточный underrun. Исторический результат Lab062 сохраняется без
|
||||
изменений: `RF_SESSION_INVALID_TX_UNDERRUN`, legacy 0/16, T2 0/16.
|
||||
|
||||
На этапе подготовки Lab066 была ограничена программным декодированием,
|
||||
common-clock и RX-only preflight; передача тогда не выполнялась. После этого
|
||||
пользователь отдельно разрешил один TX. Автоматический повтор оставался
|
||||
запрещён и не выполнялся.
|
||||
|
||||
## Радиопараметры
|
||||
|
||||
- RF: 1400000000 Гц;
|
||||
- Fs: 8000000 комплексных отсчётов/с;
|
||||
- символьная скорость: 500000 символов/с, 16 отсчётов/символ;
|
||||
- цифровая несущая: +50000 Гц;
|
||||
- TX VGA: 20 дБ;
|
||||
- RX LNA/VGA: 24/24 дБ;
|
||||
- RF amplifier и Bias-Tee: OFF;
|
||||
- 16 штатных PHY-пакетов с PRBS7-преамбулой, заголовком, FEC и CRC;
|
||||
- payload: 256 байт, детерминированный и различимый для каждого пакета;
|
||||
- геометрия: около 1,2 м, LOS в помещении, прежние антенны и поляризация;
|
||||
MASTER TX возле окна, SLAVE RX на столе.
|
||||
|
||||
## Итоговый TX-файл
|
||||
|
||||
Каждый packet waveform занимает 92256 отсчётов. Между соседними пакетами
|
||||
записаны 15 нулевых пауз по 40000 отсчётов, то есть ровно по 5 мс. Перед первым
|
||||
и после последнего пакета добавлены нулевые guards по 800000 отсчётов, или по
|
||||
100 мс.
|
||||
|
||||
Расчёт длины:
|
||||
|
||||
```text
|
||||
16 × 92256 + 15 × 40000 = 2076096 отсчётов без внешних guards
|
||||
2076096 + 2 × 800000 = 3676096 отсчётов всего
|
||||
3676096 / 8000000 = 0,459512 с
|
||||
```
|
||||
|
||||
Фактическая длина совпала с расчётом: 3676096 комплексных отсчётов,
|
||||
7352192 байта. SHA-256:
|
||||
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`.
|
||||
Повторная сборка дала те же байты. Нулевые guards и каждая из 15 пауз проверены
|
||||
по точным границам из сохранённой разметки.
|
||||
|
||||
## Программное декодирование
|
||||
|
||||
Весь конечный TX-файл дважды прошёл один и тот же строгий приёмный тракт. Режимы
|
||||
legacy и T2 запускались отдельно, автоматического fallback между ними нет.
|
||||
|
||||
| Режим | Кандидаты | Header | FEC | CRC | Выход | Payload и порядок |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| legacy | 16 | 16/16 | 16/16 | 16/16 | 16/16 | 16/16 |
|
||||
| T2 | 16 | 16/16 | 16/16 | 16/16 | 16/16 | 16/16 |
|
||||
|
||||
Позиции принятых пакетов равны ожидаемым позициям в разметке. Для каждого
|
||||
кандидата T2 заново оценивает начальные параметры по собственной преамбуле.
|
||||
Наблюдение дало по два локальных запуска слежения на пакет: для заголовка и
|
||||
полного кадра. Контекст обработки создаётся заново, состояния между пакетами
|
||||
не переносятся.
|
||||
|
||||
Аппаратный анализ применил legacy и T2 к одному сохранённому RX-файлу. До
|
||||
строгого вердикта декодеру не передавались payload или ожидаемые биты. Известный
|
||||
TX использован после вердикта только для физических диагностик и сравнения с
|
||||
Lab056 B, Lab062 и Lab066.
|
||||
|
||||
## Проверки перед preflight
|
||||
|
||||
- `python tools/quick_gate.py`: PASS, 1013 проверок ядра, 39 функциональных,
|
||||
13 аппаратных пропусков;
|
||||
- `python -m pytest tests/ -q`: 1013 passed;
|
||||
- `git diff --check`: PASS.
|
||||
|
||||
Независимый verifier пересчитал длину, SHA, все нулевые диапазоны, пакетные
|
||||
срезы, программное декодирование и RX-only метрики; расхождений нет. Read-only
|
||||
adversary проверил запрет TX, одноразовость, привязку capture к конфигурации,
|
||||
счётчики, live-проверку фона и границу oracle-диагностики; открытых замечаний
|
||||
нет.
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
Common-clock и RX-only preflight прошли; измерения приведены в
|
||||
[отдельном отчёте](lab066_rx_preflight.md). `RF TX=0`. Lab066 не
|
||||
коммитировалась, push не выполнялся, Lab067 не создавалась.
|
||||
|
||||
После отдельного разрешения выполнен один аппаратно валидный RF-сеанс без
|
||||
повторов. Legacy и T2 дали одинаковые 10/16 точных пакетов. Полные результаты
|
||||
и физическое сравнение приведены в [RF-отчёте](lab066_rf_result.md).
|
||||
118
docs/lab066_rf_result.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Lab066 — один OTA RF-сеанс полного 500K PHY-потока
|
||||
|
||||
## Аппаратная валидность
|
||||
|
||||
15 сентября 2026 года выполнен один разрешённый TX всего подготовленного
|
||||
потока. Передача запускалась один раз; автоматических повторов не было. RX
|
||||
стартовал раньше TX и сохранил 16000000 комплексных отсчётов за 2,000000 с.
|
||||
|
||||
- TX SHA-256:
|
||||
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`;
|
||||
- RX SHA-256:
|
||||
`72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0`;
|
||||
- TX underrun observations: 0, 0; maximum 0;
|
||||
- RX overrun observations: 0, 0, 0, 0; maximum 0;
|
||||
- clipping: нет;
|
||||
- cleanup errors: нет;
|
||||
- SDR-процессы до и после: `CLEAR`.
|
||||
|
||||
Непосредственно перед TX обе платы имели reg29=`0x26`; SLAVE CLKIN обнаружен
|
||||
на втором опросе через 131,291 мс. Live RX-проверка дала AC RMS 1,173567 кода,
|
||||
max |I|/|Q|=4/5 и не обнаружила clipping или сильный фон. После сеанса оба
|
||||
CLKOUT отключены.
|
||||
|
||||
## Строгое декодирование одной RX-записи
|
||||
|
||||
Legacy и T2 получили одну и ту же непрерывную IQ-запись. Поиск кандидатов,
|
||||
временная синхронизация, RRC, пороги, PHY header, FEC, CRC и критерии принятия
|
||||
не менялись. Автоматического fallback между режимами нет.
|
||||
|
||||
| Метрика | legacy | T2 |
|
||||
|---|---:|---:|
|
||||
| Кандидаты | 145 | 145 |
|
||||
| Header PASS | 10 | 10 |
|
||||
| FEC started | 10 | 10 |
|
||||
| FEC PASS | 10 | 10 |
|
||||
| CRC started | 10 | 10 |
|
||||
| CRC PASS | 10 | 10 |
|
||||
| Packet output | 10 | 10 |
|
||||
| Exact payload match | 10 | 10 |
|
||||
|
||||
Оба режима приняли пакеты 4, 5, 6, 7, 8, 10, 11, 12, 13 и 14. Пакеты 1, 2,
|
||||
3, 9, 15 и 16 не прошли PHY header у обоих режимов. Выигрыша одного режима
|
||||
над другим нет.
|
||||
|
||||
| Пакет | Legacy H/F/C/O | T2 H/F/C/O |
|
||||
|---:|:---:|:---:|
|
||||
| 1 | 0/0/0/0 | 0/0/0/0 |
|
||||
| 2 | 0/0/0/0 | 0/0/0/0 |
|
||||
| 3 | 0/0/0/0 | 0/0/0/0 |
|
||||
| 4 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 5 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 6 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 7 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 8 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 9 | 0/0/0/0 | 0/0/0/0 |
|
||||
| 10 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 11 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 12 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 13 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 14 | 1/1/1/1 | 1/1/1/1 |
|
||||
| 15 | 0/0/0/0 | 0/0/0/0 |
|
||||
| 16 | 0/0/0/0 | 0/0/0/0 |
|
||||
|
||||
H/F/C/O означает header/FEC/CRC/output. Для каждого из 16 ожидаемых пакетов
|
||||
детектор имел кандидат точно на диагностической границе пакета. У шести
|
||||
непринятых пакетов FEC и CRC не запускались из-за отказа PHY header.
|
||||
|
||||
## Физическая диагностика после строгого результата
|
||||
|
||||
Сопоставимые показатели рассчитаны отдельно по всем 16 пакетам методикой
|
||||
Lab058. Известный TX использован после завершения строгих прогонов и не влиял
|
||||
на принятие пакетов.
|
||||
|
||||
| Показатель | Lab066 |
|
||||
|---|---:|
|
||||
| Доля ошибочных символов | 0,0110466, или 1,104664% |
|
||||
| Нормированное RMS-отклонение символов | 0,528374 |
|
||||
| Согласованная мощность | 8,359398 код² |
|
||||
| Остаточная мощность | 2,333261 код² |
|
||||
| Matched/residual | +5,542117 дБ |
|
||||
| Неравномерность усиления p95–p05 | медиана 2,870070 дБ |
|
||||
| Диапазон неравномерности по пакетам | 2,724906…3,111679 дБ |
|
||||
|
||||
Остаток включает шум и немоделированные искажения; это не калиброванная
|
||||
мощность шума. Физические показатели являются диагностическими и не превращают
|
||||
непринятый пакет в успешный.
|
||||
|
||||
## Сравнение
|
||||
|
||||
| Показатель | Lab056 B | Lab062 | Lab066 |
|
||||
|---|---:|---:|---:|
|
||||
| Доля ошибочных символов | 0,034959 | 0,099287 | 0,011047 |
|
||||
| RMS-отклонение | 0,845350 | 1,220401 | 0,528374 |
|
||||
| Matched/residual | +1,464846 дБ | −1,730051 дБ | +5,542117 дБ |
|
||||
| Неравномерность полосы | 5,471382 дБ | 6,660341 дБ | 2,870070 дБ |
|
||||
|
||||
Относительно Lab056 B доля ошибочных символов ниже на 0,0239123, или на
|
||||
68,40%; RMS-отклонение ниже на 0,316976, matched/residual выше на 4,077271 дБ,
|
||||
а неравномерность меньше на 2,601312 дБ.
|
||||
|
||||
Относительно Lab062 доля ошибочных символов ниже на 0,0882400, или на 88,87%;
|
||||
RMS-отклонение ниже на 0,692027, matched/residual выше на 7,272168 дБ, а
|
||||
неравномерность меньше на 3,790271 дБ. Сравнение с Lab062 контекстное: тот
|
||||
сеанс остаётся аппаратно невалидным из-за TX underrun.
|
||||
|
||||
## Итог
|
||||
|
||||
Классификация: `PARTIAL_PACKET_RECOVERY_NO_T2_GAIN`. Аппаратно чистая запись
|
||||
дала одинаковые 10/16 точных пакетов в legacy и T2. Физическое качество заметно
|
||||
лучше Lab056 B и невалидной Lab062, однако шесть пакетов остановились на PHY
|
||||
header, поэтому результат не является полным 16/16 успехом.
|
||||
|
||||
Следующий отдельный шаг, если он будет разрешён: только офлайн сравнить ошибки
|
||||
PHY header, timing и CFO пакетов 1–3, 9, 15–16 с принятыми пакетами по уже
|
||||
сохранённому RX. Новый TX для этого не нужен.
|
||||
|
||||
Сейчас `RF TX=0`, процессы `CLEAR`. Lab066 не коммитировалась, push не
|
||||
выполнялся, Lab067 не создавалась.
|
||||
35
docs/lab066_rx_preflight.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Lab066 — common-clock и RX-only preflight
|
||||
|
||||
## Результат
|
||||
|
||||
15 сентября 2026 года выполнен один preflight без передачи. До и после него
|
||||
SDR-процессы имели состояние `CLEAR`; обе HackRF One r9 и назначение ролей
|
||||
подтверждены.
|
||||
|
||||
Общая опора прошла проверку:
|
||||
|
||||
- MASTER reg29 = `0x26`;
|
||||
- SLAVE reg29 = `0x26`;
|
||||
- SLAVE CLKIN = `DETECTED`;
|
||||
- обнаружение произошло на втором опросе через 132,113 мс.
|
||||
|
||||
RX-only команда записала 800000 комплексных отсчётов на частоте 1400 МГц с
|
||||
RX LNA/VGA 24/24 дБ. Максимум всех наблюдавшихся счётчиков overrun равен нулю.
|
||||
После проверки оба CLKOUT отключены; ошибок очистки нет.
|
||||
|
||||
## Уровень фона
|
||||
|
||||
- полный RMS: 2,225641 кода;
|
||||
- AC RMS: 0,827637 кода;
|
||||
- средние I/Q: 0,947530 / 1,835941 кода;
|
||||
- max |I| / |Q|: 4 / 5;
|
||||
- доля clipped components и samples: 0 / 0;
|
||||
- неожиданный сильный сигнал: нет;
|
||||
- SHA-256 RX-only записи:
|
||||
`ff817582e6a8fc03b489bf89f46322a9db8a9493335a4360a1b0f9bedad7c4d5`.
|
||||
|
||||
## Граница разрешения
|
||||
|
||||
Preflight не запускал TX и не расходует право на будущий единственный сеанс.
|
||||
Сейчас `RF TX=0`. Для одного Lab066 TX требуется отдельное прямое разрешение.
|
||||
Автоматические повторы, изменение усилений и переход к Lab067 запрещены.
|
||||
49
docs/lab067_current_state.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Lab067: текущее состояние
|
||||
|
||||
Дата: 15 сентября 2026 года.
|
||||
|
||||
## Граница работы
|
||||
|
||||
Lab067 выполнена полностью офлайн по сохранённой аппаратно валидной записи
|
||||
Lab066. HackRF Plus не опрашивался, аппаратные команды не запускались, RF TX
|
||||
оставался равным 0. Алгоритмы приёмника, PHY-заголовок, пороги, FEC, CRC и
|
||||
критерии принятия пакета не менялись.
|
||||
|
||||
Исторический строгий результат Lab066 сохранён:
|
||||
|
||||
- legacy: 10/16 точных пакетов;
|
||||
- T2: 10/16 точных пакетов;
|
||||
- приняты пакеты 4–8 и 10–14;
|
||||
- пакеты 1–3, 9, 15 и 16 остановились на PHY-заголовке.
|
||||
|
||||
## Проверенные исходники
|
||||
|
||||
- TX: 7 352 192 байта, 3 676 096 комплексных отсчётов, SHA-256
|
||||
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`;
|
||||
- RX: 32 000 000 байт, 16 000 000 комплексных отсчётов, 2,0 с при
|
||||
8 Мвыб/с, SHA-256
|
||||
`72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0`;
|
||||
- на расчётной позиции каждого из 16 пакетов найден строгий кандидат с
|
||||
нулевым смещением уточнённой позиции.
|
||||
|
||||
## Текущий вывод
|
||||
|
||||
Все шесть отказов локализованы в PHY-заголовке: преамбулы этих пакетов лежат
|
||||
внутри диапазона принятых пакетов, но в коротком окне от последних 128 символов
|
||||
преамбулы до конца заголовка растёт остаток. Медиана `matched/residual` снижается
|
||||
с 5,906881 dB у принятых пакетов до 4,146180 dB у отказавших.
|
||||
|
||||
28 сырых ошибок заголовка занимают разные позиции; ни одна позиция не
|
||||
повторяется между отказавшими пакетами. TX-заголовки всех 16 пакетов идентичны,
|
||||
корректны и дают текущий программный круговой тест 16/16 в legacy и T2.
|
||||
Систематическая ошибка обработки заголовка не найдена.
|
||||
|
||||
`HEADER_FAILURE_ROOT_CAUSE = LOCAL_RF_QUALITY`
|
||||
|
||||
`ONE SMALL FIX JUSTIFIED = NO`
|
||||
|
||||
Рекомендация: закрыть текущую диагностическую ветку на достигнутом результате
|
||||
10/16 и перейти к ветке управления и телеметрии CRSF/ELRS. QPSK с меньшей
|
||||
символьной скоростью можно рассматривать позднее для видеоканала.
|
||||
|
||||
Lab067 не коммитировалась. Push не выполнялся.
|
||||
151
docs/lab067_offline_result.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Lab067: офлайн-анализ отказов PHY-заголовка Lab066
|
||||
|
||||
## Результат
|
||||
|
||||
Причина шести отказов совместима с кратковременным ростом несогласованного
|
||||
остатка именно в области заголовка. Данных за небольшую ошибку логики приёмника,
|
||||
временной привязки, сопровождения несущей или состояния кандидатов нет.
|
||||
|
||||
`HEADER_FAILURE_ROOT_CAUSE = LOCAL_RF_QUALITY`
|
||||
|
||||
`ONE SMALL FIX JUSTIFIED = NO`
|
||||
|
||||
`BRANCH DECISION = NO_SMALL_FIX_JUSTIFIED`
|
||||
|
||||
Исторический результат Lab066 остаётся 10/16 для legacy и 10/16 для T2.
|
||||
Oracle-расчёты ниже диагностические и не меняют строгий результат.
|
||||
|
||||
## Целостность и геометрия данных
|
||||
|
||||
SHA-256 TX и RX совпали с зафиксированными значениями Lab066. RX имеет
|
||||
16 000 000 комплексных отсчётов, 32 000 000 байт и длительность 2,0 с при
|
||||
8 Мвыб/с. Все 16 ожидаемых позиций подтверждены строгими кандидатами;
|
||||
уточнённое смещение для каждого пакета равно 0 отсчётов.
|
||||
|
||||
## Сводная таблица
|
||||
|
||||
`P err` означает ошибки 1280-символьной преамбулы. `H raw` означает ошибки
|
||||
среди 144 переданных символов заголовка. `H vote` означает ошибки после штатной
|
||||
мягкой суммы трёх повторений. Локальная метрика рассчитана по фиксированному
|
||||
окну: последние 128 символов преамбулы и весь заголовок.
|
||||
|
||||
| Пакет | Строгий результат | P err | P corr | H raw | Копии 1/2/3 | H vote | Поля с ошибкой | CFO legacy, Hz | CFO T2/P3, Hz | Timing, отсч. | Локальный M/R, dB | RMS | Legacy/T2 diff | Классификация |
|
||||
|---:|---|---:|---:|---:|---:|---:|---|---:|---:|---:|---:|---:|---:|---|
|
||||
| 1 | HEADER FAIL | 15 | 0,898114 | 3 | 1/2/0 | 1 | length | -2,662 | -5,388 | 0 | 5,127352 | 0,554156 | 0 | HEADER_SPECIFIC_FAILURE |
|
||||
| 2 | HEADER FAIL | 10 | 0,892575 | 6 | 3/2/1 | 1 | length | -11,085 | -5,424 | 0 | 3,453566 | 0,671926 | 1 | HEADER_SPECIFIC_FAILURE |
|
||||
| 3 | HEADER FAIL | 11 | 0,883245 | 3 | 1/1/1 | 1 | magic | -2,144 | 2,717 | 0 | 4,588577 | 0,589619 | 0 | HEADER_SPECIFIC_FAILURE |
|
||||
| 4 | PASS | 12 | 0,870256 | 0 | 0/0/0 | 0 | none | -4,970 | 0,501 | 0 | 4,498689 | 0,595752 | 0 | PASSED |
|
||||
| 5 | PASS | 15 | 0,882299 | 0 | 0/0/0 | 0 | none | 3,163 | -1,271 | 0 | 8,091353 | 0,393942 | 0 | PASSED |
|
||||
| 6 | PASS | 17 | 0,883426 | 0 | 0/0/0 | 0 | none | 5,595 | 1,262 | 0 | 7,317955 | 0,430628 | 0 | PASSED |
|
||||
| 7 | PASS | 7 | 0,895704 | 1 | 0/0/1 | 0 | none | -1,265 | -1,236 | 0 | 6,319905 | 0,483064 | 1 | PASSED |
|
||||
| 8 | PASS | 10 | 0,893515 | 3 | 1/2/0 | 0 | none | 9,131 | -0,471 | 0 | 4,986495 | 0,563216 | 0 | PASSED |
|
||||
| 9 | HEADER FAIL | 13 | 0,883101 | 4 | 1/2/1 | 1 | length | 6,363 | -1,982 | 0 | 3,775053 | 0,647511 | 0 | HEADER_SPECIFIC_FAILURE |
|
||||
| 10 | PASS | 14 | 0,875698 | 1 | 0/0/1 | 0 | none | 10,521 | 4,784 | 0 | 4,964081 | 0,564672 | 0 | PASSED |
|
||||
| 11 | PASS | 14 | 0,873945 | 0 | 0/0/0 | 0 | none | -4,338 | 5,576 | 0 | 5,008753 | 0,561775 | 0 | PASSED |
|
||||
| 12 | PASS | 15 | 0,869109 | 0 | 0/0/0 | 0 | none | -14,662 | -12,429 | 0 | 6,568840 | 0,469416 | 0 | PASSED |
|
||||
| 13 | PASS | 19 | 0,887088 | 0 | 0/0/0 | 0 | none | 2,006 | -3,871 | 0 | 7,449922 | 0,424135 | 0 | PASSED |
|
||||
| 14 | PASS | 13 | 0,897960 | 1 | 0/0/1 | 0 | none | 7,211 | 3,326 | 0 | 5,493857 | 0,531260 | 1 | PASSED |
|
||||
| 15 | HEADER FAIL | 12 | 0,888738 | 7 | 2/2/3 | 2 | magic, length | -5,732 | -3,520 | 0 | 4,055203 | 0,626960 | 0 | HEADER_SPECIFIC_FAILURE |
|
||||
| 16 | HEADER FAIL | 16 | 0,880510 | 5 | 1/2/2 | 2 | version, length | 4,734 | 228,105 | 0 | 4,237157 | 0,613963 | 107 | HEADER_SPECIFIC_FAILURE |
|
||||
|
||||
Полные строки битов `EXPECTED HEADER BITS`, `RECEIVED RAW HEADER BITS`,
|
||||
`VOTED HEADER BITS`, причины строгого отказа и позиции ошибок находятся в
|
||||
`summary.json` и `header_packet_comparison.csv`.
|
||||
|
||||
## Преамбула: распределения
|
||||
|
||||
| Метрика | Passed min / median / max | Failed min / median / max |
|
||||
|---|---:|---:|
|
||||
| Ошибки, бит | 7 / 14 / 19 | 10 / 12,5 / 16 |
|
||||
| Доля ошибок | 0,005469 / 0,010938 / 0,014844 | 0,007813 / 0,009766 / 0,012500 |
|
||||
| Корреляция | 0,869109 / 0,882862 / 0,897960 | 0,880510 / 0,885991 / 0,898114 |
|
||||
| RMS отклонение | 0,489327 / 0,532530 / 0,567221 | 0,489090 / 0,523373 / 0,538197 |
|
||||
| Matched power | 7,774573 / 8,330450 / 8,667865 | 7,968245 / 8,385475 / 8,982336 |
|
||||
| Residual power | 2,053382 / 2,346123 / 2,645143 | 2,118219 / 2,258980 / 2,379173 |
|
||||
| Matched/residual, dB | 4,924949 / 5,473142 / 6,208025 | 5,381167 / 5,624735 / 6,212224 |
|
||||
|
||||
Преамбула не отделяет отказавшие пакеты от принятых: медианные физические
|
||||
показатели failed-группы не хуже. Ухудшение появляется после последних
|
||||
128 символов преамбулы и внутри заголовка. В этом окне медиана residual power
|
||||
равна 3,089822 у failed и 2,219458 у passed; медиана `matched/residual` равна
|
||||
4,146180 и 5,906881 dB соответственно.
|
||||
|
||||
## Карта ошибок заголовка
|
||||
|
||||
У отказавших пакетов найдено 28 сырых ошибочных решений. Ни одна позиция
|
||||
не повторяется в двух failed-пакетах. Ошибки встречаются во всех трёх копиях:
|
||||
9, 11 и 8 ошибок соответственно. После мягкой суммы остаётся 1 ошибка у
|
||||
пакетов 1, 2, 3 и 9 и 2 ошибки у пакетов 15 и 16.
|
||||
|
||||
Общей позиции, стабильно плохой копии, концентрации только в начале или только
|
||||
в конце заголовка нет. Это не поддерживает гипотезу о систематической ошибке
|
||||
формата или индексации заголовка.
|
||||
|
||||
## Переход преамбула → заголовок
|
||||
|
||||
На последних 128 символах преамбулы медиана фазовой RMS-ошибки составляет
|
||||
0,361 rad у passed и 0,541 rad у failed. На заголовке значения составляют
|
||||
0,388 и 0,627 rad. У failed-пакетов ошибки заголовка лежат в диапазоне 3–7
|
||||
сырых решений, а в первых 128 символах после заголовка медиана ошибок равна 0.
|
||||
|
||||
Медианная амплитуда остаётся близкой к единице. Общего скачка амплитуды,
|
||||
смещения точки выборки или одинакового фазового скачка не найдено. Наблюдается
|
||||
короткое ухудшение фазового остатка, различное по позиции в каждом пакете.
|
||||
|
||||
## Timing, CFO и T2
|
||||
|
||||
Штатное timing-смещение и диагностическое oracle-смещение равны 0 отсчётов для
|
||||
всех 16 пакетов. Расхождение legacy CFO с контрольной оценкой создаёт за весь
|
||||
144-символьный заголовок фазовую ошибку от -0,02488 до +0,02079 rad. Этот
|
||||
масштаб недостаточен для объяснения 1–2 ошибок после мягкого голосования.
|
||||
|
||||
T2 начинает влиять с символа 1280, то есть с начала заголовка. Для пакетов
|
||||
1, 3, 4–6, 8–13 и 15 его жёсткие решения заголовка побитно совпадают с legacy.
|
||||
Различия равны одному биту для пакетов 2, 7 и 14. У пакета 16 оценка P3 стала
|
||||
выбросом 228,105 Hz и изменила 107 решений. Это ухудшение одного пакета не
|
||||
объясняет шесть legacy-отказов и не даёт основания менять T2 в этой ветке.
|
||||
|
||||
## Паузы и положение в сеансе
|
||||
|
||||
Для пакета 1 исследован внешний guard длиной 800 000 отсчётов. Для пакетов
|
||||
2–16 исследованы реальные паузы по 40 000 отсчётов. AC RMS перед пакетами
|
||||
лежит в диапазоне 0,744879–1,302893. Признаков хвоста предыдущей формы по
|
||||
критерию превышения первых 8000 отсчётов над серединой паузы на 3 dB нет.
|
||||
После пакета 16 отдельно проверен конечный guard.
|
||||
|
||||
Группы START и END имеют больше ошибок заголовка, чем MIDDLE 1, однако пакет 9
|
||||
отказывает в середине, пакеты 4, 13 и 14 проходят, а timing, CFO и фон не дают
|
||||
монотонного временного тренда. Расположение пакетов не доказывает
|
||||
`START_END_TRANSIENT`.
|
||||
|
||||
## 145 кандидатов
|
||||
|
||||
Строгий журнал содержит 145 проверок:
|
||||
|
||||
- 27 проверок на точных позициях преамбулы, 16 уникальных позиций и 11
|
||||
повторных проверок той же позиции;
|
||||
- 54 локальных максимума повторяющейся PRBS-структуры внутри формы пакета;
|
||||
- 56 кандидатов в нулевых паузах;
|
||||
- 8 кандидатов вне этих областей.
|
||||
|
||||
Каждый кандидат получает новый `PacketMeasure` и полностью независимый строгий
|
||||
проход. Состояние после отказа не переносится. Граница `accepted_until`
|
||||
изменяется только после CRC PASS. Большое число кандидатов увеличивает объём
|
||||
работы, но не объясняет выборочные ошибки настоящих заголовков.
|
||||
|
||||
## TX и решение по ветке
|
||||
|
||||
Все 16 TX-заголовков побитно идентичны. Magic `0xD391`, версия 1, длина
|
||||
268 байт, checksum и тройное повторение корректны. Текущий программный прогон
|
||||
дал 16/16 точных пакетов и для legacy, и для T2.
|
||||
|
||||
Конкретная небольшая ошибка приёмника, которая объяснила бы значимую часть
|
||||
шести отказов, не найдена. Зафиксирован достигнутый результат: 10/16 точных
|
||||
полных пакетов при 500 ксимв/с и TX VGA 20 dB в аппаратно валидной OTA-записи.
|
||||
Текущую диагностическую ветку следует закрыть. Следующая проектная ветка:
|
||||
CRSF/ELRS для управления и телеметрии.
|
||||
|
||||
QPSK с меньшей скоростью символов остаётся будущим кандидатом для видеоканала.
|
||||
В Lab067 QPSK, новый PHY и изменения BPSK не выполнялись.
|
||||
|
||||
RF TX = 0. Lab067 не коммитировалась. Push не выполнялся.
|
||||
475
experiments/lab062_full_packet_rf_preflight.py
Normal file
@@ -0,0 +1,475 @@
|
||||
"""Lab062: подготовка полного 500K PHY-сеанса и RX-only preflight."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import numpy as np
|
||||
|
||||
from experiments import lab049_bpsk_symbol_rate as phy
|
||||
from experiments import lab053_packet_processing as packet_processing
|
||||
from experiments import lab053_streaming_receiver as streaming
|
||||
from experiments import lab054_hardware_control as lab054
|
||||
from experiments import lab056_abab_energy as measurements
|
||||
from experiments import lab056_capture_runner as hardware
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RAW = ROOT / "data/raw/lab062"
|
||||
OUTPUT = ROOT / "data/processed/lab062"
|
||||
POINT = hardware.OperatingPoint(
|
||||
frequency_hz=1_400_000_000,
|
||||
physical_path="MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
path_confirmed=True,
|
||||
tx_antenna="existing 1400 MHz antenna near window",
|
||||
rx_antenna="existing 1400 MHz antenna on desk",
|
||||
distance_m=1.2,
|
||||
orientation="same polarization; fixed during preflight and RF session",
|
||||
visibility="LOS indoor",
|
||||
)
|
||||
MODE = phy.PhyMode(500_000)
|
||||
PACKETS = 16
|
||||
PAYLOAD_BYTES = 256
|
||||
PREFLIGHT_SAMPLES = 800_000
|
||||
RX_SECONDS = 3
|
||||
RX_SAMPLES = RX_SECONDS * MODE.sample_rate_hz
|
||||
RX_LEAD_SECONDS = 0.5
|
||||
STRONG_AC_RMS_CODE = 20.0
|
||||
STRONG_COMPONENT_CODE = 100.0
|
||||
|
||||
|
||||
def save_new(path: Path, value: dict) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("x", encoding="utf-8") as stream:
|
||||
json.dump(value, stream, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
while block := stream.read(1024 * 1024):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def plans():
|
||||
result = lab054.plans()
|
||||
if len(result) != PACKETS or any(len(frame.plan.payload) != PAYLOAD_BYTES for frame in result):
|
||||
raise RuntimeError("Штатный генератор не дал 16 payload по 256 байт")
|
||||
return result
|
||||
|
||||
|
||||
def parameters() -> dict:
|
||||
return {
|
||||
"rf_frequency_hz": POINT.frequency_hz,
|
||||
"sample_rate_hz": MODE.sample_rate_hz,
|
||||
"symbol_rate": MODE.symbol_rate,
|
||||
"samples_per_symbol": MODE.samples_per_symbol,
|
||||
"digital_carrier_hz": lab054.phy.lab045.CARRIER_OFFSET_HZ,
|
||||
"packets": PACKETS,
|
||||
"payload_bytes": PAYLOAD_BYTES,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": False,
|
||||
"bias_tee": False,
|
||||
"geometry": asdict(POINT),
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
}
|
||||
|
||||
|
||||
def transfer_command(path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
|
||||
return hardware.command(POINT, path, samples, transmit=transmit)
|
||||
|
||||
|
||||
def prepare() -> dict:
|
||||
RAW.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
waveform_path = RAW / "tx_stream.cs8"
|
||||
if waveform_path.exists():
|
||||
raise FileExistsError("Lab062 TX уже существует; перезапись запрещена")
|
||||
with waveform_path.open("xb") as stream:
|
||||
for frame in plans():
|
||||
stream.write(phy.build_parameterized_tx_cs8(frame.plan, MODE))
|
||||
samples = waveform_path.stat().st_size // 2
|
||||
result = {
|
||||
"lab": "Lab062", "stage": "PREPARATION_ONLY", "rf_tx_count": 0,
|
||||
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
|
||||
"parameters": parameters(), "tx_samples": samples,
|
||||
"tx_bytes": waveform_path.stat().st_size,
|
||||
"tx_duration_seconds": samples / MODE.sample_rate_hz,
|
||||
"tx_sha256": sha256(waveform_path), "rx_samples": RX_SAMPLES,
|
||||
"rx_duration_seconds": RX_SECONDS, "automatic_retry": False,
|
||||
"tx_command_prepared_not_run": transfer_command(waveform_path, samples, transmit=True),
|
||||
"rx_command_prepared_not_run": transfer_command(RAW / "rx_stream.cs8", RX_SAMPLES),
|
||||
}
|
||||
save_new(RAW / "tx_metadata.json", result)
|
||||
save_new(OUTPUT / "preparation.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def validate_prepared() -> dict:
|
||||
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||
path = RAW / "tx_stream.cs8"
|
||||
expected = hashlib.sha256()
|
||||
expected_bytes = 0
|
||||
for frame in plans():
|
||||
block = phy.build_parameterized_tx_cs8(frame.plan, MODE)
|
||||
expected.update(block)
|
||||
expected_bytes += len(block)
|
||||
if (metadata.get("stage") != "PREPARATION_ONLY" or metadata.get("rf_tx_count") != 0
|
||||
or metadata.get("parameters") != parameters() or path.stat().st_size != expected_bytes
|
||||
or metadata.get("tx_sha256") != expected.hexdigest() or sha256(path) != expected.hexdigest()):
|
||||
raise RuntimeError("Подготовленный Lab062 TX или параметры изменились")
|
||||
return metadata
|
||||
|
||||
|
||||
def decode(mode: str, source_path: Path | None = None, *, diagnostics: bool = False) -> dict:
|
||||
observations = []
|
||||
receiver = streaming.StreamingReceiver(
|
||||
processor=packet_processing.PacketProcessor(carrier_tracking_mode=mode),
|
||||
packet_observer=lambda row, outcome: observations.append((row, outcome)),
|
||||
)
|
||||
received = []
|
||||
with (source_path or (RAW / "tx_stream.cs8")).open("rb") as stream:
|
||||
while block := stream.read(2 * receiver.block_samples):
|
||||
values = np.frombuffer(block, np.int8)
|
||||
samples = values[::2].astype(np.float32) + 1j * values[1::2].astype(np.float32)
|
||||
received.extend(receiver.push_iq_block(samples.astype(np.complex64)))
|
||||
received.extend(receiver.finish())
|
||||
expected = [frame.plan.payload for frame in plans()]
|
||||
actual = [item.payload for item in received]
|
||||
result = {
|
||||
"mode": mode, "candidates": len(observations), "true_packets_found": len(actual),
|
||||
"header_pass": sum(outcome.phy_header_pass for _, outcome in observations),
|
||||
"fec_started": sum(outcome.fec_attempted for _, outcome in observations),
|
||||
"fec_pass": sum(outcome.fec_pass for _, outcome in observations),
|
||||
"crc_started": sum(outcome.crc != "NOT_REACHED" for _, outcome in observations),
|
||||
"crc_pass": sum(outcome.crc == "PASS" for _, outcome in observations),
|
||||
"packet_output": len(actual),
|
||||
"exact_payload_match": sum(payload in expected for payload in actual),
|
||||
"exact_ordered_payloads": actual == expected,
|
||||
"passed": len(actual) == PACKETS and actual == expected,
|
||||
}
|
||||
if diagnostics:
|
||||
payload_to_index = {payload: index + 1 for index, payload in enumerate(expected)}
|
||||
result["passed_packet_indexes"] = sorted(
|
||||
payload_to_index[payload] for payload in actual if payload in payload_to_index
|
||||
)
|
||||
rows = []
|
||||
for row, outcome in observations:
|
||||
payload = None
|
||||
if outcome.frame is not None and outcome.frame.reception.payload_bytes is not None:
|
||||
payload = bytes(outcome.frame.reception.payload_bytes)
|
||||
traces = row.get("carrier_tracking_traces", ())
|
||||
final_frequency = None
|
||||
if traces and len(traces[-1].frequency_hz):
|
||||
final_frequency = float(traces[-1].frequency_hz[-1])
|
||||
rows.append({
|
||||
"absolute_refined": int(row["absolute_refined"]),
|
||||
"reason": outcome.reason,
|
||||
"header": bool(outcome.phy_header_pass),
|
||||
"fec_started": bool(outcome.fec_attempted),
|
||||
"fec": bool(outcome.fec_pass),
|
||||
"crc_started": outcome.crc != "NOT_REACHED",
|
||||
"crc": outcome.crc == "PASS",
|
||||
"packet_output": bool(outcome.accepted and outcome.crc == "PASS"),
|
||||
"packet_index_after_verdict": payload_to_index.get(payload),
|
||||
"initial_frequency_hz": row.get("cfo_hz"),
|
||||
"t2_final_frequency_hz": final_frequency,
|
||||
"t2_lock_status": "NOT_DEFINED_BY_IMPLEMENTATION" if mode == "t2" else "NOT_APPLICABLE",
|
||||
})
|
||||
result["rows"] = rows
|
||||
result["detector_statistics"] = dict(receiver.detection_statistics)
|
||||
result["candidate_overflow"] = receiver.candidate_overflow
|
||||
return result
|
||||
|
||||
|
||||
def software_gate() -> dict:
|
||||
metadata = validate_prepared()
|
||||
legacy = decode("legacy")
|
||||
t2 = decode("t2")
|
||||
result = {
|
||||
"lab": "Lab062", "stage": "SOFTWARE_GATE", "rf_tx_count": 0,
|
||||
"tx_sha256": metadata["tx_sha256"], "legacy": legacy, "t2": t2,
|
||||
"passed": legacy["passed"] and t2["passed"],
|
||||
}
|
||||
save_new(OUTPUT / "software_gate.json", result)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab062 software gate FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def preflight(*, runner=None, process_checker=None, clock_checker=None,
|
||||
sleep_function=time.sleep) -> dict:
|
||||
metadata = validate_prepared()
|
||||
software = json.loads((OUTPUT / "software_gate.json").read_text(encoding="utf-8"))
|
||||
if not software.get("passed") or software.get("tx_sha256") != metadata["tx_sha256"]:
|
||||
raise RuntimeError("Нет действующего software gate")
|
||||
save_new(RAW / "preflight_attempt.json", {
|
||||
"lab": "Lab062", "rf_tx_count": 0, "point": asdict(POINT),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
|
||||
def rx_only(command):
|
||||
if "-t" in command:
|
||||
raise PermissionError("TX запрещён в Lab062 preflight")
|
||||
return actual_runner(command)
|
||||
|
||||
result = {
|
||||
"lab": "Lab062", "stage": "RX_ONLY_PREFLIGHT", "passed": False,
|
||||
"rf_tx_count": 0, "point": asdict(POINT), "parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"], "stop_before_tx": True,
|
||||
}
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(rx_only)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT, rx_only, lab054.clock.TransmitGuard(), sleep_function=sleep_function
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
path = RAW / "preflight_rx.cs8"
|
||||
if path.exists():
|
||||
raise FileExistsError("Lab062 preflight RX уже существует")
|
||||
captured = rx_only(transfer_command(path, PREFLIGHT_SAMPLES))
|
||||
result["rx_process"] = {
|
||||
"returncode": captured.returncode,
|
||||
"stdout": captured.stdout or "", "stderr": captured.stderr or "",
|
||||
}
|
||||
log = (captured.stdout or "") + (captured.stderr or "")
|
||||
if captured.returncode or not hardware.clean_counter_log(log, "overrun"):
|
||||
raise RuntimeError("RX preflight: процесс или счётчик overruns не прошёл")
|
||||
if path.stat().st_size != 2 * PREFLIGHT_SAMPLES:
|
||||
raise RuntimeError("Неверная длина Lab062 preflight RX")
|
||||
level = hardware.receive_level(path)
|
||||
# У RX-only фона нет символов. Унаследованная метрика Lab056 была бы
|
||||
# привязана к её 20 ksymbol/s и здесь физически неприменима.
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
level["symbol_energy"] = "NOT_APPLICABLE_TO_BACKGROUND"
|
||||
ac_rms = float(np.sqrt(max(0.0, level["ac_power_code2"])))
|
||||
strong = bool(ac_rms >= STRONG_AC_RMS_CODE or level["max_abs_i"] >= STRONG_COMPONENT_CODE
|
||||
or level["max_abs_q"] >= STRONG_COMPONENT_CODE)
|
||||
level.update({
|
||||
"ac_rms_code": ac_rms,
|
||||
"unexpected_strong_signal": strong,
|
||||
"strong_signal_rule": (
|
||||
f"AC RMS >= {STRONG_AC_RMS_CODE} code or max |I|/|Q| >= "
|
||||
f"{STRONG_COMPONENT_CODE} code"
|
||||
),
|
||||
})
|
||||
result["rx_level"] = level
|
||||
result["rx_sha256"] = sha256(path)
|
||||
result["rx_overruns"] = 0
|
||||
if level["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping; STOP без изменения gain")
|
||||
if strong:
|
||||
raise RuntimeError("Неожиданно сильный RX-only фон; STOP без изменения gain")
|
||||
result["passed"] = True
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = lab054.disable_clock_outputs(rx_only) if clock_started else []
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "preflight_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "preflight_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab062 preflight cleanup FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def require_preflight(metadata: dict) -> dict:
|
||||
"""Проверить сохранённый preflight именно для подготовленного потока Lab062."""
|
||||
record = json.loads((RAW / "preflight_result.json").read_text(encoding="utf-8"))
|
||||
if (record.get("passed") is not True or record.get("rf_tx_count") != 0
|
||||
or record.get("point") != asdict(POINT)
|
||||
or record.get("parameters") != parameters()
|
||||
or record.get("tx_sha256") != metadata["tx_sha256"]):
|
||||
raise PermissionError("Нет успешного RX-only preflight для этой конфигурации Lab062")
|
||||
hardware.require_clock_pass(record.get("clock", {}))
|
||||
level = record.get("rx_level", {})
|
||||
if (level.get("saturation_detected") is not False
|
||||
or level.get("unexpected_strong_signal") is not False
|
||||
or record.get("rx_overruns") != 0):
|
||||
raise PermissionError("RX-only preflight не разрешает передачу")
|
||||
return record
|
||||
|
||||
|
||||
def capture_once(*, authorization: bool = False, runner=None, process_starter=None,
|
||||
process_checker=None, clock_checker=None,
|
||||
sleep_function=time.sleep) -> dict:
|
||||
"""Выполнить один RX-first сеанс и навсегда израсходовать попытку до обращения к SDR."""
|
||||
if authorization is not True:
|
||||
raise PermissionError("Нужно отдельное разрешение на единственный RF-сеанс Lab062")
|
||||
metadata = validate_prepared()
|
||||
require_preflight(metadata)
|
||||
receive_path = RAW / "rx_stream.cs8"
|
||||
if receive_path.exists() or (RAW / "capture_result.json").exists():
|
||||
raise FileExistsError("Аппаратный результат Lab062 уже существует")
|
||||
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_starter = process_starter or lab054.clock._default_process_starter
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
save_new(RAW / "hardware_attempt.json", {
|
||||
"lab": "Lab062", "authorized_once": True, "automatic_retry": False,
|
||||
"point": asdict(POINT), "parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
|
||||
result = {
|
||||
"lab": "Lab062", "stage": "SINGLE_RF_CAPTURE", "passed": False,
|
||||
"rf_tx_count": 0, "automatic_tx_repeats": 0,
|
||||
"point": asdict(POINT), "parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
}
|
||||
guard = lab054.clock.TransmitGuard()
|
||||
process = None
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(actual_runner)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT, actual_runner, guard, sleep_function=sleep_function
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
|
||||
process = process_starter(
|
||||
transfer_command(receive_path, RX_SAMPLES),
|
||||
RAW / "rx_stdout.txt", RAW / "rx_stderr.txt",
|
||||
)
|
||||
sleep_function(RX_LEAD_SECONDS)
|
||||
if process.poll() is not None:
|
||||
raise RuntimeError("RX завершился до TX")
|
||||
before_tx = hardware.receive_level(receive_path, tail_samples=65536)
|
||||
before_tx.pop("symbol_energy_code2_seconds", None)
|
||||
result["rx_level_before_tx"] = before_tx
|
||||
if before_tx["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping до TX; RF TX = 0")
|
||||
|
||||
guard.register_transmit()
|
||||
result["rf_tx_count"] = guard.transmit_calls
|
||||
transmitted = actual_runner(transfer_command(
|
||||
RAW / "tx_stream.cs8", metadata["tx_samples"], transmit=True
|
||||
))
|
||||
save_new(RAW / "tx_process_result.json", {
|
||||
"returncode": transmitted.returncode,
|
||||
"stdout": transmitted.stdout or "", "stderr": transmitted.stderr or "",
|
||||
})
|
||||
if transmitted.returncode:
|
||||
raise RuntimeError("Единственный TX завершился ошибкой; повтор запрещён")
|
||||
if process.wait(RX_SECONDS + 10):
|
||||
raise RuntimeError("RX завершился ошибкой; передача уже выполнена, повтор запрещён")
|
||||
process.close_logs()
|
||||
|
||||
result["rx_samples"] = receive_path.stat().st_size // 2
|
||||
result["rx_bytes"] = receive_path.stat().st_size
|
||||
result["rx_duration_seconds"] = result["rx_samples"] / MODE.sample_rate_hz
|
||||
if result["rx_bytes"] != RX_SAMPLES * 2:
|
||||
raise RuntimeError("Повреждённая длина RX; передача уже выполнена, повтор запрещён")
|
||||
rx_log = "".join(
|
||||
(RAW / f"rx_{kind}.txt").read_text(encoding="utf-8")
|
||||
for kind in ("stdout", "stderr")
|
||||
)
|
||||
tx_log = (transmitted.stdout or "") + (transmitted.stderr or "")
|
||||
if not hardware.clean_counter_log(rx_log, "overrun"):
|
||||
raise RuntimeError("RX overrun или неоднозначный счётчик; повтор запрещён")
|
||||
if not hardware.clean_counter_log(tx_log, "underrun"):
|
||||
raise RuntimeError("TX underrun или неоднозначный счётчик; повтор запрещён")
|
||||
result["rx_overruns"] = 0
|
||||
result["tx_underruns"] = 0
|
||||
result["rx_sha256"] = sha256(receive_path)
|
||||
level = hardware.receive_level(receive_path)
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
result["rx_level"] = level
|
||||
result["rx_clipping"] = bool(level["saturation_detected"])
|
||||
if result["rx_clipping"]:
|
||||
raise RuntimeError("RX clipping в записи; передача уже выполнена, повтор запрещён")
|
||||
result["passed"] = True
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = []
|
||||
if process is not None:
|
||||
try:
|
||||
process.terminate()
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
if clock_started:
|
||||
errors.extend(lab054.disable_clock_outputs(actual_runner))
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "capture_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "capture_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab062 RF-сеанс завершён с ошибкой; повтор запрещён")
|
||||
return result
|
||||
|
||||
|
||||
def run_functional_tests() -> None:
|
||||
assert MODE.samples_per_symbol == 16
|
||||
assert len(plans()) == 16
|
||||
assert all(len(frame.plan.payload) == 256 for frame in plans())
|
||||
assert "-t" not in transfer_command("NUL", 65536)
|
||||
assert "-t" in transfer_command("NUL", 65536, transmit=True)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--stage", choices=("prepare", "software", "preflight", "capture"), required=True)
|
||||
parser.add_argument("--authorize-one-rf", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
if arguments.stage == "prepare":
|
||||
result = prepare()
|
||||
elif arguments.stage == "software":
|
||||
result = software_gate()
|
||||
elif arguments.stage == "preflight":
|
||||
result = preflight()
|
||||
else:
|
||||
result = capture_once(authorization=arguments.authorize_one_rf)
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
print("RF TX = 0")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
330
experiments/lab062_rf_analysis.py
Normal file
@@ -0,0 +1,330 @@
|
||||
"""Lab062: офлайн-анализ единственной сохранённой RX IQ-записи."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
import csv
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import numpy as np
|
||||
from scipy import signal
|
||||
|
||||
from experiments import lab058_abab_symbol_quality as lab058
|
||||
from experiments import lab062_full_packet_rf_preflight as lab062
|
||||
|
||||
|
||||
def counter_values(text: str, name: str) -> list[int]:
|
||||
pattern = re.compile(rf"(?<![\w.+-])(\d+)\s+(?:(?:rx|tx)\s+)?{name}s?\b", re.I)
|
||||
return [int(match.group(1)) for match in pattern.finditer(text)]
|
||||
|
||||
|
||||
def locate_burst(tx: lab058.Cs8File, rx: lab058.Cs8File, frame_samples: int) -> dict:
|
||||
"""Найти начало по первому полному пакету, не передавая границы декодеру."""
|
||||
stride = lab062.MODE.samples_per_symbol
|
||||
tx_raw = np.memmap(tx.path, np.int8, mode="r")
|
||||
rx_raw = np.memmap(rx.path, np.int8, mode="r")
|
||||
tx_decimated = (
|
||||
tx_raw[0:2 * frame_samples:2 * stride].astype(np.float32)
|
||||
+ 1j * tx_raw[1:2 * frame_samples:2 * stride].astype(np.float32)
|
||||
)
|
||||
rx_decimated = (
|
||||
rx_raw[0::2 * stride].astype(np.float32)
|
||||
+ 1j * rx_raw[1::2 * stride].astype(np.float32)
|
||||
)
|
||||
tx_decimated -= np.mean(tx_decimated)
|
||||
rx_decimated -= np.mean(rx_decimated)
|
||||
correlation = signal.correlate(rx_decimated, tx_decimated, mode="valid", method="fft")
|
||||
coarse = int(np.argmax(abs(correlation))) * stride
|
||||
first = tx.read(0, frame_samples).astype(np.complex128)
|
||||
first -= np.mean(first)
|
||||
candidates = range(max(0, coarse - 2 * stride), min(rx.size - frame_samples, coarse + 2 * stride) + 1)
|
||||
values = []
|
||||
for start in candidates:
|
||||
received = rx.read(start, frame_samples).astype(np.complex128)
|
||||
received -= np.mean(received)
|
||||
values.append(abs(np.vdot(first, received)))
|
||||
offset = int(np.argmax(values))
|
||||
start = candidates.start + offset
|
||||
median = float(np.median(abs(correlation)))
|
||||
return {
|
||||
"rx_start_sample": start,
|
||||
"coarse_start_sample": coarse,
|
||||
"correlation_peak_to_median": float(abs(correlation[coarse // stride]) / max(median, 1e-30)),
|
||||
"method": "TX first-frame correlation; used only after strict receiver verdict",
|
||||
}
|
||||
|
||||
|
||||
def strict_row(rows: list[dict], nominal: int) -> dict | None:
|
||||
if not rows:
|
||||
return None
|
||||
selected = min(rows, key=lambda row: abs(row["absolute_refined"] - nominal))
|
||||
return selected if abs(selected["absolute_refined"] - nominal) <= 64 else None
|
||||
|
||||
|
||||
def diagnostic_symbol_rows(rx: lab058.Cs8File, start: int, frame_samples: int,
|
||||
mode: str) -> list[dict]:
|
||||
processor = lab062.packet_processing.PacketProcessor(carrier_tracking_mode=mode)
|
||||
rows = []
|
||||
for index, frame in enumerate(lab062.plans(), 1):
|
||||
values = rx.read(start + (index - 1) * frame_samples, frame_samples).astype(np.complex64)
|
||||
context = lab062.packet_processing.PacketMeasure()
|
||||
token = lab062.packet_processing.CURRENT.set(context)
|
||||
try:
|
||||
probe = processor.extract(values, 0, frame.plan.frame_symbol_count)
|
||||
finally:
|
||||
lab062.packet_processing.CURRENT.reset(token)
|
||||
expected = 2.0 * frame.plan.frame_bits.astype(np.float64) - 1.0
|
||||
if not probe.passed or probe.normalized_real.size != expected.size:
|
||||
rows.append({"packet_index": index, "measurable": False, "reason": probe.reason})
|
||||
continue
|
||||
soft = np.asarray(probe.normalized_real, dtype=np.float64)
|
||||
errors = np.signbit(soft) != np.signbit(expected)
|
||||
p = lab062.phy.lab045.PREAMBLE_BIT_COUNT
|
||||
h = p + lab062.phy.lab045.PHY_HEADER_BIT_COUNT
|
||||
traces = context.cache.get("carrier_tracking_traces", ())
|
||||
final_frequency = None
|
||||
if traces and len(traces[-1].frequency_hz):
|
||||
final_frequency = float(traces[-1].frequency_hz[-1])
|
||||
rows.append({
|
||||
"packet_index": index,
|
||||
"measurable": True,
|
||||
"preamble_symbol_errors": int(np.sum(errors[:p])),
|
||||
"header_symbol_errors": int(np.sum(errors[p:h])),
|
||||
"payload_symbols_before_fec_errors": int(np.sum(errors[h:])),
|
||||
"symbols": int(len(expected)),
|
||||
"symbol_errors": int(np.sum(errors)),
|
||||
"symbol_error_rate": float(np.mean(errors)),
|
||||
"symbol_rms_deviation": float(np.sqrt(np.mean((soft - expected) ** 2))),
|
||||
"initial_frequency_hz": float(probe.carrier["cfo_hz"]),
|
||||
"t2_final_frequency_hz": final_frequency,
|
||||
"t2_lock_status": "NOT_DEFINED_BY_IMPLEMENTATION" if mode == "t2" else "NOT_APPLICABLE",
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def waveform_quality(tx: lab058.Cs8File, rx: lab058.Cs8File, start: int) -> dict:
|
||||
transmitted = tx.read(0, tx.size).astype(np.complex128)
|
||||
received = rx.read(start, tx.size).astype(np.complex128)
|
||||
transmitted -= np.mean(transmitted)
|
||||
received -= np.mean(received)
|
||||
product = received * np.conj(transmitted)
|
||||
block = 4096
|
||||
count = len(product) // block
|
||||
block_values = np.mean(product[:count * block].reshape(count, block), axis=1)
|
||||
block_times = (np.arange(count) * block + 0.5 * block) / lab062.MODE.sample_rate_hz
|
||||
phase = np.unwrap(np.angle(block_values))
|
||||
weights = np.maximum(abs(block_values), 1e-12)
|
||||
design = np.column_stack((np.ones(count), block_times))
|
||||
root = np.sqrt(weights / np.max(weights))
|
||||
coefficients, *_ = np.linalg.lstsq(design * root[:, None], phase * root, rcond=None)
|
||||
frequency_hz = float(coefficients[1] / (2 * np.pi))
|
||||
times = np.arange(len(received)) / lab062.MODE.sample_rate_hz
|
||||
corrected = received * np.exp(-2j * np.pi * frequency_hz * times)
|
||||
gain = np.vdot(transmitted, corrected) / np.vdot(transmitted, transmitted)
|
||||
matched = gain * transmitted
|
||||
residual = corrected - matched
|
||||
matched_power = float(np.mean(abs(matched) ** 2))
|
||||
residual_power = float(np.mean(abs(residual) ** 2))
|
||||
|
||||
frequencies, tx_psd = signal.welch(
|
||||
transmitted, fs=lab062.MODE.sample_rate_hz, nperseg=8192,
|
||||
noverlap=4096, return_onesided=False,
|
||||
)
|
||||
_, rx_psd = signal.welch(
|
||||
corrected, fs=lab062.MODE.sample_rate_hz, nperseg=8192,
|
||||
noverlap=4096, return_onesided=False,
|
||||
)
|
||||
_, cross = signal.csd(
|
||||
corrected, transmitted, fs=lab062.MODE.sample_rate_hz, nperseg=8192,
|
||||
noverlap=4096, return_onesided=False,
|
||||
)
|
||||
transfer = np.conj(cross) / np.maximum(tx_psd, 1e-30)
|
||||
coherence = abs(cross) ** 2 / np.maximum(tx_psd * rx_psd, 1e-30)
|
||||
occupied = tx_psd >= np.max(tx_psd) * 0.01
|
||||
stable = occupied & (coherence >= 0.5)
|
||||
gain_db = 20 * np.log10(np.maximum(abs(transfer[stable]), 1e-30))
|
||||
return {
|
||||
"frequency_offset_hz": frequency_hz,
|
||||
"complex_gain": {"real": float(gain.real), "imag": float(gain.imag)},
|
||||
"matched_component_power_code2": matched_power,
|
||||
"residual_power_code2": residual_power,
|
||||
"matched_residual_db": float(10 * math.log10(matched_power / residual_power)),
|
||||
"residual_interpretation": "Contains noise, channel distortion and any TX-underrun effect; not pure noise",
|
||||
"band_bins": int(np.sum(stable)),
|
||||
"band_gain_nonuniformity_p95_p05_db": (
|
||||
float(np.percentile(gain_db, 95) - np.percentile(gain_db, 5)) if gain_db.size else None
|
||||
),
|
||||
"band_gain_max_min_db": float(np.ptp(gain_db)) if gain_db.size else None,
|
||||
"median_coherence": float(np.median(coherence[stable])) if np.any(stable) else None,
|
||||
"tx_psd_threshold_db": -20.0,
|
||||
"coherence_threshold": 0.5,
|
||||
}
|
||||
|
||||
|
||||
def lab058_quality(tx: lab058.Cs8File, rx: lab058.Cs8File, start: int,
|
||||
frame_samples: int) -> dict:
|
||||
"""Применить к каждому полному PHY-кадру неизменённую методику Lab058."""
|
||||
rows = []
|
||||
for index, frame in enumerate(lab062.plans(), 1):
|
||||
offset = (index - 1) * frame_samples
|
||||
transmitted = tx.read(offset, frame_samples)
|
||||
received = rx.read(start + offset, frame_samples)
|
||||
expected = 2.0 * frame.plan.frame_bits.astype(np.float64) - 1.0
|
||||
metrics, _working = lab058.analyze_values(received, expected, lab062.MODE.symbol_rate)
|
||||
channel, _arrays = lab058.channel_metrics(
|
||||
transmitted, received, lab062.MODE.symbol_rate, metrics["oracle"]["cfo_hz"]
|
||||
)
|
||||
rows.append({"packet_index": index, "oracle": metrics["oracle"], "channel": channel})
|
||||
selected = [row["oracle"]["metrics"] for row in rows]
|
||||
errors = sum(row["errors"] for row in selected)
|
||||
symbols = sum(row["symbols"] for row in selected)
|
||||
matched = float(np.mean([row["matched_component_power"] for row in selected]))
|
||||
residual = float(np.mean([row["residual_power"] for row in selected]))
|
||||
ripple = [row["channel"]["amplitude_ripple_p95_p05_db"] for row in rows]
|
||||
return {
|
||||
"method": "Lab058 analyze_values/oracle validation tiles, independently per full PHY packet",
|
||||
"packets": rows,
|
||||
"symbol_errors": errors,
|
||||
"symbols": symbols,
|
||||
"symbol_error_rate": errors / symbols,
|
||||
"evm_rms": float(math.sqrt(residual / matched)),
|
||||
"matched_component_power": matched,
|
||||
"residual_power": residual,
|
||||
"matched_residual_db": float(10 * math.log10(matched / residual)),
|
||||
"residual_interpretation": "Contains noise and unmodelled distortion; not pure noise",
|
||||
"band_gain_nonuniformity_p95_p05_db_median": float(np.median(ripple)),
|
||||
"band_gain_nonuniformity_p95_p05_db_min": float(np.min(ripple)),
|
||||
"band_gain_nonuniformity_p95_p05_db_max": float(np.max(ripple)),
|
||||
}
|
||||
|
||||
|
||||
def analyze() -> dict:
|
||||
capture = json.loads((lab062.RAW / "capture_result.json").read_text(encoding="utf-8"))
|
||||
legacy = json.loads((lab062.OUTPUT / "legacy_rf_decode.json").read_text(encoding="utf-8"))
|
||||
t2 = json.loads((lab062.OUTPUT / "t2_rf_decode.json").read_text(encoding="utf-8"))
|
||||
tx = lab058.Cs8File(lab062.RAW / "tx_stream.cs8")
|
||||
rx = lab058.Cs8File(lab062.RAW / "rx_stream.cs8")
|
||||
frame_samples = lab062.phy.frame_waveform_sample_count(lab062.plans()[0].plan, lab062.MODE)
|
||||
location = locate_burst(tx, rx, frame_samples)
|
||||
start = location["rx_start_sample"]
|
||||
legacy_symbols = diagnostic_symbol_rows(rx, start, frame_samples, "legacy")
|
||||
t2_symbols = diagnostic_symbol_rows(rx, start, frame_samples, "t2")
|
||||
|
||||
packet_rows = []
|
||||
for index in range(1, lab062.PACKETS + 1):
|
||||
nominal = start + (index - 1) * frame_samples
|
||||
legacy_strict = strict_row(legacy["rows"], nominal)
|
||||
t2_strict = strict_row(t2["rows"], nominal)
|
||||
packet_rows.append({
|
||||
"packet_index": index,
|
||||
"nominal_rx_start": nominal,
|
||||
"legacy": legacy_strict,
|
||||
"t2": t2_strict,
|
||||
"diagnostic_legacy": legacy_symbols[index - 1],
|
||||
"diagnostic_t2": t2_symbols[index - 1],
|
||||
})
|
||||
|
||||
rx_log = (lab062.RAW / "rx_stderr.txt").read_text(encoding="utf-8")
|
||||
tx_process = json.loads((lab062.RAW / "tx_process_result.json").read_text(encoding="utf-8"))
|
||||
tx_log = (tx_process.get("stdout") or "") + (tx_process.get("stderr") or "")
|
||||
level = lab062.hardware.receive_level(lab062.RAW / "rx_stream.cs8")
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
quality = waveform_quality(tx, rx, start)
|
||||
comparable_quality = lab058_quality(tx, rx, start, frame_samples)
|
||||
all_diag = [row for row in t2_symbols if row.get("measurable")]
|
||||
total_errors = sum(row["symbol_errors"] for row in all_diag)
|
||||
total_symbols = sum(row["symbols"] for row in all_diag)
|
||||
rms = math.sqrt(sum(row["symbol_rms_deviation"] ** 2 * row["symbols"] for row in all_diag) / total_symbols)
|
||||
legacy_set = set(legacy["passed_packet_indexes"])
|
||||
t2_set = set(t2["passed_packet_indexes"])
|
||||
result = {
|
||||
"lab": "Lab062",
|
||||
"status": "RF_SESSION_INVALID_TX_UNDERRUN",
|
||||
"rf_tx_count": capture["rf_tx_count"],
|
||||
"automatic_tx_repeats": capture["automatic_tx_repeats"],
|
||||
"integrity": {
|
||||
"tx_sha256": lab062.sha256(lab062.RAW / "tx_stream.cs8"),
|
||||
"rx_sha256": lab062.sha256(lab062.RAW / "rx_stream.cs8"),
|
||||
"rx_samples": rx.size,
|
||||
"rx_bytes": (lab062.RAW / "rx_stream.cs8").stat().st_size,
|
||||
"rx_duration_seconds": rx.size / lab062.MODE.sample_rate_hz,
|
||||
"rx_overrun_values": counter_values(rx_log, "overrun"),
|
||||
"tx_underrun_values": counter_values(tx_log, "underrun"),
|
||||
"rx_overruns_max": max(counter_values(rx_log, "overrun"), default=None),
|
||||
"tx_underruns_max": max(counter_values(tx_log, "underrun"), default=None),
|
||||
"rx_clipping": bool(level["saturation_detected"]),
|
||||
"rx_clipped_samples_fraction": level["clipped_samples_fraction"],
|
||||
},
|
||||
"clock": capture["clock"],
|
||||
"cleanup_errors": capture["cleanup_errors"],
|
||||
"location": location,
|
||||
"legacy": {key: value for key, value in legacy.items() if key != "rows"},
|
||||
"t2": {key: value for key, value in t2.items() if key != "rows"},
|
||||
"passed_by_both": sorted(legacy_set & t2_set),
|
||||
"passed_only_by_legacy": sorted(legacy_set - t2_set),
|
||||
"passed_only_by_t2": sorted(t2_set - legacy_set),
|
||||
"failed_by_both": sorted(set(range(1, 17)) - legacy_set - t2_set),
|
||||
"packet_rows": packet_rows,
|
||||
"diagnostic_symbol_error_rate_t2": total_errors / total_symbols,
|
||||
"symbol_rms_deviation_t2": rms,
|
||||
"lab058_quality": comparable_quality,
|
||||
"waveform_quality": quality,
|
||||
"lab056_comparison": {
|
||||
"lab056_B_oracle_symbol_error_rate": 0.03495896,
|
||||
"lab056_B_oracle_evm_rms": 0.845350,
|
||||
"scope": "Lab056 used a different PRBS ABAB waveform and method; values are contextual, not identical estimands",
|
||||
},
|
||||
"primary_result": (
|
||||
"The sole RF session is invalid as a clean legacy-versus-T2 acceptance test because "
|
||||
"hackrf_transfer reported one transient TX underrun; both strict modes produced 0/16."
|
||||
),
|
||||
"recommended_next_single_step": (
|
||||
"Offline localize the underrun position against packet boundaries before deciding whether a new RF lab is justified."
|
||||
),
|
||||
"rf_tx_now": 0,
|
||||
"commit": "NOT_PERFORMED", "push": "NOT_PERFORMED", "lab063": "NOT_CREATED",
|
||||
}
|
||||
lab062.OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(lab062.OUTPUT / "rf_analysis.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
with (lab062.OUTPUT / "packet_comparison.csv").open("w", newline="", encoding="utf-8-sig") as stream:
|
||||
writer = csv.writer(stream)
|
||||
writer.writerow([
|
||||
"packet_index", "legacy_header", "legacy_fec", "legacy_crc", "legacy_output",
|
||||
"t2_header", "t2_fec", "t2_crc", "t2_output", "preamble_errors_t2",
|
||||
"header_errors_t2", "payload_errors_before_fec_t2", "initial_frequency_hz_t2",
|
||||
"t2_final_frequency_hz", "t2_lock_status",
|
||||
])
|
||||
for row in packet_rows:
|
||||
legacy_row, t2_row, diagnostic = row["legacy"], row["t2"], row["diagnostic_t2"]
|
||||
writer.writerow([
|
||||
row["packet_index"], *(legacy_row.get(key) if legacy_row else False for key in ("header", "fec", "crc", "packet_output")),
|
||||
*(t2_row.get(key) if t2_row else False for key in ("header", "fec", "crc", "packet_output")),
|
||||
diagnostic.get("preamble_symbol_errors"), diagnostic.get("header_symbol_errors"),
|
||||
diagnostic.get("payload_symbols_before_fec_errors"), diagnostic.get("initial_frequency_hz"),
|
||||
diagnostic.get("t2_final_frequency_hz"), diagnostic.get("t2_lock_status"),
|
||||
])
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
result = analyze()
|
||||
concise = {key: result[key] for key in (
|
||||
"status", "rf_tx_count", "automatic_tx_repeats", "integrity", "legacy", "t2",
|
||||
"passed_by_both", "passed_only_by_legacy", "passed_only_by_t2", "failed_by_both",
|
||||
"diagnostic_symbol_error_rate_t2", "symbol_rms_deviation_t2", "waveform_quality",
|
||||
"primary_result", "recommended_next_single_step", "rf_tx_now",
|
||||
)}
|
||||
print(json.dumps(concise, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
567
experiments/lab063_tx_underrun_localization.py
Normal file
@@ -0,0 +1,567 @@
|
||||
"""Lab063: офлайн-локализация TX underrun и ухудшения качества Lab062."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import csv
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy import signal
|
||||
|
||||
from experiments import lab058_abab_symbol_quality as lab058
|
||||
from experiments import lab062_full_packet_rf_preflight as lab062
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RAW = ROOT / "data/raw/lab062"
|
||||
LAB062 = ROOT / "data/processed/lab062"
|
||||
OUTPUT = ROOT / "data/processed/lab063"
|
||||
TX_PATH = RAW / "tx_stream.cs8"
|
||||
RX_PATH = RAW / "rx_stream.cs8"
|
||||
FRAME_SAMPLES = 92_256
|
||||
PACKETS = 16
|
||||
SAMPLE_RATE = 8_000_000
|
||||
SYMBOL_RATE = 500_000
|
||||
WINDOW_SYMBOLS = 512
|
||||
WINDOW_SAMPLES = WINDOW_SYMBOLS * (SAMPLE_RATE // SYMBOL_RATE)
|
||||
STEP_SYMBOLS = 256
|
||||
STEP_SAMPLES = STEP_SYMBOLS * (SAMPLE_RATE // SYMBOL_RATE)
|
||||
SEARCH_MARGIN_SAMPLES = 12_000
|
||||
MIN_LOCAL_CORRELATION = 0.15
|
||||
RELIABLE_PACKET_START = 24_000
|
||||
RELIABLE_PACKET_END = 86_000
|
||||
LAB056_B = {
|
||||
"symbol_error_rate": 0.03495896,
|
||||
"evm_rms": 0.845350,
|
||||
"matched_residual_db": 1.464846,
|
||||
"band_ripple_db": 5.471382,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArrayReader:
|
||||
"""Минимальный интерфейс чтения для автономных проверок алгоритма."""
|
||||
|
||||
values: np.ndarray
|
||||
|
||||
@property
|
||||
def size(self) -> int:
|
||||
return int(len(self.values))
|
||||
|
||||
def read(self, start: int, count: int) -> np.ndarray:
|
||||
return np.asarray(self.values[start:start + count], dtype=np.complex64)
|
||||
|
||||
|
||||
def underrun_scale(byte_count: int = 16_672) -> dict:
|
||||
if type(byte_count) is not int or byte_count <= 0 or byte_count % 2:
|
||||
raise ValueError("Размер cs8 underrun должен быть положительным чётным числом байт")
|
||||
samples = byte_count // 2
|
||||
duration = samples / SAMPLE_RATE
|
||||
symbols = samples * SYMBOL_RATE / SAMPLE_RATE
|
||||
return {
|
||||
"bytes": byte_count,
|
||||
"complex_samples": samples,
|
||||
"duration_seconds": duration,
|
||||
"duration_milliseconds": duration * 1000,
|
||||
"symbols": symbols,
|
||||
"fraction_of_packet": samples / FRAME_SAMPLES,
|
||||
"percent_of_packet": 100 * samples / FRAME_SAMPLES,
|
||||
"interpretation": "Scale only; RX does not prove that this many RF samples were lost or repeated",
|
||||
}
|
||||
|
||||
|
||||
def actual_gap() -> dict:
|
||||
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||
total = int(metadata["tx_samples"])
|
||||
expected = FRAME_SAMPLES * PACKETS
|
||||
return {
|
||||
"tx_samples": total,
|
||||
"packets": PACKETS,
|
||||
"packet_waveform_samples": FRAME_SAMPLES,
|
||||
"packet_product_samples": expected,
|
||||
"identity_confirmed": total == expected,
|
||||
"additional_inter_packet_gap_samples": total - expected,
|
||||
"additional_inter_packet_gap_seconds": (total - expected) / SAMPLE_RATE,
|
||||
"actual": "0 additional samples (no planned 5 ms gaps)",
|
||||
"reason": (
|
||||
"IMPLEMENTATION_OMISSION: prepare() writes the 16 complete packet waveforms "
|
||||
"back-to-back and has no gap parameter or zero-fill operation"
|
||||
),
|
||||
"packet_internal_note": "Each packet waveform still contains its own guards and RRC tails",
|
||||
}
|
||||
|
||||
|
||||
def normalized_match(transmitted: np.ndarray, search: np.ndarray) -> dict:
|
||||
"""Найти локальный участок RX с нормировкой по AC-энергии каждого положения."""
|
||||
transmitted = np.asarray(transmitted, dtype=np.complex128)
|
||||
search = np.asarray(search, dtype=np.complex128)
|
||||
if transmitted.ndim != 1 or search.ndim != 1 or len(search) < len(transmitted):
|
||||
raise ValueError("Нужны одномерные TX и более длинное RX-окно")
|
||||
centered_tx = transmitted - np.mean(transmitted)
|
||||
tx_energy = float(np.vdot(centered_tx, centered_tx).real)
|
||||
if tx_energy <= 0:
|
||||
raise ValueError("TX-окно не содержит AC-энергии")
|
||||
correlation = signal.correlate(search, centered_tx, mode="valid", method="fft")
|
||||
count = len(centered_tx)
|
||||
kernel = np.ones(count, dtype=np.float64)
|
||||
sums = signal.fftconvolve(search, kernel, mode="valid")
|
||||
powers = signal.fftconvolve(abs(search) ** 2, kernel, mode="valid")
|
||||
rx_energies = np.maximum(powers - abs(sums) ** 2 / count, 0.0)
|
||||
normalized = abs(correlation) / np.sqrt(np.maximum(tx_energy * rx_energies, 1e-30))
|
||||
best = int(np.argmax(normalized))
|
||||
centered_rx = search[best:best + count] - np.mean(search[best:best + count])
|
||||
gain = complex(np.vdot(centered_tx, centered_rx) / tx_energy)
|
||||
residual = centered_rx - gain * centered_tx
|
||||
return {
|
||||
"offset": best,
|
||||
"normalized_correlation": float(normalized[best]),
|
||||
"gain": gain,
|
||||
"gain_amplitude": float(abs(gain)),
|
||||
"phase_rad": float(np.angle(gain)),
|
||||
"residual_power": float(np.mean(abs(residual) ** 2)),
|
||||
"rx_ac_power": float(np.mean(abs(centered_rx) ** 2)),
|
||||
}
|
||||
|
||||
|
||||
def track_alignment(tx, rx, rx_start: int, *, frame_samples: int = FRAME_SAMPLES,
|
||||
window_samples: int = WINDOW_SAMPLES,
|
||||
step_samples: int = STEP_SAMPLES,
|
||||
search_margin: int = SEARCH_MARGIN_SAMPLES,
|
||||
reliable_packet_start: int = RELIABLE_PACKET_START,
|
||||
reliable_packet_end: int = RELIABLE_PACKET_END) -> list[dict]:
|
||||
"""Сопоставить короткие перекрывающиеся TX-окна с RX независимо по времени."""
|
||||
rows = []
|
||||
for tx_start in range(0, tx.size - window_samples + 1, step_samples):
|
||||
expected_rx = rx_start + tx_start
|
||||
low = max(0, expected_rx - search_margin)
|
||||
high = min(rx.size, expected_rx + search_margin + window_samples)
|
||||
match = normalized_match(tx.read(tx_start, window_samples), rx.read(low, high - low))
|
||||
matched_rx = low + match.pop("offset")
|
||||
packet = min(PACKETS, tx_start // frame_samples + 1)
|
||||
position_inside_packet = tx_start - (packet - 1) * frame_samples
|
||||
inside_reliable_region = reliable_packet_start <= position_inside_packet <= reliable_packet_end
|
||||
correlation_above_threshold = match["normalized_correlation"] >= MIN_LOCAL_CORRELATION
|
||||
rows.append({
|
||||
"window_index": len(rows),
|
||||
"tx_sample": tx_start,
|
||||
"rx_sample": matched_rx,
|
||||
"time_seconds": tx_start / SAMPLE_RATE,
|
||||
"local_delay_samples": matched_rx - expected_rx,
|
||||
"packet_index": packet,
|
||||
"position_inside_packet_samples": position_inside_packet,
|
||||
"inside_reliable_region": inside_reliable_region,
|
||||
"correlation_above_threshold": correlation_above_threshold,
|
||||
"reliable_for_delay": inside_reliable_region and correlation_above_threshold,
|
||||
**match,
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def alignment_verdict(rows: list[dict], expected_jump: int) -> dict:
|
||||
valid = [row for row in rows if row.get("reliable_for_delay") is True]
|
||||
internal_low_correlation = [
|
||||
row for row in rows
|
||||
if row.get("inside_reliable_region") is True
|
||||
and row.get("correlation_above_threshold") is False
|
||||
]
|
||||
jumps = []
|
||||
for left, right in zip(valid, valid[1:]):
|
||||
change = right["local_delay_samples"] - left["local_delay_samples"]
|
||||
if abs(change) >= max(256, expected_jump // 4):
|
||||
jumps.append({
|
||||
"left_window": left["window_index"], "right_window": right["window_index"],
|
||||
"tx_sample": right["tx_sample"], "rx_sample": right["rx_sample"],
|
||||
"packet_index": right["packet_index"],
|
||||
"position_inside_packet_samples": right["position_inside_packet_samples"],
|
||||
"delay_change_samples": change,
|
||||
"left_correlation": left["normalized_correlation"],
|
||||
"right_correlation": right["normalized_correlation"],
|
||||
})
|
||||
delays = np.asarray([row["local_delay_samples"] for row in valid], dtype=np.float64)
|
||||
correlations = np.asarray([row["normalized_correlation"] for row in rows], dtype=np.float64)
|
||||
return {
|
||||
"valid_windows": len(valid),
|
||||
"total_windows": len(rows),
|
||||
"minimum_correlation": float(np.min(correlations)),
|
||||
"median_correlation": float(np.median(correlations)),
|
||||
"maximum_correlation": float(np.max(correlations)),
|
||||
"delay_min_samples": int(np.min(delays)) if len(delays) else None,
|
||||
"delay_median_samples": float(np.median(delays)) if len(delays) else None,
|
||||
"delay_max_samples": int(np.max(delays)) if len(delays) else None,
|
||||
"jump_candidates": jumps,
|
||||
"local_delay_discontinuity": bool(jumps),
|
||||
"local_delay_discontinuity_scope": (
|
||||
f"NO_IN_{len(valid)}_RELIABLE_WINDOWS; "
|
||||
f"UNDETERMINED_IN_{len(internal_low_correlation)}_INTERNAL_LOW_CORRELATION_WINDOWS"
|
||||
if not jumps else "DETECTED_IN_RELIABLE_WINDOWS"
|
||||
),
|
||||
"underrun_location": "NOT_LOCALIZED_FROM_RX" if not jumps else jumps[0],
|
||||
"reliable_region_inside_packet_samples": [RELIABLE_PACKET_START, RELIABLE_PACKET_END],
|
||||
"correlation_threshold": MIN_LOCAL_CORRELATION,
|
||||
"internal_low_correlation_windows": {
|
||||
"count": len(internal_low_correlation),
|
||||
"packet_counts": {
|
||||
str(packet): sum(row["packet_index"] == packet for row in internal_low_correlation)
|
||||
for packet in sorted({row["packet_index"] for row in internal_low_correlation})
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"window_index": row["window_index"],
|
||||
"packet_index": row["packet_index"],
|
||||
"tx_sample": row["tx_sample"],
|
||||
"rx_sample_selected_but_not_used_for_delay_verdict": row["rx_sample"],
|
||||
"position_inside_packet_samples": row["position_inside_packet_samples"],
|
||||
"normalized_correlation": row["normalized_correlation"],
|
||||
}
|
||||
for row in internal_low_correlation
|
||||
],
|
||||
"interpretation": (
|
||||
"Delay is not established in these windows; low correlation is itself a local anomaly, "
|
||||
"but it does not identify an underrun without a reliable timing discontinuity"
|
||||
),
|
||||
},
|
||||
"excluded_region_reason": (
|
||||
"Packet edges and repeated PRBS preamble have correlation aliases spaced by 2032 samples; "
|
||||
"22 additional internal windows are below the 0.15 correlation threshold. All are retained "
|
||||
"in CSV, and delay is not asserted for any excluded window"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def moments(values: np.ndarray) -> dict:
|
||||
values = np.asarray(values)
|
||||
mean = complex(np.mean(values, dtype=np.complex128))
|
||||
power = float(np.mean(abs(values.astype(np.complex128)) ** 2))
|
||||
ac_power = max(0.0, power - abs(mean) ** 2)
|
||||
return {
|
||||
"raw_power_code2": power,
|
||||
"rms_full_iq_code": math.sqrt(power),
|
||||
"mean_i": float(mean.real), "mean_q": float(mean.imag),
|
||||
"ac_power_code2": ac_power, "ac_rms_code": math.sqrt(ac_power),
|
||||
"max_abs_i": float(np.max(abs(values.real))),
|
||||
"max_abs_q": float(np.max(abs(values.imag))),
|
||||
"max_complex_magnitude": float(np.max(abs(values))),
|
||||
}
|
||||
|
||||
|
||||
def packet_quality(tx, rx, rx_start: int, background_ac_power: float,
|
||||
lab062_analysis: dict, alignment: list[dict]) -> list[dict]:
|
||||
rows = []
|
||||
physical = lab062_analysis["lab058_quality"]["packets"]
|
||||
for index in range(1, PACKETS + 1):
|
||||
values = rx.read(rx_start + (index - 1) * FRAME_SAMPLES, FRAME_SAMPLES)
|
||||
level = moments(values)
|
||||
metrics = physical[index - 1]["oracle"]["metrics"]
|
||||
channel = physical[index - 1]["channel"]
|
||||
local = [row for row in alignment if row["packet_index"] == index and row["reliable_for_delay"]]
|
||||
phases = np.unwrap([row["phase_rad"] for row in local]) if local else np.empty(0)
|
||||
row = {
|
||||
"packet_index": index,
|
||||
**level,
|
||||
"background_ac_power_code2": background_ac_power,
|
||||
"background_subtracted_useful_power_code2": max(0.0, level["ac_power_code2"] - background_ac_power),
|
||||
"symbol_error_rate": metrics["ber"],
|
||||
"symbol_rms_deviation": metrics["evm_rms"],
|
||||
"matched_component_power": metrics["matched_component_power"],
|
||||
"residual_power": metrics["residual_power"],
|
||||
"matched_residual_db": metrics["matched_residual_db"],
|
||||
"band_gain_nonuniformity_db": channel["amplitude_ripple_p95_p05_db"],
|
||||
"initial_frequency_hz": physical[index - 1]["oracle"]["cfo_hz"],
|
||||
"diagnostic_residual_phase_error_rad": channel["residual_phase_ripple_p95_p05_rad"],
|
||||
"local_delay_median_samples": float(np.median([item["local_delay_samples"] for item in local])),
|
||||
"local_delay_min_samples": int(min(item["local_delay_samples"] for item in local)),
|
||||
"local_delay_max_samples": int(max(item["local_delay_samples"] for item in local)),
|
||||
"local_correlation_median": float(np.median([item["normalized_correlation"] for item in local])),
|
||||
"local_correlation_min": float(min(item["normalized_correlation"] for item in local)),
|
||||
"local_gain_amplitude_median": float(np.median([item["gain_amplitude"] for item in local])),
|
||||
"local_phase_unwrapped_span_rad": float(np.ptp(phases)) if len(phases) else None,
|
||||
"ser_minus_lab056_b": metrics["ber"] - LAB056_B["symbol_error_rate"],
|
||||
"evm_minus_lab056_b": metrics["evm_rms"] - LAB056_B["evm_rms"],
|
||||
"matched_residual_minus_lab056_b_db": metrics["matched_residual_db"] - LAB056_B["matched_residual_db"],
|
||||
"ripple_minus_lab056_b_db": channel["amplitude_ripple_p95_p05_db"] - LAB056_B["band_ripple_db"],
|
||||
}
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
|
||||
def group_trends(rows: list[dict]) -> list[dict]:
|
||||
result = []
|
||||
for low in (1, 5, 9, 13):
|
||||
selected = rows[low - 1:low + 3]
|
||||
result.append({
|
||||
"packets": f"{low}-{low + 3}",
|
||||
"useful_power_mean": float(np.mean([row["background_subtracted_useful_power_code2"] for row in selected])),
|
||||
"symbol_error_rate_mean": float(np.mean([row["symbol_error_rate"] for row in selected])),
|
||||
"symbol_rms_deviation_mean": float(np.mean([row["symbol_rms_deviation"] for row in selected])),
|
||||
"matched_component_power_mean": float(np.mean([row["matched_component_power"] for row in selected])),
|
||||
"residual_power_mean": float(np.mean([row["residual_power"] for row in selected])),
|
||||
"matched_residual_db_mean": float(np.mean([row["matched_residual_db"] for row in selected])),
|
||||
"initial_frequency_hz_mean": float(np.mean([row["initial_frequency_hz"] for row in selected])),
|
||||
"diagnostic_residual_phase_error_rad_mean": float(np.mean(
|
||||
[row["diagnostic_residual_phase_error_rad"] for row in selected]
|
||||
)),
|
||||
"local_phase_unwrapped_span_rad_mean": float(np.mean(
|
||||
[row["local_phase_unwrapped_span_rad"] for row in selected]
|
||||
)),
|
||||
"local_gain_amplitude_median": float(np.median([row["local_gain_amplitude_median"] for row in selected])),
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def classify(packet_rows: list[dict], alignment: dict) -> dict:
|
||||
worse = [
|
||||
row for row in packet_rows
|
||||
if row["symbol_error_rate"] > LAB056_B["symbol_error_rate"]
|
||||
and row["symbol_rms_deviation"] > LAB056_B["evm_rms"]
|
||||
and row["matched_residual_db"] < LAB056_B["matched_residual_db"]
|
||||
]
|
||||
global_degradation = len(worse) >= 12
|
||||
discontinuity = alignment["local_delay_discontinuity"]
|
||||
if discontinuity and global_degradation:
|
||||
distribution, primary = "MIXED", "PARTIALLY_LOCALIZED"
|
||||
elif discontinuity:
|
||||
distribution, primary = "LOCALIZED", "PARTIALLY_LOCALIZED"
|
||||
elif global_degradation:
|
||||
distribution, primary = "GLOBAL", "PARTIALLY_LOCALIZED"
|
||||
else:
|
||||
distribution, primary = "NOT_LOCALIZED", "NOT_LOCALIZED"
|
||||
return {
|
||||
"quality_distribution": distribution,
|
||||
"primary_cause_status": primary,
|
||||
"packets_worse_than_lab056_on_ser_evm_and_matched_residual": [row["packet_index"] for row in worse],
|
||||
"global_degradation_rule": "at least 12/16 packets worse than Lab056 B on SER, EVM and matched/residual",
|
||||
"global_degradation": global_degradation,
|
||||
"local_delay_discontinuity": discontinuity,
|
||||
}
|
||||
|
||||
|
||||
def write_csv(path: Path, rows: list[dict]) -> None:
|
||||
with path.open("w", newline="", encoding="utf-8-sig") as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=list(rows[0]))
|
||||
writer.writeheader()
|
||||
for row in rows:
|
||||
writer.writerow({key: (json.dumps(value) if isinstance(value, (dict, list)) else value)
|
||||
for key, value in row.items()})
|
||||
|
||||
|
||||
def plots(alignment: list[dict], packets: list[dict]) -> None:
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
time_ms = np.asarray([row["time_seconds"] * 1000 for row in alignment])
|
||||
packet_indexes = np.arange(1, PACKETS + 1)
|
||||
|
||||
def line(name: str, values, ylabel: str, title: str) -> None:
|
||||
figure, axis = plt.subplots(figsize=(9, 4.5))
|
||||
axis.plot(time_ms, values, linewidth=1.2)
|
||||
axis.set(xlabel="Время TX, мс", ylabel=ylabel, title=title)
|
||||
axis.grid(True, alpha=0.3); figure.tight_layout()
|
||||
figure.savefig(OUTPUT / name, dpi=160); plt.close(figure)
|
||||
|
||||
figure, axis = plt.subplots(figsize=(9, 4.5))
|
||||
unreliable = [row for row in alignment if not row["reliable_for_delay"]]
|
||||
reliable = [row for row in alignment if row["reliable_for_delay"]]
|
||||
axis.scatter([row["time_seconds"] * 1000 for row in unreliable],
|
||||
[row["local_delay_samples"] for row in unreliable], s=7, alpha=0.25,
|
||||
label="исключено: края, преамбула или низкая корреляция")
|
||||
axis.plot([row["time_seconds"] * 1000 for row in reliable],
|
||||
[row["local_delay_samples"] for row in reliable], linewidth=1.2,
|
||||
label="надёжная внутренняя часть")
|
||||
axis.set(xlabel="Время TX, мс", ylabel="Локальная задержка, отсчёты",
|
||||
title="Локальная временная привязка TX и RX")
|
||||
axis.grid(True, alpha=0.3); axis.legend(); figure.tight_layout()
|
||||
figure.savefig(OUTPUT / "local_delay_vs_time.png", dpi=160); plt.close(figure)
|
||||
line("normalized_correlation_vs_time.png", [row["normalized_correlation"] for row in alignment],
|
||||
"Нормированная корреляция", "Устойчивость локального сопоставления")
|
||||
reliable_time_ms = np.asarray([row["time_seconds"] * 1000 for row in reliable])
|
||||
figure, axis = plt.subplots(figsize=(9, 4.5))
|
||||
axis.plot(reliable_time_ms, [row["gain_amplitude"] for row in reliable], linewidth=1.2)
|
||||
axis.set(xlabel="Время TX, мс", ylabel="|h|, коды RX / код TX",
|
||||
title="Локальная амплитуда канала в надёжных окнах")
|
||||
axis.grid(True, alpha=0.3); figure.tight_layout()
|
||||
figure.savefig(OUTPUT / "gain_vs_time.png", dpi=160); plt.close(figure)
|
||||
figure, axis = plt.subplots(figsize=(9, 4.5))
|
||||
phase = np.unwrap([row["phase_rad"] for row in reliable])
|
||||
axis.plot(reliable_time_ms, phase, linewidth=1.2)
|
||||
axis.set(xlabel="Время TX, мс", ylabel="Развёрнутая фаза, рад",
|
||||
title="Локальная фаза канала в надёжных окнах")
|
||||
axis.grid(True, alpha=0.3); figure.tight_layout()
|
||||
figure.savefig(OUTPUT / "phase_vs_time.png", dpi=160); plt.close(figure)
|
||||
|
||||
def bars(name: str, values, ylabel: str, title: str, baseline=None) -> None:
|
||||
figure, axis = plt.subplots(figsize=(9, 4.5))
|
||||
axis.bar(packet_indexes, values)
|
||||
if baseline is not None:
|
||||
axis.axhline(baseline, color="tab:red", linestyle="--", label="Lab056 B")
|
||||
axis.legend()
|
||||
axis.set(xlabel="Номер пакета", ylabel=ylabel, title=title, xticks=packet_indexes)
|
||||
axis.grid(True, axis="y", alpha=0.3); figure.tight_layout()
|
||||
figure.savefig(OUTPUT / name, dpi=160); plt.close(figure)
|
||||
|
||||
bars("received_power_by_packet.png", [row["background_subtracted_useful_power_code2"] for row in packets],
|
||||
"Полезная AC-мощность, код²", "Принятая мощность по пакетам")
|
||||
bars("symbol_error_rate_by_packet.png", [100 * row["symbol_error_rate"] for row in packets],
|
||||
"Ошибочные символы, %", "Диагностическая SER по пакетам", 100 * LAB056_B["symbol_error_rate"])
|
||||
bars("matched_residual_by_packet.png", [row["matched_residual_db"] for row in packets],
|
||||
"Matched/residual, дБ", "Полезная согласованная составляющая к остатку", LAB056_B["matched_residual_db"])
|
||||
|
||||
figure, axes = plt.subplots(2, 2, figsize=(11, 7))
|
||||
comparisons = [
|
||||
("SER, %", [100 * row["symbol_error_rate"] for row in packets], 100 * LAB056_B["symbol_error_rate"]),
|
||||
("EVM RMS", [row["symbol_rms_deviation"] for row in packets], LAB056_B["evm_rms"]),
|
||||
("Matched/residual, дБ", [row["matched_residual_db"] for row in packets], LAB056_B["matched_residual_db"]),
|
||||
("Неравномерность, дБ", [row["band_gain_nonuniformity_db"] for row in packets], LAB056_B["band_ripple_db"]),
|
||||
]
|
||||
for axis, (label, values, baseline) in zip(axes.flat, comparisons):
|
||||
axis.bar(packet_indexes, values); axis.axhline(baseline, color="tab:red", linestyle="--")
|
||||
axis.set(xlabel="Пакет", ylabel=label, xticks=packet_indexes); axis.grid(True, axis="y", alpha=0.3)
|
||||
figure.suptitle("Lab056 B и пакеты Lab062"); figure.tight_layout()
|
||||
figure.savefig(OUTPUT / "lab056_b_vs_lab062_packets.png", dpi=160); plt.close(figure)
|
||||
|
||||
|
||||
def local_counter_semantics() -> dict:
|
||||
binary = Path(r"C:\Users\user\Tools\HackRF\Library\bin\hackrf_transfer.exe")
|
||||
source_candidates = list(binary.parent.parent.rglob("hackrf_transfer.c")) if binary.parent.parent.exists() else []
|
||||
return {
|
||||
"binary_path": str(binary), "binary_exists": binary.exists(),
|
||||
"local_source_files": [str(path) for path in source_candidates],
|
||||
"progress_line": "1 underruns, longest 16672 bytes",
|
||||
"final_line": "0 underruns, longest 0 bytes",
|
||||
"counter_semantics": "NOT_ESTABLISHED" if not source_candidates else "LOCAL_SOURCE_REQUIRES_REVIEW",
|
||||
"note": "No local source or documentation was found that explains the reset between progress and final counters",
|
||||
}
|
||||
|
||||
|
||||
def run() -> dict:
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
lab062_analysis = json.loads((LAB062 / "rf_analysis.json").read_text(encoding="utf-8"))
|
||||
if lab062_analysis["status"] != "RF_SESSION_INVALID_TX_UNDERRUN":
|
||||
raise RuntimeError("Lab062 must remain INVALID_TX_UNDERRUN")
|
||||
tx, rx = lab058.Cs8File(TX_PATH), lab058.Cs8File(RX_PATH)
|
||||
rx_start = int(lab062_analysis["location"]["rx_start_sample"])
|
||||
gap = actual_gap()
|
||||
if not gap["identity_confirmed"] or gap["additional_inter_packet_gap_samples"] != 0:
|
||||
raise RuntimeError("Unexpected Lab062 TX accounting")
|
||||
scale = underrun_scale()
|
||||
alignment_rows = track_alignment(tx, rx, rx_start)
|
||||
alignment = alignment_verdict(alignment_rows, scale["complex_samples"])
|
||||
|
||||
before = moments(rx.read(rx_start - FRAME_SAMPLES, FRAME_SAMPLES))
|
||||
after = moments(rx.read(rx_start + tx.size, FRAME_SAMPLES))
|
||||
background_ac_power = 0.5 * (before["ac_power_code2"] + after["ac_power_code2"])
|
||||
packet_rows = packet_quality(tx, rx, rx_start, background_ac_power, lab062_analysis, alignment_rows)
|
||||
trends = group_trends(packet_rows)
|
||||
classification = classify(packet_rows, alignment)
|
||||
most_affected = sorted(
|
||||
packet_rows,
|
||||
key=lambda row: (row["symbol_error_rate"], row["symbol_rms_deviation"], -row["matched_residual_db"]),
|
||||
reverse=True,
|
||||
)[:4]
|
||||
selected = {3, 4, 12, 14}
|
||||
special = [row for row in packet_rows if row["packet_index"] in selected]
|
||||
special_common = {
|
||||
"packets": sorted(selected),
|
||||
"all_have_power_drop_below_median": all(
|
||||
row["background_subtracted_useful_power_code2"] < np.median(
|
||||
[item["background_subtracted_useful_power_code2"] for item in packet_rows]
|
||||
) for row in special
|
||||
),
|
||||
"all_have_correlation_below_median": all(
|
||||
row["local_correlation_median"] < np.median([item["local_correlation_median"] for item in packet_rows])
|
||||
for row in special
|
||||
),
|
||||
"all_have_large_delay_change": all(
|
||||
row["local_delay_max_samples"] - row["local_delay_min_samples"] >= scale["complex_samples"] // 4
|
||||
for row in special
|
||||
),
|
||||
"interpretation": "No common underrun signature unless one of all three booleans is true with localized alignment evidence",
|
||||
}
|
||||
result = {
|
||||
"lab": "Lab063", "status": "COMPLETED_OFFLINE", "rf_tx": 0,
|
||||
"hardware_queries": 0, "lab062_status_preserved": lab062_analysis["status"],
|
||||
"lab062_commit": "f8ae6c6bfceaa21b6c08dcf55d2bff996dd0dae5",
|
||||
"physical_device": "Auron HackRF Plus",
|
||||
"api_firmware_identification": "HackRF One r9",
|
||||
"integrity": {
|
||||
"tx_sha256": lab062.sha256(TX_PATH), "rx_sha256": lab062.sha256(RX_PATH),
|
||||
"tx_samples": tx.size, "rx_samples": rx.size,
|
||||
},
|
||||
"actual_inter_packet_gap": gap,
|
||||
"tx_underrun_scale": scale,
|
||||
"tx_underrun_log": local_counter_semantics(),
|
||||
"background": {"before": before, "after": after, "ac_power_code2_used": background_ac_power,
|
||||
"preflight_ac_rms_code": 0.9854828456229909},
|
||||
"local_alignment": alignment,
|
||||
"packet_quality": packet_rows,
|
||||
"group_trends": trends,
|
||||
"special_packets_3_4_12_14": special_common,
|
||||
"packets_most_affected": [row["packet_index"] for row in most_affected],
|
||||
"active_rx_level": {
|
||||
"ac_rms_min": min(row["ac_rms_code"] for row in packet_rows),
|
||||
"ac_rms_max": max(row["ac_rms_code"] for row in packet_rows),
|
||||
"max_abs_i_min": min(row["max_abs_i"] for row in packet_rows),
|
||||
"max_abs_i_max": max(row["max_abs_i"] for row in packet_rows),
|
||||
"max_abs_q_min": min(row["max_abs_q"] for row in packet_rows),
|
||||
"max_abs_q_max": max(row["max_abs_q"] for row in packet_rows),
|
||||
"mean_i_min": min(row["mean_i"] for row in packet_rows),
|
||||
"mean_i_max": max(row["mean_i"] for row in packet_rows),
|
||||
"mean_q_min": min(row["mean_q"] for row in packet_rows),
|
||||
"mean_q_max": max(row["mean_q"] for row in packet_rows),
|
||||
"local_gain_amplitude_median_min": min(row["local_gain_amplitude_median"] for row in packet_rows),
|
||||
"local_gain_amplitude_median_max": max(row["local_gain_amplitude_median"] for row in packet_rows),
|
||||
"analog_overload": "LESS_LIKELY_FROM_DIGITAL_LEVEL_BUT_NOT_EXCLUDED_WITHOUT_INSTRUMENT",
|
||||
},
|
||||
"lab056_comparison": {"reference": LAB056_B,
|
||||
"packets_worse_on_all_three_core_metrics": classification["packets_worse_than_lab056_on_ser_evm_and_matched_residual"]},
|
||||
**classification,
|
||||
"next_single_step": (
|
||||
"Before another TX, investigate the low active RX level, gain/geometry and possible analogue compression; "
|
||||
"also change TX streaming/logging so any underrun is unambiguous. Do not change RF parameters yet."
|
||||
if classification["quality_distribution"] == "GLOBAL" else
|
||||
"Improve TX streaming and underrun logging before any separately authorized repeat."
|
||||
),
|
||||
"lab063_commit": "NOT_PERFORMED", "push": "NOT_PERFORMED", "lab064": "NOT_CREATED",
|
||||
}
|
||||
write_csv(OUTPUT / "local_alignment.csv", alignment_rows)
|
||||
write_csv(OUTPUT / "packet_quality.csv", packet_rows)
|
||||
plots(alignment_rows, packet_rows)
|
||||
(OUTPUT / "summary.json").write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return result
|
||||
|
||||
|
||||
def run_functional_tests() -> None:
|
||||
scale = underrun_scale()
|
||||
assert scale["complex_samples"] == 8336
|
||||
assert abs(scale["duration_milliseconds"] - 1.042) < 1e-12
|
||||
assert scale["symbols"] == 521.0
|
||||
transmitted = np.exp(1j * np.arange(128) * 0.31)
|
||||
search = np.concatenate((np.zeros(37, complex), (0.4 + 0.2j) * transmitted, np.zeros(19, complex)))
|
||||
result = normalized_match(transmitted, search)
|
||||
assert result["offset"] == 37 and result["normalized_correlation"] > 0.999999
|
||||
|
||||
|
||||
def main() -> None:
|
||||
result = run()
|
||||
print(json.dumps({
|
||||
"status": result["status"],
|
||||
"actual_inter_packet_gap": result["actual_inter_packet_gap"],
|
||||
"tx_underrun_scale": result["tx_underrun_scale"],
|
||||
"local_alignment": result["local_alignment"],
|
||||
"packets_most_affected": result["packets_most_affected"],
|
||||
"active_rx_level": result["active_rx_level"],
|
||||
"quality_distribution": result["quality_distribution"],
|
||||
"primary_cause_status": result["primary_cause_status"],
|
||||
"next_single_step": result["next_single_step"],
|
||||
"rf_tx": result["rf_tx"],
|
||||
}, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
957
experiments/lab064_rf_level_linearity.py
Normal file
@@ -0,0 +1,957 @@
|
||||
"""Lab064: лестница цифровых уровней и измерение линейности RF-тракта.
|
||||
|
||||
Подготовка и анализ не используют пакетный приёмник. Аппаратный preflight
|
||||
работает только в режиме RX. Передача доступна отдельной командой с явным
|
||||
флагом и защищена одноразовым маркером.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import numpy as np
|
||||
from scipy import signal
|
||||
|
||||
from experiments import lab043_pluto_to_rtlsdr as shaping
|
||||
from experiments import lab054_hardware_control as lab054
|
||||
from experiments import lab055_energy_audit as energy
|
||||
from experiments import lab056_abab_energy as lab056
|
||||
from experiments import lab056_capture_runner as hardware
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RAW = ROOT / "data/raw/lab064"
|
||||
OUTPUT = ROOT / "data/processed/lab064"
|
||||
|
||||
SAMPLE_RATE = 8_000_000
|
||||
SYMBOL_RATE = 20_000
|
||||
SAMPLES_PER_SYMBOL = SAMPLE_RATE // SYMBOL_RATE
|
||||
CARRIER_HZ = 50_000
|
||||
NOMINAL_RMS_CODE = 44.0
|
||||
LEVELS = (("L0", -18.0), ("L1", -12.0), ("L2", -6.0), ("L3", 0.0))
|
||||
SEGMENT_SAMPLES = 800_000
|
||||
GAP_SAMPLES = 400_000
|
||||
LEADING_ZERO_SAMPLES = 800_000
|
||||
TRAILING_ZERO_SAMPLES = 800_000
|
||||
PRBS_SYMBOLS = SEGMENT_SAMPLES // SAMPLES_PER_SYMBOL - 42
|
||||
TRIM_SAMPLES = 21 * SAMPLES_PER_SYMBOL
|
||||
TX_SAMPLES = (
|
||||
LEADING_ZERO_SAMPLES
|
||||
+ len(LEVELS) * SEGMENT_SAMPLES
|
||||
+ (len(LEVELS) - 1) * GAP_SAMPLES
|
||||
+ TRAILING_ZERO_SAMPLES
|
||||
)
|
||||
RX_SECONDS = 2.0
|
||||
RX_SAMPLES = int(RX_SECONDS * SAMPLE_RATE)
|
||||
RX_LEAD_SECONDS = 0.5
|
||||
PREFLIGHT_SAMPLES = 800_000
|
||||
STRONG_AC_RMS_CODE = 20.0
|
||||
STRONG_COMPONENT_CODE = 100.0
|
||||
BACKGROUND_WINDOW_SAMPLES = 160_000
|
||||
|
||||
POINT = hardware.OperatingPoint(
|
||||
frequency_hz=1_400_000_000,
|
||||
physical_path="MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
path_confirmed=True,
|
||||
tx_antenna="existing 1400 MHz antenna near window",
|
||||
rx_antenna="existing 1400 MHz antenna on desk",
|
||||
distance_m=1.2,
|
||||
orientation="same polarization; fixed during preflight and RF session",
|
||||
visibility="LOS indoor",
|
||||
)
|
||||
|
||||
|
||||
def save_new(path: Path, value: dict) -> None:
|
||||
"""Записать новый JSON и не перезаписывать свидетельства опыта."""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("x", encoding="utf-8") as stream:
|
||||
json.dump(value, stream, ensure_ascii=False, indent=2, allow_nan=False)
|
||||
stream.write("\n")
|
||||
|
||||
|
||||
def save_text_new(path: Path, value: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("x", encoding="utf-8") as stream:
|
||||
stream.write(value)
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
while block := stream.read(1024 * 1024):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def parameters() -> dict:
|
||||
return {
|
||||
"rf_frequency_hz": POINT.frequency_hz,
|
||||
"sample_rate_hz": SAMPLE_RATE,
|
||||
"symbol_rate": SYMBOL_RATE,
|
||||
"samples_per_symbol": SAMPLES_PER_SYMBOL,
|
||||
"digital_carrier_hz": CARRIER_HZ,
|
||||
"rrc_rolloff": shaping.LAB042_RRC_ROLLOFF,
|
||||
"rrc_span_symbols": shaping.LAB042_RRC_SPAN_SYMBOLS,
|
||||
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
|
||||
"full_phy_packet": False,
|
||||
"tx_vga_db": 10,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": False,
|
||||
"bias_tee": False,
|
||||
"geometry": asdict(POINT),
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
"levels_db_relative_nominal": [level for _, level in LEVELS],
|
||||
}
|
||||
|
||||
|
||||
def transfer_command(path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
|
||||
return hardware.command(POINT, path, samples, transmit=transmit)
|
||||
|
||||
|
||||
def _complex_to_cs8(values: np.ndarray) -> np.ndarray:
|
||||
interleaved = np.empty(2 * len(values), dtype=np.int8)
|
||||
interleaved[::2] = np.clip(np.rint(values.real), -128, 127).astype(np.int8)
|
||||
interleaved[1::2] = np.clip(np.rint(values.imag), -128, 127).astype(np.int8)
|
||||
return interleaved
|
||||
|
||||
|
||||
def nominal_segment() -> np.ndarray:
|
||||
"""Один участок длиной 100 мс с прежними PRBS7, BPSK и RRC."""
|
||||
symbols = 2.0 * shaping.pilot_prbs7(PRBS_SYMBOLS) - 1.0
|
||||
waveform = shaping.shape_frame_like_lab042(symbols, SAMPLES_PER_SYMBOL)
|
||||
if len(waveform) != SEGMENT_SAMPLES:
|
||||
raise RuntimeError("RRC-участок не равен заранее заданным 100 мс")
|
||||
carrier = np.exp(2j * np.pi * CARRIER_HZ * np.arange(len(waveform)) / SAMPLE_RATE)
|
||||
waveform = waveform * carrier
|
||||
waveform *= NOMINAL_RMS_CODE / np.sqrt(np.mean(np.abs(waveform) ** 2))
|
||||
return waveform
|
||||
|
||||
|
||||
def level_segment(level_db: float) -> np.ndarray:
|
||||
scale = 10 ** (level_db / 20)
|
||||
return _complex_to_cs8(nominal_segment() * scale)
|
||||
|
||||
|
||||
def segment_metrics(interleaved: np.ndarray, level_db: float) -> dict:
|
||||
values = interleaved[::2].astype(np.float32) + 1j * interleaved[1::2].astype(np.float32)
|
||||
moments = energy.Moments()
|
||||
moments.add(values)
|
||||
result = moments.result(SYMBOL_RATE)
|
||||
result.update({
|
||||
"requested_relative_power_db": level_db,
|
||||
"tx_rms_code": result["rms_code"],
|
||||
"tx_peak_abs_i_code": result["max_abs_i"],
|
||||
"tx_peak_abs_q_code": result["max_abs_q"],
|
||||
"tx_peak_magnitude_code": result["peak_magnitude_code"],
|
||||
"digital_clipping": bool(result["clipped_samples_fraction"] > 0),
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def prepared_segments() -> tuple[list[dict], dict[str, np.ndarray]]:
|
||||
cursor = LEADING_ZERO_SAMPLES
|
||||
rows: list[dict] = []
|
||||
blocks: dict[str, np.ndarray] = {}
|
||||
for index, (label, level_db) in enumerate(LEVELS):
|
||||
block = level_segment(level_db)
|
||||
metrics = segment_metrics(block, level_db)
|
||||
rows.append({
|
||||
"label": label,
|
||||
"level_db": level_db,
|
||||
"start_sample": cursor,
|
||||
"samples": SEGMENT_SAMPLES,
|
||||
"duration_seconds": SEGMENT_SAMPLES / SAMPLE_RATE,
|
||||
"analysis_start_sample": cursor + TRIM_SAMPLES,
|
||||
"analysis_samples": SEGMENT_SAMPLES - 2 * TRIM_SAMPLES,
|
||||
"metrics": metrics,
|
||||
})
|
||||
blocks[label] = block
|
||||
cursor += SEGMENT_SAMPLES
|
||||
if index < len(LEVELS) - 1:
|
||||
cursor += GAP_SAMPLES
|
||||
if cursor + TRAILING_ZERO_SAMPLES != TX_SAMPLES:
|
||||
raise RuntimeError("Ошибка схемы TX waveform")
|
||||
nominal_power = rows[-1]["metrics"]["power_code2"]
|
||||
for row in rows:
|
||||
row["metrics"]["actual_relative_power_db"] = float(
|
||||
10 * np.log10(row["metrics"]["power_code2"] / nominal_power)
|
||||
)
|
||||
for lower, upper in zip(rows, rows[1:]):
|
||||
upper["tx_input_difference_from_previous_db"] = float(
|
||||
10 * np.log10(
|
||||
upper["metrics"]["power_code2"] / lower["metrics"]["power_code2"]
|
||||
)
|
||||
)
|
||||
return rows, blocks
|
||||
|
||||
|
||||
def prepare() -> dict:
|
||||
"""Создать единственный конечный TX-файл и его проверяемые метаданные."""
|
||||
RAW.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
tx_path = RAW / "tx_level_staircase.cs8"
|
||||
rows, blocks = prepared_segments()
|
||||
with tx_path.open("xb") as stream:
|
||||
stream.write(bytes(2 * LEADING_ZERO_SAMPLES))
|
||||
for index, (label, _) in enumerate(LEVELS):
|
||||
stream.write(blocks[label].tobytes())
|
||||
if index < len(LEVELS) - 1:
|
||||
stream.write(bytes(2 * GAP_SAMPLES))
|
||||
stream.write(bytes(2 * TRAILING_ZERO_SAMPLES))
|
||||
if tx_path.stat().st_size != 2 * TX_SAMPLES:
|
||||
raise RuntimeError("Неверная длина подготовленного TX")
|
||||
if any(row["metrics"]["digital_clipping"] for row in rows):
|
||||
raise RuntimeError("В подготовленном TX обнаружен цифровой clipping")
|
||||
steps = [row["tx_input_difference_from_previous_db"] for row in rows[1:]]
|
||||
if any(abs(step - 6.0) > 0.1 for step in steps):
|
||||
raise RuntimeError("Цифровая лестница не выдерживает шаг +6 дБ")
|
||||
result = {
|
||||
"lab": "Lab064",
|
||||
"stage": "PREPARATION_ONLY",
|
||||
"rf_tx_count": 0,
|
||||
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
|
||||
"parameters": parameters(),
|
||||
"segments": rows,
|
||||
"leading_zero_samples": LEADING_ZERO_SAMPLES,
|
||||
"gap_samples": GAP_SAMPLES,
|
||||
"trailing_zero_samples": TRAILING_ZERO_SAMPLES,
|
||||
"tx_samples": TX_SAMPLES,
|
||||
"tx_bytes": 2 * TX_SAMPLES,
|
||||
"tx_duration_seconds": TX_SAMPLES / SAMPLE_RATE,
|
||||
"tx_sha256": sha256(tx_path),
|
||||
"rx_samples": RX_SAMPLES,
|
||||
"rx_duration_seconds": RX_SECONDS,
|
||||
"rx_lead_seconds": RX_LEAD_SECONDS,
|
||||
"automatic_retry": False,
|
||||
"tx_command_prepared_not_run": transfer_command(tx_path, TX_SAMPLES, transmit=True),
|
||||
"rx_command_prepared_not_run": transfer_command(RAW / "rx_stream.cs8", RX_SAMPLES),
|
||||
}
|
||||
save_new(RAW / "tx_metadata.json", result)
|
||||
save_new(OUTPUT / "preparation.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def validate_prepared() -> dict:
|
||||
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||
path = RAW / "tx_level_staircase.cs8"
|
||||
rows, blocks = prepared_segments()
|
||||
digest = hashlib.sha256()
|
||||
digest.update(bytes(2 * LEADING_ZERO_SAMPLES))
|
||||
for index, (label, _) in enumerate(LEVELS):
|
||||
digest.update(blocks[label].tobytes())
|
||||
if index < len(LEVELS) - 1:
|
||||
digest.update(bytes(2 * GAP_SAMPLES))
|
||||
digest.update(bytes(2 * TRAILING_ZERO_SAMPLES))
|
||||
if (
|
||||
metadata.get("lab") != "Lab064"
|
||||
or metadata.get("stage") != "PREPARATION_ONLY"
|
||||
or metadata.get("rf_tx_count") != 0
|
||||
or metadata.get("parameters") != parameters()
|
||||
or metadata.get("segments") != rows
|
||||
or metadata.get("tx_samples") != TX_SAMPLES
|
||||
or path.stat().st_size != 2 * TX_SAMPLES
|
||||
or metadata.get("tx_sha256") != digest.hexdigest()
|
||||
or sha256(path) != digest.hexdigest()
|
||||
):
|
||||
raise RuntimeError("Подготовленный Lab064 TX или параметры изменились")
|
||||
return metadata
|
||||
|
||||
|
||||
def difference_db(new: float, old: float) -> float:
|
||||
if new <= 0 or old <= 0:
|
||||
raise ValueError("Для разности в дБ нужны положительные мощности")
|
||||
return float(10 * np.log10(new / old))
|
||||
|
||||
|
||||
def counter_observations(log: str) -> list[dict]:
|
||||
"""Сохранить все промежуточные и итоговые счётчики потерь из лога."""
|
||||
pattern = re.compile(
|
||||
r"(?<![\w.+-])(\d+)\s+(?:(rx|tx)\s+)?(overruns?|underruns?)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
return [
|
||||
{
|
||||
"count": int(match.group(1)),
|
||||
"role": (match.group(2) or "UNKNOWN").upper(),
|
||||
"kind": match.group(3).lower().rstrip("s"),
|
||||
"text": match.group(0),
|
||||
"character_offset": match.start(),
|
||||
}
|
||||
for match in pattern.finditer(log)
|
||||
]
|
||||
|
||||
|
||||
def counter_summary(log: str, expected_kind: str) -> dict:
|
||||
observations = counter_observations(log)
|
||||
selected = [row for row in observations if row["kind"] == expected_kind]
|
||||
unparsed = re.search(
|
||||
r"overrun|underrun",
|
||||
re.sub(
|
||||
r"(?<![\w.+-])\d+\s+(?:(?:rx|tx)\s+)?(?:overruns?|underruns?)\b",
|
||||
"",
|
||||
log,
|
||||
flags=re.IGNORECASE,
|
||||
),
|
||||
re.IGNORECASE,
|
||||
)
|
||||
return {
|
||||
"expected_kind": expected_kind,
|
||||
"observations": observations,
|
||||
"selected_counts": [row["count"] for row in selected],
|
||||
"final_count": selected[-1]["count"] if selected else None,
|
||||
"maximum_count": max((row["count"] for row in selected), default=None),
|
||||
"unparsed_loss_text": bool(unparsed),
|
||||
"clean": bool(selected) and not unparsed and all(row["count"] == 0 for row in observations),
|
||||
}
|
||||
|
||||
|
||||
def synthetic_linearity_gate() -> dict:
|
||||
"""Проверить метрики на известном линейном канале без аппаратуры."""
|
||||
rows, blocks = prepared_segments()
|
||||
random_generator = np.random.default_rng(64064)
|
||||
measured = []
|
||||
for row in rows:
|
||||
block = blocks[row["label"]]
|
||||
reference = block[::2].astype(np.float64) + 1j * block[1::2].astype(np.float64)
|
||||
reference = reference[TRIM_SAMPLES:-TRIM_SAMPLES]
|
||||
noise = random_generator.normal(0, 0.03, len(reference)) + 1j * random_generator.normal(
|
||||
0, 0.03, len(reference)
|
||||
)
|
||||
received = 0.37 * np.exp(0.41j) * reference + (0.8 - 0.3j) + noise
|
||||
fitted = energy.fit_channel(reference, received, SAMPLE_RATE)
|
||||
measured.append({
|
||||
"label": row["label"],
|
||||
"matched_signal_power": fitted["heldout_signal_power"],
|
||||
"residual_power": fitted["heldout_residual_power"],
|
||||
"gain_abs": fitted["gain_abs"],
|
||||
})
|
||||
step_errors = []
|
||||
for index in range(1, len(rows)):
|
||||
tx_step = rows[index]["tx_input_difference_from_previous_db"]
|
||||
rx_step = difference_db(
|
||||
measured[index]["matched_signal_power"],
|
||||
measured[index - 1]["matched_signal_power"],
|
||||
)
|
||||
step_errors.append(rx_step - tx_step)
|
||||
passed = max(abs(error) for error in step_errors) < 0.02
|
||||
return {
|
||||
"lab": "Lab064",
|
||||
"stage": "SOFTWARE_LINEARITY_GATE",
|
||||
"rf_tx_count": 0,
|
||||
"channel_model": "known linear complex gain plus DC and deterministic Gaussian noise",
|
||||
"rows": measured,
|
||||
"linearity_errors_db": step_errors,
|
||||
"passed": passed,
|
||||
}
|
||||
|
||||
|
||||
def software_gate() -> dict:
|
||||
metadata = validate_prepared()
|
||||
result = synthetic_linearity_gate()
|
||||
result["tx_sha256"] = metadata["tx_sha256"]
|
||||
save_new(OUTPUT / "software_gate.json", result)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab064 software gate FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def locate_waveform(tx: energy.Cs8File, rx: energy.Cs8File) -> dict:
|
||||
"""Найти всю лестницу по прореженной диагностической корреляции."""
|
||||
stride = 32
|
||||
|
||||
def decimated(source: energy.Cs8File) -> np.ndarray:
|
||||
chunks = [
|
||||
source.read(start, min(262_144, source.size - start))[::stride]
|
||||
for start in range(0, source.size, 262_144)
|
||||
]
|
||||
return np.concatenate(chunks).astype(np.complex128)
|
||||
|
||||
reference = decimated(tx)
|
||||
received = decimated(rx)
|
||||
if len(received) < len(reference):
|
||||
raise ValueError("RX короче TX waveform")
|
||||
reference -= reference.mean()
|
||||
received -= received.mean()
|
||||
reference_energy = float(np.sum(np.abs(reference) ** 2))
|
||||
cumulative = np.concatenate(([0.0], np.cumsum(np.abs(received) ** 2)))
|
||||
window_energy = cumulative[len(reference):] - cumulative[:-len(reference)]
|
||||
time_axis = np.arange(len(reference)) * stride / SAMPLE_RATE
|
||||
best = (-1.0, 0, 0.0)
|
||||
for frequency_hz in np.arange(-3.0, 3.01, 0.25):
|
||||
rotated = reference * np.exp(2j * np.pi * frequency_hz * time_axis)
|
||||
correlation = signal.fftconvolve(received, np.conj(rotated[::-1]), mode="valid")
|
||||
scores = np.abs(correlation) / np.sqrt(
|
||||
np.maximum(reference_energy * window_energy, 1e-30)
|
||||
)
|
||||
index = int(np.argmax(scores))
|
||||
if scores[index] > best[0]:
|
||||
best = (float(scores[index]), index * stride, float(frequency_hz))
|
||||
if best[0] < 0.05:
|
||||
raise RuntimeError("Лестница уровней не локализована; измерение UNKNOWN")
|
||||
return {"start_sample": best[1], "correlation": best[0], "coarse_cfo_hz": best[2], "stride": stride}
|
||||
|
||||
|
||||
def _window_metrics(values: np.ndarray) -> dict:
|
||||
moments = energy.Moments()
|
||||
moments.add(values)
|
||||
result = moments.result(SYMBOL_RATE)
|
||||
result["ac_rms_code"] = float(np.sqrt(max(0.0, result["ac_power_code2"])))
|
||||
return result
|
||||
|
||||
|
||||
def background_windows() -> list[dict]:
|
||||
"""Окна внутри всех нулевых участков, вдали от активных границ."""
|
||||
starts = (
|
||||
("INITIAL_GUARD", 320_000),
|
||||
("GAP_L0_L1", 1_760_000),
|
||||
("GAP_L1_L2", 2_960_000),
|
||||
("GAP_L2_L3", 4_160_000),
|
||||
("FINAL_GUARD", 5_440_000),
|
||||
)
|
||||
return [
|
||||
{
|
||||
"label": label,
|
||||
"tx_relative_start_sample": start,
|
||||
"samples": BACKGROUND_WINDOW_SAMPLES,
|
||||
"duration_seconds": BACKGROUND_WINDOW_SAMPLES / SAMPLE_RATE,
|
||||
}
|
||||
for label, start in starts
|
||||
]
|
||||
|
||||
|
||||
def summarize_background(rows: list[dict]) -> dict:
|
||||
powers = [row["metrics"]["ac_power_code2"] for row in rows]
|
||||
rms_values = [row["metrics"]["ac_rms_code"] for row in rows]
|
||||
variation_db = difference_db(max(powers), min(powers))
|
||||
return {
|
||||
"ac_power_min_code2": min(powers),
|
||||
"ac_power_max_code2": max(powers),
|
||||
"ac_rms_min_code": min(rms_values),
|
||||
"ac_rms_max_code": max(rms_values),
|
||||
"maximum_to_minimum_ac_power_db": variation_db,
|
||||
"stability_rule_db": 3.0,
|
||||
"stable": variation_db <= 3.0,
|
||||
}
|
||||
|
||||
|
||||
def residual_spectral_diagnostics(
|
||||
reference: np.ndarray,
|
||||
received: np.ndarray,
|
||||
cfo_hz: float,
|
||||
complex_gain: dict,
|
||||
) -> dict:
|
||||
"""Оценить паразитные полосы после вычитания согласованной составляющей."""
|
||||
rotated = reference * np.exp(2j * np.pi * cfo_hz * np.arange(len(reference)) / SAMPLE_RATE)
|
||||
rotated -= rotated.mean()
|
||||
centered_received = received - received.mean()
|
||||
gain = complex(complex_gain["real"], complex_gain["imag"])
|
||||
residual = centered_received - gain * rotated
|
||||
frequencies, density = energy.spectrum(residual)
|
||||
half_width = 0.675 * SYMBOL_RATE
|
||||
bands = {}
|
||||
for harmonic in (1, 2, 3):
|
||||
center = harmonic * CARRIER_HZ
|
||||
bands[f"residual_band_h{harmonic}_power_code2"] = energy.band_power(
|
||||
frequencies, density, center - half_width, center + half_width
|
||||
)
|
||||
bands["interpretation"] = (
|
||||
"Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
|
||||
)
|
||||
return bands
|
||||
|
||||
|
||||
def classify_linearity(
|
||||
rows: list[dict], comparisons: list[dict], background_summary: dict
|
||||
) -> dict:
|
||||
errors = [row["linearity_error_db"] for row in comparisons]
|
||||
l3_ratio = rows[-1]["matched_to_residual_db"]
|
||||
clipping = any(row["full_iq"]["clipped_samples_fraction"] > 0 for row in rows)
|
||||
if clipping:
|
||||
case = "INVALID_RX_CLIPPING"
|
||||
elif comparisons[-1]["linearity_error_db"] <= -1.5:
|
||||
case = "COMPRESSION"
|
||||
elif (
|
||||
max(abs(value) for value in errors) <= 1.0
|
||||
and l3_ratio < 3.0
|
||||
and background_summary["stable"]
|
||||
):
|
||||
case = "LINEAR_BUT_WEAK"
|
||||
elif (
|
||||
max(abs(value) for value in errors) <= 1.0
|
||||
and l3_ratio >= 6.0
|
||||
and background_summary["stable"]
|
||||
):
|
||||
case = "HEALTHY_LINEAR_RANGE"
|
||||
else:
|
||||
case = "NOT_LOCALIZED"
|
||||
return {
|
||||
"case": case,
|
||||
"rules_fixed_before_rf": {
|
||||
"linear_max_abs_error_db": 1.0,
|
||||
"compression_upper_step_error_at_most_db": -1.5,
|
||||
"weak_l3_matched_to_residual_below_db": 3.0,
|
||||
"healthy_l3_matched_to_residual_at_least_db": 6.0,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def capture_validity(capture: dict) -> dict:
|
||||
"""Не дать последнему нулевому счётчику скрыть промежуточную потерю."""
|
||||
reasons = []
|
||||
for role, key in (("TX", "tx_counter_summary"), ("RX", "rx_counter_summary")):
|
||||
summary = capture.get(key, {})
|
||||
if summary.get("clean") is not True:
|
||||
reasons.append(f"{role}_COUNTER_NOT_CLEAN")
|
||||
if summary.get("maximum_count") != 0:
|
||||
reasons.append(f"{role}_MAXIMUM_COUNTER_NOT_ZERO")
|
||||
if capture.get("passed") is not True:
|
||||
reasons.append("CAPTURE_RUNNER_NOT_PASSED")
|
||||
if capture.get("rx_clipping") is not False:
|
||||
reasons.append("RX_CLIPPING_NOT_EXCLUDED")
|
||||
return {"valid": not reasons, "invalid_reasons": reasons}
|
||||
|
||||
|
||||
def analyze_capture() -> dict:
|
||||
"""Проанализировать сохранённый RX только как известный измерительный сигнал."""
|
||||
metadata = validate_prepared()
|
||||
rx_path = RAW / "rx_stream.cs8"
|
||||
capture = json.loads((RAW / "capture_result.json").read_text(encoding="utf-8"))
|
||||
if capture.get("rf_tx_count") != 1 or capture.get("rx_sha256") != sha256(rx_path):
|
||||
raise RuntimeError("Нет подтверждённой единственной RX-записи Lab064")
|
||||
tx = energy.Cs8File(RAW / "tx_level_staircase.cs8")
|
||||
rx = energy.Cs8File(rx_path)
|
||||
location = locate_waveform(tx, rx)
|
||||
background_rows = []
|
||||
for window in background_windows():
|
||||
rx_start = location["start_sample"] + window["tx_relative_start_sample"]
|
||||
background_rows.append({
|
||||
**window,
|
||||
"rx_start_sample": rx_start,
|
||||
"metrics": _window_metrics(rx.read(rx_start, window["samples"])),
|
||||
})
|
||||
background_by_label = {row["label"]: row for row in background_rows}
|
||||
preceding_background = {
|
||||
"L0": "INITIAL_GUARD",
|
||||
"L1": "GAP_L0_L1",
|
||||
"L2": "GAP_L1_L2",
|
||||
"L3": "GAP_L2_L3",
|
||||
}
|
||||
rows = []
|
||||
for segment in metadata["segments"]:
|
||||
tx_start = segment["start_sample"]
|
||||
nominal_rx_start = location["start_sample"] + tx_start
|
||||
fitted = energy.analyze_rx(
|
||||
lab056.OffsetSource(tx, tx_start),
|
||||
rx,
|
||||
[nominal_rx_start],
|
||||
SEGMENT_SAMPLES,
|
||||
SAMPLES_PER_SYMBOL,
|
||||
)["frames"][0]
|
||||
actual_start = fitted["start"]
|
||||
full_values = rx.read(actual_start, SEGMENT_SAMPLES)
|
||||
active_reference = tx.read(tx_start + TRIM_SAMPLES, SEGMENT_SAMPLES - 2 * TRIM_SAMPLES)
|
||||
active_received = rx.read(actual_start + TRIM_SAMPLES, SEGMENT_SAMPLES - 2 * TRIM_SAMPLES)
|
||||
complex_gain = lab056.complex_gain(active_reference, active_received, fitted["cfo_hz"])
|
||||
matched = fitted["heldout_signal_power"]
|
||||
residual = fitted["heldout_residual_power"]
|
||||
background = background_by_label[preceding_background[segment["label"]]]
|
||||
rows.append({
|
||||
"label": segment["label"],
|
||||
"level_db": segment["level_db"],
|
||||
"rx_start_sample": actual_start,
|
||||
"alignment_adjustment_samples": fitted["alignment_adjustment"],
|
||||
"full_iq": _window_metrics(full_values),
|
||||
"background_label": background["label"],
|
||||
"background": background["metrics"],
|
||||
"background_window_start_sample": background["rx_start_sample"],
|
||||
"matched_signal_power": matched,
|
||||
"matched_signal_amplitude_rms": float(np.sqrt(matched)),
|
||||
"residual_power": residual,
|
||||
"residual_to_matched_amplitude_ratio": float(np.sqrt(residual / matched)),
|
||||
"matched_to_residual_db": difference_db(matched, residual),
|
||||
"complex_gain_h": complex_gain,
|
||||
"gain_abs": fitted["gain_abs"],
|
||||
"gain_power_abs_h_squared": fitted["gain_abs"] ** 2,
|
||||
"cfo_hz": fitted["cfo_hz"],
|
||||
"residual_spectrum": residual_spectral_diagnostics(
|
||||
active_reference, active_received, fitted["cfo_hz"], complex_gain
|
||||
),
|
||||
})
|
||||
comparisons = []
|
||||
for index in range(1, len(rows)):
|
||||
tx_difference = metadata["segments"][index]["tx_input_difference_from_previous_db"]
|
||||
rx_difference = difference_db(
|
||||
rows[index]["matched_signal_power"], rows[index - 1]["matched_signal_power"]
|
||||
)
|
||||
comparisons.append({
|
||||
"comparison": f"{rows[index]['label']}/{rows[index - 1]['label']}",
|
||||
"tx_input_difference_db": tx_difference,
|
||||
"rx_matched_power_difference_db": rx_difference,
|
||||
"linearity_error_db": rx_difference - tx_difference,
|
||||
"tx_amplitude_ratio": float(10 ** (tx_difference / 20)),
|
||||
"rx_matched_amplitude_ratio": float(10 ** (rx_difference / 20)),
|
||||
})
|
||||
total_tx_difference = difference_db(
|
||||
metadata["segments"][-1]["metrics"]["power_code2"],
|
||||
metadata["segments"][0]["metrics"]["power_code2"],
|
||||
)
|
||||
total_rx_difference = difference_db(
|
||||
rows[-1]["matched_signal_power"], rows[0]["matched_signal_power"]
|
||||
)
|
||||
total_comparison = {
|
||||
"comparison": "L3/L0",
|
||||
"tx_digital_power_difference_db": total_tx_difference,
|
||||
"rx_matched_power_difference_db": total_rx_difference,
|
||||
"total_linearity_error_db": total_rx_difference - total_tx_difference,
|
||||
"tx_amplitude_ratio": float(10 ** (total_tx_difference / 20)),
|
||||
"rx_matched_amplitude_ratio": float(10 ** (total_rx_difference / 20)),
|
||||
}
|
||||
background_summary = summarize_background(background_rows)
|
||||
validity = capture_validity(capture)
|
||||
classification = (
|
||||
classify_linearity(rows, comparisons, background_summary)
|
||||
if validity["valid"]
|
||||
else {
|
||||
"case": "INVALID_CAPTURE",
|
||||
"invalid_reasons": validity["invalid_reasons"],
|
||||
"diagnostic_metrics_only": True,
|
||||
"a_b_c_classification_permitted": False,
|
||||
}
|
||||
)
|
||||
result = {
|
||||
"lab": "Lab064",
|
||||
"stage": "OFFLINE_RF_LEVEL_LINEARITY_ANALYSIS",
|
||||
"packet_receiver_used": False,
|
||||
"location": location,
|
||||
"rows": rows,
|
||||
"background_windows": background_rows,
|
||||
"background_summary": background_summary,
|
||||
"comparisons": comparisons,
|
||||
"total_comparison": total_comparison,
|
||||
"classification": classification,
|
||||
"capture_valid": validity["valid"],
|
||||
"capture_invalid_reasons": validity["invalid_reasons"],
|
||||
"tx_underruns_maximum": capture.get("tx_counter_summary", {}).get("maximum_count"),
|
||||
"tx_underruns_final": capture.get("tx_counter_summary", {}).get("final_count"),
|
||||
"rx_overruns_maximum": capture.get("rx_counter_summary", {}).get("maximum_count"),
|
||||
"rx_overruns_final": capture.get("rx_counter_summary", {}).get("final_count"),
|
||||
"rx_clipping": any(row["full_iq"]["clipped_samples_fraction"] > 0 for row in rows),
|
||||
}
|
||||
save_new(OUTPUT / "rf_result.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def preflight(*, runner=None, process_checker=None, clock_checker=None, sleep_function=time.sleep) -> dict:
|
||||
"""Проверить common clock и 0,1 с RX-фона, не создавая TX-команду."""
|
||||
metadata = validate_prepared()
|
||||
software = json.loads((OUTPUT / "software_gate.json").read_text(encoding="utf-8"))
|
||||
if not software.get("passed") or software.get("tx_sha256") != metadata["tx_sha256"]:
|
||||
raise RuntimeError("Нет действующего Lab064 software gate")
|
||||
save_new(RAW / "preflight_attempt.json", {
|
||||
"lab": "Lab064",
|
||||
"rf_tx_count": 0,
|
||||
"point": asdict(POINT),
|
||||
"parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
|
||||
def rx_only(command: list[str]):
|
||||
if "-t" in command:
|
||||
raise PermissionError("TX запрещён в Lab064 preflight")
|
||||
return actual_runner(command)
|
||||
|
||||
result = {
|
||||
"lab": "Lab064",
|
||||
"stage": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT",
|
||||
"passed": False,
|
||||
"rf_tx_count": 0,
|
||||
"stop_before_tx": True,
|
||||
"point": asdict(POINT),
|
||||
"parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
}
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(rx_only)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT,
|
||||
rx_only,
|
||||
lab054.clock.TransmitGuard(),
|
||||
sleep_function=sleep_function,
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
path = RAW / "preflight_rx.cs8"
|
||||
if path.exists():
|
||||
raise FileExistsError("Lab064 preflight RX уже существует")
|
||||
captured = rx_only(transfer_command(path, PREFLIGHT_SAMPLES))
|
||||
stdout = captured.stdout or ""
|
||||
stderr = captured.stderr or ""
|
||||
result["rx_process"] = {"returncode": captured.returncode, "stdout": stdout, "stderr": stderr}
|
||||
result["rx_counter_summary"] = counter_summary(stdout + stderr, "overrun")
|
||||
if captured.returncode or not result["rx_counter_summary"]["clean"]:
|
||||
raise RuntimeError("RX preflight: процесс или счётчик overruns не прошёл")
|
||||
if path.stat().st_size != 2 * PREFLIGHT_SAMPLES:
|
||||
raise RuntimeError("Неверная длина Lab064 preflight RX")
|
||||
level = hardware.receive_level(path)
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
level["symbol_energy"] = "NOT_APPLICABLE_TO_BACKGROUND"
|
||||
level["ac_rms_code"] = float(np.sqrt(max(0.0, level["ac_power_code2"])))
|
||||
level["unexpected_strong_signal"] = bool(
|
||||
level["ac_rms_code"] >= STRONG_AC_RMS_CODE
|
||||
or level["max_abs_i"] >= STRONG_COMPONENT_CODE
|
||||
or level["max_abs_q"] >= STRONG_COMPONENT_CODE
|
||||
)
|
||||
level["strong_signal_rule"] = (
|
||||
f"AC RMS >= {STRONG_AC_RMS_CODE} code or max |I|/|Q| >= {STRONG_COMPONENT_CODE} code"
|
||||
)
|
||||
result["rx_level"] = level
|
||||
result["rx_sha256"] = sha256(path)
|
||||
result["rx_overruns"] = result["rx_counter_summary"]["final_count"]
|
||||
if level["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping; STOP без изменения gain")
|
||||
if level["unexpected_strong_signal"]:
|
||||
raise RuntimeError("Неожиданно сильный RX-only фон; STOP без изменения gain")
|
||||
result["passed"] = True
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = lab054.disable_clock_outputs(rx_only) if clock_started else []
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "preflight_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "preflight_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab064 preflight cleanup FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def require_preflight(metadata: dict) -> dict:
|
||||
record = json.loads((RAW / "preflight_result.json").read_text(encoding="utf-8"))
|
||||
if (
|
||||
record.get("passed") is not True
|
||||
or record.get("rf_tx_count") != 0
|
||||
or record.get("point") != asdict(POINT)
|
||||
or record.get("parameters") != parameters()
|
||||
or record.get("tx_sha256") != metadata["tx_sha256"]
|
||||
):
|
||||
raise PermissionError("Нет успешного RX-only preflight для этой конфигурации Lab064")
|
||||
hardware.require_clock_pass(record.get("clock", {}))
|
||||
level = record.get("rx_level", {})
|
||||
if (
|
||||
level.get("saturation_detected") is not False
|
||||
or level.get("unexpected_strong_signal") is not False
|
||||
or record.get("rx_overruns") != 0
|
||||
):
|
||||
raise PermissionError("RX-only preflight не разрешает передачу")
|
||||
return record
|
||||
|
||||
|
||||
def capture_once(
|
||||
*,
|
||||
authorization: bool = False,
|
||||
runner=None,
|
||||
process_starter=None,
|
||||
process_checker=None,
|
||||
clock_checker=None,
|
||||
sleep_function=time.sleep,
|
||||
) -> dict:
|
||||
"""Один RX-first сеанс; любая попытка расходуется до обращения к SDR."""
|
||||
if authorization is not True:
|
||||
raise PermissionError("Нужно отдельное разрешение на единственный RF-сеанс Lab064")
|
||||
metadata = validate_prepared()
|
||||
require_preflight(metadata)
|
||||
receive_path = RAW / "rx_stream.cs8"
|
||||
if receive_path.exists() or (RAW / "capture_result.json").exists():
|
||||
raise FileExistsError("Аппаратный результат Lab064 уже существует")
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_starter = process_starter or lab054.clock._default_process_starter
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
save_new(RAW / "hardware_attempt.json", {
|
||||
"lab": "Lab064",
|
||||
"authorized_once": True,
|
||||
"automatic_retry": False,
|
||||
"point": asdict(POINT),
|
||||
"parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
result = {
|
||||
"lab": "Lab064",
|
||||
"stage": "SINGLE_RF_LEVEL_CAPTURE",
|
||||
"passed": False,
|
||||
"rf_tx_count": 0,
|
||||
"automatic_tx_repeats": 0,
|
||||
"point": asdict(POINT),
|
||||
"parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
}
|
||||
guard = lab054.clock.TransmitGuard()
|
||||
process = None
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(actual_runner)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT, actual_runner, guard, sleep_function=sleep_function
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
process = process_starter(
|
||||
transfer_command(receive_path, RX_SAMPLES),
|
||||
RAW / "rx_stdout.txt",
|
||||
RAW / "rx_stderr.txt",
|
||||
)
|
||||
sleep_function(RX_LEAD_SECONDS)
|
||||
if process.poll() is not None:
|
||||
raise RuntimeError("RX завершился до TX")
|
||||
before_tx = hardware.receive_level(receive_path, tail_samples=65_536)
|
||||
before_tx.pop("symbol_energy_code2_seconds", None)
|
||||
result["rx_level_before_tx"] = before_tx
|
||||
if before_tx["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping до TX; RF TX = 0")
|
||||
guard.register_transmit()
|
||||
result["rf_tx_count"] = guard.transmit_calls
|
||||
transmitted = actual_runner(
|
||||
transfer_command(RAW / "tx_level_staircase.cs8", TX_SAMPLES, transmit=True)
|
||||
)
|
||||
tx_stdout = transmitted.stdout or ""
|
||||
tx_stderr = transmitted.stderr or ""
|
||||
save_text_new(RAW / "tx_stdout.txt", tx_stdout)
|
||||
save_text_new(RAW / "tx_stderr.txt", tx_stderr)
|
||||
result["tx_process"] = {
|
||||
"returncode": transmitted.returncode,
|
||||
"stdout_file": "tx_stdout.txt",
|
||||
"stderr_file": "tx_stderr.txt",
|
||||
}
|
||||
result["tx_counter_summary"] = counter_summary(tx_stdout + tx_stderr, "underrun")
|
||||
if transmitted.returncode:
|
||||
raise RuntimeError("Единственный TX завершился ошибкой; повтор запрещён")
|
||||
if process.wait(RX_SECONDS + 10):
|
||||
raise RuntimeError("RX завершился ошибкой; передача уже выполнена, повтор запрещён")
|
||||
process.close_logs()
|
||||
result["rx_samples"] = receive_path.stat().st_size // 2
|
||||
result["rx_bytes"] = receive_path.stat().st_size
|
||||
result["rx_duration_seconds"] = result["rx_samples"] / SAMPLE_RATE
|
||||
if result["rx_bytes"] != 2 * RX_SAMPLES:
|
||||
raise RuntimeError("Повреждённая длина RX; повтор запрещён")
|
||||
rx_stdout = (RAW / "rx_stdout.txt").read_text(encoding="utf-8")
|
||||
rx_stderr = (RAW / "rx_stderr.txt").read_text(encoding="utf-8")
|
||||
result["rx_counter_summary"] = counter_summary(rx_stdout + rx_stderr, "overrun")
|
||||
result["rx_sha256"] = sha256(receive_path)
|
||||
level = hardware.receive_level(receive_path)
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
result["rx_level"] = level
|
||||
result["rx_clipping"] = bool(level["saturation_detected"])
|
||||
if not result["tx_counter_summary"]["clean"]:
|
||||
raise RuntimeError("TX underrun или неоднозначный счётчик; результат сохранён, повтор запрещён")
|
||||
if not result["rx_counter_summary"]["clean"]:
|
||||
raise RuntimeError("RX overrun или неоднозначный счётчик; результат сохранён, повтор запрещён")
|
||||
if result["rx_clipping"]:
|
||||
raise RuntimeError("RX clipping в записи; результат сохранён, повтор запрещён")
|
||||
result["passed"] = True
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = []
|
||||
if process is not None:
|
||||
try:
|
||||
process.terminate()
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
if clock_started:
|
||||
errors.extend(lab054.disable_clock_outputs(actual_runner))
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "capture_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "capture_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab064 RF-сеанс завершён с ошибкой; повтор запрещён")
|
||||
return result
|
||||
|
||||
|
||||
def run_functional_tests() -> None:
|
||||
assert SAMPLES_PER_SYMBOL == 400
|
||||
assert PRBS_SYMBOLS == 1958
|
||||
assert TX_SAMPLES == 6_000_000
|
||||
rows, _ = prepared_segments()
|
||||
assert len(rows) == 4
|
||||
assert all(not row["metrics"]["digital_clipping"] for row in rows)
|
||||
assert max(abs(row["tx_input_difference_from_previous_db"] - 6.0) for row in rows[1:]) < 0.1
|
||||
assert synthetic_linearity_gate()["passed"]
|
||||
assert "-t" not in transfer_command("NUL", 65_536)
|
||||
try:
|
||||
capture_once(authorization=False)
|
||||
except PermissionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("RF без отдельного разрешения")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--stage",
|
||||
choices=("prepare", "software", "preflight", "capture", "analyze"),
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument("--authorize-one-rf", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
if arguments.stage == "prepare":
|
||||
result = prepare()
|
||||
elif arguments.stage == "software":
|
||||
result = software_gate()
|
||||
elif arguments.stage == "preflight":
|
||||
result = preflight()
|
||||
elif arguments.stage == "capture":
|
||||
result = capture_once(authorization=arguments.authorize_one_rf)
|
||||
else:
|
||||
result = analyze_capture()
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False))
|
||||
print("RF TX = 0")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
623
experiments/lab065_rf_level_linearity.py
Normal file
@@ -0,0 +1,623 @@
|
||||
"""Lab065: та же лестница уровней при TX VGA 20 дБ.
|
||||
|
||||
Подготовка, программная проверка и preflight не передают RF. Единственный
|
||||
аппаратный сеанс доступен только отдельной командой с явным разрешением.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import numpy as np
|
||||
|
||||
from experiments import lab054_hardware_control as lab054
|
||||
from experiments import lab056_capture_runner as hardware
|
||||
from experiments import lab064_rf_level_linearity as lab064
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RAW = ROOT / "data/raw/lab065"
|
||||
OUTPUT = ROOT / "data/processed/lab065"
|
||||
LAB064_RAW = ROOT / "data/raw/lab064"
|
||||
LAB064_RESULT = ROOT / "data/processed/lab064/rf_result.json"
|
||||
EXPECTED_TX_SHA256 = "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9"
|
||||
TX_VGA_DB = 20
|
||||
TX_VGA_DIFFERENCE_DB = 10.0
|
||||
MEANINGFUL_GAIN_DB = 3.0
|
||||
MEANINGFUL_RATIO_IMPROVEMENT_DB = 3.0
|
||||
MAX_LEVEL_TO_LEVEL_GAIN_SPREAD_DB = 3.0
|
||||
RESIDUAL_TRACKING_MARGIN_DB = 3.0
|
||||
|
||||
POINT = hardware.OperatingPoint(**asdict(lab064.POINT))
|
||||
|
||||
|
||||
def save_new(path: Path, value: dict) -> None:
|
||||
"""Записать новое свидетельство и не перезаписывать результат опыта."""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("x", encoding="utf-8") as stream:
|
||||
json.dump(value, stream, ensure_ascii=False, indent=2, allow_nan=False)
|
||||
stream.write("\n")
|
||||
|
||||
|
||||
def save_text_new(path: Path, value: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("x", encoding="utf-8") as stream:
|
||||
stream.write(value)
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
while block := stream.read(1024 * 1024):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def parameters() -> dict:
|
||||
result = lab064.parameters()
|
||||
result["tx_vga_db"] = TX_VGA_DB
|
||||
result["geometry_unchanged_from_lab064"] = True
|
||||
result["waveform_source"] = "Lab064 exact CS8 copy"
|
||||
return result
|
||||
|
||||
|
||||
def transfer_command(path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
|
||||
command = lab064.transfer_command(path, samples, transmit=transmit)
|
||||
if transmit:
|
||||
command[command.index("-x") + 1] = str(TX_VGA_DB)
|
||||
return command
|
||||
|
||||
|
||||
def validate_lab064_waveform() -> dict:
|
||||
source = LAB064_RAW / "tx_level_staircase.cs8"
|
||||
metadata = json.loads((LAB064_RAW / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||
digest = sha256(source)
|
||||
if (
|
||||
digest != EXPECTED_TX_SHA256
|
||||
or metadata.get("tx_sha256") != EXPECTED_TX_SHA256
|
||||
or source.stat().st_size != 2 * lab064.TX_SAMPLES
|
||||
or metadata.get("segments") != lab064.prepared_segments()[0]
|
||||
):
|
||||
raise RuntimeError("Waveform Lab064 не совпадает с зафиксированным образцом")
|
||||
return metadata
|
||||
|
||||
|
||||
def validate_prepared() -> dict:
|
||||
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||
path = RAW / "tx_level_staircase.cs8"
|
||||
if (
|
||||
metadata.get("lab") != "Lab065"
|
||||
or metadata.get("stage") != "PREPARATION_ONLY"
|
||||
or metadata.get("rf_tx_count") != 0
|
||||
or metadata.get("parameters") != parameters()
|
||||
or metadata.get("tx_sha256") != EXPECTED_TX_SHA256
|
||||
or metadata.get("source_tx_sha256") != EXPECTED_TX_SHA256
|
||||
or path.stat().st_size != 2 * lab064.TX_SAMPLES
|
||||
or sha256(path) != EXPECTED_TX_SHA256
|
||||
):
|
||||
raise RuntimeError("Подготовленный Lab065 TX или параметры изменились")
|
||||
return metadata
|
||||
|
||||
|
||||
def prepare() -> dict:
|
||||
"""Скопировать точные байты Lab064 в отдельный каталог Lab065."""
|
||||
source_metadata = validate_lab064_waveform()
|
||||
RAW.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
target = RAW / "tx_level_staircase.cs8"
|
||||
if target.exists():
|
||||
raise FileExistsError("TX-файл Lab065 уже существует")
|
||||
shutil.copyfile(LAB064_RAW / "tx_level_staircase.cs8", target)
|
||||
if sha256(target) != EXPECTED_TX_SHA256:
|
||||
target.unlink()
|
||||
raise RuntimeError("Копия waveform Lab065 не прошла SHA-256")
|
||||
result = {
|
||||
"lab": "Lab065",
|
||||
"stage": "PREPARATION_ONLY",
|
||||
"rf_tx_count": 0,
|
||||
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
|
||||
"parameters": parameters(),
|
||||
"segments": source_metadata["segments"],
|
||||
"tx_samples": lab064.TX_SAMPLES,
|
||||
"tx_bytes": 2 * lab064.TX_SAMPLES,
|
||||
"tx_duration_seconds": lab064.TX_SAMPLES / lab064.SAMPLE_RATE,
|
||||
"tx_sha256": sha256(target),
|
||||
"source_tx_sha256": source_metadata["tx_sha256"],
|
||||
"waveform_unchanged_from_lab064": True,
|
||||
"automatic_retry": False,
|
||||
"tx_command_prepared_not_run": transfer_command(target, lab064.TX_SAMPLES, transmit=True),
|
||||
"rx_command_prepared_not_run": transfer_command(RAW / "rx_stream.cs8", lab064.RX_SAMPLES),
|
||||
}
|
||||
save_new(RAW / "tx_metadata.json", result)
|
||||
save_new(OUTPUT / "preparation.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def compare_sessions(old_result: dict, new_rows: list[dict]) -> dict:
|
||||
"""Сопоставить одинаковые цифровые уровни Lab064 и Lab065."""
|
||||
old_by_label = {row["label"]: row for row in old_result["rows"]}
|
||||
rows = []
|
||||
for new in new_rows:
|
||||
old = old_by_label[new["label"]]
|
||||
comparison = {
|
||||
"label": new["label"],
|
||||
"tx_vga_nominal_difference_db": TX_VGA_DIFFERENCE_DB,
|
||||
"rx_matched_power_difference_db": lab064.difference_db(
|
||||
new["matched_signal_power"], old["matched_signal_power"]
|
||||
),
|
||||
"gain_power_abs_h_squared_difference_db": lab064.difference_db(
|
||||
new["gain_power_abs_h_squared"], old["gain_power_abs_h_squared"]
|
||||
),
|
||||
"residual_power_difference_db": lab064.difference_db(
|
||||
new["residual_power"], old["residual_power"]
|
||||
),
|
||||
"matched_to_residual_improvement_db": (
|
||||
new["matched_to_residual_db"] - old["matched_to_residual_db"]
|
||||
),
|
||||
}
|
||||
if "complex_gain_h" in new and "residual_spectrum" in new:
|
||||
old_gain = complex(
|
||||
old["complex_gain_h"]["real"], old["complex_gain_h"]["imag"]
|
||||
)
|
||||
new_gain = complex(
|
||||
new["complex_gain_h"]["real"], new["complex_gain_h"]["imag"]
|
||||
)
|
||||
comparison["complex_gain_h_old"] = old["complex_gain_h"]
|
||||
comparison["complex_gain_h_new"] = new["complex_gain_h"]
|
||||
comparison["complex_gain_phase_change_degrees"] = float(
|
||||
np.degrees(np.angle(new_gain / old_gain))
|
||||
)
|
||||
comparison["residual_spectral_band_difference_db"] = {
|
||||
f"h{harmonic}_{harmonic * lab064.CARRIER_HZ}_hz": lab064.difference_db(
|
||||
new["residual_spectrum"][f"residual_band_h{harmonic}_power_code2"],
|
||||
old["residual_spectrum"][f"residual_band_h{harmonic}_power_code2"],
|
||||
)
|
||||
for harmonic in (1, 2, 3)
|
||||
}
|
||||
rows.append(comparison)
|
||||
return {"rows": rows, "rules_fixed_before_rf": classification_rules()}
|
||||
|
||||
|
||||
def classification_rules() -> dict:
|
||||
return {
|
||||
"internal_linear_max_abs_error_db": 1.0,
|
||||
"compression_upper_step_error_at_most_db": -1.5,
|
||||
"meaningful_matched_gain_median_at_least_db": MEANINGFUL_GAIN_DB,
|
||||
"meaningful_ratio_improvement_median_at_least_db": MEANINGFUL_RATIO_IMPROVEMENT_DB,
|
||||
"maximum_level_to_level_gain_spread_db": MAX_LEVEL_TO_LEVEL_GAIN_SPREAD_DB,
|
||||
"residual_tracks_if_within_matched_gain_db": RESIDUAL_TRACKING_MARGIN_DB,
|
||||
}
|
||||
|
||||
|
||||
def classify_result(new_result: dict, cross_comparison: dict) -> dict:
|
||||
"""Выбрать один из пяти заранее заданных результатов Lab065."""
|
||||
if not new_result.get("capture_valid"):
|
||||
return {"case": "INVALID_CAPTURE", "rules_fixed_before_rf": classification_rules()}
|
||||
internal_errors = [row["linearity_error_db"] for row in new_result["comparisons"]]
|
||||
upper_error = internal_errors[-1]
|
||||
cross_rows = cross_comparison["rows"]
|
||||
matched = np.asarray([row["rx_matched_power_difference_db"] for row in cross_rows])
|
||||
residual = np.asarray([row["residual_power_difference_db"] for row in cross_rows])
|
||||
improvements = np.asarray([row["matched_to_residual_improvement_db"] for row in cross_rows])
|
||||
median_matched = float(np.median(matched))
|
||||
median_residual = float(np.median(residual))
|
||||
median_improvement = float(np.median(improvements))
|
||||
spread = float(np.ptp(matched))
|
||||
linear = max(abs(value) for value in internal_errors) <= 1.0
|
||||
if upper_error <= -1.5:
|
||||
case = "COMPRESSION_AT_HIGHER_TX_GAIN"
|
||||
elif median_matched < MEANINGFUL_GAIN_DB:
|
||||
case = "NO_MEANINGFUL_GAIN"
|
||||
elif (
|
||||
linear
|
||||
and spread <= MAX_LEVEL_TO_LEVEL_GAIN_SPREAD_DB
|
||||
and median_improvement >= MEANINGFUL_RATIO_IMPROVEMENT_DB
|
||||
):
|
||||
case = "LINEAR_GAIN_IMPROVEMENT"
|
||||
elif (
|
||||
median_residual >= median_matched - RESIDUAL_TRACKING_MARGIN_DB
|
||||
and median_improvement < MEANINGFUL_RATIO_IMPROVEMENT_DB
|
||||
):
|
||||
case = "RESIDUAL_RISES_WITH_SIGNAL"
|
||||
else:
|
||||
case = "NOT_LOCALIZED"
|
||||
return {
|
||||
"case": case,
|
||||
"summary": {
|
||||
"median_matched_gain_db": median_matched,
|
||||
"median_residual_gain_db": median_residual,
|
||||
"median_matched_to_residual_improvement_db": median_improvement,
|
||||
"matched_gain_level_spread_db": spread,
|
||||
"maximum_internal_linearity_error_db": max(abs(value) for value in internal_errors),
|
||||
"upper_step_linearity_error_db": upper_error,
|
||||
},
|
||||
"rules_fixed_before_rf": classification_rules(),
|
||||
}
|
||||
|
||||
|
||||
def software_gate() -> dict:
|
||||
metadata = validate_prepared()
|
||||
old_result = json.loads(LAB064_RESULT.read_text(encoding="utf-8"))
|
||||
synthetic = lab064.synthetic_linearity_gate()
|
||||
result = {
|
||||
"lab": "Lab065",
|
||||
"stage": "SOFTWARE_GATE",
|
||||
"rf_tx_count": 0,
|
||||
"passed": bool(synthetic["passed"] and old_result["capture_valid"]),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
"waveform_unchanged_from_lab064": metadata["waveform_unchanged_from_lab064"],
|
||||
"lab064_baseline_classification": old_result["classification"]["case"],
|
||||
"classification_rules": classification_rules(),
|
||||
}
|
||||
save_new(OUTPUT / "software_gate.json", result)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab065 software gate FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def preflight(*, runner=None, process_checker=None, clock_checker=None, sleep_function=time.sleep) -> dict:
|
||||
"""Проверить общую опору и 0,1 с RX-фона при полном запрете TX."""
|
||||
metadata = validate_prepared()
|
||||
software = json.loads((OUTPUT / "software_gate.json").read_text(encoding="utf-8"))
|
||||
if not software.get("passed") or software.get("tx_sha256") != metadata["tx_sha256"]:
|
||||
raise RuntimeError("Нет действующего Lab065 software gate")
|
||||
save_new(RAW / "preflight_attempt.json", {
|
||||
"lab": "Lab065", "rf_tx_count": 0, "point": asdict(POINT),
|
||||
"parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
|
||||
def rx_only(command: list[str]):
|
||||
if "-t" in command:
|
||||
raise PermissionError("TX запрещён в Lab065 preflight")
|
||||
return actual_runner(command)
|
||||
|
||||
result = {
|
||||
"lab": "Lab065", "stage": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT",
|
||||
"passed": False, "rf_tx_count": 0, "stop_before_tx": True,
|
||||
"point": asdict(POINT), "parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
}
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(rx_only)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT, rx_only, lab054.clock.TransmitGuard(), sleep_function=sleep_function
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
path = RAW / "preflight_rx.cs8"
|
||||
captured = rx_only(transfer_command(path, lab064.PREFLIGHT_SAMPLES))
|
||||
stdout, stderr = captured.stdout or "", captured.stderr or ""
|
||||
result["rx_process"] = {
|
||||
"returncode": captured.returncode, "stdout": stdout, "stderr": stderr,
|
||||
}
|
||||
result["rx_counter_summary"] = lab064.counter_summary(stdout + stderr, "overrun")
|
||||
if captured.returncode or not result["rx_counter_summary"]["clean"]:
|
||||
raise RuntimeError("RX preflight: процесс или счётчик overruns не прошёл")
|
||||
if path.stat().st_size != 2 * lab064.PREFLIGHT_SAMPLES:
|
||||
raise RuntimeError("Неверная длина Lab065 preflight RX")
|
||||
level = hardware.receive_level(path)
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
level["symbol_energy"] = "NOT_APPLICABLE_TO_BACKGROUND"
|
||||
level["ac_rms_code"] = float(np.sqrt(max(0.0, level["ac_power_code2"])))
|
||||
level["unexpected_strong_signal"] = bool(
|
||||
level["ac_rms_code"] >= lab064.STRONG_AC_RMS_CODE
|
||||
or level["max_abs_i"] >= lab064.STRONG_COMPONENT_CODE
|
||||
or level["max_abs_q"] >= lab064.STRONG_COMPONENT_CODE
|
||||
)
|
||||
result["rx_level"] = level
|
||||
result["rx_sha256"] = sha256(path)
|
||||
result["rx_overruns"] = result["rx_counter_summary"]["final_count"]
|
||||
if level["saturation_detected"] or level["unexpected_strong_signal"]:
|
||||
raise RuntimeError("RX-only уровень не прошёл gate; STOP без изменения gain")
|
||||
result["passed"] = True
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = lab054.disable_clock_outputs(rx_only) if clock_started else []
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "preflight_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "preflight_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab065 preflight FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def require_preflight(metadata: dict) -> dict:
|
||||
record = json.loads((RAW / "preflight_result.json").read_text(encoding="utf-8"))
|
||||
if (
|
||||
record.get("passed") is not True
|
||||
or record.get("rf_tx_count") != 0
|
||||
or record.get("point") != asdict(POINT)
|
||||
or record.get("parameters") != parameters()
|
||||
or record.get("tx_sha256") != metadata["tx_sha256"]
|
||||
):
|
||||
raise PermissionError("Нет успешного RX-only preflight для конфигурации Lab065")
|
||||
hardware.require_clock_pass(record.get("clock", {}))
|
||||
level = record.get("rx_level", {})
|
||||
if (
|
||||
level.get("saturation_detected") is not False
|
||||
or level.get("unexpected_strong_signal") is not False
|
||||
or record.get("rx_overruns") != 0
|
||||
):
|
||||
raise PermissionError("RX-only preflight не разрешает передачу")
|
||||
return record
|
||||
|
||||
|
||||
def capture_once(
|
||||
*, authorization: bool = False, runner=None, process_starter=None,
|
||||
process_checker=None, clock_checker=None, sleep_function=time.sleep,
|
||||
) -> dict:
|
||||
"""Выполнить один RX-first сеанс после отдельного прямого разрешения."""
|
||||
if authorization is not True:
|
||||
raise PermissionError("Нужно отдельное разрешение на единственный RF-сеанс Lab065")
|
||||
metadata = validate_prepared()
|
||||
require_preflight(metadata)
|
||||
receive_path = RAW / "rx_stream.cs8"
|
||||
if receive_path.exists() or (RAW / "capture_result.json").exists():
|
||||
raise FileExistsError("Аппаратный результат Lab065 уже существует")
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_starter = process_starter or lab054.clock._default_process_starter
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
save_new(RAW / "hardware_attempt.json", {
|
||||
"lab": "Lab065", "authorized_once": True, "automatic_retry": False,
|
||||
"point": asdict(POINT), "parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
result = {
|
||||
"lab": "Lab065", "stage": "SINGLE_RF_LEVEL_CAPTURE", "passed": False,
|
||||
"rf_tx_count": 0, "automatic_tx_repeats": 0, "point": asdict(POINT),
|
||||
"parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
|
||||
}
|
||||
guard = lab054.clock.TransmitGuard()
|
||||
process = None
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(actual_runner)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT, actual_runner, guard, sleep_function=sleep_function
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
process = process_starter(
|
||||
transfer_command(receive_path, lab064.RX_SAMPLES),
|
||||
RAW / "rx_stdout.txt", RAW / "rx_stderr.txt",
|
||||
)
|
||||
sleep_function(lab064.RX_LEAD_SECONDS)
|
||||
if process.poll() is not None:
|
||||
raise RuntimeError("RX завершился до TX")
|
||||
before_tx = hardware.receive_level(receive_path, tail_samples=65_536)
|
||||
before_tx.pop("symbol_energy_code2_seconds", None)
|
||||
result["rx_level_before_tx"] = before_tx
|
||||
if before_tx["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping до TX; RF TX = 0")
|
||||
guard.register_transmit()
|
||||
result["rf_tx_count"] = guard.transmit_calls
|
||||
transmitted = actual_runner(
|
||||
transfer_command(RAW / "tx_level_staircase.cs8", lab064.TX_SAMPLES, transmit=True)
|
||||
)
|
||||
tx_stdout, tx_stderr = transmitted.stdout or "", transmitted.stderr or ""
|
||||
save_text_new(RAW / "tx_stdout.txt", tx_stdout)
|
||||
save_text_new(RAW / "tx_stderr.txt", tx_stderr)
|
||||
result["tx_process"] = {
|
||||
"returncode": transmitted.returncode,
|
||||
"stdout_file": "tx_stdout.txt", "stderr_file": "tx_stderr.txt",
|
||||
}
|
||||
result["tx_counter_summary"] = lab064.counter_summary(
|
||||
tx_stdout + tx_stderr, "underrun"
|
||||
)
|
||||
if transmitted.returncode:
|
||||
raise RuntimeError("Единственный TX завершился ошибкой; повтор запрещён")
|
||||
if process.wait(lab064.RX_SECONDS + 10):
|
||||
raise RuntimeError("RX завершился ошибкой; повтор запрещён")
|
||||
process.close_logs()
|
||||
result["rx_samples"] = receive_path.stat().st_size // 2
|
||||
result["rx_bytes"] = receive_path.stat().st_size
|
||||
result["rx_duration_seconds"] = result["rx_samples"] / lab064.SAMPLE_RATE
|
||||
if result["rx_bytes"] != 2 * lab064.RX_SAMPLES:
|
||||
raise RuntimeError("Повреждённая длина RX; повтор запрещён")
|
||||
rx_stdout = (RAW / "rx_stdout.txt").read_text(encoding="utf-8")
|
||||
rx_stderr = (RAW / "rx_stderr.txt").read_text(encoding="utf-8")
|
||||
result["rx_counter_summary"] = lab064.counter_summary(
|
||||
rx_stdout + rx_stderr, "overrun"
|
||||
)
|
||||
result["rx_sha256"] = sha256(receive_path)
|
||||
level = hardware.receive_level(receive_path)
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
result["rx_level"] = level
|
||||
result["rx_clipping"] = bool(level["saturation_detected"])
|
||||
result["passed"] = True
|
||||
validity = lab064.capture_validity(result)
|
||||
if not validity["valid"]:
|
||||
result["passed"] = False
|
||||
raise RuntimeError("Запись невалидна; результат сохранён, повтор запрещён")
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = []
|
||||
if process is not None:
|
||||
try:
|
||||
process.terminate()
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
if clock_started:
|
||||
errors.extend(lab054.disable_clock_outputs(actual_runner))
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "capture_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "capture_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab065 RF-сеанс завершён с ошибкой; повтор запрещён")
|
||||
return result
|
||||
|
||||
|
||||
def analyze_capture() -> dict:
|
||||
"""Рассчитать метрики Lab065 и прямое сравнение с Lab064."""
|
||||
metadata = validate_prepared()
|
||||
rx_path = RAW / "rx_stream.cs8"
|
||||
capture = json.loads((RAW / "capture_result.json").read_text(encoding="utf-8"))
|
||||
if capture.get("rf_tx_count") != 1 or capture.get("rx_sha256") != sha256(rx_path):
|
||||
raise RuntimeError("Нет подтверждённой единственной RX-записи Lab065")
|
||||
tx = lab064.energy.Cs8File(RAW / "tx_level_staircase.cs8")
|
||||
rx = lab064.energy.Cs8File(rx_path)
|
||||
location = lab064.locate_waveform(tx, rx)
|
||||
background_rows = []
|
||||
for window in lab064.background_windows():
|
||||
rx_start = location["start_sample"] + window["tx_relative_start_sample"]
|
||||
background_rows.append({
|
||||
**window, "rx_start_sample": rx_start,
|
||||
"metrics": lab064._window_metrics(rx.read(rx_start, window["samples"])),
|
||||
})
|
||||
background_by_label = {row["label"]: row for row in background_rows}
|
||||
preceding = {"L0": "INITIAL_GUARD", "L1": "GAP_L0_L1",
|
||||
"L2": "GAP_L1_L2", "L3": "GAP_L2_L3"}
|
||||
rows = []
|
||||
for segment in metadata["segments"]:
|
||||
tx_start = segment["start_sample"]
|
||||
nominal_rx_start = location["start_sample"] + tx_start
|
||||
fitted = lab064.energy.analyze_rx(
|
||||
lab064.lab056.OffsetSource(tx, tx_start), rx, [nominal_rx_start],
|
||||
lab064.SEGMENT_SAMPLES, lab064.SAMPLES_PER_SYMBOL,
|
||||
)["frames"][0]
|
||||
actual_start = fitted["start"]
|
||||
active_reference = tx.read(
|
||||
tx_start + lab064.TRIM_SAMPLES,
|
||||
lab064.SEGMENT_SAMPLES - 2 * lab064.TRIM_SAMPLES,
|
||||
)
|
||||
active_received = rx.read(
|
||||
actual_start + lab064.TRIM_SAMPLES,
|
||||
lab064.SEGMENT_SAMPLES - 2 * lab064.TRIM_SAMPLES,
|
||||
)
|
||||
complex_gain = lab064.lab056.complex_gain(
|
||||
active_reference, active_received, fitted["cfo_hz"]
|
||||
)
|
||||
matched = fitted["heldout_signal_power"]
|
||||
residual = fitted["heldout_residual_power"]
|
||||
background = background_by_label[preceding[segment["label"]]]
|
||||
rows.append({
|
||||
"label": segment["label"], "level_db": segment["level_db"],
|
||||
"rx_start_sample": actual_start,
|
||||
"alignment_adjustment_samples": fitted["alignment_adjustment"],
|
||||
"full_iq": lab064._window_metrics(rx.read(actual_start, lab064.SEGMENT_SAMPLES)),
|
||||
"background_label": background["label"], "background": background["metrics"],
|
||||
"matched_signal_power": matched, "residual_power": residual,
|
||||
"matched_to_residual_db": lab064.difference_db(matched, residual),
|
||||
"complex_gain_h": complex_gain, "gain_abs": fitted["gain_abs"],
|
||||
"gain_power_abs_h_squared": fitted["gain_abs"] ** 2,
|
||||
"cfo_hz": fitted["cfo_hz"],
|
||||
"residual_spectrum": lab064.residual_spectral_diagnostics(
|
||||
active_reference, active_received, fitted["cfo_hz"], complex_gain
|
||||
),
|
||||
})
|
||||
comparisons = []
|
||||
for index in range(1, len(rows)):
|
||||
tx_difference = metadata["segments"][index]["tx_input_difference_from_previous_db"]
|
||||
rx_difference = lab064.difference_db(
|
||||
rows[index]["matched_signal_power"], rows[index - 1]["matched_signal_power"]
|
||||
)
|
||||
comparisons.append({
|
||||
"comparison": f"{rows[index]['label']}/{rows[index - 1]['label']}",
|
||||
"tx_input_difference_db": tx_difference,
|
||||
"rx_matched_power_difference_db": rx_difference,
|
||||
"linearity_error_db": rx_difference - tx_difference,
|
||||
})
|
||||
validity = lab064.capture_validity(capture)
|
||||
result = {
|
||||
"lab": "Lab065", "stage": "OFFLINE_RF_LEVEL_LINEARITY_ANALYSIS",
|
||||
"packet_receiver_used": False, "location": location, "rows": rows,
|
||||
"background_windows": background_rows,
|
||||
"background_summary": lab064.summarize_background(background_rows),
|
||||
"comparisons": comparisons, "capture_valid": validity["valid"],
|
||||
"capture_invalid_reasons": validity["invalid_reasons"],
|
||||
"tx_underruns_maximum": capture.get("tx_counter_summary", {}).get("maximum_count"),
|
||||
"rx_overruns_maximum": capture.get("rx_counter_summary", {}).get("maximum_count"),
|
||||
"rx_clipping": any(row["full_iq"]["clipped_samples_fraction"] > 0 for row in rows),
|
||||
}
|
||||
old_result = json.loads(LAB064_RESULT.read_text(encoding="utf-8"))
|
||||
result["lab064_to_lab065"] = compare_sessions(old_result, rows)
|
||||
result["classification"] = classify_result(result, result["lab064_to_lab065"])
|
||||
save_new(OUTPUT / "rf_result.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def run_functional_tests() -> None:
|
||||
assert transfer_command("NUL", 100, transmit=True)[-7:-5] == ["-x", "20"]
|
||||
assert "-t" not in transfer_command("NUL", 100)
|
||||
assert validate_lab064_waveform()["tx_sha256"] == EXPECTED_TX_SHA256
|
||||
assert lab064.synthetic_linearity_gate()["passed"]
|
||||
try:
|
||||
capture_once(authorization=False)
|
||||
except PermissionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("RF без отдельного разрешения")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--stage", choices=("prepare", "software", "preflight", "capture", "analyze"),
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument("--authorize-one-rf", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
if arguments.stage == "prepare":
|
||||
result = prepare()
|
||||
elif arguments.stage == "software":
|
||||
result = software_gate()
|
||||
elif arguments.stage == "preflight":
|
||||
result = preflight()
|
||||
elif arguments.stage == "capture":
|
||||
result = capture_once(authorization=arguments.authorize_one_rf)
|
||||
else:
|
||||
result = analyze_capture()
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False))
|
||||
print("RF TX NOW = 0")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
786
experiments/lab066_full_packet_rf_retest.py
Normal file
@@ -0,0 +1,786 @@
|
||||
"""Lab066: исправленный повтор полного 500K PHY-сеанса.
|
||||
|
||||
Подготовка, программная проверка и preflight не передают RF. Единственный
|
||||
аппаратный сеанс доступен только отдельной командой с явным разрешением.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
import argparse
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import numpy as np
|
||||
from scipy import signal
|
||||
|
||||
from experiments import lab049_bpsk_symbol_rate as phy
|
||||
from experiments import lab053_packet_processing as packet_processing
|
||||
from experiments import lab053_streaming_receiver as streaming
|
||||
from experiments import lab054_hardware_control as lab054
|
||||
from experiments import lab056_capture_runner as hardware
|
||||
from experiments import lab058_abab_symbol_quality as lab058
|
||||
from experiments import lab064_rf_level_linearity as lab064
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RAW = ROOT / "data/raw/lab066"
|
||||
OUTPUT = ROOT / "data/processed/lab066"
|
||||
POINT = hardware.OperatingPoint(
|
||||
frequency_hz=1_400_000_000,
|
||||
physical_path="MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
|
||||
path_confirmed=True,
|
||||
tx_antenna="existing 1400 MHz antenna near window",
|
||||
rx_antenna="existing 1400 MHz antenna on desk",
|
||||
distance_m=1.2,
|
||||
orientation="same polarization; fixed during preflight and RF session",
|
||||
visibility="LOS indoor",
|
||||
)
|
||||
MODE = phy.PhyMode(500_000)
|
||||
PACKETS = 16
|
||||
PAYLOAD_BYTES = 256
|
||||
PACKET_SAMPLES = 92_256
|
||||
INTER_PACKET_GAP_SECONDS = 0.005
|
||||
INTER_PACKET_GAP_SAMPLES = 40_000
|
||||
NUMBER_OF_GAPS = PACKETS - 1
|
||||
GUARD_SECONDS = 0.1
|
||||
GUARD_SAMPLES = 800_000
|
||||
BASE_TX_SAMPLES = PACKETS * PACKET_SAMPLES + NUMBER_OF_GAPS * INTER_PACKET_GAP_SAMPLES
|
||||
EXPECTED_TX_SAMPLES = BASE_TX_SAMPLES + 2 * GUARD_SAMPLES
|
||||
PREFLIGHT_SAMPLES = 800_000
|
||||
RX_SECONDS = 2
|
||||
RX_SAMPLES = RX_SECONDS * MODE.sample_rate_hz
|
||||
RX_LEAD_SECONDS = 0.5
|
||||
TX_VGA_DB = 20
|
||||
STRONG_AC_RMS_CODE = 20.0
|
||||
STRONG_COMPONENT_CODE = 100.0
|
||||
|
||||
|
||||
def save_new(path: Path, value: dict) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("x", encoding="utf-8") as stream:
|
||||
json.dump(value, stream, ensure_ascii=False, indent=2, allow_nan=False)
|
||||
stream.write("\n")
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
while block := stream.read(1024 * 1024):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def plans():
|
||||
result = lab054.plans()
|
||||
payloads = [frame.plan.payload for frame in result]
|
||||
if len(result) != PACKETS or any(len(payload) != PAYLOAD_BYTES for payload in payloads):
|
||||
raise RuntimeError("Штатный генератор не дал 16 payload по 256 байт")
|
||||
if len(set(payloads)) != PACKETS:
|
||||
raise RuntimeError("Payload пакетов должны однозначно различаться")
|
||||
return result
|
||||
|
||||
|
||||
def parameters() -> dict:
|
||||
return {
|
||||
"rf_frequency_hz": POINT.frequency_hz,
|
||||
"sample_rate_hz": MODE.sample_rate_hz,
|
||||
"symbol_rate": MODE.symbol_rate,
|
||||
"samples_per_symbol": MODE.samples_per_symbol,
|
||||
"digital_carrier_hz": lab054.phy.lab045.CARRIER_OFFSET_HZ,
|
||||
"packets": PACKETS,
|
||||
"payload_bytes": PAYLOAD_BYTES,
|
||||
"tx_vga_db": TX_VGA_DB,
|
||||
"rx_lna_db": 24,
|
||||
"rx_vga_db": 24,
|
||||
"rf_amplifier": False,
|
||||
"bias_tee": False,
|
||||
"geometry": asdict(POINT),
|
||||
"geometry_unchanged": True,
|
||||
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
|
||||
}
|
||||
|
||||
|
||||
def transfer_command(path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
|
||||
command = hardware.command(POINT, path, samples, transmit=transmit)
|
||||
if transmit:
|
||||
command[command.index("-x") + 1] = str(TX_VGA_DB)
|
||||
return command
|
||||
|
||||
|
||||
def build_waveform() -> tuple[bytes, list[dict]]:
|
||||
"""Собрать финальный поток с нулевыми guards и паузами."""
|
||||
blocks: list[bytes] = [bytes(2 * GUARD_SAMPLES)]
|
||||
layout: list[dict] = []
|
||||
cursor = GUARD_SAMPLES
|
||||
for index, frame in enumerate(plans(), start=1):
|
||||
packet = phy.build_parameterized_tx_cs8(frame.plan, MODE)
|
||||
if len(packet) != 2 * PACKET_SAMPLES:
|
||||
raise RuntimeError(f"Пакет {index}: ожидалось {PACKET_SAMPLES} отсчётов")
|
||||
layout.append({
|
||||
"packet_index": index,
|
||||
"start_sample": cursor,
|
||||
"samples": PACKET_SAMPLES,
|
||||
"payload_sha256": hashlib.sha256(frame.plan.payload).hexdigest(),
|
||||
})
|
||||
blocks.append(packet)
|
||||
cursor += PACKET_SAMPLES
|
||||
if index != PACKETS:
|
||||
blocks.append(bytes(2 * INTER_PACKET_GAP_SAMPLES))
|
||||
layout[-1]["following_gap_start_sample"] = cursor
|
||||
layout[-1]["following_gap_samples"] = INTER_PACKET_GAP_SAMPLES
|
||||
cursor += INTER_PACKET_GAP_SAMPLES
|
||||
blocks.append(bytes(2 * GUARD_SAMPLES))
|
||||
waveform = b"".join(blocks)
|
||||
if len(waveform) != 2 * EXPECTED_TX_SAMPLES or cursor + GUARD_SAMPLES != EXPECTED_TX_SAMPLES:
|
||||
raise RuntimeError("Неверная длина итогового TX-потока Lab066")
|
||||
return waveform, layout
|
||||
|
||||
|
||||
def prepare() -> dict:
|
||||
RAW.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
waveform_path = RAW / "tx_stream.cs8"
|
||||
if waveform_path.exists():
|
||||
raise FileExistsError("Lab066 TX уже существует; перезапись запрещена")
|
||||
waveform, layout = build_waveform()
|
||||
waveform_path.write_bytes(waveform)
|
||||
result = {
|
||||
"lab": "Lab066", "stage": "PREPARATION_ONLY", "rf_tx_count": 0,
|
||||
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
|
||||
"parameters": parameters(), "packet_samples": PACKET_SAMPLES,
|
||||
"inter_packet_gap_seconds": INTER_PACKET_GAP_SECONDS,
|
||||
"inter_packet_gap_samples": INTER_PACKET_GAP_SAMPLES,
|
||||
"number_of_gaps": NUMBER_OF_GAPS,
|
||||
"zero_guard_before_samples": GUARD_SAMPLES,
|
||||
"zero_guard_after_samples": GUARD_SAMPLES,
|
||||
"base_tx_samples": BASE_TX_SAMPLES,
|
||||
"expected_tx_samples": EXPECTED_TX_SAMPLES,
|
||||
"tx_samples": EXPECTED_TX_SAMPLES, "tx_bytes": len(waveform),
|
||||
"tx_duration_seconds": EXPECTED_TX_SAMPLES / MODE.sample_rate_hz,
|
||||
"tx_sha256": sha256(waveform_path), "layout": layout,
|
||||
"rx_samples": RX_SAMPLES, "rx_duration_seconds": RX_SECONDS,
|
||||
"automatic_retry": False,
|
||||
"tx_command_prepared_not_run": transfer_command(waveform_path, EXPECTED_TX_SAMPLES, transmit=True),
|
||||
"rx_command_prepared_not_run": transfer_command(RAW / "rx_stream.cs8", RX_SAMPLES),
|
||||
}
|
||||
save_new(RAW / "tx_metadata.json", result)
|
||||
save_new(OUTPUT / "preparation.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def validate_prepared() -> dict:
|
||||
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||
path = RAW / "tx_stream.cs8"
|
||||
expected, layout = build_waveform()
|
||||
expected_digest = hashlib.sha256(expected).hexdigest()
|
||||
actual = path.read_bytes()
|
||||
if (
|
||||
metadata.get("lab") != "Lab066"
|
||||
or metadata.get("stage") != "PREPARATION_ONLY"
|
||||
or metadata.get("rf_tx_count") != 0
|
||||
or metadata.get("parameters") != parameters()
|
||||
or metadata.get("layout") != layout
|
||||
or metadata.get("expected_tx_samples") != EXPECTED_TX_SAMPLES
|
||||
or metadata.get("tx_samples") != EXPECTED_TX_SAMPLES
|
||||
or len(actual) != 2 * EXPECTED_TX_SAMPLES
|
||||
or metadata.get("tx_sha256") != expected_digest
|
||||
or hashlib.sha256(actual).hexdigest() != expected_digest
|
||||
or actual != expected
|
||||
):
|
||||
raise RuntimeError("Подготовленный Lab066 TX или его разметка изменились")
|
||||
return metadata
|
||||
|
||||
|
||||
def decode(mode: str, source_path: Path | None = None, *, diagnostics: bool = False) -> dict:
|
||||
"""Строго декодировать весь поток; legacy и T2 запускаются отдельно."""
|
||||
observations = []
|
||||
receiver = streaming.StreamingReceiver(
|
||||
processor=packet_processing.PacketProcessor(carrier_tracking_mode=mode),
|
||||
packet_observer=lambda row, outcome: observations.append((row, outcome)),
|
||||
)
|
||||
received = []
|
||||
with (source_path or (RAW / "tx_stream.cs8")).open("rb") as stream:
|
||||
while block := stream.read(2 * receiver.block_samples):
|
||||
values = np.frombuffer(block, np.int8)
|
||||
samples = values[::2].astype(np.float32) + 1j * values[1::2].astype(np.float32)
|
||||
received.extend(receiver.push_iq_block(samples.astype(np.complex64)))
|
||||
received.extend(receiver.finish())
|
||||
expected = [frame.plan.payload for frame in plans()]
|
||||
actual = [item.payload for item in received]
|
||||
payload_to_index = {payload: index for index, payload in enumerate(expected, start=1)}
|
||||
result = {
|
||||
"mode": mode, "candidates": len(observations),
|
||||
"header_pass": sum(outcome.phy_header_pass for _, outcome in observations),
|
||||
"fec_started": sum(outcome.fec_attempted for _, outcome in observations),
|
||||
"fec_pass": sum(outcome.fec_pass for _, outcome in observations),
|
||||
"crc_started": sum(outcome.crc != "NOT_REACHED" for _, outcome in observations),
|
||||
"crc_pass": sum(outcome.crc == "PASS" for _, outcome in observations),
|
||||
"packet_output": len(actual),
|
||||
"exact_payload_match": sum(payload in payload_to_index for payload in actual),
|
||||
"packet_indexes": [payload_to_index.get(payload) for payload in actual],
|
||||
"exact_ordered_payloads": actual == expected,
|
||||
"t2_state_scope": "NEW_PREAMBLE_ESTIMATE_PER_PACKET" if mode == "t2" else "NOT_APPLICABLE",
|
||||
"fallback_between_modes": False,
|
||||
"passed": len(actual) == PACKETS and actual == expected,
|
||||
}
|
||||
if diagnostics:
|
||||
result["rows"] = [{
|
||||
"absolute_refined": int(row["absolute_refined"]),
|
||||
"reason": outcome.reason,
|
||||
"header": bool(outcome.phy_header_pass),
|
||||
"fec_started": bool(outcome.fec_attempted), "fec": bool(outcome.fec_pass),
|
||||
"crc_started": outcome.crc != "NOT_REACHED", "crc": outcome.crc == "PASS",
|
||||
"packet_output": bool(outcome.accepted and outcome.crc == "PASS"),
|
||||
"packet_index_after_verdict": payload_to_index.get(
|
||||
bytes(outcome.frame.reception.payload_bytes)
|
||||
if outcome.frame is not None and outcome.frame.reception.payload_bytes is not None else b""
|
||||
),
|
||||
"t2_tracking_runs_for_candidate": len(row.get("carrier_tracking_traces", ())),
|
||||
} for row, outcome in observations]
|
||||
result["detector_statistics"] = dict(receiver.detection_statistics)
|
||||
result["candidate_overflow"] = receiver.candidate_overflow
|
||||
if mode == "t2":
|
||||
result["t2_state_reset_evidence"] = {
|
||||
"estimate_source": "each candidate's own preamble",
|
||||
"context_scope": "new PacketMeasure context for every candidate",
|
||||
"tracking_runs_per_accepted_packet": [
|
||||
row["t2_tracking_runs_for_candidate"]
|
||||
for row in result["rows"] if row["packet_output"]
|
||||
],
|
||||
"all_accepted_packets_have_fresh_tracking_runs": all(
|
||||
row["t2_tracking_runs_for_candidate"] > 0
|
||||
for row in result["rows"] if row["packet_output"]
|
||||
),
|
||||
"persistent_tracking_state_in_packet_processor": False,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def software_gate() -> dict:
|
||||
metadata = validate_prepared()
|
||||
legacy = decode("legacy", diagnostics=True)
|
||||
t2 = decode("t2", diagnostics=True)
|
||||
result = {
|
||||
"lab": "Lab066", "stage": "SOFTWARE_GATE", "rf_tx_count": 0,
|
||||
"tx_sha256": metadata["tx_sha256"], "legacy": legacy, "t2": t2,
|
||||
"gaps_verified_zero": True,
|
||||
"t2_state_reset_verified_by_fresh_preamble_per_packet": bool(
|
||||
t2.get("t2_state_reset_evidence", {}).get(
|
||||
"all_accepted_packets_have_fresh_tracking_runs"
|
||||
)
|
||||
),
|
||||
"passed": (
|
||||
legacy["passed"] and t2["passed"]
|
||||
and t2.get("t2_state_reset_evidence", {}).get(
|
||||
"all_accepted_packets_have_fresh_tracking_runs"
|
||||
) is True
|
||||
),
|
||||
}
|
||||
save_new(OUTPUT / "software_gate.json", result)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab066 software gate FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def preflight(*, runner=None, process_checker=None, clock_checker=None,
|
||||
sleep_function=time.sleep) -> dict:
|
||||
metadata = validate_prepared()
|
||||
software = json.loads((OUTPUT / "software_gate.json").read_text(encoding="utf-8"))
|
||||
if not software.get("passed") or software.get("tx_sha256") != metadata["tx_sha256"]:
|
||||
raise RuntimeError("Нет действующего software gate Lab066")
|
||||
save_new(RAW / "preflight_attempt.json", {
|
||||
"lab": "Lab066", "rf_tx_count": 0, "point": asdict(POINT),
|
||||
"tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
|
||||
def rx_only(command):
|
||||
if "-t" in command:
|
||||
raise PermissionError("TX запрещён в Lab066 preflight")
|
||||
return actual_runner(command)
|
||||
|
||||
result = {
|
||||
"lab": "Lab066", "stage": "RX_ONLY_PREFLIGHT", "passed": False,
|
||||
"rf_tx_count": 0, "point": asdict(POINT), "parameters": parameters(),
|
||||
"tx_sha256": metadata["tx_sha256"], "stop_before_tx": True,
|
||||
}
|
||||
clock_started = False
|
||||
try:
|
||||
before = process_checker()
|
||||
result["process_before"] = before.to_dict()
|
||||
lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(rx_only)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(
|
||||
POINT, rx_only, lab054.clock.TransmitGuard(), sleep_function=sleep_function
|
||||
)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
path = RAW / "preflight_rx.cs8"
|
||||
if path.exists():
|
||||
raise FileExistsError("Lab066 preflight RX уже существует")
|
||||
captured = rx_only(transfer_command(path, PREFLIGHT_SAMPLES))
|
||||
stdout, stderr = captured.stdout or "", captured.stderr or ""
|
||||
result["rx_process"] = {"returncode": captured.returncode, "stdout": stdout, "stderr": stderr}
|
||||
result["rx_counter_summary"] = lab064.counter_summary(stdout + stderr, "overrun")
|
||||
if captured.returncode or not result["rx_counter_summary"]["clean"]:
|
||||
raise RuntimeError("RX preflight: процесс или максимум overruns не прошёл")
|
||||
if path.stat().st_size != 2 * PREFLIGHT_SAMPLES:
|
||||
raise RuntimeError("Неверная длина Lab066 preflight RX")
|
||||
level = hardware.receive_level(path)
|
||||
level.pop("symbol_energy_code2_seconds", None)
|
||||
level["symbol_energy"] = "NOT_APPLICABLE_TO_BACKGROUND"
|
||||
ac_rms = float(np.sqrt(max(0.0, level["ac_power_code2"])))
|
||||
strong = bool(ac_rms >= STRONG_AC_RMS_CODE or level["max_abs_i"] >= STRONG_COMPONENT_CODE
|
||||
or level["max_abs_q"] >= STRONG_COMPONENT_CODE)
|
||||
level.update({"ac_rms_code": ac_rms, "unexpected_strong_signal": strong})
|
||||
result["rx_level"] = level
|
||||
result["rx_sha256"] = sha256(path)
|
||||
result["rx_overruns"] = result["rx_counter_summary"]["maximum_count"]
|
||||
if level["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping; STOP без изменения gain")
|
||||
if strong:
|
||||
raise RuntimeError("Неожиданно сильный RX-only фон; STOP без изменения gain")
|
||||
result["passed"] = True
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = lab054.disable_clock_outputs(rx_only) if clock_started else []
|
||||
try:
|
||||
after = process_checker()
|
||||
result["process_after"] = after.to_dict()
|
||||
lab054.require_clear(after)
|
||||
except Exception as error:
|
||||
errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "preflight_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "preflight_result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n", encoding="utf-8"
|
||||
)
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab066 preflight FAIL")
|
||||
return result
|
||||
|
||||
|
||||
def require_preflight(metadata: dict) -> dict:
|
||||
record = json.loads((RAW / "preflight_result.json").read_text(encoding="utf-8"))
|
||||
if (record.get("passed") is not True or record.get("rf_tx_count") != 0
|
||||
or record.get("point") != asdict(POINT) or record.get("parameters") != parameters()
|
||||
or record.get("tx_sha256") != metadata["tx_sha256"]):
|
||||
raise PermissionError("Нет успешного RX-only preflight для конфигурации Lab066")
|
||||
hardware.require_clock_pass(record.get("clock", {}))
|
||||
if (record.get("rx_overruns") != 0
|
||||
or record.get("rx_level", {}).get("saturation_detected") is not False
|
||||
or record.get("rx_level", {}).get("unexpected_strong_signal") is not False):
|
||||
raise PermissionError("RX-only preflight не разрешает передачу")
|
||||
return record
|
||||
|
||||
|
||||
def capture_once(*, authorization: bool = False, runner=None, process_starter=None,
|
||||
process_checker=None, clock_checker=None, sleep_function=time.sleep) -> dict:
|
||||
"""Выполнить будущий единственный RX-first сеанс после отдельного разрешения."""
|
||||
if authorization is not True:
|
||||
raise PermissionError("Нужно отдельное разрешение на единственный RF-сеанс Lab066")
|
||||
metadata = validate_prepared()
|
||||
require_preflight(metadata)
|
||||
receive_path = RAW / "rx_stream.cs8"
|
||||
if receive_path.exists() or (RAW / "capture_result.json").exists():
|
||||
raise FileExistsError("Аппаратный результат Lab066 уже существует")
|
||||
actual_runner = runner or lab054.clock._default_command_runner
|
||||
process_starter = process_starter or lab054.clock._default_process_starter
|
||||
process_checker = process_checker or lab054.check_processes
|
||||
clock_checker = clock_checker or hardware.common_clock
|
||||
save_new(RAW / "hardware_attempt.json", {
|
||||
"lab": "Lab066", "authorized_once": True, "automatic_retry": False,
|
||||
"point": asdict(POINT), "parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
|
||||
})
|
||||
result = {
|
||||
"lab": "Lab066", "stage": "SINGLE_RF_CAPTURE", "passed": False,
|
||||
"rf_tx_count": 0, "automatic_tx_repeats": 0, "point": asdict(POINT),
|
||||
"parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
|
||||
}
|
||||
guard, process, clock_started = lab054.clock.TransmitGuard(), None, False
|
||||
try:
|
||||
before = process_checker(); result["process_before"] = before.to_dict(); lab054.require_clear(before)
|
||||
result["inventory"] = lab054.phy.lab045._verify_device_inventory(actual_runner)
|
||||
clock_started = True
|
||||
result["clock"] = clock_checker(POINT, actual_runner, guard, sleep_function=sleep_function)
|
||||
hardware.require_clock_pass(result["clock"])
|
||||
process = process_starter(transfer_command(receive_path, RX_SAMPLES), RAW / "rx_stdout.txt", RAW / "rx_stderr.txt")
|
||||
sleep_function(RX_LEAD_SECONDS)
|
||||
if process.poll() is not None:
|
||||
raise RuntimeError("RX завершился до TX")
|
||||
before_tx = hardware.receive_level(receive_path, tail_samples=65_536)
|
||||
before_tx.pop("symbol_energy_code2_seconds", None)
|
||||
before_tx_ac_rms = float(np.sqrt(max(0.0, before_tx["ac_power_code2"])))
|
||||
before_tx_strong = bool(
|
||||
before_tx_ac_rms >= STRONG_AC_RMS_CODE
|
||||
or before_tx["max_abs_i"] >= STRONG_COMPONENT_CODE
|
||||
or before_tx["max_abs_q"] >= STRONG_COMPONENT_CODE
|
||||
)
|
||||
before_tx.update({
|
||||
"ac_rms_code": before_tx_ac_rms,
|
||||
"unexpected_strong_signal": before_tx_strong,
|
||||
})
|
||||
result["rx_level_before_tx"] = before_tx
|
||||
if before_tx["saturation_detected"]:
|
||||
raise RuntimeError("RX clipping до TX; RF TX = 0")
|
||||
if before_tx_strong:
|
||||
raise RuntimeError("Неожиданно сильный фон непосредственно перед TX; RF TX = 0")
|
||||
guard.register_transmit(); result["rf_tx_count"] = guard.transmit_calls
|
||||
transmitted = actual_runner(transfer_command(RAW / "tx_stream.cs8", EXPECTED_TX_SAMPLES, transmit=True))
|
||||
tx_stdout, tx_stderr = transmitted.stdout or "", transmitted.stderr or ""
|
||||
(RAW / "tx_stdout.txt").write_text(tx_stdout, encoding="utf-8")
|
||||
(RAW / "tx_stderr.txt").write_text(tx_stderr, encoding="utf-8")
|
||||
result["tx_process"] = {"returncode": transmitted.returncode,
|
||||
"stdout_file": "tx_stdout.txt", "stderr_file": "tx_stderr.txt"}
|
||||
result["tx_counter_summary"] = lab064.counter_summary(tx_stdout + tx_stderr, "underrun")
|
||||
if transmitted.returncode:
|
||||
raise RuntimeError("Единственный TX завершился ошибкой; повтор запрещён")
|
||||
if process.wait(RX_SECONDS + 10):
|
||||
raise RuntimeError("RX завершился ошибкой; повтор запрещён")
|
||||
process.close_logs()
|
||||
rx_log = (RAW / "rx_stdout.txt").read_text(encoding="utf-8") + (RAW / "rx_stderr.txt").read_text(encoding="utf-8")
|
||||
result["rx_counter_summary"] = lab064.counter_summary(rx_log, "overrun")
|
||||
result["rx_samples"] = receive_path.stat().st_size // 2
|
||||
result["rx_sha256"] = sha256(receive_path)
|
||||
result["rx_level"] = hardware.receive_level(receive_path)
|
||||
result["rx_clipping"] = bool(result["rx_level"]["saturation_detected"])
|
||||
result["passed"] = result["rx_samples"] == RX_SAMPLES
|
||||
validity = lab064.capture_validity(result)
|
||||
if not validity["valid"] or not result["passed"]:
|
||||
result["passed"] = False
|
||||
raise RuntimeError("INVALID_CAPTURE; запись сохранена, повтор запрещён")
|
||||
except Exception as error:
|
||||
result["error"] = str(error)
|
||||
raise
|
||||
finally:
|
||||
errors = []
|
||||
if process is not None:
|
||||
try: process.terminate()
|
||||
except Exception as error: errors.append(str(error))
|
||||
if clock_started: errors.extend(lab054.disable_clock_outputs(actual_runner))
|
||||
try:
|
||||
after = process_checker(); result["process_after"] = after.to_dict(); lab054.require_clear(after)
|
||||
except Exception as error: errors.append(str(error))
|
||||
result["cleanup_errors"] = errors
|
||||
result["passed"] = result["passed"] and not errors
|
||||
save_new(RAW / "capture_result.json", result)
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUTPUT / "capture_result.json").write_text(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n", encoding="utf-8")
|
||||
if not result["passed"]:
|
||||
raise RuntimeError("Lab066 RF-сеанс невалиден; повтор запрещён")
|
||||
return result
|
||||
|
||||
|
||||
def locate_first_packet(tx: lab058.Cs8File, rx: lab058.Cs8File) -> dict:
|
||||
"""Найти первый пакет после строгого verdict, не раскрывая границы декодеру."""
|
||||
stride = MODE.samples_per_symbol
|
||||
reference = tx.read(GUARD_SAMPLES, PACKET_SAMPLES).astype(np.complex64)
|
||||
received = rx.read(0, rx.size).astype(np.complex64)
|
||||
reference = reference[::stride] - np.mean(reference[::stride])
|
||||
received = received[::stride] - np.mean(received[::stride])
|
||||
correlation = signal.correlate(received, reference, mode="valid", method="fft")
|
||||
coarse = int(np.argmax(abs(correlation))) * stride
|
||||
full_reference = tx.read(GUARD_SAMPLES, PACKET_SAMPLES).astype(np.complex128)
|
||||
full_reference -= np.mean(full_reference)
|
||||
starts = range(max(0, coarse - 2 * stride), min(rx.size - PACKET_SAMPLES, coarse + 2 * stride) + 1)
|
||||
scores = []
|
||||
for start in starts:
|
||||
values = rx.read(start, PACKET_SAMPLES).astype(np.complex128)
|
||||
values -= np.mean(values)
|
||||
scores.append(abs(np.vdot(full_reference, values)))
|
||||
first_start = starts.start + int(np.argmax(scores))
|
||||
return {
|
||||
"first_packet_rx_start_sample": first_start,
|
||||
"stream_rx_start_sample": first_start - GUARD_SAMPLES,
|
||||
"coarse_first_packet_start_sample": coarse,
|
||||
"method": "first known packet correlation after strict receiver verdict",
|
||||
}
|
||||
|
||||
|
||||
def physical_packet_diagnostics(tx: lab058.Cs8File, rx: lab058.Cs8File,
|
||||
location: dict) -> dict:
|
||||
"""Оценить 16 пакетов известным TX только после строгого решения."""
|
||||
rows = []
|
||||
stream_start = location["stream_rx_start_sample"]
|
||||
for layout, frame in zip(validate_prepared()["layout"], plans()):
|
||||
packet_index = layout["packet_index"]
|
||||
tx_start = layout["start_sample"]
|
||||
rx_start = stream_start + tx_start
|
||||
transmitted = tx.read(tx_start, PACKET_SAMPLES)
|
||||
received = rx.read(rx_start, PACKET_SAMPLES)
|
||||
expected = 2.0 * frame.plan.frame_bits.astype(np.float64) - 1.0
|
||||
metrics, _working = lab058.analyze_values(received, expected, MODE.symbol_rate)
|
||||
channel, _arrays = lab058.channel_metrics(
|
||||
transmitted, received, MODE.symbol_rate, metrics["oracle"]["cfo_hz"]
|
||||
)
|
||||
oracle = metrics["oracle"]["metrics"]
|
||||
rows.append({
|
||||
"packet_index": packet_index, "tx_start_sample": tx_start,
|
||||
"rx_start_sample": rx_start,
|
||||
"symbol_error_rate": oracle["errors"] / oracle["symbols"],
|
||||
"symbol_rms_deviation": float(np.sqrt(
|
||||
oracle["residual_power"] / oracle["matched_component_power"]
|
||||
)),
|
||||
"matched_component_power": oracle["matched_component_power"],
|
||||
"residual_power": oracle["residual_power"],
|
||||
"matched_residual_db": float(10 * np.log10(
|
||||
oracle["matched_component_power"] / oracle["residual_power"]
|
||||
)),
|
||||
"band_gain_nonuniformity_p95_p05_db": channel["amplitude_ripple_p95_p05_db"],
|
||||
})
|
||||
return {
|
||||
"method": "Lab058 oracle diagnostics per packet after strict verdict",
|
||||
"rows": rows,
|
||||
"comparison": {
|
||||
"lab056_B_oracle_symbol_error_rate": 0.03495896,
|
||||
"lab056_B_oracle_evm_rms": 0.845350,
|
||||
"lab062_source": "data/processed/lab062/rf_analysis.json",
|
||||
"scope": "Lab056 B and Lab062 are contextual comparisons, not identical estimands",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def summarize_analysis(result: dict) -> dict:
|
||||
"""Свести строгие и физические результаты без повторного декодирования."""
|
||||
legacy, t2 = result["run_a_legacy"], result["run_b_t2"]
|
||||
legacy_set, t2_set = set(legacy["packet_indexes"]), set(t2["packet_indexes"])
|
||||
layout = validate_prepared()["layout"]
|
||||
stream_start = result["location_after_strict_runs"]["stream_rx_start_sample"]
|
||||
packet_rows = []
|
||||
for layout_row in layout:
|
||||
index = layout_row["packet_index"]
|
||||
nominal = stream_start + layout_row["start_sample"]
|
||||
modes = {}
|
||||
for label, strict, accepted in (
|
||||
("legacy", legacy, index in legacy_set), ("t2", t2, index in t2_set)
|
||||
):
|
||||
candidates = [
|
||||
row for row in strict["rows"]
|
||||
if abs(row["absolute_refined"] - nominal) <= 64
|
||||
]
|
||||
best = max(candidates, key=lambda row: (
|
||||
row["packet_output"], row["crc"], row["fec"], row["header"],
|
||||
-abs(row["absolute_refined"] - nominal),
|
||||
)) if candidates else None
|
||||
modes[label] = {
|
||||
"candidate_at_expected_packet": best is not None,
|
||||
"header": accepted or bool(best and best["header"]),
|
||||
"fec": accepted or bool(best and best["fec"]),
|
||||
"crc": accepted or bool(best and best["crc"]),
|
||||
"packet_output": accepted,
|
||||
}
|
||||
packet_rows.append({"packet_index": index, **modes})
|
||||
|
||||
physical_rows = result["physical_diagnostics"]["rows"]
|
||||
matched = float(np.mean([row["matched_component_power"] for row in physical_rows]))
|
||||
residual = float(np.mean([row["residual_power"] for row in physical_rows]))
|
||||
physical = {
|
||||
"symbol_error_rate": float(np.mean([row["symbol_error_rate"] for row in physical_rows])),
|
||||
"rms_symbol_deviation": float(np.sqrt(np.mean([
|
||||
row["symbol_rms_deviation"] ** 2 for row in physical_rows
|
||||
]))),
|
||||
"matched_component_power": matched,
|
||||
"residual_power": residual,
|
||||
"matched_residual_db": float(10 * np.log10(matched / residual)),
|
||||
"band_gain_nonuniformity_p95_p05_db_median": float(np.median([
|
||||
row["band_gain_nonuniformity_p95_p05_db"] for row in physical_rows
|
||||
])),
|
||||
"band_gain_nonuniformity_p95_p05_db_min": float(np.min([
|
||||
row["band_gain_nonuniformity_p95_p05_db"] for row in physical_rows
|
||||
])),
|
||||
"band_gain_nonuniformity_p95_p05_db_max": float(np.max([
|
||||
row["band_gain_nonuniformity_p95_p05_db"] for row in physical_rows
|
||||
])),
|
||||
}
|
||||
lab056 = {
|
||||
"symbol_error_rate": 0.03495896464646465,
|
||||
"rms_symbol_deviation": 0.8453497751219741,
|
||||
"matched_residual_db": 1.4648455366051558,
|
||||
"band_gain_nonuniformity_db": 5.471381501203027,
|
||||
}
|
||||
lab062_result = json.loads(
|
||||
(ROOT / "data/processed/lab062/rf_analysis.json").read_text(encoding="utf-8")
|
||||
)["lab058_quality"]
|
||||
lab062 = {
|
||||
"symbol_error_rate": lab062_result["symbol_error_rate"],
|
||||
"rms_symbol_deviation": lab062_result["evm_rms"],
|
||||
"matched_residual_db": lab062_result["matched_residual_db"],
|
||||
"band_gain_nonuniformity_db": lab062_result[
|
||||
"band_gain_nonuniformity_p95_p05_db_median"
|
||||
],
|
||||
"capture_status": "RF_SESSION_INVALID_TX_UNDERRUN",
|
||||
}
|
||||
|
||||
def differences(reference: dict) -> dict:
|
||||
return {
|
||||
"symbol_error_rate_absolute": (
|
||||
physical["symbol_error_rate"] - reference["symbol_error_rate"]
|
||||
),
|
||||
"rms_symbol_deviation_absolute": (
|
||||
physical["rms_symbol_deviation"] - reference["rms_symbol_deviation"]
|
||||
),
|
||||
"matched_residual_db": (
|
||||
physical["matched_residual_db"] - reference["matched_residual_db"]
|
||||
),
|
||||
"band_gain_nonuniformity_db": (
|
||||
physical["band_gain_nonuniformity_p95_p05_db_median"]
|
||||
- reference["band_gain_nonuniformity_db"]
|
||||
),
|
||||
}
|
||||
|
||||
return {
|
||||
"lab": "Lab066", "capture_valid": result["capture_valid"],
|
||||
"legacy": {key: legacy[key] for key in (
|
||||
"candidates", "header_pass", "fec_started", "fec_pass", "crc_started",
|
||||
"crc_pass", "packet_output", "exact_payload_match", "packet_indexes",
|
||||
)},
|
||||
"t2": {key: t2[key] for key in (
|
||||
"candidates", "header_pass", "fec_started", "fec_pass", "crc_started",
|
||||
"crc_pass", "packet_output", "exact_payload_match", "packet_indexes",
|
||||
)},
|
||||
"passed_by_both": sorted(legacy_set & t2_set),
|
||||
"passed_only_by_legacy": sorted(legacy_set - t2_set),
|
||||
"passed_only_by_t2": sorted(t2_set - legacy_set),
|
||||
"failed_by_both": sorted(set(range(1, PACKETS + 1)) - legacy_set - t2_set),
|
||||
"packet_rows": packet_rows, "physical": physical,
|
||||
"lab056_B": lab056, "difference_from_lab056_B": differences(lab056),
|
||||
"lab062": lab062, "difference_from_lab062": differences(lab062),
|
||||
"classification": "PARTIAL_PACKET_RECOVERY_NO_T2_GAIN",
|
||||
"primary_result": (
|
||||
"Аппаратно валидный Lab066 дал одинаковые 10/16 точных пакетов "
|
||||
"в legacy и T2; физическое качество лучше Lab056 B и невалидной Lab062."
|
||||
),
|
||||
"recommended_next_single_step": (
|
||||
"Отдельно разрешить офлайн-сравнение header-ошибок пакетов "
|
||||
"1-3, 9, 15-16 с принятыми пакетами по сохранённому RX; новый TX не нужен."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def write_summary_outputs(result: dict) -> dict:
|
||||
summary = summarize_analysis(result)
|
||||
save_new(OUTPUT / "rf_summary.json", summary)
|
||||
with (OUTPUT / "packet_comparison.csv").open("x", newline="", encoding="utf-8-sig") as stream:
|
||||
writer = csv.writer(stream)
|
||||
writer.writerow([
|
||||
"packet_index", "legacy_header", "legacy_fec", "legacy_crc",
|
||||
"legacy_packet_output", "t2_header", "t2_fec", "t2_crc", "t2_packet_output",
|
||||
])
|
||||
for row in summary["packet_rows"]:
|
||||
writer.writerow([
|
||||
row["packet_index"], row["legacy"]["header"], row["legacy"]["fec"],
|
||||
row["legacy"]["crc"], row["legacy"]["packet_output"],
|
||||
row["t2"]["header"], row["t2"]["fec"], row["t2"]["crc"],
|
||||
row["t2"]["packet_output"],
|
||||
])
|
||||
return summary
|
||||
|
||||
|
||||
def analyze_saved_capture() -> dict:
|
||||
"""Строго обработать одну будущую RX-запись обоими режимами."""
|
||||
metadata = validate_prepared()
|
||||
capture = json.loads((RAW / "capture_result.json").read_text(encoding="utf-8"))
|
||||
rx_path = RAW / "rx_stream.cs8"
|
||||
if (
|
||||
capture.get("lab") != "Lab066"
|
||||
or capture.get("rf_tx_count") != 1
|
||||
or capture.get("automatic_tx_repeats") != 0
|
||||
or capture.get("point") != asdict(POINT)
|
||||
or capture.get("parameters") != parameters()
|
||||
or capture.get("tx_sha256") != metadata["tx_sha256"]
|
||||
or capture.get("rx_sha256") != sha256(rx_path)
|
||||
):
|
||||
raise RuntimeError("Нет подтверждённой единственной RX-записи Lab066")
|
||||
validity = lab064.capture_validity(capture)
|
||||
if not validity["valid"]:
|
||||
result = {
|
||||
"lab": "Lab066", "stage": "STRICT_DUAL_OFFLINE_ANALYSIS",
|
||||
"classification": "INVALID_CAPTURE", "capture_valid": False,
|
||||
"capture_invalid_reasons": validity["invalid_reasons"],
|
||||
"rf_tx_count": capture.get("rf_tx_count"), "automatic_tx_repeats": 0,
|
||||
}
|
||||
save_new(OUTPUT / "rf_result.json", result)
|
||||
return result
|
||||
legacy = decode("legacy", rx_path, diagnostics=True)
|
||||
t2 = decode("t2", rx_path, diagnostics=True)
|
||||
strict_runs_complete = (
|
||||
legacy.get("candidate_overflow") == 0
|
||||
and t2.get("candidate_overflow") == 0
|
||||
and isinstance(legacy.get("rows"), list)
|
||||
and isinstance(t2.get("rows"), list)
|
||||
)
|
||||
if not strict_runs_complete:
|
||||
raise RuntimeError("Строгие legacy/T2 прогоны не завершены; oracle-диагностика запрещена")
|
||||
tx = lab058.Cs8File(RAW / "tx_stream.cs8")
|
||||
rx = lab058.Cs8File(rx_path)
|
||||
location = locate_first_packet(tx, rx)
|
||||
result = {
|
||||
"lab": "Lab066", "stage": "STRICT_DUAL_OFFLINE_ANALYSIS",
|
||||
"tx_sha256": metadata["tx_sha256"], "rx_sha256": capture["rx_sha256"],
|
||||
"capture_valid": validity["valid"], "capture_invalid_reasons": validity["invalid_reasons"],
|
||||
"run_a_legacy": legacy, "run_b_t2": t2,
|
||||
"same_rx_capture": True, "fallback_between_modes": False,
|
||||
"strict_runs_complete": True,
|
||||
"location_after_strict_runs": location,
|
||||
"physical_diagnostics_scope": "KNOWN_TX_ONLY_AFTER_COMPLETE_STRICT_RUNS",
|
||||
"physical_diagnostics": physical_packet_diagnostics(tx, rx, location),
|
||||
"comparison_targets": ["Lab056 B", "Lab062", "Lab066"],
|
||||
}
|
||||
save_new(OUTPUT / "rf_result.json", result)
|
||||
return result
|
||||
|
||||
|
||||
def summarize_saved_analysis() -> dict:
|
||||
return write_summary_outputs(json.loads(
|
||||
(OUTPUT / "rf_result.json").read_text(encoding="utf-8")
|
||||
))
|
||||
|
||||
|
||||
def run_functional_tests() -> None:
|
||||
assert EXPECTED_TX_SAMPLES == 3_676_096
|
||||
assert transfer_command("NUL", 100, transmit=True)[-7:-5] == ["-x", "20"]
|
||||
assert "-t" not in transfer_command("NUL", 100)
|
||||
try:
|
||||
capture_once(authorization=False)
|
||||
except PermissionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("RF без отдельного разрешения")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--stage",
|
||||
choices=("prepare", "software", "preflight", "capture", "analyze", "summarize"),
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument("--authorize-one-rf", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
if arguments.stage == "prepare": result = prepare()
|
||||
elif arguments.stage == "software": result = software_gate()
|
||||
elif arguments.stage == "preflight": result = preflight()
|
||||
elif arguments.stage == "capture": result = capture_once(authorization=arguments.authorize_one_rf)
|
||||
elif arguments.stage == "analyze": result = analyze_saved_capture()
|
||||
else: result = summarize_saved_analysis()
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False))
|
||||
print("RF TX NOW = 0")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
906
experiments/lab067_header_failure_analysis.py
Normal file
@@ -0,0 +1,906 @@
|
||||
"""Lab067: офлайн-анализ отказов PHY-заголовка в сохранённой записи Lab066.
|
||||
|
||||
Строгий результат Lab066 загружается без изменений. Известный TX используется
|
||||
только после этого результата для диагностического подсчёта ошибок.
|
||||
Аппаратные команды и передача RF в этой лаборатории отсутствуют.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
if __package__ in (None, ""):
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import matplotlib
|
||||
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from experiments import lab053_packet_processing as packet_processing
|
||||
from experiments import lab058_abab_symbol_quality as symbol_quality
|
||||
from experiments import lab066_full_packet_rf_retest as lab066
|
||||
from protocol import bpsk_carrier_tracking as carrier_tracking
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RAW = ROOT / "data/raw/lab066"
|
||||
LAB066 = ROOT / "data/processed/lab066"
|
||||
OUTPUT = ROOT / "data/processed/lab067"
|
||||
TX_PATH = RAW / "tx_stream.cs8"
|
||||
RX_PATH = RAW / "rx_stream.cs8"
|
||||
TX_SHA256 = "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199"
|
||||
RX_SHA256 = "72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0"
|
||||
RX_BYTES = 32_000_000
|
||||
RX_SAMPLES = 16_000_000
|
||||
SAMPLE_RATE_HZ = 8_000_000
|
||||
SYMBOL_RATE = 500_000
|
||||
SAMPLES_PER_SYMBOL = 16
|
||||
PREAMBLE_SYMBOLS = 1280
|
||||
HEADER_SYMBOLS = 144
|
||||
HEADER_BITS = 48
|
||||
HEADER_REPETITION = 3
|
||||
POSTHEADER_SYMBOLS = 128
|
||||
FAILED_PACKETS = {1, 2, 3, 9, 15, 16}
|
||||
PASSED_PACKETS = set(range(1, 17)) - FAILED_PACKETS
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
for block in iter(lambda: stream.read(1024 * 1024), b""):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def save_json(path: Path, value: Any) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(
|
||||
json.dumps(value, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def bits_text(bits: np.ndarray) -> str:
|
||||
return "".join(str(int(value)) for value in np.asarray(bits).reshape(-1))
|
||||
|
||||
|
||||
def finite(value: float) -> float | None:
|
||||
return float(value) if np.isfinite(value) else None
|
||||
|
||||
|
||||
def source_integrity() -> tuple[dict, dict, dict]:
|
||||
"""Проверить неизменность Lab066 до любой oracle-диагностики."""
|
||||
capture = json.loads((LAB066 / "capture_result.json").read_text(encoding="utf-8"))
|
||||
strict = json.loads((LAB066 / "rf_result.json").read_text(encoding="utf-8"))
|
||||
metadata = json.loads((LAB066 / "preparation.json").read_text(encoding="utf-8"))
|
||||
tx_digest = sha256(TX_PATH)
|
||||
rx_digest = sha256(RX_PATH)
|
||||
integrity = {
|
||||
"tx_path": str(TX_PATH.relative_to(ROOT)),
|
||||
"tx_bytes": TX_PATH.stat().st_size,
|
||||
"tx_samples": TX_PATH.stat().st_size // 2,
|
||||
"tx_sha256": tx_digest,
|
||||
"rx_path": str(RX_PATH.relative_to(ROOT)),
|
||||
"rx_bytes": RX_PATH.stat().st_size,
|
||||
"rx_samples": RX_PATH.stat().st_size // 2,
|
||||
"rx_duration_seconds": (RX_PATH.stat().st_size // 2) / SAMPLE_RATE_HZ,
|
||||
"rx_sha256": rx_digest,
|
||||
"sample_rate_hz": SAMPLE_RATE_HZ,
|
||||
}
|
||||
if (
|
||||
tx_digest != TX_SHA256
|
||||
or rx_digest != RX_SHA256
|
||||
or integrity["rx_bytes"] != RX_BYTES
|
||||
or integrity["rx_samples"] != RX_SAMPLES
|
||||
or metadata.get("tx_sha256") != TX_SHA256
|
||||
or metadata.get("parameters", {}).get("sample_rate_hz") != SAMPLE_RATE_HZ
|
||||
or capture.get("rx_sha256") != RX_SHA256
|
||||
or capture.get("rf_tx_count") != 1
|
||||
or capture.get("automatic_tx_repeats") != 0
|
||||
or capture.get("passed") is not True
|
||||
or strict.get("capture_valid") is not True
|
||||
):
|
||||
raise RuntimeError("Исходники или аппаратная валидность Lab066 не подтверждены")
|
||||
for mode in ("run_a_legacy", "run_b_t2"):
|
||||
result = strict[mode]
|
||||
if (
|
||||
result.get("header_pass") != 10
|
||||
or result.get("packet_output") != 10
|
||||
or result.get("exact_payload_match") != 10
|
||||
or result.get("packet_indexes") != sorted(PASSED_PACKETS)
|
||||
):
|
||||
raise RuntimeError("Исторический строгий результат Lab066 изменился")
|
||||
return integrity, metadata, strict
|
||||
|
||||
|
||||
def extract_symbols(
|
||||
received: symbol_quality.Cs8File,
|
||||
frame_start: int,
|
||||
symbol_count: int,
|
||||
mode: str,
|
||||
) -> dict:
|
||||
"""Повторить штатные timing/carrier операции для известного окна пакета."""
|
||||
processor = packet_processing.PacketProcessor(carrier_tracking_mode=mode)
|
||||
length = (symbol_count + 42) * SAMPLES_PER_SYMBOL
|
||||
segment = received.read(frame_start, length).astype(np.complex64)
|
||||
matched = processor.filter(processor.shift(segment))
|
||||
nominal = 16 * SAMPLES_PER_SYMBOL + processor.taps.size - 1
|
||||
timing = int(processor.timing(matched, nominal, processor.expected))
|
||||
initial = processor.carrier(
|
||||
matched, nominal, timing, processor.bits, len(processor.bits)
|
||||
)
|
||||
symbols = matched[
|
||||
nominal + timing:
|
||||
nominal + timing + symbol_count * SAMPLES_PER_SYMBOL:
|
||||
SAMPLES_PER_SYMBOL
|
||||
].astype(np.complex128)
|
||||
if symbols.size != symbol_count or initial["abs_h"] == 0:
|
||||
raise RuntimeError("Не удалось извлечь диагностическое окно символов")
|
||||
times = (np.arange(symbol_count, dtype=np.float64) + 0.5) / SYMBOL_RATE
|
||||
if mode == "legacy":
|
||||
corrected = symbols * np.exp(-2j * np.pi * initial["cfo_hz"] * times)
|
||||
gain = initial["abs_h"] * np.exp(1j * initial["phase_rad"])
|
||||
corrected /= gain
|
||||
phase_trace = (
|
||||
initial["phase_rad"] + 2 * np.pi * initial["cfo_hz"] * times
|
||||
)
|
||||
frequency_trace = np.full(symbol_count, initial["cfo_hz"], dtype=float)
|
||||
detector_error = np.angle(corrected * np.where(corrected.real >= 0, 1, -1))
|
||||
estimate = {
|
||||
"frequency_hz": float(initial["cfo_hz"]),
|
||||
"gain": complex(gain),
|
||||
"phase_fit_rmse_rad": None,
|
||||
"squared_coherence": None,
|
||||
}
|
||||
else:
|
||||
estimate_value = carrier_tracking.estimate_p3(
|
||||
symbols[:PREAMBLE_SYMBOLS], processor.expected, SYMBOL_RATE
|
||||
)
|
||||
tracked = carrier_tracking.track_t2(
|
||||
symbols, SYMBOL_RATE, estimate_value, start_index=PREAMBLE_SYMBOLS
|
||||
)
|
||||
corrected = tracked.corrected.copy()
|
||||
phase_trace = tracked.phase_rad.copy()
|
||||
frequency_trace = tracked.frequency_hz.copy()
|
||||
detector_error = tracked.detector_error_rad.copy()
|
||||
estimate = {
|
||||
"frequency_hz": float(estimate_value.frequency_hz),
|
||||
"gain": complex(estimate_value.gain),
|
||||
"phase_fit_rmse_rad": float(estimate_value.phase_fit_rmse_rad),
|
||||
"squared_coherence": float(estimate_value.squared_coherence),
|
||||
}
|
||||
return {
|
||||
"corrected": corrected,
|
||||
"raw_symbols": symbols,
|
||||
"matched": matched,
|
||||
"hard_bits": (corrected.real >= 0).astype(np.uint8),
|
||||
"timing_samples": timing,
|
||||
"timing_fraction": timing / SAMPLES_PER_SYMBOL,
|
||||
"nominal": nominal,
|
||||
"sample_positions": frame_start + nominal + timing + np.arange(symbol_count) * SAMPLES_PER_SYMBOL,
|
||||
"initial": estimate,
|
||||
"phase_trace": phase_trace,
|
||||
"frequency_trace": frequency_trace,
|
||||
"detector_error": detector_error,
|
||||
}
|
||||
|
||||
|
||||
def reference_sync_diagnostic(extracted: dict, expected_bits: np.ndarray) -> dict:
|
||||
"""Oracle-проверка timing/CFO после штатного результата, без влияния на него."""
|
||||
expected = 2.0 * np.asarray(expected_bits, dtype=np.float64) - 1.0
|
||||
indexes = np.arange(len(expected), dtype=np.int64)
|
||||
candidates = []
|
||||
for offset in range(-SAMPLES_PER_SYMBOL // 2, SAMPLES_PER_SYMBOL // 2 + 1):
|
||||
start = extracted["nominal"] + offset
|
||||
symbols = extracted["matched"][
|
||||
start:start + len(expected) * SAMPLES_PER_SYMBOL:SAMPLES_PER_SYMBOL
|
||||
].astype(np.complex128)
|
||||
if len(symbols) != len(expected):
|
||||
continue
|
||||
frequency, gain, coherence = symbol_quality.fit_cfo_gain(
|
||||
symbols, expected, indexes, SYMBOL_RATE, len(expected)
|
||||
)
|
||||
candidates.append((coherence, offset, frequency, gain))
|
||||
coherence, timing, frequency, gain = max(candidates, key=lambda item: item[0])
|
||||
return {
|
||||
"scope": "KNOWN_BITS_AFTER_STRICT_VERDICT",
|
||||
"timing_offset_samples": int(timing),
|
||||
"timing_fraction": timing / SAMPLES_PER_SYMBOL,
|
||||
"frequency_hz": float(frequency),
|
||||
"gain_real": float(gain.real),
|
||||
"gain_imag": float(gain.imag),
|
||||
"coherence": float(coherence),
|
||||
}
|
||||
|
||||
|
||||
def physical_metrics(extracted: dict, expected_bits: np.ndarray, selected: slice) -> dict:
|
||||
expected = 2.0 * np.asarray(expected_bits[selected], dtype=np.float64) - 1.0
|
||||
raw = extracted["raw_symbols"][selected]
|
||||
indexes = np.arange(len(extracted["raw_symbols"]), dtype=np.float64)[selected]
|
||||
frequency = extracted["initial"]["frequency_hz"]
|
||||
times = (indexes + 0.5) / SYMBOL_RATE
|
||||
derotated = raw * np.exp(-2j * np.pi * frequency * times)
|
||||
gain = complex(extracted["initial"]["gain"])
|
||||
predicted = gain * expected
|
||||
residual = derotated - predicted
|
||||
matched_power = float(np.mean(abs(predicted) ** 2))
|
||||
residual_power = float(np.mean(abs(residual) ** 2))
|
||||
normalized = derotated / gain
|
||||
errors = int(np.count_nonzero((normalized.real >= 0) != (expected > 0)))
|
||||
correlation = float(
|
||||
abs(np.vdot(expected, raw))
|
||||
/ math.sqrt(float(np.vdot(expected, expected).real * np.vdot(raw, raw).real) + 1e-30)
|
||||
)
|
||||
return {
|
||||
"symbols": int(len(expected)),
|
||||
"errors": errors,
|
||||
"error_rate": errors / len(expected),
|
||||
"correlation": correlation,
|
||||
"matched_component_power": matched_power,
|
||||
"residual_power": residual_power,
|
||||
"matched_residual_db": finite(10 * np.log10(matched_power / residual_power)),
|
||||
"rms_symbol_deviation": float(np.sqrt(residual_power / matched_power)),
|
||||
}
|
||||
|
||||
|
||||
def header_diagnostics(received_soft: np.ndarray, expected_bits: np.ndarray) -> dict:
|
||||
soft = np.asarray(received_soft[:HEADER_SYMBOLS], dtype=np.float64)
|
||||
raw = (soft >= 0.0).astype(np.uint8)
|
||||
expected = np.asarray(expected_bits[:HEADER_SYMBOLS], dtype=np.uint8)
|
||||
raw_groups = raw.reshape(HEADER_BITS, HEADER_REPETITION)
|
||||
expected_groups = expected.reshape(HEADER_BITS, HEADER_REPETITION)
|
||||
voted = (np.sum(soft.reshape(HEADER_BITS, HEADER_REPETITION), axis=1) >= 0.0).astype(np.uint8)
|
||||
expected_voted = expected_groups[:, 0]
|
||||
fields = lab066.phy.lab045.lab044.bits_to_bytes(voted)
|
||||
magic, version, length, checksum = struct.unpack(
|
||||
lab066.phy.lab045.PHY_HEADER_FIELDS_FORMAT, fields
|
||||
)
|
||||
decoded = lab066.phy.lab045.decode_phy_header_soft(soft)
|
||||
field_errors = {
|
||||
"magic": int(np.count_nonzero(voted[:16] != expected_voted[:16])),
|
||||
"version": int(np.count_nonzero(voted[16:24] != expected_voted[16:24])),
|
||||
"length": int(np.count_nonzero(voted[24:40] != expected_voted[24:40])),
|
||||
"checksum": int(np.count_nonzero(voted[40:48] != expected_voted[40:48])),
|
||||
}
|
||||
return {
|
||||
"expected_header_bits": bits_text(expected),
|
||||
"received_raw_header_bits": bits_text(raw),
|
||||
"raw_header_errors": int(np.count_nonzero(raw != expected)),
|
||||
"copy_errors": [
|
||||
int(np.count_nonzero(raw_groups[:, index] != expected_groups[:, index]))
|
||||
for index in range(HEADER_REPETITION)
|
||||
],
|
||||
"voted_header_bits": bits_text(voted),
|
||||
"expected_voted_bits": bits_text(expected_voted),
|
||||
"voted_header_errors": int(np.count_nonzero(voted != expected_voted)),
|
||||
"magic_errors": field_errors["magic"],
|
||||
"version_errors": field_errors["version"],
|
||||
"length_errors": field_errors["length"],
|
||||
"checksum_errors": field_errors["checksum"],
|
||||
"decoded_magic": magic,
|
||||
"decoded_version": version,
|
||||
"decoded_length": length,
|
||||
"decoded_checksum": checksum,
|
||||
"accepted": bool(decoded["passed"]),
|
||||
"reason": decoded["reason"],
|
||||
"raw_error_positions": np.flatnonzero(raw != expected).astype(int).tolist(),
|
||||
"voted_error_positions": np.flatnonzero(voted != expected_voted).astype(int).tolist(),
|
||||
}
|
||||
|
||||
|
||||
def gap_metrics(received: symbol_quality.Cs8File, start: int, count: int) -> dict:
|
||||
values = received.read(start, count).astype(np.complex128)
|
||||
mean = np.mean(values)
|
||||
centered = values - mean
|
||||
edge = min(count // 4, 8000)
|
||||
first_power = float(np.mean(abs(centered[:edge]) ** 2))
|
||||
middle_start = max(edge, count // 2 - edge // 2)
|
||||
middle_power = float(np.mean(abs(centered[middle_start:middle_start + edge]) ** 2))
|
||||
tail_ratio_db = float(10 * np.log10(max(first_power, 1e-30) / max(middle_power, 1e-30)))
|
||||
return {
|
||||
"length_samples": count,
|
||||
"duration_seconds": count / SAMPLE_RATE_HZ,
|
||||
"background_ac_rms": float(np.sqrt(np.mean(abs(centered) ** 2))),
|
||||
"mean_i": float(mean.real),
|
||||
"mean_q": float(mean.imag),
|
||||
"residual_energy_mean": float(np.mean(abs(centered) ** 2)),
|
||||
"leading_edge_to_middle_db": tail_ratio_db,
|
||||
"previous_waveform_tail_detected": bool(tail_ratio_db > 3.0),
|
||||
}
|
||||
|
||||
|
||||
def strict_candidate_for_packet(rows: list[dict], expected: int) -> dict | None:
|
||||
candidates = [row for row in rows if abs(int(row["absolute_refined"]) - expected) <= 64]
|
||||
if not candidates:
|
||||
return None
|
||||
return min(
|
||||
candidates,
|
||||
key=lambda row: (
|
||||
abs(int(row["absolute_refined"]) - expected),
|
||||
int(row["absolute_refined"]),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def candidate_analysis(strict: dict, expected_starts: list[int]) -> tuple[dict, list[dict]]:
|
||||
rows = strict["run_a_legacy"]["rows"]
|
||||
packet_samples = lab066.PACKET_SAMPLES
|
||||
gap_intervals = []
|
||||
metadata = json.loads((LAB066 / "preparation.json").read_text(encoding="utf-8"))
|
||||
stream_start = strict["location_after_strict_runs"]["stream_rx_start_sample"]
|
||||
for layout in metadata["layout"][:-1]:
|
||||
low = stream_start + layout["following_gap_start_sample"]
|
||||
gap_intervals.append((low, low + layout["following_gap_samples"]))
|
||||
output_rows = []
|
||||
true = repeated = gaps = unrelated = 0
|
||||
true_positions = set()
|
||||
per_packet = {index: 0 for index in range(1, 17)}
|
||||
for number, row in enumerate(rows, start=1):
|
||||
position = int(row["absolute_refined"])
|
||||
distances = np.abs(np.asarray(expected_starts, dtype=np.int64) - position)
|
||||
nearest_index = int(np.argmin(distances)) + 1
|
||||
delta = position - expected_starts[nearest_index - 1]
|
||||
in_gap = any(low <= position < high for low, high in gap_intervals)
|
||||
if abs(delta) <= 64:
|
||||
category = "TRUE_PREAMBLE_POSITION"
|
||||
true += 1
|
||||
true_positions.add((nearest_index, position))
|
||||
elif -64 <= delta < packet_samples:
|
||||
category = "REPEATED_PRBS_LOCAL_MAXIMUM"
|
||||
repeated += 1
|
||||
elif in_gap:
|
||||
category = "ZERO_GAP"
|
||||
gaps += 1
|
||||
else:
|
||||
category = "UNRELATED"
|
||||
unrelated += 1
|
||||
if category in {"TRUE_PREAMBLE_POSITION", "REPEATED_PRBS_LOCAL_MAXIMUM"}:
|
||||
per_packet[nearest_index] += 1
|
||||
output_rows.append({
|
||||
"candidate_number": number,
|
||||
"position": position,
|
||||
"nearest_packet": nearest_index,
|
||||
"difference_samples": delta,
|
||||
"category": category,
|
||||
"strict_reason": row["reason"],
|
||||
})
|
||||
summary = {
|
||||
"total_strict_candidates": len(rows),
|
||||
"true_preamble_position_checks": true,
|
||||
"unique_true_preamble_positions": len(true_positions),
|
||||
"duplicate_true_position_checks": true - len(true_positions),
|
||||
"repeated_prbs_local_maxima": repeated,
|
||||
"zero_gap_candidates": gaps,
|
||||
"unrelated_candidates": unrelated,
|
||||
"checks_associated_with_each_real_preamble": per_packet,
|
||||
"detector_statistics": strict["run_a_legacy"]["detector_statistics"],
|
||||
"candidate_processing_state": "NEW_PACKET_MEASURE_AND_INDEPENDENT_DECODE_PER_CANDIDATE",
|
||||
"persistent_state_between_rejected_candidates": False,
|
||||
"accepted_until_changes_only_after_crc_pass": True,
|
||||
}
|
||||
return summary, output_rows
|
||||
|
||||
|
||||
def distribution(rows: list[dict], packets: set[int], key: str) -> dict:
|
||||
values = np.asarray([row[key] for row in rows if row["packet"] in packets], dtype=float)
|
||||
return {
|
||||
"minimum": float(np.min(values)),
|
||||
"median": float(np.median(values)),
|
||||
"maximum": float(np.max(values)),
|
||||
"values": {str(row["packet"]): row[key] for row in rows if row["packet"] in packets},
|
||||
}
|
||||
|
||||
|
||||
def assign_classifications(rows: list[dict]) -> None:
|
||||
passed = [row for row in rows if row["packet"] in PASSED_PACKETS]
|
||||
limits = {
|
||||
"preamble_errors_max": max(row["preamble_errors"] for row in passed),
|
||||
"preamble_mr_p10": float(np.percentile([row["preamble_matched_residual_db"] for row in passed], 10)),
|
||||
"preamble_rms_p90": float(np.percentile([row["preamble_rms_symbol_deviation"] for row in passed], 90)),
|
||||
"correlation_p10": float(np.percentile([row["preamble_correlation"] for row in passed], 10)),
|
||||
}
|
||||
for row in rows:
|
||||
if row["packet"] in PASSED_PACKETS:
|
||||
row["classification"] = "PASSED"
|
||||
continue
|
||||
physical_bad = sum((
|
||||
row["preamble_errors"] > limits["preamble_errors_max"],
|
||||
row["preamble_matched_residual_db"] < limits["preamble_mr_p10"],
|
||||
row["preamble_rms_symbol_deviation"] > limits["preamble_rms_p90"],
|
||||
row["preamble_correlation"] < limits["correlation_p10"],
|
||||
))
|
||||
if row["preamble_errors"] > limits["preamble_errors_max"]:
|
||||
row["classification"] = "PREAMBLE_ALREADY_BAD"
|
||||
elif physical_bad >= 2:
|
||||
row["classification"] = "LOCAL_RF_DEGRADATION"
|
||||
elif row["header_voted_errors"] > 0:
|
||||
row["classification"] = "HEADER_SPECIFIC_FAILURE"
|
||||
else:
|
||||
row["classification"] = "NOT_LOCALIZED"
|
||||
|
||||
|
||||
def group_summary(rows: list[dict]) -> dict:
|
||||
groups = {"START": range(1, 5), "MIDDLE_1": range(5, 9), "MIDDLE_2": range(9, 13), "END": range(13, 17)}
|
||||
result = {}
|
||||
for name, indexes in groups.items():
|
||||
selected = [row for row in rows if row["packet"] in indexes]
|
||||
result[name] = {
|
||||
"packets": list(indexes),
|
||||
"preamble_error_rate": float(np.mean([row["preamble_error_rate"] for row in selected])),
|
||||
"header_error_rate": float(np.mean([row["header_raw_errors"] / HEADER_SYMBOLS for row in selected])),
|
||||
"matched_power": float(np.mean([row["local_matched_power"] for row in selected])),
|
||||
"residual_power": float(np.mean([row["local_residual_power"] for row in selected])),
|
||||
"matched_residual_db": float(10 * np.log10(
|
||||
np.mean([row["local_matched_power"] for row in selected])
|
||||
/ np.mean([row["local_residual_power"] for row in selected])
|
||||
)),
|
||||
"initial_frequency_offset_hz": float(np.mean([row["legacy_initial_frequency_hz"] for row in selected])),
|
||||
"timing_offset_samples": float(np.mean([row["timing_offset_samples"] for row in selected])),
|
||||
"background_ac_rms": float(np.mean([row["gap_background_ac_rms"] for row in selected])),
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def make_plots(rows: list[dict], transitions: dict[int, dict], candidates: dict) -> list[str]:
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
packets = [row["packet"] for row in rows]
|
||||
colors = ["C3" if packet in FAILED_PACKETS else "C0" for packet in packets]
|
||||
plots = []
|
||||
|
||||
def save(name: str) -> None:
|
||||
plt.tight_layout()
|
||||
plt.savefig(OUTPUT / name, dpi=160)
|
||||
plt.close()
|
||||
plots.append(name)
|
||||
|
||||
plt.figure(figsize=(10, 4.5))
|
||||
plt.bar(packets, [row["preamble_errors"] for row in rows], color=colors)
|
||||
plt.xticks(packets); plt.xlabel("Пакет"); plt.ylabel("Ошибки преамбулы, бит")
|
||||
plt.grid(axis="y", alpha=.25); save("preamble_errors_by_packet.png")
|
||||
|
||||
plt.figure(figsize=(10, 4.5))
|
||||
x = np.arange(1, 17)
|
||||
plt.bar(x - .18, [row["header_raw_errors"] for row in rows], width=.36, label="raw")
|
||||
plt.bar(x + .18, [row["header_voted_errors"] for row in rows], width=.36, label="voted", color=colors)
|
||||
plt.xticks(x); plt.xlabel("Пакет"); plt.ylabel("Ошибки PHY header, бит"); plt.legend()
|
||||
plt.grid(axis="y", alpha=.25); save("header_errors_by_packet.png")
|
||||
|
||||
figure, axes = plt.subplots(3, 1, figsize=(10, 8), sharex=True)
|
||||
axes[0].bar(packets, [row["local_matched_power"] for row in rows], color=colors)
|
||||
axes[0].set_ylabel("Matched power")
|
||||
axes[1].bar(packets, [row["local_residual_power"] for row in rows], color=colors)
|
||||
axes[1].set_ylabel("Residual power")
|
||||
axes[2].bar(packets, [row["local_matched_residual_db"] for row in rows], color=colors)
|
||||
axes[2].set_ylabel("Matched/residual, dB"); axes[2].set_xlabel("Пакет"); axes[2].set_xticks(packets)
|
||||
for axis in axes: axis.grid(axis="y", alpha=.25)
|
||||
save("local_signal_quality_by_packet.png")
|
||||
|
||||
matrix = np.zeros((16, HEADER_SYMBOLS), dtype=np.uint8)
|
||||
for row in rows:
|
||||
for position in row["header_raw_error_positions"]:
|
||||
matrix[row["packet"] - 1, position] = 1
|
||||
plt.figure(figsize=(13, 5))
|
||||
plt.imshow(matrix, aspect="auto", interpolation="nearest", cmap="Reds")
|
||||
plt.xlabel("Позиция защищённого PHY header, 0...143"); plt.ylabel("Пакет")
|
||||
plt.yticks(np.arange(16), np.arange(1, 17)); plt.colorbar(label="Ошибка")
|
||||
save("header_error_position_map.png")
|
||||
|
||||
representative = [4, 1, 14, 16]
|
||||
figure, axes = plt.subplots(len(representative), 3, figsize=(13, 10), sharex=True)
|
||||
for line, packet in enumerate(representative):
|
||||
values = transitions[packet]
|
||||
x = np.arange(-128, HEADER_SYMBOLS + 128)
|
||||
axes[line, 0].plot(x, values["corrected_real"], lw=.7)
|
||||
axes[line, 0].scatter(x[values["errors"]], np.asarray(values["corrected_real"])[values["errors"]], s=12, color="red")
|
||||
axes[line, 1].plot(x, values["corrected_imag"], lw=.7)
|
||||
axes[line, 2].plot(x, values["phase_error_rad"], lw=.7)
|
||||
for axis in axes[line]:
|
||||
axis.axvline(0, color="black", lw=.8); axis.axvline(HEADER_SYMBOLS, color="black", lw=.8)
|
||||
axis.grid(alpha=.2)
|
||||
axes[line, 0].set_ylabel(f"P{packet}")
|
||||
axes[0, 0].set_title("Re, ошибки красным"); axes[0, 1].set_title("Im"); axes[0, 2].set_title("Фазовая ошибка, rad")
|
||||
for axis in axes[-1]: axis.set_xlabel("Символ относительно начала header")
|
||||
save("preamble_header_transition.png")
|
||||
|
||||
figure, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
|
||||
axes[0].bar(packets, [row["timing_offset_samples"] for row in rows], color=colors)
|
||||
axes[0].set_ylabel("Timing, samples")
|
||||
axes[1].plot(packets, [row["legacy_initial_frequency_hz"] for row in rows], "o-", label="legacy")
|
||||
axes[1].plot(packets, [row["t2_initial_frequency_hz"] for row in rows], "s-", label="T2/P3")
|
||||
axes[1].set_ylabel("CFO, Hz"); axes[1].set_xlabel("Пакет"); axes[1].legend()
|
||||
for axis in axes: axis.grid(alpha=.25)
|
||||
save("timing_and_cfo_by_packet.png")
|
||||
|
||||
plt.figure(figsize=(10, 4.5))
|
||||
plt.bar(packets, [row["gap_background_ac_rms"] for row in rows], color=colors)
|
||||
plt.xlabel("Пакет"); plt.ylabel("AC RMS перед пакетом"); plt.xticks(packets); plt.grid(axis="y", alpha=.25)
|
||||
save("gap_background_by_packet.png")
|
||||
|
||||
per_packet = candidates["checks_associated_with_each_real_preamble"]
|
||||
plt.figure(figsize=(10, 4.5))
|
||||
plt.bar(packets, [per_packet[str(packet)] if str(packet) in per_packet else per_packet[packet] for packet in packets], color=colors)
|
||||
plt.xlabel("Пакет"); plt.ylabel("Строгие проверки кандидатов"); plt.xticks(packets); plt.grid(axis="y", alpha=.25)
|
||||
save("candidate_checks_by_packet.png")
|
||||
return plots
|
||||
|
||||
|
||||
def write_csvs(rows: list[dict], candidate_rows: list[dict], bit_rows: list[dict], transition_rows: list[dict]) -> None:
|
||||
main_fields = [
|
||||
"packet", "strict_result", "candidate_position", "expected_packet_position", "candidate_difference_samples",
|
||||
"preamble_errors", "preamble_error_rate", "preamble_correlation", "header_raw_errors", "header_voted_errors",
|
||||
"header_copy_1_errors", "header_copy_2_errors", "header_copy_3_errors", "magic_errors", "version_errors",
|
||||
"length_errors", "checksum_errors", "expected_header_bits", "received_raw_header_bits", "voted_header_bits",
|
||||
"header_accepted", "header_reason", "legacy_header_bits", "t2_header_bits",
|
||||
"legacy_initial_frequency_hz", "t2_initial_frequency_hz", "reference_frequency_hz",
|
||||
"legacy_frequency_error_vs_reference_hz", "initial_phase_error_rad", "timing_offset_samples", "fractional_timing_offset",
|
||||
"reference_timing_offset_samples", "reference_fractional_timing_offset", "local_matched_power", "local_residual_power",
|
||||
"local_matched_residual_db", "rms_symbol_deviation", "legacy_t2_header_differences", "classification",
|
||||
]
|
||||
with (OUTPUT / "header_packet_comparison.csv").open("w", newline="", encoding="utf-8-sig") as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=main_fields, extrasaction="ignore")
|
||||
writer.writeheader(); writer.writerows(rows)
|
||||
for name, values in (
|
||||
("header_bit_errors.csv", bit_rows),
|
||||
("candidate_analysis.csv", candidate_rows),
|
||||
("transition_metrics.csv", transition_rows),
|
||||
):
|
||||
with (OUTPUT / name).open("w", newline="", encoding="utf-8-sig") as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=list(values[0]))
|
||||
writer.writeheader(); writer.writerows(values)
|
||||
|
||||
|
||||
def analyze() -> dict:
|
||||
integrity, metadata, strict = source_integrity()
|
||||
received = symbol_quality.Cs8File(RX_PATH)
|
||||
stream_start = int(strict["location_after_strict_runs"]["stream_rx_start_sample"])
|
||||
expected_starts = [stream_start + int(item["start_sample"]) for item in metadata["layout"]]
|
||||
plans = lab066.plans()
|
||||
packet_rows: list[dict] = []
|
||||
bit_rows: list[dict] = []
|
||||
transition_rows: list[dict] = []
|
||||
transitions: dict[int, dict] = {}
|
||||
tx_headers = []
|
||||
|
||||
for packet, (layout, frame, rx_start) in enumerate(zip(metadata["layout"], plans, expected_starts), start=1):
|
||||
expected_frame = frame.plan.frame_bits.astype(np.uint8)
|
||||
expected_header = frame.plan.phy_header_bits.astype(np.uint8)
|
||||
tx_headers.append(expected_header)
|
||||
legacy = extract_symbols(received, rx_start, PREAMBLE_SYMBOLS + HEADER_SYMBOLS, "legacy")
|
||||
t2 = extract_symbols(received, rx_start, PREAMBLE_SYMBOLS + HEADER_SYMBOLS, "t2")
|
||||
transition = extract_symbols(
|
||||
received, rx_start, PREAMBLE_SYMBOLS + HEADER_SYMBOLS + POSTHEADER_SYMBOLS, "legacy"
|
||||
)
|
||||
preamble = physical_metrics(legacy, expected_frame, slice(0, PREAMBLE_SYMBOLS))
|
||||
local = physical_metrics(legacy, expected_frame, slice(PREAMBLE_SYMBOLS - 128, PREAMBLE_SYMBOLS + HEADER_SYMBOLS))
|
||||
reference = reference_sync_diagnostic(
|
||||
legacy, expected_frame[:PREAMBLE_SYMBOLS + HEADER_SYMBOLS]
|
||||
)
|
||||
header_legacy = header_diagnostics(
|
||||
legacy["corrected"].real[PREAMBLE_SYMBOLS:], expected_header
|
||||
)
|
||||
header_t2 = header_diagnostics(
|
||||
t2["corrected"].real[PREAMBLE_SYMBOLS:], expected_header
|
||||
)
|
||||
legacy_t2_differences = int(np.count_nonzero(
|
||||
legacy["hard_bits"][PREAMBLE_SYMBOLS:] != t2["hard_bits"][PREAMBLE_SYMBOLS:]
|
||||
))
|
||||
strict_row = strict_candidate_for_packet(strict["run_a_legacy"]["rows"], rx_start)
|
||||
candidate_position = int(strict_row["absolute_refined"]) if strict_row else None
|
||||
if packet == 1:
|
||||
preceding_start = stream_start
|
||||
preceding_count = lab066.GUARD_SAMPLES
|
||||
else:
|
||||
preceding_count = lab066.INTER_PACKET_GAP_SAMPLES
|
||||
preceding_start = rx_start - preceding_count
|
||||
gap = gap_metrics(received, preceding_start, preceding_count)
|
||||
trailing = gap_metrics(received, rx_start + lab066.PACKET_SAMPLES, lab066.GUARD_SAMPLES) if packet == 16 else None
|
||||
row = {
|
||||
"packet": packet,
|
||||
"strict_result": "PASS" if packet in PASSED_PACKETS else "HEADER_FAIL",
|
||||
"candidate_position": candidate_position,
|
||||
"expected_packet_position": rx_start,
|
||||
"candidate_difference_samples": None if candidate_position is None else candidate_position - rx_start,
|
||||
"timing_offset_samples": legacy["timing_samples"],
|
||||
"fractional_timing_offset": legacy["timing_fraction"],
|
||||
"timing_metric_confidence": None,
|
||||
"preamble_symbols": preamble["symbols"],
|
||||
"preamble_errors": preamble["errors"],
|
||||
"preamble_error_rate": preamble["error_rate"],
|
||||
"preamble_correlation": preamble["correlation"],
|
||||
"preamble_rms_symbol_deviation": preamble["rms_symbol_deviation"],
|
||||
"preamble_matched_power": preamble["matched_component_power"],
|
||||
"preamble_residual_power": preamble["residual_power"],
|
||||
"preamble_matched_residual_db": preamble["matched_residual_db"],
|
||||
"legacy_initial_frequency_hz": legacy["initial"]["frequency_hz"],
|
||||
"t2_initial_frequency_hz": t2["initial"]["frequency_hz"],
|
||||
"initial_phase_error_rad": float(np.angle(legacy["initial"]["gain"])),
|
||||
"frequency_difference_legacy_t2_hz": legacy["initial"]["frequency_hz"] - t2["initial"]["frequency_hz"],
|
||||
"reference_frequency_hz": reference["frequency_hz"],
|
||||
"legacy_frequency_error_vs_reference_hz": legacy["initial"]["frequency_hz"] - reference["frequency_hz"],
|
||||
"reference_timing_offset_samples": reference["timing_offset_samples"],
|
||||
"reference_fractional_timing_offset": reference["timing_fraction"],
|
||||
"reference_coherence": reference["coherence"],
|
||||
"legacy_expected_phase_drift_preamble_rad": float(2 * np.pi * legacy["initial"]["frequency_hz"] * PREAMBLE_SYMBOLS / SYMBOL_RATE),
|
||||
"legacy_expected_phase_drift_header_rad": float(2 * np.pi * legacy["initial"]["frequency_hz"] * HEADER_SYMBOLS / SYMBOL_RATE),
|
||||
"legacy_expected_phase_drift_full_packet_rad": float(2 * np.pi * legacy["initial"]["frequency_hz"] * len(expected_frame) / SYMBOL_RATE),
|
||||
"phase_error_from_cfo_mismatch_over_header_rad": float(
|
||||
2 * np.pi * (legacy["initial"]["frequency_hz"] - reference["frequency_hz"])
|
||||
* HEADER_SYMBOLS / SYMBOL_RATE
|
||||
),
|
||||
"expected_header_bits": header_legacy["expected_header_bits"],
|
||||
"received_raw_header_bits": header_legacy["received_raw_header_bits"],
|
||||
"header_raw_errors": header_legacy["raw_header_errors"],
|
||||
"header_copy_1_errors": header_legacy["copy_errors"][0],
|
||||
"header_copy_2_errors": header_legacy["copy_errors"][1],
|
||||
"header_copy_3_errors": header_legacy["copy_errors"][2],
|
||||
"voted_header_bits": header_legacy["voted_header_bits"],
|
||||
"header_voted_errors": header_legacy["voted_header_errors"],
|
||||
"magic_errors": header_legacy["magic_errors"],
|
||||
"version_errors": header_legacy["version_errors"],
|
||||
"length_errors": header_legacy["length_errors"],
|
||||
"checksum_errors": header_legacy["checksum_errors"],
|
||||
"decoded_magic": header_legacy["decoded_magic"],
|
||||
"decoded_length": header_legacy["decoded_length"],
|
||||
"header_accepted": header_legacy["accepted"],
|
||||
"header_reason": header_legacy["reason"],
|
||||
"header_raw_error_positions": header_legacy["raw_error_positions"],
|
||||
"header_voted_error_positions": header_legacy["voted_error_positions"],
|
||||
"legacy_header_bits": header_legacy["received_raw_header_bits"],
|
||||
"t2_header_bits": header_t2["received_raw_header_bits"],
|
||||
"legacy_t2_header_differences": legacy_t2_differences,
|
||||
"local_window": "last 128 preamble symbols plus complete 144-symbol header",
|
||||
"local_matched_power": local["matched_component_power"],
|
||||
"local_residual_power": local["residual_power"],
|
||||
"local_matched_residual_db": local["matched_residual_db"],
|
||||
"rms_symbol_deviation": local["rms_symbol_deviation"],
|
||||
"header_error_rate": header_legacy["raw_header_errors"] / HEADER_SYMBOLS,
|
||||
"preceding_zero_gap_length": gap["length_samples"],
|
||||
"gap_background_ac_rms": gap["background_ac_rms"],
|
||||
"gap_mean_i": gap["mean_i"], "gap_mean_q": gap["mean_q"],
|
||||
"gap_residual_energy": gap["residual_energy_mean"],
|
||||
"previous_waveform_tail_detected": gap["previous_waveform_tail_detected"],
|
||||
"gap_leading_edge_to_middle_db": gap["leading_edge_to_middle_db"],
|
||||
"trailing_guard_packet_16": trailing,
|
||||
}
|
||||
packet_rows.append(row)
|
||||
for position in range(HEADER_SYMBOLS):
|
||||
bit_rows.append({
|
||||
"packet": packet, "protected_bit_position": position,
|
||||
"field_bit_position": position // HEADER_REPETITION,
|
||||
"copy": position % HEADER_REPETITION + 1,
|
||||
"expected": int(expected_header[position]),
|
||||
"legacy_received": int(legacy["hard_bits"][PREAMBLE_SYMBOLS + position]),
|
||||
"t2_received": int(t2["hard_bits"][PREAMBLE_SYMBOLS + position]),
|
||||
"legacy_error": int(position in header_legacy["raw_error_positions"]),
|
||||
"t2_error": int(position in header_t2["raw_error_positions"]),
|
||||
})
|
||||
start = PREAMBLE_SYMBOLS - 128
|
||||
stop = PREAMBLE_SYMBOLS + HEADER_SYMBOLS + 128
|
||||
corrected = transition["corrected"][start:stop]
|
||||
truth = 2.0 * expected_frame[start:stop] - 1.0
|
||||
errors = np.flatnonzero((corrected.real >= 0) != (truth > 0))
|
||||
phase_error = np.angle(corrected * truth)
|
||||
transitions[packet] = {
|
||||
"corrected_real": corrected.real.astype(float).tolist(),
|
||||
"corrected_imag": corrected.imag.astype(float).tolist(),
|
||||
"magnitude": abs(corrected).astype(float).tolist(),
|
||||
"phase_error_rad": phase_error.astype(float).tolist(),
|
||||
"hard_decisions": (corrected.real >= 0).astype(int).tolist(),
|
||||
"errors": errors.astype(int).tolist(),
|
||||
"sample_positions": transition["sample_positions"][start:stop].astype(int).tolist(),
|
||||
}
|
||||
regions = {"preamble_tail": slice(0, 128), "header": slice(128, 128 + HEADER_SYMBOLS), "postheader": slice(128 + HEADER_SYMBOLS, None)}
|
||||
for name, selected in regions.items():
|
||||
values = corrected[selected]
|
||||
local_truth = truth[selected]
|
||||
transition_rows.append({
|
||||
"packet": packet, "region": name,
|
||||
"symbols": len(values),
|
||||
"decision_errors": int(np.count_nonzero((values.real >= 0) != (local_truth > 0))),
|
||||
"magnitude_median": float(np.median(abs(values))),
|
||||
"magnitude_minimum": float(np.min(abs(values))),
|
||||
"phase_error_rms_rad": float(np.sqrt(np.mean(np.angle(values * local_truth) ** 2))),
|
||||
"imaginary_rms": float(np.sqrt(np.mean(values.imag ** 2))),
|
||||
"timing_offset_samples": transition["timing_samples"],
|
||||
})
|
||||
|
||||
assign_classifications(packet_rows)
|
||||
candidate_summary, candidate_rows = candidate_analysis(strict, expected_starts)
|
||||
tx_header_identical = all(np.array_equal(tx_headers[0], item) for item in tx_headers[1:])
|
||||
tx_header_decodes = [lab066.phy.lab045.decode_phy_header_soft(2.0 * item - 1.0) for item in tx_headers]
|
||||
software_current = {
|
||||
mode: lab066.decode(mode, TX_PATH, diagnostics=False)
|
||||
for mode in ("legacy", "t2")
|
||||
}
|
||||
software_16_of_16 = all(
|
||||
result["packet_output"] == 16 and result["exact_payload_match"] == 16
|
||||
for result in software_current.values()
|
||||
)
|
||||
groups = group_summary(packet_rows)
|
||||
distributions = {
|
||||
label: {
|
||||
metric: distribution(packet_rows, packets, metric)
|
||||
for metric in (
|
||||
"preamble_errors", "preamble_error_rate", "preamble_correlation",
|
||||
"preamble_rms_symbol_deviation", "preamble_matched_power",
|
||||
"preamble_residual_power", "preamble_matched_residual_db",
|
||||
"header_raw_errors", "header_voted_errors",
|
||||
)
|
||||
}
|
||||
for label, packets in (("passed", PASSED_PACKETS), ("failed", FAILED_PACKETS))
|
||||
}
|
||||
failed_classes = {str(row["packet"]): row["classification"] for row in packet_rows if row["packet"] in FAILED_PACKETS}
|
||||
passed_local_residual = float(np.median([
|
||||
row["local_residual_power"] for row in packet_rows if row["packet"] in PASSED_PACKETS
|
||||
]))
|
||||
failed_local_residual = float(np.median([
|
||||
row["local_residual_power"] for row in packet_rows if row["packet"] in FAILED_PACKETS
|
||||
]))
|
||||
passed_local_ratio = float(np.median([
|
||||
row["local_matched_residual_db"] for row in packet_rows if row["packet"] in PASSED_PACKETS
|
||||
]))
|
||||
failed_local_ratio = float(np.median([
|
||||
row["local_matched_residual_db"] for row in packet_rows if row["packet"] in FAILED_PACKETS
|
||||
]))
|
||||
failed_error_positions = [
|
||||
position for row in packet_rows if row["packet"] in FAILED_PACKETS
|
||||
for position in row["header_raw_error_positions"]
|
||||
]
|
||||
repeated_failed_positions = sorted({
|
||||
position for position in failed_error_positions
|
||||
if failed_error_positions.count(position) > 1
|
||||
})
|
||||
diagnostic_strict_agreement = all(
|
||||
row["header_accepted"] == (row["strict_result"] == "PASS")
|
||||
for row in packet_rows
|
||||
)
|
||||
timing_agreement = all(
|
||||
row["timing_offset_samples"] == row["reference_timing_offset_samples"]
|
||||
for row in packet_rows
|
||||
)
|
||||
cfo_scale_small = max(abs(row["phase_error_from_cfo_mismatch_over_header_rad"])
|
||||
for row in packet_rows) < 0.1
|
||||
candidate_state_independent = (
|
||||
candidate_summary["persistent_state_between_rejected_candidates"] is False
|
||||
and candidate_summary["accepted_until_changes_only_after_crc_pass"] is True
|
||||
)
|
||||
local_quality_conditions = {
|
||||
"tx_and_software_valid": bool(
|
||||
tx_header_identical and all(item["passed"] for item in tx_header_decodes)
|
||||
and software_16_of_16
|
||||
),
|
||||
"strict_header_reproduced": diagnostic_strict_agreement,
|
||||
"no_common_failed_error_position": not repeated_failed_positions,
|
||||
"failed_local_residual_at_least_20_percent_higher": (
|
||||
failed_local_residual > 1.2 * passed_local_residual
|
||||
),
|
||||
"failed_local_matched_residual_at_least_1_db_lower": (
|
||||
failed_local_ratio < passed_local_ratio - 1.0
|
||||
),
|
||||
"receiver_and_oracle_timing_agree": timing_agreement,
|
||||
"cfo_mismatch_phase_scale_below_0p1_rad": cfo_scale_small,
|
||||
"candidate_state_independent": candidate_state_independent,
|
||||
}
|
||||
root_cause = (
|
||||
"LOCAL_RF_QUALITY" if all(local_quality_conditions.values())
|
||||
else "NOT_LOCALIZED"
|
||||
)
|
||||
small_fix_evidence = {
|
||||
"receiver_header_logic": bool(not diagnostic_strict_agreement),
|
||||
"timing_transition": bool(not timing_agreement and not cfo_scale_small),
|
||||
"carrier_transition": bool(not cfo_scale_small and timing_agreement),
|
||||
"candidate_state_handling": bool(not candidate_state_independent),
|
||||
}
|
||||
one_small_fix = any(small_fix_evidence.values())
|
||||
plots = make_plots(packet_rows, transitions, candidate_summary)
|
||||
write_csvs(packet_rows, candidate_rows, bit_rows, transition_rows)
|
||||
save_json(OUTPUT / "transition_symbols.json", transitions)
|
||||
summary = {
|
||||
"lab": "Lab067",
|
||||
"mode": "OFFLINE_DIAGNOSTIC_ONLY",
|
||||
"rf_tx": 0,
|
||||
"hardware_accessed": False,
|
||||
"lab066_integrity": integrity,
|
||||
"historical_strict_result_unchanged": {
|
||||
"legacy": "10/16", "t2": "10/16",
|
||||
"passed_packets": sorted(PASSED_PACKETS), "failed_packets": sorted(FAILED_PACKETS),
|
||||
},
|
||||
"packet_positions_verified": len(expected_starts) == 16 and all(
|
||||
row["candidate_difference_samples"] is not None and abs(row["candidate_difference_samples"]) <= 64
|
||||
for row in packet_rows
|
||||
),
|
||||
"packet_rows": packet_rows,
|
||||
"passed_failed_distributions": distributions,
|
||||
"start_middle_end_groups": groups,
|
||||
"failed_packet_classifications": failed_classes,
|
||||
"header_error_position_analysis": {
|
||||
"failed_raw_error_count": len(failed_error_positions),
|
||||
"positions_repeated_between_failed_packets": repeated_failed_positions,
|
||||
"systematic_common_position_found": bool(repeated_failed_positions),
|
||||
"interpretation": "Позиции ошибок различаются; общей битовой позиции отказа нет.",
|
||||
},
|
||||
"local_quality_evidence": {
|
||||
"passed_local_residual_power_median": passed_local_residual,
|
||||
"failed_local_residual_power_median": failed_local_residual,
|
||||
"passed_local_matched_residual_db_median": passed_local_ratio,
|
||||
"failed_local_matched_residual_db_median": failed_local_ratio,
|
||||
"failed_minus_passed_residual_power_median": failed_local_residual - passed_local_residual,
|
||||
"failed_minus_passed_matched_residual_db_median": failed_local_ratio - passed_local_ratio,
|
||||
"scope": "last 128 preamble symbols plus complete header",
|
||||
"decision_conditions": local_quality_conditions,
|
||||
},
|
||||
"legacy_t2_header_difference": (
|
||||
"NONE" if all(row["legacy_t2_header_differences"] == 0 for row in packet_rows)
|
||||
else "PRESENT"
|
||||
),
|
||||
"t2_influence_starts_at_symbol": PREAMBLE_SYMBOLS,
|
||||
"t2_explanation": "T2 начинает сопровождение после преамбулы; его решения заголовка сравнены побитно со штатным legacy.",
|
||||
"candidate_analysis": candidate_summary,
|
||||
"tx_validation": {
|
||||
"headers_identical_at_all_packet_positions": tx_header_identical,
|
||||
"all_headers_decode_with_correct_magic_version_length_checksum": all(item["passed"] for item in tx_header_decodes),
|
||||
"decoded_packet_lengths": [item["packet_bytes"] for item in tx_header_decodes],
|
||||
"software_roundtrip_current": software_current,
|
||||
"legacy_and_t2_16_of_16": software_16_of_16,
|
||||
},
|
||||
"header_failure_root_cause": root_cause,
|
||||
"one_small_fix_justified": one_small_fix,
|
||||
"branch_decision": (
|
||||
"ONE_SMALL_FIX_JUSTIFIED" if one_small_fix
|
||||
else "NO_SMALL_FIX_JUSTIFIED"
|
||||
),
|
||||
"small_fix_assessment": {
|
||||
"evidence_flags": small_fix_evidence,
|
||||
"receiver_header_logic_error_found": small_fix_evidence["receiver_header_logic"],
|
||||
"timing_transition_error_found": small_fix_evidence["timing_transition"],
|
||||
"carrier_transition_error_explains_six_failures": small_fix_evidence["carrier_transition"],
|
||||
"candidate_state_error_found": small_fix_evidence["candidate_state_handling"],
|
||||
"reason": "TX и программный тракт дают 16/16; ошибки заголовка не имеют общей позиции, timing совпадает, а локальный остаток в failed header-окнах выше.",
|
||||
},
|
||||
"achieved_result": "10/16 exact full packets at 500 ksymbol/s, TX VGA 20 dB, hardware-valid OTA capture",
|
||||
"recommended_next_project_branch": "CRSF / ELRS CONTROL AND TELEMETRY",
|
||||
"qpsk_note": "QPSK с меньшей символьной скоростью остаётся будущим кандидатом для видеоканала; в Lab067 не реализован.",
|
||||
"plots": plots,
|
||||
"lab067_commit": "NOT PERFORMED",
|
||||
"push": "NOT PERFORMED",
|
||||
}
|
||||
save_json(OUTPUT / "summary.json", summary)
|
||||
return summary
|
||||
|
||||
|
||||
def run_functional_tests() -> None:
|
||||
expected = np.repeat(np.arange(HEADER_BITS, dtype=np.uint8) % 2, HEADER_REPETITION)
|
||||
exact = header_diagnostics(2.0 * expected.astype(float) - 1.0, expected)
|
||||
assert exact["raw_header_errors"] == 0
|
||||
assert exact["voted_header_errors"] == 0
|
||||
changed = expected.copy(); changed[3:5] ^= 1
|
||||
result = header_diagnostics(2.0 * changed.astype(float) - 1.0, expected)
|
||||
assert result["raw_header_errors"] == 2
|
||||
assert result["voted_header_errors"] == 1
|
||||
|
||||
|
||||
def main() -> None:
|
||||
run_functional_tests()
|
||||
result = analyze()
|
||||
print(json.dumps({
|
||||
"lab": result["lab"],
|
||||
"historical_strict_result": result["historical_strict_result_unchanged"],
|
||||
"root_cause": result["header_failure_root_cause"],
|
||||
"one_small_fix_justified": result["one_small_fix_justified"],
|
||||
"failed_packet_classifications": result["failed_packet_classifications"],
|
||||
"plots": result["plots"],
|
||||
}, ensure_ascii=False, indent=2))
|
||||
print("RF TX = 0")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
142
tests/test_lab062_full_packet_rf_preflight.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""Lab062: программные условия и запрет TX до отдельного разрешения."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from experiments import lab062_full_packet_rf_preflight as lab
|
||||
from tools.hackrf_process_check import ProcessCheck, ProcessState
|
||||
|
||||
|
||||
def test_fixed_packet_and_operating_point():
|
||||
assert len(lab.plans()) == 16
|
||||
assert all(len(frame.plan.payload) == 256 for frame in lab.plans())
|
||||
assert lab.parameters()["rf_frequency_hz"] == 1_400_000_000
|
||||
assert lab.parameters()["symbol_rate"] == 500_000
|
||||
assert lab.parameters()["tx_vga_db"] == 10
|
||||
assert lab.parameters()["rx_lna_db"] == lab.parameters()["rx_vga_db"] == 24
|
||||
assert lab.POINT.distance_m == 1.2 and lab.POINT.visibility == "LOS indoor"
|
||||
|
||||
|
||||
def test_rx_and_tx_commands_are_fixed_and_distinct():
|
||||
receive = lab.transfer_command("rx.cs8", 800_000)
|
||||
transmit = lab.transfer_command("tx.cs8", 100, transmit=True)
|
||||
assert "-r" in receive and "-t" not in receive
|
||||
assert "-t" in transmit and "-r" not in transmit
|
||||
assert receive[receive.index("-f") + 1] == "1400000000"
|
||||
assert receive[receive.index("-l") + 1] == receive[receive.index("-g") + 1] == "24"
|
||||
assert transmit[transmit.index("-x") + 1] == "10"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("failure", [None, "clock", "clipping", "strong", "overrun"])
|
||||
def test_preflight_never_transmits_and_fails_closed(tmp_path, monkeypatch, failure):
|
||||
raw, output = tmp_path / "raw", tmp_path / "processed"
|
||||
raw.mkdir(); output.mkdir()
|
||||
monkeypatch.setattr(lab, "RAW", raw); monkeypatch.setattr(lab, "OUTPUT", output)
|
||||
(raw / "tx_stream.cs8").write_bytes(b"test")
|
||||
metadata = {"tx_sha256": "TEST"}
|
||||
(output / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: metadata)
|
||||
monkeypatch.setattr(lab.lab054.phy.lab045, "_verify_device_inventory", lambda runner: {"passed": True})
|
||||
def clock(point, runner, guard, **kwargs):
|
||||
return {"passed": failure != "clock", "master_reg29": 0x26,
|
||||
"slave_reg29": 0x26, "slave_clkin_status": "DETECTED"}
|
||||
def runner(command):
|
||||
assert "-t" not in command
|
||||
if "-r" in command:
|
||||
value = 127 if failure == "clipping" else (110 if failure == "strong" else 1)
|
||||
Path(command[command.index("-r") + 1]).write_bytes(bytes([value, value]) * 800_000)
|
||||
log = "1 RX overruns" if failure == "overrun" else "0 RX overruns"
|
||||
return subprocess.CompletedProcess(command, 0, log, "")
|
||||
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
|
||||
kwargs = dict(runner=runner, clock_checker=clock,
|
||||
process_checker=lambda: ProcessCheck(ProcessState.CLEAR, ()),
|
||||
sleep_function=lambda _: None)
|
||||
if failure:
|
||||
with pytest.raises(RuntimeError):
|
||||
lab.preflight(**kwargs)
|
||||
else:
|
||||
assert lab.preflight(**kwargs)["passed"]
|
||||
result = __import__("json").loads((raw / "preflight_result.json").read_text())
|
||||
assert result["rf_tx_count"] == 0
|
||||
if failure is None:
|
||||
assert "symbol_energy_code2_seconds" not in result["rx_level"]
|
||||
assert result["rx_level"]["symbol_energy"] == "NOT_APPLICABLE_TO_BACKGROUND"
|
||||
|
||||
|
||||
def test_second_preflight_is_refused_before_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path)
|
||||
monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
(tmp_path / "preflight_attempt.json").write_text("{}")
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
|
||||
(tmp_path / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
|
||||
with pytest.raises(FileExistsError):
|
||||
lab.preflight(runner=lambda command: pytest.fail("hardware"))
|
||||
|
||||
|
||||
def test_capture_refuses_missing_authorization_before_files_or_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path)
|
||||
monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
with pytest.raises(PermissionError):
|
||||
lab.capture_once(
|
||||
authorization=False,
|
||||
runner=lambda command: pytest.fail("hardware"),
|
||||
)
|
||||
assert not (tmp_path / "hardware_attempt.json").exists()
|
||||
|
||||
|
||||
def test_capture_is_rx_first_single_tx_and_refuses_second_attempt(tmp_path, monkeypatch):
|
||||
raw, output = tmp_path / "raw", tmp_path / "processed"
|
||||
raw.mkdir(); output.mkdir()
|
||||
monkeypatch.setattr(lab, "RAW", raw); monkeypatch.setattr(lab, "OUTPUT", output)
|
||||
tx = raw / "tx_stream.cs8"; tx.write_bytes(b"\x01\x01" * 100)
|
||||
metadata = {"tx_sha256": lab.sha256(tx), "tx_samples": 100}
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: metadata)
|
||||
monkeypatch.setattr(lab, "require_preflight", lambda value: {})
|
||||
monkeypatch.setattr(lab, "RX_SAMPLES", 800_000)
|
||||
monkeypatch.setattr(lab.lab054.phy.lab045, "_verify_device_inventory", lambda runner: {"passed": True})
|
||||
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
|
||||
events = []
|
||||
|
||||
class Process:
|
||||
def poll(self): return None
|
||||
def wait(self, timeout): return 0
|
||||
def close_logs(self): pass
|
||||
def terminate(self): pass
|
||||
|
||||
def starter(command, stdout, stderr):
|
||||
events.append("RX")
|
||||
Path(command[command.index("-r") + 1]).write_bytes(b"\x01\x01" * 800_000)
|
||||
Path(stdout).write_text("")
|
||||
Path(stderr).write_text("0 RX overruns")
|
||||
return Process()
|
||||
|
||||
def runner(command):
|
||||
if "-t" in command:
|
||||
events.append("TX")
|
||||
return subprocess.CompletedProcess(command, 0, "0 TX underruns", "")
|
||||
return subprocess.CompletedProcess(command, 0, "", "")
|
||||
|
||||
def clock(point, runner, guard, **kwargs):
|
||||
guard.tx_allowed = True
|
||||
return {
|
||||
"passed": True, "master_reg29": 0x26, "slave_reg29": 0x26,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
}
|
||||
|
||||
result = lab.capture_once(
|
||||
authorization=True, runner=runner, process_starter=starter,
|
||||
process_checker=lambda: ProcessCheck(ProcessState.CLEAR, ()),
|
||||
clock_checker=clock, sleep_function=lambda _: None,
|
||||
)
|
||||
assert events == ["RX", "TX"]
|
||||
assert result["rf_tx_count"] == 1 and result["automatic_tx_repeats"] == 0
|
||||
assert result["rx_overruns"] == result["tx_underruns"] == 0
|
||||
assert json.loads((raw / "capture_result.json").read_text())["passed"]
|
||||
with pytest.raises(FileExistsError):
|
||||
lab.capture_once(authorization=True, runner=lambda command: pytest.fail("hardware"))
|
||||
111
tests/test_lab063_tx_underrun_localization.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""Lab063: автономные проверки локального сопоставления без SDR."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from experiments import lab063_tx_underrun_localization as lab
|
||||
|
||||
|
||||
def test_underrun_scale_is_exact():
|
||||
result = lab.underrun_scale(16_672)
|
||||
assert result["complex_samples"] == 8_336
|
||||
assert result["duration_seconds"] == 0.001042
|
||||
assert result["symbols"] == 521
|
||||
assert np.isclose(result["fraction_of_packet"], 8_336 / 92_256)
|
||||
|
||||
|
||||
def test_normalized_match_recovers_offset_gain_and_low_residual():
|
||||
random_generator = np.random.default_rng(6301)
|
||||
transmitted = (random_generator.choice((-1.0, 1.0), 2048)
|
||||
* np.exp(1j * np.arange(2048) * 0.01))
|
||||
gain = 0.35 - 0.2j
|
||||
search = np.concatenate((np.zeros(321, complex), gain * transmitted, np.zeros(400, complex)))
|
||||
result = lab.normalized_match(transmitted, search)
|
||||
assert result["offset"] == 321
|
||||
assert result["normalized_correlation"] > 0.999999
|
||||
assert abs(result["gain"] - gain) < 1e-12
|
||||
assert result["residual_power"] < 1e-20
|
||||
|
||||
|
||||
def test_alignment_localizes_inserted_delay_jump():
|
||||
random_generator = np.random.default_rng(6302)
|
||||
transmitted = random_generator.choice((-1.0, 1.0), 8192).astype(np.complex64)
|
||||
split, inserted = 4096, 700
|
||||
received = np.concatenate((np.zeros(1000), transmitted[:split], np.zeros(inserted), transmitted[split:], np.zeros(1000)))
|
||||
rows = lab.track_alignment(
|
||||
lab.ArrayReader(transmitted), lab.ArrayReader(received), 1000,
|
||||
frame_samples=8192, window_samples=512, step_samples=256, search_margin=1000,
|
||||
reliable_packet_start=0, reliable_packet_end=8192,
|
||||
)
|
||||
verdict = lab.alignment_verdict(rows, inserted)
|
||||
assert verdict["local_delay_discontinuity"]
|
||||
assert any(abs(row["delay_change_samples"]) >= inserted - 1 for row in verdict["jump_candidates"])
|
||||
|
||||
|
||||
def test_alignment_compares_reliable_windows_across_packet_boundary():
|
||||
rows = [
|
||||
{
|
||||
"window_index": 4, "tx_sample": 1000, "rx_sample": 2000,
|
||||
"packet_index": 1, "position_inside_packet_samples": 1000,
|
||||
"local_delay_samples": 0, "normalized_correlation": 0.9,
|
||||
"inside_reliable_region": True, "correlation_above_threshold": True,
|
||||
"reliable_for_delay": True,
|
||||
},
|
||||
{
|
||||
"window_index": 9, "tx_sample": 9000, "rx_sample": 10700,
|
||||
"packet_index": 2, "position_inside_packet_samples": 800,
|
||||
"local_delay_samples": 700, "normalized_correlation": 0.9,
|
||||
"inside_reliable_region": True, "correlation_above_threshold": True,
|
||||
"reliable_for_delay": True,
|
||||
},
|
||||
]
|
||||
verdict = lab.alignment_verdict(rows, 700)
|
||||
assert verdict["local_delay_discontinuity"]
|
||||
assert verdict["jump_candidates"][0]["packet_index"] == 2
|
||||
|
||||
|
||||
def test_alignment_discloses_internal_low_correlation_window():
|
||||
rows = [
|
||||
{
|
||||
"window_index": 0, "tx_sample": 1000, "rx_sample": 2000,
|
||||
"packet_index": 1, "position_inside_packet_samples": 1000,
|
||||
"local_delay_samples": 0, "normalized_correlation": 0.9,
|
||||
"inside_reliable_region": True, "correlation_above_threshold": True,
|
||||
"reliable_for_delay": True,
|
||||
},
|
||||
{
|
||||
"window_index": 1, "tx_sample": 1256, "rx_sample": 2956,
|
||||
"packet_index": 1, "position_inside_packet_samples": 1256,
|
||||
"local_delay_samples": 700, "normalized_correlation": 0.1,
|
||||
"inside_reliable_region": True, "correlation_above_threshold": False,
|
||||
"reliable_for_delay": False,
|
||||
},
|
||||
]
|
||||
verdict = lab.alignment_verdict(rows, 700)
|
||||
assert verdict["internal_low_correlation_windows"]["count"] == 1
|
||||
assert "UNDETERMINED_IN_1_INTERNAL_LOW_CORRELATION_WINDOWS" in verdict["local_delay_discontinuity_scope"]
|
||||
assert verdict["local_delay_discontinuity"] is False
|
||||
|
||||
|
||||
def test_classification_marks_global_degradation_without_jump():
|
||||
packets = [{
|
||||
"packet_index": index,
|
||||
"symbol_error_rate": 0.1,
|
||||
"symbol_rms_deviation": 1.2,
|
||||
"matched_residual_db": -1.0,
|
||||
} for index in range(1, 17)]
|
||||
result = lab.classify(packets, {"local_delay_discontinuity": False})
|
||||
assert result["quality_distribution"] == "GLOBAL"
|
||||
assert result["primary_cause_status"] == "PARTIALLY_LOCALIZED"
|
||||
|
||||
|
||||
def test_classification_marks_mixed_when_jump_and_global_damage_coexist():
|
||||
packets = [{
|
||||
"packet_index": index,
|
||||
"symbol_error_rate": 0.1,
|
||||
"symbol_rms_deviation": 1.2,
|
||||
"matched_residual_db": -1.0,
|
||||
} for index in range(1, 17)]
|
||||
result = lab.classify(packets, {"local_delay_discontinuity": True})
|
||||
assert result["quality_distribution"] == "MIXED"
|
||||
198
tests/test_lab064_rf_level_linearity.py
Normal file
@@ -0,0 +1,198 @@
|
||||
"""Lab064: цифровая лестница, измерительные метрики и запрет RF без разрешения."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from experiments import lab064_rf_level_linearity as lab
|
||||
from tools.hackrf_process_check import ProcessCheck, ProcessState
|
||||
|
||||
|
||||
def test_fixed_operating_point_and_measurement_scope():
|
||||
parameters = lab.parameters()
|
||||
assert parameters["rf_frequency_hz"] == 1_400_000_000
|
||||
assert parameters["sample_rate_hz"] == 8_000_000
|
||||
assert parameters["symbol_rate"] == 20_000
|
||||
assert parameters["samples_per_symbol"] == 400
|
||||
assert parameters["digital_carrier_hz"] == 50_000
|
||||
assert parameters["levels_db_relative_nominal"] == [-18.0, -12.0, -6.0, 0.0]
|
||||
assert parameters["full_phy_packet"] is False
|
||||
assert parameters["tx_vga_db"] == 10
|
||||
assert parameters["rx_lna_db"] == parameters["rx_vga_db"] == 24
|
||||
assert lab.POINT.distance_m == 1.2
|
||||
assert lab.POINT.visibility == "LOS indoor"
|
||||
|
||||
|
||||
def test_waveform_has_exact_guards_segments_gaps_and_levels():
|
||||
rows, blocks = lab.prepared_segments()
|
||||
assert lab.TX_SAMPLES == 6_000_000
|
||||
assert lab.TX_SAMPLES / lab.SAMPLE_RATE == 0.75
|
||||
assert all(row["samples"] == 800_000 for row in rows)
|
||||
assert [row["start_sample"] for row in rows] == [800_000, 2_000_000, 3_200_000, 4_400_000]
|
||||
assert all(len(blocks[row["label"]]) == 2 * lab.SEGMENT_SAMPLES for row in rows)
|
||||
assert all(not row["metrics"]["digital_clipping"] for row in rows)
|
||||
steps = [row["tx_input_difference_from_previous_db"] for row in rows[1:]]
|
||||
assert np.allclose(steps, 6.0, atol=0.1)
|
||||
|
||||
|
||||
def test_background_windows_cover_both_guards_and_all_gaps():
|
||||
windows = lab.background_windows()
|
||||
assert [row["label"] for row in windows] == [
|
||||
"INITIAL_GUARD", "GAP_L0_L1", "GAP_L1_L2", "GAP_L2_L3", "FINAL_GUARD"
|
||||
]
|
||||
assert all(row["samples"] == 160_000 for row in windows)
|
||||
zero_ranges = [
|
||||
(0, 800_000),
|
||||
(1_600_000, 2_000_000),
|
||||
(2_800_000, 3_200_000),
|
||||
(4_000_000, 4_400_000),
|
||||
(5_200_000, 6_000_000),
|
||||
]
|
||||
for row, (low, high) in zip(windows, zero_ranges):
|
||||
assert low <= row["tx_relative_start_sample"]
|
||||
assert row["tx_relative_start_sample"] + row["samples"] <= high
|
||||
|
||||
|
||||
def test_background_stability_uses_ac_power_range():
|
||||
stable = lab.summarize_background([
|
||||
{"metrics": {"ac_power_code2": 1.0, "ac_rms_code": 1.0}},
|
||||
{"metrics": {"ac_power_code2": 1.5, "ac_rms_code": np.sqrt(1.5)}},
|
||||
])
|
||||
changed = lab.summarize_background([
|
||||
{"metrics": {"ac_power_code2": 1.0, "ac_rms_code": 1.0}},
|
||||
{"metrics": {"ac_power_code2": 4.0, "ac_rms_code": 2.0}},
|
||||
])
|
||||
assert stable["stable"] is True
|
||||
assert changed["stable"] is False
|
||||
assert changed["maximum_to_minimum_ac_power_db"] == pytest.approx(6.020599913)
|
||||
|
||||
|
||||
def test_synthetic_linear_channel_recovers_digital_steps():
|
||||
result = lab.synthetic_linearity_gate()
|
||||
assert result["passed"]
|
||||
assert max(abs(value) for value in result["linearity_errors_db"]) < 0.02
|
||||
gains = [row["gain_abs"] for row in result["rows"]]
|
||||
assert np.allclose(gains, 0.37, atol=0.001)
|
||||
|
||||
|
||||
def test_counter_parser_keeps_every_intermediate_and_final_observation():
|
||||
log = "0 TX underruns\n1 TX underrun\n3 TX underruns\n3 TX underruns\n"
|
||||
summary = lab.counter_summary(log, "underrun")
|
||||
assert summary["selected_counts"] == [0, 1, 3, 3]
|
||||
assert summary["final_count"] == 3
|
||||
assert summary["maximum_count"] == 3
|
||||
assert summary["clean"] is False
|
||||
assert [row["character_offset"] for row in summary["observations"]] == sorted(
|
||||
row["character_offset"] for row in summary["observations"]
|
||||
)
|
||||
assert lab.counter_summary("0 RX overruns", "overrun")["clean"] is True
|
||||
|
||||
|
||||
def test_intermediate_loss_cannot_be_hidden_by_final_zero():
|
||||
tx = lab.counter_summary("0 TX underruns\n1 TX underrun\n0 TX underruns", "underrun")
|
||||
rx = lab.counter_summary("0 RX overruns", "overrun")
|
||||
validity = lab.capture_validity({
|
||||
"passed": False,
|
||||
"rx_clipping": False,
|
||||
"tx_counter_summary": tx,
|
||||
"rx_counter_summary": rx,
|
||||
})
|
||||
assert tx["final_count"] == 0
|
||||
assert tx["maximum_count"] == 1
|
||||
assert validity["valid"] is False
|
||||
assert "TX_MAXIMUM_COUNTER_NOT_ZERO" in validity["invalid_reasons"]
|
||||
assert "CAPTURE_RUNNER_NOT_PASSED" in validity["invalid_reasons"]
|
||||
|
||||
|
||||
def test_commands_fix_gains_and_keep_rx_separate_from_tx():
|
||||
receive = lab.transfer_command("rx.cs8", 800_000)
|
||||
transmit = lab.transfer_command("tx.cs8", 100, transmit=True)
|
||||
assert "-r" in receive and "-t" not in receive
|
||||
assert "-t" in transmit and "-r" not in transmit
|
||||
assert receive[receive.index("-f") + 1] == "1400000000"
|
||||
assert receive[receive.index("-s") + 1] == "8000000"
|
||||
assert receive[receive.index("-l") + 1] == receive[receive.index("-g") + 1] == "24"
|
||||
assert transmit[transmit.index("-x") + 1] == "10"
|
||||
assert receive[-5:] == ["-a", "0", "-p", "0", "-B"]
|
||||
assert transmit[-5:] == ["-a", "0", "-p", "0", "-B"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("failure", [None, "clock", "clipping", "strong", "overrun"])
|
||||
def test_preflight_never_transmits_and_fails_closed(tmp_path, monkeypatch, failure):
|
||||
raw = tmp_path / "raw"
|
||||
output = tmp_path / "processed"
|
||||
raw.mkdir()
|
||||
output.mkdir()
|
||||
monkeypatch.setattr(lab, "RAW", raw)
|
||||
monkeypatch.setattr(lab, "OUTPUT", output)
|
||||
(raw / "tx_level_staircase.cs8").write_bytes(b"test")
|
||||
metadata = {"tx_sha256": "TEST"}
|
||||
(output / "software_gate.json").write_text(
|
||||
'{"passed": true, "tx_sha256": "TEST"}', encoding="utf-8"
|
||||
)
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: metadata)
|
||||
monkeypatch.setattr(
|
||||
lab.lab054.phy.lab045,
|
||||
"_verify_device_inventory",
|
||||
lambda runner: {"passed": True},
|
||||
)
|
||||
|
||||
def clock(point, runner, guard, **kwargs):
|
||||
return {
|
||||
"passed": failure != "clock",
|
||||
"master_reg29": 0x26,
|
||||
"slave_reg29": 0x26,
|
||||
"slave_clkin_status": "DETECTED",
|
||||
}
|
||||
|
||||
def runner(command):
|
||||
assert "-t" not in command
|
||||
if "-r" in command:
|
||||
value = 127 if failure == "clipping" else (110 if failure == "strong" else 1)
|
||||
Path(command[command.index("-r") + 1]).write_bytes(bytes([value, value]) * 800_000)
|
||||
log = "1 RX overruns" if failure == "overrun" else "0 RX overruns"
|
||||
return subprocess.CompletedProcess(command, 0, log, "")
|
||||
|
||||
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
|
||||
options = {
|
||||
"runner": runner,
|
||||
"clock_checker": clock,
|
||||
"process_checker": lambda: ProcessCheck(ProcessState.CLEAR, ()),
|
||||
"sleep_function": lambda _: None,
|
||||
}
|
||||
if failure:
|
||||
with pytest.raises(RuntimeError):
|
||||
lab.preflight(**options)
|
||||
else:
|
||||
assert lab.preflight(**options)["passed"]
|
||||
result = json.loads((raw / "preflight_result.json").read_text(encoding="utf-8"))
|
||||
assert result["rf_tx_count"] == 0
|
||||
assert result["stop_before_tx"] is True
|
||||
|
||||
|
||||
def test_second_preflight_is_refused_before_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path)
|
||||
monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
(tmp_path / "preflight_attempt.json").write_text("{}", encoding="utf-8")
|
||||
(tmp_path / "software_gate.json").write_text(
|
||||
'{"passed": true, "tx_sha256": "TEST"}', encoding="utf-8"
|
||||
)
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
|
||||
with pytest.raises(FileExistsError):
|
||||
lab.preflight(runner=lambda command: pytest.fail("hardware must not run"))
|
||||
|
||||
|
||||
def test_capture_refuses_missing_authorization_before_files_or_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path)
|
||||
monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
with pytest.raises(PermissionError):
|
||||
lab.capture_once(
|
||||
authorization=False,
|
||||
runner=lambda command: pytest.fail("hardware must not run"),
|
||||
)
|
||||
assert not (tmp_path / "hardware_attempt.json").exists()
|
||||
151
tests/test_lab065_rf_level_linearity.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""Lab065: неизменный waveform, TX VGA 20 дБ и границы классификации."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from experiments import lab065_rf_level_linearity as lab
|
||||
from tools.hackrf_process_check import ProcessCheck, ProcessState
|
||||
|
||||
|
||||
def test_only_operating_parameter_change_is_tx_vga():
|
||||
old = lab.lab064.parameters()
|
||||
new = lab.parameters()
|
||||
assert new.pop("geometry_unchanged_from_lab064") is True
|
||||
assert new.pop("waveform_source") == "Lab064 exact CS8 copy"
|
||||
assert new["tx_vga_db"] == 20
|
||||
new["tx_vga_db"] = 10
|
||||
assert new == old
|
||||
|
||||
|
||||
def test_tx_command_uses_20_db_and_rx_remains_24_24():
|
||||
transmit = lab.transfer_command("tx.cs8", 100, transmit=True)
|
||||
receive = lab.transfer_command("rx.cs8", 100)
|
||||
assert transmit[transmit.index("-x") + 1] == "20"
|
||||
assert receive[receive.index("-l") + 1] == "24"
|
||||
assert receive[receive.index("-g") + 1] == "24"
|
||||
assert transmit[-5:] == receive[-5:] == ["-a", "0", "-p", "0", "-B"]
|
||||
|
||||
|
||||
def test_exact_lab064_waveform_is_required():
|
||||
metadata = lab.validate_lab064_waveform()
|
||||
assert metadata["tx_sha256"] == lab.EXPECTED_TX_SHA256
|
||||
assert metadata["tx_samples"] == 6_000_000
|
||||
|
||||
|
||||
def _result(matched_gain, residual_gain, improvements, errors=(0.0, 0.0, 0.0)):
|
||||
old = {row["label"]: row for row in json.loads(
|
||||
lab.LAB064_RESULT.read_text(encoding="utf-8")
|
||||
)["rows"]}
|
||||
rows = []
|
||||
for index, label in enumerate(("L0", "L1", "L2", "L3")):
|
||||
baseline = old[label]
|
||||
rows.append({
|
||||
"label": label,
|
||||
"matched_signal_power": baseline["matched_signal_power"] * 10 ** (matched_gain[index] / 10),
|
||||
"residual_power": baseline["residual_power"] * 10 ** (residual_gain[index] / 10),
|
||||
"matched_to_residual_db": baseline["matched_to_residual_db"] + improvements[index],
|
||||
"gain_power_abs_h_squared": baseline["gain_power_abs_h_squared"] * 10 ** (matched_gain[index] / 10),
|
||||
})
|
||||
return {
|
||||
"capture_valid": True,
|
||||
"rows": rows,
|
||||
"comparisons": [{"linearity_error_db": error} for error in errors],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("result", "expected"),
|
||||
[
|
||||
(_result([10] * 4, [1] * 4, [9] * 4), "LINEAR_GAIN_IMPROVEMENT"),
|
||||
(_result([10] * 4, [10] * 4, [0] * 4), "RESIDUAL_RISES_WITH_SIGNAL"),
|
||||
(_result([1] * 4, [0] * 4, [1] * 4), "NO_MEANINGFUL_GAIN"),
|
||||
(_result([10] * 4, [1] * 4, [9] * 4, errors=(0, 0, -1.6)), "COMPRESSION_AT_HIGHER_TX_GAIN"),
|
||||
],
|
||||
)
|
||||
def test_classification_cases(result, expected):
|
||||
comparison = lab.compare_sessions(
|
||||
json.loads(lab.LAB064_RESULT.read_text(encoding="utf-8")), result["rows"]
|
||||
)
|
||||
assert lab.classify_result(result, comparison)["case"] == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("failure", [None, "clock", "overrun", "clipping"])
|
||||
def test_preflight_never_transmits_and_fails_closed(tmp_path, monkeypatch, failure):
|
||||
raw, output = tmp_path / "raw", tmp_path / "output"
|
||||
raw.mkdir()
|
||||
output.mkdir()
|
||||
monkeypatch.setattr(lab, "RAW", raw)
|
||||
monkeypatch.setattr(lab, "OUTPUT", output)
|
||||
(raw / "tx_level_staircase.cs8").write_bytes(b"test")
|
||||
metadata = {"tx_sha256": "TEST"}
|
||||
(output / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: metadata)
|
||||
monkeypatch.setattr(lab.lab054.phy.lab045, "_verify_device_inventory", lambda runner: {"passed": True})
|
||||
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
|
||||
monkeypatch.setattr(lab, "parameters", lambda: {})
|
||||
|
||||
def clock(point, runner, guard, **kwargs):
|
||||
return {"passed": failure != "clock", "master_reg29": 0x26,
|
||||
"slave_reg29": 0x26, "slave_clkin_status": "DETECTED"}
|
||||
|
||||
def runner(command):
|
||||
assert "-t" not in command
|
||||
if "-r" in command:
|
||||
value = 127 if failure == "clipping" else 1
|
||||
Path(command[command.index("-r") + 1]).write_bytes(bytes([value, value]) * 800_000)
|
||||
log = "1 RX overruns" if failure == "overrun" else "0 RX overruns"
|
||||
return subprocess.CompletedProcess(command, 0, log, "")
|
||||
|
||||
options = {
|
||||
"runner": runner,
|
||||
"clock_checker": clock,
|
||||
"process_checker": lambda: ProcessCheck(ProcessState.CLEAR, ()),
|
||||
"sleep_function": lambda _: None,
|
||||
}
|
||||
if failure:
|
||||
with pytest.raises(RuntimeError):
|
||||
lab.preflight(**options)
|
||||
else:
|
||||
assert lab.preflight(**options)["passed"]
|
||||
assert json.loads((raw / "preflight_result.json").read_text())["rf_tx_count"] == 0
|
||||
|
||||
|
||||
def test_capture_is_unavailable_without_new_authorization():
|
||||
with pytest.raises(PermissionError):
|
||||
lab.capture_once(authorization=False)
|
||||
|
||||
|
||||
def test_existing_attempt_blocks_capture_before_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path)
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
|
||||
monkeypatch.setattr(lab, "require_preflight", lambda metadata: {})
|
||||
(tmp_path / "hardware_attempt.json").write_text("{}", encoding="utf-8")
|
||||
with pytest.raises(FileExistsError):
|
||||
lab.capture_once(
|
||||
authorization=True,
|
||||
runner=lambda command: pytest.fail("hardware must not run"),
|
||||
process_starter=lambda *args: pytest.fail("hardware must not run"),
|
||||
)
|
||||
|
||||
|
||||
def test_cli_labels_current_rf_state_after_completed_capture(monkeypatch, capsys):
|
||||
monkeypatch.setattr(
|
||||
lab,
|
||||
"capture_once",
|
||||
lambda authorization=False: {
|
||||
"stage": "SINGLE_RF_LEVEL_CAPTURE", "rf_tx_count": 1, "passed": True,
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
sys, "argv", ["lab065", "--stage", "capture", "--authorize-one-rf"]
|
||||
)
|
||||
lab.main()
|
||||
output = capsys.readouterr().out
|
||||
assert '"rf_tx_count": 1' in output
|
||||
assert output.endswith("RF TX NOW = 0\n")
|
||||
149
tests/test_lab066_full_packet_rf_retest.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""Lab066: исправленные паузы, строгий software gate и запрет TX."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from experiments import lab066_full_packet_rf_retest as lab
|
||||
from tools.hackrf_process_check import ProcessCheck, ProcessState
|
||||
|
||||
|
||||
def test_fixed_layout_and_operating_point():
|
||||
waveform, layout = lab.build_waveform()
|
||||
assert len(lab.plans()) == 16
|
||||
assert all(len(frame.plan.payload) == 256 for frame in lab.plans())
|
||||
assert len({frame.plan.payload for frame in lab.plans()}) == 16
|
||||
assert lab.PACKET_SAMPLES == 92_256
|
||||
assert lab.INTER_PACKET_GAP_SAMPLES == 40_000
|
||||
assert lab.NUMBER_OF_GAPS == 15
|
||||
assert lab.GUARD_SAMPLES == 800_000
|
||||
assert lab.BASE_TX_SAMPLES == 2_076_096
|
||||
assert lab.EXPECTED_TX_SAMPLES == 3_676_096
|
||||
assert len(waveform) == 2 * lab.EXPECTED_TX_SAMPLES
|
||||
assert [row["packet_index"] for row in layout] == list(range(1, 17))
|
||||
assert lab.parameters()["tx_vga_db"] == 20
|
||||
assert lab.parameters()["rx_lna_db"] == lab.parameters()["rx_vga_db"] == 24
|
||||
|
||||
|
||||
def test_guards_and_all_gaps_are_exact_zero():
|
||||
waveform, layout = lab.build_waveform()
|
||||
samples = memoryview(waveform)
|
||||
assert not any(samples[:2 * lab.GUARD_SAMPLES])
|
||||
assert not any(samples[-2 * lab.GUARD_SAMPLES:])
|
||||
for row in layout[:-1]:
|
||||
low = 2 * row["following_gap_start_sample"]
|
||||
high = low + 2 * row["following_gap_samples"]
|
||||
assert high - low == 2 * 40_000
|
||||
assert not any(samples[low:high])
|
||||
|
||||
|
||||
def test_transfer_commands_are_rx_only_or_fixed_tx():
|
||||
receive = lab.transfer_command("rx.cs8", 800_000)
|
||||
transmit = lab.transfer_command("tx.cs8", 100, transmit=True)
|
||||
assert "-r" in receive and "-t" not in receive
|
||||
assert "-t" in transmit and "-r" not in transmit
|
||||
assert receive[receive.index("-f") + 1] == "1400000000"
|
||||
assert receive[receive.index("-l") + 1] == receive[receive.index("-g") + 1] == "24"
|
||||
assert transmit[transmit.index("-x") + 1] == "20"
|
||||
|
||||
|
||||
def test_prepare_metadata_and_two_strict_roundtrips(tmp_path, monkeypatch):
|
||||
raw, output = tmp_path / "raw", tmp_path / "processed"
|
||||
monkeypatch.setattr(lab, "RAW", raw); monkeypatch.setattr(lab, "OUTPUT", output)
|
||||
metadata = lab.prepare()
|
||||
assert metadata["tx_samples"] == lab.EXPECTED_TX_SAMPLES
|
||||
assert metadata["tx_sha256"] == hashlib.sha256((raw / "tx_stream.cs8").read_bytes()).hexdigest()
|
||||
result = lab.software_gate()
|
||||
for mode in ("legacy", "t2"):
|
||||
row = result[mode]
|
||||
assert row["header_pass"] == row["fec_pass"] == row["crc_pass"] == 16
|
||||
assert row["packet_output"] == row["exact_payload_match"] == 16
|
||||
assert row["packet_indexes"] == list(range(1, 17))
|
||||
assert row["exact_ordered_payloads"] and row["passed"]
|
||||
assert row["fallback_between_modes"] is False
|
||||
assert result["t2"]["t2_state_scope"] == "NEW_PREAMBLE_ESTIMATE_PER_PACKET"
|
||||
assert result["t2"]["t2_state_reset_evidence"][
|
||||
"tracking_runs_per_accepted_packet"
|
||||
] == [2] * 16
|
||||
assert result["t2_state_reset_verified_by_fresh_preamble_per_packet"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("failure", [None, "clock", "clipping", "strong", "overrun"])
|
||||
def test_preflight_never_transmits_and_fails_closed(tmp_path, monkeypatch, failure):
|
||||
raw, output = tmp_path / "raw", tmp_path / "processed"
|
||||
raw.mkdir(); output.mkdir()
|
||||
monkeypatch.setattr(lab, "RAW", raw); monkeypatch.setattr(lab, "OUTPUT", output)
|
||||
(raw / "tx_stream.cs8").write_bytes(b"test")
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
|
||||
(output / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
|
||||
monkeypatch.setattr(lab.lab054.phy.lab045, "_verify_device_inventory", lambda runner: {"passed": True})
|
||||
|
||||
def clock(point, runner, guard, **kwargs):
|
||||
return {"passed": failure != "clock", "master_reg29": 0x26,
|
||||
"slave_reg29": 0x26, "slave_clkin_status": "DETECTED"}
|
||||
|
||||
def runner(command):
|
||||
assert "-t" not in command
|
||||
if "-r" in command:
|
||||
value = 127 if failure == "clipping" else (110 if failure == "strong" else 1)
|
||||
Path(command[command.index("-r") + 1]).write_bytes(bytes([value, value]) * lab.PREFLIGHT_SAMPLES)
|
||||
log = "1 RX overruns\n0 RX overruns" if failure == "overrun" else "0 RX overruns"
|
||||
return subprocess.CompletedProcess(command, 0, log, "")
|
||||
|
||||
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
|
||||
kwargs = dict(runner=runner, clock_checker=clock,
|
||||
process_checker=lambda: ProcessCheck(ProcessState.CLEAR, ()),
|
||||
sleep_function=lambda _: None)
|
||||
if failure:
|
||||
with pytest.raises(RuntimeError): lab.preflight(**kwargs)
|
||||
else:
|
||||
assert lab.preflight(**kwargs)["passed"]
|
||||
record = json.loads((raw / "preflight_result.json").read_text())
|
||||
assert record["rf_tx_count"] == 0
|
||||
if failure == "overrun":
|
||||
assert record["rx_counter_summary"]["maximum_count"] == 1
|
||||
|
||||
|
||||
def test_capture_refuses_without_authorization_before_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path); monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
with pytest.raises(PermissionError):
|
||||
lab.capture_once(authorization=False, runner=lambda command: pytest.fail("hardware"))
|
||||
assert not (tmp_path / "hardware_attempt.json").exists()
|
||||
|
||||
|
||||
def test_future_analysis_declares_strict_modes_and_physical_comparison():
|
||||
source = Path(lab.__file__).read_text(encoding="utf-8")
|
||||
assert 'decode("legacy", rx_path, diagnostics=True)' in source
|
||||
assert 'decode("t2", rx_path, diagnostics=True)' in source
|
||||
assert "physical_packet_diagnostics(tx, rx, location)" in source
|
||||
assert "if not strict_runs_complete" in source
|
||||
assert '["Lab056 B", "Lab062", "Lab066"]' in source
|
||||
|
||||
|
||||
def test_future_analysis_rejects_capture_from_another_configuration(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path); monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
rx = tmp_path / "rx_stream.cs8"; rx.write_bytes(b"\x00\x00")
|
||||
capture = {
|
||||
"lab": "Lab066", "rf_tx_count": 1, "automatic_tx_repeats": 0,
|
||||
"point": {}, "parameters": {}, "tx_sha256": "WRONG",
|
||||
"rx_sha256": lab.sha256(rx),
|
||||
}
|
||||
(tmp_path / "capture_result.json").write_text(json.dumps(capture))
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "EXPECTED"})
|
||||
monkeypatch.setattr(lab, "decode", lambda *args, **kwargs: pytest.fail("decode"))
|
||||
with pytest.raises(RuntimeError, match="подтверждённой"):
|
||||
lab.analyze_saved_capture()
|
||||
|
||||
|
||||
def test_second_preflight_is_refused_before_hardware(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(lab, "RAW", tmp_path); monkeypatch.setattr(lab, "OUTPUT", tmp_path)
|
||||
(tmp_path / "preflight_attempt.json").write_text("{}")
|
||||
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
|
||||
(tmp_path / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
|
||||
with pytest.raises(FileExistsError):
|
||||
lab.preflight(runner=lambda command: pytest.fail("hardware"))
|
||||
79
tests/test_lab067_header_failure_analysis.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Проверки диагностических вычислений Lab067 без raw IQ и оборудования."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from experiments import lab067_header_failure_analysis as lab067
|
||||
|
||||
|
||||
def test_header_diagnostics_repeats_strict_soft_combining() -> None:
|
||||
expected = lab067.lab066.plans()[0].plan.phy_header_bits.astype(np.uint8)
|
||||
soft = 2.0 * expected.astype(float) - 1.0
|
||||
# Две копии одного бита имеют неверный знак, но малы по модулю. Третья
|
||||
# правильная копия сильнее, поэтому штатная мягкая сумма сохраняет бит.
|
||||
soft[:2] *= -0.1
|
||||
result = lab067.header_diagnostics(soft, expected)
|
||||
assert result["raw_header_errors"] == 2
|
||||
assert result["voted_header_errors"] == 0
|
||||
assert result["accepted"] is True
|
||||
|
||||
|
||||
def test_header_diagnostics_reports_field_and_copy_errors() -> None:
|
||||
expected = lab067.lab066.plans()[0].plan.phy_header_bits.astype(np.uint8)
|
||||
received = expected.copy()
|
||||
received[24:26] ^= 1
|
||||
result = lab067.header_diagnostics(
|
||||
2.0 * received.astype(float) - 1.0,
|
||||
expected,
|
||||
)
|
||||
assert result["raw_header_errors"] == 2
|
||||
assert result["copy_errors"] == [1, 1, 0]
|
||||
assert result["voted_header_errors"] == 1
|
||||
assert result["magic_errors"] == 1
|
||||
assert result["accepted"] is False
|
||||
|
||||
|
||||
def test_strict_candidate_selection_uses_nearest_expected_position() -> None:
|
||||
rows = [
|
||||
{"absolute_refined": 980, "packet_output": False, "crc": False, "fec": False, "header": False},
|
||||
{"absolute_refined": 1003, "packet_output": False, "crc": False, "fec": False, "header": True},
|
||||
]
|
||||
selected = lab067.strict_candidate_for_packet(rows, 1000)
|
||||
assert selected is rows[1]
|
||||
|
||||
|
||||
def test_strict_candidate_selection_never_prefers_farther_success() -> None:
|
||||
exact_failure = {
|
||||
"absolute_refined": 1000, "packet_output": False,
|
||||
"crc": False, "fec": False, "header": False,
|
||||
}
|
||||
farther_success = {
|
||||
"absolute_refined": 1064, "packet_output": True,
|
||||
"crc": True, "fec": True, "header": True,
|
||||
}
|
||||
selected = lab067.strict_candidate_for_packet(
|
||||
[farther_success, exact_failure], 1000
|
||||
)
|
||||
assert selected is exact_failure
|
||||
|
||||
|
||||
def test_strict_candidate_tie_break_does_not_use_verdict() -> None:
|
||||
lower_failure = {
|
||||
"absolute_refined": 999, "packet_output": False,
|
||||
"crc": False, "fec": False, "header": False,
|
||||
}
|
||||
upper_success = {
|
||||
"absolute_refined": 1001, "packet_output": True,
|
||||
"crc": True, "fec": True, "header": True,
|
||||
}
|
||||
selected = lab067.strict_candidate_for_packet(
|
||||
[upper_success, lower_failure], 1000
|
||||
)
|
||||
assert selected is lower_failure
|
||||
|
||||
|
||||
def test_lab067_has_no_rf_entry_point() -> None:
|
||||
assert not hasattr(lab067, "capture_once")
|
||||
assert lab067.RX_PATH.name == "rx_stream.cs8"
|
||||
assert lab067.FAILED_PACKETS == {1, 2, 3, 9, 15, 16}
|
||||