PynectDirect SDK Python 用户手册

适用对象:第一次使用 PynectDirect 光谱仪的学生、教师和应用开发人员
运行环境:64 位 Windows、64 位 Python 3
文档范围:设备使用、Python 调用、数据采集、API 查询和故障排查

1. 阅读本手册

1.1 学习目标

完成本手册后,你应当能够:

  1. 正确放置运行文件并完成只读设备检查。
  2. 使用 Python 扫描、打开和关闭光谱仪。
  3. 读取设备信息、波长数组和光谱强度数组。
  4. 设置积分时间、平均次数和采集波段。
  5. 将测量结果保存为 CSV,并绘制光谱曲线。
  6. 根据错误码定位常见问题。
  7. 在需要时查询全部 55 个公开 API。

1.2 支持的设备

PynectDirect 使用同一套 Python 调用方式支持两类设备:

  • UV/VIS:紫外可见光谱仪。
  • NIR:近红外光谱仪。

打开设备后会自动识别类型。两类设备的基础采谱流程相同,但氙灯、外触发、DAC 和固化配置等功能可能只在特定设备上可用。调用进阶功能前,应先读取设备能力。

1.3 常用术语

术语 含义
DLL Python 实际加载的 PynectDirect 功能库
API Python 可以调用的设备功能
设备索引 扫描结果中的编号,从 0 开始
芯片序列号 USB 芯片的唯一编号,适合区分多台设备
设备序列号 光谱仪自身的业务编号
波长点数 一次光谱中包含的数据点数量
nm 纳米,波长单位
积分时间 探测器累计信号的时间,单位为微秒
平均次数 多次采集后求平均的次数

1.4 风险等级

本手册对接口使用以下风险标记:

等级 含义
只读 不改变设备参数,适合初次测试
临时设置 改变当前运行参数,关闭或恢复后不应长期保留
需谨慎 会影响外部电路、光源或触发行为,只能在实验方案明确时使用
高风险 可能修改持久数据、复位设备或影响标定,不用于课堂自由实验

2. 五分钟快速开始

2.1 准备文件

推荐将文件整理为以下形式:

PynectDirect_User/
|-- minimal_test.py
|-- python_ctypes_example.py
`-- dll/
    |-- PynectDirect.dll
    `-- ftd2xx.dll

dll 文件夹与 minimal_test.py 位于同一级。两个 DLL 必须放在同一个 dll 文件夹中。

脚本也支持把 PynectDirect.dll 直接放在 minimal_test.py 旁边,但为了保持目录整洁,建议使用上面的布局。

2.2 检查 Python 位数

在文件夹空白处打开 PowerShell,然后运行:

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

正确输出为:

64

如果输出 32,需要安装 64 位 Python。32 位 Python 无法加载本 SDK 的 64 位 DLL。

2.3 连接设备

  1. 将光谱仪连接到电脑 USB 接口。
  2. 等待 Windows 完成设备识别。
  3. 关闭其他可能正在使用光谱仪的软件。
  4. 不要同时启动两个采集程序访问同一台设备。

2.4 运行只读测试

minimal_test.py 所在目录运行:

python .\minimal_test.py --read-only

典型输出如下。设备型号、序列号、波长范围和强度值会因设备而不同。

[PASS] DLL load: ...\dll\PynectDirect.dll
[PASS] API exports: 55 functions
[PASS] Device scan: found 1 device(s)
[PASS] Open device: 0
[PASS] Device information
[PASS] Wavelength count: 2048
[PASS] Full wavelength data: points=2048 ...
[PASS] Full spectrum: points=2048 ...
[PASS] Close device: closed

Summary: PASS=17 SKIP=2 FAIL=0

满足以下条件即可认为基础环境可用:

  • DLL loadPASS,且路径指向预期的 DLL。
  • API exports 显示 55 functions
  • Device scan 至少发现一台设备。
  • Full spectrumPASS
  • 最终汇总中的 FAIL=0

SKIP 不等于失败。只读模式会主动跳过参数写入和指定波段设置,因此通常会出现 SKIP

3. 使用最小测试工具

3.1 输出状态

状态 含义 处理方式
PASS 该检查成功 无需处理
SKIP 当前模式或设备能力不适用 查看同一行原因
FAIL 加载、参数、通信或数据检查失败 按第 14 章排查

3.2 测试项目说明

项目 检查内容
DLL load 是否找到并加载 PynectDirect.dll 及其依赖
API exports DLL 是否包含全部 55 个公开函数
Device scan 当前可访问的设备数量
FTDI chip serial USB 芯片序列号
Open device 是否成功打开指定设备
Capabilities 当前设备支持的功能集合
Device identity 协议、型号、设备序列号和硬件版本
Wavelength count 光谱数据点数量
Full wavelength data 完整波长数组是否可读
Full spectrum 是否成功采集强度数组
Close device 是否可靠释放设备

3.3 命令参数

python .\minimal_test.py --help
参数 默认值 用途
--dll PATH 自动查找 指定 PynectDirect.dll 的完整路径
--device-index N 0 测试扫描结果中的第 N 台设备
--read-only 关闭 只读测试,不改变当前参数
--integration-us N 50000 普通测试使用的临时积分时间,单位微秒
--average N 1 普通测试使用的临时平均次数,范围 1-255

常用命令:

# 第一次连接时推荐
python .\minimal_test.py --read-only

# 测试第二台设备
python .\minimal_test.py --read-only --device-index 1

# 临时使用 100 ms 积分时间和 3 次平均
python .\minimal_test.py --integration-us 100000 --average 3

# 明确指定 DLL
python .\minimal_test.py --dll .\dll\PynectDirect.dll --read-only

不带 --read-only 时,工具会先保存当前积分时间、平均次数和触发设置,再使用测试值,结束时恢复原值。发生异常时也会尝试恢复并关闭设备。该工具不会默认执行 Flash 写入、设备复位、固化参数、DAC 输出或氙灯控制。

4. 第一次连接设备

4.1 Python 文件布局

minimal_test.py 旁边新建 device_info.py

PynectDirect_User/
|-- minimal_test.py
|-- device_info.py
`-- dll/
    |-- PynectDirect.dll
    `-- ftd2xx.dll

minimal_test.py 已包含全部 API 的 Python 类型绑定。自己的程序可以直接复用这些经过验证的加载和绑定功能。

4.2 读取设备信息

将以下内容写入 device_info.py

import ctypes

from minimal_test import (
    PROTOCOL_AUTO,
    PynectDirectDeviceInfo,
    bind_api,
    decode_char_array,
    find_dll,
    load_library,
)


SUCCESS = 0


def require_success(code, operation):
    if code != SUCCESS:
        raise RuntimeError(f"{operation} failed, return code={code}")


sdk = load_library(find_dll())
bind_api(sdk)
opened = False

try:
    device_count = sdk.pynect_direct_scan_devices()
    if device_count <= 0:
        raise RuntimeError("没有发现可用设备")

    require_success(
        sdk.pynect_direct_set_protocol_preference(PROTOCOL_AUTO),
        "设置自动识别",
    )
    require_success(sdk.pynect_direct_open_device(0), "打开设备")
    opened = True

    info = PynectDirectDeviceInfo()
    require_success(
        sdk.pynect_direct_get_device_info(ctypes.byref(info)),
        "读取设备信息",
    )

    print("协议:", decode_char_array(info.protocol_name))
    print("型号:", decode_char_array(info.model))
    print("序列号:", decode_char_array(info.serial_number))
    print("硬件版本:", decode_char_array(info.hardware_version))
    print(f"能力值: 0x{info.capabilities:08X}")
finally:
    if opened:
        sdk.pynect_direct_close_device()

运行:

python .\device_info.py

预期会显示设备协议、型号、序列号、硬件版本和能力值。无论读取是否成功,finally 都会执行关闭操作。

4.3 基本调用顺序

所有常规程序都应遵循以下顺序:

  1. 加载 DLL 并绑定 API。
  2. 扫描设备。
  3. 选择设备索引或芯片序列号。
  4. 打开设备。
  5. 读取能力和设备信息。
  6. 设置临时参数并采集数据。
  7. finally 中关闭设备。

5. 第一次采集完整光谱

5.1 完整采集示例

SDK 已附带可直接运行的只读例程:

python .\python_ctypes_example.py

例程中的 read_full_spectrum(device_index=0) 返回两个普通 Python 列表:

from python_ctypes_example import read_full_spectrum

wavelengths, intensities = read_full_spectrum(device_index=0)
print(len(wavelengths), len(intensities))

该函数不会修改积分时间、平均次数或其他设备参数。下面给出相同流程的展开版本,便于学习每一步的调用方式。

在同一目录新建 capture_spectrum.py

import ctypes

from minimal_test import bind_api, find_dll, load_library


SUCCESS = 0


def require_success(code, operation):
    if code != SUCCESS:
        raise RuntimeError(f"{operation} failed, return code={code}")


sdk = load_library(find_dll())
bind_api(sdk)
opened = False

try:
    device_count = sdk.pynect_direct_scan_devices()
    if device_count <= 0:
        raise RuntimeError("没有发现可用设备")

    require_success(sdk.pynect_direct_open_device(0), "打开设备")
    opened = True

    point_count = ctypes.c_uint16()
    require_success(
        sdk.pynect_direct_get_wavelength_count(ctypes.byref(point_count)),
        "读取波长点数",
    )
    if point_count.value == 0:
        raise RuntimeError("设备返回的波长点数为 0")

    wavelengths = (ctypes.c_double * point_count.value)()
    intensities = (ctypes.c_uint16 * point_count.value)()

    require_success(
        sdk.pynect_direct_get_full_wavelength_data(
            wavelengths, point_count.value
        ),
        "读取波长数组",
    )
    require_success(
        sdk.pynect_direct_get_full_spectrum_data(
            intensities, point_count.value
        ),
        "采集光谱强度",
    )

    print("点数:", point_count.value)
    print(f"波长范围: {wavelengths[0]:.3f} - {wavelengths[-1]:.3f} nm")
    print("强度范围:", min(intensities), "-", max(intensities))
    print("前 5 个数据点:")
    for index in range(min(5, point_count.value)):
        print(f"  {wavelengths[index]:.3f} nm, {intensities[index]}")
finally:
    if opened:
        sdk.pynect_direct_close_device()

运行:

python .\capture_spectrum.py

典型结果:

点数: 2048
波长范围: 780.294 - 1002.186 nm
强度范围: 90 - 319
前 5 个数据点:
  780.294 nm, 116
  ...

具体数字取决于设备、光源、样品、积分时间和环境。

5.2 为什么先读取点数

不同型号可能返回不同的数据点数量。必须先调用 pynect_direct_get_wavelength_count,再按该数量分配波长和强度数组。数组过小会返回参数错误,写死 2048 会导致程序无法兼容其他型号。

5.3 一次采集得到什么

  • wavelengths[index]:第 index 个数据点对应的波长,单位 nm。
  • intensities[index]:同一位置的原始强度值。
  • 两个数组长度必须相同。
  • pynect_direct_get_full_spectrum_data 会取得一次新的光谱数据。

6. 理解光谱数据

6.1 波长和强度必须按索引配对

第 N 个波长只对应第 N 个强度:

wavelengths[0]  <-> intensities[0]
wavelengths[1]  <-> intensities[1]
...
wavelengths[N]  <-> intensities[N]

不要分别排序两个数组,也不要删除其中一个数组的单独数据点。

6.2 强度值的含义

光谱强度为无符号 16 位原始数值,理论存储范围为 0-65535。实际有效范围、饱和位置和暗信号水平由设备及测量条件决定。

原始强度不是透过率、吸光度或反射率。得到这些物理量通常还需要暗背景、参考光谱和相应计算流程。

6.3 数据质量检查

每次采集后至少检查:

  • 点数是否大于 0。
  • 波长是否从小到大排列。
  • 结束波长是否大于起始波长。
  • 强度是否全部接近 0。
  • 是否出现大量相同的最大值,可能表示饱和。
  • 多次测量的峰位置是否稳定。

7. 设置测量参数和波长范围

7.1 积分时间

积分时间单位为微秒。50000 表示 50 ms,100000 表示 100 ms。

import ctypes

old_integration = ctypes.c_uint()
require_success(
    sdk.pynect_direct_get_integration_time(ctypes.byref(old_integration)),
    "读取积分时间",
)
require_success(
    sdk.pynect_direct_set_integration_time(100000),
    "设置积分时间",
)
print("原积分时间:", old_integration.value, "us")

一般规律:

  • 信号过弱时,可逐步增加积分时间。
  • 信号饱和时,应减小积分时间。
  • 积分时间越长,单次测量等待时间通常越长。
  • 可用范围以具体设备说明为准;参数必须为正数。

该设置属于临时设置。实验结束前建议恢复原值。

7.2 平均次数

平均次数通常使用 1-255

import ctypes

old_average = ctypes.c_uint8()
require_success(
    sdk.pynect_direct_get_average_number(ctypes.byref(old_average)),
    "读取平均次数",
)
require_success(
    sdk.pynect_direct_set_average_number(3),
    "设置平均次数",
)
print("原平均次数:", old_average.value)

增加平均次数通常可以降低随机波动,但会增加采集时间。课堂实验可依次比较 135 次平均的差异。

7.3 恢复临时参数

修改参数前先读取旧值,并在 finally 中恢复:

old_integration = ctypes.c_uint()
old_average = ctypes.c_uint8()

require_success(
    sdk.pynect_direct_get_integration_time(ctypes.byref(old_integration)),
    "读取积分时间",
)
require_success(
    sdk.pynect_direct_get_average_number(ctypes.byref(old_average)),
    "读取平均次数",
)

try:
    require_success(
        sdk.pynect_direct_set_integration_time(100000),
        "设置积分时间",
    )
    require_success(
        sdk.pynect_direct_set_average_number(3),
        "设置平均次数",
    )
    # 在这里完成测量
finally:
    sdk.pynect_direct_set_average_number(old_average.value)
    sdk.pynect_direct_set_integration_time(old_integration.value)

7.4 指定波长范围

指定波段只改变本次程序返回的数据窗口,不改变设备的物理波长范围。

import ctypes

from minimal_test import PynectDirectSelectedWavelengthRange


request_start_nm = 900.0
request_end_nm = 950.0

full_start = ctypes.c_double()
full_end = ctypes.c_double()
require_success(
    sdk.pynect_direct_get_full_wavelength_range(
        ctypes.byref(full_start), ctypes.byref(full_end)
    ),
    "读取完整波长范围",
)

if not (
    full_start.value <= request_start_nm
    < request_end_nm <= full_end.value
):
    raise ValueError(
        f"请求范围必须位于 {full_start.value:.3f} - "
        f"{full_end.value:.3f} nm 内"
    )

require_success(
    sdk.pynect_direct_set_selected_wavelength_range(
        request_start_nm, request_end_nm
    ),
    "设置波长范围",
)

selected = PynectDirectSelectedWavelengthRange()
require_success(
    sdk.pynect_direct_get_selected_wavelength_range(
        ctypes.byref(selected)
    ),
    "读取实际波长范围",
)

wavelengths = (ctypes.c_double * selected.count)()
intensities = (ctypes.c_uint16 * selected.count)()
require_success(
    sdk.pynect_direct_get_selected_wavelength_data(
        wavelengths, selected.count
    ),
    "读取波段波长",
)
require_success(
    sdk.pynect_direct_get_selected_spectrum_data(
        intensities, selected.count
    ),
    "读取波段光谱",
)

print(
    f"请求范围: {selected.request_start_nm:.3f} - "
    f"{selected.request_end_nm:.3f} nm"
)
print(
    f"实际范围: {selected.actual_start_nm:.3f} - "
    f"{selected.actual_end_nm:.3f} nm"
)
print("点数:", selected.count)

实际范围可能与请求值略有差异,因为设备只能选择已有像素对应的波长点。后续数组长度必须使用 selected.count

8. 保存 CSV 数据

采集完成后,可使用 Python 自带的 csv 模块保存数据:

import csv
from datetime import datetime
from pathlib import Path


output_path = Path(__file__).resolve().parent / "spectrum.csv"
measured_at = datetime.now().isoformat(timespec="seconds")

with output_path.open("w", newline="", encoding="utf-8-sig") as file:
    writer = csv.writer(file)
    writer.writerow(["measured_at", measured_at])
    writer.writerow(["wavelength_nm", "intensity"])
    writer.writerows(zip(wavelengths, intensities))

print("数据已保存:", output_path)

生成的文件结构如下:

measured_at,2026-07-31T17:10:00
wavelength_nm,intensity
780.294,116
780.402,121
...

建议同时记录:

  • 设备型号和序列号。
  • 积分时间。
  • 平均次数。
  • 样品名称。
  • 暗背景或参考光谱信息。
  • 测量时间。

utf-8-sig 便于 Windows 表格软件正确识别中文。保存完成后再关闭设备,不要在采集过程中移动或断开 USB。

9. 绘制光谱曲线

9.1 安装绘图库

首次使用时运行:

python -m pip install matplotlib

9.2 从 CSV 绘图

spectrum.csv 旁边新建 plot_spectrum.py

import csv
from pathlib import Path

import matplotlib.pyplot as plt


csv_path = Path(__file__).resolve().parent / "spectrum.csv"
wavelengths = []
intensities = []

with csv_path.open("r", encoding="utf-8-sig", newline="") as file:
    reader = csv.reader(file)
    next(reader)  # 测量时间
    next(reader)  # 列标题
    for wavelength_nm, intensity in reader:
        wavelengths.append(float(wavelength_nm))
        intensities.append(int(intensity))

plt.figure(figsize=(9, 5))
plt.plot(wavelengths, intensities, color="#1261A0", linewidth=1.2)
plt.xlabel("Wavelength (nm)")
plt.ylabel("Intensity")
plt.title("PynectDirect Spectrum")
plt.grid(True, alpha=0.25)
plt.tight_layout()
plt.show()

运行:

python .\plot_spectrum.py

如果图形为空,先检查 CSV 是否包含数据、两列长度是否相同,以及波长列能否转换为数字。

10. 多设备使用

10.1 索引与芯片序列号

设备索引由当前扫描顺序决定,重新插拔后可能改变。长期绑定某台设备时,应记录芯片序列号,并按序列号打开。

import ctypes


device_count = sdk.pynect_direct_scan_devices()
if device_count <= 0:
    raise RuntimeError("没有发现设备")

chip_serials = []
for index in range(device_count):
    serial_buffer = ctypes.create_string_buffer(64)
    require_success(
        sdk.pynect_direct_get_chip_serial_by_index(
            index, serial_buffer, len(serial_buffer)
        ),
        f"读取设备 {index} 的芯片序列号",
    )
    chip_serial = serial_buffer.value.decode(
        "utf-8", errors="replace"
    )
    chip_serials.append(chip_serial)
    print(index, chip_serial)

target_serial = chip_serials[0]
require_success(
    sdk.pynect_direct_open_device_by_chip_serial(
        target_serial.encode("utf-8")
    ),
    "按芯片序列号打开设备",
)

10.2 多台设备的推荐顺序

同一个 Python 进程一次只操作一台当前设备:

  1. 扫描并保存全部芯片序列号。
  2. 按第一个序列号打开、采集、关闭。
  3. 按下一个序列号打开、采集、关闭。
  4. 每台设备的数据文件中记录对应芯片序列号和设备序列号。

不要在多个线程中同时调用同一个 DLL 实例。确需并行采集时,应先与设备供应方确认支持方式。

11. 识别设备能力

11.1 为什么要检查能力

不同设备支持的功能不同。基础采谱通常可用,但 UV/VIS 专用功能在 NIR 设备上可能返回 -11。正确做法是打开设备后读取能力值,再决定是否调用某项功能。

11.2 能力表

能力名称 数值 说明
PYNECT_DIRECT_CAP_INTEGRATION_TIME 0x00000001 积分时间
PYNECT_DIRECT_CAP_AVERAGE_NUMBER 0x00000002 平均次数
PYNECT_DIRECT_CAP_LEVEL_OUTPUT 0x00000004 电平输出
PYNECT_DIRECT_CAP_FLASH 0x00000008 Flash/FLS
PYNECT_DIRECT_CAP_WAVELENGTH_DATA 0x00000010 波长数据
PYNECT_DIRECT_CAP_SPECTRUM_DATA 0x00000020 光谱数据
PYNECT_DIRECT_CAP_TEMPERATURE 0x00000040 温度
PYNECT_DIRECT_CAP_HARDWARE_VERSION 0x00000080 硬件版本
PYNECT_DIRECT_CAP_CALIBRATION 0x00000100 标定系数
PYNECT_DIRECT_CAP_XENON 0x00000200 氙灯控制
PYNECT_DIRECT_CAP_USB_STATUS 0x00000400 USB 状态和速率参数
PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER 0x00000800 外触发
PYNECT_DIRECT_CAP_DAC 0x00001000 DAC 输出
PYNECT_DIRECT_CAP_STORED_CONFIG 0x00002000 固化配置
PYNECT_DIRECT_CAP_SLIT_WIDTH 0x00004000 狭缝宽度

11.3 Python 检查方式

import ctypes


CAP_EXTERNAL_TRIGGER = 0x00000800
capabilities = ctypes.c_uint32()
require_success(
    sdk.pynect_direct_get_capabilities(ctypes.byref(capabilities)),
    "读取设备能力",
)

if capabilities.value & CAP_EXTERNAL_TRIGGER:
    print("设备支持外触发")
else:
    print("设备不支持外触发")

12. 高级功能与安全

12.1 功能风险速查

功能 代表 API 风险 使用要求
设备信息和光谱读取 pynect_direct_get_* 只读 打开设备后使用
积分时间和平均次数 pynect_direct_set_integration_time 临时设置 先保存旧值,结束时恢复
GPIO 电平 pynect_direct_set_level_output 需谨慎 明确外部接线和位定义
氙灯 pynect_direct_set_xenon_* 需谨慎 确认光源、电源和散热条件
外触发 pynect_direct_set_external_trigger_config 需谨慎 确认触发电平和时序
DAC pynect_direct_set_dac_voltage 需谨慎 确认输出连接和允许码值
Flash 写入 pynect_direct_flash_write 高风险 备份数据并获得教师或供应方授权
固化配置 pynect_direct_set_stored_* 高风险 记录原值,确认设备型号和参数范围
设备复位 pynect_direct_reset_device 高风险 停止采集,准备重新初始化

12.2 氙灯控制

氙灯功能通常只在支持 PYNECT_DIRECT_CAP_XENON 的 UV/VIS 设备上可用。周期和高电平宽度的单位都是 10 ns。设置前必须确认:

  • 高电平宽度不大于周期。
  • 光源和设备允许连续或单次模式。
  • 实验现场具备必要的防护措施。
  • 异常退出时能够关闭光源。

不要把氙灯写操作放入自动启动程序。

12.3 外触发

推荐流程:

  1. 读取并记录原触发配置。
  2. 根据设备说明设置触发开关、类型和帧数。
  3. 由外部信号触发采集。
  4. 查询采集状态。
  5. 读取指定帧数据。
  6. 恢复原触发配置。

触发电平、接线和时序错误可能造成无数据或持续等待。课堂基础实验应保持触发关闭,使用普通光谱读取接口。

12.4 Flash 和固化配置

Flash 和固化参数会影响设备长期状态,不属于基础采谱操作。使用前必须:

  • 确认设备型号和目标扇区。
  • 备份原始数据并校验备份可读。
  • 明确写入数据长度和格式。
  • 获得教师、实验室管理员或设备供应方授权。
  • 写入后重新读取并核对结果。

学生练习不得执行 Flash 写入、固化配置或设备复位。

13. 错误处理与安全关闭

13.1 错误码

多数函数成功时返回 0,失败时返回负数:

返回值 名称 用户含义
0 PYNECT_DIRECT_SUCCESS 操作成功
-1 PYNECT_DIRECT_ERR_NOT_INITIALIZED 所需状态尚未建立,例如未设置指定波段
-2 PYNECT_DIRECT_ERR_DEVICE_NOT_FOUND 未找到目标设备
-3 PYNECT_DIRECT_ERR_DEVICE_NOT_OPEN 尚未打开设备
-4 PYNECT_DIRECT_ERR_COMM_FAIL 设备通信或返回数据异常
-5 PYNECT_DIRECT_ERR_INVALID_PARAM 参数、范围或缓冲区长度错误
-6 PYNECT_DIRECT_ERR_OPEN_FAILED 设备打开失败
-7 PYNECT_DIRECT_ERR_CRC 数据校验失败
-8 PYNECT_DIRECT_ERR_PREAMBLE 接收数据格式异常
-9 PYNECT_DIRECT_ERR_TIMEOUT 等待设备响应超时
-10 PYNECT_DIRECT_ERR_WPROT 写入条件或写保护状态不满足
-11 PYNECT_DIRECT_ERR_UNSUPPORTED 当前设备不支持该功能

注意以下例外:

  • pynect_direct_scan_devices 成功时返回设备数量,0 表示未发现设备。
  • pynect_direct_is_open 返回 10 表示状态。
  • pynect_direct_flash_read 成功时返回读取的字节数。
  • pynect_direct_external_spectrum_control 成功时返回数据字节数。
  • pynect_direct_close_device 没有返回值。

13.2 统一检查返回值

ERROR_NAMES = {
    -1: "状态未初始化",
    -2: "未找到设备",
    -3: "设备未打开",
    -4: "通信失败",
    -5: "参数错误",
    -6: "打开失败",
    -7: "数据校验失败",
    -8: "数据格式异常",
    -9: "设备响应超时",
    -10: "写保护状态不满足",
    -11: "设备不支持该功能",
}


def require_success(code, operation):
    if code != 0:
        reason = ERROR_NAMES.get(code, "未知错误")
        raise RuntimeError(
            f"{operation}失败: {reason}, return code={code}"
        )

13.3 始终关闭设备

设备成功打开后,后续代码必须使用 try/finally

opened = False
try:
    require_success(sdk.pynect_direct_open_device(0), "打开设备")
    opened = True
    # 读取或采集
finally:
    if opened:
        sdk.pynect_direct_close_device()

不要依赖程序自然退出释放设备。异常退出后,如果其他软件无法打开设备,先关闭残留的 Python 进程,再重新插拔 USB。

14. 故障排查

14.1 提示无法加载 DLL

依次检查:

  1. Python 位数检查结果是否为 64
  2. 文件名是否严格为 PynectDirect.dll
  3. ftd2xx.dll 是否与 PynectDirect.dll 在同一文件夹。
  4. dll 文件夹是否与 minimal_test.py 同级。
  5. 是否误把 DLL 放入多一层同名文件夹。
  6. 使用 --dll 指定路径后是否仍然失败。

测试命令:

python .\minimal_test.py --dll .\dll\PynectDirect.dll --read-only

14.2 Device scan 显示 0

  • 确认 USB 线支持数据传输,而不是仅供电。
  • 更换电脑 USB 接口,避免无供电的扩展接口。
  • 在 Windows 设备管理器中确认设备已识别。
  • 关闭其他光谱软件和残留的 Python 进程。
  • 重新插拔设备后再运行只读测试。

14.3 返回 -6,设备打开失败

最常见原因是设备已被另一个程序占用。关闭其他采集程序后重试。如果多台设备同时连接,先运行只读测试确认索引,再使用正确的 --device-index

14.4 返回 -9,设备响应超时

  • 检查 USB 连接是否松动。
  • 降低程序连续调用频率。
  • 确认外触发没有处于等待状态。
  • 关闭设备并重新打开;仍无效时重新插拔 USB。

14.5 返回 -11,设备不支持该功能

这通常不是故障。读取能力值,只调用当前设备明确支持的功能。NIR 设备不一定支持 UV/VIS 的氙灯、外触发、DAC 或固化配置。

14.6 返回 -5,参数错误

重点检查:

  • 平均次数是否在 1-255
  • 积分时间是否为正数并符合设备范围。
  • 起始波长是否小于结束波长。
  • 请求波段是否位于完整波长范围内。
  • 数组长度是否至少等于设备返回的点数。
  • 字符串缓冲区和数据缓冲区是否足够大。

14.7 指定波段返回 -1

必须先调用 pynect_direct_set_selected_wavelength_range,成功后再读取指定波段信息和数据。设备复位或重新打开后,应重新设置波段。

14.8 光谱全为低值、噪声大或饱和

  • 确认光源、光纤和样品位置。
  • 信号过低时逐步增加积分时间。
  • 噪声较大时适当增加平均次数。
  • 大量数据接近上限时减小积分时间。
  • 保持测量条件稳定,多采几次比较。

14.9 向技术支持提供什么

请准备:

  • minimal_test.py --read-only 的完整输出。
  • Python 版本和 64 位检查结果。
  • DLL 实际加载路径。
  • 设备型号、芯片序列号和设备序列号。
  • 出错 API 名称和返回值。
  • 能否被其他设备软件正常识别。

15. 完整 API 参考

本章所有 Python 示例均假定:已经使用 load_library() 加载 DLL,已经使用 bind_api(sdk) 设置函数类型,并已定义第 13.2 节中的 require_success()。需要设备连接的接口还假定设备已经成功打开。示例中的 ctypes 来自 Python 标准库。

参数表中的“输入”表示 Python 向 DLL 提供值;“输出”表示 DLL 将结果写入 Python 创建的变量或数组;“输入/输出”表示调用前需要提供容量,调用后从同一对象读取结果。

15.1 设备发现、打开和识别

pynect_direct_scan_devices

功能说明

刷新当前 USB 设备列表,在控制台打印每台设备的索引、描述和芯片序列号,并返回本次发现的设备数量。该函数不打开设备。

适用范围

UV/VIS、NIR;只读;不要求任何 capability 位。

调用前提

设备已连接并被 Windows 识别。其他程序不应独占目标设备。扫描可以在设备未打开时调用。

输入参数

无。

输出参数

无输出容器;设备列表直接打印到控制台。

返回值

返回值 含义
1-64 本次发现的设备数量;每台设备可用索引为 0count - 1
0 未发现设备,或底层 USB 枚举失败;该函数不返回负错误码

Python 示例

device_count = sdk.pynect_direct_scan_devices()
if device_count == 0:
    print("No PynectDirect device found")
else:
    print(f"Found {device_count} device(s)")

常见错误

0 当作成功状态码。此函数与多数 API 不同,0 表示没有可用设备。关联 API:pynect_direct_get_chip_serial_by_indexpynect_direct_open_device

pynect_direct_get_chip_serial_by_index

功能说明

根据扫描索引读取 USB 芯片序列号。该序列号用于稳定区分多台设备,与光谱仪业务序列号不是同一个字段。

适用范围

UV/VIS、NIR;只读;不要求设备已经打开。

调用前提

建议先调用 pynect_direct_scan_devices,并确保 index 小于扫描返回数量。

输入参数

参数 Python 类型 方向 范围/容量 说明
index int 输入 0device_count - 1 扫描列表索引
chip_serial ctypes.create_string_buffer 输入/输出 建议 64 字节 接收以 \0 结尾的芯片序列号
max_len int 输入 至少 2;通常传 len(buffer) 缓冲区总字节数,包含结尾字符空间

输出参数

成功后从 chip_serial.value 取得字节串,再按 UTF-8 解码。若缓冲区较小,字符串会被截断,因此建议分配 64 字节。

返回值

返回值 含义
0 读取成功
-2 未发现设备或索引超出当前设备数量
-5 索引为负、缓冲区无效或 max_len <= 1

Python 示例

import ctypes

device_count = sdk.pynect_direct_scan_devices()
if device_count > 0:
    serial_buffer = ctypes.create_string_buffer(64)
    code = sdk.pynect_direct_get_chip_serial_by_index(
        0, serial_buffer, len(serial_buffer)
    )
    require_success(code, "Get chip serial")
    chip_serial = serial_buffer.value.decode("utf-8", errors="replace")
    print(chip_serial)

常见错误

传入业务序列号接口的缓冲区结果作为芯片序列号,或在重新插拔设备后继续使用旧索引。关联 API:pynect_direct_open_device_by_chip_serialpynect_direct_get_serial_number

pynect_direct_open_device

功能说明

按当前扫描索引打开一台设备,并识别设备协议和 capability。若当前已有设备打开,函数会先关闭旧设备,再尝试打开新索引。

适用范围

UV/VIS、NIR;连接操作;成功后建立 DLL 的当前设备状态。

调用前提

先扫描设备并选择有效索引。目标设备不能被其他进程独占。需要强制协议时,应在打开前调用 pynect_direct_set_protocol_preference

输入参数

参数 Python 类型 方向 范围 说明
index int 输入 0device_count - 1 当前 USB 枚举列表中的设备索引

输出参数

无。成功后可通过 pynect_direct_is_openpynect_direct_get_device_protocolpynect_direct_get_capabilities 查询连接状态。

返回值

返回值 含义
0 打开成功
-6 索引无效、设备被占用或 USB 打开失败

Python 示例

device_count = sdk.pynect_direct_scan_devices()
if device_count <= 0:
    raise RuntimeError("No device found")
require_success(sdk.pynect_direct_open_device(0), "Open device")
print("Device opened")

常见错误

直接写死索引而不先扫描;多台设备重新插拔后索引可能变化。打开新设备会关闭旧设备,因此不要假设两个索引可以在同一 DLL 实例中同时保持打开。关联 API:pynect_direct_scan_devicespynect_direct_close_device

pynect_direct_open_device_by_chip_serial

功能说明

遍历当前 USB 设备,找到芯片序列号完全匹配的设备并打开。适合设备索引可能变化的多设备系统。

适用范围

UV/VIS、NIR;连接操作。

调用前提

已通过 pynect_direct_get_chip_serial_by_index 取得非空芯片序列号。目标设备没有被其他程序占用。

输入参数

参数 Python 类型 方向 格式 说明
chip_serial bytes / ctypes.c_char_p 输入 非空、以 \0 终止 USB 芯片序列号;Python 字符串需先编码为字节串

输出参数

无。成功后建立当前设备连接。

返回值

返回值 含义
0 找到匹配设备并打开成功
-2 没有设备或没有匹配序列号
-5 chip_serial 为空
-6 找到设备但打开失败或设备被占用

Python 示例

chip_serial = "B002P00S"
code = sdk.pynect_direct_open_device_by_chip_serial(
    chip_serial.encode("utf-8")
)
require_success(code, "Open device by chip serial")

常见错误

传入光谱仪业务序列号而不是 USB 芯片序列号,或直接传 Python str 而未编码。关联 API:pynect_direct_get_chip_serial_by_indexpynect_direct_get_serial_number

pynect_direct_close_device

功能说明

关闭当前设备句柄,清除协议、能力和指定波段状态。重复调用是允许的。

适用范围

UV/VIS、NIR;资源清理操作。

调用前提

无。建议只要设备曾成功打开,就在 finally 中调用。

输入参数

无。

输出参数

无。

返回值

没有返回值,Python 中结果为 None。无法通过返回值判断底层关闭状态,可在关闭后调用 pynect_direct_is_open 验证。

Python 示例

opened = False
try:
    require_success(sdk.pynect_direct_open_device(0), "Open device")
    opened = True
    # Perform read-only operations here.
finally:
    if opened:
        sdk.pynect_direct_close_device()

常见错误

只在正常路径关闭设备,异常时遗漏清理;这会导致下一次程序无法打开设备。关联 API:pynect_direct_open_devicepynect_direct_is_open

pynect_direct_is_open

功能说明

读取 DLL 当前设备状态,不与设备通信。

适用范围

UV/VIS、NIR;只读状态查询。

调用前提

无。

输入参数

无。

输出参数

无输出容器;状态直接通过函数返回值给出。

返回值

返回值 含义
1 DLL 当前记录为设备已打开
0 当前没有打开的设备

Python 示例

if sdk.pynect_direct_is_open() == 1:
    print("Device is open")
else:
    print("Device is closed")

常见错误

把返回值当作标准错误码,认为 0 表示成功。此函数的 0 表示“未打开”。关联 API:pynect_direct_open_devicepynect_direct_close_device

pynect_direct_set_protocol_preference

功能说明

设置下一次打开设备时使用的协议偏好。AUTO 会自动识别设备;强制模式用于已确认型号但自动识别异常的现场。

适用范围

UV/VIS、NIR;临时连接设置;不写入设备。

调用前提

当前必须没有打开的设备。正常使用推荐自动模式。

输入参数

参数 Python 类型 方向 允许值 说明
protocol int / ctypes.c_int 输入 012 0=AUTO1=UVVIS_CM22=NIR_LEGACY

输出参数

无。设置只影响后续打开操作。

返回值

返回值 含义
0 偏好设置成功
-3 当前已有设备打开;应先关闭设备
-5 protocol 不是 012

Python 示例

PROTOCOL_AUTO = 0
if sdk.pynect_direct_is_open():
    sdk.pynect_direct_close_device()
require_success(
    sdk.pynect_direct_set_protocol_preference(PROTOCOL_AUTO),
    "Set protocol preference",
)

常见错误

在设备已打开后设置偏好,或无依据地强制错误协议。关联 API:pynect_direct_open_devicepynect_direct_get_device_protocol

pynect_direct_get_device_protocol

功能说明

读取当前已打开设备实际采用的协议类型,而不是打开前设置的偏好值。

适用范围

UV/VIS、NIR;只读。

调用前提

设备已成功打开。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 输出值 说明
protocol ctypes.c_int() + ctypes.byref() 输出 12、可能的 255 1=UVVIS_CM22=NIR_LEGACY255=UNKNOWN

返回值

返回值 含义
0 输出变量已写入协议值
-3 设备未打开
-5 输出变量无效

Python 示例

import ctypes

protocol = ctypes.c_int()
require_success(
    sdk.pynect_direct_get_device_protocol(ctypes.byref(protocol)),
    "Get device protocol",
)
print("Protocol value:", protocol.value)

常见错误

忘记使用 ctypes.byref(protocol),或在打开设备前查询。关联 API:pynect_direct_set_protocol_preferencepynect_direct_get_device_info

pynect_direct_get_capabilities

功能说明

读取当前设备的 32 位 capability 位掩码。每一位表示一类功能是否可用。

适用范围

UV/VIS、NIR;只读;调用任何设备专用功能前都应执行。

调用前提

设备已成功打开并完成协议识别。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 宽度 说明
capabilities ctypes.c_uint32() + ctypes.byref() 输出 32 位无符号整数 按第 11.2 节列出的位值进行按位与检查

返回值

返回值 含义
0 capability 值读取成功
-3 设备未打开
-5 输出变量无效

Python 示例

import ctypes

CAP_SPECTRUM_DATA = 0x00000020
capabilities = ctypes.c_uint32()
require_success(
    sdk.pynect_direct_get_capabilities(ctypes.byref(capabilities)),
    "Get capabilities",
)
if capabilities.value & CAP_SPECTRUM_DATA:
    print("Spectrum acquisition is supported")

常见错误

直接比较整个值是否等于某个 capability,而不是使用按位与;一台设备通常同时设置多个能力位。关联 API:pynect_direct_get_device_info 以及所有返回 -11 的设备专用接口。

pynect_direct_get_device_info

功能说明

一次读取协议、capability、设备型号、业务序列号和硬件版本。个别字符串读取失败时,对应字段可能为空,但函数仍可返回成功,因此应用应检查必要字段。

适用范围

UV/VIS、NIR;只读。

调用前提

设备已成功打开。Python 结构定义必须与 minimal_test.py 中的 PynectDirectDeviceInfo 一致。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 字段 说明
info PynectDirectDeviceInfo() + ctypes.byref() 输出 protocol 协议整数值
info.capabilities ctypes.c_uint32 字段 输出 capability 位掩码 功能支持集合
info.protocol_name 32 字节字符数组 输出 协议名称 使用 decode_char_array 解码
info.model 32 字节字符数组 输出 设备型号 读取失败时可能为空
info.serial_number 32 字节字符数组 输出 业务序列号 NIR 可能返回通用标识
info.hardware_version 64 字节字符数组 输出 硬件版本 读取失败时可能为空

返回值

返回值 含义
0 基本信息对象已写入;仍应检查字符串字段是否为空
-3 设备未打开
-5 info 输出对象无效

Python 示例

import ctypes
from minimal_test import PynectDirectDeviceInfo, decode_char_array

info = PynectDirectDeviceInfo()
require_success(
    sdk.pynect_direct_get_device_info(ctypes.byref(info)),
    "Get device info",
)
print("Protocol:", decode_char_array(info.protocol_name))
print("Model:", decode_char_array(info.model))
print("Serial:", decode_char_array(info.serial_number))
print(f"Capabilities: 0x{info.capabilities:08X}")

常见错误

结构字段顺序或字符数组长度定义错误会导致错误数据;不要自行缩短结构定义。关联 API:pynect_direct_get_device_protocolpynect_direct_get_capabilities、各单项身份接口。

15.2 临时测量参数

pynect_direct_set_integration_time

功能说明

设置当前会话的探测器积分时间。该值直接影响信号强度和单次采集耗时,不写入设备的默认固化配置。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_INTEGRATION_TIME;临时设置。

调用前提

设备已打开。先读取并记录原积分时间,确认目标值符合具体设备允许范围。

输入参数

参数 Python 类型 方向 单位/范围 说明
us int / ctypes.c_uint 输入 微秒;建议大于 0,上限以设备资料为准 50000 表示 50 ms;过大可能导致饱和和长时间等待

输出参数

无。成功后后续采谱使用新的积分时间。

返回值

返回值 含义
0 设置成功
-3 设备未打开
-4/-7/-8/-9 通信、校验、数据格式或超时错误

Python 示例

integration_us = 100_000
require_success(
    sdk.pynect_direct_set_integration_time(integration_us),
    "Set integration time",
)

常见错误

把毫秒值直接当微秒传入,例如希望 100 ms 却传 100;正确值是 100000。修改前不保存旧值也会影响后续实验。关联 API:pynect_direct_get_integration_timepynect_direct_set_stored_integration_time

pynect_direct_get_integration_time

功能说明

读取当前会话实际使用的积分时间。

适用范围

UV/VIS、NIR;要求积分时间能力;只读。

调用前提

设备已打开。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位 说明
us ctypes.c_uint() + ctypes.byref() 输出 微秒 32 位无符号积分时间值

返回值

返回值 含义
0 输出变量已写入当前积分时间
-3 设备未打开
-5 输出变量无效
-4/-7/-8/-9 设备返回异常或通信超时

Python 示例

import ctypes

integration_us = ctypes.c_uint()
require_success(
    sdk.pynect_direct_get_integration_time(
        ctypes.byref(integration_us)
    ),
    "Get integration time",
)
print(f"Integration: {integration_us.value} us")

常见错误

传入整数而不是 ctypes 输出变量,或忘记 ctypes.byref()。该值是当前运行参数,不是固化默认值。关联 API:pynect_direct_set_integration_timepynect_direct_get_stored_integration_time

pynect_direct_set_average_number

功能说明

设置当前会话一次结果所使用的光谱平均次数。增加平均次数通常降低随机波动,同时增加采集时间。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_AVERAGE_NUMBER;临时设置。

调用前提

设备已打开。先读取原值,并评估积分时间与平均次数共同造成的总等待时间。

输入参数

参数 Python 类型 方向 范围 说明
count int / ctypes.c_uint8 输入 推荐 1-255 1 表示不做多次平均;不要使用 0,不同设备对 0 的处理可能不同

输出参数

无。成功后后续采谱使用新的平均次数。

返回值

返回值 含义
0 设置成功
-3 设备未打开
-4/-7/-8/-9 通信或响应错误

Python 示例

average_count = 3
require_success(
    sdk.pynect_direct_set_average_number(average_count),
    "Set average number",
)

常见错误

将平均次数设置为 0,或在长积分时间下使用很大的平均次数导致程序看似无响应。关联 API:pynect_direct_get_average_numberpynect_direct_set_stored_average_number

pynect_direct_get_average_number

功能说明

读取当前会话的光谱平均次数。

适用范围

UV/VIS、NIR;要求平均次数能力;只读。

调用前提

设备已打开。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围 说明
count ctypes.c_uint8() + ctypes.byref() 输出 0-255 当前设备报告的平均次数;正常应用通常应为 1-255

返回值

返回值 含义
0 输出变量已写入平均次数
-3 设备未打开
-5 输出变量无效
-4/-7/-8/-9 通信或响应错误

Python 示例

import ctypes

average_count = ctypes.c_uint8()
require_success(
    sdk.pynect_direct_get_average_number(
        ctypes.byref(average_count)
    ),
    "Get average number",
)
print("Average count:", average_count.value)

常见错误

使用 ctypes.c_uint 接收 8 位结果,或把运行时平均次数与固化默认值混淆。关联 API:pynect_direct_set_average_numberpynect_direct_get_stored_average_number

pynect_direct_set_level_output

功能说明

设置设备数字电平输出位掩码。每一位对应的物理输出由具体设备定义。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_LEVEL_OUTPUT;需谨慎,可能驱动外部电路。

调用前提

设备已打开,已确认接线、电平标准和每一位的用途。先读取原值以便恢复。

输入参数

参数 Python 类型 方向 范围 说明
level int / ctypes.c_uint8 输入 0x00-0xFF 8 位输出掩码;每一位的含义以设备资料为准

输出参数

无。成功后物理输出可能立即变化。

返回值

返回值 含义
0 输出位设置成功
-3 设备未打开
-4/-7/-8/-9 通信或设备响应错误

Python 示例

ALLOW_LEVEL_OUTPUT = False
target_level = 0x01

if ALLOW_LEVEL_OUTPUT:
    require_success(
        sdk.pynect_direct_set_level_output(target_level),
        "Set level output",
    )
else:
    print("Level output skipped; enable only after checking wiring")

常见错误

level 当作单个逻辑值而忽略它是位掩码,或未确认外部负载就执行示例。关联 API:pynect_direct_get_level_output、设备接线说明。

pynect_direct_get_level_output

功能说明

读取当前数字电平输出位掩码。

适用范围

UV/VIS、NIR;要求电平输出能力;只读。

调用前提

设备已打开。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围 说明
level ctypes.c_uint8() + ctypes.byref() 输出 0x00-0xFF 当前 8 位输出掩码

返回值

返回值 含义
0 输出变量已写入当前位掩码
-3 设备未打开
-5 输出变量无效
-4/-7/-8/-9 通信或响应错误

Python 示例

import ctypes

level = ctypes.c_uint8()
require_success(
    sdk.pynect_direct_get_level_output(ctypes.byref(level)),
    "Get level output",
)
print(f"Level mask: 0x{level.value:02X}")

常见错误

将返回的十进制数直接理解成某一路电平而不做按位检查。关联 API:pynect_direct_set_level_output

15.3 波长与光谱

pynect_direct_get_wavelength_count

功能说明

读取当前设备完整光谱包含的数据点数量。该点数决定波长数组和强度数组的最小容量。

适用范围

UV/VIS、NIR;要求波长数据能力;只读。

调用前提

设备已打开。每次更换设备后都应重新读取,不能写死为某个型号的固定点数。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位/范围 说明
count ctypes.c_uint16() + ctypes.byref() 输出 数据点;0-65535 成功后保存完整波长和完整强度的点数

返回值

返回值 含义
0 点数读取成功;仍应检查 count.value > 0
-3 设备未打开
-5 输出变量无效或内存分配失败
-4/-7/-8/-9 数据长度异常、通信或超时错误

Python 示例

import ctypes

point_count = ctypes.c_uint16()
require_success(
    sdk.pynect_direct_get_wavelength_count(ctypes.byref(point_count)),
    "Get wavelength count",
)
if point_count.value == 0:
    raise RuntimeError("Device returned zero wavelength points")
print("Points:", point_count.value)

常见错误

把函数返回值 0 当作点数;真正点数位于 count.value。关联 API:pynect_direct_get_full_wavelength_datapynect_direct_get_full_spectrum_data

pynect_direct_get_full_wavelength_range

功能说明

读取设备完整数据集的起始波长和结束波长,结果按从小到大返回。

适用范围

UV/VIS、NIR;要求波长数据能力;只读。

调用前提

设备已打开。结果用于验证用户请求波段是否合法。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位 说明
start_nm ctypes.c_double() + ctypes.byref() 输出 nm 完整范围的最小波长
end_nm ctypes.c_double() + ctypes.byref() 输出 nm 完整范围的最大波长,应大于 start_nm

返回值

返回值 含义
0 两个输出变量已写入
-3 设备未打开
-5 任一输出变量无效或内部数据分配失败
-4/-7/-8/-9 波长数据或通信异常

Python 示例

import ctypes

start_nm = ctypes.c_double()
end_nm = ctypes.c_double()
require_success(
    sdk.pynect_direct_get_full_wavelength_range(
        ctypes.byref(start_nm), ctypes.byref(end_nm)
    ),
    "Get full wavelength range",
)
print(f"Range: {start_nm.value:.3f}-{end_nm.value:.3f} nm")

常见错误

将输出变量顺序写反,或把 nm 当作像素索引。关联 API:pynect_direct_set_selected_wavelength_rangepynect_direct_get_full_wavelength_data

pynect_direct_get_full_wavelength_data

功能说明

读取每个探测器数据点对应的完整波长数组。该接口只读取波长坐标,不采集强度。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_WAVELENGTH_DATA;只读。

调用前提

设备已打开;先调用 pynect_direct_get_wavelength_count,按返回点数创建 ctypes.c_double 数组。

输入参数

参数 Python 类型 方向 单位/容量 说明
buffer (ctypes.c_double * count)() 输入/输出 count 个 double 元素 DLL 将波长依次写入该数组
max_len int 输入 数据点,不是字节数 必须至少等于完整波长点数

输出参数

成功后 buffer[0:max_len] 中的有效部分为波长值,单位 nm。实际有效元素数等于 pynect_direct_get_wavelength_count 返回值。

返回值

返回值 含义
0 波长数组读取成功
-3 设备未打开
-5 缓冲区无效、max_len <= 0 或容量小于实际点数
-4/-7/-8/-9 返回数据长度异常、通信或超时错误

Python 示例

import ctypes

count = ctypes.c_uint16()
require_success(
    sdk.pynect_direct_get_wavelength_count(ctypes.byref(count)),
    "Get wavelength count",
)
wavelengths = (ctypes.c_double * count.value)()
require_success(
    sdk.pynect_direct_get_full_wavelength_data(
        wavelengths, count.value
    ),
    "Get full wavelength data",
)
print(wavelengths[0], wavelengths[count.value - 1])

常见错误

max_len 传成数组字节数,或使用 ctypes.c_float 数组。关联 API:pynect_direct_get_wavelength_countpynect_direct_get_full_spectrum_data

pynect_direct_get_full_spectrum_data

功能说明

触发或读取一次完整光谱采集,将每个波长点对应的原始强度写入数组。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_SPECTRUM_DATA;只读采集。

调用前提

设备已打开;已按完整点数创建 ctypes.c_uint16 数组。普通软件采集时应确保外触发没有处于等待状态。

输入参数

参数 Python 类型 方向 单位/容量 说明
buffer (ctypes.c_uint16 * count)() 输入/输出 count 个 16 位元素 接收原始光谱强度
max_len int 输入 数据点,不是字节数 必须至少等于完整光谱点数

输出参数

成功后 buffer[index] 与波长数组的 wavelengths[index] 一一对应。每个强度值存储范围为 0-65535

返回值

返回值 含义
0 完整强度数组采集成功
-3 设备未打开
-5 缓冲区无效、容量不足或内存分配失败
-4/-7/-8/-9 光谱格式、通信、校验或等待超时错误

Python 示例

import ctypes

count = ctypes.c_uint16()
require_success(
    sdk.pynect_direct_get_wavelength_count(ctypes.byref(count)),
    "Get wavelength count",
)
intensities = (ctypes.c_uint16 * count.value)()
require_success(
    sdk.pynect_direct_get_full_spectrum_data(
        intensities, count.value
    ),
    "Get full spectrum data",
)
print("Intensity range:", min(intensities), max(intensities))

常见错误

使用字节数组接收 16 位强度,或在外触发等待状态下调用普通采谱接口。关联 API:pynect_direct_get_full_wavelength_data、积分时间和平均次数接口。

pynect_direct_set_selected_wavelength_range

功能说明

根据用户请求的波长边界选择一段连续像素窗口,供后续指定波段接口使用。该设置只保存在当前 DLL 会话中。

适用范围

UV/VIS、NIR;临时设置;不改变设备标定或物理波长范围。

调用前提

设备已打开;先读取完整波长范围。start_nm < end_nm,并且两个边界都位于完整范围内。

输入参数

参数 Python 类型 方向 单位/范围 说明
start_nm float / ctypes.c_double 输入 nm;不小于完整起始波长 请求波段下限
end_nm float / ctypes.c_double 输入 nm;不大于完整结束波长 请求波段上限,必须大于 start_nm

输出参数

无直接输出。实际匹配边界、像素索引和点数通过 pynect_direct_get_selected_wavelength_range 读取。

返回值

返回值 含义
0 已找到至少一个有效数据点并保存波段状态
-3 设备未打开
-5 边界顺序错误、超出完整范围、没有匹配点或内存分配失败
-4/-7/-8/-9 读取完整波长数据时发生通信错误

Python 示例

request_start_nm = 900.0
request_end_nm = 950.0
require_success(
    sdk.pynect_direct_set_selected_wavelength_range(
        request_start_nm, request_end_nm
    ),
    "Set selected wavelength range",
)

常见错误

使用设备范围以外的边界,或认为设置后会永久改变硬件。设备关闭、重新打开或复位后需要重新设置。关联 API:pynect_direct_get_full_wavelength_rangepynect_direct_get_selected_wavelength_range

pynect_direct_get_selected_wavelength_range

功能说明

读取最近一次指定波段设置的请求边界、实际像素边界、像素索引和数据点数。

适用范围

UV/VIS、NIR;只读。

调用前提

设备已打开,并已成功调用 pynect_direct_set_selected_wavelength_range

输入参数

无普通输入值。

输出参数

字段 Python 类型 方向 单位 说明
out_range.request_start_nm float 字段 输出 nm 用户请求的起始边界
out_range.request_end_nm float 字段 输出 nm 用户请求的结束边界
out_range.actual_start_nm float 字段 输出 nm 第一个匹配像素的实际波长
out_range.actual_end_nm float 字段 输出 nm 最后一个匹配像素的实际波长
start_pixel / end_pixel 16 位整数字段 输出 像素索引 完整数组中的闭区间索引
count 16 位整数字段 输出 数据点 后续指定波段数组的最小容量

返回值

返回值 含义
0 输出结构已写入
-1 当前会话尚未设置有效波段
-3 设备未打开
-5 输出对象无效

Python 示例

import ctypes
from minimal_test import PynectDirectSelectedWavelengthRange

selected = PynectDirectSelectedWavelengthRange()
require_success(
    sdk.pynect_direct_get_selected_wavelength_range(
        ctypes.byref(selected)
    ),
    "Get selected wavelength range",
)
print(selected.actual_start_nm, selected.actual_end_nm, selected.count)

常见错误

未先设置波段,或使用请求边界计算数组长度;正确容量是 selected.count。关联 API:pynect_direct_set_selected_wavelength_range、两个指定波段数据接口。

pynect_direct_get_selected_wavelength_data

功能说明

读取当前指定波段内的波长数组。

适用范围

UV/VIS、NIR;只读。

调用前提

设备已打开,波段已设置,并已读取 selected.count

输入参数

参数 Python 类型 方向 容量 说明
buffer (ctypes.c_double * selected.count)() 输入/输出 至少 selected.count 个元素 接收波段内的 nm 值
max_len int 输入 数据点 必须大于或等于 selected.count

输出参数

成功后数组包含从 actual_start_nmactual_end_nm 的连续波长点。

返回值

返回值 含义
0 波段波长数组读取成功
-1 未设置有效波段
-3 设备未打开
-5 缓冲区无效或容量不足
-4/-7/-8/-9 完整波长读取或通信错误

Python 示例

selected_wavelengths = (ctypes.c_double * selected.count)()
require_success(
    sdk.pynect_direct_get_selected_wavelength_data(
        selected_wavelengths, selected.count
    ),
    "Get selected wavelength data",
)
print(selected_wavelengths[0], selected_wavelengths[selected.count - 1])

常见错误

用完整光谱点数代替 selected.count 虽不会必然失败,但会造成含义不清;容量过小会返回 -5。关联 API:pynect_direct_get_selected_wavelength_range

pynect_direct_get_selected_spectrum_data

功能说明

采集一次完整光谱,并把当前指定波段对应的强度区间复制到输出数组。

适用范围

UV/VIS、NIR;只读采集。

调用前提

设备已打开,波段已设置,并已按 selected.count 创建 16 位强度数组。

输入参数

参数 Python 类型 方向 容量 说明
buffer (ctypes.c_uint16 * selected.count)() 输入/输出 至少 selected.count 个元素 接收波段强度
max_len int 输入 数据点 必须大于或等于 selected.count

输出参数

成功后 buffer[index]pynect_direct_get_selected_wavelength_data 返回数组的相同索引一一对应。

返回值

返回值 含义
0 波段强度采集成功
-1 未设置有效波段
-3 设备未打开
-5 缓冲区无效、容量不足或内存分配失败
-4/-7/-8/-9 完整光谱采集或通信错误

Python 示例

selected_intensities = (ctypes.c_uint16 * selected.count)()
require_success(
    sdk.pynect_direct_get_selected_spectrum_data(
        selected_intensities, selected.count
    ),
    "Get selected spectrum data",
)
print(min(selected_intensities), max(selected_intensities))

常见错误

波长数组与强度数组使用了不同的波段状态,或修改波段后仍沿用旧 count。关联 API:pynect_direct_set_selected_wavelength_rangepynect_direct_get_selected_wavelength_data

15.4 设备状态与身份信息

pynect_direct_get_calibration_coefficients

功能说明

读取设备保存的四个波长标定系数,通常按从低阶到高阶的顺序存入数组。该接口只读取系数,不修改标定。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_CALIBRATION;只读。

调用前提

设备已打开。应用必须分配恰当的双精度数组;系数的物理解释应遵循设备标定资料。

输入参数

参数 Python 类型 方向 容量 说明
coeffs (ctypes.c_double * 4)() 输入/输出 至少 4 个 double 接收四个波长标定系数

输出参数

成功后可读取 coeffs[0]coeffs[3]。本接口不返回系数数量,调用方必须固定分配 4 个元素。

返回值

返回值 含义
0 四个系数读取成功
-3 设备未打开
-5 输出数组无效
-4/-7/-8/-9 数据长度、校验、通信或超时错误

Python 示例

import ctypes

coefficients = (ctypes.c_double * 4)()
require_success(
    sdk.pynect_direct_get_calibration_coefficients(coefficients),
    "Get calibration coefficients",
)
print(list(coefficients))

常见错误

只分配一个 ctypes.c_double,或自行用系数替换 SDK 已返回的波长数组。关联 API:pynect_direct_get_full_wavelength_data

pynect_direct_get_temperature

功能说明

读取设备内部温度传感器值并转换为摄氏度浮点数。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_TEMPERATURE;只读。

调用前提

设备已打开且能力位表明支持温度读取。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位 说明
temp_c ctypes.c_float() + ctypes.byref() 输出 摄氏度 当前设备报告的内部温度,可为小数或负数

返回值

返回值 含义
0 温度读取成功
-3 设备未打开
-5 输出变量无效
-4/-7/-8/-9 返回长度、通信、校验或超时错误

Python 示例

import ctypes

temperature_c = ctypes.c_float()
require_success(
    sdk.pynect_direct_get_temperature(ctypes.byref(temperature_c)),
    "Get temperature",
)
print(f"Temperature: {temperature_c.value:.2f} C")

常见错误

使用 ctypes.c_double 接收单精度输出,或将某些设备报告的固定值直接认定为环境温度。关联 API:pynect_direct_get_capabilities

pynect_direct_get_hardware_version

功能说明

读取设备硬件版本并写入以 \0 结尾的字符串缓冲区。

适用范围

UV/VIS、NIR;要求硬件版本能力;只读。

调用前提

设备已打开。准备至少 2 字节的可写字符缓冲区,推荐 64 字节以避免截断。

输入参数

参数 Python 类型 方向 范围/容量 说明
version_str ctypes.create_string_buffer 输入/输出 推荐 64 字节 接收硬件版本字符串
max_len int 输入 至少 2 缓冲区总字节数,包含结尾 \0 空间

输出参数

成功后读取 version_str.value 并解码。UV/VIS 常见格式类似 03.4D.43.63;NIR 可能返回设备定义的版本文本。

返回值

返回值 含义
0 版本字符串读取成功
-3 设备未打开
-5 缓冲区无效或 max_len <= 1
-4/-7/-8/-9 返回数据或通信异常

Python 示例

import ctypes

version_buffer = ctypes.create_string_buffer(64)
require_success(
    sdk.pynect_direct_get_hardware_version(
        version_buffer, len(version_buffer)
    ),
    "Get hardware version",
)
print(version_buffer.value.decode("utf-8", errors="replace"))

常见错误

传入不可写的 Python 字符串,或 max_len 大于实际缓冲区容量。关联 API:pynect_direct_get_device_info

pynect_direct_get_serial_number

功能说明

读取光谱仪业务序列号。该编号由设备保存,与 USB 芯片序列号用途不同。

适用范围

UV/VIS、NIR;只读。NIR 当前可能返回通用字符串 NIR,不保证是唯一编号。

调用前提

设备已打开。需要唯一绑定时,应同时记录芯片序列号。

输入参数

参数 Python 类型 方向 范围/容量 说明
sn ctypes.create_string_buffer 输入/输出 推荐 64 字节 接收设备业务序列号
max_len int 输入 至少 2 缓冲区总字节数,包含结尾空间

输出参数

成功后从 sn.value 读取字节串并解码。UV/VIS 最多读取设备保存的 12 字节内容;缓冲区较小时会截断。

返回值

返回值 含义
0 序列号或 NIR 通用标识读取成功
-3 设备未打开
-5 缓冲区无效或 max_len <= 1
-4/-7/-8/-9 UV/VIS 数据长度或通信异常

Python 示例

import ctypes

serial_buffer = ctypes.create_string_buffer(64)
require_success(
    sdk.pynect_direct_get_serial_number(
        serial_buffer, len(serial_buffer)
    ),
    "Get device serial number",
)
print(serial_buffer.value.decode("utf-8", errors="replace"))

常见错误

把返回值用于多设备唯一绑定,而忽略 NIR 可能只返回 NIR。关联 API:pynect_direct_get_chip_serial_by_index

pynect_direct_get_detector_serial_number

功能说明

读取 UV/VIS 设备中的探测器序列号,最多复制设备返回的 8 字节标识。

适用范围

UV/VIS;只读。NIR 返回 -11

调用前提

设备已打开。必须提供有效可写缓冲区;推荐 16 或 64 字节。由于低层接口要求严格,禁止传空缓冲区或小于 2 的长度。

输入参数

参数 Python 类型 方向 范围/容量 说明
sn ctypes.create_string_buffer 输入/输出 推荐至少 16 字节 接收最多 8 字节探测器序列号及结尾字符
max_len int 输入 至少 2,不得超过实际容量 缓冲区总字节数

输出参数

成功后从 sn.value 取得探测器标识并解码。内容是否可打印由设备数据决定。

返回值

返回值 含义
0 探测器序列号读取成功
-3 设备未打开
-4/-7/-8/-9 返回长度或通信异常
-11 当前设备不是支持该功能的 UV/VIS 设备

Python 示例

import ctypes

detector_serial = ctypes.create_string_buffer(16)
code = sdk.pynect_direct_get_detector_serial_number(
    detector_serial, len(detector_serial)
)
if code == -11:
    print("Detector serial is not supported")
else:
    require_success(code, "Get detector serial")
    print(detector_serial.value.decode("utf-8", errors="replace"))

常见错误

在 NIR 设备上把 -11 当作硬件故障,或传入长度为 0 的缓冲区。关联 API:pynect_direct_get_capabilitiespynect_direct_get_serial_number

pynect_direct_get_device_model

功能说明

读取设备型号字符串。UV/VIS 返回设备保存的型号;NIR 根据可用硬件信息生成型号,信息不足时返回 NIR

适用范围

UV/VIS、NIR;只读。

调用前提

设备已打开,并准备有效字符缓冲区。

输入参数

参数 Python 类型 方向 范围/容量 说明
model ctypes.create_string_buffer 输入/输出 推荐 64 字节 接收型号字符串
max_len int 输入 至少 2 包含结尾字符空间的缓冲区容量

输出参数

成功后从 model.value 取得字节串。UV/VIS 最多使用设备返回的 16 字节型号内容。

返回值

返回值 含义
0 型号读取成功;NIR 无详细信息时也可能返回通用 NIR
-3 设备未打开
-5 缓冲区无效或长度不足
-4/-7/-8/-9 UV/VIS 返回数据或通信异常

Python 示例

import ctypes

model_buffer = ctypes.create_string_buffer(64)
require_success(
    sdk.pynect_direct_get_device_model(
        model_buffer, len(model_buffer)
    ),
    "Get device model",
)
print(model_buffer.value.decode("utf-8", errors="replace"))

常见错误

把通用 NIR 文本误认为唯一型号,或忽略字符串可能被截断。关联 API:pynect_direct_get_device_info

pynect_direct_get_usb_status

功能说明

读取 UV/VIS 设备内部报告的 USB 连接状态。它是设备状态字段,不替代 Windows 设备枚举结果。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_USB_STATUS;只读。NIR 返回 -11

调用前提

设备已打开并支持 USB 状态查询。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 输出值 说明
connected ctypes.c_int() + ctypes.byref() 输出 10 1 表示设备报告已连接,0 表示未连接

返回值

返回值 含义
0 状态变量已写入;连接状态应读取 connected.value
-3 设备未打开
-4/-7/-8/-9 返回数据或通信异常
-11 当前设备不支持该查询

Python 示例

import ctypes

connected = ctypes.c_int()
code = sdk.pynect_direct_get_usb_status(ctypes.byref(connected))
if code == -11:
    print("USB status query is not supported")
else:
    require_success(code, "Get USB status")
    print("Connected:", bool(connected.value))

常见错误

把函数返回的 0 理解为“未连接”;连接状态位于输出变量中。关联 API:pynect_direct_scan_devicespynect_direct_get_capabilities

15.5 氙灯、USB 参数与 DAC

pynect_direct_set_xenon_pulse

功能说明

设置氙灯脉冲的完整周期和一个周期内的高电平宽度。该函数会改变当前光源时序,不负责打开或关闭氙灯,也不会自动检查目标时序是否适合具体灯源。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_XENON。NIR 返回 -11。这是光源控制接口,须由熟悉设备和光源安全要求的人员使用。

调用前提

设备已打开,capability 已确认。建议先用 pynect_direct_get_xenon_mode 确认氙灯处于关闭状态,再读取并记录原脉冲参数。目标值必须来自设备资料或实验室批准的配置。

输入参数

参数 Python 类型 方向 单位/范围 说明
period_10ns Python int,按 ctypes.c_uint 传入 输入 10 ns;0-4294967295 一个完整脉冲周期的计数值
high_10ns Python int,按 ctypes.c_uint 传入 输入 10 ns;0-4294967295 一个周期内高电平持续时间;通常不得大于 period_10ns

Python 传入前应自行检查设备允许范围。超出无符号 32 位范围的整数可能被截断,不能依赖 DLL 自动拒绝。

输出参数

无。

返回值

返回值 含义
0 脉冲参数设置成功
-3 设备未打开
-4/-7/-8/-9 设备拒绝、返回校验异常或通信超时
-10 设备报告当前写操作受保护
-11 当前设备不支持氙灯控制

Python 示例

ALLOW_XENON_CONFIGURATION = False

# 以下数值仅演示 10 ns 计数的写法,必须替换为设备批准值。
target_period_10ns = 10_000_000
target_high_10ns = 1_000_000

if ALLOW_XENON_CONFIGURATION:
    if not (0 < target_high_10ns <= target_period_10ns <= 0xFFFFFFFF):
        raise ValueError("Invalid xenon pulse timing")
    require_success(
        sdk.pynect_direct_set_xenon_pulse(
            target_period_10ns, target_high_10ns
        ),
        "Set xenon pulse",
    )
else:
    print("Skipped: xenon configuration is disabled")

常见错误

把参数单位误写成微秒或毫秒;高电平宽度大于周期;氙灯已点亮时直接改变时序;未记录原值。关联 API:pynect_direct_get_xenon_pulsepynect_direct_get_xenon_modepynect_direct_set_xenon_mode

pynect_direct_get_xenon_pulse

功能说明

读取当前氙灯脉冲周期和高电平宽度。函数只查询参数,不改变氙灯模式或输出状态。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_XENON;只读。NIR 返回 -11

调用前提

设备已打开并支持氙灯控制。准备两个独立的无符号 32 位输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位/范围 说明
period ctypes.c_uint() + ctypes.byref() 输出 10 ns;无符号 32 位 当前完整周期计数
high ctypes.c_uint() + ctypes.byref() 输出 10 ns;无符号 32 位 当前高电平宽度计数

实际时间可按 计数值 * 10 ns 换算;换算为秒时乘以 1e-8

返回值

返回值 含义
0 两个输出变量均已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持氙灯控制

Python 示例

import ctypes

period = ctypes.c_uint()
high = ctypes.c_uint()
code = sdk.pynect_direct_get_xenon_pulse(
    ctypes.byref(period), ctypes.byref(high)
)
if code == -11:
    print("Xenon pulse query is not supported")
else:
    require_success(code, "Get xenon pulse")
    print("Period:", period.value, "x 10 ns")
    print("High level:", high.value, "x 10 ns")
    print("Period (s):", period.value * 1e-8)

常见错误

使用 ctypes.c_uint16 导致数据宽度不足;忘记 ctypes.byref();把函数返回码当成周期。关联 API:pynect_direct_set_xenon_pulse

pynect_direct_set_xenon_mode

功能说明

设置氙灯工作模式,可关闭氙灯、进入连续模式或请求单次模式。该函数可能直接改变光源输出状态。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_XENON。NIR 返回 -11。除关闭模式外均属于需要现场确认的光源操作。

调用前提

设备已打开,氙灯脉冲参数已确认,光路和防护装置已就绪。应用应在 finally 清理流程或退出流程中关闭氙灯。

输入参数

参数 Python 类型 方向 允许值 说明
mode Python int,按 ctypes.c_uint8 传入 输入 0x00 XENON_OFF,关闭
mode 同上 输入 0x01 XENON_CONTINUOUS,连续模式
mode 同上 输入 0x81 XENON_SINGLE,单次模式

不要传入表中未定义的模式值。

输出参数

无。

返回值

返回值 含义
0 模式命令执行成功
-3 设备未打开
-4/-7/-8/-9 设备返回、校验或通信异常
-10 设备报告当前写操作受保护
-11 当前设备不支持氙灯控制

Python 示例

XENON_OFF = 0x00
XENON_CONTINUOUS = 0x01
XENON_SINGLE = 0x81
ALLOW_XENON_OUTPUT = False

target_mode = XENON_OFF
if target_mode not in (XENON_OFF, XENON_CONTINUOUS, XENON_SINGLE):
    raise ValueError("Unknown xenon mode")

if target_mode == XENON_OFF or ALLOW_XENON_OUTPUT:
    require_success(
        sdk.pynect_direct_set_xenon_mode(target_mode),
        "Set xenon mode",
    )
else:
    print("Skipped: xenon output is disabled")

常见错误

把十进制 81 当作十六进制 0x81;程序异常退出后没有关闭氙灯;未先确认脉冲时序。关联 API:pynect_direct_get_xenon_modepynect_direct_set_xenon_pulse

pynect_direct_get_xenon_mode

功能说明

读取当前氙灯模式。函数只查询状态,不会关闭或触发氙灯。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_XENON;只读。NIR 返回 -11

调用前提

设备已打开并支持氙灯控制。准备一个 8 位无符号输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 输出值 说明
mode ctypes.c_uint8() + ctypes.byref() 输出 0x00 关闭
mode 同上 输出 0x01 连续模式
mode 同上 输出 0x81 单次模式

设备若返回其他值,应用应显示原始十六进制值,而不是自行猜测含义。

返回值

返回值 含义
0 mode.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持氙灯控制

Python 示例

import ctypes

mode_names = {
    0x00: "off",
    0x01: "continuous",
    0x81: "single",
}
mode = ctypes.c_uint8()
code = sdk.pynect_direct_get_xenon_mode(ctypes.byref(mode))
if code == -11:
    print("Xenon mode query is not supported")
else:
    require_success(code, "Get xenon mode")
    print(mode_names.get(mode.value, f"unknown 0x{mode.value:02X}"))

常见错误

ctypes.c_int 替代 8 位输出容器,或把未知值默认当成关闭。关联 API:pynect_direct_set_xenon_mode

pynect_direct_set_usb_baud_rate

功能说明

写入 UV/VIS 设备的 USB 通信速率参数。该参数是设备使用的无符号 16 位配置码,不保证与常见串口波特率数值一一对应。

适用范围

UV/VIS;设备专用维护接口。NIR 返回 -11。错误设置可能造成后续通信失败,不建议普通测量程序调用。

调用前提

设备已打开,已读取并保存原参数,并从设备供应方资料获得准确的目标配置码和恢复方法。仅凭常见波特率数值不得调用。

输入参数

参数 Python 类型 方向 范围 说明
rate Python int,按 ctypes.c_uint16 传入 输入 0-65535 设备 USB 速率原始参数,不直接表示 bit/s

输出参数

无。

返回值

返回值 含义
0 参数写入命令成功;仍需验证后续通信
-3 设备未打开
-4/-7/-8/-9 设备返回、校验或通信异常
-10 设备报告当前写操作受保护
-11 当前设备不支持该设置

Python 示例

ALLOW_USB_PARAMETER_WRITE = False
approved_rate_parameter = None

if ALLOW_USB_PARAMETER_WRITE:
    if approved_rate_parameter is None:
        raise ValueError("Set the supplier-approved USB parameter first")
    if not 0 <= approved_rate_parameter <= 0xFFFF:
        raise ValueError("USB parameter must fit uint16")
    require_success(
        sdk.pynect_direct_set_usb_baud_rate(approved_rate_parameter),
        "Set USB rate parameter",
    )
else:
    print("Skipped: USB parameter write is disabled")

常见错误

直接传入 115200 等常见串口波特率并认为设备会自动换算;未保存原值;写入后没有立即验证设备通信。关联 API:pynect_direct_get_usb_baud_ratepynect_direct_get_usb_status

pynect_direct_get_usb_baud_rate

功能说明

读取 UV/VIS 设备保存的 USB 通信速率原始参数。该函数不会修改通信设置。

适用范围

UV/VIS;只读。NIR 返回 -11

调用前提

设备已打开。准备一个无符号 16 位输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围 说明
rate ctypes.c_uint16() + ctypes.byref() 输出 0-65535 设备 USB 速率原始参数

除非设备资料明确给出换算关系,否则只记录 rate.value,不要在界面中加上 bit/s、baud 等单位。

返回值

返回值 含义
0 rate.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持该查询

Python 示例

import ctypes

rate_parameter = ctypes.c_uint16()
code = sdk.pynect_direct_get_usb_baud_rate(
    ctypes.byref(rate_parameter)
)
if code == -11:
    print("USB rate parameter is not supported")
else:
    require_success(code, "Get USB rate parameter")
    print("Raw USB rate parameter:", rate_parameter.value)

常见错误

给原始值附加未经确认的波特率单位;把返回码 0 当成查询结果;输出变量类型错误。关联 API:pynect_direct_set_usb_baud_rate

pynect_direct_set_dac_voltage

功能说明

设置设备 DAC 的 12 位原始输出码。函数名中的 voltage 不表示参数单位是伏特;实际电压取决于设备的基准电压、输出电路和标定关系。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_DAC。NIR 返回 -11。该接口会改变硬件输出,调用前必须确认外部负载和允许电压范围。

调用前提

设备已打开,DAC capability 已确认,外部连接安全,并已根据设备资料把目标电压换算成批准的原始码值。建议先读取并记录当前码值。

输入参数

参数 Python 类型 方向 范围/单位 说明
dac_value Python int,按 ctypes.c_uint16 传入 输入 原始码 0-4095 12 位 DAC 码值,不是 mV 或 V

尽管 ABI 容器可表示到 65535,应用层必须限制到 12 位范围。

输出参数

无。

返回值

返回值 含义
0 DAC 码值设置成功
-3 设备未打开
-4/-7/-8/-9 设备返回、校验或通信异常
-10 设备报告当前写操作受保护
-11 当前设备不支持 DAC

Python 示例

ALLOW_DAC_OUTPUT = False
approved_dac_code = 0

if ALLOW_DAC_OUTPUT:
    if not 0 <= approved_dac_code <= 4095:
        raise ValueError("DAC code must be in the 12-bit range")
    require_success(
        sdk.pynect_direct_set_dac_voltage(approved_dac_code),
        "Set DAC code",
    )
else:
    print("Skipped: DAC output is disabled")

常见错误

3.33300 等电压值直接传入;只按 uint16 范围检查而允许大于 4095;未确认负载。关联 API:pynect_direct_get_dac_voltagepynect_direct_get_capabilities

pynect_direct_get_dac_voltage

功能说明

读取当前 DAC 的 12 位原始码值。函数只返回设备码值,不执行电压换算。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_DAC;只读。NIR 返回 -11

调用前提

设备已打开并支持 DAC。准备一个无符号 16 位输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围/单位 说明
dac_value ctypes.c_uint16() + ctypes.byref() 输出 原始码 0-4095 当前 12 位 DAC 码值

若需要显示电压,必须使用设备资料规定的换算和标定参数,不能只按 码值 / 4095 猜测。

返回值

返回值 含义
0 dac_value.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持 DAC

Python 示例

import ctypes

dac_code = ctypes.c_uint16()
code = sdk.pynect_direct_get_dac_voltage(ctypes.byref(dac_code))
if code == -11:
    print("DAC query is not supported")
else:
    require_success(code, "Get DAC code")
    print("Raw 12-bit DAC code:", dac_code.value)

常见错误

把码值直接显示为伏特;使用有符号 16 位容器;把函数返回码当作 DAC 数据。关联 API:pynect_direct_set_dac_voltage

15.6 外触发采集

pynect_direct_set_external_trigger_config

功能说明

设置外触发开关、触发类型和计划采集帧数。该函数改变当前设备的采集触发方式;配置错误时,普通采谱可能一直等待外部信号或超时。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER。NIR 返回 -11。这是临时采集配置,修改前应保存原配置并在实验结束后恢复。

调用前提

设备已打开,外部触发 capability 已确认,触发线、电平和时序符合设备资料。课堂基础实验应保持外触发关闭。

输入参数

参数 Python 类型 方向 常用值/范围 说明
enable Python int,按 ctypes.c_uint8 传入 输入 0x000xAA 0x00TRIGGER_OFF0xAA 为 SDK 定义的 TRIGGER_ON_RISE
trig_type Python int,按 ctypes.c_uint8 传入 输入 常用 0xBB 0xBBTRIGGER_ON_LEVEL;其他取值必须以设备资料为准
scan_num Python int,按 ctypes.c_uint8 传入 输入 1-255 计划采集帧数;0 的含义依设备而定,不建议使用

三个参数在 ABI 中都是无符号 8 位值。SDK 不替应用验证组合是否适合具体固件。

输出参数

无。

返回值

返回值 含义
0 外触发配置命令成功
-3 设备未打开
-4/-7/-8/-9 设备返回、校验或通信异常
-10 设备报告当前写操作受保护
-11 当前设备不支持外触发

Python 示例

import ctypes

TRIGGER_OFF = 0x00
TRIGGER_ON_LEVEL = 0xBB
ALLOW_TRIGGER_CONFIGURATION = False

old_enable = ctypes.c_uint8()
old_type = ctypes.c_uint8()
old_scan_num = ctypes.c_uint8()
require_success(
    sdk.pynect_direct_get_external_trigger_config(
        ctypes.byref(old_enable),
        ctypes.byref(old_type),
        ctypes.byref(old_scan_num),
    ),
    "Read trigger configuration before changing it",
)

if ALLOW_TRIGGER_CONFIGURATION:
    require_success(
        sdk.pynect_direct_set_external_trigger_config(
            TRIGGER_OFF, TRIGGER_ON_LEVEL, 1
        ),
        "Disable external trigger",
    )
else:
    print("Skipped: trigger configuration is disabled")

常见错误

修改前未保存三个原值;把 scan_num 当成字节数;不了解接线就启用外触发;实验结束后未恢复配置。关联 API:pynect_direct_get_external_trigger_configpynect_direct_get_spectrum_capture_status

pynect_direct_get_external_trigger_config

功能说明

读取当前外触发开关值、触发类型值和计划采集帧数。函数只查询配置,不会启动采集。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER;只读。NIR 返回 -11

调用前提

设备已打开并支持外触发。准备三个独立的无符号 8 位输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 输出范围 说明
enable ctypes.c_uint8() + ctypes.byref() 输出 0-255 外触发开关原始值;常见 0x000xAA
trig_type ctypes.c_uint8() + ctypes.byref() 输出 0-255 触发类型原始值;常见电平类型为 0xBB
scan_num ctypes.c_uint8() + ctypes.byref() 输出 0-255 设备报告的计划帧数

未知原始值应以十六进制记录并查询对应设备资料。

返回值

返回值 含义
0 三个输出变量均已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持外触发

Python 示例

import ctypes

enable = ctypes.c_uint8()
trigger_type = ctypes.c_uint8()
scan_num = ctypes.c_uint8()
code = sdk.pynect_direct_get_external_trigger_config(
    ctypes.byref(enable),
    ctypes.byref(trigger_type),
    ctypes.byref(scan_num),
)
if code == -11:
    print("External trigger is not supported")
else:
    require_success(code, "Get external trigger configuration")
    print(f"Enable: 0x{enable.value:02X}")
    print(f"Type: 0x{trigger_type.value:02X}")
    print("Scan frames:", scan_num.value)

常见错误

只读取一个输出变量;忘记 ctypes.byref();看到未知值后自行套用其他型号设备的含义。关联 API:pynect_direct_set_external_trigger_config

pynect_direct_get_spectrum_capture_status

功能说明

读取外触发采集的 64 位原始状态字,用于判断设备当前采集状态或已完成情况。SDK 返回完整状态值,但不替应用解释各状态位。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER;只读。NIR 返回 -11

调用前提

设备已打开并支持外触发。若要判断具体位含义,必须准备对应型号和固件版本的设备资料。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围/格式 说明
status ctypes.c_uint64() + ctypes.byref() 输出 0-0xFFFFFFFFFFFFFFFF 64 位原始采集状态字

在没有位定义资料时,可记录十六进制值用于诊断,不应仅凭某次观察给各位命名。

返回值

返回值 含义
0 status.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持外触发状态查询

Python 示例

import ctypes

capture_status = ctypes.c_uint64()
code = sdk.pynect_direct_get_spectrum_capture_status(
    ctypes.byref(capture_status)
)
if code == -11:
    print("Capture status is not supported")
else:
    require_success(code, "Get capture status")
    print(f"Capture status: 0x{capture_status.value:016X}")

常见错误

使用 ctypes.c_uint32 丢失高 32 位;把函数返回码当成状态值;没有设备位定义就把非零值解释为“已完成”。关联 API:pynect_direct_get_external_trigger_configpynect_direct_external_spectrum_control

pynect_direct_external_spectrum_control

功能说明

请求并读取指定外触发帧的原始光谱强度。缓冲区中每个数据点是无符号 16 位整数,但公开参数按字节指针传入;函数成功后返回写入的总字节数,而不是标准成功码 0

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER 和光谱数据能力;只读采集。NIR 返回 -11

调用前提

设备已打开;外触发已按设备资料配置;目标帧已经采集完成;frame_index 的起始编号已按设备资料确认。先查询完整光谱点数,并按该点数分配 16 位数组。

输入参数

参数 Python 类型 方向 单位/范围 说明
frame_index Python int,按 ctypes.c_uint16 传入 输入 0-65535,帧编号 要读取的外触发帧;是否从 0 开始以设备资料为准
data_out (ctypes.c_uint16 * point_capacity)() 后转换为 POINTER(c_uint8) 输入/输出 point_capacity 个 16 位元素 接收原始强度;虽然参数是字节指针,实际按 16 位光谱点写入
max_len Python int 输入 数据点 缓冲区最多容纳的光谱点数,不是字节数

缓冲区实际字节容量必须至少为 max_len * 2。不要把 max_len 传成字节容量。

输出参数

成功时,前 返回值 / 2ctypes.c_uint16 元素有效。每个元素范围为 0-65535,并应与设备的波长数组按索引对应。

返回值

返回值 含义
>= 0 成功读取的字节数;有效点数为 返回值 // 2,正常结果应为偶数
-3 设备未打开
-4 设备报告帧读取失败、状态异常或返回数据异常
-5 内存或缓冲区相关参数无效
-7/-8 数据校验或包头异常
-9 等待指定帧准备完成时超时
-11 当前设备不支持外触发帧读取

Python 示例

import ctypes

frame_index = 0
point_count = ctypes.c_uint16()
require_success(
    sdk.pynect_direct_get_wavelength_count(ctypes.byref(point_count)),
    "Get wavelength count",
)

intensities = (ctypes.c_uint16 * point_count.value)()
byte_pointer = ctypes.cast(
    intensities, ctypes.POINTER(ctypes.c_uint8)
)
byte_count = sdk.pynect_direct_external_spectrum_control(
    frame_index, byte_pointer, point_count.value
)
if byte_count < 0:
    raise RuntimeError(f"Read external frame failed: {byte_count}")
if byte_count % 2 != 0:
    raise RuntimeError("Device returned an odd spectrum byte count")

valid_points = byte_count // 2
values = list(intensities[:valid_points])
print("External frame points:", valid_points)
print("First intensity values:", values[:5])

常见错误

成功时仍要求返回 0;按字节数分配 c_uint8 数组后直接当 16 位强度使用;把 max_len 传成数组字节数;目标帧未完成就读取。关联 API:pynect_direct_get_wavelength_countpynect_direct_get_spectrum_capture_status、完整波长数据接口。

15.7 Flash、写保护和设备复位

pynect_direct_set_flash_write_protect

功能说明

向设备发送 Flash/FLS 写保护流程使用的设备定义状态命令。该函数没有布尔参数,不是通用的“打开保护”或“关闭保护”开关;具体作用必须结合设备型号的维护资料理解。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_FLASH;高风险维护接口。普通测量、课堂实验和自动启动程序不得调用。

调用前提

设备已打开,已取得设备供应方或实验室管理员的明确维护授权,并已确认该型号对本命令的定义、当前状态和恢复流程。

输入参数

无。

输出参数

无。命令执行后的状态应另用 pynect_direct_get_flash_write_protect 查询。

返回值

返回值 含义
0 设备已接受状态命令;不等于任何 Flash 数据已写入
-3 设备未打开
-4/-7/-8/-9 设备拒绝、返回校验异常或通信超时
-10 设备报告写保护相关拒绝

Python 示例

import ctypes

ALLOW_FLASH_MAINTENANCE = False

if ALLOW_FLASH_MAINTENANCE:
    before = ctypes.c_int()
    require_success(
        sdk.pynect_direct_get_flash_write_protect(ctypes.byref(before)),
        "Read Flash state before maintenance command",
    )
    print("State before command:", before.value)

    require_success(
        sdk.pynect_direct_set_flash_write_protect(),
        "Set device-defined Flash protection state",
    )
else:
    print("Skipped: Flash maintenance is disabled")

常见错误

根据英文函数名猜测它一定会“锁定”或“解锁”设备;未查询原状态;把返回 0 当成 Flash 写入成功。关联 API:pynect_direct_get_flash_write_protectpynect_direct_flash_write

pynect_direct_get_flash_write_protect

功能说明

读取设备报告的 Flash/FLS 写保护流程状态,并转换成 10。该布尔值只表示设备状态标记是否处于已启用值,不应脱离设备维护资料解释为“当前一定可写”或“当前一定不可写”。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_FLASH;只读。

调用前提

设备已打开。准备一个 ctypes.c_int 输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 输出值 说明
enabled ctypes.c_int() + ctypes.byref() 输出 1 设备报告保护流程状态标记已启用
enabled 同上 输出 0 设备报告该状态标记未启用

返回值

返回值 含义
0 enabled.value 已写入;状态值不在函数返回码中
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-5 输出指针无效

Python 示例

import ctypes

flash_state = ctypes.c_int()
require_success(
    sdk.pynect_direct_get_flash_write_protect(
        ctypes.byref(flash_state)
    ),
    "Get Flash protection state",
)
print("Device state flag enabled:", bool(flash_state.value))

常见错误

把函数返回的 0 当成“保护关闭”;看到输出 1 就在没有授权的情况下执行写入;使用 c_uint8 代替 c_int。关联 API:pynect_direct_set_flash_write_protect

pynect_direct_reset_device

功能说明

向当前设备发送复位命令。复位可能中断正在进行的采集,并使临时参数或当前设备状态发生变化;DLL 中已选择的波长范围会失效,不能直接继续调用指定波段读取接口。

适用范围

UV/VIS、NIR;高风险设备控制接口。仅用于明确的故障恢复或维护流程。

调用前提

设备已打开;采集已停止;未保存的测量结果已处理;应用已准备在复位后重新确认设备是否在线、重新查询 capability,并恢复所需临时参数和波长范围。

输入参数

无。

输出参数

无。

返回值

返回值 含义
0 复位命令发送成功;仍需执行复位后检查
-3 设备未打开
-4/-7/-8/-9 设备返回、校验或通信异常

Python 示例

ALLOW_DEVICE_RESET = False

if ALLOW_DEVICE_RESET:
    require_success(
        sdk.pynect_direct_reset_device(),
        "Reset device",
    )
    print("Reset sent; recheck connection, settings, and wavelength range")
else:
    print("Skipped: device reset is disabled")

常见错误

采集过程中复位;复位成功后立即读取指定波段而没有重新设置范围;把命令发送成功等同于设备已经完全恢复。关联 API:pynect_direct_is_openpynect_direct_get_capabilitiespynect_direct_set_selected_wavelength_range

pynect_direct_flash_read

功能说明

读取指定 Flash/FLS 扇区的原始字节。函数不会解析标定、身份或配置字段;成功时直接返回实际读取字节数,而不是标准成功码 0

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_FLASH;只读维护接口。读取到的数据可能包含重要设备配置,必须按设备资料解释。

调用前提

设备已打开,扇区编号已由设备资料确认,调用者已按字节容量分配输出数组。UV/VIS 单次有效读取上限约为 1024 字节;更大的内容应按设备规定分段读取。

输入参数

参数 Python 类型 方向 单位/范围 说明
sector Python int,按 ctypes.c_uint16 传入 输入 0-65535,扇区编号 只能使用设备资料明确允许读取的扇区
data_out (ctypes.c_uint8 * max_len)() 输入/输出 max_len 字节 接收原始 Flash/FLS 内容
max_len Python int 输入 字节,必须大于 0 输出缓冲区容量;不是 16 位数据点数量

输出参数

成功后 data_out[0:返回值] 有效。未读取到的数组尾部仍是初始化值,不属于设备返回内容。

返回值

返回值 含义
>= 0 实际读取字节数;只处理该长度内的数据
-3 设备未打开
-4 设备报告读取失败或返回数据异常
-5 缓冲区无效、长度不大于 0 或内存分配失败
-7/-8 数据校验或包头异常
-9 等待 Flash 读取完成时超时

Python 示例

import ctypes

# 扇区编号必须替换为设备资料明确允许读取的值。
approved_sector = 0
buffer_capacity = 1024
buffer = (ctypes.c_uint8 * buffer_capacity)()

bytes_read = sdk.pynect_direct_flash_read(
    approved_sector, buffer, buffer_capacity
)
if bytes_read < 0:
    raise RuntimeError(f"Flash read failed: {bytes_read}")

payload = bytes(buffer[:bytes_read])
print("Flash bytes read:", bytes_read)
print("First bytes:", payload[:16].hex(" "))

常见错误

成功时要求返回 0;把 max_len 当成数据点数;解析整个缓冲区而不是前 bytes_read 字节;未经资料确认就猜测扇区含义。关联 API:pynect_direct_get_flash_write_protectpynect_direct_get_capabilities

pynect_direct_flash_write

功能说明

把原始字节写入指定 Flash/FLS 扇区。错误的扇区、长度或数据可能破坏设备标定、身份信息或启动配置,导致设备测量错误或无法使用。

适用范围

UV/VIS、NIR;要求 PYNECT_DIRECT_CAP_FLASH;最高风险维护接口。学生实验禁止调用,普通应用不应提供此功能入口。

调用前提

设备已打开;已获得书面维护授权;目标扇区和精确数据格式已由设备供应方确认;原扇区已有可验证备份;电源和 USB 连接稳定;写后校验及失败恢复方案已准备完成。

输入参数

参数 Python 类型 方向 单位/范围 说明
sector Python int,按 ctypes.c_uint16 传入 输入 0-65535,扇区编号 必须是维护资料明确授权的目标扇区
data (ctypes.c_uint8 * len)(...) 输入 len 字节 待写入的完整原始字节,不得使用试验数据
len Python int 输入 字节,必须大于 0 必须等于数组中有效数据的准确长度,并符合设备单次写入限制

输出参数

无。写入成功后应使用设备规定的只读方式重新读取并核对内容;不要仅依赖返回码判断长期保存结果。

返回值

返回值 含义
0 写入流程完成
-3 设备未打开
-4 设备拒绝写入、完成状态异常或数据返回异常
-5 数据为空、长度不大于 0 或内存分配失败
-7/-8 数据校验或包头异常
-9 等待写入完成时超时
-10 设备报告写保护,写入未获允许

Python 示例

import ctypes

ALLOW_FLASH_WRITE = False
approved_sector = None
approved_payload = None

if ALLOW_FLASH_WRITE:
    if approved_sector is None or approved_payload is None:
        raise ValueError("Approved sector and payload are required")
    if not 0 <= approved_sector <= 0xFFFF:
        raise ValueError("Flash sector must fit uint16")

    payload_bytes = bytes(approved_payload)
    if not payload_bytes:
        raise ValueError("Flash payload must not be empty")
    data = (ctypes.c_uint8 * len(payload_bytes)).from_buffer_copy(
        payload_bytes
    )
    require_success(
        sdk.pynect_direct_flash_write(
            approved_sector, data, len(payload_bytes)
        ),
        "Write approved Flash payload",
    )
else:
    print("Skipped: Flash write is disabled")

常见错误

用全零或随机数据测试写接口;扇区编号偏移一位;len 与有效数组长度不一致;没有备份和写后校验;出现超时后立即重复写入。关联 API:pynect_direct_flash_readpynect_direct_get_flash_write_protect

15.8 固化配置与狭缝信息

pynect_direct_set_stored_integration_time

功能说明

把默认积分时间写入设备的固化配置。它不同于只影响当前会话的 pynect_direct_set_integration_time,可能改变设备以后上电或初始化时采用的默认值。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_STORED_CONFIG;高风险持久配置接口。NIR 返回 -11

调用前提

设备已打开;设备型号和允许的积分时间范围已确认;已读取并记录原固化值;已获得教师、实验室管理员或设备维护人员授权。

输入参数

参数 Python 类型 方向 单位/范围 说明
us Python int,按 ctypes.c_uint 传入 输入 微秒;ABI 范围 0-4294967295 要保存的默认积分时间;应用应限制为设备允许的正整数范围

ABI 可表示范围不等于设备有效范围。超出设备资料范围的值不得通过试错方式写入。

输出参数

无。写入后应调用对应读取函数核对。

返回值

返回值 含义
0 固化积分时间写入命令成功
-3 设备未打开
-4/-7/-8/-9 设备拒绝、返回校验异常或通信超时
-10 设备报告当前写操作受保护
-11 当前设备不支持固化配置

Python 示例

import ctypes

ALLOW_STORED_CONFIG_WRITE = False
approved_integration_us = None

if ALLOW_STORED_CONFIG_WRITE:
    if approved_integration_us is None:
        raise ValueError("An approved integration time is required")
    if not 0 < approved_integration_us <= 0xFFFFFFFF:
        raise ValueError("Stored integration time must fit uint32")

    old_value = ctypes.c_uint()
    require_success(
        sdk.pynect_direct_get_stored_integration_time(
            ctypes.byref(old_value)
        ),
        "Read stored integration time",
    )
    print("Original stored value:", old_value.value, "us")
    require_success(
        sdk.pynect_direct_set_stored_integration_time(
            approved_integration_us
        ),
        "Set stored integration time",
    )
else:
    print("Skipped: stored configuration write is disabled")

常见错误

误以为该设置只对当前采集有效;单位使用毫秒;未记录原值;用 ABI 最大范围代替设备有效范围。关联 API:pynect_direct_get_stored_integration_timepynect_direct_set_integration_time

pynect_direct_set_stored_average_number

功能说明

把默认光谱平均次数写入设备的固化配置。较大的默认平均次数会增加以后采集的总耗时,且可能影响依赖响应时间的实验流程。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_STORED_CONFIG;高风险持久配置接口。NIR 返回 -11

调用前提

设备已打开;已确认设备允许范围并记录原固化值;已评估目标平均次数对采集时间的影响;已获得维护授权。

输入参数

参数 Python 类型 方向 范围/单位 说明
count Python int,按 ctypes.c_uint8 传入 输入 通常 1-255 要保存的默认平均次数;准确有效范围以设备资料为准

不建议写入 0,除非具体设备资料明确规定其含义。

输出参数

无。写入后应调用对应读取函数核对。

返回值

返回值 含义
0 固化平均次数写入命令成功
-3 设备未打开
-4/-7/-8/-9 设备拒绝、返回校验异常或通信超时
-10 设备报告当前写操作受保护
-11 当前设备不支持固化配置

Python 示例

import ctypes

ALLOW_STORED_CONFIG_WRITE = False
approved_average_count = None

if ALLOW_STORED_CONFIG_WRITE:
    if approved_average_count is None:
        raise ValueError("An approved average count is required")
    if not 1 <= approved_average_count <= 255:
        raise ValueError("Stored average count must be 1-255")

    old_value = ctypes.c_uint8()
    require_success(
        sdk.pynect_direct_get_stored_average_number(
            ctypes.byref(old_value)
        ),
        "Read stored average count",
    )
    print("Original stored average:", old_value.value)
    require_success(
        sdk.pynect_direct_set_stored_average_number(
            approved_average_count
        ),
        "Set stored average count",
    )
else:
    print("Skipped: stored configuration write is disabled")

常见错误

把平均次数当作毫秒;写入 0 而未确认设备含义;忽略平均次数会成倍增加采集时间;把固化值与当前会话值混淆。关联 API:pynect_direct_get_stored_average_numberpynect_direct_set_average_number

pynect_direct_set_stored_smoothing_width

功能说明

把默认平滑宽度写入设备的固化配置。平滑会影响光谱细节和峰形,错误参数可能降低后续测量的光谱分辨表现。

适用范围

仅应在报告 PYNECT_DIRECT_CAP_STORED_CONFIG 的 UV/VIS 设备上使用;高风险持久配置接口。当前库对 NIR 调用可能返回 0 但不实际应用设置,因此 NIR 程序必须依靠 capability 检查跳过此函数,不能仅根据返回码判断是否生效。

调用前提

设备已打开;capability 已确认;平滑宽度的允许范围和定义已从设备资料取得;已记录原固化值并获得维护授权。

输入参数

参数 Python 类型 方向 范围/单位 说明
width Python int,按 ctypes.c_uint8 传入 输入 ABI 范围 0-255,设备定义的宽度值 准确有效范围、是否允许 0 以及窗口含义均以设备资料为准

输出参数

无。UV/VIS 写入后应调用对应读取函数核对;NIR 的成功返回不得视为已应用。

返回值

返回值 含义
0 UV/VIS 写入命令成功;NIR 当前也可能返回 0 但不执行设置
-3 设备未打开
-4/-7/-8/-9 UV/VIS 设备拒绝、返回校验异常或通信超时
-10 设备报告当前写操作受保护
-11 当前协议不是受支持的 UV/VIS 或兼容 NIR 路径

Python 示例

import ctypes

CAP_STORED_CONFIG = 0x00002000
ALLOW_STORED_CONFIG_WRITE = False
approved_smoothing_width = None

capabilities = ctypes.c_uint32()
require_success(
    sdk.pynect_direct_get_capabilities(ctypes.byref(capabilities)),
    "Get capabilities",
)

if not capabilities.value & CAP_STORED_CONFIG:
    print("Skipped: stored smoothing is not supported")
elif ALLOW_STORED_CONFIG_WRITE:
    if approved_smoothing_width is None:
        raise ValueError("An approved smoothing width is required")
    if not 0 <= approved_smoothing_width <= 255:
        raise ValueError("Smoothing width must fit uint8")
    require_success(
        sdk.pynect_direct_set_stored_smoothing_width(
            approved_smoothing_width
        ),
        "Set stored smoothing width",
    )
else:
    print("Skipped: stored configuration write is disabled")

常见错误

在 NIR 上看到返回 0 就认为设置已保存;未检查 capability;用试错方式寻找宽度范围;忽略平滑对峰形的影响。关联 API:pynect_direct_get_stored_smoothing_widthpynect_direct_get_capabilities

pynect_direct_get_stored_integration_time

功能说明

读取设备固化配置中的默认积分时间。该值不一定等于当前会话正在使用的积分时间。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_STORED_CONFIG;只读。NIR 返回 -11

调用前提

设备已打开并支持固化配置。准备一个与无符号 32 位参数匹配的 ctypes.c_uint 输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位/范围 说明
us ctypes.c_uint() + ctypes.byref() 输出 微秒;无符号 32 位 设备保存的默认积分时间

返回值

返回值 含义
0 us.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持固化配置读取

Python 示例

import ctypes

stored_us = ctypes.c_uint()
code = sdk.pynect_direct_get_stored_integration_time(
    ctypes.byref(stored_us)
)
if code == -11:
    print("Stored integration time is not supported")
else:
    require_success(code, "Get stored integration time")
    print("Stored integration time:", stored_us.value, "us")

常见错误

使用 c_uint8c_uint16 截断数值;把固化默认值当成当前实际值;误把单位当成毫秒。关联 API:pynect_direct_get_integration_timepynect_direct_set_stored_integration_time

pynect_direct_get_stored_average_number

功能说明

读取设备固化配置中的默认平均次数。该值用于了解设备保存的启动配置,不一定等于当前会话的平均次数。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_STORED_CONFIG;只读。NIR 返回 -11

调用前提

设备已打开并支持固化配置。准备一个无符号 8 位输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围/单位 说明
count ctypes.c_uint8() + ctypes.byref() 输出 0-255 设备保存的默认平均次数;有效含义以设备资料为准

返回值

返回值 含义
0 count.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持固化配置读取

Python 示例

import ctypes

stored_average = ctypes.c_uint8()
code = sdk.pynect_direct_get_stored_average_number(
    ctypes.byref(stored_average)
)
if code == -11:
    print("Stored average count is not supported")
else:
    require_success(code, "Get stored average count")
    print("Stored average count:", stored_average.value)

常见错误

把函数返回码当成平均次数;把固化默认值与当前平均次数混淆;看到 0 后擅自解释其设备含义。关联 API:pynect_direct_get_average_numberpynect_direct_set_stored_average_number

pynect_direct_get_stored_smoothing_width

功能说明

读取设备固化配置中的默认平滑宽度。返回的是设备定义的原始宽度值,SDK 不解释窗口算法或平滑阶数。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_STORED_CONFIG;只读。NIR 返回 -11

调用前提

设备已打开并支持固化配置。若要解释该值对光谱处理的影响,需要对应设备的平滑参数资料。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 范围/单位 说明
width ctypes.c_uint8() + ctypes.byref() 输出 0-255,设备定义宽度值 设备保存的默认平滑宽度

返回值

返回值 含义
0 width.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持该固化配置查询

Python 示例

import ctypes

stored_width = ctypes.c_uint8()
code = sdk.pynect_direct_get_stored_smoothing_width(
    ctypes.byref(stored_width)
)
if code == -11:
    print("Stored smoothing width is not supported")
else:
    require_success(code, "Get stored smoothing width")
    print("Stored smoothing width:", stored_width.value)

常见错误

把宽度值直接解释成 nm 或像素数;在 NIR 上调用后忽略 -11;把读取成功误认为当前光谱一定已应用该平滑。关联 API:pynect_direct_set_stored_smoothing_width

pynect_direct_get_slit_width

功能说明

读取设备保存的入射狭缝宽度信息。该值是设备身份和光学配置参数,不会改变狭缝,也不能单独代表光谱分辨率。

适用范围

UV/VIS;要求 PYNECT_DIRECT_CAP_SLIT_WIDTH;只读。NIR 返回 -11

调用前提

设备已打开并支持狭缝信息查询。准备一个无符号 8 位输出变量。

输入参数

无普通输入值。

输出参数

参数 Python 类型 方向 单位/范围 说明
um ctypes.c_uint8() + ctypes.byref() 输出 微米(um);0-255 设备报告的狭缝宽度

文档使用 ASCII um 与函数参数名保持一致,其物理单位为微米。

返回值

返回值 含义
0 um.value 已写入
-3 设备未打开
-4/-7/-8/-9 返回数据长度、校验或通信异常
-11 当前设备不支持狭缝宽度查询

Python 示例

import ctypes

slit_width_um = ctypes.c_uint8()
code = sdk.pynect_direct_get_slit_width(
    ctypes.byref(slit_width_um)
)
if code == -11:
    print("Slit width query is not supported")
else:
    require_success(code, "Get slit width")
    print("Slit width:", slit_width_um.value, "um")

常见错误

使用 c_uint16 后忽略 ABI 定义;把数值单位当成 nm;仅凭狭缝宽度推断整机分辨率。关联 API:pynect_direct_get_device_modelpynect_direct_get_hardware_version

16. 学生实验

以下实验默认禁止调用第 12 章列出的高风险写接口。

16.1 实验一:环境和设备检查

目标:完成只读测试并识别设备。

步骤:

  1. 检查 Python 为 64 位。
  2. 检查 dll 文件夹内包含两个 DLL。
  3. 运行 minimal_test.py --read-only
  4. 记录 DLL 路径、芯片序列号、设备型号、设备序列号和波长点数。

完成标准:最终 FAIL=0,并能解释 PASSSKIP 的区别。

16.2 实验二:采集并保存完整光谱

目标:理解波长和强度的一一对应关系。

步骤:

  1. 运行完整光谱采集程序。
  2. 检查点数和波长范围。
  3. 保存 spectrum.csv
  4. 随机检查 5 行数据是否能在 Python 输出中找到对应值。

完成标准:CSV 中数据行数与波长点数一致,两列数据无错位。

16.3 实验三:绘制光谱曲线

目标:将 CSV 转换为可检查的光谱图。

步骤:

  1. 使用第 9 章脚本绘图。
  2. 标注样品名称、积分时间和平均次数。
  3. 检查横轴波长范围与设备返回范围一致。
  4. 描述主要峰或变化区域,不要求进行物质定性结论。

完成标准:图形包含标题、横轴名称、纵轴名称,数据没有明显错位。

16.4 实验四:积分时间比较

目标:观察积分时间对强度和采集时间的影响。

建议设置:使用设备允许范围内的三个积分时间,每个条件保持样品、光路和平均次数不变。

记录:

  • 每个积分时间的最大强度。
  • 是否出现饱和。
  • 单次采集大致耗时。
  • 实验结束后是否恢复原积分时间。

16.5 实验五:平均次数比较

目标:观察平均次数对随机波动和采集时间的影响。

建议依次使用 135 次平均,保持积分时间和样品不变。比较曲线平滑程度和采集时间,并在结束后恢复原值。

16.6 实验六:指定波段采集

目标:理解请求范围、实际范围和像素点之间的关系。

步骤:

  1. 读取设备完整波长范围。
  2. 在完整范围内选择一个较小波段。
  3. 设置波段并读取实际范围。
  4. 保存指定波段的波长和强度。
  5. 比较请求边界与实际边界的差异。

16.7 提交检查清单

提交前确认:

  • [ ] 记录 Python 位数和测试命令。
  • [ ] 记录设备型号、芯片序列号和设备序列号。
  • [ ] 记录积分时间、平均次数和测量时间。
  • [ ] CSV 数据行数与点数一致。
  • [ ] 波长和强度按索引配对。
  • [ ] 图形坐标轴和单位完整。
  • [ ] 程序使用 try/finally 关闭设备。
  • [ ] 临时参数已恢复。
  • [ ] 没有执行 Flash 写入、固化配置、设备复位或未知输出操作。

17. 日常使用检查表

测量前:

  • 检查 USB、光源、光纤和样品位置。
  • 确认没有其他程序占用设备。
  • 确认加载的 DLL 路径正确。
  • 记录原积分时间和平均次数。

测量中:

  • 检查光谱点数和波长范围。
  • 检查强度是否过低或饱和。
  • 保持样品和光路稳定。
  • 为每次数据记录设备和参数信息。

测量后:

  • 恢复临时参数。
  • 关闭设备。
  • 保存 CSV、图形和实验记录。
  • 确认文件可以重新打开和读取。