import numpy as np
import unittest
from iblutil.spacer import Spacer
[docs]
class TestSpacer(unittest.TestCase):
[docs]
def test_spacer(self):
spacer = Spacer(dt_start=.02, dt_end=.4, n_pulses=8, tup=.05)
np.testing.assert_equal(spacer.times.size, 15)
sig = spacer.generate_template(fs=1000)
ac = np.correlate(sig, sig, 'full') / np.sum(sig**2)
# import matplotlib.pyplot as plt
# plt.plot(ac)
# plt.show()
ac[sig.size - 100: sig.size + 100] = 0 # remove the main peak
# the autocorrelation side lobes should be less than 30%
assert np.max(ac) < .3
[docs]
def test_find_spacers(self):
"""Generates a fake signal with 2 spacers and finds them"""
fs = 1000
spacer = Spacer(dt_start=.02, dt_end=.4, n_pulses=8, tup=.05)
start_times = [4.38, 96.58]
template = spacer.generate_template(fs)
signal = np.zeros(int(start_times[-1] * fs + template.size * 2))
for start_time in start_times:
signal[int(start_time * fs): int(start_time * fs) + template.size] = template
spacer_times = spacer.find_spacers(signal, fs=fs)
np.testing.assert_allclose(spacer_times, start_times)
[docs]
class TestSpacersFromFronts(unittest.TestCase):
"""Tests for Spacer.find_spacers_from_fronts"""
[docs]
def setUp(self) -> None:
self.spacer_1 = Spacer()
self.spacer_2 = Spacer(n_pulses=12, tup=.1)
[docs]
def test_empty(self):
"""Test behaviour when a signal is passed in that doesn't contain any spacers."""
# Make some random pulses
times, polarities = self._random_pulses()
fronts = {'times': times, 'polarities': polarities}
self.assertEqual(0, self.spacer_1.find_spacers_from_fronts(fronts).size)
[docs]
def test_noise(self):
"""Test whether it can deal with noise in the spacer signal.
NB: Currently any noise will result in no spacer detection.
"""
Fs = 1000
template = self.spacer_1.generate_template(fs=Fs)
ind, val = self.to_fronts(template)
t0 = 15. # start at 15 seconds
times = ind * 1 / Fs + t0
# Add some noise to the signal
noise_times, noise_p = self._random_pulses(t0, 4, pw=.005, t_max=times[-1] - t0)
fronts = {
'times': np.sort(np.concatenate([times, noise_times])),
'polarities': np.concatenate([val, noise_p])
}
t = self.spacer_1.find_spacers_from_fronts(fronts)
self.assertEqual(0, len(t)) # NB This could one day return 1
np.testing.assert_allclose(t, t0, rtol=1e-3)
@staticmethod
def _random_pulses(t0=0., n=50, pw=None, t_max=200):
if not pw:
times = np.sort(np.random.randint(0, t_max, n) + np.random.rand(n)) + t0
else:
times = np.random.randint(0, t_max, n) + np.random.rand(n) + t0
times = np.sort(np.concatenate([times, times + pw]))
polarities = np.ones_like(times)
polarities[1::2] = -1.
return times, polarities
[docs]
@staticmethod
def to_fronts(signal):
"""
Detects Rising and Falling edges of a voltage signal, returns indices and polarities.
Parameters
----------
signal : numpy.array
Array on which to compute RMS
Returns
-------
numpy.array
Array of indices
numpy.array
Array of polarities {1, -1}
"""
d = np.diff(signal)
ind = np.array(np.where(np.abs(d) >= 1))
sign = d[tuple(ind)]
ind[-1] += 1
return (ind[0], sign) if len(ind) == 1 else (ind, sign)