dsp

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: 6 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EstimateCarrierOffsetHz added in v0.2.8

func EstimateCarrierOffsetHz(iq []complex64, sampleRateHz, searchHz float64) float64

EstimateCarrierOffsetHz finds the dominant narrowband carrier offset in a complex baseband capture: the frequency (in Hz, positive = above centre) carrying the most averaged power within ±searchHz of DC. It is the single-carrier wrapper over EstimateCarrierCandidatesHz, preserving the original two-stage coarse+fine behaviour. Returns 0 for an empty input or zero searchHz.

func Mag2dB added in v0.4.3

func Mag2dB(mag float64) float64

Mag2dB converts a linear amplitude (magnitude) ratio to decibels (20·log10). Use it for an RMS or voltage figure; use Power2dB for a power figure. Values at or below zero clamp to -300 dB.

func Power2dB added in v0.4.3

func Power2dB(power float64) float64

Power2dB converts a linear power ratio to decibels (10·log10). Values at or below zero clamp to a floor of -300 dB rather than returning -Inf/NaN.

func RMS added in v0.4.3

func RMS(iq []complex64) float64

RMS returns the root-mean-square amplitude of a complex baseband buffer: sqrt(mean(|x|^2)). For a full-scale complex sinusoid (|x| = 1) this is 1.0, so RMS in dBFS is 20·log10(RMS) (0 dBFS at full scale). It is the numpy `sqrt(mean(abs(x)**2))` capture-level power measure that no exported helper provided. Returns 0 for an empty buffer.

Types

type AGC

type AGC struct {
	Reference float32 // target output magnitude (default 1.0)
	Rate      float32 // power-EMA coefficient (typical 1e-3 to 1e-2)
	MaxGain   float32 // ceiling that bounds the gain on near-silent input
	// contains filtered or unexported fields
}

AGC normalises the average magnitude of a complex IQ stream toward Reference. It tracks signal power with an exponential moving average and applies that as a feed-forward scale, so an occasional near-zero sample — a linear-modulation symbol stream passes through the origin on π phase transitions — cannot spike the gain the way a per-sample feedback loop would.

func NewAGC

func NewAGC(reference, rate, maxGain float32) *AGC

func (*AGC) Gain

func (a *AGC) Gain() float32

Gain reports the scale Process would currently apply.

func (*AGC) Process

func (a *AGC) Process(dst, src []complex64) []complex64

func (*AGC) Reset added in v0.1.8

func (a *AGC) Reset()

Reset clears the tracked power so a stream re-sync or re-tune does not carry a stale gain into the new signal.

type AudioAGC

type AudioAGC struct {
	// contains filtered or unexported fields
}

AudioAGC is the real-valued counterpart of AGC, sized for the post-demod chain in internal/voice/composer. The IQ-domain AGC drives the *complex* baseband magnitude toward a target with a single adaptation rate, which works because FM has a constant envelope on air. After demod the signal is voice — bursty, with short loud transients separated by quieter passages — so a single rate either pumps badly (too fast) or never catches up (too slow).

AudioAGC is a classic envelope-follower + gain stage:

abs   = |x|
if abs > level: level += (1 - α_attack) × (abs - level)   // ramp up fast
else:           level += (1 - α_release) × (abs - level)  // ramp down slow
gain  = clamp(reference / max(level, floor), 0, MaxGain)
y[n]  = x[n] × gain

Attack / release are time constants (in samples after construction); short attack catches transients before they clip, long release keeps the gain steady through speech gaps so the output doesn't pump. Reference is the target |y| ≈ Reference; pick something that leaves headroom for downstream stages (typical 0.3 keeps int16 conversion comfortably under MaxInt16 at the existing 10 000-scale in voice/composer).

AudioAGC is not safe for concurrent Process calls — pin it to a single demod goroutine and Reset between calls.

func NewAudioAGC

func NewAudioAGC(cfg AudioAGCConfig) *AudioAGC

NewAudioAGC builds an envelope-follower-based AGC. Bad parameters trip a panic at startup so misconfiguration shows up loudly rather than silently producing wrong audio.

func (*AudioAGC) Gain

func (a *AudioAGC) Gain() float32

Gain returns the current adaptive gain. Useful for diagnostics; not needed for normal operation.

func (*AudioAGC) Process

func (a *AudioAGC) Process(dst, src []float32) []float32

Process applies the AGC to src and writes to dst (or appends to it). dst is reused if it has enough capacity; in-place src == dst is supported.

func (*AudioAGC) Reset

func (a *AudioAGC) Reset()

Reset clears the running envelope estimate so the next Process call starts from silence (gain begins at MaxGain).

type AudioAGCConfig

type AudioAGCConfig struct {
	Reference  float32       // target |output| (default 0.3)
	Attack     time.Duration // ramp-up time constant (default 5 ms)
	Release    time.Duration // ramp-down time constant (default 200 ms)
	MaxGain    float32       // ceiling on adaptive gain (default 64.0)
	SampleRate float64       // sample rate of the audio stream (Hz, required)
}

AudioAGCConfig configures NewAudioAGC. All time constants are in real time; the constructor folds in the sample rate.

type CarrierCandidate added in v0.4.3

type CarrierCandidate struct {
	OffsetHz   float64
	RelPowerDb float64
}

CarrierCandidate is one detected narrowband carrier: its offset from DC (Hz, positive = above centre) and its averaged power relative to the strongest carrier in the band (dB, ≤ 0; the dominant carrier is 0 dB).

func EstimateCarrierCandidatesHz added in v0.4.3

func EstimateCarrierCandidatesHz(iq []complex64, sampleRateHz, searchHz, minSpacingHz float64, maxCandidates int) []CarrierCandidate

EstimateCarrierCandidatesHz returns up to maxCandidates carrier offsets in ±searchHz of DC, strongest first and each fine-refined. It generalises the single-peak EstimateCarrierOffsetHz so a control channel that is NOT the dominant carrier in a wideband capture can still be found and tried: a louder adjacent voice carrier no longer hides it. Coarse local maxima within minSpacingHz of an already-kept (stronger) candidate are suppressed, and candidates more than carrierCandidateFloorDb below the peak are dropped.

The search is the same bounded averaged periodogram as the single-carrier path (a Goertzel-style DFT, no full FFT): a coarse pass scores every bin across the band, local maxima are kept (spacing- and floor-gated), then each is refined with a fine pass around its coarse bin.

minSpacingHz ≤ 0 uses defaultCarrierMinSpacingHz; maxCandidates ≤ 0 uses defaultMaxCarrierCandidates. Returns nil for an empty input or zero searchHz.

type NCO added in v0.2.8

type NCO struct {
	// contains filtered or unexported fields
}

NCO is a numerically-controlled oscillator for frequency-shifting a complex baseband stream — i.e. "tuning" a channel that sits at a non-zero offset down to 0 Hz. Mix multiplies each input sample by e^{-j·2π·offsetHz·n/Fs}, so a spectral component at +offsetHz lands at DC.

The phasor is generated recursively (one complex multiply per sample) rather than via a Cos/Sin call. Repeated float32 multiplies let the magnitude drift off the unit circle, so renorm folds it back every renormEveryN samples — one sqrt amortised over thousands of samples, well below the surrounding numeric noise floor.

This mirrors the private oscillator inside internal/dsp/tuner's DDC bank, exported here as a standalone primitive the replay path and the single-channel down-converter (ccdecoder.Downconverter) reuse to tune an off-centre recorded capture without standing up a whole tuner bank.

func NewNCO added in v0.2.8

func NewNCO(offsetHz, sampleRateHz float64) *NCO

NewNCO returns an oscillator that shifts a component at +offsetHz down to DC at the given sample rate. A zero offset yields an identity mix.

func (*NCO) Mix added in v0.2.8

func (n *NCO) Mix(dst, src []complex64) []complex64

Mix writes the frequency-shifted src into dst (reused when it has capacity) and returns it. src is not read after the matching dst element is written, so in-place operation (dst aliasing src) is safe.

func (*NCO) Reset added in v0.2.8

func (n *NCO) Reset()

Reset returns the phasor to 1+0j (zero phase).

func (*NCO) SetOffset added in v0.2.8

func (n *NCO) SetOffset(offsetHz, sampleRateHz float64)

SetOffset retunes the oscillator. The phasor (current phase) is left untouched so a mid-stream retune stays phase-continuous.

type RealResampler

type RealResampler struct {
	L, M int
	// contains filtered or unexported fields
}

RealResampler is the real-valued counterpart of Resampler. Same polyphase decomposition, same L/M rate, but operates on float32 audio instead of complex64 IQ. Sized for the post-demod chain in internal/voice/composer where the FM demod hands real audio to a resampler that retunes 48 kHz → 8 kHz (or arbitrary rational ratios) with proper anti-aliasing built into the polyphase prototype filter.

The complex Resampler stays where it is — IQ paths still want it. Splitting them keeps each loop hot on the right data type without the complex64 ↔ float32 conversion overhead that an interface would impose.

RealResampler is not safe for concurrent Process calls — pin it to a single demod goroutine and Reset between calls.

func NewRealResampler

func NewRealResampler(L, M, tapsPerBranch int, beta float64) *RealResampler

NewRealResampler builds a real-valued resampler with rate L/M using a Kaiser-window LPF of total length tapsPerBranch*L. Cutoff is min(0.5/L, 0.5/M) so the prototype rejects images and aliases for both the interpolation and decimation steps. Bad parameters trip a panic at construction so misconfiguration shows up loudly.

func (*RealResampler) Process

func (r *RealResampler) Process(dst, src []float32) []float32

Process consumes len(src) input samples and returns approximately len(src)*L/M output samples. dst is reused if it has capacity.

func (*RealResampler) Reset

func (r *RealResampler) Reset()

Reset clears the running history and commutator state so the next Process call starts from silence.

type Resampler

type Resampler struct {
	L, M int
	// contains filtered or unexported fields
}

Resampler is a polyphase rational resampler with rate L/M. It interpolates by L (using polyphase branches of an LPF) and decimates by M.

func NewResampler

func NewResampler(L, M, tapsPerBranch int, beta float64) *Resampler

NewResampler builds a resampler with rate L/M using a Kaiser-window LPF of total length tapsPerBranch*L. Cutoff is min(0.5/L, 0.5/M) so that the filter rejects images and aliases for both interpolation and decimation.

func (*Resampler) Process

func (r *Resampler) Process(dst, src []complex64) []complex64

Process consumes len(src) input samples and returns approximately len(src)*L/M output samples. dst is reused if it has capacity.

func (*Resampler) Reset added in v0.1.8

func (r *Resampler) Reset()

Reset clears the sample history and the commutator / decimator state so the next Process call starts a fresh stream — leaving the prototype filter branches intact.

Directories

Path Synopsis
Package blind estimates the symbol/baud rate and a coarse modulation class of an unknown carrier directly from its channelized IQ, with no protocol decoder.
Package blind estimates the symbol/baud rate and a coarse modulation class of an unknown carrier directly from its channelized IQ, with no protocol decoder.
Package channelizer implements an M-channel critically-sampled polyphase channelizer.
Package channelizer implements an M-channel critically-sampled polyphase channelizer.
Package demod contains baseband demodulators that convert IQ streams into real-valued symbol streams (or audio, for FM).
Package demod contains baseband demodulators that convert IQ streams into real-valued symbol streams (or audio, for FM).
Package diag provides developer/diagnostic helpers — IQ-sample decimation, energy + bandwidth estimation — that feed the web console's Constellation panel.
Package diag provides developer/diagnostic helpers — IQ-sample decimation, energy + bandwidth estimation — that feed the web console's Constellation panel.
Package diversity combines IQ streams from N receivers tuned to the same frequency into a single per-sample IQ stream that's stronger and less faded than any one source.
Package diversity combines IQ streams from N receivers tuned to the same frequency into a single per-sample IQ stream that's stronger and less faded than any one source.
Package equalizer implements adaptive channel equalizers used to fight simulcast distortion — the inter-symbol interference produced when multiple transmitters cover the same frequency at slightly different arrival delays at the receiver.
Package equalizer implements adaptive channel equalizers used to fight simulcast distortion — the inter-symbol interference produced when multiple transmitters cover the same frequency at slightly different arrival delays at the receiver.
Package fft provides a swappable FFT abstraction.
Package fft provides a swappable FFT abstraction.
Package filter implements the FIR/CIC/halfband primitives used by the DSP pipeline.
Package filter implements the FIR/CIC/halfband primitives used by the DSP pipeline.
Package iqpower holds the small, reusable IQ signal-level helpers shared by the decode engines for operator-facing diagnostics: a window-averaged dBFS power measure plus the "looks dead" threshold.
Package iqpower holds the small, reusable IQ signal-level helpers shared by the decode engines for operator-facing diagnostics: a window-averaged dBFS power measure plus the "looks dead" threshold.
Package loudness implements ITU-R BS.1770-4 / EBU R128 integrated loudness measurement and the gain math used for per-call loudness normalization.
Package loudness implements ITU-R BS.1770-4 / EBU R128 integrated loudness measurement and the gain math used for per-call loudness normalization.
Package phase holds the small phase-domain helpers the rotation analyses use — the native Go equivalents of numpy.unwrap and the per-symbol differential phase that makes a π/4-DQPSK constellation's rotation visible.
Package phase holds the small phase-domain helpers the rotation analyses use — the native Go equivalents of numpy.unwrap and the per-symbol differential phase that makes a π/4-DQPSK constellation's rotation visible.
Package spectrum produces frame-rate-limited windowed FFT magnitude frames from a stream of IQ chunks.
Package spectrum produces frame-rate-limited windowed FFT magnitude frames from a stream of IQ chunks.
Package stats provides small, dependency-free numerical helpers — the descriptive statistics, histogram, mode, argmax, normalized cross-correlation and peak-finding that the signal-analysis paths reach for.
Package stats provides small, dependency-free numerical helpers — the descriptive statistics, histogram, mode, argmax, normalized cross-correlation and peak-finding that the signal-analysis paths reach for.
Package sync provides symbol-time recovery and frame sync correlators.
Package sync provides symbol-time recovery and frame sync correlators.
Package tuner extracts narrow-band baseband IQ for one or more frequency offsets from a single wide-band SDR IQ stream.
Package tuner extracts narrow-band baseband IQ for one or more frequency offsets from a single wide-band SDR IQ stream.
Package window provides standard window functions for FIR design and FFT pre-processing.
Package window provides standard window functions for FIR design and FFT pre-processing.

Jump to

Keyboard shortcuts

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