Skip to content

[FEATURE] add harmonic_distortion for sweep measurements - #57

Open
aguirreSL wants to merge 1 commit into
PyTTaMaster:developmentfrom
aguirreSL:dev-harmonic_distortion
Open

aguirreSL wants to merge 1 commit into
PyTTaMaster:developmentfrom
aguirreSL:dev-harmonic_distortion

Conversation

@aguirreSL

@aguirreSL aguirreSL commented Oct 31, 2026 •

Copy link
Copy Markdown

Adds pytta.harmonic_distortion(ir, sweep, numHarmonics=5), which takes the impulse response of a system measured with an exponential sweep and returns the impulse responses of its harmonics and the total harmonic distortion (THD) over frequency.

Deconvolution by an exponential sweep puts the harmonic of order k L ln(k) seconds before the linear response, where L is the time the sweep takes to multiply its frequency by e (Farina, AES 108th Convention, 2000). In a circular deconvolution the harmonics wrap to the end of the response, where the function cuts them. All harmonics use the same window, whose length is the gap between the two highest orders, so no window overlaps the next harmonic. The window starts 10 % of its length before the harmonic and has a Tukey taper of 10 % at each end. The THD at an excitation frequency f is sqrt(sum |H_k(k f)|^2 for k >= 2) / |H_1(f)|.

The sweep is given either by its rate in octaves per second or as the sweep signal itself. In that case the rate is read from the slope of its group delay, computed as Re{FFT(n x) / FFT(x)} so that no phase unwrapping is needed.

The tests in tests/test_harmonic_distortion.py drive a memoryless system y = x + a x^2 + b x^3 with a sweep of amplitude A. Its harmonics are known (1 + 3 b A^2 / 4, a A / 2 and b A^2 / 4), and the function finds the three of them within 5e-5 (relative), with the THD within 0.6 % from 100 Hz to 2 kHz. A linear resonance gives a THD below 6e-9, which checks that the linear response does not leak into the windows of the harmonics. The rate read from the sweep gives the same result as the rate given by number.

This answers the THD part of #45 for sweep measurements.

Checked with Python 3.12, numpy 1.26 and scipy 1.17. The existing tests behave as on development: the 7 failures there (3 in test_h5io.py, 4 in test_pyttasave.py) are the same with and without this change.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

From the impulse response of a system measured with an exponential
sweep, cut the impulse responses of the harmonics, which deconvolution
puts L ln(k) seconds before the linear response (Farina, 2000), and
compute the total harmonic distortion over frequency. All harmonics use
the same window, the gap between the two highest orders, so none
overlaps the next. The sweep is given by its rate in octaves per second
or as the sweep itself, whose rate is read from its group delay.

Tests pin the harmonic levels and THD of a memoryless polynomial system,
whose values are known, the absence of distortion for a linear
resonance, and the rate read from the sweep.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant