Now that we have used the SpikeProcessing class directly to perform spike detection, we are now going to build another pipeline.
Instead of generating random signals, we are going to simulate our own neural signals and add some artifacts! To plot the signals we are again using the pipeline.MplSweepDataDisplay class that we have used previously in example10.
To accomplish this task we first need to generate some random background noise and initialize the MCsweep object with it. After that, it is business as usual. All the objects that we need for this task are already there. We just have to connect them to build our pipeline.
A new object that we have not used is an object that contains the stimulation parameter that we need to simulate stimulation artifacts.
Yet again, we are building this pipeline step by step.
Generating an MCsweep object containing background noise is achieved by these lines:
sampleRate = 24414 numChannels = 32 sweepLen = int(sampleRate/2) #generate random background noise sigTupel=[tuple(0.000015*np.random.normal(0,1.0, numChannels*sweepLen))] data=RZ2ephys.MCsweep(sigTupel,sweepLen,sampleRate,0)
And as before, we are going to generate multiple MCsweep objects and then plot the results.
Our first pipeline is very similar to example9. All we do here is setting up the pipeline to generate, filter, and simulate the data. Furthermore, here we are only going to simulate spike data. The artifacts will be added in the next pipeline.
This is the working code:
def example14(): sampleRate = 24414 numChannels = 32 sweepLen = int(sampleRate/2) aDatasource = pipeline.Datasource() aDatasource.setName("Eyphs PushDataSource") sweepSpikeFiltFiltPlugin = pipeline.SweepSpikeFiltFiltPlugin() sweepSpikeFiltFiltPlugin.setName("Eyphs SweepSpikeFiltFiltPlugin") aSpikeSimulationPlugin = pipeline.SpikeSimulationPlugin() aSpikeSimulationPlugin.setName("Eyphs SpikeSimulationPlugin") aSpikeSimulationPlugin.loadSpikeTemplates("FilteredSpikeTemplates.npy") debugSpikeAmplitudes = [0.0001, 0.0001, 0.0001, 0.0001] debugSpikePositions = [0.1, 0.2, 0.3, 0.4] aSpikeSimulationPlugin.setDebug(False) aSpikeSimulationPlugin.setDebugSpikeAmplitudes(debugSpikeAmplitudes) aSpikeSimulationPlugin.setDebugSpikePositions(debugSpikePositions) aDisplay = pipeline.MplSweepDataDisplay(plt) aDatasink = pipeline.Datasink() aDatasink.setName("Eyphs DataSink") #assemble the pipeline aDatasource.setOutput(sweepSpikeFiltFiltPlugin) sweepSpikeFiltFiltPlugin.setOutput(aSpikeSimulationPlugin) aSpikeSimulationPlugin.setOutput(aDisplay) aDisplay.setOutput(aDatasink) for n in range(100): #generate multichannel data and random background noise sigTupel=tuple(0.000015*np.random.normal(0,1.0, numChannels*sweepLen)) data=RZ2ephys.MCsweep(sigTupel,sweepLen,sampleRate,0) aDataframe = pipeline.Dataframe() aDataframe.setFrameNumber(n) aDataframe.setData(data) aDatasource.addDataframe(aDataframe) aDatasource.run()
The program structure is virtually identical to example9. However, the pipeline.SpikeSimulationPlugin() is new and deserves some explanation. The point of this class is to stimulate neural signals if no actual recording can be performed and or if the recording hardware is not available. A software simulation makes it possible to develop and test software that processes electrophysiological data without loading or recording actual data. A simulation also has the advantage of giving full control over various spike parameters which is very useful for developing and testing spike detection and sorting algorithms. The SpikeSimulationPlugin has two modes of operation -- a regular and a debug mode. In the regular mode, the simulation is simply generating artificial spikes per channel with random amplitude and then adding them to the already existing signals. In our case, this just simulated background noise. However, in debug mode, we need to load actual spike templates, because some spike detection algorithms work better with real rather than simulated spikes. Furthermore, we can add spikes at specific points in time and positions. This is E.g. super helpful for checking if the spike times extracted by some detection algorithm are correct. Usually, the maximum number of spikes is set to a default of 10 spikes. But this value can be changed using setMaxNumSpikes() if needed.
Before adding the artifact simulation, here is the output of example14()
Let's continue with the code regarding the simulated electrical stimulation. For that, we have to add the relevant stimulation parameters and initialize the stimulation parameter object with them. For this example, we are using the clickTrainObject() from the ClickTrainLibrary. Note, that the ClickTrainLibrary offers various other objects and you need to take the one that fits the purpose of your experiment. We are creating a basic example here, so the clickTrainObject() will do the job. if you go all the way back to Fig5, you will see the role of the stimulation parameter object. You will also notice that we are going to attach it to the data frame.
import ClickTrainLibrary as stimulatorModule
#set stimulation parameters level_dB=10 # in dB in relation to 100 uA clickRate=50 # 300, 900, 1800 ears='left' # other options are 'right' and 'both' Must be set to 'left' or 'right' during simulation duration = 0.001 # This defines the duration of the stimulation numClicks = 1 nloop = 0 reference_Simulation=0.100 # set reference to 100 uA levelRePeak = True # normally we set levels relative to RMS If you want to set them relative to abs max instead, set this to True clickShape_CI_Simulation = np.array([0,1,0,-1,0]) # use a biphasic pulse #set up the stimulation parameter object stimulatorModule.soundPlayer=None stim=stimulatorModule.clickTrainObject() stim.clickShape= clickShape_CI_Simulation stim.stimParams['duration (s)']=duration # this parameter controls the duration of the stimulation. stim.stimParams['ABL (dB)']=level_dB #ABL means Average Binaural Level stim.stimParams['clickRate (Hz)']=clickRate stim.stimParams['Nloop']=nloop stim.stimParams['numClicks']=numClicks stim.reference=reference stim.clickShape=ci_clickshape stim.levelRePeak=levelRePeak stim.ready()
The artifact simulation is added by the ArtifactSimulationPlugin.
aArtifactSimulationPlugin = pipeline.ArtifactSimulationPlugin() aArtifactSimulationPlugin.setName("Eyphs ArtifactSimulationPlugin") aArtifactSimulationPlugin.setEnabled(enableCISimulation) aSpikeSimulationPlugin = pipeline.SpikeSimulationPlugin()
Like all plugins, it can be enabled or disabled by setting setEnabled() to True or False
After adding the new features to example 14 we get some new code:
import ClickTrainLibrary as stimulatorModule import copy def example15(): sampleRate = 24414 numChannels = 32 sweepLen = int(sampleRate/2) enableCISimulation = True aDatasource = pipeline.Datasource() aDatasource.setName("Eyphs PushDataSource") sweepSpikeFiltFiltPlugin = pipeline.SweepSpikeFiltFiltPlugin() sweepSpikeFiltFiltPlugin.setName("Eyphs SweepSpikeFiltFiltPlugin") aSpikeSimulationPlugin = pipeline.SpikeSimulationPlugin() aSpikeSimulationPlugin.setName("Eyphs SpikeSimulationPlugin") aSpikeSimulationPlugin.loadSpikeTemplates("FilteredSpikeTemplates.npy") debugSpikeAmplitudes = [0.0001, 0.0001, 0.0001, 0.0001] debugSpikePositions = [0.1, 0.2, 0.3, 0.4] aSpikeSimulationPlugin.setDebug(False) aSpikeSimulationPlugin.setDebugSpikeAmplitudes(debugSpikeAmplitudes) aSpikeSimulationPlugin.setDebugSpikePositions(debugSpikePositions) aArtifactSimulationPlugin = pipeline.ArtifactSimulationPlugin() aArtifactSimulationPlugin.setName("Eyphs ArtifactSimulationPlugin") aArtifactSimulationPlugin.setEnabled(enableCISimulation) aSpikeSimulationPlugin = pipeline.SpikeSimulationPlugin() aDisplay = pipeline.MplSweepDataDisplay(plt) aDatasink = pipeline.Datasink() aDatasink.setName("Eyphs DataSink") #assemble the pipeline aDatasource.setOutput(sweepSpikeFiltFiltPlugin) sweepSpikeFiltFiltPlugin.setOutput(aSpikeSimulationPlugin) aSpikeSimulationPlugin.setOutput(aArtifactSimulationPlugin) aArtifactSimulationPlugin.setOutput(aDisplay) aDisplay.setOutput(aDatasink) #set up the stimulation parameter object #set stimulation parameters level_dB=5 # in dB in relation to 100 uA clickRate=50 # 300, 900, 1800 ears='left' # other options are 'right' and 'both' Must be set to 'left' or 'right' during simulation duration = 0.01 # This defines the duration of the stimulation numClicks = 1 nloop = 0 reference_Simulation=0.100 # set reference to 100 uA levelRePeak = True # normally we set levels relative to RMS If you want to set them relative to abs max instead, set this to True clickShape_CI_Simulation = np.array([0,1,0,-1,0]) # use a biphasic pulse #set up the stimulation parameter object stimulatorModule.soundPlayer=stimulatorModule.pygameSoundHardware() stimulatorModule.soundPlayer.sampleRate = 25000 stim=stimulatorModule.clickTrainObject() stim.clickShape= clickShape_CI_Simulation stim.stimParams['duration (s)']=duration # this parameter controls the duration of the stimulation. stim.stimParams['ABL (dB)']=level_dB #ABL means Average Binaural Level stim.stimParams['clickRate (Hz)']=clickRate stim.stimParams['Nloop']=nloop stim.stimParams['numClicks']=numClicks stim.reference=reference_Simulation stim.clickShape=clickShape_CI_Simulation stim.levelRePeak=levelRePeak stim.ears=ears stim.ready() for n in range(100): #generate multichannel data and random background noise sigTupel=tuple(0.000015*np.random.normal(0,1.0, numChannels*sweepLen)) data=RZ2ephys.MCsweep(sigTupel,sweepLen,sampleRate,0) stimObjectCopy = copy.deepcopy(stim) aDataframe = pipeline.Dataframe() aDataframe.setStimObject(stimObjectCopy) aDataframe.setFrameNumber(n) aDataframe.setData(data) aDatasource.addDataframe(aDataframe) aDatasource.run()
And the output of the program looks like this:
Where are the spikes? Did we do something wrong? No! The stimulation artifacts are so enormously large that the spikes are simply not visible because the plot automatically scales to the largest amplitude -- the stimulation artifact.
So what can we do? We need to get rid of the artifact. And the simplest method is called blanking which for us simply means deleting the part of the signal that is contaminated with the stimulation artifacts. After that step, we will see the spikes again! The blanking is already available as a plugin. All we need to do is adding it to the pipeline.