Files
radar_system/python_app/hardware_full/laser_control/monitoring

Контроль температуры при вариации тока лазера

Набор из трёх развязанных компонентов для автоматизации измерений в режиме вариации тока лазера 1 (CHANGE_CURRENT_LD1). Пока плата гоняет свип тока, температуры лазеров должны оставаться на заданных статичных уставках. Эти модули раз в свип считывают реальную температуру и предупреждают, если она разошлась с целью.

Зачем это нужно

При запуске вариации тока из GUI изменённые значения температуры могут фактически не дойти до цели — реальная температура остаётся прежней, и измерение становится некорректным. Плата после старта задачи гоняет свип автономно и никак не сигнализирует, что уставка не достигнута. Эти модули закрывают пробел: независимо опрашивают плату и валидируют температуру относительно уставок, зафиксированных в момент старта вариации.

⚠️ В прошивке реализована только вариация тока (CHANGE_CURRENT_LD1). Вариация температуры не поддерживается и в этот API не заложена.

Архитектура

[starter]  ── TASK_ENABLE ──►  плата          кратко открыл порт, послал, закрыл
   │  пишет session.json (target temp1/2, tolerance, variation_type)
   ▼
[monitor]  ── TRANS_ENABLE ──►  плата          владеет портом всё время работы
   │  раз в свип: get_measurements()
   │  дописывает строку в readings.jsonl (seq, temp1, temp2, temp_ext, I1, I2)
   ▼
[checker]  читает session.json + tail readings.jsonl
        сверяет temp1↔target_temp1 и temp2↔target_temp2, |Δ|>tol ─► WARNING в консоль
  • Порт лазера эксклюзивен. starter трогает его кратко, затем monitor владеет им всё время. checker порт не трогает вовсе — читает только файлы.
  • Связь через файлы (JSONL + JSON), а не сокеты, — процессы стартуют, останавливаются и перезапускаются независимо, без рукопожатия.
  • Сверяются оба лазера по внутренним temp1/temp2 (не по внешним термисторам temp_ext*), каждый со своим допуском (по умолчанию 0.03 °C).

Быстрый старт (CLI, два процесса)

Терминал 1 — стартовать вариацию и мониторить температуру:

python -m python_app.scripts.laser_temp_monitor \
    --config run_config.json \
    --start

Терминал 2 — валидировать температуру и печатать предупреждения:

python -m python_app.scripts.laser_temp_checker

Пример вывода чекера при расхождении и возврате в допуск:

WARNING laser_temp_checker: Laser 1 temperature off target: measured 28.100 °C,
        target 28.000 °C, Δ=+0.100 °C exceeds tolerance ±0.030 °C [seq=1]
INFO    laser_temp_checker: Laser 1 temperature back within tolerance:
        28.000 °C (target 28.000, |Δ|=0.000 ≤ 0.030) [seq=2]

Остановка — Ctrl+C (SIGINT) в любом из процессов.

Конфигурация

Параметры берутся из run_config.json, секция radar.laser_control. Мониторинг использует блок variation и новое поле temp_tolerance_c:

{
  "radar": {
    "model": "kamil_adc",
    "laser_control": {
      "enabled": true,
      "port": "/dev/ttyUSB0",
      "mode": "variation",
      "pi_coeff1_p": 2560,
      "pi_coeff1_i": 128,
      "pi_coeff2_p": 2560,
      "pi_coeff2_i": 128,
      "variation": {
        "variation_type": "CHANGE_CURRENT_LD1",
        "static_temp1": 28.0,
        "static_temp2": 28.9,
        "static_current1": 33.0,
        "static_current2": 35.0,
        "min_value": 33.0,
        "max_value": 60.0,
        "step": 0.05,
        "time_step": 50,
        "delay_time": 10,
        "temp_tolerance_c": 0.03
      }
    }
  }
}

Ключевые поля для мониторинга:

Поле Смысл
port Серийный порт лазерной платы (пусто → автоопределение)
static_temp1 / static_temp2 Целевые статичные температуры лазеров 1/2, °C
min_value / max_value / step Диапазон и шаг свипа тока, мА — из них считается период свипа
time_step / delay_time Тайминги точки (мкс / мс) — тоже входят в период свипа
temp_tolerance_c Допуск сверки, °C (по умолчанию 0.03)

Опции CLI

laser_temp_monitor

Аргумент По умолчанию Назначение
--config — (обязателен) Путь к run_config.json
--start выкл. Послать CHANGE_CURRENT_LD1 перед мониторингом и записать сессию
--strategy computed computed (раз в свип) или interval:<ms> (фикс. период)
--readings <tmp>/laser_temp_readings.jsonl Куда дописывать показания
--session <tmp>/laser_variation_session.json Куда писать снимок сессии (с --start)

Мониторить уже запущенную из GUI/пайплайна вариацию (без повторного старта):

python -m python_app.scripts.laser_temp_monitor --config run_config.json

Фиксированный период вместо расчётного (напр. раз в 500 мс):

python -m python_app.scripts.laser_temp_monitor \
    --config run_config.json --strategy interval:500

laser_temp_checker

Аргумент По умолчанию Назначение
--session <tmp>/laser_variation_session.json Снимок с целями и допуском
--readings <tmp>/laser_temp_readings.jsonl Какой канал показаний тайлить
--tolerance из сессии Переопределить допуск, °C
--reminder-every 0 (выкл.) Повторять предупреждение каждые N показаний, пока вне допуска
--from-start выкл. Проверить весь файл показаний, а не только новые строки

Разные пути для нескольких одновременных прогонов:

# монитор
python -m python_app.scripts.laser_temp_monitor --config cfg.json --start \
    --readings /tmp/run7.jsonl --session /tmp/run7.session.json
# чекер
python -m python_app.scripts.laser_temp_checker \
    --readings /tmp/run7.jsonl --session /tmp/run7.session.json --reminder-every 20

Интеграция с пайплайном Kamil ADC

Когда вариацию стартует штатный пайплайн (apply_kamil_adc_laser_control), снимок сессии session.json пишется автоматически. Достаточно запустить только чекер (и, при желании, монитор без --start, чтобы он опрашивал плату). Так консоль получит предупреждения о рассинхроне температуры прямо во время захвата.

Встраивание в свой код (без CLI)

import threading
from python_app.hardware_full.laser_control.controller import LaserController
from python_app.hardware_full.laser_control.monitoring import (
    LaserTemperatureMonitor, LaserTemperatureChecker, LaserVariationSession,
    ReadingWriter, ReadingReader, resolve_period_s,
)

# 1. Зафиксировать цели при старте вариации
session = LaserVariationSession(
    variation_type="CHANGE_CURRENT_LD1",
    target_temp1=28.0, target_temp2=28.9, tolerance_c=0.03,
)

# 2. Монитор (в проде controller — реальный LaserController)
period = resolve_period_s("computed", min_value=33.0, max_value=60.0, step=0.05,
                          time_step_us=50, delay_time_ms=10)
stop = threading.Event()
with LaserController(port="/dev/ttyUSB0") as ctrl, ReadingWriter("readings.jsonl") as w:
    monitor = LaserTemperatureMonitor(ctrl, w, period_s=period)
    threading.Thread(target=monitor.run, args=(stop,), daemon=True).start()

    # 3. Чекер: тайлить показания и валидировать оба лазера
    checker = LaserTemperatureChecker.from_session(session)
    reader = ReadingReader("readings.jsonl")
    while not stop.is_set():
        for reading in reader.poll():
            checker.process(reading)   # печатает WARNING при |Δ| > tolerance
        stop.wait(0.2)

LaserTemperatureChecker.evaluate(reading) возвращает список LaserDeviation (по лазеру: измеренное, цель, Δ, в допуске ли) без логирования — удобно для собственной обработки/накопления статистики.

Формат IPC-файлов

session.json:

{
  "variation_type": "CHANGE_CURRENT_LD1",
  "target_temp1": 28.0,
  "target_temp2": 28.9,
  "tolerance_c": 0.03,
  "started_at_iso": "2026-07-27T12:00:00"
}

readings.jsonl (по одной строке-объекту на свип):

{"seq":0,"mono_ns":123456789,"temp1":28.0,"temp2":28.9,"temp_ext1":22.0,"temp_ext2":23.0,"current1":33.0,"current2":35.0}

Как определяется «раз в свип»

Плата не отдаёт явную границу свипа, поэтому период оценивается из параметров:

num_steps    = round(|max_value - min_value| / step) + 1
per_point_s  = delay_time / 1000  +  time_step / 1_000_000
sweep_period = num_steps × per_point_s

Монитор публикует одно показание за такой период. Если нужен другой темп — --strategy interval:<ms>. (Внутренний счётчик TO6 платы существует, но его семантика не гарантирована, поэтому для тайминга он не используется.)

Тесты

python -m pytest python_app/tests/test_laser_temp_monitoring.py -q

Покрыто: round-trip сессии, tail JSONL (включая усечённую последнюю строку), расчёт периода свипа, маппинг измерений монитором, и валидация чекера по каждому лазеру отдельно (порог, граница допуска, повторные предупреждения, восстановление).