aprs.me/docs/architecture/overview.md

3.3 KiB
Raw Blame History

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.