The tests/ directory held 50 laboratory programs and no tests. They model channels, run hundreds of repetitions and write CSV, PNG and reports; calling that a test suite blocked introducing a real one, because any pytest run would have collected the labs and re-executed every experiment. - move all 50 lab programs to experiments/ with git mv, preserving history - rewrite the 38 cross-imports between labs from tests.labNNN to experiments.labNNN - leave tests/ empty for actual fast checks of protocol/ - point quick_gate and the hook at the new layout and add experiments/ to the syntax sweep - update the paths quoted in the Lab042 specification and the verifier agent definition This also defuses the import-time work finding without touching 41 files: the labs still create directories and write files on import, but nothing imports them now except the gate, which does so deliberately. Gate passes: syntax clean, protocol imports, 15 lab modules import, 2 functional suites run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
27 KiB
Lab042 — техническое задание
Передача JPEG через настоящий радиотракт: кабельная петля на одном PlutoSDR
Документ предназначен для исполнителя, не участвовавшего в предыдущих лабораторных. Всё, что нужно для работы, изложено здесь.
1. Зачем эта лабораторная
За Lab001–Lab041 собран весь тракт передачи изображения, кроме одного звена: передатчик ни разу не включался. Во всём проекте нет ни одного вызова передачи через SDR.
Цель Lab042 — соединить уже готовые части и получить изображение, прошедшее через настоящий радиотракт. Не улучшить, не оптимизировать, не измерить дальность. Получить картинку на выходе.
Всё, что не требуется для этой цели, в Lab042 не делается.
2. Что уже готово и должно быть переиспользовано
Ничего из перечисленного писать заново не нужно.
| Звено | Где взять |
|---|---|
| Разбиение JPEG на фрагменты и сборка обратно | protocol/image_fragments.py, примеры в experiments/lab007_image_fragmentation.py |
| Пакет с CRC32, кодирование и разбор | protocol/packet.py |
| Подбор размера и качества JPEG | experiments/lab010_image_optimization.py, готовые профили в data/processed/lab010 |
| Формирование радиокадра с преамбулой и маркером | build_radio_frame в experiments/lab018_bpsk_radio_frame.py |
| Фильтр приподнятого косинуса | root_raised_cosine_taps там же |
| Модуляция BPSK | bpsk_modulate там же |
| Поиск кадра в потоке отсчётов | find_radio_frame в experiments/lab019_bpsk_receiver.py |
| Оценка и коррекция ухода частоты | estimate_carrier_parameters, correct_phase_and_frequency там же |
| Демодуляция и разбор кадра | bpsk_demodulate, decode_radio_frame там же |
| Работа с Pluto на приём | experiments/lab024a_pluto_rx_capture.py |
Единственное, чего нет в проекте — передача. Это и есть основной объём работы.
3. Почему петля на одном устройстве, а не два устройства
Передатчик и приёмник одного PlutoSDR работают от общего опорного генератора. Поэтому между ними практически нет ухода несущей частоты и не расходятся часы.
Это снимает ровно те две проблемы, которые в проекте проработаны слабее всего, и позволяет получить картинку сразу. Разведение на два устройства — отдельная лабораторная Lab043, там появится настоящий уход частоты, и вот там понадобятся наработки Lab021–Lab023.
Не пытайтесь сделать Lab042 и Lab043 одной работой. Разница между ними — вся сложность синхронизации.
4. Наличное оборудование
Подтверждено по состоянию на 10 августа 2026 года.
| Позиция | Состояние |
|---|---|
| Pluto+ — SDR, 2 приёмника и 2 передатчика, гигабитный Ethernet | есть |
| RTL-SDR — приёмный донгл | есть, понадобится в Lab043 |
| Кабели SMA | будут найдены |
| Антенны: штыревые на магнитном основании, короткая штыревая | есть, в Lab042 не используются |
| Рамочная антенна Donut AM (0,5–2 МГц) | к проекту отношения не имеет |
| Аттенюатор AT30S, 30 дБ, до 200 Вт, разъёмы SMA | есть |
Аппаратных препятствий к началу работы нет. Запас по мощности у аттенюатора избыточен на много порядков — рассчитан на передатчики в сотни ватт, а у нас доли милливатта, — но это никак не мешает: ослабление от уровня сигнала не зависит.
Важно: устройство — Pluto+, а не оригинальный ADALM-PLUTO. Отличия, которые имеют значение:
- связь по Ethernet, а не по USB; адрес
ip:192.168.2.1уже подтверждён работой Lab024a; - два приёмных и два передающих канала; в Lab042 используется только первый канал каждого;
- вопреки надписи на корпусе, устройство сообщает о себе как
Analog Devices PlutoSDR Rev.C (Z7010-AD9361)и представлено одним приёмным и одним передающим каналом: буферные устройстваcf-ad9361-lpcиcf-ad9361-dds-core-lpcнесут по паре каналов I и Q. Поэтому классadi.Plutoподходит, переход наadi.ad9361не требуется; - чип AD9361, а не урезанный AD9363. Диапазон перестройки приёмника от 70 МГц до 6 ГГц, передатчика от 46,9 МГц до 6 ГГц. Целевой диапазон проекта ниже 350 МГц устройству доступен штатно, без доработки прошивки. Для Lab044 это снимает ранее открытый вопрос;
- прошивка
v0.32-1-g7bdc-dirty, ядро 4.19. Обновление в рамках Lab042 не выполняется.
5. Требования к схеме и сохранности оборудования
Разрешения на работу в эфире у владельца проекта есть, вопрос законности здесь не рассматривается. Перечисленное ниже защищает оборудование и чистоту опыта.
- Антенны убрать со стола до начала работы. Причина техническая: антенна на передатчике или приёмнике создаёт паразитный путь распространения параллельно кабельному. Уровень на входе приёмника перестаёт соответствовать расчёту, появляется переотражённый сигнал, и при отказе будет невозможно понять, что именно не сработало. Кабельная петля ценна тем, что в ней известно всё; антенна это свойство разрушает.
- Передатчик не включать при отключённом кабеле: выход без нагрузки вреден для оконечного каскада.
- Аттенюатор AT30S включается в разрыв между выходом передатчика и входом приёмника. Не параллельно, не на свободный порт — именно последовательно в тракт.
- Перед первым включением передатчика убедиться глазами, что аттенюатор действительно в цепи. Это единственное, что стоит между опечаткой в настройке усиления и сгоревшим приёмником.
Расчёт уровней
| Величина | Значение |
|---|---|
| Выход передатчика Pluto+ при усилении 0 дБ | около +7 дБм |
| Предельно допустимый уровень на входе приёмника | около +2,5 дБм |
| Ослабление аттенюатора | 30 дБ |
| Уровень на входе приёмника при усилении 0 дБ | около −23 дБм |
Вывод: с аттенюатором в разрыве весь диапазон tx_hardwaregain_chan0 от −89 до 0 дБ безопасен для приёмника. Запас до порога повреждения составляет более 25 дБ даже в самом худшем случае.
Порядок подбора усиления:
- Начать с
tx_hardwaregain_chan0 = -30. Это даёт около −53 дБм на входе приёмника — заведомо чистый и заведомо безопасный уровень. - Если сигнал слишком слаб, поднимать шагами по 10 дБ.
- Если созвездие разваливается при сильном сигнале — это насыщение приёмника, лечится уменьшением усиления, а не увеличением.
Без аттенюатора работать запрещено даже при малом усилении: защита нужна не от расчётного уровня, а от ошибки оператора.
6. Программное окружение: препятствие, которое надо снять первым
Проверка окружения 10 августа 2026 года показала следующее.
Интерпретатор, доступный в командной строке — C:\Users\user\AppData\Local\Programs\Python\Python313\python.exe, версия 3.13.1. В нём отсутствуют pyadi-iio и libiio, то есть работать с Pluto+ из него невозможно. Также отсутствует scipy, который нужен лабораторным Lab020–Lab022, Lab024b и Lab025.
При этом Lab024a когда-то отработала с настоящим устройством. Значит, существует второе окружение — по всей видимости, среда Anaconda, — где всё установлено, и запуск шёл оттуда.
Первое действие исполнителя: определить, в каком окружении запускаются лабораторные, и зафиксировать это. Пока это не сделано, невозможно ни повторить Lab024a, ни начать Lab042.
Требуется:
- Найти окружение, где установлены
pyadi-iioиlibiio, и проверить связь с устройством. - Записать в отчёт: путь к интерпретатору, версию Python, версии
pyadi-iioиlibiio. - Если такого окружения нет — установить
pyadi-iioиlibiio, зафиксировав версии.
Расчётные лабораторные, включая Lab041, в окружении без pyadi-iio запускаются нормально — им нужны только numpy, matplotlib и opencv. Разделение аппаратных и расчётных зависимостей относится к этапу стабилизации и в Lab042 не решается, но результат проверки надо записать: он понадобится при составлении описания окружения.
7. Параметры тракта
Значения тракта взяты из Lab018 и менять их в этой лабораторной запрещено, за единственным исключением, описанным сразу под таблицей. Причина запрета: переход на железо сам по себе вносит достаточно неизвестных. Менять одновременно и параметры сигнала — значит потерять возможность понять причину отказа.
| Параметр | Значение | Источник |
|---|---|---|
| Символьная скорость | 20 000 симв/с | SYMBOL_RATE, Lab018 |
| Отсчётов на символ | 128 | изменено против Lab018, см. ниже |
| Частота дискретизации | 2 560 000 Гц | произведение двух предыдущих |
| Скругление фильтра | 0,35 | RRC_ROLLOFF, Lab018 |
| Длина фильтра | 10 символов | RRC_SPAN_SYMBOLS, Lab018 |
| Преамбула | 64 бита | PREAMBLE_BIT_COUNT, Lab018 |
| Слово синхронизации | 0xD391 | RADIO_SYNC_WORD, Lab018 |
| Защитный интервал | 16 символов | GUARD_SYMBOL_COUNT, Lab018 |
| Амплитуда передачи | 0,70 | TX_AMPLITUDE, Lab018 |
Обязательное отступление от Lab018: частота дискретизации
Опрос устройства 10 августа 2026 года показал, что минимальная частота дискретизации AD9361 составляет 2 083 333 Гц. Частота 640 000 Гц из Lab018 физически недостижима: она втрое ниже предела. Понизить её можно только включением встроенного КИХ-фильтра с прореживанием, что добавляет к первому включению передатчика ещё один непроверенный механизм.
Принято решение поднять число отсчётов на символ с 32 до 128. Тогда:
- символьная скорость остаётся 20 000 симв/с, как в Lab018;
- скругление, длина фильтра, преамбула, слово синхронизации и защитный интервал — без изменений;
- частота дискретизации становится 20 000 × 128 = 2 560 000 Гц, что выше минимума с запасом.
Меняется только степень передискретизации — величина, относящаяся к стыку с устройством, а не к самому сигналу. Форма спектра, скорость и структура кадра остаются теми же, что проверены в Lab018–Lab023. Это единственное разрешённое расхождение с Lab018, и оно вынужденное.
Полезная скорость получается около 20 кбит/с. Для картинки в несколько килобайт это единицы секунд. Этого достаточно: цель — картинка, а не скорость.
Побочное следствие: фильтр приподнятого косинуса при 128 отсчётах на символ и длине 10 символов получается на 1281 отвод. Свёртку следует выполнять через быстрое преобразование Фурье (scipy.signal.fftconvolve), иначе формирование сигнала займёт недопустимо много времени.
Параметры PlutoSDR
| Параметр | Значение | Примечание |
|---|---|---|
| Адрес устройства | ip:192.168.2.1 |
как в Lab024a |
| Несущая передачи и приёма | 435 МГц | обе одинаковые |
| Частота дискретизации | 2 560 000 | совпадает с трактом, минимум устройства 2 083 333 |
| Полоса радиочастотного тракта | 200 000 Гц | минимум Pluto, сигнал занимает около 27 кГц |
| Усиление передатчика | начать с −30 дБ | с аттенюатором безопасен весь диапазон, см. раздел 5 |
| Режим усиления приёмника | manual |
автоматика мешает измерениям |
| Усиление приёмника | начать с 20 дБ | подобрать по амплитуде |
| Циклический буфер передачи | выключен | см. раздел 9, ловушка 1 |
Про несущую: 435 МГц выбрана потому, что нижняя граница PlutoSDR составляет около 325 МГц, а целевой диапазон проекта ниже 350 МГц находится у самого края. Поскольку передача идёт по кабелю и в эфир не выходит, выбор частоты здесь ни на что не влияет. Вопрос рабочего диапазона решается отдельно перед Lab044.
8. Порядок работы
Передающая сторона
- Взять исходное изображение из
data/raw, привести к профилю из Lab010 так, чтобы JPEG получился не крупнее 5 КБ. - Разбить на фрагменты через
protocol/image_fragments.py. - Каждый фрагмент упаковать в пакет с CRC32 через
protocol/packet.py. - Каждый пакет обернуть в радиокадр через
build_radio_frame. - Промодулировать BPSK и отфильтровать.
- Привести к целым числам и передать.
Приёмная сторона
- Запустить приём до начала передачи.
- Найти кадры через
find_radio_frame. - Скорректировать фазу и частоту.
- Демодулировать, разобрать кадр, проверить CRC32.
- Собрать изображение из уцелевших фрагментов и сохранить на диск.
Приём и передача идут в одном процессе. Приём запускается первым — иначе начало первого кадра теряется безвозвратно.
9. Ловушки, на которых спотыкаются в первый раз
Перечислено то, что почти наверняка будет сделано неправильно без предупреждения.
-
Циклический буфер передачи. У PlutoSDR буфер передачи по умолчанию повторяется бесконечно. Кажется, что передан один кадр, а передаётся бесконечный поток. Признак: приёмник видит тысячи копий одного кадра, а передача не заканчивается. Буфер надо явно выключить и явно освобождать после передачи.
-
Масштаб отсчётов. PlutoSDR принимает целые числа, а не дробные значения от минус единицы до единицы. Готовый сигнал надо умножить на масштабный множитель, обычно 2 в степени 14. Признак ошибки: передаётся почти тишина, приёмник ничего не находит.
-
Приём запущен после передачи. Начало кадра теряется, преамбула не находится. Признак:
find_radio_frameне срабатывает при заведомо исправном сигнале. -
Размер приёмного буфера меньше кадра. Кадр не помещается целиком, поиск преамбулы срывается. Буфер должен вмещать кадр с запасом минимум вдвое.
-
Автоматическая регулировка усиления приёмника. Меняет амплитуду в процессе и портит оценку. Режим должен быть ручным.
-
Насыщение приёмника. При слишком слабом ослаблении сигнал упирается в потолок и созвездие разваливается, хотя уровень кажется отличным. Признак: демодуляция не работает при очень сильном сигнале. Лечится увеличением ослабления, а не усилением.
-
Переходный процесс в начале передачи. Первые несколько десятков отсчётов недостоверны. Поэтому в тракте есть защитный интервал в 16 символов — его нельзя убирать.
10. Что считается выполненной работой
Лабораторная принимается, если выполнено всё перечисленное.
Обязательный результат
- Файл принятого изображения на диске, открывающийся просмотрщиком.
- Принятое изображение совпадает с переданным при отсутствии потерь — сравнение по контрольной сумме всего файла.
- Работа воспроизводится: два запуска подряд дают одинаковый успешный результат.
Обязательные измерения
| Величина | Как получить |
|---|---|
| Размер переданного JPEG, байт | из файла |
| Число фрагментов и пакетов | из разбиения |
| Число принятых кадров | счётчик приёмника |
| Число кадров, не прошедших CRC32 | счётчик приёмника |
| Доля успешно принятых фрагментов | отношение |
| Полное время передачи, секунды | измерение |
| Фактическая полезная скорость, кбит/с | расчёт из двух предыдущих |
| Усиление передатчика и приёмника | из настроек устройства |
| Ослабление аттенюатора, дБ | 30, AT30S |
Обязательные файлы в data/processed/lab042
- принятое изображение;
- CSV с измерениями из таблицы выше;
- текстовый отчёт;
- график созвездия принятого сигнала;
- график спектра принятого сигнала.
Требования к коду
- Ничего из
protocol/не переписывать. Только использовать. - Параметры тракта из раздела 5 не менять.
- Все константы вынести в начало файла с указанием источника, как это сделано в Lab041.
- Никаких вписанных руками результатов: каждое число в отчёте должно вычисляться из измерений текущего запуска. Это отдельно проверяется при приёмке.
- Файл лабораторной:
experiments/lab042_pluto_image_loopback.py, запуск черезpython -m experiments.lab042_pluto_image_loopbackиз корня проекта.
11. Что в этой лабораторной делать не надо
Перечислено, чтобы не расширять объём.
- Не реализовывать повторную передачу потерянных фрагментов. Потери фиксируются, но не восстанавливаются.
- Не подключать исправление стираний из
protocol/packet_erasure_fec.py. - Не поднимать скорость и не менять модуляцию.
- Не выходить в эфир и не подключать антенну. Это вопрос не разрешений, а объёма: эфирная передача — предмет Lab044, и смешивать её с первым включением передатчика нельзя.
- Не работать с двумя устройствами.
- Не трогать логику сеансов, аварийной остановки и приоритетов — она к передаче картинки отношения не имеет.
Всё это относится к последующим лабораторным.
12. Порядок приёмки
- Исполнитель выполняет работу и сообщает о готовности.
- Проверяется
python tools/quick_gate.py— должен проходить. - Проводится аудит: соответствие разделу 10, отсутствие вписанных результатов, происхождение каждого числа в отчёте.
- При наличии замечаний они оформляются списком с указанием файла и строки.
- После приёмки в
PROJECT_LOG.mdдобавляется новая запись по принятому в проекте образцу.
13. Что дальше
- Lab043 — разведение на два устройства: передаёт PlutoSDR, принимает RTL-SDR. Появляется настоящий уход несущей частоты и расхождение часов. Здесь понадобятся Lab021–Lab023.
- Lab044 — выбор рабочего диапазона и первый выход в эфир на малой мощности. Разрешения имеются, поэтому вопрос сводится к техническому выбору диапазона, мощности и антенны.
- Повышение скорости, исправление стираний и передача последовательности кадров — после того, как картинка устойчиво проходит.