qlsdk2 0.3.0a2__tar.gz → 0.4.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.
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/PKG-INFO +2 -2
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/setup.py +4 -10
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/__init__.py +1 -0
- qlsdk2-0.4.0/src/qlsdk/core/__init__.py +5 -0
- qlsdk2-0.4.0/src/qlsdk/core/crc/__init__.py +5 -0
- qlsdk2-0.4.0/src/qlsdk/core/crc/crctools.py +95 -0
- qlsdk2-0.4.0/src/qlsdk/core/device.py +25 -0
- qlsdk2-0.4.0/src/qlsdk/core/entity/__init__.py +92 -0
- qlsdk2-0.4.0/src/qlsdk/core/exception.py +0 -0
- qlsdk2-0.4.0/src/qlsdk/core/filter/__init__.py +1 -0
- qlsdk2-0.4.0/src/qlsdk/core/filter/norch.py +59 -0
- qlsdk2-0.4.0/src/qlsdk/core/local.py +34 -0
- qlsdk2-0.4.0/src/qlsdk/core/message/__init__.py +2 -0
- qlsdk2-0.4.0/src/qlsdk/core/message/command.py +324 -0
- qlsdk2-0.4.0/src/qlsdk/core/message/tcp.py +0 -0
- qlsdk2-0.4.0/src/qlsdk/core/message/udp.py +96 -0
- qlsdk2-0.4.0/src/qlsdk/core/network/__init__.py +34 -0
- qlsdk2-0.4.0/src/qlsdk/core/network/monitor.py +55 -0
- qlsdk2-0.4.0/src/qlsdk/core/utils.py +70 -0
- qlsdk2-0.4.0/src/qlsdk/persist/__init__.py +2 -0
- qlsdk2-0.4.0/src/qlsdk/persist/rsc_edf.py +299 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/__init__.py +7 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/command/__init__.py +214 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/command/message.py +239 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/device_manager.py +119 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/discover.py +87 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/eegion.py +360 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/entity.py +447 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/paradigm.py +313 -0
- qlsdk2-0.4.0/src/qlsdk/rsc/proxy.py +76 -0
- qlsdk2-0.4.0/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
- qlsdk2-0.4.0/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk2.egg-info/PKG-INFO +2 -2
- qlsdk2-0.4.0/src/qlsdk2.egg-info/SOURCES.txt +48 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk2.egg-info/requires.txt +1 -1
- qlsdk2-0.4.0/test/test.py +168 -0
- qlsdk2-0.3.0a2/src/qlsdk/persist/__init__.py +0 -1
- qlsdk2-0.3.0a2/src/qlsdk2.egg-info/SOURCES.txt +0 -19
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/README.md +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/setup.cfg +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/ar4/__init__.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/ar4m/__init__.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/persist/edf.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/sdk/__init__.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/sdk/ar4sdk.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/sdk/hub.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/x8/__init__.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk/x8m/__init__.py +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/src/qlsdk2.egg-info/top_level.txt +0 -0
- {qlsdk2-0.3.0a2 → qlsdk2-0.4.0}/test/test_ar4m.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: qlsdk2
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: SDK for quanlan device
|
|
5
5
|
Home-page: https://github.com/hehuajun/qlsdk
|
|
6
6
|
Author: hehuajun
|
|
@@ -12,7 +12,7 @@ Requires-Python: >=3.9
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
Requires-Dist: loguru>=0.6.0
|
|
14
14
|
Requires-Dist: numpy>=1.23.5
|
|
15
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: bitarray>=1.5.3
|
|
16
16
|
Provides-Extra: dev
|
|
17
17
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
18
18
|
Requires-Dist: twine>=3.0; extra == "dev"
|
|
@@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
|
|
|
6
6
|
|
|
7
7
|
setuptools.setup(
|
|
8
8
|
name="qlsdk2",
|
|
9
|
-
version="0.
|
|
9
|
+
version="0.4.0",
|
|
10
10
|
author="hehuajun",
|
|
11
11
|
author_email="hehuajun@eegion.com",
|
|
12
12
|
description="SDK for quanlan device",
|
|
@@ -24,13 +24,7 @@ setuptools.setup(
|
|
|
24
24
|
install_requires=open("requirements.txt").read().splitlines(),
|
|
25
25
|
include_package_data=True,
|
|
26
26
|
package_data={
|
|
27
|
-
# "
|
|
28
|
-
"qlsdk
|
|
29
|
-
|
|
30
|
-
},
|
|
31
|
-
# entry_points={
|
|
32
|
-
# 'console_scripts': [
|
|
33
|
-
# 'qlsdk-cli=qlsdk.cli:main', # 如果有命令行工具
|
|
34
|
-
# ],
|
|
35
|
-
# },
|
|
27
|
+
# "qlsdk2": ["/**/*.dll"],
|
|
28
|
+
"qlsdk": ["./**/*.dll"]
|
|
29
|
+
}
|
|
36
30
|
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# len=256
|
|
2
|
+
CRC_HI = [
|
|
3
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
|
4
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
|
5
|
+
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
|
6
|
+
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
|
7
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
|
8
|
+
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
|
9
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
|
10
|
+
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
|
11
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
|
12
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
|
13
|
+
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
|
14
|
+
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
|
15
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
|
16
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
|
17
|
+
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
|
18
|
+
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
|
19
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
|
20
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
|
21
|
+
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
|
22
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
|
23
|
+
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
|
24
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
|
25
|
+
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
|
26
|
+
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
|
27
|
+
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
|
28
|
+
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
# len=256
|
|
32
|
+
CRC_LO = [
|
|
33
|
+
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
|
|
34
|
+
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
|
|
35
|
+
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
|
|
36
|
+
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
|
|
37
|
+
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
|
|
38
|
+
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
|
|
39
|
+
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
|
|
40
|
+
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
|
|
41
|
+
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
|
|
42
|
+
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
|
|
43
|
+
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
|
|
44
|
+
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
|
|
45
|
+
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
|
|
46
|
+
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
|
|
47
|
+
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
|
|
48
|
+
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
|
|
49
|
+
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
|
|
50
|
+
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
|
|
51
|
+
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
|
|
52
|
+
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
|
|
53
|
+
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
|
|
54
|
+
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
|
|
55
|
+
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
|
|
56
|
+
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
|
|
57
|
+
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
|
|
58
|
+
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
def crc16(data):
|
|
62
|
+
crcHi = 0xFF
|
|
63
|
+
crcLo = 0xFF
|
|
64
|
+
for i in range(len(data)):
|
|
65
|
+
index = crcHi ^ data[i]
|
|
66
|
+
crcHi = crcLo ^ CRC_HI[index]
|
|
67
|
+
crcLo = CRC_LO[index]
|
|
68
|
+
|
|
69
|
+
return crcHi << 8 | crcLo
|
|
70
|
+
|
|
71
|
+
class CRCEnum(object):
|
|
72
|
+
CRC8 = 8
|
|
73
|
+
CRC16 = 16
|
|
74
|
+
CRC32 = 32
|
|
75
|
+
|
|
76
|
+
class CRC(object):
|
|
77
|
+
def __init__(self, width=CRCEnum.CRC16) :
|
|
78
|
+
self.width = width
|
|
79
|
+
self.crcHi = 0xFF
|
|
80
|
+
self.crcLo = 0xFF
|
|
81
|
+
|
|
82
|
+
def _init(self):
|
|
83
|
+
if self.width == CRCEnum.CRC16:
|
|
84
|
+
self.crcHi = 0xFF
|
|
85
|
+
self.crcLo = 0xFF
|
|
86
|
+
|
|
87
|
+
def calc(self, data):
|
|
88
|
+
for i in range(len(data)):
|
|
89
|
+
index = self.crcHi ^ data[i]
|
|
90
|
+
self.crcHi = self.crcLo ^ CRC_HI[index]
|
|
91
|
+
self.crcLo = CRC_LO[index]
|
|
92
|
+
|
|
93
|
+
def checksum(self):
|
|
94
|
+
return self.crcHi << 8 | self.crcLo
|
|
95
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
class BaseDevice(object):
|
|
4
|
+
def __init__(self, socket = None):
|
|
5
|
+
self.socket = socket
|
|
6
|
+
self.device_name = None
|
|
7
|
+
self.device_type = None
|
|
8
|
+
self.device_id = None
|
|
9
|
+
|
|
10
|
+
@property
|
|
11
|
+
def acq_channels(self) :
|
|
12
|
+
return None
|
|
13
|
+
@property
|
|
14
|
+
def sample_range(self) -> int:
|
|
15
|
+
return None
|
|
16
|
+
@property
|
|
17
|
+
def sample_rate(self) -> int:
|
|
18
|
+
return None
|
|
19
|
+
@property
|
|
20
|
+
def sample_num(self) -> int:
|
|
21
|
+
return 10
|
|
22
|
+
@property
|
|
23
|
+
def resolution(self):
|
|
24
|
+
return 24
|
|
25
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from qlsdk.core.utils import to_channels
|
|
2
|
+
from loguru import logger
|
|
3
|
+
|
|
4
|
+
class DataPacket(object):
|
|
5
|
+
def __init__(self, device_type, device_id, channels, data):
|
|
6
|
+
self.data = data
|
|
7
|
+
self.channels = None
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RscPacket(object):
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.time_stamp = None
|
|
13
|
+
self.pkg_id = None
|
|
14
|
+
self.result = None
|
|
15
|
+
self.channels = None
|
|
16
|
+
self.origin_sample_rate = None
|
|
17
|
+
self.sample_rate = None
|
|
18
|
+
self.sample_num = None
|
|
19
|
+
self.resolution = None
|
|
20
|
+
self.filter = None
|
|
21
|
+
self.data_len = None
|
|
22
|
+
self.trigger = None
|
|
23
|
+
self.eeg = None
|
|
24
|
+
|
|
25
|
+
def transfer(self, body: bytes) -> 'RscPacket':
|
|
26
|
+
self.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
27
|
+
self.result = body[8]
|
|
28
|
+
self.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
29
|
+
self.channels = to_channels(body[13: 45])
|
|
30
|
+
self.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
|
|
31
|
+
self.sample_rate = int.from_bytes(body[49: 53], 'little')
|
|
32
|
+
self.sample_num = int.from_bytes(body[53: 57], 'little')
|
|
33
|
+
self.resolution = int(int(body[57]) / 8)
|
|
34
|
+
self.filter = body[58]
|
|
35
|
+
self.data_len = int.from_bytes(body[59: 63], 'little')
|
|
36
|
+
# 步径 相同通道的点间隔
|
|
37
|
+
step = int(len(self.channels) * self.resolution + 4)
|
|
38
|
+
self.trigger = [int.from_bytes(body[i:i+4], 'little') for i in range(63, len(body) - 3, step)]
|
|
39
|
+
b_eeg = body[63:]
|
|
40
|
+
ch_num = len(self.channels)
|
|
41
|
+
self.eeg = [
|
|
42
|
+
[
|
|
43
|
+
int.from_bytes(b_eeg[i * self.resolution + 4 + j * step:i * self.resolution + 4 + j * step + 3], 'big', signed=True)
|
|
44
|
+
for j in range(self.sample_num)
|
|
45
|
+
]
|
|
46
|
+
for i in range(ch_num)
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# logger.trace(self)
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def __str__(self):
|
|
53
|
+
return f"""
|
|
54
|
+
time_stamp: {self.time_stamp}
|
|
55
|
+
pkg_id: {self.pkg_id}
|
|
56
|
+
origin_sample_rate: {self.origin_sample_rate}
|
|
57
|
+
sample_rate: {self.sample_rate}
|
|
58
|
+
sample_num: {self.sample_num}
|
|
59
|
+
resolution: {self.resolution}
|
|
60
|
+
filter: {self.filter}
|
|
61
|
+
channels: {self.channels}
|
|
62
|
+
data len: {self.data_len}
|
|
63
|
+
trigger: {self.trigger}
|
|
64
|
+
eeg: {self.eeg}
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
class ImpedancePacket(object):
|
|
68
|
+
def __init__(self):
|
|
69
|
+
self.time_stamp = None
|
|
70
|
+
self.pkg_id = None
|
|
71
|
+
self.result = None
|
|
72
|
+
self.channels = None
|
|
73
|
+
self.data_len = None
|
|
74
|
+
self.impedance = None
|
|
75
|
+
|
|
76
|
+
def transfer(self, body:bytes) -> 'ImpedancePacket':
|
|
77
|
+
self.time_stamp = int.from_bytes(body[0:8], 'little')
|
|
78
|
+
self.result = body[8]
|
|
79
|
+
self.pkg_id = int.from_bytes(body[9: 13], 'little')
|
|
80
|
+
self.channels = to_channels(body[13: 45])
|
|
81
|
+
|
|
82
|
+
logger.debug(f"impedance: {self}")
|
|
83
|
+
|
|
84
|
+
def __str__(self):
|
|
85
|
+
return f"""
|
|
86
|
+
time_stamp: {self.time_stamp}
|
|
87
|
+
pkg_id: {self.pkg_id}
|
|
88
|
+
result: {self.result}
|
|
89
|
+
channels: {self.channels}
|
|
90
|
+
data len: {self.data_len}
|
|
91
|
+
impedance: {self.impedance}
|
|
92
|
+
"""
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .norch import notch_filter_50hz
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def notch_filter_50hz(data: np.ndarray,
|
|
4
|
+
fs: float,
|
|
5
|
+
notch_width: float = 2.0,
|
|
6
|
+
max_harmonics: int = 5) -> np.ndarray:
|
|
7
|
+
"""
|
|
8
|
+
多通道50Hz谐波陷波滤波器
|
|
9
|
+
|
|
10
|
+
参数:
|
|
11
|
+
data : 输入信号,形状为 [通道数, 采样点数] 的二维数组
|
|
12
|
+
fs : 采样频率 (Hz)
|
|
13
|
+
notch_width : 陷波带宽 (Hz),默认2Hz
|
|
14
|
+
max_harmonics : 最大谐波次数,默认处理前10次谐波
|
|
15
|
+
|
|
16
|
+
返回:
|
|
17
|
+
滤波后的信号,形状与输入相同
|
|
18
|
+
"""
|
|
19
|
+
# 输入校验
|
|
20
|
+
if data.ndim != 2:
|
|
21
|
+
raise ValueError("输入必须为二维数组 [channels, samples]")
|
|
22
|
+
if fs <= 0:
|
|
23
|
+
raise ValueError("采样频率必须为正数")
|
|
24
|
+
|
|
25
|
+
n_channels, n_samples = data.shape
|
|
26
|
+
nyquist = fs / 2
|
|
27
|
+
processed = np.empty_like(data)
|
|
28
|
+
|
|
29
|
+
# 生成频率轴
|
|
30
|
+
freqs = np.fft.fftfreq(n_samples, 1/fs)
|
|
31
|
+
|
|
32
|
+
for ch in range(n_channels):
|
|
33
|
+
# FFT变换
|
|
34
|
+
fft_data = np.fft.fft(data[ch])
|
|
35
|
+
|
|
36
|
+
# 生成陷波掩模
|
|
37
|
+
mask = np.ones(n_samples, dtype=bool)
|
|
38
|
+
|
|
39
|
+
# 计算需要消除的谐波
|
|
40
|
+
for k in range(1, max_harmonics+1):
|
|
41
|
+
target_freq = 50 * k
|
|
42
|
+
|
|
43
|
+
# 超过奈奎斯特频率则停止
|
|
44
|
+
if target_freq > nyquist:
|
|
45
|
+
break
|
|
46
|
+
|
|
47
|
+
# 生成陷波范围
|
|
48
|
+
notch_range = (np.abs(freqs - target_freq) <= notch_width/2) | \
|
|
49
|
+
(np.abs(freqs + target_freq) <= notch_width/2)
|
|
50
|
+
|
|
51
|
+
mask &= ~notch_range
|
|
52
|
+
|
|
53
|
+
# 应用频域滤波
|
|
54
|
+
filtered_fft = fft_data * mask
|
|
55
|
+
|
|
56
|
+
# 逆变换并取实数部分
|
|
57
|
+
processed[ch] = np.real(np.fft.ifft(filtered_fft))
|
|
58
|
+
|
|
59
|
+
return processed
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import socket
|
|
3
|
+
from time import time
|
|
4
|
+
|
|
5
|
+
# 读取本机全部ip列表
|
|
6
|
+
# return list
|
|
7
|
+
def get_ips():
|
|
8
|
+
return socket.gethostbyname_ex(socket.gethostname())[-1]
|
|
9
|
+
|
|
10
|
+
# 读取本机的ip地址
|
|
11
|
+
# return str
|
|
12
|
+
def get_ip():
|
|
13
|
+
# 优先读取活跃ip地址
|
|
14
|
+
routes = os.popen('route print').readlines()
|
|
15
|
+
for idx, item in enumerate(routes):
|
|
16
|
+
if ' 0.0.0.0 ' in item and len(item.split()) > 2:
|
|
17
|
+
return item.split()[-2]
|
|
18
|
+
|
|
19
|
+
# 取第一个地址
|
|
20
|
+
ips = get_ips()
|
|
21
|
+
if len(ips) > 0 :
|
|
22
|
+
return ips[0]
|
|
23
|
+
|
|
24
|
+
raise ValueError("Ip address not exists.")
|
|
25
|
+
|
|
26
|
+
def get_cache(fname=None):
|
|
27
|
+
if fname is None:
|
|
28
|
+
fname = int(time())
|
|
29
|
+
|
|
30
|
+
cpath = os.path.abspath(os.path.abspath(__file__))
|
|
31
|
+
print(cpath)
|
|
32
|
+
|
|
33
|
+
if __name__ == '__main__':
|
|
34
|
+
get_cache()
|