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_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}")
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:

  1. Front End Marker Stream Output: Within a front end experimental paradigm adjust the marker_outlettimestamp to be time.time()and not local_clock()when an experiment is designed for the IDUN.
  1. Modify LSL_sources.py: Within LSL_sources.py, and the class LslEegSource(), add a specific instance in the time correction wherein if the IDUN is the EEG source, remove the Unix offset from the timing data.
  1. Remove Unix Offset at the end: When saving the data simply take the difference between the unix epoch and local_clock and clear it from all EEG data timestamps
  1. 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 from time.time()and local_clock()as it leaves the IDUN

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.