carriers

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 carriers holds protocol-neutral carrier-discovery primitives: finding candidate carriers in a power spectrum and snapping them onto the standard land-mobile channel rasters. It sits below both internal/hunt (the live sweeper) and internal/siglab (the offline wideband survey) so the two share one implementation of the peak detector and channel-grid logic rather than drifting copies — the import direction is carriers ← {hunt, siglab}, never the reverse.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InferGrid

func InferGrid(freqs []uint32) (stepHz, phaseHz uint32)

InferGrid picks the channel raster (step, phase in Hz) that best explains the candidate frequencies. For each standard step it treats the residuals (f mod step) as points on a circle and measures their concentration (mean resultant length); the raster fits when the residuals pile up at one phase. It returns the coarsest step whose residuals are tightly clustered — the coarsest raster the channels genuinely sit on — and that cluster's phase.

With too few candidates, or no step clustering tightly, it falls back to the finest standard step at phase 0: a lone carrier (the 440.125 MHz repro) then still snaps to the nearest 6.25 kHz point, correcting the FFT-bin offset.

func SnapHz

func SnapHz(f, step, phase, maxCorrectionHz uint32) uint32

SnapHz snaps f to the nearest point on the (step, phase) grid, but only when that point is within maxCorrectionHz of f — so a small FFT-bin quantization error is corrected to the true channel centre while a carrier that genuinely sits off the raster (e.g. an analog channel on a different plan) is left where it is. maxCorrectionHz == 0, or step == 0, disables snapping.

Types

type Occupancy added in v0.5.9

type Occupancy struct {
	CenterHz     uint32  `json:"center_hz"`
	LowHz        uint32  `json:"low_hz"`
	HighHz       uint32  `json:"high_hz"`
	OccupiedBwHz uint32  `json:"occupied_bw_hz"`
	PowerDbFS    float32 `json:"power_dbfs"` // median power across the span
	SNRDb        float32 `json:"snr_db"`     // median power above the noise floor

	EdgeClippedLow  bool `json:"edge_clipped_low,omitempty"`
	EdgeClippedHigh bool `json:"edge_clipped_high,omitempty"`
}

Occupancy is one contiguous span of spectrum standing above the noise floor — a signal far wider than a single land-mobile channel, such as an OFDM cellular/WiFi block. Where DetectPeaks finds narrow carriers, DetectOccupancy finds these plateaus.

EdgeClippedLow/High flag a span whose energy runs right up to the frame's usable edge: the emitter is at least OccupiedBwHz wide, but its true extent continues past what a single tune can see. The live sweeper stitches such clipped spans across adjacent overlapping steps to recover the real width (see internal/hunt). The flags are how it knows two steps' spans are the same emitter seen at a seam.

func DetectOccupancy added in v0.5.9

func DetectOccupancy(frame spectrum.Frame, opts OccupancyOptions) []Occupancy

DetectOccupancy finds contiguous spans of occupied spectrum in a frame — the wideband counterpart to DetectPeaks. It estimates the same robust noise floor (low-quartile), marks every non-guard bin standing ThresholdDb above it, coalesces adjacent occupied bins into runs, and returns each run at least MinBwHz wide as an Occupancy with absolute frequency bounds and edge-clip flags. Spans are returned sorted by ascending low edge so a caller can stitch neighbours.

type OccupancyOptions added in v0.5.9

type OccupancyOptions struct {
	// ThresholdDb is the minimum power above the estimated noise floor for a
	// bin to count as occupied. It is intentionally lower than the peak
	// detector's: a wide OFDM plateau is flatter and lower per-bin than a
	// narrow carrier, but contiguous. 0 ⇒ 6 dB.
	ThresholdDb float32
	// MinBwHz is the shortest contiguous run reported. The default sits well
	// above any land-mobile channel so a normal narrowband carrier never
	// registers as "wideband"; only genuinely wide emitters do. 0 ⇒ 200 kHz.
	MinBwHz uint32
	// GuardBins drops this many bins at each band edge (the SDR rolloff) from
	// detection; a run that reaches the first/last scanned bin is flagged
	// edge-clipped. 0 ⇒ a default proportional to the FFT size.
	GuardBins int
	// FloorDbFS overrides the per-frame noise-floor estimate. A signal wider
	// than the tune fills a whole step, so that step's own low-quartile floor
	// sits *inside* the signal and the per-frame estimate detects nothing. The
	// live sweeper passes a sweep-wide floor (learned from quiet steps) here so
	// a fully-occupied step is still recognised as occupied and stitches to its
	// neighbours. 0 ⇒ use the per-frame low-quartile estimate.
	FloorDbFS float32
}

OccupancyOptions tune the wideband-occupancy detector.

type Peak

type Peak struct {
	FreqHz    uint32
	PowerDbFS float32
	SNRDb     float32
}

Peak is a candidate carrier found in a spectrum frame: its absolute frequency, its power, and how far it stands above the estimated noise floor.

func DetectPeaks

func DetectPeaks(frame spectrum.Frame, opts PeakOptions) []Peak

DetectPeaks finds candidate carriers in a spectrum frame. It estimates a robust noise floor (low-quartile of the bin powers), keeps local maxima that stand ThresholdDb above it, drops the DC bin and the band-edge guard bins, and enforces a minimum carrier spacing (stronger peak wins). Bin indices are mapped to absolute Hz using the frame's center and sample rate. Returned peaks are sorted by descending SNR.

type PeakOptions

type PeakOptions struct {
	// ThresholdDb is the minimum power above the estimated noise floor for a
	// local maximum to count as a carrier. 0 ⇒ a sensible default (10 dB).
	ThresholdDb float32
	// MinSpacingHz is the minimum separation between reported peaks; when two
	// maxima fall closer than this the stronger one wins. 0 ⇒ 6.25 kHz (the
	// tightest trunking channel step).
	MinSpacingHz uint32
	// GuardBins drops this many bins at each band edge (rolloff) from
	// consideration. 0 ⇒ a default proportional to the FFT size.
	GuardBins int
}

PeakOptions tune the carrier detector.

Jump to

Keyboard shortcuts

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