115 lines
4.6 KiB
Python
Executable File
115 lines
4.6 KiB
Python
Executable File
#!/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()
|