sccn / lsl_archived

Multi-modal time-synched data transmission over local network
242 stars 134 forks source link

read multiple streams in python #95

Closed matt-erhart closed 7 years ago

matt-erhart commented 8 years ago

I'd like to bring in multiple synced streams to python (EEG (cogniotics), eye tracking (eyeX from c#), and markers (from psychopy) ) rather than recording with the lab viewer. What's the best way to pull this all into one python matrix?

dmedine commented 8 years ago

If you want synchronize the streams automatically, this is not yet possible in the current Pyhton implementation. This is a feature of the latest commit to liblsl, bu it is still undocumented and largely untested. Once we have validated the effectiveness of this, we will update the python lsl wrapper to facilitate the use of this functionality.

However, in the meantime, you can manually do time correction by periodically estimating the time offset between inlet and oulet using time_correction. See the code for pylsl.py (line 704) for more information.

On 2/29/2016 9:34 AM, Matt Erhart wrote:

I'd like to bring in multiple synced streams to python (EEG (cogniotics), eye tracking (eyeX from c#), and markers (from psychopy) ) rather than recording with the lab viewer. What's the best way to pull this all into one python matrix?

— Reply to this email directly or view it on GitHub https://github.com/sccn/labstreaminglayer/issues/95.

cboulay commented 8 years ago

Getting multiple streams is doable with the syncing technique David mentioned above.

Do they need to be in the same matrix? That's the difficult part because the different devices will have different sampling rates.

Here's how I would do it:

  1. If all your devices can be set to have a n^2 sampling rate (512, 1024, etc) then set them to do so.
  2. Run all the outlets for your devices (in separate processes).
  3. Open up inlets for each stream.
  4. Find the lowest common multiple sampling rate --> Fs_lcm. e.g., 30 Hz and 50 Hz --> 150 Hz.
  5. On each loop iteration
    1. Pull chunks, timestamps from each stream.
    2. Create a time vector spanning the earliest-to-latest timestamps at the Fs_lcm. nSamples = len(matrix)
    3. Create an empty matrix of shape n_chans_all_streams by nSamples
    4. Fill in the matrix with your data from the different streams.
    5. Use your favourite interpolation algorithm to fill in the rest, or leave it empty if you can work with sparse matrices.

Edit: fixed list formatting.

matt-erhart commented 8 years ago

Thanks for the quick response. I suppose they don't need to be in matrix form. A better question might be: how do I read multiple inlets into one process at the same time? 3 different variables that include data from different devices but over the same time period would do just fine, I believe.

cboulay commented 8 years ago

Sure. Here's how I do it in my signal processing GUI.

        # Find the brain signal inlet.
        streams = resolve_byprop("type", "RawBrainSignal")
        print "Found inlet of type RawBrainSignal"
        self.raw_inlet = StreamInlet(streams[0])
        inlet_info = self.raw_inlet.info()
        inlet_Fs = inlet_info.nominal_srate()
        chans_xml = [inlet_info.desc().first_child(), ]
        for ix in range(inlet_info.channel_count()-1):
            chans_xml.append(chans_xml[-1].next_sibling(name="channel"))

        streams = resolve_byprop("type", "TaskState")
        self.task_inlet = StreamInlet(streams[0])
        taskinlet_info = self.task_inlet.info()
        taskinlet_Fs = taskinlet_info.nominal_srate()
        taskinletchans_xml = [taskinlet_info.desc().first_child(), ]
        for ix in range(taskinlet_info.channel_count()-1):
            taskinletchans_xml.append(taskinletchans_xml[-1].next_sibling(name="channel"))

#In the update loop...
chunk, timestamps = self.raw_inlet.pull_chunk(timeout=0.05)
taskinletchunk, taskinlettimestamps = self.task_inlet.pull_chunk(timeout=0.01)
#Do stuff with chunk and taskinletchunk if the respective timstamps exist
cboulay commented 7 years ago

Please feel free to re-open this issue if you're still having trouble.