示例#1
0
def xdf2fif(filename, interactive=False, outdir=None):
    """
    Convert XDF format
    """
    from pyxdf import pyxdf
    pyxdf.logger.setLevel('WARNING')

    fdir, fname, fext = qc.parse_path_list(filename)
    if outdir is None:
        outdir = fdir
    elif outdir[-1] != '/':
        outdir += '/'

    fiffile = outdir + fname + '.fif'

    # channel x times
    data = pyxdf.load_xdf(filename)
    raw_data = data[0][0]['time_series'].T
    if np.max(raw_data[:-1]) < 1:
        logger.info('Assuming the signal unit is volate (V). Converting to uV')
        raw_data[:-1] *= 10**6

    signals = np.concatenate((raw_data[-1, :].reshape(1,
                                                      -1), raw_data[:-1, :]))

    sample_rate = int(data[0][0]['info']['nominal_srate'][0])
    # TODO: check the event channel index and move to the 0-th index
    # in LSL, usually the name is TRIG or STI 014.
    ch_names = []
    for ch in data[0][0]['info']['desc'][0]['channels'][0]['channel']:
        ch_names.append(ch['label'][0])
    trig_ch_guess = pu.find_event_channel(signals, ch_names)
    if trig_ch_guess is None:
        trig_ch_guess = 0
    ch_names = ['TRIGGER'
                ] + ch_names[:trig_ch_guess] + ch_names[trig_ch_guess + 1:]
    ch_info = ['stim'] + ['eeg'] * (len(ch_names) - 1)

    # fif header creation
    info = mne.create_info(ch_names, sample_rate, ch_info)
    raw = mne.io.RawArray(signals, info)
    #raw.add_events(events_index, stim_channel='TRIGGER')

    # save and close
    raw.save(fiffile, verbose=False, overwrite=True, fmt='double')
    logger.info('Saved to %s' % fiffile)

    saveChannels2txt(outdir, ch_names)
示例#2
0
def xdf2fif(filename, interactive=False, outdir=None):
    """
    Convert XDF format
    """
    from xdf import xdf
    
    fdir, fname, fext = qc.parse_path_list(filename)
    if outdir is None:
        outdir = fdir
    elif outdir[-1] != '/':
        outdir += '/'

    fiffile = outdir + fname + '.fif'

    # channel x times
    data = xdf.load_xdf(filename)
    raw_data = data[0][0]['time_series'].T
    signals = np.concatenate((raw_data[-1, :].reshape(1, -1), raw_data[:-1, :]))

    sample_rate = int(data[0][0]['info']['nominal_srate'][0])
    # TODO: check the event channel index and move to the 0-th index
    # in LSL, usually the name is TRIG or STI 014.
    ch_names = []
    for ch in data[0][0]['info']['desc'][0]['channels'][0]['channel']:
        ch_names.append(ch['label'][0])
    trig_ch_guess = pu.find_event_channel(signals, ch_names)
    if trig_ch_guess is None:
        trig_ch_guess = 0
    ch_names =[ch_names[trig_ch_guess]] + ch_names[:trig_ch_guess] + ch_names[trig_ch_guess+1:]
    ch_info = ['stim'] + ['eeg'] * (len(ch_names)-1)

    # fif header creation
    info = mne.create_info(ch_names, sample_rate, ch_info)
    raw = mne.io.RawArray(signals, info)

    # save and close
    raw.save(fiffile, verbose=False, overwrite=True, fmt='double')
    print('Saved to', fiffile)
示例#3
0
def pcl2fif(filename,
            interactive=False,
            outdir=None,
            external_event=None,
            offset=0,
            overwrite=False,
            precision='single'):
    """
    PyCNBI Python pickle file

    Params
    --------
    outdir: If None, it will be the subdirectory of the fif file.
    external_event: Event file in text format. Each row should be: "SAMPLE_INDEX 0 EVENT_TYPE"
    precision: Data matrix format. 'single' improves backward compatability.
    """
    fdir, fname, fext = qc.parse_path_list(filename)
    if outdir is None:
        outdir = fdir + 'fif/'
    elif outdir[-1] != '/':
        outdir += '/'

    data = qc.load_obj(filename)

    if type(data['signals']) == list:
        signals_raw = np.array(data['signals'][0]).T  # to channels x samples
    else:
        signals_raw = data['signals'].T  # to channels x samples
    sample_rate = data['sample_rate']

    if 'ch_names' not in data:
        ch_names = ['CH%d' % (x + 1) for x in range(signals_raw.shape[0])]
    else:
        ch_names = data['ch_names']

    # search for event channel
    trig_ch = pu.find_event_channel(signals_raw, ch_names)
    ''' TODO: REMOVE
    # exception
    if trig_ch is None:
        logger.warning('Inferred event channel is None.')
        if interactive:
            logger.warning('If you are sure everything is alright, press Enter.')
            input()

    # fix wrong event channel
    elif trig_ch_guess != trig_ch:
        logger.warning('Specified event channel (%d) != inferred event channel (%d).' % (trig_ch, trig_ch_guess))
        if interactive: input('Press Enter to fix. Event channel will be set to %d.' % trig_ch_guess)
        ch_names.insert(trig_ch_guess, ch_names.pop(trig_ch))
        trig_ch = trig_ch_guess
        logger.info('New channel list:')
        for c in ch_names:
            logger.info('%s' % c)
        logger.info('Event channel is now set to %d' % trig_ch)
    '''

    # move trigger channel to index 0
    if trig_ch is None:
        # assuming no event channel exists, add a event channel to index 0 for consistency.
        logger.warning(
            'No event channel was not found. Adding a blank event channel to index 0.'
        )
        eventch = np.zeros([1, signals_raw.shape[1]])
        signals = np.concatenate((eventch, signals_raw), axis=0)
        num_eeg_channels = signals_raw.shape[
            0]  # data['channels'] is not reliable any more
        trig_ch = 0
        ch_names = ['TRIGGER'
                    ] + ['CH%d' % (x + 1) for x in range(num_eeg_channels)]
    elif trig_ch == 0:
        signals = signals_raw
        num_eeg_channels = data['channels'] - 1
    else:
        # move event channel to 0
        logger.info('Moving event channel %d to 0.' % trig_ch)
        signals = np.concatenate(
            (signals_raw[[trig_ch]], signals_raw[:trig_ch],
             signals_raw[trig_ch + 1:]),
            axis=0)
        assert signals_raw.shape == signals.shape
        num_eeg_channels = data['channels'] - 1
        ch_names.pop(trig_ch)
        trig_ch = 0
        ch_names.insert(trig_ch, 'TRIGGER')
        logger.info('New channel list:')
        for c in ch_names:
            logger.info('%s' % c)

    ch_info = ['stim'] + ['eeg'] * num_eeg_channels
    info = mne.create_info(ch_names, sample_rate, ch_info)

    # create Raw object
    raw = mne.io.RawArray(signals, info)
    raw._times = data['timestamps']  # seems to have no effect

    if external_event is not None:
        raw._data[0] = 0  # erase current events
        events_index = event_timestamps_to_indices(filename, external_event,
                                                   offset)
        if len(events_index) == 0:
            logger.warning('No events were found in the event file')
        else:
            logger.info('Found %d events' % len(events_index))
            raw.add_events(events_index, stim_channel='TRIGGER')

    qc.make_dirs(outdir)
    fiffile = outdir + fname + '.fif'

    raw.save(fiffile, verbose=False, overwrite=overwrite, fmt=precision)
    logger.info('Saved to %s' % fiffile)

    saveChannels2txt(outdir, ch_names)

    return True
示例#4
0
    def connect(self, find_any=True):
        """
        Run in child process
        """
        server_found = False
        amps = []
        channels = 0
        while server_found == False:
            if self.amp_name is None and self.amp_serial is None:
                logger.info("Looking for a streaming server...")
            else:
                logger.info("Looking for %s (Serial %s) ..." %
                            (self.amp_name, self.amp_serial))
            streamInfos = pylsl.resolve_streams()
            if len(streamInfos) > 0:
                # For now, only 1 amp is supported by a single StreamReceiver object.
                for si in streamInfos:
                    # is_slave= ('true'==pylsl.StreamInlet(si).info().desc().child('amplifier').child('settings').child('is_slave').first_child().value() )
                    inlet = pylsl.StreamInlet(si)
                    # LSL XML parser has a bug which crashes so do not use for now
                    #amp_serial = inlet.info().desc().child('acquisition').child_value('serial_number')
                    amp_serial = 'N/A'
                    amp_name = si.name()

                    # connect to a specific amp only?
                    if self.amp_serial is not None and self.amp_serial != amp_serial:
                        continue

                    # connect to a specific amp only?
                    if self.amp_name is not None and self.amp_name != amp_name:
                        continue

                    # EEG streaming server only?
                    if self.eeg_only and si.type() != 'EEG':
                        continue

                    if 'USBamp' in amp_name:
                        logger.info(
                            'Found USBamp streaming server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        self._lsl_tr_channel = 16
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'BioSemi' in amp_name:
                        logger.info(
                            'Found BioSemi streaming server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        self._lsl_tr_channel = 0  # or subtract -6684927? (value when trigger==0)
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'SmartBCI' in amp_name:
                        logger.info(
                            'Found SmartBCI streaming server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        self._lsl_tr_channel = 23
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'StreamPlayer' in amp_name:
                        logger.info(
                            'Found StreamPlayer streaming server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        self._lsl_tr_channel = 0
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'openvibeSignal' in amp_name:
                        logger.info(
                            'Found an Openvibe signal streaming server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        ch_list = pu.lsl_channel_list(inlet)
                        self._lsl_tr_channel = find_event_channel(
                            ch_names=ch_list)
                        channels += si.channel_count()
                        amps.append(si)
                        server_found = True
                        # OpenVibe standard unit is Volts, which is not ideal for some numerical computations
                        self.multiplier = 10**6  # change V -> uV unit for OpenVibe sources
                        break
                    elif 'openvibeMarkers' in amp_name:
                        logger.info(
                            'Found an Openvibe markers server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        ch_list = pu.lsl_channel_list(inlet)
                        self._lsl_tr_channel = find_event_channel(
                            ch_names=ch_list)
                        channels += si.channel_count()
                        amps.append(si)
                        server_found = True
                        break
                    elif find_any:
                        logger.info(
                            'Found a streaming server %s (type %s, amp_serial %s) @ %s.'
                            % (amp_name, si.type(), amp_serial, si.hostname()))
                        ch_list = pu.lsl_channel_list(inlet)
                        self._lsl_tr_channel = find_event_channel(
                            ch_names=ch_list)
                        channels += si.channel_count()
                        amps.append(si)
                        server_found = True
                        break
            time.sleep(1)

        self.amp_name = amp_name

        # define EEG channel indices
        self._lsl_eeg_channels = list(range(channels))
        if self._lsl_tr_channel is None:
            logger.warning(
                'Trigger channel not fonud. Adding an empty channel 0.')
        else:
            if self._lsl_tr_channel != 0:
                logger.info_yellow(
                    'Trigger channel found at index %d. Moving to index 0.' %
                    self._lsl_tr_channel)
            self._lsl_eeg_channels.pop(self._lsl_tr_channel)
        self._lsl_eeg_channels = np.array(self._lsl_eeg_channels)
        self.tr_channel = 0  # trigger channel is always set to 0.
        self.eeg_channels = np.arange(
            1, channels)  # signal channels start from 1.

        # create new inlets to read from the stream
        inlets_master = []
        inlets_slaves = []
        for amp in amps:
            # data type of the 2nd argument (max_buflen) is int according to LSL C++ specification!
            inlet = pylsl.StreamInlet(amp, max_buflen=self.stream_bufsec)
            inlets_master.append(inlet)
            self.buffers.append([])
            self.timestamps.append([])

        inlets = inlets_master + inlets_slaves
        sample_rate = amps[0].nominal_srate()
        logger.info('Channels: %d' % channels)
        logger.info('LSL Protocol version: %s' % amps[0].version())
        logger.info('Source sampling rate: %.1f' % sample_rate)
        logger.info('Unit multiplier: %.1f' % self.multiplier)

        #self.winsize = int(self.winsec * sample_rate)
        #self.bufsize = int(self.bufsec * sample_rate)
        self.winsize = int(round(self.winsec * sample_rate))
        self.bufsize = int(round(self.bufsec * sample_rate))
        self.stream_bufsize = int(round(self.stream_bufsec * sample_rate))
        self.sample_rate = sample_rate
        self.connected = True
        self.ch_list = ch_list
        self.inlets = inlets  # Note: not picklable!

        # TODO: check if there's any problem with multiple inlets
        if len(self.inlets) > 1:
            logger.warning(
                'Merging of multiple acquisition servers is not supported yet. Only %s will be used.'
                % amps[0].name())
            '''
            for i in range(1, len(self.inlets)):
                chunk, tslist = self.inlets[i].pull_chunk(max_samples=self.stream_bufsize)
                self.buffers[i].extend(chunk)
                self.timestamps[i].extend(tslist)
                if self.bufsize > 0 and len(self.buffers[i]) > self.bufsize:
                    self.buffers[i] = self.buffers[i][-self.bufsize:]
            '''

        # create channel info
        if self._lsl_tr_channel is None:
            self.ch_list = ['TRIGGER'] + self.ch_list
        else:
            for i, chn in enumerate(self.ch_list):
                if chn == 'TRIGGER' or chn == 'TRG' or 'STI ' in chn:
                    self.ch_list.pop(i)
                    self.ch_list = ['TRIGGER'] + self.ch_list
                    break
        logger.info('self.ch_list %s' % self.ch_list)

        # fill in initial buffer
        logger.info('Waiting to fill initial buffer of length %d' %
                    (self.winsize))
        while len(self.timestamps[0]) < self.winsize:
            self.acquire()
            time.sleep(0.1)
        self.ready = True
        logger.info('Start receiving stream data.')