Lab056: compare 20 and 500 ksymbol/s in controlled RF session
This commit is contained in:
226
PROJECT_LOG.md
226
PROJECT_LOG.md
@@ -2218,3 +2218,229 @@ HEAD прежний0368c6d3e930f4d245e056223fd4e5c4f70e6b49, main; предыд
|
|||||||
изменения Lab054 сохранены. Git add/commit/push/pull/fetch не выполнялись.
|
изменения Lab054 сохранены. Git add/commit/push/pull/fetch не выполнялись.
|
||||||
Следующий этап: STOP и согласование измерительной схемы, приборов и допустимой
|
Следующий этап: STOP и согласование измерительной схемы, приборов и допустимой
|
||||||
частоты; никакого автоматического аппаратного продолжения.
|
частоты; никакого автоматического аппаратного продолжения.
|
||||||
|
|
||||||
|
# Запись 047
|
||||||
|
|
||||||
|
## Дата
|
||||||
|
|
||||||
|
9 сентября 2026 года
|
||||||
|
|
||||||
|
## Тема
|
||||||
|
|
||||||
|
Lab055 — фиксация результата; Lab056 — программная подготовка эфирного ABAB
|
||||||
|
|
||||||
|
## Выполнено
|
||||||
|
|
||||||
|
По новому явному разрешению пользователя Lab055 зафиксирована коммитом
|
||||||
|
5add95ecb8865f59075bbbced08d97b183fb3b48 с сообщением
|
||||||
|
Lab055: audit received signal energy across symbol rates.
|
||||||
|
В коммите семь файлов только Lab055: анализатор, тесты, отчёт, два JSON,
|
||||||
|
запись 046 и раздел индекса. Предшествующие изменения Lab054 сохранены
|
||||||
|
незакоммиченными. Перед коммитом quick_gate 823/28/13 PASS, pytest 823 passed.
|
||||||
|
Push не выполнялся. Исторический Lab054 NOT_A, 0/16, не изменён;
|
||||||
|
повторная проверка SHA-256 всех 34 входов Lab055 не выявила изменений.
|
||||||
|
|
||||||
|
Прямой выбор пользователя — эфирный опыт вместо предпочтительного кабеля
|
||||||
|
из приложения. Подготовлен один TX-файл ABAB: 20/500/20/500 ксимв/с,
|
||||||
|
прежние PRBS7/BPSK/RRC, Fs 8 Мвыб/с и цифровая несущая +50 кГц.
|
||||||
|
Созданы experiments/lab056_abab_energy.py, lab056_capture_runner.py,
|
||||||
|
tests/test_lab056_preparation.py, docs/lab056_current_state.md и малые
|
||||||
|
результаты data/processed/lab056. Большие CS8 находятся в data/raw/lab056.
|
||||||
|
|
||||||
|
TX RMS всего сегмента A/B: 44,004413/44,001925 код; разница мощности
|
||||||
|
−0,000491 дБ. Активные участки отдельно: −0,042606 дБ; клиппинг 0.
|
||||||
|
TX 10 588 544 отсчёта, 1,323568 с; RX планируется 3 с с упреждением 0,5 с.
|
||||||
|
A1=A2 и B1=B2 побайтно. Пять gaps по 0,1 с содержат нулевой цифровой IQ
|
||||||
|
при работающем TX-процессе: это не доказательство выключенного RF.
|
||||||
|
|
||||||
|
Анализатор сохраняет полную и AC-мощность RX, локальный фон, устойчивую
|
||||||
|
разность мощностей, комплексный коэффициент передачи, согласованную
|
||||||
|
компоненту, остаток и интегральный спектр. Мощность и энергия символа
|
||||||
|
выводятся отдельно. RX не выравнивается по амплитуде после приёма.
|
||||||
|
Искусственные записи с одинаковым усилением и падением B на 6,020600 дБ
|
||||||
|
обработаны успешно: оценки разницы коэффициента передачи соответственно
|
||||||
|
+0,001786/−0,002894 дБ и −6,014144/−6,022175 дБ для двух пар.
|
||||||
|
Это программные проверки, не аппаратный результат.
|
||||||
|
|
||||||
|
Подготовлен RX-first runner с единственной конечной TX-попыткой, без повтора,
|
||||||
|
проверкой процессов, SHA, размеров, счётчиков потерь и cleanup. Common clock
|
||||||
|
проверен только mock-тестами; реальные reg29/CLKIN сейчас NOT_PERFORMED.
|
||||||
|
Частота не назначена: 1400 МГц пересекает запрещённую для излучения полосу
|
||||||
|
1400–1427 МГц (ITU RR2024, п. 5.340). Для будущего опыта нужны допустимая
|
||||||
|
частота/полоса, страна и подтверждение антенн/неизменной геометрии.
|
||||||
|
Эфирный ABAB измеряет весь путь, включая антенны и распространение;
|
||||||
|
он не изолирует внутренние аппаратные потери HackRF и не измеряет dBm.
|
||||||
|
|
||||||
|
Verifier независимо пересчитал коэффициенты, проверил TX и mock-отказы.
|
||||||
|
Воспроизведённая ошибка разбора счётчиков с префиксом RX/TX исправлена;
|
||||||
|
повторная проверка закрыла находку. Остаточных воспроизведённых находок нет.
|
||||||
|
34 целевых теста PASS; итоговые quick_gate 857/30/13 PASS,
|
||||||
|
pytest 857 passed, 0 failed, 96,50 с; git diff --check PASS.
|
||||||
|
HackRF не запускался, RF TX=0. Lab056 не коммитилась; push не выполнялся.
|
||||||
|
Следующий этап: STOP, согласование допустимой эфирной конфигурации и отдельное
|
||||||
|
разрешение на конкретный однократный RF-сеанс перед аппаратным preflight.
|
||||||
|
|
||||||
|
# Запись 048
|
||||||
|
|
||||||
|
## Дата
|
||||||
|
|
||||||
|
10 сентября 2026 года
|
||||||
|
|
||||||
|
## Тема
|
||||||
|
|
||||||
|
Lab056 — принятое пользователем изменение методики на контролируемый OTA
|
||||||
|
|
||||||
|
## Выполнено
|
||||||
|
|
||||||
|
Пользователь сообщил об отсутствии необходимого SMA-переходника и осознанно
|
||||||
|
выбрал эфирный ABAB. Кабельный TX → 30 дБ → RX отложен до получения
|
||||||
|
переходника как отдельная проверка. Центральную частоту определяет пользователь:
|
||||||
|
RF_FREQ = USER_AUTHORIZED_RF_HZ. Конкретное значение пока не сообщено;
|
||||||
|
обязательные поля страны/регуляторной полосы убраны из параметров runner,
|
||||||
|
национальная регуляторика повторно не исследовалась. В отчёте подготовлены
|
||||||
|
поля антенн, расстояния, ориентации/поляризации, LOS/NLOS и частоты.
|
||||||
|
|
||||||
|
Готовый ABAB waveform, метаданные, прежние результаты и исходная сводка
|
||||||
|
857 passed сохранены. RMS A/B 44,004413/44,001925 код, разница мощности
|
||||||
|
−0,000491 дБ, TX 1,323568 с, RX 3 с. Усиления TX10/RX24/24,
|
||||||
|
Fs 8 Мвыб/с и несущая +50 кГц не менялись; внешних PA/LNA нет.
|
||||||
|
|
||||||
|
В runner оформлен отдельный RX-only preflight: common clock с обоими
|
||||||
|
reg29=0x26 и SLAVE CLKIN=DETECTED, 0,1 с приёма фона для проверки уровня,
|
||||||
|
сохранение результата и cleanup, затем STOP перед отдельным разрешением TX.
|
||||||
|
При частоте UNKNOWN аппаратных действий нет. capture_once требует PASS
|
||||||
|
этой конфигурации, отдельной авторизации и свежей проверки опоры.
|
||||||
|
При клиппинге до TX — RF TX=0; при обнаружении только в записи ABAB —
|
||||||
|
RF TX=1 и отказ без повтора. Счётчик уже выполненной передачи не обнуляется.
|
||||||
|
Ни частота, ни gains автоматически не подбираются.
|
||||||
|
|
||||||
|
Добавлен явный комплексный h прежней модели, сводки X/Y/Z и повторяемость
|
||||||
|
A2/A1, B2/B1. Те же искусственные IQ прочитаны повторно с новыми именами
|
||||||
|
выходных JSON. Для равного усиления X/Y/Z −0,046404/−0,000553/−14,025804 дБ;
|
||||||
|
для ослабления B на 6,020600 дБ: −6,039124/−6,018156/−20,018524 дБ.
|
||||||
|
Это программные значения, не аппаратный результат Lab056. Эфирный опыт
|
||||||
|
будет характеризовать весь путь, а не отдельно внутренние потери HackRF.
|
||||||
|
|
||||||
|
44 целевых теста PASS. Итоговые quick_gate 867/30/13 PASS,
|
||||||
|
pytest 867 passed, 0 failed, 89,32 с; git diff --check PASS.
|
||||||
|
Verifier независимо исполнил 44 случая с данными в памяти, без записи
|
||||||
|
и оборудования; воспроизведённых находок нет. Исторический Lab054 NOT_A
|
||||||
|
сохранён. HEAD 5add95ecb8865f59075bbbced08d97b183fb3b48, Lab056 не коммитилась,
|
||||||
|
push не выполнялся. Аппаратных команд нет, RF TX=0.
|
||||||
|
Следующий этап: получить RF_FREQ, выполнить разрешённый RX-only аппаратный
|
||||||
|
preflight, остановиться с кратким отчётом и ждать отдельного разрешения
|
||||||
|
на один ABAB-сеанс. Кабельный контроль и PA автоматически не добавляются.
|
||||||
|
|
||||||
|
# Запись 049
|
||||||
|
|
||||||
|
## Дата
|
||||||
|
|
||||||
|
10 сентября 2026 года
|
||||||
|
|
||||||
|
## Тема
|
||||||
|
|
||||||
|
Lab056 — аппаратный common-clock и RX-only preflight на частоте пользователя
|
||||||
|
|
||||||
|
## Выполнено
|
||||||
|
|
||||||
|
Пользователь задал RF_FREQ=1400000000 Гц только для текущей лабораторной
|
||||||
|
проверки. Это не рабочая частота SDR_Rover; ориентир полевых работ около
|
||||||
|
180 МГц и варианты 50/144 МГц здесь не исследуются. Задана геометрия OTA:
|
||||||
|
имеющиеся антенны 1400 МГц, LOS, неподвижное оборудование, одинаковые
|
||||||
|
поляризация/ориентация, расстояние примерно 3–5 м. Точные модели антенн
|
||||||
|
и измеренное расстояние не сообщены; описание сохранено без выдуманных данных.
|
||||||
|
|
||||||
|
Выполнен только разрешённый аппаратный preflight. Инвентаризация подтвердила
|
||||||
|
MASTER ...323b97d3 и SLAVE ...2e5327d3. После коротких RX-start обоих устройств
|
||||||
|
MASTER reg29=0x26, SLAVE reg29=0x26, SLAVE CLKIN=DETECTED; опора обнаружена
|
||||||
|
первым запросом через 67,787 мс ожидания. Затем SLAVE записал 800 000
|
||||||
|
комплексных отсчётов, 0,1 с, на той же частоте, Fs 8 Мвыб/с, RX24/24 дБ,
|
||||||
|
RF amplifier/Bias-Tee OFF. Внешних PA/LNA нет. TX не запускался.
|
||||||
|
|
||||||
|
CS8 1 600 000 байт, SHA-256
|
||||||
|
c4257f26e4cc8562eb32228f9a50fdeb055879c928af0744cdf4a81cf7cd085b.
|
||||||
|
RMS 2,203152571 код, мощность 4,85388125 код², AC-мощность 0,6658039582 код²,
|
||||||
|
максимумы абсолютных I/Q 4/6 код. Клиппинг 0 из 800 000 отсчётов, overruns 0.
|
||||||
|
Отсутствие клиппинга фона без TX не гарантирует отсутствие перегрузки
|
||||||
|
при будущем включении TX. После проверки CLKOUT обеих плат выключены,
|
||||||
|
процессы CLEAR до/после, cleanup без ошибок.
|
||||||
|
|
||||||
|
Сохранены исходный RX, preflight_result/attempt, план и журнал фактических
|
||||||
|
команд. Три hackrf_transfer-команды были только RX: два коротких запуска
|
||||||
|
для опоры и запись фона; TX-команд 0. Отчёт docs/lab056_rx_preflight.md.
|
||||||
|
Verifier только по сохранённым файлам подтвердил 12 команд, регистры,
|
||||||
|
CLKIN, параметры и cleanup; пересчитал SHA и мощность/клиппинг вторым
|
||||||
|
способом. Воспроизведённых расхождений нет, PASS, аппаратных команд не было.
|
||||||
|
Код и готовый TX не менялись; SHA совпадают с проверенной версией.
|
||||||
|
Последние программные проверки этой версии: 44 целевых теста,
|
||||||
|
quick_gate 867/30/13 PASS, pytest 867 passed. Git diff --check PASS.
|
||||||
|
Lab054 NOT_A сохранён. HEAD 5add95ecb8865f59075bbbced08d97b183fb3b48;
|
||||||
|
Lab056 не коммитилась, push не выполнялся. RF TX=0.
|
||||||
|
Следующий этап: STOP с техническим READY FOR SINGLE ABAB TX=YES;
|
||||||
|
дождаться отдельного разрешения пользователя на единственный ABAB TX.
|
||||||
|
|
||||||
|
# Запись 050
|
||||||
|
|
||||||
|
## Дата
|
||||||
|
|
||||||
|
10 сентября 2026 года
|
||||||
|
|
||||||
|
## Тема
|
||||||
|
|
||||||
|
Lab056 — единственный OTA ABAB 20/500 ксимв/с; большой провал не воспроизведён
|
||||||
|
|
||||||
|
## Выполнено
|
||||||
|
|
||||||
|
После отдельного явного разрешения пользователя выполнен один конечный
|
||||||
|
ABAB TX с неизменным ранее подготовленным файлом 10 588 544 отсчёта,
|
||||||
|
1,323568 с. RX запущен первым и сохранил 24 000 000 комплексных отсчётов,
|
||||||
|
48 000 000 байт, 3 с. Частота 1400000000 Гц только для Lab056,
|
||||||
|
Fs 8 Мвыб/с, TX10/RX24/24 дБ, несущая +50 кГц, прежние PRBS7/BPSK/RRC;
|
||||||
|
RF amplifier/Bias-Tee OFF, внешних PA/LNA нет. Геометрия по заданию
|
||||||
|
пользователя: имеющиеся антенны 1400 МГц, LOS, примерно 3–5 м,
|
||||||
|
неподвижность и одинаковые поляризация/ориентация. Параметры между A/B
|
||||||
|
не менялись. Код, пороги, timing, FEC/CRC и цифровой TX не менялись.
|
||||||
|
|
||||||
|
Свежая опора перед сеансом PASS: оба reg29=0x26, SLAVE CLKIN=DETECTED
|
||||||
|
через 137,359 мс штатного ожидания. По журналу RX стартовал на 0,517342 с
|
||||||
|
раньше TX. RX overruns=0, TX underruns=0, клиппинг 0 до и после ABAB.
|
||||||
|
Полный RX RMS 2,235380477 код, максимумы абсолютных I/Q 5/6 код.
|
||||||
|
Все 14 аппаратных команд сохранены; среди них один TX без повтора.
|
||||||
|
CLKOUT после опыта выключены, процессы CLEAR, cleanup без ошибок.
|
||||||
|
|
||||||
|
Подготовленный анализатор нашёл четыре сегмента с корреляцией 0,384366.
|
||||||
|
Начала A1/B1/A2/B2 в RX: 5 254 791 / 7 709 991 / 10 149 062 /
|
||||||
|
12 604 263; общий сдвиг относительно TX расходится лишь на один отсчёт.
|
||||||
|
Все активные окна 0,2048 с, порядок ABAB и gaps подтверждены.
|
||||||
|
Полезные мощности после фона A1/B1/A2/B2:
|
||||||
|
0,462680958 / 0,476010015 / 0,414075173 / 0,423007891 код².
|
||||||
|
Согласованные компоненты: 0,312320987 / 0,297815525 /
|
||||||
|
0,299566893 / 0,285847043 код². Raw/AC/фон/комплексный h/остаток/спектры
|
||||||
|
по каждому сегменту приведены в docs/lab056_rf_result.md и rf_analysis.json.
|
||||||
|
|
||||||
|
Итоги по средним линейным мощностям:
|
||||||
|
X, принятая мощность B/A после вычитания фона: +0,108895 дБ;
|
||||||
|
Y, коэффициент передачи abs(h)² B/A: −0,163042 дБ;
|
||||||
|
Z, энергия символа этой же принятой мощности: −13,870505 дБ.
|
||||||
|
Повтор A2/A1 по мощности −0,482024 дБ, B2/B1 −0,512676 дБ;
|
||||||
|
по abs(h)² −0,181074/−0,178136 дБ. Парные оценки B/A близки.
|
||||||
|
|
||||||
|
Прежний провал порядка 15,41 дБ в одной короткой OTA-сессии не воспроизвёлся.
|
||||||
|
Это не выделяет собственные потери HackRF из антенн и распространения
|
||||||
|
и не объясняет причину старого различия раздельных опытов. Кабельный контроль
|
||||||
|
после SMA-переходника остаётся отдельным необходимым этапом для такого
|
||||||
|
разделения. PA не назначен. Packet/PHY PASS, видео и FEC не исследовались.
|
||||||
|
Исторический Lab054 NOT_A, 0/16 сохранён; 34 исходных SHA Lab055 не изменились.
|
||||||
|
|
||||||
|
Verifier только по сохранённым файлам независимо подтвердил команды,
|
||||||
|
SHA/размеры/клиппинг, четыре начала, мощности/фон и комплексный МНК;
|
||||||
|
расчёты согласованы до 1e−12, воспроизведённых находок нет.
|
||||||
|
RX SHA e336dd653fa554058052fcf851e9f2462f66b8cf7c710815ca2b9a09ffc23ad4.
|
||||||
|
TX SHA 5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600.
|
||||||
|
Сохранены raw IQ, журналы, машинная сводка rf_result.json и график
|
||||||
|
data/processed/lab056/rf_overview.png. Код прежний: последние проверки
|
||||||
|
44 целевых теста, quick_gate 867/30/13, pytest 867 passed; diff check PASS.
|
||||||
|
HEAD 5add95ecb8865f59075bbbced08d97b183fb3b48. Lab056 не коммитилась,
|
||||||
|
push не выполнялся. Выполненных TX=1, сейчас RF TX=0, повторов нет.
|
||||||
|
Следующий этап: STOP и рассмотрение отчёта пользователем; никакого
|
||||||
|
автоматического второго RF-сеанса, кабельного контроля или добавления PA.
|
||||||
|
|||||||
76
data/processed/lab056/acceptance.json
Normal file
76
data/processed/lab056/acceptance.json
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-09-09",
|
||||||
|
"stage": "SOFTWARE_READY_RF_CONFIGURATION_PENDING",
|
||||||
|
"lab055_commit": "5add95ecb8865f59075bbbced08d97b183fb3b48",
|
||||||
|
"new_head": "5add95ecb8865f59075bbbced08d97b183fb3b48",
|
||||||
|
"lab055_commit_files": 7,
|
||||||
|
"lab055_inputs_sha256_rechecked": 34,
|
||||||
|
"lab055_inputs_changed": [],
|
||||||
|
"lab054_official_result": "NOT_A",
|
||||||
|
"lab054_packets": "0/16",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"hardware_commands_executed": 0,
|
||||||
|
"hardware_common_clock_preflight": "NOT_PERFORMED",
|
||||||
|
"mock_common_clock": "PASS",
|
||||||
|
"rf_frequency_hz": null,
|
||||||
|
"physical_path": "OTA_REQUESTED_FREQUENCY_AND_GEOMETRY_PENDING",
|
||||||
|
"waiting_for_explicit_rf_authorization": true,
|
||||||
|
"lab056_committed": false,
|
||||||
|
"push_performed": false,
|
||||||
|
"targeted_tests": {
|
||||||
|
"passed": 34,
|
||||||
|
"failed": 0
|
||||||
|
},
|
||||||
|
"quick_gate": {
|
||||||
|
"command": "python tools/quick_gate.py",
|
||||||
|
"passed": true,
|
||||||
|
"core_tests": 857,
|
||||||
|
"functional_labs": 30,
|
||||||
|
"skipped_labs": 13
|
||||||
|
},
|
||||||
|
"pytest": {
|
||||||
|
"command": "python -m pytest tests/ -q -p no:cacheprovider",
|
||||||
|
"passed": 857,
|
||||||
|
"failed": 0,
|
||||||
|
"elapsed_seconds": 96.5
|
||||||
|
},
|
||||||
|
"diff_check": "PASS",
|
||||||
|
"verifier": {
|
||||||
|
"mode": "READ_ONLY_SOFTWARE",
|
||||||
|
"remaining_reproduced_findings": 0,
|
||||||
|
"counter_parser_finding": "FIXED_AND_REPRODUCIBLY_CLOSED"
|
||||||
|
},
|
||||||
|
"synthetic_equal": {
|
||||||
|
"passed": true,
|
||||||
|
"expected_gain_difference_db": 0.0,
|
||||||
|
"measured_pairs_db": [
|
||||||
|
0.0017861283874782213,
|
||||||
|
-0.0028938012026523408
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"synthetic_minus6db": {
|
||||||
|
"passed": true,
|
||||||
|
"expected_gain_difference_db": -6.020599913279624,
|
||||||
|
"measured_pairs_db": [
|
||||||
|
-6.014143555521402,
|
||||||
|
-6.022174975174189
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"power_units": "RX_CODE_SQUARED_NOT_CALIBRATED_RF_WATTS",
|
||||||
|
"gap_is_zero_digital_iq_not_rf_off": true,
|
||||||
|
"ota_scope": "ENTIRE_TX_ANTENNA_PROPAGATION_RX_PATH_NOT_ISOLATED_HACKRF",
|
||||||
|
"artifact_sha256": {
|
||||||
|
"experiments/lab056_abab_energy.py": "a36d277998b9b07c97eed0cfedec98ffed1ee2ea44e150f870b191b9a2d957c7",
|
||||||
|
"experiments/lab056_capture_runner.py": "4ccfec4bfd123602d61dba5107e5ac7763326aaaf6cdb2b1e7dbcfdfcb059488",
|
||||||
|
"tests/test_lab056_preparation.py": "74b428096a28c91ce603d3e13ad03ef9526a1a5a4be2d5001ec61441a4d1da1e",
|
||||||
|
"experiments/lab054_hardware_control.py": "199046f939d39eaa7b2a5769149dd43052b353bba79b4cccba60b7ab72071101",
|
||||||
|
"experiments/lab055_energy_audit.py": "ac4da1e31bc20b1740aebfab31f1368adb4ed6afe845ba8c4e45f579ea0f3753",
|
||||||
|
"data/processed/lab056/preparation.json": "535375d08a0699ac4be31992ddbb6c5d175b3156a0a3b508b45d7cb567c24071",
|
||||||
|
"data/processed/lab056/software_equal.json": "2c8bd05a0a1d488961a9a353c3ba9bf624e929792b66371bdb2b190b2cf22ca8",
|
||||||
|
"data/processed/lab056/software_minus6db.json": "c015de2d5238d5d3c1ea9d56b0e4a5da200f2e0f14e3640fa27a396ffd489f67",
|
||||||
|
"data/raw/lab056/tx_abab.cs8": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"data/raw/lab056/tx_metadata.json": "535375d08a0699ac4be31992ddbb6c5d175b3156a0a3b508b45d7cb567c24071",
|
||||||
|
"data/raw/lab056/synthetic_equal.cs8": "a88afcc1bbeb50092f9f5baca6eb607d25daf508e0fafcdcada318818d9ffd62",
|
||||||
|
"data/raw/lab056/synthetic_minus6db.cs8": "a189c1ffaf029983d41f7d87e262cbbc28ba740fc8915a75e41ee0adec9c1fe6"
|
||||||
|
}
|
||||||
|
}
|
||||||
135
data/processed/lab056/ota_correction.json
Normal file
135
data/processed/lab056/ota_correction.json
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-09-10",
|
||||||
|
"stage": "OTA_CORRECTION_READY_WAITING_USER_RF_FREQ",
|
||||||
|
"head": "5add95ecb8865f59075bbbced08d97b183fb3b48",
|
||||||
|
"method": "CONTROLLED_OTA_ABAB",
|
||||||
|
"reason": "USER_REPORTED_MISSING_SMA_ADAPTER",
|
||||||
|
"conducted_test": "DEFERRED_SEPARATE_CONTROL_AFTER_ADAPTER",
|
||||||
|
"rf_frequency_hz": null,
|
||||||
|
"rf_frequency_source": "USER_AUTHORIZED_RF_HZ",
|
||||||
|
"country_required": false,
|
||||||
|
"national_regulatory_research_performed": false,
|
||||||
|
"geometry": {
|
||||||
|
"tx_antenna": null,
|
||||||
|
"rx_antenna": null,
|
||||||
|
"distance_m": null,
|
||||||
|
"orientation": null,
|
||||||
|
"los_nlos": null
|
||||||
|
},
|
||||||
|
"rf_tx": 0,
|
||||||
|
"hardware_commands_executed": 0,
|
||||||
|
"hardware_common_clock": "NOT_PERFORMED",
|
||||||
|
"next_step": "USER_RF_FREQ_THEN_RX_ONLY_PREFLIGHT_THEN_STOP_BEFORE_TX_AUTHORIZATION",
|
||||||
|
"preflight_rx_seconds": 0.1,
|
||||||
|
"rx_capture_seconds": 3.0,
|
||||||
|
"tx_seconds": 1.323568,
|
||||||
|
"preflight_background_does_not_prove_no_clipping_during_tx": true,
|
||||||
|
"saturation_policy": {
|
||||||
|
"before_tx": "STOP_WITH_TX_COUNT_0",
|
||||||
|
"during_tx": "PRESERVE_ACTUAL_TX_COUNT_1_AND_STOP_NO_RETRY"
|
||||||
|
},
|
||||||
|
"baseline_acceptance_preserved": {
|
||||||
|
"path": "data/processed/lab056/acceptance.json",
|
||||||
|
"sha256": "ba48c2bb22947333aee99d5e72b0c6b661e617b1047b2672cb2d4c21ef721320",
|
||||||
|
"pytest_passed": 857
|
||||||
|
},
|
||||||
|
"targeted_tests": {
|
||||||
|
"passed": 44,
|
||||||
|
"failed": 0
|
||||||
|
},
|
||||||
|
"quick_gate": {
|
||||||
|
"passed": true,
|
||||||
|
"core_tests": 867,
|
||||||
|
"functional_labs": 30,
|
||||||
|
"skipped_labs": 13
|
||||||
|
},
|
||||||
|
"pytest": {
|
||||||
|
"command": "python -m pytest tests/ -q -p no:cacheprovider",
|
||||||
|
"passed": 867,
|
||||||
|
"failed": 0,
|
||||||
|
"elapsed_seconds": 89.32
|
||||||
|
},
|
||||||
|
"verifier": {
|
||||||
|
"mode": "READ_ONLY_IN_MEMORY",
|
||||||
|
"cases_passed": 44,
|
||||||
|
"remaining_reproduced_findings": 0
|
||||||
|
},
|
||||||
|
"original_segment_measurements_and_pairs_unchanged": true,
|
||||||
|
"synthetic_comparison_summaries": {
|
||||||
|
"equal": {
|
||||||
|
"repeatability": [
|
||||||
|
{
|
||||||
|
"comparison": "A2/A1",
|
||||||
|
"raw_rx_power_difference_db": -0.0012085532551280598,
|
||||||
|
"background_difference_db": -0.0017255178918689754,
|
||||||
|
"received_power_difference_db": -0.005652782408264756,
|
||||||
|
"channel_gain_difference_db": -0.0023081300526506692,
|
||||||
|
"matched_component_difference_db": -0.00230813005264874
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comparison": "B2/B1",
|
||||||
|
"raw_rx_power_difference_db": 0.00036367664985406537,
|
||||||
|
"background_difference_db": -0.011492673362299905,
|
||||||
|
"received_power_difference_db": 0.004534904727870617,
|
||||||
|
"channel_gain_difference_db": -0.006988059642781434,
|
||||||
|
"matched_component_difference_db": -0.006988059642780951
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": -0.04640374115997541,
|
||||||
|
"channel_gain_difference_db": -0.000552584218408658,
|
||||||
|
"energy_per_symbol_difference_db": -14.025803827880353,
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"
|
||||||
|
},
|
||||||
|
"minus6db": {
|
||||||
|
"repeatability": [
|
||||||
|
{
|
||||||
|
"comparison": "A2/A1",
|
||||||
|
"raw_rx_power_difference_db": -0.0012085532551280598,
|
||||||
|
"background_difference_db": -0.0017255178918689754,
|
||||||
|
"received_power_difference_db": -0.005652782408264756,
|
||||||
|
"channel_gain_difference_db": -0.0023081300526506692,
|
||||||
|
"matched_component_difference_db": -0.00230813005264874
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comparison": "B2/B1",
|
||||||
|
"raw_rx_power_difference_db": 0.0016044036249902335,
|
||||||
|
"background_difference_db": -0.011492673362299905,
|
||||||
|
"received_power_difference_db": 0.016513577295241087,
|
||||||
|
"channel_gain_difference_db": -0.0103395497054386,
|
||||||
|
"matched_component_difference_db": -0.010339549705438118
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": -6.039124271880495,
|
||||||
|
"channel_gain_difference_db": -6.018156341674954,
|
||||||
|
"energy_per_symbol_difference_db": -20.01852435860087,
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lab055_inputs_rechecked": 34,
|
||||||
|
"lab055_inputs_changed": [],
|
||||||
|
"lab054_official": "NOT_A, 0/16",
|
||||||
|
"preserved_artifact_sha256": {
|
||||||
|
"experiments/lab054_hardware_control.py": "199046f939d39eaa7b2a5769149dd43052b353bba79b4cccba60b7ab72071101",
|
||||||
|
"experiments/lab055_energy_audit.py": "ac4da1e31bc20b1740aebfab31f1368adb4ed6afe845ba8c4e45f579ea0f3753",
|
||||||
|
"data/processed/lab056/preparation.json": "535375d08a0699ac4be31992ddbb6c5d175b3156a0a3b508b45d7cb567c24071",
|
||||||
|
"data/processed/lab056/software_equal.json": "2c8bd05a0a1d488961a9a353c3ba9bf624e929792b66371bdb2b190b2cf22ca8",
|
||||||
|
"data/processed/lab056/software_minus6db.json": "c015de2d5238d5d3c1ea9d56b0e4a5da200f2e0f14e3640fa27a396ffd489f67",
|
||||||
|
"data/raw/lab056/tx_abab.cs8": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"data/raw/lab056/tx_metadata.json": "535375d08a0699ac4be31992ddbb6c5d175b3156a0a3b508b45d7cb567c24071",
|
||||||
|
"data/raw/lab056/synthetic_equal.cs8": "a88afcc1bbeb50092f9f5baca6eb607d25daf508e0fafcdcada318818d9ffd62",
|
||||||
|
"data/raw/lab056/synthetic_minus6db.cs8": "a189c1ffaf029983d41f7d87e262cbbc28ba740fc8915a75e41ee0adec9c1fe6"
|
||||||
|
},
|
||||||
|
"new_artifact_sha256": {
|
||||||
|
"experiments/lab056_abab_energy.py": "323408b3bc1aa02c8b2f2855cd6295ac2efd99f13afec2ca0c872aa013a78f96",
|
||||||
|
"experiments/lab056_capture_runner.py": "24c17060b8b3e3f6899f9e922ae818e7ae2efba456c5f5cada7c69b5e5429334",
|
||||||
|
"tests/test_lab056_preparation.py": "d48aef79ae2455ca397862082a89f4c5f9a7e4d678e9b464c8489dd0e319a5ac",
|
||||||
|
"docs/lab056_current_state.md": "1accf50e4b67ec60fd6e8f041048d4b4b4a04551ebb51e43dafb0d52e40c7e35",
|
||||||
|
"data/processed/lab056/software_equal_ota_summary.json": "2a04b074eb9f59e726fd9cb70744498384ce4a5aa3c3bd7e92ae21f4e55d05a1",
|
||||||
|
"data/processed/lab056/software_minus6db_ota_summary.json": "01eb38bdc7f3bbd5bf468959cf7395a2b865318d6340cc9a70683576606cd3e0"
|
||||||
|
},
|
||||||
|
"lab056_commit": false,
|
||||||
|
"push": false,
|
||||||
|
"diff_check": "PASS"
|
||||||
|
}
|
||||||
173
data/processed/lab056/preparation.json
Normal file
173
data/processed/lab056/preparation.json
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
{
|
||||||
|
"stage": "PREPARATION_ONLY",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"rf_frequency_hz": null,
|
||||||
|
"physical_path": "OTA_REQUESTED_FREQUENCY_AND_GEOMETRY_PENDING",
|
||||||
|
"sample_rate_hz": 8000000,
|
||||||
|
"carrier_hz": 50000,
|
||||||
|
"tx_samples": 10588544,
|
||||||
|
"tx_bytes": 21177088,
|
||||||
|
"tx_seconds": 1.323568,
|
||||||
|
"rx_samples": 24000000,
|
||||||
|
"rx_seconds": 3.0,
|
||||||
|
"rx_lead_seconds": 0.5,
|
||||||
|
"gap_seconds": 0.1,
|
||||||
|
"gap_is_zero_digital_iq_not_rf_off": true,
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"start": 800000,
|
||||||
|
"samples": 1655200,
|
||||||
|
"active_start": 808400,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"whole": {
|
||||||
|
"samples": 1655200,
|
||||||
|
"mean_i": -3.624939584340261e-06,
|
||||||
|
"mean_q": -8.458192363460609e-06,
|
||||||
|
"power_code2": 1936.3883820686322,
|
||||||
|
"ac_power_code2": 1936.3883820685476,
|
||||||
|
"rms_code": 44.00441321127498,
|
||||||
|
"peak_magnitude_code": 69.29646455628166,
|
||||||
|
"max_abs_i": 68.0,
|
||||||
|
"max_abs_q": 69.0,
|
||||||
|
"papr_db": 3.9442968742176006,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.09681941910343161
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 2.99072265625e-05,
|
||||||
|
"mean_q": -0.0008270263671875,
|
||||||
|
"power_code2": 1956.0524084472656,
|
||||||
|
"ac_power_code2": 1956.0524077623986,
|
||||||
|
"rms_code": 44.22728126900031,
|
||||||
|
"peak_magnitude_code": 69.29646455628166,
|
||||||
|
"max_abs_i": 68.0,
|
||||||
|
"max_abs_q": 69.0,
|
||||||
|
"papr_db": 3.9004166907590005,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.09780262042236328
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"start": 3255200,
|
||||||
|
"samples": 1639072,
|
||||||
|
"active_start": 3255536,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"whole": {
|
||||||
|
"samples": 1639072,
|
||||||
|
"mean_i": 0.00011042833993869702,
|
||||||
|
"mean_q": -0.002146336463559868,
|
||||||
|
"power_code2": 1936.1693793805275,
|
||||||
|
"ac_power_code2": 1936.169374761573,
|
||||||
|
"rms_code": 44.00192472359053,
|
||||||
|
"peak_magnitude_code": 68.6804193347711,
|
||||||
|
"max_abs_i": 66.0,
|
||||||
|
"max_abs_q": 68.0,
|
||||||
|
"papr_db": 3.8672252879366917,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.003872338758761055
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": -8.544921875e-06,
|
||||||
|
"mean_q": -0.002210693359375,
|
||||||
|
"power_code2": 1936.9563952636718,
|
||||||
|
"ac_power_code2": 1936.9563903764335,
|
||||||
|
"rms_code": 44.010866786098084,
|
||||||
|
"peak_magnitude_code": 68.6804193347711,
|
||||||
|
"max_abs_i": 66.0,
|
||||||
|
"max_abs_q": 68.0,
|
||||||
|
"papr_db": 3.865460322480824,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0038739127905273434
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"start": 5694272,
|
||||||
|
"samples": 1655200,
|
||||||
|
"active_start": 5702672,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"whole": {
|
||||||
|
"samples": 1655200,
|
||||||
|
"mean_i": -3.624939584340261e-06,
|
||||||
|
"mean_q": -8.458192363460609e-06,
|
||||||
|
"power_code2": 1936.3883820686322,
|
||||||
|
"ac_power_code2": 1936.3883820685476,
|
||||||
|
"rms_code": 44.00441321127498,
|
||||||
|
"peak_magnitude_code": 69.29646455628166,
|
||||||
|
"max_abs_i": 68.0,
|
||||||
|
"max_abs_q": 69.0,
|
||||||
|
"papr_db": 3.9442968742176006,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.09681941910343161
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 2.99072265625e-05,
|
||||||
|
"mean_q": -0.0008270263671875,
|
||||||
|
"power_code2": 1956.0524084472656,
|
||||||
|
"ac_power_code2": 1956.0524077623986,
|
||||||
|
"rms_code": 44.22728126900031,
|
||||||
|
"peak_magnitude_code": 69.29646455628166,
|
||||||
|
"max_abs_i": 68.0,
|
||||||
|
"max_abs_q": 69.0,
|
||||||
|
"papr_db": 3.9004166907590005,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.09780262042236328
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"start": 8149472,
|
||||||
|
"samples": 1639072,
|
||||||
|
"active_start": 8149808,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"whole": {
|
||||||
|
"samples": 1639072,
|
||||||
|
"mean_i": 0.00011042833993869702,
|
||||||
|
"mean_q": -0.002146336463559868,
|
||||||
|
"power_code2": 1936.1693793805275,
|
||||||
|
"ac_power_code2": 1936.169374761573,
|
||||||
|
"rms_code": 44.00192472359053,
|
||||||
|
"peak_magnitude_code": 68.6804193347711,
|
||||||
|
"max_abs_i": 66.0,
|
||||||
|
"max_abs_q": 68.0,
|
||||||
|
"papr_db": 3.8672252879366917,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.003872338758761055
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": -8.544921875e-06,
|
||||||
|
"mean_q": -0.002210693359375,
|
||||||
|
"power_code2": 1936.9563952636718,
|
||||||
|
"ac_power_code2": 1936.9563903764335,
|
||||||
|
"rms_code": 44.010866786098084,
|
||||||
|
"peak_magnitude_code": 68.6804193347711,
|
||||||
|
"max_abs_i": 66.0,
|
||||||
|
"max_abs_q": 68.0,
|
||||||
|
"papr_db": 3.865460322480824,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0038739127905273434
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"whole_power_difference_db": -0.000491208472157679,
|
||||||
|
"active_power_difference_db": -0.0426064264748898
|
||||||
|
}
|
||||||
530
data/processed/lab056/rf_analysis.json
Normal file
530
data/processed/lab056/rf_analysis.json
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
{
|
||||||
|
"scope": "TX_REFERENCE_DIAGNOSTICS_NOT_INDEPENDENT_PACKET_RX",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"rx_sha256": "e336dd653fa554058052fcf851e9f2462f66b8cf7c710815ca2b9a09ffc23ad4",
|
||||||
|
"alignment": {
|
||||||
|
"start": 4454784,
|
||||||
|
"correlation": 0.3843661479500724,
|
||||||
|
"coarse_cfo_hz": 0.0,
|
||||||
|
"stride": 32
|
||||||
|
},
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9375872802734375,
|
||||||
|
"mean_q": 1.827298583984375,
|
||||||
|
"power_code2": 5.303053588867187,
|
||||||
|
"ac_power_code2": 1.084963565705345,
|
||||||
|
"rms_code": 2.3028359882690705,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.88257840909542,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002651526794433594,
|
||||||
|
"band_27000_power": 0.3472643231791639,
|
||||||
|
"band_675000_power": 0.5082992616247881,
|
||||||
|
"power_standard_error": 0.020810438717554197
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9454104166666667,
|
||||||
|
"mean_q": 1.8225354166666667,
|
||||||
|
"power_code2": 4.857945833333333,
|
||||||
|
"ac_power_code2": 0.642509632387152,
|
||||||
|
"rms_code": 2.204074824803671,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.759453298373341,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00024289729166666665,
|
||||||
|
"band_27000_power": 0.011441788935577513,
|
||||||
|
"band_675000_power": 0.1346566488652154,
|
||||||
|
"power_standard_error": 0.011146582753825698
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9274020833333333,
|
||||||
|
"mean_q": 1.8395416666666666,
|
||||||
|
"power_code2": 4.84604375,
|
||||||
|
"ac_power_code2": 0.6020555824262157,
|
||||||
|
"rms_code": 2.2013731510127945,
|
||||||
|
"peak_magnitude_code": 5.0,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 4.0,
|
||||||
|
"papr_db": 7.125526780497051,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002423021875,
|
||||||
|
"band_27000_power": 0.009082996916230657,
|
||||||
|
"band_675000_power": 0.10440265872939565,
|
||||||
|
"power_standard_error": 0.005986244399660487
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.6222826074066838,
|
||||||
|
"background_subtracted_raw": 0.46268095829866107,
|
||||||
|
"background_subtracted_uncertainty": 0.029702310624809113,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.46268095829866107,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": -0.008543898749645305,
|
||||||
|
"imag": 0.009310849226837265,
|
||||||
|
"phase_rad_at_active_start": 2.3132658926165255
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0001596901191690867,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 5254791,
|
||||||
|
"alignment_adjustment": 7,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9375872802734375,
|
||||||
|
"mean_q": 1.827298583984375,
|
||||||
|
"power_code2": 5.303053588867187,
|
||||||
|
"ac_power_code2": 1.084963565705345,
|
||||||
|
"rms_code": 2.3028359882690705,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.88257840909542,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002651526794433594
|
||||||
|
},
|
||||||
|
"cfo_hz": -0.028056675950692833,
|
||||||
|
"gain_abs": 0.012636855588677378,
|
||||||
|
"dc_i": 0.9388651889450522,
|
||||||
|
"dc_q": 1.8271201059862494,
|
||||||
|
"train_signal_power": 0.312403496916226,
|
||||||
|
"train_residual_power": 0.722356125700177,
|
||||||
|
"train_actual_ac_power": 1.0347596226164035,
|
||||||
|
"train_cross_power": 7.854827899222983e-17,
|
||||||
|
"heldout_signal_power": 0.31232098749561993,
|
||||||
|
"heldout_residual_power": 0.8215137283109276,
|
||||||
|
"heldout_actual_ac_power": 1.135170838604222,
|
||||||
|
"heldout_cross_power": 0.0013361227976749755,
|
||||||
|
"heldout_residual_band_27000_power": 0.03428707776367196,
|
||||||
|
"heldout_residual_band_675000_power": 0.18938129777540513,
|
||||||
|
"heldout_residual_band_1000000_power": 0.21334080727941662,
|
||||||
|
"heldout_residual_matched_power": 11.421082264513538,
|
||||||
|
"effective_signal_to_residual_db": -4.200136561843322
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9203680419921875,
|
||||||
|
"mean_q": 1.8407562255859375,
|
||||||
|
"power_code2": 5.328170166015625,
|
||||||
|
"ac_power_code2": 1.0927093512617043,
|
||||||
|
"rms_code": 2.30828294756419,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 9.266344273087686,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.0656340332031251e-05,
|
||||||
|
"band_27000_power": 0.029697349664473127,
|
||||||
|
"band_675000_power": 0.5128322432606733,
|
||||||
|
"power_standard_error": 0.031085529715571047
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9274020833333333,
|
||||||
|
"mean_q": 1.8395416666666666,
|
||||||
|
"power_code2": 4.84604375,
|
||||||
|
"ac_power_code2": 0.6020555824262157,
|
||||||
|
"rms_code": 2.2013731510127945,
|
||||||
|
"peak_magnitude_code": 5.0,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 4.0,
|
||||||
|
"papr_db": 7.125526780497051,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 9.692087499999999e-06,
|
||||||
|
"band_27000_power": 0.009082996916230657,
|
||||||
|
"band_675000_power": 0.10440265872939565,
|
||||||
|
"power_standard_error": 0.005986244399660487
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9213041666666667,
|
||||||
|
"mean_q": 1.8316708333333334,
|
||||||
|
"power_code2": 4.8351625,
|
||||||
|
"ac_power_code2": 0.6313430907986115,
|
||||||
|
"rms_code": 2.198900293328463,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.3056227355026895,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 9.670325e-06,
|
||||||
|
"band_27000_power": 0.011430099601181243,
|
||||||
|
"band_675000_power": 0.13125486016140642,
|
||||||
|
"power_standard_error": 0.0037407319242408464
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.6166993366124136,
|
||||||
|
"background_subtracted_raw": 0.4760100146492907,
|
||||||
|
"background_subtracted_uncertainty": 0.03454282477879906,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.4760100146492907,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": -0.008089659584664989,
|
||||||
|
"imag": 0.009397483555426113,
|
||||||
|
"phase_rad_at_active_start": 2.2815457153366783
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.00015375528937026635,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 7709991,
|
||||||
|
"alignment_adjustment": 7,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9203680419921875,
|
||||||
|
"mean_q": 1.8407562255859375,
|
||||||
|
"power_code2": 5.328170166015625,
|
||||||
|
"ac_power_code2": 1.0927093512617043,
|
||||||
|
"rms_code": 2.30828294756419,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 9.266344273087686,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.0656340332031251e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": -0.0731458112990823,
|
||||||
|
"gain_abs": 0.012399810053797854,
|
||||||
|
"dc_i": 0.920656229639791,
|
||||||
|
"dc_q": 1.8411261312835556,
|
||||||
|
"train_signal_power": 0.2978190572101343,
|
||||||
|
"train_residual_power": 0.7068527315140937,
|
||||||
|
"train_actual_ac_power": 1.0046717887242282,
|
||||||
|
"train_cross_power": -6.682154829462661e-17,
|
||||||
|
"heldout_signal_power": 0.29781552489257346,
|
||||||
|
"heldout_residual_power": 0.884789710339823,
|
||||||
|
"heldout_actual_ac_power": 1.1807473535638735,
|
||||||
|
"heldout_cross_power": -0.001857881668523386,
|
||||||
|
"heldout_residual_band_27000_power": 0.015187641333986188,
|
||||||
|
"heldout_residual_band_675000_power": 0.217181000785572,
|
||||||
|
"heldout_residual_band_1000000_power": 0.24523520739571264,
|
||||||
|
"heldout_residual_matched_power": 3.0703433988460103,
|
||||||
|
"effective_signal_to_residual_db": -4.72892729922049
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9284002685546875,
|
||||||
|
"mean_q": 1.84388427734375,
|
||||||
|
"power_code2": 5.311703491210937,
|
||||||
|
"ac_power_code2": 1.0498672043230375,
|
||||||
|
"rms_code": 2.304713320829933,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 8.062450931004047,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002655851745605469,
|
||||||
|
"band_27000_power": 0.33592297470769195,
|
||||||
|
"band_675000_power": 0.500120184644421,
|
||||||
|
"power_standard_error": 0.01600143643844262
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.92130625,
|
||||||
|
"mean_q": 1.8316708333333334,
|
||||||
|
"power_code2": 4.83516875,
|
||||||
|
"ac_power_code2": 0.6313455020269103,
|
||||||
|
"rms_code": 2.198901714492942,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.30561712175391,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002417584375,
|
||||||
|
"band_27000_power": 0.011430090514699676,
|
||||||
|
"band_675000_power": 0.13125475431725353,
|
||||||
|
"power_standard_error": 0.0037385404799615833
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9283375,
|
||||||
|
"mean_q": 1.8603020833333332,
|
||||||
|
"power_code2": 4.962772916666666,
|
||||||
|
"ac_power_code2": 0.6402385615060755,
|
||||||
|
"rms_code": 2.2277281963171958,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 8.357545135545596,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00024813864583333333,
|
||||||
|
"band_27000_power": 0.011660806052520012,
|
||||||
|
"band_675000_power": 0.13208548597324482,
|
||||||
|
"power_standard_error": 0.009340294961573032
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.6357920317664929,
|
||||||
|
"background_subtracted_raw": 0.4140751725565446,
|
||||||
|
"background_subtracted_uncertainty": 0.017352868452820306,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.4140751725565446,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": -0.007000883443887156,
|
||||||
|
"imag": 0.010205712402071436,
|
||||||
|
"phase_rad_at_active_start": 2.172048716307076
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.000153168934628688,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 10149062,
|
||||||
|
"alignment_adjustment": 6,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9284002685546875,
|
||||||
|
"mean_q": 1.84388427734375,
|
||||||
|
"power_code2": 5.311703491210937,
|
||||||
|
"ac_power_code2": 1.0498672043230375,
|
||||||
|
"rms_code": 2.304713320829933,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 8.062450931004047,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002655851745605469
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.005915202430533777,
|
||||||
|
"gain_abs": 0.012376143770524322,
|
||||||
|
"dc_i": 0.9271611521067152,
|
||||||
|
"dc_q": 1.8443065713552416,
|
||||||
|
"train_signal_power": 0.29964603349233376,
|
||||||
|
"train_residual_power": 0.7155398694209132,
|
||||||
|
"train_actual_ac_power": 1.0151859029132475,
|
||||||
|
"train_cross_power": 2.4270516152391507e-16,
|
||||||
|
"heldout_signal_power": 0.29956689346716014,
|
||||||
|
"heldout_residual_power": 0.7826844440402193,
|
||||||
|
"heldout_actual_ac_power": 1.084551933216437,
|
||||||
|
"heldout_cross_power": 0.002300595709057756,
|
||||||
|
"heldout_residual_band_27000_power": 0.03443073439045887,
|
||||||
|
"heldout_residual_band_675000_power": 0.19776523377923858,
|
||||||
|
"heldout_residual_band_1000000_power": 0.22176016276354857,
|
||||||
|
"heldout_residual_matched_power": 11.341896374078306,
|
||||||
|
"effective_signal_to_residual_db": -4.170928864706292
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9302490234375,
|
||||||
|
"mean_q": 1.854871826171875,
|
||||||
|
"power_code2": 5.396551513671875,
|
||||||
|
"ac_power_code2": 1.0906387765392669,
|
||||||
|
"rms_code": 2.3230478931076464,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.806675296737527,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.0793103027343752e-05,
|
||||||
|
"band_27000_power": 0.030437416621842406,
|
||||||
|
"band_675000_power": 0.5333148504916424,
|
||||||
|
"power_standard_error": 0.03318734421277559
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9283375,
|
||||||
|
"mean_q": 1.8603020833333332,
|
||||||
|
"power_code2": 4.962772916666666,
|
||||||
|
"ac_power_code2": 0.6402385615060755,
|
||||||
|
"rms_code": 2.2277281963171958,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 8.357545135545596,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 9.925545833333333e-06,
|
||||||
|
"band_27000_power": 0.011660770674569443,
|
||||||
|
"band_675000_power": 0.13208533141133466,
|
||||||
|
"power_standard_error": 0.009339092140283283
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9385041666666667,
|
||||||
|
"mean_q": 1.8505104166666666,
|
||||||
|
"power_code2": 5.000202083333333,
|
||||||
|
"ac_power_code2": 0.6950232102907972,
|
||||||
|
"rms_code": 2.236113164250265,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.634104411823291,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.0000404166666666e-05,
|
||||||
|
"band_27000_power": 0.011507358850526561,
|
||||||
|
"band_675000_power": 0.12385207349410385,
|
||||||
|
"power_standard_error": 0.037784295029485354
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.6676308858984363,
|
||||||
|
"background_subtracted_raw": 0.42300789064083055,
|
||||||
|
"background_subtracted_uncertainty": 0.04722771590637799,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.42300789064083055,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": -0.006710536973189343,
|
||||||
|
"imag": 0.010126447128417442,
|
||||||
|
"phase_rad_at_active_start": 2.1560299377113283
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.000147576237913175,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 12604263,
|
||||||
|
"alignment_adjustment": 7,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9302490234375,
|
||||||
|
"mean_q": 1.854871826171875,
|
||||||
|
"power_code2": 5.396551513671875,
|
||||||
|
"ac_power_code2": 1.0906387765392669,
|
||||||
|
"rms_code": 2.3230478931076464,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.806675296737527,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.0793103027343752e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.0026425717900911143,
|
||||||
|
"gain_abs": 0.012148096061242479,
|
||||||
|
"dc_i": 0.9315836612485033,
|
||||||
|
"dc_q": 1.8553663362050554,
|
||||||
|
"train_signal_power": 0.28585043299602836,
|
||||||
|
"train_residual_power": 0.7245263851886631,
|
||||||
|
"train_actual_ac_power": 1.010376818184691,
|
||||||
|
"train_cross_power": -8.954642582992278e-17,
|
||||||
|
"heldout_signal_power": 0.2858470426337267,
|
||||||
|
"heldout_residual_power": 0.8807229722868642,
|
||||||
|
"heldout_actual_ac_power": 1.1709047864903603,
|
||||||
|
"heldout_cross_power": 0.004334771569769229,
|
||||||
|
"heldout_residual_band_27000_power": 0.018795072832326204,
|
||||||
|
"heldout_residual_band_675000_power": 0.3125053797217844,
|
||||||
|
"heldout_residual_band_1000000_power": 0.3566678823693195,
|
||||||
|
"heldout_residual_matched_power": 4.361909383453252,
|
||||||
|
"effective_signal_to_residual_db": -4.887056210122219
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"pair": 1,
|
||||||
|
"received_power_difference_db": 0.1233446340794126,
|
||||||
|
"channel_gain_difference_db": -0.1644798001918496,
|
||||||
|
"matched_component_difference_db": -0.20653835698711864,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.185938443707496
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pair": 2,
|
||||||
|
"received_power_difference_db": 0.09269277113182464,
|
||||||
|
"channel_gain_difference_db": -0.16154256719796828,
|
||||||
|
"matched_component_difference_db": -0.2036011239932389,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.183001210713615
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comparison_summary": {
|
||||||
|
"repeatability": [
|
||||||
|
{
|
||||||
|
"comparison": "A2/A1",
|
||||||
|
"raw_rx_power_difference_db": 0.007078081316567549,
|
||||||
|
"background_difference_db": 0.0932741793444109,
|
||||||
|
"received_power_difference_db": -0.4820243490149181,
|
||||||
|
"channel_gain_difference_db": -0.18107353366707263,
|
||||||
|
"matched_component_difference_db": -0.1810735336670716
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comparison": "B2/B1",
|
||||||
|
"raw_rx_power_difference_db": 0.055382405794076286,
|
||||||
|
"background_difference_db": 0.34462938523123676,
|
||||||
|
"received_power_difference_db": -0.5126762119625052,
|
||||||
|
"channel_gain_difference_db": -0.17813630067319114,
|
||||||
|
"matched_component_difference_db": -0.17813630067319214
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": 0.10889530008758355,
|
||||||
|
"channel_gain_difference_db": -0.16304154709643187,
|
||||||
|
"energy_per_symbol_difference_db": -13.870504786632791,
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"
|
||||||
|
},
|
||||||
|
"outside_waveform_background": [
|
||||||
|
{
|
||||||
|
"start": 3654784,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 0.9425475,
|
||||||
|
"mean_q": 1.823755,
|
||||||
|
"power_code2": 4.8400925,
|
||||||
|
"ac_power_code2": 0.6256144102187502,
|
||||||
|
"rms_code": 2.2000210226268293,
|
||||||
|
"peak_magnitude_code": 5.0,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 4.0,
|
||||||
|
"papr_db": 7.1308634705743845,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000242004625,
|
||||||
|
"band_27000_power": 0.011134411836977165,
|
||||||
|
"band_675000_power": 0.12535191126457693,
|
||||||
|
"power_standard_error": 0.002472279873801531
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 15443328,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 0.938865,
|
||||||
|
"mean_q": 1.832065,
|
||||||
|
"power_code2": 4.891855,
|
||||||
|
"ac_power_code2": 0.6539253475500004,
|
||||||
|
"rms_code": 2.2117538289782614,
|
||||||
|
"peak_magnitude_code": 5.0,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 4.0,
|
||||||
|
"papr_db": 7.0846643308644,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00024459275,
|
||||||
|
"band_27000_power": 0.012745444722198074,
|
||||||
|
"band_675000_power": 0.13232609430446385,
|
||||||
|
"power_standard_error": 0.008612381752597367
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_note": "Внутренние gaps: нулевой цифровой IQ при работающем TX; это не RF OFF. Внешние окна также требуют сверки времён запуска/остановки по журналам.",
|
||||||
|
"power_units": "RX code squared, not calibrated watts/dBm",
|
||||||
|
"residual_note": "Шум и неописанные моделью искажения; RX не нормируется для выравнивания A/B"
|
||||||
|
}
|
||||||
BIN
data/processed/lab056/rf_overview.png
Normal file
BIN
data/processed/lab056/rf_overview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 303 KiB |
336
data/processed/lab056/rf_result.json
Normal file
336
data/processed/lab056/rf_result.json
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-09-10",
|
||||||
|
"stage": "OTA_ABAB_VALID_LARGE_DROP_NOT_REPRODUCED_STOP",
|
||||||
|
"rf_tx_current": 0,
|
||||||
|
"completed_tx_sessions": 1,
|
||||||
|
"automatic_tx_repeats": 0,
|
||||||
|
"authorization_consumed": true,
|
||||||
|
"hardware": {
|
||||||
|
"passed": true,
|
||||||
|
"rf_tx_count": 1,
|
||||||
|
"point": {
|
||||||
|
"frequency_hz": 1400000000,
|
||||||
|
"physical_path": "MASTER TX -> TX antenna -> free-space LOS -> RX antenna -> SLAVE RX; user-specified approximate distance 3-5 m; fixed equipment and cables",
|
||||||
|
"path_confirmed": true,
|
||||||
|
"tx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"rx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"distance_m": null,
|
||||||
|
"orientation": "Both stationary; same antenna orientation and polarization, per user",
|
||||||
|
"visibility": "LOS"
|
||||||
|
},
|
||||||
|
"process_before": {
|
||||||
|
"state": "CLEAR",
|
||||||
|
"attempts": [
|
||||||
|
{
|
||||||
|
"state": "CLEAR",
|
||||||
|
"mechanism": "psutil",
|
||||||
|
"matches": [],
|
||||||
|
"inspected": 302,
|
||||||
|
"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": 137.35900004394352,
|
||||||
|
"elapsed_ms": 137.35900004394352,
|
||||||
|
"polls": [
|
||||||
|
{
|
||||||
|
"query": 1,
|
||||||
|
"start_ms": 50.7242000894621,
|
||||||
|
"end_ms": 69.19760000891984,
|
||||||
|
"raw": "CLKIN status: no clock signal detected",
|
||||||
|
"state": "NOT_DETECTED",
|
||||||
|
"exit_code": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"query": 2,
|
||||||
|
"start_ms": 119.4211000110954,
|
||||||
|
"end_ms": 137.35900004394352,
|
||||||
|
"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.938690185546875,
|
||||||
|
"mean_q": 1.825347900390625,
|
||||||
|
"power_code2": 4.84039306640625,
|
||||||
|
"ac_power_code2": 0.6273588445037603,
|
||||||
|
"rms_code": 2.2000893314604864,
|
||||||
|
"peak_magnitude_code": 4.47213595499958,
|
||||||
|
"max_abs_i": 3.0,
|
||||||
|
"max_abs_q": 4.0,
|
||||||
|
"papr_db": 6.161493654989252,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002420196533203125,
|
||||||
|
"saturation_detected": false,
|
||||||
|
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||||
|
},
|
||||||
|
"rx_sha256": "e336dd653fa554058052fcf851e9f2462f66b8cf7c710815ca2b9a09ffc23ad4",
|
||||||
|
"rx_level": {
|
||||||
|
"samples": 24000000,
|
||||||
|
"mean_i": 0.9305354583333333,
|
||||||
|
"mean_q": 1.8321060833333334,
|
||||||
|
"power_code2": 4.996925875,
|
||||||
|
"ac_power_code2": 0.7744169351973671,
|
||||||
|
"rms_code": 2.235380476563218,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 9.545096066616024,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00024984629375,
|
||||||
|
"saturation_detected": false,
|
||||||
|
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||||
|
},
|
||||||
|
"process_after": {
|
||||||
|
"state": "CLEAR",
|
||||||
|
"attempts": [
|
||||||
|
{
|
||||||
|
"state": "CLEAR",
|
||||||
|
"mechanism": "psutil",
|
||||||
|
"matches": [],
|
||||||
|
"inspected": 302,
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cleanup_errors": []
|
||||||
|
},
|
||||||
|
"rx_bytes": 48000000,
|
||||||
|
"rx_samples": 24000000,
|
||||||
|
"rx_seconds": 3.0,
|
||||||
|
"tx_bytes": 21177088,
|
||||||
|
"tx_samples": 10588544,
|
||||||
|
"tx_waveform_seconds": 1.323568,
|
||||||
|
"rx_overruns": 0,
|
||||||
|
"tx_underruns": 0,
|
||||||
|
"rx_clipped_samples": 0,
|
||||||
|
"rx_clipped_components": 0,
|
||||||
|
"all_four_segments_present": true,
|
||||||
|
"segmentation_start_spread_samples": 1,
|
||||||
|
"hardware_parameters_constant_between_segments": true,
|
||||||
|
"physical_geometry_source": "USER_DESCRIPTION_NOT_SOFTWARE_MEASUREMENT",
|
||||||
|
"audited_hardware_commands": 14,
|
||||||
|
"audited_tx_commands": 1,
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"start_sample": 5254791,
|
||||||
|
"end_sample_exclusive": 6909991,
|
||||||
|
"start_seconds": 0.656848875,
|
||||||
|
"end_seconds": 0.863748875,
|
||||||
|
"inferred_sequence_start_sample": 4454791,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"raw_rx_power_code2": 5.303053588867187,
|
||||||
|
"rx_ac_power_code2": 1.084963565705345,
|
||||||
|
"background_power_code2": 0.6222826074066838,
|
||||||
|
"background_subtracted_signal_power_code2": 0.46268095829866107,
|
||||||
|
"background_subtracted_uncertainty_code2": 0.029702310624809113,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"complex_gain_h": {
|
||||||
|
"real": -0.008543898749645305,
|
||||||
|
"imag": 0.009310849226837265,
|
||||||
|
"phase_rad_at_active_start": 2.3132658926165255
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0001596901191690867,
|
||||||
|
"matched_signal_component_power_code2": 0.31232098749561993,
|
||||||
|
"residual_power_code2": 0.8215137283109276,
|
||||||
|
"spectral_band_27000_power_code2": 0.3472643231791639,
|
||||||
|
"spectral_band_675000_power_code2": 0.5082992616247881
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"start_sample": 7709991,
|
||||||
|
"end_sample_exclusive": 9349063,
|
||||||
|
"start_seconds": 0.963748875,
|
||||||
|
"end_seconds": 1.168632875,
|
||||||
|
"inferred_sequence_start_sample": 4454791,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"raw_rx_power_code2": 5.328170166015625,
|
||||||
|
"rx_ac_power_code2": 1.0927093512617043,
|
||||||
|
"background_power_code2": 0.6166993366124136,
|
||||||
|
"background_subtracted_signal_power_code2": 0.4760100146492907,
|
||||||
|
"background_subtracted_uncertainty_code2": 0.03454282477879906,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"complex_gain_h": {
|
||||||
|
"real": -0.008089659584664989,
|
||||||
|
"imag": 0.009397483555426113,
|
||||||
|
"phase_rad_at_active_start": 2.2815457153366783
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.00015375528937026635,
|
||||||
|
"matched_signal_component_power_code2": 0.29781552489257346,
|
||||||
|
"residual_power_code2": 0.884789710339823,
|
||||||
|
"spectral_band_27000_power_code2": 0.029697349664473127,
|
||||||
|
"spectral_band_675000_power_code2": 0.5128322432606733
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"start_sample": 10149062,
|
||||||
|
"end_sample_exclusive": 11804262,
|
||||||
|
"start_seconds": 1.26863275,
|
||||||
|
"end_seconds": 1.47553275,
|
||||||
|
"inferred_sequence_start_sample": 4454790,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"raw_rx_power_code2": 5.311703491210937,
|
||||||
|
"rx_ac_power_code2": 1.0498672043230375,
|
||||||
|
"background_power_code2": 0.6357920317664929,
|
||||||
|
"background_subtracted_signal_power_code2": 0.4140751725565446,
|
||||||
|
"background_subtracted_uncertainty_code2": 0.017352868452820306,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"complex_gain_h": {
|
||||||
|
"real": -0.007000883443887156,
|
||||||
|
"imag": 0.010205712402071436,
|
||||||
|
"phase_rad_at_active_start": 2.172048716307076
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.000153168934628688,
|
||||||
|
"matched_signal_component_power_code2": 0.29956689346716014,
|
||||||
|
"residual_power_code2": 0.7826844440402193,
|
||||||
|
"spectral_band_27000_power_code2": 0.33592297470769195,
|
||||||
|
"spectral_band_675000_power_code2": 0.500120184644421
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"start_sample": 12604263,
|
||||||
|
"end_sample_exclusive": 14243335,
|
||||||
|
"start_seconds": 1.575532875,
|
||||||
|
"end_seconds": 1.780416875,
|
||||||
|
"inferred_sequence_start_sample": 4454791,
|
||||||
|
"active_samples": 1638400,
|
||||||
|
"raw_rx_power_code2": 5.396551513671875,
|
||||||
|
"rx_ac_power_code2": 1.0906387765392669,
|
||||||
|
"background_power_code2": 0.6676308858984363,
|
||||||
|
"background_subtracted_signal_power_code2": 0.42300789064083055,
|
||||||
|
"background_subtracted_uncertainty_code2": 0.04722771590637799,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"complex_gain_h": {
|
||||||
|
"real": -0.006710536973189343,
|
||||||
|
"imag": 0.010126447128417442,
|
||||||
|
"phase_rad_at_active_start": 2.1560299377113283
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.000147576237913175,
|
||||||
|
"matched_signal_component_power_code2": 0.2858470426337267,
|
||||||
|
"residual_power_code2": 0.8807229722868642,
|
||||||
|
"spectral_band_27000_power_code2": 0.030437416621842406,
|
||||||
|
"spectral_band_675000_power_code2": 0.5333148504916424
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comparison_summary": {
|
||||||
|
"repeatability": [
|
||||||
|
{
|
||||||
|
"comparison": "A2/A1",
|
||||||
|
"raw_rx_power_difference_db": 0.007078081316567549,
|
||||||
|
"background_difference_db": 0.0932741793444109,
|
||||||
|
"received_power_difference_db": -0.4820243490149181,
|
||||||
|
"channel_gain_difference_db": -0.18107353366707263,
|
||||||
|
"matched_component_difference_db": -0.1810735336670716
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comparison": "B2/B1",
|
||||||
|
"raw_rx_power_difference_db": 0.055382405794076286,
|
||||||
|
"background_difference_db": 0.34462938523123676,
|
||||||
|
"received_power_difference_db": -0.5126762119625052,
|
||||||
|
"channel_gain_difference_db": -0.17813630067319114,
|
||||||
|
"matched_component_difference_db": -0.17813630067319214
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": 0.10889530008758355,
|
||||||
|
"channel_gain_difference_db": -0.16304154709643187,
|
||||||
|
"energy_per_symbol_difference_db": -13.870504786632791,
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"
|
||||||
|
},
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"pair": 1,
|
||||||
|
"received_power_difference_db": 0.1233446340794126,
|
||||||
|
"channel_gain_difference_db": -0.1644798001918496,
|
||||||
|
"matched_component_difference_db": -0.20653835698711864,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.185938443707496
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pair": 2,
|
||||||
|
"received_power_difference_db": 0.09269277113182464,
|
||||||
|
"channel_gain_difference_db": -0.16154256719796828,
|
||||||
|
"matched_component_difference_db": -0.2036011239932389,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.183001210713615
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE",
|
||||||
|
"units": "RX_CODE_SQUARED_AND_CODE_SQUARED_SECONDS_NOT_CALIBRATED_WATTS_OR_JOULES",
|
||||||
|
"interpretation": {
|
||||||
|
"historical_15_41_db_drop_reproduced": false,
|
||||||
|
"isolated_hackrf_loss_established": false,
|
||||||
|
"scope": "ENTIRE_OTA_PATH_IN_THIS_SESSION",
|
||||||
|
"conducted_control_required_to_separate_hardware_and_channel": true,
|
||||||
|
"external_amplifier_necessity_established": false,
|
||||||
|
"packet_acceptance_tested": false
|
||||||
|
},
|
||||||
|
"verifier": {
|
||||||
|
"passed": true,
|
||||||
|
"mode": "SAVED_FILES_READ_ONLY_NO_HARDWARE",
|
||||||
|
"independent_methods": [
|
||||||
|
"INTEGER_RAW_MOMENTS",
|
||||||
|
"LOCAL_VARIANCE",
|
||||||
|
"WEIGHTED_PHASE_CFO",
|
||||||
|
"COMPLEX_LSTSQ"
|
||||||
|
],
|
||||||
|
"remaining_reproduced_findings": 0
|
||||||
|
},
|
||||||
|
"lab054_official": "NOT_A, 0/16",
|
||||||
|
"lab055_inputs_unchanged": 34,
|
||||||
|
"last_software_tests_same_code": {
|
||||||
|
"targeted": 44,
|
||||||
|
"pytest": 867,
|
||||||
|
"quick_gate": [
|
||||||
|
867,
|
||||||
|
30,
|
||||||
|
13
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"code_or_tx_changed": false,
|
||||||
|
"head": "5add95ecb8865f59075bbbced08d97b183fb3b48",
|
||||||
|
"lab056_committed": false,
|
||||||
|
"push": false,
|
||||||
|
"stop": true,
|
||||||
|
"artifact_sha256": {
|
||||||
|
"data/raw/lab056/tx_abab.cs8": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"data/raw/lab056/tx_metadata.json": "535375d08a0699ac4be31992ddbb6c5d175b3156a0a3b508b45d7cb567c24071",
|
||||||
|
"data/raw/lab056/rx_stream.cs8": "e336dd653fa554058052fcf851e9f2462f66b8cf7c710815ca2b9a09ffc23ad4",
|
||||||
|
"data/raw/lab056/hardware_attempt.json": "24dfed564a260c6286233a21823cf13f5d19731adaa5bf6664debeddde634ffd",
|
||||||
|
"data/raw/lab056/capture_result.json": "107ff5b43cd98acbb6ea77631894a1ff2fcb95868702f2dafa73675b69a16bdd",
|
||||||
|
"data/raw/lab056/tx_process_result.json": "88e13edd733430b2a42dd176ff275f518be39e1733da6b904a03493b99354475",
|
||||||
|
"data/raw/lab056/rx_stdout.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||||
|
"data/raw/lab056/rx_stderr.txt": "3b0c3dc749f353bad62f36b198745d9dd2850372ad2a0450ae645afad300361a",
|
||||||
|
"data/processed/lab056/rf_analysis.json": "21576f57910458a3fbe0090894a6ba61540b8a1a08094fca16cd06c46058d141",
|
||||||
|
"data/processed/lab056/rf_session_authorization.json": "52bf979934c2c27bee055b07e09685248d411a48b79f49b6f000b5e2c631e0c1",
|
||||||
|
"data/processed/lab056/rf_session_commands.json": "c816829a57f8613c29fa228ce1a03b93df79997e619d8dfaa827186c7820171b",
|
||||||
|
"data/processed/lab056/rf_overview.png": "c08a8dab99677afb46b0cc43dbf2360da47da35a03dc00fdec48618c7c794f4f",
|
||||||
|
"experiments/lab056_capture_runner.py": "24c17060b8b3e3f6899f9e922ae818e7ae2efba456c5f5cada7c69b5e5429334",
|
||||||
|
"experiments/lab056_abab_energy.py": "323408b3bc1aa02c8b2f2855cd6295ac2efd99f13afec2ca0c872aa013a78f96"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
data/processed/lab056/rf_session_authorization.json
Normal file
30
data/processed/lab056/rf_session_authorization.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"authorized_tx_sessions": 1,
|
||||||
|
"automatic_retry": false,
|
||||||
|
"point": {
|
||||||
|
"frequency_hz": 1400000000,
|
||||||
|
"physical_path": "MASTER TX -> TX antenna -> free-space LOS -> RX antenna -> SLAVE RX; user-specified approximate distance 3-5 m; fixed equipment and cables",
|
||||||
|
"path_confirmed": true,
|
||||||
|
"tx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"rx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"distance_m": null,
|
||||||
|
"orientation": "Both stationary; same antenna orientation and polarization, per user",
|
||||||
|
"visibility": "LOS"
|
||||||
|
},
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"tx_samples": 10588544,
|
||||||
|
"tx_seconds": 1.323568,
|
||||||
|
"rx_samples": 24000000,
|
||||||
|
"rx_seconds": 3.0,
|
||||||
|
"tx_gain_db": 10,
|
||||||
|
"rx_gain_db": [
|
||||||
|
24,
|
||||||
|
24
|
||||||
|
],
|
||||||
|
"amp": false,
|
||||||
|
"bias_tee": false,
|
||||||
|
"sample_rate_hz": 8000000,
|
||||||
|
"digital_carrier_hz": 50000,
|
||||||
|
"source": "Explicit user authorization in current turn",
|
||||||
|
"recorded_at_utc": "2026-09-10T09:32:24.185185+00:00"
|
||||||
|
}
|
||||||
262
data/processed/lab056/rf_session_commands.json
Normal file
262
data/processed/lab056/rf_session_commands.json
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
{
|
||||||
|
"tx_process_invocations": 1,
|
||||||
|
"automatic_retry": false,
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_info.exe"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.697133+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "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\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.719605+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.719707+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.736985+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.737066+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.753370+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-r",
|
||||||
|
"NUL",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"65536",
|
||||||
|
"-l",
|
||||||
|
"24",
|
||||||
|
"-g",
|
||||||
|
"24",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.753426+00:00",
|
||||||
|
"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 65536/0Mio\nStop with Ctrl-C\n 0.3 MiB / 0.018 sec = 14.8 MiB/second, average power -29.9 dBfs, 9376 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.01795 s\nhackrf_stop_rx() done\nTransfer statistics:\n274144 bytes transferred by M0\n262144 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.790201+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_debug.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"--si5351c",
|
||||||
|
"-n",
|
||||||
|
"29",
|
||||||
|
"-r"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.790253+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "[ 29] -> 0x26\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.807254+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-o",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.807310+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.823113+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-i"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.858007+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "CLKIN status: no clock signal detected\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.876412+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-i"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.926726+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "CLKIN status: clock signal detected\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.944618+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-r",
|
||||||
|
"NUL",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"65536",
|
||||||
|
"-l",
|
||||||
|
"24",
|
||||||
|
"-g",
|
||||||
|
"24",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.944681+00:00",
|
||||||
|
"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 65536/0Mio\nStop with Ctrl-C\n 0.3 MiB / 0.017 sec = 15.4 MiB/second, average power -35.4 dBfs, 8832 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.01704 s\nhackrf_stop_rx() done\nTransfer statistics:\n274976 bytes transferred by M0\n262144 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.979112+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_debug.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"--si5351c",
|
||||||
|
"-n",
|
||||||
|
"29",
|
||||||
|
"-r"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.979161+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "[ 29] -> 0x26\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:24.995463+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-r",
|
||||||
|
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab056\\rx_stream.cs8",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"24000000",
|
||||||
|
"-l",
|
||||||
|
"24",
|
||||||
|
"-g",
|
||||||
|
"24",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:24.995551+00:00",
|
||||||
|
"background_process": true,
|
||||||
|
"stdout_path": "C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab056\\rx_stdout.txt",
|
||||||
|
"stderr_path": "C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab056\\rx_stderr.txt",
|
||||||
|
"returncode": 0,
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:28.029923+00:00",
|
||||||
|
"returncode_after_cleanup": 0,
|
||||||
|
"cleanup_at_utc": "2026-09-10T09:32:28.683438+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-t",
|
||||||
|
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab056\\tx_abab.cs8",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"10588544",
|
||||||
|
"-x",
|
||||||
|
"10",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:25.512893+00:00",
|
||||||
|
"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 10588544/10Mio\nStop with Ctrl-C\n16.0 MiB / 1.004 sec = 15.9 MiB/second, average power -11.3 dBfs, 13920 bytes filled in buffer, 0 underruns, longest 0 bytes\n 5.2 MiB / 0.322 sec = 16.1 MiB/second, average power -11.2 dBfs, 5120 bytes filled in buffer, 0 underruns, longest 0 bytes\n\nExiting...\nTotal time: 1.32538 s\nhackrf_stop_tx() done\nTransfer statistics:\n21200896 bytes transferred by M0\n21200896 bytes transferred by M4\n0 underruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:26.901844+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:28.683494+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:28.698845+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:32:28.698895+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:32:28.714042+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orchestration_finished_at_utc": "2026-09-10T09:32:29.203758+00:00"
|
||||||
|
}
|
||||||
218
data/processed/lab056/rx_preflight_commands.json
Normal file
218
data/processed/lab056/rx_preflight_commands.json
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
{
|
||||||
|
"rf_tx_count": 0,
|
||||||
|
"tx_commands": 0,
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_info.exe"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:28.777578+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "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\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:28.849704+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:28.849843+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:28.881216+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:28.881271+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:28.899025+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-r",
|
||||||
|
"NUL",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"65536",
|
||||||
|
"-l",
|
||||||
|
"24",
|
||||||
|
"-g",
|
||||||
|
"24",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:28.899092+00:00",
|
||||||
|
"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 65536/0Mio\nStop with Ctrl-C\n 0.3 MiB / 0.019 sec = 13.5 MiB/second, average power -27.9 dBfs, 9280 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.01979 s\nhackrf_stop_rx() done\nTransfer statistics:\n276256 bytes transferred by M0\n262144 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:28.950743+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_debug.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"--si5351c",
|
||||||
|
"-n",
|
||||||
|
"29",
|
||||||
|
"-r"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:28.950806+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "[ 29] -> 0x26\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:28.980731+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-o",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:28.980796+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:28.996692+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-i"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:29.030946+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "CLKIN status: clock signal detected\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:29.048522+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-r",
|
||||||
|
"NUL",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"65536",
|
||||||
|
"-l",
|
||||||
|
"24",
|
||||||
|
"-g",
|
||||||
|
"24",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:29.048596+00:00",
|
||||||
|
"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 65536/0Mio\nStop with Ctrl-C\n 0.3 MiB / 0.019 sec = 14.0 MiB/second, average power -32.0 dBfs, 9120 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.01894 s\nhackrf_stop_rx() done\nTransfer statistics:\n273920 bytes transferred by M0\n262144 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:29.084743+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_debug.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"--si5351c",
|
||||||
|
"-n",
|
||||||
|
"29",
|
||||||
|
"-r"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:29.084857+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "[ 29] -> 0x26\n",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:29.100716+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-r",
|
||||||
|
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab056\\preflight_rx.cs8",
|
||||||
|
"-f",
|
||||||
|
"1400000000",
|
||||||
|
"-s",
|
||||||
|
"8000000",
|
||||||
|
"-n",
|
||||||
|
"800000",
|
||||||
|
"-l",
|
||||||
|
"24",
|
||||||
|
"-g",
|
||||||
|
"24",
|
||||||
|
"-a",
|
||||||
|
"0",
|
||||||
|
"-p",
|
||||||
|
"0",
|
||||||
|
"-B"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:29.100863+00:00",
|
||||||
|
"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.119 sec = 15.4 MiB/second, average power -35.2 dBfs, 15552 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11922 s\nhackrf_stop_rx() done\nTransfer statistics:\n1913376 bytes transferred by M0\n1900544 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:29.239488+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc323b97d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:29.271470+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:29.288606+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_clock.exe",
|
||||||
|
"-d",
|
||||||
|
"000000000000000097c063dc2e5327d3",
|
||||||
|
"-o",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"started_at_utc": "2026-09-10T09:16:29.288689+00:00",
|
||||||
|
"returncode": 0,
|
||||||
|
"stdout": "",
|
||||||
|
"stderr": "",
|
||||||
|
"finished_at_utc": "2026-09-10T09:16:29.309700+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
26
data/processed/lab056/rx_preflight_plan.json
Normal file
26
data/processed/lab056/rx_preflight_plan.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"point": {
|
||||||
|
"frequency_hz": 1400000000,
|
||||||
|
"physical_path": "MASTER TX -> TX antenna -> free-space LOS -> RX antenna -> SLAVE RX; user-specified approximate distance 3-5 m; fixed equipment and cables",
|
||||||
|
"path_confirmed": true,
|
||||||
|
"tx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"rx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"distance_m": null,
|
||||||
|
"orientation": "Both stationary; same antenna orientation and polarization, per user",
|
||||||
|
"visibility": "LOS"
|
||||||
|
},
|
||||||
|
"distance_description": "Approximately 3-5 m per user; exact measured value not supplied",
|
||||||
|
"frequency_role": "CURRENT_LAB056_TEST_ONLY_NOT_ROVER_OPERATING_BAND",
|
||||||
|
"field_band_context": "Approximately 180 MHz considered separately; 50/144 MHz outside Lab056",
|
||||||
|
"scope": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT_THEN_STOP",
|
||||||
|
"tx_authorized": false,
|
||||||
|
"rf_tx_count": 0,
|
||||||
|
"rx_preflight_samples": 800000,
|
||||||
|
"sample_rate_hz": 8000000,
|
||||||
|
"rx_lna_db": 24,
|
||||||
|
"rx_vga_db": 24,
|
||||||
|
"rf_amplifier": false,
|
||||||
|
"bias_tee": false,
|
||||||
|
"external_pa_lna": false,
|
||||||
|
"recorded_at_utc": "2026-09-10T09:16:28.289788+00:00"
|
||||||
|
}
|
||||||
114
data/processed/lab056/rx_preflight_result.json
Normal file
114
data/processed/lab056/rx_preflight_result.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"passed": true,
|
||||||
|
"rf_tx_count": 0,
|
||||||
|
"point": {
|
||||||
|
"frequency_hz": 1400000000,
|
||||||
|
"physical_path": "MASTER TX -> TX antenna -> free-space LOS -> RX antenna -> SLAVE RX; user-specified approximate distance 3-5 m; fixed equipment and cables",
|
||||||
|
"path_confirmed": true,
|
||||||
|
"tx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"rx_antenna": "Existing 1400 MHz antenna, as described by user; model/marking not supplied",
|
||||||
|
"distance_m": null,
|
||||||
|
"orientation": "Both stationary; same antenna orientation and polarization, per user",
|
||||||
|
"visibility": "LOS"
|
||||||
|
},
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"stop_before_tx": true,
|
||||||
|
"process_before": {
|
||||||
|
"state": "CLEAR",
|
||||||
|
"attempts": [
|
||||||
|
{
|
||||||
|
"state": "CLEAR",
|
||||||
|
"mechanism": "psutil",
|
||||||
|
"matches": [],
|
||||||
|
"inspected": 297,
|
||||||
|
"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": 67.78709997888654,
|
||||||
|
"elapsed_ms": 67.78709997888654,
|
||||||
|
"polls": [
|
||||||
|
{
|
||||||
|
"query": 1,
|
||||||
|
"start_ms": 50.16230000182986,
|
||||||
|
"end_ms": 67.78709997888654,
|
||||||
|
"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.119 sec = 15.4 MiB/second, average power -35.2 dBfs, 15552 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11922 s\nhackrf_stop_rx() done\nTransfer statistics:\n1913376 bytes transferred by M0\n1900544 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.92535625,
|
||||||
|
"mean_q": 1.82532,
|
||||||
|
"power_code2": 4.85388125,
|
||||||
|
"ac_power_code2": 0.6658039581859372,
|
||||||
|
"rms_code": 2.20315257074947,
|
||||||
|
"peak_magnitude_code": 6.082762530298219,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.821125769372884,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002426940625,
|
||||||
|
"saturation_detected": false,
|
||||||
|
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
|
||||||
|
},
|
||||||
|
"rx_sha256": "c4257f26e4cc8562eb32228f9a50fdeb055879c928af0744cdf4a81cf7cd085b",
|
||||||
|
"process_after": {
|
||||||
|
"state": "CLEAR",
|
||||||
|
"attempts": [
|
||||||
|
{
|
||||||
|
"state": "CLEAR",
|
||||||
|
"mechanism": "psutil",
|
||||||
|
"matches": [],
|
||||||
|
"inspected": 297,
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cleanup_errors": [],
|
||||||
|
"source_path": "data/raw/lab056/preflight_result.json",
|
||||||
|
"source_sha256": "b691e51653f9e814627fd100cf99de0f22fef837b177030227ecddc5af573cf3",
|
||||||
|
"ready_for_single_abab_tx": true,
|
||||||
|
"tx_authorization_received": false,
|
||||||
|
"verifier": {
|
||||||
|
"mode": "SAVED_FILES_READ_ONLY",
|
||||||
|
"passed": true,
|
||||||
|
"reproduced_discrepancies": 0,
|
||||||
|
"independent_recalculation": "INTEGER_MOMENTS_AND_RAIL_COUNTS",
|
||||||
|
"hardware_commands": 0
|
||||||
|
},
|
||||||
|
"audited_hardware_commands": 12,
|
||||||
|
"audited_rx_transfers": 3,
|
||||||
|
"audited_tx_commands": 0,
|
||||||
|
"frequency_role": "LAB056_TEST_ONLY_NOT_ROVER_OPERATING_BAND",
|
||||||
|
"stop": true
|
||||||
|
}
|
||||||
489
data/processed/lab056/software_equal.json
Normal file
489
data/processed/lab056/software_equal.json
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
{
|
||||||
|
"scope": "TX_REFERENCE_DIAGNOSTICS_NOT_INDEPENDENT_PACKET_RX",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"rx_sha256": "a88afcc1bbeb50092f9f5baca6eb607d25daf508e0fafcdcada318818d9ffd62",
|
||||||
|
"alignment": {
|
||||||
|
"start": 4109216,
|
||||||
|
"correlation": 0.712109578315035,
|
||||||
|
"coarse_cfo_hz": 0.25,
|
||||||
|
"stride": 32
|
||||||
|
},
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219,
|
||||||
|
"band_27000_power": 1.729349736724617,
|
||||||
|
"band_675000_power": 1.8014764601725908,
|
||||||
|
"power_standard_error": 0.039755811858960266
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0012270833333334,
|
||||||
|
"mean_q": 2.0008,
|
||||||
|
"power_code2": 5.88834375,
|
||||||
|
"ac_power_code2": 0.8826874375998264,
|
||||||
|
"rms_code": 2.4265909729494997,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.924048425663756,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002944171875,
|
||||||
|
"band_27000_power": 0.002998337896541192,
|
||||||
|
"band_675000_power": 0.0744039583655369,
|
||||||
|
"power_standard_error": 0.0012691882704167598
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029416375,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8838202337955727,
|
||||||
|
"background_subtracted_raw": 1.7124521150103154,
|
||||||
|
"background_subtracted_uncertainty": 0.039782742266144235,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7124521150103154,
|
||||||
|
"channel_gain_power": 0.0007924265932905088,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 4909213,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.1968177799965115,
|
||||||
|
"gain_abs": 0.028150072704888503,
|
||||||
|
"dc_i": 0.9997948620728929,
|
||||||
|
"dc_q": 2.0010324962850192,
|
||||||
|
"train_signal_power": 1.55023266362049,
|
||||||
|
"train_residual_power": 0.9894122701085495,
|
||||||
|
"train_actual_ac_power": 2.539644933729041,
|
||||||
|
"train_cross_power": 2.1058155219577658e-15,
|
||||||
|
"heldout_signal_power": 1.549823229026632,
|
||||||
|
"heldout_residual_power": 0.8945490859865625,
|
||||||
|
"heldout_actual_ac_power": 2.6529010618609536,
|
||||||
|
"heldout_cross_power": 0.20852874684775813,
|
||||||
|
"heldout_residual_band_27000_power": 0.010080618023636462,
|
||||||
|
"heldout_residual_band_675000_power": 0.08179753911385561,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11760791625473926,
|
||||||
|
"heldout_residual_matched_power": 3.4888049404460095,
|
||||||
|
"effective_signal_to_residual_db": 2.3867798958438566
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998636474609375,
|
||||||
|
"mean_q": 1.9995599365234376,
|
||||||
|
"power_code2": 7.572622680664063,
|
||||||
|
"ac_power_code2": 2.5771079324942088,
|
||||||
|
"rms_code": 2.751839871915527,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 7.0,
|
||||||
|
"papr_db": 8.367570257627738,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5145245361328126e-05,
|
||||||
|
"band_27000_power": 0.08434245734375428,
|
||||||
|
"band_675000_power": 1.7818539229368797,
|
||||||
|
"power_standard_error": 0.03929664272862704
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.176655e-05,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766129166666667e-05,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8848416145507811,
|
||||||
|
"background_subtracted_raw": 1.6922663179434276,
|
||||||
|
"background_subtracted_uncertainty": 0.03930519138544367,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.6922663179434276,
|
||||||
|
"channel_gain_power": 0.0007927525626002337,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 7364413,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998636474609375,
|
||||||
|
"mean_q": 1.9995599365234376,
|
||||||
|
"power_code2": 7.572622680664063,
|
||||||
|
"ac_power_code2": 2.5771079324942088,
|
||||||
|
"rms_code": 2.751839871915527,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 7.0,
|
||||||
|
"papr_db": 8.367570257627738,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5145245361328126e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.20030217648162243,
|
||||||
|
"gain_abs": 0.028155861958040526,
|
||||||
|
"dc_i": 0.9986008794024919,
|
||||||
|
"dc_q": 2.00024705167195,
|
||||||
|
"train_signal_power": 1.5355362521933293,
|
||||||
|
"train_residual_power": 0.9879108077929747,
|
||||||
|
"train_actual_ac_power": 2.523447059986303,
|
||||||
|
"train_cross_power": -1.306454944227653e-15,
|
||||||
|
"heldout_signal_power": 1.5355180397870447,
|
||||||
|
"heldout_residual_power": 0.8925132940453717,
|
||||||
|
"heldout_actual_ac_power": 2.630769751790605,
|
||||||
|
"heldout_cross_power": 0.2027384179581883,
|
||||||
|
"heldout_residual_band_27000_power": 0.0033142687288771526,
|
||||||
|
"heldout_residual_band_675000_power": 0.0813443897071584,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11762789507725498,
|
||||||
|
"heldout_residual_matched_power": 0.9819762872749349,
|
||||||
|
"effective_signal_to_residual_db": 2.3564022937017817
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875,
|
||||||
|
"band_27000_power": 1.7263462989934586,
|
||||||
|
"band_675000_power": 1.7983862355807485,
|
||||||
|
"power_standard_error": 0.03961683202831068
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029415322916666667,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002941610416666667,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8834691483528649,
|
||||||
|
"background_subtracted_raw": 1.7102246351553774,
|
||||||
|
"background_subtracted_uncertainty": 0.03964405579856132,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7102246351553774,
|
||||||
|
"channel_gain_power": 0.0007920055569282462,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 9803485,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.19617739757807567,
|
||||||
|
"gain_abs": 0.02814259328719097,
|
||||||
|
"dc_i": 1.0004532662364058,
|
||||||
|
"dc_q": 2.0003859700871685,
|
||||||
|
"train_signal_power": 1.5494089856585476,
|
||||||
|
"train_residual_power": 0.9895407718712785,
|
||||||
|
"train_actual_ac_power": 2.5389497575298265,
|
||||||
|
"train_cross_power": 4.0495384823202584e-16,
|
||||||
|
"heldout_signal_power": 1.548999768607681,
|
||||||
|
"heldout_residual_power": 0.8941535479569137,
|
||||||
|
"heldout_actual_ac_power": 2.6484381326123456,
|
||||||
|
"heldout_cross_power": 0.20528481604775145,
|
||||||
|
"heldout_residual_band_27000_power": 0.009765782070021769,
|
||||||
|
"heldout_residual_band_675000_power": 0.08171891866368976,
|
||||||
|
"heldout_residual_band_1000000_power": 0.1177276686242853,
|
||||||
|
"heldout_residual_matched_power": 3.3724974710374163,
|
||||||
|
"effective_signal_to_residual_db": 2.3863924873600086
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.99942626953125,
|
||||||
|
"mean_q": 1.999466552734375,
|
||||||
|
"power_code2": 7.5732568359375,
|
||||||
|
"ac_power_code2": 2.5765374722048655,
|
||||||
|
"rms_code": 2.751955093372256,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.739298282383329,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5146513671875001e-05,
|
||||||
|
"band_27000_power": 0.08437478193987907,
|
||||||
|
"band_675000_power": 1.7815424066137258,
|
||||||
|
"power_standard_error": 0.03932113008198285
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766441666666666e-05,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9988083333333333,
|
||||||
|
"mean_q": 1.9994125,
|
||||||
|
"power_code2": 5.878066666666666,
|
||||||
|
"ac_power_code2": 0.8827982347743042,
|
||||||
|
"rms_code": 2.4244724512080285,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.457388406130481,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1756133333333333e-05,
|
||||||
|
"band_27000_power": 0.0029587129864172557,
|
||||||
|
"band_675000_power": 0.07444268200354834,
|
||||||
|
"power_standard_error": 0.0016031313210695718
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8825031661848954,
|
||||||
|
"background_subtracted_raw": 1.69403430601997,
|
||||||
|
"background_subtracted_uncertainty": 0.039335034220138454,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.69403430601997,
|
||||||
|
"channel_gain_power": 0.0007914780017038821,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 12258685,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.99942626953125,
|
||||||
|
"mean_q": 1.999466552734375,
|
||||||
|
"power_code2": 7.5732568359375,
|
||||||
|
"ac_power_code2": 2.5765374722048655,
|
||||||
|
"rms_code": 2.751955093372256,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.739298282383329,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5146513671875001e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.197924755610056,
|
||||||
|
"gain_abs": 0.028133218829417335,
|
||||||
|
"dc_i": 1.0007017709706703,
|
||||||
|
"dc_q": 1.999435627739676,
|
||||||
|
"train_signal_power": 1.5330674686733408,
|
||||||
|
"train_residual_power": 0.9881175175558363,
|
||||||
|
"train_actual_ac_power": 2.5211849862291755,
|
||||||
|
"train_cross_power": -1.4430123762565473e-15,
|
||||||
|
"heldout_signal_power": 1.5330492855483506,
|
||||||
|
"heldout_residual_power": 0.8931876810530448,
|
||||||
|
"heldout_actual_ac_power": 2.6318932139011078,
|
||||||
|
"heldout_cross_power": 0.20565624729971124,
|
||||||
|
"heldout_residual_band_27000_power": 0.0033209337668698995,
|
||||||
|
"heldout_residual_band_675000_power": 0.08160471700839024,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11754379856225462,
|
||||||
|
"heldout_residual_matched_power": 0.9863150049618183,
|
||||||
|
"effective_signal_to_residual_db": 2.346133924823488
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"pair": 1,
|
||||||
|
"received_power_difference_db": -0.051497256938167285,
|
||||||
|
"channel_gain_difference_db": 0.0017861283874782213,
|
||||||
|
"matched_component_difference_db": -0.04027242840779062,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.019672515128168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pair": 2,
|
||||||
|
"received_power_difference_db": -0.04130956980203142,
|
||||||
|
"channel_gain_difference_db": -0.0028938012026523408,
|
||||||
|
"matched_component_difference_db": -0.044952357997923256,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.0243524447183
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outside_waveform_background": [
|
||||||
|
{
|
||||||
|
"start": 3309216,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 0.9997525,
|
||||||
|
"mean_q": 2.0,
|
||||||
|
"power_code2": 5.8853875,
|
||||||
|
"ac_power_code2": 0.8858824387437494,
|
||||||
|
"rms_code": 2.4259817600303593,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.926229353802586,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294269375,
|
||||||
|
"band_27000_power": 0.00300239645688663,
|
||||||
|
"band_675000_power": 0.07464470494185707,
|
||||||
|
"power_standard_error": 0.001189001018090443
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 15097760,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 1.00201,
|
||||||
|
"mean_q": 1.9998775,
|
||||||
|
"power_code2": 5.8860125,
|
||||||
|
"ac_power_code2": 0.8824784448937502,
|
||||||
|
"rms_code": 2.4261105704398553,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.451521679042596,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294300625,
|
||||||
|
"band_27000_power": 0.002877436011683443,
|
||||||
|
"band_675000_power": 0.07431026562360402,
|
||||||
|
"power_standard_error": 0.0010078630955450837
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_note": "Внутренние gaps: нулевой цифровой IQ при работающем TX; это не RF OFF. Внешние окна также требуют сверки времён запуска/остановки по журналам.",
|
||||||
|
"power_units": "RX code squared, not calibrated watts/dBm",
|
||||||
|
"residual_note": "Шум и неописанные моделью искажения; RX не нормируется для выравнивания A/B",
|
||||||
|
"synthetic": true,
|
||||||
|
"expected_gain_difference_db": 0.0,
|
||||||
|
"known_sequence_start": 4160013,
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
532
data/processed/lab056/software_equal_ota_summary.json
Normal file
532
data/processed/lab056/software_equal_ota_summary.json
Normal file
@@ -0,0 +1,532 @@
|
|||||||
|
{
|
||||||
|
"scope": "TX_REFERENCE_DIAGNOSTICS_NOT_INDEPENDENT_PACKET_RX",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"rx_sha256": "a88afcc1bbeb50092f9f5baca6eb607d25daf508e0fafcdcada318818d9ffd62",
|
||||||
|
"alignment": {
|
||||||
|
"start": 4109216,
|
||||||
|
"correlation": 0.712109578315035,
|
||||||
|
"coarse_cfo_hz": 0.25,
|
||||||
|
"stride": 32
|
||||||
|
},
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219,
|
||||||
|
"band_27000_power": 1.729349736724617,
|
||||||
|
"band_675000_power": 1.8014764601725908,
|
||||||
|
"power_standard_error": 0.039755811858960266
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0012270833333334,
|
||||||
|
"mean_q": 2.0008,
|
||||||
|
"power_code2": 5.88834375,
|
||||||
|
"ac_power_code2": 0.8826874375998264,
|
||||||
|
"rms_code": 2.4265909729494997,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.924048425663756,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002944171875,
|
||||||
|
"band_27000_power": 0.002998337896541192,
|
||||||
|
"band_675000_power": 0.0744039583655369,
|
||||||
|
"power_standard_error": 0.0012691882704167598
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029416375,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8838202337955727,
|
||||||
|
"background_subtracted_raw": 1.7124521150103154,
|
||||||
|
"background_subtracted_uncertainty": 0.039782742266144235,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7124521150103154,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": -0.0027287468398317707,
|
||||||
|
"imag": -0.028017504063970737,
|
||||||
|
"phase_rad_at_active_start": -1.6678844763817968
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0007924265932905088,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 4909213,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.1968177799965115,
|
||||||
|
"gain_abs": 0.028150072704888503,
|
||||||
|
"dc_i": 0.9997948620728929,
|
||||||
|
"dc_q": 2.0010324962850192,
|
||||||
|
"train_signal_power": 1.55023266362049,
|
||||||
|
"train_residual_power": 0.9894122701085495,
|
||||||
|
"train_actual_ac_power": 2.539644933729041,
|
||||||
|
"train_cross_power": 2.1058155219577658e-15,
|
||||||
|
"heldout_signal_power": 1.549823229026632,
|
||||||
|
"heldout_residual_power": 0.8945490859865625,
|
||||||
|
"heldout_actual_ac_power": 2.6529010618609536,
|
||||||
|
"heldout_cross_power": 0.20852874684775813,
|
||||||
|
"heldout_residual_band_27000_power": 0.010080618023636462,
|
||||||
|
"heldout_residual_band_675000_power": 0.08179753911385561,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11760791625473926,
|
||||||
|
"heldout_residual_matched_power": 3.4888049404460095,
|
||||||
|
"effective_signal_to_residual_db": 2.3867798958438566
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998636474609375,
|
||||||
|
"mean_q": 1.9995599365234376,
|
||||||
|
"power_code2": 7.572622680664063,
|
||||||
|
"ac_power_code2": 2.5771079324942088,
|
||||||
|
"rms_code": 2.751839871915527,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 7.0,
|
||||||
|
"papr_db": 8.367570257627738,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5145245361328126e-05,
|
||||||
|
"band_27000_power": 0.08434245734375428,
|
||||||
|
"band_675000_power": 1.7818539229368797,
|
||||||
|
"power_standard_error": 0.03929664272862704
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.176655e-05,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766129166666667e-05,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8848416145507811,
|
||||||
|
"background_subtracted_raw": 1.6922663179434276,
|
||||||
|
"background_subtracted_uncertainty": 0.03930519138544367,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.6922663179434276,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": 0.007947194796737946,
|
||||||
|
"imag": -0.027011009930451232,
|
||||||
|
"phase_rad_at_active_start": -1.2846501572766547
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0007927525626002337,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 7364413,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998636474609375,
|
||||||
|
"mean_q": 1.9995599365234376,
|
||||||
|
"power_code2": 7.572622680664063,
|
||||||
|
"ac_power_code2": 2.5771079324942088,
|
||||||
|
"rms_code": 2.751839871915527,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 7.0,
|
||||||
|
"papr_db": 8.367570257627738,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5145245361328126e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.20030217648162243,
|
||||||
|
"gain_abs": 0.028155861958040526,
|
||||||
|
"dc_i": 0.9986008794024919,
|
||||||
|
"dc_q": 2.00024705167195,
|
||||||
|
"train_signal_power": 1.5355362521933293,
|
||||||
|
"train_residual_power": 0.9879108077929747,
|
||||||
|
"train_actual_ac_power": 2.523447059986303,
|
||||||
|
"train_cross_power": -1.306454944227653e-15,
|
||||||
|
"heldout_signal_power": 1.5355180397870447,
|
||||||
|
"heldout_residual_power": 0.8925132940453717,
|
||||||
|
"heldout_actual_ac_power": 2.630769751790605,
|
||||||
|
"heldout_cross_power": 0.2027384179581883,
|
||||||
|
"heldout_residual_band_27000_power": 0.0033142687288771526,
|
||||||
|
"heldout_residual_band_675000_power": 0.0813443897071584,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11762789507725498,
|
||||||
|
"heldout_residual_matched_power": 0.9819762872749349,
|
||||||
|
"effective_signal_to_residual_db": 2.3564022937017817
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875,
|
||||||
|
"band_27000_power": 1.7263462989934586,
|
||||||
|
"band_675000_power": 1.7983862355807485,
|
||||||
|
"power_standard_error": 0.03961683202831068
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029415322916666667,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002941610416666667,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8834691483528649,
|
||||||
|
"background_subtracted_raw": 1.7102246351553774,
|
||||||
|
"background_subtracted_uncertainty": 0.03964405579856132,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7102246351553774,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": 0.01755314566236957,
|
||||||
|
"imag": -0.02199755973474973,
|
||||||
|
"phase_rad_at_active_start": -0.8973012782222721
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0007920055569282462,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 9803485,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.19617739757807567,
|
||||||
|
"gain_abs": 0.02814259328719097,
|
||||||
|
"dc_i": 1.0004532662364058,
|
||||||
|
"dc_q": 2.0003859700871685,
|
||||||
|
"train_signal_power": 1.5494089856585476,
|
||||||
|
"train_residual_power": 0.9895407718712785,
|
||||||
|
"train_actual_ac_power": 2.5389497575298265,
|
||||||
|
"train_cross_power": 4.0495384823202584e-16,
|
||||||
|
"heldout_signal_power": 1.548999768607681,
|
||||||
|
"heldout_residual_power": 0.8941535479569137,
|
||||||
|
"heldout_actual_ac_power": 2.6484381326123456,
|
||||||
|
"heldout_cross_power": 0.20528481604775145,
|
||||||
|
"heldout_residual_band_27000_power": 0.009765782070021769,
|
||||||
|
"heldout_residual_band_675000_power": 0.08171891866368976,
|
||||||
|
"heldout_residual_band_1000000_power": 0.1177276686242853,
|
||||||
|
"heldout_residual_matched_power": 3.3724974710374163,
|
||||||
|
"effective_signal_to_residual_db": 2.3863924873600086
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.99942626953125,
|
||||||
|
"mean_q": 1.999466552734375,
|
||||||
|
"power_code2": 7.5732568359375,
|
||||||
|
"ac_power_code2": 2.5765374722048655,
|
||||||
|
"rms_code": 2.751955093372256,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.739298282383329,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5146513671875001e-05,
|
||||||
|
"band_27000_power": 0.08437478193987907,
|
||||||
|
"band_675000_power": 1.7815424066137258,
|
||||||
|
"power_standard_error": 0.03932113008198285
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766441666666666e-05,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9988083333333333,
|
||||||
|
"mean_q": 1.9994125,
|
||||||
|
"power_code2": 5.878066666666666,
|
||||||
|
"ac_power_code2": 0.8827982347743042,
|
||||||
|
"rms_code": 2.4244724512080285,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.457388406130481,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1756133333333333e-05,
|
||||||
|
"band_27000_power": 0.0029587129864172557,
|
||||||
|
"band_675000_power": 0.07444268200354834,
|
||||||
|
"power_standard_error": 0.0016031313210695718
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8825031661848954,
|
||||||
|
"background_subtracted_raw": 1.69403430601997,
|
||||||
|
"background_subtracted_uncertainty": 0.039335034220138454,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.69403430601997,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": 0.02447249803166995,
|
||||||
|
"imag": -0.013877133774443209,
|
||||||
|
"phase_rad_at_active_start": -0.5158392359795115
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0007914780017038821,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 12258685,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.99942626953125,
|
||||||
|
"mean_q": 1.999466552734375,
|
||||||
|
"power_code2": 7.5732568359375,
|
||||||
|
"ac_power_code2": 2.5765374722048655,
|
||||||
|
"rms_code": 2.751955093372256,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.739298282383329,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.5146513671875001e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.197924755610056,
|
||||||
|
"gain_abs": 0.028133218829417335,
|
||||||
|
"dc_i": 1.0007017709706703,
|
||||||
|
"dc_q": 1.999435627739676,
|
||||||
|
"train_signal_power": 1.5330674686733408,
|
||||||
|
"train_residual_power": 0.9881175175558363,
|
||||||
|
"train_actual_ac_power": 2.5211849862291755,
|
||||||
|
"train_cross_power": -1.4430123762565473e-15,
|
||||||
|
"heldout_signal_power": 1.5330492855483506,
|
||||||
|
"heldout_residual_power": 0.8931876810530448,
|
||||||
|
"heldout_actual_ac_power": 2.6318932139011078,
|
||||||
|
"heldout_cross_power": 0.20565624729971124,
|
||||||
|
"heldout_residual_band_27000_power": 0.0033209337668698995,
|
||||||
|
"heldout_residual_band_675000_power": 0.08160471700839024,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11754379856225462,
|
||||||
|
"heldout_residual_matched_power": 0.9863150049618183,
|
||||||
|
"effective_signal_to_residual_db": 2.346133924823488
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"pair": 1,
|
||||||
|
"received_power_difference_db": -0.051497256938167285,
|
||||||
|
"channel_gain_difference_db": 0.0017861283874782213,
|
||||||
|
"matched_component_difference_db": -0.04027242840779062,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.019672515128168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pair": 2,
|
||||||
|
"received_power_difference_db": -0.04130956980203142,
|
||||||
|
"channel_gain_difference_db": -0.0028938012026523408,
|
||||||
|
"matched_component_difference_db": -0.044952357997923256,
|
||||||
|
"matched_component_symbol_energy_difference_db": -14.0243524447183
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comparison_summary": {
|
||||||
|
"repeatability": [
|
||||||
|
{
|
||||||
|
"comparison": "A2/A1",
|
||||||
|
"raw_rx_power_difference_db": -0.0012085532551280598,
|
||||||
|
"background_difference_db": -0.0017255178918689754,
|
||||||
|
"received_power_difference_db": -0.005652782408264756,
|
||||||
|
"channel_gain_difference_db": -0.0023081300526506692,
|
||||||
|
"matched_component_difference_db": -0.00230813005264874
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comparison": "B2/B1",
|
||||||
|
"raw_rx_power_difference_db": 0.00036367664985406537,
|
||||||
|
"background_difference_db": -0.011492673362299905,
|
||||||
|
"received_power_difference_db": 0.004534904727870617,
|
||||||
|
"channel_gain_difference_db": -0.006988059642781434,
|
||||||
|
"matched_component_difference_db": -0.006988059642780951
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": -0.04640374115997541,
|
||||||
|
"channel_gain_difference_db": -0.000552584218408658,
|
||||||
|
"energy_per_symbol_difference_db": -14.025803827880353,
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"
|
||||||
|
},
|
||||||
|
"outside_waveform_background": [
|
||||||
|
{
|
||||||
|
"start": 3309216,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 0.9997525,
|
||||||
|
"mean_q": 2.0,
|
||||||
|
"power_code2": 5.8853875,
|
||||||
|
"ac_power_code2": 0.8858824387437494,
|
||||||
|
"rms_code": 2.4259817600303593,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.926229353802586,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294269375,
|
||||||
|
"band_27000_power": 0.00300239645688663,
|
||||||
|
"band_675000_power": 0.07464470494185707,
|
||||||
|
"power_standard_error": 0.001189001018090443
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 15097760,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 1.00201,
|
||||||
|
"mean_q": 1.9998775,
|
||||||
|
"power_code2": 5.8860125,
|
||||||
|
"ac_power_code2": 0.8824784448937502,
|
||||||
|
"rms_code": 2.4261105704398553,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.451521679042596,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294300625,
|
||||||
|
"band_27000_power": 0.002877436011683443,
|
||||||
|
"band_675000_power": 0.07431026562360402,
|
||||||
|
"power_standard_error": 0.0010078630955450837
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_note": "Внутренние gaps: нулевой цифровой IQ при работающем TX; это не RF OFF. Внешние окна также требуют сверки времён запуска/остановки по журналам.",
|
||||||
|
"power_units": "RX code squared, not calibrated watts/dBm",
|
||||||
|
"residual_note": "Шум и неописанные моделью искажения; RX не нормируется для выравнивания A/B",
|
||||||
|
"synthetic": true,
|
||||||
|
"source_result_preserved": "software_equal.json"
|
||||||
|
}
|
||||||
489
data/processed/lab056/software_minus6db.json
Normal file
489
data/processed/lab056/software_minus6db.json
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
{
|
||||||
|
"scope": "TX_REFERENCE_DIAGNOSTICS_NOT_INDEPENDENT_PACKET_RX",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"rx_sha256": "a189c1ffaf029983d41f7d87e262cbbc28ba740fc8915a75e41ee0adec9c1fe6",
|
||||||
|
"alignment": {
|
||||||
|
"start": 4109216,
|
||||||
|
"correlation": 0.593709534474723,
|
||||||
|
"coarse_cfo_hz": 0.25,
|
||||||
|
"stride": 32
|
||||||
|
},
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219,
|
||||||
|
"band_27000_power": 1.729349736724617,
|
||||||
|
"band_675000_power": 1.8014764601725908,
|
||||||
|
"power_standard_error": 0.039755811858960266
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0012270833333334,
|
||||||
|
"mean_q": 2.0008,
|
||||||
|
"power_code2": 5.88834375,
|
||||||
|
"ac_power_code2": 0.8826874375998264,
|
||||||
|
"rms_code": 2.4265909729494997,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.924048425663756,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002944171875,
|
||||||
|
"band_27000_power": 0.002998337896541192,
|
||||||
|
"band_675000_power": 0.0744039583655369,
|
||||||
|
"power_standard_error": 0.0012691882704167598
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029416375,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8838202337955727,
|
||||||
|
"background_subtracted_raw": 1.7124521150103154,
|
||||||
|
"background_subtracted_uncertainty": 0.039782742266144235,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7124521150103154,
|
||||||
|
"channel_gain_power": 0.0007924265932905088,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 4909213,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.1968177799965115,
|
||||||
|
"gain_abs": 0.028150072704888503,
|
||||||
|
"dc_i": 0.9997948620728929,
|
||||||
|
"dc_q": 2.0010324962850192,
|
||||||
|
"train_signal_power": 1.55023266362049,
|
||||||
|
"train_residual_power": 0.9894122701085495,
|
||||||
|
"train_actual_ac_power": 2.539644933729041,
|
||||||
|
"train_cross_power": 2.1058155219577658e-15,
|
||||||
|
"heldout_signal_power": 1.549823229026632,
|
||||||
|
"heldout_residual_power": 0.8945490859865625,
|
||||||
|
"heldout_actual_ac_power": 2.6529010618609536,
|
||||||
|
"heldout_cross_power": 0.20852874684775813,
|
||||||
|
"heldout_residual_band_27000_power": 0.010080618023636462,
|
||||||
|
"heldout_residual_band_675000_power": 0.08179753911385561,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11760791625473926,
|
||||||
|
"heldout_residual_matched_power": 3.4888049404460095,
|
||||||
|
"effective_signal_to_residual_db": 2.3867798958438566
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998985595703125,
|
||||||
|
"mean_q": 1.99885986328125,
|
||||||
|
"power_code2": 6.303458251953125,
|
||||||
|
"ac_power_code2": 1.3100452784940613,
|
||||||
|
"rms_code": 2.5106688853676276,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.132049758894885,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.260691650390625e-05,
|
||||||
|
"band_27000_power": 0.023313641821752495,
|
||||||
|
"band_675000_power": 0.5022288931564798,
|
||||||
|
"power_standard_error": 0.009890376052273048
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.176655e-05,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766129166666667e-05,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8848416145507811,
|
||||||
|
"background_subtracted_raw": 0.42520366394328013,
|
||||||
|
"background_subtracted_uncertainty": 0.009924287307427941,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.42520366394328013,
|
||||||
|
"channel_gain_power": 0.0001984013788928856,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 7364413,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998985595703125,
|
||||||
|
"mean_q": 1.99885986328125,
|
||||||
|
"power_code2": 6.303458251953125,
|
||||||
|
"ac_power_code2": 1.3100452784940613,
|
||||||
|
"rms_code": 2.5106688853676276,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.132049758894885,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.260691650390625e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.2030817936052521,
|
||||||
|
"gain_abs": 0.014085502436650445,
|
||||||
|
"dc_i": 0.9994693255003283,
|
||||||
|
"dc_q": 1.9991360434375782,
|
||||||
|
"train_signal_power": 0.38429709867591966,
|
||||||
|
"train_residual_power": 0.9122921896535339,
|
||||||
|
"train_actual_ac_power": 1.2965892883294532,
|
||||||
|
"train_cross_power": 1.1296519275560969e-16,
|
||||||
|
"heldout_signal_power": 0.38429254067549146,
|
||||||
|
"heldout_residual_power": 0.8884947950547913,
|
||||||
|
"heldout_actual_ac_power": 1.3235018891986572,
|
||||||
|
"heldout_cross_power": 0.050714553468375057,
|
||||||
|
"heldout_residual_band_27000_power": 0.00304194937150677,
|
||||||
|
"heldout_residual_band_675000_power": 0.07681375917338087,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11305538222420118,
|
||||||
|
"heldout_residual_matched_power": 0.9157792365512673,
|
||||||
|
"effective_signal_to_residual_db": -3.639929333248138
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875,
|
||||||
|
"band_27000_power": 1.7263462989934586,
|
||||||
|
"band_675000_power": 1.7983862355807485,
|
||||||
|
"power_standard_error": 0.03961683202831068
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029415322916666667,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002941610416666667,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8834691483528649,
|
||||||
|
"background_subtracted_raw": 1.7102246351553774,
|
||||||
|
"background_subtracted_uncertainty": 0.03964405579856132,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7102246351553774,
|
||||||
|
"channel_gain_power": 0.0007920055569282462,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 9803485,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.19617739757807567,
|
||||||
|
"gain_abs": 0.02814259328719097,
|
||||||
|
"dc_i": 1.0004532662364058,
|
||||||
|
"dc_q": 2.0003859700871685,
|
||||||
|
"train_signal_power": 1.5494089856585476,
|
||||||
|
"train_residual_power": 0.9895407718712785,
|
||||||
|
"train_actual_ac_power": 2.5389497575298265,
|
||||||
|
"train_cross_power": 4.0495384823202584e-16,
|
||||||
|
"heldout_signal_power": 1.548999768607681,
|
||||||
|
"heldout_residual_power": 0.8941535479569137,
|
||||||
|
"heldout_actual_ac_power": 2.6484381326123456,
|
||||||
|
"heldout_cross_power": 0.20528481604775145,
|
||||||
|
"heldout_residual_band_27000_power": 0.009765782070021769,
|
||||||
|
"heldout_residual_band_675000_power": 0.08171891866368976,
|
||||||
|
"heldout_residual_band_1000000_power": 0.1177276686242853,
|
||||||
|
"heldout_residual_matched_power": 3.3724974710374163,
|
||||||
|
"effective_signal_to_residual_db": 2.3863924873600086
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9994140625,
|
||||||
|
"mean_q": 1.999407958984375,
|
||||||
|
"power_code2": 6.305787353515625,
|
||||||
|
"ac_power_code2": 1.3093266987428072,
|
||||||
|
"rms_code": 2.511132683375298,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.317395958495091,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.261157470703125e-05,
|
||||||
|
"band_27000_power": 0.023440988618055048,
|
||||||
|
"band_675000_power": 0.5015327722551061,
|
||||||
|
"power_standard_error": 0.009843366386499481
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766441666666666e-05,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9988083333333333,
|
||||||
|
"mean_q": 1.9994125,
|
||||||
|
"power_code2": 5.878066666666666,
|
||||||
|
"ac_power_code2": 0.8827982347743042,
|
||||||
|
"rms_code": 2.4244724512080285,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.457388406130481,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1756133333333333e-05,
|
||||||
|
"band_27000_power": 0.0029587129864172557,
|
||||||
|
"band_675000_power": 0.07444268200354834,
|
||||||
|
"power_standard_error": 0.0016031313210695718
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8825031661848954,
|
||||||
|
"background_subtracted_raw": 0.4268235325579117,
|
||||||
|
"background_subtracted_uncertainty": 0.009898762952718625,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.4268235325579117,
|
||||||
|
"channel_gain_power": 0.00019792959281029668,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 12258685,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9994140625,
|
||||||
|
"mean_q": 1.999407958984375,
|
||||||
|
"power_code2": 6.305787353515625,
|
||||||
|
"ac_power_code2": 1.3093266987428072,
|
||||||
|
"rms_code": 2.511132683375298,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.317395958495091,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.261157470703125e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.20036016971332649,
|
||||||
|
"gain_abs": 0.014068745246477977,
|
||||||
|
"dc_i": 1.0007268354608458,
|
||||||
|
"dc_q": 1.9995225464921158,
|
||||||
|
"train_signal_power": 0.3833832641867325,
|
||||||
|
"train_residual_power": 0.9125567848168452,
|
||||||
|
"train_actual_ac_power": 1.2959400490035784,
|
||||||
|
"train_cross_power": 3.459732500488144e-16,
|
||||||
|
"heldout_signal_power": 0.3833787170249446,
|
||||||
|
"heldout_residual_power": 0.8881519217870418,
|
||||||
|
"heldout_actual_ac_power": 1.3227168214883227,
|
||||||
|
"heldout_cross_power": 0.051186182676336164,
|
||||||
|
"heldout_residual_band_27000_power": 0.003087916076869909,
|
||||||
|
"heldout_residual_band_675000_power": 0.0766452188649128,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11237262287689712,
|
||||||
|
"heldout_residual_matched_power": 0.9135235192972193,
|
||||||
|
"effective_signal_to_residual_db": -3.648592601804231
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"pair": 1,
|
||||||
|
"received_power_difference_db": -6.05021438090514,
|
||||||
|
"channel_gain_difference_db": -6.014143555521402,
|
||||||
|
"matched_component_difference_db": -6.056202112316672,
|
||||||
|
"matched_component_symbol_energy_difference_db": -20.035602199037047
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pair": 2,
|
||||||
|
"received_power_difference_db": -6.028048021201634,
|
||||||
|
"channel_gain_difference_db": -6.022174975174189,
|
||||||
|
"matched_component_difference_db": -6.06423353196946,
|
||||||
|
"matched_component_symbol_energy_difference_db": -20.04363361868984
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outside_waveform_background": [
|
||||||
|
{
|
||||||
|
"start": 3309216,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 0.9997525,
|
||||||
|
"mean_q": 2.0,
|
||||||
|
"power_code2": 5.8853875,
|
||||||
|
"ac_power_code2": 0.8858824387437494,
|
||||||
|
"rms_code": 2.4259817600303593,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.926229353802586,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294269375,
|
||||||
|
"band_27000_power": 0.00300239645688663,
|
||||||
|
"band_675000_power": 0.07464470494185707,
|
||||||
|
"power_standard_error": 0.001189001018090443
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 15097760,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 1.00201,
|
||||||
|
"mean_q": 1.9998775,
|
||||||
|
"power_code2": 5.8860125,
|
||||||
|
"ac_power_code2": 0.8824784448937502,
|
||||||
|
"rms_code": 2.4261105704398553,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.451521679042596,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294300625,
|
||||||
|
"band_27000_power": 0.002877436011683443,
|
||||||
|
"band_675000_power": 0.07431026562360402,
|
||||||
|
"power_standard_error": 0.0010078630955450837
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_note": "Внутренние gaps: нулевой цифровой IQ при работающем TX; это не RF OFF. Внешние окна также требуют сверки времён запуска/остановки по журналам.",
|
||||||
|
"power_units": "RX code squared, not calibrated watts/dBm",
|
||||||
|
"residual_note": "Шум и неописанные моделью искажения; RX не нормируется для выравнивания A/B",
|
||||||
|
"synthetic": true,
|
||||||
|
"expected_gain_difference_db": -6.020599913279624,
|
||||||
|
"known_sequence_start": 4160013,
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
532
data/processed/lab056/software_minus6db_ota_summary.json
Normal file
532
data/processed/lab056/software_minus6db_ota_summary.json
Normal file
@@ -0,0 +1,532 @@
|
|||||||
|
{
|
||||||
|
"scope": "TX_REFERENCE_DIAGNOSTICS_NOT_INDEPENDENT_PACKET_RX",
|
||||||
|
"rf_tx": 0,
|
||||||
|
"tx_sha256": "5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600",
|
||||||
|
"rx_sha256": "a189c1ffaf029983d41f7d87e262cbbc28ba740fc8915a75e41ee0adec9c1fe6",
|
||||||
|
"alignment": {
|
||||||
|
"start": 4109216,
|
||||||
|
"correlation": 0.593709534474723,
|
||||||
|
"coarse_cfo_hz": 0.25,
|
||||||
|
"stride": 32
|
||||||
|
},
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"label": "A1",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219,
|
||||||
|
"band_27000_power": 1.729349736724617,
|
||||||
|
"band_675000_power": 1.8014764601725908,
|
||||||
|
"power_standard_error": 0.039755811858960266
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0012270833333334,
|
||||||
|
"mean_q": 2.0008,
|
||||||
|
"power_code2": 5.88834375,
|
||||||
|
"ac_power_code2": 0.8826874375998264,
|
||||||
|
"rms_code": 2.4265909729494997,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.924048425663756,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002944171875,
|
||||||
|
"band_27000_power": 0.002998337896541192,
|
||||||
|
"band_675000_power": 0.0744039583655369,
|
||||||
|
"power_standard_error": 0.0012691882704167598
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029416375,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8838202337955727,
|
||||||
|
"background_subtracted_raw": 1.7124521150103154,
|
||||||
|
"background_subtracted_uncertainty": 0.039782742266144235,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7124521150103154,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": -0.0027287468398317707,
|
||||||
|
"imag": -0.028017504063970737,
|
||||||
|
"phase_rad_at_active_start": -1.6678844763817968
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0007924265932905088,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 4909213,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9995880126953125,
|
||||||
|
"mean_q": 2.00025390625,
|
||||||
|
"power_code2": 7.596464233398438,
|
||||||
|
"ac_power_code2": 2.596272348805888,
|
||||||
|
"rms_code": 2.7561683971409363,
|
||||||
|
"peak_magnitude_code": 6.708203932499369,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 7.726010163935343,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0003798232116699219
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.1968177799965115,
|
||||||
|
"gain_abs": 0.028150072704888503,
|
||||||
|
"dc_i": 0.9997948620728929,
|
||||||
|
"dc_q": 2.0010324962850192,
|
||||||
|
"train_signal_power": 1.55023266362049,
|
||||||
|
"train_residual_power": 0.9894122701085495,
|
||||||
|
"train_actual_ac_power": 2.539644933729041,
|
||||||
|
"train_cross_power": 2.1058155219577658e-15,
|
||||||
|
"heldout_signal_power": 1.549823229026632,
|
||||||
|
"heldout_residual_power": 0.8945490859865625,
|
||||||
|
"heldout_actual_ac_power": 2.6529010618609536,
|
||||||
|
"heldout_cross_power": 0.20852874684775813,
|
||||||
|
"heldout_residual_band_27000_power": 0.010080618023636462,
|
||||||
|
"heldout_residual_band_675000_power": 0.08179753911385561,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11760791625473926,
|
||||||
|
"heldout_residual_matched_power": 3.4888049404460095,
|
||||||
|
"effective_signal_to_residual_db": 2.3867798958438566
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B1",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998985595703125,
|
||||||
|
"mean_q": 1.99885986328125,
|
||||||
|
"power_code2": 6.303458251953125,
|
||||||
|
"ac_power_code2": 1.3100452784940613,
|
||||||
|
"rms_code": 2.5106688853676276,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.132049758894885,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.260691650390625e-05,
|
||||||
|
"band_27000_power": 0.023313641821752495,
|
||||||
|
"band_675000_power": 0.5022288931564798,
|
||||||
|
"power_standard_error": 0.009890376052273048
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989395833333333,
|
||||||
|
"mean_q": 2.000110416666667,
|
||||||
|
"power_code2": 5.883275,
|
||||||
|
"ac_power_code2": 0.8849530299913191,
|
||||||
|
"rms_code": 2.42554633021099,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.9277884895206405,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.176655e-05,
|
||||||
|
"band_27000_power": 0.003012451963227414,
|
||||||
|
"band_675000_power": 0.07417080321149919,
|
||||||
|
"power_standard_error": 0.0013506571909630695
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766129166666667e-05,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8848416145507811,
|
||||||
|
"background_subtracted_raw": 0.42520366394328013,
|
||||||
|
"background_subtracted_uncertainty": 0.009924287307427941,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.42520366394328013,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": 0.003963777071987038,
|
||||||
|
"imag": -0.013516280931398084,
|
||||||
|
"phase_rad_at_active_start": -1.2855349404464855
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0001984013788928856,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 7364413,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.998985595703125,
|
||||||
|
"mean_q": 1.99885986328125,
|
||||||
|
"power_code2": 6.303458251953125,
|
||||||
|
"ac_power_code2": 1.3100452784940613,
|
||||||
|
"rms_code": 2.5106688853676276,
|
||||||
|
"peak_magnitude_code": 6.4031242374328485,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.132049758894885,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.260691650390625e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.2030817936052521,
|
||||||
|
"gain_abs": 0.014085502436650445,
|
||||||
|
"dc_i": 0.9994693255003283,
|
||||||
|
"dc_q": 1.9991360434375782,
|
||||||
|
"train_signal_power": 0.38429709867591966,
|
||||||
|
"train_residual_power": 0.9122921896535339,
|
||||||
|
"train_actual_ac_power": 1.2965892883294532,
|
||||||
|
"train_cross_power": 1.1296519275560969e-16,
|
||||||
|
"heldout_signal_power": 0.38429254067549146,
|
||||||
|
"heldout_residual_power": 0.8884947950547913,
|
||||||
|
"heldout_actual_ac_power": 1.3235018891986572,
|
||||||
|
"heldout_cross_power": 0.050714553468375057,
|
||||||
|
"heldout_residual_band_27000_power": 0.00304194937150677,
|
||||||
|
"heldout_residual_band_675000_power": 0.07681375917338087,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11305538222420118,
|
||||||
|
"heldout_residual_matched_power": 0.9157792365512673,
|
||||||
|
"effective_signal_to_residual_db": -3.639929333248138
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "A2",
|
||||||
|
"symbol_rate": 20000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875,
|
||||||
|
"band_27000_power": 1.7263462989934586,
|
||||||
|
"band_675000_power": 1.7983862355807485,
|
||||||
|
"power_standard_error": 0.03961683202831068
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9989791666666666,
|
||||||
|
"mean_q": 2.00009375,
|
||||||
|
"power_code2": 5.883064583333334,
|
||||||
|
"ac_power_code2": 0.8847301991102432,
|
||||||
|
"rms_code": 2.4255029547154408,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.45369731942485,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.00029415322916666667,
|
||||||
|
"band_27000_power": 0.0030190450807723868,
|
||||||
|
"band_675000_power": 0.07463668207172841,
|
||||||
|
"power_standard_error": 0.0009021263608471298
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.0002941610416666667,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8834691483528649,
|
||||||
|
"background_subtracted_raw": 1.7102246351553774,
|
||||||
|
"background_subtracted_uncertainty": 0.03964405579856132,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 1.7102246351553774,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": 0.01755314566236957,
|
||||||
|
"imag": -0.02199755973474973,
|
||||||
|
"phase_rad_at_active_start": -0.8973012782222721
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.0007920055569282462,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 9803485,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 1.000250244140625,
|
||||||
|
"mean_q": 2.0000390625,
|
||||||
|
"power_code2": 7.5943505859375,
|
||||||
|
"ac_power_code2": 2.5936937835082423,
|
||||||
|
"rms_code": 2.755784931001964,
|
||||||
|
"peak_magnitude_code": 7.211102550927978,
|
||||||
|
"max_abs_i": 5.0,
|
||||||
|
"max_abs_q": 6.0,
|
||||||
|
"papr_db": 8.355127015785026,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000379717529296875
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.19617739757807567,
|
||||||
|
"gain_abs": 0.02814259328719097,
|
||||||
|
"dc_i": 1.0004532662364058,
|
||||||
|
"dc_q": 2.0003859700871685,
|
||||||
|
"train_signal_power": 1.5494089856585476,
|
||||||
|
"train_residual_power": 0.9895407718712785,
|
||||||
|
"train_actual_ac_power": 2.5389497575298265,
|
||||||
|
"train_cross_power": 4.0495384823202584e-16,
|
||||||
|
"heldout_signal_power": 1.548999768607681,
|
||||||
|
"heldout_residual_power": 0.8941535479569137,
|
||||||
|
"heldout_actual_ac_power": 2.6484381326123456,
|
||||||
|
"heldout_cross_power": 0.20528481604775145,
|
||||||
|
"heldout_residual_band_27000_power": 0.009765782070021769,
|
||||||
|
"heldout_residual_band_675000_power": 0.08171891866368976,
|
||||||
|
"heldout_residual_band_1000000_power": 0.1177276686242853,
|
||||||
|
"heldout_residual_matched_power": 3.3724974710374163,
|
||||||
|
"effective_signal_to_residual_db": 2.3863924873600086
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "B2",
|
||||||
|
"symbol_rate": 500000,
|
||||||
|
"raw_rx": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9994140625,
|
||||||
|
"mean_q": 1.999407958984375,
|
||||||
|
"power_code2": 6.305787353515625,
|
||||||
|
"ac_power_code2": 1.3093266987428072,
|
||||||
|
"rms_code": 2.511132683375298,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.317395958495091,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.261157470703125e-05,
|
||||||
|
"band_27000_power": 0.023440988618055048,
|
||||||
|
"band_675000_power": 0.5015327722551061,
|
||||||
|
"power_standard_error": 0.009843366386499481
|
||||||
|
},
|
||||||
|
"local_zero_iq_background_windows": [
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 1.0004854166666666,
|
||||||
|
"mean_q": 2.0000104166666666,
|
||||||
|
"power_code2": 5.883220833333334,
|
||||||
|
"ac_power_code2": 0.8822080975954867,
|
||||||
|
"rms_code": 2.425535164315977,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.6186376661535915,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1766441666666666e-05,
|
||||||
|
"band_27000_power": 0.003053650977045963,
|
||||||
|
"band_675000_power": 0.073961606327725,
|
||||||
|
"power_standard_error": 0.0012067695631605175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"samples": 480000,
|
||||||
|
"mean_i": 0.9988083333333333,
|
||||||
|
"mean_q": 1.9994125,
|
||||||
|
"power_code2": 5.878066666666666,
|
||||||
|
"ac_power_code2": 0.8827982347743042,
|
||||||
|
"rms_code": 2.4244724512080285,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.457388406130481,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.1756133333333333e-05,
|
||||||
|
"band_27000_power": 0.0029587129864172557,
|
||||||
|
"band_675000_power": 0.07444268200354834,
|
||||||
|
"power_standard_error": 0.0016031313210695718
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_power": 0.8825031661848954,
|
||||||
|
"background_subtracted_raw": 0.4268235325579117,
|
||||||
|
"background_subtracted_uncertainty": 0.009898762952718625,
|
||||||
|
"background_subtracted_stable": true,
|
||||||
|
"background_subtracted_signal_power": 0.4268235325579117,
|
||||||
|
"channel_gain_complex": {
|
||||||
|
"real": 0.012227408500259825,
|
||||||
|
"imag": -0.006958453432916718,
|
||||||
|
"phase_rad_at_active_start": -0.517378791591959
|
||||||
|
},
|
||||||
|
"channel_gain_power": 0.00019792959281029668,
|
||||||
|
"measurement": {
|
||||||
|
"frame": 1,
|
||||||
|
"start": 12258685,
|
||||||
|
"alignment_adjustment": -3,
|
||||||
|
"rx_active": {
|
||||||
|
"samples": 1638400,
|
||||||
|
"mean_i": 0.9994140625,
|
||||||
|
"mean_q": 1.999407958984375,
|
||||||
|
"power_code2": 6.305787353515625,
|
||||||
|
"ac_power_code2": 1.3093266987428072,
|
||||||
|
"rms_code": 2.511132683375298,
|
||||||
|
"peak_magnitude_code": 5.830951894845301,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 7.317395958495091,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 1.261157470703125e-05
|
||||||
|
},
|
||||||
|
"cfo_hz": 0.20036016971332649,
|
||||||
|
"gain_abs": 0.014068745246477977,
|
||||||
|
"dc_i": 1.0007268354608458,
|
||||||
|
"dc_q": 1.9995225464921158,
|
||||||
|
"train_signal_power": 0.3833832641867325,
|
||||||
|
"train_residual_power": 0.9125567848168452,
|
||||||
|
"train_actual_ac_power": 1.2959400490035784,
|
||||||
|
"train_cross_power": 3.459732500488144e-16,
|
||||||
|
"heldout_signal_power": 0.3833787170249446,
|
||||||
|
"heldout_residual_power": 0.8881519217870418,
|
||||||
|
"heldout_actual_ac_power": 1.3227168214883227,
|
||||||
|
"heldout_cross_power": 0.051186182676336164,
|
||||||
|
"heldout_residual_band_27000_power": 0.003087916076869909,
|
||||||
|
"heldout_residual_band_675000_power": 0.0766452188649128,
|
||||||
|
"heldout_residual_band_1000000_power": 0.11237262287689712,
|
||||||
|
"heldout_residual_matched_power": 0.9135235192972193,
|
||||||
|
"effective_signal_to_residual_db": -3.648592601804231
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pairs": [
|
||||||
|
{
|
||||||
|
"pair": 1,
|
||||||
|
"received_power_difference_db": -6.05021438090514,
|
||||||
|
"channel_gain_difference_db": -6.014143555521402,
|
||||||
|
"matched_component_difference_db": -6.056202112316672,
|
||||||
|
"matched_component_symbol_energy_difference_db": -20.035602199037047
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pair": 2,
|
||||||
|
"received_power_difference_db": -6.028048021201634,
|
||||||
|
"channel_gain_difference_db": -6.022174975174189,
|
||||||
|
"matched_component_difference_db": -6.06423353196946,
|
||||||
|
"matched_component_symbol_energy_difference_db": -20.04363361868984
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comparison_summary": {
|
||||||
|
"repeatability": [
|
||||||
|
{
|
||||||
|
"comparison": "A2/A1",
|
||||||
|
"raw_rx_power_difference_db": -0.0012085532551280598,
|
||||||
|
"background_difference_db": -0.0017255178918689754,
|
||||||
|
"received_power_difference_db": -0.005652782408264756,
|
||||||
|
"channel_gain_difference_db": -0.0023081300526506692,
|
||||||
|
"matched_component_difference_db": -0.00230813005264874
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comparison": "B2/B1",
|
||||||
|
"raw_rx_power_difference_db": 0.0016044036249902335,
|
||||||
|
"background_difference_db": -0.011492673362299905,
|
||||||
|
"received_power_difference_db": 0.016513577295241087,
|
||||||
|
"channel_gain_difference_db": -0.0103395497054386,
|
||||||
|
"matched_component_difference_db": -0.010339549705438118
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": -6.039124271880495,
|
||||||
|
"channel_gain_difference_db": -6.018156341674954,
|
||||||
|
"energy_per_symbol_difference_db": -20.01852435860087,
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"
|
||||||
|
},
|
||||||
|
"outside_waveform_background": [
|
||||||
|
{
|
||||||
|
"start": 3309216,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 0.9997525,
|
||||||
|
"mean_q": 2.0,
|
||||||
|
"power_code2": 5.8853875,
|
||||||
|
"ac_power_code2": 0.8858824387437494,
|
||||||
|
"rms_code": 2.4259817600303593,
|
||||||
|
"peak_magnitude_code": 5.385164807134504,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.926229353802586,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294269375,
|
||||||
|
"band_27000_power": 0.00300239645688663,
|
||||||
|
"band_675000_power": 0.07464470494185707,
|
||||||
|
"power_standard_error": 0.001189001018090443
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 15097760,
|
||||||
|
"samples": 400000,
|
||||||
|
"mean_i": 1.00201,
|
||||||
|
"mean_q": 1.9998775,
|
||||||
|
"power_code2": 5.8860125,
|
||||||
|
"ac_power_code2": 0.8824784448937502,
|
||||||
|
"rms_code": 2.4261105704398553,
|
||||||
|
"peak_magnitude_code": 5.0990195135927845,
|
||||||
|
"max_abs_i": 4.0,
|
||||||
|
"max_abs_q": 5.0,
|
||||||
|
"papr_db": 6.451521679042596,
|
||||||
|
"clipped_components_fraction": 0.0,
|
||||||
|
"clipped_samples_fraction": 0.0,
|
||||||
|
"symbol_energy_code2_seconds": 0.000294300625,
|
||||||
|
"band_27000_power": 0.002877436011683443,
|
||||||
|
"band_675000_power": 0.07431026562360402,
|
||||||
|
"power_standard_error": 0.0010078630955450837
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_note": "Внутренние gaps: нулевой цифровой IQ при работающем TX; это не RF OFF. Внешние окна также требуют сверки времён запуска/остановки по журналам.",
|
||||||
|
"power_units": "RX code squared, not calibrated watts/dBm",
|
||||||
|
"residual_note": "Шум и неописанные моделью искажения; RX не нормируется для выравнивания A/B",
|
||||||
|
"synthetic": true,
|
||||||
|
"source_result_preserved": "software_minus6db.json"
|
||||||
|
}
|
||||||
@@ -406,6 +406,51 @@
|
|||||||
- **Далее:** STOP. Отдельное согласование измерительной схемы и допустимой
|
- **Далее:** STOP. Отдельное согласование измерительной схемы и допустимой
|
||||||
частоты; усилитель не назначен, Git add/commit/push/pull/fetch не выполнялись.
|
частоты; усилитель не назначен, Git add/commit/push/pull/fetch не выполнялись.
|
||||||
|
|
||||||
|
## Lab056 — подготовка одного эфирного ABAB, 20 и 500 ксимв/с
|
||||||
|
|
||||||
|
- **База:** Lab055 зафиксирована коммитом
|
||||||
|
`5add95ecb8865f59075bbbced08d97b183fb3b48`, только её семь файлов; без push.
|
||||||
|
- **Подготовлено:** один PRBS7/BPSK/RRC TX-файл ABAB, 1,323568 с;
|
||||||
|
Fs 8 Мвыб/с, цифровая несущая +50 кГц. RMS A/B 44,004413/44,001925 код,
|
||||||
|
разница мощности −0,000491 дБ; клиппинг 0. RX планируется на 3 с.
|
||||||
|
- **Проверено программно:** известные разницы усиления 0 и −6,020600 дБ
|
||||||
|
восстановлены с ошибкой менее 0,3 дБ в обеих парах. Анализируются отдельно
|
||||||
|
принятая мощность, фон, коэффициент передачи, остаток, спектр и энергия символа.
|
||||||
|
- **Проверки:** 34 целевых теста; quick_gate 857/30/13 PASS,
|
||||||
|
pytest 857 passed, 0 failed; verifier без остаточных находок; diff check PASS.
|
||||||
|
- **Граница:** пользователь выбрал эфир; частота/полоса и геометрия пока
|
||||||
|
не согласованы. 1400 МГц для излучения не используется. Общий такт проверен
|
||||||
|
только mock-вызовами; аппаратный preflight NOT_PERFORMED. RF TX=0.
|
||||||
|
Эфирный результат будет характеризовать весь путь с антеннами и распространением.
|
||||||
|
- **Отчёт:** [состояние](lab056_current_state.md),
|
||||||
|
[подготовка TX](../data/processed/lab056/preparation.json),
|
||||||
|
[сводка проверок](../data/processed/lab056/acceptance.json), журнал 047.
|
||||||
|
- **Далее:** STOP и отдельное разрешение на согласованный RF-сеанс.
|
||||||
|
Lab056 не коммитилась, push не выполнялся; Lab054 NOT_A сохранён.
|
||||||
|
- **Корректировка, запись 048:** кабельный контроль отложен из-за отсутствия
|
||||||
|
SMA-переходника; принят контролируемый OTA. RF_FREQ задаёт пользователь,
|
||||||
|
страна и границы диапазона не запрашиваются. Конкретная частота пока UNKNOWN.
|
||||||
|
Добавлен отдельный RX-only preflight с STOP перед разрешением TX, проверки
|
||||||
|
клиппинга до/после передачи, вывод комплексного h и повторов A2/A1, B2/B1.
|
||||||
|
TX и прежние результаты сохранены. 44 целевых теста; quick_gate 867/30/13
|
||||||
|
PASS, pytest 867 passed; verifier без находок. Аппаратных команд нет, RF TX=0.
|
||||||
|
[Сводка корректировки](../data/processed/lab056/ota_correction.json).
|
||||||
|
- **Аппаратный preflight, запись 049:** пользователь задал 1400000000 Гц
|
||||||
|
только для Lab056, OTA LOS примерно 3–5 м. Common clock PASS: оба reg29
|
||||||
|
0x26, SLAVE CLKIN DETECTED. RX-only 800 000 отсчётов/0,1 с, overruns 0,
|
||||||
|
клиппинг 0, RMS 2,203153 код. CLKOUT выключены, процессы CLEAR, RF TX=0.
|
||||||
|
[Отчёт preflight](lab056_rx_preflight.md). STOP до отдельного разрешения TX.
|
||||||
|
- **Единственный ABAB, запись 050:** после отдельного разрешения один TX
|
||||||
|
1,323568 с и RX 3 с; 4/4 сегмента, ошибка согласования начал ≤1 отсчёта,
|
||||||
|
клиппинг/overruns/underruns 0. X=+0,108895 дБ, Y=−0,163042 дБ,
|
||||||
|
Z=−13,870505 дБ. Повтор мощности A2/A1 −0,482024 дБ, B2/B1 −0,512676 дБ;
|
||||||
|
повтор коэффициента передачи примерно −0,18 дБ у обоих режимов.
|
||||||
|
Прежний провал 15,41 дБ в этой OTA-сессии не воспроизвёлся; собственные
|
||||||
|
потери HackRF не изолированы. Verifier подтвердил сырые данные и расчёты.
|
||||||
|
[RF-отчёт](lab056_rf_result.md), [сводка](../data/processed/lab056/rf_result.json),
|
||||||
|
[график](../data/processed/lab056/rf_overview.png). Выполненных TX 1, сейчас
|
||||||
|
RF TX=0, повторов нет. STOP, Lab056 не коммитилась, push не выполнялся.
|
||||||
|
|
||||||
## Правило хранения данных
|
## Правило хранения данных
|
||||||
|
|
||||||
Большие записи IQ, потоки `cs8`, бинарные объекты и другие аппаратные исходные
|
Большие записи IQ, потоки `cs8`, бинарные объекты и другие аппаратные исходные
|
||||||
|
|||||||
326
docs/lab056_current_state.md
Normal file
326
docs/lab056_current_state.md
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
# Lab056 — подготовка сравнения 20 и 500 ксимв/с в одной сессии
|
||||||
|
|
||||||
|
Подготовка 9 сентября 2026 года; корректировка OTA 10 сентября 2026 года.
|
||||||
|
Lab056 завершила единственный разрешённый OTA ABAB-сеанс 10 сентября.
|
||||||
|
Принятая мощность B/A +0,108895 дБ, коэффициент передачи по мощности
|
||||||
|
−0,163042 дБ, энергия символа −13,870505 дБ. Прежний провал 15,41 дБ
|
||||||
|
не воспроизвёлся. [Полный RF-отчёт](lab056_rf_result.md).
|
||||||
|
Выполненных передач 1, повторов 0; **RF TX сейчас = 0, STOP**.
|
||||||
|
Ниже сохранено описание этапов подготовки и preflight; актуальный итог
|
||||||
|
приведён в RF-отчёте и в заключительном блоке состояния.
|
||||||
|
|
||||||
|
Простыми словами: для будущего опыта создан файл, который дважды чередует
|
||||||
|
медленную и быструю передачу с практически одинаковой цифровой амплитудой.
|
||||||
|
Анализатор на искусственных записях различает равное усиление и падение на 6 дБ.
|
||||||
|
Как изменятся полезный принятый сигнал и фон между A/B в реальном эфире,
|
||||||
|
ещё не измерено; пока записан только RX-фон без TX.
|
||||||
|
Одинаковый TX VGA и одинаковая цифровая мощность не доказывают одинаковую
|
||||||
|
мощность на выходе HackRF. Для абсолютных ватт/dBm нужен калиброванный прибор.
|
||||||
|
|
||||||
|
## Зафиксированная база и границы задания
|
||||||
|
|
||||||
|
По новому явному разрешению пользователя создан коммит Lab055:
|
||||||
|
|
||||||
|
`5add95ecb8865f59075bbbced08d97b183fb3b48`
|
||||||
|
|
||||||
|
`Lab055: audit received signal energy across symbol rates`
|
||||||
|
|
||||||
|
В коммите ровно семь файлов: анализатор и тесты Lab055, её отчёт, два JSON
|
||||||
|
результата, только запись 046 журнала и только раздел Lab055 индекса.
|
||||||
|
Предшествующие незакоммиченные изменения Lab054 сохранены в рабочем каталоге.
|
||||||
|
Перед коммитом quick_gate: 823 теста, 28 функциональных проверок, 13 штатных
|
||||||
|
пропусков; pytest: 823 passed, 0 failed. Push не выполнялся.
|
||||||
|
|
||||||
|
Lab055 установила почти равную цифровую мощность активного TX
|
||||||
|
(1950,308/1950,598 код²), снижение согласованной RX-компоненты на 15,413 дБ
|
||||||
|
и сопоставимый фон в одинаковой полосе. Разные аппаратные сессии не позволяют
|
||||||
|
приписать это снижение одной скорости. Lab054 остаётся **NOT_A, 0/16**.
|
||||||
|
Повторно проверены все 34 SHA-256 входов Lab055: изменений нет.
|
||||||
|
|
||||||
|
Пользователь осознанно изменил методику: необходимого SMA-переходника нет,
|
||||||
|
поэтому сейчас выполняется подготовка контролируемого эфирного ABAB.
|
||||||
|
Кабельный контроль TX → 30 дБ → RX отложен до получения переходника
|
||||||
|
и будет отдельной проверкой, без автоматического запуска после Lab056.
|
||||||
|
|
||||||
|
Схема: MASTER TX → TX-антенна → свободное пространство → RX-антенна → SLAVE RX.
|
||||||
|
На всю ABAB сохраняются антенны, кабели, одинаковая поляризация, расстояние,
|
||||||
|
ориентация, положение оборудования, частота, усиления и common clock.
|
||||||
|
Антенны не трогают; по возможности исключают движение людей возле них.
|
||||||
|
Внешние PA и LNA, в том числе TQP3M9037, не используются.
|
||||||
|
|
||||||
|
`RF_FREQ = USER_AUTHORIZED_RF_HZ = 1400000000`: значение сообщил пользователь.
|
||||||
|
Частоту, разрешённую для лаборатории, определяет пользователь. Страна и
|
||||||
|
границы национальных диапазонов больше не являются обязательными полями
|
||||||
|
runner; повторное исследование национальной регуляторики не проводилось.
|
||||||
|
1400 МГц не подставляется по умолчанию. Mock-значения 915/916 МГц также
|
||||||
|
не являются выбором частоты для аппаратного опыта.
|
||||||
|
|
||||||
|
| Параметр эфирной схемы | Текущее значение |
|
||||||
|
|---|---|
|
||||||
|
| Центральная RF-частота | 1400000000 Гц, только лабораторная Lab056 |
|
||||||
|
| TX-антенна / RX-антенна | Имеющиеся антенны 1400 МГц по описанию пользователя; модели/маркировка не сообщены |
|
||||||
|
| Расстояние TX–RX | Примерно 3–5 м по заданию; точное измерение не сообщено |
|
||||||
|
| Взаимная ориентация и поляризация | Одинаковые, антенны неподвижны, по заданию пользователя |
|
||||||
|
| Прямая видимость LOS / отсутствие прямой видимости NLOS | LOS |
|
||||||
|
|
||||||
|
После конкретного RF_FREQ выполнен аппаратный RX-only preflight, PASS.
|
||||||
|
Сейчас STOP. TX — только после отдельного разрешения на один ABAB-сеанс.
|
||||||
|
Само сообщение частоты не является разрешением на TX. Частота 1400 МГц
|
||||||
|
не назначена рабочей частотой SDR_Rover: ориентир около 180 МГц и варианты
|
||||||
|
50/144 МГц рассматриваются отдельно, вне Lab056.
|
||||||
|
|
||||||
|
## Подготовленный TX
|
||||||
|
|
||||||
|
Файл: `data/raw/lab056/tx_abab.cs8`, signed int8 IQ, 21 177 088 байт.
|
||||||
|
SHA-256: `5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600`.
|
||||||
|
Параметры сохранены в `data/raw/lab056/tx_metadata.json` и
|
||||||
|
[preparation.json](../data/processed/lab056/preparation.json).
|
||||||
|
|
||||||
|
Используются прежние PRBS7, BPSK и RRC из тракта Lab043/Lab042:
|
||||||
|
roll-off 0,35, span 10 символов, защитные участки по 16 символов.
|
||||||
|
Fs = 8 000 000 комплексных отсчётов/с, цифровая несущая +50 кГц.
|
||||||
|
Алгоритмы PHY/FEC/CRC, пороги и штатный приёмник не изменялись.
|
||||||
|
|
||||||
|
| Параметр | A: 20 ксимв/с | B: 500 ксимв/с |
|
||||||
|
|---|---:|---:|
|
||||||
|
| Отсчётов на символ | 400 | 16 |
|
||||||
|
| Полезных PRBS7-символов | 4096 | 102400 |
|
||||||
|
| Длительность активных символов | 0,204800 с | 0,204800 с |
|
||||||
|
| Длительность сегмента с защитными участками | 0,206900 с | 0,204884 с |
|
||||||
|
| RMS всего сегмента после int8 | 44,004413 код | 44,001925 код |
|
||||||
|
| Средняя мощность всего сегмента | 1936,388382 код² | 1936,169379 код² |
|
||||||
|
| RMS активного участка | 44,227281 код | 44,010867 код |
|
||||||
|
| Средняя мощность активного участка | 1956,052408 код² | 1936,956395 код² |
|
||||||
|
| Клиппинг | 0 | 0 |
|
||||||
|
|
||||||
|
Разница средней мощности B/A всего сегмента: **−0,000491 дБ**,
|
||||||
|
критерий подготовки |разница| ≤ 0,01 дБ выполнен. Активные участки отличаются
|
||||||
|
на −0,042606 дБ из-за разной доли защитных участков; это явно сохранено
|
||||||
|
в метаданных и учитывается при сравнении коэффициента передачи.
|
||||||
|
RMS указан в кодах, средняя мощность — RMS² в код².
|
||||||
|
|
||||||
|
Последовательность: gap → A1 → gap → B1 → gap → A2 → gap → B2 → gap.
|
||||||
|
Каждый gap длится 0,1 с. A1 и A2 совпадают побайтно; B1 и B2 тоже.
|
||||||
|
Один файл содержит 10 588 544 комплексных отсчёта, **1,323568 с**.
|
||||||
|
План RX: 24 000 000 отсчётов, **3 с**, запуск на 0,5 с раньше TX.
|
||||||
|
|
||||||
|
Важно: gap — нулевые цифровые IQ при работающем TX-процессе.
|
||||||
|
Это не выключение передатчика; утечка несущей и собственный фон возможны.
|
||||||
|
Полная длительность файла включает эти участки.
|
||||||
|
|
||||||
|
## Измерения сохранённого RX
|
||||||
|
|
||||||
|
Анализатор `experiments/lab056_abab_energy.py` сначала ищет всю ABAB
|
||||||
|
корреляцией с известным TX: шаг 32 отсчёта, сетка остаточного частотного
|
||||||
|
смещения (CFO) от −2 до +2 Гц с шагом 0,25 Гц. Затем существующий метод
|
||||||
|
Lab055 уточняет задержку, CFO, фазу, постоянное смещение и комплексный
|
||||||
|
коэффициент на обучающих плитках; мощность компоненты и остатка проверяется
|
||||||
|
на отдельных плитках. При недостаточном совпадении результат UNKNOWN.
|
||||||
|
Порог 0,05 относится только к этой диагностической локализации;
|
||||||
|
он не меняет пороги PHY/CRC и не даёт независимого приёма пакетов.
|
||||||
|
|
||||||
|
Прореживание без фильтра используется только при грубом поиске. Мощности
|
||||||
|
и спектры вычисляются по исходным полным отсчётам. RX не масштабируется
|
||||||
|
для искусственного выравнивания A/B.
|
||||||
|
|
||||||
|
Для каждого A1/B1/A2/B2 сохраняются:
|
||||||
|
|
||||||
|
- Полная средняя IQ-мощность `mean(|r|²)` и мощность после удаления среднего
|
||||||
|
(AC), RMS, постоянное смещение, пик и клиппинг.
|
||||||
|
- Два ближайших окна фона по 60 мс в соседних нулевых цифровых участках.
|
||||||
|
Дополнительно — доступные окна по 50 мс вне всей последовательности.
|
||||||
|
Внешние окна нельзя автоматически назвать RF OFF без проверки времён
|
||||||
|
запуска/остановки: в текущей подготовке аппаратного фона ещё нет.
|
||||||
|
- Разность AC-мощности и среднего локального фона. Разброс оценивается
|
||||||
|
по неперекрывающимся блокам 4 мс, включая отличие фона до и после сегмента.
|
||||||
|
Если превышение не больше трёх оценок неопределённости, итоговая мощность
|
||||||
|
полезного сигнала `null`, а исходная разность сохраняется. Это критерий
|
||||||
|
численной устойчивости, не калиброванный доверительный интервал.
|
||||||
|
- Комплексный коэффициент `h = sum(conj(s)*r) / sum(|s|²)` после согласования
|
||||||
|
и удаления постоянного смещения; сравнивается `|h|²`.
|
||||||
|
- Мощность согласованной компоненты и остатка. Остаток включает шум и
|
||||||
|
искажения, которые не описывает модель; это не чистый тепловой шум.
|
||||||
|
- Интегральная мощность спектра Welch в полосах 27 и 675 кГц вокруг
|
||||||
|
+50 кГц. Для A штатная полоса 27 кГц, для B — 675 кГц;
|
||||||
|
обе полосы записываются также для сравнения фона в одинаковых условиях.
|
||||||
|
|
||||||
|
Отдельно для двух пар сохраняются B/A в дБ по принятой мощности после
|
||||||
|
вычитания фона, по `|h|²`, по согласованной компоненте и по её энергии
|
||||||
|
символа `P/Rs`. Единицы мощности — коды RX², энергии — код²·с;
|
||||||
|
это относительные цифровые оценки, не калиброванные ватты и джоули.
|
||||||
|
При одинаковой принятой мощности рост скорости в 25 раз даст уменьшение
|
||||||
|
энергии символа на 13,9794 дБ. Равенство принятой мощности сначала проверяется.
|
||||||
|
|
||||||
|
Корректировка добавляет явный вывод `h.real`, `h.imag` и фазы относительно
|
||||||
|
начала активного участка. Он вычисляется на тех же обучающих плитках
|
||||||
|
при уже найденном CFO; его модуль проверяется на совпадение с моделью
|
||||||
|
Lab055. Алгоритм согласования и разделение обучения/проверки сохранены.
|
||||||
|
|
||||||
|
Отдельно выводятся A2/A1 и B2/B1: полная raw-мощность, фон, полезная
|
||||||
|
мощность после вычитания фона, `|h|²` и согласованная компонента.
|
||||||
|
Итоговые X/Y/Z в `comparison_summary` используют отношение средних
|
||||||
|
линейных мощностей двух B к двум A. X — полезная принятая мощность
|
||||||
|
после вычитания фона, Y — `|h|²`, Z — энергия символа этой же полезной
|
||||||
|
мощности. Если вычитание фона неустойчиво хотя бы в одном сегменте,
|
||||||
|
X/Z остаются `null`; компонентные метрики и повторяемость сохраняются.
|
||||||
|
|
||||||
|
## Проверка искусственными записями
|
||||||
|
|
||||||
|
Созданы две явно синтетические записи CS8 по 3 с: одинаковое усиление
|
||||||
|
обоих режимов и уменьшение амплитуды B вдвое (мощность −6,020600 дБ).
|
||||||
|
В модель добавлены шум, постоянное смещение, CFO +0,2 Гц, фаза и неизвестное
|
||||||
|
для поиска начало 4 160 013 отсчётов. Известное начало записано в результат
|
||||||
|
после анализа и не передавалось алгоритму поиска.
|
||||||
|
|
||||||
|
| Вход модели | Пара | Разница принятой мощности B/A | Разница `|h|²` B/A | Разница энергии согласованной компоненты |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| Одинаковое усиление | 1 | −0,051497 дБ | +0,001786 дБ | −14,019673 дБ |
|
||||||
|
| Одинаковое усиление | 2 | −0,041310 дБ | −0,002894 дБ | −14,024352 дБ |
|
||||||
|
| B: −6,020600 дБ | 1 | −6,050214 дБ | −6,014144 дБ | −20,035602 дБ |
|
||||||
|
| B: −6,020600 дБ | 2 | −6,028048 дБ | −6,022175 дБ | −20,043634 дБ |
|
||||||
|
|
||||||
|
Обе проверки PASS: ошибка коэффициента передачи в каждой паре меньше
|
||||||
|
заданных 0,3 дБ. Разница принятой мощности включает небольшое различие
|
||||||
|
цифровой мощности активных участков. Это результаты модели, не RF Lab056.
|
||||||
|
Данные: [равное усиление](../data/processed/lab056/software_equal.json),
|
||||||
|
[ослабление B](../data/processed/lab056/software_minus6db.json).
|
||||||
|
|
||||||
|
При корректировке те же сохранённые IQ проанализированы без перегенерации.
|
||||||
|
Прежние файлы результатов сохранены; новые сводки:
|
||||||
|
[равное усиление, OTA summary](../data/processed/lab056/software_equal_ota_summary.json),
|
||||||
|
[ослабление B, OTA summary](../data/processed/lab056/software_minus6db_ota_summary.json).
|
||||||
|
|
||||||
|
| Искусственная запись | X: мощность B/A | Y: коэффициент передачи B/A | Z: энергия символа B/A | Повтор `|h|²`, A2/A1 | Повтор `|h|²`, B2/B1 |
|
||||||
|
|---|---:|---:|---:|---:|---:|
|
||||||
|
| Равное усиление | −0,046404 дБ | −0,000553 дБ | −14,025804 дБ | −0,002308 дБ | −0,006988 дБ |
|
||||||
|
| B: −6,020600 дБ | −6,039124 дБ | −6,018156 дБ | −20,018524 дБ | −0,002308 дБ | −0,010340 дБ |
|
||||||
|
|
||||||
|
Это по-прежнему результаты программной модели. Они не устанавливают
|
||||||
|
величину эфирного различия Lab056.
|
||||||
|
|
||||||
|
Повторный анализ существующего файла с новым именем результата:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python experiments/lab056_abab_energy.py analyze --rx data/raw/lab056/synthetic_equal.cs8 --output data/processed/lab056/recheck_equal.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Существующие выходные файлы не перезаписываются. Команды `prepare` и
|
||||||
|
`software` создают данные только при отсутствии соответствующих файлов.
|
||||||
|
|
||||||
|
## Подготовка однократного аппаратного запуска
|
||||||
|
|
||||||
|
`experiments/lab056_capture_runner.py` не имеет аппаратного CLI. Без конкретного
|
||||||
|
положительного целого RF_FREQ из задания пользователя аппаратные действия
|
||||||
|
не начинаются. Частота одинакова для всех RX-команд и единственной TX-команды;
|
||||||
|
цифровая несущая остаётся +50 кГц. Страна/регуляторная полоса не запрашиваются
|
||||||
|
программой. Геометрия сохраняется вместе с частотой в отдельном OperatingPoint.
|
||||||
|
|
||||||
|
Роли плат сохранены: MASTER/TX `000000000000000097c063dc323b97d3`,
|
||||||
|
SLAVE/RX `000000000000000097c063dc2e5327d3`. Планируется прежнее тактирование
|
||||||
|
10 МГц TCXO → MASTER → CLKOUT → SLAVE CLKIN. Требуются оба reg29 = 0x26
|
||||||
|
и SLAVE CLKIN = DETECTED после короткого RX-start. Сначала логика проверена
|
||||||
|
mock-вызовами; теперь эти значения подтверждены аппаратным preflight.
|
||||||
|
|
||||||
|
Вызов `preflight_only` после получения RF_FREQ проверит инвентаризацию,
|
||||||
|
common clock и сохранит 0,1 с RX без TX на заданной частоте при RX 24/24 дБ.
|
||||||
|
Он проверит размер, явный нулевой счётчик потерь, уровень и клиппинг,
|
||||||
|
запишет `preflight_result.json`, выключит CLKOUT и завершится с STOP.
|
||||||
|
Любая TX-команда внутри этого этапа запрещена обёрткой runner.
|
||||||
|
Файл попытки создаётся эксклюзивно; автоматического повтора нет.
|
||||||
|
|
||||||
|
После отдельного разрешения `capture_once` потребует PASS preflight той же
|
||||||
|
частоты, геометрии и SHA TX. Поскольку после preflight CLKOUT выключены,
|
||||||
|
перед фактическим сеансом опора проверяется заново при запуске. Это проверка
|
||||||
|
готовности перед единственным TX, а не повтор передачи.
|
||||||
|
|
||||||
|
TX VGA 10 дБ, RX LNA/VGA 24/24 дБ, RF amplifier и Bias-Tee OFF,
|
||||||
|
Fs 8 Мвыб/с фиксированы на всю ABAB. RX запускается первым; затем допускается
|
||||||
|
один TX-процесс с конечным числом отсчётов, без `-R`. Маркер попытки создаётся
|
||||||
|
эксклюзивно; ошибка расходует попытку, автоматического повтора нет.
|
||||||
|
Требуются процессы CLEAR до/после, правильный размер RX, явные нулевые
|
||||||
|
RX overruns/TX underruns и успешное выключение CLKOUT обеих плат.
|
||||||
|
После упреждения RX проверяются последние 65 536 записанных отсчётов:
|
||||||
|
при rail-кодах −128/127 TX не запускается, RF TX=0 и STOP. Полная запись
|
||||||
|
проверяется также после ABAB. Если клиппинг появился только при передаче,
|
||||||
|
фиксируется RF TX=1 и невалидное измерение без повтора. Уже выполненная
|
||||||
|
передача не переобозначается нулём. В отчёте сохраняются RMS, пики I/Q,
|
||||||
|
доли клиппинга; отсутствие rail-кодов само по себе не доказывает отсутствие
|
||||||
|
аналоговой компрессии. Gain не меняется автоматически ни в одном сценарии.
|
||||||
|
Аппаратные помощники переиспользованы из существующей Lab054; её файлы
|
||||||
|
`experiments/lab054_hardware_control.py` и `tools/hackrf_process_check.py`
|
||||||
|
включаются в фиксацию Lab056 как необходимые зависимости вместе с их
|
||||||
|
двумя тестовыми файлами. Без них runner Lab056 не импортируется в чистой
|
||||||
|
копии репозитория, что независимо воспроизведено verifier. Старые отчёты,
|
||||||
|
данные и записи журнала Lab054 в эту фиксацию не входят. Реализации
|
||||||
|
зависимостей не переписывались; аппаратная логика осталась проверенной.
|
||||||
|
|
||||||
|
Verifier независимо подтвердил SHA/размеры TX, нулевые gaps, побайтную
|
||||||
|
повторяемость A/B и пересчитал коэффициенты передачи вторым методом.
|
||||||
|
Он воспроизвёл ошибку разбора журнала `1 RX overruns\n0 overruns`;
|
||||||
|
исправленный разбор отклоняет ненулевые, неизвестные и неразобранные
|
||||||
|
счётчики. Повторные mock-вызовы `capture_once` закрыли находку.
|
||||||
|
Остаточных воспроизведённых находок нет. Оборудование verifier не использовал.
|
||||||
|
|
||||||
|
Исходная подготовка: 34 целевых теста passed; quick_gate: 857 тестов,
|
||||||
|
30 функциональных проверок, 13 штатных пропусков, PASS. Полный pytest:
|
||||||
|
857 passed, 0 failed, 96,50 с (кеш отключён из-за прежнего отказа доступа).
|
||||||
|
`git diff --check`: PASS. Машинная сводка:
|
||||||
|
[acceptance.json](../data/processed/lab056/acceptance.json).
|
||||||
|
|
||||||
|
Корректировка OTA: 44 целевых теста PASS; quick_gate 867 тестов /
|
||||||
|
30 функциональных проверок / 13 штатных пропусков, PASS; pytest
|
||||||
|
867 passed, 0 failed, 89,32 с. Verifier исполнил все 44 сценария
|
||||||
|
в памяти без файлов и оборудования; остаточных находок нет.
|
||||||
|
Новая сводка [ota_correction.json](../data/processed/lab056/ota_correction.json)
|
||||||
|
дополняет исходную acceptance, не заменяя исторические 857 passed.
|
||||||
|
|
||||||
|
## Как читать будущий эфирный результат
|
||||||
|
|
||||||
|
Эфирный ABAB сравнит весь путь: TX, антенны, распространение, RX. Малое
|
||||||
|
различие означает, что большой провал не воспроизведён в этом пути и этой
|
||||||
|
сессии. Оно не доказывает равенство мощности непосредственно на выходе TX.
|
||||||
|
Большое повторяемое различие тоже нельзя автоматически отнести внутрь
|
||||||
|
HackRF: частотная избирательность антенн и многолучевого распространения
|
||||||
|
остаётся частью измерения. Для отделения аппаратного эффекта пригодится
|
||||||
|
отдельно согласованный кабельный или калиброванный измерительный опыт.
|
||||||
|
|
||||||
|
Ни необходимость PA, ни изменение результатов Lab047/Lab054 из этой
|
||||||
|
подготовки не следуют. Архитектура проекта сохраняется: управление через
|
||||||
|
RadioMaster/ExpressLRS/CRSF, SDR — видеотракт с целью минимум 5 свежих кадров/с.
|
||||||
|
|
||||||
|
## Состояние перед STOP
|
||||||
|
|
||||||
|
```text
|
||||||
|
LAB055 COMMIT = 5add95ecb8865f59075bbbced08d97b183fb3b48
|
||||||
|
NEW HEAD = 5add95ecb8865f59075bbbced08d97b183fb3b48
|
||||||
|
LAB056 RESULT = OTA_ABAB_VALID_LARGE_DROP_NOT_REPRODUCED_STOP
|
||||||
|
PHYSICAL CONNECTION = OTA, LOS, fixed antennas, same polarization/orientation, approximately 3-5 m per user
|
||||||
|
RF FREQUENCY = 1400000000 Hz (Lab056 test only)
|
||||||
|
A SYMBOL RATE = 20 ksymbol/s
|
||||||
|
B SYMBOL RATE = 500 ksymbol/s
|
||||||
|
TX RMS A = 44.004413 code
|
||||||
|
TX RMS B = 44.001925 code
|
||||||
|
TX POWER DIFFERENCE B/A = -0.000491 dB (whole segment)
|
||||||
|
ABAB TX DURATION = 1.323568 s
|
||||||
|
RX PLANNED DURATION = 3 s, lead 0.5 s
|
||||||
|
COMMON CLOCK PREFLIGHT = HARDWARE PASS, both reg29=0x26, SLAVE CLKIN=DETECTED
|
||||||
|
RX PREFLIGHT = PASS, 800000 complex samples, 0.1 s, overruns=0
|
||||||
|
CLIPPING = 0
|
||||||
|
READY FOR ANOTHER TX = NO (authorized single attempt consumed)
|
||||||
|
COMPLETED TX SESSIONS = 1
|
||||||
|
AUTOMATIC TX REPEATS = 0
|
||||||
|
RX ABAB RECORD = 24000000 complex samples, 3 s, clipping=0, overruns=0
|
||||||
|
TX UNDERRUNS = 0
|
||||||
|
RECEIVED POWER DIFFERENCE B/A = +0.108895 dB
|
||||||
|
TX-MATCHED CHANNEL GAIN DIFFERENCE B/A = -0.163042 dB
|
||||||
|
ENERGY PER SYMBOL DIFFERENCE = -13.870505 dB
|
||||||
|
BASELINE pytest = 857 passed (сохранённый результат подготовки)
|
||||||
|
quick_gate = PASS, 867 tests / 30 functional / 13 skipped
|
||||||
|
pytest = PASS, 867 passed / 0 failed (после корректировки)
|
||||||
|
git diff --check = PASS
|
||||||
|
RF TX = 0
|
||||||
|
LAB056 COMMIT = NOT_PERFORMED
|
||||||
|
PUSH = NOT_PERFORMED
|
||||||
|
CURRENT SINGLE-TX AUTHORIZATION = CONSUMED
|
||||||
|
STOP
|
||||||
|
```
|
||||||
222
docs/lab056_rf_result.md
Normal file
222
docs/lab056_rf_result.md
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
# Lab056 — единственный OTA ABAB: результат
|
||||||
|
|
||||||
|
10 сентября 2026 года, сеанс 12:32:24–12:32:28 по Москве.
|
||||||
|
|
||||||
|
**Внутри одного короткого эфирного сеанса большой провал при переходе
|
||||||
|
20 → 500 ксимв/с не воспроизвёлся.** Принятая мощность после вычитания фона
|
||||||
|
у B выше A на 0,109 дБ; согласованный коэффициент передачи по мощности
|
||||||
|
ниже на 0,163 дБ. Обе пары A/B дают близкие результаты. Энергия символа
|
||||||
|
уменьшилась на 13,871 дБ: при почти одинаковой принятой мощности она
|
||||||
|
приходится на в 25 раз большее число символов в секунду.
|
||||||
|
|
||||||
|
Это результат всего OTA-пути с антеннами и распространением. Он не
|
||||||
|
измеряет выходные dBm HackRF и не объясняет задним числом причину прежнего
|
||||||
|
различия Lab047/Lab054 на 15,41 дБ. Lab054 остаётся **NOT_A, 0/16**.
|
||||||
|
|
||||||
|
Выполнена ровно **одна** передача. Сейчас **RF TX = 0**, передатчик
|
||||||
|
остановлен; повторов нет. Разрешённая попытка израсходована. **STOP.**
|
||||||
|
|
||||||
|
## Фактические условия
|
||||||
|
|
||||||
|
Пользователь отдельно разрешил этот сеанс после аппаратного RX-only
|
||||||
|
preflight. Частота 1 400 000 000 Гц относится только к Lab056, не является
|
||||||
|
выбором рабочего диапазона SDR_Rover. Ориентир около 180 МГц и варианты
|
||||||
|
50/144 МГц рассматриваются отдельно и здесь не исследовались.
|
||||||
|
|
||||||
|
OTA: MASTER TX → TX-антенна → LOS → RX-антенна → SLAVE RX. По заданию
|
||||||
|
пользователя антенны неподвижны, ориентация и поляризация одинаковы,
|
||||||
|
расстояние примерно 3–5 м, кабели не меняются. Использованы имеющиеся
|
||||||
|
антенны на 1400 МГц; модели/маркировка и точное измеренное расстояние
|
||||||
|
не сообщены. Программа не измеряет геометрию; это описание пользователя.
|
||||||
|
|
||||||
|
Один сохранённый TX-файл содержит A1 → gap → B1 → gap → A2 → gap → B2
|
||||||
|
с дополнительными начальным/конечным gaps. A = 20 ксимв/с, B = 500 ксимв/с.
|
||||||
|
Fs 8 Мвыб/с; цифровая несущая +50 кГц; прежние PRBS7/BPSK/RRC.
|
||||||
|
TX VGA 10 дБ, RX LNA/VGA 24/24 дБ, RF amplifier/Bias-Tee OFF.
|
||||||
|
Внешние PA/LNA отсутствуют. Частота, усиления, Fs и параметры формы
|
||||||
|
не менялись между сегментами. PHY/FEC/CRC, пороги и timing не изменялись.
|
||||||
|
|
||||||
|
Цифровой RMS всего сегмента A/B: 44,004413/44,001925 код;
|
||||||
|
разница мощности B/A −0,000491 дБ. Разница активных частей −0,042606 дБ
|
||||||
|
из-за разной доли защитных участков сохранена в метаданных и не скрывается.
|
||||||
|
RX не нормировался для искусственного выравнивания A/B.
|
||||||
|
|
||||||
|
## Полнота записи и аппаратные проверки
|
||||||
|
|
||||||
|
| Проверка | Фактический результат |
|
||||||
|
|---|---|
|
||||||
|
| Число TX-процессов | 1, без `-R`, конечный `-n 10588544` |
|
||||||
|
| TX-файл | 21 177 088 байт, 10 588 544 комплексных отсчёта, 1,323568 с |
|
||||||
|
| Основной RX | 48 000 000 байт, 24 000 000 комплексных отсчётов, ровно 3 с |
|
||||||
|
| Порядок запуска | RX раньше TX на 0,517342 с по журналу запуска процессов |
|
||||||
|
| MASTER / SLAVE reg29 | 0x26 / 0x26 |
|
||||||
|
| SLAVE CLKIN | DETECTED в пределах штатного ожидания запуска опоры |
|
||||||
|
| RX overruns / TX underruns | 0 / 0 по всем явным счётчикам журналов |
|
||||||
|
| Клиппинг RX до TX | 0 / 65 536 отсчётов проверенного хвоста |
|
||||||
|
| Клиппинг полного RX после ABAB | 0 / 24 000 000 отсчётов; 0 / 48 000 000 компонент |
|
||||||
|
| Полный RX: RMS / максимумы абсолютных I,Q | 2,235380477 код / 5, 6 код |
|
||||||
|
| Процессы до / после | CLEAR / CLEAR |
|
||||||
|
| Завершение | CLKOUT обеих плат выключены, ошибок cleanup нет |
|
||||||
|
|
||||||
|
Общая опора проверена при запуске заново, поскольку предыдущий preflight
|
||||||
|
завершился выключением CLKOUT. Первый запрос CLKIN ещё не обнаружил опору;
|
||||||
|
второй обнаружил её через 137,359 мс штатного ожидания. После PASS
|
||||||
|
состоялся единственный TX; опрос CLKIN не является повтором передачи.
|
||||||
|
|
||||||
|
В журнале 14 команд: инвентаризация, опора, два коротких RX-start по
|
||||||
|
65 536 отсчётов для выбора опоры, один основной RX, один TX и cleanup.
|
||||||
|
После запуска TX до его окончания других аппаратных команд нет.
|
||||||
|
Параметры команд и их исходный вывод сохранены; аппаратной перестройки
|
||||||
|
между A/B не было. Физическая неподвижность антенн следует из заданных
|
||||||
|
пользователем условий, а не из программного измерения.
|
||||||
|
|
||||||
|
Утилита указала TX transfer time 1,32538 с и RX transfer time 3,01535 с.
|
||||||
|
Это служебные интервалы обмена, отличные от длительности отсчётов файла;
|
||||||
|
они не используются вместо расчёта длительности по числу отсчётов и Fs.
|
||||||
|
Отсутствие rail-кодов не является калиброванной проверкой аналоговой
|
||||||
|
компрессии; обнаруженных цифровых признаков насыщения нет.
|
||||||
|
|
||||||
|
## Все четыре сегмента
|
||||||
|
|
||||||
|
Подготовленный анализатор нашёл ABAB с нормированной корреляцией 0,384366
|
||||||
|
и грубым остаточным CFO 0 Гц. Оценённые начала всей последовательности
|
||||||
|
по отдельным сегментам: 4 454 791, 4 454 791, 4 454 790, 4 454 791 отсчёт.
|
||||||
|
Разброс составляет один отсчёт, 0,125 мкс. Все сегменты расположены
|
||||||
|
внутри RX, идут в правильном порядке и не перекрываются.
|
||||||
|
|
||||||
|
| Сегмент | Начало сегмента, отсчёт RX | Начало, с | Конец, с | Активных отсчётов |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| A1 | 5 254 791 | 0,656848875 | 0,863748875 | 1 638 400 |
|
||||||
|
| B1 | 7 709 991 | 0,963748875 | 1,168632875 | 1 638 400 |
|
||||||
|
| A2 | 10 149 062 | 1,268632750 | 1,475532750 | 1 638 400 |
|
||||||
|
| B2 | 12 604 263 | 1,575532875 | 1,780416875 | 1 638 400 |
|
||||||
|
|
||||||
|
Длительность активного участка каждого сегмента 0,2048 с. С защитными
|
||||||
|
участками A длится 0,2069 с, B — 0,204884 с. Межсегментные интервалы
|
||||||
|
составляют 0,1 с с расхождением не более одного отсчёта из-за согласования.
|
||||||
|
В спектрах A и B видны соответственно узкая и широкая полосы PRBS/BPSK.
|
||||||
|
Границы, ширина спектров и согласованные компоненты подтверждают
|
||||||
|
наличие четырёх сегментов; это диагностическое измерение с TX-эталоном,
|
||||||
|
не независимый приём пакетов.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Измерения A1/B1/A2/B2
|
||||||
|
|
||||||
|
Все мощности ниже — **код² RX**, не ватты/dBm. `h` выражает отношение
|
||||||
|
цифровых кодов RX/TX после согласования и удаления постоянного смещения.
|
||||||
|
Для вычитания фона используется AC-мощность — после удаления среднего,
|
||||||
|
поэтому она показана отдельно от RAW, включающего постоянную составляющую.
|
||||||
|
|
||||||
|
| Метрика | A1 | B1 | A2 | B2 |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| RAW RX POWER, включая среднее | 5,303053589 | 5,328170166 | 5,311703491 | 5,396551514 |
|
||||||
|
| RX AC POWER, без среднего | 1,084963566 | 1,092709351 | 1,049867204 | 1,090638777 |
|
||||||
|
| BACKGROUND POWER, AC | 0,622282607 | 0,616699337 | 0,635792032 | 0,667630886 |
|
||||||
|
| BACKGROUND-SUBTRACTED SIGNAL POWER | 0,462680958 | 0,476010015 | 0,414075173 | 0,423007891 |
|
||||||
|
| Оценка неопределённости вычитания фона | 0,029702311 | 0,034542825 | 0,017352868 | 0,047227716 |
|
||||||
|
| TX-MATCHED COMPLEX GAIN h | −0,008543899 + j0,009310849 | −0,008089660 + j0,009397484 | −0,007000883 + j0,010205712 | −0,006710537 + j0,010126447 |
|
||||||
|
| Коэффициент передачи по мощности, abs(h)² | 0,000159690119 | 0,000153755289 | 0,000153168935 | 0,000147576238 |
|
||||||
|
| MATCHED SIGNAL COMPONENT POWER | 0,312320987 | 0,297815525 | 0,299566893 | 0,285847043 |
|
||||||
|
| RESIDUAL POWER | 0,821513728 | 0,884789710 | 0,782684444 | 0,880722972 |
|
||||||
|
| INTEGRATED SPECTRAL POWER, полоса 27 кГц | 0,347264323 | 0,029697350 | 0,335922975 | 0,030437417 |
|
||||||
|
| INTEGRATED SPECTRAL POWER, полоса 675 кГц | 0,508299262 | 0,512832243 | 0,500120185 | 0,533314850 |
|
||||||
|
|
||||||
|
Полосы спектрального интегрирования центрированы на +50 кГц. Фактическая
|
||||||
|
расчётная полоса A — 27 кГц, B — 675 кГц; строка 675 кГц также даёт
|
||||||
|
сравнение в общей полосе. Спектральные интегралы включают фон. Различие
|
||||||
|
значений в полосе 27 кГц между A/B не является потерей полной мощности B:
|
||||||
|
большая часть широкого сигнала B лежит вне этой узкой полосы.
|
||||||
|
|
||||||
|
Вычитание фона устойчиво во всех четырёх сегментах по заранее подготовленному
|
||||||
|
критерию: превышение больше трёх оценок неопределённости. Эта оценка учитывает
|
||||||
|
разброс блоков и отличие фона до/после, но не является калиброванным
|
||||||
|
доверительным интервалом. Фон берётся из ближайших нулевых цифровых gaps;
|
||||||
|
TX-процесс в них работает, поэтому gaps нельзя называть выключенным RF.
|
||||||
|
Отдельные внешние окна фона и их спектры сохранены в полном JSON.
|
||||||
|
|
||||||
|
Комплексный h оценён на обучающих плитках; его фаза относится к началу
|
||||||
|
активного участка после коррекции CFO. Мощность согласованной компоненты
|
||||||
|
и остатка измерена на проверочных плитках. Остаток содержит шум и
|
||||||
|
неописанные моделью искажения. Поэтому он не равен отдельно измеренному
|
||||||
|
фону, а согласованная компонента не равна всей мощности после вычитания фона.
|
||||||
|
Полная RAW/AC-статистика относится ко всему активному участку; плиточные
|
||||||
|
метрики не следует складывать с ней как одну точную энергетическую сумму.
|
||||||
|
|
||||||
|
## Повторяемость и итоговые X/Y/Z
|
||||||
|
|
||||||
|
В таблице повторяемости знак относится ко **второму повтору относительно
|
||||||
|
первого**: A2/A1 и B2/B1, 10 log10 отношения мощностей.
|
||||||
|
|
||||||
|
| Сравнение повторов | RAW RX | Фон AC | Полезная мощность после фона | abs(h)² | Согласованная компонента |
|
||||||
|
|---|---:|---:|---:|---:|---:|
|
||||||
|
| A1 / A2 REPEATABILITY: A2/A1 | +0,007078 дБ | +0,093274 дБ | −0,482024 дБ | −0,181074 дБ | −0,181074 дБ |
|
||||||
|
| B1 / B2 REPEATABILITY: B2/B1 | +0,055382 дБ | +0,344629 дБ | −0,512676 дБ | −0,178136 дБ | −0,178136 дБ |
|
||||||
|
|
||||||
|
| Пара A/B | Принятая мощность B/A после фона | abs(h)² B/A | Согласованная компонента B/A |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| B1/A1 | +0,123345 дБ | −0,164480 дБ | −0,206538 дБ |
|
||||||
|
| B2/A2 | +0,092693 дБ | −0,161543 дБ | −0,203601 дБ |
|
||||||
|
|
||||||
|
Итоговые X/Y/Z — отношение средних **линейных** мощностей B1/B2 к A1/A2,
|
||||||
|
а не среднее значений в дБ. Z рассчитано из той же мощности после фона,
|
||||||
|
что X, с делением на скорость символов. Это энергия в код²·с, не джоули.
|
||||||
|
|
||||||
|
```text
|
||||||
|
500K / 20K RECEIVED POWER DIFFERENCE = +0.108895 dB
|
||||||
|
500K / 20K TX-MATCHED CHANNEL GAIN DIFFERENCE = -0.163042 dB
|
||||||
|
ENERGY PER SYMBOL DIFFERENCE = -13.870505 dB
|
||||||
|
```
|
||||||
|
|
||||||
|
Расчёт Z: +0,108895 − 10 log10(500000/20000) = −13,870505 дБ.
|
||||||
|
Отдельная энергия согласованной компоненты в парных результатах изменилась
|
||||||
|
на −14,185938 и −14,183001 дБ; это другой числитель, её не подменяют Z.
|
||||||
|
|
||||||
|
## Инженерная интерпретация
|
||||||
|
|
||||||
|
1. Внутри этой сессии 20 и 500 ксимв/с дали почти равные мощности. Небольшое
|
||||||
|
различие повторилось в обеих парах; утверждать точную частотную
|
||||||
|
характеристику по двум повторам нельзя.
|
||||||
|
2. Вторые повторы слабее первых на 0,48/0,51 дБ по мощности после фона
|
||||||
|
и примерно на 0,18 дБ по коэффициенту передачи. Фон также немного
|
||||||
|
меняется и содержит кратковременные всплески; при этом относительное
|
||||||
|
сравнение B/A сохраняется: расхождение двух оценок X около 0,031 дБ,
|
||||||
|
двух оценок Y около 0,003 дБ.
|
||||||
|
3. Прежний провал порядка 15,41 дБ **не воспроизвёлся** в текущей короткой
|
||||||
|
OTA-сессии. Причина различия старых раздельных опытов остаётся
|
||||||
|
неустановленной; их результаты не пересматриваются.
|
||||||
|
4. По OTA нельзя объявить эффект собственной аппаратной потерей HackRF
|
||||||
|
Plus: антенны и распространение входят в измеренный путь. В этой
|
||||||
|
сессии большого эффекта вообще не обнаружено. Абсолютная мощность
|
||||||
|
на выходе передатчика не измерена.
|
||||||
|
5. Кабельный контроль после получения SMA-переходника остаётся необходимым
|
||||||
|
отдельным этапом для отделения свойств аппаратуры от антенн и канала.
|
||||||
|
OTA его не заменяет. Автоматического повторения опыта или добавления
|
||||||
|
PA/внешнего LNA нет; необходимость усилителя не установлена.
|
||||||
|
|
||||||
|
## Проверка и артефакты
|
||||||
|
|
||||||
|
Verifier независимо прочитал журналы и raw IQ без оборудования и записи
|
||||||
|
файлов: проверил число команд, постоянство параметров, опору, размеры,
|
||||||
|
SHA, отсутствие клиппинга/потерь и согласованность четырёх начал. Raw AC,
|
||||||
|
два окна фона, CFO и комплексный МНК пересчитаны вторым способом;
|
||||||
|
результаты совпали с анализатором до 1e−12. Воспроизведённых находок нет.
|
||||||
|
|
||||||
|
RX SHA-256: `e336dd653fa554058052fcf851e9f2462f66b8cf7c710815ca2b9a09ffc23ad4`.
|
||||||
|
TX SHA-256: `5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600`.
|
||||||
|
|
||||||
|
- [Полный подготовленный анализ](../data/processed/lab056/rf_analysis.json).
|
||||||
|
- [Итоговая машинная сводка](../data/processed/lab056/rf_result.json).
|
||||||
|
- [Авторизация и фиксированные параметры](../data/processed/lab056/rf_session_authorization.json).
|
||||||
|
- [Фактические команды и время](../data/processed/lab056/rf_session_commands.json).
|
||||||
|
- Raw: `data/raw/lab056/rx_stream.cs8`, `capture_result.json`,
|
||||||
|
`hardware_attempt.json`, `tx_process_result.json`, `rx_stdout.txt`, `rx_stderr.txt`.
|
||||||
|
|
||||||
|
Исходные результаты подготовки, preflight и искусственные записи сохранены.
|
||||||
|
Код анализатора/runner и TX не изменялись; использована проверенная версия
|
||||||
|
44 целевых теста / quick_gate 867/30/13 / pytest 867 passed. Для анализа
|
||||||
|
аппаратной записи полные тесты повторно не запускались. Diff check PASS.
|
||||||
|
HEAD `5add95ecb8865f59075bbbced08d97b183fb3b48`, Lab056 не коммитилась,
|
||||||
|
push не выполнялся. Приём пакетов, видео и FEC не проверялись.
|
||||||
|
|
||||||
|
**Выполненных TX-сеансов: 1. RF TX сейчас = 0. STOP.**
|
||||||
98
docs/lab056_rx_preflight.md
Normal file
98
docs/lab056_rx_preflight.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Lab056 — аппаратный common-clock и RX-only preflight
|
||||||
|
|
||||||
|
10 сентября 2026 года, 12:16:28–12:16:29 по Москве.
|
||||||
|
|
||||||
|
**Аппаратный preflight PASS. RF TX = 0. STOP перед отдельным разрешением TX.**
|
||||||
|
Проверена общая опора и записан фон при выключенном передатчике.
|
||||||
|
Сравнение A/B по эфиру ещё не выполнялось.
|
||||||
|
|
||||||
|
```text
|
||||||
|
RF_FREQ = 1400000000 Hz
|
||||||
|
COMMON CLOCK = PASS; MASTER reg29=0x26; SLAVE reg29=0x26; SLAVE CLKIN=DETECTED
|
||||||
|
RX PREFLIGHT = PASS; 800000 complex samples; 0.1 s; RX overruns=0
|
||||||
|
CLIPPING = 0 / 800000 samples (0%)
|
||||||
|
RF TX = 0
|
||||||
|
READY FOR SINGLE ABAB TX = YES (technical preflight; separate authorization pending)
|
||||||
|
STOP
|
||||||
|
```
|
||||||
|
|
||||||
|
## Условия
|
||||||
|
|
||||||
|
По заданию пользователя 1400 МГц — только частота текущего лабораторного
|
||||||
|
Lab056. Она не назначает рабочий диапазон SDR_Rover. Ориентир дальнейших
|
||||||
|
полевых работ около 180 МГц рассматривается отдельно; антенна на этот
|
||||||
|
диапазон уже имеется. Варианты 50/144 МГц здесь не исследуются.
|
||||||
|
|
||||||
|
Схема задана пользователем: MASTER TX → TX-антенна → LOS → RX-антенна → SLAVE RX.
|
||||||
|
Используются имеющиеся антенны на 1400 МГц. Точные модели/маркировка не
|
||||||
|
сообщены и программно не установлены; они не являлись препятствием для
|
||||||
|
этого RX-only этапа. Антенны неподвижны, ориентация и поляризация одинаковы.
|
||||||
|
Расстояние по заданию примерно 3–5 м; точное измеренное значение отсутствует.
|
||||||
|
Кабели и геометрия сохраняются на будущую ABAB.
|
||||||
|
|
||||||
|
Аппаратный RX: Fs 8 Мвыб/с, LNA/VGA 24/24 дБ, RF amplifier и Bias-Tee OFF.
|
||||||
|
Внешние PA/LNA не используются. TX VGA 10 дБ остаётся запланированным
|
||||||
|
параметром будущего сеанса; TX в этом этапе не включался.
|
||||||
|
|
||||||
|
MASTER `000000000000000097c063dc323b97d3` и SLAVE
|
||||||
|
`000000000000000097c063dc2e5327d3` подтверждены свежей инвентаризацией.
|
||||||
|
Обе платы определены как HackRF One r9 с firmware/libhackrf 2024.02.1.
|
||||||
|
|
||||||
|
## Что выполнено
|
||||||
|
|
||||||
|
Перед оборудованием подтверждены HEAD, пустой Git index, SHA проверенного
|
||||||
|
кода и неизменность готового TX. Проверка процессов дала CLEAR.
|
||||||
|
|
||||||
|
Выполнены короткие RX-start по 65 536 отсчётов на MASTER и SLAVE для выбора
|
||||||
|
источника тактирования, чтения reg29 обеих плат и SLAVE CLKIN. Оба reg29
|
||||||
|
равны 0x26; CLKIN обнаружен первым запросом, через 67,787 мс после начала
|
||||||
|
ожидания опоры. Затем SLAVE записал 800 000 комплексных отсчётов RX, 0,1 с.
|
||||||
|
Все три команды hackrf_transfer работали только с `-r`, на одной частоте
|
||||||
|
и с одинаковыми RX-параметрами. Журнал команд не содержит `-t`.
|
||||||
|
|
||||||
|
| Метрика сохранённого RX | Значение |
|
||||||
|
|---|---:|
|
||||||
|
| Размер CS8 | 1 600 000 байт |
|
||||||
|
| Комплексных отсчётов | 800 000 |
|
||||||
|
| Полная средняя мощность | 4,85388125 код² |
|
||||||
|
| Мощность после удаления среднего | 0,6658039582 код² |
|
||||||
|
| RMS полного IQ | 2,203152571 код |
|
||||||
|
| Среднее I / Q | 0,92535625 / 1,82532 код |
|
||||||
|
| Максимум абсолютного I / Q | 4 / 6 код |
|
||||||
|
| Максимальная комплексная амплитуда | 6,08276253 код |
|
||||||
|
| Отсчётов с rail-кодами −128/127 | 0 |
|
||||||
|
| RX overruns | 0 |
|
||||||
|
|
||||||
|
Цифровых признаков насыщения в этом RX-only фоне нет. Это не доказывает
|
||||||
|
отсутствие перегрузки при включённом TX: его сигнал пока отсутствовал.
|
||||||
|
Из этих отсчётов также нельзя установить абсолютную RF-мощность или
|
||||||
|
доказать отсутствие аналоговой компрессии. При будущем разрешённом запуске
|
||||||
|
сохраняются подготовленные проверки клиппинга без автоподбора gain.
|
||||||
|
|
||||||
|
SHA-256 RX: `c4257f26e4cc8562eb32228f9a50fdeb055879c928af0744cdf4a81cf7cd085b`.
|
||||||
|
SHA-256 TX: `5b34bbe1a8e6d037d2d6d367a74e9eb84e8940db7da61600ffaa7f7497d4f600`.
|
||||||
|
После проверки CLKOUT обеих плат выключены успешно, процессы CLEAR,
|
||||||
|
ошибок cleanup нет. Больше аппаратных действий не выполнялось.
|
||||||
|
|
||||||
|
## Доказательства и STOP
|
||||||
|
|
||||||
|
- [План и описание геометрии](../data/processed/lab056/rx_preflight_plan.json).
|
||||||
|
- [Журнал фактических команд с временем и выводом](../data/processed/lab056/rx_preflight_commands.json).
|
||||||
|
- [Результат RX-only preflight](../data/processed/lab056/rx_preflight_result.json).
|
||||||
|
- Исходный RX и исходный результат: `data/raw/lab056/preflight_rx.cs8`,
|
||||||
|
`preflight_result.json`; маркер попытки: `preflight_attempt.json`.
|
||||||
|
|
||||||
|
Код и TX waveform не менялись. Последние программные проверки относятся
|
||||||
|
к этой же версии: 44 целевых теста, quick_gate 867/30/13 PASS и pytest
|
||||||
|
867 passed. Полные тесты повторно не запускались для записи аппаратного
|
||||||
|
результата. Git diff --check PASS. Lab054 NOT_A, 0/16 сохранён.
|
||||||
|
|
||||||
|
Verifier проверил только сохранённые файлы: 12 команд, три RX-start,
|
||||||
|
ни одной TX-команды; регистры, CLKIN, cleanup и параметры подтверждены
|
||||||
|
по исходному выводу. Целочисленный пересчёт мощности/клиппинга и SHA
|
||||||
|
полностью согласован с JSON. Воспроизведённых расхождений нет, PASS.
|
||||||
|
Дополнительных аппаратных команд verifier не выполнял.
|
||||||
|
|
||||||
|
HEAD `5add95ecb8865f59075bbbced08d97b183fb3b48`. Lab056 не коммитилась,
|
||||||
|
push не выполнялся. Технические условия preflight выполнены; отдельного
|
||||||
|
разрешения на единственный ABAB TX ещё нет. **STOP.**
|
||||||
359
experiments/lab054_hardware_control.py
Normal file
359
experiments/lab054_hardware_control.py
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
"""Lab054: подготовка короткого BPSK-контроля; RF только отдельной командой."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
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 lab047_jpeg_object_transport as hardware
|
||||||
|
from experiments import lab049_bpsk_symbol_rate as phy
|
||||||
|
from experiments.lab053_packet_processing import bind
|
||||||
|
from experiments.lab053_streaming_receiver import StreamingReceiver
|
||||||
|
from tools.hackrf_process_check import check_processes, require_clear
|
||||||
|
|
||||||
|
clock = hardware.lab044.lab043
|
||||||
|
MODE = phy.PhyMode(500_000)
|
||||||
|
RF_HZ = 1_400_000_000
|
||||||
|
PACKETS = 16
|
||||||
|
RX_SECONDS = 3
|
||||||
|
RX_LEAD_SECONDS = 0.5
|
||||||
|
RX_SAMPLES = RX_SECONDS * MODE.sample_rate_hz
|
||||||
|
DIRECTORY = Path("data/raw/lab054")
|
||||||
|
OUTPUT = Path("data/processed/lab054")
|
||||||
|
|
||||||
|
|
||||||
|
def save(path: Path, value: dict) -> None:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def plans():
|
||||||
|
"""Обычный бинарный объект, 16 фрагментов прежнего формата; не видеопоток."""
|
||||||
|
random_generator = np.random.default_rng(54054)
|
||||||
|
data = random_generator.integers(0, 256, PACKETS * phy.lab046.FRAGMENT_DATA_SIZE, dtype=np.uint8)
|
||||||
|
return phy.lab046.build_object_frames(data.tobytes(), object_id=54054)
|
||||||
|
|
||||||
|
|
||||||
|
def parameters() -> dict:
|
||||||
|
return {"rf_hz": RF_HZ, "sample_rate_hz": MODE.sample_rate_hz,
|
||||||
|
"symbol_rate": MODE.symbol_rate, "samples_per_symbol": MODE.samples_per_symbol,
|
||||||
|
"carrier_hz": phy.lab045.CARRIER_OFFSET_HZ,
|
||||||
|
"master_tx": clock.MASTER_SERIAL, "slave_rx": clock.SLAVE_SERIAL,
|
||||||
|
"tx_vga_db": 10, "rx_lna_db": 24, "rx_vga_db": 24,
|
||||||
|
"amplifier": False, "bias_tee": False,
|
||||||
|
"threshold": hardware.FIXED_HARDWARE_CANDIDATE_THRESHOLD}
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_command(serial: str, path: Path | str, samples: int, *, transmit=False) -> list[str]:
|
||||||
|
command = [str(clock.HACKRF_TOOLS_DIR / "hackrf_transfer.exe"), "-d", serial,
|
||||||
|
"-t" if transmit else "-r", str(path), "-f", str(RF_HZ),
|
||||||
|
"-s", str(MODE.sample_rate_hz), "-n", str(samples)]
|
||||||
|
command += ["-x", "10"] if transmit else ["-l", "24", "-g", "24"]
|
||||||
|
return command + ["-a", "0", "-p", "0", "-B"]
|
||||||
|
|
||||||
|
|
||||||
|
def short_receive(serial, runner):
|
||||||
|
"""Штатный запуск приёмника для выбора опоры, без передающего режима."""
|
||||||
|
result = runner(transfer_command(serial, "NUL", clock.SHORT_RX_SAMPLE_COUNT))
|
||||||
|
if result.returncode:
|
||||||
|
raise RuntimeError(f"Короткий RX не выполнен: {result.stderr}")
|
||||||
|
|
||||||
|
|
||||||
|
def disable_clock_outputs(runner) -> list[str]:
|
||||||
|
"""Попытаться отключить обе платы даже при timeout первой команды."""
|
||||||
|
errors = []
|
||||||
|
for serial in (clock.MASTER_SERIAL, clock.SLAVE_SERIAL):
|
||||||
|
try:
|
||||||
|
clock._set_clkout(serial, False, runner)
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"{serial}: {error}")
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
clock_preflight = bind(clock.common_clock_preflight, _short_rx=short_receive,
|
||||||
|
_disable_clock_outputs=disable_clock_outputs)
|
||||||
|
|
||||||
|
|
||||||
|
def preflight_only(directory: Path = OUTPUT, runner=None, process_checker=None) -> dict:
|
||||||
|
"""Реальный RX-only preflight; выходы тактирования выключаются в finally."""
|
||||||
|
runner = runner or clock._default_command_runner
|
||||||
|
process_checker = process_checker or check_processes
|
||||||
|
record = {"rf_tx_count": 0, "passed": False}
|
||||||
|
guard = clock.TransmitGuard()
|
||||||
|
clock_started = False
|
||||||
|
try:
|
||||||
|
process_state = process_checker()
|
||||||
|
record["process_check"] = process_state.to_dict()
|
||||||
|
require_clear(process_state)
|
||||||
|
record["inventory"] = phy.lab045._verify_device_inventory(runner)
|
||||||
|
clock_started = True
|
||||||
|
record["clock"] = clock_preflight(runner, guard)
|
||||||
|
record["passed"] = True
|
||||||
|
except Exception as error:
|
||||||
|
record["error"] = str(error)
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
record["cleanup_errors"] = disable_clock_outputs(runner) if clock_started else []
|
||||||
|
record["cleanup_performed"] = clock_started
|
||||||
|
if clock_started:
|
||||||
|
try:
|
||||||
|
process_state = process_checker()
|
||||||
|
record["process_check_after_cleanup"] = process_state.to_dict()
|
||||||
|
require_clear(process_state)
|
||||||
|
except Exception as error:
|
||||||
|
record["cleanup_errors"].append(str(error))
|
||||||
|
record["passed"] = record["passed"] and not record["cleanup_errors"]
|
||||||
|
save(directory / "common_clock_preflight.json", record)
|
||||||
|
if not record["passed"]:
|
||||||
|
raise RuntimeError("Не удалось выключить CLKOUT")
|
||||||
|
return record
|
||||||
|
|
||||||
|
|
||||||
|
def prepare(directory: Path = DIRECTORY) -> dict:
|
||||||
|
directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
path = directory / "tx_stream.cs8"
|
||||||
|
if path.exists():
|
||||||
|
raise FileExistsError("TX-файл уже существует; подготовка его не перезаписывает")
|
||||||
|
with path.open("xb") as stream:
|
||||||
|
for frame in plans():
|
||||||
|
stream.write(phy.build_parameterized_tx_cs8(frame.plan, MODE))
|
||||||
|
count = path.stat().st_size // 2
|
||||||
|
result = {"stage": "PREPARATION_ONLY", "rf_tx_count": 0,
|
||||||
|
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
|
||||||
|
"parameters": parameters(), "packets": PACKETS, "tx_samples": count,
|
||||||
|
"tx_bytes": path.stat().st_size, "tx_seconds": count / MODE.sample_rate_hz,
|
||||||
|
"tx_sha256": hardware._sha256_file(path), "rx_seconds": RX_SECONDS,
|
||||||
|
"rx_samples": RX_SAMPLES, "rx_lead_seconds": RX_LEAD_SECONDS,
|
||||||
|
"rx_command": transfer_command(clock.SLAVE_SERIAL, directory / "rx_stream.cs8", RX_SAMPLES),
|
||||||
|
"tx_command": transfer_command(clock.MASTER_SERIAL, path, count, transmit=True)}
|
||||||
|
save(directory / "tx_metadata.json", result)
|
||||||
|
save(OUTPUT / "preparation.json", result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def validate_prepared(directory: Path) -> dict:
|
||||||
|
metadata = json.loads((directory / "tx_metadata.json").read_text(encoding="utf-8"))
|
||||||
|
path = directory / "tx_stream.cs8"
|
||||||
|
expected = hashlib.sha256()
|
||||||
|
count = 0
|
||||||
|
for frame in plans():
|
||||||
|
waveform = phy.build_parameterized_tx_cs8(frame.plan, MODE)
|
||||||
|
expected.update(waveform)
|
||||||
|
count += len(waveform)
|
||||||
|
if (metadata["parameters"] != parameters() or metadata["stage"] != "PREPARATION_ONLY"
|
||||||
|
or metadata["rf_tx_count"] != 0 or metadata["packets"] != PACKETS
|
||||||
|
or metadata["tx_samples"] * 2 != count or path.stat().st_size != count
|
||||||
|
or metadata["tx_sha256"] != expected.hexdigest()
|
||||||
|
or hardware._sha256_file(path) != expected.hexdigest()):
|
||||||
|
raise RuntimeError("TX-артефакты или параметры изменены")
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def decode_file(path: Path) -> tuple[dict, list]:
|
||||||
|
"""RX получает только CS8-файл; TX truth отсутствует в интерфейсе."""
|
||||||
|
observations = []
|
||||||
|
receiver = StreamingReceiver(packet_observer=lambda row, outcome: observations.append((row.copy(), outcome)))
|
||||||
|
received = []
|
||||||
|
clipping = 0
|
||||||
|
try:
|
||||||
|
with path.open("rb") as stream:
|
||||||
|
while block := stream.read(2 * 65536):
|
||||||
|
values = np.frombuffer(block, np.int8)
|
||||||
|
if len(values) % 2:
|
||||||
|
raise ValueError("Неполный IQ-отсчёт")
|
||||||
|
clipping += int(np.count_nonzero((values == -128) | (values == 127)))
|
||||||
|
samples = (values[::2].astype(np.float32) + 1j * values[1::2]).astype(np.complex64)
|
||||||
|
received.extend(receiver.push_iq_block(samples))
|
||||||
|
received.extend(receiver.finish())
|
||||||
|
finally:
|
||||||
|
if not receiver.closed:
|
||||||
|
receiver.finish()
|
||||||
|
rows = []
|
||||||
|
for row, outcome in observations:
|
||||||
|
frame = outcome.frame
|
||||||
|
rows.append({"accepted": outcome.accepted, "reason": outcome.reason,
|
||||||
|
"sync_pass": outcome.refinement_pass and outcome.timing_pass and outcome.carrier_pass,
|
||||||
|
"phy_header_pass": outcome.phy_header_pass, "fec_pass": outcome.fec_pass,
|
||||||
|
"crc": outcome.crc, "start": row["absolute_refined"],
|
||||||
|
"timing": frame.timing_offset_samples if frame else None,
|
||||||
|
"cfo_hz": frame.estimated_cfo_hz if frame else None,
|
||||||
|
"phase_rad": frame.estimated_phase_rad if frame else None,
|
||||||
|
"snr_db": frame.snr_db if frame else None,
|
||||||
|
"preamble_errors": frame.preamble_errors if frame else None})
|
||||||
|
return {"independent": True, "packets": len(received),
|
||||||
|
"frames_detected": len({r["start"] for r in rows if r["sync_pass"]}),
|
||||||
|
"frames_detected_definition": "Unique refined starts with refinement/timing/carrier PASS; may include false candidates",
|
||||||
|
"strict_candidates": len(rows), "phy_header_pass": sum(r["phy_header_pass"] for r in rows),
|
||||||
|
"fec_pass": sum(r["fec_pass"] for r in rows),
|
||||||
|
"crc_pass": sum(r["crc"] == phy.lab045.CRC_PASS for r in rows),
|
||||||
|
"crc_fail": sum(r["crc"] == phy.lab045.CRC_FAIL for r in rows),
|
||||||
|
"clipping_components": clipping, "rows": rows}, observations
|
||||||
|
|
||||||
|
|
||||||
|
def roundtrip(directory: Path = DIRECTORY) -> dict:
|
||||||
|
validate_prepared(directory)
|
||||||
|
result, observations = decode_file(directory / "tx_stream.cs8")
|
||||||
|
result.update(compare_after_verdict(observations))
|
||||||
|
result["passed"] = (result["packets"] == result["exact_packets"] == result["unique_exact_packets"] == PACKETS
|
||||||
|
and result["post_fec_errors"] == 0)
|
||||||
|
save(OUTPUT / "software_roundtrip.json", result)
|
||||||
|
if not result["passed"]:
|
||||||
|
raise RuntimeError("Программный roundtrip FAIL")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def compare_after_verdict(observations) -> dict:
|
||||||
|
"""Сверка разрешена только после завершения независимого приёма."""
|
||||||
|
by_payload = {frame.plan.payload: frame.plan for frame in plans()}
|
||||||
|
comparisons = []
|
||||||
|
matched_payloads = set()
|
||||||
|
exact_count = 0
|
||||||
|
for _, outcome in observations:
|
||||||
|
if outcome.accepted and outcome.frame:
|
||||||
|
frame = outcome.frame
|
||||||
|
plan = by_payload.get(frame.reception.payload_bytes)
|
||||||
|
if plan is not None:
|
||||||
|
if frame.reception.packet_bytes == plan.packet:
|
||||||
|
matched_payloads.add(frame.reception.payload_bytes)
|
||||||
|
exact_count += 1
|
||||||
|
comparisons.append(phy.lab045.diagnostic_pre_and_post_fec(
|
||||||
|
frame.reception, frame.post_preamble_soft, plan))
|
||||||
|
return {"exact_packets": exact_count, "unique_exact_packets": len(matched_payloads),
|
||||||
|
"payload_matched_packets": len(comparisons),
|
||||||
|
"pre_fec_errors": sum(item["pre_fec_coded"]["bit_errors"] for item in comparisons) if comparisons else None,
|
||||||
|
"post_fec_errors": sum(item["post_fec_packet"]["bit_errors"] for item in comparisons) if comparisons else None,
|
||||||
|
"error_scope": "CRC accepted packets matched by payload AFTER independent verdict; unmatched packets UNKNOWN"}
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_capture(directory: Path = DIRECTORY) -> dict:
|
||||||
|
"""Офлайн-анализ сохранённой записи, без повторной передачи."""
|
||||||
|
evidence = json.loads((directory / "capture_result.json").read_text(encoding="utf-8"))
|
||||||
|
path = directory / "rx_stream.cs8"
|
||||||
|
if evidence.get("rx_sha256") != hardware._sha256_file(path):
|
||||||
|
raise RuntimeError("Нет подтверждённого SHA записи")
|
||||||
|
result, observations = decode_file(path)
|
||||||
|
save(OUTPUT / "independent_result.json", result)
|
||||||
|
result.update(compare_after_verdict(observations))
|
||||||
|
result.update(frames_tx=PACKETS, rx_overruns="UNKNOWN_UNLESS_EXPLICIT_IN_LOG",
|
||||||
|
tx_underruns="UNKNOWN_UNLESS_EXPLICIT_IN_LOG")
|
||||||
|
result["result"] = "A" if (result["unique_exact_packets"] >= 15 and evidence["passed"]
|
||||||
|
and result["post_fec_errors"] == 0
|
||||||
|
and result["clipping_components"] == 0) else "NOT_A"
|
||||||
|
result["scope"] = "One short control only; no long-term stability or video RF claim"
|
||||||
|
save(OUTPUT / "hardware_result.json", result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def capture_once(directory: Path = DIRECTORY, *, authorization=False, runner=None,
|
||||||
|
process_starter=None, process_checker=None, sleep_function=time.sleep) -> dict:
|
||||||
|
"""Один конечный TX; эксклюзивный маркер запрещает повтор после любой ошибки."""
|
||||||
|
if authorization is not True:
|
||||||
|
raise PermissionError("Нужно отдельное разрешение пользователя на Lab054 RF")
|
||||||
|
metadata = validate_prepared(directory)
|
||||||
|
runner = runner or clock._default_command_runner
|
||||||
|
process_starter = process_starter or clock._default_process_starter
|
||||||
|
process_checker = process_checker or check_processes
|
||||||
|
with (directory / "hardware_attempt.json").open("x", encoding="utf-8") as stream:
|
||||||
|
json.dump({"authorized_once": True, "automatic_retry": False}, stream)
|
||||||
|
guard = clock.TransmitGuard()
|
||||||
|
process = None
|
||||||
|
clock_started = False
|
||||||
|
result = {"passed": False, "rf_tx_count": 0}
|
||||||
|
try:
|
||||||
|
process_state = process_checker()
|
||||||
|
result["process_check"] = process_state.to_dict()
|
||||||
|
require_clear(process_state)
|
||||||
|
result["inventory"] = phy.lab045._verify_device_inventory(runner)
|
||||||
|
clock_started = True
|
||||||
|
result["clock"] = clock_preflight(runner, guard)
|
||||||
|
receive_path = directory / "rx_stream.cs8"
|
||||||
|
process = process_starter(transfer_command(clock.SLAVE_SERIAL, receive_path, RX_SAMPLES),
|
||||||
|
directory / "rx_stdout.txt", directory / "rx_stderr.txt")
|
||||||
|
sleep_function(RX_LEAD_SECONDS)
|
||||||
|
if process.poll() is not None:
|
||||||
|
raise RuntimeError("RX завершился до TX")
|
||||||
|
guard.register_transmit()
|
||||||
|
result["rf_tx_count"] = guard.transmit_calls
|
||||||
|
transmitted = runner(transfer_command(clock.MASTER_SERIAL, directory / "tx_stream.cs8",
|
||||||
|
metadata["tx_samples"], transmit=True))
|
||||||
|
(directory / "tx_stdout.txt").write_text(transmitted.stdout or "", encoding="utf-8")
|
||||||
|
(directory / "tx_stderr.txt").write_text(transmitted.stderr or "", encoding="utf-8")
|
||||||
|
if transmitted.returncode or process.wait(RX_SECONDS + 10):
|
||||||
|
raise RuntimeError("Ошибка единственного TX/RX; повтор запрещён")
|
||||||
|
process.close_logs()
|
||||||
|
if receive_path.stat().st_size != RX_SAMPLES * 2:
|
||||||
|
raise RuntimeError("Неверный размер RX")
|
||||||
|
for role in ("rx", "tx"):
|
||||||
|
log = "".join((directory / f"{role}_{kind}.txt").read_text(encoding="utf-8") for kind in ("stdout", "stderr"))
|
||||||
|
if not clock._transfer_log_is_clean(log):
|
||||||
|
raise RuntimeError(f"{role}: overrun/underrun; STOP")
|
||||||
|
result.update(passed=True, rx_sha256=hardware._sha256_file(receive_path))
|
||||||
|
except Exception as error:
|
||||||
|
result["error"] = str(error)
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
result["cleanup_errors"] = []
|
||||||
|
if process is not None:
|
||||||
|
try:
|
||||||
|
process.terminate()
|
||||||
|
except Exception as error:
|
||||||
|
result["cleanup_errors"].append(str(error))
|
||||||
|
if clock_started:
|
||||||
|
result["cleanup_errors"].extend(disable_clock_outputs(runner))
|
||||||
|
try:
|
||||||
|
process_state = process_checker()
|
||||||
|
result["process_check_after_cleanup"] = process_state.to_dict()
|
||||||
|
require_clear(process_state)
|
||||||
|
result["lingering_processes"] = []
|
||||||
|
except Exception as error:
|
||||||
|
result["cleanup_errors"].append(str(error))
|
||||||
|
result["lingering_processes"] = None
|
||||||
|
result["passed"] = result["passed"] and not result["cleanup_errors"] and not result["lingering_processes"]
|
||||||
|
save(directory / "capture_result.json", result)
|
||||||
|
if not result["passed"]:
|
||||||
|
raise RuntimeError("Очистка аппаратного сеанса не прошла; STOP")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def run_functional_tests() -> None:
|
||||||
|
assert MODE.samples_per_symbol == 16
|
||||||
|
assert len(plans()) == PACKETS
|
||||||
|
assert "-t" not in transfer_command(clock.MASTER_SERIAL, "NUL", 65536)
|
||||||
|
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", "roundtrip", "preflight", "capture", "analyze"), required=True)
|
||||||
|
parser.add_argument("--authorize-one-rf", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.stage == "prepare":
|
||||||
|
result = prepare()
|
||||||
|
elif args.stage == "roundtrip":
|
||||||
|
result = roundtrip()
|
||||||
|
elif args.stage == "preflight":
|
||||||
|
result = preflight_only()
|
||||||
|
elif args.stage == "analyze":
|
||||||
|
result = analyze_capture()
|
||||||
|
else:
|
||||||
|
result = capture_once(authorization=args.authorize_one_rf)
|
||||||
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
312
experiments/lab056_abab_energy.py
Normal file
312
experiments/lab056_abab_energy.py
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
"""Lab056: программная подготовка ABAB и диагностика сохранённого RX."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
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 lab043_pluto_to_rtlsdr as shaping
|
||||||
|
from experiments import lab055_energy_audit as energy
|
||||||
|
|
||||||
|
SAMPLE_RATE = 8_000_000
|
||||||
|
CARRIER = 50_000
|
||||||
|
GAP_SAMPLES = 800_000
|
||||||
|
RX_SAMPLES = 24_000_000
|
||||||
|
RX_LEAD_SECONDS = .5
|
||||||
|
RAW_DIRECTORY = energy.PROJECT_ROOT / "data/raw/lab056"
|
||||||
|
OUTPUT = energy.PROJECT_ROOT / "data/processed/lab056"
|
||||||
|
|
||||||
|
|
||||||
|
def save_new(path: Path, value: dict) -> None:
|
||||||
|
text = json.dumps(value, ensure_ascii=False, indent=2, allow_nan=False) + "\n"
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with path.open("x", encoding="utf-8") as stream:
|
||||||
|
stream.write(text)
|
||||||
|
|
||||||
|
|
||||||
|
def build_burst(symbol_rate: int) -> np.ndarray:
|
||||||
|
"""Прежние PRBS7/BPSK/RRC, 204,8 мс символов, RMS всего burst=44."""
|
||||||
|
if symbol_rate not in (20000, 500000):
|
||||||
|
raise ValueError("Разрешены только два исследуемых режима")
|
||||||
|
symbols = 2. * shaping.pilot_prbs7(4096 * (symbol_rate // 20000)) - 1.
|
||||||
|
samples_per_symbol = SAMPLE_RATE // symbol_rate
|
||||||
|
waveform = shaping.shape_frame_like_lab042(symbols, samples_per_symbol)
|
||||||
|
waveform *= np.exp(2j * np.pi * CARRIER * np.arange(len(waveform)) / SAMPLE_RATE)
|
||||||
|
waveform *= 44 / np.sqrt(np.mean(abs(waveform)**2))
|
||||||
|
values = np.empty(2 * len(waveform), dtype=np.int8)
|
||||||
|
values[::2] = np.clip(np.rint(waveform.real), -128, 127).astype(np.int8)
|
||||||
|
values[1::2] = np.clip(np.rint(waveform.imag), -128, 127).astype(np.int8)
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def prepare(directory: Path = RAW_DIRECTORY) -> dict:
|
||||||
|
"""Создать единственный конечный TX-файл; аппаратная частота не назначена."""
|
||||||
|
directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
tx_path, metadata_path = directory / "tx_abab.cs8", directory / "tx_metadata.json"
|
||||||
|
if tx_path.exists() or metadata_path.exists():
|
||||||
|
raise FileExistsError("Артефакты Lab056 уже существуют")
|
||||||
|
bursts = {rate: build_burst(rate) for rate in (20000, 500000)}
|
||||||
|
segments = []
|
||||||
|
cursor = GAP_SAMPLES
|
||||||
|
with tx_path.open("xb") as stream:
|
||||||
|
stream.write(bytes(2 * GAP_SAMPLES))
|
||||||
|
for label, rate in (("A1", 20000), ("B1", 500000), ("A2", 20000), ("B2", 500000)):
|
||||||
|
values = bursts[rate]
|
||||||
|
count = len(values) // 2
|
||||||
|
complex_values = values[::2].astype(np.float32) + 1j * values[1::2].astype(np.float32)
|
||||||
|
whole, active = energy.Moments(), energy.Moments()
|
||||||
|
whole.add(complex_values)
|
||||||
|
trim = 21 * (SAMPLE_RATE // rate)
|
||||||
|
active.add(complex_values[trim:-trim])
|
||||||
|
segments.append({"label": label, "symbol_rate": rate, "start": cursor,
|
||||||
|
"samples": count, "active_start": cursor + trim, "active_samples": count - 2 * trim,
|
||||||
|
"whole": whole.result(rate), "active": active.result(rate)})
|
||||||
|
stream.write(values.tobytes())
|
||||||
|
stream.write(bytes(2 * GAP_SAMPLES))
|
||||||
|
cursor += count + GAP_SAMPLES
|
||||||
|
result = {"stage": "PREPARATION_ONLY", "rf_tx": 0, "rf_frequency_hz": None,
|
||||||
|
"physical_path": "OTA_REQUESTED_FREQUENCY_AND_GEOMETRY_PENDING", "sample_rate_hz": SAMPLE_RATE,
|
||||||
|
"carrier_hz": CARRIER, "tx_samples": cursor, "tx_bytes": 2 * cursor,
|
||||||
|
"tx_seconds": cursor / SAMPLE_RATE, "rx_samples": RX_SAMPLES,
|
||||||
|
"rx_seconds": RX_SAMPLES / SAMPLE_RATE, "rx_lead_seconds": RX_LEAD_SECONDS,
|
||||||
|
"gap_seconds": GAP_SAMPLES / SAMPLE_RATE, "gap_is_zero_digital_iq_not_rf_off": True,
|
||||||
|
"tx_sha256": energy.sha256(tx_path), "segments": segments,
|
||||||
|
"whole_power_difference_db": float(10 * np.log10(segments[1]["whole"]["power_code2"] / segments[0]["whole"]["power_code2"])),
|
||||||
|
"active_power_difference_db": float(10 * np.log10(segments[1]["active"]["power_code2"] / segments[0]["active"]["power_code2"]))}
|
||||||
|
if abs(result["whole_power_difference_db"]) > .01 or any(s["whole"]["clipped_samples_fraction"] for s in segments):
|
||||||
|
raise RuntimeError("Цифровая подготовка не прошла проверку мощности/клиппинга")
|
||||||
|
save_new(metadata_path, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def locate_sequence(tx: energy.Cs8File, rx: energy.Cs8File) -> dict:
|
||||||
|
"""Диагностическое согласование полной ABAB; TX reference разрешён.
|
||||||
|
|
||||||
|
Грубая сетка 32 отсчёта использует всю последовательность, затем начало
|
||||||
|
каждого burst уточняется прежним методом Lab055 на обучающей плитке.
|
||||||
|
Прореживание без антиалиасинга здесь только для корреляционного поиска,
|
||||||
|
а не для измерений мощности или спектра.
|
||||||
|
"""
|
||||||
|
stride = 32
|
||||||
|
def decimated(source):
|
||||||
|
chunks = [source.read(start, min(262144, source.size - start))[::stride]
|
||||||
|
for start in range(0, source.size, 262144)]
|
||||||
|
return np.concatenate(chunks).astype(np.complex128)
|
||||||
|
reference, received = decimated(tx), decimated(rx)
|
||||||
|
if len(received) < len(reference):
|
||||||
|
raise ValueError("RX короче TX")
|
||||||
|
reference -= reference.mean()
|
||||||
|
received -= received.mean()
|
||||||
|
cumulative = np.concatenate(([0.], np.cumsum(abs(received)**2)))
|
||||||
|
window_energy = cumulative[len(reference):] - cumulative[:-len(reference)]
|
||||||
|
reference_energy = np.sum(abs(reference)**2)
|
||||||
|
time = np.arange(len(reference)) * stride / SAMPLE_RATE
|
||||||
|
best = (-1., 0, 0.)
|
||||||
|
for frequency in np.arange(-2., 2.01, .25):
|
||||||
|
rotated = reference * np.exp(2j * np.pi * frequency * time)
|
||||||
|
correlation = signal.fftconvolve(received, np.conj(rotated[::-1]), mode="valid")
|
||||||
|
scores = 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))
|
||||||
|
# Это качество диагностического совпадения, не критерий PHY/CRC.
|
||||||
|
if best[0] < .05:
|
||||||
|
raise ValueError("ABAB не локализована достаточно уверенно; измерение UNKNOWN")
|
||||||
|
return {"start": best[1], "correlation": best[0], "coarse_cfo_hz": best[2], "stride": stride}
|
||||||
|
|
||||||
|
|
||||||
|
class OffsetSource:
|
||||||
|
def __init__(self, source: energy.Cs8File, offset: int):
|
||||||
|
self.source, self.offset = source, offset
|
||||||
|
|
||||||
|
def read(self, start: int, count: int) -> np.ndarray:
|
||||||
|
return self.source.read(self.offset + start, count)
|
||||||
|
|
||||||
|
|
||||||
|
def window_power(values: np.ndarray, symbol_rate: int) -> dict:
|
||||||
|
moments = energy.Moments()
|
||||||
|
moments.add(values)
|
||||||
|
result = moments.result(symbol_rate)
|
||||||
|
centered = values - values.mean()
|
||||||
|
frequencies, density = energy.spectrum(centered)
|
||||||
|
for width in (27000, 675000):
|
||||||
|
result[f"band_{width}_power"] = energy.band_power(frequencies, density, CARRIER - width / 2, CARRIER + width / 2)
|
||||||
|
size = 32000 # Неперекрывающиеся блоки по 4 мс для оценки разброса мощности.
|
||||||
|
powers = [float(np.mean(abs(centered[start:start + size])**2)) for start in range(0, len(centered) - size + 1, size)]
|
||||||
|
result["power_standard_error"] = float(np.std(powers, ddof=1) / np.sqrt(len(powers))) if len(powers) > 1 else None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def difference_db(new: float | None, old: float | None) -> float | None:
|
||||||
|
if new is None or old is None or new <= 0 or old <= 0:
|
||||||
|
return None
|
||||||
|
return float(10 * np.log10(new / old))
|
||||||
|
|
||||||
|
|
||||||
|
def complex_gain(reference: np.ndarray, received: np.ndarray, cfo_hz: float) -> dict:
|
||||||
|
"""Вывести комплексный h прежней модели; те же чётные обучающие плитки."""
|
||||||
|
boundaries = np.linspace(0, len(reference), 9, dtype=int)
|
||||||
|
train = np.zeros(len(reference), dtype=bool)
|
||||||
|
for tile in range(0, 8, 2):
|
||||||
|
train[boundaries[tile]:boundaries[tile + 1]] = True
|
||||||
|
rotated = reference * np.exp(2j * np.pi * cfo_hz * np.arange(len(reference)) / SAMPLE_RATE)
|
||||||
|
x, y = rotated[train], received[train].astype(np.complex128)
|
||||||
|
x, y = x - x.mean(), y - y.mean()
|
||||||
|
gain = np.vdot(x, y) / max(float(np.sum(abs(x)**2)), 1e-30)
|
||||||
|
return {"real": float(gain.real), "imag": float(gain.imag),
|
||||||
|
"phase_rad_at_active_start": float(np.angle(gain))}
|
||||||
|
|
||||||
|
|
||||||
|
def comparison_summary(rows: list[dict]) -> dict:
|
||||||
|
"""Сравнить повторы отдельно; X/Z используют средние линейные мощности."""
|
||||||
|
by_label = {row["label"]: row for row in rows}
|
||||||
|
repeatability = []
|
||||||
|
for mode in ("A", "B"):
|
||||||
|
first, second = by_label[mode + "1"], by_label[mode + "2"]
|
||||||
|
repeatability.append({"comparison": mode + "2/" + mode + "1",
|
||||||
|
"raw_rx_power_difference_db": difference_db(second["raw_rx"]["power_code2"], first["raw_rx"]["power_code2"]),
|
||||||
|
"background_difference_db": difference_db(second["background_power"], first["background_power"]),
|
||||||
|
"received_power_difference_db": difference_db(second["background_subtracted_signal_power"], first["background_subtracted_signal_power"]),
|
||||||
|
"channel_gain_difference_db": difference_db(second["channel_gain_power"], first["channel_gain_power"]),
|
||||||
|
"matched_component_difference_db": difference_db(second["measurement"]["heldout_signal_power"], first["measurement"]["heldout_signal_power"])})
|
||||||
|
def mean(mode, key):
|
||||||
|
values = [by_label[mode + str(number)][key] for number in (1, 2)]
|
||||||
|
return None if any(value is None or value <= 0 for value in values) else float(np.mean(values))
|
||||||
|
a_power, b_power = mean("A", "background_subtracted_signal_power"), mean("B", "background_subtracted_signal_power")
|
||||||
|
return {"repeatability": repeatability,
|
||||||
|
"averaging": "RATIO_OF_MEAN_LINEAR_POWERS_B1_B2_OVER_A1_A2",
|
||||||
|
"received_power_difference_db": difference_db(b_power, a_power),
|
||||||
|
"channel_gain_difference_db": difference_db(mean("B", "channel_gain_power"), mean("A", "channel_gain_power")),
|
||||||
|
"energy_per_symbol_difference_db": difference_db(None if b_power is None else b_power / 500000,
|
||||||
|
None if a_power is None else a_power / 20000),
|
||||||
|
"energy_basis": "BACKGROUND_SUBTRACTED_RECEIVED_POWER_DIVIDED_BY_SYMBOL_RATE"}
|
||||||
|
|
||||||
|
|
||||||
|
def analyze(directory: Path, rx_path: Path) -> dict:
|
||||||
|
metadata = energy.read_json(directory / "tx_metadata.json")
|
||||||
|
tx_path = directory / "tx_abab.cs8"
|
||||||
|
if energy.sha256(tx_path) != metadata["tx_sha256"]:
|
||||||
|
raise ValueError("TX SHA не совпал")
|
||||||
|
tx, rx = energy.Cs8File(tx_path), energy.Cs8File(rx_path)
|
||||||
|
alignment = locate_sequence(tx, rx)
|
||||||
|
rows = []
|
||||||
|
for segment in metadata["segments"]:
|
||||||
|
rate, count = segment["symbol_rate"], segment["samples"]
|
||||||
|
start = alignment["start"] + segment["start"]
|
||||||
|
measurement = energy.analyze_rx(OffsetSource(tx, segment["start"]), rx, [start], count,
|
||||||
|
SAMPLE_RATE // rate)["frames"][0]
|
||||||
|
start = measurement["start"]
|
||||||
|
trim = 21 * (SAMPLE_RATE // rate)
|
||||||
|
received_active = rx.read(start + trim, count - 2 * trim)
|
||||||
|
raw = window_power(received_active, rate)
|
||||||
|
gain = complex_gain(tx.read(segment["start"] + trim, count - 2 * trim), received_active, measurement["cfo_hz"])
|
||||||
|
if not np.isclose(gain["real"]**2 + gain["imag"]**2, measurement["gain_abs"]**2, rtol=1e-10, atol=1e-15):
|
||||||
|
raise ValueError("Вывод комплексного h расходится с прежней моделью Lab055")
|
||||||
|
windows = [window_power(rx.read(begin, 480000), rate) for begin in
|
||||||
|
(start - 640000, start + count + 160000)]
|
||||||
|
background = float(np.mean([w["ac_power_code2"] for w in windows]))
|
||||||
|
excess = raw["ac_power_code2"] - background
|
||||||
|
uncertainty = float(np.sqrt(raw["power_standard_error"]**2 +
|
||||||
|
sum(w["power_standard_error"]**2 for w in windows) / 4 +
|
||||||
|
((windows[0]["ac_power_code2"] - windows[1]["ac_power_code2"]) / 2)**2))
|
||||||
|
stable = excess > 3 * uncertainty
|
||||||
|
rows.append({"label": segment["label"], "symbol_rate": rate, "raw_rx": raw,
|
||||||
|
"local_zero_iq_background_windows": windows, "background_power": background,
|
||||||
|
"background_subtracted_raw": excess, "background_subtracted_uncertainty": uncertainty,
|
||||||
|
"background_subtracted_stable": stable, "background_subtracted_signal_power": excess if stable else None,
|
||||||
|
"channel_gain_complex": gain, "channel_gain_power": measurement["gain_abs"]**2, "measurement": measurement})
|
||||||
|
by_label = {r["label"]: r for r in rows}
|
||||||
|
pairs = []
|
||||||
|
for number in (1, 2):
|
||||||
|
a, b = by_label[f"A{number}"], by_label[f"B{number}"]
|
||||||
|
pairs.append({"pair": number,
|
||||||
|
"received_power_difference_db": difference_db(b["background_subtracted_signal_power"], a["background_subtracted_signal_power"]),
|
||||||
|
"channel_gain_difference_db": difference_db(b["channel_gain_power"], a["channel_gain_power"]),
|
||||||
|
"matched_component_difference_db": difference_db(b["measurement"]["heldout_signal_power"], a["measurement"]["heldout_signal_power"]),
|
||||||
|
"matched_component_symbol_energy_difference_db": difference_db(b["measurement"]["heldout_signal_power"] / b["symbol_rate"], a["measurement"]["heldout_signal_power"] / a["symbol_rate"])})
|
||||||
|
# До начала и после конца всего цифрового файла: отдельные внешние окна.
|
||||||
|
outside = []
|
||||||
|
for begin in (alignment["start"] - 800000, alignment["start"] + tx.size + 400000):
|
||||||
|
if 0 <= begin and begin + 400000 <= rx.size:
|
||||||
|
outside.append({"start": begin, **window_power(rx.read(begin, 400000), 20000)})
|
||||||
|
return {"scope": "TX_REFERENCE_DIAGNOSTICS_NOT_INDEPENDENT_PACKET_RX", "rf_tx": 0,
|
||||||
|
"tx_sha256": metadata["tx_sha256"], "rx_sha256": energy.sha256(rx_path),
|
||||||
|
"alignment": alignment, "segments": rows, "pairs": pairs, "comparison_summary": comparison_summary(rows),
|
||||||
|
"outside_waveform_background": outside,
|
||||||
|
"background_note": "Внутренние gaps: нулевой цифровой IQ при работающем TX; это не RF OFF. Внешние окна также требуют сверки времён запуска/остановки по журналам.",
|
||||||
|
"power_units": "RX code squared, not calibrated watts/dBm",
|
||||||
|
"residual_note": "Шум и неописанные моделью искажения; RX не нормируется для выравнивания A/B"}
|
||||||
|
|
||||||
|
|
||||||
|
def software_capture(directory: Path, b_gain: float, name: str) -> dict:
|
||||||
|
"""Синтетический CS8 с известным усилением, DC и CFO; не аппаратная запись."""
|
||||||
|
metadata = energy.read_json(directory / "tx_metadata.json")
|
||||||
|
tx = energy.Cs8File(directory / "tx_abab.cs8")
|
||||||
|
path = directory / f"synthetic_{name}.cs8"
|
||||||
|
random_generator = np.random.default_rng(56056)
|
||||||
|
lead = 4_160_013
|
||||||
|
with path.open("xb") as stream:
|
||||||
|
for low in range(0, RX_SAMPLES, 262144):
|
||||||
|
count = min(262144, RX_SAMPLES - low)
|
||||||
|
values = (1 + 2j + .6 * (random_generator.normal(size=count) + 1j * random_generator.normal(size=count)))
|
||||||
|
first, last = max(low, lead), min(low + count, lead + tx.size)
|
||||||
|
if first < last:
|
||||||
|
source = tx.read(first - lead, last - first)
|
||||||
|
gains = np.full(len(source), .03)
|
||||||
|
indexes = np.arange(first - lead, last - lead)
|
||||||
|
for segment in metadata["segments"]:
|
||||||
|
if segment["label"].startswith("B"):
|
||||||
|
gains[(indexes >= segment["start"]) & (indexes < segment["start"] + segment["samples"])] = b_gain
|
||||||
|
values[first - low:last - low] += source * gains * np.exp(1j * (.7 + 2 * np.pi * .2 * np.arange(first, last) / SAMPLE_RATE))
|
||||||
|
packed = np.empty(2 * count, dtype=np.int8)
|
||||||
|
packed[::2] = np.clip(np.rint(values.real), -128, 127)
|
||||||
|
packed[1::2] = np.clip(np.rint(values.imag), -128, 127)
|
||||||
|
stream.write(packed.tobytes())
|
||||||
|
result = analyze(directory, path)
|
||||||
|
expected = 20 * np.log10(b_gain / .03)
|
||||||
|
errors = [abs(p["channel_gain_difference_db"] - expected) for p in result["pairs"]]
|
||||||
|
result.update(synthetic=True, expected_gain_difference_db=float(expected),
|
||||||
|
known_sequence_start=lead, passed=bool(max(errors) < .3))
|
||||||
|
if not result["passed"]:
|
||||||
|
raise RuntimeError(f"Синтетическая проверка не прошла: {errors}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def run_functional_tests() -> None:
|
||||||
|
assert difference_db(.25, 1) == 10 * np.log10(.25)
|
||||||
|
assert difference_db(None, 1) is None
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("stage", choices=("prepare", "software", "analyze"))
|
||||||
|
parser.add_argument("--rx", type=Path)
|
||||||
|
parser.add_argument("--output", type=Path)
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.stage == "prepare":
|
||||||
|
result = prepare()
|
||||||
|
save_new(OUTPUT / "preparation.json", result)
|
||||||
|
elif args.stage == "software":
|
||||||
|
for name, gain in (("equal", .03), ("minus6db", .015)):
|
||||||
|
result = software_capture(RAW_DIRECTORY, gain, name)
|
||||||
|
save_new(OUTPUT / f"software_{name}.json", result)
|
||||||
|
print(name, result["pairs"], flush=True)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if args.rx is None or args.output is None:
|
||||||
|
parser.error("Для сохранённого RX нужны --rx и новый --output")
|
||||||
|
result = analyze(RAW_DIRECTORY, args.rx)
|
||||||
|
save_new(args.output, result)
|
||||||
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
263
experiments/lab056_capture_runner.py
Normal file
263
experiments/lab056_capture_runner.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
"""Lab056: конечный RX-first runner; этот модуль не имеет аппаратного CLI."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict, dataclass
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
from experiments import lab054_hardware_control as previous
|
||||||
|
from experiments import lab056_abab_energy as waveform
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class OperatingPoint:
|
||||||
|
"""Частота пользователя и описание неподвижного эфирного тракта."""
|
||||||
|
|
||||||
|
frequency_hz: int | None = None
|
||||||
|
physical_path: str = "MASTER TX -> TX antenna -> free space -> RX antenna -> SLAVE RX"
|
||||||
|
path_confirmed: bool = False
|
||||||
|
tx_antenna: str = "UNKNOWN"
|
||||||
|
rx_antenna: str = "UNKNOWN"
|
||||||
|
distance_m: float | None = None
|
||||||
|
orientation: str = "UNKNOWN"
|
||||||
|
visibility: str = "UNKNOWN"
|
||||||
|
|
||||||
|
def validate(self) -> None:
|
||||||
|
# Допустимую лабораторную частоту определяет пользователь, не этот модуль.
|
||||||
|
if type(self.frequency_hz) is not int or self.frequency_hz <= 0:
|
||||||
|
raise PermissionError("Нужно конкретное целое RF_FREQ в Гц от пользователя")
|
||||||
|
|
||||||
|
|
||||||
|
def command(point: OperatingPoint, path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
|
||||||
|
point.validate()
|
||||||
|
if not isinstance(samples, int) or samples <= 0:
|
||||||
|
raise ValueError("Нужен положительный конечный счётчик отсчётов")
|
||||||
|
clock = previous.clock
|
||||||
|
result = [str(clock.HACKRF_TOOLS_DIR / "hackrf_transfer.exe"), "-d",
|
||||||
|
clock.MASTER_SERIAL if transmit else clock.SLAVE_SERIAL, "-t" if transmit else "-r",
|
||||||
|
str(path), "-f", str(point.frequency_hz), "-s", str(waveform.SAMPLE_RATE), "-n", str(samples)]
|
||||||
|
return result + (["-x", "10"] if transmit else ["-l", "24", "-g", "24"]) + ["-a", "0", "-p", "0", "-B"]
|
||||||
|
|
||||||
|
|
||||||
|
def common_clock(point: OperatingPoint, runner, guard, *, sleep_function=time.sleep) -> dict:
|
||||||
|
"""Прежняя логика регистров/CLKIN; короткий RX использует новую согласованную RF."""
|
||||||
|
point.validate()
|
||||||
|
def short_receive(serial, command_runner):
|
||||||
|
parameters = command(point, "NUL", previous.clock.SHORT_RX_SAMPLE_COUNT)
|
||||||
|
parameters[parameters.index("-d") + 1] = serial
|
||||||
|
result = command_runner(parameters)
|
||||||
|
if result.returncode:
|
||||||
|
raise RuntimeError("Короткий RX-start не выполнен")
|
||||||
|
preflight = previous.bind(previous.clock.common_clock_preflight, _short_rx=short_receive,
|
||||||
|
_disable_clock_outputs=previous.disable_clock_outputs)
|
||||||
|
return preflight(runner, guard, sleep_function=sleep_function)
|
||||||
|
|
||||||
|
|
||||||
|
def validated_metadata(directory: Path) -> dict:
|
||||||
|
metadata = waveform.energy.read_json(directory / "tx_metadata.json")
|
||||||
|
path = directory / "tx_abab.cs8"
|
||||||
|
if (metadata["stage"] != "PREPARATION_ONLY" or metadata["rf_tx"] != 0 or
|
||||||
|
metadata["sample_rate_hz"] != waveform.SAMPLE_RATE or metadata["carrier_hz"] != waveform.CARRIER or
|
||||||
|
metadata["tx_samples"] * 2 != path.stat().st_size or
|
||||||
|
waveform.energy.sha256(path) != metadata["tx_sha256"] or
|
||||||
|
metadata["rx_samples"] != waveform.RX_SAMPLES or
|
||||||
|
[(s["label"], s["symbol_rate"]) for s in metadata["segments"]] !=
|
||||||
|
[("A1", 20000), ("B1", 500000), ("A2", 20000), ("B2", 500000)] or
|
||||||
|
abs(metadata["whole_power_difference_db"]) > .01):
|
||||||
|
raise ValueError("Подготовленный TX/параметры изменились")
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def clean_counter_log(log: str, expected: str) -> bool:
|
||||||
|
"""Все упоминания потерь должны быть разобраны и иметь явный нулевой счётчик."""
|
||||||
|
pattern = re.compile(r"(?<![\w.+-])(\d+)\s+(?:(?:rx|tx)\s+)?(overruns?|underruns?)\b", re.I)
|
||||||
|
matches = list(pattern.finditer(log))
|
||||||
|
if not matches or any(int(match[1]) != 0 for match in matches):
|
||||||
|
return False
|
||||||
|
if not any(match[2].lower().rstrip("s") == expected for match in matches):
|
||||||
|
return False
|
||||||
|
return re.search(r"overrun|underrun", pattern.sub("", log), re.I) is None
|
||||||
|
|
||||||
|
|
||||||
|
def require_clock_pass(result: dict) -> None:
|
||||||
|
if (not result.get("passed") or result.get("master_reg29") != 0x26 or
|
||||||
|
result.get("slave_reg29") != 0x26 or result.get("slave_clkin_status") != "DETECTED"):
|
||||||
|
raise RuntimeError("Common clock FAIL")
|
||||||
|
|
||||||
|
|
||||||
|
def receive_level(path: Path, *, tail_samples: int | None = None) -> dict:
|
||||||
|
"""Уровень CS8 без изменения gain; rail-коды являются признаком клиппинга."""
|
||||||
|
source = waveform.energy.Cs8File(path)
|
||||||
|
if source.size < 65536:
|
||||||
|
raise RuntimeError("Недостаточно сохранённых RX-отсчётов для проверки уровня")
|
||||||
|
start = max(0, source.size - tail_samples) if tail_samples else 0
|
||||||
|
moments = waveform.energy.Moments()
|
||||||
|
for begin in range(start, source.size, 262144):
|
||||||
|
moments.add(source.read(begin, min(262144, source.size - begin)))
|
||||||
|
result = moments.result(20000)
|
||||||
|
result["saturation_detected"] = result["clipped_samples_fraction"] > 0
|
||||||
|
result["analog_compression"] = "UNKNOWN_WITHOUT_CALIBRATION"
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def preflight_only(directory: Path, point: OperatingPoint, *, runner=None,
|
||||||
|
process_checker=None, clock_checker=None, sleep_function=time.sleep) -> dict:
|
||||||
|
"""Только RX: опора и 0,1 с фона; сохранить результат и STOP до разрешения TX."""
|
||||||
|
point.validate()
|
||||||
|
metadata = validated_metadata(directory)
|
||||||
|
waveform.save_new(directory / "preflight_attempt.json", {"point": asdict(point), "rf_tx_count": 0})
|
||||||
|
actual_runner = runner or previous.clock._default_command_runner
|
||||||
|
process_checker = process_checker or previous.check_processes
|
||||||
|
clock_checker = clock_checker or common_clock
|
||||||
|
def receive_only_runner(parameters):
|
||||||
|
if "-t" in parameters:
|
||||||
|
raise PermissionError("TX запрещён внутри preflight_only")
|
||||||
|
return actual_runner(parameters)
|
||||||
|
record = {"passed": False, "rf_tx_count": 0, "point": asdict(point),
|
||||||
|
"tx_sha256": metadata["tx_sha256"], "stop_before_tx": True}
|
||||||
|
clock_started = False
|
||||||
|
try:
|
||||||
|
before = process_checker()
|
||||||
|
record["process_before"] = before.to_dict()
|
||||||
|
previous.require_clear(before)
|
||||||
|
record["inventory"] = previous.phy.lab045._verify_device_inventory(receive_only_runner)
|
||||||
|
clock_started = True
|
||||||
|
record["clock"] = clock_checker(point, receive_only_runner, previous.clock.TransmitGuard(),
|
||||||
|
sleep_function=sleep_function)
|
||||||
|
require_clock_pass(record["clock"])
|
||||||
|
path = directory / "preflight_rx.cs8"
|
||||||
|
if path.exists():
|
||||||
|
raise FileExistsError("RX preflight уже существует")
|
||||||
|
captured = receive_only_runner(command(point, path, 800000))
|
||||||
|
record["rx_process"] = {"returncode": captured.returncode,
|
||||||
|
"stdout": captured.stdout, "stderr": captured.stderr}
|
||||||
|
if captured.returncode or not clean_counter_log(captured.stdout + captured.stderr, "overrun"):
|
||||||
|
raise RuntimeError("RX preflight: ошибка процесса или счётчиков")
|
||||||
|
if path.stat().st_size != 1600000:
|
||||||
|
raise RuntimeError("Неверный размер RX preflight")
|
||||||
|
record["rx_level"] = receive_level(path)
|
||||||
|
record["rx_sha256"] = waveform.energy.sha256(path)
|
||||||
|
if record["rx_level"]["saturation_detected"]:
|
||||||
|
raise RuntimeError("RX saturation до TX; STOP без изменения gains")
|
||||||
|
record["passed"] = True
|
||||||
|
except Exception as error:
|
||||||
|
record["error"] = str(error)
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
errors = previous.disable_clock_outputs(receive_only_runner) if clock_started else []
|
||||||
|
try:
|
||||||
|
after = process_checker()
|
||||||
|
record["process_after"] = after.to_dict()
|
||||||
|
previous.require_clear(after)
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(str(error))
|
||||||
|
record["cleanup_errors"] = errors
|
||||||
|
record["passed"] = record["passed"] and not errors
|
||||||
|
waveform.save_new(directory / "preflight_result.json", record)
|
||||||
|
if not record["passed"]:
|
||||||
|
raise RuntimeError("Preflight cleanup FAIL")
|
||||||
|
return record
|
||||||
|
|
||||||
|
|
||||||
|
def require_preflight(directory: Path, point: OperatingPoint, metadata: dict) -> None:
|
||||||
|
record = waveform.energy.read_json(directory / "preflight_result.json")
|
||||||
|
if (record.get("passed") is not True or record.get("rf_tx_count") != 0 or
|
||||||
|
record.get("point") != asdict(point) or record.get("tx_sha256") != metadata["tx_sha256"]):
|
||||||
|
raise PermissionError("Нужен успешный RX-only preflight именно этой конфигурации")
|
||||||
|
require_clock_pass(record.get("clock", {}))
|
||||||
|
if record.get("rx_level", {}).get("saturation_detected") is not False:
|
||||||
|
raise PermissionError("Нет подтверждённой проверки уровня RX до TX")
|
||||||
|
|
||||||
|
|
||||||
|
def capture_once(directory: Path, point: OperatingPoint, *, authorization: bool = False,
|
||||||
|
runner=None, process_starter=None, process_checker=None,
|
||||||
|
clock_checker=None, sleep_function=time.sleep) -> dict:
|
||||||
|
"""Один RX и один TX; ошибка расходует попытку, автоматического повтора нет."""
|
||||||
|
if authorization is not True:
|
||||||
|
raise PermissionError("Нужно отдельное разрешение на конкретный RF-сеанс Lab056")
|
||||||
|
point.validate()
|
||||||
|
metadata = validated_metadata(directory)
|
||||||
|
require_preflight(directory, point, metadata)
|
||||||
|
receive_path = directory / "rx_stream.cs8"
|
||||||
|
if receive_path.exists() or (directory / "capture_result.json").exists():
|
||||||
|
raise FileExistsError("Аппаратные результаты уже существуют")
|
||||||
|
runner = runner or previous.clock._default_command_runner
|
||||||
|
process_starter = process_starter or previous.clock._default_process_starter
|
||||||
|
process_checker = process_checker or previous.check_processes
|
||||||
|
clock_checker = clock_checker or common_clock
|
||||||
|
waveform.save_new(directory / "hardware_attempt.json", {"authorized_once": True,
|
||||||
|
"point": asdict(point), "tx_sha256": metadata["tx_sha256"], "automatic_retry": False})
|
||||||
|
result = {"passed": False, "rf_tx_count": 0, "point": asdict(point)}
|
||||||
|
guard = previous.clock.TransmitGuard()
|
||||||
|
process, clock_started = None, False
|
||||||
|
try:
|
||||||
|
before = process_checker()
|
||||||
|
result["process_before"] = before.to_dict()
|
||||||
|
previous.require_clear(before)
|
||||||
|
result["inventory"] = previous.phy.lab045._verify_device_inventory(runner)
|
||||||
|
clock_started = True
|
||||||
|
result["clock"] = clock_checker(point, runner, guard, sleep_function=sleep_function)
|
||||||
|
require_clock_pass(result["clock"])
|
||||||
|
process = process_starter(command(point, receive_path, waveform.RX_SAMPLES),
|
||||||
|
directory / "rx_stdout.txt", directory / "rx_stderr.txt")
|
||||||
|
sleep_function(waveform.RX_LEAD_SECONDS)
|
||||||
|
if process.poll() is not None:
|
||||||
|
raise RuntimeError("RX завершился до TX")
|
||||||
|
result["rx_level_before_tx"] = receive_level(receive_path, tail_samples=65536)
|
||||||
|
if result["rx_level_before_tx"]["saturation_detected"]:
|
||||||
|
raise RuntimeError("RX saturation до TX; STOP без изменения gains")
|
||||||
|
guard.register_transmit()
|
||||||
|
result["rf_tx_count"] = guard.transmit_calls
|
||||||
|
transmitted = runner(command(point, directory / "tx_abab.cs8", metadata["tx_samples"], transmit=True))
|
||||||
|
waveform.save_new(directory / "tx_process_result.json", {"returncode": transmitted.returncode,
|
||||||
|
"stdout": transmitted.stdout, "stderr": transmitted.stderr})
|
||||||
|
if transmitted.returncode or process.wait(13):
|
||||||
|
raise RuntimeError("Единственный TX/RX завершился ошибкой")
|
||||||
|
process.close_logs()
|
||||||
|
if receive_path.stat().st_size != waveform.RX_SAMPLES * 2:
|
||||||
|
raise RuntimeError("Неверный размер RX")
|
||||||
|
rx_log = "".join((directory / f"rx_{kind}.txt").read_text(encoding="utf-8") for kind in ("stdout", "stderr"))
|
||||||
|
for role, log, counter in (("RX", rx_log, "overrun"),
|
||||||
|
("TX", transmitted.stdout + transmitted.stderr, "underrun")):
|
||||||
|
if not clean_counter_log(log, counter):
|
||||||
|
raise RuntimeError(f"{role}: нет явного нулевого счётчика потерь")
|
||||||
|
result.update(rx_sha256=waveform.energy.sha256(receive_path), rx_level=receive_level(receive_path))
|
||||||
|
if result["rx_level"]["saturation_detected"]:
|
||||||
|
raise RuntimeError("RX saturation в сохранённом сеансе; TX уже был, повтор запрещён")
|
||||||
|
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(previous.disable_clock_outputs(runner))
|
||||||
|
try:
|
||||||
|
after = process_checker()
|
||||||
|
result["process_after"] = after.to_dict()
|
||||||
|
previous.require_clear(after)
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(str(error))
|
||||||
|
result["cleanup_errors"] = errors
|
||||||
|
result["passed"] = result["passed"] and not errors
|
||||||
|
waveform.save_new(directory / "capture_result.json", result)
|
||||||
|
if not result["passed"]:
|
||||||
|
raise RuntimeError("Cleanup FAIL; повтор запрещён")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def run_functional_tests() -> None:
|
||||||
|
try:
|
||||||
|
capture_once(Path("UNUSED"), OperatingPoint())
|
||||||
|
except PermissionError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise AssertionError("Разрешение проверяется раньше любых действий")
|
||||||
159
tests/test_hackrf_process_check.py
Normal file
159
tests/test_hackrf_process_check.py
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
"""Достоверность перечисления процессов и запрет аппаратных действий при FAIL."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from experiments import lab054_hardware_control as lab
|
||||||
|
from tools import hackrf_process_check as check
|
||||||
|
|
||||||
|
|
||||||
|
def snapshot(names=("python.exe",), errors=()):
|
||||||
|
return check.classify_snapshot("fixture", [check.ProcessIdentity(index + 1, name)
|
||||||
|
for index, name in enumerate(names)], errors)
|
||||||
|
|
||||||
|
|
||||||
|
def completed(stdout="", stderr="", code=0):
|
||||||
|
return lambda command: subprocess.CompletedProcess(command, code, stdout, stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_full_snapshot():
|
||||||
|
result = check.check_processes([lambda: snapshot()])
|
||||||
|
assert result.state is check.ProcessState.CLEAR
|
||||||
|
check.require_clear(result)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("name", ["hackrf_transfer.exe", "hackrf_clock.exe", "HACKRF_DEBUG.EXE",
|
||||||
|
"hackrf_info.exe", "sdrangel.exe", "SDRSharp", "gqrx"])
|
||||||
|
def test_busy_known_sdr_clients(name):
|
||||||
|
result = check.check_processes([lambda: snapshot((name,), errors=("partial snapshot",)),
|
||||||
|
lambda: pytest.fail("BUSY запрещает fallback")])
|
||||||
|
assert result.state is check.ProcessState.BUSY
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
check.require_clear(result)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("names,errors", [((), ()), (("python",), ("Access denied",)), (("",), ())])
|
||||||
|
def test_unknown_never_becomes_clear(names, errors):
|
||||||
|
result = check.check_processes([lambda: snapshot(names, errors)])
|
||||||
|
assert result.state is check.ProcessState.UNKNOWN
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
check.require_clear(result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tasklist_access_denied_is_unknown():
|
||||||
|
result = check.scan_tasklist(completed(stderr="ERROR: Access denied", code=1))
|
||||||
|
assert result.state is check.ProcessState.UNKNOWN
|
||||||
|
assert "Access denied" in str(result.errors)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fallback_to_powershell_after_psutil_unavailable(monkeypatch):
|
||||||
|
powershell = check.scan_powershell
|
||||||
|
monkeypatch.setattr(check, "scan_psutil", lambda: snapshot((), ("ImportError: psutil",)))
|
||||||
|
monkeypatch.setattr(check, "scan_powershell", lambda: powershell(
|
||||||
|
completed('[{"Id":12,"ProcessName":"python"}]')))
|
||||||
|
monkeypatch.setattr(check, "scan_tasklist", lambda: pytest.fail("Полный CLEAR уже получен"))
|
||||||
|
result = check.check_processes()
|
||||||
|
assert result.state is check.ProcessState.CLEAR
|
||||||
|
assert [item.state for item in result.attempts] == [check.ProcessState.UNKNOWN, check.ProcessState.CLEAR]
|
||||||
|
|
||||||
|
|
||||||
|
def test_tasklist_is_last_fallback(monkeypatch):
|
||||||
|
tasklist = check.scan_tasklist
|
||||||
|
monkeypatch.setattr(check, "scan_psutil", lambda: snapshot((), ("unavailable",)))
|
||||||
|
monkeypatch.setattr(check, "scan_powershell", lambda: snapshot((), ("denied",)))
|
||||||
|
monkeypatch.setattr(check, "scan_tasklist", lambda: tasklist(
|
||||||
|
completed('"python.exe","123","Console","1","123 K"\n')))
|
||||||
|
result = check.check_processes()
|
||||||
|
assert result.state is check.ProcessState.CLEAR
|
||||||
|
assert len(result.attempts) == 3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("output", ["", "[]", "null", "not json", '[{"Id":4,"ProcessName":null}]'])
|
||||||
|
def test_powershell_malformed_not_clear(output):
|
||||||
|
assert check.scan_powershell(completed(output)).state is check.ProcessState.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("output", ["", "INFO: No tasks are running", '"python.exe","bad pid","a","b","c"'])
|
||||||
|
def test_tasklist_malformed_not_clear(output):
|
||||||
|
assert check.scan_tasklist(completed(output)).state is check.ProcessState.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
def test_provider_exception_and_empty_provider_list_unknown():
|
||||||
|
def failed():
|
||||||
|
raise PermissionError("denied")
|
||||||
|
assert check.check_processes([failed]).state is check.ProcessState.UNKNOWN
|
||||||
|
assert check.check_processes([]).state is check.ProcessState.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mechanism", ["powershell", "tasklist"])
|
||||||
|
@pytest.mark.parametrize("malformed_first", [False, True])
|
||||||
|
def test_busy_survives_malformed_neighbor(mechanism, malformed_first):
|
||||||
|
if mechanism == "powershell":
|
||||||
|
rows = [{"Id":42,"ProcessName":"hackrf_transfer"}, {}]
|
||||||
|
if malformed_first:
|
||||||
|
rows.reverse()
|
||||||
|
scan = check.scan_powershell(completed(json.dumps(rows)))
|
||||||
|
else:
|
||||||
|
rows = ['"hackrf_transfer.exe","42","Console","1","100 K"', "BROKEN"]
|
||||||
|
if malformed_first:
|
||||||
|
rows.reverse()
|
||||||
|
scan = check.scan_tasklist(completed("\n".join(rows)))
|
||||||
|
assert scan.errors
|
||||||
|
result = check.check_processes([lambda: scan, lambda: pytest.fail("Найденный BUSY нельзя потерять")])
|
||||||
|
assert result.state is check.ProcessState.BUSY
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("disappeared", [False, True])
|
||||||
|
def test_psutil_access_denied_or_exited_process(monkeypatch, disappeared):
|
||||||
|
class NoSuchProcess(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unreadable():
|
||||||
|
raise NoSuchProcess() if disappeared else PermissionError("Access denied")
|
||||||
|
|
||||||
|
module = SimpleNamespace(NoSuchProcess=NoSuchProcess, process_iter=lambda: iter([
|
||||||
|
SimpleNamespace(pid=1, name=lambda: "python.exe"),
|
||||||
|
SimpleNamespace(pid=2, name=unreadable)]))
|
||||||
|
monkeypatch.setattr(check.importlib, "import_module", lambda name: module)
|
||||||
|
result = check.scan_psutil()
|
||||||
|
assert result.state is (check.ProcessState.CLEAR if disappeared else check.ProcessState.UNKNOWN)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("state", list(check.ProcessState))
|
||||||
|
def test_preflight_only_clear_reaches_hardware(tmp_path, monkeypatch, state):
|
||||||
|
actions = []
|
||||||
|
scan = snapshot(("hackrf_transfer.exe",)) if state is check.ProcessState.BUSY else snapshot()
|
||||||
|
if state is check.ProcessState.UNKNOWN:
|
||||||
|
scan = snapshot((), ("tasklist Access denied",))
|
||||||
|
result = check.ProcessCheck(state, (scan,))
|
||||||
|
monkeypatch.setattr(lab.phy.lab045, "_verify_device_inventory", lambda runner: actions.append("inventory") or {})
|
||||||
|
monkeypatch.setattr(lab, "clock_preflight", lambda runner, guard: actions.append("clock") or {"passed": True})
|
||||||
|
monkeypatch.setattr(lab, "disable_clock_outputs", lambda runner: actions.append("cleanup") or [])
|
||||||
|
if state is check.ProcessState.CLEAR:
|
||||||
|
assert lab.preflight_only(tmp_path, runner=lambda _: None, process_checker=lambda: result)["passed"]
|
||||||
|
assert actions == ["inventory", "clock", "cleanup"]
|
||||||
|
else:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
lab.preflight_only(tmp_path, runner=lambda _: None, process_checker=lambda: result)
|
||||||
|
assert actions == []
|
||||||
|
report = json.loads((tmp_path / "common_clock_preflight.json").read_text(encoding="utf-8"))
|
||||||
|
assert report["process_check"]["state"] == state.value
|
||||||
|
assert report["rf_tx_count"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_unknown_after_cleanup_rejects_preflight(tmp_path, monkeypatch):
|
||||||
|
states = iter([check.ProcessCheck(check.ProcessState.CLEAR, (snapshot(),)),
|
||||||
|
check.ProcessCheck(check.ProcessState.UNKNOWN, (snapshot((), ("denied",)),))])
|
||||||
|
monkeypatch.setattr(lab.phy.lab045, "_verify_device_inventory", lambda runner: {})
|
||||||
|
monkeypatch.setattr(lab, "clock_preflight", lambda runner, guard: {"passed": True})
|
||||||
|
monkeypatch.setattr(lab, "disable_clock_outputs", lambda runner: [])
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
lab.preflight_only(tmp_path, runner=lambda _: None, process_checker=lambda: next(states))
|
||||||
|
report = json.loads((tmp_path / "common_clock_preflight.json").read_text(encoding="utf-8"))
|
||||||
|
assert not report["passed"]
|
||||||
|
assert report["process_check_after_cleanup"]["state"] == "UNKNOWN"
|
||||||
140
tests/test_lab054_hardware_control.py
Normal file
140
tests/test_lab054_hardware_control.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
"""Lab054: фиксированные RF-команды и отказы runner без оборудования."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from experiments import lab054_hardware_control as lab
|
||||||
|
from tools.hackrf_process_check import ProcessCheck, ProcessState
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("failure", [None, "master", "slave", "clkin", "receive"])
|
||||||
|
def test_clock_gates_never_transmit(failure):
|
||||||
|
commands = []
|
||||||
|
|
||||||
|
def runner(command):
|
||||||
|
commands.append(command)
|
||||||
|
output, code = "", 0
|
||||||
|
if "--si5351c" in command:
|
||||||
|
serial = command[command.index("-d") + 1]
|
||||||
|
bad = ((failure == "master" and serial == lab.clock.MASTER_SERIAL)
|
||||||
|
or (failure == "slave" and serial == lab.clock.SLAVE_SERIAL))
|
||||||
|
output = "0x0e" if bad else "0x26"
|
||||||
|
elif "-i" in command:
|
||||||
|
output = "UNKNOWN" if failure == "clkin" else "CLKIN status: clock signal detected"
|
||||||
|
elif "-r" in command:
|
||||||
|
code = int(failure == "receive")
|
||||||
|
return subprocess.CompletedProcess(command, code, output, "")
|
||||||
|
|
||||||
|
guard = lab.clock.TransmitGuard()
|
||||||
|
if failure:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
lab.clock_preflight(runner, guard, sleep_function=lambda _: None)
|
||||||
|
assert not guard.tx_allowed
|
||||||
|
else:
|
||||||
|
assert lab.clock_preflight(runner, guard, sleep_function=lambda _: None)["passed"]
|
||||||
|
assert guard.tx_allowed
|
||||||
|
assert not any("-t" in command for command in commands)
|
||||||
|
for command in commands:
|
||||||
|
if Path(command[0]).name == "hackrf_transfer.exe":
|
||||||
|
assert command[command.index("-f") + 1] == "1400000000"
|
||||||
|
assert command[command.index("-l") + 1] == command[command.index("-g") + 1] == "24"
|
||||||
|
|
||||||
|
|
||||||
|
def test_authorization_precedes_all_side_effects(tmp_path):
|
||||||
|
with pytest.raises(PermissionError):
|
||||||
|
lab.capture_once(tmp_path, runner=lambda _: pytest.fail("hardware"))
|
||||||
|
assert list(tmp_path.iterdir()) == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("failure", [None, "clock", "early_rx", "tx", "cleanup"])
|
||||||
|
def test_one_shot_order_and_no_retry(tmp_path, monkeypatch, failure):
|
||||||
|
events = []
|
||||||
|
monkeypatch.setattr(lab, "validate_prepared", lambda directory: {"tx_samples": 123})
|
||||||
|
monkeypatch.setattr(lab.phy.lab045, "_verify_device_inventory", lambda runner: {})
|
||||||
|
|
||||||
|
def preflight(runner, guard):
|
||||||
|
if failure == "clock":
|
||||||
|
raise RuntimeError("clock")
|
||||||
|
guard.allow()
|
||||||
|
return {"passed": True}
|
||||||
|
|
||||||
|
monkeypatch.setattr(lab, "clock_preflight", preflight)
|
||||||
|
monkeypatch.setattr(lab, "disable_clock_outputs", lambda runner: events.append("clocks_off") or [])
|
||||||
|
|
||||||
|
class Process:
|
||||||
|
def poll(self):
|
||||||
|
return 1 if failure == "early_rx" else None
|
||||||
|
|
||||||
|
def wait(self, timeout):
|
||||||
|
with (tmp_path / "rx_stream.cs8").open("wb") as stream:
|
||||||
|
stream.truncate(lab.RX_SAMPLES * 2)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def close_logs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
events.append("terminate")
|
||||||
|
if failure == "cleanup":
|
||||||
|
raise RuntimeError("cleanup")
|
||||||
|
|
||||||
|
def starter(command, stdout, stderr):
|
||||||
|
events.append("rx")
|
||||||
|
stdout.write_text("")
|
||||||
|
stderr.write_text("")
|
||||||
|
return Process()
|
||||||
|
|
||||||
|
def runner(command):
|
||||||
|
assert "-t" in command and "-R" not in command
|
||||||
|
events.append("tx")
|
||||||
|
return subprocess.CompletedProcess(command, int(failure == "tx"), "", "")
|
||||||
|
|
||||||
|
kwargs = dict(authorization=True, runner=runner, process_starter=starter,
|
||||||
|
process_checker=lambda: ProcessCheck(ProcessState.CLEAR, ()),
|
||||||
|
sleep_function=lambda _: events.append("lead"))
|
||||||
|
if failure:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
lab.capture_once(tmp_path, **kwargs)
|
||||||
|
else:
|
||||||
|
assert lab.capture_once(tmp_path, **kwargs)["passed"]
|
||||||
|
assert events.count("tx") == (0 if failure in ("clock", "early_rx") else 1)
|
||||||
|
if "tx" in events:
|
||||||
|
assert events.index("rx") < events.index("lead") < events.index("tx")
|
||||||
|
assert "clocks_off" in events
|
||||||
|
assert json.loads((tmp_path / "capture_result.json").read_text())["passed"] == (failure is None)
|
||||||
|
before = events.copy()
|
||||||
|
with pytest.raises(FileExistsError):
|
||||||
|
lab.capture_once(tmp_path, **kwargs)
|
||||||
|
assert events == before
|
||||||
|
|
||||||
|
|
||||||
|
def test_clock_cleanup_continues_after_timeout():
|
||||||
|
commands = []
|
||||||
|
|
||||||
|
def runner(command):
|
||||||
|
commands.append(command)
|
||||||
|
raise subprocess.TimeoutExpired(command, 1)
|
||||||
|
|
||||||
|
errors = lab.disable_clock_outputs(runner)
|
||||||
|
assert len(commands) == len(errors) == 2
|
||||||
|
assert [command[2] for command in commands] == [lab.clock.MASTER_SERIAL, lab.clock.SLAVE_SERIAL]
|
||||||
|
|
||||||
|
|
||||||
|
def test_payload_match_does_not_hide_changed_packet():
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
plan = lab.plans()[0].plan
|
||||||
|
packet = bytes([plan.packet[0] ^ 1]) + plan.packet[1:]
|
||||||
|
reception = SimpleNamespace(payload_bytes=plan.payload, packet_bytes=packet)
|
||||||
|
frame = SimpleNamespace(reception=reception,
|
||||||
|
post_preamble_soft=lab.phy.lab045.modulate_bits_to_ideal_soft(
|
||||||
|
__import__("numpy").concatenate((plan.phy_header_bits, plan.interleaved_bits))))
|
||||||
|
result = lab.compare_after_verdict([(None, SimpleNamespace(accepted=True, frame=frame))])
|
||||||
|
assert result["payload_matched_packets"] == 1
|
||||||
|
assert result["exact_packets"] == result["unique_exact_packets"] == 0
|
||||||
|
assert result["post_fec_errors"] == 1
|
||||||
248
tests/test_lab056_preparation.py
Normal file
248
tests/test_lab056_preparation.py
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
"""Lab056: цифровые условия и отказы runner без оборудования."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from dataclasses import asdict
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from experiments import lab056_abab_energy as lab
|
||||||
|
from experiments import lab056_capture_runner as capture
|
||||||
|
from tools.hackrf_process_check import ProcessCheck, ProcessState
|
||||||
|
|
||||||
|
|
||||||
|
def point():
|
||||||
|
# Только синтетическая конфигурация теста; это не разрешение на эфир.
|
||||||
|
return capture.OperatingPoint(915000000, physical_path="SIMULATED", path_confirmed=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_authorization_has_no_side_effects(tmp_path):
|
||||||
|
with pytest.raises(PermissionError):
|
||||||
|
capture.capture_once(tmp_path, point(), runner=lambda _: pytest.fail("hardware"))
|
||||||
|
assert list(tmp_path.iterdir()) == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("configuration", [capture.OperatingPoint(),
|
||||||
|
capture.OperatingPoint(0), capture.OperatingPoint(True), capture.OperatingPoint(915e6)])
|
||||||
|
def test_missing_or_invalid_frequency_precedes_io(tmp_path, configuration):
|
||||||
|
with pytest.raises(PermissionError):
|
||||||
|
capture.capture_once(tmp_path, configuration, authorization=True)
|
||||||
|
with pytest.raises(PermissionError):
|
||||||
|
capture.preflight_only(tmp_path, configuration)
|
||||||
|
assert list(tmp_path.iterdir()) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_fixed_gain_and_finite_nonrepeating_commands():
|
||||||
|
rx = capture.command(point(), "rx.cs8", 24000000)
|
||||||
|
tx = capture.command(point(), "tx.cs8", 1000000, transmit=True)
|
||||||
|
assert rx[rx.index("-l") + 1] == rx[rx.index("-g") + 1] == "24"
|
||||||
|
assert tx[tx.index("-x") + 1] == "10"
|
||||||
|
for command in (rx, tx):
|
||||||
|
assert "-R" not in command
|
||||||
|
assert command[command.index("-a") + 1] == command[command.index("-p") + 1] == "0"
|
||||||
|
assert command[command.index("-f") + 1] == "915000000"
|
||||||
|
assert command[command.index("-s") + 1] == "8000000"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("failure", [None, "master", "slave", "clkin", "receive"])
|
||||||
|
def test_common_clock_logic_uses_agreed_frequency(failure):
|
||||||
|
commands = []
|
||||||
|
def runner(command):
|
||||||
|
commands.append(command)
|
||||||
|
output, code = "", 0
|
||||||
|
if "--si5351c" in command:
|
||||||
|
serial = command[command.index("-d") + 1]
|
||||||
|
bad = ((failure == "master" and serial == capture.previous.clock.MASTER_SERIAL) or
|
||||||
|
(failure == "slave" and serial == capture.previous.clock.SLAVE_SERIAL))
|
||||||
|
output = "0x0e" if bad else "0x26"
|
||||||
|
elif "-i" in command:
|
||||||
|
output = "UNKNOWN" if failure == "clkin" else "CLKIN status: clock signal detected"
|
||||||
|
elif "-r" in command:
|
||||||
|
code = int(failure == "receive")
|
||||||
|
assert command[command.index("-f") + 1] == "915000000"
|
||||||
|
return subprocess.CompletedProcess(command, code, output, "")
|
||||||
|
guard = capture.previous.clock.TransmitGuard()
|
||||||
|
if failure:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
capture.common_clock(point(), runner, guard, sleep_function=lambda _: None)
|
||||||
|
assert not guard.tx_allowed
|
||||||
|
else:
|
||||||
|
assert capture.common_clock(point(), runner, guard, sleep_function=lambda _: None)["passed"]
|
||||||
|
assert not any("-t" in command for command in commands)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("failure", [None, "busy", "clock", "early_rx", "tx", "counters", "cleanup", "rx_role_loss", "tx_role_loss", "clipping_before", "clipping_during"])
|
||||||
|
def test_one_shot_rx_first_and_failures(tmp_path, monkeypatch, failure):
|
||||||
|
events = []
|
||||||
|
monkeypatch.setattr(capture, "validated_metadata", lambda _: {"tx_sha256": "TEST", "tx_samples": 200})
|
||||||
|
monkeypatch.setattr(capture.previous.phy.lab045, "_verify_device_inventory", lambda _: {"passed": True})
|
||||||
|
monkeypatch.setattr(lab, "RX_SAMPLES", 65536)
|
||||||
|
approved = {"passed": True, "rf_tx_count": 0, "point": asdict(point()), "tx_sha256": "TEST",
|
||||||
|
"clock": {"passed": True, "master_reg29": 38, "slave_reg29": 38, "slave_clkin_status": "DETECTED"},
|
||||||
|
"rx_level": {"saturation_detected": False}}
|
||||||
|
lab.save_new(tmp_path / "preflight_result.json", approved)
|
||||||
|
def clock_checker(point, runner, guard, **kwargs):
|
||||||
|
events.append("clock")
|
||||||
|
if failure == "clock":
|
||||||
|
raise RuntimeError("clock")
|
||||||
|
guard.allow()
|
||||||
|
return {"passed": True, "master_reg29": 38, "slave_reg29": 38, "slave_clkin_status": "DETECTED"}
|
||||||
|
class Process:
|
||||||
|
def poll(self):
|
||||||
|
return 1 if failure == "early_rx" else None
|
||||||
|
|
||||||
|
def wait(self, timeout):
|
||||||
|
(tmp_path / "rx_stream.cs8").write_bytes(bytes([127 if failure == "clipping_during" else 1]) * 131072)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def close_logs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
events.append("terminate")
|
||||||
|
if failure == "cleanup":
|
||||||
|
raise RuntimeError("cleanup")
|
||||||
|
def starter(command, stdout, stderr):
|
||||||
|
events.append("rx")
|
||||||
|
(tmp_path / "rx_stream.cs8").write_bytes(bytes([127 if failure == "clipping_before" else 1]) * 131072)
|
||||||
|
stdout.write_text("")
|
||||||
|
stderr.write_text("1 RX overruns\n0 overruns" if failure == "rx_role_loss" else "0 overruns")
|
||||||
|
return Process()
|
||||||
|
def runner(command):
|
||||||
|
if "-t" in command:
|
||||||
|
events.append("tx")
|
||||||
|
log = "" if failure == "counters" else ("1 TX underruns\n0 underruns" if failure == "tx_role_loss" else "0 underruns")
|
||||||
|
return subprocess.CompletedProcess(command, int(failure == "tx"), "", log)
|
||||||
|
events.append("cleanup")
|
||||||
|
return subprocess.CompletedProcess(command, 0, "", "")
|
||||||
|
kwargs = dict(authorization=True, runner=runner, process_starter=starter,
|
||||||
|
process_checker=lambda: ProcessCheck(ProcessState.BUSY if failure == "busy" else ProcessState.CLEAR, ()),
|
||||||
|
clock_checker=clock_checker, sleep_function=lambda _: events.append("lead"))
|
||||||
|
if failure:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
capture.capture_once(tmp_path, point(), **kwargs)
|
||||||
|
else:
|
||||||
|
assert capture.capture_once(tmp_path, point(), **kwargs)["passed"]
|
||||||
|
assert events.count("tx") == (0 if failure in ("busy", "clock", "early_rx", "clipping_before") else 1)
|
||||||
|
if "tx" in events:
|
||||||
|
assert events.index("rx") < events.index("lead") < events.index("tx")
|
||||||
|
before = events.copy()
|
||||||
|
with pytest.raises(FileExistsError):
|
||||||
|
capture.capture_once(tmp_path, point(), **kwargs)
|
||||||
|
assert events == before
|
||||||
|
result = json.loads((tmp_path / "capture_result.json").read_text())
|
||||||
|
assert result["passed"] == (failure is None)
|
||||||
|
assert result["rf_tx_count"] == events.count("tx")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("failure", [None, "clock", "clipping", "tx_attempt", "cleanup"])
|
||||||
|
def test_rx_only_preflight_stops_before_tx(tmp_path, monkeypatch, failure):
|
||||||
|
calls = []
|
||||||
|
monkeypatch.setattr(capture, "validated_metadata", lambda _: {"tx_sha256": "TEST"})
|
||||||
|
monkeypatch.setattr(capture.previous.phy.lab045, "_verify_device_inventory", lambda _: {"passed": True})
|
||||||
|
def runner(command):
|
||||||
|
calls.append(command)
|
||||||
|
assert "-t" not in command
|
||||||
|
if "-r" in command:
|
||||||
|
Path(command[command.index("-r") + 1]).write_bytes(bytes([127 if failure == "clipping" else 1]) * 1600000)
|
||||||
|
if failure == "cleanup":
|
||||||
|
raise RuntimeError("cleanup")
|
||||||
|
return subprocess.CompletedProcess(command, 0, "0 RX overruns", "")
|
||||||
|
def clock_checker(point, runner, guard, **kwargs):
|
||||||
|
if failure == "tx_attempt":
|
||||||
|
runner(["hackrf_transfer", "-t", "forbidden"])
|
||||||
|
return {"passed": True, "master_reg29": 14 if failure == "clock" else 38,
|
||||||
|
"slave_reg29": 38, "slave_clkin_status": "DETECTED"}
|
||||||
|
kwargs = dict(runner=runner, clock_checker=clock_checker,
|
||||||
|
process_checker=lambda: ProcessCheck(ProcessState.CLEAR, ()))
|
||||||
|
if failure:
|
||||||
|
with pytest.raises((RuntimeError, PermissionError)):
|
||||||
|
capture.preflight_only(tmp_path, point(), **kwargs)
|
||||||
|
else:
|
||||||
|
assert capture.preflight_only(tmp_path, point(), **kwargs)["passed"]
|
||||||
|
result = json.loads((tmp_path / "preflight_result.json").read_text())
|
||||||
|
assert result["rf_tx_count"] == 0
|
||||||
|
assert result["passed"] == (failure is None)
|
||||||
|
assert not (tmp_path / "hardware_attempt.json").exists()
|
||||||
|
with pytest.raises(FileExistsError):
|
||||||
|
capture.preflight_only(tmp_path, point(), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def test_preflight_cannot_authorize_another_frequency(tmp_path):
|
||||||
|
approved = {"passed": True, "rf_tx_count": 0, "point": asdict(point()), "tx_sha256": "TEST",
|
||||||
|
"clock": {"passed": True, "master_reg29": 38, "slave_reg29": 38, "slave_clkin_status": "DETECTED"},
|
||||||
|
"rx_level": {"saturation_detected": False}}
|
||||||
|
lab.save_new(tmp_path / "preflight_result.json", approved)
|
||||||
|
capture.require_preflight(tmp_path, point(), {"tx_sha256": "TEST"})
|
||||||
|
with pytest.raises(PermissionError):
|
||||||
|
capture.require_preflight(tmp_path, capture.OperatingPoint(916000000), {"tx_sha256": "TEST"})
|
||||||
|
|
||||||
|
|
||||||
|
def test_external_frequency_needs_no_country_or_band_fields():
|
||||||
|
configuration = capture.OperatingPoint(916000000)
|
||||||
|
command = capture.command(configuration, "NUL", 65536)
|
||||||
|
assert command[command.index("-f") + 1] == "916000000"
|
||||||
|
|
||||||
|
|
||||||
|
def test_complex_gain_and_repeatability_are_not_rx_normalization():
|
||||||
|
random_generator = np.random.default_rng(56)
|
||||||
|
reference = random_generator.normal(size=8000).astype(complex)
|
||||||
|
expected = .3 + .4j
|
||||||
|
received = 1 + 2j + expected * reference
|
||||||
|
gain = lab.complex_gain(reference, received, 0)
|
||||||
|
assert complex(gain["real"], gain["imag"]) == pytest.approx(expected)
|
||||||
|
rows = []
|
||||||
|
for label, power in (("A1", 4), ("B1", 1), ("A2", 8), ("B2", 2)):
|
||||||
|
rows.append({"label": label, "background_power": 1, "raw_rx": {"power_code2": power + 1},
|
||||||
|
"background_subtracted_signal_power": power, "channel_gain_power": power,
|
||||||
|
"measurement": {"heldout_signal_power": power}})
|
||||||
|
result = lab.comparison_summary(rows)
|
||||||
|
assert result["received_power_difference_db"] == pytest.approx(-6.020599913)
|
||||||
|
assert result["energy_per_symbol_difference_db"] == pytest.approx(-20)
|
||||||
|
assert [r["channel_gain_difference_db"] for r in result["repeatability"]] == pytest.approx([3.010299957, 3.010299957])
|
||||||
|
rows[-1]["background_subtracted_signal_power"] = None
|
||||||
|
assert lab.comparison_summary(rows)["received_power_difference_db"] is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_nonpositive_excess_is_not_a_power_ratio():
|
||||||
|
assert lab.difference_db(-1, 1) is None
|
||||||
|
assert lab.difference_db(None, 1) is None
|
||||||
|
assert lab.difference_db(.25, 1) == pytest.approx(-6.020599913)
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_result_does_not_leave_partial_json(tmp_path):
|
||||||
|
path = tmp_path / "result.json"
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
lab.save_new(path, {"invalid": float("nan")})
|
||||||
|
assert not path.exists()
|
||||||
|
lab.save_new(path, {"passed": True})
|
||||||
|
with pytest.raises(FileExistsError):
|
||||||
|
lab.save_new(path, {"passed": False})
|
||||||
|
assert json.loads(path.read_text())["passed"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("log,expected,passed", [
|
||||||
|
("0 overruns", "overrun", True), ("0 RX overrun", "overrun", True),
|
||||||
|
("0 TX underruns", "underrun", True), ("1 RX overruns\n0 overruns", "overrun", False),
|
||||||
|
("1 TX underrun\n0 underruns", "underrun", False),
|
||||||
|
("unknown overruns\n0 overruns", "overrun", False),
|
||||||
|
("0 overruns\nunderrun detected", "overrun", False),
|
||||||
|
("0.0 overruns", "overrun", False), ("-1 overruns", "overrun", False),
|
||||||
|
("0 underruns", "overrun", False), ("", "overrun", False)])
|
||||||
|
def test_all_loss_mentions_must_be_explicit_zero(log, expected, passed):
|
||||||
|
assert capture.clean_counter_log(log, expected) is passed
|
||||||
|
|
||||||
|
|
||||||
|
def test_deterministic_ab_rms_and_no_clipping():
|
||||||
|
powers = []
|
||||||
|
for rate in (20000, 500000):
|
||||||
|
raw = lab.build_burst(rate)
|
||||||
|
values = raw[::2].astype(float) + 1j * raw[1::2].astype(float)
|
||||||
|
assert not np.any((raw == -128) | (raw == 127))
|
||||||
|
powers.append(float(np.mean(abs(values)**2)))
|
||||||
|
np.testing.assert_array_equal(raw, lab.build_burst(rate))
|
||||||
|
assert abs(lab.difference_db(powers[1], powers[0])) < .01
|
||||||
178
tools/hackrf_process_check.py
Normal file
178
tools/hackrf_process_check.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
"""Проверка конфликтующих SDR-процессов без обращения к оборудованию."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import csv
|
||||||
|
from dataclasses import asdict, dataclass
|
||||||
|
from enum import Enum
|
||||||
|
import importlib
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
from pathlib import PureWindowsPath
|
||||||
|
import subprocess
|
||||||
|
from typing import Callable, Iterable
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessState(str, Enum):
|
||||||
|
CLEAR = "CLEAR"
|
||||||
|
BUSY = "BUSY"
|
||||||
|
UNKNOWN = "UNKNOWN"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ProcessIdentity:
|
||||||
|
pid: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ProcessScan:
|
||||||
|
state: ProcessState
|
||||||
|
mechanism: str
|
||||||
|
matches: tuple[ProcessIdentity, ...] = ()
|
||||||
|
inspected: int = 0
|
||||||
|
errors: tuple[str, ...] = ()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ProcessCheck:
|
||||||
|
state: ProcessState
|
||||||
|
attempts: tuple[ProcessScan, ...]
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
|
# Эти программы способны открыть SDR. Их наличие консервативно даёт BUSY,
|
||||||
|
# даже если конкретный экземпляр пока не открыл устройство. Это проверка имён,
|
||||||
|
# а не доказательство отсутствия произвольного переименованного USB-клиента.
|
||||||
|
SDR_PROCESS_NAMES = frozenset({
|
||||||
|
"gqrx", "sdrangel", "sdrsharp", "sdrpp", "sdr++", "cubicsdr", "hdsdr",
|
||||||
|
"sdrconnect", "gnuradio-companion", "osmocom_fft", "osmocom_siggen",
|
||||||
|
"soapysdrserver", "soapyremote", "soapy_sdr_server", "urh",
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def is_conflicting(name: str) -> bool:
|
||||||
|
normalized = PureWindowsPath(name).name.casefold()
|
||||||
|
if normalized.endswith(".exe"):
|
||||||
|
normalized = normalized[:-4]
|
||||||
|
return normalized.startswith("hackrf_") or normalized in SDR_PROCESS_NAMES
|
||||||
|
|
||||||
|
|
||||||
|
def classify_snapshot(mechanism: str, rows: Iterable[ProcessIdentity],
|
||||||
|
errors: Iterable[str] = ()) -> ProcessScan:
|
||||||
|
"""Пустое, неполное или неразобранное перечисление не означает CLEAR."""
|
||||||
|
failures = list(errors)
|
||||||
|
matches = []
|
||||||
|
count = 0
|
||||||
|
try:
|
||||||
|
for row in rows:
|
||||||
|
if (not isinstance(row, ProcessIdentity) or type(row.pid) is not int or row.pid < 0
|
||||||
|
or not isinstance(row.name, str) or not row.name.strip()):
|
||||||
|
failures.append("Некорректная запись процесса")
|
||||||
|
continue
|
||||||
|
count += 1
|
||||||
|
if is_conflicting(row.name):
|
||||||
|
matches.append(row)
|
||||||
|
except Exception as error:
|
||||||
|
failures.append(f"{type(error).__name__}: {error}")
|
||||||
|
if count == 0:
|
||||||
|
failures.append("Не получено ни одного процесса")
|
||||||
|
state = ProcessState.BUSY if matches else (ProcessState.UNKNOWN if failures else ProcessState.CLEAR)
|
||||||
|
return ProcessScan(state, mechanism, tuple(matches), count, tuple(failures))
|
||||||
|
|
||||||
|
|
||||||
|
def scan_psutil() -> ProcessScan:
|
||||||
|
"""NoSuchProcess — обычное завершение; отказ доступа сохраняет UNKNOWN."""
|
||||||
|
rows, errors = [], []
|
||||||
|
try:
|
||||||
|
psutil = importlib.import_module("psutil")
|
||||||
|
clear_cache = getattr(psutil.process_iter, "cache_clear", None)
|
||||||
|
if clear_cache is not None:
|
||||||
|
clear_cache()
|
||||||
|
for process in psutil.process_iter():
|
||||||
|
try:
|
||||||
|
rows.append(ProcessIdentity(process.pid, process.name()))
|
||||||
|
except psutil.NoSuchProcess:
|
||||||
|
continue
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"pid={process.pid}: {type(error).__name__}: {error}")
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"{type(error).__name__}: {error}")
|
||||||
|
return classify_snapshot("psutil", rows, errors)
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(command: list[str]) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(command, capture_output=True, text=True, encoding="utf-8",
|
||||||
|
errors="strict", timeout=10,
|
||||||
|
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0))
|
||||||
|
|
||||||
|
|
||||||
|
def scan_powershell(runner: Callable = run_command) -> ProcessScan:
|
||||||
|
"""Get-Process завершает перечисление целиком или возвращает ошибку."""
|
||||||
|
script = ("$ErrorActionPreference='Stop'; "
|
||||||
|
"[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new(); "
|
||||||
|
"try { $items=@(Get-Process -ErrorAction Stop | "
|
||||||
|
"Select-Object Id,ProcessName); "
|
||||||
|
"ConvertTo-Json -InputObject $items -Compress } "
|
||||||
|
"catch { [Console]::Error.WriteLine($_.Exception.Message); exit 1 }")
|
||||||
|
rows, errors = [], []
|
||||||
|
try:
|
||||||
|
result = runner(["powershell.exe", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script])
|
||||||
|
if result.returncode != 0 or result.stderr.strip():
|
||||||
|
raise RuntimeError(f"exit={result.returncode}: {result.stderr.strip()}")
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
if not isinstance(data, list):
|
||||||
|
raise ValueError("Ожидался полный JSON-массив процессов")
|
||||||
|
for row in data:
|
||||||
|
try:
|
||||||
|
rows.append(ProcessIdentity(row["Id"], row["ProcessName"]))
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"{type(error).__name__}: {error}")
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"{type(error).__name__}: {error}")
|
||||||
|
return classify_snapshot("powershell_get_process", rows, errors)
|
||||||
|
|
||||||
|
|
||||||
|
def scan_tasklist(runner: Callable = run_command) -> ProcessScan:
|
||||||
|
"""Резервный полный CSV; текст ошибки или пустой вывод не равен CLEAR."""
|
||||||
|
rows, errors = [], []
|
||||||
|
try:
|
||||||
|
result = runner(["tasklist.exe", "/FO", "CSV", "/NH"])
|
||||||
|
if result.returncode != 0 or result.stderr.strip():
|
||||||
|
raise RuntimeError(f"exit={result.returncode}: {result.stderr.strip()}")
|
||||||
|
for row in csv.reader(io.StringIO(result.stdout), strict=True):
|
||||||
|
try:
|
||||||
|
if len(row) != 5:
|
||||||
|
raise ValueError("Некорректная строка tasklist CSV")
|
||||||
|
rows.append(ProcessIdentity(int(row[1]), row[0]))
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"{type(error).__name__}: {error}")
|
||||||
|
except Exception as error:
|
||||||
|
errors.append(f"{type(error).__name__}: {error}")
|
||||||
|
return classify_snapshot("tasklist", rows, errors)
|
||||||
|
|
||||||
|
|
||||||
|
def check_processes(providers: Iterable[Callable[[], ProcessScan]] | None = None) -> ProcessCheck:
|
||||||
|
"""UNKNOWN разрешает другой источник; только полноценный CLEAR открывает gate."""
|
||||||
|
attempts = []
|
||||||
|
sources = (scan_psutil, scan_powershell, scan_tasklist) if providers is None else providers
|
||||||
|
for provider in sources:
|
||||||
|
try:
|
||||||
|
scan = provider()
|
||||||
|
if not isinstance(scan, ProcessScan) or not isinstance(scan.state, ProcessState):
|
||||||
|
raise TypeError("Некорректный ответ проверки процессов")
|
||||||
|
except Exception as error:
|
||||||
|
scan = ProcessScan(ProcessState.UNKNOWN, getattr(provider, "__name__", "provider"),
|
||||||
|
errors=(f"{type(error).__name__}: {error}",))
|
||||||
|
attempts.append(scan)
|
||||||
|
if scan.state in (ProcessState.CLEAR, ProcessState.BUSY):
|
||||||
|
return ProcessCheck(scan.state, tuple(attempts))
|
||||||
|
return ProcessCheck(ProcessState.UNKNOWN, tuple(attempts))
|
||||||
|
|
||||||
|
|
||||||
|
def require_clear(result: ProcessCheck) -> None:
|
||||||
|
if not isinstance(result, ProcessCheck) or result.state is not ProcessState.CLEAR:
|
||||||
|
detail = result.to_dict() if isinstance(result, ProcessCheck) else repr(result)
|
||||||
|
raise RuntimeError(f"Проверка процессов не дала CLEAR: {detail}")
|
||||||
Reference in New Issue
Block a user