dsp

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: Apache-2.0 Imports: 4 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 intended for tuning a recorded wideband capture's control channel down to 0 Hz before a receiver that expects a channelised, centred stream (the SDR tuner does this in the live pipeline; a file replay has to do it here).

The search is a two-stage averaged periodogram evaluated directly (a bounded Goertzel-style DFT, no full FFT): a coarse pass locates the peak bin across the band, a fine pass refines it around that bin. This costs O(nFreq · window · nWindows) — cheap for the small bounded grids used — and avoids a transform-size/zero-pad dependency.

For a suppressed-carrier linear modulation (π/4-DQPSK / LSM) the power spectral density is symmetric about the carrier, so its peak coincides with the carrier offset. Returns 0 for an empty input or zero searchHz.

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