filter

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

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

View Source
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

func Gaussian(sps, nSymbols int, bt float64) []float32

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

func HalfbandLowpass(n int) []float32

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

func LowpassKaiser(n int, fc, beta float64) []float32

LowpassKaiser designs a length-N (odd) lowpass FIR with cutoff fc (normalized; 0.5 = Nyquist) using a Kaiser window with shape beta.

func RootRaisedCosine

func RootRaisedCosine(sps, nSymbols int, alpha float64) []float32

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

func NewHighPass(sampleRate, cutoffHz float64) *Biquad

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

func NewLowPass(sampleRate, cutoffHz float64) *Biquad

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.

func (*Biquad) Process added in v0.5.3

func (f *Biquad) Process(pcm []float64)

Process applies the section in place over pcm, carrying state across calls so successive frames filter continuously. A nil receiver is a no-op so callers can hold an optional *Biquad and invoke it unconditionally.

func (*Biquad) Reset added in v0.5.3

func (f *Biquad) Reset()

Reset clears the section's state words so the next sample starts from a quiescent filter. Call on stream re-sync alongside the rest of the decode state. A nil receiver is a no-op.

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.

func NewFIR

func NewFIR(taps []float32) *FIR

func (*FIR) Process

func (f *FIR) Process(dst, src []complex64) []complex64

Process consumes one input slice and returns an output slice of the same length. dst is reused if it has enough capacity.

func (*FIR) Reset

func (f *FIR) Reset()

Reset clears the internal history.

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

func NewRealFIR(taps []float32) *RealFIR

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.

func (*RealFIR) Process

func (f *RealFIR) Process(dst, src []float32) []float32

Process consumes one input slice and returns an output slice of the same length. dst is reused if it has enough capacity. In-place operation (dst == src) is supported.

func (*RealFIR) Reset

func (f *RealFIR) Reset()

Reset clears the internal history so the next Process starts fresh.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL