Stimulation signals

OSS-DBSv2 separates the stimulation waveform from the geometric and material model. This makes it possible to study how different pulse definitions affect the frequency-domain solve and the reconstructed time-domain results.

Signal concepts

The code distinguishes between:

  • time-domain signal definitions such as rectangular, trapezoidal, or triangular pulses

  • a frequency-domain representation used by the volume conductor solver

This separation is useful because the solver can evaluate the field at selected frequencies and then reconstruct the signal in time when needed.

Supported signal types

Time-domain pulse signals

These signals define a periodic waveform that is Fourier-transformed for the frequency-domain FEM solve and optionally reconstructed in the time domain afterwards.

  • Rectangle ("Type": "Rectangle") — standard rectangular DBS pulse. The most common and best-tested signal type.

  • Trapezoid ("Type": "Trapezoid") — trapezoidal pulse with a finite rise time controlled by PulseTopWidth[us].

  • Triangle ("Type": "Triangle") — triangular pulse (trapezoid with zero top width).

Common parameters for all time-domain signals:

  • Frequency[Hz] — stimulation repetition rate (e.g. 130 Hz for standard DBS)

  • PulseWidth[us] — duration of the primary pulse phase

  • CounterPulseWidth[us] — duration of the charge-balancing counter pulse (0 to omit)

  • InterPulseWidth[us] — gap between primary and counter pulse

  • CounterAmplitude — amplitude of the counter pulse relative to the primary pulse (default 1.0)

Multisine

The multisine mode ("Type": "Multisine") bypasses waveform generation entirely. Instead, a list of discrete frequencies is solved with unit amplitude at each:

"StimulationSignal": {
  "Type": "Multisine",
  "ListOfFrequencies": [130.0, 1000.0, 10000.0],
  "CurrentControlled": false
}

This is useful for impedance spectroscopy, single-frequency studies, or when the frequency content is known in advance. No time-domain reconstruction is performed.

Frequency-domain settings

SpectrumMode

For time-domain signals, SpectrumMode controls how many frequencies are actually solved:

  • "FullSpectrum" (default) — solves at every harmonic up to the cutoff frequency. Accurate but expensive for high cutoff frequencies.

  • "OctaveBand" — solves only at octave-band centre frequencies and interpolates. Much faster with minimal loss of accuracy for typical DBS pulses.

CutoffFrequency

CutoffFrequency (default: 1e6 Hz) sets the upper limit of the Fourier spectrum. Harmonics above this frequency are discarded. For most DBS applications, 0.5–1 MHz is sufficient. Lower values reduce the number of FEM solves.

CurrentControlled

CurrentControlled selects between voltage-controlled and current-controlled stimulation modes. See Stimulation modes in the volume conductor documentation for a detailed description of all supported cases.

API reference

Signal classes

class ossdbs.stimulation_signals.rectangle_signal.RectangleSignal(frequency: float, pulse_width: float, inter_pulse_width: float | None = 0.0, counter_pulse_width: float | None = 0.0, counter_pulse_amplitude: float | None = 1.0)[source]

Bases: TimeDomainSignal

Represents a rectangular signal.

Parameters:
  • frequency (float) – Frequency [Hz] of the signal.

  • pulse_width (float) – Relative pulse width of one period.

  • counter_pulse_width (float) – Relative width of counter pulse of one period.

  • inter_pulse_width (float) – Relative width between pulse and counter pulse of one period.

get_time_domain_signal(dt: float, timesteps: int) ndarray[source]

Build time domain signal.

Parameters:
  • dt (float) – Time difference of the signal.

  • timesteps (int) – Number of steps in the signal.

class ossdbs.stimulation_signals.trapezoid_signal.TrapezoidSignal(frequency: float, pulse_width: float, inter_pulse_width: float, top_width: float, counter_pulse_width: float | None = None, counter_pulse_amplitude: float | None = 1.0)[source]

Bases: TimeDomainSignal

Represents trapezoid signal.

Parameters:
  • frequency (float) – Frequency [Hz] of the signal.

  • pulse_width (float) – Relative pulse width of one period.

  • top_width (float) – Relative top width of trapzoid signal.

  • counter_pulse_width (float) – Relative width of counter pulse of one period.

  • inter_pulse_width (float) – Relative width between pulse and counter pulse of one period.

get_active_time() float[source]

Return time during which the stimulator is active.

get_time_domain_signal(dt: float, timesteps: int) ndarray[source]

Build time domain signal.

Parameters:
  • dt (float) – Time difference of the signal.

  • timesteps (int) – Number of steps in the signal.

class ossdbs.stimulation_signals.triangle_signal.TriangleSignal(frequency: float, pulse_width: float, inter_pulse_width: float | None = 0.0, counter_pulse_width: float | None = 0.0, counter_pulse_amplitude: float | None = 1.0)[source]

Bases: TimeDomainSignal

Represents a triangular signal.

Parameters:
  • frequency (float) – Frequency [Hz] of the signal.

  • pulse_width (float) – Relative pulse width of one period.

  • counter_pulse_width (float) – Relative width of counter pulse of one period.

  • inter_pulse_width (float) – Relative width between pulse and counter pulse of one period.

get_time_domain_signal(dt: float, timesteps: int) ndarray[source]

Build time domain signal.

Parameters:
  • dt (float) – Time difference of the signal.

  • timesteps (int) – Number of steps in the signal.

Base classes

class ossdbs.stimulation_signals.signal.FrequencyDomainSignal(frequencies: ndarray, amplitudes: ndarray, current_controlled: bool, base_frequency: float, cutoff_frequency: float, signal_length: int, octave_band_approximation: bool = False)[source]

Bases: object

Store information for freqency domain signal.

amplitudes: ndarray
base_frequency: float
current_controlled: bool
cutoff_frequency: float
frequencies: ndarray
octave_band_approximation: bool = False
signal_length: int
class ossdbs.stimulation_signals.signal.TimeDomainSignal(frequency: float, pulse_width: float, inter_pulse_width: float | None = 0.0, counter_pulse_width: float | None = 0.0, counter_pulse_amplitude: float | None = 1.0)[source]

Bases: ABC

Template for Signals.

Parameters:
  • frequency (float) – Frequency [Hz] of the signal.

  • pulse_width (float) – Relative pulse width of one period.

  • counter_pulse_width (float) – Relative width of counter pulse of one period.

  • inter_pulse_width (float) – Relative width between pulse and counter pulse of one period.

Notes

Amplitudes are relative: the primary pulse has amplitude 1.0 and the counter pulse amplitude is given as a fraction (e.g. 0.5 means half the primary amplitude). The actual voltage or current scaling is applied externally by the volume conductor model.

property amplitude: float

Return signal amplitude.

property counter_amplitude: float

Get amplitude of counterpulse.

property frequency: float

Return frequency of signal.

Return type:

float

get_active_time() float[source]

Return time during which the stimulator is active.

get_adjusted_cutoff_frequency(cutoff_frequency: float) float[source]

Adjust cutoff frequency to signal frequency.

Double the cutoff frequency to account for FFT and actually sample until there.

get_fft_spectrum(cutoff_frequency: float) tuple[numpy.ndarray, numpy.ndarray, int][source]

FFT spectrum of time-domain signal.

Parameters:

cutoff_frequency (float) – Highest considered frequency.

abstract get_time_domain_signal(dt: float, timesteps: int) ndarray[source]

Time-domain signal for given timestep.

plot_time_domain_signal(cutoff_frequency, output_path, show=False)[source]

Plot signal and export to PDF.

retrieve_time_domain_signal(fft_signal: ndarray, cutoff_frequency: float, signal_length: int) tuple[numpy.ndarray, numpy.ndarray][source]

Compute time-domain signal by FFT.

Helper functions

ossdbs.stimulation_signals.utilities.adjust_cutoff_frequency(cutoff_frequency, frequency)[source]

Function to make cutoff frequency multiple of stimulation frequency.

ossdbs.stimulation_signals.utilities.get_indices_in_octave_band(freq_idx: int, frequency_indices: list, cutoff_frequency_index: int) list | ndarray[source]

Get indices of frequencies in octave band.

Notes

We start evaluating from the bottom. I.e., it is checked if there is an overlap with frequencies from the octave band below (already computed). The minimum frequencies are increased until there is no overlap with the previous band.

ossdbs.stimulation_signals.utilities.get_maximum_octave_band_index(freq_idx: int) int[source]

Get index of highest frequency in octave band.

ossdbs.stimulation_signals.utilities.get_minimum_octave_band_index(freq_idx: int) int[source]

Get index of lowest frequency in octave band.

ossdbs.stimulation_signals.utilities.get_octave_band_indices(frequencies: ndarray) ndarray[source]

Return indices of octave band frequencies.

ossdbs.stimulation_signals.utilities.get_timesteps(cutoff_frequency: float, base_frequency: float, n_frequencies: int) ndarray[source]

Return list with timesteps.

ossdbs.stimulation_signals.utilities.reconstruct_time_signals(freq_domain_signal: ndarray, signal_length: int) ndarray[source]

Compute time signals from frequency-domain data.

Parameters:
  • freq_domain_signal (np.ndarray) – Frequency-domain signal to be transformed

  • signal_length (int) – Length of initial time-domain signal

ossdbs.stimulation_signals.utilities.retrieve_time_domain_signal_from_fft(fft_signal: ndarray, cutoff_frequency: float, base_frequency: float, signal_length: int) tuple[numpy.ndarray, numpy.ndarray][source]

Compute time-domain signal via fft.

Parameters:
  • fft_signal (np.ndarray) – Frequency-domain signal

  • signal_length (int) – Length of original time-domain signal

  • cutoff_frequency (float) – Highest considered frequency

  • base_frequency (float) – Frequency of time-domain signal (often 130 Hz)