#!/usr/bin/env python3 """ Реалтайм-плоттер для свипов из виртуального COM-порта. Формат строк: - "Sweep_start" — начало нового свипа (предыдущий считается завершённым) - "s CH X Y" — точка (номер канала, индекс X, значение Y), все целые со знаком Отрисовываются четыре графика: - Сырые данные: последний полученный свип (Y vs X) - Водопад сырых данных: последние N свипов - FFT текущего свипа - B-scan: водопад FFT-строк Зависимости: numpy. PySerial опционален — при его отсутствии используется сырой доступ к TTY через termios. GUI: matplotlib (совместимый) или pyqtgraph (быстрый). """ import argparse import sys def build_parser() -> argparse.ArgumentParser: 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( "--spec-mean-sec", type=float, default=0.0, help=( "Вычитание среднего по каждой частоте за последние N секунд " "в водопаде спектров (0 — отключить)" ), ) 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( "--norm-type", choices=["projector", "simple"], default="projector", help="Тип нормировки: projector (по огибающим в [-1000,+1000]) или simple (raw/calib)", ) parser.add_argument( "--bin", dest="bin_mode", action="store_true", help="Бинарный протокол: 16-bit поток, 0xFFFF+канал для старта свипа, точки point,value,'\\n'", ) return parser def main(): args = build_parser().parse_args() if args.backend == "pg": from rfg_adc_plotter.gui.pyqtgraph_backend import run_pyqtgraph try: run_pyqtgraph(args) except Exception as e: sys.stderr.write(f"[error] PyQtGraph бэкенд недоступен: {e}\n") sys.exit(1) return if args.backend == "auto": try: from rfg_adc_plotter.gui.pyqtgraph_backend import run_pyqtgraph run_pyqtgraph(args) return except Exception: pass # Откатываемся на matplotlib from rfg_adc_plotter.gui.matplotlib_backend import run_matplotlib run_matplotlib(args) if __name__ == "__main__": main()