Lab043: validate pilot-aided short BPSK link

This commit is contained in:
LittleSam129
2026-08-19 18:14:13 +03:00
parent fa2e473c6d
commit f0fa7e8a46
6 changed files with 4416 additions and 0 deletions

View File

@@ -0,0 +1,521 @@
# Lab043. Передача JPEG с Pluto+ на независимый RTL-SDR
## 1. Цель лабораторной
Передать тот же подготовленный JPEG, который использован в Lab042, с
Pluto+ на отдельный RTL-SDR и восстановить его побайтово.
В Lab042 передатчик и приёмник находились в одном Pluto+ и использовали
общий опорный генератор. Lab043 должна убрать это упрощение и раздельно
измерить три эффекта:
1. рассогласование несущих частот передатчика и приёмника;
2. относительное рассогласование частот дискретизации двух устройств;
3. начальную фазу выбора отсчёта символа внутри символьного интервала.
Лабораторная считается успешной только при выполнении радиокритериев,
прикладных критериев и программных проверок, определённых в разделе 11.
Запуск программы сам по себе успехом не считается.
## 2. Границы работы
Lab043 остаётся кабельной. Эфир добавил бы многолучёвость, внешние сигналы
и неконтролируемые потери, хотя предмет этой работы состоит в
синхронизации независимых устройств.
В Lab043 не входят:
- передача через антенны;
- выбор рабочего диапазона 200-250 МГц;
- ARQ и повторная передача;
- исправление стираний и новая FEC;
- потоковое видео;
- скачки по частоте;
- изменение логики управления и безопасности ровера.
Эфирный тракт остаётся предметом следующей лабораторной.
## 3. Подтверждённое оборудование и окружение
Состояние оборудования проверено непосредственным опросом. Перед
реализацией и перед каждым аппаратным прогоном его необходимо проверить
повторно.
### 3.1. Передатчик
- Pluto+;
- устройство должно открываться успешным `iio.Context`, а не проверяться
только командой `ping`;
- чип AD9361;
- используется первый передающий канал.
Конкретный рабочий URI IIO определяется повторным опросом перед опытом и
сохраняется в метаданных запуска. Исторически работали сетевой URI
`ip:192.168.2.1` и прямой USB IIO, но ни один из них нельзя считать
доступным без текущей проверки.
### 3.2. Приёмник
- USB ID `VID_0BDA&PID_2838`;
- имя библиотеки: `Generic RTL2832U OEM`;
- тюнер, сообщённый библиотекой: Fitipower FC0013;
- основной интерфейс `MI_00` использует WinUSB от libwdi;
- устройство открыто из Python через `pyrtlsdr` и `librtlsdr`;
- установка 435 МГц подтверждена чтением значения обратно;
- частота дискретизации 2 400 000 отсчётов/с работает;
- получены комплексные отсчёты;
- доступные аппаратные усиления находятся в диапазоне от -9,9 до
19,7 дБ и задаются дискретными ступенями устройства.
Ошибка второго USB-интерфейса не считается отказом SDR. Основной
интерфейс `MI_00`, через который работает `librtlsdr`, исправен.
### 3.3. Зафиксированное программное окружение
- Python 3.13.1 x64;
- `pyrtlsdr` 0.5.0;
- `pyrtlsdrlib` 0.0.5;
- поставляемая пакетом нативная `librtlsdr` v0.9.0;
- `pyadi-iio` и `pylibiio` для Pluto+.
Принятый способ доступа к RTL-SDR: API `pyrtlsdr` с поставляемой
`pyrtlsdrlib` нативной библиотекой. Малый собственный DLL-адаптер не
нужен. Реализация должна проверять версии и выдавать понятную ошибку, если
библиотека или устройство недоступны. Пользовательский путь к DLL
зашивать в код запрещено.
## 4. Физическая схема и безопасность
Схема:
`Pluto+ TX -> SMA-кабель -> AT30S 30 дБ -> переходник/кабель -> RTL-SDR RX`
Обязательные условия перед каждым включением TX:
1. Антенны Pluto+ и RTL-SDR сняты.
2. AT30S включён последовательно в тракт, а не подключён к свободному
порту.
3. Все разъёмы затянуты, кабель не отсоединяется при включённом TX.
4. Пользователь отдельно подтверждает эту физическую схему.
5. Первый заранее определённый рабочий режим: TX -30 дБ, RTL-SDR
19,7 дБ, несущая 435 МГц, номинально 2,4 Мвыб/с на обоих устройствах.
6. Автоматический перебор усилений, частот, порогов и способов
синхронизации запрещён.
7. Если первый режим не проходит, программа сохраняет исходные данные и
диагностику и останавливается. Следующий прогон разрешён только после
разбора причины и с изменением ровно одного заранее названного
параметра.
При TX -30 дБ расчётный уровень после AT30S составляет около -53 дБм.
Это значение следует из бюджета Lab042: около +7 дБм при TX 0 дБ,
минус 30 дБ настройки TX и минус 30 дБ аттенюатора. Расчёт не заменяет
проверку наличия аттенюатора и правильности схемы.
## 5. Номинальные параметры сигнала и обязательное чтение обратно
| Параметр | Запрошенное значение | Происхождение |
|---|---:|---|
| Несущая TX и RX | 435 МГц | Кабельная рабочая точка Lab042 |
| Символьная скорость | 20 000 симв/с | Lab018-Lab023 и Lab042 |
| Частота дискретизации TX | 2 400 000 отсчётов/с | Поддерживается Pluto+ и проверена на RTL-SDR |
| Частота дискретизации RX | 2 400 000 отсчётов/с | Проверена непосредственным опросом RTL-SDR |
| Номинальное число отсчётов на символ | 120 | 2 400 000 / 20 000 |
| Полоса TX | 200 кГц | Минимальная полоса AD9361, использованная в Lab042 |
| Размер данных фрагмента | 512 байт | Lab042 |
| Модуляция | BPSK | Lab018-Lab023 и Lab042 |
Одинаковое номинальное значение 2,4 Мвыб/с выбрано специально. Lab043
измеряет физическую ошибку генераторов, а не искусственно созданную
разницу номинальных частот дискретизации.
Перед аппаратным опытом программа обязана установить и прочитать обратно:
- частоту дискретизации TX Pluto+;
- частоту дискретизации RX RTL-SDR;
- центральную частоту TX Pluto+;
- центральную частоту RX RTL-SDR;
- усиление TX Pluto+;
- фактически выбранную ступень усиления RTL-SDR.
Для каждого параметра в отчёте и метаданных хранятся отдельные поля
`requested` и `actual`. Если API не предоставляет независимого чтения
обратно, поле `actual` имеет значение `N/A`, а не копию запрошенного
значения. Передача не начинается, если частота дискретизации или несущая
не прочитаны обратно либо отличаются от запрошенных больше, чем допускает
API устройства и заранее установленная проверка.
## 6. Двухтоновая калибровка
Перед BPSK Pluto+ передаёт два комплексных тона, симметричных относительно
несущей:
`-Fcal` и `+Fcal`, где `Fcal = 50 кГц`.
50 кГц выбраны до измерения: оба тона находятся внутри полосы 200 кГц,
не сливаются около нуля и дают разнос 100 кГц для оценки масштаба частот.
RTL-SDR измеряет положения двух пиков относительно своей настроенной
несущей: `f_low` и `f_high`.
### 6.1. Рассогласование несущей
`carrier_offset_hz = (f_low + f_high) / 2`
`carrier_offset_ppm = carrier_offset_hz / 435000000 * 1e6`
Положительный результат означает, что принятый спектр сдвинут вверх по
частоте. Для компенсации сырые комплексные отсчёты умножаются на:
`exp(-j * 2*pi*carrier_offset_hz*n/Fs_rx_actual)`
Знак поправки проверяется независимо: после компенсации среднее положение
двух тонов должно стать ближе к нулю. Рассогласование несущей не должно
подменяться оценкой ошибки частоты дискретизации.
### 6.2. Относительная ошибка частот дискретизации
`clock_scale = (f_high - f_low) / (2*Fcal)`
`sample_clock_error_ppm = (clock_scale - 1) * 1e6`
Здесь положительная ошибка означает, что масштаб передающего такта больше
масштаба приёмного. Для перехода принятых отсчётов на временную сетку
передатчика ожидаемая длина после передискретизации равна:
`len_corrected = round(len_received * clock_scale)`
Направление этой операции не принимается на веру. Оно подтверждается
машинными тестами из раздела 8. После передискретизации накопленная ошибка
символьного времени должна уменьшаться.
### 6.3. Начальная фаза выбора отсчёта символа
После грубой CFO-коррекции и передискретизации приёмник отдельно ищет
начальную фазу выбора отсчёта в диапазоне от 0 до 119 номинальных
отсчётов на символ. Это не CFO и не ошибка такта. В результатах отдельно
хранятся:
- `carrier_offset_hz` и `carrier_offset_ppm`;
- `clock_scale` и `sample_clock_error_ppm`;
- `symbol_sample_phase` и остаточная ошибка символьного времени.
### 6.4. Формальное определение шумового фона
Шумовой фон и превышение пиков вычисляются одинаковым детерминированным
алгоритмом:
1. Из комплексных отсчётов вычитается среднее.
2. Оценивается спектральная плотность мощности методом Welch с окном Hann,
`nfft = nperseg = 65536` и перекрытием 50 процентов.
3. Разрешение одного БПФ вычисляется по прочитанной обратно частоте как
`delta_f = Fs_rx_actual / 65536`. При 2,4 Мвыб/с оно равно
`36,62109375 Гц`.
4. Анализируется полоса от -100 до +100 кГц относительно настроенной
несущей.
5. Нижний пик ищется в диапазоне от -80 до -20 кГц, верхний от +20 до
+80 кГц. Эти непересекающиеся окна заданы до измерения.
6. После нахождения кандидатов ожидаемых тонов из оценки шума исключаются
защитные зоны шириной +/-2 кГц вокруг измеренного положения каждого
кандидата. Каждая полузона занимает около 55 разрешающих элементов БПФ
при 2,4 Мвыб/с, поэтому утечка основного лепестка и ближайших боковых
лепестков тона не попадает в медиану шума.
7. Шумовая мощность `P_noise` равна медиане линейных значений мощности
всех оставшихся спектральных элементов анализируемой полосы.
8. Для каждого тона вычисляется
`peak_excess_db = 10*log10(P_peak/P_noise)`.
Оба пика должны превышать шумовой фон не менее чем на 10 дБ. Порог 10 дБ,
полоса анализа, окна поиска и защитные зоны зафиксированы до аппаратного
измерения. Если нет двух конечных пиков, недостаточно элементов шума или
хотя бы один пик не проходит порог, оценка этого захвата недействительна.
### 6.5. Повторы и представление недействительных оценок
Без изменения схемы выполняются три отдельных калибровочных захвата. Для
каждого захвата отдельно сохраняются:
- `f_low_hz` и `f_high_hz`;
- `low_peak_excess_db` и `high_peak_excess_db`;
- `carrier_offset_hz` и `carrier_offset_ppm`;
- `clock_scale` и `sample_clock_error_ppm`;
- остаточная CFO после коррекции;
- остаточная ошибка разноса тонов в герцах и ppm;
- признак достоверности и причина отказа.
По трём захватам вычисляются среднее, минимум, максимум и стандартное
отклонение каждой конечной оценки, а также число недействительных
захватов. Стандартное отклонение считается по совокупности всех конечных
оценок с `ddof=0`. Недействительное значение записывается как `NaN` в CSV
и как `N/A` в текстовом отчёте. Оно никогда не заменяется нулём. Если
конечных значений нет, все агрегаты этой величины равны `N/A`.
## 7. Диагностическая последовательность передачи
Аппаратная часть выполняется по ступеням. Переход к следующей ступени
разрешён только после сохранения результатов предыдущей:
1. двухтоновые калибровочные захваты;
2. известная короткая BPSK-последовательность;
3. символьная синхронизация и измерение BER известной последовательности;
4. один пакет с CRC;
5. десять пакетированных фрагментов JPEG;
6. сборка JPEG;
7. проверка размера и SHA-256.
Известная последовательность задаётся до измерения как PRBS11 длиной
2047 бит с полиномом `x^11 + x^9 + 1` и фиксированным ненулевым начальным
состоянием, записанным в метаданные. BER вычисляется прямым сравнением
каждого принятого бита с ожидаемым до упаковки в пакет и до проверки CRC.
CRC не может использоваться вместо измерения BER.
### 7.1. Контрольный опыт PRBS11 после проверки непрерывного async-приёма
Короткая запись S и длинная запись L являются двумя отдельными аппаратными
захватами. Каждый захват выполняется одним непрерывным
`rtlsdr_read_async()` и содержит в этой последовательности: входной запас,
двухтоновую калибровку, фиксированный защитный участок 0,10 с, один
начальный маркер, непрерывную PRBS11 и выходной запас. Полезная PRBS11
длится 0,25 с в S и не менее 2,0 с в L. Внутри полезной PRBS11 нет
периодической повторной синхронизации.
Оценки CFO и масштаба частоты дискретизации для обработки PRBS11 берутся
только из двух тонов в той же самой IQ-записи. Оценку из предыдущего
двухтонового опыта или из другой записи применять запрещено. Если тоны в
текущем захвате недостоверны, PRBS11 этой записи не интерпретируется.
Для обоих захватов заранее фиксируются 435 МГц, номинальные 2 400 000
отсчётов/с TX и RX, 20 000 символов/с, TX -30 дБ, запрос усиления RTL-SDR
19,7 дБ и `Fcal = 50 кГц`. Асинхронный приём использует callback 262144
байта, 15 буферов и отбрасывает первый callback как разогревочный без
перезапуска сеанса. Автоматический подбор параметров запрещён.
Одна и та же IQ-запись обрабатывается режимами A, B и C. В B и C грубая
CFO берётся из тонов этой записи; в C дополнительно применяется
`clock_scale` этих же тонов. Разрешённая защищённая тонкая CFO-коррекция не
заменяет двухтоновое измерение. Для длинной записи SRO дополнительно
оценивается независимо по накопленному дрейфу символьной фазы.
## 8. Обязательные программные проверки до аппаратного опыта
На синтетических комплексных данных проверяются четыре ошибки частоты
дискретизации: +20, -20, +100 и -100 ppm. Для каждого случая тест обязан
проверить:
- знак оценки `sample_clock_error_ppm`;
- величину оценки в заранее заданном допуске;
- направление изменения длины при передискретизации;
- уменьшение остаточной накопленной ошибки символьного времени после
поправки.
Для положительной ошибки длина должна изменяться в направлении,
предписанном формулой `round(N * clock_scale)`, для отрицательной в
противоположном. Тест должен упасть, если вместо `clock_scale` применена
обратная величина. Допуск оценки и критерий уменьшения остаточной ошибки
задаются в тесте до аппаратного измерения и печатаются в его результате.
Дополнительно синтетические тесты проверяют:
- знак грубой CFO-коррекции для положительной и отрицательной CFO;
- уменьшение остаточной CFO после поправки;
- отдельное восстановление начальной фазы выбора отсчёта символа;
- явный `N/A`, если два тона не найдены достоверно;
- формулу шумового фона и порог 10 дБ;
- BER известной последовательности до CRC;
- отсутствие ложного успеха при неполном пакете или изображении;
- формирование кода возврата только по полному набору критериев.
До прохождения этих проверок передатчик включать нельзя.
## 9. Порядок аппаратного эксперимента
1. Открыть Pluto+ успешным `iio.Context` и записать URI и идентификаторы.
2. Открыть RTL-SDR через `pyrtlsdr` и записать имя устройства и тюнера.
3. Установить и прочитать обратно параметры из раздела 5.
4. Получить отдельное подтверждение пользователя, что кабельная схема из
раздела 4 собрана и AT30S находится в разрыве.
5. Запустить один непрерывный приём RTL-SDR за 0,5 с до передачи Pluto+.
6. Выполнить три отдельных двухтоновых калибровочных захвата.
7. Сохранить оценки каждого захвата, агрегаты и не менее одного
эталонного сырого IQ-захвата.
8. Передать известную BPSK-последовательность и измерить BER.
9. Принять один пакет и проверить CRC.
10. Передать десять фрагментов JPEG и проверить критерии раздела 11.
Если любая ступень не проходит, переход к следующей запрещён. Исходные
данные и отрицательный результат сохраняются без подгонки. Изменение
одного параметра для следующего запуска оформляется как отдельное заранее
объяснённое действие.
Весь опыт принимается RTL-SDR как одна непрерывная временная
последовательность. Продолжительность приёма вычисляется из фактически
сформированной передаваемой последовательности:
`T_rx = 0,5 с + N_tx / Fs_tx_actual + 0,5 с`.
Здесь `N_tx` является фактическим числом комплексных отсчётов всей
сформированной TX-последовательности. Общая длительность опыта не задаётся
произвольной константой. Поток разрешено читать блоками, например около
262144 отсчётов, но блоки объединяются без пропусков и перестановок в одну
последовательность. Размер блока чтения является параметром реализации, а
не физическим защитным интервалом. Десять отдельных запусков приёмника для
десяти кадров запрещены, потому что они уничтожили бы наблюдаемое
накопление ошибки тактов.
Между BPSK-кадрами не добавляются новые интервалы. Используется структура
Lab042: `GUARD_SYMBOL_COUNT` импортируется из её текущей конфигурации без
копии числовой константы в Lab043, а формирование кадра повторяет ту же
последовательность переднего интервала, символов, заднего интервала и RRC.
Готовая функция Lab042 жёстко связана с 128 отсчётами на символ, поэтому
при 120 отсчётах Lab043 переиспользуются её параметры, а не скрытая копия
её глобальной частоты дискретизации. Если в дальнейшем отдельный интервал
исчезнет из Lab042, Lab043 не создаёт новый без отдельного диагностического
решения.
## 10. Три режима обработки одной записи
Одна и та же сохранённая BPSK/IQ-запись обрабатывается тремя режимами:
- режим A: без грубой CFO-коррекции и без коррекции частоты
дискретизации;
- режим B: только грубая CFO-коррекция из двухтоновой калибровки;
- режим C: грубая CFO-коррекция, коррекция частоты дискретизации и
защищённая тонкая CFO-коррекция.
Во всех режимах начальная фаза выбора отсчёта символа оценивается и
записывается отдельно. Для каждого режима сохраняются BER известной
последовательности, остаточная CFO, остаточная временная ошибка,
корреляция маркера, число найденных кадров, число разобранных заголовков и
CRC пакетов.
В режиме C защищённая функция `should_apply_cfo_correction` из
`protocol/bpsk_radio.py` остаётся тонкой коррекцией после грубого
измерения. Она не используется как единственный способ поиска сдвига в
десятки килогерц, поскольку символьная оценка неоднозначна за пределами
половины символьной скорости.
Провал режимов A или B не считается провалом лабораторной. Они являются
контрольными измерениями. Критерии передачи применяются к режиму C.
## 11. Критерии приёмки
### 11.1. Радиокритерии режима C
- найдены все 10 кадров;
- разобраны все 10 пакетов;
- CRC32 сошлась у всех 10 пакетов.
### 11.2. Прикладные критерии режима C
- восстановлены все 10 уникальных фрагментов;
- JPEG собран без заполнения отсутствующих данных;
- размер принятого JPEG совпадает с размером переданного;
- SHA-256 принятого JPEG совпадает с SHA-256 переданного.
### 11.3. Общий результат
Код возврата 0 разрешён только при одновременном выполнении:
- всех радиокритериев;
- всех прикладных критериев;
- всех обязательных программных проверок раздела 8.
Для подтверждения воспроизводимости заранее выбранная рабочая точка
запускается три раза подряд без изменения схемы и параметров. Каждый
повтор сохраняется отдельно. Три повтора являются проверкой
воспроизводимости, а не статистической оценкой вероятности отказа.
Отрицательный результат допустим и сохраняется полностью. Запрещено
автоматически поднимать усиление, менять частоту, пороги или алгоритм
синхронизации после неудачного прогона.
## 12. Измерения и артефакты
### 12.1. Измерения передачи
Для каждого запуска и каждого режима обработки сохраняются:
- запрошенные и фактические усиления, несущие и частоты дискретизации;
- число принятых отсчётов;
- отдельные оценки CFO, ошибки частоты дискретизации и начальной
символьной фазы;
- BER известной последовательности до CRC;
- число найденных кадров и разобранных заголовков;
- CRC каждого пакета;
- решение защищённой тонкой CFO-коррекции и его диагностические величины;
- корреляция маркера и корректно определённая EVM;
- число и номера восстановленных фрагментов;
- размер и SHA-256 переданного и принятого JPEG;
- длительность сигнала и полезная скорость.
Каждое число вычисляется из текущего запуска. Перенесённые константы
снабжаются ссылкой на лабораторную-источник. Невычислимая величина
записывается как `NaN` или `N/A`, но не как правдоподобный ноль.
### 12.2. План программных артефактов
Предполагаемые файлы после отдельного согласования реализации:
- `experiments/lab043_pluto_to_rtlsdr.py`;
- `tests/test_lab043_calibration.py`;
- `data/processed/lab043/lab043_calibration.csv`;
- `data/processed/lab043/lab043_frames.csv`;
- `data/processed/lab043/lab043_summary.csv`;
- `data/processed/lab043/lab043_report.txt`;
- графики калибровочного спектра, остаточной ошибки и доставки
фрагментов.
### 12.3. Эталонный сырой IQ-захват
Не менее одного полного эталонного захвата до CFO- и SRO-коррекций
сохраняется в `data/raw/lab043/` и не добавляется в Git.
Формат отсчётов: NumPy `.npy`, одномерный массив `complex64` в порядке
приёма. Нормирование исходных 8-битных I/Q отсчётов и порядок I/Q должны
быть однозначно описаны в метаданных. Рядом сохраняется UTF-8 JSON с тем
же базовым именем и полями:
- локальная метка времени и UTC;
- идентификаторы устройств и модель тюнера;
- версии Python, `pyrtlsdr`, `pyrtlsdrlib`, `librtlsdr`, `pyadi-iio` и
`pylibiio`;
- запрошенные и фактические частоты дискретизации, несущие и усиления;
- число отсчётов, длительность, dtype и порядок байтов;
- идентификатор и SHA-256 переданной формы сигнала;
- результаты калибровки, применённые к этой записи;
- SHA-256 файла `.npy`.
IQ-файл содержит всю единую последовательность приёма. Ожидаемое число
отсчётов вычисляется как `ceil(T_rx * Fs_rx_actual)`. Чтение выполняется
последовательными блоками до достижения этого числа, последний блок
обрезается только после получения требуемого количества отсчётов.
### 12.4. Разделение общего и лабораторного кода
Общие математические примитивы обнаружения двух известных тонов, оценки
грубой CFO и относительной ошибки частот дискретизации, грубой частотной
коррекции и передискретизации размещаются в `protocol/bpsk_radio.py` либо
в другом подходящем существующем модуле `protocol/`. Каждый такой перенос
сопровождается быстрым синтетическим тестом.
В `experiments/lab043_pluto_to_rtlsdr.py` остаются сценарий лабораторной,
конкретное `Fcal`, три калибровочных захвата, методика оценки шума Lab043,
режимы A/B/C, PRBS11, контрольный пакет, JPEG, отчётные артефакты,
критерии приёмки и аппаратная последовательность. Общий код не зависит от
конкретного JPEG и номера лабораторной.
## 13. Точка остановки перед аппаратным опытом
Программная реализация и синтетические проверки разрешены. Перед
аппаратным опытом необходимо отдельно:
1. пройти все обязательные программные проверки раздела 8;
2. повторно проверить доступность устройств и чтение параметров обратно;
3. получить подтверждение пользователя о схеме и отдельное разрешение на
включение TX.
Способ Python-доступа к RTL-SDR уже выбран и не является вопросом точки
остановки.
До отдельного разрешения передатчик не включается, усиление Pluto+ не
изменяется, аппаратная калибровка и передача тонов, BPSK или JPEG не
выполняются. Операции `git add`, `commit`, `push`, `pull` и `fetch` также
не выполняются.