示例#1
0
def to_physiosignal(uuid, waveform_name, t, s, dt):

    physio_label = ''
    t_first_trigger = None

    # specify suffix:
    if 'PULS' in waveform_name:
        physio_label = 'cardiac'

    elif 'RESP' in waveform_name:
        physio_label = 'respiratory'

    elif 'EXT' in waveform_name:
        physio_label = 'external_trigger'

    elif 'ECG' in waveform_name:
        physio_label = 'ecg'

    elif "ACQUISITION_INFO" in waveform_name:
        physio_label = 'trigger'
        # We only care about the trigger for each volume, so keep only
        #   the timepoints for which the trigger signal is 1:
        t = t[np.where(s == 1)]
        s = np.full(len(t), True)
        # time for the first trigger:
        t_first_trigger = t[0] / 1000

    physio_signal = PhysioSignal(
        label=physio_label,
        uuid=uuid,
        samples_per_second=1000 / dt,  # dt is in ms.
        sampling_times=t / 1000,
        physiostarttime=t[0] / 1000,
        signal=s)
    return physio_label, physio_signal, t_first_trigger
示例#2
0
def test_calculate_trigger_events(capfd, mySignal, trigger_timing):
    """
    Make sure you get as many triggers in the trigger signal
    as elements there are in the trigger timing (between the
    beginning of the recording and the end)
    """

    # 1) If you try to calculate it for a signal for which we cannot calculate
    #    the timing, it should print an error and return None:
    assert PhysioSignal(label='simulated', physiostarttime=PHYSIO_START_TIME
                        ).calculate_trigger_events(trigger_timing) is None
    assert capfd.readouterr(
    ).out == "Unable to calculate the recording timing\n"

    # 2) Run it successfully:
    # calculate trigger events:
    trig_signal = mySignal.calculate_trigger_events(trigger_timing)

    assert isinstance(trig_signal, np.ndarray)

    # calculate how many triggers there are between the first and last sampling_times:
    num_trig_within_physio_samples = np.bitwise_and(
        np.array(trigger_timing) >= mySignal.sampling_times[0],
        np.array(trigger_timing) <= mySignal.sampling_times[-1])

    assert (sum(trig_signal) == sum(num_trig_within_physio_samples))
示例#3
0
def pmu2bids(physio_files, verbose=False):
    """
    Function to read a list of Siemens PMU physio files and
    save them as a BIDS physiological recording.

    Parameters
    ----------
    physio_files : list of str
        list of paths to files with a Siemens PMU recording
    verbose : bool
        verbose flag

    Returns
    -------
    physio : PhysioData
        PhysioData with the contents of the file
    """

    # In case we are handled just a single file, make it a one-element list:
    if isinstance(physio_files, str):
        physio_files = [physio_files]

    # Init PhysioData object to hold physio signals:
    physio = PhysioData()

    # Read the files from the list, extract the relevant information and
    #   add a new PhysioSignal to the list:
    for f in physio_files:
        physio_type, MDHTime, sampling_rate, physio_signal = readpmu(f, verbose=verbose)

        testSamplingRate(
                            sampling_rate = sampling_rate,
                            Nsamples = len(physio_signal),
                            logTimes=MDHTime
        )

        # specify label:
        if 'PULS' in physio_type:
            physio_label = 'cardiac'

        elif 'RESP' in physio_type:
            physio_label = 'respiratory'

        elif "TRIGGER" in physio_type:
            physio_label = 'trigger'

        else:
            physio_label = physio_type

        physio.append_signal(
            PhysioSignal(
                label=physio_label,
                units='',
                samples_per_second=sampling_rate,
                physiostarttime=MDHTime[0],
                signal=physio_signal
            )
        )

    return physio
示例#4
0
def test_plug_missing_data():
    """   Test for plug_missing_data   """

    # generate a PhysioSignal with a temporal series and corresponding fake signal with
    #   missing timepoints:
    st = [i / 1 for i in range(35) if i % 10]
    spamSignal = PhysioSignal(label='simulated',
                              samples_per_second=1,
                              sampling_times=st,
                              signal=[i for i in range(len(st))])

    spamSignal.plug_missing_data()
    assert all(np.ediff1d(spamSignal.sampling_times)) == 1
    assert all(
        np.isnan(spamSignal.signal[[
            i for i in range(len(spamSignal.signal)) if not (i + 1) % 10
        ]]))
    assert len(spamSignal.signal) == spamSignal.samples_count
示例#5
0
def mySignal(scope="module"):
    """    Simulate a PhysioSignal object    """

    mySignal = PhysioSignal(
        label='simulated',
        samples_per_second=PHYSIO_SAMPLES_PER_SECOND,
        physiostarttime=PHYSIO_START_TIME,
        neuralstarttime=PHYSIO_START_TIME + SCANNER_DELAY,
        signal=PHYSIO_SAMPLES_COUNT * [0]  # fill with zeros
    )

    return mySignal
示例#6
0
def myphysiodata(scope="module"):
    """   Create a "PhysioData" object with barebones content  """

    myphysiodata = PhysioData([
        PhysioSignal(label=l,
                     samples_per_second=PHYSIO_SAMPLES_PER_SECOND,
                     physiostarttime=PHYSIO_START_TIME,
                     neuralstarttime=PHYSIO_START_TIME + SCANNER_DELAY,
                     signal=[i for i in range(PHYSIO_SAMPLES_COUNT)])
        for l in LABELS
    ])
    return myphysiodata
示例#7
0
def test_append_signal(myphysiodata):
    """
    Tests that "append_signal" does what it is supposed to do
    """

    # Make a copy of myphysiodata to make sure we don't modify it,
    #  so that it is later available unmodified to other tests:
    physdata = copy.deepcopy(myphysiodata)
    physdata.append_signal(PhysioSignal(label='extra_signal'))

    mylabels = LABELS.copy()
    mylabels.append('extra_signal')
    assert physdata.labels() == mylabels
示例#8
0
def myphysiodata_with_edf_trigger(myphysiodata,
                                  simulated_edf_trigger_signal,
                                  scope="module"):
    myphysiodata_with_edf_trigger = copy.deepcopy(myphysiodata)

    # add a trigger signal to the physiodata_with_trigger:
    myphysiodata_with_edf_trigger.append_signal(
        PhysioSignal(label='trigger',
                     samples_per_second=TRIGGER_SAMPLES_PER_SECOND,
                     physiostarttime=TRIGGER_START_TIME,
                     neuralstarttime=TRIGGER_START_TIME,
                     signal=simulated_edf_trigger_signal))
    return myphysiodata_with_edf_trigger
示例#9
0
def test_calculate_timing(mySignal):
    """
    Test for calculate_timing
    It checks that it gives an error when it is supposed to, and it returns the
    correct timing when the neccessary parameters are present
    """

    # 1) Try with a PhysioSignal without sampling rate:
    with pytest.raises(Exception) as e_info:
        PhysioSignal(label='simulated',
                     physiostarttime=PHYSIO_START_TIME).calculate_timing()

    # 2) With a correct signal:
    mySignal.calculate_timing()
    assert len(mySignal.sampling_times) == PHYSIO_SAMPLES_COUNT
    assert mySignal.sampling_times[0] == mySignal.physiostarttime
    np.testing.assert_allclose(np.ediff1d(mySignal.sampling_times),
                               1 / mySignal.samples_per_second, 1e-10)
示例#10
0
def test_matching_trigger_signal(mySignal, trigger_timing):
    """
    Test that both PhysioSignals (the original signal and the derived one with the trigger)
    have the same fields.
    It requires the result of "test_calculate_trigger_events"
    """

    # calculate trigger events:
    trig_signal = mySignal.calculate_trigger_events(trigger_timing)

    trigger_physiosignal = PhysioSignal.matching_trigger_signal(
        mySignal, trig_signal)

    assert isinstance(trigger_physiosignal, PhysioSignal)
    assert trigger_physiosignal.label == 'trigger'
    assert trigger_physiosignal.samples_per_second == mySignal.samples_per_second
    assert trigger_physiosignal.physiostarttime == mySignal.physiostarttime
    assert trigger_physiosignal.neuralstarttime == mySignal.neuralstarttime
    assert trigger_physiosignal.sampling_times == mySignal.sampling_times
    assert all(trigger_physiosignal.signal == trig_signal)
示例#11
0
def acq2bids(physio_acq_files, trigger_labels=['trigger', 'digital input']):
    """Reads the physiological data from a series of AcqKnowledge
    files and stores it in a PhysioData member

    Parameters
    ----------
    physio_acq_files : list of str
        List of paths of the original physio files
    trigger_labels : list of str
        List with labels of the channel that carries the scanner trigger.
        Just one word from the channel name is enough

    Returns
    -------
    physio : PhysioData
        PhysioData with the contents of the file
    """
    # In case we are handled just a single file, make it a one-element list:
    if isinstance(physio_acq_files, str):
        physio_acq_files = [physio_acq_files]
    if not isinstance(trigger_labels, list):
        trigger_labels = [trigger_labels]

    # Init PhysioData object to hold physio signals:
    physio = PhysioData()

    # Read the files from the list, extract the relevant information and
    #   add a new PhysioSignal to the list:
    trigger_channel = ''
    for physio_acq in physio_acq_files:
        # Extract data from AcqKnowledge file:
        physio_data = bioread.read(physio_acq)
        # Get the time the file was created:
        physiostarttime = physio_data.earliest_marker_created_at

        for item in physio_data.channels:
            physio_label = ''

            # specify label:
            if 'puls' in item.name.lower():
                physio_label = 'cardiac'

            elif 'resp' in item.name.lower():
                physio_label = 'respiratory'

            elif any(
                [tl.lower() in item.name.lower() for tl in trigger_labels]):
                physio_label = 'trigger'
                trigger_channel = item.name

            else:
                physio_label = item.name

            if physio_label:
                physio.append_signal(
                    PhysioSignal(label=physio_label,
                                 samples_per_second=item.samples_per_second,
                                 sampling_times=item.time_index,
                                 physiostarttime=physiostarttime.timestamp(),
                                 signal=item.data,
                                 units=item.units))

    # Get the "neuralstarttime" for the PhysioSignals by finding the first trigger.
    # We do this after we have read all signals to make sure we have read the trigger
    # (if present in the file. If not present, use the physiostart time. This is the
    # same as assuming the physiological recording started at the same time as the
    # neural recording.)
    # This assumes that the channel named "trigger" indeed contains the scanner trigger
    # and not something else (e.g., stimulus trigger). So we print a warning.
    neuralstarttime = ''
    if trigger_channel:
        print(
            'Warning: Assuming "{}" channel corresponds to the scanner trigger'
            .format(trigger_channel))
        # The sampling_times are w.r.t. the start of the recording, so we need
        # to also add the 'physiostarttime' (time when file was created):
        neuralstarttime = physio.get_scanner_onset(
        ) + physiostarttime.timestamp()
    for p_signal in physio.signals:
        p_signal.neuralstarttime = neuralstarttime or p_signal.physiostarttime
        # we also fill with NaNs the places for which there is missing data:
        p_signal.plug_missing_data()

    return physio