99 lines
4.4 KiB
Python
99 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Точка входа для RFG ADC Data Plotter.
|
||
|
||
Реалтайм-плоттер для свипов из виртуального COM-порта.
|
||
|
||
Формат строк:
|
||
- "Sweep_start" — начало нового свипа (предыдущий считается завершённым)
|
||
- "s X Y" — точка (индекс X, значение Y), все целые со знаком
|
||
|
||
Отрисовываются шесть графиков:
|
||
- Левый верхний: последний полученный свип (Y vs X)
|
||
- Правый верхний: водопад (последние N свипов во времени)
|
||
- Левый средний: FFT спектр текущего свипа
|
||
- Правый средний: B-scan (водопад FFT спектров)
|
||
- Левый нижний: Фаза спектра (развернутая)
|
||
- Правый нижний: Водопад фазы
|
||
|
||
Оптимизации для скорости:
|
||
- Парсинг и чтение в фоновой нити
|
||
- Анимация с обновлением только данных (без лишнего пересоздания фигур)
|
||
- Кольцевой буфер под водопад с фиксированным числом свипов
|
||
|
||
Зависимости: matplotlib, numpy. PySerial опционален — при его отсутствии
|
||
используется сырой доступ к TTY через termios.
|
||
"""
|
||
|
||
import argparse
|
||
import sys
|
||
|
||
|
||
def main():
|
||
"""Основная функция CLI."""
|
||
parser = argparse.ArgumentParser(
|
||
description=(
|
||
"Читает свипы из виртуального COM-порта и рисует: "
|
||
"последний свип и водопад (реалтайм)."
|
||
)
|
||
)
|
||
parser.add_argument(
|
||
"port",
|
||
help="Путь к порту, например /dev/ttyACM1 или COM3 (COM10+: \\\\.\\COM10)",
|
||
)
|
||
parser.add_argument("--baud", type=int, default=115200, help="Скорость (по умолчанию 115200)")
|
||
parser.add_argument("--max-sweeps", type=int, default=200, help="Количество видимых свипов в водопаде")
|
||
parser.add_argument("--max-fps", type=float, default=30.0, help="Лимит частоты отрисовки, кадров/с")
|
||
parser.add_argument("--cmap", default="viridis", help="Цветовая карта водопада")
|
||
parser.add_argument(
|
||
"--spec-clip",
|
||
default="2,98",
|
||
help=(
|
||
"Процентильная обрезка уровней водопада спектров, % (min,max). "
|
||
"Напр. 2,98. 'off' — отключить"
|
||
),
|
||
)
|
||
parser.add_argument("--title", default="ADC Sweeps", help="Заголовок окна")
|
||
parser.add_argument(
|
||
"--fancy",
|
||
action="store_true",
|
||
help="Заполнять выпавшие точки средними значениями между соседними",
|
||
)
|
||
parser.add_argument(
|
||
"--ylim",
|
||
type=str,
|
||
default=None,
|
||
help="Фиксированные Y-пределы для кривой формата min,max (например -1000,1000). По умолчанию авто",
|
||
)
|
||
parser.add_argument(
|
||
"--backend",
|
||
choices=["auto", "pg", "mpl"],
|
||
default="auto",
|
||
help="Графический бэкенд: pyqtgraph (pg) — быстрее; matplotlib (mpl) — совместимый. По умолчанию auto",
|
||
)
|
||
|
||
args = parser.parse_args()
|
||
|
||
# Попробуем быстрый бэкенд (pyqtgraph) при auto/pg
|
||
if args.backend in ("auto", "pg"):
|
||
try:
|
||
from .visualization.pyqtgraph_backend import run_pyqtgraph
|
||
return run_pyqtgraph(args)
|
||
except Exception as e:
|
||
if args.backend == "pg":
|
||
sys.stderr.write(f"[error] PyQtGraph бэкенд недоступен: {e}\n")
|
||
sys.exit(1)
|
||
# При auto — тихо откатываемся на matplotlib
|
||
|
||
# Fallback на matplotlib
|
||
try:
|
||
from .visualization.matplotlib_backend import run_matplotlib
|
||
return run_matplotlib(args)
|
||
except Exception as e:
|
||
sys.stderr.write(f"[error] Matplotlib бэкенд недоступен: {e}\n")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|