3 Commits

Author SHA1 Message Date
LittleSam129
af7c05f2da Lab067: localize remaining PHY header failures 2026-09-15 17:45:38 +03:00
LittleSam129
b27a5cf00e Lab066: validate 500 ksymbol/s full-packet OTA reception 2026-09-15 15:51:57 +03:00
LittleSam129
dacf04f2d0 Lab065: validate OTA gain improvement at TX VGA 20 dB 2026-09-15 13:42:08 +03:00
42 changed files with 53058 additions and 0 deletions

View File

@@ -2861,3 +2861,143 @@ Quick gate после сеанса прошёл:986 тестов ядра,37 ф
полное сравнение L3/L0 и двенадцать спектральных значений непосредственно из полное сравнение L3/L0 и двенадцать спектральных значений непосредственно из
CS8. Расхождений нет, вердикт `чисто`. Оборудование при проверке не опрашивалось, CS8. Расхождений нет, вердикт `чисто`. Оборудование при проверке не опрашивалось,
RF TX остаётся равным нулю. RF TX остаётся равным нулю.
# Запись 060
## Дата
15 сентября 2026 года
## Тема
Lab064 зафиксирована; подготовка Lab065 при TX VGA 20 дБ и RX-only preflight
## Выполнено
После проверки уже подготовленного индекса по прямому разрешению пользователя
создан коммит `cff226e0bf9e36a627829ea11a4ca3292ae20953` с сообщением
`Lab064: measure OTA level linearity at TX VGA 10 dB`. В коммит вошли только
12 файлов и фрагментов Lab064; изменения Lab054 и Lab058 и сырые IQ не вошли.
Push не выполнялся.
Пользователь подтвердил неизменность геометрии, антенн, поляризации, USB,
clock-кабеля и RF-кабелей. Для Lab065 TX VGA изменён с 10 до 20 дБ. Частота
1400000000 Гц, RX LNA/VGA 24/24 дБ, RF amplifier и Bias-Tee OFF, PRBS7/BPSK/RRC,
20000 символов/с, Fs 8000000 отсчётов/с, несущая +50000 Гц и лестница
−18/−12/−6/0 дБ сохранены. TX-файл побайтово совпадает с Lab064: 6000000
отсчётов, 12000000 байт, 0,75 с, SHA-256
fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9.
Подготовлены RX-first runner без повторов, fail-closed проверка максимальных
счётчиков и анализ двух видов линейности: внутри Lab065 и для одинаковых
уровней Lab064/Lab065. До RF численно заданы границы пяти классификаций.
Software gate и 12 целевых тестов прошли.
RX-only preflight прошёл. Обе HackRF One r9 и роли подтверждены,
MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED на третьем опросе через201,986мс.
RX записал800000 отсчётов фона: полный RMS2,333068, AC RMS1,056565 кода,
max|I|/|Q|=6/8, clipping0, overruns0, неожиданно сильного сигнала нет.
Процессы до и после CLEAR, cleanup без ошибок.
После отдельного прямого разрешения выполнен ровно один OTA-сеанс Lab065.
Перед TX MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED на втором опросе через
136,897мс. RX стартовал раньше TX. TX underrun maximum0, RX overrun maximum0,
clipping0, cleanup без ошибок; процессы после CLEAR. RX:16000000 отсчётов,
2,0с, SHA-256 845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e.
Автоматических и ручных повторов не было.
Внутренние ошибки ступеней Lab065 равны−0,077363/+0,034934/−0,013858дБ;
компрессии L2/L3 нет. Относительно Lab064 matched power вырос на
11,179328/11,119466/11,323670/11,287650дБ. Медианный прирост+11,233489дБ,
медианное изменение остатка+0,517004дБ, улучшение matched/residual+10,734495дБ.
Фон стабилен: разброс1,002840дБ при границе3дБ. Остаточные полосы50/100/150кГц
на L3 выросли на7,304593/6,432281/4,996232дБ, то есть слабее matched power.
Классификация `LINEAR_GAIN_IMPROVEMENT`.
Независимая read-only проверка пересчитала RX SHA и размер, все счётчики,
локализацию, фон, matched/residual, комплексный h, CFO, внутренние ступени,
сравнение с Lab064 и полосы50/100/150кГц. Расхождений нет. Adversary подтвердил
одноразовость runner и нашёл неоднозначную подпись текущего состояния RF после
сеанса; она исправлена на `RF TX NOW=0` и закрыта отдельным тестом. Финальные
шлюзы: quick gate1000/38/13 PASS, pytest1000 passed, diff check PASS.
RF TX=0. Lab065 не коммитировалась, push не выполнялся, Lab066 не создана.
Следующий этап: STOP, рассмотрение результата и отдельное разрешение на коммит
Lab065; повторная передача или изменение усилений запрещены.
# Запись 061
## Дата
15 сентября 2026 года
## Тема
Lab065 зафиксирована; подготовка исправленного Lab066 и RX-only preflight
## Выполнено
По прямому разрешению пользователя создан локальный коммит Lab065
`dacf04f2d0a15c0c1b56a7fb527db61fc2e38462` с сообщением
`Lab065: validate OTA gain improvement at TX VGA 20 dB`. Push не выполнялся.
Lab066 подготовлена как исправленный повтор цели Lab062. Исторический результат
Lab062 сохранён: `RF_SESSION_INVALID_TX_UNDERRUN`, legacy/T2 0/16. В Lab066
использованы16 штатных полных PHY-пакетов по256 байт при500ксимв/с, Fs8Мвыб/с,
цифровой несущей+50кГц, RF1400МГц, TX VGA20дБ и RX LNA/VGA24/24дБ. RF amplifier
и Bias-Tee выключены; геометрия около1,2м, LOS, антенны и поляризация прежние.
Каждый packet waveform содержит92256 отсчётов. Между пакетами записаны15
фактических нулевых пауз по40000 отсчётов, то есть по5мс. Добавлены начальный и
конечный guards по800000 отсчётов, или по100мс. Длина без guards равна2076096,
полная длина3676096 отсчётов,7352192 байта,0,459512с. SHA-256 TX:
621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199.
Весь конечный TX-файл независимо обработан legacy и T2. Оба режима дали16/16
HEADER, FEC, CRC, packet output и точных payload в правильном порядке. Позиции
пакетов совпали с разметкой пауз. Между режимами fallback отсутствует; T2 для
каждого пакета заново оценивает состояние по его собственной преамбуле.
Программные шлюзы: quick gate1013 тестов ядра/39 функциональных/13 аппаратных
пропусков PASS; pytest1013 passed; diff check PASS. Затем выполнен один RX-only
preflight. Обе HackRF One r9 и роли подтверждены. MASTER/SLAVE reg29=0x26,
SLAVE CLKIN=DETECTED на втором опросе через132,113мс. Фон:800000 отсчётов,
полный RMS2,225641, AC RMS0,827637, max|I|/|Q|=4/5, clipping0, maximum
overruns0, сильного сигнала нет. Cleanup без ошибок, процессы после CLEAR.
Независимый verifier пересчитал SHA, длину,15 нулевых пауз, оба guards,
16 канонических packet waveform, payload и RX-only метрики; расхождений нет.
Read-only adversary проверил запрет TX, одноразовость, связь preflight/capture
с конфигурацией, максимум счётчиков, live-фон перед TX и границу диагностик;
после исправлений открытых замечаний нет.
RF TX=0. Lab066 не коммитировалась, push не выполнялся, Lab067 не создавалась.
Следующий этап: STOP и ожидание отдельного прямого разрешения на один TX Lab066.
После отдельного прямого разрешения выполнен ровно один OTA RF-сеанс Lab066.
Передача всего потока запущена одним вызовом, автоматических повторов не было.
Перед TX MASTER/SLAVE reg29=0x26, SLAVE CLKIN=DETECTED на втором опросе через
131,291мс. Live RX-фон: AC RMS1,173567, max|I|/|Q|=4/5, clipping0. TX underrun
observations0/0, RX overrun observations0/0/0/0; максимумы0/0. RX записал
16000000 отсчётов за2,0с, SHA-256
72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0.
Cleanup без ошибок, процессы после CLEAR; аппаратная запись валидна.
Одна RX-запись независимо обработана legacy и T2 без fallback. Оба режима дали
145 кандидатов,10 HEADER/FEC/CRC PASS,10 packet output и10 точных payload.
Оба приняли пакеты4–8 и10–14; пакеты1–3,9,15,16 остановились на PHY header.
Выигрыш T2 равен нулю.
Диагностика всех16 пакетов после строгого результата дала SER0,0110466,
RMS-отклонение0,528374, matched/residual+5,542117дБ и медианную неравномерность
полосы2,870070дБ. Это лучше Lab056 B на0,0239123 SER,0,316976 RMS,
4,077271дБ matched/residual и2,601312дБ неравномерности. Относительно Lab062
улучшения составляют0,0882400 SER,0,692027 RMS,7,272168дБ matched/residual и
3,790271дБ неравномерности; сравнение с Lab062 контекстное из-за её underrun.
Классификация `PARTIAL_PACKET_RECOVERY_NO_T2_GAIN`: аппаратно чистый сеанс
существенно улучшил физическое качество, но не достиг16/16, а T2 не изменил
строгий результат. RF TX=0, повтор не выполнялся. Lab066 не коммитировалась,
push не выполнялся, Lab067 не создавалась. Следующий возможный одиночный шаг —
отдельно разрешённый офлайн-анализ PHY header не прошедших пакетов по сохранённой
IQ-записи; новый TX не нужен.

View File

@@ -0,0 +1,232 @@
{
"lab": "Lab065",
"stage": "SINGLE_RF_LEVEL_CAPTURE",
"passed": true,
"rf_tx_count": 1,
"automatic_tx_repeats": 0,
"point": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"parameters": {
"rf_frequency_hz": 1400000000,
"sample_rate_hz": 8000000,
"symbol_rate": 20000,
"samples_per_symbol": 400,
"digital_carrier_hz": 50000,
"rrc_rolloff": 0.35,
"rrc_span_symbols": 10,
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
"full_phy_packet": false,
"tx_vga_db": 20,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": false,
"bias_tee": false,
"geometry": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
"levels_db_relative_nominal": [
-18.0,
-12.0,
-6.0,
0.0
],
"geometry_unchanged_from_lab064": true,
"waveform_source": "Lab064 exact CS8 copy"
},
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
"process_before": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 292,
"errors": []
}
]
},
"inventory": {
"passed": true,
"serials": [
"000000000000000097c063dc2e5327d3",
"000000000000000097c063dc323b97d3"
],
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3",
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
},
"clock": {
"passed": true,
"master_reg29": 38,
"slave_reg29": 38,
"slave_clkin_status": "DETECTED",
"slave_clock_wait": {
"state": "DETECTED",
"queries": 2,
"time_to_detect_ms": 136.89709990285337,
"elapsed_ms": 136.89709990285337,
"polls": [
{
"query": 1,
"start_ms": 50.08170008659363,
"end_ms": 67.4550998955965,
"raw": "CLKIN status: no clock signal detected",
"state": "NOT_DETECTED",
"exit_code": 0
},
{
"query": 2,
"start_ms": 117.92290001176298,
"end_ms": 136.89709990285337,
"raw": "CLKIN status: clock signal detected",
"state": "DETECTED",
"exit_code": 0
}
]
},
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3"
},
"rx_level_before_tx": {
"samples": 65536,
"mean_i": 0.9449310302734375,
"mean_q": 1.8423919677734375,
"power_code2": 4.87310791015625,
"ac_power_code2": 0.5858050952665499,
"rms_code": 2.2075117010236323,
"peak_magnitude_code": 4.47213595499958,
"max_abs_i": 3.0,
"max_abs_q": 4.0,
"papr_db": 6.132239671543243,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"saturation_detected": false,
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
},
"tx_process": {
"returncode": 0,
"stdout_file": "tx_stdout.txt",
"stderr_file": "tx_stderr.txt"
},
"tx_counter_summary": {
"expected_kind": "underrun",
"observations": [
{
"count": 0,
"role": "UNKNOWN",
"kind": "underrun",
"text": "0 underruns",
"character_offset": 337
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "underrun",
"text": "0 underruns",
"character_offset": 509
}
],
"selected_counts": [
0,
0
],
"final_count": 0,
"maximum_count": 0,
"unparsed_loss_text": false,
"clean": true
},
"rx_samples": 16000000,
"rx_bytes": 32000000,
"rx_duration_seconds": 2.0,
"rx_counter_summary": {
"expected_kind": "overrun",
"observations": [
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 337
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 459
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 581
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 752
}
],
"selected_counts": [
0,
0,
0,
0
],
"final_count": 0,
"maximum_count": 0,
"unparsed_loss_text": false,
"clean": true
},
"rx_sha256": "845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e",
"rx_level": {
"samples": 16000000,
"mean_i": 0.942780625,
"mean_q": 1.8349653125,
"power_code2": 5.6163274375,
"ac_power_code2": 1.3603944325463857,
"rms_code": 2.3698792031451728,
"peak_magnitude_code": 10.816653826391969,
"max_abs_i": 10.0,
"max_abs_q": 10.0,
"papr_db": 13.187334420841685,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"saturation_detected": false,
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
},
"rx_clipping": false,
"process_after": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 292,
"errors": []
}
]
},
"cleanup_errors": []
}

View File

@@ -0,0 +1,181 @@
{
"lab": "Lab065",
"stage": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT",
"passed": true,
"rf_tx_count": 0,
"stop_before_tx": true,
"point": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"parameters": {
"rf_frequency_hz": 1400000000,
"sample_rate_hz": 8000000,
"symbol_rate": 20000,
"samples_per_symbol": 400,
"digital_carrier_hz": 50000,
"rrc_rolloff": 0.35,
"rrc_span_symbols": 10,
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
"full_phy_packet": false,
"tx_vga_db": 20,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": false,
"bias_tee": false,
"geometry": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
"levels_db_relative_nominal": [
-18.0,
-12.0,
-6.0,
0.0
],
"geometry_unchanged_from_lab064": true,
"waveform_source": "Lab064 exact CS8 copy"
},
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
"process_before": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 289,
"errors": []
}
]
},
"inventory": {
"passed": true,
"serials": [
"000000000000000097c063dc2e5327d3",
"000000000000000097c063dc323b97d3"
],
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3",
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
},
"clock": {
"passed": true,
"master_reg29": 38,
"slave_reg29": 38,
"slave_clkin_status": "DETECTED",
"slave_clock_wait": {
"state": "DETECTED",
"queries": 3,
"time_to_detect_ms": 201.98559993878007,
"elapsed_ms": 201.98559993878007,
"polls": [
{
"query": 1,
"start_ms": 50.617999862879515,
"end_ms": 66.89479993656278,
"raw": "CLKIN status: no clock signal detected",
"state": "NOT_DETECTED",
"exit_code": 0
},
{
"query": 2,
"start_ms": 117.23309988155961,
"end_ms": 136.1208998132497,
"raw": "CLKIN status: no clock signal detected",
"state": "NOT_DETECTED",
"exit_code": 0
},
{
"query": 3,
"start_ms": 187.1324998792261,
"end_ms": 201.98559993878007,
"raw": "CLKIN status: clock signal detected",
"state": "DETECTED",
"exit_code": 0
}
]
},
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3"
},
"rx_process": {
"returncode": 0,
"stdout": "",
"stderr": "call hackrf_set_sample_rate(8000000 Hz/8.000 MHz)\ncall hackrf_set_hw_sync_mode(0)\ncall hackrf_set_freq(1400000000 Hz/1400.000 MHz)\ncall hackrf_set_amp_enable(0)\ncall hackrf_set_antenna_enable(0)\nsamples_to_xfer 800000/0Mio\nStop with Ctrl-C\n 1.8 MiB / 0.116 sec = 15.9 MiB/second, average power -34.8 dBfs, 9024 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11556 s\nhackrf_stop_rx() done\nTransfer statistics:\n1847520 bytes transferred by M0\n1835008 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n"
},
"rx_counter_summary": {
"expected_kind": "overrun",
"observations": [
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 333
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 502
}
],
"selected_counts": [
0,
0
],
"final_count": 0,
"maximum_count": 0,
"unparsed_loss_text": false,
"clean": true
},
"rx_level": {
"samples": 800000,
"mean_i": 0.94889625,
"mean_q": 1.85107375,
"power_code2": 5.4432075,
"ac_power_code2": 1.1163293787968742,
"rms_code": 2.333068258752838,
"peak_magnitude_code": 8.06225774829855,
"max_abs_i": 6.0,
"max_abs_q": 8.0,
"papr_db": 10.770584663112091,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"saturation_detected": false,
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
"symbol_energy": "NOT_APPLICABLE_TO_BACKGROUND",
"ac_rms_code": 1.0565648956864289,
"unexpected_strong_signal": false
},
"rx_sha256": "be3c61dfa1bc40d1a9f6777689026804a1ccfc340b82680d3ec5304ddd68f78c",
"rx_overruns": 0,
"process_after": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 289,
"errors": []
}
]
},
"cleanup_errors": []
}

View File

@@ -0,0 +1,219 @@
{
"lab": "Lab065",
"stage": "PREPARATION_ONLY",
"rf_tx_count": 0,
"head": "cff226e0bf9e36a627829ea11a4ca3292ae20953",
"parameters": {
"rf_frequency_hz": 1400000000,
"sample_rate_hz": 8000000,
"symbol_rate": 20000,
"samples_per_symbol": 400,
"digital_carrier_hz": 50000,
"rrc_rolloff": 0.35,
"rrc_span_symbols": 10,
"prbs": "PRBS7 x^7+x^6+1, deterministic initial state",
"full_phy_packet": false,
"tx_vga_db": 20,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": false,
"bias_tee": false,
"geometry": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
"levels_db_relative_nominal": [
-18.0,
-12.0,
-6.0,
0.0
],
"geometry_unchanged_from_lab064": true,
"waveform_source": "Lab064 exact CS8 copy"
},
"segments": [
{
"label": "L0",
"level_db": -18.0,
"start_sample": 800000,
"samples": 800000,
"duration_seconds": 0.1,
"analysis_start_sample": 808400,
"analysis_samples": 783200,
"metrics": {
"samples": 800000,
"mean_i": -9.625e-05,
"mean_q": 2.75e-05,
"power_code2": 30.87065625,
"ac_power_code2": 30.870656239979688,
"rms_code": 5.556136809870686,
"peak_magnitude_code": 9.219544457292887,
"max_abs_i": 9.0,
"max_abs_q": 9.0,
"papr_db": 4.398730638796093,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0015435328125,
"requested_relative_power_db": -18.0,
"tx_rms_code": 5.556136809870686,
"tx_peak_abs_i_code": 9.0,
"tx_peak_abs_q_code": 9.0,
"tx_peak_magnitude_code": 9.219544457292887,
"digital_clipping": false,
"actual_relative_power_db": -17.97368512020099
}
},
{
"label": "L1",
"level_db": -12.0,
"start_sample": 2000000,
"samples": 800000,
"duration_seconds": 0.1,
"analysis_start_sample": 2008400,
"analysis_samples": 783200,
"metrics": {
"samples": 800000,
"mean_i": -5e-05,
"mean_q": 5e-05,
"power_code2": 122.2567675,
"ac_power_code2": 122.25676749499999,
"rms_code": 11.056978226441435,
"peak_magnitude_code": 17.804493814764857,
"max_abs_i": 17.0,
"max_abs_q": 17.0,
"papr_db": 4.1378635346865025,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.006112838375,
"requested_relative_power_db": -12.0,
"tx_rms_code": 11.056978226441435,
"tx_peak_abs_i_code": 17.0,
"tx_peak_abs_q_code": 17.0,
"tx_peak_magnitude_code": 17.804493814764857,
"digital_clipping": false,
"actual_relative_power_db": -11.99641465105681
},
"tx_input_difference_from_previous_db": 5.9772704691441785
},
{
"label": "L2",
"level_db": -6.0,
"start_sample": 3200000,
"samples": 800000,
"duration_seconds": 0.1,
"analysis_start_sample": 3208400,
"analysis_samples": 783200,
"metrics": {
"samples": 800000,
"mean_i": 5.625e-05,
"mean_q": 2.625e-05,
"power_code2": 486.5064875,
"ac_power_code2": 486.50648749614686,
"rms_code": 22.05689206347984,
"peak_magnitude_code": 35.12833614050059,
"max_abs_i": 34.0,
"max_abs_q": 35.0,
"papr_db": 4.0422652379446395,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.024325324375,
"requested_relative_power_db": -6.0,
"tx_rms_code": 22.05689206347984,
"tx_peak_abs_i_code": 34.0,
"tx_peak_abs_q_code": 35.0,
"tx_peak_magnitude_code": 35.12833614050059,
"digital_clipping": false,
"actual_relative_power_db": -5.998257379520232
},
"tx_input_difference_from_previous_db": 5.998157271536577
},
{
"label": "L3",
"level_db": 0.0,
"start_sample": 4400000,
"samples": 800000,
"duration_seconds": 0.1,
"analysis_start_sample": 4408400,
"analysis_samples": 783200,
"metrics": {
"samples": 800000,
"mean_i": 8.25e-05,
"mean_q": 0.00011125,
"power_code2": 1936.04021375,
"ac_power_code2": 1936.0402137308172,
"rms_code": 44.00045697205883,
"peak_magnitude_code": 69.6419413859206,
"max_abs_i": 69.0,
"max_abs_q": 69.0,
"papr_db": 3.9882736474748155,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0968020106875,
"requested_relative_power_db": 0.0,
"tx_rms_code": 44.00045697205883,
"tx_peak_abs_i_code": 69.0,
"tx_peak_abs_q_code": 69.0,
"tx_peak_magnitude_code": 69.6419413859206,
"digital_clipping": false,
"actual_relative_power_db": 0.0
},
"tx_input_difference_from_previous_db": 5.998257379520232
}
],
"tx_samples": 6000000,
"tx_bytes": 12000000,
"tx_duration_seconds": 0.75,
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
"source_tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
"waveform_unchanged_from_lab064": true,
"automatic_retry": false,
"tx_command_prepared_not_run": [
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
"-d",
"000000000000000097c063dc323b97d3",
"-t",
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab065\\tx_level_staircase.cs8",
"-f",
"1400000000",
"-s",
"8000000",
"-n",
"6000000",
"-x",
"20",
"-a",
"0",
"-p",
"0",
"-B"
],
"rx_command_prepared_not_run": [
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
"-d",
"000000000000000097c063dc2e5327d3",
"-r",
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab065\\rx_stream.cs8",
"-f",
"1400000000",
"-s",
"8000000",
"-n",
"16000000",
"-l",
"24",
"-g",
"24",
"-a",
"0",
"-p",
"0",
"-B"
]
}

View File

@@ -0,0 +1,515 @@
{
"lab": "Lab065",
"stage": "OFFLINE_RF_LEVEL_LINEARITY_ANALYSIS",
"packet_receiver_used": false,
"location": {
"start_sample": 4217216,
"correlation": 0.28566040369708917,
"coarse_cfo_hz": 0.0,
"stride": 32
},
"rows": [
{
"label": "L0",
"level_db": -18.0,
"rx_start_sample": 5017215,
"alignment_adjustment_samples": -1,
"full_iq": {
"samples": 800000,
"mean_i": 0.9456825,
"mean_q": 1.849345,
"power_code2": 5.91858,
"ac_power_code2": 1.6041876801687494,
"rms_code": 2.4328131864160882,
"peak_magnitude_code": 7.280109889280518,
"max_abs_i": 6.0,
"max_abs_q": 7.0,
"papr_db": 9.520583473606806,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.000295929,
"ac_rms_code": 1.2665653082919766
},
"background_label": "INITIAL_GUARD",
"background": {
"samples": 160000,
"mean_i": 0.9375625,
"mean_q": 1.83864375,
"power_code2": 4.84290625,
"ac_power_code2": 0.5832719691796884,
"rms_code": 2.200660412239926,
"peak_magnitude_code": 4.47213595499958,
"max_abs_i": 3.0,
"max_abs_q": 4.0,
"papr_db": 6.1592393370019245,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.00024214531250000003,
"ac_rms_code": 0.7637224425009969
},
"matched_signal_power": 0.009849829325472823,
"residual_power": 1.147671646182205,
"matched_to_residual_db": -20.663889470509574,
"complex_gain_h": {
"real": -0.0069147093491316635,
"imag": 0.016278708105995708,
"phase_rad_at_active_start": 1.9724722764994853
},
"gain_abs": 0.01768642256034779,
"gain_power_abs_h_squared": 0.00031280954298317925,
"cfo_hz": 0.051385857393477286,
"residual_spectrum": {
"residual_band_h1_power_code2": 0.005700729949335216,
"residual_band_h2_power_code2": 0.004639110794845057,
"residual_band_h3_power_code2": 0.003007515042889266,
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
}
},
{
"label": "L1",
"level_db": -12.0,
"rx_start_sample": 6217220,
"alignment_adjustment_samples": 4,
"full_iq": {
"samples": 800000,
"mean_i": 0.943035,
"mean_q": 1.84948125,
"power_code2": 5.45620125,
"ac_power_code2": 1.1463053446734381,
"rms_code": 2.335851290215197,
"peak_magnitude_code": 7.810249675906654,
"max_abs_i": 7.0,
"max_abs_q": 7.0,
"papr_db": 10.48439454272797,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.00027281006250000003,
"ac_rms_code": 1.0706565017191265
},
"background_label": "GAP_L0_L1",
"background": {
"samples": 160000,
"mean_i": 0.94049375,
"mean_q": 1.8457,
"power_code2": 4.89708125,
"ac_power_code2": 0.6059442662109387,
"rms_code": 2.2129349854887286,
"peak_magnitude_code": 5.0990195135927845,
"max_abs_i": 4.0,
"max_abs_q": 5.0,
"papr_db": 7.250360382891476,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0002448540625,
"ac_rms_code": 0.7784242199539648
},
"matched_signal_power": 0.038319468321711674,
"residual_power": 0.6202840436902609,
"matched_to_residual_db": -12.091711347697867,
"complex_gain_h": {
"real": -0.010179637303215595,
"imag": 0.014270629544800722,
"phase_rad_at_active_start": 2.1904112551609387
},
"gain_abs": 0.017529286443833295,
"gain_power_abs_h_squared": 0.00030727588322995773,
"cfo_hz": -0.015456037277818255,
"residual_spectrum": {
"residual_band_h1_power_code2": 0.0075272705585461884,
"residual_band_h2_power_code2": 0.005178438128918921,
"residual_band_h3_power_code2": 0.0030044589971250603,
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
}
},
{
"label": "L2",
"level_db": -6.0,
"rx_start_sample": 7417219,
"alignment_adjustment_samples": 3,
"full_iq": {
"samples": 800000,
"mean_i": 0.94256,
"mean_q": 1.85174125,
"power_code2": 5.55749625,
"ac_power_code2": 1.2401312394484378,
"rms_code": 2.3574342514691686,
"peak_magnitude_code": 7.280109889280518,
"max_abs_i": 6.0,
"max_abs_q": 7.0,
"papr_db": 9.793966912760688,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0002778748125,
"ac_rms_code": 1.1136117992588073
},
"background_label": "GAP_L1_L2",
"background": {
"samples": 160000,
"mean_i": 0.94511875,
"mean_q": 1.8435375,
"power_code2": 4.89561875,
"ac_power_code2": 0.6037387844921858,
"rms_code": 2.2126045173053406,
"peak_magnitude_code": 8.06225774829855,
"max_abs_i": 7.0,
"max_abs_q": 7.0,
"papr_db": 11.231057671986083,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0002447809375,
"ac_rms_code": 0.7770062963015073
},
"matched_signal_power": 0.15371936701352645,
"residual_power": 0.7099975700355287,
"matched_to_residual_db": -6.645282748943733,
"complex_gain_h": {
"real": -0.00913356439809709,
"imag": 0.015044605957089683,
"phase_rad_at_active_start": 2.1164192954656
},
"gain_abs": 0.01760006156291179,
"gain_power_abs_h_squared": 0.000309762167018285,
"cfo_hz": -0.02238057704898943,
"residual_spectrum": {
"residual_band_h1_power_code2": 0.016197144376941752,
"residual_band_h2_power_code2": 0.007835153285271243,
"residual_band_h3_power_code2": 0.003956469302302745,
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
}
},
{
"label": "L3",
"level_db": 0.0,
"rx_start_sample": 8617220,
"alignment_adjustment_samples": 4,
"full_iq": {
"samples": 800000,
"mean_i": 0.941795,
"mean_q": 1.85647375,
"power_code2": 6.38914875,
"ac_power_code2": 2.055676143535938,
"rms_code": 2.52767655169723,
"peak_magnitude_code": 8.602325267042627,
"max_abs_i": 7.0,
"max_abs_q": 8.0,
"papr_db": 10.637887203839098,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.00031945743750000003,
"ac_rms_code": 1.4337629314276255
},
"background_label": "GAP_L2_L3",
"background": {
"samples": 160000,
"mean_i": 0.9481,
"mean_q": 1.8589625,
"power_code2": 4.9655625,
"ac_power_code2": 0.6109273135937494,
"rms_code": 2.228354213315289,
"peak_magnitude_code": 6.324555320336759,
"max_abs_i": 5.0,
"max_abs_q": 6.0,
"papr_db": 9.060915387364618,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.000248278125,
"ac_rms_code": 0.7816183938430246
},
"matched_signal_power": 0.609773488771332,
"residual_power": 1.8217771923731447,
"matched_to_residual_db": -4.753267221075902,
"complex_gain_h": {
"real": -0.009107358713761582,
"imag": 0.015027742846344474,
"phase_rad_at_active_start": 2.1156425161969685
},
"gain_abs": 0.017572052748526183,
"gain_power_abs_h_squared": 0.0003087770377969866,
"cfo_hz": -0.006443832004222744,
"residual_spectrum": {
"residual_band_h1_power_code2": 0.05148134451389538,
"residual_band_h2_power_code2": 0.019651663054535452,
"residual_band_h3_power_code2": 0.008610269373925542,
"interpretation": "Diagnostic residual bands at 50/100/150 kHz; not calibrated RF harmonic power"
}
}
],
"background_windows": [
{
"label": "INITIAL_GUARD",
"tx_relative_start_sample": 320000,
"samples": 160000,
"duration_seconds": 0.02,
"rx_start_sample": 4537216,
"metrics": {
"samples": 160000,
"mean_i": 0.9375625,
"mean_q": 1.83864375,
"power_code2": 4.84290625,
"ac_power_code2": 0.5832719691796884,
"rms_code": 2.200660412239926,
"peak_magnitude_code": 4.47213595499958,
"max_abs_i": 3.0,
"max_abs_q": 4.0,
"papr_db": 6.1592393370019245,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.00024214531250000003,
"ac_rms_code": 0.7637224425009969
}
},
{
"label": "GAP_L0_L1",
"tx_relative_start_sample": 1760000,
"samples": 160000,
"duration_seconds": 0.02,
"rx_start_sample": 5977216,
"metrics": {
"samples": 160000,
"mean_i": 0.94049375,
"mean_q": 1.8457,
"power_code2": 4.89708125,
"ac_power_code2": 0.6059442662109387,
"rms_code": 2.2129349854887286,
"peak_magnitude_code": 5.0990195135927845,
"max_abs_i": 4.0,
"max_abs_q": 5.0,
"papr_db": 7.250360382891476,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0002448540625,
"ac_rms_code": 0.7784242199539648
}
},
{
"label": "GAP_L1_L2",
"tx_relative_start_sample": 2960000,
"samples": 160000,
"duration_seconds": 0.02,
"rx_start_sample": 7177216,
"metrics": {
"samples": 160000,
"mean_i": 0.94511875,
"mean_q": 1.8435375,
"power_code2": 4.89561875,
"ac_power_code2": 0.6037387844921858,
"rms_code": 2.2126045173053406,
"peak_magnitude_code": 8.06225774829855,
"max_abs_i": 7.0,
"max_abs_q": 7.0,
"papr_db": 11.231057671986083,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0002447809375,
"ac_rms_code": 0.7770062963015073
}
},
{
"label": "GAP_L2_L3",
"tx_relative_start_sample": 4160000,
"samples": 160000,
"duration_seconds": 0.02,
"rx_start_sample": 8377216,
"metrics": {
"samples": 160000,
"mean_i": 0.9481,
"mean_q": 1.8589625,
"power_code2": 4.9655625,
"ac_power_code2": 0.6109273135937494,
"rms_code": 2.228354213315289,
"peak_magnitude_code": 6.324555320336759,
"max_abs_i": 5.0,
"max_abs_q": 6.0,
"papr_db": 9.060915387364618,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.000248278125,
"ac_rms_code": 0.7816183938430246
}
},
{
"label": "FINAL_GUARD",
"tx_relative_start_sample": 5440000,
"samples": 160000,
"duration_seconds": 0.02,
"rx_start_sample": 9657216,
"metrics": {
"samples": 160000,
"mean_i": 0.94553125,
"mean_q": 1.8361,
"power_code2": 5.00006875,
"ac_power_code2": 0.7347761952734375,
"rms_code": 2.2360833504142907,
"peak_magnitude_code": 9.486832980505138,
"max_abs_i": 9.0,
"max_abs_q": 9.0,
"papr_db": 12.55266533595234,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.0002500034375,
"ac_rms_code": 0.8571908744692966
}
}
],
"background_summary": {
"ac_power_min_code2": 0.5832719691796884,
"ac_power_max_code2": 0.7347761952734375,
"ac_rms_min_code": 0.7637224425009969,
"ac_rms_max_code": 0.8571908744692966,
"maximum_to_minimum_ac_power_db": 1.0028397226073626,
"stability_rule_db": 3.0,
"stable": true
},
"comparisons": [
{
"comparison": "L1/L0",
"tx_input_difference_db": 5.9772704691441785,
"rx_matched_power_difference_db": 5.899907694072936,
"linearity_error_db": -0.07736277507124267
},
{
"comparison": "L2/L1",
"tx_input_difference_db": 5.998157271536577,
"rx_matched_power_difference_db": 6.033091127942342,
"linearity_error_db": 0.03493385640576463
},
{
"comparison": "L3/L2",
"tx_input_difference_db": 5.998257379520232,
"rx_matched_power_difference_db": 5.9843995108689985,
"linearity_error_db": -0.013857868651233396
}
],
"capture_valid": true,
"capture_invalid_reasons": [],
"tx_underruns_maximum": 0,
"rx_overruns_maximum": 0,
"rx_clipping": false,
"lab064_to_lab065": {
"rows": [
{
"label": "L0",
"tx_vga_nominal_difference_db": 10.0,
"rx_matched_power_difference_db": 11.179327767173566,
"gain_power_abs_h_squared_difference_db": 11.179327767173568,
"residual_power_difference_db": -0.24327621738947727,
"matched_to_residual_improvement_db": 11.422603984563047,
"complex_gain_h_old": {
"real": 0.0018760642981310563,
"imag": -0.0045080629571906915,
"phase_rad_at_active_start": -1.1764391150184978
},
"complex_gain_h_new": {
"real": -0.0069147093491316635,
"imag": 0.016278708105995708,
"phase_rad_at_active_start": 1.9724722764994853
},
"complex_gain_phase_change_degrees": -179.5806672053524,
"residual_spectral_band_difference_db": {
"h1_50000_hz": 2.869745130254167,
"h2_100000_hz": 2.6545689216728405,
"h3_150000_hz": 2.25865088789477
}
},
{
"label": "L1",
"tx_vga_nominal_difference_db": 10.0,
"rx_matched_power_difference_db": 11.119465772157373,
"gain_power_abs_h_squared_difference_db": 11.119465772157373,
"residual_power_difference_db": -2.3662859353114283,
"matched_to_residual_improvement_db": 13.4857517074688,
"complex_gain_h_old": {
"real": 0.004471119012843702,
"imag": -0.001937685853593986,
"phase_rad_at_active_start": -0.40894566669212534
},
"complex_gain_h_new": {
"real": -0.010179637303215595,
"imag": 0.014270629544800722,
"phase_rad_at_active_start": 2.1904112551609387
},
"complex_gain_phase_change_degrees": 148.9321810702975,
"residual_spectral_band_difference_db": {
"h1_50000_hz": 3.0326799855695237,
"h2_100000_hz": 2.378560725133286,
"h3_150000_hz": 1.3007160057992286
}
},
{
"label": "L2",
"tx_vga_nominal_difference_db": 10.0,
"rx_matched_power_difference_db": 11.323669836462136,
"gain_power_abs_h_squared_difference_db": 11.323669836462136,
"residual_power_difference_db": 1.2772838599246652,
"matched_to_residual_improvement_db": 10.046385976537472,
"complex_gain_h_old": {
"real": 0.0032772531007889995,
"imag": -0.003478187284280655,
"phase_rad_at_active_start": -0.8151334504253348
},
"complex_gain_h_new": {
"real": -0.00913356439809709,
"imag": 0.015044605957089683,
"phase_rad_at_active_start": 2.1164192954656
},
"complex_gain_phase_change_degrees": 167.96559975953804,
"residual_spectral_band_difference_db": {
"h1_50000_hz": 5.594944314904455,
"h2_100000_hz": 4.091263491385863,
"h3_150000_hz": 2.888396722883724
}
},
{
"label": "L3",
"tx_vga_nominal_difference_db": 10.0,
"rx_matched_power_difference_db": 11.287649575482401,
"gain_power_abs_h_squared_difference_db": 11.287649575482403,
"residual_power_difference_db": 3.907580735290519,
"matched_to_residual_improvement_db": 7.380068840191884,
"complex_gain_h_old": {
"real": 0.003384387504328541,
"imag": -0.003391322182689881,
"phase_rad_at_active_start": -0.7864216245934512
},
"complex_gain_h_new": {
"real": -0.009107358713761582,
"imag": 0.015027742846344474,
"phase_rad_at_active_start": 2.1156425161969685
},
"complex_gain_phase_change_degrees": 166.2760271435506,
"residual_spectral_band_difference_db": {
"h1_50000_hz": 7.304592641009381,
"h2_100000_hz": 6.432280867349566,
"h3_150000_hz": 4.996231985221501
}
}
],
"rules_fixed_before_rf": {
"internal_linear_max_abs_error_db": 1.0,
"compression_upper_step_error_at_most_db": -1.5,
"meaningful_matched_gain_median_at_least_db": 3.0,
"meaningful_ratio_improvement_median_at_least_db": 3.0,
"maximum_level_to_level_gain_spread_db": 3.0,
"residual_tracks_if_within_matched_gain_db": 3.0
}
},
"classification": {
"case": "LINEAR_GAIN_IMPROVEMENT",
"summary": {
"median_matched_gain_db": 11.233488671327983,
"median_residual_gain_db": 0.5170038212675939,
"median_matched_to_residual_improvement_db": 10.73449498055026,
"matched_gain_level_spread_db": 0.20420406430476312,
"maximum_internal_linearity_error_db": 0.07736277507124267,
"upper_step_linearity_error_db": -0.013857868651233396
},
"rules_fixed_before_rf": {
"internal_linear_max_abs_error_db": 1.0,
"compression_upper_step_error_at_most_db": -1.5,
"meaningful_matched_gain_median_at_least_db": 3.0,
"meaningful_ratio_improvement_median_at_least_db": 3.0,
"maximum_level_to_level_gain_spread_db": 3.0,
"residual_tracks_if_within_matched_gain_db": 3.0
}
}
}

View File

@@ -0,0 +1,17 @@
{
"lab": "Lab065",
"stage": "SOFTWARE_GATE",
"rf_tx_count": 0,
"passed": true,
"tx_sha256": "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9",
"waveform_unchanged_from_lab064": true,
"lab064_baseline_classification": "LINEAR_BUT_WEAK",
"classification_rules": {
"internal_linear_max_abs_error_db": 1.0,
"compression_upper_step_error_at_most_db": -1.5,
"meaningful_matched_gain_median_at_least_db": 3.0,
"meaningful_ratio_improvement_median_at_least_db": 3.0,
"maximum_level_to_level_gain_spread_db": 3.0,
"residual_tracks_if_within_matched_gain_db": 3.0
}
}

View File

@@ -0,0 +1,224 @@
{
"lab": "Lab066",
"stage": "SINGLE_RF_CAPTURE",
"passed": true,
"rf_tx_count": 1,
"automatic_tx_repeats": 0,
"point": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"parameters": {
"rf_frequency_hz": 1400000000,
"sample_rate_hz": 8000000,
"symbol_rate": 500000,
"samples_per_symbol": 16,
"digital_carrier_hz": 50000.0,
"packets": 16,
"payload_bytes": 256,
"tx_vga_db": 20,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": false,
"bias_tee": false,
"geometry": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"geometry_unchanged": true,
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
},
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
"process_before": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 300,
"errors": []
}
]
},
"inventory": {
"passed": true,
"serials": [
"000000000000000097c063dc2e5327d3",
"000000000000000097c063dc323b97d3"
],
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3",
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
},
"clock": {
"passed": true,
"master_reg29": 38,
"slave_reg29": 38,
"slave_clkin_status": "DETECTED",
"slave_clock_wait": {
"state": "DETECTED",
"queries": 2,
"time_to_detect_ms": 131.2907999381423,
"elapsed_ms": 131.2907999381423,
"polls": [
{
"query": 1,
"start_ms": 50.125200068578124,
"end_ms": 66.83470006100833,
"raw": "CLKIN status: no clock signal detected",
"state": "NOT_DETECTED",
"exit_code": 0
},
{
"query": 2,
"start_ms": 116.86519999057055,
"end_ms": 131.2907999381423,
"raw": "CLKIN status: clock signal detected",
"state": "DETECTED",
"exit_code": 0
}
]
},
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3"
},
"rx_level_before_tx": {
"samples": 65536,
"mean_i": 0.952423095703125,
"mean_q": 1.83642578125,
"power_code2": 5.656829833984375,
"ac_power_code2": 1.377260430715979,
"rms_code": 2.3784090972716143,
"peak_magnitude_code": 5.385164807134504,
"max_abs_i": 4.0,
"max_abs_q": 5.0,
"papr_db": 7.0982488319834305,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"saturation_detected": false,
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
"ac_rms_code": 1.173567395046394,
"unexpected_strong_signal": false
},
"tx_process": {
"returncode": 0,
"stdout_file": "tx_stdout.txt",
"stderr_file": "tx_stderr.txt"
},
"tx_counter_summary": {
"expected_kind": "underrun",
"observations": [
{
"count": 0,
"role": "UNKNOWN",
"kind": "underrun",
"text": "0 underruns",
"character_offset": 336
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "underrun",
"text": "0 underruns",
"character_offset": 506
}
],
"selected_counts": [
0,
0
],
"final_count": 0,
"maximum_count": 0,
"unparsed_loss_text": false,
"clean": true
},
"rx_counter_summary": {
"expected_kind": "overrun",
"observations": [
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 337
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 459
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 580
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 751
}
],
"selected_counts": [
0,
0,
0,
0
],
"final_count": 0,
"maximum_count": 0,
"unparsed_loss_text": false,
"clean": true
},
"rx_samples": 16000000,
"rx_sha256": "72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0",
"rx_level": {
"samples": 16000000,
"mean_i": 0.9510684375,
"mean_q": 1.844211625,
"power_code2": 5.0153848125,
"ac_power_code2": 0.7097371219061666,
"rms_code": 2.239505483918269,
"peak_magnitude_code": 5.830951894845301,
"max_abs_i": 4.0,
"max_abs_q": 5.0,
"papr_db": 8.311746565484663,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"symbol_energy_code2_seconds": 0.000250769240625,
"saturation_detected": false,
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION"
},
"rx_clipping": false,
"process_after": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 300,
"errors": []
}
]
},
"cleanup_errors": []
}

View File

@@ -0,0 +1,17 @@
packet_index,legacy_header,legacy_fec,legacy_crc,legacy_packet_output,t2_header,t2_fec,t2_crc,t2_packet_output
1,False,False,False,False,False,False,False,False
2,False,False,False,False,False,False,False,False
3,False,False,False,False,False,False,False,False
4,True,True,True,True,True,True,True,True
5,True,True,True,True,True,True,True,True
6,True,True,True,True,True,True,True,True
7,True,True,True,True,True,True,True,True
8,True,True,True,True,True,True,True,True
9,False,False,False,False,False,False,False,False
10,True,True,True,True,True,True,True,True
11,True,True,True,True,True,True,True,True
12,True,True,True,True,True,True,True,True
13,True,True,True,True,True,True,True,True
14,True,True,True,True,True,True,True,True
15,False,False,False,False,False,False,False,False
16,False,False,False,False,False,False,False,False
1 packet_index legacy_header legacy_fec legacy_crc legacy_packet_output t2_header t2_fec t2_crc t2_packet_output
2 1 False False False False False False False False
3 2 False False False False False False False False
4 3 False False False False False False False False
5 4 True True True True True True True True
6 5 True True True True True True True True
7 6 True True True True True True True True
8 7 True True True True True True True True
9 8 True True True True True True True True
10 9 False False False False False False False False
11 10 True True True True True True True True
12 11 True True True True True True True True
13 12 True True True True True True True True
14 13 True True True True True True True True
15 14 True True True True True True True True
16 15 False False False False False False False False
17 16 False False False False False False False False

View File

@@ -0,0 +1,164 @@
{
"lab": "Lab066",
"stage": "RX_ONLY_PREFLIGHT",
"passed": true,
"rf_tx_count": 0,
"point": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"parameters": {
"rf_frequency_hz": 1400000000,
"sample_rate_hz": 8000000,
"symbol_rate": 500000,
"samples_per_symbol": 16,
"digital_carrier_hz": 50000.0,
"packets": 16,
"payload_bytes": 256,
"tx_vga_db": 20,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": false,
"bias_tee": false,
"geometry": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"geometry_unchanged": true,
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
},
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
"stop_before_tx": true,
"process_before": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 294,
"errors": []
}
]
},
"inventory": {
"passed": true,
"serials": [
"000000000000000097c063dc2e5327d3",
"000000000000000097c063dc323b97d3"
],
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3",
"raw_output": "hackrf_info version: 2024.02.1\nlibhackrf version: 2024.02.1 (0.9)\nFound HackRF\nIndex: 0\nSerial number: 000000000000000097c063dc323b97d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0x00744f5a\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One\n\nFound HackRF\nIndex: 1\nSerial number: 000000000000000097c063dc2e5327d3\nBoard ID Number: 4 (HackRF One)\nFirmware Version: 2024.02.1 (API:1.08)\nPart ID Number: 0xa000cb3c 0xbc4b4739\nHardware Revision: r9\nHardware does not appear to have been manufactured by Great Scott Gadgets.\nHardware supported by installed firmware:\n HackRF One"
},
"clock": {
"passed": true,
"master_reg29": 38,
"slave_reg29": 38,
"slave_clkin_status": "DETECTED",
"slave_clock_wait": {
"state": "DETECTED",
"queries": 2,
"time_to_detect_ms": 132.11270002648234,
"elapsed_ms": 132.11270002648234,
"polls": [
{
"query": 1,
"start_ms": 50.09360006079078,
"end_ms": 66.02549995295703,
"raw": "CLKIN status: no clock signal detected",
"state": "NOT_DETECTED",
"exit_code": 0
},
{
"query": 2,
"start_ms": 116.46570009179413,
"end_ms": 132.11270002648234,
"raw": "CLKIN status: clock signal detected",
"state": "DETECTED",
"exit_code": 0
}
]
},
"master_serial": "000000000000000097c063dc323b97d3",
"slave_serial": "000000000000000097c063dc2e5327d3"
},
"rx_process": {
"returncode": 0,
"stdout": "",
"stderr": "call hackrf_set_sample_rate(8000000 Hz/8.000 MHz)\ncall hackrf_set_hw_sync_mode(0)\ncall hackrf_set_freq(1400000000 Hz/1400.000 MHz)\ncall hackrf_set_amp_enable(0)\ncall hackrf_set_antenna_enable(0)\nsamples_to_xfer 800000/0Mio\nStop with Ctrl-C\n 1.8 MiB / 0.115 sec = 15.9 MiB/second, average power -35.2 dBfs, 11712 bytes free in buffer, 0 overruns, longest 0 bytes\n\nExiting...\nTotal time: 0.11546 s\nhackrf_stop_rx() done\nTransfer statistics:\n1850016 bytes transferred by M0\n1835008 bytes transferred by M4\n0 overruns, longest 0 bytes\nhackrf_close() done\nhackrf_exit() done\nfclose() done\nexit\n"
},
"rx_counter_summary": {
"expected_kind": "overrun",
"observations": [
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 334
},
{
"count": 0,
"role": "UNKNOWN",
"kind": "overrun",
"text": "0 overruns",
"character_offset": 503
}
],
"selected_counts": [
0,
0
],
"final_count": 0,
"maximum_count": 0,
"unparsed_loss_text": false,
"clean": true
},
"rx_level": {
"samples": 800000,
"mean_i": 0.94753,
"mean_q": 1.83594125,
"power_code2": 4.95347625,
"ac_power_code2": 0.6849828756484371,
"rms_code": 2.2256406381084974,
"peak_magnitude_code": 5.385164807134504,
"max_abs_i": 4.0,
"max_abs_q": 5.0,
"papr_db": 7.674879128392212,
"clipped_components_fraction": 0.0,
"clipped_samples_fraction": 0.0,
"saturation_detected": false,
"analog_compression": "UNKNOWN_WITHOUT_CALIBRATION",
"symbol_energy": "NOT_APPLICABLE_TO_BACKGROUND",
"ac_rms_code": 0.8276369225985735,
"unexpected_strong_signal": false
},
"rx_sha256": "ff817582e6a8fc03b489bf89f46322a9db8a9493335a4360a1b0f9bedad7c4d5",
"rx_overruns": 0,
"process_after": {
"state": "CLEAR",
"attempts": [
{
"state": "CLEAR",
"mechanism": "psutil",
"matches": [],
"inspected": 294,
"errors": []
}
]
},
"cleanup_errors": []
}

View File

@@ -0,0 +1,217 @@
{
"lab": "Lab066",
"stage": "PREPARATION_ONLY",
"rf_tx_count": 0,
"head": "dacf04f2d0a15c0c1b56a7fb527db61fc2e38462",
"parameters": {
"rf_frequency_hz": 1400000000,
"sample_rate_hz": 8000000,
"symbol_rate": 500000,
"samples_per_symbol": 16,
"digital_carrier_hz": 50000.0,
"packets": 16,
"payload_bytes": 256,
"tx_vga_db": 20,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": false,
"bias_tee": false,
"geometry": {
"frequency_hz": 1400000000,
"physical_path": "MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
"path_confirmed": true,
"tx_antenna": "existing 1400 MHz antenna near window",
"rx_antenna": "existing 1400 MHz antenna on desk",
"distance_m": 1.2,
"orientation": "same polarization; fixed during preflight and RF session",
"visibility": "LOS indoor"
},
"geometry_unchanged": true,
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN"
},
"packet_samples": 92256,
"inter_packet_gap_seconds": 0.005,
"inter_packet_gap_samples": 40000,
"number_of_gaps": 15,
"zero_guard_before_samples": 800000,
"zero_guard_after_samples": 800000,
"base_tx_samples": 2076096,
"expected_tx_samples": 3676096,
"tx_samples": 3676096,
"tx_bytes": 7352192,
"tx_duration_seconds": 0.459512,
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
"layout": [
{
"packet_index": 1,
"start_sample": 800000,
"samples": 92256,
"payload_sha256": "414dee46d0555da2caabe0d4e446eb52d22be2f380ed4529fd7f192420d06465",
"following_gap_start_sample": 892256,
"following_gap_samples": 40000
},
{
"packet_index": 2,
"start_sample": 932256,
"samples": 92256,
"payload_sha256": "f5304773cc626858c4ad1cfe509bfe40418cfee6d9c65718c7c271c664465f2d",
"following_gap_start_sample": 1024512,
"following_gap_samples": 40000
},
{
"packet_index": 3,
"start_sample": 1064512,
"samples": 92256,
"payload_sha256": "e5604108e4a0a0441b962cf65822838d817a228d5df899dfd64dab958f32a59c",
"following_gap_start_sample": 1156768,
"following_gap_samples": 40000
},
{
"packet_index": 4,
"start_sample": 1196768,
"samples": 92256,
"payload_sha256": "43a7b5866bcf826920aae650830abfd2fa75b6e5460078c1bbde3ec7b461703f",
"following_gap_start_sample": 1289024,
"following_gap_samples": 40000
},
{
"packet_index": 5,
"start_sample": 1329024,
"samples": 92256,
"payload_sha256": "46836a613f080ddeba9c1b23bc8d8dd981db24975115074fa01b31699099efb8",
"following_gap_start_sample": 1421280,
"following_gap_samples": 40000
},
{
"packet_index": 6,
"start_sample": 1461280,
"samples": 92256,
"payload_sha256": "6ff8c9b941d37c39834f8cc5be3fdf563b3e55c51fc0c7d7344d4e046a15d13c",
"following_gap_start_sample": 1553536,
"following_gap_samples": 40000
},
{
"packet_index": 7,
"start_sample": 1593536,
"samples": 92256,
"payload_sha256": "273543a396d7499a1e1b325415b38555c72bb2665a227cd88aff5b797e18875d",
"following_gap_start_sample": 1685792,
"following_gap_samples": 40000
},
{
"packet_index": 8,
"start_sample": 1725792,
"samples": 92256,
"payload_sha256": "20051b24e17274d3b0cf3c170ea09559409c18ff429a3020ff28cb83d9b7bb3b",
"following_gap_start_sample": 1818048,
"following_gap_samples": 40000
},
{
"packet_index": 9,
"start_sample": 1858048,
"samples": 92256,
"payload_sha256": "9239a9fdb3fc94354cc8372dfa06867a18500ad13f867ea3a4cd4468e76bb9f0",
"following_gap_start_sample": 1950304,
"following_gap_samples": 40000
},
{
"packet_index": 10,
"start_sample": 1990304,
"samples": 92256,
"payload_sha256": "997be4e3b7a26708a48afd8ac0932460dbb34d67a359f1770dd76214f810e658",
"following_gap_start_sample": 2082560,
"following_gap_samples": 40000
},
{
"packet_index": 11,
"start_sample": 2122560,
"samples": 92256,
"payload_sha256": "fb1d718a4c2fa358f24ba34197b3100384b0df4229efae846944949894526903",
"following_gap_start_sample": 2214816,
"following_gap_samples": 40000
},
{
"packet_index": 12,
"start_sample": 2254816,
"samples": 92256,
"payload_sha256": "778de948620ddcf8c70f639f72ed042f97915234a57055421af9cbeb16c47e1f",
"following_gap_start_sample": 2347072,
"following_gap_samples": 40000
},
{
"packet_index": 13,
"start_sample": 2387072,
"samples": 92256,
"payload_sha256": "cabcd273073d70d686900e180be3c59e970cb3633bb370ba2198e64540727190",
"following_gap_start_sample": 2479328,
"following_gap_samples": 40000
},
{
"packet_index": 14,
"start_sample": 2519328,
"samples": 92256,
"payload_sha256": "3d6fda80bdd274d781399b1d1831cb4cb4a7c17bcd69930330564bf0ed6eb125",
"following_gap_start_sample": 2611584,
"following_gap_samples": 40000
},
{
"packet_index": 15,
"start_sample": 2651584,
"samples": 92256,
"payload_sha256": "ece74586bedf93fc3f5e07f41f64eea4383425d43424fe209ca30d911279c6ff",
"following_gap_start_sample": 2743840,
"following_gap_samples": 40000
},
{
"packet_index": 16,
"start_sample": 2783840,
"samples": 92256,
"payload_sha256": "bba4133aea193e327938abc18d2a0215f1df0e821d378d530dc0b47498af9a5c"
}
],
"rx_samples": 16000000,
"rx_duration_seconds": 2,
"automatic_retry": false,
"tx_command_prepared_not_run": [
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
"-d",
"000000000000000097c063dc323b97d3",
"-t",
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab066\\tx_stream.cs8",
"-f",
"1400000000",
"-s",
"8000000",
"-n",
"3676096",
"-x",
"20",
"-a",
"0",
"-p",
"0",
"-B"
],
"rx_command_prepared_not_run": [
"C:\\Users\\user\\Tools\\HackRF\\Library\\bin\\hackrf_transfer.exe",
"-d",
"000000000000000097c063dc2e5327d3",
"-r",
"C:\\Users\\user\\Desktop\\projects\\SDR_Rover\\data\\raw\\lab066\\rx_stream.cs8",
"-f",
"1400000000",
"-s",
"8000000",
"-n",
"16000000",
"-l",
"24",
"-g",
"24",
"-a",
"0",
"-p",
"0",
"-B"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,382 @@
{
"lab": "Lab066",
"capture_valid": true,
"legacy": {
"candidates": 145,
"header_pass": 10,
"fec_started": 10,
"fec_pass": 10,
"crc_started": 10,
"crc_pass": 10,
"packet_output": 10,
"exact_payload_match": 10,
"packet_indexes": [
4,
5,
6,
7,
8,
10,
11,
12,
13,
14
]
},
"t2": {
"candidates": 145,
"header_pass": 10,
"fec_started": 10,
"fec_pass": 10,
"crc_started": 10,
"crc_pass": 10,
"packet_output": 10,
"exact_payload_match": 10,
"packet_indexes": [
4,
5,
6,
7,
8,
10,
11,
12,
13,
14
]
},
"passed_by_both": [
4,
5,
6,
7,
8,
10,
11,
12,
13,
14
],
"passed_only_by_legacy": [],
"passed_only_by_t2": [],
"failed_by_both": [
1,
2,
3,
9,
15,
16
],
"packet_rows": [
{
"packet_index": 1,
"legacy": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
},
"t2": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
}
},
{
"packet_index": 2,
"legacy": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
},
"t2": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
}
},
{
"packet_index": 3,
"legacy": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
},
"t2": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
}
},
{
"packet_index": 4,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 5,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 6,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 7,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 8,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 9,
"legacy": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
},
"t2": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
}
},
{
"packet_index": 10,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 11,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 12,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 13,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 14,
"legacy": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
},
"t2": {
"candidate_at_expected_packet": true,
"header": true,
"fec": true,
"crc": true,
"packet_output": true
}
},
{
"packet_index": 15,
"legacy": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
},
"t2": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
}
},
{
"packet_index": 16,
"legacy": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
},
"t2": {
"candidate_at_expected_packet": true,
"header": false,
"fec": false,
"crc": false,
"packet_output": false
}
}
],
"physical": {
"symbol_error_rate": 0.011046635434412266,
"rms_symbol_deviation": 0.5283737542111951,
"matched_component_power": 8.359397909690596,
"residual_power": 2.333261015636848,
"matched_residual_db": 5.542116733950265,
"band_gain_nonuniformity_p95_p05_db_median": 2.8700698316435833,
"band_gain_nonuniformity_p95_p05_db_min": 2.7249057267348604,
"band_gain_nonuniformity_p95_p05_db_max": 3.1116789732634516
},
"lab056_B": {
"symbol_error_rate": 0.03495896464646465,
"rms_symbol_deviation": 0.8453497751219741,
"matched_residual_db": 1.4648455366051558,
"band_gain_nonuniformity_db": 5.471381501203027
},
"difference_from_lab056_B": {
"symbol_error_rate_absolute": -0.023912329212052382,
"rms_symbol_deviation_absolute": -0.3169760209107789,
"matched_residual_db": 4.077271197345109,
"band_gain_nonuniformity_db": -2.6013116695594434
},
"lab062": {
"symbol_error_rate": 0.0992866269165247,
"rms_symbol_deviation": 1.2204009809742542,
"matched_residual_db": -1.7300509612417754,
"band_gain_nonuniformity_db": 6.660340655081576,
"capture_status": "RF_SESSION_INVALID_TX_UNDERRUN"
},
"difference_from_lab062": {
"symbol_error_rate_absolute": -0.08823999148211244,
"rms_symbol_deviation_absolute": -0.6920272267630591,
"matched_residual_db": 7.27216769519204,
"band_gain_nonuniformity_db": -3.790270823437993
},
"classification": "PARTIAL_PACKET_RECOVERY_NO_T2_GAIN",
"primary_result": "Аппаратно валидный Lab066 дал одинаковые 10/16 точных пакетов в legacy и T2; физическое качество лучше Lab056 B и невалидной Lab062.",
"recommended_next_single_step": "Отдельно разрешить офлайн-сравнение header-ошибок пакетов 1-3, 9, 15-16 с принятыми пакетами по сохранённому RX; новый TX не нужен."
}

View File

@@ -0,0 +1,507 @@
{
"lab": "Lab066",
"stage": "SOFTWARE_GATE",
"rf_tx_count": 0,
"tx_sha256": "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199",
"legacy": {
"mode": "legacy",
"candidates": 16,
"header_pass": 16,
"fec_started": 16,
"fec_pass": 16,
"crc_started": 16,
"crc_pass": 16,
"packet_output": 16,
"exact_payload_match": 16,
"packet_indexes": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16
],
"exact_ordered_payloads": true,
"t2_state_scope": "NOT_APPLICABLE",
"fallback_between_modes": false,
"passed": true,
"rows": [
{
"absolute_refined": 800000,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 1,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 932256,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 2,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1064512,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 3,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1196768,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 4,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1329024,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 5,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1461280,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 6,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1593536,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 7,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1725792,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 8,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1858048,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 9,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 1990304,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 10,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 2122560,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 11,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 2254816,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 12,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 2387072,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 13,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 2519328,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 14,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 2651584,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 15,
"t2_tracking_runs_for_candidate": 0
},
{
"absolute_refined": 2783840,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 16,
"t2_tracking_runs_for_candidate": 0
}
],
"detector_statistics": {
"total_local_peaks": 80004,
"first_stage_candidates": 21193,
"cheap_stage_checked": 21193,
"cheap_stage_rejected": 20800,
"clusters": 37,
"representative_checks": 16,
"inside_accepted_frame": 377
},
"candidate_overflow": 0
},
"t2": {
"mode": "t2",
"candidates": 16,
"header_pass": 16,
"fec_started": 16,
"fec_pass": 16,
"crc_started": 16,
"crc_pass": 16,
"packet_output": 16,
"exact_payload_match": 16,
"packet_indexes": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16
],
"exact_ordered_payloads": true,
"t2_state_scope": "NEW_PREAMBLE_ESTIMATE_PER_PACKET",
"fallback_between_modes": false,
"passed": true,
"rows": [
{
"absolute_refined": 800000,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 1,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 932256,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 2,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1064512,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 3,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1196768,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 4,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1329024,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 5,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1461280,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 6,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1593536,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 7,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1725792,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 8,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1858048,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 9,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 1990304,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 10,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 2122560,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 11,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 2254816,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 12,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 2387072,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 13,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 2519328,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 14,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 2651584,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 15,
"t2_tracking_runs_for_candidate": 2
},
{
"absolute_refined": 2783840,
"reason": "CRC_PASS",
"header": true,
"fec_started": true,
"fec": true,
"crc_started": true,
"crc": true,
"packet_output": true,
"packet_index_after_verdict": 16,
"t2_tracking_runs_for_candidate": 2
}
],
"detector_statistics": {
"total_local_peaks": 80004,
"first_stage_candidates": 21193,
"cheap_stage_checked": 21193,
"cheap_stage_rejected": 20800,
"clusters": 37,
"representative_checks": 16,
"inside_accepted_frame": 377
},
"candidate_overflow": 0,
"t2_state_reset_evidence": {
"estimate_source": "each candidate's own preamble",
"context_scope": "new PacketMeasure context for every candidate",
"tracking_runs_per_accepted_packet": [
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
],
"all_accepted_packets_have_fresh_tracking_runs": true,
"persistent_tracking_state_in_packet_processor": false
}
},
"gaps_verified_zero": true,
"t2_state_reset_verified_by_fresh_preamble_per_packet": true,
"passed": true
}

View File

@@ -0,0 +1,146 @@
candidate_number,position,nearest_packet,difference_samples,category,strict_reason
1,4902630,1,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
2,4900598,1,-2032,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
3,4904662,1,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
4,4898566,1,-4064,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
5,4896534,1,-6096,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
6,4906694,1,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
7,4894502,1,-8128,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
8,4908726,1,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
9,4910758,1,8128,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
10,4902630,1,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
11,4900598,1,-2032,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
12,4904662,1,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
13,4898566,1,-4064,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
14,4906694,1,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
15,4902630,1,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
16,4900598,1,-2032,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
17,4904662,1,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
18,4968617,1,65987,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
19,4958408,1,55778,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
20,4982295,2,-52591,UNRELATED,PHY_HEADER_MAGIC_MISMATCH
21,5034886,2,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
22,5032854,2,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
23,5036918,2,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
24,5030822,2,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
25,5028790,2,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
26,5038949,2,4063,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
27,5026758,2,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
28,5040981,2,6095,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
29,5043013,2,8127,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
30,5034886,2,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
31,5032854,2,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
32,5034886,2,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
33,5036918,2,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
34,5032854,2,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
35,5036918,2,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
36,5030822,2,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
37,5167142,3,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
38,5165110,3,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
39,5169174,3,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
40,5163078,3,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
41,5161046,3,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
42,5171206,3,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
43,5159014,3,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
44,5173238,3,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
45,5175270,3,8128,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
46,5167142,3,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
47,5169174,3,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
48,5165110,3,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
49,5171206,3,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
50,5163078,3,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
51,5167142,3,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
52,5169174,3,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
53,5165110,3,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
54,5198234,3,31092,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
55,5196202,3,29060,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
56,5194172,3,27030,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
57,5228601,3,61459,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
58,5230634,3,63492,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
59,5232665,3,65523,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
60,5220198,3,53056,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
61,5299398,4,0,TRUE_PREAMBLE_POSITION,CRC_PASS
62,5431654,5,0,TRUE_PREAMBLE_POSITION,CRC_PASS
63,5563910,6,0,TRUE_PREAMBLE_POSITION,CRC_PASS
64,5696166,7,0,TRUE_PREAMBLE_POSITION,CRC_PASS
65,5828422,8,0,TRUE_PREAMBLE_POSITION,CRC_PASS
66,5960678,9,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
67,5958646,9,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
68,5962710,9,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
69,5956614,9,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
70,5954582,9,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
71,5952550,9,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
72,5960678,9,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
73,5958646,9,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
74,5962710,9,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
75,5956614,9,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
76,5960678,9,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_CHECKSUM_MISMATCH
77,5958646,9,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
78,5962710,9,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
79,5964742,9,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
80,5966774,9,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
81,5968806,9,8128,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
82,5964742,9,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
83,6017761,9,57083,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
84,6015727,9,55049,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
85,6092934,10,0,TRUE_PREAMBLE_POSITION,CRC_PASS
86,6225190,11,0,TRUE_PREAMBLE_POSITION,CRC_PASS
87,6355414,12,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
88,6353382,12,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
89,6351350,12,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
90,6349317,12,-8129,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
91,6355414,12,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
92,6353382,12,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
93,6351350,12,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
94,6355414,12,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
95,6357446,12,0,TRUE_PREAMBLE_POSITION,CRC_PASS
96,6487670,13,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
97,6485638,13,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
98,6483606,13,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
99,6481574,13,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
100,6487670,13,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
101,6485638,13,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
102,6483606,13,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
103,6487670,13,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
104,6485638,13,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
105,6489702,13,0,TRUE_PREAMBLE_POSITION,CRC_PASS
106,6617894,14,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
107,6615862,14,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
108,6613830,14,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
109,6617894,14,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
110,6621958,14,0,TRUE_PREAMBLE_POSITION,CRC_PASS
111,6750150,15,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
112,6748118,15,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
113,6746086,15,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
114,6750150,15,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
115,6748118,15,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
116,6750150,15,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
117,6754214,15,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
118,6752182,15,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
119,6756246,15,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
120,6758278,15,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
121,6760309,15,6095,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
122,6762341,15,8127,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
123,6754214,15,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
124,6752182,15,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
125,6756246,15,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
126,6758278,15,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
127,6754214,15,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_MAGIC_MISMATCH
128,6752182,15,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
129,6756246,15,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
130,6880374,16,-6096,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
131,6878342,16,-8128,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
132,6886470,16,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_VERSION_MISMATCH
133,6884438,16,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
134,6888502,16,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
135,6882406,16,-4064,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
136,6890534,16,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
137,6892566,16,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
138,6894597,16,8127,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
139,6888502,16,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
140,6886470,16,0,TRUE_PREAMBLE_POSITION,PHY_HEADER_VERSION_MISMATCH
141,6892566,16,6096,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
142,6890534,16,4064,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
143,6884438,16,-2032,ZERO_GAP,PHY_HEADER_MAGIC_MISMATCH
144,6888502,16,2032,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
145,6902724,16,16254,REPEATED_PRBS_LOCAL_MAXIMUM,PHY_HEADER_MAGIC_MISMATCH
1 candidate_number position nearest_packet difference_samples category strict_reason
2 1 4902630 1 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
3 2 4900598 1 -2032 UNRELATED PHY_HEADER_MAGIC_MISMATCH
4 3 4904662 1 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
5 4 4898566 1 -4064 UNRELATED PHY_HEADER_MAGIC_MISMATCH
6 5 4896534 1 -6096 UNRELATED PHY_HEADER_MAGIC_MISMATCH
7 6 4906694 1 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
8 7 4894502 1 -8128 UNRELATED PHY_HEADER_MAGIC_MISMATCH
9 8 4908726 1 6096 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
10 9 4910758 1 8128 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
11 10 4902630 1 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
12 11 4900598 1 -2032 UNRELATED PHY_HEADER_MAGIC_MISMATCH
13 12 4904662 1 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
14 13 4898566 1 -4064 UNRELATED PHY_HEADER_MAGIC_MISMATCH
15 14 4906694 1 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
16 15 4902630 1 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
17 16 4900598 1 -2032 UNRELATED PHY_HEADER_MAGIC_MISMATCH
18 17 4904662 1 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
19 18 4968617 1 65987 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
20 19 4958408 1 55778 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
21 20 4982295 2 -52591 UNRELATED PHY_HEADER_MAGIC_MISMATCH
22 21 5034886 2 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
23 22 5032854 2 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
24 23 5036918 2 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
25 24 5030822 2 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
26 25 5028790 2 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
27 26 5038949 2 4063 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
28 27 5026758 2 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
29 28 5040981 2 6095 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
30 29 5043013 2 8127 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
31 30 5034886 2 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
32 31 5032854 2 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
33 32 5034886 2 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
34 33 5036918 2 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
35 34 5032854 2 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
36 35 5036918 2 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
37 36 5030822 2 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
38 37 5167142 3 0 TRUE_PREAMBLE_POSITION PHY_HEADER_MAGIC_MISMATCH
39 38 5165110 3 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
40 39 5169174 3 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
41 40 5163078 3 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
42 41 5161046 3 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
43 42 5171206 3 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
44 43 5159014 3 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
45 44 5173238 3 6096 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
46 45 5175270 3 8128 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
47 46 5167142 3 0 TRUE_PREAMBLE_POSITION PHY_HEADER_MAGIC_MISMATCH
48 47 5169174 3 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
49 48 5165110 3 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
50 49 5171206 3 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
51 50 5163078 3 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
52 51 5167142 3 0 TRUE_PREAMBLE_POSITION PHY_HEADER_MAGIC_MISMATCH
53 52 5169174 3 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
54 53 5165110 3 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
55 54 5198234 3 31092 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
56 55 5196202 3 29060 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
57 56 5194172 3 27030 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
58 57 5228601 3 61459 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
59 58 5230634 3 63492 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
60 59 5232665 3 65523 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
61 60 5220198 3 53056 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
62 61 5299398 4 0 TRUE_PREAMBLE_POSITION CRC_PASS
63 62 5431654 5 0 TRUE_PREAMBLE_POSITION CRC_PASS
64 63 5563910 6 0 TRUE_PREAMBLE_POSITION CRC_PASS
65 64 5696166 7 0 TRUE_PREAMBLE_POSITION CRC_PASS
66 65 5828422 8 0 TRUE_PREAMBLE_POSITION CRC_PASS
67 66 5960678 9 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
68 67 5958646 9 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
69 68 5962710 9 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
70 69 5956614 9 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
71 70 5954582 9 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
72 71 5952550 9 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
73 72 5960678 9 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
74 73 5958646 9 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
75 74 5962710 9 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
76 75 5956614 9 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
77 76 5960678 9 0 TRUE_PREAMBLE_POSITION PHY_HEADER_CHECKSUM_MISMATCH
78 77 5958646 9 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
79 78 5962710 9 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
80 79 5964742 9 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
81 80 5966774 9 6096 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
82 81 5968806 9 8128 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
83 82 5964742 9 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
84 83 6017761 9 57083 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
85 84 6015727 9 55049 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
86 85 6092934 10 0 TRUE_PREAMBLE_POSITION CRC_PASS
87 86 6225190 11 0 TRUE_PREAMBLE_POSITION CRC_PASS
88 87 6355414 12 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
89 88 6353382 12 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
90 89 6351350 12 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
91 90 6349317 12 -8129 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
92 91 6355414 12 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
93 92 6353382 12 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
94 93 6351350 12 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
95 94 6355414 12 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
96 95 6357446 12 0 TRUE_PREAMBLE_POSITION CRC_PASS
97 96 6487670 13 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
98 97 6485638 13 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
99 98 6483606 13 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
100 99 6481574 13 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
101 100 6487670 13 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
102 101 6485638 13 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
103 102 6483606 13 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
104 103 6487670 13 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
105 104 6485638 13 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
106 105 6489702 13 0 TRUE_PREAMBLE_POSITION CRC_PASS
107 106 6617894 14 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
108 107 6615862 14 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
109 108 6613830 14 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
110 109 6617894 14 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
111 110 6621958 14 0 TRUE_PREAMBLE_POSITION CRC_PASS
112 111 6750150 15 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
113 112 6748118 15 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
114 113 6746086 15 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
115 114 6750150 15 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
116 115 6748118 15 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
117 116 6750150 15 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
118 117 6754214 15 0 TRUE_PREAMBLE_POSITION PHY_HEADER_MAGIC_MISMATCH
119 118 6752182 15 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
120 119 6756246 15 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
121 120 6758278 15 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
122 121 6760309 15 6095 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
123 122 6762341 15 8127 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
124 123 6754214 15 0 TRUE_PREAMBLE_POSITION PHY_HEADER_MAGIC_MISMATCH
125 124 6752182 15 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
126 125 6756246 15 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
127 126 6758278 15 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
128 127 6754214 15 0 TRUE_PREAMBLE_POSITION PHY_HEADER_MAGIC_MISMATCH
129 128 6752182 15 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
130 129 6756246 15 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
131 130 6880374 16 -6096 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
132 131 6878342 16 -8128 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
133 132 6886470 16 0 TRUE_PREAMBLE_POSITION PHY_HEADER_VERSION_MISMATCH
134 133 6884438 16 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
135 134 6888502 16 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
136 135 6882406 16 -4064 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
137 136 6890534 16 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
138 137 6892566 16 6096 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
139 138 6894597 16 8127 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
140 139 6888502 16 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
141 140 6886470 16 0 TRUE_PREAMBLE_POSITION PHY_HEADER_VERSION_MISMATCH
142 141 6892566 16 6096 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
143 142 6890534 16 4064 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
144 143 6884438 16 -2032 ZERO_GAP PHY_HEADER_MAGIC_MISMATCH
145 144 6888502 16 2032 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH
146 145 6902724 16 16254 REPEATED_PRBS_LOCAL_MAXIMUM PHY_HEADER_MAGIC_MISMATCH

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,17 @@
packet,strict_result,candidate_position,expected_packet_position,candidate_difference_samples,preamble_errors,preamble_error_rate,preamble_correlation,header_raw_errors,header_voted_errors,header_copy_1_errors,header_copy_2_errors,header_copy_3_errors,magic_errors,version_errors,length_errors,checksum_errors,expected_header_bits,received_raw_header_bits,voted_header_bits,header_accepted,header_reason,legacy_header_bits,t2_header_bits,legacy_initial_frequency_hz,t2_initial_frequency_hz,reference_frequency_hz,legacy_frequency_error_vs_reference_hz,initial_phase_error_rad,timing_offset_samples,fractional_timing_offset,reference_timing_offset_samples,reference_fractional_timing_offset,local_matched_power,local_residual_power,local_matched_residual_db,rms_symbol_deviation,legacy_t2_header_differences,classification
1,HEADER_FAIL,4902630,4902630,0,15,0.01171875,0.8981139142983712,3,1,1,2,0,0,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000,110100111001000100000001000000010000110100001000,False,CHECKSUM_MISMATCH,111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000,111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000,-2.662272181646709,-5.3879680108224575,-2.6542601970280364,-0.008011984618672585,1.8341164291785854,0,0.0,0,0.0,8.982336070665001,2.7583799759789063,5.127352085281537,0.5541564541425134,0,HEADER_SPECIFIC_FAILURE
2,HEADER_FAIL,5034886,5034886,0,10,0.0078125,0.892574719565451,6,1,3,2,1,0,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000001000001110000111111000000000000000000111000000000,110100111001000100000001000000010010110000001000,False,CHECKSUM_MISMATCH,111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000001000001110000111111000000000000000000111000000000,111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000011000001110000111111000000000000000000111000000000,-11.085402212024045,-5.423727145761877,2.664169158774504,-13.749571370798549,1.8865220093820032,0,0.0,0,0.0,8.566883580374462,3.867819679092585,3.4535664637802532,0.6719263565992358,1,HEADER_SPECIFIC_FAILURE
3,HEADER_FAIL,5167142,5167142,0,11,0.00859375,0.8832445602414427,3,1,1,1,1,1,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100110001000100000001000000010000110000001000,False,MAGIC_MISMATCH,111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-2.14390103272374,2.717358680061899,4.3756668288999,-6.51956786162364,1.837976468233186,0,0.0,0,0.0,8.391114739306301,2.917171371178907,4.5885771684300085,0.589618554298201,0,HEADER_SPECIFIC_FAILURE
4,PASS,5299398,5299398,0,12,0.009375,0.8702559090330103,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-4.969843852460903,0.5011874511379912,3.928841196279306,-8.898685048740209,1.823004794704275,0,0.0,0,0.0,8.221366239802954,2.9179318124892393,4.498688563977461,0.5957520861601466,0,PASSED
5,PASS,5431654,5431654,0,15,0.01171875,0.882298526549992,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,3.163024030078619,-1.2710648335872299,1.1365290837564324,2.026494946322187,1.8355890323417587,0,0.0,0,0.0,8.188755217773341,1.2708156134102264,8.091353471792932,0.39394203715952975,0,PASSED
6,PASS,5563910,5563910,0,17,0.01328125,0.8834263035683648,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,5.595453900367469,1.2623874159193273,-4.567512776067651,10.162966676435119,1.7721238280090583,0,0.0,0,0.0,8.667865149110309,1.6073728447094802,7.317955191422993,0.43062797582960244,0,PASSED
7,PASS,5696166,5696166,0,7,0.00546875,0.8957036694801145,1,0,0,0,1,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000001,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000001,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-1.2654548420073324,-1.236432022604831,-6.203901481459032,4.9384466394516995,1.8155098517430892,0,0.0,0,0.0,8.65014337171221,2.0185187625245264,6.319905151171046,0.4830640770103008,1,PASSED
8,PASS,5828422,5828422,0,10,0.0078125,0.8935154947988603,3,0,1,2,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000,110100111001000100000001000000010000110000001000,True,,111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000,111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000,9.131379183391202,-0.4710188398180419,4.02876530540373,5.102613877987472,1.6973544391851634,0,0.0,0,0.0,8.476859162285322,2.688967210304384,4.986494612866651,0.5632163702340075,0,PASSED
9,HEADER_FAIL,5960678,5960678,0,13,0.01015625,0.8831012072365596,4,1,1,2,1,0,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000,110100111001000100000001000000011000110000001000,False,CHECKSUM_MISMATCH,111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000,6.363378164483905,-1.9815016298670118,8.696281322717777,-2.332903158233872,1.7240388753166553,0,0.0,0,0.0,8.379835253157635,3.513421286396085,3.7750525233897125,0.6475113327264761,0,HEADER_SPECIFIC_FAILURE
10,PASS,6092934,6092934,0,14,0.0109375,0.8756981622368548,1,0,0,0,1,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,10.521050631968299,4.784422094967777,8.894608707236657,1.6264419247316422,1.6172834220436263,0,0.0,0,0.0,7.7745725932128815,2.478953865410118,4.964080786327893,0.5646716196549459,0,PASSED
11,PASS,6225190,6225190,0,14,0.0109375,0.8739448578846127,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-4.3377788034794795,5.576222280700207,6.2889992427567645,-10.626778046236243,1.8042319285279784,0,0.0,0,0.0,8.281301835417366,2.613504672826743,5.008753330856255,0.5617749030480063,0,PASSED
12,PASS,6357446,6357446,0,15,0.01171875,0.8691086828109845,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,-14.662329907046818,-12.429475985972452,-7.481009787452366,-7.181320119594452,1.9299825595605988,0,0.0,0,0.0,8.105161843192903,1.785984725783043,6.568839508606049,0.4694161471804715,0,PASSED
13,PASS,6489702,6489702,0,19,0.01484375,0.8870876180609537,0,0,0,0,0,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,2.005711398433546,-3.8713317585287843,-9.48236864846436,11.488080046897906,1.7755837460769197,0,0.0,0,0.0,8.379598937532618,1.5074086416620165,7.44992232360151,0.42413477685697976,0,PASSED
14,PASS,6621958,6621958,0,13,0.01015625,0.8979602974476922,1,0,0,0,1,0,0,0,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000110000000000000111111000000000000000000111000000000,110100111001000100000001000000010000110000001000,True,,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000110000000000000111111000000000000000000111000000000,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,7.210729633183354,3.326192426336044,-3.8425458753995345,11.053275508582889,1.6778789052916618,0,0.0,0,0.0,8.575750726813316,2.4203963781579967,5.493856544116208,0.5312600671059894,1,PASSED
15,HEADER_FAIL,6754214,6754214,0,12,0.009375,0.8887378180983065,7,2,2,2,3,1,0,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000,110101111001000100000001001000010000110000001000,False,MAGIC_MISMATCH,111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000,111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000,-5.7315977256001345,-3.5200403113930196,0.10139388237069231,-5.832991607970826,1.7811965898235738,0,0.0,0,0.0,7.968245167205011,3.1321489434833123,4.055202820682332,0.6269600363881329,0,HEADER_SPECIFIC_FAILURE
16,HEADER_FAIL,6886470,6886470,0,16,0.0125,0.8805097957519517,5,2,1,2,2,0,1,1,0,111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000,101111000111000000111111111000000111000000000111000000000011000000000111000000000000001100000111000000000000111111000000000000000000111000000000,110100111001000100010001000001010000110000001000,False,VERSION_MISMATCH,101111000111000000111111111000000111000000000111000000000011000000000111000000000000001100000111000000000000111111000000000000000000111000000000,000111000000111111100000000101011110110001101000011101111000001111000110111101111011100000111001010010111111000000111111111111111011000111111111,4.7344866536019055,228.10491338016192,10.381403191597384,-5.646916537995478,1.6829380426891485,0,0.0,0,0.0,8.084601213892752,3.04749463363358,4.237156529516785,0.613962963260252,107,HEADER_SPECIFIC_FAILURE
1 packet strict_result candidate_position expected_packet_position candidate_difference_samples preamble_errors preamble_error_rate preamble_correlation header_raw_errors header_voted_errors header_copy_1_errors header_copy_2_errors header_copy_3_errors magic_errors version_errors length_errors checksum_errors expected_header_bits received_raw_header_bits voted_header_bits header_accepted header_reason legacy_header_bits t2_header_bits legacy_initial_frequency_hz t2_initial_frequency_hz reference_frequency_hz legacy_frequency_error_vs_reference_hz initial_phase_error_rad timing_offset_samples fractional_timing_offset reference_timing_offset_samples reference_fractional_timing_offset local_matched_power local_residual_power local_matched_residual_db rms_symbol_deviation legacy_t2_header_differences classification
2 1 HEADER_FAIL 4902630 4902630 0 15 0.01171875 0.8981139142983712 3 1 1 2 0 0 0 1 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000 110100111001000100000001000000010000110100001000 False CHECKSUM_MISMATCH 111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000 111111000101000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000110000000000000111000000000 -2.662272181646709 -5.3879680108224575 -2.6542601970280364 -0.008011984618672585 1.8341164291785854 0 0.0 0 0.0 8.982336070665001 2.7583799759789063 5.127352085281537 0.5541564541425134 0 HEADER_SPECIFIC_FAILURE
3 2 HEADER_FAIL 5034886 5034886 0 10 0.0078125 0.892574719565451 6 1 3 2 1 0 0 1 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000001000001110000111111000000000000000000111000000000 110100111001000100000001000000010010110000001000 False CHECKSUM_MISMATCH 111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000001000001110000111111000000000000000000111000000000 111111000111000000111111111000000111000100000111000000000000000000000111000000000000000000000011000001110000111111000000000000000000111000000000 -11.085402212024045 -5.423727145761877 2.664169158774504 -13.749571370798549 1.8865220093820032 0 0.0 0 0.0 8.566883580374462 3.867819679092585 3.4535664637802532 0.6719263565992358 1 HEADER_SPECIFIC_FAILURE
4 3 HEADER_FAIL 5167142 5167142 0 11 0.00859375 0.8832445602414427 3 1 1 1 1 1 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100110001000100000001000000010000110000001000 False MAGIC_MISMATCH 111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111110001000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 -2.14390103272374 2.717358680061899 4.3756668288999 -6.51956786162364 1.837976468233186 0 0.0 0 0.0 8.391114739306301 2.917171371178907 4.5885771684300085 0.589618554298201 0 HEADER_SPECIFIC_FAILURE
5 4 PASS 5299398 5299398 0 12 0.009375 0.8702559090330103 0 0 0 0 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 -4.969843852460903 0.5011874511379912 3.928841196279306 -8.898685048740209 1.823004794704275 0 0.0 0 0.0 8.221366239802954 2.9179318124892393 4.498688563977461 0.5957520861601466 0 PASSED
6 5 PASS 5431654 5431654 0 15 0.01171875 0.882298526549992 0 0 0 0 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 3.163024030078619 -1.2710648335872299 1.1365290837564324 2.026494946322187 1.8355890323417587 0 0.0 0 0.0 8.188755217773341 1.2708156134102264 8.091353471792932 0.39394203715952975 0 PASSED
7 6 PASS 5563910 5563910 0 17 0.01328125 0.8834263035683648 0 0 0 0 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 5.595453900367469 1.2623874159193273 -4.567512776067651 10.162966676435119 1.7721238280090583 0 0.0 0 0.0 8.667865149110309 1.6073728447094802 7.317955191422993 0.43062797582960244 0 PASSED
8 7 PASS 5696166 5696166 0 7 0.00546875 0.8957036694801145 1 0 0 0 1 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000001 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000001 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 -1.2654548420073324 -1.236432022604831 -6.203901481459032 4.9384466394516995 1.8155098517430892 0 0.0 0 0.0 8.65014337171221 2.0185187625245264 6.319905151171046 0.4830640770103008 1 PASSED
9 8 PASS 5828422 5828422 0 10 0.0078125 0.8935154947988603 3 0 1 2 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000 110100111001000100000001000000010000110000001000 True 111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000 111001000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000010000111000000000 9.131379183391202 -0.4710188398180419 4.02876530540373 5.102613877987472 1.6973544391851634 0 0.0 0 0.0 8.476859162285322 2.688967210304384 4.986494612866651 0.5632163702340075 0 PASSED
10 9 HEADER_FAIL 5960678 5960678 0 13 0.01015625 0.8831012072365596 4 1 1 2 1 0 0 1 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000 110100111001000100000001000000011000110000001000 False CHECKSUM_MISMATCH 111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000011010000000000000000000111000000000000000000000111011000000000111111000000000000000000111000000000 6.363378164483905 -1.9815016298670118 8.696281322717777 -2.332903158233872 1.7240388753166553 0 0.0 0 0.0 8.379835253157635 3.513421286396085 3.7750525233897125 0.6475113327264761 0 HEADER_SPECIFIC_FAILURE
11 10 PASS 6092934 6092934 0 14 0.0109375 0.8756981622368548 1 0 0 0 1 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111110111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 10.521050631968299 4.784422094967777 8.894608707236657 1.6264419247316422 1.6172834220436263 0 0.0 0 0.0 7.7745725932128815 2.478953865410118 4.964080786327893 0.5646716196549459 0 PASSED
12 11 PASS 6225190 6225190 0 14 0.0109375 0.8739448578846127 0 0 0 0 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 -4.3377788034794795 5.576222280700207 6.2889992427567645 -10.626778046236243 1.8042319285279784 0 0.0 0 0.0 8.281301835417366 2.613504672826743 5.008753330856255 0.5617749030480063 0 PASSED
13 12 PASS 6357446 6357446 0 15 0.01171875 0.8691086828109845 0 0 0 0 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 -14.662329907046818 -12.429475985972452 -7.481009787452366 -7.181320119594452 1.9299825595605988 0 0.0 0 0.0 8.105161843192903 1.785984725783043 6.568839508606049 0.4694161471804715 0 PASSED
14 13 PASS 6489702 6489702 0 19 0.01484375 0.8870876180609537 0 0 0 0 0 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 2.005711398433546 -3.8713317585287843 -9.48236864846436 11.488080046897906 1.7755837460769197 0 0.0 0 0.0 8.379598937532618 1.5074086416620165 7.44992232360151 0.42413477685697976 0 PASSED
15 14 PASS 6621958 6621958 0 13 0.01015625 0.8979602974476922 1 0 0 0 1 0 0 0 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000110000000000000111111000000000000000000111000000000 110100111001000100000001000000010000110000001000 True 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000110000000000000111111000000000000000000111000000000 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 7.210729633183354 3.326192426336044 -3.8425458753995345 11.053275508582889 1.6778789052916618 0 0.0 0 0.0 8.575750726813316 2.4203963781579967 5.493856544116208 0.5312600671059894 1 PASSED
16 15 HEADER_FAIL 6754214 6754214 0 12 0.009375 0.8887378180983065 7 2 2 2 3 1 0 1 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000 110101111001000100000001001000010000110000001000 False MAGIC_MISMATCH 111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000 111111000111000011111111111000000111000000000111000000000000000000000111001000110000000000000111000000000000111111000000000000000000111001100000 -5.7315977256001345 -3.5200403113930196 0.10139388237069231 -5.832991607970826 1.7811965898235738 0 0.0 0 0.0 7.968245167205011 3.1321489434833123 4.055202820682332 0.6269600363881329 0 HEADER_SPECIFIC_FAILURE
17 16 HEADER_FAIL 6886470 6886470 0 16 0.0125 0.8805097957519517 5 2 1 2 2 0 1 1 0 111111000111000000111111111000000111000000000111000000000000000000000111000000000000000000000111000000000000111111000000000000000000111000000000 101111000111000000111111111000000111000000000111000000000011000000000111000000000000001100000111000000000000111111000000000000000000111000000000 110100111001000100010001000001010000110000001000 False VERSION_MISMATCH 101111000111000000111111111000000111000000000111000000000011000000000111000000000000001100000111000000000000111111000000000000000000111000000000 000111000000111111100000000101011110110001101000011101111000001111000110111101111011100000111001010010111111000000111111111111111011000111111111 4.7344866536019055 228.10491338016192 10.381403191597384 -5.646916537995478 1.6829380426891485 0 0.0 0 0.0 8.084601213892752 3.04749463363358 4.237156529516785 0.613962963260252 107 HEADER_SPECIFIC_FAILURE

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -0,0 +1,49 @@
packet,region,symbols,decision_errors,magnitude_median,magnitude_minimum,phase_error_rms_rad,imaginary_rms,timing_offset_samples
1,preamble_tail,128,0,1.0988442307313235,0.43466351049701235,0.36545432588063453,0.3677449037710503,0
1,header,144,3,1.0815752733984212,0.5778378379912865,0.6187439503436525,0.551502400297575,0
1,postheader,128,2,1.1114278978759744,0.4714742841146855,0.5693534448896019,0.49591231338100494,0
2,preamble_tail,128,0,1.074345037947809,0.5715381413183729,0.5169701760998907,0.4918784327994302,0
2,header,144,6,1.0888449278310801,0.4753465266196904,0.7542889272180515,0.6186207192551644,0
2,postheader,128,0,1.1317475637994039,0.6993167478293558,0.28345944315453764,0.2904885233790264,0
3,preamble_tail,128,0,1.0919580830559625,0.4931835747157359,0.577421183092019,0.5161252295458062,0
3,header,144,4,1.152609811941216,0.6541218073505881,0.5437712455230955,0.433109513511742,0
3,postheader,128,0,1.0857706086674181,0.3396593308737327,0.2976136824815135,0.3003548051915534,0
4,preamble_tail,128,0,1.1087294828126495,0.5024192881228802,0.6792582574914069,0.6647511818530001,0
4,header,144,0,1.1507766975854852,0.42562892480326897,0.3156584316410723,0.33839321792646554,0
4,postheader,128,0,1.1449814637127051,0.5937437256113064,0.29460252451816493,0.31949283720140587,0
5,preamble_tail,128,0,1.1434636342884739,0.6047967729049536,0.35172140313369066,0.3535407042323131,0
5,header,144,0,1.1363877434085625,0.6007022877000862,0.2522418948656545,0.27555358339271957,0
5,postheader,128,5,1.0966485328526976,0.41749527315450785,0.6464901825183261,0.5318845761533794,0
6,preamble_tail,128,0,1.1318097006554524,0.6221411610236426,0.2794053247219388,0.30212984078304644,0
6,header,144,0,1.118944116022563,0.604506255970942,0.4000248179092374,0.40617877592291496,0
6,postheader,128,3,1.1203373379450718,0.5733983281027794,0.6433269203677763,0.5585770337413266,0
7,preamble_tail,128,0,1.098335506453933,0.685298350349321,0.31267846128306165,0.3400209740040822,0
7,header,144,1,1.0859515485682385,0.4918795883455653,0.49770271058274784,0.48404835853198735,0
7,postheader,128,3,1.078419996569039,0.44698965664144213,0.5990105590866479,0.5112821191437632,0
8,preamble_tail,128,0,1.0950824513758386,0.5415446078858628,0.37054349208551474,0.38513707581929985,0
8,header,144,3,1.0904644019616199,0.43076194377790394,0.6094384014423195,0.5588536614362375,0
8,postheader,128,2,1.0935087479377619,0.63642176553416,0.4463244127331142,0.43911459515487505,0
9,preamble_tail,128,2,1.093901338967227,0.4388973792742324,0.5923392165896195,0.5322335060592657,0
9,header,144,4,1.1195324105485278,0.49998913621143454,0.611185029044535,0.5659527404173302,0
9,postheader,128,0,1.1053805607840526,0.640256963735712,0.28052596266122526,0.30630431218636367,0
10,preamble_tail,128,3,1.1419841905626784,0.5060536577715014,0.5971065417005673,0.5330612346503504,0
10,header,144,1,1.1147443336597755,0.6501217642416475,0.39358947394490806,0.40010760065192014,0
10,postheader,128,0,1.1787629415552199,0.42900718390148934,0.2926514547716184,0.3306690896219249,0
11,preamble_tail,128,3,1.1704570892176922,0.6573740193329803,0.6151666707460389,0.6107317885152251,0
11,header,144,0,1.12981219997058,0.5282686497447672,0.324658042677611,0.3384614869201178,0
11,postheader,128,0,1.1070044356208588,0.5046571226194821,0.33630657442006734,0.36220237044356646,0
12,preamble_tail,128,4,1.1293212243971127,0.5975355368621857,0.5056848722345468,0.41499438743931705,0
12,header,144,0,1.1346606346022847,0.37281496808234904,0.2862848068844218,0.3172306512537894,0
12,postheader,128,1,1.1452864327334882,0.4013942634977783,0.4893751135204076,0.4862028130220328,0
13,preamble_tail,128,0,1.1020405947443312,0.5476951608093663,0.26825405646139866,0.299908757841907,0
13,header,144,0,1.1246852022329477,0.5448659176501547,0.3819108989758959,0.3972670513029744,0
13,postheader,128,2,1.0925865889420296,0.5942311935828164,0.5526657411269684,0.47037502313805846,0
14,preamble_tail,128,0,1.0986885517578937,0.4240058786452209,0.307170345418273,0.30833136259021016,0
14,header,144,1,1.1117645619461933,0.5698477702591676,0.5701021956757241,0.5678681526072823,0
14,postheader,128,3,1.106509873733971,0.5370530426198264,0.594466571272217,0.5534673376824929,0
15,preamble_tail,128,1,1.0605799496659238,0.5096523999436898,0.4395937997414911,0.41506255958111626,0
15,header,144,7,1.0998344881084918,0.562650314086593,0.6887166853647746,0.5814901000470972,0
15,postheader,128,2,1.1227074410764701,0.5161407533490777,0.5946319825247028,0.5701686238399535,0
16,preamble_tail,128,2,1.1150803467611397,0.5398340206811254,0.5646446219736847,0.48272300998033535,0
16,header,144,5,1.1024025293008193,0.41214915514462663,0.6355591984079474,0.49483023185734204,0
16,postheader,128,0,1.1777412846771014,0.3344663773895672,0.3015102248779219,0.3386110581722476,0
1 packet region symbols decision_errors magnitude_median magnitude_minimum phase_error_rms_rad imaginary_rms timing_offset_samples
2 1 preamble_tail 128 0 1.0988442307313235 0.43466351049701235 0.36545432588063453 0.3677449037710503 0
3 1 header 144 3 1.0815752733984212 0.5778378379912865 0.6187439503436525 0.551502400297575 0
4 1 postheader 128 2 1.1114278978759744 0.4714742841146855 0.5693534448896019 0.49591231338100494 0
5 2 preamble_tail 128 0 1.074345037947809 0.5715381413183729 0.5169701760998907 0.4918784327994302 0
6 2 header 144 6 1.0888449278310801 0.4753465266196904 0.7542889272180515 0.6186207192551644 0
7 2 postheader 128 0 1.1317475637994039 0.6993167478293558 0.28345944315453764 0.2904885233790264 0
8 3 preamble_tail 128 0 1.0919580830559625 0.4931835747157359 0.577421183092019 0.5161252295458062 0
9 3 header 144 4 1.152609811941216 0.6541218073505881 0.5437712455230955 0.433109513511742 0
10 3 postheader 128 0 1.0857706086674181 0.3396593308737327 0.2976136824815135 0.3003548051915534 0
11 4 preamble_tail 128 0 1.1087294828126495 0.5024192881228802 0.6792582574914069 0.6647511818530001 0
12 4 header 144 0 1.1507766975854852 0.42562892480326897 0.3156584316410723 0.33839321792646554 0
13 4 postheader 128 0 1.1449814637127051 0.5937437256113064 0.29460252451816493 0.31949283720140587 0
14 5 preamble_tail 128 0 1.1434636342884739 0.6047967729049536 0.35172140313369066 0.3535407042323131 0
15 5 header 144 0 1.1363877434085625 0.6007022877000862 0.2522418948656545 0.27555358339271957 0
16 5 postheader 128 5 1.0966485328526976 0.41749527315450785 0.6464901825183261 0.5318845761533794 0
17 6 preamble_tail 128 0 1.1318097006554524 0.6221411610236426 0.2794053247219388 0.30212984078304644 0
18 6 header 144 0 1.118944116022563 0.604506255970942 0.4000248179092374 0.40617877592291496 0
19 6 postheader 128 3 1.1203373379450718 0.5733983281027794 0.6433269203677763 0.5585770337413266 0
20 7 preamble_tail 128 0 1.098335506453933 0.685298350349321 0.31267846128306165 0.3400209740040822 0
21 7 header 144 1 1.0859515485682385 0.4918795883455653 0.49770271058274784 0.48404835853198735 0
22 7 postheader 128 3 1.078419996569039 0.44698965664144213 0.5990105590866479 0.5112821191437632 0
23 8 preamble_tail 128 0 1.0950824513758386 0.5415446078858628 0.37054349208551474 0.38513707581929985 0
24 8 header 144 3 1.0904644019616199 0.43076194377790394 0.6094384014423195 0.5588536614362375 0
25 8 postheader 128 2 1.0935087479377619 0.63642176553416 0.4463244127331142 0.43911459515487505 0
26 9 preamble_tail 128 2 1.093901338967227 0.4388973792742324 0.5923392165896195 0.5322335060592657 0
27 9 header 144 4 1.1195324105485278 0.49998913621143454 0.611185029044535 0.5659527404173302 0
28 9 postheader 128 0 1.1053805607840526 0.640256963735712 0.28052596266122526 0.30630431218636367 0
29 10 preamble_tail 128 3 1.1419841905626784 0.5060536577715014 0.5971065417005673 0.5330612346503504 0
30 10 header 144 1 1.1147443336597755 0.6501217642416475 0.39358947394490806 0.40010760065192014 0
31 10 postheader 128 0 1.1787629415552199 0.42900718390148934 0.2926514547716184 0.3306690896219249 0
32 11 preamble_tail 128 3 1.1704570892176922 0.6573740193329803 0.6151666707460389 0.6107317885152251 0
33 11 header 144 0 1.12981219997058 0.5282686497447672 0.324658042677611 0.3384614869201178 0
34 11 postheader 128 0 1.1070044356208588 0.5046571226194821 0.33630657442006734 0.36220237044356646 0
35 12 preamble_tail 128 4 1.1293212243971127 0.5975355368621857 0.5056848722345468 0.41499438743931705 0
36 12 header 144 0 1.1346606346022847 0.37281496808234904 0.2862848068844218 0.3172306512537894 0
37 12 postheader 128 1 1.1452864327334882 0.4013942634977783 0.4893751135204076 0.4862028130220328 0
38 13 preamble_tail 128 0 1.1020405947443312 0.5476951608093663 0.26825405646139866 0.299908757841907 0
39 13 header 144 0 1.1246852022329477 0.5448659176501547 0.3819108989758959 0.3972670513029744 0
40 13 postheader 128 2 1.0925865889420296 0.5942311935828164 0.5526657411269684 0.47037502313805846 0
41 14 preamble_tail 128 0 1.0986885517578937 0.4240058786452209 0.307170345418273 0.30833136259021016 0
42 14 header 144 1 1.1117645619461933 0.5698477702591676 0.5701021956757241 0.5678681526072823 0
43 14 postheader 128 3 1.106509873733971 0.5370530426198264 0.594466571272217 0.5534673376824929 0
44 15 preamble_tail 128 1 1.0605799496659238 0.5096523999436898 0.4395937997414911 0.41506255958111626 0
45 15 header 144 7 1.0998344881084918 0.562650314086593 0.6887166853647746 0.5814901000470972 0
46 15 postheader 128 2 1.1227074410764701 0.5161407533490777 0.5946319825247028 0.5701686238399535 0
47 16 preamble_tail 128 2 1.1150803467611397 0.5398340206811254 0.5646446219736847 0.48272300998033535 0
48 16 header 144 5 1.1024025293008193 0.41214915514462663 0.6355591984079474 0.49483023185734204 0
49 16 postheader 128 0 1.1777412846771014 0.3344663773895672 0.3015102248779219 0.3386110581722476 0

File diff suppressed because it is too large Load Diff

View File

@@ -663,3 +663,81 @@
- **Независимая проверка:** прямой пересчёт из TX/RX CS8 подтвердил все SHA, - **Независимая проверка:** прямой пересчёт из TX/RX CS8 подтвердил все SHA,
счётчики, фоны, matched/residual/`h`, ступени и спектральные полосы; счётчики, фоны, matched/residual/`h`, ступени и спектральные полосы;
расхождений нет. Quick gate и pytest после RF:986 passed,0 failed. расхождений нет. Quick gate и pytest после RF:986 passed,0 failed.
## Lab065: измерение уровня при TX VGA 20 дБ
- **База:** Lab064 зафиксирована коммитом
`cff226e0bf9e36a627829ea11a4ca3292ae20953`; push не выполнялся.
- **Единственное изменение параметра:** TX VGA 10 → 20 дБ. Частота 1400 МГц,
RX LNA/VGA 24/24 дБ, усилители, геометрия, антенны и кабели сохранены.
- **Waveform:** точная копия Lab064, 6000000 отсчётов, 0,75 с; SHA-256
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
- **Подготовлено:** RX-first runner, максимальные счётчики потерь, сравнение
линейности внутри Lab065 и по одинаковым уровням Lab064/Lab065, численные
правила классификации. Software gate PASS.
- **RX-only preflight:** обе HackRF и роли подтверждены, reg29=0x26 у MASTER и
SLAVE, CLKIN обнаружен на третьем опросе через201,986мс. Фон:800000 отсчётов,
AC RMS1,056565 кода, max|I|/|Q|=6/8, clipping0, overruns0. Cleanup PASS.
- **Граница:** `RF TX=0`. Lab065 не коммитировалась. Нужны рассмотрение
подготовки и отдельное прямое разрешение на один TX; Lab066 не начинать.
- [Текущее состояние](lab065_current_state.md),
[RX-only отчёт](lab065_rx_preflight.md),
[подготовка](../data/processed/lab065/preparation.json),
[software gate](../data/processed/lab065/software_gate.json),
[preflight](../data/processed/lab065/preflight_result.json).
- **Единственный RF-сеанс:** TX1, автоматических повторов0; underrun/overrun/
clipping0, cleanup PASS, процессы CLEAR. RX16000000 отсчётов, SHA-256
`845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e`.
- **Линейность:** ошибки ступеней −0,077363/+0,034934/−0,013858дБ, компрессии
L2/L3 нет. Медианный прирост matched power+11,233489дБ, изменение остатка
+0,517004дБ, улучшение matched/residual+10,734495дБ.
- **Результат:** `LINEAR_GAIN_IMPROVEMENT`. Фон стабилен; диагностические полосы
остатка растут слабее matched power. [RF-отчёт](lab065_rf_result.md),
[RF JSON](../data/processed/lab065/rf_result.json),
[capture JSON](../data/processed/lab065/capture_result.json).
- **STOP:** `RF TX=0`; повторов и изменения усилений не было. Lab065 не
коммитировалась, push отсутствует, Lab066 не создана.
- **Независимая проверка:** прямой пересчёт CS8 подтвердил SHA, счётчики,
локализацию, фон, matched/residual, `h`, CFO и спектральные полосы. Расхождений
нет. Quick gate1000/38/13 PASS, pytest1000 passed, diff check PASS.
- **Фиксация:** Lab065 зафиксирована локальным коммитом
`dacf04f2d0a15c0c1b56a7fb527db61fc2e38462`; push не выполнялся.
## Lab066 — исправленный повтор полного 500K PHY-сеанса
- **Основание:** Lab062 остаётся `RF_SESSION_INVALID_TX_UNDERRUN`, legacy/T2
0/16. Lab066 исправляет отсутствовавшие межпакетные паузы и использует
максимальный наблюдавшийся счётчик потерь.
- **TX:** 16 полных PHY-пакетов по256 байт, packet waveform92256 отсчётов;
15 нулевых пауз по40000 отсчётов/5мс и guards по800000 отсчётов/100мс.
Всего3676096 отсчётов,0,459512с; SHA-256
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`.
- **Параметры:** 1400МГц, Fs8Мвыб/с,500ксимв/с, несущая+50кГц,
TX VGA20дБ, RX LNA/VGA24/24дБ, amplifier/Bias-Tee OFF; прежняя геометрия.
- **Software gate:** legacy и T2 независимо дали16/16 HEADER/FEC/CRC,
packet output и точных упорядоченных payload. Fallback отсутствует; T2
инициализируется по собственной преамбуле каждого пакета.
- **RX-only preflight:** MASTER/SLAVE reg29=0x26, CLKIN DETECTED через132,113мс;
AC RMS0,827637, max|I|/|Q|=4/5, clipping0, maximum overruns0. Cleanup PASS,
процессы CLEAR.
- **Проверки:** quick gate1013/39/13 PASS, pytest1013 passed, diff check PASS;
verifier и adversary не оставили открытых замечаний.
- [Состояние](lab066_current_state.md), [RX-only отчёт](lab066_rx_preflight.md),
[подготовка](../data/processed/lab066/preparation.json),
[software gate](../data/processed/lab066/software_gate.json),
[preflight](../data/processed/lab066/preflight_result.json).
- **STOP:** RF TX=0; требуется отдельное прямое разрешение на один TX. Lab066
не коммитировалась, push не выполнялся, Lab067 не создавалась.
- **Единственный RF-сеанс:** один TX, повторов0; maximum underrun/overrun0/0,
clipping0, cleanup PASS, процессы CLEAR. RX16000000 отсчётов, SHA-256
`72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0`.
- **Строгий результат:** legacy и T2 совпали — по10/16 точных пакетов;
приняты4–8 и10–14, отказали на header пакеты1–3,9,15,16. Выигрыша T2 нет.
- **Физическое качество:** SER0,011047, RMS0,528374, matched/residual
+5,542117дБ, медианная неравномерность2,870070дБ. Все четыре показателя лучше
Lab056 B и невалидной Lab062.
- **Итог:** `PARTIAL_PACKET_RECOVERY_NO_T2_GAIN`. [RF-отчёт](lab066_rf_result.md),
[RF summary](../data/processed/lab066/rf_summary.json),
[packet CSV](../data/processed/lab066/packet_comparison.csv).
- **После сеанса:** RF TX=0, второй TX не выполнялся. Lab066 не
коммитировалась, push не выполнялся, Lab067 не создавалась.

View File

@@ -0,0 +1,70 @@
# Lab065: подготовка измерения при TX VGA 20 дБ
Lab065 повторяет измерение Lab064 с одним изменением аппаратного параметра:
TX VGA повышен с 10 до 20 дБ. Частота 1400000000 Гц, RX LNA/VGA 24/24 дБ,
геометрия, антенны, кабели, общая опора и цифровой waveform сохранены.
TX-файл Lab065 является побайтовой копией Lab064. Его SHA-256 должен быть равен
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
Длина остаётся равной 6000000 комплексных отсчётов и 0,75 с. Ступени
−18/−12/−6/0 дБ, PRBS7, BPSK, RRC, 20000 символов/с, Fs 8000000 отсчётов/с и
цифровая несущая +50000 Гц не меняются.
## Сравнение с Lab064
Для L0, L1, L2 и L3 анализатор выдаёт изменение согласованной мощности,
`|h|²`, остаточной мощности и matched/residual. Номинальное изменение TX VGA
равно +10 дБ и приводится как справочная величина, а не как условие PASS.
До RF зафиксированы численные правила:
- линейность внутри Lab065: максимальная абсолютная ошибка шага не более 1 дБ;
- компрессия: ошибка верхнего шага не более −1,5 дБ;
- заметный прирост: медиана прироста matched power не менее 3 дБ;
- заметное улучшение matched/residual: медиана не менее 3 дБ;
- согласованность прироста четырёх уровней: разброс не более 3 дБ;
- остаток считается растущим почти как сигнал, если его медианный прирост
отстаёт от медианного прироста matched power не более чем на 3 дБ.
Эти пороги нужны для воспроизводимой классификации конкретного опыта. Они не
заменяют измерение абсолютной RF-мощности.
## Аппаратная граница
Подготовка допускает программные проверки, common-clock preflight и RX-only
запись фона. Любая команда с `-t` внутри preflight запрещена. Передача Lab065
требует нового прямого разрешения пользователя и в текущий этап не входит.
Автоматических повторов и изменения усилений после ошибки нет.
`RF TX = 0`.
## Результат подготовки
Подготовка выполнена от HEAD
`cff226e0bf9e36a627829ea11a4ca3292ae20953`. Software gate прошёл. Побайтовая
копия waveform имеет требуемый SHA-256, подготовленная TX-команда содержит
TX VGA 20 дБ, а RX-команда сохраняет LNA/VGA 24/24 дБ.
Common-clock и RX-only preflight прошёл. Обе HackRF One r9 обнаружены с
прежними ролями. MASTER и SLAVE имеют reg29 `0x26`; SLAVE CLKIN обнаружен на
третьем опросе через 201,986 мс. В RX-фоне из 800000 отсчётов AC RMS равен
1,056565 кода, max |I|/|Q| равны 6/8, clipping и overruns отсутствуют.
Процессы до и после проверки имеют состояние CLEAR, ошибок очистки нет.
Lab065 готова к рассмотрению перед одним RF-сеансом. Текущая подготовка такого
разрешения не содержит. `RF TX = 0`.
## Единственный RF-сеанс
После отдельного прямого разрешения выполнен один TX. Общая опора прошла:
MASTER/SLAVE reg29 `0x26`, SLAVE CLKIN обнаружен на втором опросе через
136,897 мс. TX underrun, RX overrun, clipping и ошибки очистки отсутствуют.
Повторов не было. RX содержит 16000000 отсчётов, SHA-256
`845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e`.
Медианный прирост matched power относительно Lab064 равен +11,233489 дБ,
медианное изменение остатка +0,517004 дБ, улучшение matched/residual
+10,734495 дБ. Ошибки ступеней равны −0,077363/+0,034934/−0,013858 дБ.
Признаков компрессии нет. Классификация: `LINEAR_GAIN_IMPROVEMENT`.
[Полный RF-отчёт](lab065_rf_result.md). `RF TX = 0`.

114
docs/lab065_rf_result.md Normal file
View File

@@ -0,0 +1,114 @@
# Lab065: один OTA-сеанс при TX VGA 20 дБ
15 сентября 2026 года выполнен один разрешённый OTA-сеанс Lab065 на частоте
1400000000 Гц. Передача использовала точную копию waveform Lab064 с SHA-256
`fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9`.
Автоматических и ручных повторов не было.
## Аппаратная валидность
Перед TX обе HackRF One r9 и их роли подтверждены. MASTER и SLAVE имели reg29
`0x26`; SLAVE CLKIN обнаружен на втором опросе через 136,897 мс. RX стартовал
раньше TX. Результат сеанса:
```text
COMPLETED TX SESSIONS = 1
AUTOMATIC TX REPEATS = 0
TX UNDERRUN MAXIMUM = 0
RX OVERRUN MAXIMUM = 0
RX CLIPPING = NO
CLEANUP ERRORS = []
SDR PROCESSES = CLEAR
```
RX содержит 16000000 комплексных отсчётов, 32000000 байт и 2,0 с. SHA-256:
`845d7b7dcaef70e179ab69e6ba0041601859630786d2f4d51c16a8c1c7869d7e`.
Максимумы всей записи равны `|I|=10` и `|Q|=10`; rail-кодов нет.
## Линейность внутри Lab065
| Переход | TX, дБ | RX matched, дБ | Ошибка, дБ |
|---|---:|---:|---:|
| L0 → L1 | +5,977270 | +5,899908 | −0,077363 |
| L1 → L2 | +5,998157 | +6,033091 | +0,034934 |
| L2 → L3 | +5,998257 | +5,984400 | −0,013858 |
Максимальная абсолютная ошибка равна 0,077363 дБ при границе 1 дБ. Ошибка
верхней ступени равна −0,013858 дБ при пороге компрессии −1,5 дБ. Согласованная
мощность L3 выросла относительно L2 на 5,984400 дБ. Признаков компрессии на
L2/L3 нет.
## Сравнение Lab064 и Lab065
| Уровень | Matched, дБ | Остаток, дБ | Улучшение matched/residual, дБ | Изменение \|h\|², дБ |
|---|---:|---:|---:|---:|
| L0 | +11,179328 | −0,243276 | +11,422604 | +11,179328 |
| L1 | +11,119466 | −2,366286 | +13,485752 | +11,119466 |
| L2 | +11,323670 | +1,277284 | +10,046386 | +11,323670 |
| L3 | +11,287650 | +3,907581 | +7,380069 | +11,287650 |
Медианный прирост согласованной мощности равен +11,233489 дБ. Его разброс по
четырём уровням составляет 0,204204 дБ. Медианное изменение остатка равно
+0,517004 дБ, а медианное улучшение matched/residual равно +10,734495 дБ.
При TX VGA 20 дБ matched/residual для L0/L1/L2/L3 равен
−20,663889/−12,091711/−6,645283/−4,753267 дБ. Отношение улучшилось на каждом
уровне, хотя даже на L3 согласованная составляющая остаётся слабее остатка.
## Комплексный коэффициент передачи
| Уровень | h Lab064 | h Lab065 | \|h\| Lab064 | \|h\| Lab065 | Изменение фазы, градусы |
|---|---|---|---:|---:|---:|
| L0 | 0,001876 − j0,004508 | −0,006915 + j0,016279 | 0,004883 | 0,017686 | −179,581 |
| L1 | 0,004471 − j0,001938 | −0,010180 + j0,014271 | 0,004873 | 0,017529 | +148,932 |
| L2 | 0,003277 − j0,003478 | −0,009134 + j0,015045 | 0,004779 | 0,017600 | +167,966 |
| L3 | 0,003384 − j0,003391 | −0,009107 + j0,015028 | 0,004791 | 0,017572 | +166,276 |
Модуль `h` внутри Lab065 почти постоянен: 0,017529…0,017686. Его мощность
относительно Lab064 выросла на 11,119…11,324 дБ, что согласуется с matched
power. Фаза между отдельными сеансами не имеет непрерывной временной привязки,
поэтому приведённый поворот является диагностикой конкретных записей, а не
калиброванной фазовой характеристикой тракта.
## Фон и остаточные полосы
Пять нулевых окон Lab065 имеют AC RMS 0,763722…0,857191 кода. Разброс фоновой
AC-мощности равен 1,002840 дБ при границе 3 дБ, поэтому фон классифицирован как
стабильный. В Lab064 разброс составлял 0,119530 дБ; фон Lab065 стабильный, но
межоконный разброс стал больше.
Изменение диагностических полос остатка относительно Lab064:
| Уровень | 50 кГц, дБ | 100 кГц, дБ | 150 кГц, дБ |
|---|---:|---:|---:|
| L0 | +2,869745 | +2,654569 | +2,258651 |
| L1 | +3,032680 | +2,378561 | +1,300716 |
| L2 | +5,594944 | +4,091263 | +2,888397 |
| L3 | +7,304593 | +6,432281 | +4,996232 |
На верхних уровнях остаточные полосы выросли, особенно около 50 кГц. На L3 их
рост остаётся ниже прироста matched power +11,287650 дБ, а matched/residual
улучшился на 7,380069 дБ. Эти цифровые полосы не являются калиброванным
измерением RF-гармоник.
## Итог
```text
CLASSIFICATION = LINEAR_GAIN_IMPROVEMENT
```
Повышение TX VGA с 10 до 20 дБ дало устойчивый прирост согласованной принятой
мощности примерно на 11,2 дБ без потери линейности. Остаток вырос существенно
меньше, поэтому отношение полезной составляющей к остатку улучшилось на всех
четырёх уровнях. Верхняя ступень не показывает компрессии.
Результат остаётся относительным измерением всего OTA-тракта. Он не определяет
абсолютную RF-мощность и не доказывает прохождение полного PHY-пакета, FEC, CRC,
T2 или JPEG.
Независимая проверка пересчитала сохранённый CS8, локализацию, фон, matched,
residual, `h`, CFO, внутреннюю линейность и сравнение с Lab064. Расхождений нет.
Рекомендуемый следующий одиночный шаг: рассмотреть scope Lab065 и отдельно
разрешить локальный коммит. Новую RF-передачу для этого выполнять не требуется.
`RF TX = 0`. Lab065 не коммитировалась, push не выполнялся, Lab066 не создана.

View File

@@ -0,0 +1,83 @@
# Lab065: подготовка и RX-only preflight
15 сентября 2026 года подготовлена Lab065 с TX VGA 20 дБ. Пользователь
подтвердил неизменность расстояния около 1,2 м, прямой видимости, положения TX
возле окна и RX на столе, антенн, поляризации, USB, общей опоры и RF-кабелей.
## Неизменность сигнала
TX-файл Lab065 является побайтовой копией файла Lab064:
```text
TX samples = 6000000
TX bytes = 12000000
TX duration = 0.750000 s
TX SHA-256 = fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9
WAVEFORM UNCHANGED FROM LAB064 = YES
```
Сохранены PRBS7, BPSK, RRC, ступени −18/−12/−6/0 дБ, интервалы и нулевые
защитные участки, 20000 символов/с, Fs 8000000 комплексных отсчётов/с и
цифровая несущая +50000 Гц. Из аппаратных параметров изменён только TX VGA:
10 → 20 дБ. RX LNA/VGA остаются равными 24/24 дБ; RF amplifier и Bias-Tee
выключены.
Software gate прошёл. Он подтвердил SHA, пригодность результата Lab064 как
базы сравнения, синтетическую проверку линейности и наличие численных правил
классификации, зафиксированных до RF.
## Common clock
Перед аппаратными командами проверка процессов через psutil осмотрела 289
процессов и получила CLEAR. Обнаружены обе HackRF One r9 с firmware и
libhackrf 2024.02.1. Роли совпали с Lab064:
- MASTER `000000000000000097c063dc323b97d3`;
- SLAVE `000000000000000097c063dc2e5327d3`.
Результат общей опоры:
```text
MASTER reg29 = 0x26
SLAVE reg29 = 0x26
SLAVE CLKIN = DETECTED
CLKIN query = 3
time to detect = 201.986 ms
```
Первые два опроса не обнаружили CLKIN, третий опрос прошёл в пределах штатного
окна ожидания. Итоговый common-clock gate имеет статус PASS.
## RX-only фон
SLAVE записал 800000 комплексных отсчётов при 1400000000 Гц и RX LNA/VGA
24/24 дБ.
| Метрика | Значение |
|---|---:|
| Полный RMS IQ | 2,333068 кода |
| AC RMS после удаления среднего | 1,056565 кода |
| Средние I / Q | 0,948896 / 1,851074 кода |
| max \|I\| / \|Q\| | 6 / 8 кодов |
| Максимальная комплексная амплитуда | 8,062258 кода |
| Clipping | 0 / 800000 отсчётов |
| RX overruns, максимум / финал | 0 / 0 |
| Неожиданно сильный сигнал | нет |
SHA-256 RX-фона:
`be3c61dfa1bc40d1a9f6777689026804a1ccfc340b82680d3ec5304ddd68f78c`.
Оба CLKOUT выключены без ошибок. Повторная проверка процессов получила CLEAR.
Команд передачи не было.
```text
LAB065 PREPARATION = PASS
COMMON CLOCK = PASS
RX PREFLIGHT = PASS
RF TX = 0
READY FOR SINGLE LAB065 TX = YES
WAITING FOR EXPLICIT RF AUTHORIZATION = YES
```
Готовность означает, что программные и аппаратные входные проверки пройдены.
Она не разрешает передачу и не предсказывает результат измерения.

View File

@@ -0,0 +1,92 @@
# Lab066 — исправленный повтор полного 500K PHY-сеанса
## Цель и границы
Lab066 повторяет исходную цель Lab062 после двух установленных дефектов того
сеанса: пакеты Lab062 шли вплотную, без заявленных пауз 5 мс, а в TX-журнале
был промежуточный underrun. Исторический результат Lab062 сохраняется без
изменений: `RF_SESSION_INVALID_TX_UNDERRUN`, legacy 0/16, T2 0/16.
На этапе подготовки Lab066 была ограничена программным декодированием,
common-clock и RX-only preflight; передача тогда не выполнялась. После этого
пользователь отдельно разрешил один TX. Автоматический повтор оставался
запрещён и не выполнялся.
## Радиопараметры
- RF: 1400000000 Гц;
- Fs: 8000000 комплексных отсчётов/с;
- символьная скорость: 500000 символов/с, 16 отсчётов/символ;
- цифровая несущая: +50000 Гц;
- TX VGA: 20 дБ;
- RX LNA/VGA: 24/24 дБ;
- RF amplifier и Bias-Tee: OFF;
- 16 штатных PHY-пакетов с PRBS7-преамбулой, заголовком, FEC и CRC;
- payload: 256 байт, детерминированный и различимый для каждого пакета;
- геометрия: около 1,2 м, LOS в помещении, прежние антенны и поляризация;
MASTER TX возле окна, SLAVE RX на столе.
## Итоговый TX-файл
Каждый packet waveform занимает 92256 отсчётов. Между соседними пакетами
записаны 15 нулевых пауз по 40000 отсчётов, то есть ровно по 5 мс. Перед первым
и после последнего пакета добавлены нулевые guards по 800000 отсчётов, или по
100 мс.
Расчёт длины:
```text
16 × 92256 + 15 × 40000 = 2076096 отсчётов без внешних guards
2076096 + 2 × 800000 = 3676096 отсчётов всего
3676096 / 8000000 = 0,459512 с
```
Фактическая длина совпала с расчётом: 3676096 комплексных отсчётов,
7352192 байта. SHA-256:
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`.
Повторная сборка дала те же байты. Нулевые guards и каждая из 15 пауз проверены
по точным границам из сохранённой разметки.
## Программное декодирование
Весь конечный TX-файл дважды прошёл один и тот же строгий приёмный тракт. Режимы
legacy и T2 запускались отдельно, автоматического fallback между ними нет.
| Режим | Кандидаты | Header | FEC | CRC | Выход | Payload и порядок |
|---|---:|---:|---:|---:|---:|---:|
| legacy | 16 | 16/16 | 16/16 | 16/16 | 16/16 | 16/16 |
| T2 | 16 | 16/16 | 16/16 | 16/16 | 16/16 | 16/16 |
Позиции принятых пакетов равны ожидаемым позициям в разметке. Для каждого
кандидата T2 заново оценивает начальные параметры по собственной преамбуле.
Наблюдение дало по два локальных запуска слежения на пакет: для заголовка и
полного кадра. Контекст обработки создаётся заново, состояния между пакетами
не переносятся.
Аппаратный анализ применил legacy и T2 к одному сохранённому RX-файлу. До
строгого вердикта декодеру не передавались payload или ожидаемые биты. Известный
TX использован после вердикта только для физических диагностик и сравнения с
Lab056 B, Lab062 и Lab066.
## Проверки перед preflight
- `python tools/quick_gate.py`: PASS, 1013 проверок ядра, 39 функциональных,
13 аппаратных пропусков;
- `python -m pytest tests/ -q`: 1013 passed;
- `git diff --check`: PASS.
Независимый verifier пересчитал длину, SHA, все нулевые диапазоны, пакетные
срезы, программное декодирование и RX-only метрики; расхождений нет. Read-only
adversary проверил запрет TX, одноразовость, привязку capture к конфигурации,
счётчики, live-проверку фона и границу oracle-диагностики; открытых замечаний
нет.
## Текущее состояние
Common-clock и RX-only preflight прошли; измерения приведены в
[отдельном отчёте](lab066_rx_preflight.md). `RF TX=0`. Lab066 не
коммитировалась, push не выполнялся, Lab067 не создавалась.
После отдельного разрешения выполнен один аппаратно валидный RF-сеанс без
повторов. Legacy и T2 дали одинаковые 10/16 точных пакетов. Полные результаты
и физическое сравнение приведены в [RF-отчёте](lab066_rf_result.md).

118
docs/lab066_rf_result.md Normal file
View File

@@ -0,0 +1,118 @@
# Lab066 — один OTA RF-сеанс полного 500K PHY-потока
## Аппаратная валидность
15 сентября 2026 года выполнен один разрешённый TX всего подготовленного
потока. Передача запускалась один раз; автоматических повторов не было. RX
стартовал раньше TX и сохранил 16000000 комплексных отсчётов за 2,000000 с.
- TX SHA-256:
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`;
- RX SHA-256:
`72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0`;
- TX underrun observations: 0, 0; maximum 0;
- RX overrun observations: 0, 0, 0, 0; maximum 0;
- clipping: нет;
- cleanup errors: нет;
- SDR-процессы до и после: `CLEAR`.
Непосредственно перед TX обе платы имели reg29=`0x26`; SLAVE CLKIN обнаружен
на втором опросе через 131,291 мс. Live RX-проверка дала AC RMS 1,173567 кода,
max |I|/|Q|=4/5 и не обнаружила clipping или сильный фон. После сеанса оба
CLKOUT отключены.
## Строгое декодирование одной RX-записи
Legacy и T2 получили одну и ту же непрерывную IQ-запись. Поиск кандидатов,
временная синхронизация, RRC, пороги, PHY header, FEC, CRC и критерии принятия
не менялись. Автоматического fallback между режимами нет.
| Метрика | legacy | T2 |
|---|---:|---:|
| Кандидаты | 145 | 145 |
| Header PASS | 10 | 10 |
| FEC started | 10 | 10 |
| FEC PASS | 10 | 10 |
| CRC started | 10 | 10 |
| CRC PASS | 10 | 10 |
| Packet output | 10 | 10 |
| Exact payload match | 10 | 10 |
Оба режима приняли пакеты 4, 5, 6, 7, 8, 10, 11, 12, 13 и 14. Пакеты 1, 2,
3, 9, 15 и 16 не прошли PHY header у обоих режимов. Выигрыша одного режима
над другим нет.
| Пакет | Legacy H/F/C/O | T2 H/F/C/O |
|---:|:---:|:---:|
| 1 | 0/0/0/0 | 0/0/0/0 |
| 2 | 0/0/0/0 | 0/0/0/0 |
| 3 | 0/0/0/0 | 0/0/0/0 |
| 4 | 1/1/1/1 | 1/1/1/1 |
| 5 | 1/1/1/1 | 1/1/1/1 |
| 6 | 1/1/1/1 | 1/1/1/1 |
| 7 | 1/1/1/1 | 1/1/1/1 |
| 8 | 1/1/1/1 | 1/1/1/1 |
| 9 | 0/0/0/0 | 0/0/0/0 |
| 10 | 1/1/1/1 | 1/1/1/1 |
| 11 | 1/1/1/1 | 1/1/1/1 |
| 12 | 1/1/1/1 | 1/1/1/1 |
| 13 | 1/1/1/1 | 1/1/1/1 |
| 14 | 1/1/1/1 | 1/1/1/1 |
| 15 | 0/0/0/0 | 0/0/0/0 |
| 16 | 0/0/0/0 | 0/0/0/0 |
H/F/C/O означает header/FEC/CRC/output. Для каждого из 16 ожидаемых пакетов
детектор имел кандидат точно на диагностической границе пакета. У шести
непринятых пакетов FEC и CRC не запускались из-за отказа PHY header.
## Физическая диагностика после строгого результата
Сопоставимые показатели рассчитаны отдельно по всем 16 пакетам методикой
Lab058. Известный TX использован после завершения строгих прогонов и не влиял
на принятие пакетов.
| Показатель | Lab066 |
|---|---:|
| Доля ошибочных символов | 0,0110466, или 1,104664% |
| Нормированное RMS-отклонение символов | 0,528374 |
| Согласованная мощность | 8,359398 код² |
| Остаточная мощность | 2,333261 код² |
| Matched/residual | +5,542117 дБ |
| Неравномерность усиления p95–p05 | медиана 2,870070 дБ |
| Диапазон неравномерности по пакетам | 2,724906…3,111679 дБ |
Остаток включает шум и немоделированные искажения; это не калиброванная
мощность шума. Физические показатели являются диагностическими и не превращают
непринятый пакет в успешный.
## Сравнение
| Показатель | Lab056 B | Lab062 | Lab066 |
|---|---:|---:|---:|
| Доля ошибочных символов | 0,034959 | 0,099287 | 0,011047 |
| RMS-отклонение | 0,845350 | 1,220401 | 0,528374 |
| Matched/residual | +1,464846 дБ | −1,730051 дБ | +5,542117 дБ |
| Неравномерность полосы | 5,471382 дБ | 6,660341 дБ | 2,870070 дБ |
Относительно Lab056 B доля ошибочных символов ниже на 0,0239123, или на
68,40%; RMS-отклонение ниже на 0,316976, matched/residual выше на 4,077271 дБ,
а неравномерность меньше на 2,601312 дБ.
Относительно Lab062 доля ошибочных символов ниже на 0,0882400, или на 88,87%;
RMS-отклонение ниже на 0,692027, matched/residual выше на 7,272168 дБ, а
неравномерность меньше на 3,790271 дБ. Сравнение с Lab062 контекстное: тот
сеанс остаётся аппаратно невалидным из-за TX underrun.
## Итог
Классификация: `PARTIAL_PACKET_RECOVERY_NO_T2_GAIN`. Аппаратно чистая запись
дала одинаковые 10/16 точных пакетов в legacy и T2. Физическое качество заметно
лучше Lab056 B и невалидной Lab062, однако шесть пакетов остановились на PHY
header, поэтому результат не является полным 16/16 успехом.
Следующий отдельный шаг, если он будет разрешён: только офлайн сравнить ошибки
PHY header, timing и CFO пакетов 1–3, 9, 15–16 с принятыми пакетами по уже
сохранённому RX. Новый TX для этого не нужен.
Сейчас `RF TX=0`, процессы `CLEAR`. Lab066 не коммитировалась, push не
выполнялся, Lab067 не создавалась.

View File

@@ -0,0 +1,35 @@
# Lab066 — common-clock и RX-only preflight
## Результат
15 сентября 2026 года выполнен один preflight без передачи. До и после него
SDR-процессы имели состояние `CLEAR`; обе HackRF One r9 и назначение ролей
подтверждены.
Общая опора прошла проверку:
- MASTER reg29 = `0x26`;
- SLAVE reg29 = `0x26`;
- SLAVE CLKIN = `DETECTED`;
- обнаружение произошло на втором опросе через 132,113 мс.
RX-only команда записала 800000 комплексных отсчётов на частоте 1400 МГц с
RX LNA/VGA 24/24 дБ. Максимум всех наблюдавшихся счётчиков overrun равен нулю.
После проверки оба CLKOUT отключены; ошибок очистки нет.
## Уровень фона
- полный RMS: 2,225641 кода;
- AC RMS: 0,827637 кода;
- средние I/Q: 0,947530 / 1,835941 кода;
- max |I| / |Q|: 4 / 5;
- доля clipped components и samples: 0 / 0;
- неожиданный сильный сигнал: нет;
- SHA-256 RX-only записи:
`ff817582e6a8fc03b489bf89f46322a9db8a9493335a4360a1b0f9bedad7c4d5`.
## Граница разрешения
Preflight не запускал TX и не расходует право на будущий единственный сеанс.
Сейчас `RF TX=0`. Для одного Lab066 TX требуется отдельное прямое разрешение.
Автоматические повторы, изменение усилений и переход к Lab067 запрещены.

View File

@@ -0,0 +1,49 @@
# Lab067: текущее состояние
Дата: 15 сентября 2026 года.
## Граница работы
Lab067 выполнена полностью офлайн по сохранённой аппаратно валидной записи
Lab066. HackRF Plus не опрашивался, аппаратные команды не запускались, RF TX
оставался равным 0. Алгоритмы приёмника, PHY-заголовок, пороги, FEC, CRC и
критерии принятия пакета не менялись.
Исторический строгий результат Lab066 сохранён:
- legacy: 10/16 точных пакетов;
- T2: 10/16 точных пакетов;
- приняты пакеты 4–8 и 10–14;
- пакеты 1–3, 9, 15 и 16 остановились на PHY-заголовке.
## Проверенные исходники
- TX: 7 352 192 байта, 3 676 096 комплексных отсчётов, SHA-256
`621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199`;
- RX: 32 000 000 байт, 16 000 000 комплексных отсчётов, 2,0 с при
8 Мвыб/с, SHA-256
`72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0`;
- на расчётной позиции каждого из 16 пакетов найден строгий кандидат с
нулевым смещением уточнённой позиции.
## Текущий вывод
Все шесть отказов локализованы в PHY-заголовке: преамбулы этих пакетов лежат
внутри диапазона принятых пакетов, но в коротком окне от последних 128 символов
преамбулы до конца заголовка растёт остаток. Медиана `matched/residual` снижается
с 5,906881 dB у принятых пакетов до 4,146180 dB у отказавших.
28 сырых ошибок заголовка занимают разные позиции; ни одна позиция не
повторяется между отказавшими пакетами. TX-заголовки всех 16 пакетов идентичны,
корректны и дают текущий программный круговой тест 16/16 в legacy и T2.
Систематическая ошибка обработки заголовка не найдена.
`HEADER_FAILURE_ROOT_CAUSE = LOCAL_RF_QUALITY`
`ONE SMALL FIX JUSTIFIED = NO`
Рекомендация: закрыть текущую диагностическую ветку на достигнутом результате
10/16 и перейти к ветке управления и телеметрии CRSF/ELRS. QPSK с меньшей
символьной скоростью можно рассматривать позднее для видеоканала.
Lab067 не коммитировалась. Push не выполнялся.

View File

@@ -0,0 +1,151 @@
# Lab067: офлайн-анализ отказов PHY-заголовка Lab066
## Результат
Причина шести отказов совместима с кратковременным ростом несогласованного
остатка именно в области заголовка. Данных за небольшую ошибку логики приёмника,
временной привязки, сопровождения несущей или состояния кандидатов нет.
`HEADER_FAILURE_ROOT_CAUSE = LOCAL_RF_QUALITY`
`ONE SMALL FIX JUSTIFIED = NO`
`BRANCH DECISION = NO_SMALL_FIX_JUSTIFIED`
Исторический результат Lab066 остаётся 10/16 для legacy и 10/16 для T2.
Oracle-расчёты ниже диагностические и не меняют строгий результат.
## Целостность и геометрия данных
SHA-256 TX и RX совпали с зафиксированными значениями Lab066. RX имеет
16 000 000 комплексных отсчётов, 32 000 000 байт и длительность 2,0 с при
8 Мвыб/с. Все 16 ожидаемых позиций подтверждены строгими кандидатами;
уточнённое смещение для каждого пакета равно 0 отсчётов.
## Сводная таблица
`P err` означает ошибки 1280-символьной преамбулы. `H raw` означает ошибки
среди 144 переданных символов заголовка. `H vote` означает ошибки после штатной
мягкой суммы трёх повторений. Локальная метрика рассчитана по фиксированному
окну: последние 128 символов преамбулы и весь заголовок.
| Пакет | Строгий результат | P err | P corr | H raw | Копии 1/2/3 | H vote | Поля с ошибкой | CFO legacy, Hz | CFO T2/P3, Hz | Timing, отсч. | Локальный M/R, dB | RMS | Legacy/T2 diff | Классификация |
|---:|---|---:|---:|---:|---:|---:|---|---:|---:|---:|---:|---:|---:|---|
| 1 | HEADER FAIL | 15 | 0,898114 | 3 | 1/2/0 | 1 | length | -2,662 | -5,388 | 0 | 5,127352 | 0,554156 | 0 | HEADER_SPECIFIC_FAILURE |
| 2 | HEADER FAIL | 10 | 0,892575 | 6 | 3/2/1 | 1 | length | -11,085 | -5,424 | 0 | 3,453566 | 0,671926 | 1 | HEADER_SPECIFIC_FAILURE |
| 3 | HEADER FAIL | 11 | 0,883245 | 3 | 1/1/1 | 1 | magic | -2,144 | 2,717 | 0 | 4,588577 | 0,589619 | 0 | HEADER_SPECIFIC_FAILURE |
| 4 | PASS | 12 | 0,870256 | 0 | 0/0/0 | 0 | none | -4,970 | 0,501 | 0 | 4,498689 | 0,595752 | 0 | PASSED |
| 5 | PASS | 15 | 0,882299 | 0 | 0/0/0 | 0 | none | 3,163 | -1,271 | 0 | 8,091353 | 0,393942 | 0 | PASSED |
| 6 | PASS | 17 | 0,883426 | 0 | 0/0/0 | 0 | none | 5,595 | 1,262 | 0 | 7,317955 | 0,430628 | 0 | PASSED |
| 7 | PASS | 7 | 0,895704 | 1 | 0/0/1 | 0 | none | -1,265 | -1,236 | 0 | 6,319905 | 0,483064 | 1 | PASSED |
| 8 | PASS | 10 | 0,893515 | 3 | 1/2/0 | 0 | none | 9,131 | -0,471 | 0 | 4,986495 | 0,563216 | 0 | PASSED |
| 9 | HEADER FAIL | 13 | 0,883101 | 4 | 1/2/1 | 1 | length | 6,363 | -1,982 | 0 | 3,775053 | 0,647511 | 0 | HEADER_SPECIFIC_FAILURE |
| 10 | PASS | 14 | 0,875698 | 1 | 0/0/1 | 0 | none | 10,521 | 4,784 | 0 | 4,964081 | 0,564672 | 0 | PASSED |
| 11 | PASS | 14 | 0,873945 | 0 | 0/0/0 | 0 | none | -4,338 | 5,576 | 0 | 5,008753 | 0,561775 | 0 | PASSED |
| 12 | PASS | 15 | 0,869109 | 0 | 0/0/0 | 0 | none | -14,662 | -12,429 | 0 | 6,568840 | 0,469416 | 0 | PASSED |
| 13 | PASS | 19 | 0,887088 | 0 | 0/0/0 | 0 | none | 2,006 | -3,871 | 0 | 7,449922 | 0,424135 | 0 | PASSED |
| 14 | PASS | 13 | 0,897960 | 1 | 0/0/1 | 0 | none | 7,211 | 3,326 | 0 | 5,493857 | 0,531260 | 1 | PASSED |
| 15 | HEADER FAIL | 12 | 0,888738 | 7 | 2/2/3 | 2 | magic, length | -5,732 | -3,520 | 0 | 4,055203 | 0,626960 | 0 | HEADER_SPECIFIC_FAILURE |
| 16 | HEADER FAIL | 16 | 0,880510 | 5 | 1/2/2 | 2 | version, length | 4,734 | 228,105 | 0 | 4,237157 | 0,613963 | 107 | HEADER_SPECIFIC_FAILURE |
Полные строки битов `EXPECTED HEADER BITS`, `RECEIVED RAW HEADER BITS`,
`VOTED HEADER BITS`, причины строгого отказа и позиции ошибок находятся в
`summary.json` и `header_packet_comparison.csv`.
## Преамбула: распределения
| Метрика | Passed min / median / max | Failed min / median / max |
|---|---:|---:|
| Ошибки, бит | 7 / 14 / 19 | 10 / 12,5 / 16 |
| Доля ошибок | 0,005469 / 0,010938 / 0,014844 | 0,007813 / 0,009766 / 0,012500 |
| Корреляция | 0,869109 / 0,882862 / 0,897960 | 0,880510 / 0,885991 / 0,898114 |
| RMS отклонение | 0,489327 / 0,532530 / 0,567221 | 0,489090 / 0,523373 / 0,538197 |
| Matched power | 7,774573 / 8,330450 / 8,667865 | 7,968245 / 8,385475 / 8,982336 |
| Residual power | 2,053382 / 2,346123 / 2,645143 | 2,118219 / 2,258980 / 2,379173 |
| Matched/residual, dB | 4,924949 / 5,473142 / 6,208025 | 5,381167 / 5,624735 / 6,212224 |
Преамбула не отделяет отказавшие пакеты от принятых: медианные физические
показатели failed-группы не хуже. Ухудшение появляется после последних
128 символов преамбулы и внутри заголовка. В этом окне медиана residual power
равна 3,089822 у failed и 2,219458 у passed; медиана `matched/residual` равна
4,146180 и 5,906881 dB соответственно.
## Карта ошибок заголовка
У отказавших пакетов найдено 28 сырых ошибочных решений. Ни одна позиция
не повторяется в двух failed-пакетах. Ошибки встречаются во всех трёх копиях:
9, 11 и 8 ошибок соответственно. После мягкой суммы остаётся 1 ошибка у
пакетов 1, 2, 3 и 9 и 2 ошибки у пакетов 15 и 16.
Общей позиции, стабильно плохой копии, концентрации только в начале или только
в конце заголовка нет. Это не поддерживает гипотезу о систематической ошибке
формата или индексации заголовка.
## Переход преамбула → заголовок
На последних 128 символах преамбулы медиана фазовой RMS-ошибки составляет
0,361 rad у passed и 0,541 rad у failed. На заголовке значения составляют
0,388 и 0,627 rad. У failed-пакетов ошибки заголовка лежат в диапазоне 3–7
сырых решений, а в первых 128 символах после заголовка медиана ошибок равна 0.
Медианная амплитуда остаётся близкой к единице. Общего скачка амплитуды,
смещения точки выборки или одинакового фазового скачка не найдено. Наблюдается
короткое ухудшение фазового остатка, различное по позиции в каждом пакете.
## Timing, CFO и T2
Штатное timing-смещение и диагностическое oracle-смещение равны 0 отсчётов для
всех 16 пакетов. Расхождение legacy CFO с контрольной оценкой создаёт за весь
144-символьный заголовок фазовую ошибку от -0,02488 до +0,02079 rad. Этот
масштаб недостаточен для объяснения 1–2 ошибок после мягкого голосования.
T2 начинает влиять с символа 1280, то есть с начала заголовка. Для пакетов
1, 3, 4–6, 8–13 и 15 его жёсткие решения заголовка побитно совпадают с legacy.
Различия равны одному биту для пакетов 2, 7 и 14. У пакета 16 оценка P3 стала
выбросом 228,105 Hz и изменила 107 решений. Это ухудшение одного пакета не
объясняет шесть legacy-отказов и не даёт основания менять T2 в этой ветке.
## Паузы и положение в сеансе
Для пакета 1 исследован внешний guard длиной 800 000 отсчётов. Для пакетов
2–16 исследованы реальные паузы по 40 000 отсчётов. AC RMS перед пакетами
лежит в диапазоне 0,744879–1,302893. Признаков хвоста предыдущей формы по
критерию превышения первых 8000 отсчётов над серединой паузы на 3 dB нет.
После пакета 16 отдельно проверен конечный guard.
Группы START и END имеют больше ошибок заголовка, чем MIDDLE 1, однако пакет 9
отказывает в середине, пакеты 4, 13 и 14 проходят, а timing, CFO и фон не дают
монотонного временного тренда. Расположение пакетов не доказывает
`START_END_TRANSIENT`.
## 145 кандидатов
Строгий журнал содержит 145 проверок:
- 27 проверок на точных позициях преамбулы, 16 уникальных позиций и 11
повторных проверок той же позиции;
- 54 локальных максимума повторяющейся PRBS-структуры внутри формы пакета;
- 56 кандидатов в нулевых паузах;
- 8 кандидатов вне этих областей.
Каждый кандидат получает новый `PacketMeasure` и полностью независимый строгий
проход. Состояние после отказа не переносится. Граница `accepted_until`
изменяется только после CRC PASS. Большое число кандидатов увеличивает объём
работы, но не объясняет выборочные ошибки настоящих заголовков.
## TX и решение по ветке
Все 16 TX-заголовков побитно идентичны. Magic `0xD391`, версия 1, длина
268 байт, checksum и тройное повторение корректны. Текущий программный прогон
дал 16/16 точных пакетов и для legacy, и для T2.
Конкретная небольшая ошибка приёмника, которая объяснила бы значимую часть
шести отказов, не найдена. Зафиксирован достигнутый результат: 10/16 точных
полных пакетов при 500 ксимв/с и TX VGA 20 dB в аппаратно валидной OTA-записи.
Текущую диагностическую ветку следует закрыть. Следующая проектная ветка:
CRSF/ELRS для управления и телеметрии.
QPSK с меньшей скоростью символов остаётся будущим кандидатом для видеоканала.
В Lab067 QPSK, новый PHY и изменения BPSK не выполнялись.
RF TX = 0. Lab067 не коммитировалась. Push не выполнялся.

View File

@@ -0,0 +1,623 @@
"""Lab065: та же лестница уровней при TX VGA 20 дБ.
Подготовка, программная проверка и preflight не передают RF. Единственный
аппаратный сеанс доступен только отдельной командой с явным разрешением.
"""
from __future__ import annotations
from dataclasses import asdict
import argparse
import hashlib
import json
from pathlib import Path
import shutil
import subprocess
import sys
import time
if __package__ in (None, ""):
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import numpy as np
from experiments import lab054_hardware_control as lab054
from experiments import lab056_capture_runner as hardware
from experiments import lab064_rf_level_linearity as lab064
ROOT = Path(__file__).resolve().parents[1]
RAW = ROOT / "data/raw/lab065"
OUTPUT = ROOT / "data/processed/lab065"
LAB064_RAW = ROOT / "data/raw/lab064"
LAB064_RESULT = ROOT / "data/processed/lab064/rf_result.json"
EXPECTED_TX_SHA256 = "fa260719659b2984fe29588238415622cd75c6daefec6cc9130ba1bf851bd3f9"
TX_VGA_DB = 20
TX_VGA_DIFFERENCE_DB = 10.0
MEANINGFUL_GAIN_DB = 3.0
MEANINGFUL_RATIO_IMPROVEMENT_DB = 3.0
MAX_LEVEL_TO_LEVEL_GAIN_SPREAD_DB = 3.0
RESIDUAL_TRACKING_MARGIN_DB = 3.0
POINT = hardware.OperatingPoint(**asdict(lab064.POINT))
def save_new(path: Path, value: dict) -> None:
"""Записать новое свидетельство и не перезаписывать результат опыта."""
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("x", encoding="utf-8") as stream:
json.dump(value, stream, ensure_ascii=False, indent=2, allow_nan=False)
stream.write("\n")
def save_text_new(path: Path, value: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("x", encoding="utf-8") as stream:
stream.write(value)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while block := stream.read(1024 * 1024):
digest.update(block)
return digest.hexdigest()
def parameters() -> dict:
result = lab064.parameters()
result["tx_vga_db"] = TX_VGA_DB
result["geometry_unchanged_from_lab064"] = True
result["waveform_source"] = "Lab064 exact CS8 copy"
return result
def transfer_command(path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
command = lab064.transfer_command(path, samples, transmit=transmit)
if transmit:
command[command.index("-x") + 1] = str(TX_VGA_DB)
return command
def validate_lab064_waveform() -> dict:
source = LAB064_RAW / "tx_level_staircase.cs8"
metadata = json.loads((LAB064_RAW / "tx_metadata.json").read_text(encoding="utf-8"))
digest = sha256(source)
if (
digest != EXPECTED_TX_SHA256
or metadata.get("tx_sha256") != EXPECTED_TX_SHA256
or source.stat().st_size != 2 * lab064.TX_SAMPLES
or metadata.get("segments") != lab064.prepared_segments()[0]
):
raise RuntimeError("Waveform Lab064 не совпадает с зафиксированным образцом")
return metadata
def validate_prepared() -> dict:
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
path = RAW / "tx_level_staircase.cs8"
if (
metadata.get("lab") != "Lab065"
or metadata.get("stage") != "PREPARATION_ONLY"
or metadata.get("rf_tx_count") != 0
or metadata.get("parameters") != parameters()
or metadata.get("tx_sha256") != EXPECTED_TX_SHA256
or metadata.get("source_tx_sha256") != EXPECTED_TX_SHA256
or path.stat().st_size != 2 * lab064.TX_SAMPLES
or sha256(path) != EXPECTED_TX_SHA256
):
raise RuntimeError("Подготовленный Lab065 TX или параметры изменились")
return metadata
def prepare() -> dict:
"""Скопировать точные байты Lab064 в отдельный каталог Lab065."""
source_metadata = validate_lab064_waveform()
RAW.mkdir(parents=True, exist_ok=True)
OUTPUT.mkdir(parents=True, exist_ok=True)
target = RAW / "tx_level_staircase.cs8"
if target.exists():
raise FileExistsError("TX-файл Lab065 уже существует")
shutil.copyfile(LAB064_RAW / "tx_level_staircase.cs8", target)
if sha256(target) != EXPECTED_TX_SHA256:
target.unlink()
raise RuntimeError("Копия waveform Lab065 не прошла SHA-256")
result = {
"lab": "Lab065",
"stage": "PREPARATION_ONLY",
"rf_tx_count": 0,
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
"parameters": parameters(),
"segments": source_metadata["segments"],
"tx_samples": lab064.TX_SAMPLES,
"tx_bytes": 2 * lab064.TX_SAMPLES,
"tx_duration_seconds": lab064.TX_SAMPLES / lab064.SAMPLE_RATE,
"tx_sha256": sha256(target),
"source_tx_sha256": source_metadata["tx_sha256"],
"waveform_unchanged_from_lab064": True,
"automatic_retry": False,
"tx_command_prepared_not_run": transfer_command(target, lab064.TX_SAMPLES, transmit=True),
"rx_command_prepared_not_run": transfer_command(RAW / "rx_stream.cs8", lab064.RX_SAMPLES),
}
save_new(RAW / "tx_metadata.json", result)
save_new(OUTPUT / "preparation.json", result)
return result
def compare_sessions(old_result: dict, new_rows: list[dict]) -> dict:
"""Сопоставить одинаковые цифровые уровни Lab064 и Lab065."""
old_by_label = {row["label"]: row for row in old_result["rows"]}
rows = []
for new in new_rows:
old = old_by_label[new["label"]]
comparison = {
"label": new["label"],
"tx_vga_nominal_difference_db": TX_VGA_DIFFERENCE_DB,
"rx_matched_power_difference_db": lab064.difference_db(
new["matched_signal_power"], old["matched_signal_power"]
),
"gain_power_abs_h_squared_difference_db": lab064.difference_db(
new["gain_power_abs_h_squared"], old["gain_power_abs_h_squared"]
),
"residual_power_difference_db": lab064.difference_db(
new["residual_power"], old["residual_power"]
),
"matched_to_residual_improvement_db": (
new["matched_to_residual_db"] - old["matched_to_residual_db"]
),
}
if "complex_gain_h" in new and "residual_spectrum" in new:
old_gain = complex(
old["complex_gain_h"]["real"], old["complex_gain_h"]["imag"]
)
new_gain = complex(
new["complex_gain_h"]["real"], new["complex_gain_h"]["imag"]
)
comparison["complex_gain_h_old"] = old["complex_gain_h"]
comparison["complex_gain_h_new"] = new["complex_gain_h"]
comparison["complex_gain_phase_change_degrees"] = float(
np.degrees(np.angle(new_gain / old_gain))
)
comparison["residual_spectral_band_difference_db"] = {
f"h{harmonic}_{harmonic * lab064.CARRIER_HZ}_hz": lab064.difference_db(
new["residual_spectrum"][f"residual_band_h{harmonic}_power_code2"],
old["residual_spectrum"][f"residual_band_h{harmonic}_power_code2"],
)
for harmonic in (1, 2, 3)
}
rows.append(comparison)
return {"rows": rows, "rules_fixed_before_rf": classification_rules()}
def classification_rules() -> dict:
return {
"internal_linear_max_abs_error_db": 1.0,
"compression_upper_step_error_at_most_db": -1.5,
"meaningful_matched_gain_median_at_least_db": MEANINGFUL_GAIN_DB,
"meaningful_ratio_improvement_median_at_least_db": MEANINGFUL_RATIO_IMPROVEMENT_DB,
"maximum_level_to_level_gain_spread_db": MAX_LEVEL_TO_LEVEL_GAIN_SPREAD_DB,
"residual_tracks_if_within_matched_gain_db": RESIDUAL_TRACKING_MARGIN_DB,
}
def classify_result(new_result: dict, cross_comparison: dict) -> dict:
"""Выбрать один из пяти заранее заданных результатов Lab065."""
if not new_result.get("capture_valid"):
return {"case": "INVALID_CAPTURE", "rules_fixed_before_rf": classification_rules()}
internal_errors = [row["linearity_error_db"] for row in new_result["comparisons"]]
upper_error = internal_errors[-1]
cross_rows = cross_comparison["rows"]
matched = np.asarray([row["rx_matched_power_difference_db"] for row in cross_rows])
residual = np.asarray([row["residual_power_difference_db"] for row in cross_rows])
improvements = np.asarray([row["matched_to_residual_improvement_db"] for row in cross_rows])
median_matched = float(np.median(matched))
median_residual = float(np.median(residual))
median_improvement = float(np.median(improvements))
spread = float(np.ptp(matched))
linear = max(abs(value) for value in internal_errors) <= 1.0
if upper_error <= -1.5:
case = "COMPRESSION_AT_HIGHER_TX_GAIN"
elif median_matched < MEANINGFUL_GAIN_DB:
case = "NO_MEANINGFUL_GAIN"
elif (
linear
and spread <= MAX_LEVEL_TO_LEVEL_GAIN_SPREAD_DB
and median_improvement >= MEANINGFUL_RATIO_IMPROVEMENT_DB
):
case = "LINEAR_GAIN_IMPROVEMENT"
elif (
median_residual >= median_matched - RESIDUAL_TRACKING_MARGIN_DB
and median_improvement < MEANINGFUL_RATIO_IMPROVEMENT_DB
):
case = "RESIDUAL_RISES_WITH_SIGNAL"
else:
case = "NOT_LOCALIZED"
return {
"case": case,
"summary": {
"median_matched_gain_db": median_matched,
"median_residual_gain_db": median_residual,
"median_matched_to_residual_improvement_db": median_improvement,
"matched_gain_level_spread_db": spread,
"maximum_internal_linearity_error_db": max(abs(value) for value in internal_errors),
"upper_step_linearity_error_db": upper_error,
},
"rules_fixed_before_rf": classification_rules(),
}
def software_gate() -> dict:
metadata = validate_prepared()
old_result = json.loads(LAB064_RESULT.read_text(encoding="utf-8"))
synthetic = lab064.synthetic_linearity_gate()
result = {
"lab": "Lab065",
"stage": "SOFTWARE_GATE",
"rf_tx_count": 0,
"passed": bool(synthetic["passed"] and old_result["capture_valid"]),
"tx_sha256": metadata["tx_sha256"],
"waveform_unchanged_from_lab064": metadata["waveform_unchanged_from_lab064"],
"lab064_baseline_classification": old_result["classification"]["case"],
"classification_rules": classification_rules(),
}
save_new(OUTPUT / "software_gate.json", result)
if not result["passed"]:
raise RuntimeError("Lab065 software gate FAIL")
return result
def preflight(*, runner=None, process_checker=None, clock_checker=None, sleep_function=time.sleep) -> dict:
"""Проверить общую опору и 0,1 с RX-фона при полном запрете TX."""
metadata = validate_prepared()
software = json.loads((OUTPUT / "software_gate.json").read_text(encoding="utf-8"))
if not software.get("passed") or software.get("tx_sha256") != metadata["tx_sha256"]:
raise RuntimeError("Нет действующего Lab065 software gate")
save_new(RAW / "preflight_attempt.json", {
"lab": "Lab065", "rf_tx_count": 0, "point": asdict(POINT),
"parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
})
actual_runner = runner or lab054.clock._default_command_runner
process_checker = process_checker or lab054.check_processes
clock_checker = clock_checker or hardware.common_clock
def rx_only(command: list[str]):
if "-t" in command:
raise PermissionError("TX запрещён в Lab065 preflight")
return actual_runner(command)
result = {
"lab": "Lab065", "stage": "COMMON_CLOCK_AND_RX_ONLY_PREFLIGHT",
"passed": False, "rf_tx_count": 0, "stop_before_tx": True,
"point": asdict(POINT), "parameters": parameters(),
"tx_sha256": metadata["tx_sha256"],
}
clock_started = False
try:
before = process_checker()
result["process_before"] = before.to_dict()
lab054.require_clear(before)
result["inventory"] = lab054.phy.lab045._verify_device_inventory(rx_only)
clock_started = True
result["clock"] = clock_checker(
POINT, rx_only, lab054.clock.TransmitGuard(), sleep_function=sleep_function
)
hardware.require_clock_pass(result["clock"])
path = RAW / "preflight_rx.cs8"
captured = rx_only(transfer_command(path, lab064.PREFLIGHT_SAMPLES))
stdout, stderr = captured.stdout or "", captured.stderr or ""
result["rx_process"] = {
"returncode": captured.returncode, "stdout": stdout, "stderr": stderr,
}
result["rx_counter_summary"] = lab064.counter_summary(stdout + stderr, "overrun")
if captured.returncode or not result["rx_counter_summary"]["clean"]:
raise RuntimeError("RX preflight: процесс или счётчик overruns не прошёл")
if path.stat().st_size != 2 * lab064.PREFLIGHT_SAMPLES:
raise RuntimeError("Неверная длина Lab065 preflight RX")
level = hardware.receive_level(path)
level.pop("symbol_energy_code2_seconds", None)
level["symbol_energy"] = "NOT_APPLICABLE_TO_BACKGROUND"
level["ac_rms_code"] = float(np.sqrt(max(0.0, level["ac_power_code2"])))
level["unexpected_strong_signal"] = bool(
level["ac_rms_code"] >= lab064.STRONG_AC_RMS_CODE
or level["max_abs_i"] >= lab064.STRONG_COMPONENT_CODE
or level["max_abs_q"] >= lab064.STRONG_COMPONENT_CODE
)
result["rx_level"] = level
result["rx_sha256"] = sha256(path)
result["rx_overruns"] = result["rx_counter_summary"]["final_count"]
if level["saturation_detected"] or level["unexpected_strong_signal"]:
raise RuntimeError("RX-only уровень не прошёл gate; STOP без изменения gain")
result["passed"] = True
except Exception as error:
result["error"] = str(error)
raise
finally:
errors = lab054.disable_clock_outputs(rx_only) if clock_started else []
try:
after = process_checker()
result["process_after"] = after.to_dict()
lab054.require_clear(after)
except Exception as error:
errors.append(str(error))
result["cleanup_errors"] = errors
result["passed"] = result["passed"] and not errors
save_new(RAW / "preflight_result.json", result)
OUTPUT.mkdir(parents=True, exist_ok=True)
(OUTPUT / "preflight_result.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
encoding="utf-8",
)
if not result["passed"]:
raise RuntimeError("Lab065 preflight FAIL")
return result
def require_preflight(metadata: dict) -> dict:
record = json.loads((RAW / "preflight_result.json").read_text(encoding="utf-8"))
if (
record.get("passed") is not True
or record.get("rf_tx_count") != 0
or record.get("point") != asdict(POINT)
or record.get("parameters") != parameters()
or record.get("tx_sha256") != metadata["tx_sha256"]
):
raise PermissionError("Нет успешного RX-only preflight для конфигурации Lab065")
hardware.require_clock_pass(record.get("clock", {}))
level = record.get("rx_level", {})
if (
level.get("saturation_detected") is not False
or level.get("unexpected_strong_signal") is not False
or record.get("rx_overruns") != 0
):
raise PermissionError("RX-only preflight не разрешает передачу")
return record
def capture_once(
*, authorization: bool = False, runner=None, process_starter=None,
process_checker=None, clock_checker=None, sleep_function=time.sleep,
) -> dict:
"""Выполнить один RX-first сеанс после отдельного прямого разрешения."""
if authorization is not True:
raise PermissionError("Нужно отдельное разрешение на единственный RF-сеанс Lab065")
metadata = validate_prepared()
require_preflight(metadata)
receive_path = RAW / "rx_stream.cs8"
if receive_path.exists() or (RAW / "capture_result.json").exists():
raise FileExistsError("Аппаратный результат Lab065 уже существует")
actual_runner = runner or lab054.clock._default_command_runner
process_starter = process_starter or lab054.clock._default_process_starter
process_checker = process_checker or lab054.check_processes
clock_checker = clock_checker or hardware.common_clock
save_new(RAW / "hardware_attempt.json", {
"lab": "Lab065", "authorized_once": True, "automatic_retry": False,
"point": asdict(POINT), "parameters": parameters(),
"tx_sha256": metadata["tx_sha256"],
})
result = {
"lab": "Lab065", "stage": "SINGLE_RF_LEVEL_CAPTURE", "passed": False,
"rf_tx_count": 0, "automatic_tx_repeats": 0, "point": asdict(POINT),
"parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
}
guard = lab054.clock.TransmitGuard()
process = None
clock_started = False
try:
before = process_checker()
result["process_before"] = before.to_dict()
lab054.require_clear(before)
result["inventory"] = lab054.phy.lab045._verify_device_inventory(actual_runner)
clock_started = True
result["clock"] = clock_checker(
POINT, actual_runner, guard, sleep_function=sleep_function
)
hardware.require_clock_pass(result["clock"])
process = process_starter(
transfer_command(receive_path, lab064.RX_SAMPLES),
RAW / "rx_stdout.txt", RAW / "rx_stderr.txt",
)
sleep_function(lab064.RX_LEAD_SECONDS)
if process.poll() is not None:
raise RuntimeError("RX завершился до TX")
before_tx = hardware.receive_level(receive_path, tail_samples=65_536)
before_tx.pop("symbol_energy_code2_seconds", None)
result["rx_level_before_tx"] = before_tx
if before_tx["saturation_detected"]:
raise RuntimeError("RX clipping до TX; RF TX = 0")
guard.register_transmit()
result["rf_tx_count"] = guard.transmit_calls
transmitted = actual_runner(
transfer_command(RAW / "tx_level_staircase.cs8", lab064.TX_SAMPLES, transmit=True)
)
tx_stdout, tx_stderr = transmitted.stdout or "", transmitted.stderr or ""
save_text_new(RAW / "tx_stdout.txt", tx_stdout)
save_text_new(RAW / "tx_stderr.txt", tx_stderr)
result["tx_process"] = {
"returncode": transmitted.returncode,
"stdout_file": "tx_stdout.txt", "stderr_file": "tx_stderr.txt",
}
result["tx_counter_summary"] = lab064.counter_summary(
tx_stdout + tx_stderr, "underrun"
)
if transmitted.returncode:
raise RuntimeError("Единственный TX завершился ошибкой; повтор запрещён")
if process.wait(lab064.RX_SECONDS + 10):
raise RuntimeError("RX завершился ошибкой; повтор запрещён")
process.close_logs()
result["rx_samples"] = receive_path.stat().st_size // 2
result["rx_bytes"] = receive_path.stat().st_size
result["rx_duration_seconds"] = result["rx_samples"] / lab064.SAMPLE_RATE
if result["rx_bytes"] != 2 * lab064.RX_SAMPLES:
raise RuntimeError("Повреждённая длина RX; повтор запрещён")
rx_stdout = (RAW / "rx_stdout.txt").read_text(encoding="utf-8")
rx_stderr = (RAW / "rx_stderr.txt").read_text(encoding="utf-8")
result["rx_counter_summary"] = lab064.counter_summary(
rx_stdout + rx_stderr, "overrun"
)
result["rx_sha256"] = sha256(receive_path)
level = hardware.receive_level(receive_path)
level.pop("symbol_energy_code2_seconds", None)
result["rx_level"] = level
result["rx_clipping"] = bool(level["saturation_detected"])
result["passed"] = True
validity = lab064.capture_validity(result)
if not validity["valid"]:
result["passed"] = False
raise RuntimeError("Запись невалидна; результат сохранён, повтор запрещён")
except Exception as error:
result["error"] = str(error)
raise
finally:
errors = []
if process is not None:
try:
process.terminate()
except Exception as error:
errors.append(str(error))
if clock_started:
errors.extend(lab054.disable_clock_outputs(actual_runner))
try:
after = process_checker()
result["process_after"] = after.to_dict()
lab054.require_clear(after)
except Exception as error:
errors.append(str(error))
result["cleanup_errors"] = errors
result["passed"] = result["passed"] and not errors
save_new(RAW / "capture_result.json", result)
OUTPUT.mkdir(parents=True, exist_ok=True)
(OUTPUT / "capture_result.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
encoding="utf-8",
)
if not result["passed"]:
raise RuntimeError("Lab065 RF-сеанс завершён с ошибкой; повтор запрещён")
return result
def analyze_capture() -> dict:
"""Рассчитать метрики Lab065 и прямое сравнение с Lab064."""
metadata = validate_prepared()
rx_path = RAW / "rx_stream.cs8"
capture = json.loads((RAW / "capture_result.json").read_text(encoding="utf-8"))
if capture.get("rf_tx_count") != 1 or capture.get("rx_sha256") != sha256(rx_path):
raise RuntimeError("Нет подтверждённой единственной RX-записи Lab065")
tx = lab064.energy.Cs8File(RAW / "tx_level_staircase.cs8")
rx = lab064.energy.Cs8File(rx_path)
location = lab064.locate_waveform(tx, rx)
background_rows = []
for window in lab064.background_windows():
rx_start = location["start_sample"] + window["tx_relative_start_sample"]
background_rows.append({
**window, "rx_start_sample": rx_start,
"metrics": lab064._window_metrics(rx.read(rx_start, window["samples"])),
})
background_by_label = {row["label"]: row for row in background_rows}
preceding = {"L0": "INITIAL_GUARD", "L1": "GAP_L0_L1",
"L2": "GAP_L1_L2", "L3": "GAP_L2_L3"}
rows = []
for segment in metadata["segments"]:
tx_start = segment["start_sample"]
nominal_rx_start = location["start_sample"] + tx_start
fitted = lab064.energy.analyze_rx(
lab064.lab056.OffsetSource(tx, tx_start), rx, [nominal_rx_start],
lab064.SEGMENT_SAMPLES, lab064.SAMPLES_PER_SYMBOL,
)["frames"][0]
actual_start = fitted["start"]
active_reference = tx.read(
tx_start + lab064.TRIM_SAMPLES,
lab064.SEGMENT_SAMPLES - 2 * lab064.TRIM_SAMPLES,
)
active_received = rx.read(
actual_start + lab064.TRIM_SAMPLES,
lab064.SEGMENT_SAMPLES - 2 * lab064.TRIM_SAMPLES,
)
complex_gain = lab064.lab056.complex_gain(
active_reference, active_received, fitted["cfo_hz"]
)
matched = fitted["heldout_signal_power"]
residual = fitted["heldout_residual_power"]
background = background_by_label[preceding[segment["label"]]]
rows.append({
"label": segment["label"], "level_db": segment["level_db"],
"rx_start_sample": actual_start,
"alignment_adjustment_samples": fitted["alignment_adjustment"],
"full_iq": lab064._window_metrics(rx.read(actual_start, lab064.SEGMENT_SAMPLES)),
"background_label": background["label"], "background": background["metrics"],
"matched_signal_power": matched, "residual_power": residual,
"matched_to_residual_db": lab064.difference_db(matched, residual),
"complex_gain_h": complex_gain, "gain_abs": fitted["gain_abs"],
"gain_power_abs_h_squared": fitted["gain_abs"] ** 2,
"cfo_hz": fitted["cfo_hz"],
"residual_spectrum": lab064.residual_spectral_diagnostics(
active_reference, active_received, fitted["cfo_hz"], complex_gain
),
})
comparisons = []
for index in range(1, len(rows)):
tx_difference = metadata["segments"][index]["tx_input_difference_from_previous_db"]
rx_difference = lab064.difference_db(
rows[index]["matched_signal_power"], rows[index - 1]["matched_signal_power"]
)
comparisons.append({
"comparison": f"{rows[index]['label']}/{rows[index - 1]['label']}",
"tx_input_difference_db": tx_difference,
"rx_matched_power_difference_db": rx_difference,
"linearity_error_db": rx_difference - tx_difference,
})
validity = lab064.capture_validity(capture)
result = {
"lab": "Lab065", "stage": "OFFLINE_RF_LEVEL_LINEARITY_ANALYSIS",
"packet_receiver_used": False, "location": location, "rows": rows,
"background_windows": background_rows,
"background_summary": lab064.summarize_background(background_rows),
"comparisons": comparisons, "capture_valid": validity["valid"],
"capture_invalid_reasons": validity["invalid_reasons"],
"tx_underruns_maximum": capture.get("tx_counter_summary", {}).get("maximum_count"),
"rx_overruns_maximum": capture.get("rx_counter_summary", {}).get("maximum_count"),
"rx_clipping": any(row["full_iq"]["clipped_samples_fraction"] > 0 for row in rows),
}
old_result = json.loads(LAB064_RESULT.read_text(encoding="utf-8"))
result["lab064_to_lab065"] = compare_sessions(old_result, rows)
result["classification"] = classify_result(result, result["lab064_to_lab065"])
save_new(OUTPUT / "rf_result.json", result)
return result
def run_functional_tests() -> None:
assert transfer_command("NUL", 100, transmit=True)[-7:-5] == ["-x", "20"]
assert "-t" not in transfer_command("NUL", 100)
assert validate_lab064_waveform()["tx_sha256"] == EXPECTED_TX_SHA256
assert lab064.synthetic_linearity_gate()["passed"]
try:
capture_once(authorization=False)
except PermissionError:
pass
else:
raise AssertionError("RF без отдельного разрешения")
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--stage", choices=("prepare", "software", "preflight", "capture", "analyze"),
required=True,
)
parser.add_argument("--authorize-one-rf", action="store_true")
arguments = parser.parse_args()
if arguments.stage == "prepare":
result = prepare()
elif arguments.stage == "software":
result = software_gate()
elif arguments.stage == "preflight":
result = preflight()
elif arguments.stage == "capture":
result = capture_once(authorization=arguments.authorize_one_rf)
else:
result = analyze_capture()
print(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False))
print("RF TX NOW = 0")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,786 @@
"""Lab066: исправленный повтор полного 500K PHY-сеанса.
Подготовка, программная проверка и preflight не передают RF. Единственный
аппаратный сеанс доступен только отдельной командой с явным разрешением.
"""
from __future__ import annotations
from dataclasses import asdict
import argparse
import csv
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import time
if __package__ in (None, ""):
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import numpy as np
from scipy import signal
from experiments import lab049_bpsk_symbol_rate as phy
from experiments import lab053_packet_processing as packet_processing
from experiments import lab053_streaming_receiver as streaming
from experiments import lab054_hardware_control as lab054
from experiments import lab056_capture_runner as hardware
from experiments import lab058_abab_symbol_quality as lab058
from experiments import lab064_rf_level_linearity as lab064
ROOT = Path(__file__).resolve().parents[1]
RAW = ROOT / "data/raw/lab066"
OUTPUT = ROOT / "data/processed/lab066"
POINT = hardware.OperatingPoint(
frequency_hz=1_400_000_000,
physical_path="MASTER TX near window -> LOS indoor -> SLAVE RX on desk",
path_confirmed=True,
tx_antenna="existing 1400 MHz antenna near window",
rx_antenna="existing 1400 MHz antenna on desk",
distance_m=1.2,
orientation="same polarization; fixed during preflight and RF session",
visibility="LOS indoor",
)
MODE = phy.PhyMode(500_000)
PACKETS = 16
PAYLOAD_BYTES = 256
PACKET_SAMPLES = 92_256
INTER_PACKET_GAP_SECONDS = 0.005
INTER_PACKET_GAP_SAMPLES = 40_000
NUMBER_OF_GAPS = PACKETS - 1
GUARD_SECONDS = 0.1
GUARD_SAMPLES = 800_000
BASE_TX_SAMPLES = PACKETS * PACKET_SAMPLES + NUMBER_OF_GAPS * INTER_PACKET_GAP_SAMPLES
EXPECTED_TX_SAMPLES = BASE_TX_SAMPLES + 2 * GUARD_SAMPLES
PREFLIGHT_SAMPLES = 800_000
RX_SECONDS = 2
RX_SAMPLES = RX_SECONDS * MODE.sample_rate_hz
RX_LEAD_SECONDS = 0.5
TX_VGA_DB = 20
STRONG_AC_RMS_CODE = 20.0
STRONG_COMPONENT_CODE = 100.0
def save_new(path: Path, value: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("x", encoding="utf-8") as stream:
json.dump(value, stream, ensure_ascii=False, indent=2, allow_nan=False)
stream.write("\n")
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while block := stream.read(1024 * 1024):
digest.update(block)
return digest.hexdigest()
def plans():
result = lab054.plans()
payloads = [frame.plan.payload for frame in result]
if len(result) != PACKETS or any(len(payload) != PAYLOAD_BYTES for payload in payloads):
raise RuntimeError("Штатный генератор не дал 16 payload по 256 байт")
if len(set(payloads)) != PACKETS:
raise RuntimeError("Payload пакетов должны однозначно различаться")
return result
def parameters() -> dict:
return {
"rf_frequency_hz": POINT.frequency_hz,
"sample_rate_hz": MODE.sample_rate_hz,
"symbol_rate": MODE.symbol_rate,
"samples_per_symbol": MODE.samples_per_symbol,
"digital_carrier_hz": lab054.phy.lab045.CARRIER_OFFSET_HZ,
"packets": PACKETS,
"payload_bytes": PAYLOAD_BYTES,
"tx_vga_db": TX_VGA_DB,
"rx_lna_db": 24,
"rx_vga_db": 24,
"rf_amplifier": False,
"bias_tee": False,
"geometry": asdict(POINT),
"geometry_unchanged": True,
"clock_path": "10 MHz TCXO -> MASTER -> MASTER CLKOUT -> SLAVE CLKIN",
}
def transfer_command(path: Path | str, samples: int, *, transmit: bool = False) -> list[str]:
command = hardware.command(POINT, path, samples, transmit=transmit)
if transmit:
command[command.index("-x") + 1] = str(TX_VGA_DB)
return command
def build_waveform() -> tuple[bytes, list[dict]]:
"""Собрать финальный поток с нулевыми guards и паузами."""
blocks: list[bytes] = [bytes(2 * GUARD_SAMPLES)]
layout: list[dict] = []
cursor = GUARD_SAMPLES
for index, frame in enumerate(plans(), start=1):
packet = phy.build_parameterized_tx_cs8(frame.plan, MODE)
if len(packet) != 2 * PACKET_SAMPLES:
raise RuntimeError(f"Пакет {index}: ожидалось {PACKET_SAMPLES} отсчётов")
layout.append({
"packet_index": index,
"start_sample": cursor,
"samples": PACKET_SAMPLES,
"payload_sha256": hashlib.sha256(frame.plan.payload).hexdigest(),
})
blocks.append(packet)
cursor += PACKET_SAMPLES
if index != PACKETS:
blocks.append(bytes(2 * INTER_PACKET_GAP_SAMPLES))
layout[-1]["following_gap_start_sample"] = cursor
layout[-1]["following_gap_samples"] = INTER_PACKET_GAP_SAMPLES
cursor += INTER_PACKET_GAP_SAMPLES
blocks.append(bytes(2 * GUARD_SAMPLES))
waveform = b"".join(blocks)
if len(waveform) != 2 * EXPECTED_TX_SAMPLES or cursor + GUARD_SAMPLES != EXPECTED_TX_SAMPLES:
raise RuntimeError("Неверная длина итогового TX-потока Lab066")
return waveform, layout
def prepare() -> dict:
RAW.mkdir(parents=True, exist_ok=True)
OUTPUT.mkdir(parents=True, exist_ok=True)
waveform_path = RAW / "tx_stream.cs8"
if waveform_path.exists():
raise FileExistsError("Lab066 TX уже существует; перезапись запрещена")
waveform, layout = build_waveform()
waveform_path.write_bytes(waveform)
result = {
"lab": "Lab066", "stage": "PREPARATION_ONLY", "rf_tx_count": 0,
"head": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(),
"parameters": parameters(), "packet_samples": PACKET_SAMPLES,
"inter_packet_gap_seconds": INTER_PACKET_GAP_SECONDS,
"inter_packet_gap_samples": INTER_PACKET_GAP_SAMPLES,
"number_of_gaps": NUMBER_OF_GAPS,
"zero_guard_before_samples": GUARD_SAMPLES,
"zero_guard_after_samples": GUARD_SAMPLES,
"base_tx_samples": BASE_TX_SAMPLES,
"expected_tx_samples": EXPECTED_TX_SAMPLES,
"tx_samples": EXPECTED_TX_SAMPLES, "tx_bytes": len(waveform),
"tx_duration_seconds": EXPECTED_TX_SAMPLES / MODE.sample_rate_hz,
"tx_sha256": sha256(waveform_path), "layout": layout,
"rx_samples": RX_SAMPLES, "rx_duration_seconds": RX_SECONDS,
"automatic_retry": False,
"tx_command_prepared_not_run": transfer_command(waveform_path, EXPECTED_TX_SAMPLES, transmit=True),
"rx_command_prepared_not_run": transfer_command(RAW / "rx_stream.cs8", RX_SAMPLES),
}
save_new(RAW / "tx_metadata.json", result)
save_new(OUTPUT / "preparation.json", result)
return result
def validate_prepared() -> dict:
metadata = json.loads((RAW / "tx_metadata.json").read_text(encoding="utf-8"))
path = RAW / "tx_stream.cs8"
expected, layout = build_waveform()
expected_digest = hashlib.sha256(expected).hexdigest()
actual = path.read_bytes()
if (
metadata.get("lab") != "Lab066"
or metadata.get("stage") != "PREPARATION_ONLY"
or metadata.get("rf_tx_count") != 0
or metadata.get("parameters") != parameters()
or metadata.get("layout") != layout
or metadata.get("expected_tx_samples") != EXPECTED_TX_SAMPLES
or metadata.get("tx_samples") != EXPECTED_TX_SAMPLES
or len(actual) != 2 * EXPECTED_TX_SAMPLES
or metadata.get("tx_sha256") != expected_digest
or hashlib.sha256(actual).hexdigest() != expected_digest
or actual != expected
):
raise RuntimeError("Подготовленный Lab066 TX или его разметка изменились")
return metadata
def decode(mode: str, source_path: Path | None = None, *, diagnostics: bool = False) -> dict:
"""Строго декодировать весь поток; legacy и T2 запускаются отдельно."""
observations = []
receiver = streaming.StreamingReceiver(
processor=packet_processing.PacketProcessor(carrier_tracking_mode=mode),
packet_observer=lambda row, outcome: observations.append((row, outcome)),
)
received = []
with (source_path or (RAW / "tx_stream.cs8")).open("rb") as stream:
while block := stream.read(2 * receiver.block_samples):
values = np.frombuffer(block, np.int8)
samples = values[::2].astype(np.float32) + 1j * values[1::2].astype(np.float32)
received.extend(receiver.push_iq_block(samples.astype(np.complex64)))
received.extend(receiver.finish())
expected = [frame.plan.payload for frame in plans()]
actual = [item.payload for item in received]
payload_to_index = {payload: index for index, payload in enumerate(expected, start=1)}
result = {
"mode": mode, "candidates": len(observations),
"header_pass": sum(outcome.phy_header_pass for _, outcome in observations),
"fec_started": sum(outcome.fec_attempted for _, outcome in observations),
"fec_pass": sum(outcome.fec_pass for _, outcome in observations),
"crc_started": sum(outcome.crc != "NOT_REACHED" for _, outcome in observations),
"crc_pass": sum(outcome.crc == "PASS" for _, outcome in observations),
"packet_output": len(actual),
"exact_payload_match": sum(payload in payload_to_index for payload in actual),
"packet_indexes": [payload_to_index.get(payload) for payload in actual],
"exact_ordered_payloads": actual == expected,
"t2_state_scope": "NEW_PREAMBLE_ESTIMATE_PER_PACKET" if mode == "t2" else "NOT_APPLICABLE",
"fallback_between_modes": False,
"passed": len(actual) == PACKETS and actual == expected,
}
if diagnostics:
result["rows"] = [{
"absolute_refined": int(row["absolute_refined"]),
"reason": outcome.reason,
"header": bool(outcome.phy_header_pass),
"fec_started": bool(outcome.fec_attempted), "fec": bool(outcome.fec_pass),
"crc_started": outcome.crc != "NOT_REACHED", "crc": outcome.crc == "PASS",
"packet_output": bool(outcome.accepted and outcome.crc == "PASS"),
"packet_index_after_verdict": payload_to_index.get(
bytes(outcome.frame.reception.payload_bytes)
if outcome.frame is not None and outcome.frame.reception.payload_bytes is not None else b""
),
"t2_tracking_runs_for_candidate": len(row.get("carrier_tracking_traces", ())),
} for row, outcome in observations]
result["detector_statistics"] = dict(receiver.detection_statistics)
result["candidate_overflow"] = receiver.candidate_overflow
if mode == "t2":
result["t2_state_reset_evidence"] = {
"estimate_source": "each candidate's own preamble",
"context_scope": "new PacketMeasure context for every candidate",
"tracking_runs_per_accepted_packet": [
row["t2_tracking_runs_for_candidate"]
for row in result["rows"] if row["packet_output"]
],
"all_accepted_packets_have_fresh_tracking_runs": all(
row["t2_tracking_runs_for_candidate"] > 0
for row in result["rows"] if row["packet_output"]
),
"persistent_tracking_state_in_packet_processor": False,
}
return result
def software_gate() -> dict:
metadata = validate_prepared()
legacy = decode("legacy", diagnostics=True)
t2 = decode("t2", diagnostics=True)
result = {
"lab": "Lab066", "stage": "SOFTWARE_GATE", "rf_tx_count": 0,
"tx_sha256": metadata["tx_sha256"], "legacy": legacy, "t2": t2,
"gaps_verified_zero": True,
"t2_state_reset_verified_by_fresh_preamble_per_packet": bool(
t2.get("t2_state_reset_evidence", {}).get(
"all_accepted_packets_have_fresh_tracking_runs"
)
),
"passed": (
legacy["passed"] and t2["passed"]
and t2.get("t2_state_reset_evidence", {}).get(
"all_accepted_packets_have_fresh_tracking_runs"
) is True
),
}
save_new(OUTPUT / "software_gate.json", result)
if not result["passed"]:
raise RuntimeError("Lab066 software gate FAIL")
return result
def preflight(*, runner=None, process_checker=None, clock_checker=None,
sleep_function=time.sleep) -> dict:
metadata = validate_prepared()
software = json.loads((OUTPUT / "software_gate.json").read_text(encoding="utf-8"))
if not software.get("passed") or software.get("tx_sha256") != metadata["tx_sha256"]:
raise RuntimeError("Нет действующего software gate Lab066")
save_new(RAW / "preflight_attempt.json", {
"lab": "Lab066", "rf_tx_count": 0, "point": asdict(POINT),
"tx_sha256": metadata["tx_sha256"],
})
actual_runner = runner or lab054.clock._default_command_runner
process_checker = process_checker or lab054.check_processes
clock_checker = clock_checker or hardware.common_clock
def rx_only(command):
if "-t" in command:
raise PermissionError("TX запрещён в Lab066 preflight")
return actual_runner(command)
result = {
"lab": "Lab066", "stage": "RX_ONLY_PREFLIGHT", "passed": False,
"rf_tx_count": 0, "point": asdict(POINT), "parameters": parameters(),
"tx_sha256": metadata["tx_sha256"], "stop_before_tx": True,
}
clock_started = False
try:
before = process_checker()
result["process_before"] = before.to_dict()
lab054.require_clear(before)
result["inventory"] = lab054.phy.lab045._verify_device_inventory(rx_only)
clock_started = True
result["clock"] = clock_checker(
POINT, rx_only, lab054.clock.TransmitGuard(), sleep_function=sleep_function
)
hardware.require_clock_pass(result["clock"])
path = RAW / "preflight_rx.cs8"
if path.exists():
raise FileExistsError("Lab066 preflight RX уже существует")
captured = rx_only(transfer_command(path, PREFLIGHT_SAMPLES))
stdout, stderr = captured.stdout or "", captured.stderr or ""
result["rx_process"] = {"returncode": captured.returncode, "stdout": stdout, "stderr": stderr}
result["rx_counter_summary"] = lab064.counter_summary(stdout + stderr, "overrun")
if captured.returncode or not result["rx_counter_summary"]["clean"]:
raise RuntimeError("RX preflight: процесс или максимум overruns не прошёл")
if path.stat().st_size != 2 * PREFLIGHT_SAMPLES:
raise RuntimeError("Неверная длина Lab066 preflight RX")
level = hardware.receive_level(path)
level.pop("symbol_energy_code2_seconds", None)
level["symbol_energy"] = "NOT_APPLICABLE_TO_BACKGROUND"
ac_rms = float(np.sqrt(max(0.0, level["ac_power_code2"])))
strong = bool(ac_rms >= STRONG_AC_RMS_CODE or level["max_abs_i"] >= STRONG_COMPONENT_CODE
or level["max_abs_q"] >= STRONG_COMPONENT_CODE)
level.update({"ac_rms_code": ac_rms, "unexpected_strong_signal": strong})
result["rx_level"] = level
result["rx_sha256"] = sha256(path)
result["rx_overruns"] = result["rx_counter_summary"]["maximum_count"]
if level["saturation_detected"]:
raise RuntimeError("RX clipping; STOP без изменения gain")
if strong:
raise RuntimeError("Неожиданно сильный RX-only фон; STOP без изменения gain")
result["passed"] = True
except Exception as error:
result["error"] = str(error)
raise
finally:
errors = lab054.disable_clock_outputs(rx_only) if clock_started else []
try:
after = process_checker()
result["process_after"] = after.to_dict()
lab054.require_clear(after)
except Exception as error:
errors.append(str(error))
result["cleanup_errors"] = errors
result["passed"] = result["passed"] and not errors
save_new(RAW / "preflight_result.json", result)
OUTPUT.mkdir(parents=True, exist_ok=True)
(OUTPUT / "preflight_result.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n", encoding="utf-8"
)
if not result["passed"]:
raise RuntimeError("Lab066 preflight FAIL")
return result
def require_preflight(metadata: dict) -> dict:
record = json.loads((RAW / "preflight_result.json").read_text(encoding="utf-8"))
if (record.get("passed") is not True or record.get("rf_tx_count") != 0
or record.get("point") != asdict(POINT) or record.get("parameters") != parameters()
or record.get("tx_sha256") != metadata["tx_sha256"]):
raise PermissionError("Нет успешного RX-only preflight для конфигурации Lab066")
hardware.require_clock_pass(record.get("clock", {}))
if (record.get("rx_overruns") != 0
or record.get("rx_level", {}).get("saturation_detected") is not False
or record.get("rx_level", {}).get("unexpected_strong_signal") is not False):
raise PermissionError("RX-only preflight не разрешает передачу")
return record
def capture_once(*, authorization: bool = False, runner=None, process_starter=None,
process_checker=None, clock_checker=None, sleep_function=time.sleep) -> dict:
"""Выполнить будущий единственный RX-first сеанс после отдельного разрешения."""
if authorization is not True:
raise PermissionError("Нужно отдельное разрешение на единственный RF-сеанс Lab066")
metadata = validate_prepared()
require_preflight(metadata)
receive_path = RAW / "rx_stream.cs8"
if receive_path.exists() or (RAW / "capture_result.json").exists():
raise FileExistsError("Аппаратный результат Lab066 уже существует")
actual_runner = runner or lab054.clock._default_command_runner
process_starter = process_starter or lab054.clock._default_process_starter
process_checker = process_checker or lab054.check_processes
clock_checker = clock_checker or hardware.common_clock
save_new(RAW / "hardware_attempt.json", {
"lab": "Lab066", "authorized_once": True, "automatic_retry": False,
"point": asdict(POINT), "parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
})
result = {
"lab": "Lab066", "stage": "SINGLE_RF_CAPTURE", "passed": False,
"rf_tx_count": 0, "automatic_tx_repeats": 0, "point": asdict(POINT),
"parameters": parameters(), "tx_sha256": metadata["tx_sha256"],
}
guard, process, clock_started = lab054.clock.TransmitGuard(), None, False
try:
before = process_checker(); result["process_before"] = before.to_dict(); lab054.require_clear(before)
result["inventory"] = lab054.phy.lab045._verify_device_inventory(actual_runner)
clock_started = True
result["clock"] = clock_checker(POINT, actual_runner, guard, sleep_function=sleep_function)
hardware.require_clock_pass(result["clock"])
process = process_starter(transfer_command(receive_path, RX_SAMPLES), RAW / "rx_stdout.txt", RAW / "rx_stderr.txt")
sleep_function(RX_LEAD_SECONDS)
if process.poll() is not None:
raise RuntimeError("RX завершился до TX")
before_tx = hardware.receive_level(receive_path, tail_samples=65_536)
before_tx.pop("symbol_energy_code2_seconds", None)
before_tx_ac_rms = float(np.sqrt(max(0.0, before_tx["ac_power_code2"])))
before_tx_strong = bool(
before_tx_ac_rms >= STRONG_AC_RMS_CODE
or before_tx["max_abs_i"] >= STRONG_COMPONENT_CODE
or before_tx["max_abs_q"] >= STRONG_COMPONENT_CODE
)
before_tx.update({
"ac_rms_code": before_tx_ac_rms,
"unexpected_strong_signal": before_tx_strong,
})
result["rx_level_before_tx"] = before_tx
if before_tx["saturation_detected"]:
raise RuntimeError("RX clipping до TX; RF TX = 0")
if before_tx_strong:
raise RuntimeError("Неожиданно сильный фон непосредственно перед TX; RF TX = 0")
guard.register_transmit(); result["rf_tx_count"] = guard.transmit_calls
transmitted = actual_runner(transfer_command(RAW / "tx_stream.cs8", EXPECTED_TX_SAMPLES, transmit=True))
tx_stdout, tx_stderr = transmitted.stdout or "", transmitted.stderr or ""
(RAW / "tx_stdout.txt").write_text(tx_stdout, encoding="utf-8")
(RAW / "tx_stderr.txt").write_text(tx_stderr, encoding="utf-8")
result["tx_process"] = {"returncode": transmitted.returncode,
"stdout_file": "tx_stdout.txt", "stderr_file": "tx_stderr.txt"}
result["tx_counter_summary"] = lab064.counter_summary(tx_stdout + tx_stderr, "underrun")
if transmitted.returncode:
raise RuntimeError("Единственный TX завершился ошибкой; повтор запрещён")
if process.wait(RX_SECONDS + 10):
raise RuntimeError("RX завершился ошибкой; повтор запрещён")
process.close_logs()
rx_log = (RAW / "rx_stdout.txt").read_text(encoding="utf-8") + (RAW / "rx_stderr.txt").read_text(encoding="utf-8")
result["rx_counter_summary"] = lab064.counter_summary(rx_log, "overrun")
result["rx_samples"] = receive_path.stat().st_size // 2
result["rx_sha256"] = sha256(receive_path)
result["rx_level"] = hardware.receive_level(receive_path)
result["rx_clipping"] = bool(result["rx_level"]["saturation_detected"])
result["passed"] = result["rx_samples"] == RX_SAMPLES
validity = lab064.capture_validity(result)
if not validity["valid"] or not result["passed"]:
result["passed"] = False
raise RuntimeError("INVALID_CAPTURE; запись сохранена, повтор запрещён")
except Exception as error:
result["error"] = str(error)
raise
finally:
errors = []
if process is not None:
try: process.terminate()
except Exception as error: errors.append(str(error))
if clock_started: errors.extend(lab054.disable_clock_outputs(actual_runner))
try:
after = process_checker(); result["process_after"] = after.to_dict(); lab054.require_clear(after)
except Exception as error: errors.append(str(error))
result["cleanup_errors"] = errors
result["passed"] = result["passed"] and not errors
save_new(RAW / "capture_result.json", result)
OUTPUT.mkdir(parents=True, exist_ok=True)
(OUTPUT / "capture_result.json").write_text(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n", encoding="utf-8")
if not result["passed"]:
raise RuntimeError("Lab066 RF-сеанс невалиден; повтор запрещён")
return result
def locate_first_packet(tx: lab058.Cs8File, rx: lab058.Cs8File) -> dict:
"""Найти первый пакет после строгого verdict, не раскрывая границы декодеру."""
stride = MODE.samples_per_symbol
reference = tx.read(GUARD_SAMPLES, PACKET_SAMPLES).astype(np.complex64)
received = rx.read(0, rx.size).astype(np.complex64)
reference = reference[::stride] - np.mean(reference[::stride])
received = received[::stride] - np.mean(received[::stride])
correlation = signal.correlate(received, reference, mode="valid", method="fft")
coarse = int(np.argmax(abs(correlation))) * stride
full_reference = tx.read(GUARD_SAMPLES, PACKET_SAMPLES).astype(np.complex128)
full_reference -= np.mean(full_reference)
starts = range(max(0, coarse - 2 * stride), min(rx.size - PACKET_SAMPLES, coarse + 2 * stride) + 1)
scores = []
for start in starts:
values = rx.read(start, PACKET_SAMPLES).astype(np.complex128)
values -= np.mean(values)
scores.append(abs(np.vdot(full_reference, values)))
first_start = starts.start + int(np.argmax(scores))
return {
"first_packet_rx_start_sample": first_start,
"stream_rx_start_sample": first_start - GUARD_SAMPLES,
"coarse_first_packet_start_sample": coarse,
"method": "first known packet correlation after strict receiver verdict",
}
def physical_packet_diagnostics(tx: lab058.Cs8File, rx: lab058.Cs8File,
location: dict) -> dict:
"""Оценить 16 пакетов известным TX только после строгого решения."""
rows = []
stream_start = location["stream_rx_start_sample"]
for layout, frame in zip(validate_prepared()["layout"], plans()):
packet_index = layout["packet_index"]
tx_start = layout["start_sample"]
rx_start = stream_start + tx_start
transmitted = tx.read(tx_start, PACKET_SAMPLES)
received = rx.read(rx_start, PACKET_SAMPLES)
expected = 2.0 * frame.plan.frame_bits.astype(np.float64) - 1.0
metrics, _working = lab058.analyze_values(received, expected, MODE.symbol_rate)
channel, _arrays = lab058.channel_metrics(
transmitted, received, MODE.symbol_rate, metrics["oracle"]["cfo_hz"]
)
oracle = metrics["oracle"]["metrics"]
rows.append({
"packet_index": packet_index, "tx_start_sample": tx_start,
"rx_start_sample": rx_start,
"symbol_error_rate": oracle["errors"] / oracle["symbols"],
"symbol_rms_deviation": float(np.sqrt(
oracle["residual_power"] / oracle["matched_component_power"]
)),
"matched_component_power": oracle["matched_component_power"],
"residual_power": oracle["residual_power"],
"matched_residual_db": float(10 * np.log10(
oracle["matched_component_power"] / oracle["residual_power"]
)),
"band_gain_nonuniformity_p95_p05_db": channel["amplitude_ripple_p95_p05_db"],
})
return {
"method": "Lab058 oracle diagnostics per packet after strict verdict",
"rows": rows,
"comparison": {
"lab056_B_oracle_symbol_error_rate": 0.03495896,
"lab056_B_oracle_evm_rms": 0.845350,
"lab062_source": "data/processed/lab062/rf_analysis.json",
"scope": "Lab056 B and Lab062 are contextual comparisons, not identical estimands",
},
}
def summarize_analysis(result: dict) -> dict:
"""Свести строгие и физические результаты без повторного декодирования."""
legacy, t2 = result["run_a_legacy"], result["run_b_t2"]
legacy_set, t2_set = set(legacy["packet_indexes"]), set(t2["packet_indexes"])
layout = validate_prepared()["layout"]
stream_start = result["location_after_strict_runs"]["stream_rx_start_sample"]
packet_rows = []
for layout_row in layout:
index = layout_row["packet_index"]
nominal = stream_start + layout_row["start_sample"]
modes = {}
for label, strict, accepted in (
("legacy", legacy, index in legacy_set), ("t2", t2, index in t2_set)
):
candidates = [
row for row in strict["rows"]
if abs(row["absolute_refined"] - nominal) <= 64
]
best = max(candidates, key=lambda row: (
row["packet_output"], row["crc"], row["fec"], row["header"],
-abs(row["absolute_refined"] - nominal),
)) if candidates else None
modes[label] = {
"candidate_at_expected_packet": best is not None,
"header": accepted or bool(best and best["header"]),
"fec": accepted or bool(best and best["fec"]),
"crc": accepted or bool(best and best["crc"]),
"packet_output": accepted,
}
packet_rows.append({"packet_index": index, **modes})
physical_rows = result["physical_diagnostics"]["rows"]
matched = float(np.mean([row["matched_component_power"] for row in physical_rows]))
residual = float(np.mean([row["residual_power"] for row in physical_rows]))
physical = {
"symbol_error_rate": float(np.mean([row["symbol_error_rate"] for row in physical_rows])),
"rms_symbol_deviation": float(np.sqrt(np.mean([
row["symbol_rms_deviation"] ** 2 for row in physical_rows
]))),
"matched_component_power": matched,
"residual_power": residual,
"matched_residual_db": float(10 * np.log10(matched / residual)),
"band_gain_nonuniformity_p95_p05_db_median": float(np.median([
row["band_gain_nonuniformity_p95_p05_db"] for row in physical_rows
])),
"band_gain_nonuniformity_p95_p05_db_min": float(np.min([
row["band_gain_nonuniformity_p95_p05_db"] for row in physical_rows
])),
"band_gain_nonuniformity_p95_p05_db_max": float(np.max([
row["band_gain_nonuniformity_p95_p05_db"] for row in physical_rows
])),
}
lab056 = {
"symbol_error_rate": 0.03495896464646465,
"rms_symbol_deviation": 0.8453497751219741,
"matched_residual_db": 1.4648455366051558,
"band_gain_nonuniformity_db": 5.471381501203027,
}
lab062_result = json.loads(
(ROOT / "data/processed/lab062/rf_analysis.json").read_text(encoding="utf-8")
)["lab058_quality"]
lab062 = {
"symbol_error_rate": lab062_result["symbol_error_rate"],
"rms_symbol_deviation": lab062_result["evm_rms"],
"matched_residual_db": lab062_result["matched_residual_db"],
"band_gain_nonuniformity_db": lab062_result[
"band_gain_nonuniformity_p95_p05_db_median"
],
"capture_status": "RF_SESSION_INVALID_TX_UNDERRUN",
}
def differences(reference: dict) -> dict:
return {
"symbol_error_rate_absolute": (
physical["symbol_error_rate"] - reference["symbol_error_rate"]
),
"rms_symbol_deviation_absolute": (
physical["rms_symbol_deviation"] - reference["rms_symbol_deviation"]
),
"matched_residual_db": (
physical["matched_residual_db"] - reference["matched_residual_db"]
),
"band_gain_nonuniformity_db": (
physical["band_gain_nonuniformity_p95_p05_db_median"]
- reference["band_gain_nonuniformity_db"]
),
}
return {
"lab": "Lab066", "capture_valid": result["capture_valid"],
"legacy": {key: legacy[key] for key in (
"candidates", "header_pass", "fec_started", "fec_pass", "crc_started",
"crc_pass", "packet_output", "exact_payload_match", "packet_indexes",
)},
"t2": {key: t2[key] for key in (
"candidates", "header_pass", "fec_started", "fec_pass", "crc_started",
"crc_pass", "packet_output", "exact_payload_match", "packet_indexes",
)},
"passed_by_both": sorted(legacy_set & t2_set),
"passed_only_by_legacy": sorted(legacy_set - t2_set),
"passed_only_by_t2": sorted(t2_set - legacy_set),
"failed_by_both": sorted(set(range(1, PACKETS + 1)) - legacy_set - t2_set),
"packet_rows": packet_rows, "physical": physical,
"lab056_B": lab056, "difference_from_lab056_B": differences(lab056),
"lab062": lab062, "difference_from_lab062": differences(lab062),
"classification": "PARTIAL_PACKET_RECOVERY_NO_T2_GAIN",
"primary_result": (
"Аппаратно валидный Lab066 дал одинаковые 10/16 точных пакетов "
"в legacy и T2; физическое качество лучше Lab056 B и невалидной Lab062."
),
"recommended_next_single_step": (
"Отдельно разрешить офлайн-сравнение header-ошибок пакетов "
"1-3, 9, 15-16 с принятыми пакетами по сохранённому RX; новый TX не нужен."
),
}
def write_summary_outputs(result: dict) -> dict:
summary = summarize_analysis(result)
save_new(OUTPUT / "rf_summary.json", summary)
with (OUTPUT / "packet_comparison.csv").open("x", newline="", encoding="utf-8-sig") as stream:
writer = csv.writer(stream)
writer.writerow([
"packet_index", "legacy_header", "legacy_fec", "legacy_crc",
"legacy_packet_output", "t2_header", "t2_fec", "t2_crc", "t2_packet_output",
])
for row in summary["packet_rows"]:
writer.writerow([
row["packet_index"], row["legacy"]["header"], row["legacy"]["fec"],
row["legacy"]["crc"], row["legacy"]["packet_output"],
row["t2"]["header"], row["t2"]["fec"], row["t2"]["crc"],
row["t2"]["packet_output"],
])
return summary
def analyze_saved_capture() -> dict:
"""Строго обработать одну будущую RX-запись обоими режимами."""
metadata = validate_prepared()
capture = json.loads((RAW / "capture_result.json").read_text(encoding="utf-8"))
rx_path = RAW / "rx_stream.cs8"
if (
capture.get("lab") != "Lab066"
or capture.get("rf_tx_count") != 1
or capture.get("automatic_tx_repeats") != 0
or capture.get("point") != asdict(POINT)
or capture.get("parameters") != parameters()
or capture.get("tx_sha256") != metadata["tx_sha256"]
or capture.get("rx_sha256") != sha256(rx_path)
):
raise RuntimeError("Нет подтверждённой единственной RX-записи Lab066")
validity = lab064.capture_validity(capture)
if not validity["valid"]:
result = {
"lab": "Lab066", "stage": "STRICT_DUAL_OFFLINE_ANALYSIS",
"classification": "INVALID_CAPTURE", "capture_valid": False,
"capture_invalid_reasons": validity["invalid_reasons"],
"rf_tx_count": capture.get("rf_tx_count"), "automatic_tx_repeats": 0,
}
save_new(OUTPUT / "rf_result.json", result)
return result
legacy = decode("legacy", rx_path, diagnostics=True)
t2 = decode("t2", rx_path, diagnostics=True)
strict_runs_complete = (
legacy.get("candidate_overflow") == 0
and t2.get("candidate_overflow") == 0
and isinstance(legacy.get("rows"), list)
and isinstance(t2.get("rows"), list)
)
if not strict_runs_complete:
raise RuntimeError("Строгие legacy/T2 прогоны не завершены; oracle-диагностика запрещена")
tx = lab058.Cs8File(RAW / "tx_stream.cs8")
rx = lab058.Cs8File(rx_path)
location = locate_first_packet(tx, rx)
result = {
"lab": "Lab066", "stage": "STRICT_DUAL_OFFLINE_ANALYSIS",
"tx_sha256": metadata["tx_sha256"], "rx_sha256": capture["rx_sha256"],
"capture_valid": validity["valid"], "capture_invalid_reasons": validity["invalid_reasons"],
"run_a_legacy": legacy, "run_b_t2": t2,
"same_rx_capture": True, "fallback_between_modes": False,
"strict_runs_complete": True,
"location_after_strict_runs": location,
"physical_diagnostics_scope": "KNOWN_TX_ONLY_AFTER_COMPLETE_STRICT_RUNS",
"physical_diagnostics": physical_packet_diagnostics(tx, rx, location),
"comparison_targets": ["Lab056 B", "Lab062", "Lab066"],
}
save_new(OUTPUT / "rf_result.json", result)
return result
def summarize_saved_analysis() -> dict:
return write_summary_outputs(json.loads(
(OUTPUT / "rf_result.json").read_text(encoding="utf-8")
))
def run_functional_tests() -> None:
assert EXPECTED_TX_SAMPLES == 3_676_096
assert transfer_command("NUL", 100, transmit=True)[-7:-5] == ["-x", "20"]
assert "-t" not in transfer_command("NUL", 100)
try:
capture_once(authorization=False)
except PermissionError:
pass
else:
raise AssertionError("RF без отдельного разрешения")
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--stage",
choices=("prepare", "software", "preflight", "capture", "analyze", "summarize"),
required=True,
)
parser.add_argument("--authorize-one-rf", action="store_true")
arguments = parser.parse_args()
if arguments.stage == "prepare": result = prepare()
elif arguments.stage == "software": result = software_gate()
elif arguments.stage == "preflight": result = preflight()
elif arguments.stage == "capture": result = capture_once(authorization=arguments.authorize_one_rf)
elif arguments.stage == "analyze": result = analyze_saved_capture()
else: result = summarize_saved_analysis()
print(json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False))
print("RF TX NOW = 0")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,906 @@
"""Lab067: офлайн-анализ отказов PHY-заголовка в сохранённой записи Lab066.
Строгий результат Lab066 загружается без изменений. Известный TX используется
только после этого результата для диагностического подсчёта ошибок.
Аппаратные команды и передача RF в этой лаборатории отсутствуют.
"""
from __future__ import annotations
import csv
import hashlib
import json
import math
from pathlib import Path
import struct
import sys
from typing import Any
if __package__ in (None, ""):
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from experiments import lab053_packet_processing as packet_processing
from experiments import lab058_abab_symbol_quality as symbol_quality
from experiments import lab066_full_packet_rf_retest as lab066
from protocol import bpsk_carrier_tracking as carrier_tracking
ROOT = Path(__file__).resolve().parents[1]
RAW = ROOT / "data/raw/lab066"
LAB066 = ROOT / "data/processed/lab066"
OUTPUT = ROOT / "data/processed/lab067"
TX_PATH = RAW / "tx_stream.cs8"
RX_PATH = RAW / "rx_stream.cs8"
TX_SHA256 = "621b0bb8ec72bba4f522c79a5c77669c850740ea481270c4dcc2a7317819f199"
RX_SHA256 = "72ff7ddd8b30baa4a5c066361ca1f8dfa664f1da37f65c0c373b211ce6cf00f0"
RX_BYTES = 32_000_000
RX_SAMPLES = 16_000_000
SAMPLE_RATE_HZ = 8_000_000
SYMBOL_RATE = 500_000
SAMPLES_PER_SYMBOL = 16
PREAMBLE_SYMBOLS = 1280
HEADER_SYMBOLS = 144
HEADER_BITS = 48
HEADER_REPETITION = 3
POSTHEADER_SYMBOLS = 128
FAILED_PACKETS = {1, 2, 3, 9, 15, 16}
PASSED_PACKETS = set(range(1, 17)) - FAILED_PACKETS
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def save_json(path: Path, value: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(value, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
encoding="utf-8",
)
def bits_text(bits: np.ndarray) -> str:
return "".join(str(int(value)) for value in np.asarray(bits).reshape(-1))
def finite(value: float) -> float | None:
return float(value) if np.isfinite(value) else None
def source_integrity() -> tuple[dict, dict, dict]:
"""Проверить неизменность Lab066 до любой oracle-диагностики."""
capture = json.loads((LAB066 / "capture_result.json").read_text(encoding="utf-8"))
strict = json.loads((LAB066 / "rf_result.json").read_text(encoding="utf-8"))
metadata = json.loads((LAB066 / "preparation.json").read_text(encoding="utf-8"))
tx_digest = sha256(TX_PATH)
rx_digest = sha256(RX_PATH)
integrity = {
"tx_path": str(TX_PATH.relative_to(ROOT)),
"tx_bytes": TX_PATH.stat().st_size,
"tx_samples": TX_PATH.stat().st_size // 2,
"tx_sha256": tx_digest,
"rx_path": str(RX_PATH.relative_to(ROOT)),
"rx_bytes": RX_PATH.stat().st_size,
"rx_samples": RX_PATH.stat().st_size // 2,
"rx_duration_seconds": (RX_PATH.stat().st_size // 2) / SAMPLE_RATE_HZ,
"rx_sha256": rx_digest,
"sample_rate_hz": SAMPLE_RATE_HZ,
}
if (
tx_digest != TX_SHA256
or rx_digest != RX_SHA256
or integrity["rx_bytes"] != RX_BYTES
or integrity["rx_samples"] != RX_SAMPLES
or metadata.get("tx_sha256") != TX_SHA256
or metadata.get("parameters", {}).get("sample_rate_hz") != SAMPLE_RATE_HZ
or capture.get("rx_sha256") != RX_SHA256
or capture.get("rf_tx_count") != 1
or capture.get("automatic_tx_repeats") != 0
or capture.get("passed") is not True
or strict.get("capture_valid") is not True
):
raise RuntimeError("Исходники или аппаратная валидность Lab066 не подтверждены")
for mode in ("run_a_legacy", "run_b_t2"):
result = strict[mode]
if (
result.get("header_pass") != 10
or result.get("packet_output") != 10
or result.get("exact_payload_match") != 10
or result.get("packet_indexes") != sorted(PASSED_PACKETS)
):
raise RuntimeError("Исторический строгий результат Lab066 изменился")
return integrity, metadata, strict
def extract_symbols(
received: symbol_quality.Cs8File,
frame_start: int,
symbol_count: int,
mode: str,
) -> dict:
"""Повторить штатные timing/carrier операции для известного окна пакета."""
processor = packet_processing.PacketProcessor(carrier_tracking_mode=mode)
length = (symbol_count + 42) * SAMPLES_PER_SYMBOL
segment = received.read(frame_start, length).astype(np.complex64)
matched = processor.filter(processor.shift(segment))
nominal = 16 * SAMPLES_PER_SYMBOL + processor.taps.size - 1
timing = int(processor.timing(matched, nominal, processor.expected))
initial = processor.carrier(
matched, nominal, timing, processor.bits, len(processor.bits)
)
symbols = matched[
nominal + timing:
nominal + timing + symbol_count * SAMPLES_PER_SYMBOL:
SAMPLES_PER_SYMBOL
].astype(np.complex128)
if symbols.size != symbol_count or initial["abs_h"] == 0:
raise RuntimeError("Не удалось извлечь диагностическое окно символов")
times = (np.arange(symbol_count, dtype=np.float64) + 0.5) / SYMBOL_RATE
if mode == "legacy":
corrected = symbols * np.exp(-2j * np.pi * initial["cfo_hz"] * times)
gain = initial["abs_h"] * np.exp(1j * initial["phase_rad"])
corrected /= gain
phase_trace = (
initial["phase_rad"] + 2 * np.pi * initial["cfo_hz"] * times
)
frequency_trace = np.full(symbol_count, initial["cfo_hz"], dtype=float)
detector_error = np.angle(corrected * np.where(corrected.real >= 0, 1, -1))
estimate = {
"frequency_hz": float(initial["cfo_hz"]),
"gain": complex(gain),
"phase_fit_rmse_rad": None,
"squared_coherence": None,
}
else:
estimate_value = carrier_tracking.estimate_p3(
symbols[:PREAMBLE_SYMBOLS], processor.expected, SYMBOL_RATE
)
tracked = carrier_tracking.track_t2(
symbols, SYMBOL_RATE, estimate_value, start_index=PREAMBLE_SYMBOLS
)
corrected = tracked.corrected.copy()
phase_trace = tracked.phase_rad.copy()
frequency_trace = tracked.frequency_hz.copy()
detector_error = tracked.detector_error_rad.copy()
estimate = {
"frequency_hz": float(estimate_value.frequency_hz),
"gain": complex(estimate_value.gain),
"phase_fit_rmse_rad": float(estimate_value.phase_fit_rmse_rad),
"squared_coherence": float(estimate_value.squared_coherence),
}
return {
"corrected": corrected,
"raw_symbols": symbols,
"matched": matched,
"hard_bits": (corrected.real >= 0).astype(np.uint8),
"timing_samples": timing,
"timing_fraction": timing / SAMPLES_PER_SYMBOL,
"nominal": nominal,
"sample_positions": frame_start + nominal + timing + np.arange(symbol_count) * SAMPLES_PER_SYMBOL,
"initial": estimate,
"phase_trace": phase_trace,
"frequency_trace": frequency_trace,
"detector_error": detector_error,
}
def reference_sync_diagnostic(extracted: dict, expected_bits: np.ndarray) -> dict:
"""Oracle-проверка timing/CFO после штатного результата, без влияния на него."""
expected = 2.0 * np.asarray(expected_bits, dtype=np.float64) - 1.0
indexes = np.arange(len(expected), dtype=np.int64)
candidates = []
for offset in range(-SAMPLES_PER_SYMBOL // 2, SAMPLES_PER_SYMBOL // 2 + 1):
start = extracted["nominal"] + offset
symbols = extracted["matched"][
start:start + len(expected) * SAMPLES_PER_SYMBOL:SAMPLES_PER_SYMBOL
].astype(np.complex128)
if len(symbols) != len(expected):
continue
frequency, gain, coherence = symbol_quality.fit_cfo_gain(
symbols, expected, indexes, SYMBOL_RATE, len(expected)
)
candidates.append((coherence, offset, frequency, gain))
coherence, timing, frequency, gain = max(candidates, key=lambda item: item[0])
return {
"scope": "KNOWN_BITS_AFTER_STRICT_VERDICT",
"timing_offset_samples": int(timing),
"timing_fraction": timing / SAMPLES_PER_SYMBOL,
"frequency_hz": float(frequency),
"gain_real": float(gain.real),
"gain_imag": float(gain.imag),
"coherence": float(coherence),
}
def physical_metrics(extracted: dict, expected_bits: np.ndarray, selected: slice) -> dict:
expected = 2.0 * np.asarray(expected_bits[selected], dtype=np.float64) - 1.0
raw = extracted["raw_symbols"][selected]
indexes = np.arange(len(extracted["raw_symbols"]), dtype=np.float64)[selected]
frequency = extracted["initial"]["frequency_hz"]
times = (indexes + 0.5) / SYMBOL_RATE
derotated = raw * np.exp(-2j * np.pi * frequency * times)
gain = complex(extracted["initial"]["gain"])
predicted = gain * expected
residual = derotated - predicted
matched_power = float(np.mean(abs(predicted) ** 2))
residual_power = float(np.mean(abs(residual) ** 2))
normalized = derotated / gain
errors = int(np.count_nonzero((normalized.real >= 0) != (expected > 0)))
correlation = float(
abs(np.vdot(expected, raw))
/ math.sqrt(float(np.vdot(expected, expected).real * np.vdot(raw, raw).real) + 1e-30)
)
return {
"symbols": int(len(expected)),
"errors": errors,
"error_rate": errors / len(expected),
"correlation": correlation,
"matched_component_power": matched_power,
"residual_power": residual_power,
"matched_residual_db": finite(10 * np.log10(matched_power / residual_power)),
"rms_symbol_deviation": float(np.sqrt(residual_power / matched_power)),
}
def header_diagnostics(received_soft: np.ndarray, expected_bits: np.ndarray) -> dict:
soft = np.asarray(received_soft[:HEADER_SYMBOLS], dtype=np.float64)
raw = (soft >= 0.0).astype(np.uint8)
expected = np.asarray(expected_bits[:HEADER_SYMBOLS], dtype=np.uint8)
raw_groups = raw.reshape(HEADER_BITS, HEADER_REPETITION)
expected_groups = expected.reshape(HEADER_BITS, HEADER_REPETITION)
voted = (np.sum(soft.reshape(HEADER_BITS, HEADER_REPETITION), axis=1) >= 0.0).astype(np.uint8)
expected_voted = expected_groups[:, 0]
fields = lab066.phy.lab045.lab044.bits_to_bytes(voted)
magic, version, length, checksum = struct.unpack(
lab066.phy.lab045.PHY_HEADER_FIELDS_FORMAT, fields
)
decoded = lab066.phy.lab045.decode_phy_header_soft(soft)
field_errors = {
"magic": int(np.count_nonzero(voted[:16] != expected_voted[:16])),
"version": int(np.count_nonzero(voted[16:24] != expected_voted[16:24])),
"length": int(np.count_nonzero(voted[24:40] != expected_voted[24:40])),
"checksum": int(np.count_nonzero(voted[40:48] != expected_voted[40:48])),
}
return {
"expected_header_bits": bits_text(expected),
"received_raw_header_bits": bits_text(raw),
"raw_header_errors": int(np.count_nonzero(raw != expected)),
"copy_errors": [
int(np.count_nonzero(raw_groups[:, index] != expected_groups[:, index]))
for index in range(HEADER_REPETITION)
],
"voted_header_bits": bits_text(voted),
"expected_voted_bits": bits_text(expected_voted),
"voted_header_errors": int(np.count_nonzero(voted != expected_voted)),
"magic_errors": field_errors["magic"],
"version_errors": field_errors["version"],
"length_errors": field_errors["length"],
"checksum_errors": field_errors["checksum"],
"decoded_magic": magic,
"decoded_version": version,
"decoded_length": length,
"decoded_checksum": checksum,
"accepted": bool(decoded["passed"]),
"reason": decoded["reason"],
"raw_error_positions": np.flatnonzero(raw != expected).astype(int).tolist(),
"voted_error_positions": np.flatnonzero(voted != expected_voted).astype(int).tolist(),
}
def gap_metrics(received: symbol_quality.Cs8File, start: int, count: int) -> dict:
values = received.read(start, count).astype(np.complex128)
mean = np.mean(values)
centered = values - mean
edge = min(count // 4, 8000)
first_power = float(np.mean(abs(centered[:edge]) ** 2))
middle_start = max(edge, count // 2 - edge // 2)
middle_power = float(np.mean(abs(centered[middle_start:middle_start + edge]) ** 2))
tail_ratio_db = float(10 * np.log10(max(first_power, 1e-30) / max(middle_power, 1e-30)))
return {
"length_samples": count,
"duration_seconds": count / SAMPLE_RATE_HZ,
"background_ac_rms": float(np.sqrt(np.mean(abs(centered) ** 2))),
"mean_i": float(mean.real),
"mean_q": float(mean.imag),
"residual_energy_mean": float(np.mean(abs(centered) ** 2)),
"leading_edge_to_middle_db": tail_ratio_db,
"previous_waveform_tail_detected": bool(tail_ratio_db > 3.0),
}
def strict_candidate_for_packet(rows: list[dict], expected: int) -> dict | None:
candidates = [row for row in rows if abs(int(row["absolute_refined"]) - expected) <= 64]
if not candidates:
return None
return min(
candidates,
key=lambda row: (
abs(int(row["absolute_refined"]) - expected),
int(row["absolute_refined"]),
),
)
def candidate_analysis(strict: dict, expected_starts: list[int]) -> tuple[dict, list[dict]]:
rows = strict["run_a_legacy"]["rows"]
packet_samples = lab066.PACKET_SAMPLES
gap_intervals = []
metadata = json.loads((LAB066 / "preparation.json").read_text(encoding="utf-8"))
stream_start = strict["location_after_strict_runs"]["stream_rx_start_sample"]
for layout in metadata["layout"][:-1]:
low = stream_start + layout["following_gap_start_sample"]
gap_intervals.append((low, low + layout["following_gap_samples"]))
output_rows = []
true = repeated = gaps = unrelated = 0
true_positions = set()
per_packet = {index: 0 for index in range(1, 17)}
for number, row in enumerate(rows, start=1):
position = int(row["absolute_refined"])
distances = np.abs(np.asarray(expected_starts, dtype=np.int64) - position)
nearest_index = int(np.argmin(distances)) + 1
delta = position - expected_starts[nearest_index - 1]
in_gap = any(low <= position < high for low, high in gap_intervals)
if abs(delta) <= 64:
category = "TRUE_PREAMBLE_POSITION"
true += 1
true_positions.add((nearest_index, position))
elif -64 <= delta < packet_samples:
category = "REPEATED_PRBS_LOCAL_MAXIMUM"
repeated += 1
elif in_gap:
category = "ZERO_GAP"
gaps += 1
else:
category = "UNRELATED"
unrelated += 1
if category in {"TRUE_PREAMBLE_POSITION", "REPEATED_PRBS_LOCAL_MAXIMUM"}:
per_packet[nearest_index] += 1
output_rows.append({
"candidate_number": number,
"position": position,
"nearest_packet": nearest_index,
"difference_samples": delta,
"category": category,
"strict_reason": row["reason"],
})
summary = {
"total_strict_candidates": len(rows),
"true_preamble_position_checks": true,
"unique_true_preamble_positions": len(true_positions),
"duplicate_true_position_checks": true - len(true_positions),
"repeated_prbs_local_maxima": repeated,
"zero_gap_candidates": gaps,
"unrelated_candidates": unrelated,
"checks_associated_with_each_real_preamble": per_packet,
"detector_statistics": strict["run_a_legacy"]["detector_statistics"],
"candidate_processing_state": "NEW_PACKET_MEASURE_AND_INDEPENDENT_DECODE_PER_CANDIDATE",
"persistent_state_between_rejected_candidates": False,
"accepted_until_changes_only_after_crc_pass": True,
}
return summary, output_rows
def distribution(rows: list[dict], packets: set[int], key: str) -> dict:
values = np.asarray([row[key] for row in rows if row["packet"] in packets], dtype=float)
return {
"minimum": float(np.min(values)),
"median": float(np.median(values)),
"maximum": float(np.max(values)),
"values": {str(row["packet"]): row[key] for row in rows if row["packet"] in packets},
}
def assign_classifications(rows: list[dict]) -> None:
passed = [row for row in rows if row["packet"] in PASSED_PACKETS]
limits = {
"preamble_errors_max": max(row["preamble_errors"] for row in passed),
"preamble_mr_p10": float(np.percentile([row["preamble_matched_residual_db"] for row in passed], 10)),
"preamble_rms_p90": float(np.percentile([row["preamble_rms_symbol_deviation"] for row in passed], 90)),
"correlation_p10": float(np.percentile([row["preamble_correlation"] for row in passed], 10)),
}
for row in rows:
if row["packet"] in PASSED_PACKETS:
row["classification"] = "PASSED"
continue
physical_bad = sum((
row["preamble_errors"] > limits["preamble_errors_max"],
row["preamble_matched_residual_db"] < limits["preamble_mr_p10"],
row["preamble_rms_symbol_deviation"] > limits["preamble_rms_p90"],
row["preamble_correlation"] < limits["correlation_p10"],
))
if row["preamble_errors"] > limits["preamble_errors_max"]:
row["classification"] = "PREAMBLE_ALREADY_BAD"
elif physical_bad >= 2:
row["classification"] = "LOCAL_RF_DEGRADATION"
elif row["header_voted_errors"] > 0:
row["classification"] = "HEADER_SPECIFIC_FAILURE"
else:
row["classification"] = "NOT_LOCALIZED"
def group_summary(rows: list[dict]) -> dict:
groups = {"START": range(1, 5), "MIDDLE_1": range(5, 9), "MIDDLE_2": range(9, 13), "END": range(13, 17)}
result = {}
for name, indexes in groups.items():
selected = [row for row in rows if row["packet"] in indexes]
result[name] = {
"packets": list(indexes),
"preamble_error_rate": float(np.mean([row["preamble_error_rate"] for row in selected])),
"header_error_rate": float(np.mean([row["header_raw_errors"] / HEADER_SYMBOLS for row in selected])),
"matched_power": float(np.mean([row["local_matched_power"] for row in selected])),
"residual_power": float(np.mean([row["local_residual_power"] for row in selected])),
"matched_residual_db": float(10 * np.log10(
np.mean([row["local_matched_power"] for row in selected])
/ np.mean([row["local_residual_power"] for row in selected])
)),
"initial_frequency_offset_hz": float(np.mean([row["legacy_initial_frequency_hz"] for row in selected])),
"timing_offset_samples": float(np.mean([row["timing_offset_samples"] for row in selected])),
"background_ac_rms": float(np.mean([row["gap_background_ac_rms"] for row in selected])),
}
return result
def make_plots(rows: list[dict], transitions: dict[int, dict], candidates: dict) -> list[str]:
OUTPUT.mkdir(parents=True, exist_ok=True)
packets = [row["packet"] for row in rows]
colors = ["C3" if packet in FAILED_PACKETS else "C0" for packet in packets]
plots = []
def save(name: str) -> None:
plt.tight_layout()
plt.savefig(OUTPUT / name, dpi=160)
plt.close()
plots.append(name)
plt.figure(figsize=(10, 4.5))
plt.bar(packets, [row["preamble_errors"] for row in rows], color=colors)
plt.xticks(packets); plt.xlabel("Пакет"); plt.ylabel("Ошибки преамбулы, бит")
plt.grid(axis="y", alpha=.25); save("preamble_errors_by_packet.png")
plt.figure(figsize=(10, 4.5))
x = np.arange(1, 17)
plt.bar(x - .18, [row["header_raw_errors"] for row in rows], width=.36, label="raw")
plt.bar(x + .18, [row["header_voted_errors"] for row in rows], width=.36, label="voted", color=colors)
plt.xticks(x); plt.xlabel("Пакет"); plt.ylabel("Ошибки PHY header, бит"); plt.legend()
plt.grid(axis="y", alpha=.25); save("header_errors_by_packet.png")
figure, axes = plt.subplots(3, 1, figsize=(10, 8), sharex=True)
axes[0].bar(packets, [row["local_matched_power"] for row in rows], color=colors)
axes[0].set_ylabel("Matched power")
axes[1].bar(packets, [row["local_residual_power"] for row in rows], color=colors)
axes[1].set_ylabel("Residual power")
axes[2].bar(packets, [row["local_matched_residual_db"] for row in rows], color=colors)
axes[2].set_ylabel("Matched/residual, dB"); axes[2].set_xlabel("Пакет"); axes[2].set_xticks(packets)
for axis in axes: axis.grid(axis="y", alpha=.25)
save("local_signal_quality_by_packet.png")
matrix = np.zeros((16, HEADER_SYMBOLS), dtype=np.uint8)
for row in rows:
for position in row["header_raw_error_positions"]:
matrix[row["packet"] - 1, position] = 1
plt.figure(figsize=(13, 5))
plt.imshow(matrix, aspect="auto", interpolation="nearest", cmap="Reds")
plt.xlabel("Позиция защищённого PHY header, 0...143"); plt.ylabel("Пакет")
plt.yticks(np.arange(16), np.arange(1, 17)); plt.colorbar(label="Ошибка")
save("header_error_position_map.png")
representative = [4, 1, 14, 16]
figure, axes = plt.subplots(len(representative), 3, figsize=(13, 10), sharex=True)
for line, packet in enumerate(representative):
values = transitions[packet]
x = np.arange(-128, HEADER_SYMBOLS + 128)
axes[line, 0].plot(x, values["corrected_real"], lw=.7)
axes[line, 0].scatter(x[values["errors"]], np.asarray(values["corrected_real"])[values["errors"]], s=12, color="red")
axes[line, 1].plot(x, values["corrected_imag"], lw=.7)
axes[line, 2].plot(x, values["phase_error_rad"], lw=.7)
for axis in axes[line]:
axis.axvline(0, color="black", lw=.8); axis.axvline(HEADER_SYMBOLS, color="black", lw=.8)
axis.grid(alpha=.2)
axes[line, 0].set_ylabel(f"P{packet}")
axes[0, 0].set_title("Re, ошибки красным"); axes[0, 1].set_title("Im"); axes[0, 2].set_title("Фазовая ошибка, rad")
for axis in axes[-1]: axis.set_xlabel("Символ относительно начала header")
save("preamble_header_transition.png")
figure, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
axes[0].bar(packets, [row["timing_offset_samples"] for row in rows], color=colors)
axes[0].set_ylabel("Timing, samples")
axes[1].plot(packets, [row["legacy_initial_frequency_hz"] for row in rows], "o-", label="legacy")
axes[1].plot(packets, [row["t2_initial_frequency_hz"] for row in rows], "s-", label="T2/P3")
axes[1].set_ylabel("CFO, Hz"); axes[1].set_xlabel("Пакет"); axes[1].legend()
for axis in axes: axis.grid(alpha=.25)
save("timing_and_cfo_by_packet.png")
plt.figure(figsize=(10, 4.5))
plt.bar(packets, [row["gap_background_ac_rms"] for row in rows], color=colors)
plt.xlabel("Пакет"); plt.ylabel("AC RMS перед пакетом"); plt.xticks(packets); plt.grid(axis="y", alpha=.25)
save("gap_background_by_packet.png")
per_packet = candidates["checks_associated_with_each_real_preamble"]
plt.figure(figsize=(10, 4.5))
plt.bar(packets, [per_packet[str(packet)] if str(packet) in per_packet else per_packet[packet] for packet in packets], color=colors)
plt.xlabel("Пакет"); plt.ylabel("Строгие проверки кандидатов"); plt.xticks(packets); plt.grid(axis="y", alpha=.25)
save("candidate_checks_by_packet.png")
return plots
def write_csvs(rows: list[dict], candidate_rows: list[dict], bit_rows: list[dict], transition_rows: list[dict]) -> None:
main_fields = [
"packet", "strict_result", "candidate_position", "expected_packet_position", "candidate_difference_samples",
"preamble_errors", "preamble_error_rate", "preamble_correlation", "header_raw_errors", "header_voted_errors",
"header_copy_1_errors", "header_copy_2_errors", "header_copy_3_errors", "magic_errors", "version_errors",
"length_errors", "checksum_errors", "expected_header_bits", "received_raw_header_bits", "voted_header_bits",
"header_accepted", "header_reason", "legacy_header_bits", "t2_header_bits",
"legacy_initial_frequency_hz", "t2_initial_frequency_hz", "reference_frequency_hz",
"legacy_frequency_error_vs_reference_hz", "initial_phase_error_rad", "timing_offset_samples", "fractional_timing_offset",
"reference_timing_offset_samples", "reference_fractional_timing_offset", "local_matched_power", "local_residual_power",
"local_matched_residual_db", "rms_symbol_deviation", "legacy_t2_header_differences", "classification",
]
with (OUTPUT / "header_packet_comparison.csv").open("w", newline="", encoding="utf-8-sig") as stream:
writer = csv.DictWriter(stream, fieldnames=main_fields, extrasaction="ignore")
writer.writeheader(); writer.writerows(rows)
for name, values in (
("header_bit_errors.csv", bit_rows),
("candidate_analysis.csv", candidate_rows),
("transition_metrics.csv", transition_rows),
):
with (OUTPUT / name).open("w", newline="", encoding="utf-8-sig") as stream:
writer = csv.DictWriter(stream, fieldnames=list(values[0]))
writer.writeheader(); writer.writerows(values)
def analyze() -> dict:
integrity, metadata, strict = source_integrity()
received = symbol_quality.Cs8File(RX_PATH)
stream_start = int(strict["location_after_strict_runs"]["stream_rx_start_sample"])
expected_starts = [stream_start + int(item["start_sample"]) for item in metadata["layout"]]
plans = lab066.plans()
packet_rows: list[dict] = []
bit_rows: list[dict] = []
transition_rows: list[dict] = []
transitions: dict[int, dict] = {}
tx_headers = []
for packet, (layout, frame, rx_start) in enumerate(zip(metadata["layout"], plans, expected_starts), start=1):
expected_frame = frame.plan.frame_bits.astype(np.uint8)
expected_header = frame.plan.phy_header_bits.astype(np.uint8)
tx_headers.append(expected_header)
legacy = extract_symbols(received, rx_start, PREAMBLE_SYMBOLS + HEADER_SYMBOLS, "legacy")
t2 = extract_symbols(received, rx_start, PREAMBLE_SYMBOLS + HEADER_SYMBOLS, "t2")
transition = extract_symbols(
received, rx_start, PREAMBLE_SYMBOLS + HEADER_SYMBOLS + POSTHEADER_SYMBOLS, "legacy"
)
preamble = physical_metrics(legacy, expected_frame, slice(0, PREAMBLE_SYMBOLS))
local = physical_metrics(legacy, expected_frame, slice(PREAMBLE_SYMBOLS - 128, PREAMBLE_SYMBOLS + HEADER_SYMBOLS))
reference = reference_sync_diagnostic(
legacy, expected_frame[:PREAMBLE_SYMBOLS + HEADER_SYMBOLS]
)
header_legacy = header_diagnostics(
legacy["corrected"].real[PREAMBLE_SYMBOLS:], expected_header
)
header_t2 = header_diagnostics(
t2["corrected"].real[PREAMBLE_SYMBOLS:], expected_header
)
legacy_t2_differences = int(np.count_nonzero(
legacy["hard_bits"][PREAMBLE_SYMBOLS:] != t2["hard_bits"][PREAMBLE_SYMBOLS:]
))
strict_row = strict_candidate_for_packet(strict["run_a_legacy"]["rows"], rx_start)
candidate_position = int(strict_row["absolute_refined"]) if strict_row else None
if packet == 1:
preceding_start = stream_start
preceding_count = lab066.GUARD_SAMPLES
else:
preceding_count = lab066.INTER_PACKET_GAP_SAMPLES
preceding_start = rx_start - preceding_count
gap = gap_metrics(received, preceding_start, preceding_count)
trailing = gap_metrics(received, rx_start + lab066.PACKET_SAMPLES, lab066.GUARD_SAMPLES) if packet == 16 else None
row = {
"packet": packet,
"strict_result": "PASS" if packet in PASSED_PACKETS else "HEADER_FAIL",
"candidate_position": candidate_position,
"expected_packet_position": rx_start,
"candidate_difference_samples": None if candidate_position is None else candidate_position - rx_start,
"timing_offset_samples": legacy["timing_samples"],
"fractional_timing_offset": legacy["timing_fraction"],
"timing_metric_confidence": None,
"preamble_symbols": preamble["symbols"],
"preamble_errors": preamble["errors"],
"preamble_error_rate": preamble["error_rate"],
"preamble_correlation": preamble["correlation"],
"preamble_rms_symbol_deviation": preamble["rms_symbol_deviation"],
"preamble_matched_power": preamble["matched_component_power"],
"preamble_residual_power": preamble["residual_power"],
"preamble_matched_residual_db": preamble["matched_residual_db"],
"legacy_initial_frequency_hz": legacy["initial"]["frequency_hz"],
"t2_initial_frequency_hz": t2["initial"]["frequency_hz"],
"initial_phase_error_rad": float(np.angle(legacy["initial"]["gain"])),
"frequency_difference_legacy_t2_hz": legacy["initial"]["frequency_hz"] - t2["initial"]["frequency_hz"],
"reference_frequency_hz": reference["frequency_hz"],
"legacy_frequency_error_vs_reference_hz": legacy["initial"]["frequency_hz"] - reference["frequency_hz"],
"reference_timing_offset_samples": reference["timing_offset_samples"],
"reference_fractional_timing_offset": reference["timing_fraction"],
"reference_coherence": reference["coherence"],
"legacy_expected_phase_drift_preamble_rad": float(2 * np.pi * legacy["initial"]["frequency_hz"] * PREAMBLE_SYMBOLS / SYMBOL_RATE),
"legacy_expected_phase_drift_header_rad": float(2 * np.pi * legacy["initial"]["frequency_hz"] * HEADER_SYMBOLS / SYMBOL_RATE),
"legacy_expected_phase_drift_full_packet_rad": float(2 * np.pi * legacy["initial"]["frequency_hz"] * len(expected_frame) / SYMBOL_RATE),
"phase_error_from_cfo_mismatch_over_header_rad": float(
2 * np.pi * (legacy["initial"]["frequency_hz"] - reference["frequency_hz"])
* HEADER_SYMBOLS / SYMBOL_RATE
),
"expected_header_bits": header_legacy["expected_header_bits"],
"received_raw_header_bits": header_legacy["received_raw_header_bits"],
"header_raw_errors": header_legacy["raw_header_errors"],
"header_copy_1_errors": header_legacy["copy_errors"][0],
"header_copy_2_errors": header_legacy["copy_errors"][1],
"header_copy_3_errors": header_legacy["copy_errors"][2],
"voted_header_bits": header_legacy["voted_header_bits"],
"header_voted_errors": header_legacy["voted_header_errors"],
"magic_errors": header_legacy["magic_errors"],
"version_errors": header_legacy["version_errors"],
"length_errors": header_legacy["length_errors"],
"checksum_errors": header_legacy["checksum_errors"],
"decoded_magic": header_legacy["decoded_magic"],
"decoded_length": header_legacy["decoded_length"],
"header_accepted": header_legacy["accepted"],
"header_reason": header_legacy["reason"],
"header_raw_error_positions": header_legacy["raw_error_positions"],
"header_voted_error_positions": header_legacy["voted_error_positions"],
"legacy_header_bits": header_legacy["received_raw_header_bits"],
"t2_header_bits": header_t2["received_raw_header_bits"],
"legacy_t2_header_differences": legacy_t2_differences,
"local_window": "last 128 preamble symbols plus complete 144-symbol header",
"local_matched_power": local["matched_component_power"],
"local_residual_power": local["residual_power"],
"local_matched_residual_db": local["matched_residual_db"],
"rms_symbol_deviation": local["rms_symbol_deviation"],
"header_error_rate": header_legacy["raw_header_errors"] / HEADER_SYMBOLS,
"preceding_zero_gap_length": gap["length_samples"],
"gap_background_ac_rms": gap["background_ac_rms"],
"gap_mean_i": gap["mean_i"], "gap_mean_q": gap["mean_q"],
"gap_residual_energy": gap["residual_energy_mean"],
"previous_waveform_tail_detected": gap["previous_waveform_tail_detected"],
"gap_leading_edge_to_middle_db": gap["leading_edge_to_middle_db"],
"trailing_guard_packet_16": trailing,
}
packet_rows.append(row)
for position in range(HEADER_SYMBOLS):
bit_rows.append({
"packet": packet, "protected_bit_position": position,
"field_bit_position": position // HEADER_REPETITION,
"copy": position % HEADER_REPETITION + 1,
"expected": int(expected_header[position]),
"legacy_received": int(legacy["hard_bits"][PREAMBLE_SYMBOLS + position]),
"t2_received": int(t2["hard_bits"][PREAMBLE_SYMBOLS + position]),
"legacy_error": int(position in header_legacy["raw_error_positions"]),
"t2_error": int(position in header_t2["raw_error_positions"]),
})
start = PREAMBLE_SYMBOLS - 128
stop = PREAMBLE_SYMBOLS + HEADER_SYMBOLS + 128
corrected = transition["corrected"][start:stop]
truth = 2.0 * expected_frame[start:stop] - 1.0
errors = np.flatnonzero((corrected.real >= 0) != (truth > 0))
phase_error = np.angle(corrected * truth)
transitions[packet] = {
"corrected_real": corrected.real.astype(float).tolist(),
"corrected_imag": corrected.imag.astype(float).tolist(),
"magnitude": abs(corrected).astype(float).tolist(),
"phase_error_rad": phase_error.astype(float).tolist(),
"hard_decisions": (corrected.real >= 0).astype(int).tolist(),
"errors": errors.astype(int).tolist(),
"sample_positions": transition["sample_positions"][start:stop].astype(int).tolist(),
}
regions = {"preamble_tail": slice(0, 128), "header": slice(128, 128 + HEADER_SYMBOLS), "postheader": slice(128 + HEADER_SYMBOLS, None)}
for name, selected in regions.items():
values = corrected[selected]
local_truth = truth[selected]
transition_rows.append({
"packet": packet, "region": name,
"symbols": len(values),
"decision_errors": int(np.count_nonzero((values.real >= 0) != (local_truth > 0))),
"magnitude_median": float(np.median(abs(values))),
"magnitude_minimum": float(np.min(abs(values))),
"phase_error_rms_rad": float(np.sqrt(np.mean(np.angle(values * local_truth) ** 2))),
"imaginary_rms": float(np.sqrt(np.mean(values.imag ** 2))),
"timing_offset_samples": transition["timing_samples"],
})
assign_classifications(packet_rows)
candidate_summary, candidate_rows = candidate_analysis(strict, expected_starts)
tx_header_identical = all(np.array_equal(tx_headers[0], item) for item in tx_headers[1:])
tx_header_decodes = [lab066.phy.lab045.decode_phy_header_soft(2.0 * item - 1.0) for item in tx_headers]
software_current = {
mode: lab066.decode(mode, TX_PATH, diagnostics=False)
for mode in ("legacy", "t2")
}
software_16_of_16 = all(
result["packet_output"] == 16 and result["exact_payload_match"] == 16
for result in software_current.values()
)
groups = group_summary(packet_rows)
distributions = {
label: {
metric: distribution(packet_rows, packets, metric)
for metric in (
"preamble_errors", "preamble_error_rate", "preamble_correlation",
"preamble_rms_symbol_deviation", "preamble_matched_power",
"preamble_residual_power", "preamble_matched_residual_db",
"header_raw_errors", "header_voted_errors",
)
}
for label, packets in (("passed", PASSED_PACKETS), ("failed", FAILED_PACKETS))
}
failed_classes = {str(row["packet"]): row["classification"] for row in packet_rows if row["packet"] in FAILED_PACKETS}
passed_local_residual = float(np.median([
row["local_residual_power"] for row in packet_rows if row["packet"] in PASSED_PACKETS
]))
failed_local_residual = float(np.median([
row["local_residual_power"] for row in packet_rows if row["packet"] in FAILED_PACKETS
]))
passed_local_ratio = float(np.median([
row["local_matched_residual_db"] for row in packet_rows if row["packet"] in PASSED_PACKETS
]))
failed_local_ratio = float(np.median([
row["local_matched_residual_db"] for row in packet_rows if row["packet"] in FAILED_PACKETS
]))
failed_error_positions = [
position for row in packet_rows if row["packet"] in FAILED_PACKETS
for position in row["header_raw_error_positions"]
]
repeated_failed_positions = sorted({
position for position in failed_error_positions
if failed_error_positions.count(position) > 1
})
diagnostic_strict_agreement = all(
row["header_accepted"] == (row["strict_result"] == "PASS")
for row in packet_rows
)
timing_agreement = all(
row["timing_offset_samples"] == row["reference_timing_offset_samples"]
for row in packet_rows
)
cfo_scale_small = max(abs(row["phase_error_from_cfo_mismatch_over_header_rad"])
for row in packet_rows) < 0.1
candidate_state_independent = (
candidate_summary["persistent_state_between_rejected_candidates"] is False
and candidate_summary["accepted_until_changes_only_after_crc_pass"] is True
)
local_quality_conditions = {
"tx_and_software_valid": bool(
tx_header_identical and all(item["passed"] for item in tx_header_decodes)
and software_16_of_16
),
"strict_header_reproduced": diagnostic_strict_agreement,
"no_common_failed_error_position": not repeated_failed_positions,
"failed_local_residual_at_least_20_percent_higher": (
failed_local_residual > 1.2 * passed_local_residual
),
"failed_local_matched_residual_at_least_1_db_lower": (
failed_local_ratio < passed_local_ratio - 1.0
),
"receiver_and_oracle_timing_agree": timing_agreement,
"cfo_mismatch_phase_scale_below_0p1_rad": cfo_scale_small,
"candidate_state_independent": candidate_state_independent,
}
root_cause = (
"LOCAL_RF_QUALITY" if all(local_quality_conditions.values())
else "NOT_LOCALIZED"
)
small_fix_evidence = {
"receiver_header_logic": bool(not diagnostic_strict_agreement),
"timing_transition": bool(not timing_agreement and not cfo_scale_small),
"carrier_transition": bool(not cfo_scale_small and timing_agreement),
"candidate_state_handling": bool(not candidate_state_independent),
}
one_small_fix = any(small_fix_evidence.values())
plots = make_plots(packet_rows, transitions, candidate_summary)
write_csvs(packet_rows, candidate_rows, bit_rows, transition_rows)
save_json(OUTPUT / "transition_symbols.json", transitions)
summary = {
"lab": "Lab067",
"mode": "OFFLINE_DIAGNOSTIC_ONLY",
"rf_tx": 0,
"hardware_accessed": False,
"lab066_integrity": integrity,
"historical_strict_result_unchanged": {
"legacy": "10/16", "t2": "10/16",
"passed_packets": sorted(PASSED_PACKETS), "failed_packets": sorted(FAILED_PACKETS),
},
"packet_positions_verified": len(expected_starts) == 16 and all(
row["candidate_difference_samples"] is not None and abs(row["candidate_difference_samples"]) <= 64
for row in packet_rows
),
"packet_rows": packet_rows,
"passed_failed_distributions": distributions,
"start_middle_end_groups": groups,
"failed_packet_classifications": failed_classes,
"header_error_position_analysis": {
"failed_raw_error_count": len(failed_error_positions),
"positions_repeated_between_failed_packets": repeated_failed_positions,
"systematic_common_position_found": bool(repeated_failed_positions),
"interpretation": "Позиции ошибок различаются; общей битовой позиции отказа нет.",
},
"local_quality_evidence": {
"passed_local_residual_power_median": passed_local_residual,
"failed_local_residual_power_median": failed_local_residual,
"passed_local_matched_residual_db_median": passed_local_ratio,
"failed_local_matched_residual_db_median": failed_local_ratio,
"failed_minus_passed_residual_power_median": failed_local_residual - passed_local_residual,
"failed_minus_passed_matched_residual_db_median": failed_local_ratio - passed_local_ratio,
"scope": "last 128 preamble symbols plus complete header",
"decision_conditions": local_quality_conditions,
},
"legacy_t2_header_difference": (
"NONE" if all(row["legacy_t2_header_differences"] == 0 for row in packet_rows)
else "PRESENT"
),
"t2_influence_starts_at_symbol": PREAMBLE_SYMBOLS,
"t2_explanation": "T2 начинает сопровождение после преамбулы; его решения заголовка сравнены побитно со штатным legacy.",
"candidate_analysis": candidate_summary,
"tx_validation": {
"headers_identical_at_all_packet_positions": tx_header_identical,
"all_headers_decode_with_correct_magic_version_length_checksum": all(item["passed"] for item in tx_header_decodes),
"decoded_packet_lengths": [item["packet_bytes"] for item in tx_header_decodes],
"software_roundtrip_current": software_current,
"legacy_and_t2_16_of_16": software_16_of_16,
},
"header_failure_root_cause": root_cause,
"one_small_fix_justified": one_small_fix,
"branch_decision": (
"ONE_SMALL_FIX_JUSTIFIED" if one_small_fix
else "NO_SMALL_FIX_JUSTIFIED"
),
"small_fix_assessment": {
"evidence_flags": small_fix_evidence,
"receiver_header_logic_error_found": small_fix_evidence["receiver_header_logic"],
"timing_transition_error_found": small_fix_evidence["timing_transition"],
"carrier_transition_error_explains_six_failures": small_fix_evidence["carrier_transition"],
"candidate_state_error_found": small_fix_evidence["candidate_state_handling"],
"reason": "TX и программный тракт дают 16/16; ошибки заголовка не имеют общей позиции, timing совпадает, а локальный остаток в failed header-окнах выше.",
},
"achieved_result": "10/16 exact full packets at 500 ksymbol/s, TX VGA 20 dB, hardware-valid OTA capture",
"recommended_next_project_branch": "CRSF / ELRS CONTROL AND TELEMETRY",
"qpsk_note": "QPSK с меньшей символьной скоростью остаётся будущим кандидатом для видеоканала; в Lab067 не реализован.",
"plots": plots,
"lab067_commit": "NOT PERFORMED",
"push": "NOT PERFORMED",
}
save_json(OUTPUT / "summary.json", summary)
return summary
def run_functional_tests() -> None:
expected = np.repeat(np.arange(HEADER_BITS, dtype=np.uint8) % 2, HEADER_REPETITION)
exact = header_diagnostics(2.0 * expected.astype(float) - 1.0, expected)
assert exact["raw_header_errors"] == 0
assert exact["voted_header_errors"] == 0
changed = expected.copy(); changed[3:5] ^= 1
result = header_diagnostics(2.0 * changed.astype(float) - 1.0, expected)
assert result["raw_header_errors"] == 2
assert result["voted_header_errors"] == 1
def main() -> None:
run_functional_tests()
result = analyze()
print(json.dumps({
"lab": result["lab"],
"historical_strict_result": result["historical_strict_result_unchanged"],
"root_cause": result["header_failure_root_cause"],
"one_small_fix_justified": result["one_small_fix_justified"],
"failed_packet_classifications": result["failed_packet_classifications"],
"plots": result["plots"],
}, ensure_ascii=False, indent=2))
print("RF TX = 0")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,151 @@
"""Lab065: неизменный waveform, TX VGA 20 дБ и границы классификации."""
from __future__ import annotations
import json
from pathlib import Path
import subprocess
import sys
import pytest
from experiments import lab065_rf_level_linearity as lab
from tools.hackrf_process_check import ProcessCheck, ProcessState
def test_only_operating_parameter_change_is_tx_vga():
old = lab.lab064.parameters()
new = lab.parameters()
assert new.pop("geometry_unchanged_from_lab064") is True
assert new.pop("waveform_source") == "Lab064 exact CS8 copy"
assert new["tx_vga_db"] == 20
new["tx_vga_db"] = 10
assert new == old
def test_tx_command_uses_20_db_and_rx_remains_24_24():
transmit = lab.transfer_command("tx.cs8", 100, transmit=True)
receive = lab.transfer_command("rx.cs8", 100)
assert transmit[transmit.index("-x") + 1] == "20"
assert receive[receive.index("-l") + 1] == "24"
assert receive[receive.index("-g") + 1] == "24"
assert transmit[-5:] == receive[-5:] == ["-a", "0", "-p", "0", "-B"]
def test_exact_lab064_waveform_is_required():
metadata = lab.validate_lab064_waveform()
assert metadata["tx_sha256"] == lab.EXPECTED_TX_SHA256
assert metadata["tx_samples"] == 6_000_000
def _result(matched_gain, residual_gain, improvements, errors=(0.0, 0.0, 0.0)):
old = {row["label"]: row for row in json.loads(
lab.LAB064_RESULT.read_text(encoding="utf-8")
)["rows"]}
rows = []
for index, label in enumerate(("L0", "L1", "L2", "L3")):
baseline = old[label]
rows.append({
"label": label,
"matched_signal_power": baseline["matched_signal_power"] * 10 ** (matched_gain[index] / 10),
"residual_power": baseline["residual_power"] * 10 ** (residual_gain[index] / 10),
"matched_to_residual_db": baseline["matched_to_residual_db"] + improvements[index],
"gain_power_abs_h_squared": baseline["gain_power_abs_h_squared"] * 10 ** (matched_gain[index] / 10),
})
return {
"capture_valid": True,
"rows": rows,
"comparisons": [{"linearity_error_db": error} for error in errors],
}
@pytest.mark.parametrize(
("result", "expected"),
[
(_result([10] * 4, [1] * 4, [9] * 4), "LINEAR_GAIN_IMPROVEMENT"),
(_result([10] * 4, [10] * 4, [0] * 4), "RESIDUAL_RISES_WITH_SIGNAL"),
(_result([1] * 4, [0] * 4, [1] * 4), "NO_MEANINGFUL_GAIN"),
(_result([10] * 4, [1] * 4, [9] * 4, errors=(0, 0, -1.6)), "COMPRESSION_AT_HIGHER_TX_GAIN"),
],
)
def test_classification_cases(result, expected):
comparison = lab.compare_sessions(
json.loads(lab.LAB064_RESULT.read_text(encoding="utf-8")), result["rows"]
)
assert lab.classify_result(result, comparison)["case"] == expected
@pytest.mark.parametrize("failure", [None, "clock", "overrun", "clipping"])
def test_preflight_never_transmits_and_fails_closed(tmp_path, monkeypatch, failure):
raw, output = tmp_path / "raw", tmp_path / "output"
raw.mkdir()
output.mkdir()
monkeypatch.setattr(lab, "RAW", raw)
monkeypatch.setattr(lab, "OUTPUT", output)
(raw / "tx_level_staircase.cs8").write_bytes(b"test")
metadata = {"tx_sha256": "TEST"}
(output / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
monkeypatch.setattr(lab, "validate_prepared", lambda: metadata)
monkeypatch.setattr(lab.lab054.phy.lab045, "_verify_device_inventory", lambda runner: {"passed": True})
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
monkeypatch.setattr(lab, "parameters", lambda: {})
def clock(point, runner, guard, **kwargs):
return {"passed": failure != "clock", "master_reg29": 0x26,
"slave_reg29": 0x26, "slave_clkin_status": "DETECTED"}
def runner(command):
assert "-t" not in command
if "-r" in command:
value = 127 if failure == "clipping" else 1
Path(command[command.index("-r") + 1]).write_bytes(bytes([value, value]) * 800_000)
log = "1 RX overruns" if failure == "overrun" else "0 RX overruns"
return subprocess.CompletedProcess(command, 0, log, "")
options = {
"runner": runner,
"clock_checker": clock,
"process_checker": lambda: ProcessCheck(ProcessState.CLEAR, ()),
"sleep_function": lambda _: None,
}
if failure:
with pytest.raises(RuntimeError):
lab.preflight(**options)
else:
assert lab.preflight(**options)["passed"]
assert json.loads((raw / "preflight_result.json").read_text())["rf_tx_count"] == 0
def test_capture_is_unavailable_without_new_authorization():
with pytest.raises(PermissionError):
lab.capture_once(authorization=False)
def test_existing_attempt_blocks_capture_before_hardware(tmp_path, monkeypatch):
monkeypatch.setattr(lab, "RAW", tmp_path)
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
monkeypatch.setattr(lab, "require_preflight", lambda metadata: {})
(tmp_path / "hardware_attempt.json").write_text("{}", encoding="utf-8")
with pytest.raises(FileExistsError):
lab.capture_once(
authorization=True,
runner=lambda command: pytest.fail("hardware must not run"),
process_starter=lambda *args: pytest.fail("hardware must not run"),
)
def test_cli_labels_current_rf_state_after_completed_capture(monkeypatch, capsys):
monkeypatch.setattr(
lab,
"capture_once",
lambda authorization=False: {
"stage": "SINGLE_RF_LEVEL_CAPTURE", "rf_tx_count": 1, "passed": True,
},
)
monkeypatch.setattr(
sys, "argv", ["lab065", "--stage", "capture", "--authorize-one-rf"]
)
lab.main()
output = capsys.readouterr().out
assert '"rf_tx_count": 1' in output
assert output.endswith("RF TX NOW = 0\n")

View File

@@ -0,0 +1,149 @@
"""Lab066: исправленные паузы, строгий software gate и запрет TX."""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
import subprocess
import pytest
from experiments import lab066_full_packet_rf_retest as lab
from tools.hackrf_process_check import ProcessCheck, ProcessState
def test_fixed_layout_and_operating_point():
waveform, layout = lab.build_waveform()
assert len(lab.plans()) == 16
assert all(len(frame.plan.payload) == 256 for frame in lab.plans())
assert len({frame.plan.payload for frame in lab.plans()}) == 16
assert lab.PACKET_SAMPLES == 92_256
assert lab.INTER_PACKET_GAP_SAMPLES == 40_000
assert lab.NUMBER_OF_GAPS == 15
assert lab.GUARD_SAMPLES == 800_000
assert lab.BASE_TX_SAMPLES == 2_076_096
assert lab.EXPECTED_TX_SAMPLES == 3_676_096
assert len(waveform) == 2 * lab.EXPECTED_TX_SAMPLES
assert [row["packet_index"] for row in layout] == list(range(1, 17))
assert lab.parameters()["tx_vga_db"] == 20
assert lab.parameters()["rx_lna_db"] == lab.parameters()["rx_vga_db"] == 24
def test_guards_and_all_gaps_are_exact_zero():
waveform, layout = lab.build_waveform()
samples = memoryview(waveform)
assert not any(samples[:2 * lab.GUARD_SAMPLES])
assert not any(samples[-2 * lab.GUARD_SAMPLES:])
for row in layout[:-1]:
low = 2 * row["following_gap_start_sample"]
high = low + 2 * row["following_gap_samples"]
assert high - low == 2 * 40_000
assert not any(samples[low:high])
def test_transfer_commands_are_rx_only_or_fixed_tx():
receive = lab.transfer_command("rx.cs8", 800_000)
transmit = lab.transfer_command("tx.cs8", 100, transmit=True)
assert "-r" in receive and "-t" not in receive
assert "-t" in transmit and "-r" not in transmit
assert receive[receive.index("-f") + 1] == "1400000000"
assert receive[receive.index("-l") + 1] == receive[receive.index("-g") + 1] == "24"
assert transmit[transmit.index("-x") + 1] == "20"
def test_prepare_metadata_and_two_strict_roundtrips(tmp_path, monkeypatch):
raw, output = tmp_path / "raw", tmp_path / "processed"
monkeypatch.setattr(lab, "RAW", raw); monkeypatch.setattr(lab, "OUTPUT", output)
metadata = lab.prepare()
assert metadata["tx_samples"] == lab.EXPECTED_TX_SAMPLES
assert metadata["tx_sha256"] == hashlib.sha256((raw / "tx_stream.cs8").read_bytes()).hexdigest()
result = lab.software_gate()
for mode in ("legacy", "t2"):
row = result[mode]
assert row["header_pass"] == row["fec_pass"] == row["crc_pass"] == 16
assert row["packet_output"] == row["exact_payload_match"] == 16
assert row["packet_indexes"] == list(range(1, 17))
assert row["exact_ordered_payloads"] and row["passed"]
assert row["fallback_between_modes"] is False
assert result["t2"]["t2_state_scope"] == "NEW_PREAMBLE_ESTIMATE_PER_PACKET"
assert result["t2"]["t2_state_reset_evidence"][
"tracking_runs_per_accepted_packet"
] == [2] * 16
assert result["t2_state_reset_verified_by_fresh_preamble_per_packet"] is True
@pytest.mark.parametrize("failure", [None, "clock", "clipping", "strong", "overrun"])
def test_preflight_never_transmits_and_fails_closed(tmp_path, monkeypatch, failure):
raw, output = tmp_path / "raw", tmp_path / "processed"
raw.mkdir(); output.mkdir()
monkeypatch.setattr(lab, "RAW", raw); monkeypatch.setattr(lab, "OUTPUT", output)
(raw / "tx_stream.cs8").write_bytes(b"test")
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
(output / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
monkeypatch.setattr(lab.lab054.phy.lab045, "_verify_device_inventory", lambda runner: {"passed": True})
def clock(point, runner, guard, **kwargs):
return {"passed": failure != "clock", "master_reg29": 0x26,
"slave_reg29": 0x26, "slave_clkin_status": "DETECTED"}
def runner(command):
assert "-t" not in command
if "-r" in command:
value = 127 if failure == "clipping" else (110 if failure == "strong" else 1)
Path(command[command.index("-r") + 1]).write_bytes(bytes([value, value]) * lab.PREFLIGHT_SAMPLES)
log = "1 RX overruns\n0 RX overruns" if failure == "overrun" else "0 RX overruns"
return subprocess.CompletedProcess(command, 0, log, "")
monkeypatch.setattr(lab.lab054, "disable_clock_outputs", lambda runner: [])
kwargs = dict(runner=runner, clock_checker=clock,
process_checker=lambda: ProcessCheck(ProcessState.CLEAR, ()),
sleep_function=lambda _: None)
if failure:
with pytest.raises(RuntimeError): lab.preflight(**kwargs)
else:
assert lab.preflight(**kwargs)["passed"]
record = json.loads((raw / "preflight_result.json").read_text())
assert record["rf_tx_count"] == 0
if failure == "overrun":
assert record["rx_counter_summary"]["maximum_count"] == 1
def test_capture_refuses_without_authorization_before_hardware(tmp_path, monkeypatch):
monkeypatch.setattr(lab, "RAW", tmp_path); monkeypatch.setattr(lab, "OUTPUT", tmp_path)
with pytest.raises(PermissionError):
lab.capture_once(authorization=False, runner=lambda command: pytest.fail("hardware"))
assert not (tmp_path / "hardware_attempt.json").exists()
def test_future_analysis_declares_strict_modes_and_physical_comparison():
source = Path(lab.__file__).read_text(encoding="utf-8")
assert 'decode("legacy", rx_path, diagnostics=True)' in source
assert 'decode("t2", rx_path, diagnostics=True)' in source
assert "physical_packet_diagnostics(tx, rx, location)" in source
assert "if not strict_runs_complete" in source
assert '["Lab056 B", "Lab062", "Lab066"]' in source
def test_future_analysis_rejects_capture_from_another_configuration(tmp_path, monkeypatch):
monkeypatch.setattr(lab, "RAW", tmp_path); monkeypatch.setattr(lab, "OUTPUT", tmp_path)
rx = tmp_path / "rx_stream.cs8"; rx.write_bytes(b"\x00\x00")
capture = {
"lab": "Lab066", "rf_tx_count": 1, "automatic_tx_repeats": 0,
"point": {}, "parameters": {}, "tx_sha256": "WRONG",
"rx_sha256": lab.sha256(rx),
}
(tmp_path / "capture_result.json").write_text(json.dumps(capture))
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "EXPECTED"})
monkeypatch.setattr(lab, "decode", lambda *args, **kwargs: pytest.fail("decode"))
with pytest.raises(RuntimeError, match="подтверждённой"):
lab.analyze_saved_capture()
def test_second_preflight_is_refused_before_hardware(tmp_path, monkeypatch):
monkeypatch.setattr(lab, "RAW", tmp_path); monkeypatch.setattr(lab, "OUTPUT", tmp_path)
(tmp_path / "preflight_attempt.json").write_text("{}")
monkeypatch.setattr(lab, "validate_prepared", lambda: {"tx_sha256": "TEST"})
(tmp_path / "software_gate.json").write_text('{"passed": true, "tx_sha256": "TEST"}')
with pytest.raises(FileExistsError):
lab.preflight(runner=lambda command: pytest.fail("hardware"))

View File

@@ -0,0 +1,79 @@
"""Проверки диагностических вычислений Lab067 без raw IQ и оборудования."""
from __future__ import annotations
import numpy as np
from experiments import lab067_header_failure_analysis as lab067
def test_header_diagnostics_repeats_strict_soft_combining() -> None:
expected = lab067.lab066.plans()[0].plan.phy_header_bits.astype(np.uint8)
soft = 2.0 * expected.astype(float) - 1.0
# Две копии одного бита имеют неверный знак, но малы по модулю. Третья
# правильная копия сильнее, поэтому штатная мягкая сумма сохраняет бит.
soft[:2] *= -0.1
result = lab067.header_diagnostics(soft, expected)
assert result["raw_header_errors"] == 2
assert result["voted_header_errors"] == 0
assert result["accepted"] is True
def test_header_diagnostics_reports_field_and_copy_errors() -> None:
expected = lab067.lab066.plans()[0].plan.phy_header_bits.astype(np.uint8)
received = expected.copy()
received[24:26] ^= 1
result = lab067.header_diagnostics(
2.0 * received.astype(float) - 1.0,
expected,
)
assert result["raw_header_errors"] == 2
assert result["copy_errors"] == [1, 1, 0]
assert result["voted_header_errors"] == 1
assert result["magic_errors"] == 1
assert result["accepted"] is False
def test_strict_candidate_selection_uses_nearest_expected_position() -> None:
rows = [
{"absolute_refined": 980, "packet_output": False, "crc": False, "fec": False, "header": False},
{"absolute_refined": 1003, "packet_output": False, "crc": False, "fec": False, "header": True},
]
selected = lab067.strict_candidate_for_packet(rows, 1000)
assert selected is rows[1]
def test_strict_candidate_selection_never_prefers_farther_success() -> None:
exact_failure = {
"absolute_refined": 1000, "packet_output": False,
"crc": False, "fec": False, "header": False,
}
farther_success = {
"absolute_refined": 1064, "packet_output": True,
"crc": True, "fec": True, "header": True,
}
selected = lab067.strict_candidate_for_packet(
[farther_success, exact_failure], 1000
)
assert selected is exact_failure
def test_strict_candidate_tie_break_does_not_use_verdict() -> None:
lower_failure = {
"absolute_refined": 999, "packet_output": False,
"crc": False, "fec": False, "header": False,
}
upper_success = {
"absolute_refined": 1001, "packet_output": True,
"crc": True, "fec": True, "header": True,
}
selected = lab067.strict_candidate_for_packet(
[upper_success, lower_failure], 1000
)
assert selected is lower_failure
def test_lab067_has_no_rf_entry_point() -> None:
assert not hasattr(lab067, "capture_once")
assert lab067.RX_PATH.name == "rx_stream.cs8"
assert lab067.FAILED_PACKETS == {1, 2, 3, 9, 15, 16}