Files
SDR-Rover/docs/lab042_hardware_loopback_spec.md
LittleSam129 c486039053 Split experiments from tests
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>
2026-08-10 14:34:58 +03:00

27 KiB
Raw Permalink Blame History

Lab042 — техническое задание

Передача JPEG через настоящий радиотракт: кабельная петля на одном PlutoSDR

Документ предназначен для исполнителя, не участвовавшего в предыдущих лабораторных. Всё, что нужно для работы, изложено здесь.


1. Зачем эта лабораторная

За Lab001Lab041 собран весь тракт передачи изображения, кроме одного звена: передатчик ни разу не включался. Во всём проекте нет ни одного вызова передачи через 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, там появится настоящий уход частоты, и вот там понадобятся наработки Lab021Lab023.

Не пытайтесь сделать Lab042 и Lab043 одной работой. Разница между ними — вся сложность синхронизации.


4. Наличное оборудование

Подтверждено по состоянию на 10 августа 2026 года.

Позиция Состояние
Pluto+ — SDR, 2 приёмника и 2 передатчика, гигабитный Ethernet есть
RTL-SDR — приёмный донгл есть, понадобится в Lab043
Кабели SMA будут найдены
Антенны: штыревые на магнитном основании, короткая штыревая есть, в Lab042 не используются
Рамочная антенна Donut AM (0,52 МГц) к проекту отношения не имеет
Аттенюатор 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. Требования к схеме и сохранности оборудования

Разрешения на работу в эфире у владельца проекта есть, вопрос законности здесь не рассматривается. Перечисленное ниже защищает оборудование и чистоту опыта.

  1. Антенны убрать со стола до начала работы. Причина техническая: антенна на передатчике или приёмнике создаёт паразитный путь распространения параллельно кабельному. Уровень на входе приёмника перестаёт соответствовать расчёту, появляется переотражённый сигнал, и при отказе будет невозможно понять, что именно не сработало. Кабельная петля ценна тем, что в ней известно всё; антенна это свойство разрушает.
  2. Передатчик не включать при отключённом кабеле: выход без нагрузки вреден для оконечного каскада.
  3. Аттенюатор AT30S включается в разрыв между выходом передатчика и входом приёмника. Не параллельно, не на свободный порт — именно последовательно в тракт.
  4. Перед первым включением передатчика убедиться глазами, что аттенюатор действительно в цепи. Это единственное, что стоит между опечаткой в настройке усиления и сгоревшим приёмником.

Расчёт уровней

Величина Значение
Выход передатчика Pluto+ при усилении 0 дБ около +7 дБм
Предельно допустимый уровень на входе приёмника около +2,5 дБм
Ослабление аттенюатора 30 дБ
Уровень на входе приёмника при усилении 0 дБ около 23 дБм

Вывод: с аттенюатором в разрыве весь диапазон tx_hardwaregain_chan0 от 89 до 0 дБ безопасен для приёмника. Запас до порога повреждения составляет более 25 дБ даже в самом худшем случае.

Порядок подбора усиления:

  1. Начать с tx_hardwaregain_chan0 = -30. Это даёт около 53 дБм на входе приёмника — заведомо чистый и заведомо безопасный уровень.
  2. Если сигнал слишком слаб, поднимать шагами по 10 дБ.
  3. Если созвездие разваливается при сильном сигнале — это насыщение приёмника, лечится уменьшением усиления, а не увеличением.

Без аттенюатора работать запрещено даже при малом усилении: защита нужна не от расчётного уровня, а от ошибки оператора.


6. Программное окружение: препятствие, которое надо снять первым

Проверка окружения 10 августа 2026 года показала следующее.

Интерпретатор, доступный в командной строке — C:\Users\user\AppData\Local\Programs\Python\Python313\python.exe, версия 3.13.1. В нём отсутствуют pyadi-iio и libiio, то есть работать с Pluto+ из него невозможно. Также отсутствует scipy, который нужен лабораторным Lab020Lab022, Lab024b и Lab025.

При этом Lab024a когда-то отработала с настоящим устройством. Значит, существует второе окружение — по всей видимости, среда Anaconda, — где всё установлено, и запуск шёл оттуда.

Первое действие исполнителя: определить, в каком окружении запускаются лабораторные, и зафиксировать это. Пока это не сделано, невозможно ни повторить Lab024a, ни начать Lab042.

Требуется:

  1. Найти окружение, где установлены pyadi-iio и libiio, и проверить связь с устройством.
  2. Записать в отчёт: путь к интерпретатору, версию Python, версии pyadi-iio и libiio.
  3. Если такого окружения нет — установить 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 Гц, что выше минимума с запасом.

Меняется только степень передискретизации — величина, относящаяся к стыку с устройством, а не к самому сигналу. Форма спектра, скорость и структура кадра остаются теми же, что проверены в Lab018Lab023. Это единственное разрешённое расхождение с 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. Порядок работы

Передающая сторона

  1. Взять исходное изображение из data/raw, привести к профилю из Lab010 так, чтобы JPEG получился не крупнее 5 КБ.
  2. Разбить на фрагменты через protocol/image_fragments.py.
  3. Каждый фрагмент упаковать в пакет с CRC32 через protocol/packet.py.
  4. Каждый пакет обернуть в радиокадр через build_radio_frame.
  5. Промодулировать BPSK и отфильтровать.
  6. Привести к целым числам и передать.

Приёмная сторона

  1. Запустить приём до начала передачи.
  2. Найти кадры через find_radio_frame.
  3. Скорректировать фазу и частоту.
  4. Демодулировать, разобрать кадр, проверить CRC32.
  5. Собрать изображение из уцелевших фрагментов и сохранить на диск.

Приём и передача идут в одном процессе. Приём запускается первым — иначе начало первого кадра теряется безвозвратно.


9. Ловушки, на которых спотыкаются в первый раз

Перечислено то, что почти наверняка будет сделано неправильно без предупреждения.

  1. Циклический буфер передачи. У PlutoSDR буфер передачи по умолчанию повторяется бесконечно. Кажется, что передан один кадр, а передаётся бесконечный поток. Признак: приёмник видит тысячи копий одного кадра, а передача не заканчивается. Буфер надо явно выключить и явно освобождать после передачи.

  2. Масштаб отсчётов. PlutoSDR принимает целые числа, а не дробные значения от минус единицы до единицы. Готовый сигнал надо умножить на масштабный множитель, обычно 2 в степени 14. Признак ошибки: передаётся почти тишина, приёмник ничего не находит.

  3. Приём запущен после передачи. Начало кадра теряется, преамбула не находится. Признак: find_radio_frame не срабатывает при заведомо исправном сигнале.

  4. Размер приёмного буфера меньше кадра. Кадр не помещается целиком, поиск преамбулы срывается. Буфер должен вмещать кадр с запасом минимум вдвое.

  5. Автоматическая регулировка усиления приёмника. Меняет амплитуду в процессе и портит оценку. Режим должен быть ручным.

  6. Насыщение приёмника. При слишком слабом ослаблении сигнал упирается в потолок и созвездие разваливается, хотя уровень кажется отличным. Признак: демодуляция не работает при очень сильном сигнале. Лечится увеличением ослабления, а не усилением.

  7. Переходный процесс в начале передачи. Первые несколько десятков отсчётов недостоверны. Поэтому в тракте есть защитный интервал в 16 символов — его нельзя убирать.


10. Что считается выполненной работой

Лабораторная принимается, если выполнено всё перечисленное.

Обязательный результат

  1. Файл принятого изображения на диске, открывающийся просмотрщиком.
  2. Принятое изображение совпадает с переданным при отсутствии потерь — сравнение по контрольной сумме всего файла.
  3. Работа воспроизводится: два запуска подряд дают одинаковый успешный результат.

Обязательные измерения

Величина Как получить
Размер переданного 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. Порядок приёмки

  1. Исполнитель выполняет работу и сообщает о готовности.
  2. Проверяется python tools/quick_gate.py — должен проходить.
  3. Проводится аудит: соответствие разделу 10, отсутствие вписанных результатов, происхождение каждого числа в отчёте.
  4. При наличии замечаний они оформляются списком с указанием файла и строки.
  5. После приёмки в PROJECT_LOG.md добавляется новая запись по принятому в проекте образцу.

13. Что дальше

  • Lab043 — разведение на два устройства: передаёт PlutoSDR, принимает RTL-SDR. Появляется настоящий уход несущей частоты и расхождение часов. Здесь понадобятся Lab021Lab023.
  • Lab044 — выбор рабочего диапазона и первый выход в эфир на малой мощности. Разрешения имеются, поэтому вопрос сводится к техническому выбору диапазона, мощности и антенны.
  • Повышение скорости, исправление стираний и передача последовательности кадров — после того, как картинка устойчиво проходит.