plasma-app 2.2.2__tar.gz → 2.2.4__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.
- {plasma_app-2.2.2 → plasma_app-2.2.4}/PKG-INFO +28 -3
- {plasma_app-2.2.2 → plasma_app-2.2.4}/README.md +22 -2
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/__init__.py +1 -1
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/config.py +28 -6
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/device.py +32 -1
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/extract/pipeline.py +5 -5
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/extractor.py +26 -2
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma_app.egg-info/PKG-INFO +28 -3
- {plasma_app-2.2.2 → plasma_app-2.2.4}/pyproject.toml +7 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/LICENSE +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/__main__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/__pyinstaller/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/__pyinstaller/hook-plasma.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/api.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/app_context.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/build_info.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/config.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/ctl.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/desktop_shortcut.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/bitalino.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/ble_scan.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/bleak_probe.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/bleak_reconnect_test.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/detect.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/extract/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/extract/__main__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/extract/clocksync.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/extract/options.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/formats/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/gyro_bias.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/nus_sim.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/nus_stream.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/_common.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/clocksync.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/control.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/downloader.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/imu.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/sqc.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/tools.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/uuid_tools.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/panels/viewer.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/quaternion.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/records.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense/signal_quality.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense_demo/__init__.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense_demo/config.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense_demo/device.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense_demo/fake_stream.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/msense_demo/faults.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/obs.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/pupil_labs.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/qb2.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/shimmer.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/devices/template.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/integrated_panel.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/journal.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/lsl_recorder.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/lsl_session.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/lsl_util.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/plot_util.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/plugins.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/resources/icons/plasma.icns +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/resources/icons/plasma.ico +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/resources/icons/plasma.png +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/resources/icons/plasma.svg +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/status.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma/xdf_writer.py +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma_app.egg-info/SOURCES.txt +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma_app.egg-info/dependency_links.txt +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma_app.egg-info/entry_points.txt +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma_app.egg-info/requires.txt +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/plasma_app.egg-info/top_level.txt +0 -0
- {plasma_app-2.2.2 → plasma_app-2.2.4}/setup.cfg +0 -0
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plasma-app
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: PLASMA — Platform for LSL-based Acquisition of Sensor Metrics and Analytics
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/YuyiChang/PLASMA
|
|
7
7
|
Project-URL: Repository, https://github.com/YuyiChang/PLASMA
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
8
13
|
Requires-Python: >=3.10
|
|
9
14
|
Description-Content-Type: text/markdown
|
|
10
15
|
License-File: LICENSE
|
|
@@ -54,10 +59,30 @@ PLASMA: Platform for LSL-based Acquisition of Sensor Metrics and Analytics
|
|
|
54
59
|
configure, pair MSense wristbands, run a session, read the memo panel, recover
|
|
55
60
|
from faults.
|
|
56
61
|
|
|
62
|
+
[](https://pypi.org/project/plasma-app/)
|
|
63
|
+
[](https://pypi.org/project/plasma-app/)
|
|
64
|
+
[](LICENSE)
|
|
65
|
+
[](https://github.com/yuyichang/PLASMA/releases/latest)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Download
|
|
69
|
+
|
|
70
|
+
| Platform | Architecture | Download |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| macOS | Apple Silicon (arm64) | [Download `.dmg`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_MacOS_arm64.dmg) |
|
|
73
|
+
| Windows | x64 | [Download Installer `.exe`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Windows_x64_Setup.exe) |
|
|
74
|
+
| Linux | x64 | [Download `.tar.gz`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Linux_x64.tar.gz) |
|
|
75
|
+
| Linux | ARM64 | [Download `.tar.gz`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Linux_arm64.tar.gz) |
|
|
76
|
+
|
|
77
|
+
> For other packages and previous versions, see [GitHub Releases](https://github.com/YuyiChang/PLASMA/releases).
|
|
78
|
+
|
|
79
|
+
|
|
57
80
|
## quickstart
|
|
58
81
|
|
|
59
82
|
**macOS: `brew install --cask yuyichang/plasma/plasma`** is the recommended
|
|
60
|
-
way to get PLASMA — no Python, no `liblsl`, no Gatekeeper dance.
|
|
83
|
+
way to get PLASMA — no Python, no `liblsl`, no Gatekeeper dance. **Windows:
|
|
84
|
+
`scoop bucket add plasma https://github.com/YuyiChang/scoop-plasma` then
|
|
85
|
+
`scoop install plasma`** does the same, without the SmartScreen warning. See
|
|
61
86
|
[Install & first launch](https://yuyichang.github.io/PLASMA/playbook/install/)
|
|
62
87
|
for that and every other install path (prebuilt binaries, PyPI, from source).
|
|
63
88
|
|
|
@@ -101,7 +126,7 @@ plugins: `msense`, `qb2`, `pupil`, `shimmer`, `obs` (and `all`).
|
|
|
101
126
|
- [x] linux aarch64 build — `PLASMA_Linux_arm64` for Jetson Orin (JetPack 6); CI job `build-linux-arm64`
|
|
102
127
|
- [ ] infrequent stream (e.g., PLASMA journaler) shows green after recording stop
|
|
103
128
|
- [ ] doc for msense workflow with better screenshots
|
|
104
|
-
- [
|
|
129
|
+
- [x] memo `stage` transition logic optimization (e.g., timing of "ready to start" msg wrt sensor init status)
|
|
105
130
|
|
|
106
131
|
## developing a sensor plugin
|
|
107
132
|
|
|
@@ -6,10 +6,30 @@ PLASMA: Platform for LSL-based Acquisition of Sensor Metrics and Analytics
|
|
|
6
6
|
configure, pair MSense wristbands, run a session, read the memo panel, recover
|
|
7
7
|
from faults.
|
|
8
8
|
|
|
9
|
+
[](https://pypi.org/project/plasma-app/)
|
|
10
|
+
[](https://pypi.org/project/plasma-app/)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://github.com/yuyichang/PLASMA/releases/latest)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Download
|
|
16
|
+
|
|
17
|
+
| Platform | Architecture | Download |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| macOS | Apple Silicon (arm64) | [Download `.dmg`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_MacOS_arm64.dmg) |
|
|
20
|
+
| Windows | x64 | [Download Installer `.exe`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Windows_x64_Setup.exe) |
|
|
21
|
+
| Linux | x64 | [Download `.tar.gz`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Linux_x64.tar.gz) |
|
|
22
|
+
| Linux | ARM64 | [Download `.tar.gz`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Linux_arm64.tar.gz) |
|
|
23
|
+
|
|
24
|
+
> For other packages and previous versions, see [GitHub Releases](https://github.com/YuyiChang/PLASMA/releases).
|
|
25
|
+
|
|
26
|
+
|
|
9
27
|
## quickstart
|
|
10
28
|
|
|
11
29
|
**macOS: `brew install --cask yuyichang/plasma/plasma`** is the recommended
|
|
12
|
-
way to get PLASMA — no Python, no `liblsl`, no Gatekeeper dance.
|
|
30
|
+
way to get PLASMA — no Python, no `liblsl`, no Gatekeeper dance. **Windows:
|
|
31
|
+
`scoop bucket add plasma https://github.com/YuyiChang/scoop-plasma` then
|
|
32
|
+
`scoop install plasma`** does the same, without the SmartScreen warning. See
|
|
13
33
|
[Install & first launch](https://yuyichang.github.io/PLASMA/playbook/install/)
|
|
14
34
|
for that and every other install path (prebuilt binaries, PyPI, from source).
|
|
15
35
|
|
|
@@ -53,7 +73,7 @@ plugins: `msense`, `qb2`, `pupil`, `shimmer`, `obs` (and `all`).
|
|
|
53
73
|
- [x] linux aarch64 build — `PLASMA_Linux_arm64` for Jetson Orin (JetPack 6); CI job `build-linux-arm64`
|
|
54
74
|
- [ ] infrequent stream (e.g., PLASMA journaler) shows green after recording stop
|
|
55
75
|
- [ ] doc for msense workflow with better screenshots
|
|
56
|
-
- [
|
|
76
|
+
- [x] memo `stage` transition logic optimization (e.g., timing of "ready to start" msg wrt sensor init status)
|
|
57
77
|
|
|
58
78
|
## developing a sensor plugin
|
|
59
79
|
|
|
@@ -46,7 +46,12 @@ def merge_msense_records(existing, scanned, overwrite):
|
|
|
46
46
|
|
|
47
47
|
``overwrite=True`` → one fresh record per scanned device, nothing kept.
|
|
48
48
|
``overwrite=False`` → ``existing`` plus a record for every scanned address
|
|
49
|
-
not already listed (case-insensitive on "UUID / MAC Address").
|
|
49
|
+
not already listed (case-insensitive on "UUID / MAC Address"). A scanned
|
|
50
|
+
address that IS already listed has that record's Name refreshed to the
|
|
51
|
+
freshly advertised name instead — Nickname/Enabled/IMU Stream are left
|
|
52
|
+
alone — so a firmware update that changes the advertised name reaches the
|
|
53
|
+
table via Append without needing Overwrite (which would also reset those
|
|
54
|
+
per-device toggles for every other configured wristband).
|
|
50
55
|
|
|
51
56
|
Fresh records use the advertised name as Name, blank Nickname, Enabled on,
|
|
52
57
|
IMU Stream off. Returns ``(records, summary_str)``.
|
|
@@ -64,13 +69,30 @@ def merge_msense_records(existing, scanned, overwrite):
|
|
|
64
69
|
records = [_fresh(dev) for dev in scanned]
|
|
65
70
|
return records, f"Overwrote table with {len(records)} scanned wristband(s)"
|
|
66
71
|
|
|
72
|
+
scanned_by_addr = {dev["address"].strip().upper(): dev for dev in scanned}
|
|
73
|
+
records = []
|
|
74
|
+
renamed = 0
|
|
75
|
+
for rec in existing:
|
|
76
|
+
dev = scanned_by_addr.get(str(rec.get("UUID / MAC Address", "")).strip().upper())
|
|
77
|
+
if dev and dev["name"] != rec.get("Name", ""):
|
|
78
|
+
rec = {**rec, "Name": dev["name"]}
|
|
79
|
+
renamed += 1
|
|
80
|
+
records.append(rec)
|
|
81
|
+
|
|
67
82
|
have = {str(rec.get("UUID / MAC Address", "")).strip().upper() for rec in existing}
|
|
68
83
|
added = [_fresh(dev) for dev in scanned if dev["address"].strip().upper() not in have]
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
records += added
|
|
85
|
+
|
|
86
|
+
unchanged = len(scanned) - len(added) - renamed
|
|
87
|
+
parts = []
|
|
88
|
+
if added:
|
|
89
|
+
parts.append(f"{len(added)} new")
|
|
90
|
+
if renamed:
|
|
91
|
+
parts.append(f"{renamed} renamed")
|
|
92
|
+
if unchanged:
|
|
93
|
+
parts.append(f"{unchanged} unchanged")
|
|
94
|
+
msg = "Appended " + ", ".join(parts) if parts else "No changes"
|
|
95
|
+
return records, msg
|
|
74
96
|
|
|
75
97
|
|
|
76
98
|
# ── device_info.json import (YAMS provisioning format) ───────────────────────
|
|
@@ -74,6 +74,10 @@ BATTERY_CHAR_UUID = "00002a19-0000-1000-8000-00805f9b34fb"
|
|
|
74
74
|
|
|
75
75
|
SQC_MIN_MTU = 128
|
|
76
76
|
SQC_DEBUG = True # emit per-notification telemetry (printed off the BLE thread)
|
|
77
|
+
# devices with no active SQC/live session still get unsolicited NUS traffic
|
|
78
|
+
# (boot chatter, or firmware that exposes the NUS char but doesn't speak the
|
|
79
|
+
# SQC/live protocol) — coalesce that noise instead of one debug line/packet
|
|
80
|
+
NUS_IDLE_LOG_WINDOW_S = 5.0
|
|
77
81
|
|
|
78
82
|
# NOTE: the overall per-transaction backstop timeout stays DISABLED — the real
|
|
79
83
|
# BLE data rate is far slower than the handoff doc's provisional 35/45 s
|
|
@@ -173,6 +177,11 @@ class MotionSenseHRV(PlasmaDevice):
|
|
|
173
177
|
# addr -> live INFINITY-stream state (see _new_live_state); only ever
|
|
174
178
|
# one NUS stream (SQC snapshot OR live) active per wristband at a time.
|
|
175
179
|
self.live_state = {}
|
|
180
|
+
# addr -> {"count", "bytes", "window_start"} — coalesces the
|
|
181
|
+
# "ignored (no active stream)" debug line so a chatty/idle wristband
|
|
182
|
+
# doesn't flood the console one line per notification (see
|
|
183
|
+
# _log_nus_ignored / NUS_IDLE_LOG_WINDOW_S)
|
|
184
|
+
self._nus_idle_rx = {}
|
|
176
185
|
# addr -> {"status": confirmed|unconfirmed|unverifiable|unknown,
|
|
177
186
|
# "checked_at": ts} — set by collection_ctl(addr, False), see
|
|
178
187
|
# _confirm_acq_stopped. Cleared on the next collection_ctl(addr, True).
|
|
@@ -1325,7 +1334,7 @@ class MotionSenseHRV(PlasmaDevice):
|
|
|
1325
1334
|
elif live and live.get("session") and not live["session"].is_terminal:
|
|
1326
1335
|
self._handle_live_notification(live, name, bytes(data), now, t_entry)
|
|
1327
1336
|
else:
|
|
1328
|
-
self.
|
|
1337
|
+
self._log_nus_ignored(name, len(data))
|
|
1329
1338
|
except Exception as e:
|
|
1330
1339
|
self.info(f"Error handling NUS data from {name}: {e}")
|
|
1331
1340
|
|
|
@@ -1520,6 +1529,28 @@ class MotionSenseHRV(PlasmaDevice):
|
|
|
1520
1529
|
except queue.Full:
|
|
1521
1530
|
pass # never block the BLE thread on a slow console
|
|
1522
1531
|
|
|
1532
|
+
def _log_nus_ignored(self, name, nbytes):
|
|
1533
|
+
# first notification in a window prints immediately (so isolated,
|
|
1534
|
+
# one-off chatter is still visible); anything after that within
|
|
1535
|
+
# NUS_IDLE_LOG_WINDOW_S is tallied silently and flushed as one
|
|
1536
|
+
# rolled-up line — keeps a continuously-chattering/no-session
|
|
1537
|
+
# wristband (e.g. firmware that exposes NUS but doesn't speak the
|
|
1538
|
+
# SQC/live protocol) from spamming a line per packet
|
|
1539
|
+
st = self._nus_idle_rx.setdefault(name, {"count": 0, "bytes": 0, "window_start": 0.0})
|
|
1540
|
+
now = time.time()
|
|
1541
|
+
st["count"] += 1
|
|
1542
|
+
st["bytes"] += nbytes
|
|
1543
|
+
if now - st["window_start"] < NUS_IDLE_LOG_WINDOW_S:
|
|
1544
|
+
return
|
|
1545
|
+
if st["count"] > 1:
|
|
1546
|
+
self._sqc_debug(
|
|
1547
|
+
name, f"rx {st['count']}x ({st['bytes']}B total) ignored (no active stream)")
|
|
1548
|
+
else:
|
|
1549
|
+
self._sqc_debug(name, f"rx {nbytes}B ignored (no active stream)")
|
|
1550
|
+
st["count"] = 0
|
|
1551
|
+
st["bytes"] = 0
|
|
1552
|
+
st["window_start"] = now
|
|
1553
|
+
|
|
1523
1554
|
def _sqc_diag_summary(self, name, store=None):
|
|
1524
1555
|
d = (store or self.sqc_state).get(name, {}).get("diag")
|
|
1525
1556
|
if not d:
|
|
@@ -316,19 +316,19 @@ class DataExtractor():
|
|
|
316
316
|
out_dir=self.out_dir, id=id)
|
|
317
317
|
|
|
318
318
|
def generate_csv_for_pattern(self, in_dir, type_prefix: str, search_key: str, out_dir="./", id=-1):
|
|
319
|
-
#
|
|
320
|
-
|
|
321
|
-
|
|
319
|
+
# An empty ID means the binary starts with its sensor tag (e.g.
|
|
320
|
+
# ppg1047.bin), so there is no participant encoding to expand.
|
|
321
|
+
if self.ignore_id_parsing or id == "":
|
|
322
|
+
file_name = type_prefix
|
|
322
323
|
else:
|
|
323
324
|
if str(id) in self.encoding_alias.keys():
|
|
324
325
|
alias = self.encoding_alias[str(id)]
|
|
325
326
|
print('=====', id, alias)
|
|
326
|
-
file_name = f"{type_prefix}".replace(id, alias)
|
|
327
327
|
else:
|
|
328
328
|
sub_id = str(id)[:-2]
|
|
329
329
|
ses_id = str(id)[-2:]
|
|
330
330
|
alias = f"sub-{sub_id}_ses-{ses_id}_{self.note}_"
|
|
331
|
-
|
|
331
|
+
file_name = alias + type_prefix[len(str(id)):]
|
|
332
332
|
|
|
333
333
|
print(type_prefix, search_key, '********')
|
|
334
334
|
# session_dfs: one DataFrame per distinct recording/session sharing this
|
|
@@ -15,6 +15,27 @@ from ..extract.options import (
|
|
|
15
15
|
from ..extract.pipeline import extract_dir, extract_zip, get_session_encoding
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
_SAVE_FORMATS = ("feather", "csv", "pickle")
|
|
19
|
+
_PREF_CONFIG_KEY = "msense_extraction"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _saved_save_format():
|
|
23
|
+
from plasma.config import device_config
|
|
24
|
+
blob = device_config.get_plugin_config(_PREF_CONFIG_KEY)
|
|
25
|
+
saved = blob.get("save_format") if isinstance(blob, dict) else None
|
|
26
|
+
return saved if saved in _SAVE_FORMATS else ExtractionOptions().save_format
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _save_save_format(value):
|
|
30
|
+
from plasma.config import device_config
|
|
31
|
+
if value not in _SAVE_FORMATS:
|
|
32
|
+
raise ValueError(f"Unsupported extraction save format: {value}")
|
|
33
|
+
existing = device_config.get_plugin_config(_PREF_CONFIG_KEY)
|
|
34
|
+
blob = dict(existing) if isinstance(existing, dict) else {}
|
|
35
|
+
blob["save_format"] = value
|
|
36
|
+
device_config.update_plugin_config(_PREF_CONFIG_KEY, blob)
|
|
37
|
+
|
|
38
|
+
|
|
18
39
|
class ExtractionOptionsPanel:
|
|
19
40
|
"""The advanced-options accordion. Build one inside each Blocks context.
|
|
20
41
|
|
|
@@ -32,8 +53,11 @@ class ExtractionOptionsPanel:
|
|
|
32
53
|
with gr.Row():
|
|
33
54
|
self.legacy_fs = gr.Checkbox(False, label="(Uncommon) legacy sampling rate")
|
|
34
55
|
self.save_format = gr.Radio(
|
|
35
|
-
|
|
36
|
-
info="feather (fast, binary) · csv (plain text, needed for clocksync) ·
|
|
56
|
+
_SAVE_FORMATS, value=_saved_save_format(), label="Save format",
|
|
57
|
+
info="feather (fast, binary) · csv (plain text, needed for clocksync) · "
|
|
58
|
+
"pickle · selection saved for future launches")
|
|
59
|
+
self.save_format.change(_save_save_format, inputs=self.save_format,
|
|
60
|
+
outputs=None)
|
|
37
61
|
self.ignore_id_parsing = gr.Checkbox(False, label="Ignore subject/session ID parsing")
|
|
38
62
|
with gr.Row():
|
|
39
63
|
self.ppg_format = gr.Dropdown(PPG_FORMAT_CHOICES, value="auto",
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plasma-app
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: PLASMA — Platform for LSL-based Acquisition of Sensor Metrics and Analytics
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/YuyiChang/PLASMA
|
|
7
7
|
Project-URL: Repository, https://github.com/YuyiChang/PLASMA
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
8
13
|
Requires-Python: >=3.10
|
|
9
14
|
Description-Content-Type: text/markdown
|
|
10
15
|
License-File: LICENSE
|
|
@@ -54,10 +59,30 @@ PLASMA: Platform for LSL-based Acquisition of Sensor Metrics and Analytics
|
|
|
54
59
|
configure, pair MSense wristbands, run a session, read the memo panel, recover
|
|
55
60
|
from faults.
|
|
56
61
|
|
|
62
|
+
[](https://pypi.org/project/plasma-app/)
|
|
63
|
+
[](https://pypi.org/project/plasma-app/)
|
|
64
|
+
[](LICENSE)
|
|
65
|
+
[](https://github.com/yuyichang/PLASMA/releases/latest)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Download
|
|
69
|
+
|
|
70
|
+
| Platform | Architecture | Download |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| macOS | Apple Silicon (arm64) | [Download `.dmg`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_MacOS_arm64.dmg) |
|
|
73
|
+
| Windows | x64 | [Download Installer `.exe`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Windows_x64_Setup.exe) |
|
|
74
|
+
| Linux | x64 | [Download `.tar.gz`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Linux_x64.tar.gz) |
|
|
75
|
+
| Linux | ARM64 | [Download `.tar.gz`](https://github.com/YuyiChang/PLASMA/releases/latest/download/PLASMA_Linux_arm64.tar.gz) |
|
|
76
|
+
|
|
77
|
+
> For other packages and previous versions, see [GitHub Releases](https://github.com/YuyiChang/PLASMA/releases).
|
|
78
|
+
|
|
79
|
+
|
|
57
80
|
## quickstart
|
|
58
81
|
|
|
59
82
|
**macOS: `brew install --cask yuyichang/plasma/plasma`** is the recommended
|
|
60
|
-
way to get PLASMA — no Python, no `liblsl`, no Gatekeeper dance.
|
|
83
|
+
way to get PLASMA — no Python, no `liblsl`, no Gatekeeper dance. **Windows:
|
|
84
|
+
`scoop bucket add plasma https://github.com/YuyiChang/scoop-plasma` then
|
|
85
|
+
`scoop install plasma`** does the same, without the SmartScreen warning. See
|
|
61
86
|
[Install & first launch](https://yuyichang.github.io/PLASMA/playbook/install/)
|
|
62
87
|
for that and every other install path (prebuilt binaries, PyPI, from source).
|
|
63
88
|
|
|
@@ -101,7 +126,7 @@ plugins: `msense`, `qb2`, `pupil`, `shimmer`, `obs` (and `all`).
|
|
|
101
126
|
- [x] linux aarch64 build — `PLASMA_Linux_arm64` for Jetson Orin (JetPack 6); CI job `build-linux-arm64`
|
|
102
127
|
- [ ] infrequent stream (e.g., PLASMA journaler) shows green after recording stop
|
|
103
128
|
- [ ] doc for msense workflow with better screenshots
|
|
104
|
-
- [
|
|
129
|
+
- [x] memo `stage` transition logic optimization (e.g., timing of "ready to start" msg wrt sensor init status)
|
|
105
130
|
|
|
106
131
|
## developing a sensor plugin
|
|
107
132
|
|
|
@@ -15,6 +15,13 @@ dependencies = [
|
|
|
15
15
|
"pylsl",
|
|
16
16
|
"numpy",
|
|
17
17
|
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
]
|
|
18
25
|
|
|
19
26
|
[project.urls]
|
|
20
27
|
Homepage = "https://github.com/YuyiChang/PLASMA"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|