Documentation
¶
Overview ¶
Package filter implements the FIR/CIC/halfband primitives used by the DSP pipeline. All filters operate on complex64 IQ samples or real float32 signals; coefficients are stored as float32.
Index ¶
- Constants
- func Gaussian(sps, nSymbols int, bt float64) []float32
- func HalfbandLowpass(n int) []float32
- func LowpassKaiser(n int, fc, beta float64) []float32
- func RootRaisedCosine(sps, nSymbols int, alpha float64) []float32
- type Biquad
- type CICDecimator
- type DeEmphasis
- type FIR
- type Halfband2x
- type RealFIR
Constants ¶
const ( DeEmphasis75us = 75 * time.Microsecond // FM broadcast in North America DeEmphasis50us = 50 * time.Microsecond // FM broadcast in Europe / most other regions )
Common pre-emphasis time constants. Pass to NewDeEmphasis with the PCM sample rate the filter will see.
Variables ¶
This section is empty.
Functions ¶
func Gaussian ¶
Gaussian returns the impulse response of a Gaussian pulse-shaping / matched filter parameterised by samples-per-symbol, span (in symbols), and the BT product (3 dB bandwidth × symbol period). BT = 0.3 is the standard GFSK premod for EDACS / GE-Marc; BT = 0.5 is typical for Bluetooth. Taps are normalised to unit DC gain so a sustained NRZ level passes through unchanged at the symbol centre.
func HalfbandLowpass ¶
HalfbandLowpass returns coefficients for a length-N halfband lowpass suitable for ×2 decimation. Roughly half the taps are zero (every other tap except the center). Designed via Kaiser window with cutoff at fs/4.
func LowpassKaiser ¶
LowpassKaiser designs a length-N (odd) lowpass FIR with cutoff fc (normalized; 0.5 = Nyquist) using a Kaiser window with shape beta.
func RootRaisedCosine ¶
RootRaisedCosine returns the impulse response of a root-raised-cosine pulse-shaping filter. sps = samples per symbol, nSymbols = total span, alpha = roll-off (0 < alpha ≤ 1). The filter is normalized to unit energy.
Types ¶
type Biquad ¶ added in v0.5.3
type Biquad struct {
// contains filtered or unexported fields
}
Biquad is a second-order IIR section (the RBJ "Audio EQ Cookbook" biquad) operating on float64 PCM in place. It backs the band-limiting high-pass / low-pass stages of the optional voice enhancement chain (internal/voice/mbe.VoiceEnhancer) — the rival decoders OP25 and Trunk Recorder band-limit their output to the telephone band to trim rumble and quantization hiss, and this is the primitive that lets the enhancer do the same.
The transfer function is the bilinear-transformed analog prototype
H(s) = 1 / (1 + s/Q + s²)
realized as the difference equation
y[n] = b0·x[n] + b1·x[n−1] + b2·x[n−2] − a1·y[n−1] − a2·y[n−2]
with the coefficients normalized so a0 = 1. The section runs as a transposed direct-form II, which keeps the two state words (z1, z2) numerically well-behaved at the low corner frequencies the voice chain uses (a 250 Hz high-pass at 8 kHz).
A Biquad is continuous across Process calls — the caller keeps one per stream and Resets it only on stream re-sync, exactly like mbe.DCBlock. It is NOT safe for concurrent Process calls.
func NewHighPass ¶ added in v0.5.3
NewHighPass returns a Butterworth high-pass biquad with its −3 dB corner at cutoffHz for a stream sampled at sampleRate Hz. cutoffHz is clamped below Nyquist; a non-positive sampleRate or cutoffHz yields a unity pass-through.
func NewLowPass ¶ added in v0.5.3
NewLowPass returns a Butterworth low-pass biquad with its −3 dB corner at cutoffHz for a stream sampled at sampleRate Hz. cutoffHz is clamped below Nyquist; a non-positive sampleRate or cutoffHz yields a unity pass-through.
type CICDecimator ¶
type CICDecimator struct {
// contains filtered or unexported fields
}
CICDecimator is a multi-stage CIC decimator with rate factor R and N stages. Output is at input/R rate. Suitable for an initial coarse decimation before a sharper FIR; CIC has a sin(x)/x droop that should be compensated by a downstream FIR.
func NewCICDecimator ¶
func NewCICDecimator(rate, stages int) *CICDecimator
func (*CICDecimator) Gain ¶
func (c *CICDecimator) Gain() int64
Gain returns the DC gain of the cascade: R^N. Callers typically divide the output by this value (or shift right by ceil(N*log2(R))).
func (*CICDecimator) ProcessReal ¶
func (c *CICDecimator) ProcessReal(dst []int64, src []int64) []int64
ProcessReal decimates a real signal scaled into int16 (caller scales as needed). Returns the decimated samples appended to dst.
type DeEmphasis ¶
type DeEmphasis struct {
// contains filtered or unexported fields
}
DeEmphasis is the single-pole IIR low-pass that recovers the pre-emphasized treble curve broadcast FM transmitters apply for SNR. The transmitter pre-emphasizes (boosts highs) with a single-pole high-shelf characterized by time constant τ; the receiver inverts with the matching low-pass:
H(s) = 1 / (1 + sτ)
Discretized impulse-invariant at sample rate fs, the difference equation is:
α = exp(-1 / (τ × fs)) y[n] = (1 - α) × x[n] + α × y[n-1]
The DC gain is unity, the −3 dB cutoff is fc = 1 / (2π τ).
Use NewDeEmphasisUS for the 75 µs constant standard in NA, or NewDeEmphasisEU for the 50 µs constant used in most of the world.
DeEmphasis is *not* safe for concurrent Process calls — pin it to a single demod goroutine and Reset between calls.
func NewDeEmphasis ¶
func NewDeEmphasis(tau time.Duration, sampleRate float64) *DeEmphasis
NewDeEmphasis builds a de-emphasis filter tuned to time constant τ at the given sample rate. Both must be positive; the constructor panics otherwise so misconfiguration trips at startup rather than silently producing wrong audio.
func NewDeEmphasisUS ¶
func NewDeEmphasisUS(sampleRate float64) *DeEmphasis
NewDeEmphasisUS is shorthand for NewDeEmphasis(DeEmphasis75us, sampleRate).
func (*DeEmphasis) Process ¶
func (d *DeEmphasis) Process(dst, src []float32) []float32
Process applies the filter to src and writes the result to dst (or appends to it). dst is reused if it has enough capacity. In-place operation (dst == src) is supported.
func (*DeEmphasis) Reset ¶
func (d *DeEmphasis) Reset()
Reset clears the filter's running state. Call between calls so stale audio from one transmission doesn't bleed into the next.
type FIR ¶
type FIR struct {
// contains filtered or unexported fields
}
FIR is a linear-phase finite-impulse-response filter for complex64 IQ. It maintains an internal sample history so consecutive Process calls produce a continuous output stream.
type Halfband2x ¶
type Halfband2x struct {
// contains filtered or unexported fields
}
Halfband2x decimates by 2 using a halfband FIR. Internally it just routes the decimated stream out of a regular FIR; we keep this as a convenience because we know half the taps multiply by zero and could be skipped in a SIMD pass later.
func NewHalfband2x ¶
func NewHalfband2x(taps []float32) *Halfband2x
func (*Halfband2x) Process ¶
func (h *Halfband2x) Process(dst, src []complex64) []complex64
type RealFIR ¶
type RealFIR struct {
// contains filtered or unexported fields
}
RealFIR is the real-valued counterpart of FIR. Same circular-buffer convolution shape, but operates on float32 audio samples instead of complex IQ. Sized for the post-demod chain in internal/voice/composer where the FM demod hands real audio to a band-limiting LPF before the second decimation to PCM.
Like FIR, it isn't safe for concurrent Process calls — pin it to a single demod goroutine and Reset between calls.
func NewRealFIR ¶
NewRealFIR copies taps into a new filter and allocates the matching history ring. The constructor panics on an empty tap slice so a misconfiguration trips at startup.