适用文件:python_ctypes_example.py
运行环境:64 位 Windows、64 位 Python 3
示例用途:读取设备信息并完成一次指定波长范围的光谱测量

1. 示例功能

python_ctypes_example.py 是一个可以直接运行的交互式 Python 示例。程序会按照提示完成以下操作:

  1. 搜索并打开 PynectDirect 光谱仪。
  2. 读取设备序列号。
  3. 读取并显示设备完整波长范围。
  4. 读取当前积分时间和平均次数。
  5. 让用户输入本次测量使用的积分时间和平均次数。
  6. 让用户输入准备读取的起始波长和结束波长。
  7. 显示设备实际选择的波长范围。
  8. 读取所选范围内的全部波长值和对应的强度值。
  9. 说明实际读取的总点数,并等间隔打印最多 10 组代表数据。
  10. 恢复运行前的积分时间和平均次数,并关闭设备。

本例程设置的是本次运行期间使用的临时测量参数,不会写入设备的默认固化配置。

2. 运行环境和文件布局

2.1 Python 要求

必须使用 64 位 Python。可以执行以下命令检查:

python -c "import struct; print(struct.calcsize('P') * 8)"

正确输出为:

64

本例程只使用 Python 标准库,不需要执行 pip install

2.2 推荐的交付包布局

在完整 SDK 交付包中,文件布局如下:

PynectDirect_SDK/
|-- bin/
|   |-- PynectDirect.dll
|   `-- ftd2xx.dll
`-- examples/
    |-- python_ctypes_example.py
    `-- python_ctypes_example_guide.md

PynectDirect.dllftd2xx.dll 必须放在同一个目录中。程序可以从交付包的 bin 目录自动找到它们。

2.3 单独复制例程时的布局

如果只复制 Python 例程,建议使用同级 dll 目录:

my_spectrum_demo/
|-- python_ctypes_example.py
|-- python_ctypes_example_guide.md
`-- dll/
    |-- PynectDirect.dll
    `-- ftd2xx.dll

程序也支持把两个 DLL 直接放在 python_ctypes_example.py 同级目录。

3. 运行例程

打开 PowerShell,进入 SDK 交付包目录后执行:

python .\examples\python_ctypes_example.py

如果已经进入 examples 目录,也可以执行:

python .\python_ctypes_example.py

程序会扫描设备。只连接一台设备时自动使用设备索引 0;连接多台设备时,会先提示选择设备索引。

4. 逐项输入说明

程序会先读取设备当前状态,再提示输入本次测量参数。方括号中的数值是默认值,直接按 Enter 即采用该值。

提示项目 输入类型 单位或范围 直接按 Enter 作用
设备索引 整数 扫描结果中的 0 至最大索引 使用 0 选择要打开的设备;仅多设备时出现
积分时间 正整数 微秒(us),不超过设备允许范围 保留当前积分时间 设置本次测量的临时积分时间
平均次数 整数 1-255 保留当前平均次数 设置本次测量的临时平均次数
起始波长 数字 nm,位于完整波长范围内 使用完整范围起点 选择本次读取范围的起点
结束波长 数字 nm,位于完整波长范围内 使用完整范围终点 选择本次读取范围的终点

起始波长必须小于结束波长。输入文字、超出范围的数值或无效组合时,程序会说明问题并重新提示,不需要重新启动。

4.1 输入提示示例

假设设备当前积分时间为 50000 us,平均次数为 1,完整波长范围为 350-850 nm

Device serial number: PYN-TEST-001
Full wavelength range: 350.000-850.000 nm
Current integration time: 50000 us
Current average count: 1
Integration time in us [50000]: 100000
Average count [1]: 3
Start wavelength in nm [350.000]: 400
End wavelength in nm [850.000]: 600

以上输入表示:

  • 本次测量使用 100000 us 的积分时间。
  • 每个输出光谱点使用 3 次平均。
  • 只读取请求范围 400-600 nm 内的光谱数据。

4.2 全部采用默认值

如果希望保留当前积分时间和平均次数,并读取完整波长范围,在四个参数提示处直接按 Enter 即可。

5. 输出内容说明

5.1 测量摘要

采集完成后,程序首先输出:

输出项目 含义
Device serial number 设备序列号
Full wavelength range 设备支持的完整波长范围
Integration time 本次测量实际使用的积分时间,单位 us
Average count 本次测量实际使用的平均次数
Requested wavelength range 用户输入的起始波长和结束波长
Actual wavelength range 设备按实际像素位置匹配后的范围
Actual wavelength points read 所选范围内实际读取的数据点总数

请求范围和实际范围可能存在很小差异。这是因为设备只能选择实际存在的像素和标定波长点。处理数据时应以 Actual wavelength range 和实际返回数组为准。

5.2 波长值和强度值

例程会读取所选范围内的全部光谱点,但为了让控制台输出便于查看,只打印最多 10 组代表数据。实际点数不少于 10 时,程序按原始索引等间隔选取 10 组,并保留首点和尾点;实际点数少于 10 时,程序打印全部数据。

例如,某次测量实际读取 810 个点时,输出开头如下:

Actual wavelength points read: 810
Showing 10 evenly spaced points:
Index   Wavelength (nm)   Intensity
0       800.090000        1200
90      811.197000        1380
...
809     899.911000        1760

三列含义如下:

  • Index:所选范围内的数据序号,从 0 开始。
  • Wavelength (nm):该点的波长值,单位 nm。
  • Intensity:相同索引位置对应的原始光谱强度值,范围为 0-65535

波长值与强度值按索引一一对应。例如,索引 1500.000000 nm 对应强度值 2300。不要对两个数组分别排序,否则会破坏这种对应关系。

控制台只省略显示,不会丢弃测量数据。run_interactive_measurement() 返回的 result.wavelengthsresult.intensities 仍包含本次实际读取的全部数据;上例中两个列表都包含 810 个元素。

6. 在自己的程序中复用

6.1 读取完整光谱

原有 read_full_spectrum(device_index=0) 函数继续保留。它不进行交互输入,返回两个普通 Python 列表:

from python_ctypes_example import read_full_spectrum

wavelengths, intensities = read_full_spectrum(device_index=0)
print("Point count:", len(wavelengths))
print("First pair:", wavelengths[0], intensities[0])

6.2 执行交互式选择波段测量

run_interactive_measurement() 会显示逐项提示,并返回 SelectedSpectrumResult

from python_ctypes_example import run_interactive_measurement

result = run_interactive_measurement()
for wavelength, intensity in zip(
    result.wavelengths, result.intensities
):
    print(wavelength, intensity)

结果对象的主要字段如下:

字段 类型 含义
serial_number str 设备序列号
full_start_nmfull_end_nm float 完整波长范围
integration_us int 本次积分时间,单位 us
average_count int 本次平均次数
request_start_nmrequest_end_nm float 用户请求范围
actual_start_nmactual_end_nm float 设备实际匹配范围
start_pixelend_pixel int 实际起止像素
wavelengths list 所选范围的波长列表
intensities list 与波长列表对应的强度列表

正常返回时,len(result.wavelengths)len(result.intensities) 始终相同。

7. 参数恢复和设备关闭

程序会在修改前保存原积分时间和平均次数。完成采集或遇到采集错误时,程序都会尝试:

  1. 恢复原平均次数。
  2. 恢复原积分时间。
  3. 关闭设备。

如果恢复失败,程序会输出以 WARNING: 开头的提示。此时不要继续测量,应关闭其他可能使用设备的软件,重新打开设备并检查参数。

程序正常结束后,选择波长范围不会写入设备的长期配置。下次运行仍会重新读取完整范围并提示输入。

8. 常见错误

8.1 找不到 PynectDirect.dll

可能看到:

ERROR: PynectDirect.dll not found

检查:

  • 文件名是否严格为 PynectDirect.dll
  • PynectDirect.dllftd2xx.dll 是否位于同一个目录。
  • DLL 是否位于脚本同级目录、同级 dll 目录或交付包 bin 目录。

8.2 Python 位数不正确

如果使用 32 位 Python,会看到需要 64 位 Python 的提示。安装并使用 64 位 Python 后重新运行。

8.3 没有发现设备

可能看到:

ERROR: No PynectDirect device found

检查 USB 连接和设备供电,并关闭其他正在占用光谱仪的软件。

8.4 输入值无效

  • 积分时间必须是正整数,单位为微秒。
  • 平均次数必须在 1-255 之间。
  • 起始波长和结束波长必须位于显示的完整范围内。
  • 起始波长必须小于结束波长。

程序会重新提示,按要求输入即可。

8.5 API 返回负错误码

错误信息会包含操作名称和返回码,例如:

ERROR: Get selected spectrum data failed, return code=-9

常见返回码包括:

返回码 含义
-3 设备未打开
-4 设备通信或返回数据异常
-5 参数或数组容量无效
-7/-8 数据校验或数据包异常
-9 设备响应超时
-11 当前设备不支持该功能

完整错误码和 API 参数说明请查阅 docs/PynectDirect_API_Guide.md

9. 安全说明

  • 本例程只修改当前测量使用的临时积分时间和平均次数。
  • 本例程不会调用 Flash、固化配置、氙灯、DAC 或设备复位功能。
  • 不要在设备采集过程中拔出 USB 线。
  • 较长积分时间和较大平均次数会增加等待时间,并不表示程序失去响应。
  • 使用自己的程序调用时,也应保证异常路径最终关闭设备。