Reading markers derived from LSL in EEG data from ANT Neuro eego amplifiers in the .CNT format

Event markers that have been sent via LSL to the eego recording software (as opposed to an analog TTL trigger sent to the amplifier) may not be exported to the .EVT file created by eego. If you’re experiencing this, there’s an easy fix: delete the .EVT file! The markers are correctly encoded in the .CNT file. Without the .EVT, the libeep library (for which mne.io.read_raw_ant() is a wrapper) looks in the .CNT.

Could you share on GitHub - mscheltienne/antio: Python package to handle I/O with the CNT format from ANT Neuro. a set of files with/without .evt and a minimum loading script with mne.io.read_raw_ant (uses antio under the hood) which demonstrates this? It kind of sounds like a bug that could be fixed by having antio (and the underlying libeep) load both source of events and merge them.

Mathieu

Of course! Would you like me to create a new “issue” for this?

1 Like

Thanks a lot. For x-ref: Reading ANT Neuro .CNT files that contain LSL derived markers: antio vs. MNE, with and without an EVT file · Issue #92 · mscheltienne/antio · GitHub

You bet. I tentatively agree with the merging idea, but everything should be present in the .CNT. ANT is considering getting rid of the EVT file entirely.

While we’re here, it’d be wonderful if mne.io.read_raw_ant() could extract impedance values. The antio version can do it, and I’ve been using this kludgy approach:

# Get the impedances using the older version of antio

cnt = read_cnt(file_path)
onsets,_,descriptions,impedances,_ = read_triggers(cnt)

# Place them in the mne.Info object as a DataFrame

raw.info['description'] = pd.DataFrame(
    impedances, # 2xN array where N is the number of channels. Impedances collected at the beginning and end of a recording
    index=pd.Index( # Timestamps for the impedance measures
        [onset/raw.info['sfreq'] for i,onset in enumerate(onsets) if descriptions[i]=='impedance'], # divide the sample number by the sample frequency to get the time in seconds
        name='time',
    ),
    columns=pd.Index( # Channel names
        raw.ch_names,
        name='channel',
    ),
).stack().rename('impedance').reset_index().to_json(orient='index') # Rearrange and convert to json string

# These can be extracted with:
#     df = pd.read_json(StringIO(raw.info['description']), orient='index').set_index(['time','channel'])