Skip to content

Commit cf7a707

Browse files
committed
Add din_lsl_monitor.py script to verify DIN stream.
1 parent 934dba2 commit cf7a707

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

scripts/din_lsl_monitor.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Monitor DIN events from the EGIAmpServer LSL markers stream.
4+
5+
Resolves the DIN stream (type "Markers", name ending in "_DIN") and prints
6+
each event with its timestamp, raw value, and individual bit states.
7+
8+
Usage:
9+
python din_lsl_monitor.py # auto-resolve DIN stream
10+
python din_lsl_monitor.py --bits # show individual DIN bit breakdown
11+
python din_lsl_monitor.py --raw # show only value and timestamp
12+
"""
13+
14+
import argparse
15+
import sys
16+
import pylsl
17+
18+
19+
DIN_BITS = 16
20+
21+
22+
def format_bits(value):
23+
"""Format a 16-bit value as individual DIN line states."""
24+
active = [f"DIN{i+1}" for i in range(DIN_BITS) if value & (1 << i)]
25+
return ", ".join(active) if active else "(none)"
26+
27+
28+
def resolve_din_stream(timeout=10.0):
29+
"""Resolve the DIN markers stream."""
30+
# First show all available streams for diagnostics
31+
print(f"Searching for LSL streams (timeout={timeout}s)...")
32+
all_streams = pylsl.resolve_streams(timeout)
33+
if all_streams:
34+
print(f"All available streams:")
35+
for s in all_streams:
36+
print(f" {s.name()} (type={s.type()}, fmt={s.channel_format()}, "
37+
f"ch={s.channel_count()}, rate={s.nominal_srate()}, "
38+
f"src={s.source_id()})")
39+
print()
40+
else:
41+
print("No LSL streams found at all.\n")
42+
return None
43+
44+
# Look for DIN stream by name
45+
din_streams = [s for s in all_streams if s.name().endswith("_DIN")]
46+
if not din_streams:
47+
print("No '_DIN' stream found.")
48+
return None
49+
if len(din_streams) > 1:
50+
print("Multiple DIN streams found:")
51+
for i, s in enumerate(din_streams):
52+
print(f" [{i}] {s.name()} ({s.source_id()})")
53+
print(f"Using: {din_streams[0].name()}")
54+
return din_streams[0]
55+
56+
57+
def main():
58+
parser = argparse.ArgumentParser(description="Monitor DIN events from EGIAmpServer LSL stream")
59+
parser.add_argument("--bits", action="store_true", help="Show individual DIN bit breakdown")
60+
parser.add_argument("--raw", action="store_true", help="Minimal output: value and timestamp only")
61+
parser.add_argument("--timeout", type=float, default=10.0, help="Stream resolve timeout (seconds)")
62+
args = parser.parse_args()
63+
64+
info = resolve_din_stream(args.timeout)
65+
if info is None:
66+
print("No DIN stream found.", file=sys.stderr)
67+
sys.exit(1)
68+
69+
print(f"Connected to: {info.name()} (source: {info.source_id()})")
70+
print(f" Format: {info.channel_format()}, Channels: {info.channel_count()}")
71+
print()
72+
73+
inlet = pylsl.StreamInlet(info, processing_flags=pylsl.proc_clocksync)
74+
75+
if args.raw:
76+
print("timestamp,value")
77+
else:
78+
print("Waiting for DIN events... (Ctrl+C to stop)\n")
79+
header = f"{'Timestamp':>16s} {'Value':>7s} {'Hex':>6s} {'Binary':>18s}"
80+
if args.bits:
81+
header += f" {'Active Lines'}"
82+
print(header)
83+
print("-" * len(header + " " * 3 + "-" * 30 if args.bits else header))
84+
85+
try:
86+
while True:
87+
sample, timestamp = inlet.pull_sample(timeout=1.0)
88+
if sample is None:
89+
continue
90+
91+
value = int(sample[0])
92+
93+
if args.raw:
94+
print(f"{timestamp:.6f},{value}")
95+
else:
96+
line = f"{timestamp:16.6f} {value:7d} 0x{value:04X} {value:016b}"
97+
if args.bits:
98+
line += f" {format_bits(value)}"
99+
print(line)
100+
101+
except KeyboardInterrupt:
102+
print("\nStopped.")
103+
104+
105+
if __name__ == "__main__":
106+
main()

scripts/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# Requirements for impedance_viewer.py
1+
# Requirements for test/utility scripts
22
pylsl>=1.16.0
33
numpy>=1.20.0
44
matplotlib>=3.5.0
5+
pyserial>=3.5

0 commit comments

Comments
 (0)