Documentation
¶
Overview ¶
ADS-B / aircraft log writer — drains KindAircraftReport events off the shared bus and writes one row per decoded Mode-S frame to the SQLite aircraft_log table. Mirrors vessellog.go / aprslog.go / dsclog.go.
APRS log writer — drains KindAPRSPacket events off the shared bus and writes one row per decoded packet to the SQLite aprs_log table. Mirrors pagerlog.go and locationlog.go in structure.
Bookmark storage — operator-managed conventional channel bookmarks. Persisted to SQLite alongside the call log and location log so they survive a daemon restart and back up with the rest of the daemon state.
The store is decoupled from the SDR pool and the scanner — a bookmark is plain metadata (frequency + display name + mode + notes); the spectrum panel uses it to render click-to-tune markers, the conventional scanner uses it as an alternative to the YAML channel list. Mutations are bus-published so subscribed surfaces (web SPA, TUI) can re-render without polling.
DSC log writer — drains KindDSCMessage events off the shared bus and writes one row per decoded sequence to the SQLite dsc_log table. Mirrors aprslog.go / vessellog.go / pagerlog.go.
Operator-applied names for radios and talkgroups. Persisted to SQLite alongside the call log so a name applied from a console while watching live traffic is still there after a restart.
These layer OVER the per-system talkgroup_file / rid_alias_file catalogues rather than replacing them: the daemon loads the files first, then applies label rows on top (see the daemon's label merge). An operator who prefers to keep a single hand-edited file can export the merged result back to CSV and stop using the store.
LoRa frame log writer — drains KindLoRaFrame events off the shared bus and writes one row per decoded PHY frame to the SQLite lora_log table. Mirrors m17log.go / dsclog.go.
M17 link-setup log writer — drains KindM17LinkSetup events off the shared bus and writes one row per reassembled Link Setup Frame to the SQLite m17_log table. Mirrors dsclog.go / aprslog.go.
MDC1200 log writer — drains KindMDC1200Message events off the shared bus and writes one row per decoded signaling burst to the SQLite mdc1200_log table. Mirrors dsclog.go / aprslog.go.
PagerLog persists POCSAG (and eventually FLEX) pager messages to the SQLite pager_log table by subscribing to events.KindPagerMessage on the shared bus. The decoder pipeline publishes one event per fully-reassembled page; this consumer writes a row and the web panel queries the recent rows via /api/v1/pager/messages.
Package storage persists GopherTrunk's runtime data to disk.
The default backend is SQLite via the pure-Go `modernc.org/sqlite` driver — CGO_ENABLED=0 stays true across the daemon and the daemon cross-compiles to linux/arm64 without toolchain gymnastics.
Layout:
sqlite.go Open + schema migrations. One-shot at startup.
calllog.go CallLog: subscribes to events.KindCallStart /
KindCallEnd from the trunking engine, writes rows
keyed by (device serial, started_at).
retention.go Background sweeper that deletes DB rows + the WAV /
raw files written by internal/voice older than a
configurable cutoff.
The API's /api/v1/calls/history endpoint reads through the `History` query helpers exposed here. There is no gRPC call-log service today (history is REST only).
AIS / vessel log writer — drains KindAISMessage events off the shared bus and writes one row per decoded message to the SQLite vessel_log table. Mirrors aprslog.go and pagerlog.go in structure.
Index ¶
- type AISMessage
- type APRSLog
- type APRSPacket
- type AircraftLog
- type AircraftReport
- type Bookmark
- type BookmarkStore
- func (s *BookmarkStore) Create(ctx context.Context, b Bookmark) (Bookmark, error)
- func (s *BookmarkStore) Delete(ctx context.Context, id int64) error
- func (s *BookmarkStore) Get(ctx context.Context, id int64) (Bookmark, error)
- func (s *BookmarkStore) List(ctx context.Context) ([]Bookmark, error)
- func (s *BookmarkStore) Update(ctx context.Context, b Bookmark) (Bookmark, error)
- type CallLog
- type CallRow
- type DB
- type DSCLog
- type DSCMessage
- type HistoryFilter
- type Label
- type LabelKind
- type LabelStore
- func (s *LabelStore) Delete(ctx context.Context, kind LabelKind, system string, id uint32) error
- func (s *LabelStore) Get(ctx context.Context, kind LabelKind, system string, id uint32) (Label, error)
- func (s *LabelStore) List(ctx context.Context, kind LabelKind, system string) ([]Label, error)
- func (s *LabelStore) Upsert(ctx context.Context, l Label) (Label, error)
- type LoRaFrame
- type LoRaLog
- type LocationLog
- type LocationRow
- type M17LinkSetup
- type M17Log
- type MDC1200Log
- type MDC1200Message
- type PagerLog
- type PagerMessage
- type RIDSummary
- type RIDSummaryFilter
- type Retention
- type RetentionOptions
- type VesselLog
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AISMessage ¶ added in v0.2.6
type AISMessage struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
MMSI uint32 `json:"mmsi"`
Type string `json:"type"`
Body string `json:"body"`
// Position fields — populated when the message carries lat/lon.
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
SpeedOverGround float64 `json:"sog,omitempty"`
CourseOverGround float64 `json:"cog,omitempty"`
Heading int `json:"heading,omitempty"`
HasPosition bool `json:"has_position"`
// Static-data fields — populated for types 5 and 24.
VesselName string `json:"vessel_name,omitempty"`
Callsign string `json:"callsign,omitempty"`
Destination string `json:"destination,omitempty"`
ShipType int `json:"ship_type,omitempty"`
IMO uint32 `json:"imo,omitempty"`
// RawHex is the wire-bit payload as hex for round-tripping into
// offline decoders / debugging unrecognised message types.
RawHex string `json:"raw_hex"`
FCSOK bool `json:"fcs_ok"`
}
AISMessage is one persisted decoded AIS message. Position-bearing types (1/2/3/4/18/19/27) carry Latitude / Longitude + COG / SOG + Heading; static-data types (5/24) carry VesselName / Callsign / Destination etc. Everything else stays empty so the column shape is stable across types.
type APRSLog ¶ added in v0.2.4
type APRSLog struct {
// contains filtered or unexported fields
}
APRSLog drains KindAPRSPacket events to the aprs_log table. The bus subscription / drain / close lifecycle is the shared eventLog[APRSPacket]; this type adds only the APRS-specific insert and Recent.
func NewAPRSLog ¶ added in v0.2.4
NewAPRSLog wires the log to the bus. Subscription happens at construction so events published before Run() begins aren't lost.
func (APRSLog) Close ¶ added in v0.2.4
func (e APRSLog) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
type APRSPacket ¶ added in v0.2.4
type APRSPacket struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
Src string `json:"src"`
Dst string `json:"dst"`
Path string `json:"path"`
Type string `json:"type"`
Body string `json:"body"`
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
RawInfo string `json:"raw_info"`
FCSOK bool `json:"fcs_ok"`
}
APRSPacket is one persisted decoded APRS packet. Mirrors the aprs.Packet shape with the AX.25 envelope flattened to a callsign-string triple (src, dst, path) and the sub-payload summarised into a single "body" field for compact rendering. Latitude / Longitude are populated only for position-bearing types and stay 0 otherwise.
type AircraftLog ¶ added in v0.2.6
type AircraftLog struct {
// contains filtered or unexported fields
}
AircraftLog drains KindAircraftReport events until ctx cancels or the bus closes.
func NewAircraftLog ¶ added in v0.2.6
NewAircraftLog wires the log to the bus. Subscription happens at construction so events published before Run() begins aren't lost.
func (*AircraftLog) Close ¶ added in v0.2.6
func (a *AircraftLog) Close() error
Close releases the bus subscription and waits for Run to drain.
func (*AircraftLog) CurrentAircraft ¶ added in v0.3.0
func (a *AircraftLog) CurrentAircraft(maxAge time.Duration) ([]AircraftReport, error)
CurrentAircraft returns the latest known state of each aircraft seen within maxAge, one row per ICAO. Because aircraft_log stores one message type per row (identification, position, and velocity arrive as separate Mode-S messages), this coalesces the most recent non-empty value of each field group — callsign, position, altitude, velocity — into a single live-state record. ReceivedAt is the aircraft's last-seen time. Rows are newest-last-seen first.
maxAge ≤ 0 defaults to 5 minutes. This powers the "currently visible aircraft" panel, distinct from the raw message log Recent returns.
func (*AircraftLog) Recent ¶ added in v0.2.6
func (a *AircraftLog) Recent(limit int) ([]AircraftReport, error)
Recent returns the most recent reports, newest first, capped at limit. limit ≤ 0 picks 200; limit > 5000 caps at 5000. ADS-B is the highest-rate decoder (aircraft transmit ~2 msg/s), so operators commonly want a tighter window — set limit lower if rendering full panels live.
type AircraftReport ¶ added in v0.2.6
type AircraftReport struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
// 24-bit ICAO aircraft address — rendered as a 6-char hex
// string in the JSON ("4840D6") for readability while staying
// integer in the SQL column for index efficiency.
ICAO uint32 `json:"icao"`
ICAOHex string `json:"icao_hex"`
Kind string `json:"kind"` // "ident" | "airborne-pos" | "velocity" | ...
Body string `json:"body"` // kind-specific summary
CRCValid bool `json:"crc_valid"`
// Identification fields (KindIdentification).
Callsign string `json:"callsign,omitempty"`
Category int `json:"category,omitempty"`
// Position fields (KindAirbornePosition / KindSurfacePosition).
// Surfaces the globally-decoded lat/lon when the per-ICAO
// CPR pairing succeeded; HasPosition distinguishes that from
// "raw CPR fields preserved but no global decode".
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
Altitude int `json:"altitude_ft,omitempty"`
HasPosition bool `json:"has_position"`
HasAltitude bool `json:"has_altitude"`
// Velocity fields (KindAirborneVelocity).
GroundSpeedKn int `json:"ground_speed_kn,omitempty"`
TrackDeg float64 `json:"track_deg,omitempty"`
VerticalRateFPM int `json:"vertical_rate_fpm,omitempty"`
// Raw 112-bit (or 56-bit) frame hex. Round-trips into raw_hex
// for debugging frames the parser doesn't fully decode.
RawHex string `json:"raw_hex"`
}
AircraftReport is one persisted decoded Mode-S frame. Different message kinds populate different columns: identification frames fill Callsign + Category, airborne-position fills lat/lon + altitude, velocity fills GroundSpeedKn + TrackDeg + VerticalRateFPM, etc. Everything else stays empty so the column shape is stable across kinds.
type Bookmark ¶ added in v0.2.3
type Bookmark struct {
ID int64 `json:"id"`
Name string `json:"name"`
FreqHz uint32 `json:"freq_hz"`
Mode string `json:"mode"` // "FM", "NFM", "AM", "USB", "LSB", "CW", "DMR", ...
CTCSSHz float64 `json:"ctcss_hz,omitempty"`
DCSCode uint16 `json:"dcs_code,omitempty"`
Notes string `json:"notes,omitempty"`
Group string `json:"group,omitempty"` // operator-defined category
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Bookmark is one row in the bookmarks table.
type BookmarkStore ¶ added in v0.2.3
type BookmarkStore struct {
// contains filtered or unexported fields
}
BookmarkStore is the CRUD layer over the bookmarks table.
func NewBookmarkStore ¶ added in v0.2.3
func NewBookmarkStore(db *DB, bus *events.Bus) (*BookmarkStore, error)
NewBookmarkStore returns a store backed by the given DB. The bus is optional — when nil, no mutation events are published (useful for tests). When non-nil, every Create / Update / Delete publishes a bookmark.{created,updated,deleted} event so UI surfaces refresh without polling.
func (*BookmarkStore) Create ¶ added in v0.2.3
Create inserts a bookmark and returns it with ID + timestamps populated. Name is required; mode defaults to "FM" when empty.
func (*BookmarkStore) Delete ¶ added in v0.2.3
func (s *BookmarkStore) Delete(ctx context.Context, id int64) error
Delete removes the bookmark by id. Returns sql.ErrNoRows when the id is unknown.
func (*BookmarkStore) Get ¶ added in v0.2.3
Get returns the bookmark with the given id, or sql.ErrNoRows when it's missing.
func (*BookmarkStore) List ¶ added in v0.2.3
func (s *BookmarkStore) List(ctx context.Context) ([]Bookmark, error)
List returns all bookmarks sorted by group then name. Empty result is not an error.
type CallLog ¶
type CallLog struct {
// contains filtered or unexported fields
}
CallLog persists trunking calls to the SQLite call_log table by subscribing to events.KindCallStart and events.KindCallEnd on the shared events bus.
Rows are keyed by (device_serial, started_at). On CallStart we INSERT with a NULL ended_at; on CallEnd we UPDATE the matching row with the ended_at, duration, and end-reason. The unique index in the schema keeps duplicate-start events idempotent.
func NewCallLog ¶
NewCallLog wires the call log to the bus. It subscribes immediately so callers can publish events before Run is called.
func (*CallLog) SetRIDResolver ¶ added in v0.9.0
SetRIDResolver installs the source-radio alias resolver used to stamp source_alpha onto call records — "who was talking", not just the RID. Safe to call once before Run. A nil fn (or nil resolver) leaves source_alpha NULL.
type CallRow ¶
type CallRow struct {
ID int64 `json:"id"`
System string `json:"system"`
Protocol string `json:"protocol"`
GroupID uint32 `json:"group_id"`
SourceID uint32 `json:"source_id"`
FrequencyHz uint32 `json:"frequency_hz"`
Encrypted bool `json:"encrypted"`
AlgorithmID uint8 `json:"algorithm_id"`
KeyID uint16 `json:"key_id"`
Emergency bool `json:"emergency"`
DataCall bool `json:"data_call"`
// Individual is true for a unit-to-unit / private call, where GroupID is a
// target radio address rather than a talkgroup. Omitted for group calls.
Individual bool `json:"individual,omitempty"`
// Timeslot is the 1-based DMR TDMA slot (0 = n/a, 1 = TS1, 2 = TS2).
Timeslot uint8 `json:"timeslot,omitempty"`
// Priority is the call's on-air signalled priority (service-options low
// 3 bits, 0 = none/lowest). Distinct from a talkgroup's configured priority.
Priority uint8 `json:"priority,omitempty"`
DeviceSerial string `json:"device_serial"`
StartedAt time.Time `json:"started_at"`
EndedAt time.Time `json:"ended_at,omitempty"` // zero if call still active
DurationMs int64 `json:"duration_ms,omitempty"`
EndReason string `json:"end_reason,omitempty"`
TalkgroupAlpha string `json:"talkgroup_alpha,omitempty"`
// SourceAlpha is the resolved alias/name of the source radio (RID), from
// the operator RID catalogue + live-decoded talker aliases; "" when
// unresolved.
SourceAlpha string `json:"source_alpha,omitempty"`
// SignalDbFS is the call's mean received channel power in dBFS
// (channel power, not calibrated RSSI or SNR). nil when unmeasured.
SignalDbFS *float64 `json:"signal_dbfs,omitempty"`
// EVMPct / SNRDb are the call's demod quality — RMS error-vector
// magnitude (%) and estimated symbol SNR (dB). nil when unmeasured
// (only P25 Phase 1 chains measure them). These ARE the demod-quality
// figures to compare against another decoder, unlike SignalDbFS.
EVMPct *float64 `json:"evm_pct,omitempty"`
SNRDb *float64 `json:"snr_db,omitempty"`
// HasRecording is true when a finished recording is on disk for this call
// (recording_path is set). The path itself is deliberately not serialised —
// the UI plays via GET /api/v1/calls/{id}/audio, not a filesystem path.
HasRecording bool `json:"has_recording,omitempty"`
}
CallRow is one row from the call_log table.
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB is a thin wrapper over *sql.DB that lets the call-log + retention helpers share a typed handle. The schema is migrated on Open.
func Open ¶
Open creates (or opens) a SQLite database at path and applies the embedded schema migrations. The path's parent directory is created if missing.
`:memory:` and the standard "file:..." DSN forms are passed through to the driver — useful for tests.
func (*DB) RIDSummaries ¶ added in v0.9.3
func (d *DB) RIDSummaries(ctx context.Context, f RIDSummaryFilter) ([]RIDSummary, error)
RIDSummaries aggregates the call_log by source_id (radio ID) into a durable per-radio view: total call count, first/last-seen, and the system, protocol, alias and last GROUP talkgroup of the most recent call. Rows with source_id 0 (unknown source) are excluded. Ordered by most-recently-seen first.
This is the persisted basis for the Radio IDs list, so an observed radio no longer collapses out of the list once the live affiliation tracker's 30-minute idle TTL sweeps it — the list reflects every radio seen over the daemon's lifetime and survives control-channel re-locks and restarts.
func (*DB) RecordingPathByID ¶ added in v0.9.5
RecordingPathByID returns the on-disk recording path for one call row, or "" when the call has no recording (or the id is unknown). The audio endpoint resolves a call id to a file this way so the filesystem path is never exposed to the client.
type DSCLog ¶ added in v0.2.6
type DSCLog struct {
// contains filtered or unexported fields
}
DSCLog drains KindDSCMessage events until ctx cancels or the bus closes.
func NewDSCLog ¶ added in v0.2.6
NewDSCLog wires the log to the bus. Subscription happens at construction so events published before Run() begins aren't lost.
func (DSCLog) Close ¶ added in v0.2.6
func (e DSCLog) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
type DSCMessage ¶ added in v0.2.6
type DSCMessage struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
Format string `json:"format"` // "distress" | "all-ships" | "individual" | "group" | ...
Category string `json:"category"` // "distress" | "urgency" | "safety" | "routine"
SelfMMSI uint64 `json:"self_mmsi"`
TargetMMSI uint64 `json:"target_mmsi,omitempty"`
Nature string `json:"nature,omitempty"` // distress nature ("fire", "sinking", ...)
TimeUTC string `json:"time_utc,omitempty"` // HH:MM, distress only
// Position fields — populated only on distress alerts that
// included a position field with a non-sentinel value.
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
HasPosition bool `json:"has_position"`
Body string `json:"body"` // type-specific summary
RawHex string `json:"raw_hex"` // hex-encoded 7-bit symbol stream
}
DSCMessage is one persisted decoded DSC sequence.
type HistoryFilter ¶
type HistoryFilter struct {
System string
GroupID uint32 // 0 = no filter (filters call_log.group_id)
SourceID uint32 // 0 = no filter (filters call_log.source_id — RID)
Since time.Time
Until time.Time
Limit int
OnlyEnded bool
}
HistoryFilter narrows a History query.
type Label ¶ added in v0.9.9
type Label struct {
ID int64 `json:"id"`
Kind LabelKind `json:"kind"`
// System scopes the label to one trunking system. Empty applies it
// wherever the id is seen, which matches the daemon's single merged
// catalogue.
System string `json:"system,omitempty"`
TargetID uint32 `json:"target_id"`
// Name is the RID alias or the talkgroup alpha tag.
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Tag string `json:"tag,omitempty"`
Group string `json:"group,omitempty"`
Owner string `json:"owner,omitempty"` // radios only
Icon string `json:"icon,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Label is one operator-applied name plus the descriptive fields that travel with it. Policy fields (priority, lockout, scan, watch) are deliberately NOT here: those are existing in-memory mutations, and making them durable is a separate behaviour change.
type LabelKind ¶ added in v0.9.9
type LabelKind string
LabelKind distinguishes the two catalogues a label can target.
func ParseLabelKind ¶ added in v0.9.9
ParseLabelKind validates a wire/query value.
type LabelStore ¶ added in v0.9.9
type LabelStore struct {
// contains filtered or unexported fields
}
LabelStore is the CRUD layer over the labels table.
func NewLabelStore ¶ added in v0.9.9
func NewLabelStore(db *DB, bus *events.Bus) (*LabelStore, error)
NewLabelStore returns a store backed by the given DB. The bus is optional — when nil no mutation events are published.
func (*LabelStore) Delete ¶ added in v0.9.9
Delete removes one label. Returns sql.ErrNoRows when there was none.
func (*LabelStore) Get ¶ added in v0.9.9
func (s *LabelStore) Get(ctx context.Context, kind LabelKind, system string, id uint32) (Label, error)
Get returns one label, or sql.ErrNoRows when it is missing.
func (*LabelStore) List ¶ added in v0.9.9
List returns labels of one kind, newest-updated first. An empty kind returns both kinds. A non-empty system returns that system's labels AND the system-agnostic ones, since both apply to it.
func (*LabelStore) Upsert ¶ added in v0.9.9
Upsert writes a complete label row, replacing any existing row for the same (kind, system, target_id). The caller supplies the whole record — it builds it from the already-merged in-memory catalogue entry — so there is no read-modify-write here and no lost-update race between two consoles.
type LoRaFrame ¶ added in v0.3.8
type LoRaFrame struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
FrequencyHz uint32 `json:"frequency_hz"` // sub-channel centre frequency
SF int `json:"sf"` // spreading factor 7..12
CR int `json:"cr"` // coding rate 1..4 (4/5..4/8)
BandwidthHz int `json:"bandwidth_hz"`
RSSI float64 `json:"rssi"`
SNR float64 `json:"snr"`
CFOHz float64 `json:"cfo_hz"`
CRCOK bool `json:"crc_ok"`
PayloadHex string `json:"payload_hex"` // de-whitened PHY payload
// LoRaWAN MAC fields (populated when IsLoRaWAN is true).
IsLoRaWAN bool `json:"is_lorawan"`
MType string `json:"mtype"` // e.g. "unconfirmed-up", "join-request"
DevAddr string `json:"dev_addr"` // hex, network data frames
FCnt uint32 `json:"fcnt"` // frame counter
FPort int `json:"fport"` // -1 when absent
MICOK bool `json:"mic_ok"` // MIC verified against a known key
Decrypted bool `json:"decrypted"` // FRMPayload was decrypted
DecodedJS string `json:"decoded"` // best-effort decoded payload summary
}
LoRaFrame is one persisted LoRa frame: the PHY parameters and payload plus any LoRaWAN MAC fields recovered from it. A frame with HasCRC=false carries CRCOK=true (nothing to check). The LoRaWAN fields are zero/empty for non-LoRaWAN traffic (IsLoRaWAN=false).
type LoRaLog ¶ added in v0.3.8
type LoRaLog struct {
// contains filtered or unexported fields
}
LoRaLog drains KindLoRaFrame events until ctx cancels or the bus closes.
func NewLoRaLog ¶ added in v0.3.8
NewLoRaLog wires the log to the bus. Subscription happens at construction so frames published before Run() begins aren't lost.
func (LoRaLog) Close ¶ added in v0.3.8
func (e LoRaLog) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
type LocationLog ¶ added in v0.1.9
type LocationLog struct {
// contains filtered or unexported fields
}
LocationLog persists geographic fixes to the SQLite location_log table by subscribing to events.KindLocation on the shared bus.
func NewLocationLog ¶ added in v0.1.9
NewLocationLog wires the location log to the bus. It subscribes immediately so callers can publish before Run starts.
func (LocationLog) Close ¶ added in v0.1.9
func (e LocationLog) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
func (*LocationLog) Recent ¶ added in v0.1.9
func (l *LocationLog) Recent(limit int) ([]LocationRow, error)
Recent returns the most recent fixes, newest first, capped at limit.
type LocationRow ¶ added in v0.1.9
type LocationRow struct {
ID int64 `json:"id"`
System string `json:"system"`
Protocol string `json:"protocol"`
RadioID uint32 `json:"radio_id"`
Talkgroup uint32 `json:"talkgroup"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
SpeedKnots float64 `json:"speed_knots"`
HeadingDeg float64 `json:"heading_deg"`
ReportedAt time.Time `json:"reported_at"`
}
LocationRow is one persisted fix, returned by Recent.
type M17LinkSetup ¶ added in v0.2.9
type M17LinkSetup struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
Src string `json:"src"`
Dst string `json:"dst"`
Mode string `json:"mode"` // "voice" | "data" | "voice+data" | "packet"
CAN uint8 `json:"can"` // channel-access number
Meta string `json:"meta"` // hex-encoded META block
CRCOK bool `json:"crc_ok"`
Body string `json:"body"` // display summary
}
M17LinkSetup is one persisted M17 link-setup frame.
type M17Log ¶ added in v0.2.9
type M17Log struct {
// contains filtered or unexported fields
}
M17Log drains KindM17LinkSetup events until ctx cancels or the bus closes.
func NewM17Log ¶ added in v0.2.9
NewM17Log wires the log to the bus. Subscription happens at construction so events published before Run() begins aren't lost.
func (M17Log) Close ¶ added in v0.2.9
func (e M17Log) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
type MDC1200Log ¶ added in v0.2.7
type MDC1200Log struct {
// contains filtered or unexported fields
}
MDC1200Log drains KindMDC1200Message events until ctx cancels or the bus closes.
func NewMDC1200Log ¶ added in v0.2.7
NewMDC1200Log wires the log to the bus. Subscription happens at construction so events published before Run() begins aren't lost.
func (MDC1200Log) Close ¶ added in v0.2.7
func (e MDC1200Log) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
func (*MDC1200Log) Recent ¶ added in v0.2.7
func (m *MDC1200Log) Recent(limit int) ([]MDC1200Message, error)
Recent returns the most recent bursts, newest first, capped at limit. limit ≤ 0 picks 200; limit > 5000 caps at 5000.
type MDC1200Message ¶ added in v0.2.7
type MDC1200Message struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
Op uint8 `json:"op"`
Arg uint8 `json:"arg"`
UnitID uint16 `json:"unit_id"`
Operation string `json:"operation"` // "PTT ID" | "Emergency" | ... ("" if unknown)
Body string `json:"body"`
RawHex string `json:"raw_hex"`
CRCOK bool `json:"crc_ok"`
}
MDC1200Message is one persisted decoded MDC1200 burst.
type PagerLog ¶ added in v0.2.3
type PagerLog struct {
// contains filtered or unexported fields
}
PagerLog drains KindPagerMessage off the bus and writes one row per page. Mirrors LocationLog's Run/Close lifecycle.
func NewPagerLog ¶ added in v0.2.3
NewPagerLog wires the log to the bus. The bus subscription happens at construction time so the decoder can publish before Run begins without losing events.
func (PagerLog) Close ¶ added in v0.2.3
func (e PagerLog) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.
type PagerMessage ¶ added in v0.2.3
type PagerMessage struct {
ID int64 `json:"id"`
ReceivedAt time.Time `json:"received_at"`
Protocol string `json:"protocol"` // "pocsag" | "flex"
RIC uint32 `json:"ric"`
Func uint8 `json:"func"` // 0..3 = A..D
Encoding string `json:"encoding"`
Body string `json:"body"`
Corrected int `json:"corrected"`
}
PagerMessage is one persisted page. Mirrors the pocsag.Page shape but with the "encoding" enum widened to a string for storage + JSON convenience.
type RIDSummary ¶ added in v0.9.3
type RIDSummary struct {
SourceID uint32
System string // system of the most recent call
Protocol string // protocol of the most recent call
// LastTalkgroup is the group_id of the most recent GROUP call (individual=0)
// this radio was heard on; 0 when the radio has only ever made individual /
// unit-to-unit calls (whose group_id is a target radio, not a talkgroup).
LastTalkgroup uint32
CallCount uint64
FirstSeen time.Time
LastSeen time.Time
// SourceAlpha is the resolved alias from the most recent call, "" if none.
SourceAlpha string
}
RIDSummary is one aggregated per-radio (source_id) row derived from the persisted call_log. It is the durable, all-time view of a radio the Radio IDs list falls back to when the in-memory affiliation tracker has swept the radio out of its 30-minute idle window (issue: RID list "reset after each CC lock").
type RIDSummaryFilter ¶ added in v0.9.3
type RIDSummaryFilter struct {
System string // "" = all systems
SourceID uint32 // 0 = all radios (filters call_log.source_id)
Limit int // <= 0 = all radios
}
RIDSummaryFilter narrows RIDSummaries.
type Retention ¶
type Retention struct {
// contains filtered or unexported fields
}
Retention deletes old data on a schedule:
- call_log rows with started_at older than CallRowMaxAge.
- decoder log-table rows (pager_log, aprs_log, vessel_log, dsc_log, aircraft_log, mdc1200_log, m17_log, location_log) with received_at older than LogRowMaxAge.
- WAV / raw files under FilesRoot whose modification time is older than FilesMaxAge.
File deletion is opt-in by setting FilesRoot; an empty value skips the filesystem sweep. The sweeper is idempotent and safe to run concurrently with the log writers (SQLite serialises).
func NewRetention ¶
func NewRetention(opts RetentionOptions) (*Retention, error)
type RetentionOptions ¶
type RetentionOptions struct {
DB *DB
// FilesRoot is the directory the voice recorder writes WAV / raw
// files under. Empty disables the filesystem sweep.
FilesRoot string
// CallRowMaxAge: call_log rows with started_at older than this are
// deleted. Zero (the default) disables call-row deletion.
CallRowMaxAge time.Duration
// LogRowMaxAge: decoder log-table rows (pager_log, aprs_log,
// vessel_log, dsc_log, aircraft_log, mdc1200_log, m17_log,
// location_log) with received_at older than this are deleted. Zero
// (the default) disables decoder-log deletion.
LogRowMaxAge time.Duration
// FilesMaxAge: files older than this (mtime) are deleted. Zero
// disables file deletion.
FilesMaxAge time.Duration
// Interval between sweeps. Default 1 h.
Interval time.Duration
Log *slog.Logger
}
RetentionOptions configure a Retention sweeper.
type VesselLog ¶ added in v0.2.6
type VesselLog struct {
// contains filtered or unexported fields
}
VesselLog drains KindAISMessage events until ctx cancels or the bus closes.
func NewVesselLog ¶ added in v0.2.6
NewVesselLog wires the log to the bus. Subscription happens at construction so events published before Run() begins aren't lost.
func (VesselLog) Close ¶ added in v0.2.6
func (e VesselLog) Close() error
Close releases the bus subscription and waits (bounded) for Run to drain.