Files
SDR-Rover/docs/lab042_hardware_loopback_spec.md
LittleSam129 49c7009ca5 Lab042: drop the regulatory framing, keep the technical reasons
The project owner holds the permits for over-the-air work, so legality is
not a constraint this document should be reasoning about.

- retitle the section from "safety requirements" to what it actually
  covers: circuit integrity and not destroying the hardware
- rewrite the antenna rule on its real technical ground. An antenna opens
  a radiating path in parallel with the cable, so the level at the
  receiver stops matching the budget and reflections appear; the value of
  a cable loopback is that everything in it is known
- reframe "no over-the-air" as a scope boundary against Lab044 rather
  than a prohibition
- note in the roadmap that Lab044 is purely a technical choice of band,
  power and antenna

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 13:34:07 +03:00

271 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lab042 — техническое задание
## Передача JPEG через настоящий радиотракт: кабельная петля на одном PlutoSDR
Документ предназначен для исполнителя, не участвовавшего в предыдущих лабораторных. Всё, что нужно для работы, изложено здесь.
---
## 1. Зачем эта лабораторная
За Lab001Lab041 собран весь тракт передачи изображения, кроме одного звена: **передатчик ни разу не включался**. Во всём проекте нет ни одного вызова передачи через SDR.
Цель Lab042 — соединить уже готовые части и получить **изображение, прошедшее через настоящий радиотракт**. Не улучшить, не оптимизировать, не измерить дальность. Получить картинку на выходе.
Всё, что не требуется для этой цели, в Lab042 не делается.
---
## 2. Что уже готово и должно быть переиспользовано
Ничего из перечисленного писать заново не нужно.
| Звено | Где взять |
|---|---|
| Разбиение JPEG на фрагменты и сборка обратно | `protocol/image_fragments.py`, примеры в `tests/lab007_image_fragmentation.py` |
| Пакет с CRC32, кодирование и разбор | `protocol/packet.py` |
| Подбор размера и качества JPEG | `tests/lab010_image_optimization.py`, готовые профили в `data/processed/lab010` |
| Формирование радиокадра с преамбулой и маркером | `build_radio_frame` в `tests/lab018_bpsk_radio_frame.py` |
| Фильтр приподнятого косинуса | `root_raised_cosine_taps` там же |
| Модуляция BPSK | `bpsk_modulate` там же |
| Поиск кадра в потоке отсчётов | `find_radio_frame` в `tests/lab019_bpsk_receiver.py` |
| Оценка и коррекция ухода частоты | `estimate_carrier_parameters`, `correct_phase_and_frequency` там же |
| Демодуляция и разбор кадра | `bpsk_demodulate`, `decode_radio_frame` там же |
| Работа с Pluto на приём | `tests/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 используется только первый канал каждого;
- класс `adi.Pluto` в Lab024a отработал на приёме. Начинать с него. Если передача на этом классе не пойдёт из-за двухканальности, перейти на `adi.ad9361` с тем же адресом. Это единственное разрешённое отступление от задания, и о нём нужно сообщить.
## 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 |
| Отсчётов на символ | 32 | `SAMPLES_PER_SYMBOL`, Lab018 |
| Частота дискретизации | 640 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 |
Полезная скорость получается около 20 кбит/с. Для картинки в несколько килобайт это единицы секунд. Этого достаточно: цель — картинка, а не скорость.
### Параметры PlutoSDR
| Параметр | Значение | Примечание |
|---|---|---|
| Адрес устройства | `ip:192.168.2.1` | как в Lab024a |
| Несущая передачи и приёма | 435 МГц | обе одинаковые |
| Частота дискретизации | 640 000 | совпадает с трактом |
| Полоса радиочастотного тракта | 200 000 Гц | минимум Pluto, сигнал занимает около 27 кГц |
| Усиление передатчика | начать с 30 дБ | с аттенюатором безопасен весь диапазон, см. раздел 5 |
| Режим усиления приёмника | `manual` | автоматика мешает измерениям |
| Усиление приёмника | начать с 20 дБ | подобрать по амплитуде |
| Циклический буфер передачи | **выключен** | см. раздел 7 |
Про несущую: 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.
- Никаких вписанных руками результатов: каждое число в отчёте должно вычисляться из измерений текущего запуска. Это отдельно проверяется при приёмке.
- Файл лабораторной: `tests/lab042_pluto_image_loopback.py`, запуск через `python -m tests.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** — выбор рабочего диапазона и первый выход в эфир на малой мощности. Разрешения имеются, поэтому вопрос сводится к техническому выбору диапазона, мощности и антенны.
- Повышение скорости, исправление стираний и передача последовательности кадров — после того, как картинка устойчиво проходит.