Documentation
¶
Overview ¶
Package cchunt is the multi-system control-channel scanner.
The supervisor owns one control SDR and multiplexes `trunking.Hunter` runs across every configured trunked system. On success a system parks until cc.lost; on failure the supervisor exponentially backs off, publishes KindHuntFailed, and advances to the next system. Operators can hold a system on its current lock, resume the loop, or force a re-hunt — these mutation surfaces are exposed through the API cockpit and the TUI Scanner panel.
This package is the orchestration layer only. It does not produce `cc.locked` events itself — those must come from the IQ-domain protocol decoders (P25 / DMR / NXDN / YSF / ...). Without those upstream feeders the supervisor will always exhaust the candidate list and report failed; that's intentional, not a bug. See the README "Roadmap" / "Known gaps" sections for the broader IQ-domain decoder dependency.
Index ¶
- type HuntState
- type IQHealthProvider
- type Options
- type Supervisor
- func (s *Supervisor) ForceRetune(name string) bool
- func (s *Supervisor) Hold(name string) bool
- func (s *Supervisor) PauseAll()
- func (s *Supervisor) Resume(name string) bool
- func (s *Supervisor) ResumeAll()
- func (s *Supervisor) Run(ctx context.Context) error
- func (s *Supervisor) SetIQHealthProvider(p IQHealthProvider)
- func (s *Supervisor) Snapshot() []SystemStatus
- func (s *Supervisor) SwapTuner(t Tuner) error
- type SystemStatus
- type Tuner
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type HuntState ¶
type HuntState string
HuntState is the per-system status surfaced through the REST cockpit + TUI. It maps onto a small set of user-facing words rather than the raw supervisor internals.
const ( // StateIdle means the supervisor hasn't run for this system yet // (only seen briefly at startup before the first hunt round). StateIdle HuntState = "idle" // StateHunting means the supervisor is actively walking the // system's candidate CCs. StateHunting HuntState = "hunting" // StateLocked means a hunt succeeded — a cc.locked event was // observed within the dwell window and persisted to the cache. StateLocked HuntState = "locked" // StateFailed means the last hunt round exhausted without a // lock; the supervisor is in backoff before retrying. StateFailed HuntState = "failed" // StateHeld means the operator pinned the supervisor on its // current lock (or on its current failed state) — retunes are // suppressed until Resume. StateHeld HuntState = "held" )
type IQHealthProvider ¶ added in v0.3.1
type IQHealthProvider interface {
IQHealth(system string) (trunking.HuntDiagnostics, bool)
}
IQHealthProvider is the optional hook the supervisor queries when a hunt round fails, so the cchunt.failed event/log can explain why rather than just reporting the symptom. internal/scanner/ccdecoder's Decoder satisfies it. Decoupled via an interface so the supervisor doesn't import the decoder (avoids a cycle) and tests can substitute a fake or omit it entirely.
type Options ¶
type Options struct {
Bus *events.Bus
Log *slog.Logger
Tuner Tuner
Cache *trunking.Cache
Systems []trunking.System
// Dwell is forwarded to each Hunter.
Dwell time.Duration
// InitialBackoff is the sleep after the first hunt round that
// exhausts without a lock. Doubles per consecutive failure, up to
// MaxBackoff.
InitialBackoff time.Duration
MaxBackoff time.Duration
}
Options configure a new Supervisor.
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor multiplexes per-system trunking.Hunter runs over one shared control SDR. Run blocks until ctx cancels and never returns an error other than ctx.Err() — individual hunt failures are reported on the bus and via Snapshot.
func New ¶
func New(opts Options) (*Supervisor, error)
New constructs a Supervisor. Returns an error if opts.Bus or opts.Tuner are missing. opts.Systems may be empty — the supervisor then idles harmlessly.
func (*Supervisor) ForceRetune ¶
func (s *Supervisor) ForceRetune(name string) bool
ForceRetune asks the in-flight Hunter (if any) for the named system to bail out so the next round picks it up immediately. If no hunt is in flight (e.g. the system is in backoff) it clears the backoff so the next round restarts the hunt.
func (*Supervisor) Hold ¶
func (s *Supervisor) Hold(name string) bool
Hold pins the supervisor on the named system's current state — no further retunes happen until Resume. Returns false if the system isn't configured.
func (*Supervisor) PauseAll ¶ added in v0.3.5
func (s *Supervisor) PauseAll()
PauseAll holds every configured system and bails any in-flight hunt, so the supervisor stops retuning the control SDR. It is used by the live hunt's "borrow the control SDR" path (when no spare SDR is available) to quiesce cchunt before sweeping, then ResumeAll restores normal scanning. Idempotent.
func (*Supervisor) Resume ¶
func (s *Supervisor) Resume(name string) bool
Resume undoes Hold. The next Run iteration picks the system back up in the next round-robin slot.
func (*Supervisor) ResumeAll ¶ added in v0.3.5
func (s *Supervisor) ResumeAll()
ResumeAll undoes PauseAll, returning every system to the round-robin with a fresh backoff. Idempotent.
func (*Supervisor) Run ¶
func (s *Supervisor) Run(ctx context.Context) error
Run blocks until ctx cancels. It walks the configured systems in round-robin order and hunts each one, sleeping per the per-system backoff window on failure. Operator hold short-circuits the round (the held system is skipped; the supervisor advances to the next).
func (*Supervisor) SetIQHealthProvider ¶ added in v0.3.1
func (s *Supervisor) SetIQHealthProvider(p IQHealthProvider)
SetIQHealthProvider wires an optional IQ-health source the supervisor queries on hunt failure to enrich the cchunt.failed event/log. Call before Run (the daemon does, right after building the ccdecoder). Safe to call with nil to clear it.
func (*Supervisor) Snapshot ¶
func (s *Supervisor) Snapshot() []SystemStatus
Snapshot returns a copy of every system's current status. Safe to call concurrently with Run.
func (*Supervisor) SwapTuner ¶ added in v0.2.2
func (s *Supervisor) SwapTuner(t Tuner) error
SwapTuner replaces the supervisor's control tuner. Newly-spawned hunters use the fresh handle; in-flight hunters keep the previous reference until they finish. Any armed retune channels are closed so the loop drops out of its current dwell and re-hunts immediately with the new tuner — the closed channel triggers the per-system hctx cancel via the goroutine armRetune spawned. Returns an error only when t is nil.
Used by the daemon's USB reacquisition path so a freshly re-opened SDR replaces a disconnected one without a process restart. See issue #345.
type SystemStatus ¶
type SystemStatus struct {
Name string `json:"name"`
Protocol string `json:"protocol"`
State HuntState `json:"state"`
AttemptedFreqHz uint32 `json:"attempted_freq_hz,omitempty"`
AttemptIndex int `json:"attempt_index,omitempty"`
TotalCandidates int `json:"total_candidates,omitempty"`
LockedFreqHz uint32 `json:"locked_freq_hz,omitempty"`
LockedAt time.Time `json:"locked_at,omitempty"`
NAC uint16 `json:"nac,omitempty"`
LastFailedAt time.Time `json:"last_failed_at,omitempty"`
BackoffMs int `json:"backoff_ms,omitempty"`
LastGrantAt time.Time `json:"last_grant_at,omitempty"`
}
SystemStatus is the per-system snapshot the supervisor exposes to the REST handler (and indirectly to the TUI cockpit panel). Time-valued fields are zero when not applicable (e.g. LockedAt is zero before the first successful hunt).