diff --git a/neurodsp/sim/periodic.py b/neurodsp/sim/periodic.py index 92e7d2d5..c94a51ed 100644 --- a/neurodsp/sim/periodic.py +++ b/neurodsp/sim/periodic.py @@ -38,6 +38,13 @@ def sim_oscillation(n_seconds, fs, freq, cycle='sine', phase=0, **cycle_params): sig : 1d array Simulated oscillation. + Notes + ----- + Depending on the requested frequency (`freq`), sampling rate (`fs`), and signal + length (`n_seconds`), the simulated signal may have a non-integer number of cycles. + If so, the frequency-domain representation of the signal will have some power in non-simulated + frequencies. To avoid this, choose `n_seconds` and `fs` to create an integer number of cycles. + Examples -------- Simulate a continuous sinusoidal oscillation at 5 Hz: diff --git a/neurodsp/sim/utils.py b/neurodsp/sim/utils.py new file mode 100644 index 00000000..c422bae7 --- /dev/null +++ b/neurodsp/sim/utils.py @@ -0,0 +1,30 @@ +"""Utility functions for simulations.""" + +################################################################################################### +################################################################################################### + +def check_osc_def(n_seconds, fs, freq): + """Check whether a requested oscillation definition will have an integer number of cycles. + + Parameters + ---------- + n_seconds : float + Simulation time, in seconds. + fs : float + Signal sampling rate, in Hz. + freq : float + Oscillation frequency. + + Returns + ------- + bool + Whether the definition will have an integer number of cycles. + """ + + # Sampling rate check: check if the number of samples per cycle is an integer + srate_check = (fs/freq).is_integer() + + # Time check: check if signal length matches an integer number of cycles + time_check = (n_seconds * freq).is_integer() + + return srate_check and time_check diff --git a/neurodsp/tests/sim/test_utils.py b/neurodsp/tests/sim/test_utils.py new file mode 100644 index 00000000..bf04871b --- /dev/null +++ b/neurodsp/tests/sim/test_utils.py @@ -0,0 +1,24 @@ +"""Tests for neurodsp.sim.utils.""" + +from neurodsp.sim.utils import * + +################################################################################################### +################################################################################################### + +def test_check_osc_def(): + + n_seconds = 1.0 + fs = 100 + freq = 10 + + # Check definition that should pass + assert check_osc_def(n_seconds, fs, freq) + + # Check a definition that should fail the sampling rate check + assert not check_osc_def(1.05, fs, freq) + + # Check a definition that should fail the time check + assert not check_osc_def(n_seconds, 1111, freq) + + # Check a definition that should fail both checks + assert not check_osc_def(1.05, 1111, freq)