def load_stimulus(self, recording):
        """Return an instance of stimuli.Stimulus"""

        #### I don't know whether I should try to parse this from metadata, or just find square pulses in the command waveform.
        ### I think finding square pulses would be simpler, but makes the assumption that pulses are square. Which is probably usually true.
        ### what if I check the wavegenerator widget data for the function name (pulse) and then findSquarepulses, or raise an exception if it's a different function?
        if isinstance(recording, PatchClampRecording):
            items = []

            fh = DataManager.getFileHandle(recording.meta['file_name'])
            seqDir = PatchEPhys.getParent(fh, 'ProtocolSequence')
            if seqDir is not None:
                dev_info = seqDir.info()['devices'][recording.device_id]

                if dev_info['mode'].lower() == 'vc':
                    units = 'V'
                elif dev_info['mode'].lower() == 'ic':
                    units = 'A'
                else:
                    units = None

                if dev_info['holdingCheck']:
                    items.append(stimuli.Offset(dev_info['holdingSpin']))

                stim_pulses = PatchEPhys.getStimParams(fh)

                for p in stim_pulses:
                    if p['function_type'] == 'pulse':
                        items.append(
                            stimuli.SquarePulse(p['start'], p['length'],
                                                p['amplitude']))
                    elif p['function_type'] == 'pulseTrain':
                        items.append(
                            stimuli.SquarePulseTrain(p['start'],
                                                     p['pulse_number'],
                                                     p['length'],
                                                     p['amplitude'],
                                                     p['period']))

                desc = seqDir.shortName()[:-4]
                return stimuli.Stimulus(desc, items=items, units=units)

        else:
            raise Exception('not implemented yet')
Example #2
0
def test_pulse_train():
    s1 = stimuli.Stimulus("stimulus 1")
    sp1 = stimuli.SquarePulse(start_time=0.1, duration=0.2, amplitude=10, parent=s1)
    sp2 = stimuli.SquarePulse(start_time=0.2, duration=0.2, amplitude=-10, description="square pulse 2", units='A', parent=s1)

    # test PulseTrain
    pt1 = stimuli.SquarePulseTrain(start_time=0.5, n_pulses=3, pulse_duration=0.02, interval=0.1, amplitude=1.0, parent=s1, units='A')
    assert s1.items == (sp1, sp2, pt1)

    # check sub-pulse start times
    assert pt1.global_start_time == 0.5
    assert pt1.start_time == 0.5
    assert pt1.items[0].global_start_time == 0.5
    assert pt1.items[0].start_time == 0.0
    assert pt1.items[1].global_start_time == 0.6
    assert pt1.items[1].start_time == 0.1
    assert pt1.items[2].global_start_time == 0.7
    assert pt1.items[2].start_time == 0.2
    assert pt1.pulse_times == [0.0, 0.1, 0.2]
    assert pt1.global_pulse_times == [0.5, 0.6, 0.7]

    # test waveform eval with all three items
    s1_data = s1.eval(n_pts=1000, dt=0.001).data
    test_data = np.zeros(1000)
    test_data[100:300] = 10
    test_data[200:400] -= 10
    test_data[500:520] += 1
    test_data[600:620] += 1
    test_data[700:720] += 1
    assert np.all(s1_data == test_data)

    test_mask = (s1_data != 0)
    test_mask[100:400] = True
    assert np.all(test_mask == s1.mask(n_pts=1000, dt=0.001).data)

    # test save/load
    state = s1.save()
    assert state == OrderedDict([
        ('type', 'Stimulus'),
        ('args', OrderedDict([
            ('start_time', 0),
            ('description', 'stimulus 1'),
            ('units', None),
        ])),
        ('items', [
            OrderedDict([
                ('type', 'SquarePulse'),
                ('args', OrderedDict([
                    ('start_time', 0.1), 
                    ('description', 'square pulse'),
                    ('units', None),
                    ('duration', 0.2),
                    ('amplitude', 10),
                ])),
                ('items', []),
            ]),
            OrderedDict([
                ('type', 'SquarePulse'),
                ('args', OrderedDict([
                    ('start_time', 0.2), 
                    ('description', 'square pulse 2'),
                    ('units', 'A'),
                    ('duration', 0.2),
                    ('amplitude', -10),
                ])),
                ('items', []),
            ]),
            OrderedDict([
                ('type', 'SquarePulseTrain'),
                ('args', OrderedDict([
                    ('start_time', 0.5), 
                    ('description', 'square pulse train'),
                    ('units', 'A'),
                    ('n_pulses', 3),
                    ('pulse_duration', 0.02),
                    ('amplitude', 1.0),
                    ('interval', 0.1),
                ])),
                ('items', []),
            ]),
        ])
    ])
    s2 = stimuli.load_stimulus(state)
    assert isinstance(s2, stimuli.Stimulus)
    assert s2.items[0] == s1.items[0]
    assert s2.items[1] == s1.items[1]
    assert s2.items[2] == s1.items[2]
    assert s2.items[2].items[0] == s1.items[2].items[0]
    assert s2.items[2].items[1] == s1.items[2].items[1]
    assert s2.items[2].items[2] == s1.items[2].items[2]
Example #3
0
    def load_stimulus_items(self, rec):
        items = []

        # Add holding offset, determine units
        if rec.clamp_mode == 'ic':
            units = 'A'
            items.append(
                stimuli.Offset(
                    start_time=0,
                    amplitude=rec.holding_current,
                    description="holding current",
                    units=units,
                ))
        elif rec.clamp_mode == 'vc':
            units = 'V'
            items.append(
                stimuli.Offset(
                    start_time=0,
                    amplitude=rec.holding_potential,
                    description="holding potential",
                    units=units,
                ))
        else:
            units = None

        # inserted test pulse?
        #if rec.has_inserted_test_pulse:
        #    self.append_item(rec.inserted_test_pulse.stimulus)
        if rec.test_pulse is not None:
            items.append(rec.test_pulse.stimulus)

        notebook = rec.meta['notebook']

        if 'Stim Wave Note' in notebook:
            # Stim Wave Note format is explained here:
            # https://alleninstitute.github.io/MIES/file/_m_i_e_s___wave_builder_8ipf.html#_CPPv319WB_GetWaveNoteEntry4wave8variable6string8variable8variable

            # read stimulus structure from notebook
            #version, epochs = rec._stim_wave_note()
            version, epochs = parser.parse_stim_wave_note(notebook)
            assert len(epochs) > 0
            scale = (1e-3 if rec.clamp_mode == 'vc' else
                     1e-12) * notebook['Stim Scale Factor']
            t = (notebook['Delay onset oodDAQ'] + notebook['Delay onset user']
                 + notebook['Delay onset auto']) * 1e-3

            # if dDAQ is active, add delay from previous channels
            if notebook['Distributed DAQ'] == 1.0:
                ddaq_delay = notebook['Delay distributed DAQ'] * 1e-3
                for dev in rec.parent.devices:
                    other_rec = rec.parent[dev]
                    if other_rec is rec:
                        break
                    #_, epochs = rec._stim_wave_note()
                    if 'Stim Wave Note' in other_rec.meta['notebook']:
                        _, other_epochs = parser.parse_stim_wave_note(
                            other_rec.meta['notebook'])
                        for ep in other_epochs:
                            dt = float(ep.get('Duration', 0)) * 1e-3
                            t += dt
                        t += ddaq_delay

            for epoch_n, epoch in enumerate(epochs):
                try:
                    if epoch['Epoch'] == 'nan':
                        # Sweep-specific entry; not sure if we need to do anything with this.
                        continue

                    stim_type = epoch.get('Type')
                    duration = float(epoch.get('Duration', 0)) * 1e-3
                    name = "Epoch %d" % int(epoch['Epoch'])
                    if stim_type == 'Square pulse':
                        item = stimuli.SquarePulse(
                            start_time=t,
                            amplitude=float(epoch['Amplitude']) * scale,
                            duration=duration,
                            description=name,
                            units=units,
                        )
                    elif stim_type == 'Pulse Train':
                        assert epoch[
                            'Poisson distribution'] == 'False', "Poisson distributed pulse train not supported"
                        assert epoch[
                            'Mixed frequency'] == 'False', "Mixed frequency pulse train not supported"
                        assert epoch[
                            'Pulse Type'] == 'Square', "Pulse train with %s pulse type not supported"
                        item = stimuli.SquarePulseTrain(
                            start_time=t,
                            n_pulses=int(epoch['Number of pulses']),
                            pulse_duration=float(epoch['Pulse duration']) *
                            1e-3,
                            amplitude=float(epoch['Amplitude']) * scale,
                            interval=float(epoch['Pulse To Pulse Length']) *
                            1e-3,
                            description=name,
                            units=units,
                        )
                    elif stim_type == 'Sin Wave':
                        # bug in stim wave note version 2: log chirp field is inverted
                        is_chirp = epoch['Log chirp'] == (
                            'False' if version <= 2 else 'True')
                        if is_chirp:
                            assert epoch[
                                'FunctionType'] == 'Sin', "Chirp wave function type %s not supported" % epoch[
                                    'Function type']
                            item = stimuli.Chirp(
                                start_time=t,
                                start_frequency=float(epoch['Frequency']),
                                end_frequency=float(epoch['End frequency']),
                                duration=duration,
                                amplitude=float(epoch['Amplitude']) * scale,
                                phase=0,
                                offset=float(epoch['Offset']) * scale,
                                description=name,
                                units=units,
                            )
                        else:
                            if epoch['FunctionType'] == 'Sin':
                                phase = 0
                            elif epoch['FunctionType'] == 'Cos':
                                phase = np.pi / 2.0
                            else:
                                raise ValueError(
                                    "Unsupported sine wave function type: %r" %
                                    epoch['FunctionType'])

                            item = stimuli.Sine(
                                start_time=t,
                                frequency=float(epoch['Frequency']),
                                duration=duration,
                                amplitude=float(epoch['Amplitude']) * scale,
                                phase=phase,
                                offset=float(epoch['Offset']) * scale,
                                description=name,
                                units=units,
                            )
                    else:
                        print(epoch)
                        print(
                            "Warning: unknown stimulus type %s in %s sweep %s"
                            % (stim_type, self._file_path,
                               rec.meta['sweep_name']))
                        item = None
                except Exception as exc:
                    print(
                        "Warning: error reading stimulus epoch %d in %s sweep %s: %s"
                        % (epoch_n, self._file_path, rec.meta['sweep_name'],
                           str(exc)))

                t += duration
                if item is not None:
                    items.append(item)

        return items