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