3.3 KiB
Architecture Overview
System Purpose
Real-time visualization of APRS (Automatic Packet Reporting System) packets on an interactive map. Tracks amateur radio stations, weather data, and station telemetry via APRS-IS network feed.
Technology Stack
| Layer | Technology |
|---|---|
| Language | Elixir 1.17+ |
| Web Framework | Phoenix 1.8 + LiveView 1.2 |
| Database | PostgreSQL + PostGIS |
| APRS Parsing | aprs (Gleam BEAM library) |
| Map Client | Leaflet (lazy-loaded) |
| Charts | Chart.js (lazy-loaded) |
| Metrics | PromEx + Telemetry |
| Deployment | Kubernetes (ArgoCD), Fly.io, Docker |
High-Level Data Flow
APRS-IS Network (TCP)
│
▼
Aprsme.Is (GenServer — TCP connection manager)
│ raw APRS frames parsed via aprs.parse/1
▼
PacketProducer (GenStage — water-mark backpressure)
│ buffers incoming packets, signals backpressure upstream
▼
PacketConsumer pool (GenStage consumers)
│ sanitize → normalize → insert into Postgres
│ broadcast to PubSub
├──► Postgres packets table (daily-partitioned)
└──► Broadcasting
├── SpatialPubSub (viewport-filtered, grid-indexed)
├── StreamingPacketsPubSub (ETS-based, bounds-filtered)
└── Phoenix.PubSub (per-callsign topics)
│
▼
LiveView clients (MapLive, PacketsLive, InfoLive)
Mobile WebSocket clients (MobileChannel)
Supervision Tree
Aprsme.Application
├── Aprsme.Repo
├── Phoenix.PubSub
├── Aprsme.Cache / DeviceCache / WeatherCache / RegexCache
├── Aprsme.RateLimiter
├── Aprsme.PacketPipelineSupervisor
│ ├── PacketProducer
│ └── PacketConsumerPool
│ └── PacketConsumer (×N)
├── Aprsme.BroadcastTaskSupervisor
├── Aprsme.Is.IsSupervisor
│ └── Aprsme.Is
├── Aprsme.SpatialPubSub
├── Aprsme.StreamingPacketsPubSub
├── Aprsme.CleanupScheduler
├── Aprsme.PartitionManager
├── Aprsme.PostgresNotifier
├── Aprsme.ConnectionMonitor
├── Aprsme.SignalHandler
├── Aprsme.ShutdownHandler
├── Aprsme.PromEx
├── Cluster modules (optional, via libcluster)
└── AprsmeWeb.Endpoint (Bandit)
Key Design Decisions
Database Partitioning
The packets table is daily-partitioned via PartitionManager. High ingestion volume makes partitioning essential for query performance and retention cleanup.
Spatial Filtering
Rather than broadcasting every packet to every client, SpatialPubSub maintains a 1° grid spatial index. Clients register their map viewport bounds; only packets intersecting those bounds are delivered.
Backpressure
PacketProducer uses water-mark backpressure (>80% buffer → pause TCP; <30% → resume) to prevent memory exhaustion under load spikes.
Symbol System
APRS symbols are rendered client-side from sprite sheets. AprsSymbol provides server-side symbol table lookups for info pages.
Clustering
Optional multi-node deployment via libcluster. Uses leader election to designate the node that maintains the APRS-IS connection. Packets are relayed to other nodes via PacketDistributor.
Lazy Asset Loading
The map bundle (Leaflet + plugins) and chart bundle (Chart.js) are loaded on-demand only for pages that need them, keeping initial page load small.