Connecting the Idun to Bessy
This spike is focused on connecting the IDUN guardian to current Bessy Python Tools. Notably outlining the challenges the IDUN guardian presents and potential solutions.
Connecting to the IDUN
To connect to the IDUN I have found it best to have a separate python script handing connection with the headset independent of other processes (e.g. experiments, classifiers, etc.). Mostly because the headset takes a few seconds to connect and I have found LSL sometimes loses the stream before it comes in because the headset hasn’t connected properly yet. Below are some key considerations for a IDUN Collect Script:
- API Key: to access the IDUN SDK (and package tools), you need an API key. I have found it is best to save the key as a
.txtfile and call it into the script as needed to avoid having it directly in your code
api_path = os.path.join(directory, "API_Key.txt")
with open(api_path, 'r') as file:
my_api_token = file.read().strip() # Strip removes any whitespace or newline characters
print(f"API Key: {my_api_token}")
- Recording Timer: the IDUN requires a recording timer (some int for number of seconds to record for). Once recording, the IDUN will not stop recording until this timer is up. However you can send stop commands which can be read in. My solution has been having a 2hr recording timer and in my experiments, after all stimulus presentation is complete, write
.txtfile saying stop. Then in my IDUN Connection/Collection script I have acheck_stop_signalcommand.
def check_stop_signal():
"""Check if the stop signal file exists."""
return os.path.exists("stop_signal.txt")
[...]
while not check_stop_signal():
await asyncio.sleep(5) # Non-blocking check for stop signal
print("Stop signal received. Stopping tasks...")
await stop_task(recording_task)
Other than that you can find a working version of connecting to the IDUN and streaming the subsequent EEG data here https://github.com/Adam-Luoma/IDUN_Test/blob/master/IDUN_Collect.py
To integrate into Bessy, I think the best solution would be keeping the attached script for connecting to the IDUN to run as a separate script like how any headset needs to be connected with independent of Bessy. I can also refine this script for sharing within Bessy and removing some tasks (e.g. check battery).
IDUN and LSL
The biggest pain regarding the IDUN is connecting with LSL. Sending data via LSL from the IDUN is very straightforward:
info = StreamInfo("IDUN", "EEG", 1, 250, "float32", client.address)
lsl_outlet = StreamOutlet(info, 20, 360)
def lsl_stream_handler(event):
message = event.message
eeg = message["raw_eeg"]
most_recent_ts = eeg[-1]["timestamp"]
data = [sample["ch1"] for sample in eeg]
lsl_outlet.push_chunk(data, most_recent_ts)
client.subscribe_live_insights(
raw_eeg=True,
handler=lsl_stream_handler,
)
However, the pain is in how the IDUN handles timing. When the IDUN sends EEG streams via LSL they reference time compared to the Unix Epoch. On the other hand LSL references time since the computer has booted up. So instead of taking the LSL Timestamp as local_clock()I have to take it as time.time(). This presents problems when synchronizing with other LSL streams.
Here are 4 possible solutions for this issue:
- Front End Marker Stream Output: Within a front end experimental paradigm adjust the
marker_outlettimestamp to betime.time()and notlocal_clock()when an experiment is designed for the IDUN.
- Pro: Easy to implement at the experiment level
- Con: Experiments are headset specific unless further accounted for in the experiment code. All subsequent timing is in Unix Epoch time (may break other classifiers and tools)
- Modify
LSL_sources.py: WithinLSL_sources.py, and the classLslEegSource(), add a specific instance in the time correction wherein if the IDUN is the EEG source, remove the Unix offset from the timing data.
- Pro: Works across Bessy so that LSL data coming in is consistent in timing independent of headset. This would also help with any simultaneous recordings of IDUN + another headset
- Con: Unclear when to calculate the time difference. Unclear if running this calculation as data is streaming in will alter timing accuracy as this process may take time
- Remove Unix Offset at the end: When saving the data simply take the difference between the
unix epochandlocal_clockand clear it from all EEG data timestamps
- Pro: One calculation so limited bandwidth needed
- Con: Unable to work in real time classification
- Modify
IDUN_Collect: Since we are running an independent script to connect to the IDUN and push EEG data through LSL, take off the difference fromtime.time()andlocal_clock()as it leaves the IDUN
- Pro: No need to change current Bessy infrastructure (make the IDUN fit the tool)
- Con: Unclear if running this calculation for every chunk pushed will cause delays and/or timing issues
I currently think option 4 is the best case to limit the changes we need to make to an already functional tool. Instead I can make some changes to the IDUN_Collect script linked here to make it better suited to fit into Bessy.
As of now these are the biggest barriers I see. I have yet to attempt real time classification with the IDUN, so there may be plenty of barriers I have yet to discover. As I continue to encounter them I will keep documenting them here.