Documentation
¶
Index ¶
- Constants
- type BBox
- type CacheEntry
- type HeatPoint
- type HeatmapResult
- type HistoryStore
- type LatLon
- type MemCache
- type PersistentCache
- func (p *PersistentCache) Close()
- func (p *PersistentCache) Gen() uint64
- func (p *PersistentCache) Lookup(callsigns []string) map[string]LatLon
- func (p *PersistentCache) QueryBBox(bbox BBox, maxAge time.Duration) []Station
- func (p *PersistentCache) QueryHeatmap(window time.Duration, bbox BBox) (*HeatmapResult, error)
- func (p *PersistentCache) Reconfigure(hdb HistoryStore) error
- func (p *PersistentCache) RecordRxEvent(ev RxEvent)
- func (p *PersistentCache) Update(entries []CacheEntry)
- type Position
- type RxEvent
- type Station
- type StationStore
- type Weather
Constants ¶
const MaxStations = 50000
MaxStations is the hard upper bound on resident MemCache entries. The TTL-based prune (memMaxAge, default 24h) is the primary eviction path for routine operation; this cap is a safety bound for the pathological case of an IS feed with a broad filter heard over a long window. When exceeded, prune evicts oldest-LastHeard first until the cap is satisfied. Sized to bound resident memory at roughly 25 MiB even with full 200-position trails on every station.
const MaxTrailLen = 200
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CacheEntry ¶
type CacheEntry struct {
Key string
IsObject bool
Killed bool // object/item with Live==false → delete from cache
Callsign string
// Source is the AX.25 source callsign that transmitted the packet —
// the originating station of an object/item. Empty for ordinary
// station packets, where Callsign already is the source.
Source string
Lat, Lon float64
HasPos bool
Alt float64
HasAlt bool
Speed float64
Course int
HasCourse bool
Symbol [2]byte
Via string
Path []string
Hops int
Direction string
Gated bool // arrived as the inner packet of an Internet-to-RF third-party gate
Channel uint32
Comment string
Weather *Weather
Timestamp time.Time
}
CacheEntry is the output of ExtractEntry, ready for MemCache.Update().
func ExtractEntry ¶
func ExtractEntry(decoded *aprs.DecodedAPRSPacket, source, dir string, ch uint32) []CacheEntry
ExtractEntry converts a decoded APRS packet into cache update(s). Most packets produce one entry; a packet carrying an object/item produces an entry for the object plus (if the originator has a position) an entry for the originating station. Returns nil if the packet has no useful station data.
type HeatPoint ¶ added in v0.14.11
HeatPoint is one aggregated heatmap bucket: a coordinate and the total number of directly-received packets attributed to it in the query window.
type HeatmapResult ¶ added in v0.14.11
HeatmapResult is the aggregate returned by a heatmap query. Points are the located buckets, MaxCount is the largest single-bucket count (for client-side weight normalization), and Unlocatable counts packets whose attributed transmitter had no known position in the window.
type HistoryStore ¶
type HistoryStore interface {
WriteEntries(entries []CacheEntry) error
LoadRecent(maxAge time.Duration, trailLimit int) (map[string]*Station, error)
Prune(maxAge time.Duration) error
QueryHeatmap(window time.Duration, bbox BBox) (*HeatmapResult, error)
RecordRxEvent(ev RxEvent) error
Close() error
}
HistoryStore is the persistence interface consumed by PersistentCache. historydb.DB implements it; the interface lives here to avoid an import cycle.
type MemCache ¶
type MemCache struct {
// contains filtered or unexported fields
}
MemCache is an in-memory StationStore for RF-scale traffic. Safe for concurrent use.
func NewMemCache ¶
func (*MemCache) Hydrate ¶
Hydrate bulk-loads stations into the cache, typically from a persistent store on startup. Existing entries are not overwritten.
func (*MemCache) QueryBBox ¶
QueryBBox returns stations within the bounding box heard within maxAge.
func (*MemCache) Update ¶
func (c *MemCache) Update(entries []CacheEntry)
Update applies cache entries produced by ExtractEntry.
type PersistentCache ¶
type PersistentCache struct {
// contains filtered or unexported fields
}
PersistentCache wraps a MemCache with optional SQLite persistence. When persistence is disabled, it behaves identically to a bare MemCache. Persistence can be toggled at runtime via Reconfigure.
func NewPersistentCache ¶
func NewPersistentCache(logger *slog.Logger) *PersistentCache
NewPersistentCache creates a PersistentCache with persistence disabled. Call Reconfigure to enable it.
func (*PersistentCache) Close ¶
func (p *PersistentCache) Close()
Close shuts down persistence and the in-memory cache.
func (*PersistentCache) Gen ¶
func (p *PersistentCache) Gen() uint64
Gen returns the in-memory generation counter (ETag support).
func (*PersistentCache) Lookup ¶
func (p *PersistentCache) Lookup(callsigns []string) map[string]LatLon
Lookup delegates to the in-memory cache.
func (*PersistentCache) QueryBBox ¶
func (p *PersistentCache) QueryBBox(bbox BBox, maxAge time.Duration) []Station
QueryBBox delegates to the in-memory cache.
func (*PersistentCache) QueryHeatmap ¶ added in v0.14.11
func (p *PersistentCache) QueryHeatmap(window time.Duration, bbox BBox) (*HeatmapResult, error)
QueryHeatmap returns aggregated directly-received-packet heat over the window within bbox. Returns an empty result when persistence is disabled.
func (*PersistentCache) Reconfigure ¶
func (p *PersistentCache) Reconfigure(hdb HistoryStore) error
Reconfigure enables, disables, or changes the persistence backend. Pass a non-nil HistoryStore to enable, or nil to disable.
func (*PersistentCache) RecordRxEvent ¶ added in v0.14.11
func (p *PersistentCache) RecordRxEvent(ev RxEvent)
RecordRxEvent persists one heatmap reception event, best-effort. It is a no-op when persistence is disabled. Called once per RF frame from the ingest edge; failures are logged, never surfaced, so a history-write hiccup cannot disrupt packet processing (mirrors Update).
func (*PersistentCache) Update ¶
func (p *PersistentCache) Update(entries []CacheEntry)
Update applies entries to the in-memory cache and, if persistence is enabled, writes them to the history database.
type Position ¶
type Position struct {
Lat float64
Lon float64
Alt float64
HasAlt bool
Speed float64 // knots
Course int
HasCourse bool
Via string
Path []string
Hops int
Direction string
Gated bool
Channel uint32
Comment string
Timestamp time.Time
}
Position is a single position fix with metadata. Per-position metadata (Via, Path, etc.) captures the packet context at the time this position was reported, enabling accurate historical trail display.
type RxEvent ¶ added in v0.14.11
type RxEvent struct {
Timestamp time.Time
AttrKey string
Hops int
Lat float64
Lon float64
HasPos bool
}
RxEvent is one directly-received-packet reception, ready to persist for the heatmap. It is recorded once per physical RF frame at the ingest edge, not per station-cache entry, so a single frame counts once regardless of how many cache entries it produces. AttrKey is the station key of the last RF transmitter (origin for direct, last-hop digipeater for digipeated); Lat/Lon are set only when HasPos is true, otherwise the position is resolved at query time from AttrKey's latest known fix.
func BuildRxEvent ¶ added in v0.14.11
func BuildRxEvent(pkt *aprs.DecodedAPRSPacket) (RxEvent, bool)
BuildRxEvent derives the heatmap reception event for a directly-received RF frame, returning ok=false when the frame should not contribute heat. It is the single source of truth for heatmap attribution:
- Internet-to-RF gated frames (third-party) never count — nothing of that station was heard on our radio.
- Digipeated frames (a last H-bit "*" digipeater in the path) are attributed to that digipeater's station key with HasPos=false; the originating station's coordinates are deliberately not used.
- Direct frames are attributed to the origin, carrying the packet's own coordinates when present. Positionless direct frames (messages, status) still count, resolved at query time from the origin's known position.
type Station ¶
type Station struct {
Key string // "stn:W1ABC-9" or "obj:SHELTER1"
Callsign string // display name (callsign or object/item name)
IsObject bool // true for APRS objects/items
Source string // originating station callsign for objects/items; empty for regular stations
Positions []Position // newest first; cap at MaxTrailLen. Static stations have len 1.
Symbol [2]byte // [table, code]
Via string // "rf" or "is"
Path []string // digi path from AX.25 header
Hops int // count of H-bit digi addresses
Direction string // "RX", "TX", "IS"
Gated bool // Internet-to-RF gated (inner of a third-party packet)
Channel uint32
Comment string
Weather *Weather // nil if not a weather station
LastHeard time.Time
// LastDirectHeard is the timestamp of the most recent reception heard
// directly on RF (RX, zero digi hops). Set only by direct receptions and
// never advanced by digipeated/gated/IS copies, so the Live Map "Direct
// RX" filter can age a station out of the selected time window even when
// the fix stays classified as direct for display (issues #130 + #349).
// Zero value means the station has never been heard directly.
LastDirectHeard time.Time
}
Station is the last-known state of a single station or object. One entry per composite key. Position history is kept for moving stations only — static stations that beacon the same coordinates repeatedly don't accumulate trail points.
type StationStore ¶
type StationStore interface {
// QueryBBox returns stations within the bounding box heard within maxAge.
// Returned slice is a snapshot — caller owns it.
QueryBBox(bbox BBox, maxAge time.Duration) []Station
// Lookup returns positions for the given callsigns regardless of bbox.
// Used for server-side digi path resolution. Missing callsigns omitted.
Lookup(callsigns []string) map[string]LatLon
}
StationStore is the read interface consumed by the API handler. MemCache implements it for RF-scale traffic; a future DB-backed implementation can serve full APRS-IS feeds behind this same interface.
type Weather ¶
type Weather struct {
Temp float64
HasTemp bool
WindSpeed float64
HasWindSpeed bool
WindDir int
HasWindDir bool
WindGust float64
HasWindGust bool
Humidity int
HasHumidity bool
Pressure float64
HasPressure bool
Rain1h float64
HasRain1h bool
Rain24h float64
HasRain24h bool
Snow24h float64
HasSnow24h bool
Luminosity int
HasLuminosity bool
}