Getting the Spike times

For certain experiments, it is interesting to know when a neuron or a population fires action potentials -- often in response to some event or stimulus. The time when the firing happens is referred to as the spike time. Since neurons respond to specific stimuli in a characteristic way it's also of particular interest what the response means.  The field of research concerned with understanding how neurons encode information is called Neural Encoding https://en.wikipedia.org/wiki/Neural_coding
The hope is that with a better understanding of how neurons encode information, we may also be able to better communicate with them via neural stimulation.
To get the spike times of the detected spikes from example12 we need to know when the spikes cross the determined threshold and then temporarily align them. To get the actual spike times we need to divide the extracted spikes by the sampling frequency again.
Moreover, to get a better result, we also take into account that neurons need some time before they can fire again. This duration of silence after each action potentials is called the dead time[Deger et al, 2012]. The time span where neurons are incapable of repeating a specific action -- firing an action potential -- is called the refractory period. Being able to cut out spikes with respect to the dead time also offers the ability to cleanly cut out the spike shapes and align them. This is very much needed to perform spike sorting. To cut out extracted waveforms the spike processing class provides the method extract_waveforms().

To get the spike times of the spikes detected in example 12 all we have to add are these three lines of code:

crossings = spikeProcessing.detect_threshold_crossings(filteredSignalChunk, spike_threshold, dead_time = 0.003) # dead time of 3 ms
spks = spikeProcessing.align_to_minimum(filteredSignalChunk, crossings, search_range = 0.0002) # search range 2 ms
timeStamps = spks/sampling_frequency

The complete code looks like this:

def example13():
    from_in_s=1095
    to_in_s=1123
    sampling_frequency = 5000
    #load real data
    filteredSignal = np.load("FilteredData.npy")
    spikeProcessing = pipeline.SpikeProcessing(sampling_frequency, sigmas=-5)
   
    filteredSignalChunk = filteredSignal[int(from_in_s*sampling_frequency):int(to_in_s*sampling_frequency)]
    
    spikes_in_range, spike_threshold = spikeProcessing.getSpikesInRange(filteredSignalChunk, start = 0.0)
    spikes_in_range = spikes_in_range*sampling_frequency
    
    plt.plot(filteredSignalChunk)    
    plt.plot(spikes_in_range, [spike_threshold]*spikes_in_range.shape[0], 'ro', ms=2)
    crossings = spikeProcessing.detect_threshold_crossings(filteredSignalChunk, spike_threshold, dead_time = 0.003) # dead time of 3 ms
    spks = spikeProcessing.align_to_minimum(filteredSignalChunk, crossings, search_range = 0.0002) # search range 2 ms
    timeStamps = spks/sampling_frequency 

[Deger et al, 2012]Deger M, Helias M, Boucsein C, Rotter S. Statistical properties of superimposed stationary spike trains. J Comput Neurosci. 2012;32(3):443-463. doi:10.1007/s10827-011-0362-8