# Оборудование Тип радара задаёт поле `radar.model` профиля, а `radar.driver_mode` — работать с настоящим железом (`"native"`) или с симулятором (`"mock"`). Ключи профиля описаны в главе [Файл конфигурации](configuration.md), принцип измерения — в главе [Как работает радар](principles.md), термины — в [словаре](glossary.md). | Радар | `radar.model` | Что подключается | Процесс сбора | | --- | --- | --- | --- | | Одиночный LibreVNA | `librevna` | один LibreVNA по USB, два ВЧ-ключа на GPIO | `python_app.scripts.matrix_raw_producer` | | Многоприборная схема | `librevna_multi` | три LibreVNA по USB, петля триггера; в режиме переключения стимула — контроллер STM32 и ключи на портах мастера | `python_app.scripts.matrix_raw_producer` | | Радиофотонный радар с АЦП | `rph_radar`, `radar.rph_radar.source: "e502"` | АЦП L-Card E-502 по USB, плата лазеров по последовательному порту, два ВЧ-ключа на GPIO | `python_app.scripts.rph_radar_raw_producer` и программа `rph_radar_collector` | | Радиофотонный радар с платой RFG | `rph_radar`, `radar.rph_radar.source: "rfg_udp"` | плата RFG по Ethernet, плата лазеров по последовательному порту; одна пара антенн | `python_app.scripts.rph_radar_raw_producer` | | Симулятор | `librevna` или `librevna_multi` с `driver_mode: "mock"` | ничего | `python_app.scripts.matrix_raw_producer` | Для радиофотонного радара симулятора нет: он работает только с `driver_mode: "native"`. Процесс сбора открывает приборы и при неудаче повторяет попытки бесконечно, с паузой от 1 до 10 с: прибор можно подключить и после запуска. Его сообщения пишутся в `python_app/runtime/logs/acquisition.err.log`, а в журнал окна попадают, только если процесс завершился. Ошибки сбора и их признаки собраны в главе [Неполадки](troubleshooting.md). ## LibreVNA LibreVNA — двухпортовый векторный анализатор цепей (ВАЦ) с USB. В системе используются платы ревизии B. Прошивки — изменённая версия 1.6.5, протокол версии 14 (программа проверяет версию при подключении одиночного прибора). | Предел | Значение | | --- | --- | | Частота | 0–6 ГГц | | Полоса ПЧ | 6 Гц–50 кГц | | Число точек | до 4501 | | Мощность | от −40 до 0 дБм | | Задержка на стадии `dwell_us` | до 10 239 мкс | Ниже 25 МГц генератор прибора переключается на другую микросхему, и развёртка идёт с неравномерными длинными паузами: если развёртка должна быть быстрой, начинайте её не ниже 30 МГц (так сделано в `run_config_librevna_multi_per_point_lowband.example.json`). С `driver_mode: "native"` программа при запуске окна, загрузке профиля и нажатии **Apply Radar** читает пределы прибора и подрезает поля секции **Radar** под них (`Radar fields were adjusted to satisfy device limits.` в журнале). У многоприборной схемы пределы читаются с мастера. Если прибор с `driver_mode: "native"` не подключён, окно программы не открывается: чтение пределов при старте завершается ошибкой. USB-доступ к прибору даёт правило udev `/etc/udev/rules.d/99-radar-librevna.rules` (группа `plugdev`). Его ставит `./start.sh` при обычном запуске с профилем LibreVNA; при запуске без экрана (`--headless`) правило не ставится. ## Одиночный LibreVNA Прибор подключается по USB; если `radar.serial` пуст, берётся первый найденный. Порт 1 излучает, порт 2 принимает. Выходной ВЧ-ключ (`switches.port1`) подключает порт 1 к одной из передающих антенн, входной (`switches.port2`) — порт 2 к одной из приёмных. Ключами управляет компьютер через GPIO. Сбор одной коллекции: 1. Для каждой пары из списка сбора программа выставляет выходной ключ, затем входной, и ждёт время установления `run.settling_ms`. 2. Запускает развёртку и принимает все точки. Если какая-то точка не пришла, коллекция не публикуется, а прибор открывается заново. 3. Публикует коллекцию в кольцо `raw` и начинает следующую. S21 одиночного прибора — отношение сигнала порта 2 к сигналу опорного приёмника. **Apply Radar** открывает прибор, отправляет настройки развёртки и закрывает его. ## Многоприборная схема Три LibreVNA: мастер (`radar.serial`) и два ведомых (`radar.multi_device.slave_serials`, ровно два). Мастер на каждой частоте излучает по очереди с порта 1 и порта 2, ведомые не излучают и принимают каждый на оба порта. S21 — сигнал приёмника ведомого, делённый на сигнал опорного приёмника мастера на той же стадии. | Номер | Что это | | --- | --- | | вход 0, 1 | первый ведомый из `slave_serials`, порт 1 и порт 2 | | вход 2, 3 | второй ведомый, порт 1 и порт 2 | | выход 0, 1 (без переключения стимула) | мастер, порт 1 и порт 2 | | выход 0, 1, 2, 3 (переключение стимула, два положения) | порт 1 положение A, порт 1 положение B, порт 2 положение A, порт 2 положение B | Номер выхода совпадает с номером стадии прибора. Многоприборная схема всегда снимает полную матрицу: список пар `run.combos` у неё заменяется всеми парами, и в окне он не редактируется. Приборы синхронизированы аппаратным сигналом запуска. Петля триггера: `TRIGGER_OUT` мастера → первый ведомый → второй ведомый → `TRIGGER_IN` мастера. Вернувшийся сигнал означает «все готовы». Мастер измеряет по вернувшемуся сигналу, поэтому задержки в петле одинаковы для всех приборов и в отношении «приёмник/опорный» сокращаются. Опорная частота: поле `radar.multi_device.force_external_reference` включает внешнюю опору 10 МГц сразу на всех трёх приборах (раздельно задать нельзя). Во всех профилях оно выключено. Включайте его, только если опора 10 МГц действительно подана на все приборы. Признак отсутствующей опоры при включённом поле — мастер не отдаёт точки: `Packet not received from <серийный номер> within 1.0 s`. Сбор: перед первой коллекцией программа настраивает опору на всех приборах, затем отправляет настройки развёртки сначала ведомым, потом мастеру, чтобы приёмники были готовы раньше, чем мастер выдаст запуск. Развёртка идёт непрерывно. Каждый прибор передаёт свои точки по отдельному USB; программа ждёт начала развёртки на всех трёх и принимает цикл, только если начала совпали по времени. Если пропало не больше 5 % точек трассы, они заменяются нулями (`Zero-filled dropped sweep point(s) to keep the cycle…`), иначе цикл отбрасывается. | Сбой | Предел | | --- | --- | | от прибора нет ни одной точки | 1,5 с | | нет начала развёртки | 6 с | | цикл не закончился | 8 с | | повторные попытки без переподключения | 2 | | переподключение всех приборов | `radar.multi_device.recovery_attempts` раз (по умолчанию 3), не дольше 10 с | После этого процесс сбора пересоздаёт подключение и ждёт приборы бесконечно. Если не пришло ни одной точки ни от одного прибора, в журнале процесса сбора будет `No datapoints received from at least one device; hardware trigger sync did not start. Check Trigger Out/In loop and 10 MHz reference wiring.` **Apply Radar** в этой схеме приборы не трогает: их настраивает процесс сбора (`Multi-device raw producer will configure all LibreVNA devices`), кнопка только перечитывает пределы мастера. Прошивки: мастер работает с образом `combined.vnafw` (обычная раскладка стадий с аппаратным триггером) или `combined_per_point_switching.vnafw` (переключение стимула), ведомые — с `combined_external_lo_slave.vnafw`. Образы и правила их сборки — в разделе «Прошивки» ниже. ## Переключение стимула внутри частотной точки В этом режиме одна развёртка даёт матрицу 4 × 4 вместо 2 × 4: на каждом порту мастера стоит ВЧ-ключ, и на каждой частоте каждый порт излучает дважды, через оба положения ключа. Все 16 пар одной частоты измеряются за доли миллисекунды, поэтому движение радара между ними пренебрежимо мало. Полное описание — [per_point_switching.md](../device_firmware/per_point_switching.md). Что нужно: - прошить мастер образом `combined_per_point_switching.vnafw`; ведомые не трогать; - поставить контроллер ключей (плата WeAct STM32G431CB с прошивкой `device_firmware/switch_controller`) в разрыв петли триггера между `TRIGGER_OUT` мастера и `TRIGGER_IN` первого ведомого; - в профиле задать `radar.multi_device.stimulus_switch_positions: 2` и `radar.sweep.dwell_us: 2`. Ключами на портах мастера управляет контроллер, а не компьютер; разделы `switches` у многоприборной схемы программа сама ставит в `mock`. | Вывод STM32 | Куда | | --- | --- | | PB0 (вход) | `TRIGGER_OUT` мастера | | PB1 (выход, повторяет PB0) | `TRIGGER_IN` первого ведомого | | PB2 | ключ порта 1 мастера | | PB6 | ключ порта 2 мастера | | PB5 | отладка: импульс 2 мкс на каждую поправку чётности | Контроллер переключает оба ключа по каждому спаду сигнала запуска (положение A — логический 0, B — 1). Если пауза между стадиями в 1,5 раза длиннее наименьшей из последних 16 пауз, то есть на границе развёртки, он принудительно возвращает положение A и считает это поправкой чётности. Задержка, которую он вносит в петлю (около 50 нс), одинакова для всех приборов и на фазу не влияет. Через 200 мс простоя триггера контроллер печатает отчёт по USB (115 200 бод): `trigger cycles`, `resyncs (sweep ends)`, `parity corrections` (должно быть `0 (ok)`), `stages per resync` (ожидается число точек × число стадий), паузы и их гистограмма. Проверка на железе — `python_app/scripts/per_point_switching_check.py --config <профиль>`: она отказывает, если медиана модуля S21 какого-то выхода ниже −80 дБ (признак старой прошивки мастера) или если два положения одного порта без подключённого ключа различаются больше чем на 6 дБ. С ключом нужен флаг `--switch-connected`. > **Внимание.** Полоса ПЧ 1 кГц в этом режиме воспроизводимо роняет приборы > (`LIBUSB_ERROR_IO`). Работают 5, 10, 20 и 50 кГц; рабочее значение — 50 кГц. ## ВЧ-ключи H7992 Ключ подключает порт прибора к одной из до четырёх антенн. Положение задают две линии GPIO, A (`pin_a`) и B (`pin_b`); линии нужны обе даже при двух положениях. | Положение | A | B | | --- | --- | --- | | 0 | 0 | 0 | | 1 | 0 | 1 | | 2 | 1 | 0 | | 3 | 1 | 1 | Номера линий в профилях — номера линий GPIO-чипа (`gpio_chip`, обычно `/dev/gpiochip0`), а не номера контактов разъёма. Во всех профилях-примерах, кроме примера с платой RFG (там ключей нет), выходной ключ на линиях 17 (A) и 27 (B), входной — на 22 (A) и 23 (B). При открытии программа выставляет ключ в `default_position`. Ключ с `driver_mode: "mock"` никуда не переключается, программа только помнит его положение. Кто управляет ключами: | Радар | Кто переключает | | --- | --- | | одиночный LibreVNA | процесс сбора на Python | | многоприборная схема | процесс сбора, только если `multi_device.output_switch_positions` или `input_switch_positions` больше 1; в режиме переключения стимула — контроллер STM32 | | радиофотонный с E-502 | программа `rph_radar_collector`, если оба ключа `native`; иначе процесс сбора | > **Внимание.** На Raspberry Pi 5 номер GPIO-чипа 40-контактного разъёма менялся от > версии ядра к версии. Перед работой проверьте его на самой машине: `gpiodetect` и > `gpioinfo -c gpiochip0`. Неверный чип означает ключи, которые молча не > переключаются. ## Радиофотонный радар Плата лазеров меняет ток лазера LD1 ступенями, при этом ток второго лазера и температуры обоих постоянны. На каждой ступени оцифровываются основной сигнал и опорный. Опорное плечо имеет постоянную задержку, поэтому его фаза растёт с частотой линейно; по двум опорным точкам `phase_calibration` (фаза — частота) программа определяет частоту каждой ступени. S21 — основной сигнал, делённый на модуль опорного. Развёртки, которые не покрывают выходную полосу `radar.rph_radar.band`, отбрасываются, остальные пересчитываются на её равномерную сетку (по умолчанию 2,1–5,5 ГГц, 2048 точек). Поля `radar.sweep` для этого радара не используются, в окне вместо секции LibreVNA показывается секция RPH. ### Плата лазеров Плата подключается по последовательному порту (115 200 бод) и программируется кнопкой **Apply Radar**, если в профиле `laser_control.enabled: true`. Программа проверяет все значения до отправки первого байта, затем сбрасывает плату и отправляет постоянные токи и температуры, а в режиме `variation` — ещё задачу развёртки тока LD1. Каждая команда должна получить ответ «состояние 0»; иначе `Device rejected command: STATE 0x…`. После закрытия порта развёртка лазера продолжается. | Величина | Пределы | По умолчанию | | --- | --- | --- | | температура лазеров | 15–40 °C | 24,0 и 24,9 °C | | ток лазеров | 15–110 мА | 97,3 и 102,0 мА | | развёртка тока LD1 | 15–110 мА, начало меньше конца | 97,3 → 107,0 мА | | шаг тока | 0,002–0,5 мА | 0,05 мА | | длительность ступени | 20–100 мкс | 50 мкс | | пауза между развёртками | 3–10 мс | 10 мс | При запуске без экрана **Apply Radar** выполняется автоматически через 0,5 с после старта, а через 1 с после него — **Start**, даже если **Apply Radar** не удался. В веб-интерфейсе **Apply Radar** нет. ### АЦП L-Card E-502 Данные с АЦП читает отдельная программа `build/bin/rph_radar_collector` (собирается командой `make rph_radar_collector`, в обычный `make` не входит). Её запускает и останавливает процесс сбора; программа живёт, только пока жив её запустивший процесс. Библиотеки L-Card `libx502api.so` и `libe502api.so` она загружает при запуске, по умолчанию из `~/.local/lib`. Развёртки она передаёт процессу сбора через псевдотерминал `rph_radar.tty_path` (в примере `/tmp/ttyADC_data`). Устройство выбирается первое найденное. Подробности — в [README коллектора](../data_acq_and_processing/rph_radar_collector/README.md). Параметры коллектора задаются списком `radar.rph_radar.args`: | Параметр | Что задаёт | По умолчанию | | --- | --- | --- | | `range:<В>` | диапазон входа ±В: `10`, `5`, `2`, `1`, `0.5` или `0.2` | ±2 В | | `internal_ref_hz:<Гц>` | внутренняя опорная частота, она же наибольшая частота АЦП: `2000000` или `1500000` | 2 МГц | | `duration_ms:<мс>` | наибольшая длительность развёртки, больше 0 и не больше 60 000 мс; более длинная считается испорченной | 100 мс | | `do8_cycle_period:` | период выхода DO8 в словах цифрового вывода, 4–262144, причём НОК(4, n) не больше 262144 (нечётные n больше 65 536 не подходят) | 8 | Остальные слова в `args` (`profile:phase`, `clock:internal`, `start:di_syn2_rise`, `stop:di_syn2_fall`, `sample_clock_hz:max`, `packet_limit:0`, `do1_toggle_per_frame`, `do1_pair_subtract_avg`, `do8_freq_ref`) только подтверждают единственный поддерживаемый режим; любое другое слово отвергается. Как коллектор использует цифровые линии АЦП: | Линия | Что делает коллектор | | --- | --- | | DI_SYN2 | фронт открывает развёртку, спад закрывает | | DI1 | смена уровня отмечает новую ступень | | DI2 | делит отсчёты ступени на две группы; значение ступени — среднее разности групп | | DI8 | отсчёты с высоким DI8 идут в опорный канал | | DO1 | меняется на каждом кадре | | DO2 | высокий, пока идёт поток | | DO8 | высокий на двух последних словах каждого периода `do8_cycle_period` | Если ключами управляет коллектор, он переключает их сам между развёртками, ждёт `run.settling_ms` (не меньше 1 мс) и открывает новый сеанс АЦП; первая развёртка после переключения отбрасывается. ### Плата RFG Плата RFG шлёт по UDP точки развёртки на адрес `radar.rph_radar.udp_bind`. В примере это проводной адрес компьютера `192.168.2.1:5000`: привязка к проводному интерфейсу отсекает чужие широковещательные пакеты (например, из Wi-Fi), а любой непонятный пакет программа считает ошибкой. В каждой точке три канала; основной сигнал — разность каналов 0 и 1 (синхронный детектор), опорный — канал 2. Требования профиля: - `laser_control.mode: "variation"` — даже если лазер запускают из другой программы: по его настройкам вычисляется длина развёртки; - у обоих ключей одно положение, то есть одна пара антенн: без метки от источника нельзя доказать, что развёртка после переключения свежая. Длина развёртки — ⌈(конец − начало) / шаг⌉ ступеней в кодах ЦАП тока; при настройках по умолчанию 199 ступеней по 50 мкс. Развёртка принимается, только если пришли все ступени подряд. Плата теряет несколько процентов точек, такие развёртки отбрасываются. Время каждой точки — метка ядра о приёме пакета. ### Фазовая калибровка Фазовые якоря `radar.rph_radar.phase_calibration` измеряются программой `python_app/scripts/rph_radar_calibrate.py`. Перед ней примените настройки лазера кнопкой **Apply Radar** и остановите сбор. ```sh .venv/bin/python -m python_app.scripts.rph_radar_calibrate --config run_config.json --sweeps 200 .venv/bin/python -m python_app.scripts.rph_radar_calibrate --config run_config.json --sweeps 200 --apply ``` Первая команда снимает развёртки и печатает измеренные фазы начала и конца, вторая снимает заново и записывает в профиль только `phase0_rad` и `phase1_rad`, не трогая остальные байты файла. Частотные якоря `freq0_hz` и `freq1_hz` должны заранее описывать известные концы развёртки. `--warmup` задаёт, сколько первых развёрток отбросить (по умолчанию 10). Команды лазеру программа не отправляет. Если профиль изменился во время съёма, она его не перезаписывает. ## Симулятор С `driver_mode: "mock"` процесс сбора не трогает железо и моделирует один точечный отражатель. Отражатель движется по восьмисекундному циклу: вдоль ряда антенн — вокруг его середины (размах — половина ширины ряда плюс 0,25 м, но не меньше 0,75 м), по дальности — между 3,25 и 4,25 м, на высоте 0,25 м. S21 каждой пары считается по настоящим расстояниям от антенн из профиля; коллекции идут не чаще чем раз в 50 мс. Шума, помех, переключения ключей и движения радара симулятор не моделирует; скорость радара при работе с ним должна быть нулевой. Предобработке по-прежнему нужны наборы; для симулятора подходят нейтральные (глава [Калибровка и предобработка](preprocessing.md)). ## GPIO-кнопка К компьютеру можно подключить кнопку, которая снимает временный референс — то же, что **Capture Temporary Reference**. По умолчанию она выключена (раздел `control_button` профиля, ни в одном примере не включена). Схема, на которую рассчитаны умолчания: кнопка между линией GPIO и землёй, внутренняя подтяжка к питанию, нажатие — спад уровня. Дребезг подавляет ядро (`debounce_ms`, по умолчанию 50 мс). Настройки кнопки читаются при запуске окна; после загрузки другого профиля нужно перезапустить программу. ## Тележка `device_firmware/cart_firmware` — прошивка платы WeAct STM32G431CB, которая заменяет пульт тележки: читает джойстик и выдаёт команды движения в формате SBUS. С программой радара она не связана. Скорость для поправки на движение радар получает от клиента TCP-локатора (глава [Веб-интерфейс, кнопка и локатор](web.md)). Протокол пульта — [protocol.md](../device_firmware/cart_firmware/docs/protocol.md). ## Прошивки | Где | Что | На чём работает | | --- | --- | --- | | `device_firmware/multi_libreVNA_firmware/combined.vnafw` | образ мастера: обычные стадии и аппаратный триггер | LibreVNA-мастер | | `device_firmware/multi_libreVNA_firmware/combined_per_point_switching.vnafw` | образ мастера с переключением стимула | LibreVNA-мастер | | `device_firmware/multi_libreVNA_firmware/combined_external_lo_slave.vnafw` | образ ведомого | LibreVNA-ведомые | | `device_firmware/multi_libreVNA_firmware/reproduce/` | полное дерево LibreVNA 1.6.5 с доработками и комплект воспроизводимой сборки | ПК разработчика | | `device_firmware/switch_controller/` | контроллер ключей для переключения стимула | WeAct STM32G431CB | | `device_firmware/cart_firmware/` | пульт тележки | WeAct STM32G431CB | Образ `.vnafw` заливается в LibreVNA программой LibreVNA-GUI (Device → Firmware Update). Платы STM32 прошиваются через PlatformIO и ST-Link: `pio run -t upload`. > **Внимание.** `top_master.bin` и `combined.vnafw` — единственные копии рабочего > битстрима мастера и рабочего образа прошивки, битстрим заново не воспроизводится. > Никогда их не перезаписывайте; новые сборки сохраняйте под новыми именами. Подробно: - [reproduce/README.md](../device_firmware/multi_libreVNA_firmware/reproduce/README.md) — как устроен комплект сборки, отличия от LibreVNA 1.6.5, что воспроизводится побайтово; - [install_windows/README.txt](../device_firmware/multi_libreVNA_firmware/reproduce/install_windows/README.txt) — сборка мастера с переключением стимула на Windows; - [per_point_switching.md](../device_firmware/per_point_switching.md) — переключение стимула: раскладка стадий, изменения FPGA и микроконтроллера, порядок ввода в строй; - [switch_controller/README.md](../device_firmware/switch_controller/README.md) — контроллер ключей: разводка, отчёт, отладка; - [librevna_protocol.md](../device_firmware/librevna_protocol.md) — USB-протокол LibreVNA и форматы кадров между процессами программы.