#!/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", ) parser.add_argument( "--ref-out", type=str, default=None, help="Сохранить медиану последних 1000 свипов в указанный файл при накоплении данных", ) parser.add_argument( "--ref-in", type=str, default=None, help="Загрузить медиану из файла и вычитать её из входящего сигнала", ) 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()