加入 langgraph-vivado-install, langgraph-plan-view, 更新 fft-analyse

This commit is contained in:
Myfro9
2026-05-22 17:18:38 -05:00
parent c7e4723d85
commit 15e8c5efa3
3 changed files with 259 additions and 24 deletions
+26 -14
View File
@@ -234,7 +234,10 @@ def analyse(
v = r.get(col, "").strip()
if v and v.lower() not in ("x", "z", "nan"):
# If it looks like hex (has a-f OR is exactly 4 hex digits)
if re.match(r"^[0-9a-fA-F]+$", v):
if v.lower().startswith("0x") or (
re.match(r"^[0-9a-fA-F]+$", v)
and any(c in v.lower() for c in "abcdef")
):
return True
break
return False
@@ -259,10 +262,14 @@ def analyse(
i_mean, q_mean = float(np.mean(i_data)), float(np.mean(q_data))
iq = (i_data[:fft_n] - i_mean) + 1j * (q_data[:fft_n] - q_mean)
win = np.hanning(fft_n)
cg = float(np.sum(win))
cg = float(np.sum(win)) # coherent gain (for peak amplitude)
win_power = float(np.sum(win ** 2)) # for PSD normalisation
fft_data = np.fft.fftshift(np.fft.fft(iq * win, n=fft_n))
amp = np.abs(fft_data) / cg
amp = np.abs(fft_data) / cg # peak amplitude (linear, for OBW / peaks)
dbfs = 20.0 * np.log10((amp + np.finfo(np.float64).eps) / full_scale)
# Power spectral density: dBFS/Hz (normalised to full-scale power)
psd = (np.abs(fft_data) ** 2) / (full_scale ** 2 * win_power * fs_hz)
psd_dbfs_hz = 10.0 * np.log10(psd + np.finfo(np.float64).eps)
freq_hz = (np.arange(fft_n) - fft_n / 2.0) * fs_hz / fft_n
freq_mhz = freq_hz / 1e6
@@ -279,6 +286,7 @@ def analyse(
result = dict(
i_data=i_data, q_data=q_data, mag_time=mag_time, i_mean=i_mean, q_mean=q_mean,
fft_n=fft_n, freq_hz=freq_hz, freq_mhz=freq_mhz, amp=amp, dbfs=dbfs,
psd_dbfs_hz=psd_dbfs_hz,
obw90=obw90, obw99=obw99, frac90=frac90, frac99=frac99, peaks=peaks,
fs_hz=fs_hz, full_scale=full_scale, i_col=i_col, q_col=q_col,
input_name=input_path.name, skip=s,
@@ -366,8 +374,9 @@ def _plot(result: dict, xlim_mhz, out_dir: Path, prefix: str, title: str | None)
axes[1].grid(True, alpha=0.3)
axes[1].legend(loc="upper right")
# panel 3 — spectrum
axes[2].plot(freq_mhz, dbfs, color="#1f5a95", linewidth=1.0)
# panel 3 — spectrum (PSD)
psd_dbfs_hz = result["psd_dbfs_hz"]
axes[2].plot(freq_mhz, psd_dbfs_hz, color="#1f5a95", linewidth=1.0)
obw90, obw99 = result["obw90"], result["obw99"]
if obw90 > 0:
axes[2].axvline(-obw90 / 2, color="#ff7f0e", ls="--", lw=1.3,
@@ -377,23 +386,26 @@ def _plot(result: dict, xlim_mhz, out_dir: Path, prefix: str, title: str | None)
axes[2].axvline(-obw99 / 2, color="#9467bd", ls="--", lw=1.3,
label=f"99% OBW: {obw99:.2f} MHz")
axes[2].axvline( obw99 / 2, color="#9467bd", ls="--", lw=1.3)
# annotate top peaks using PSD value at peak bin
for p in result["peaks"][:5]:
if xlim_mhz[0] <= p["freq_mhz"] <= xlim_mhz[1]:
axes[2].plot(p["freq_mhz"], p["dbfs"], "o", color="#c23b22", ms=6)
axes[2].annotate(f"{p['freq_mhz']:.2f} MHz", (p["freq_mhz"], p["dbfs"]),
psd_val = float(psd_dbfs_hz[p["bin"]])
axes[2].plot(p["freq_mhz"], psd_val, "o", color="#c23b22", ms=6)
axes[2].annotate(f"{p['freq_mhz']:.2f} MHz\n{psd_val:.1f} dBFS/Hz",
(p["freq_mhz"], psd_val),
xytext=(0, 8), textcoords="offset points",
ha="center", fontsize=11)
ha="center", fontsize=10)
axes[2].set_title(f"{fft_n}-pt complex FFT, Hann window, DC removed, "
f"fs = {fs_hz/1e6:.2f} MHz, FS ref = {result['full_scale']:.0f}")
axes[2].set_xlabel("Frequency (MHz)")
axes[2].set_ylabel("Magnitude (dBFS)")
axes[2].set_ylabel("PSD (dBFS/Hz)")
axes[2].set_xlim(xlim_mhz)
in_view = (freq_mhz >= xlim_mhz[0]) & (freq_mhz <= xlim_mhz[1])
if np.any(in_view):
y_top = max(-20.0, float(np.ceil(np.max(dbfs[in_view]) / 10) * 10 + 10))
y_top = max(-60.0, float(np.ceil(np.max(psd_dbfs_hz[in_view]) / 10) * 10 + 10))
else:
y_top = 10.0
axes[2].set_ylim(-140, y_top)
y_top = -60.0
axes[2].set_ylim(y_top - 120, y_top)
axes[2].grid(True, alpha=0.3)
axes[2].legend(loc="lower right")
@@ -406,8 +418,8 @@ def _plot(result: dict, xlim_mhz, out_dir: Path, prefix: str, title: str | None)
def _auto_fs(path: Path) -> float:
name = path.stem.lower()
for pat, hz in [("491", 491.52e6), ("245", 245.76e6), ("240m", 245.76e6),
("122", 122.88e6), ("120m", 122.88e6), ("61", 61.44e6),
for pat, hz in [("491", 491.52e6), ("245", 245.76e6), ("240m", 240e6),
("122", 122.88e6), ("120m", 120e6), ("61", 61.44e6),
("250m", 250e6), ("125m", 125e6)]:
if pat in name:
return hz