events

package
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: May 27, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package events implements an in-process pub/sub bus used by the engine to publish trunking events. A separate API surface (gRPC, WebSocket) subscribes without coupling to the engine.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bus

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

func NewBus

func NewBus(buffer int) *Bus

func (*Bus) Close

func (b *Bus) Close()

func (*Bus) Publish

func (b *Bus) Publish(e Event) int

Publish delivers e to every subscriber. Slow subscribers drop the event rather than blocking the publisher; we count drops via the returned int.

func (*Bus) Subscribe

func (b *Bus) Subscribe() *Subscription

type DecodeError

type DecodeError struct {
	Protocol string
	Stage    Stage
}

DecodeError is the payload published with KindDecodeError. Protocol packages publish this when an FEC primitive returns errCount == -1 (or a parser short-circuits on a structural failure) so the metrics collector can increment gophertrunk_decode_errors_total without each package having to hold a *metrics.Metrics handle.

type Event

type Event struct {
	Kind      Kind
	Timestamp time.Time
	Payload   any
}

type Kind

type Kind string
const (
	KindSDRAttached Kind = "sdr.attached"
	KindSDRDetached Kind = "sdr.detached"
	KindCCLocked    Kind = "cc.locked"
	KindCCLost      Kind = "cc.lost"
	KindCallStart   Kind = "call.start"
	KindCallEnd     Kind = "call.end"
	// KindCallComplete fires after the recorder has finished writing a
	// call's WAV to disk — the WAV header length fields are patched and
	// the file is closed and ready to read. It carries the same call
	// metadata as KindCallEnd plus the on-disk audio path
	// (trunking.CallComplete). The outbound-streaming subsystem
	// (internal/broadcast) subscribes to it to upload completed calls
	// to Broadcastify Calls / RdioScanner / OpenMHz / Icecast. Distinct
	// from KindCallEnd because KindCallEnd fires the instant the engine
	// releases the voice channel, before the WAV is flushed.
	KindCallComplete Kind = "call.complete"
	KindGrant        Kind = "grant"
	KindToneAlert    Kind = "tone.alert"
	KindDecodeError  Kind = "decode.error"
	KindError        Kind = "error"
	// Scanner subsystem (internal/scanner/cchunt):
	//   KindHuntProgress fires once per CC candidate the hunter
	//     tries — payload identifies which system + frequency +
	//     position in the candidate list, so the TUI can render
	//     "trying 851.012500 MHz (2/3)".
	//   KindHuntFailed fires when a system exhausts its CC list
	//     without locking; payload carries the next backoff window
	//     so operators can see "retry in 5 s".
	KindHuntProgress Kind = "cchunt.progress"
	KindHuntFailed   Kind = "cchunt.failed"
	// KindAffiliation fires when a radio unit affiliates with a
	// talkgroup. P25 control-channel publishes one per Group
	// Affiliation Response TSBK (opcode 0x28); the payload identifies
	// the source unit, the group it's joining, and the response code
	// (accepted / denied / refused / failed) from the system. Useful
	// downstream as a "who is listening where" feed for telemetry
	// dashboards.
	//
	// KindUnitRegistration fires when a radio registers (or
	// deregisters) on a site. P25 control-channel publishes one per
	// Unit Registration Response TSBK (opcode 0x2C); the payload
	// identifies the source unit, the WACN + System ID it's
	// registering on, and the response code. Useful as a "which
	// radios are on which site" feed.
	KindAffiliation      Kind = "affiliation"
	KindUnitRegistration Kind = "registration"
	// KindAudioState fires when an operator changes the live-audio
	// cockpit — volume, mute, or recording-gate. The payload is the
	// new state (the same shape as GET /api/v1/audio). Subscribers
	// can re-render volume sliders / mute indicators instantly
	// instead of waiting for the next 3 s poll tick. Emitted by
	// the HTTP API's PATCH /api/v1/audio handler.
	KindAudioState Kind = "audio.state"
	// KindPatch fires when a trunked system announces (or cancels) a
	// patch / dynamic-regroup — a super-group that merges several
	// talkgroups onto one channel. P25 Phase 2 publishes one per
	// Motorola group-regroup or Harris regroup MAC PDU. The payload
	// (trunking.Patch) carries the super-group, its member talkgroups,
	// and whether the patch is being added or removed.
	KindPatch Kind = "patch"
	// KindTalkerAlias fires when a radio's display name (its "talker
	// alias") has been fully reassembled from the multi-fragment vendor
	// MAC PDUs that carry it. P25 Phase 2 publishes one per completed
	// alias. The payload (trunking.TalkerAlias) is keyed by source unit
	// so a consumer can associate it with the active call.
	KindTalkerAlias Kind = "talker.alias"
	// KindLocation fires when a subscriber unit reports a geographic
	// position over the air — P25 Motorola Unit GPS, P25 L3Harris
	// Talker GPS, or DMR LRRP. The payload (trunking.Location) carries
	// the reporting radio, the lat/lon fix, and optional speed/heading.
	// The storage layer persists it; the API + web map surface it.
	KindLocation Kind = "location"
	// KindCallEncryption fires when the voice composer recovers an
	// Encryption Sync from the in-call signalling — e.g. a P25 Phase 1
	// LDU2 Encryption Sync, where ALGID/KID are only available after
	// the call has started (the grant TSBK carries only the encrypted
	// bit). The engine subscribes, backfills the bound ActiveCall's
	// Grant so downstream consumers (storage, SSE, TUI) see the
	// real algorithm/key on CallEnd, and republishes the event with
	// the call's system / protocol / group context so live listeners
	// (SSE, TUI) can patch the active row in flight. P25 Phase 2 and
	// any protocol whose grant carries ALGID/KID at grant time does
	// not need this event — the values are already on the Grant.
	KindCallEncryption Kind = "call.encryption"
	// KindBookmarkCreated / KindBookmarkUpdated / KindBookmarkDeleted
	// fire whenever the bookmarks store mutates. Payload is a
	// storage.Bookmark (or {ID} for deletes). Surfaced over SSE / WS
	// so the web SPA + TUI can refresh their bookmark list without
	// polling.
	KindBookmarkCreated Kind = "bookmark.created"
	KindBookmarkUpdated Kind = "bookmark.updated"
	KindBookmarkDeleted Kind = "bookmark.deleted"
	// KindPagerMessage fires when the POCSAG decoder finishes
	// reassembling one page (address codeword + 0..N message
	// codewords, terminated by an idle or next address). Payload
	// is a storage.PagerMessage carrying RIC + function +
	// decoded text + per-page bit-error count. Surfaced over SSE /
	// WS for the live pager panel.
	KindPagerMessage Kind = "pager.message"
	// KindAPRSPacket fires when the APRS decoder finishes one
	// frame off the air — UI frame decoded, info field parsed
	// into the appropriate APRS sub-type (position, message,
	// status, bulletin, status, etc.). Payload is a
	// storage.APRSPacket carrying source + destination + path +
	// decoded sub-payload. Surfaced over SSE / WS for the live
	// APRS panel.
	KindAPRSPacket Kind = "aprs.packet"
)

type Stage

type Stage string

Stage names a particular FEC / parser checkpoint inside a protocol decoder. Stages are used as Prometheus label values, so the wire shapes below are part of the Stage's public contract — extend the const block, don't rename existing entries.

const (
	StageNIDBCH          Stage = "nid-bch"          // P25 Phase 1 NID BCH(63,16,11)
	StageTSBKTrellis     Stage = "tsbk-trellis"     // P25 Phase 1 TSBK ½-rate trellis
	StageTSBKCRC         Stage = "tsbk-crc"         // P25 Phase 1 TSBK CRC trailer
	StageNoBandPlan      Stage = "no-bandplan"      // Voice grant arrived for an unknown channel ID / LCN
	StageSlotTypeHamming Stage = "slottype-hamming" // DMR slot-type Hamming(20,8)
	StageVoiceHeaderBPTC Stage = "voiceheader-bptc" // DMR Tier II Voice LC Header BPTC(196,96)
	StageVoiceHeaderRS   Stage = "voiceheader-rs"   // DMR Tier II Voice LC Header RS(12,9,4)
	StageSACCHTrellis    Stage = "sacch-trellis"    // NXDN SACCH ½-rate trellis
)

Known decode stages. Add new ones here and reference them from the publishing protocol package; the events bus itself stays neutral.

type Subscription

type Subscription struct {
	C <-chan Event
	// contains filtered or unexported fields
}

func (*Subscription) Close

func (s *Subscription) Close()

Jump to

Keyboard shortcuts

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