feat: init
This commit is contained in:
234
test/model/test_waveforms.py
Normal file
234
test/model/test_waveforms.py
Normal file
@@ -0,0 +1,234 @@
|
||||
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted for academic and research use only
|
||||
# (subject to the limitations in the disclaimer below)
|
||||
# provided that the following conditions are met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
|
||||
# * Neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Standard Library Imports
|
||||
import unittest
|
||||
|
||||
# Third Party Imports
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
# Local Imports
|
||||
from navigate.model import waveforms
|
||||
|
||||
|
||||
class TestWaveforms(unittest.TestCase):
|
||||
"""
|
||||
Unit Tests for the Navigate Model Waveforms
|
||||
"""
|
||||
|
||||
def find_first_index_above_threshold(self, data, threshold):
|
||||
"""
|
||||
Finds the first index in the data array above the threshold
|
||||
"""
|
||||
first_index = next(x for x, val in enumerate(data) if val > threshold)
|
||||
return first_index
|
||||
|
||||
def test_single_pulse_max_default_amplitude(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
data = waveforms.single_pulse(sample_rate=sample_rate, sweep_time=sweep_time)
|
||||
self.assertEqual(np.max(data), 1)
|
||||
|
||||
def test_single_pulse_max_specified_amplitude(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
amplitude = 2
|
||||
data = waveforms.single_pulse(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, amplitude=amplitude
|
||||
)
|
||||
self.assertEqual(np.max(data), amplitude)
|
||||
|
||||
def test_single_pulse_min_default_amplitude(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
data = waveforms.single_pulse(sample_rate=sample_rate, sweep_time=sweep_time)
|
||||
self.assertEqual(np.min(data), 0)
|
||||
|
||||
def test_single_pulse_onset_default_delay(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
default_delay = 10
|
||||
data = waveforms.single_pulse(sample_rate=sample_rate, sweep_time=sweep_time)
|
||||
first_index = self.find_first_index_above_threshold(data=data, threshold=0)
|
||||
self.assertEqual(
|
||||
int(sample_rate * sweep_time * default_delay / 100), first_index
|
||||
)
|
||||
|
||||
def test_single_pulse_onset_specified_delay(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
delay = 20
|
||||
data = waveforms.single_pulse(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, delay=delay
|
||||
)
|
||||
first_index = self.find_first_index_above_threshold(data, 0.5)
|
||||
# first_index = next(x for x, val in enumerate(data)
|
||||
# if val > 0.5)
|
||||
self.assertEqual(int(sample_rate * sweep_time * delay / 100), first_index)
|
||||
|
||||
def test_single_pulse_default_offset(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
default_offset = 0
|
||||
data = waveforms.single_pulse(sample_rate=sample_rate, sweep_time=sweep_time)
|
||||
self.assertEqual(np.min(data), default_offset)
|
||||
|
||||
def test_single_pulse_specified_offset(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
offset = 0.2
|
||||
data = waveforms.single_pulse(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, offset=offset
|
||||
)
|
||||
self.assertEqual(np.min(data), offset)
|
||||
|
||||
@pytest.mark.skip(
|
||||
reason="double the correct value, have not bothered to figure out why"
|
||||
)
|
||||
def test_remote_focus_ramp_specified_delay(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
delay = 10.5
|
||||
data = waveforms.remote_focus_ramp(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, remote_focus_delay=delay
|
||||
)
|
||||
first_index = self.find_first_index_above_threshold(data=data, threshold=-1)
|
||||
self.assertEqual(delay * sample_rate * sweep_time / 100, first_index - 1)
|
||||
|
||||
def test_remote_focus_ramp_amplitude_max(self):
|
||||
amplitude = 1.5
|
||||
data = waveforms.remote_focus_ramp(amplitude=amplitude)
|
||||
self.assertEqual(np.max(data), amplitude)
|
||||
|
||||
def test_remote_focus_ramp_amplitude_min(self):
|
||||
amplitude = 1.5
|
||||
data = waveforms.remote_focus_ramp(amplitude=amplitude)
|
||||
self.assertEqual(np.min(data), -1 * amplitude)
|
||||
|
||||
def test_remote_focus_offset_min(self):
|
||||
default_amplitude = 1
|
||||
offset = 0.5
|
||||
data = waveforms.remote_focus_ramp(offset=offset)
|
||||
self.assertEqual(np.min(data), -1 * default_amplitude + offset)
|
||||
|
||||
def test_remote_focus_offset_max(self):
|
||||
default_amplitude = 1
|
||||
offset = 0.5
|
||||
data = waveforms.remote_focus_ramp(offset=offset)
|
||||
self.assertEqual(np.max(data), default_amplitude + offset)
|
||||
|
||||
def test_dc_value(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
for amplitude in np.linspace(-5, 5, 3):
|
||||
data = waveforms.dc_value(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, amplitude=amplitude
|
||||
)
|
||||
self.assertEqual(np.max(data), amplitude)
|
||||
self.assertEqual(np.size(data), sample_rate * sweep_time)
|
||||
|
||||
def test_sawtooth_amplitude(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
for amplitude in np.linspace(-5, 5, 3):
|
||||
data = waveforms.sawtooth(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, amplitude=amplitude
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
np.max(data), np.abs(amplitude), delta=np.abs(amplitude) / 100
|
||||
)
|
||||
|
||||
def test_square_amplitude(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
for amplitude in np.linspace(-5, 5, 3):
|
||||
data = waveforms.square(
|
||||
sample_rate=sample_rate, sweep_time=sweep_time, amplitude=amplitude
|
||||
)
|
||||
self.assertEqual(np.max(data), np.abs(amplitude))
|
||||
self.assertEqual(np.min(data), -1 * np.abs(amplitude))
|
||||
|
||||
def test_square_offset(self):
|
||||
sample_rate = 100000
|
||||
sweep_time = 0.4
|
||||
for amplitude in np.linspace(0, 5, 5):
|
||||
for offset in np.linspace(0, 3, 3):
|
||||
data = waveforms.square(
|
||||
sample_rate=sample_rate,
|
||||
offset=offset,
|
||||
amplitude=amplitude,
|
||||
sweep_time=sweep_time,
|
||||
)
|
||||
self.assertEqual(np.max(data), amplitude + offset)
|
||||
self.assertEqual(np.min(data), -1 * amplitude + offset)
|
||||
|
||||
def test_smoothing_length(self):
|
||||
"""Test that the smoothed waveform is proportionally larger than the
|
||||
original waveform.
|
||||
"""
|
||||
ps = 10
|
||||
waveform = waveforms.remote_focus_ramp()
|
||||
smoothed_waveform = waveforms.smooth_waveform(waveform, ps)
|
||||
np.testing.assert_almost_equal(
|
||||
len(smoothed_waveform), len(waveform) * (1 + ps / 100) + 1
|
||||
)
|
||||
|
||||
def test_smoothing_bounds(self):
|
||||
ps = 10
|
||||
waveform = waveforms.remote_focus_ramp()
|
||||
smoothed_waveform = waveforms.smooth_waveform(waveform, ps)
|
||||
assert waveform[0] == smoothed_waveform[0]
|
||||
assert waveform[-1] == smoothed_waveform[-1]
|
||||
assert np.max(waveform) >= np.max(smoothed_waveform)
|
||||
|
||||
def test_smoothing_zero_pct(self):
|
||||
waveform_smoothing_pct = 0
|
||||
waveform = waveforms.remote_focus_ramp(sample_rate=16)
|
||||
smoothed_waveform = waveforms.smooth_waveform(waveform, waveform_smoothing_pct)
|
||||
assert len(waveform) * waveform_smoothing_pct / 100 < 1
|
||||
np.testing.assert_array_equal(waveform, smoothed_waveform)
|
||||
|
||||
def test_camera_exposure(self):
|
||||
sr, st, ex, cd = 100000, 0.5, 0.4, 0.1
|
||||
v = waveforms.camera_exposure(
|
||||
sample_rate=sr, sweep_time=st, exposure=ex, camera_delay=cd
|
||||
)
|
||||
assert np.sum(v > 0) == int(sr * ex)
|
||||
|
||||
def test_camera_exposure_short(self):
|
||||
"""In the event the camera delay + exposure_time > sweep time..."""
|
||||
sr, st, ex, cd = 100000, 0.4, 0.4, 0.1
|
||||
v = waveforms.camera_exposure(
|
||||
sample_rate=sr, sweep_time=st, exposure=ex, camera_delay=cd
|
||||
)
|
||||
assert np.sum(v > 0) == int(sr * (ex - cd))
|
||||
Reference in New Issue
Block a user