Noise filters for LabJack readings, applied where data is read (never to the
archive). The same pipeline, with the same arithmetic, is implemented in Rust in
rust-ljm/src/filters.rs for exports; shared test vectors in
testdata/filter-vectors.json check that both give the same output.
Order, per sample:
Despike (raw volts): grey opening, a running minimum then a running maximum over
w = ceil(2.5 ms × fs) samples. It removes upward spikes narrower than w and
keeps wider pulses and anything downward. Output sample j needs input up to
j + w - 1, so this stage delays its output by w - 1 samples.
Remove the 10 Hz square wave (raw volts): subtract a template of one period,
fs / 10 samples, indexed by the sample count since the segment started (the
source runs on the LabJack clock). Each phase bin holds a running mean of the
past samples in that bin (cumulative at first, then exponential with a 30 s
memory); the template is the bin mean minus the running mean of all samples, so
the signal level is kept. The template is read before the current sample updates it, so the
stage is causal and adds no delay.
Remove the 11.906 Hz square wave the same way (it runs beside stage 2: each
template learns from the sample minus the other's current template). Its period is not a whole number
of samples, so samples go to phase bins by their fractional phase, with four bins
per sample of period (at least 64); a bin not yet reached uses the mean of its
group of four.
High-pass, then low-pass: 2nd-order Butterworth biquads (bilinear transform with
prewarping). Causal here; exports run them forward and backward.
NaN samples pass through as NaN; the filters hold their state over them and the
sample count still advances. A caller breaks the segment on a long gap
(RawFilterChain.breakSegment) or resets on a new run.
Noise filters for LabJack readings, applied where data is read (never to the archive). The same pipeline, with the same arithmetic, is implemented in Rust in
rust-ljm/src/filters.rsfor exports; shared test vectors intestdata/filter-vectors.jsoncheck that both give the same output.Order, per sample:
w = ceil(2.5 ms × fs)samples. It removes upward spikes narrower thanwand keeps wider pulses and anything downward. Output samplejneeds input up toj + w - 1, so this stage delays its output byw - 1samples.fs / 10samples, indexed by the sample count since the segment started (the source runs on the LabJack clock). Each phase bin holds a running mean of the past samples in that bin (cumulative at first, then exponential with a 30 s memory); the template is the bin mean minus the running mean of all samples, so the signal level is kept. The template is read before the current sample updates it, so the stage is causal and adds no delay.NaNsamples pass through asNaN; the filters hold their state over them and the sample count still advances. A caller breaks the segment on a long gap (RawFilterChain.breakSegment) or resets on a new run.