added docs

This commit is contained in:
Ayzen
2026-10-16 18:35:02 +03:00
parent b88d704126
commit ad2b3e555a
19 changed files with 3719 additions and 663 deletions
+428
View File
@@ -0,0 +1,428 @@
# Файл конфигурации
Все настройки программы хранятся в профиле — файле JSON. В профиле две части:
конфигурация сбора и обработки (разделы `radar`, `switches`, `control_button`,
`logging`, `run`, `preprocess`, `gpr`, `rings`) и состояние окна (раздел `gui`).
Незнакомые термины (полоса ПЧ, дБм, пара, коллекция) — в [словаре](glossary.md).
| Где | Что |
| --- | --- |
| `run_config.json` в корне | профиль для разработки: симулятор многоприборной схемы, `driver_mode: "mock"`, уровень журнала `debug` |
| `run_config_examples/` | готовые профили под каждое железо (таблица ниже) |
| `run_configs/` | профили, которые можно загрузить из веб-интерфейса |
| `python_app/runtime/run_config.json` | рабочая копия, которую программа пишет при каждом **Start**; по ней работают процессы конвейера |
> **Внимание.** Корневой `run_config.json` — профиль симулятора: с ним программа не
> обращается к железу. На машине с радаром работайте с профилем из
> `run_config_examples/`, скопированным и настроенным под установку.
## Как профиль загружается и сохраняется
При запуске программа берёт профиль по порядку:
1. путь из переменной окружения `RADAR_SYSTEM_PROFILE` (её задаёт
`./start.sh --profile ПУТЬ`);
2. последний использованный профиль из `python_app/runtime/gui_session_state.json`;
3. корневой `run_config.json`.
Относительный путь отсчитывается от каталога проекта. Если выбранный профиль не
читается, программа пишет
`Failed to load the last selected config profile; falling back to root run_config.json.`
и загружает корневой; если не читается файл сеанса —
`Failed to read GUI session-state; using root run_config.json.` Если не читается и
корневой профиль, программа не запускается. Профиль без раздела `gui` загружается со
значениями окна по умолчанию; при запуске (кроме корневого профиля) об этом пишется
`Loaded legacy run config without GUI defaults: <путь>`, при **Load Config…** —
`kind=legacy run config`
в строке `Config profile loaded: …`.
**Load Config…** загружает другой профиль; это возможно только при остановленном
сборе (`Stop all pipeline processes before loading a config profile`) и не во время
съёма наборов.
**Save Config…** записывает текущие значения всех полей окна, выбранные наборы и
геометрию антенн. Загруженный или сохранённый профиль становится стартовым для
следующего запуска.
> **Внимание.** Изменения в окне и в веб-интерфейсе, в том числе выбор наборов и
> временный референс, в профиль сами не записываются. После перезапуска программы они
> пропадут, если не нажать **Save Config…**.
Правила чтения:
- нет раздела или ключа — берётся значение по умолчанию. Умолчания схемы — минимальные
заглушки (`points: 1`, `if_bandwidth_hz: 1.0`, пустые имена колец), а не рабочие
настройки: начинайте с профиля-примера;
- типы строгие: целое число нельзя записать как `5.0`, логическое — как `1`; ошибка
называет ключ (`points must be a JSON integer`);
- неизвестные ключи пропускаются и при сохранении исчезают;
- раздел `gui` с неизвестным номером версии отвергается (`Unsupported gui.version: N`).
## Когда изменение вступает в силу
| Обозначение | Когда |
| --- | --- |
| сразу | сразу, в том числе во время сбора |
| Apply Radar | при нажатии **Apply Radar** (без экрана — автоматически при запуске) |
| Start | при следующем **Start** или **Single Capture**: программа заново пишет рабочую копию профиля и перезапускает процессы сбора и предобработки |
| Start, процессор | как Start, но перезапускается и процессор, а с ним TCP-локатор: его клиенты отключаются, история обработки очищается |
| профиль | ключа нет в окне: правьте файл и загрузите его через **Load Config…** при остановленном сборе |
Процессор перезапускается, если изменились: ключ радара (`radar_key`), опорная частота
многоприборной схемы, `driver_mode` радара и ключей, число положений ключей, список
пар, любой ключ раздела `rings`, любой ключ `run.locator_server`, путь к live-файлу
или `run.idle_sleep_ms`. Смена наборов предобработки процессор не перезапускает
(исключение — нейтральные наборы).
## Ключ радара
`radar_key` — строка, по которой программа находит наборы калибровки и референса:
они лежат в каталогах `python_app/data/<вид набора>/<radar_key>/`. Смена любой
величины, из которой строится ключ, скрывает наборы, снятые при старых значениях.
| Радар | Из чего строится | Пример (из профилей-примеров) |
| --- | --- | --- |
| `librevna` | модель, серийный номер (`no_serial`, если пуст), начало и конец развёртки, число точек, полоса ПЧ, мощность | `librevna_no_serial_st1000000_sp6000000000_p201_if50000_pw-10` |
| `librevna_multi` | то же для мастера и обоих ведомых, плюс положения ключей `_out`, `_in`, `_stim` | `librevna_multi_209E307A5532_20A1307D5532_2072306C5532_st500000000_sp6000000000_p1031_if50000_pw-3_out1_in1_stim2` |
| `rph_radar` | первые 16 знаков хеша SHA-256 от выходной полосы, фазовой калибровки, параметров коллектора (`args`) или источника `rfg_udp`, числа положений ключей и настроек лазера (если плата лазеров включена или источник — RFG) | `rph_radar_2afdaa32e154b122` |
`dwell_us`, путь к программам и тайм-ауты в ключ не входят.
## Профили-примеры
| Файл | Радар | Что в нём |
| --- | --- | --- |
| `run_config.json` (корневой) | `librevna_multi`, `mock` | симулятор схемы с переключением стимула: 16 пар, 1 МГц–6 ГГц, 201 точка, вертикальная база антенн 0,445 м, журнал `debug` |
| `run_config_librevna.example.json` | `librevna`, `native` | одиночный LibreVNA, выходной ключ на 2 положения (линии 17, 27), входной на 4 (линии 22, 23), 8 пар, 1 МГц–6 ГГц, 201 точка |
| `run_config_librevna_multi.example.json` | `librevna_multi`, `native` | приборы `209E307A5532` (мастер), `20A1307D5532`, `2072306C5532`; 8 пар, 1 МГц–6 ГГц, 201 точка |
| `run_config_librevna_multi.example2.json` | `librevna_multi`, `native` | то же для второго комплекта: `206830985532`, `206930965532`, `206930A15532` |
| `run_config_librevna_multi_per_point.example.json` | `librevna_multi`, `native` | переключение стимула, комплект `209E307A5532`; 16 пар, 0,5–6 ГГц, 1031 точка, −3 дБм, `dwell_us: 2`, измеренная геометрия антенн; режим `sar3d` |
| `run_config_librevna_multi_per_point_lowband.example.json` | `librevna_multi`, `native` | переключение стимула, комплект `206830985532`; 30–200 МГц, 201 точка — проверка ключей на низких частотах; у антенн y = z = 0, Tx2 и Tx3 совпадают с Tx0 и Tx1 |
| `run_config_rph_radar.example.json` | `rph_radar`, `e502` | АЦП E-502, плата лазеров на `/dev/ttyUSB0` в режиме `variation`, ключи на 2 и 4 положения, 8 пар |
| `run_config_rph_radar_rfg.example.json` | `rph_radar`, `rfg_udp` | плата RFG на `192.168.2.1:5000`, плата лазеров на `/dev/serial/by-id/usb-Silicon_Labs_CP2102_…`, одна пара, измеренные фазовые якоря |
| `run_config_simulator.example.json` | `librevna`, `mock` | симулятор одиночного LibreVNA: 8 пар, 1 МГц–6 ГГц, 201 точка; режим `gpr` |
`run_configs/run_config.json` — профиль симулятора одиночного LibreVNA для
веб-интерфейса, близкий к `run_config_simulator.example.json`.
## radar
| Ключ | Тип, по умолчанию | Что делает | Когда |
| --- | --- | --- | --- |
| `radar.model` | строка, `"librevna"` | тип радара: `"librevna"`, `"librevna_multi"` или `"rph_radar"`. Старое имя `"kamil_adc"` отвергается с подсказкой перейти на `rph_radar` | профиль; Start, процессор |
| `radar.serial` | строка, `""` | серийный номер прибора; у многоприборной схемы — мастера (обязателен). Пустой у одиночного — первый найденный прибор | профиль; Start, процессор |
| `radar.driver_mode` | строка, `"mock"` | `"native"` — настоящее железо, `"mock"` — симулятор. Радиофотонному радару нужен `"native"` | профиль; Start, процессор |
### radar.sweep
Развёртка LibreVNA. Радиофотонный радар эти ключи не использует (у него
`radar.rph_radar.band`), а `points` у него в профиль не записывается. Поля редактируются в секции
**Radar**; с подключённым прибором они подрезаются под его пределы.
| Ключ | Тип, по умолчанию | Поле | Что делает | Ключ радара |
| --- | --- | --- | --- | --- |
| `start_hz` | число, `1000000.0` | **Start (Hz)** | начало развёртки, Гц, не меньше 0 | да |
| `stop_hz` | число, `6000000000.0` | **Stop (Hz)** | конец развёртки, Гц, больше начала | да |
| `points` | целое, `1` | **Points** | число частотных точек | да |
| `if_bandwidth_hz` | число, `1.0` | **IF bandwidth (Hz)** | полоса ПЧ, Гц, больше 0 | да |
| `stimulus_power_dbm` | число, `-30.0` | **Stimulus power (dBm)** | мощность излучения, дБм | да |
| `dwell_us` | целое, `0` | — | дополнительная задержка на каждой стадии прибора, мкс; при отправке значение молча ограничивается диапазоном 0–65535, а прибор — 10 239 мкс. В режиме переключения стимула в это время контроллер переключает ключ; в примерах `2` | нет |
Изменения применяются при **Start**; у одиночного LibreVNA — ещё и при
**Apply Radar**.
### radar.multi_device
Только для `librevna_multi`; другие радары эти ключи игнорируют. В окне не
редактируются.
| Ключ | Тип, по умолчанию | Что делает |
| --- | --- | --- |
| `slave_serials` | список строк, `[]` | серийные номера двух ведомых, ровно два. Первый даёт входы 0 и 1, второй — 2 и 3; порядок важен. Можно записать строкой через запятую; старое имя `slave_serial_numbers` тоже читается |
| `force_external_reference` | логическое, `false` | внешняя опорная частота 10 МГц на всех трёх приборах. Включайте, только если опора подана на все приборы (глава [Оборудование](hardware.md)) |
| `recovery_attempts` | целое, `3` | сколько раз переподключать приборы после сбоя, прежде чем передать ошибку процессу сбора |
| `output_switch_positions` | целое, `1` | положения настоящего ВЧ-ключа на выходах мастера, которым управляет компьютер (линии из `switches.port1`). 1 — ключа нет. Ни один пример его не использует |
| `input_switch_positions` | целое, `1` | то же для входов ведомых (линии из `switches.port2`) |
| `stimulus_switch_positions` | целое, `1` | положения ключа переключения стимула внутри частотной точки; `2` — режим переключения стимула. Прошивка мастера поддерживает 1, 2 и 4, контроллер STM32 — 2 |
Все ключи, кроме `recovery_attempts`, входят в ключ радара или в условие перезапуска
процессора.
### radar.rph_radar
Только для `rph_radar`. Поля без пометки «—» редактируются в секции **Radar**.
| Ключ | Тип, по умолчанию | Поле | Что делает |
| --- | --- | --- | --- |
| `source` | строка, `"e502"` | — | источник: `"e502"` — АЦП L-Card через `rph_radar_collector`, `"rfg_udp"` — плата RFG по UDP |
| `udp_bind` | строка, `"0.0.0.0:5000"` | — | только RFG: адрес `IPv4:порт`, на котором принимаются пакеты |
| `project_dir` | строка, `""` | **Project dir** | рабочий каталог коллектора; пусто — каталог проекта |
| `executable_path` | строка, `""` | **Executable path** | путь к коллектору, обычно `build/bin/rph_radar_collector` |
| `tty_path` | строка, `""` | **TTY path** | путь псевдотерминала, через который коллектор передаёт развёртки; старый файл по этому пути удаляется перед запуском |
| `args` | список строк, `[]` | **Arguments** (по строке на параметр) | параметры коллектора (глава [Оборудование](hardware.md)); `tty:` здесь задавать нельзя |
| `env` | объект строк, `{}` | **Environment JSON** | переменные окружения коллектора; `~` и `$ПЕРЕМЕННАЯ` раскрываются. Если `env` не задаёт `LD_LIBRARY_PATH`, в начало унаследованного `LD_LIBRARY_PATH` добавляется `~/.local/lib` |
| `startup_timeout_s` | число, `5.0` | **Startup timeout (s)** | сколько ждать появления псевдотерминала после запуска коллектора |
| `sweep_timeout_s` | число, `5.0` | **Read timeout (s)** | сколько ждать пригодной развёртки, покрывающей полосу, прежде чем переподключиться |
| `stop_timeout_s` | число, `2.0` | **Stop timeout (s)** | ожидание остановки коллектора; на деле не больше 0,5 с, затем он завершается принудительно |
| `phase_calibration.phase0_rad`, `freq0_hz` | `0.0`, `2046000000.0` | — | первая опорная точка «фаза опорного сигнала — частота» |
| `phase_calibration.phase1_rad`, `freq1_hz` | `300.0`, `5612000000.0` | — | вторая опорная точка; фазы и частоты точек должны различаться |
| `band.start_hz`, `band.stop_hz` | `2100000000.0`, `5500000000.0` | — | выходная полоса: развёртка пересчитывается на её сетку; развёртка, не покрывающая полосу, отбрасывается |
| `band.points` | `2048` | — | число точек выходной сетки, не меньше 2 |
Для `source: "e502"` поля `executable_path` и `tty_path` обязательны, а `project_dir`, если
задан, должен быть каталогом. Все три тайм-аута должны быть конечными положительными
числами (`radar.rph_radar.<ключ> must be finite and positive`).
Фазы `phase0_rad` и `phase1_rad` записывает программа фазовой калибровки
(`rph_radar_calibrate --apply`). Полоса и якоря входят в ключ радара, поэтому после
новой фазовой калибровки старые наборы не видны.
### radar.laser_control
Плата лазеров радиофотонного радара. Поля — в секции **Radar**, применяются кнопкой
**Apply Radar**.
| Ключ | По умолчанию | Что делает |
| --- | --- | --- |
| `enabled` | `false` | программировать плату при **Apply Radar** |
| `port` | `""` | последовательный порт платы, 115 200 бод |
| `mode` | `"manual"` | `"manual"` — постоянные токи и температуры, `"variation"` — развёртка тока LD1 (плате RFG нужен этот режим) |
| `pi_coeff1_p`, `pi_coeff1_i` | `1280`, `25` | коэффициенты ПИ-регулятора первого канала |
| `pi_coeff2_p`, `pi_coeff2_i` | `1280`, `179` | коэффициенты ПИ-регулятора второго канала |
| `manual.temp1`, `manual.temp2` | `24.0`, `24.9` | температуры лазеров, °C, 15–40 |
| `manual.current1`, `manual.current2` | `97.3`, `102.0` | токи лазеров, мА, 15–110 |
| `variation.variation_type` | `"CHANGE_CURRENT_LD1"` | что меняется; другого варианта нет |
| `variation.static_temp1`, `static_temp2` | `24.0`, `24.9` | температуры во время развёртки |
| `variation.static_current1`, `static_current2` | `97.3`, `102.0` | постоянные токи во время развёртки |
| `variation.min_value`, `max_value` | `97.3`, `107.0` | пределы развёртки тока LD1, мА, начало меньше конца |
| `variation.step` | `0.05` | шаг тока, мА, 0,002–0,5 |
| `variation.time_step` | `50` | длительность ступени, мкс, 20–100 |
| `variation.delay_time` | `10` | пауза между развёртками, мс, 3–10 |
## switches
Два ВЧ-ключа: `port1` — выходной (передающие антенны), `port2` — входной (приёмные).
Таблица положений — в главе [Оборудование](hardware.md).
| Ключ | Тип, по умолчанию | Что делает |
| --- | --- | --- |
| `name` | строка, `""` | имя ключа в сообщениях и в GPIO (`radar_<имя>`) |
| `driver_mode` | `"mock"` | `"native"` — управлять линиями GPIO, `"mock"` — только помнить положение |
| `radar_port` | целое, `0` | порт прибора, к которому подключён ключ: 1 или 2. Задайте его явно: `0` не проходит проверку при **Start** (`switches.<имя>.radar_port must be 1 or 2`) |
| `positions` | `1` | число положений, у настоящего ключа 1–4. Задаёт число выходов или входов матрицы |
| `default_position` | `0` | положение после открытия ключа, меньше `positions` |
| `gpio_chip` | `""` | GPIO-чип, например `/dev/gpiochip0`; для `native` обязателен |
| `pin_a`, `pin_b` | `-1` | номера линий A и B на чипе; для `native` — разные и неотрицательные |
Поле `driver` допускается только со значением `"h7992"`, `invert_logic` — только
`false`; оба при сохранении убираются. Другие значения отвергаются ещё до обращения к
GPIO. У многоприборной схемы программа сама переписывает в этих разделах `driver_mode`
(`"mock"`), `radar_port`, `positions` (положения настоящего ключа × выходы или входы
приборов) и `default_position` (`0`): значения из файла не используются. Читаются
только `name`, `gpio_chip`, `pin_a` и `pin_b` — для настоящих ключей на выходах мастера
или входах ведомых, если `multi_device.output_switch_positions` или
`input_switch_positions` больше 1; их режим берётся из `radar.driver_mode`. Изменения
применяются при **Start**;
число положений и режим перезапускают процессор.
## control_button
GPIO-кнопка, которая снимает временный референс. Настройки читаются один раз при
запуске программы.
| Ключ | По умолчанию | Что делает |
| --- | --- | --- |
| `enabled` | `false` | включить кнопку |
| `gpio_chip` | `"/dev/gpiochip0"` | GPIO-чип |
| `pin` | `-1` | номер линии на чипе (не номер контакта разъёма); отрицательный — кнопка не включается |
| `active_low` | `true` | `true` — нажатие опускает линию в 0 (кнопка на землю), `false` — поднимает в 1 |
| `bias` | `""` | подтяжка: `"pull_up"`, `"pull_down"`, `"disabled"`; пусто — к питанию при `active_low`, к земле иначе |
| `debounce_ms` | `50` | подавление дребезга средствами ядра, мс; 0 — без подавления |
| `action` | `"capture_tmp_reference"` | действие по нажатию; другого нет |
## logging
| Ключ | По умолчанию | Что делает |
| --- | --- | --- |
| `level` | `"info"` | наименьший уровень записей журнала: `debug`, `info`, `warning` или `error`. Меняется списком **Level** над журналом окна; записывается при **Save Config…** |
На уровне `debug` в журнал попадают трассы тайминга переключений; корневой профиль
работает с ним.
## run
| Ключ | Тип, по умолчанию | Что делает | Когда |
| --- | --- | --- | --- |
| `settling_ms` | целое, `0` | пауза после переключения ВЧ-ключей перед развёрткой, мс. Поле **Settling (ms)** | Start |
| `idle_sleep_ms` | целое, `2` | сколько процессы предобработки и обработки спят, когда нет входных данных, мс | Start, процессор |
| `continuous` | логическое, `false` | непрерывный сбор или одна коллекция. Программа сама ставит `true` при **Start** и `false` при **Single Capture**; значение из профиля важно только для `./start.sh --producer-only` | — |
| `processing_live_config_path` | строка, `""` | путь к live-файлу. Программа всегда подставляет `python_app/runtime/processing_live.json` своего каталога | — |
| `combos` | список `{"input", "output"}`, `[]` | пары сбора в порядке обхода (ниже) | Start, процессор |
| `locator_server` | объект | TCP-локатор (ниже) | Start, процессор |
### run.combos
Список пар `{"input": вход, "output": выход}`; пустой список — полная матрица:
выходы по порядку, внутри каждого — все входы. Пар не больше 4096, повторы и номера
вне матрицы отвергаются.
- Одиночный LibreVNA и радиофотонный радар: при **Start** список берётся из окна —
**Run combos** (`вход:выход` через запятую, пусто — полная матрица) или
**Single combo**. В режимах pass-through и B-scan, если оба ключа `mock` или у обоих
не больше одного положения, программа снимает только пару `0:0`.
- Многоприборная схема: список всегда равен полной матрице; в окне он показан, но не
редактируется.
### run.locator_server
TCP-сервер, который отдаёт объекты GPR внешним программам и принимает от них
скорость (протокол — в главе [Веб-интерфейс, кнопка и локатор](web.md)). В окне не
редактируется. Старое имя раздела `run.locator` тоже читается.
| Ключ | По умолчанию | Что делает |
| --- | --- | --- |
| `host` | `"0.0.0.0"` | адрес, на котором сервер ждёт клиентов; `0.0.0.0` — все интерфейсы |
| `port` | `8888` | TCP-порт, 1–65535 |
| `device_id` | `3` | номер устройства в заголовке каждого пакета |
| `protocol_version` | `1` | поле `ver` в пакетах |
| `max_payload_bytes` | `65536` | наибольший размер входящего пакета; клиент с большим пакетом отключается |
| `client_queue_size` | `32` | очередь пакетов на клиента; при переполнении выбрасываются старые |
| `logger_name` | `"locator_runtime"` | не используется |
Ключ `enabled` в этом разделе программа не сохраняет: локатор включён всегда.
## preprocess
| Ключ | Что делает |
| --- | --- |
| `s21.calibration.set_name` | имя выбранного набора калибровки (through) |
| `s21.reference.set_name` | имя выбранного набора референса |
| `s21.object_calibration.set_name` | имя выбранной калибровки по объекту; пусто — не применять |
| `s21.*.bundle_path` | путь к файлу набора для препроцессора; программа заполняет его сама при **Start**, в профиле — пусто |
| `notch.enabled` | включить режекторный фильтр, по умолчанию `false` |
| `notch.bands_hz` | полосы подавления `[нижняя, верхняя]`, Гц, по умолчанию `[]` |
| `notch.taper_width_hz` | ширина плавного края, Гц, не меньше 0, по умолчанию `40000000.0` |
| `notch.taper_type` | форма края: `"cosine"` (по умолчанию) или `"hard"`; как работает фильтр — в главе [Калибровка и предобработка](preprocessing.md) |
Наборы выбираются в окне **Preprocessing Setup** и записываются в профиль при
**Save Config…**. Изменения применяются при **Start**.
## gpr
Геометрия антенн для GPR и 3D SAR и для симулятора. Редактируется таблицами
**Tx geometry** и **Rx geometry** на страницах GPR и 3D SAR; правка таблицы сразу
уходит процессору.
| Ключ | Запись | Что делает |
| --- | --- | --- |
| `tx_geometry` | `{"output_pos", "x_m", "y_m", "z_m"}` | положения передающих антенн: номер выхода и координаты в осях радара, м |
| `rx_geometry` | `{"input_pos", "x_m", "y_m", "z_m"}` | положения приёмных антенн: номер входа и координаты, м |
Записей может быть меньше, чем выходов или входов: антенны без записи в GPR и 3D SAR
не участвуют. Номер должен быть меньше числа выходов (входов), без повторов, иначе
`gpr.tx_geometry output_pos is out of range [0, N)`. При сужении матрицы лишние
записи нужно удалить. Отсутствующие `y_m` и `z_m` считаются нулями. Оси — в главе
[Как работает радар](principles.md).
## rings
Пять колец разделяемой памяти, через которые процессы передают данные
(глава [Устройство программы](architecture.md)):
| Кольцо | Кто пишет | Кто читает |
| --- | --- | --- |
| `raw` | процесс сбора | препроцессор |
| `raw_tap` | процесс сбора, копия | окно программы |
| `preprocessed` | препроцессор | процессор |
| `preprocessed_tap` | препроцессор, копия | окно программы |
| `results` | процессор | окно программы |
| Ключ | По умолчанию | Что делает |
| --- | --- | --- |
| `name` | `""` | имя сегмента разделяемой памяти, например `/radar_raw`. Пустое имя у `raw_tap` или `preprocessed_tap` отключает копию и соответствующую историю окна |
| `capacity` | `1` | сколько кадров помещается в кольцо; при переполнении затирается самый старый |
| `slot_size_bytes` | `4096` | наибольший размер кадра, байт, кратный 8 |
Слоты `raw`, `preprocessed` и включённых копий должны вмещать коллекцию:
44 + пары × (28 + 12 × точки) байт. Например, 16 пар по 1031 точке требуют 198 444
байт. Программа проверяет это до запуска
(`rings.raw.slot_size_bytes must be at least N for this sweep`). Слот `results` должен
вмещать результат; изображения 3D SAR при настройках по умолчанию занимают около
1 МБ. Во всех примерах слоты по 2 МиБ (`2097152`) и ёмкость 50.
Ёмкость влияет и на историю: процессор хранит до 50 коллекций, но не больше
ёмкости `preprocessed` и `results`, а B-scan во время сбора показывает столько же. В
GPR и 3D SAR процессор при этом хранит не меньше, чем нужно обработке: 1 +
**Mean count** (если включено вычитание среднего) + **SAR_FRAME_RADIUS** (в 3D SAR),
независимо от ёмкости колец.
Калибровке по объекту нужно кольцо `preprocessed_tap`.
## gui
Состояние окна: значения всех полей, которые программа запоминает. Раздел
записывается целиком при **Save Config…**. Если его нет, программа берёт значения по
умолчанию, а часть вычисляет из профиля: номера антенн — из геометрии и списка пар,
окно видимости GPR — из положения антенн, каталог сохранения —
`python_app/data/snapshots`.
| Ключ | Поле в окне | Смысл |
| --- | --- | --- |
| `version` | — | версия раздела, всегда `1` |
| `switches.combo_mode` | кнопки **Select** у **Run combos** и **Single combo** | откуда брать пары сбора: `"text"` или `"single"` |
| `switches.combos_text` | **Run combos** | список пар `вход:выход` |
| `switches.single_input`, `single_output` | **Input**, **Output** в строке **Single combo** | одна пара |
| `processing.selected_mode` | **Mode** | режим обработки: `pass_through`, `bscan`, `gpr`, `sar3d` |
| `processing.pass_through.*` | страница pass-through | `show_magnitude`, `show_phase`, `unwrap_phase`, `combo_filter`, `fixed_y_enabled`, `y_min_db`, `y_max_db` |
| `processing.bscan.*` | страница B-scan | `axis`, `cut_m`, `max_depth_m`, `gain`, `start_freq_mhz`, `stop_freq_mhz`, `subtract_mean_ascan`, `history_window_scans` |
| `processing.gpr.*` | страница GPR | `input_positions`, `output_positions`, `min_depth_m`, `max_depth_m`, `range_comp_power`, `object_min_frac`, `score_mode`, `motion_mode`, `look_angle_deg`, `direction_sign`, `speed_m_s`, `ignore_socket_speed_enabled`, `max_detected_objects_to_draw`, `draw_top_m_objects`, `object_approach_min_frames`, `start_freq_mhz`, `stop_freq_mhz`, `background_subtract_enabled`, `background_mean_count`, `remove_sidelobe_objects_enabled`, `bp_ground_depth_m`, `pair_depth_equalize`, `render_mode`, `visible_x_min_m`, `visible_x_max_m`, `visible_z_min_m`, `visible_z_max_m` |
| `processing.sar3d.*` | страница 3D SAR | `input_positions`, `output_positions`, `min_depth_m`, `max_depth_m`, `start_freq_mhz`, `stop_freq_mhz`, `speed_m_s`, `ignore_socket_speed_enabled`, `look_angle_deg`, `frame_radius`, `voxel_x_m`, `voxel_y_m`, `voxel_z_m`, `bp_ground_depth_m`, `comp_range_power`, `use_compensation`, `normalize_each_frame`, `pair_depth_equalize`, `background_subtract_enabled`, `background_mean_count`, `bg_gate_enabled`, `clean_enabled`, `x_size_m`, `y_min_m`, `y_max_m`, `z_size_m`, `cloud_threshold`, `zoom_percent`, `mark_enabled` |
| `data_actions.save_path` | **Path** | каталог для сохранения данных; относительный путь — от рабочего каталога процесса |
| `data_actions.save_name` | **Name** | имя сохраняемого набора данных, по умолчанию `snapshot_manual` |
| `data_actions.record_count` | **Sweeps to record** | сколько коллекций записать на диск, по умолчанию 100 |
| `preprocess_dialog.set_name` | **Set name** | имя нового набора, по умолчанию `set_001` |
| `preprocess_dialog.radar_config_dir` | **Directory** | каталог вариантов радара |
| `preprocess_dialog.use_all_radar_configs` | **Capture current combo for all found radar configs** | съём по всем вариантам |
| `preprocess_dialog.median_sweep_count` | **Sweeps per combo (median)** | развёрток на пару, по умолчанию 5 |
Ключи страниц обработки названы по полям окна; что делает каждое поле, сколько оно по
умолчанию и в каких пределах — в главе [Режимы обработки](processing.md). Типы,
варианты выбора, объявленные в схеме границы и версия проверяются при чтении, и
нарушение отвергает весь профиль: `gui.processing.gpr.score_mode must be one of: peak, combined`,
`gui.processing.sar3d.zoom_percent must be <= 1000`. Значение в пределах схемы, но вне
пределов поля окна поле молча подрезает к своей границе (например, `frame_radius: 1000`
станет 24), и подрезанное значение запишется при следующем **Save Config…**.
`preprocess_dialog.median_sweep_count` меньше 1 поднимается до 1.
## processing_live.json
Живые настройки процессора. Файл `python_app/runtime/processing_live.json` пишет
программа при каждом изменении настройки обработки, смене режима, правке геометрии,
очистке истории и удалении последнего измерения; читает его только процессор. Править
его вручную не нужно: программа перезаписывает его целиком.
В файле 53 ключа. Ключи настроек обработки соответствуют полям окна:
| Ключ | Поле |
| --- | --- |
| `processor_mode` | **Mode** |
| `bscan_axis`, `bscan_cut_m`, `bscan_max_depth_m`, `bscan_gain`, `bscan_start_freq_mhz`, `bscan_stop_freq_mhz` | поля B-scan |
| `gpr_input_positions`, `gpr_output_positions`, `gpr_min_depth_m`, `gpr_max_depth_m`, `gpr_start_freq_mhz`, `gpr_stop_freq_mhz`, `gpr_speed_m_s`, `gpr_look_angle_deg`, `ignore_socket_speed`, `gpr_background_subtract_enabled`, `gpr_background_mean_count`, `pair_depth_equalize` | общие поля GPR и 3D SAR — со страницы выбранного режима |
| `gpr_range_comp_power`, `gpr_object_min_frac`, `gpr_score_mode`, `gpr_motion_mode`, `gpr_direction_sign`, `gpr_max_detected_objects_to_draw`, `gpr_draw_top_m_objects`, `gpr_object_approach_min_frames`, `gpr_remove_sidelobe_objects_enabled`, `gpr_bp_ground_depth_m`, `gpr_visible_x_min_m`, `gpr_visible_x_max_m`, `gpr_visible_z_min_m`, `gpr_visible_z_max_m` | поля GPR |
| `sar3d_frame_radius`, `sar3d_voxel_x_m`, `sar3d_voxel_y_m`, `sar3d_voxel_z_m`, `sar3d_bp_ground_depth_m`, `sar3d_comp_range_power`, `sar3d_use_compensation`, `sar3d_normalize_each_frame`, `sar3d_x_size_m`, `sar3d_y_min_m`, `sar3d_y_max_m`, `sar3d_z_size_m`, `sar3d_bg_gate_enabled`, `sar3d_clean_enabled` | поля 3D SAR |
| `tx_geometry`, `rx_geometry` | таблицы геометрии; заменяют `gpr` профиля в процессоре |
Служебные ключи:
| Ключ | Что делает |
| --- | --- |
| `settings_revision` | номер ревизии настроек (время записи по монотонным часам, нс). Процессор возвращает его в каждом результате, и окно рисует GPR и 3D SAR только текущей ревизии |
| `reprocess_current_result` | пересчитывать ли сохранённые коллекции при новой ревизии; `false` — пока окно само пересчитывает остановленный B-scan |
| `history_clear_before_ns` | граница **Clear Runtime History**: коллекции, начатые не позже этого момента, удаляются из истории процессора |
| `history_removed` | пары `{"collection_id", "capture_start_ns"}`, удалённые **Remove Last Measurement**; список очищается при **Clear Runtime History** |
Процессор отвергает файл целиком, если в нём ошибка JSON, число не того типа или вне
float32, неизвестное значение `processor_mode`, `gpr_score_mode` или
`gpr_motion_mode`, геометрия с номером вне матрицы или с повтором, отрицательные
`sar3d_x_size_m` или `sar3d_z_size_m`, `sar3d_y_max_m` не больше `sar3d_y_min_m`.
Тогда он работает с последними принятыми настройками и пишет
`data_processor warning: failed to refresh live processing config: …` в
`python_app/runtime/logs/data_processor.err.log`. Неизвестные ключи пропускаются.