PynectDirect SDK 用户使用手册
版本:v3.0.1
适用对象:使用 PynectDirect 动态库进行二次开发的用户
适用场景:入门级学生、实验室研究人员、企业软件开发人员
运行环境:64 位 Windows7/10/11、64 位 Python 3(如使用 Python 集成)
配套文件:bin/PynectDirect.dll、bin/ftd2xx.dll、include/pynect_direct.h
下载链接:PynectDirect SDK
目录
1. SDK 简介
PynectDirect 是谱研互联(深圳谱研互联科技有限公司)光纤光谱仪的 Windows 动态库 SDK,仅供本公司光纤光谱仪使用,不支持其他厂家的光纤光谱仪。您只需要两件事就可以开始开发:
- 动态库:
PynectDirect.dll(SDK 本体)+ftd2xx.dll(FTDI USB 驱动,必须与 SDK 同目录) - 头文件:
pynect_direct.h(C 语言函数声明,是所有语言的签名权威来源)
SDK 通过 USB 直接与光纤光谱仪通信,提供 58 个公开函数,覆盖:设备发现与打开(支持同时打开多台)、参数设置(积分时间、平均次数、氙灯、触发)、波长与光谱数据采集、设备状态读取、Flash 与固化配置等功能。
1.1 支持设备
| 设备类型 | 说明 |
|---|---|
| SPEC-CDS350、SPEC-CMS960 | 紫外可见光纤光谱仪 |
| NIR-F950 | 近红外光纤光谱仪 |
打开设备后 SDK 自动识别设备类型。基础采谱流程两类设备相同;氙灯、外触发、DAC、固化配置等高级功能仅特定设备支持,调用前应查询设备能力(见 4.3 节)。
1.2 语言支持
SDK 导出标准 C ABI(extern "C"、__cdecl),因此任何支持 C ABI 的语言都可以直接调用:
| 语言 | 集成方式 | 例程位置 |
|---|---|---|
| C | 链接 lib/libPynectDirect.dll.a(或静态库) |
第 5 章 |
| C++ | 直接包含 pynect_direct.h(自动 extern "C") |
第 6 章 |
| C# | DllImport("PynectDirect.dll") P/Invoke |
第 7 章 |
| Python | ctypes 加载 DLL |
第 8 章 |
| 其他 | 任何支持 C ABI 的语言(Rust/Go/Delphi 等) | 按头文件签名声明即可 |
1.3 数据流模型(重要)
- 设备每次传输全量像素:一次读取返回所有像素点的波长或强度(例如 2048 点)。
- 波长范围来自全量数据:
pynect_direct_get_full_wavelength_range返回完整波长数组的第一个和最后一个点(即全部像素的最小/最大波长),该值必然正确。 - 指定波段在上位机截取:
pynect_direct_set_selected_wavelength_range只在上位机记录一个像素窗口,get_selected_*系列接口从全量数据中截取窗口内容返回,不会修改设备内置输出范围。
因此,切换显示波段只需在上位机完成,无需与设备交互,快速且不影响其他程序对设备的访问。
2. 快速开始
2.1 文件布局
推荐把 SDK 文件整理为如下结构(以 Python 为例,其他语言同理):
MyProject/
|-- 您的代码文件(xxx.py / xxx.c / xxx.cpp / xxx.cs / ...)
`-- dll/
|-- PynectDirect.dll
`-- ftd2xx.dll
PynectDirect.dll 和 ftd2xx.dll 必须放在同一个文件夹中。
- C/C++:将
include目录加入头文件搜索路径,运行时 DLL 位于程序目录或系统 PATH。 - C#:DLL 放在程序输出目录(exe 旁),或使用绝对路径。
- Python:在脚本中用
os.add_dll_directory()将dll文件夹加入搜索路径后再ctypes.CDLL加载。
2.2 环境要求
| 项目 | 要求 |
|---|---|
| 操作系统 | 64 位 Windows |
| 语言运行时 | 均为 64 位(32 位进程无法加载 64 位 DLL) |
| C/C++ 编译 | MinGW-w64 GCC 或 MSVC(64 位) |
| C# | .NET Framework 4.x 或 .NET 6+(x64 目标平台) |
| Python | 64 位 Python 3 |
2.3 验证运行环境
Python 快速验证(无需连接设备):
python -c "import ctypes; d=ctypes.CDLL(r'dll\PynectDirect.dll'); print('load ok, api =', d.pynect_direct_scan_devices())"
能打印 load ok, api = 0 或设备数量即说明 DLL 加载成功。
完整硬件验证(需连接设备):
python .\examples\python\minimal_test.py --read-only
应看到 FAIL=0。只读模式不会修改设备任何参数。
2.4 连接设备
- 将光纤光谱仪通过 USB 连接到电脑。
- 等待 Windows 完成驱动安装(FTDI 驱动需已安装)。
- 关闭其他正在使用该光纤光谱仪的软件(设备为独占访问)。
- 运行自己的程序。
3. 基础概念
3.1 调用顺序
所有程序的调用顺序基本相同:
1. pynect_direct_scan_devices() 扫描设备,确认存在
2. (可选) get_chip_serial_by_index() 查看各设备芯片序列号
3. pynect_direct_open_device(0) 打开设备(或按序列号打开)
4. get_device_info() / get_capabilities() 读取设备信息与能力
5. get_wavelength_count() 获取数据点数 N
6. get_full_wavelength_data() 获取波长坐标(double 数组,N 个)
7. get_full_spectrum_data() 采集强度(uint16 数组,N 个)
8. (可选) set_selected_wavelength_range() 设置显示波段,然后 get_selected_*
9. pynect_direct_close_device() 关闭设备
3.2 返回值约定
- 绝大多数函数成功返回
0,失败返回负错误码(见第 12 章)。 - 例外:
pynect_direct_scan_devices:返回设备数量,0表示没有设备(不是错误)。pynect_direct_flash_read/pynect_direct_external_spectrum_control:成功时返回实际读取字节数。pynect_direct_is_open:返回1(打开)/0(关闭)。pynect_direct_close_device:无返回值(void)。
3.3 数组容量约定
- 波长数组元素类型:double(8 字节)
- 强度数组元素类型:uint16(2 字节)
- 数组长度:调用
pynect_direct_get_wavelength_count得到 N,然后用 N 分配数组,把 N 作为max_len传入。 - 字符串缓冲区:建议分配 64 字节,
max_len包含结尾的\0空间。
3.4 会话规则
- 支持同时打开多台设备(最多
PYNECT_DIRECT_MAX_DEVICES= 4 台)。pynect_direct_open_device打开新设备时不会关闭已打开的设备。 - SDK 采用"当前设备"模型:所有参数和数据 API 操作的是当前选中的设备。
pynect_direct_open_device(0)→ 打开设备 0,并设为当前设备pynect_direct_open_device(1)→ 打开设备 1,并设为当前设备(设备 0 保持打开)pynect_direct_select_device(0)→ 切换当前设备为设备 0pynect_direct_get_open_device_count()→ 当前已打开设备数pynect_direct_close_device()→ 关闭当前选中的设备pynect_direct_close_all_devices()→ 关闭全部设备- 重复打开同一台设备(同芯片序列号)不会重复建句柄,只切换为当前设备。
- 每台设备有独立的状态:协议、能力、参数(积分时间、平均次数)、选定波段窗口互不影响。
- 当前会话参数(积分时间、平均次数等)在关闭/复位/重新上电后不保留。
- 固化配置(
stored_*系列)是设备的非易失参数,写入后掉电不丢失,会影响以后上电默认值,请谨慎使用。 - 设备复位(
reset_device)后,该设备已设置的选定波段窗口失效,需要重新设置。
4. 通用数据类型与常量
4.1 错误码
| 宏 | 值 | 含义 |
|---|---|---|
PYNECT_DIRECT_SUCCESS |
0 | 成功 |
PYNECT_DIRECT_ERR_NOT_INITIALIZED |
-1 | 未初始化(如未设置选定波段就读取) |
PYNECT_DIRECT_ERR_DEVICE_NOT_FOUND |
-2 | 未发现设备或索引越界 |
PYNECT_DIRECT_ERR_DEVICE_NOT_OPEN |
-3 | 设备未打开 |
PYNECT_DIRECT_ERR_COMM_FAIL |
-4 | 通信失败 |
PYNECT_DIRECT_ERR_INVALID_PARAM |
-5 | 参数无效 |
PYNECT_DIRECT_ERR_OPEN_FAILED |
-6 | 打开设备失败(被占用/驱动异常) |
PYNECT_DIRECT_ERR_CRC |
-7 | 响应 CRC 校验失败 |
PYNECT_DIRECT_ERR_PREAMBLE |
-8 | 响应帧头不匹配 |
PYNECT_DIRECT_ERR_TIMEOUT |
-9 | 设备响应超时 |
PYNECT_DIRECT_ERR_WPROT |
-10 | 写保护错误 |
PYNECT_DIRECT_ERR_UNSUPPORTED |
-11 | 当前设备不支持该功能 |
4.2 协议枚举
typedef enum PynectDirectDeviceProtocol {
PYNECT_DIRECT_PROTOCOL_AUTO = 0, // 自动识别(默认推荐)
PYNECT_DIRECT_PROTOCOL_UVVIS_CM2 = 1, // UV/VIS
PYNECT_DIRECT_PROTOCOL_NIR_LEGACY = 2, // NIR
PYNECT_DIRECT_PROTOCOL_UNKNOWN = 255
} PynectDirectDeviceProtocol;
4.3 能力位(capabilities)
pynect_direct_get_capabilities 返回 32 位掩码,判断设备支持哪些功能:
| 位 | 宏 | 功能 |
|---|---|---|
| 0x00000001 | PYNECT_DIRECT_CAP_INTEGRATION_TIME |
积分时间 |
| 0x00000002 | PYNECT_DIRECT_CAP_AVERAGE_NUMBER |
平均次数 |
| 0x00000004 | PYNECT_DIRECT_CAP_LEVEL_OUTPUT |
电平输出 |
| 0x00000008 | PYNECT_DIRECT_CAP_FLASH |
Flash 读写 |
| 0x00000010 | PYNECT_DIRECT_CAP_WAVELENGTH_DATA |
波长数据 |
| 0x00000020 | PYNECT_DIRECT_CAP_SPECTRUM_DATA |
光谱数据 |
| 0x00000040 | PYNECT_DIRECT_CAP_TEMPERATURE |
温度 |
| 0x00000080 | PYNECT_DIRECT_CAP_HARDWARE_VERSION |
硬件版本 |
| 0x00000100 | PYNECT_DIRECT_CAP_CALIBRATION |
标定系数 |
| 0x00000200 | PYNECT_DIRECT_CAP_XENON |
氙灯 |
| 0x00000400 | PYNECT_DIRECT_CAP_USB_STATUS |
USB 状态 |
| 0x00000800 | PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER |
外触发 |
| 0x00001000 | PYNECT_DIRECT_CAP_DAC |
DAC |
| 0x00002000 | PYNECT_DIRECT_CAP_STORED_CONFIG |
固化配置 |
| 0x00004000 | PYNECT_DIRECT_CAP_SLIT_WIDTH |
狭缝宽度 |
4.4 常用常量
// 氙灯模式
#define XENON_OFF 0x00 // 关闭
#define XENON_CONTINUOUS 0x01 // 连续
#define XENON_SINGLE 0x81 // 单次
// 外触发类型
#define TRIGGER_OFF 0x00 // 关闭
#define TRIGGER_ON_RISE 0xAA // 上升沿触发
#define TRIGGER_ON_LEVEL 0xBB // 电平触发
4.5 数据结构
设备信息:
typedef struct PynectDirectDeviceInfo {
PynectDirectDeviceProtocol protocol; // 实际协议类型
uint32_t capabilities; // 能力位
char protocol_name[32]; // 协议名称字符串
char model[32]; // 型号字符串
char serial_number[32]; // 业务序列号字符串
char hardware_version[64]; // 硬件版本字符串
} PynectDirectDeviceInfo;
选定波段信息(get_selected_wavelength_range 返回):
typedef struct PynectDirectSelectedWavelengthRange {
double request_start_nm; // 请求的起始波长(nm)
double request_end_nm; // 请求的终止波长(nm)
double actual_start_nm; // 实际截取的起始波长(对应像素的真实波长)
double actual_end_nm; // 实际截取的终止波长
uint16_t start_pixel; // 起始像素索引(从 0 开始)
uint16_t end_pixel; // 终止像素索引
uint16_t count; // 窗口内像素点数
} PynectDirectSelectedWavelengthRange;
5. C 语言例程
5.1 编译与运行
# 进入例程目录(依赖的 DLL、头文件、库均已放入该目录)
cd examples/c
# 方式一:直接运行(已预编译,无需安装任何开发环境)
spectrum_demo.exe
# 方式二:自行编译(MinGW-w64,64 位)
gcc spectrum_demo.c -Iinclude -Llib -lPynectDirect -o spectrum_demo.exe
程序运行结束后会暂停并提示"请按任意键继续",按任意键即可退出窗口,方便查看结果(源码中为
system("pause"))。使用 MSVC 时:
cl spectrum_demo.c /Iinclude /link /LIBPATH:lib(需.lib导入库,可联系厂商获取;GCC 环境使用libPynectDirect.dll.a)。完整源码见交付包
examples/c/spectrum_demo.c(即 5.2 节内容)。
5.2 完整源码(spectrum_demo.c)
#define PYNECT_DIRECT_USE_DLL /* 使用动态库时定义 */
#include "pynect_direct.h"
#include <stdio.h>
#include <stdlib.h>
static int require(int code, const char* what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
printf("[FAIL] %s: return code = %d\n", what, code);
return 0;
}
return 1;
}
int main(void)
{
PynectDirectDeviceInfo info;
PynectDirectSelectedWavelengthRange sel;
uint16_t count = 0, i;
double* wavelengths = NULL;
uint16_t* spectrum = NULL;
double start_nm = 0.0, end_nm = 0.0;
unsigned int integration = 0;
int n;
/* 1. 扫描设备 */
n = pynect_direct_scan_devices();
if (n <= 0) {
printf("No device found.\n");
return 1;
}
printf("Found %d device(s).\n", n);
/* 2. 打开设备(索引 0) */
if (!require(pynect_direct_open_device(0), "open_device")) goto done;
/* 3. 读取设备信息 */
if (require(pynect_direct_get_device_info(&info), "get_device_info")) {
printf("Protocol: %s\n", info.protocol_name);
printf("Model: %s\n", info.model);
printf("Serial: %s\n", info.serial_number);
printf("Hardware: %s\n", info.hardware_version);
}
/* 4. 数据点数 */
if (!require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count")) goto done;
printf("Wavelength count: %u\n", count);
if (count == 0) goto done;
/* 5. 波长范围(全量波长数组首尾,必然正确) */
if (!require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm), "get_full_wavelength_range")) goto done;
printf("Wavelength range: %.2f ~ %.2f nm\n", start_nm, end_nm);
/* 6. 分配数组 */
wavelengths = (double*)malloc(count * sizeof(double));
spectrum = (uint16_t*)malloc(count * sizeof(uint16_t));
if (!wavelengths || !spectrum) {
printf("Out of memory.\n");
goto done;
}
/* 7. 读取波长坐标 */
if (!require(pynect_direct_get_full_wavelength_data(wavelengths, count), "get_full_wavelength_data")) goto done;
/* 8. 设置并读取积分时间(临时,不写入固化配置) */
if (require(pynect_direct_get_integration_time(&integration), "get_integration_time"))
printf("Current integration time: %u us\n", integration);
pynect_direct_set_integration_time(50000); /* 50 ms */
/* 9. 采集光谱 */
if (!require(pynect_direct_get_full_spectrum_data(spectrum, count), "get_full_spectrum_data")) goto done;
/* 10. 两列打印:波长 + 对应强度(前 5 点 + 后 5 点) */
printf("%6s %13s %9s\n", "Index", "Wavelength(nm)", "Intensity");
printf("%6s %13s %9s\n", "-----", "--------------", "---------");
for (i = 0; i < 5 && i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
if (count > 10) printf("... (total %u points)\n", count);
for (i = count > 5 ? count - 5 : 0; i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
/* 11. 指定波段(上位机截取,不修改设备) */
if (start_nm < 400.0 && end_nm > 800.0) {
if (require(pynect_direct_set_selected_wavelength_range(400.0, 800.0), "set_selected_range")) {
if (require(pynect_direct_get_selected_wavelength_range(&sel), "get_selected_range")) {
printf("Selected: pixels %u..%u, count=%u, actual %.2f..%.2f nm\n",
sel.start_pixel, sel.end_pixel, sel.count,
sel.actual_start_nm, sel.actual_end_nm);
}
}
} else {
printf("Device does not cover 400-800 nm, skip selected range.\n");
}
/* 12. 关闭设备 */
done:
free(wavelengths);
free(spectrum);
pynect_direct_close_device();
printf("Done.\n");
return 0;
}
#define PYNECT_DIRECT_USE_DLL /* 使用动态库时定义 */
#include "pynect_direct.h"
#include <stdio.h>
#include <stdlib.h>
static int require(int code, const char* what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
printf("[FAIL] %s: return code = %d\n", what, code);
return 0;
}
return 1;
}
int main(void)
{
PynectDirectDeviceInfo info;
PynectDirectSelectedWavelengthRange sel;
uint16_t count = 0, i;
double* wavelengths = NULL;
uint16_t* spectrum = NULL;
double start_nm = 0.0, end_nm = 0.0;
unsigned int integration = 0;
int n;
/* 1. 扫描设备 */
n = pynect_direct_scan_devices();
if (n <= 0) {
printf("No device found.\n");
system("pause");
return 1;
}
printf("Found %d device(s).\n", n);
/* 2. 打开设备(索引 0) */
if (!require(pynect_direct_open_device(0), "open_device")) goto done;
/* 3. 读取设备信息 */
if (require(pynect_direct_get_device_info(&info), "get_device_info")) {
printf("Protocol: %s\n", info.protocol_name);
printf("Model: %s\n", info.model);
printf("Serial: %s\n", info.serial_number);
printf("Hardware: %s\n", info.hardware_version);
}
/* 4. 数据点数 */
if (!require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count")) goto done;
printf("Wavelength count: %u\n", count);
if (count == 0) goto done;
/* 5. 波长范围(全量波长数组首尾,必然正确) */
if (!require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm), "get_full_wavelength_range")) goto done;
printf("Wavelength range: %.2f ~ %.2f nm\n", start_nm, end_nm);
/* 6. 分配数组 */
wavelengths = (double*)malloc(count * sizeof(double));
spectrum = (uint16_t*)malloc(count * sizeof(uint16_t));
if (!wavelengths || !spectrum) {
printf("Out of memory.\n");
goto done;
}
/* 7. 读取波长坐标 */
if (!require(pynect_direct_get_full_wavelength_data(wavelengths, count), "get_full_wavelength_data")) goto done;
/* 8. 设置并读取积分时间(临时,不写入固化配置) */
if (require(pynect_direct_get_integration_time(&integration), "get_integration_time"))
printf("Current integration time: %u us\n", integration);
pynect_direct_set_integration_time(50000); /* 50 ms */
/* 9. 采集光谱 */
if (!require(pynect_direct_get_full_spectrum_data(spectrum, count), "get_full_spectrum_data")) goto done;
/* 10. 两列打印:波长 + 对应强度(前 5 点 + 后 5 点) */
printf("%6s %13s %9s\n", "Index", "Wavelength(nm)", "Intensity");
printf("%6s %13s %9s\n", "-----", "--------------", "---------");
for (i = 0; i < 5 && i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
if (count > 10) printf("... (total %u points)\n", count);
for (i = count > 5 ? count - 5 : 0; i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
/* 11. 指定波段(上位机截取,不修改设备) */
if (start_nm < 400.0 && end_nm > 800.0) {
if (require(pynect_direct_set_selected_wavelength_range(400.0, 800.0), "set_selected_range")) {
if (require(pynect_direct_get_selected_wavelength_range(&sel), "get_selected_range")) {
printf("Selected: pixels %u..%u, count=%u, actual %.2f..%.2f nm\n",
sel.start_pixel, sel.end_pixel, sel.count,
sel.actual_start_nm, sel.actual_end_nm);
}
}
} else {
printf("Device does not cover 400-800 nm, skip selected range.\n");
}
/* 12. 关闭设备 */
done:
free(wavelengths);
free(spectrum);
pynect_direct_close_device();
printf("Done.\n");
system("pause");
return 0;
}
5.3 输出示例
Found 1 device(s).
Protocol: UVVIS_CM2
Model: PYNECT-UV-2048
Serial: SP123456
Hardware: 1.0
Wavelength count: 2048
Wavelength range: 199.04 ~ 1019.50 nm
Current integration time: 10000 us
Index Wavelength(nm) Intensity
----- -------------- ---------
0 199.04 128
1 200.08 130
2 201.12 127
3 202.16 125
4 203.20 131
... (total 2048 points)
2043 1018.46 122
2044 1019.50 121
2045 1020.54 119
2046 1021.58 124
2047 1022.62 126
Selected: pixels 808..1616, count=809, actual 400.00..800.00 nm
Done.
请按任意键继续. . .
6. C++ 语言例程
6.1 编译与运行
cd examples/cpp
# 方式一:直接运行(已预编译)
spectrum_demo.exe
# 方式二:自行编译
g++ spectrum_demo.cpp -Iinclude -Llib -lPynectDirect -o spectrum_demo.exe
程序运行结束后会暂停并提示"请按任意键继续",按任意键退出窗口(源码中为
system("pause"))。
头文件已包含 extern "C",C++ 直接 #include "pynect_direct.h" 即可。
完整源码见交付包 examples/cpp/spectrum_demo.cpp(即 6.2 节内容)。
6.2 完整源码(spectrum_demo.cpp)
#define PYNECT_DIRECT_USE_DLL
#include "pynect_direct.h"
#include <iomanip>
#include <iostream>
#include <vector>
#include <string>
static void require(int code, const std::string& what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
std::cerr << "[FAIL] " << what << ": return code = " << code << std::endl;
throw std::runtime_error(what);
}
}
int main()
{
try {
/* 1. 扫描设备 */
int n = pynect_direct_scan_devices();
if (n <= 0) {
std::cout << "No device found." << std::endl;
return 1;
}
std::cout << "Found " << n << " device(s)." << std::endl;
/* 2. 打开设备 */
require(pynect_direct_open_device(0), "open_device");
/* 3. 设备信息 */
PynectDirectDeviceInfo info{};
require(pynect_direct_get_device_info(&info), "get_device_info");
std::cout << "Model: " << info.model << std::endl
<< "Serial: " << info.serial_number << std::endl
<< "Hardware:" << info.hardware_version << std::endl;
/* 4. 数据点数 */
uint16_t count = 0;
require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count");
std::cout << "Wavelength count: " << count << std::endl;
/* 5. 波长范围 */
double start_nm = 0.0, end_nm = 0.0;
require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm),
"get_full_wavelength_range");
std::cout << "Wavelength range: " << start_nm << " ~ " << end_nm << " nm" << std::endl;
/* 6. 读取波长坐标 */
std::vector<double> wavelengths(count);
require(pynect_direct_get_full_wavelength_data(wavelengths.data(), count),
"get_full_wavelength_data");
/* 7. 设置积分时间并采集光谱 */
pynect_direct_set_integration_time(50000); /* 50 ms,临时设置 */
std::vector<uint16_t> spectrum(count);
require(pynect_direct_get_full_spectrum_data(spectrum.data(), count),
"get_full_spectrum_data");
/* 8. 两列打印:波长 + 对应强度(前 5 点 + 后 5 点) */
std::cout << std::setw(6) << "Index" << std::setw(15) << "Wavelength(nm)"
<< std::setw(11) << "Intensity" << std::endl;
std::cout << std::setw(6) << "-----" << std::setw(15) << "--------------"
<< std::setw(11) << "---------" << std::endl;
for (int i = 0; i < 5; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
if (count > 10)
std::cout << "... (total " << count << " points)" << std::endl;
for (int i = count - 5; i < count; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
/* 9. 指定波段(上位机截取) */
if (start_nm < 400.0 && end_nm > 800.0) {
require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range");
PynectDirectSelectedWavelengthRange sel{};
require(pynect_direct_get_selected_wavelength_range(&sel),
"get_selected_range");
std::cout << "Selected: pixels " << sel.start_pixel << ".."
<< sel.end_pixel << ", count=" << sel.count << std::endl;
}
/* 10. 关闭设备 */
pynect_direct_close_device();
std::cout << "Done." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
#define PYNECT_DIRECT_USE_DLL
#include "pynect_direct.h"
#include <iomanip>
#include <iostream>
#include <vector>
#include <string>
static void require(int code, const std::string& what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
std::cerr << "[FAIL] " << what << ": return code = " << code << std::endl;
throw std::runtime_error(what);
}
}
int main()
{
try {
/* 1. 扫描设备 */
int n = pynect_direct_scan_devices();
if (n <= 0) {
std::cout << "No device found." << std::endl;
system("pause");
return 1;
}
std::cout << "Found " << n << " device(s)." << std::endl;
/* 2. 打开设备 */
require(pynect_direct_open_device(0), "open_device");
/* 3. 设备信息 */
PynectDirectDeviceInfo info{};
require(pynect_direct_get_device_info(&info), "get_device_info");
std::cout << "Model: " << info.model << std::endl
<< "Serial: " << info.serial_number << std::endl
<< "Hardware:" << info.hardware_version << std::endl;
/* 4. 数据点数 */
uint16_t count = 0;
require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count");
std::cout << "Wavelength count: " << count << std::endl;
/* 5. 波长范围 */
double start_nm = 0.0, end_nm = 0.0;
require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm),
"get_full_wavelength_range");
std::cout << "Wavelength range: " << start_nm << " ~ " << end_nm << " nm" << std::endl;
/* 6. 读取波长坐标 */
std::vector<double> wavelengths(count);
require(pynect_direct_get_full_wavelength_data(wavelengths.data(), count),
"get_full_wavelength_data");
/* 7. 设置积分时间并采集光谱 */
pynect_direct_set_integration_time(50000); /* 50 ms,临时设置 */
std::vector<uint16_t> spectrum(count);
require(pynect_direct_get_full_spectrum_data(spectrum.data(), count),
"get_full_spectrum_data");
/* 8. 两列打印:波长 + 对应强度(前 5 点 + 后 5 点) */
std::cout << std::setw(6) << "Index" << std::setw(15) << "Wavelength(nm)"
<< std::setw(11) << "Intensity" << std::endl;
std::cout << std::setw(6) << "-----" << std::setw(15) << "--------------"
<< std::setw(11) << "---------" << std::endl;
for (int i = 0; i < 5; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
if (count > 10)
std::cout << "... (total " << count << " points)" << std::endl;
for (int i = count - 5; i < count; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
/* 9. 指定波段(上位机截取) */
if (start_nm < 400.0 && end_nm > 800.0) {
require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range");
PynectDirectSelectedWavelengthRange sel{};
require(pynect_direct_get_selected_wavelength_range(&sel),
"get_selected_range");
std::cout << "Selected: pixels " << sel.start_pixel << ".."
<< sel.end_pixel << ", count=" << sel.count << std::endl;
}
/* 10. 关闭设备 */
pynect_direct_close_device();
std::cout << "Done." << std::endl;
system("pause");
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
system("pause");
return 1;
}
return 0;
}
7. C# 语言例程
7.1 编译与运行
cd examples/csharp
# 方式一:直接运行(已预编译,.NET Framework 4.x,Windows 自带运行时)
spectrum_demo.exe
# 方式二:自行编译(.NET Framework 自带编译器,64 位)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /platform:x64 spectrum_demo.cs
# 方式三:.NET SDK
dotnet build -c Release
程序运行结束后会提示"按任意键退出...",按任意键关闭窗口(源码中为
WaitExit())。完整源码见交付包
examples/csharp/spectrum_demo.cs(即 7.2 节内容)。示例使用 C# 5 兼容写法,.NET Framework 4.x 与 .NET 6+ 均可编译。
7.2 完整源码(spectrum_demo.cs)
using System;
using System.Runtime.InteropServices;
using System.Text;
class PynectDirectDemo
{
// ---------- 数据结构(与 pynect_direct.h 对应) ----------
[StructLayout(LayoutKind.Sequential)]
struct PynectDirectDeviceInfo
{
public int protocol;
public uint capabilities;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string protocol_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string model;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string serial_number;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string hardware_version;
}
[StructLayout(LayoutKind.Sequential)]
struct PynectDirectSelectedWavelengthRange
{
public double request_start_nm;
public double request_end_nm;
public double actual_start_nm;
public double actual_end_nm;
public ushort start_pixel;
public ushort end_pixel;
public ushort count;
}
// ---------- DllImport(全部为 cdecl 调用约定) ----------
const string DLL = "PynectDirect.dll";
const int SUCCESS = 0;
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_scan_devices();
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_open_device(int index);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern void pynect_direct_close_device();
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_device_info(ref PynectDirectDeviceInfo info);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_wavelength_count(ref ushort count);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_full_wavelength_range(ref double start, ref double end);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_full_wavelength_data(double[] buffer, int max_len);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_full_spectrum_data(ushort[] buffer, int max_len);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_set_integration_time(uint us);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_integration_time(ref uint us);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_set_selected_wavelength_range(double start, double end);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_selected_wavelength_range(
ref PynectDirectSelectedWavelengthRange range);
static void Require(int code, string what)
{
if (code != SUCCESS)
throw new InvalidOperationException(string.Format(
"{0} failed, code={1}", what, code));
}
static void WaitExit()
{
Console.Write("按任意键退出...");
try { Console.ReadKey(); }
catch { /* 非交互环境(输入被重定向)时直接退出 */ }
}
static void Main()
{
try
{
int n = pynect_direct_scan_devices();
if (n <= 0) { Console.WriteLine("No device found."); WaitExit(); return; }
Console.WriteLine(string.Format("Found {0} device(s).", n));
Require(pynect_direct_open_device(0), "open_device");
var info = new PynectDirectDeviceInfo();
Require(pynect_direct_get_device_info(ref info), "get_device_info");
Console.WriteLine(string.Format(
"Model: {0}, Serial: {1}, HW: {2}",
info.model, info.serial_number, info.hardware_version));
ushort count = 0;
Require(pynect_direct_get_wavelength_count(ref count), "get_wavelength_count");
Console.WriteLine(string.Format("Wavelength count: {0}", count));
double start = 0, end = 0;
Require(pynect_direct_get_full_wavelength_range(ref start, ref end),
"get_full_wavelength_range");
Console.WriteLine(string.Format(
"Wavelength range: {0:F2} ~ {1:F2} nm", start, end));
var wavelengths = new double[count];
Require(pynect_direct_get_full_wavelength_data(wavelengths, count),
"get_full_wavelength_data");
uint integration = 0;
Require(pynect_direct_get_integration_time(ref integration), "get_integration_time");
Console.WriteLine(string.Format("Current integration time: {0} us", integration));
pynect_direct_set_integration_time(50000);
var spectrum = new ushort[count];
Require(pynect_direct_get_full_spectrum_data(spectrum, count),
"get_full_spectrum_data");
Console.WriteLine(string.Format(
"{0,-6} {1,13} {2,9}", "Index", "Wavelength(nm)", "Intensity"));
Console.WriteLine(string.Format(
"{0,-6} {1,13} {2,9}", "-----", "--------------", "---------"));
for (int i = 0; i < 5 && i < count; i++)
Console.WriteLine(string.Format(
"{0,-6} {1,13:F3} {2,9}", i, wavelengths[i], spectrum[i]));
if (count > 10)
Console.WriteLine(string.Format("... (total {0} points)", count));
for (int i = (count > 5 ? count - 5 : 0); i < count; i++)
Console.WriteLine(string.Format(
"{0,-6} {1,13:F3} {2,9}", i, wavelengths[i], spectrum[i]));
if (start < 400.0 && end > 800.0)
{
Require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range");
var sel = new PynectDirectSelectedWavelengthRange();
Require(pynect_direct_get_selected_wavelength_range(ref sel),
"get_selected_range");
Console.WriteLine(string.Format(
"Selected: pixels {0}..{1}, count={2}",
sel.start_pixel, sel.end_pixel, sel.count));
}
pynect_direct_close_device();
Console.WriteLine("Done.");
WaitExit();
}
catch (Exception e)
{
Console.WriteLine(string.Format("Error: {0}", e.Message));
WaitExit();
}
}
}
8. Python 语言例程
8.1 环境
- 64 位 Python 3(验证:
python -c "import struct; print(8 * struct.calcsize('P'))",输出64) - 无需安装任何第三方库(只用标准库
ctypes)
完整源码见交付包
examples/python/spectrum_demo.py(即 8.2 节内容)。在examples/python/目录下运行,minimal_test.py与两个 DLL 均在该目录。
8.2 完整源码(spectrum_demo.py)
import ctypes
import os
from pathlib import Path
DLL_DIR = Path(__file__).resolve().parent / "dll" # 修改为您的 dll 目录
def load_sdk():
"""加载 DLL,并把 dll 目录加入依赖搜索路径(ftd2xx.dll 同目录)"""
if os.name == "nt" and hasattr(os, "add_dll_directory"):
os.add_dll_directory(str(DLL_DIR))
return ctypes.CDLL(str(DLL_DIR / "PynectDirect.dll"))
# ---------- 数据结构 ----------
class PynectDirectDeviceInfo(ctypes.Structure):
_fields_ = [
("protocol", ctypes.c_int),
("capabilities", ctypes.c_uint32),
("protocol_name", ctypes.c_char * 32),
("model", ctypes.c_char * 32),
("serial_number", ctypes.c_char * 32),
("hardware_version", ctypes.c_char * 64),
]
class PynectDirectSelectedWavelengthRange(ctypes.Structure):
_fields_ = [
("request_start_nm", ctypes.c_double),
("request_end_nm", ctypes.c_double),
("actual_start_nm", ctypes.c_double),
("actual_end_nm", ctypes.c_double),
("start_pixel", ctypes.c_uint16),
("end_pixel", ctypes.c_uint16),
("count", ctypes.c_uint16),
]
P_U16 = ctypes.POINTER(ctypes.c_uint16)
P_UINT = ctypes.POINTER(ctypes.c_uint)
P_DOUBLE = ctypes.POINTER(ctypes.c_double)
# ---------- 类型绑定(本次例程用到的函数) ----------
def bind_api(sdk):
sdk.pynect_direct_scan_devices.restype = ctypes.c_int
sdk.pynect_direct_open_device.argtypes = [ctypes.c_int]
sdk.pynect_direct_open_device.restype = ctypes.c_int
sdk.pynect_direct_close_device.restype = None
sdk.pynect_direct_get_device_info.argtypes = [ctypes.POINTER(PynectDirectDeviceInfo)]
sdk.pynect_direct_get_device_info.restype = ctypes.c_int
sdk.pynect_direct_get_wavelength_count.argtypes = [P_U16]
sdk.pynect_direct_get_wavelength_count.restype = ctypes.c_int
sdk.pynect_direct_get_full_wavelength_range.argtypes = [P_DOUBLE, P_DOUBLE]
sdk.pynect_direct_get_full_wavelength_range.restype = ctypes.c_int
sdk.pynect_direct_get_full_wavelength_data.argtypes = [P_DOUBLE, ctypes.c_int]
sdk.pynect_direct_get_full_wavelength_data.restype = ctypes.c_int
sdk.pynect_direct_get_full_spectrum_data.argtypes = [P_U16, ctypes.c_int]
sdk.pynect_direct_get_full_spectrum_data.restype = ctypes.c_int
sdk.pynect_direct_get_integration_time.argtypes = [P_UINT]
sdk.pynect_direct_get_integration_time.restype = ctypes.c_int
sdk.pynect_direct_set_integration_time.argtypes = [ctypes.c_uint]
sdk.pynect_direct_set_integration_time.restype = ctypes.c_int
sdk.pynect_direct_set_selected_wavelength_range.argtypes = [ctypes.c_double, ctypes.c_double]
sdk.pynect_direct_set_selected_wavelength_range.restype = ctypes.c_int
sdk.pynect_direct_get_selected_wavelength_range.argtypes = [ctypes.POINTER(PynectDirectSelectedWavelengthRange)]
sdk.pynect_direct_get_selected_wavelength_range.restype = ctypes.c_int
def require(code, what):
if code != 0:
raise RuntimeError(f"{what} failed, return code={code}")
def main():
sdk = load_sdk()
bind_api(sdk)
n = sdk.pynect_direct_scan_devices()
if n <= 0:
print("No device found.")
return
print(f"Found {n} device(s).")
require(sdk.pynect_direct_open_device(0), "open_device")
try:
info = PynectDirectDeviceInfo()
require(sdk.pynect_direct_get_device_info(ctypes.byref(info)), "get_device_info")
print(f"Model: {info.model.decode().strip(chr(0))}")
print(f"Serial: {info.serial_number.decode().strip(chr(0))}")
print(f"Hardware: {info.hardware_version.decode().strip(chr(0))}")
count = ctypes.c_uint16()
require(sdk.pynect_direct_get_wavelength_count(ctypes.byref(count)), "get_wavelength_count")
n = count.value
print(f"Wavelength count: {n}")
start, end = ctypes.c_double(), ctypes.c_double()
require(sdk.pynect_direct_get_full_wavelength_range(
ctypes.byref(start), ctypes.byref(end)), "get_full_wavelength_range")
print(f"Wavelength range: {start.value:.2f} ~ {end.value:.2f} nm")
wl = (ctypes.c_double * n)()
require(sdk.pynect_direct_get_full_wavelength_data(wl, n), "get_full_wavelength_data")
integration = ctypes.c_uint()
require(sdk.pynect_direct_get_integration_time(ctypes.byref(integration)),
"get_integration_time")
print(f"Current integration time: {integration.value} us")
sdk.pynect_direct_set_integration_time(50000)
spec = (ctypes.c_uint16 * n)()
require(sdk.pynect_direct_get_full_spectrum_data(spec, n), "get_full_spectrum_data")
print(f"{'Index':<6}{'Wavelength(nm)':>15}{'Intensity':>10}")
print(f"{'-----':<6}{'--------------':>15}{'---------':>10}")
for i in range(min(5, n)):
print(f"{i:<6}{wl[i]:>15.3f}{spec[i]:>10}")
if n > 10:
print(f"... (total {n} points)")
for i in range(max(0, n - 5), n):
print(f"{i:<6}{wl[i]:>15.3f}{spec[i]:>10}")
if start.value < 400.0 and end.value > 800.0:
require(sdk.pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range")
sel = PynectDirectSelectedWavelengthRange()
require(sdk.pynect_direct_get_selected_wavelength_range(ctypes.byref(sel)),
"get_selected_range")
print(f"Selected: pixels {sel.start_pixel}..{sel.end_pixel}, count={sel.count}")
finally:
sdk.pynect_direct_close_device()
print("Done.")
if __name__ == "__main__":
main()
8.3 运行
python .\spectrum_demo.py
全部 58 个 API 的完整 Python 类型绑定见
examples/python/minimal_test.py的API_SIGNATURES,可直接复用。
9. 完整 API 参考
约定:成功返回
0(有说明的除外);*标记输出参数;字符缓冲区建议 64 字节。
9.1 设备发现、打开与识别
| 函数 | 说明 |
|---|---|
pynect_direct_scan_devices() |
刷新 USB 设备列表,打印每台设备索引、描述和芯片序列号,返回设备数量(不打开设备) |
pynect_direct_get_chip_serial_by_index(index, chip_serial, max_len) |
按扫描索引读取 USB 芯片序列号,用于稳定区分多台设备 |
pynect_direct_open_device(index) |
按索引打开设备并识别协议与能力,设为当前设备;不关闭其他已打开设备(重复打开同设备只切换) |
pynect_direct_open_device_by_chip_serial(chip_serial) |
按芯片序列号打开匹配设备,适合索引可能变化的多设备系统 |
pynect_direct_select_device(device_index) |
切换当前设备(0 起编号,与打开顺序一致),用于多设备场景 |
pynect_direct_get_open_device_count() |
返回当前已打开设备数量 |
pynect_direct_close_device() |
关闭当前选中的设备;重复调用允许 |
pynect_direct_close_all_devices() |
关闭全部已打开设备 |
pynect_direct_is_open() |
返回 1 当前设备打开 / 0 关闭(不与设备通信) |
pynect_direct_set_protocol_preference(protocol) |
设置打开设备时使用的协议偏好(默认 AUTO 自动识别) |
pynect_direct_get_device_protocol(protocol*) |
读取当前设备实际协议类型 |
pynect_direct_get_capabilities(capabilities*) |
读取当前设备 32 位能力位掩码 |
pynect_direct_get_device_info(info*) |
一次读取当前设备协议、能力、型号、序列号、硬件版本 |
9.2 积分时间与平均次数
| 函数 | 说明 |
|---|---|
pynect_direct_set_integration_time(us) |
设置当前会话积分时间(微秒),不影响固化配置 |
pynect_direct_get_integration_time(us*) |
读取当前会话积分时间 |
pynect_direct_set_average_number(count) |
设置平均次数(1-255),次数多则噪声低、耗时增 |
pynect_direct_get_average_number(count*) |
读取平均次数 |
9.3 波长与光谱数据(核心)
| 函数 | 说明 |
|---|---|
pynect_direct_get_wavelength_count(count*) |
读取完整数据点数 N,决定数组容量 |
pynect_direct_get_full_wavelength_range(start_nm*, end_nm*) |
读取完整波长数组的首尾(即所有像素最小/最大波长),按从小到大返回 |
pynect_direct_get_full_wavelength_data(buffer, max_len) |
读取每个像素对应的完整波长数组(double) |
pynect_direct_get_full_spectrum_data(buffer, max_len) |
采集一次完整光谱,返回每个像素强度(uint16) |
pynect_direct_set_selected_wavelength_range(start_nm, end_nm) |
在上位机选择连续像素窗口(不修改设备输出),仅保存在当前会话 |
pynect_direct_get_selected_wavelength_range(out_range*) |
读取选定波段的请求/实际边界、像素索引、点数 |
pynect_direct_get_selected_wavelength_data(buffer, max_len) |
从全量波长中截取选定窗口 |
pynect_direct_get_selected_spectrum_data(buffer, max_len) |
采集完整光谱后截取选定窗口的强度 |
9.4 设备状态与身份信息
| 函数 | 说明 |
|---|---|
pynect_direct_get_calibration_coefficients(coeffs[4]) |
读取 4 个波长标定系数(只读) |
pynect_direct_get_temperature(temp_c*) |
读取设备内部温度(摄氏度) |
pynect_direct_get_hardware_version(version_str, max_len) |
读取硬件版本字符串 |
pynect_direct_get_serial_number(sn, max_len) |
读取光纤光谱仪业务序列号 |
pynect_direct_get_detector_serial_number(sn, max_len) |
读取探测器序列号(UV/VIS) |
pynect_direct_get_device_model(model, max_len) |
读取设备型号 |
pynect_direct_get_usb_status(connected*) |
读取设备内部报告的 USB 连接状态 |
pynect_direct_set_usb_baud_rate(rate) |
写入 USB 通信速率参数(16 位配置码) |
pynect_direct_get_usb_baud_rate(rate*) |
读取 USB 通信速率参数 |
9.5 氙灯、DAC 与电平输出
| 函数 | 说明 |
|---|---|
pynect_direct_set_xenon_pulse(period_10ns, high_10ns) |
设置氙灯脉冲周期与高电平宽度(单位 10ns) |
pynect_direct_get_xenon_pulse(period*, high*) |
读取氙灯脉冲参数 |
pynect_direct_set_xenon_mode(mode) |
设置氙灯模式(XENON_OFF/XENON_CONTINUOUS/XENON_SINGLE) |
pynect_direct_get_xenon_mode(mode*) |
读取氙灯模式 |
pynect_direct_set_dac_voltage(dac_value) |
设置 DAC 12 位原始输出码(非电压值) |
pynect_direct_get_dac_voltage(dac_value*) |
读取 DAC 原始码值 |
pynect_direct_set_level_output(level) |
设置数字电平输出位掩码 |
pynect_direct_get_level_output(level*) |
读取电平输出位掩码 |
9.6 外触发采集
| 函数 | 说明 |
|---|---|
pynect_direct_set_external_trigger_config(enable, trig_type, scan_num) |
设置外触发开关、类型(上升沿 0xAA/电平 0xBB)和计划帧数 |
pynect_direct_get_external_trigger_config(enable*, trig_type*, scan_num*) |
读取外触发配置 |
pynect_direct_get_spectrum_capture_status(status*) |
读取外触发采集 64 位原始状态字 |
pynect_direct_external_spectrum_control(frame_index, data_out, max_len) |
读取指定外触发帧的强度;成功返回写入字节数 |
9.7 Flash 与设备复位(高风险)
| 函数 | 说明 |
|---|---|
pynect_direct_set_flash_write_protect() |
发送设备定义写保护命令(无参数) |
pynect_direct_get_flash_write_protect(enabled*) |
读取写保护状态标记(1/0) |
pynect_direct_reset_device() |
复位设备;会使临时参数和选定波段失效 |
pynect_direct_flash_read(sector, data_out, max_len) |
读取指定扇区原始字节;成功返回实际字节数 |
pynect_direct_flash_write(sector, data, len) |
写入指定扇区;错误数据可能破坏标定,谨慎使用 |
9.8 固化配置与狭缝(非易失)
| 函数 | 说明 |
|---|---|
pynect_direct_set_stored_integration_time(us) |
写入默认积分时间(影响以后上电默认值) |
pynect_direct_set_stored_average_number(count) |
写入默认平均次数 |
pynect_direct_set_stored_smoothing_width(width) |
写入默认平滑宽度 |
pynect_direct_get_stored_integration_time(us*) |
读取固化默认积分时间 |
pynect_direct_get_stored_average_number(count*) |
读取固化默认平均次数 |
pynect_direct_get_stored_smoothing_width(width*) |
读取固化默认平滑宽度 |
pynect_direct_get_slit_width(um*) |
读取入射狭缝宽度信息 |
10. 典型应用流程
10.1 单次采集并显示(最常见)
流程:扫描 → 打开 → 读点数 → 读波长 → 采光谱 → 显示/保存 → 关闭。
Python 最小示例(类型绑定见 8.2 节 bind_api;此处省略):
import ctypes
from minimal_test import find_dll, load_library, bind_api # 复用已验证绑定
sdk = load_library(find_dll())
bind_api(sdk)
require(sdk.pynect_direct_open_device(0), "open") # 1. 打开设备 0
count = ctypes.c_uint16()
require(sdk.pynect_direct_get_wavelength_count(ctypes.byref(count)), "count")
n = count.value # 2. 数据点数 N
wl = (ctypes.c_double * n)()
require(sdk.pynect_direct_get_full_wavelength_data(wl, n), "wavelength")
sdk.pynect_direct_set_integration_time(50000) # 3. 积分时间 50 ms
spec = (ctypes.c_uint16 * n)()
require(sdk.pynect_direct_get_full_spectrum_data(spec, n), "spectrum")
for i in range(0, n, max(1, n // 10)): # 4. 均匀取 10 点显示
print(f"{wl[i]:8.3f} nm {spec[i]}")
print(f"... 共 {n} 点,范围 {wl[0]:.2f} ~ {wl[-1]:.2f} nm")
sdk.pynect_direct_close_device() # 5. 关闭设备
C 语言完整版见第 5.2 节(spectrum_demo.c),仅把其中"读信息/选区"部分去掉即为最小单次采集流程。
10.2 指定波段显示
set_selected_wavelength_range(400, 800)
get_selected_wavelength_range(&sel) // 得到 sel.count
get_selected_wavelength_data(wl_buf, sel.count)
get_selected_spectrum_data(spec_buf, sel.count)
切换波段只是重新调用 set_selected_wavelength_range,设备无感知,速度很快。
10.3 多设备管理(同时打开两台设备)
/* 1. 扫描设备,确认芯片序列号 */
int n = pynect_direct_scan_devices();
for (int i = 0; i < n; i++) {
char chip[64];
if (pynect_direct_get_chip_serial_by_index(i, chip, sizeof(chip)) == 0)
printf("device %d: %s\n", i, chip);
}
/* 2. 分别用芯片序列号打开两台设备(互不影响,均保持打开) */
pynect_direct_open_device_by_chip_serial("FTXXXX1");
pynect_direct_open_device_by_chip_serial("FTXXXX2");
printf("open devices: %d\n", pynect_direct_get_open_device_count()); // 2
/* 3. 切换当前设备并采集(每台设备状态独立) */
pynect_direct_select_device(0); /* 当前:第一台 */
uint16_t count;
pynect_direct_get_wavelength_count(&count);
pynect_direct_get_full_spectrum_data(spec0, count);
pynect_direct_select_device(1); /* 当前:第二台 */
pynect_direct_get_wavelength_count(&count);
pynect_direct_get_full_spectrum_data(spec1, count);
/* 4. 结束:全部关闭或逐个关闭 */
pynect_direct_close_all_devices();
10.4 数据保存
把 wl[](double)与 spec[](uint16)两列写入 CSV 即可(示例见 Python 手册 python_ctypes_example_guide.md)。
11. 常见问题(FAQ)
Q1:DLL 加载失败,提示找不到 PynectDirect.dll?
PynectDirect.dll 和 ftd2xx.dll 必须同目录;检查路径和位数(必须 64 位进程)。
Q2:Python 调用返回奇怪的结果?
忘记设置 argtypes/restype 了。所有 Python 调用必须先绑定类型(见 8.2 节 bind_api)。
Q3:scan_devices 返回 0?
设备未连接、驱动未装、或设备被其他程序占用。插拔设备后重试。
Q4:打开设备返回 -6?
设备被其他进程独占(如另一个采集软件)。关闭其他程序后重试。
Q5:get_selected_* 返回 -1?
尚未调用 set_selected_wavelength_range,或设备复位后窗口失效。先设置选区。
Q6:强度全为 0 或全部饱和?
积分时间过短或过长。调整 set_integration_time;确认光源/氙灯工作正常。
Q7:同一台设备能否两个程序同时访问?
不能。同一台物理设备只能被一个进程打开,第二个进程打开会失败(-6)。但同一个程序内可以同时打开多台不同设备(最多 4 台),用 select_device 切换操作。
Q7b:同时打开多台设备后,参数会互相影响吗?
不会。每台设备有独立的协议、能力、积分时间、平均次数和选定波段状态;切换设备后读写的是该设备自己的状态。
Q8:设置的积分时间关机后还在吗?
不在。set_integration_time 只影响当前会话;要持久保存请使用 set_stored_integration_time(会改变设备默认值,谨慎)。
Q9:32 位程序能调用吗?
不能。SDK 是 64 位 DLL。
Q10:C# 调用时返回乱码或崩溃?
检查 P/Invoke 是否使用 CallingConvention.Cdecl,结构体字段顺序/大小是否与头文件一致,编译目标是否为 x64。
12. 错误码与故障排查
12.1 错误码速查
| 返回码 | 含义 | 处理建议 |
|---|---|---|
| 0 | 成功 | - |
| -1 | 未初始化(选区未设置) | 先调用 set_selected_wavelength_range |
| -2 | 设备未找到/索引越界 | 重新扫描;检查设备连接 |
| -3 | 设备未打开 | 先调用 open_device |
| -4 | 通信失败 | 检查线缆;重新插拔设备 |
| -5 | 参数无效 | 检查索引、范围、数组容量、字符串缓冲区 |
| -6 | 打开失败(被占用) | 关闭其他占用程序 |
| -7 | CRC 校验失败 | 通信干扰,重试;换线缆 |
| -8 | 帧头不匹配 | 协议识别异常;尝试强制指定协议 |
| -9 | 超时 | 检查设备响应;重新上电 |
| -10 | 写保护 | 检查 Flash 写保护状态 |
| -11 | 不支持 | 先查 get_capabilities 确认功能位 |
12.2 排查顺序
- 确认
bin/目录有且仅有PynectDirect.dll+ftd2xx.dll(例程目录内同样各有一份)。 - 确认 Python/程序是 64 位。
- 运行
examples/python/minimal_test.py --read-only,看哪一步 FAIL。 - 确认没有其他软件占用设备。
- 插拔设备 USB 后重试。
附录:交付包结构
PynectDirect_SDK/
|-- README.md
|-- bin/
| |-- PynectDirect.dll # SDK 动态库
| `-- ftd2xx.dll # FTDI 驱动(必需)
|-- include/
| `-- pynect_direct.h # 公开头文件(签名权威来源)
|-- lib/
| |-- libPynectDirect.dll.a # GCC 导入库
| `-- libPynectDirect.a # GCC 静态库
|-- examples/ # 每个例程一个独立目录,依赖文件均已放入
| |-- c/ # C 例程:源码 + include + lib + 两个 DLL
| | |-- spectrum_demo.c
| | |-- include/pynect_direct.h
| | |-- lib/libPynectDirect.dll.a, libPynectDirect.a
| | `-- PynectDirect.dll, ftd2xx.dll
| |-- cpp/ # C++ 例程(结构同 c/)
| |-- csharp/ # C# 例程:spectrum_demo.cs + 两个 DLL
| `-- python/ # Python 例程:全部 py 脚本 + 两个 DLL
| |-- spectrum_demo.py # 单次采集(第 8 章)
| |-- minimal_test.py # 设备冒烟测试
| |-- python_ctypes_example.py # 交互式指定波段示例 + 指南
| `-- dual_spectrometer_test.py # 双光纤光谱仪(UV/VIS + NIR)同时采集测试
`-- docs/
|-- PynectDirect_API_Guide.md
`-- PynectDirect用户使用手册v3.0.1.md # 本文档