sccn / labstreaminglayer

LabStreamingLayer super repository comprising submodules for LSL and associated apps.
Other
522 stars 157 forks source link

How to avoid delay in 2 PC? #117

Closed ChadChen68 closed 4 months ago

ChadChen68 commented 4 months ago

Hi All I'm trying to build LSL for EEG in Python (data stream) and MATLAB (Event stream). It's not easy for me to understand LSL documents and other examples.

I already have a device that can receive data and events and then output both in 1 stream from "computer A" (I call it "local data"), and use labrecorder to receive stream in "computer B" (I call it "LSL data"). After loading xdf data to EEGLAB and analysis in MATLAB, "local data" and "LSL data" are alike.

But here is my problem, I tried to build 2 streams, one is a data stream (from "computer A") and an event stream (from "computer C"), but I still don't understand how to use local_time to synchronize 2 streams. I added local_time into my script but still not helpful.

By the way, if I add time stamps should I use push_chunk or keep using push_sample, cause there seems no difference in my data? And do I need StreamInlet cause I only use Streamoutlet to push my stream but receive it and still work.

Sincerely Ming-Chuan

cboulay commented 4 months ago

You cannot use StreamOutlet to receive data. You must be mistaken there.

If you want 2 streams to arrive with their timestamps synchronized to the same base, then you must enable timestamp post-processing on your inlet. https://github.com/labstreaminglayer/pylsl/blob/master/pylsl/pylsl.py#L698-L701

As for whether or not you provide manual timestamps to the push_sample or push_chunk calls, I highly recommend you do not and you let LSL stamp them automatically, unless you know the true offset between the event and when you make the push_ call, in which case the timestamp should be pylsl.local_clock() - offset.

ChadChen68 commented 4 months ago

I'm sorry for being so late in replying

I have read the file you recommended. So I did some practice and tried to synchronize 2 streams on the same PC.

At first, I use "SendData" and "SendStringMarkers" examples to generate an event and signal. And based on the liblsl example "ReceiveData" and "ReceiveStringMarkers".

To me, it looks like they are not synchronized

By the way, after I use outlet to push data and events, I just need to run my inlet script and it should synchronize my 2 streams, right?

Here is my inlet

螢幕擷取畫面 2024-03-01 171617

And here is the result With proc tp_proc Without proc tp_noproc

cboulay commented 4 months ago

The mistake here is that the pull_sample calls are blocking, waiting for data to become available, but they are only pulling one sample at a time. Your EEG stream is accumulating data because you're only pulling a single sample then waiting for a marker, then pulling a single sample of EEG again and waiting for a marker again.

You have a couple options. You can either call pull_sample with timeout=0.0 and skip over the case when nothing is returned, or you can call pull_chunk to get all samples in the buffer and then only look at the most recent timestamp.

Try this instead:

import pylsl

def main():
    # first resolve an EEG stream on the lab network
    print("looking for an EEG stream...")
    eeg_streams = pylsl.resolve_stream('type', 'EEG')
    marker_streams = pylsl.resolve_stream('type', 'Markers')

    # create a new inlet to read from the stream
    eeg_inlet = pylsl.StreamInlet(eeg_streams[0], processing_flags=pylsl.proc_ALL)
    marker_inlet = pylsl.StreamInlet(marker_streams[0], processing_flags=pylsl.proc_ALL)

    while True:
        # Wait until a marker is received...
        marker, marker_ts = marker_inlet.pull_sample()
        # And get whatever eeg has accumulated since the laste marker.
        eeg_chunk, eeg_ts = eeg_inlet.pull_chunk(timeout=0.0)

        # Print output
        print(f"Marker: {marker}\t\tMarkerTS:\t{marker_ts:3f}")
        if len(eeg_ts):
            print(f"EEG most recent ts: \t\t{eeg_ts[-1]:.3f}\tDelta: {eeg_ts[-1] - marker_ts:.3f}")

if __name__ == '__main__':
    main()

I get this output:

Marker: ['Testtest']        MarkerTS:   425804.079366
EEG most recent ts:         425804.082  Delta: 0.003
Marker: ['Marker']      MarkerTS:   425806.760775
EEG most recent ts:         425806.762  Delta: 0.001
Marker: ['Testtest']        MarkerTS:   425808.099470
EEG most recent ts:         425808.092  Delta: -0.007
Marker: ['Testtest']        MarkerTS:   425808.507533
EEG most recent ts:         425808.502  Delta: -0.006
ChadChen68 commented 4 months ago

I'm so grateful for your help @cboulay, before your help I thought I was just like a headless fly.

I changed a little bit because my event is not continuous, so I added timeout=0.0, to avoid my inlet waiting for my event stream even if that time should be no events.

# create a new inlet to read from the stream
eeg_inlet = pylsl.StreamInlet(eeg_streams[0], processing_flags=pylsl.proc_ALL)
marker_inlet = pylsl.StreamInlet(marker_streams[0], processing_flags=pylsl.proc_ALL)

while True:
    # Wait until a marker is received...
    marker, marker_ts = marker_inlet.pull_sample(timeout=0.0)
    # And get whatever eeg has accumulated since the laste marker.
    eeg_chunk, eeg_ts = eeg_inlet.pull_sample(timeout=0.0)

    # Print output
    if marker_ts :
        print(f"Marker: {marker}\t\tMarkerTS:\t{marker_ts:3f}")
   print(f"Marker: {EEG}\t\eeg_ts:\t{eeg_chunk:3f}")

I believe the clocks are synchronized, and I don't know mine offset, so I will keep let timestamp catch automatically. but there's still one thing I can't figure out. The red box is where my event should be, and the yellow box is where my event actually occurred after I load xdf from xdfimport plugin. It was lag 0.0479891017312184 second.

In this video https://www.youtube.com/watch?v=tDDkrmv3ZKE have mention that It might cause lagging because of network, router, load of network, load of PC. I also set both stream sample rate to 250Hz.

Do you have any clue that might cause 0.048 second lagging? I will try to change my 2.4G router to 5G router and see it will get better or not.

image

cboulay commented 4 months ago

Are you using more than 1 computer? Are they connected over wi-fi? If so that will cause poor synchronization. Briefly, the synchronization algorithm relies on the roundtrip time between the receiver and the sender being symmetrical -- same time to send and receive. This breaks down in wi-fi if packets are dropped and have to be re-sent. If synchronization is important then please run all applications on the same computer or use a wired LAN. If you absolutely need to use a wireless setup then there are some settings you can change.

The red box is where my event should be, and the yellow box is where my event actually occurred after I load xdf from xdfimport plugin.

I don't know what this means.

A couple comments about your new code: