Difference between revisions of "DataHub"
(→Python) |
|||
| Line 47: | Line 47: | ||
==Usage== | ==Usage== | ||
=== Python === | === Python === | ||
| − | + | Example demonstrating how to send LSL stream and marker data: | |
<syntaxhighlight lang="python" line> | <syntaxhighlight lang="python" line> | ||
#!/usr/bin/env python3.10 | #!/usr/bin/env python3.10 | ||
| Line 109: | Line 109: | ||
marker_outlet.push_sample(["Stop"]) | marker_outlet.push_sample(["Stop"]) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | the settings.py file for LSL: | ||
| + | <syntaxhighlight lang="python" line> | ||
| + | # settings.py | ||
| + | |||
| + | class BaseSettings: | ||
| + | """Basisklasse met standaardinstellingen voor een LSL stream.""" | ||
| + | def __init__(self): | ||
| + | self.stream_name = "BaseStream" | ||
| + | self.stream_type = "Unknown" | ||
| + | self.channel_count = 1 | ||
| + | self.sample_rate = 0 | ||
| + | self.channel_format = "float32" | ||
| + | self.source_id = "BaseSource" | ||
| + | |||
| + | self.channel_names = [] | ||
| + | self.push_chunk_size = 1 | ||
| + | self.push_interval = 0.001 | ||
| + | |||
| + | self.use_random_data = False | ||
| + | self.dummy_string = "Test" | ||
| + | self.random_string_options = ["A", "B", "C"] | ||
| + | |||
| + | self.verbose = True | ||
| + | self.quit_method = "psychopy" | ||
| + | self.user_stop_message = "Press ENTER/RETURN to stop acquisition." | ||
| + | |||
| + | self.default_buffer = ["Test"] | ||
| + | |||
| + | # the scope is not implemented yet | ||
| + | self.scope = "lan" | ||
| + | |||
| + | def get_stream_options(self): | ||
| + | if self.scope in ["local", "lan", "internet"]: | ||
| + | return dict() | ||
| + | else: | ||
| + | raise ValueError(f"Unknown scope: {self.scope}") | ||
| + | |||
| + | |||
| + | class DynamicMultiChannelStreamSettings(BaseSettings): | ||
| + | def __init__( | ||
| + | self, | ||
| + | n_channels=8, | ||
| + | stream_name="DynamicStream", | ||
| + | stream_type="EEG", | ||
| + | sample_rate=256, | ||
| + | channel_format="float32", | ||
| + | channel_names=None | ||
| + | ): | ||
| + | super().__init__() | ||
| + | self.stream_name = stream_name | ||
| + | self.stream_type = stream_type | ||
| + | self.channel_count = n_channels | ||
| + | self.sample_rate = sample_rate | ||
| + | self.channel_format = channel_format | ||
| + | self.source_id = f"{stream_name}_Source" | ||
| + | |||
| + | if channel_names is not None: | ||
| + | if len(channel_names) != n_channels: | ||
| + | raise ValueError(f"Number of channel names ({len(channel_names)}) does not match n_channels ({n_channels})") | ||
| + | self.channel_names = channel_names | ||
| + | else: | ||
| + | self.channel_names = [f"Chan{i+1}" for i in range(n_channels)] | ||
| + | |||
| + | self.default_buffer = [0.0 for _ in range(n_channels)] | ||
| + | |||
| + | class MarkerStreamSettings(BaseSettings): | ||
| + | def __init__(self): | ||
| + | super().__init__() | ||
| + | self.stream_name = "MarkerStream" | ||
| + | self.stream_type = "Markers" | ||
| + | self.channel_count = 1 | ||
| + | self.sample_rate = 0 # 0 Hz = irregular sampling | ||
| + | self.channel_format = "string" | ||
| + | self.source_id = "MarkerSource" | ||
| + | |||
| + | self.channel_names = [] # niet nodig voor markers | ||
| + | self.default_buffer = ["TestMarker"] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Revision as of 11:18, 28 April 2025
| Developer(s) | Christian Kothe; Chadwick Boulay. |
|---|---|
| Development status | -in development- |
| Written in | C, C++, Python, Java, C#, MATLAB |
| Operating system | Windows, Linux, MacOS, Android, iOS |
| Type | Data collection |
| License | Open source |
| Website | LSL webpage |
Resources
| Downloads | |
|---|---|
| Manuals | |
| Templates | |
The DataHub Makes use of the lab streaming layer. The lab streaming layer (LSL) is a system for the unified collection of measurement time series in research experiments that handles both the networking, time-synchronization, (near-) real-time access as well as optionally the centralized collection, viewing and disk recording of the data.
Installation
Our support for LSL is mainly done in python. Download python here: Please choose a 64 bit version.. Run the installer and make sure to add Python to the file path (it's an option in the installer).
Open a command prompt, start with upgrading the pip installer by typing:
c:>python -m pip install --upgrade pip
Then:
c:>pip install pylsl
more info: cross platform pylsl
Versions
The TSG uses the version 1.15.0. Open a command and type the following to find the version used:
c:>python
>>> import pylsl
>>> print(pylsl.__version__)
Usage
Python
Example demonstrating how to send LSL stream and marker data:
1#!/usr/bin/env python3.10
2# -*- coding: utf-8 -*-
3
4import time
5import numpy as np
6import keyboard
7import random
8from pylsl import StreamInfo, StreamOutlet, local_clock
9from LSLsettings import MarkerStreamSettings, DynamicMultiChannelStreamSettings
10
11print(f"Setting up streams...")
12marker_settings = MarkerStreamSettings()
13
14# info marker StreamInfo, outlet
15marker_info = StreamInfo(
16 name=marker_settings.stream_name,
17 type=marker_settings.stream_type,
18 channel_count=marker_settings.channel_count,
19 nominal_srate=marker_settings.sample_rate,
20 channel_format=marker_settings.channel_format,
21 source_id=marker_settings.source_id
22)
23marker_outlet = StreamOutlet(marker_info, chunk_size=marker_settings.push_chunk_size)
24
25data_settings = DynamicMultiChannelStreamSettings(
26 n_channels=4,
27 stream_name="TestStream",
28 stream_type="TestData",
29 sample_rate=100,
30 channel_names=["A", "B", "C", "D"]
31)
32
33# info data StreamInfo, outlet
34data_info = StreamInfo(
35 name=data_settings.stream_name,
36 type=data_settings.stream_type,
37 channel_count=data_settings.channel_count,
38 nominal_srate=data_settings.sample_rate,
39 channel_format=data_settings.channel_format,
40 source_id=data_settings.source_id
41)
42data_outlet = StreamOutlet(data_info, chunk_size=data_settings.push_chunk_size)
43
44running = True
45marker_outlet.push_sample(["Start"])
46buffer_dtype = object if data_settings.channel_format == "string" else float
47buffer_in = np.array(data_settings.default_buffer, dtype=buffer_dtype)
48
49print("Start streaming... Press 'q' to stop.")
50
51while not(keyboard.is_pressed('q')):
52
53 random_numbers = [random.randint(0, 1) for _ in range(4)]
54 random_numbers = [num + 1 for num in random_numbers]
55 data_outlet.push_sample(random_numbers)
56
57 time.sleep(data_settings.push_interval)
58
59marker_outlet.push_sample(["Stop"])
the settings.py file for LSL:
1# settings.py
2
3class BaseSettings:
4 """Basisklasse met standaardinstellingen voor een LSL stream."""
5 def __init__(self):
6 self.stream_name = "BaseStream"
7 self.stream_type = "Unknown"
8 self.channel_count = 1
9 self.sample_rate = 0
10 self.channel_format = "float32"
11 self.source_id = "BaseSource"
12
13 self.channel_names = []
14 self.push_chunk_size = 1
15 self.push_interval = 0.001
16
17 self.use_random_data = False
18 self.dummy_string = "Test"
19 self.random_string_options = ["A", "B", "C"]
20
21 self.verbose = True
22 self.quit_method = "psychopy"
23 self.user_stop_message = "Press ENTER/RETURN to stop acquisition."
24
25 self.default_buffer = ["Test"]
26
27 # the scope is not implemented yet
28 self.scope = "lan"
29
30 def get_stream_options(self):
31 if self.scope in ["local", "lan", "internet"]:
32 return dict()
33 else:
34 raise ValueError(f"Unknown scope: {self.scope}")
35
36
37class DynamicMultiChannelStreamSettings(BaseSettings):
38 def __init__(
39 self,
40 n_channels=8,
41 stream_name="DynamicStream",
42 stream_type="EEG",
43 sample_rate=256,
44 channel_format="float32",
45 channel_names=None
46 ):
47 super().__init__()
48 self.stream_name = stream_name
49 self.stream_type = stream_type
50 self.channel_count = n_channels
51 self.sample_rate = sample_rate
52 self.channel_format = channel_format
53 self.source_id = f"{stream_name}_Source"
54
55 if channel_names is not None:
56 if len(channel_names) != n_channels:
57 raise ValueError(f"Number of channel names ({len(channel_names)}) does not match n_channels ({n_channels})")
58 self.channel_names = channel_names
59 else:
60 self.channel_names = [f"Chan{i+1}" for i in range(n_channels)]
61
62 self.default_buffer = [0.0 for _ in range(n_channels)]
63
64class MarkerStreamSettings(BaseSettings):
65 def __init__(self):
66 super().__init__()
67 self.stream_name = "MarkerStream"
68 self.stream_type = "Markers"
69 self.channel_count = 1
70 self.sample_rate = 0 # 0 Hz = irregular sampling
71 self.channel_format = "string"
72 self.source_id = "MarkerSource"
73
74 self.channel_names = [] # niet nodig voor markers
75 self.default_buffer = ["TestMarker"]
Matlab
Please, read the instructions on the GitHub labstreaminglayer website (https://github.com/labstreaminglayer/liblsl-Matlab) on how to prepare Matlab to work with LSL. You can either use the latest release for your Matlab version, or if that doesn't workout well, build it from the source files. Make sure to add the liblsl-Matlab folder to your path recursively to make it available to your own scripts.
A short example for sending lsl streaming data:
1%% instantiate the library
2disp('Loading library...');
3lib = lsl_loadlib();
4
5% make a new stream outlet
6disp('Creating a new streaminfo...');
7info = lsl_streaminfo(lib,'BioSemi','EEG',8,100,'cf_float32','sdfwerr32432');
8
9disp('Opening an outlet...');
10outlet = lsl_outlet(info);
11
12% send data into the outlet, sample by sample
13disp('Now transmitting data...');
14while true
15 outlet.push_sample(randn(8,1));
16 pause(0.01);
17end
A short example for receiving lsl streaming data:
1%% instantiate the library
2disp('Loading the library...');
3lib = lsl_loadlib();
4
5% resolve a stream...
6disp('Resolving an EEG stream...');
7result = {};
8while isempty(result)
9 result = lsl_resolve_byprop(lib,'type','EEG'); end
10
11% create a new inlet
12disp('Opening an inlet...');
13inlet = lsl_inlet(result{1});
14
15disp('Now receiving data...');
16while true
17 % get data from the inlet
18 [vec,ts] = inlet.pull_sample();
19 % and display it
20 fprintf('%.2f\t',vec);
21 fprintf('%.5f\n',ts);
22end