内容
# 西格伦特 SDS MCP
用于通过原始 TCP 上的 SCPI 控制西格伦特 SDS824X HD 示波器。
**项目状态**:SDS824X HD 硬件测试版。基于测量的自动设置功能在真实硬件上可用。
## 快速开始
```bash
# 安装
python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
# 测试
pytest -q
# 运行 MCP 服务器(标准输入输出,用于 AI 客户端配置)
python -m siglent_sds_mcp.server
# 快速连接测试
python examples/tcp_idn_test.py <示波器 IP 地址>
```
## 自动设置 — 一键屏幕设置
主要功能:指向未知信号,让示波器测量引擎找到可用的 VDIV / OFST / TDIV 设置。该命令还会保存最终设置路径的截图。
```bash
# 向后兼容的 CLI 名称;内部使用基于测量的自动设置
python examples/auto_find_waveform_tcp.py <示波器 IP 地址> --channels C1 C2 C3 C4
# 带有信号类型提示的更好触发斜率策略
python examples/auto_find_waveform_tcp.py <示波器 IP 地址> --signal-hint uart
# 直接 TTL 接线 / 1X 探头
python examples/auto_find_waveform_tcp.py <示波器 IP 地址> \
--channels C1 \
--signal-hint clock \
--probe 1
# 弱周期信号策略
python examples/auto_find_waveform_tcp.py <示波器 IP 地址> \
--channels C1 \
--signal-hint clock \
--noise-floor 0.05 \
--min-signal-vpp 0.005
# 仅在明确请求时重启采集
python examples/auto_find_waveform_tcp.py <示波器 IP 地址> --restart-after-capture
```
默认行为:`leave_stopped=true`。该工具故意在最终可见帧上让示波器停止。返回对象包括 `screen_hold`、`final_panel_state`、`measurements`、`final_settings`、`probe_steps`、`screenshot` 和 `compatibility_parameters`。
兼容性说明:`coarse_timebase`、`initial_vdiv`、`max_points` 和 `refine_attempts` 被历史 `auto_find_waveform` API 接受,因此旧的 MCP/CLI 调用者不会中断。当前的基于测量的适配器路径拥有实际的扫描和细化逻辑,因此这些兼容性字段在 JSON 中报告,但不被测量路径直接使用。
## 波形捕获模式
`get_waveform_tcp` 默认 `capture_mode="immediate"`。
```text
STOP -> WF? DAT2
```
这故意跳过了 `WFSU`。SDS824X HD 现场调试显示,在 STOP 后发送 `WFSU SP,1,NP,0,FP,0` 可以刷新/替换停止帧,然后再进行 `WF? DAT2`,这可能会丢失间歇性的 UART/RS485 突发信号,并且经常返回空闲帧。
对于稳定/重复信号,仅调用者可以请求旧配置路径:
```text
STOP -> WFSU SP,1,NP,0,FP,0 -> WF? DAT2
```
通过在 MCP 中设置以下内容来使用它:
```json
{
"capture_mode": "configured"
}
```
元数据记录 `capture.mode`、`capture.wfsu_sent`、`decode.dt_source`、`parsed.trdl_s`、`parsed.effective_start_s`、`parsed.effective_end_s`,以及在使用备用时间时发出警告。
## UART 解码
`analyze_uart_csv_file` 现在执行真正的 UART 8N1 字节解码,而不仅仅是报告边缘时序。它检测下降沿起始位,采样 8 个数据位 LSB-first,验证停止位,并返回解码字节:
```json
{
"decoded_hex": "48 69",
"decoded_ascii": "Hi",
"frames": [
{"byte_hex": "0x48", "stop_ok": true, "framing_ok": true}
]
}
```
当两个电压电平可见时,阈值基于直方图,具有最小/最大中间值回落。这对于小幅度 UART(如高=5.2 V 和低=4.95 V)更鲁棒,因为简单的最小/最大 Vpp 规则过于脆弱。
## 架构
```
MCP 客户端(AI)
│ MCP 工具调用(标准输入输出)
▼
server.py — FastMCP 工具,自动重新连接
│
▼
sds_tcp_adapter.py — SDS800X HD 命令适配器
│ 通道 / 采集 / 触发 / 测量 / 截图 /
│ 波形捕获(WAVEDESC 自适应解码 + 包络抽取)/
│ 基于测量的自动设置
▼
waveform_capture.py — 立即/配置的 WF? DAT2 捕获模式
│ 立即模式通过跳过 WFSU 保存停止帧
▼
tcp_transport.py — RawTcpTransport
│ 套接字级 SCPI,IEEE 488.2 二进制块解析器,
│ 线程安全(RLock),预查询套接字刷新
▼
auto_setup.py — 历史 auto_find_waveform API 的兼容性包装
西格伦特 SDS824X HD 示波器(LAN 端口 5025)
```
## 命令验证流程
```
候选 → 官方文档 → 已测试 → 已实施 → 安全工具
```
在 `docs/sds824x-hd-command-matrix.md` 中跟踪。不要将未经测试的命令作为默认 MCP 工具公开。
## 关键设计决策
### 基于测量的自动设置
`SDS800XHDTcpAdapter.auto_setup()` 使用示波器测量(`PKPK`、`MEAN`、`FREQ`、`PER`、`MAX`、`MIN`)选择显示设置。这避免了依赖单独的离线 CSV 分析器进行第一遍屏幕设置。
### 弱周期信号策略
`noise_floor_v` 被视为强信号阈值。当示波器报告有效的 `FREQ` 或 `PER` 且 `PKPK >= min_signal_vpp` 时,可以接受低幅度信号。这处理了实际现场观察到的情况,例如稳定的 7.89 kHz 信号,仅约 22.5 mV 峰峰值。
### WAVEDESC 自适应解码
`WF? DAT2` 返回 8 位有符号字节。电压解码查询 `WF? DESC` 获取 WAVEDESC 描述符,并使用描述符导出的 `codes_per_div` 和当前面板 `VDIV? / OFST?` 进行解码。
### 时间基准和 dt 策略
DAT2 时序应尽可能使用 WAVEDESC `HORIZ_INTERVAL`。`SARA?` 被记录为元数据,仅用作备用。当 `dt_source` 备用到 SARA 或 TDIV 时,会发出元数据警告,因为如果备用不匹配 DAT2 内存间隔,UART/协议解码可能会出错。
### 最小/最大包络抽取
当 `max_points` <原始样本数时,每个桶输出最小值和最大值,而不是天真的步长采样。保留了步长会错过的毛刺/尖峰。
### ARM/STOP 行为
`get_waveform_tcp` 立即模式冻结当前帧并直接读取 DAT2。这是间歇性信号的默认设置。较旧的 WFSU 路径仅通过 `capture_mode="configured"` 可用。
### 触发电平策略
`C?:TRLV <level>` 在 SDS824X HD 固件 `4.8.12.1.1.6.5` 上是一个已知问题。基于显示的自动设置默认不依赖此命令。必须显式请求 `set_trigger_level=true`。
## 项目结构
```
src/siglent_sds_mcp/
server.py — MCP 工具,自动重新连接,FastMCP
sds_tcp_adapter.py — 命令适配器,WAVEDESC 解码,包络,自动设置
waveform_capture.py — WF? DAT2 捕获模式,立即跳过 WFSU
tcp_transport.py — 原始 TCP 套接字,锁,二进制块解析器
auto_setup.py — 历史 auto_find_waveform API 的兼容性包装
uart_analyzer.py — 离线 UART CSV 分析器和 8N1 字节解码器
rs485_analyzer.py — RS485 差分对分析器
modbus_timing.py — Modbus RTU 时序计算器
report.py — Markdown 字段报告生成器
artifacts.py — 带时间戳的工件路径,JSON 编写器
transport.py — PyVISA 备用(旧版,未连接到 MCP)
scope_driver.py — 带有安全门的 SiglentSDSDriver(旧版)
docs/
architecture.md — 分层设计,UART 捕获参考
sds824x-hd-command-matrix.md — 按命令验证状态
sds824x-hd-knowledge-base.md — 仪器特定知识
verification-workflow.md — 硬件验证流程
tests/ — pytest,解析器测试,TCP 传输测试,自动设置助手测试
examples/ — auto_find_waveform_tcp,TCP IDN 探针,波形/RS485 捕获
```
## 安全模型
| 允许 | 阻止 |
|---------|---------|
| `*IDN?`,运行/停止/单次 | `*RST`,工厂重置 |
| 通道/时间基准/触发器设置 | 固件更新 |
| 测量查询 | 网络配置更改 |
| 截图/波形获取 | 文件删除/格式化 |
| 离线波形分析 | 任意 SCPI 写入 |
原始 SCPI 写入不作为 MCP 工具公开。`safe_scpi_query_tcp` 仅接受后缀为 `?` 的命令。
## 测试覆盖率
```bash
pytest -q
```
关键测试区域:
- `test_unit_parsing.py` — `_parse_voltage`、`_parse_time`、`_parse_sample_rate`
- `test_wavedesc.py` / `test_wavedesc_parser.py` — 合成 WAVEDESC 解码,ASCII 前缀处理
- `test_tcp_binary_prefix.py` — `query_binary` IEEE 488.2 / BMP 前缀跳过
- `test_auto_setup.py` — `_pick_vdiv`、`_pick_tdiv`、测量解析器和 SCPI 数字格式化
- `test_auto_find_compat.py` — 弱周期检测,屏幕保持,截图工件,兼容性参数
- `test_waveform_capture_modes.py` — 立即模式跳过 WFSU;配置模式发送 WFSU 并带有警告
- `test_uart_decoder.py` — UART 8N1 字节解码,十六进制/ASCII 输出,低 Vpp 阈值
- `test_tcp_transport_parser.py` — 套接字对二进制块解析
## 目标设备
- **西格伦特 SDS824X HD** / SDS800X HD 系列
- 原始 TCP 上的 SCPI,端口 5025
- 已验证的固件:4.8.12.1.1.6.5
## 参考
- `MagnusJohansson/siglent-sds-mcp` — 上游西格伦特 SDS MCP 参考(SDS1104X-E 类)
- [`docs/upstream-reference.md`](docs/upstream-reference.md)