qldsdk-lm 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qldsdk_lm-0.1.0/LICENSE +21 -0
- qldsdk_lm-0.1.0/PKG-INFO +163 -0
- qldsdk_lm-0.1.0/README.md +119 -0
- qldsdk_lm-0.1.0/pyproject.toml +124 -0
- qldsdk_lm-0.1.0/setup.cfg +4 -0
- qldsdk_lm-0.1.0/setup.py +24 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/__init__.py +46 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/core/__init__.py +14 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/core/base.py +398 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/core/exceptions.py +17 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/devices/__init__.py +5 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/devices/ar4.py +23 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/devices/sdk_device.py +170 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/devices/x8.py +14 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/persist/__init__.py +4 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/persist/edf_handler.py +150 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/sdk/__init__.py +23 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/sdk/ar4_sdk.py +913 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/sdk/libs/Ar4SDK.h +347 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/sdk/libs/liblibAr4SDK.dll +0 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/sdk/libs/libwinpthread-1.dll +0 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/utils/__init__.py +10 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/utils/converter.py +52 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm/utils/logger.py +98 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm.egg-info/PKG-INFO +163 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm.egg-info/SOURCES.txt +27 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm.egg-info/dependency_links.txt +1 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm.egg-info/requires.txt +27 -0
- qldsdk_lm-0.1.0/src/qldsdk_lm.egg-info/top_level.txt +1 -0
qldsdk_lm-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 QLSDK Project Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
qldsdk_lm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qldsdk-lm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for X8/X8M/AR4/AR4M portable EEG devices
|
|
5
|
+
Author: QLSDK Project Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: eeg,biosignal,neuroscience,ar4,x8
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Requires-Python: <3.13,>=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: numpy>=1.21.0
|
|
22
|
+
Requires-Dist: loguru>=0.6.0
|
|
23
|
+
Requires-Dist: pyedflib==0.1.42
|
|
24
|
+
Provides-Extra: analysis
|
|
25
|
+
Requires-Dist: scipy>=1.7.0; extra == "analysis"
|
|
26
|
+
Requires-Dist: pandas>=1.3.0; extra == "analysis"
|
|
27
|
+
Provides-Extra: visualization
|
|
28
|
+
Requires-Dist: matplotlib>=3.4.0; extra == "visualization"
|
|
29
|
+
Provides-Extra: gui
|
|
30
|
+
Requires-Dist: PySide6>=6.0.0; extra == "gui"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-timeout>=2.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
37
|
+
Requires-Dist: flake8>=4.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: mypy>=0.950; extra == "dev"
|
|
39
|
+
Requires-Dist: sphinx>=4.5.0; extra == "dev"
|
|
40
|
+
Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "dev"
|
|
41
|
+
Provides-Extra: all
|
|
42
|
+
Requires-Dist: qldsdk-lm[analysis,gui,visualization]; extra == "all"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# qldsdk-lm
|
|
46
|
+
|
|
47
|
+
Python SDK for X8/X8M/AR4/AR4M portable EEG devices(Windows x64,Python 3.10–3.12)。
|
|
48
|
+
|
|
49
|
+
通过 ctypes 封装厂商 `liblibAr4SDK.dll`,提供设备发现、连接、采集、数据订阅、数字/物理值换算和 EDF+ 存储。
|
|
50
|
+
|
|
51
|
+
| 设备 | 说明 | 真机验证 |
|
|
52
|
+
|------|------|----------|
|
|
53
|
+
| AR4 / AR4M | 4 通道 EEG + 三轴加速度计,单设备 / Hub | 是 |
|
|
54
|
+
| X8 / X8M | 8 通道 EEG,单设备 / Hub | 否 |
|
|
55
|
+
|
|
56
|
+
## 安装
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install qldsdk-lm # 依赖 numpy、loguru、pyedflib==0.1.42
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
包内自带 x64 的 `liblibAr4SDK.dll`,只发布 `win_amd64` wheel。pyedflib 0.1.42 只提供 cp310–cp312 的 Windows wheel,因此不支持 Python 3.13。
|
|
63
|
+
|
|
64
|
+
从源码开发:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
py -3.12 -m venv venv
|
|
68
|
+
venv\Scripts\activate
|
|
69
|
+
pip install -e .
|
|
70
|
+
pip install -r requirements-dev.txt # 测试依赖
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 使用
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import time
|
|
77
|
+
from queue import Empty
|
|
78
|
+
from qldsdk_lm import AR4M, configure_logger
|
|
79
|
+
|
|
80
|
+
configure_logger(log_dir="./logs", level="INFO")
|
|
81
|
+
|
|
82
|
+
hub = AR4M()
|
|
83
|
+
try:
|
|
84
|
+
hub.search() # 后台每 2 秒枚举一次,新设备自动连接
|
|
85
|
+
deadline = time.time() + 15
|
|
86
|
+
while not hub.devices and time.time() < deadline:
|
|
87
|
+
time.sleep(0.5)
|
|
88
|
+
|
|
89
|
+
mac, dev = next(iter(hub.devices.items()))
|
|
90
|
+
print(dev.box_name, dev.serial_number, dev.box_mac, dev.sample_frequency, "Hz")
|
|
91
|
+
|
|
92
|
+
dev.set_storage_path("./data") # EDF+:ar4_<序列号>_<时间>.edf
|
|
93
|
+
topic, q = dev.subscribe("physical") # eeg 单位 µV,acc 单位 mG
|
|
94
|
+
dev.start_acquisition()
|
|
95
|
+
try:
|
|
96
|
+
end = time.time() + 10
|
|
97
|
+
while time.time() < end:
|
|
98
|
+
try:
|
|
99
|
+
pkt = q.get(timeout=1) # pkt.eeg[ch][sample]
|
|
100
|
+
except Empty:
|
|
101
|
+
continue
|
|
102
|
+
finally:
|
|
103
|
+
dev.stop_acquisition()
|
|
104
|
+
dev.unsubscribe(topic)
|
|
105
|
+
finally:
|
|
106
|
+
hub.close() # 必须:释放 SDK 连接
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
已知 MAC 时可直接 `AR4("0x43df64e8335b3b9b")`(构造即连接),用完调用 `disconnect()`。
|
|
110
|
+
|
|
111
|
+
## API 概要
|
|
112
|
+
|
|
113
|
+
设备 `AR4` / `X8`(基类 `LMDevice`):
|
|
114
|
+
|
|
115
|
+
- 标识:`box_name` 设备名称,`serial_number` 设备序列号(SDK box_info 的 `boxId`),`box_mac` MAC(`0x` + 16 位小写十六进制),`device_tag` 日志/文件名标记(序列号,未知时为 MAC),`head_mac` 记录子 MAC
|
|
116
|
+
- 参数:`sample_frequency`、`channel_count`、`eeg_phy_min` / `eeg_phy_max`、`connected`、`recording`
|
|
117
|
+
- 方法:`start_acquisition()`、`stop_acquisition()`、`disconnect()`、`subscribe("digital" | "physical")`、`unsubscribe(topic)`、`eeg2phy()`、`acc2phy()`、`set_storage_path(path, enable=True)`
|
|
118
|
+
- `battery_level` 尚未实现,恒为 0
|
|
119
|
+
|
|
120
|
+
Hub `AR4M` / `X8M`:`search()`、`stop_search()`、`close()`、`devices`、`add_device(mac) -> bool`、`remove_device(mac) -> bool`
|
|
121
|
+
|
|
122
|
+
数据包 `LMPacket`:`time_stamp`(ms)、`pkg_id`、`notify_id`、`eeg[ch][sample]`、`acc[axis][sample]`(无加速度计时为 None)。digital 订阅为 int 数字量,physical 订阅为 float 物理量。
|
|
123
|
+
|
|
124
|
+
异常(均继承 `LMDeviceException`):`DeviceConnectionError`、`AcquisitionError`、`DLLLoadError`。
|
|
125
|
+
|
|
126
|
+
## EDF 存储
|
|
127
|
+
|
|
128
|
+
- pyedflib 0.1.42 写 EDF+,每条记录 1 秒
|
|
129
|
+
- 采样率取设备实际采样率,仅支持 500 / 1000 Hz;其它采样率在 `start_acquisition()` 时抛 `AcquisitionError`
|
|
130
|
+
- 写入设备原始数字量,头部使用设备的数字/物理量程,读取端还原为 µV
|
|
131
|
+
- 头部 equipment 字段为 `<设备类型>_<序列号>`,recording additional 字段为 `MAC_<mac>`
|
|
132
|
+
- 丢包时由 SDK 补 0,停止采集时末尾不足 1 秒的部分补物理 0
|
|
133
|
+
|
|
134
|
+
## 日志
|
|
135
|
+
|
|
136
|
+
设备相关日志以 `[序列号]` 开头,并在 `record["extra"]["device"]` 中携带序列号,可按设备过滤:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from loguru import logger
|
|
140
|
+
logger.add("dev_210125010255.log", filter=lambda r: r["extra"].get("device") == "210125010255")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 测试
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
venv\Scripts\python -m pytest tests/unit # 不需要设备
|
|
147
|
+
venv\Scripts\python -m pytest tests/integration -m integration # 需要 AR4
|
|
148
|
+
venv\Scripts\python -m pytest tests/system -m system # 需要 AR4,约 18 分钟
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 注意:SDK 独占串口和 UDP 54366
|
|
152
|
+
|
|
153
|
+
同一时刻只能有一个进程加载 SDK。若上一个进程没有退出(或另一个程序正在采集),新进程打开串口会失败(Windows 错误 5)、绑定 UDP 54366 会失败,表现为设备枚举恒为 0。遇到时先确认没有其它进程占用:
|
|
154
|
+
|
|
155
|
+
```powershell
|
|
156
|
+
Get-NetUDPEndpoint -LocalPort 54366 | Select OwningProcess
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
卡在串口 I/O 里的进程无法用 taskkill 结束,需要拔插 USB 接收器。
|
|
160
|
+
|
|
161
|
+
## 许可证
|
|
162
|
+
|
|
163
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# qldsdk-lm
|
|
2
|
+
|
|
3
|
+
Python SDK for X8/X8M/AR4/AR4M portable EEG devices(Windows x64,Python 3.10–3.12)。
|
|
4
|
+
|
|
5
|
+
通过 ctypes 封装厂商 `liblibAr4SDK.dll`,提供设备发现、连接、采集、数据订阅、数字/物理值换算和 EDF+ 存储。
|
|
6
|
+
|
|
7
|
+
| 设备 | 说明 | 真机验证 |
|
|
8
|
+
|------|------|----------|
|
|
9
|
+
| AR4 / AR4M | 4 通道 EEG + 三轴加速度计,单设备 / Hub | 是 |
|
|
10
|
+
| X8 / X8M | 8 通道 EEG,单设备 / Hub | 否 |
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install qldsdk-lm # 依赖 numpy、loguru、pyedflib==0.1.42
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
包内自带 x64 的 `liblibAr4SDK.dll`,只发布 `win_amd64` wheel。pyedflib 0.1.42 只提供 cp310–cp312 的 Windows wheel,因此不支持 Python 3.13。
|
|
19
|
+
|
|
20
|
+
从源码开发:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
py -3.12 -m venv venv
|
|
24
|
+
venv\Scripts\activate
|
|
25
|
+
pip install -e .
|
|
26
|
+
pip install -r requirements-dev.txt # 测试依赖
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 使用
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import time
|
|
33
|
+
from queue import Empty
|
|
34
|
+
from qldsdk_lm import AR4M, configure_logger
|
|
35
|
+
|
|
36
|
+
configure_logger(log_dir="./logs", level="INFO")
|
|
37
|
+
|
|
38
|
+
hub = AR4M()
|
|
39
|
+
try:
|
|
40
|
+
hub.search() # 后台每 2 秒枚举一次,新设备自动连接
|
|
41
|
+
deadline = time.time() + 15
|
|
42
|
+
while not hub.devices and time.time() < deadline:
|
|
43
|
+
time.sleep(0.5)
|
|
44
|
+
|
|
45
|
+
mac, dev = next(iter(hub.devices.items()))
|
|
46
|
+
print(dev.box_name, dev.serial_number, dev.box_mac, dev.sample_frequency, "Hz")
|
|
47
|
+
|
|
48
|
+
dev.set_storage_path("./data") # EDF+:ar4_<序列号>_<时间>.edf
|
|
49
|
+
topic, q = dev.subscribe("physical") # eeg 单位 µV,acc 单位 mG
|
|
50
|
+
dev.start_acquisition()
|
|
51
|
+
try:
|
|
52
|
+
end = time.time() + 10
|
|
53
|
+
while time.time() < end:
|
|
54
|
+
try:
|
|
55
|
+
pkt = q.get(timeout=1) # pkt.eeg[ch][sample]
|
|
56
|
+
except Empty:
|
|
57
|
+
continue
|
|
58
|
+
finally:
|
|
59
|
+
dev.stop_acquisition()
|
|
60
|
+
dev.unsubscribe(topic)
|
|
61
|
+
finally:
|
|
62
|
+
hub.close() # 必须:释放 SDK 连接
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
已知 MAC 时可直接 `AR4("0x43df64e8335b3b9b")`(构造即连接),用完调用 `disconnect()`。
|
|
66
|
+
|
|
67
|
+
## API 概要
|
|
68
|
+
|
|
69
|
+
设备 `AR4` / `X8`(基类 `LMDevice`):
|
|
70
|
+
|
|
71
|
+
- 标识:`box_name` 设备名称,`serial_number` 设备序列号(SDK box_info 的 `boxId`),`box_mac` MAC(`0x` + 16 位小写十六进制),`device_tag` 日志/文件名标记(序列号,未知时为 MAC),`head_mac` 记录子 MAC
|
|
72
|
+
- 参数:`sample_frequency`、`channel_count`、`eeg_phy_min` / `eeg_phy_max`、`connected`、`recording`
|
|
73
|
+
- 方法:`start_acquisition()`、`stop_acquisition()`、`disconnect()`、`subscribe("digital" | "physical")`、`unsubscribe(topic)`、`eeg2phy()`、`acc2phy()`、`set_storage_path(path, enable=True)`
|
|
74
|
+
- `battery_level` 尚未实现,恒为 0
|
|
75
|
+
|
|
76
|
+
Hub `AR4M` / `X8M`:`search()`、`stop_search()`、`close()`、`devices`、`add_device(mac) -> bool`、`remove_device(mac) -> bool`
|
|
77
|
+
|
|
78
|
+
数据包 `LMPacket`:`time_stamp`(ms)、`pkg_id`、`notify_id`、`eeg[ch][sample]`、`acc[axis][sample]`(无加速度计时为 None)。digital 订阅为 int 数字量,physical 订阅为 float 物理量。
|
|
79
|
+
|
|
80
|
+
异常(均继承 `LMDeviceException`):`DeviceConnectionError`、`AcquisitionError`、`DLLLoadError`。
|
|
81
|
+
|
|
82
|
+
## EDF 存储
|
|
83
|
+
|
|
84
|
+
- pyedflib 0.1.42 写 EDF+,每条记录 1 秒
|
|
85
|
+
- 采样率取设备实际采样率,仅支持 500 / 1000 Hz;其它采样率在 `start_acquisition()` 时抛 `AcquisitionError`
|
|
86
|
+
- 写入设备原始数字量,头部使用设备的数字/物理量程,读取端还原为 µV
|
|
87
|
+
- 头部 equipment 字段为 `<设备类型>_<序列号>`,recording additional 字段为 `MAC_<mac>`
|
|
88
|
+
- 丢包时由 SDK 补 0,停止采集时末尾不足 1 秒的部分补物理 0
|
|
89
|
+
|
|
90
|
+
## 日志
|
|
91
|
+
|
|
92
|
+
设备相关日志以 `[序列号]` 开头,并在 `record["extra"]["device"]` 中携带序列号,可按设备过滤:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from loguru import logger
|
|
96
|
+
logger.add("dev_210125010255.log", filter=lambda r: r["extra"].get("device") == "210125010255")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 测试
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
venv\Scripts\python -m pytest tests/unit # 不需要设备
|
|
103
|
+
venv\Scripts\python -m pytest tests/integration -m integration # 需要 AR4
|
|
104
|
+
venv\Scripts\python -m pytest tests/system -m system # 需要 AR4,约 18 分钟
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 注意:SDK 独占串口和 UDP 54366
|
|
108
|
+
|
|
109
|
+
同一时刻只能有一个进程加载 SDK。若上一个进程没有退出(或另一个程序正在采集),新进程打开串口会失败(Windows 错误 5)、绑定 UDP 54366 会失败,表现为设备枚举恒为 0。遇到时先确认没有其它进程占用:
|
|
110
|
+
|
|
111
|
+
```powershell
|
|
112
|
+
Get-NetUDPEndpoint -LocalPort 54366 | Select OwningProcess
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
卡在串口 I/O 里的进程无法用 taskkill 结束,需要拔插 USB 接收器。
|
|
116
|
+
|
|
117
|
+
## 许可证
|
|
118
|
+
|
|
119
|
+
MIT
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qldsdk-lm"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for X8/X8M/AR4/AR4M portable EEG devices"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
# pyedflib 0.1.42 仅提供 cp310–cp312 的 Windows wheel
|
|
11
|
+
requires-python = ">=3.10,<3.13"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
authors = [
|
|
15
|
+
{name = "QLSDK Project Team"}
|
|
16
|
+
]
|
|
17
|
+
keywords = ["eeg", "biosignal", "neuroscience", "ar4", "x8"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Intended Audience :: Science/Research",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Scientific/Engineering",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Operating System :: Microsoft :: Windows",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
dependencies = [
|
|
32
|
+
"numpy>=1.21.0",
|
|
33
|
+
"loguru>=0.6.0",
|
|
34
|
+
"pyedflib==0.1.42",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
analysis = [
|
|
39
|
+
"scipy>=1.7.0",
|
|
40
|
+
"pandas>=1.3.0",
|
|
41
|
+
]
|
|
42
|
+
visualization = [
|
|
43
|
+
"matplotlib>=3.4.0",
|
|
44
|
+
]
|
|
45
|
+
gui = [
|
|
46
|
+
"PySide6>=6.0.0",
|
|
47
|
+
]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=7.0.0",
|
|
50
|
+
"pytest-cov>=3.0.0",
|
|
51
|
+
"pytest-timeout>=2.1.0",
|
|
52
|
+
"black>=22.0.0",
|
|
53
|
+
"isort>=5.10.0",
|
|
54
|
+
"flake8>=4.0.0",
|
|
55
|
+
"mypy>=0.950",
|
|
56
|
+
"sphinx>=4.5.0",
|
|
57
|
+
"sphinx-rtd-theme>=1.0.0",
|
|
58
|
+
]
|
|
59
|
+
all = [
|
|
60
|
+
"qldsdk-lm[analysis,visualization,gui]",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.setuptools]
|
|
64
|
+
package-dir = {"" = "src"}
|
|
65
|
+
|
|
66
|
+
[tool.setuptools.packages.find]
|
|
67
|
+
where = ["src"]
|
|
68
|
+
include = ["qldsdk_lm*"]
|
|
69
|
+
|
|
70
|
+
[tool.setuptools.package-data]
|
|
71
|
+
qldsdk_lm = ["sdk/libs/*.dll", "sdk/libs/*.h"]
|
|
72
|
+
|
|
73
|
+
[tool.black]
|
|
74
|
+
line-length = 100
|
|
75
|
+
target-version = ['py310', 'py311', 'py312']
|
|
76
|
+
include = '\.pyi?$'
|
|
77
|
+
|
|
78
|
+
[tool.isort]
|
|
79
|
+
profile = "black"
|
|
80
|
+
line_length = 100
|
|
81
|
+
multi_line_output = 3
|
|
82
|
+
|
|
83
|
+
[tool.mypy]
|
|
84
|
+
python_version = "3.10"
|
|
85
|
+
warn_return_any = true
|
|
86
|
+
warn_unused_configs = true
|
|
87
|
+
disallow_untyped_defs = false
|
|
88
|
+
disallow_incomplete_defs = false
|
|
89
|
+
check_untyped_defs = true
|
|
90
|
+
no_implicit_optional = true
|
|
91
|
+
|
|
92
|
+
[tool.pytest.ini_options]
|
|
93
|
+
minversion = "7.0"
|
|
94
|
+
testpaths = ["tests"]
|
|
95
|
+
python_files = ["test_*.py"]
|
|
96
|
+
python_classes = ["Test*"]
|
|
97
|
+
python_functions = ["test_*"]
|
|
98
|
+
addopts = [
|
|
99
|
+
"-v",
|
|
100
|
+
"--strict-markers",
|
|
101
|
+
"--tb=short",
|
|
102
|
+
]
|
|
103
|
+
markers = [
|
|
104
|
+
"unit: Unit tests",
|
|
105
|
+
"integration: Integration tests",
|
|
106
|
+
"hardware: Tests requiring physical hardware",
|
|
107
|
+
"slow: Slow running tests",
|
|
108
|
+
]
|
|
109
|
+
timeout = 300
|
|
110
|
+
|
|
111
|
+
[tool.coverage.run]
|
|
112
|
+
source = ["src"]
|
|
113
|
+
omit = ["*/tests/*", "*/examples/*"]
|
|
114
|
+
|
|
115
|
+
[tool.coverage.report]
|
|
116
|
+
exclude_lines = [
|
|
117
|
+
"pragma: no cover",
|
|
118
|
+
"def __repr__",
|
|
119
|
+
"raise AssertionError",
|
|
120
|
+
"raise NotImplementedError",
|
|
121
|
+
"if __name__ == .__main__.:",
|
|
122
|
+
"if TYPE_CHECKING:",
|
|
123
|
+
"@abstractmethod",
|
|
124
|
+
]
|
qldsdk_lm-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""qldsdk-lm 构建脚本:元数据在 pyproject.toml,这里只修正 wheel 平台标签。
|
|
2
|
+
|
|
3
|
+
包内自带 x64 的 liblibAr4SDK.dll,wheel 不能标成 py3-none-any,
|
|
4
|
+
否则 Linux / macOS / 32 位 Python 也会装上一个无法加载的包。
|
|
5
|
+
纯 Python 代码不依赖解释器 ABI,因此标签为 py3-none-win_amd64。
|
|
6
|
+
"""
|
|
7
|
+
from setuptools import setup
|
|
8
|
+
|
|
9
|
+
try: # setuptools >= 70.1 自带 bdist_wheel;新版 wheel 已移除该模块
|
|
10
|
+
from setuptools.command.bdist_wheel import bdist_wheel
|
|
11
|
+
except ImportError:
|
|
12
|
+
from wheel.bdist_wheel import bdist_wheel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PlatformWheel(bdist_wheel):
|
|
16
|
+
def finalize_options(self):
|
|
17
|
+
super().finalize_options()
|
|
18
|
+
self.root_is_pure = False
|
|
19
|
+
|
|
20
|
+
def get_tag(self):
|
|
21
|
+
return "py3", "none", "win_amd64"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
setup(cmdclass={"bdist_wheel": PlatformWheel})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""QLSDK-LM: Python SDK for X8/X8M/AR4/AR4M Portable EEG Devices
|
|
2
|
+
|
|
3
|
+
Examples:
|
|
4
|
+
>>> from qldsdk_lm import AR4M
|
|
5
|
+
>>> hub = AR4M()
|
|
6
|
+
>>> hub.search()
|
|
7
|
+
>>> ...
|
|
8
|
+
>>> for mac, device in hub.devices.items():
|
|
9
|
+
... print(device.box_name, device.serial_number, mac)
|
|
10
|
+
>>> hub.close()
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
from .core import (
|
|
16
|
+
AcquisitionError,
|
|
17
|
+
DeviceConnectionError,
|
|
18
|
+
DLLLoadError,
|
|
19
|
+
Hub,
|
|
20
|
+
LMDevice,
|
|
21
|
+
LMDeviceException,
|
|
22
|
+
LMPacket,
|
|
23
|
+
ValueType,
|
|
24
|
+
)
|
|
25
|
+
from .devices import AR4, AR4M, X8, X8M
|
|
26
|
+
from .utils import configure_logger, device_logger, digital_to_physical, physical_to_digital
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"__version__",
|
|
30
|
+
"Hub",
|
|
31
|
+
"LMDevice",
|
|
32
|
+
"LMPacket",
|
|
33
|
+
"ValueType",
|
|
34
|
+
"AR4",
|
|
35
|
+
"AR4M",
|
|
36
|
+
"X8",
|
|
37
|
+
"X8M",
|
|
38
|
+
"LMDeviceException",
|
|
39
|
+
"DeviceConnectionError",
|
|
40
|
+
"AcquisitionError",
|
|
41
|
+
"DLLLoadError",
|
|
42
|
+
"configure_logger",
|
|
43
|
+
"device_logger",
|
|
44
|
+
"digital_to_physical",
|
|
45
|
+
"physical_to_digital",
|
|
46
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""核心模块:基类与异常"""
|
|
2
|
+
from .base import Hub, LMDevice, LMPacket, ValueType
|
|
3
|
+
from .exceptions import AcquisitionError, DeviceConnectionError, DLLLoadError, LMDeviceException
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"Hub",
|
|
7
|
+
"LMDevice",
|
|
8
|
+
"LMPacket",
|
|
9
|
+
"ValueType",
|
|
10
|
+
"LMDeviceException",
|
|
11
|
+
"DeviceConnectionError",
|
|
12
|
+
"AcquisitionError",
|
|
13
|
+
"DLLLoadError",
|
|
14
|
+
]
|