# Калибровка и предобработка Препроцессор (`data_preprocessor`) по очереди применяет к каждой коллекции четыре шага и только потом отдаёт её на обработку. Зачем нужен каждый шаг, объяснено в главе [Как работает радар](principles.md). ## Цепочка предобработки Для каждой пары и каждой частотной точки: | Шаг | Формула | Набор | | --- | --- | --- | | 1. Калибровка | S21 ← S21 / S21_through | набор калибровки (through) | | 2. Вычитание референса | S21 ← S21 − S21_ref / S21_through | набор референса | | 3. Режекторный фильтр | S21 ← S21 · m(f) | настройки `preprocess.notch` в профиле | | 4. Калибровка по объекту | S21 ← S21 · F | набор калибровки по объекту, если выбран | Референс делится на through один раз при загрузке, поэтому вычитается уже откалиброванный фон. Точки, где |S21_through|² не больше 10⁻¹², на шаге 1 остаются как есть, без деления. Окно съёма и номер коллекции не меняются. У каждого набора должна быть каждая пара сбора, а сетка частот — совпадать с развёрткой: то же число точек, а каждая частота отличается от своей не больше чем на 10⁻⁵ её значения, но допуск не меньше 1 мГц (на 6 ГГц — 60 кГц). Наборы проверяются при запуске препроцессора, и неподходящий набор его останавливает. Если это случилось, пока программа ждёт готовности процессов, **Start** отказывает с сообщением ``Failed to start pipeline: RuntimeError: Process `data_preprocessor` exited with code 1.`` и текстом ошибки. Если позже, в журнале окна появляется отчёт о завершении процесса, а программа перезапускает конвейер с нарастающей паузой. Коллекцию, которая не прошла проверку уже во время работы, препроцессор отбрасывает и пишет об этом в свой журнал `python_app/runtime/logs/data_preprocessor.err.log`. В окне программы это видно только по тому, что изображение перестаёт обновляться. ## Наборы | Набор | Каталог | Обязателен | | --- | --- | --- | | S21 Calibration — калибровка (through) | `python_app/data/s21_calibration/` | да | | S21 Reference — референс | `python_app/data/s21_reference/` | да | | Object Calibration — калибровка по объекту | `python_app/data/s21_object_calibration/` | нет | Набор — архив NPZ с массивами частот и S21 по парам и JSON-файл рядом с описанием (формат — в главе [Данные на диске](data.md)). Наборы лежат в подкаталогах с именем `radar_key` — ключа конфигурации радара: - у LibreVNA ключ складывается из модели, серийных номеров, начала и конца развёртки, числа точек, полосы ПЧ и мощности, а у многоприборной схемы ещё из положений ключей; - у радиофотонного радара ключ — хеш выходной полосы, фазовой калибровки, параметров источника, положений ключей и настроек лазера; лазер входит в ключ, если плата лазеров включена в профиле или источник — плата RFG. Программа показывает только наборы текущего ключа. Если изменить любую из этих величин (например, число точек), старые наборы пропадают из списков, а выбор сбрасывается с записью `Preprocess selection reset: <старый ключ> -> <новый ключ>; cleared {...}` в журнале. Так программа не даёт применить калибровку, снятую при других настройках. Если вернуть прежние настройки, наборы снова появятся в списках, но выбрать их придётся заново. Наборы применяются при запуске сбора: программа пересобирает из выбранных наборов файлы `python_app/runtime/*_bundle.bin` и заново запускает препроцессор. Запуск — это **Start**, а также продолжение сбора после приостановки: после съёма набора, временного референса или нейтральных наборов сбор, если он шёл, продолжается уже с новыми наборами. Смена выбора в списке во время сбора применится при следующем запуске. Перед запуском программа проверяет наборы и, если что-то не так, отказывается запускаться, ничего не остановив. Окно ошибки показывает `Failed to start pipeline: <тип ошибки>: <текст>`, где текст — один из этих: | Условие | Сообщение | | --- | --- | | не выбраны калибровка или референс | `Select all required preprocess sets in Preprocessing Panel before Start: S21 Calibration, S21 Reference` | | файла набора нет для текущего ключа | `Missing set files for <вид>//<имя>` | | в наборе нет какой-то пары сбора | `Selected <набор> set does not cover requested run combos` | | архив повреждён или содержит не числа | `Corrupted preprocess set <вид>/<ключ>/<имя>: …` | | калибровка по объекту не подходит | см. раздел «Калибровка по объекту» | ## Окно Preprocessing Setup Открывается кнопкой **Preprocessing…**. Окно не модальное: пока оно открыто, главным окном можно пользоваться. Группа **Preprocess Sets**: | Элемент | Что делает | | --- | --- | | **Set name** | имя нового набора; по умолчанию `set_001`. Для этого вида набора и этого ключа радара имя должно быть новым | | **Create Neutral S21 Sets** | создаёт нейтральные наборы (см. ниже). Кнопка есть только у радиофотонного радара, у многоприборной схемы и у одиночного LibreVNA в режиме `mock` | | **Refresh Sets** | перечитывает списки наборов с диска и каталог вариантов радара | | **Sweeps per combo (median)** | сколько развёрток снимать на каждую пару, от 1 до 99, по умолчанию 5. В набор идёт медиана по каждой частоте, отдельно по вещественной и мнимой части: при трёх развёртках и больше одиночный сбой развёртки в набор не попадает. Это же число развёрток берут временный референс и калибровка по объекту | | **Radar Config Variants** → **Directory**, **Browse…** | каталог с профилями JSON для съёма наборов сразу под несколько настроек развёртки (см. ниже). Под полем — итог просмотра: `Directory: <каталог> \| json=…, valid=…, skipped=…, duplicates=…` (файлов JSON, подходящих вариантов, пропущенных, повторов) или `No radar config directory selected.` | | **Capture current combo for all found radar configs** | включает съём по всем вариантам из каталога | | **S21 calibration**, **S21 reference** | выбор набора калибровки и референса для следующего **Start** | | **Object calibration** | выбор калибровки по объекту; **Off** — не применять | | **Create…** | открывает окно калибровки по объекту | Группа **Capture All Antenna Pairs**: | Элемент | Что делает | | --- | --- | | **Active type**, **Progress**, **Current combo** | вид снимаемого набора, сколько пар снято из скольких, какая пара следующая; без съёма — ``, `0 / 0`, после последней пары — `` | | **Start S21 Calibration**, **Start S21 Reference** | начинают съём набора | | **Capture Current Combo** | снимает следующую пару | | **Capture All Remaining** | снимает подряд все оставшиеся пары | | **Undo Last Capture** | отменяет последний съём, чтобы переснять пару | | **Save Captured Set** | сохраняет набор и выбирает его; доступна, когда сняты все пары | | **Abort Sequence** | прерывает съём и отбрасывает снятое | Ниже — журнал съёма (`S21 Calibration: 1/8 | input=0 output=0` на каждую пару), строка состояния (сначала `Ready`) и графики модуля и фазы последней снятой развёртки (до первого съёма — `Preview will appear after the first successful capture.`). Последняя развёртка рисуется и в главном окне. ## Съём калибровки и референса 1. Выберите профиль и настройки радара. Наборы привязаны к ключу радара, поэтому снимайте их при тех настройках развёртки, с которыми будете работать. 2. Подготовьте установку. Для калибровки — эталонный тракт для текущей пары; для референса — пустую сцену: всё, что должно остаться в фоне, на месте, целей нет. 3. Нажмите **Preprocessing…**, задайте **Set name** и **Sweeps per combo (median)**. 4. Нажмите **Start S21 Calibration** или **Start S21 Reference**. Сбор приостанавливается, и на время съёма прибором управляет окно предобработки. 5. Снимайте пары кнопкой **Capture Current Combo** или все сразу кнопкой **Capture All Remaining**. Проверяйте каждую развёртку по графикам; неудачную переснимите через **Undo Last Capture**. При ошибке съёма уже снятые пары сохраняются, и съём можно повторить. 6. Когда сняты все пары, нажмите **Save Captured Set**. Набор сохраняется и выбирается, приостановленный сбор продолжается. Съём всегда идёт по полной матрице положений ключей, а не только по парам сбора. - Одиночный LibreVNA: для каждой пары программа выставляет ключи, ждёт время установления (`run.settling_ms`, в окне — **Settling (ms)**) и снимает нужное число развёрток этой пары. - Многоприборная схема при калибровке: прибор снимает всю матрицу, а в набор идёт только текущая пара, по одной за нажатие. **Capture All Remaining** здесь недоступна. - Многоприборная схема при съёме референса: одно нажатие снимает всю матрицу нужное число раз, и набор сразу готов. ### Варианты радара Иногда наборы нужны сразу под несколько развёрток — например, под разные полосы частот. Для этого в **Directory** указывают каталог с профилями JSON. Профиль считается вариантом, если в его `radar.sweep` есть хотя бы одно из полей `start_hz`, `stop_hz`, `points`, `if_bandwidth_hz`, `stimulus_power_dbm`; эти поля накладываются на текущий профиль, повторы одного ключа пропускаются. С включённой **Capture current combo for all found radar configs** каждое нажатие снимает пару во всех вариантах подряд, с перенастройкой прибора. **Save Captured Set** сохраняет по набору на каждый вариант под одним именем, а картинки развёрток — в `python_app/data/preview_png/<вид>/<имя>//i<вход>_o<выход>.png`. ## Временный референс Кнопка **Capture Temporary Reference** в секции **Data Actions** снимает референс одним нажатием, не открывая окно предобработки. То же действие есть в веб-интерфейсе и на GPIO-кнопке. Программа приостанавливает сбор, снимает полную матрицу с текущими настройками (медиана **Sweeps per combo (median)** развёрток) и сохраняет её как набор `tmp_reference`, заменяя прежний. Затем она выбирает этот набор, очищает историю обработки, рисует последнюю развёртку с подписью `Tmp S21 Reference last trace` и продолжает сбор. Процессор и локатор при этом не перезапускаются, клиенты локатора не отключаются. Временный референс нужен, когда сцена или положение радара изменились: после перестановки, на новом месте, после заметного изменения обстановки. ## Нейтральные наборы **Create Neutral S21 Sets** сохраняет под именем из **Set name** два набора: калибровку, равную 1, и референс, равный 0, для всех пар на текущей сетке частот. С ними данные проходят предобработку без изменений. Они нужны, чтобы запустить конвейер, пока настоящей калибровки нет: с симулятором, при первой проверке прибора. Если набор с таким именем уже есть, программа откажется. После создания наборы выбираются, история очищается, а процессор перезапускается: сразу, если сбор шёл, иначе при следующем **Start**. ## Режекторный фильтр Фильтр настраивается только в профиле, в разделе `preprocess.notch`; в окне программы его нет. | Ключ | По умолчанию | Что делает | | --- | --- | --- | | `enabled` | `false` | включает фильтр | | `bands_hz` | `[]` | полосы подавления: список пар `[нижняя, верхняя]` в герцах, верхняя не меньше нижней | | `taper_width_hz` | `40e6` | ширина плавного края w, Гц, не меньше 0 | | `taper_type` | `"cosine"` | форма края: `"cosine"` — косинусный спад, `"hard"` — без спада | Внутри полосы [L, H] множитель m(f) равен нулю. При `"cosine"` по краям полосы он плавно спадает от 1 до 0 по закону 0,5·(1 + cos(…)) на участках [L − w, L] и [H, H + w]. При `"hard"` (или w = 0) зануляется вся полоса [L − w, H + w]. Края соседних полос перемножаются. Во всех профилях-примерах `enabled: true` и `bands_hz: []`, то есть фильтр ничего не подавляет. Изменение фильтра применяется при следующем **Start** и сбрасывает историю. ## Калибровка по объекту Калибровка по объекту находит для каждой пары комплексную поправку по частоте так, чтобы отклик известного отражателя у всех пар совпадал по фазе. Эталон — ноутбук `MIMO_calibration.ipynb`, он хранится вне репозитория. Поправку можно строить по нескольким положениям отражателя: они уточняют друг друга. Что нужно заранее: - кольцо `rings.preprocessed_tap` в профиле: калибровка снимает данные из него; - таблицы геометрии антенн: в калибровку входят только пары, у которых есть геометрия обеих антенн, остальные получают поправку 1; - выбранный набор калибровки (through): поправка запоминает его имя и содержимое и годится только вместе с ним; - металлический шар известного радиуса (или другой точечный отражатель) и способ измерить положение его центра. Окно **MIMO Object Calibration** открывается кнопкой **Create…** в окне предобработки. Вверху — подсказка о порядке работы (`Capture data, then enter the measured reflector centre: …`), внизу — строка состояния (сначала `Ready to capture`), а на месте карт фаз до расчёта — `Pair phases at the target will appear after calculation.` | Элемент | Что делает | | --- | --- | | **Set name** | имя набора поправки | | таблица **Target**, **Sweeps**, **X (m)**, **Y (m)**, **Z (m)** | снятые положения отражателя: сколько коллекций снято и измеренный центр | | **Capture Data** | снимает данные для нового положения отражателя | | **Remove Selected Target** | удаляет выбранное положение | | **Sphere radius (m, 0 = point)** | радиус шара, по умолчанию 0,16 м; 0 — точечный отражатель | | **Antenna origin height above ground (m)** | высота начала осей антенн над землёй, по умолчанию 0,92 м | | **Radar downward tilt (°)** | наклон радара вниз, по умолчанию 10° | | **Calculation Settings** | раскрывает остальные параметры расчёта (таблица ниже) | | **Calculate and Save** | считает поправку в фоне и сохраняет набор | | **Cancel Operation** | прерывает съём или расчёт | | **Close** | закрывает окно | Параметры в **Calculation Settings**: | Параметр | По умолчанию | Что делает | | --- | --- | --- | | **Estimation band start (MHz)**, **Estimation band stop (MHz)** | 2000 и 6000 | полоса, по которой оценивается поправка; в ней должно быть не меньше 4 точек | | **Echo path gate half-width (m)** | 0,15 | полуширина окна вокруг ожидаемого пути до отражателя, в котором ищется его эхо | | **Gate taper width (m)** | 0,03 | ширина плавного края этого окна | | **Coarse path search (±m, 0 = off)** | 0,6 | предварительный поиск: на сколько можно сдвинуть отражатель по дальности до самого сильного эха | | **Coordinate refinement (±m)** | 0,1 | в каких пределах уточнять координаты отражателя | | **Refine coordinates** X, Y, Z | все три | по каким осям уточнять координаты | | **Antenna phase model** | `linear` | модель фазы антенны по частоте: `const` — постоянная, `linear` — линейная (задержка), `poly3` — многочлен третьей степени, `raw` — без сглаживания | | **Equalize amplitudes** | выключен | выравнивать и амплитуды, а не только фазы | Порядок: 1. Поставьте отражатель в сцену и измерьте положение его центра в осях сцены: X — вдоль ряда антенн, Y — высота над землёй, Z — вдоль земли от радара. 2. Нажмите **Capture Data**. Программа приостанавливает текущий сбор и запускает конвейер с этим же профилем, но без калибровки по объекту и по всем парам из таблиц геометрии (многоприборная схема — по полной матрице). Она собирает столько новых коллекций, сколько задано в **Sweeps per combo (median)**, и возвращает прежний режим работы. Предел ожидания — 300 с. 3. Введите X, Y, Z в строке нового положения. 4. Повторите для других положений, если нужно. Все положения должны быть сняты при одних и тех же настройках радара, геометрии и предобработке. 5. Нажмите **Calculate and Save**. Расчёт идёт в фоне. После него окно показывает карты фаз пар в точке отражателя до и после поправки (**Before**, **After**): строки — передающие антенны, столбцы — приёмные, фаза — относительно суммы по всем парам, по шкале ±180°, с разбросом в градусах. **Capture Data** отказывает, если идёт однократный съём или запись на диск (`Finish the single capture or disk recording before taking calibration data`), если нет таблиц геометрии (`Configure Tx/Rx antenna positions before taking calibration data`) или кольца копии (`Object calibration requires rings.preprocessed_tap; enable it in the run profile`). 6. Если набор подходит к текущим настройкам, он выбирается и применяется при следующем **Start** (`Applies on the next Start.`). Иначе он сохраняется невыбранным с объяснением `Not selected for the current configuration: <причина>`. Коллекции одного положения усредняются. По каждой частоте поправка раскладывается на множители передающих и приёмных антенн; фазы сглаживаются выбранной моделью и продолжаются на всю сетку частот. Поправка пары — сопряжённая фаза произведения множителей, а с **Equalize amplitudes** — обратное произведение. Перед каждым **Start** программа проверяет, что поправка снята при тех же настройках радара, той же геометрии и с тем же набором калибровки (по имени и содержимому), что она и калибровка покрывают все пары сбора, не содержит нулей и имеет ту же сетку частот. Сообщения при отказе: - `Object calibration has no capture provenance; capture it again`; - `Object calibration radar configuration differs from the captured state` (а также `antenna geometry`, `through calibration name`, `through calibration content`); - `Object calibration and through must cover input=i, output=o`; - `Object calibration corrections must be nonzero`; - `Object calibration frequency grid differs for input=i, output=o`. Если изменить таблицу геометрии так, что она перестала совпадать с геометрией выбранной поправки, выбор поправки снимается, а работающий с ней сбор приостанавливается и продолжается уже без неё.