ai: студенческая копия лабораторной 5 (DDS-генератор RP2040 + АЧХ интегратора) из РПД
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Хост-скрипт: автоматическое снятие АЧХ с DDS-генератора (RP2040).
|
||||
|
||||
ЧЕРНОВИК (AI-сгенерировано, 2026-10-26). Требует проверки преподавателем.
|
||||
|
||||
Управляет генератором 01-dds-generator-rp2040.ino по Serial, запускает
|
||||
логарифмический свип, собирает точки "P <Гц> <мВ>" и строит АЧХ в дБ.
|
||||
|
||||
Использование:
|
||||
python 02-izmerenie-achh.py --port /dev/ttyACM0 \
|
||||
--f0 10 --f1 100000 --n 25 --dwell 150 --out achh.csv
|
||||
|
||||
Зависимости: pyserial (обязательно), matplotlib (опционально, для графика).
|
||||
pip install pyserial matplotlib
|
||||
|
||||
ПРОТОКОЛ (строки, \n):
|
||||
-> W S режим синуса
|
||||
-> SWEEP <f0> <f1> <n> <dwell_ms>
|
||||
<- P <Гц> <мВ> точка АЧХ
|
||||
<- DONE свип завершён
|
||||
|
||||
ВНИМАНИЕ: вход ADC0 генератора — 0..3,3 В. Сигнал с выхода исследуемой
|
||||
цепи выше 3,3 В подавать только через делитель/пиковый детектор.
|
||||
Нормировка на выходе: gain_dB = 20*log10(Uout/Uin). Входное напряжение
|
||||
Uin берётся из аргумента --uin (действующая или амплитудная — как задано,
|
||||
скрипт работает с размахом, мВ).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
|
||||
try:
|
||||
import serial # type: ignore
|
||||
except ImportError: # pragma: no cover
|
||||
print("Нужен pyserial: pip install pyserial", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
def open_port(port: str, baud: int = 115200, timeout: float = 0.2):
|
||||
return serial.Serial(port, baud, timeout=timeout)
|
||||
|
||||
|
||||
def send(ser, line: str) -> None:
|
||||
ser.write((line + "\n").encode("ascii"))
|
||||
ser.flush()
|
||||
|
||||
|
||||
def collect_sweep(ser, f0: float, f1: float, n: int, dwell: int,
|
||||
uin_mv: float, timeout_s: float = 120.0):
|
||||
"""Запустить свип и вернуть список (f, uout_mv, gain_db)."""
|
||||
send(ser, "W S") # синус
|
||||
time.sleep(0.2)
|
||||
send(ser, f"SWEEP {f0:.3f} {f1:.3f} {n} {dwell}")
|
||||
|
||||
points: list[tuple[float, float, float]] = []
|
||||
t0 = time.time()
|
||||
while True:
|
||||
if time.time() - t0 > timeout_s:
|
||||
print("таймаут свипа", file=sys.stderr)
|
||||
break
|
||||
raw = ser.readline().decode("ascii", "replace").strip()
|
||||
if not raw:
|
||||
continue
|
||||
if raw == "DONE":
|
||||
break
|
||||
if raw.startswith("P "):
|
||||
_, fs, mv = raw.split()
|
||||
f = float(fs)
|
||||
uout = float(mv)
|
||||
gain = 20.0 * math.log10(max(uout, 1e-6) / max(uin_mv, 1e-6))
|
||||
points.append((f, uout, gain))
|
||||
print(f" {f:>9.1f} Гц {uout:>7.1f} мВ {gain:+6.2f} дБ")
|
||||
return points
|
||||
|
||||
|
||||
def write_csv(path: str, points) -> None:
|
||||
with open(path, "w", newline="", encoding="utf-8") as fh:
|
||||
w = csv.writer(fh)
|
||||
w.writerow(["freq_hz", "uout_mv", "gain_db"])
|
||||
for row in points:
|
||||
w.writerow([f"{row[0]:.3f}", f"{row[1]:.3f}", f"{row[2]:.4f}"])
|
||||
print(f"CSV: {path}")
|
||||
|
||||
|
||||
def plot(points) -> None:
|
||||
try:
|
||||
import matplotlib.pyplot as plt # type: ignore
|
||||
except ImportError:
|
||||
print("matplotlib нет — график пропущен (CSV сохранён)", file=sys.stderr)
|
||||
return
|
||||
xs = [p[0] for p in points]
|
||||
ys = [p[2] for p in points]
|
||||
plt.semilogx(xs, ys, "o-")
|
||||
plt.xlabel("Частота, Гц")
|
||||
plt.ylabel("Усиление, дБ")
|
||||
plt.grid(True, which="both")
|
||||
plt.title("АЧХ (автоснятие с RP2040 DDS)")
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Автоснятие АЧХ через RP2040 DDS")
|
||||
ap.add_argument("--port", required=True, help="например /dev/ttyACM0 или COM5")
|
||||
ap.add_argument("--f0", type=float, default=10.0)
|
||||
ap.add_argument("--f1", type=float, default=100000.0)
|
||||
ap.add_argument("--n", type=int, default=25)
|
||||
ap.add_argument("--dwell", type=int, default=150, help="мс на точку")
|
||||
ap.add_argument("--uin", type=float, default=500.0,
|
||||
help="входное напряжение (размах), мВ")
|
||||
ap.add_argument("--out", default="achh.csv")
|
||||
ap.add_argument("--plot", action="store_true")
|
||||
args = ap.parse_args()
|
||||
|
||||
ser = open_port(args.port)
|
||||
time.sleep(0.5)
|
||||
ser.reset_input_buffer()
|
||||
try:
|
||||
points = collect_sweep(ser, args.f0, args.f1, args.n, args.dwell, args.uin)
|
||||
finally:
|
||||
ser.close()
|
||||
if not points:
|
||||
print("точек не получено", file=sys.stderr)
|
||||
return 1
|
||||
write_csv(args.out, points)
|
||||
if args.plot:
|
||||
plot(points)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user