3. 光模块DDM实时监控数据采集

任务说明

通过 SFF-8472 标准,光模块 DDM(数字诊断监控)数据位于 A2h 页的 96~105 字节,共 5 个 16 位大端参数:温度、电压、偏置电流、发送光功率、接收光功率。本案例实现周期性读取这 5 个参数,按物理量换算后追加写入 CSV 文件,便于后续分析。

环境与依赖

  • Python 3.8+,依赖 pyserial(pip install pyserial)。
  • 光模块通过 I2C 转 USB 适配器(如 CH341、FT2232)接入,本案例使用其串口命令协议:发送 R <addr_hex> <len>,返回十六进制字符串。
  • 若使用其他适配器,只需替换 read_i2c 函数中的串口交互部分,其余逻辑不变。

DDM 数据换算

原始值为 16 位无符号整数(大端),换算关系:

  • 温度 = raw / 256.0(℃)
  • 电压 = raw / 10000.0(V)
  • 偏置电流 = raw × 2 / 1000.0(mA)
  • 收/发光功率 = raw / 10000.0(mW)
定时器触发 read_i2c(0xA2, 96, 10) 解析 5 个 16 位大端值 物理量换算 (温度/电压/…) 追加写入 CSV(时间戳 + 5 个字段) sleep(interval) → 下一轮采集

完整代码

import serial, time, csv, struct
from datetime import datetime

PORT = "COM3"          # Linux 下改为 /dev/ttyUSB0
BAUD = 9600
INTERVAL = 2.0         # 采集周期(秒)
CSV_FILE = "ddm_log.csv"
A2 = 0xA2              # DDM 页地址
DDM_OFFSET = 96        # DDM 起始字节
DDM_LEN = 10           # 5 个 16 位参数

def read_i2c(ser, dev_addr, offset, length):
    """通过适配器串口协议读取 I2C 数据,返回 bytes。"""
    cmd = f"R {dev_addr:02X} {offset:02X} {length:02X}\n"
    ser.reset_input_buffer()
    ser.write(cmd.encode())
    line = ser.readline().decode().strip()
    # 适配器返回形如 "D0 01 23 ..." 的十六进制串
    hexs = line.split()
    if len(hexs) != length:
        raise IOError(f"读取长度不符: {line!r}")
    return bytes(int(h, 16) for h in hexs)

def parse_ddm(raw):
    """解析 10 字节为 5 个物理量。"""
    temp, volt, bias, txp, rxp = struct.unpack(">5H", raw)
    return {
        "temperature_C": temp / 256.0,
        "voltage_V":     volt / 10000.0,
        "bias_mA":       bias * 2 / 1000.0,
        "tx_power_mW":   txp / 10000.0,
        "rx_power_mW":   rxp / 10000.0,
    }

def main():
    ser = serial.Serial(PORT, BAUD, timeout=1)
    fields = ["timestamp", "temperature_C", "voltage_V",
              "bias_mA", "tx_power_mW", "rx_power_mW"]
    with open(CSV_FILE, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        try:
            while True:
                raw = read_i2c(ser, A2, DDM_OFFSET, DDM_LEN)
                row = parse_ddm(raw)
                row["timestamp"] = datetime.now().isoformat(timespec="seconds")
                writer.writerow(row)
                f.flush()
                print(row)
                time.sleep(INTERVAL)
        except KeyboardInterrupt:
            print("采集结束")
        finally:
            ser.close()

if __name__ == "__main__":
    main()

运行与验证

  1. 确认适配器串口号(Windows 设备管理器 / Linux ls /dev/ttyUSB*),修改 PORT。
  2. 执行 python ddm_monitor.py,终端每 2 秒打印一行字典。
  3. 按 Ctrl+C 停止,检查当前目录下 ddm_log.csv:
timestamp,temperature_C,voltage_V,bias_mA,tx_power_mW,rx_power_mW
2024-05-01T10:00:00,42.35,3.3012,6.512,0.5012,0.4780
2024-05-01T10:00:02,42.37,3.3010,6.514,0.5010,0.4778

通过条件:CSV 表头正确、每行时间戳递增、5 个数值落在光模块合理范围(温度 -40~100℃、电压 3.0~3.6V、偏置电流 0~100mA、光功率 0~10mW)。若读取长度不符会抛出 IOError,说明适配器协议或地址需核对。