Add a causal half-Gaussian option to the Gaussian smoothing filter - #229
Merged
Conversation
FilterByDesignTransformer applies the kernel causally via lfilter, so the
acausal half of the symmetric Gaussian is pure group delay: (n_taps - 1) / 2,
or width * sigma samples. At 100 Hz a 50 ms sigma costs 200 ms of lag, which
is a large fraction of a closed-loop control budget.
`causal=True` keeps only the causal half of the same kernel -- peak at lag 0,
tail extending only into the past -- renormalized to unit sum. Its group delay
is the half-Gaussian centroid, sigma * sqrt(2 / pi) ~= 0.8 * sigma.
The two modes are not interchangeable at equal sigma; compare them at matched
white-noise variance reduction, sum(b ** 2). On that footing (100 Hz):
noise gain symmetric causal
0.141 sigma=20ms, 80ms sigma=38ms, 27ms
0.056 sigma=50ms, 200ms sigma=98ms, 75ms
The default is False, preserving the existing symmetric kernel. kernel_size
still overrides the automatic length and still warns when undersized; in
causal mode it counts causal taps and is checked against the one-sided
recommended length, width * sigma + 1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
FilterByDesignTransformerapplies the Gaussian kernel causally vialfilter, so the acausal half of the symmetric kernel becomes pure group delay —(n_taps - 1) / 2, i.e.width * sigmasamples. For a real-time BCI feature stream at 100 Hz, a 50 ms sigma costs 200 ms of lag, a large fraction of a closed-loop control budget.What
Adds
causal: bool = FalsetoGaussianSmoothingSettings(and togaussian_smoothing_filter_design). WhenTrue, the design isgaussian(2*K - 1, std=sigma)[K-1:]withK = int(width*sigma + 1), normalized to unit sum: the peak sits at lag 0 and the tail extends only into the past. Group delay becomes the half-Gaussian centroid,sigma * sqrt(2/pi)≈0.8 * sigma.The default of
Falsepreserves current behavior exactly.The two modes are not interchangeable at equal sigma — halving the kernel also halves the effective averaging window. They should be compared at matched white-noise variance reduction,
sum(b**2), not at matched sigma. Measured at 100 Hz:sigmastays in seconds and is scaled byfsindesign_wrapper, unchanged. Thekernel_sizeoverride and the too-small-kernel warning both apply to the causal branch, wherekernel_sizecounts causal taps and is checked against the one-sided recommended lengthwidth * sigma + 1.The settings docstring documents the group delay of each mode and notes that the causal kernel's stopband rolls off less steeply for a given sigma.
Tests
11 new tests (63 pass in the file, 71 with
test_filter.py):argmax == 0, is strictly decreasing, and equals the renormalized half of the symmetric kernel of the same sigmascipy.signal.group_delaymatchessigma * sqrt(2/pi)within a tap(n_taps - 1) / 2sum(b**2)at less than half the delaykernel_sizeoverride and undersized/identity warnings in causal modefsscaling still holdsNote for reviewers
Discrete truncation biases the causal group delay low relative to
sigma*sqrt(2/pi)by a near-constant ~1/π tap (7.51 vs 7.82 at σ=9.8), because then=0tap gets full weight where the continuous centroid integral gives it half. It's in the favorable direction, but it's why the group-delay test usesabs=1.0rather than something tighter.