EAS Station System Architecture
Document Overview
This document provides comprehensive architectural diagrams and flowcharts for the entire EAS Station system, covering all major components, data flows, and operational workflows. It serves as a visual reference for understanding how the system operates from end to end.
Related Documents:
Table of Contents
- System Overview
- Core Components
- Alert Processing Pipeline
- Audio Ingest System
- Broadcast Workflow
- Verification System
- Data Flow Diagrams
- Component Interactions
- Deployment Architecture
- Professional Diagrams
System Overview
High-Level Architecture
EAS Station uses a separated service architecture where hardware access is isolated into dedicated systemd services for reliability and security:
graph TB
subgraph "External Sources"
NOAA[NOAA Weather Service<br>CAP Feeds]
IPAWS[FEMA IPAWS<br>CAP Feeds]
RF[RF Signals<br>162 MHz NOAA WX]
end
subgraph "EAS Station Services"
subgraph "Application Layer"
APP[app<br>Flask Web UI<br>Port 5000]
NOAA_POLL[noaa-poller<br>CAP Polling]
IPAWS_POLL[ipaws-poller<br>CAP Polling]
end
subgraph "Hardware Services"
SDR_SVC[sdr-service<br>SDR + Audio<br>USB Access]
HW_SVC[hardware-service<br>GPIO/OLED/VFD<br>Port 5001]
end
subgraph "Infrastructure"
REDIS[(Redis<br>Cache + IPC)]
DB[(PostgreSQL 17<br>+ PostGIS 3.4)]
ICECAST[Icecast<br>Audio Streaming]
NGINX[nginx<br>Reverse Proxy<br>HTTPS]
end
end
subgraph "Physical Hardware"
SDR_DEV[SDR Receivers<br>RTL-SDR/Airspy]
GPIO[GPIO Pins<br>Relay Control]
OLED[OLED Display<br>SSD1306]
LED[LED Signs<br>Alpha Protocol]
VFD[VFD Display<br>Noritake]
end
subgraph "Outputs"
TX[FM Transmitter]
BROWSER[Web Browser]
STREAM[Audio Streams]
end
%% Data flows
NOAA --> NOAA_POLL
IPAWS --> IPAWS_POLL
NOAA_POLL --> DB
IPAWS_POLL --> DB
RF --> SDR_DEV --> SDR_SVC
APP --> DB
APP --> REDIS
SDR_SVC --> REDIS
SDR_SVC --> ICECAST
HW_SVC --> REDIS
SDR_SVC --> SDR_DEV
HW_SVC --> GPIO --> TX
HW_SVC --> OLED
HW_SVC --> LED
HW_SVC --> VFD
NGINX --> APP
BROWSER --> NGINX
ICECAST --> STREAM
style APP fill:#d4edda
style SDR_SVC fill:#e1f5ff
style HW_SVC fill:#fff3e0
style DB fill:#fff3cd
style REDIS fill:#f8d7da
Service Responsibilities
| Service |
Hardware Access |
Purpose |
Config Source |
| app |
None |
Web UI, API, configuration |
/app-config/.env |
| noaa-poller |
None |
NOAA CAP XML polling |
/app-config/.env |
| ipaws-poller |
None |
FEMA IPAWS polling |
/app-config/.env |
| sdr-service |
USB (/dev/bus/usb) |
SDR capture, audio processing, SAME decoding |
/app-config/.env |
| hardware-service |
GPIO, I2C |
Relay control, displays (OLED/VFD/LED) |
/app-config/.env |
| nginx |
None |
HTTPS termination, reverse proxy |
Environment vars |
| redis |
None |
Cache, inter-service communication |
Volume-based |
| icecast |
None |
Audio streaming |
/app-config/.env |
System Layers
| Layer |
Purpose |
Key Components |
| External Sources |
Alert origins and RF monitoring |
NOAA, IPAWS, SDR receivers |
| Ingestion |
Fetch and validate CAP alerts |
Pollers, validators |
| Data |
Persistent storage and spatial processing |
PostgreSQL, PostGIS |
| Processing |
Business logic and orchestration |
Alert manager, audio/radio controllers |
| Application |
User interface and APIs |
Flask web app, REST endpoints |
| Output |
Broadcast generation |
SAME encoder, TTS, GPIO, LED |
| Verification |
Capture and validate broadcasts |
SDR capture, SAME decoder |
Core Components
Component Dependency Map
graph LR
subgraph "Core Modules"
MODELS[app_core/models.py<br>Database Models]
EXT[app_core/extensions.py<br>Flask Extensions]
ALERTS[app_core/alerts.py<br>Alert Management]
BOUNDARIES[app_core/boundaries.py<br>Spatial Processing]
LOCATION[app_core/location.py<br>Location Services]
end
subgraph "Audio System"
AUDIO_INGEST[app_core/audio/ingest.py<br>Audio Controller]
AUDIO_SOURCES[app_core/audio/sources.py<br>Source Adapters]
AUDIO_METER[app_core/audio/metering.py<br>Monitoring]
AUTO_FWD[app_core/audio/auto_forward.py<br>Automatic Alert Forwarding]
ALERT_FWD[app_core/audio/alert_forwarding.py<br>OTA Alert Forwarding]
end
subgraph "Radio System"
RADIO_MGR[app_core/radio/manager.py<br>Radio Manager]
RADIO_DRV[app_core/radio/drivers.py<br>SoapySDR Drivers]
end
subgraph "Utilities"
EAS_UTIL[app_utils/eas.py<br>SAME Generator]
UTILS[app_utils/__init__.py<br>Common Utilities]
end
subgraph "Web Application"
WEBAPP[webapp/__init__.py<br>Flask App Factory]
ROUTES[webapp/admin/<br>Route Handlers]
TEMPLATES[templates/<br>Jinja2 Views]
end
subgraph "Background Services"
CAP_POLL[poller/cap_poller.py<br>NOAA Poller]
IPAWS_POLL[poller/ipaws_poller.py<br>IPAWS Poller]
end
%% Dependencies
EXT --> MODELS
ALERTS --> MODELS
BOUNDARIES --> MODELS
LOCATION --> MODELS
AUDIO_INGEST --> MODELS
RADIO_MGR --> MODELS
WEBAPP --> EXT
WEBAPP --> ROUTES
ROUTES --> TEMPLATES
ROUTES --> ALERTS
ROUTES --> AUDIO_INGEST
ROUTES --> RADIO_MGR
ROUTES --> EAS_UTIL
CAP_POLL --> ALERTS
CAP_POLL --> AUTO_FWD
IPAWS_POLL --> ALERTS
AUDIO_SOURCES --> AUDIO_INGEST
AUDIO_METER --> AUDIO_INGEST
RADIO_DRV --> RADIO_MGR
AUTO_FWD --> EAS_UTIL
AUTO_FWD --> MODELS
ALERT_FWD --> AUTO_FWD
Alert Processing Pipeline
End-to-End Alert Flow
sequenceDiagram
participant NOAA as NOAA/IPAWS
participant Poller as CAP Poller
participant Validator as Validator
participant AlertMgr as Alert Manager
participant DB as Database
participant Spatial as Spatial Engine
participant Web as Web UI
participant Operator as Operator
NOAA->>Poller: CAP XML Feed
Poller->>Poller: Fetch on Schedule
Poller->>Validator: Parse CAP XML
Validator->>Validator: Schema Validation
Validator->>Validator: Normalize Geometry
alt Valid CAP
Validator->>AlertMgr: Validated Alert
AlertMgr->>AlertMgr: Check Duplicates
alt New Alert
AlertMgr->>DB: Store CAPAlert
AlertMgr->>Spatial: Process Geometry
Spatial->>Spatial: Calculate Intersections
Spatial->>DB: Store Intersections
DB->>Web: Alert Available
Web->>Operator: Dashboard Update
Operator->>Operator: Review Alert
else Duplicate
AlertMgr->>AlertMgr: Log & Skip
end
else Invalid CAP
Validator->>Poller: Reject
Poller->>Poller: Log Error
end
Alert Ingestion Flowchart
flowchart TD
START([Polling Interval<br>Triggered]) --> FETCH[Fetch CAP Feed]
FETCH --> PARSE{Parse XML<br>Successful?}
PARSE -->|No| LOG_ERR[Log Parse Error]
LOG_ERR --> END([End])
PARSE -->|Yes| VALIDATE{Schema<br>Valid?}
VALIDATE -->|No| LOG_SCHEMA[Log Schema Error]
LOG_SCHEMA --> END
VALIDATE -->|Yes| EXTRACT[Extract Alert Data]
EXTRACT --> GEOM{Has<br>Geometry?}
GEOM -->|No| USE_SAME[Use SAME Codes]
GEOM -->|Yes| NORM_GEOM[Normalize Geometry]
USE_SAME --> DUP_CHECK
NORM_GEOM --> DUP_CHECK{Duplicate<br>Check}
DUP_CHECK -->|Duplicate| LOG_DUP[Log Duplicate]
LOG_DUP --> END
DUP_CHECK -->|New| STORE[Store to Database]
STORE --> SPATIAL[Calculate Intersections]
SPATIAL --> UPDATE_UI[Update Web UI]
UPDATE_UI --> NOTIFY[Notify Operators]
NOTIFY --> END
style START fill:#e1f5ff
style END fill:#e1f5ff
style STORE fill:#d4edda
style LOG_ERR fill:#f8d7da
style LOG_SCHEMA fill:#f8d7da
style LOG_DUP fill:#fff3cd
Spatial Processing Detail
flowchart TD
START([Alert with Geometry]) --> TYPE{Geometry<br>Type?}
TYPE -->|Polygon| POLY[Parse Polygon Coords]
TYPE -->|Circle| CIRCLE[Parse Center + Radius]
TYPE -->|SAME Codes| SAME[Lookup FIPS Codes]
POLY --> VALID_POLY{Valid<br>Polygon?}
VALID_POLY -->|No| ERROR[Log Geometry Error]
VALID_POLY -->|Yes| CREATE_GEOM[Create PostGIS Geometry]
CIRCLE --> BUFFER[Create Buffer Geometry]
BUFFER --> CREATE_GEOM
SAME --> LOOKUP[Query Boundary Table]
LOOKUP --> CREATE_GEOM
CREATE_GEOM --> INTERSECT[ST_Intersects Query]
INTERSECT --> BOUNDARIES[(Boundary Table)]
BOUNDARIES --> RESULTS[Intersection Results]
RESULTS --> CALCULATE[Calculate Areas]
CALCULATE --> STORE[(Store Intersections)]
STORE --> DONE([Complete])
ERROR --> DONE
style START fill:#e1f5ff
style DONE fill:#e1f5ff
style STORE fill:#d4edda
style ERROR fill:#f8d7da
Audio Ingest System
Audio Ingest Architecture
Note: Audio metrics are stored in Redis for real-time access, not PostgreSQL. The sdr-service publishes metrics to Redis keys (e.g., eas:audio:metrics), and the app service reads from Redis for UI display.
graph TB
subgraph "sdr-service (systemd)"
subgraph "Audio Sources"
SDR_SRC[SDR Receiver<br>RadioManager]
ALSA_SRC[ALSA Device<br>hw:X,Y]
HTTP_SRC[HTTP Stream<br>Icecast/Shoutcast]
FILE_SRC[Audio File<br>WAV/MP3]
end
subgraph "Audio Controller"
CONTROLLER[AudioIngestController]
PRIORITY[Priority Selection]
BUFFER[Audio Buffer Queue]
end
subgraph "Monitoring"
METER[AudioMeter<br>Peak/RMS Levels]
SILENCE[SilenceDetector<br>Threshold Detection]
HEALTH[HealthMonitor<br>Health Score 0-100]
end
end
subgraph "Redis Cache"
METRICS[(eas:audio:metrics<br>5s TTL)]
WAVEFORM[(eas:waveform:*<br>Real-time)]
COMMANDS[(eas:commands<br>Pub/Sub)]
end
subgraph "app service"
UI[Audio Sources Page<br>/audio/sources]
API[REST API<br>/api/audio/*]
JS[JavaScript Monitor<br>Real-time Updates]
end
subgraph "PostgreSQL"
ALERTS_DB[(AudioAlert<br>Persistent alerts only)]
CONFIG_DB[(AudioSourceConfigDB<br>Source settings)]
end
%% Connections
SDR_SRC --> CONTROLLER
ALSA_SRC --> CONTROLLER
HTTP_SRC --> CONTROLLER
FILE_SRC --> CONTROLLER
CONTROLLER --> PRIORITY
PRIORITY --> BUFFER
BUFFER --> METER
BUFFER --> SILENCE
METER --> HEALTH
SILENCE --> HEALTH
HEALTH --> METRICS
HEALTH --> WAVEFORM
SILENCE --> ALERTS_DB
METRICS --> API
WAVEFORM --> API
CONFIG_DB --> API
API --> UI
UI --> JS
JS --> API
style SDR_SRC fill:#e1f5ff
style CONTROLLER fill:#fff3cd
style METRICS fill:#f8d7da
style UI fill:#d4edda
Audio Source Lifecycle
stateDiagram-v2
[*] --> CONFIGURED: Create Source
CONFIGURED --> STARTING: start()
STARTING --> RUNNING: Capture Thread Started
STARTING --> ERROR: Start Failed
RUNNING --> STOPPING: stop()
RUNNING --> ERROR: Capture Error
RUNNING --> DISCONNECTED: Connection Lost
STOPPING --> STOPPED: Clean Shutdown
ERROR --> STOPPED: Manual Reset
DISCONNECTED --> STARTING: Reconnect Attempt
STOPPED --> STARTING: Restart
STOPPED --> [*]: Delete Source
note right of RUNNING
- Reading audio chunks
- Updating metrics
- Detecting silence
- Calculating health
end note
note right of ERROR
- Log error details
- Alert operators
- Attempt recovery
end note
Audio Metrics Flow
Updated: Metrics flow through Redis for real-time access. Only persistent alerts go to PostgreSQL.
sequenceDiagram
participant Source as Audio Source
participant Controller as Controller
participant Meter as Audio Meter
participant Silence as Silence Detector
participant Health as Health Monitor
participant Redis as Redis Cache
participant DB as PostgreSQL
participant App as app service
participant UI as Web UI
loop Every Audio Chunk (in sdr-service)
Source->>Controller: Audio Data
Controller->>Controller: Convert to PCM
Controller->>Meter: Process Chunk
Meter->>Meter: Calculate Peak/RMS
Meter->>Health: Level Data
Controller->>Silence: Check Audio
Silence->>Silence: Check Threshold
alt Silence Detected
Silence->>Health: Silence Event
Silence->>DB: Store Alert (persistent)
end
Health->>Health: Update Health Score
Health->>Redis: Publish Metrics (5s TTL)
end
loop UI Refresh (in app)
App->>Redis: GET eas:audio:metrics
Redis-->>App: Metrics JSON
App->>UI: Update Display
UI->>UI: Render VU meters
end
Broadcast Workflow
EAS Workflow Process
flowchart TD
START([Operator Initiates<br>EAS Workflow]) --> SELECT_TYPE{Alert Type}
SELECT_TYPE -->|Manual| MANUAL[Select Event Code]
SELECT_TYPE -->|From CAP| CAP_SELECT[Select CAP Alert]
MANUAL --> CONFIG[Configure Message]
CAP_SELECT --> EXTRACT[Extract CAP Data]
EXTRACT --> CONFIG
CONFIG --> SAME_GEN[Generate SAME Header]
SAME_GEN --> VALIDATE{SAME Header<br>Valid?}
VALIDATE -->|No| ERROR[Show Error]
ERROR --> CONFIG
VALIDATE -->|Yes| NARR{Include<br>Narration?}
NARR -->|No| BUILD_AUDIO
NARR -->|Yes| TTS[Generate TTS Audio]
TTS --> TTS_OK{TTS<br>Success?}
TTS_OK -->|No| TTS_ERROR[TTS Error]
TTS_ERROR --> NARR
TTS_OK -->|Yes| BUILD_AUDIO
BUILD_AUDIO[Build Complete Audio]
BUILD_AUDIO --> PREVIEW[Show Preview Player]
PREVIEW --> APPROVE{Operator<br>Approves?}
APPROVE -->|No| CONFIG
APPROVE -->|Yes| STORE_FILE[Store Audio File]
STORE_FILE --> GPIO_CHECK{GPIO<br>Configured?}
GPIO_CHECK -->|Yes| TRANSMIT[Key Transmitter]
GPIO_CHECK -->|No| SKIP_TX[Skip Transmission]
TRANSMIT --> TX_WAIT[Transmit Audio]
TX_WAIT --> UNKEY[Unkey Transmitter]
UNKEY --> LED_CHECK
SKIP_TX --> LED_CHECK
LED_CHECK{LED Sign<br>Configured?}
LED_CHECK -->|Yes| LED_SEND[Send LED Message]
LED_CHECK -->|No| SKIP_LED[Skip LED]
LED_SEND --> LOG
SKIP_LED --> LOG
LOG[Log EAS Message]
LOG --> DB_STORE[Store to Database]
DB_STORE --> COMPLETE([Workflow Complete])
style START fill:#e1f5ff
style COMPLETE fill:#d4edda
style ERROR fill:#f8d7da
style TTS_ERROR fill:#f8d7da
SAME Generation Detail
flowchart LR
START([SAME Generator]) --> PREAMBLE[Generate Preamble<br>16 bytes 0xAB]
PREAMBLE --> HEADER[Build SAME Header<br>ZCZC-ORG-EEE-...]
HEADER --> ENCODE[FSK Encode<br>520.83 baud]
ENCODE --> TRIPLET{Generate<br>3x Headers}
TRIPLET --> ATT_SIGNAL[Generate Attention<br>853Hz + 960Hz]
ATT_SIGNAL --> NARRATION{Has<br>Narration?}
NARRATION -->|Yes| TTS_AUDIO[Append TTS Audio]
NARRATION -->|No| EOM
TTS_AUDIO --> EOM[Generate EOM<br>NNNN x3]
EOM --> SILENCE[Add 1s Silence]
SILENCE --> OUTPUT[(Audio WAV File)]
style START fill:#e1f5ff
style OUTPUT fill:#d4edda
Audio Generation Pipeline
sequenceDiagram
participant Operator
participant Workflow as EAS Workflow
participant SAME as SAME Generator
participant TTS as TTS Service
participant Audio as Audio Builder
participant GPIO as GPIO Controller
participant Storage as File Storage
participant DB as Database
Operator->>Workflow: Submit EAS Form
Workflow->>Workflow: Validate Input
Workflow->>SAME: Generate SAME Header
SAME->>SAME: Build Header String
SAME->>SAME: FSK Encode
SAME->>SAME: Triplet Headers
SAME->>SAME: Attention Signal
SAME-->>Workflow: SAME Audio Buffer
alt Narration Enabled
Workflow->>TTS: Request Narration
TTS->>TTS: Generate Speech
TTS-->>Workflow: TTS Audio Buffer
end
SAME->>SAME: Generate EOM
Workflow->>Audio: Combine Audio Segments
Audio->>Audio: Normalize Levels
Audio->>Storage: Save WAV File
Storage-->>Workflow: File Path
Workflow->>DB: Store EASMessage
alt GPIO Configured
Workflow->>GPIO: Key Transmitter
GPIO->>GPIO: Wait for Audio
GPIO->>GPIO: Unkey Transmitter
end
Workflow-->>Operator: Success + Audio Player
Verification System
SDR Capture & Verification Flow
flowchart TD
START([SDR Monitoring Active]) --> SCAN[Scan Frequencies]
SCAN --> DETECT{Energy<br>Detected?}
DETECT -->|No| WAIT[Wait]
WAIT --> SCAN
DETECT -->|Yes| RECORD[Start Recording]
RECORD --> SQUELCH{Squelch<br>Open?}
SQUELCH -->|No| STOP_REC[Stop Recording]
STOP_REC --> SCAN
SQUELCH -->|Yes| CONTINUE[Continue Recording]
CONTINUE --> TIMEOUT{Max Duration<br>Reached?}
TIMEOUT -->|Yes| FINALIZE
TIMEOUT -->|No| SQUELCH
FINALIZE[Finalize Recording]
FINALIZE --> SAVE[Save Audio File]
SAVE --> DECODE[Attempt SAME Decode]
DECODE --> VALID{Valid SAME<br>Header?}
VALID -->|No| LOG_FAIL[Log Decode Failure]
LOG_FAIL --> SCAN
VALID -->|Yes| EXTRACT[Extract SAME Data]
EXTRACT --> MATCH{Matches<br>Transmitted?}
MATCH -->|Yes| VERIFY_OK[Mark as Verified]
MATCH -->|No| MISMATCH[Log Mismatch]
VERIFY_OK --> STORE[Store Verification]
MISMATCH --> STORE
STORE --> DB[(Database)]
DB --> SCAN
style START fill:#e1f5ff
style VERIFY_OK fill:#d4edda
style LOG_FAIL fill:#f8d7da
style MISMATCH fill:#fff3cd
Verification Workflow
sequenceDiagram
participant TX as Transmitter
participant SDR as SDR Receiver
participant Radio as Radio Manager
participant Decoder as SAME Decoder
participant DB as Database
participant UI as Web UI
TX->>TX: Transmit EAS
TX->>SDR: RF Signal
SDR->>Radio: Audio Stream
Radio->>Radio: Start Recording
Radio->>Radio: Detect SAME Tones
alt SAME Detected
Radio->>Decoder: Audio File
Decoder->>Decoder: Decode SAME Header
alt Valid Header
Decoder->>Decoder: Extract Data
Decoder->>DB: Store Verification
DB->>DB: Match with Transmitted
alt Match Found
DB->>UI: Verification Success
else No Match
DB->>UI: Orphan Reception
end
else Invalid Header
Decoder->>DB: Store Failed Decode
DB->>UI: Decode Failure
end
else No SAME Detected
Radio->>DB: No Activity
end
Data Flow Diagrams
Database Entity Relationships
erDiagram
CAPAlert ||--o{ Intersection : "intersects"
CAPAlert ||--o{ EASMessage : "generates"
Boundary ||--o{ Intersection : "affected_by"
RadioReceiver ||--o{ RadioReceiverStatus : "has"
AudioSourceMetrics ||--|| AudioHealthStatus : "health_of"
AudioAlert }o--|| AudioSourceMetrics : "triggered_by"
LEDMessage }o--|| CAPAlert : "displays"
CAPAlert {
int id PK
string identifier UK
string event
string severity
string urgency
timestamp sent
timestamp expires
geometry geom
string area_desc
}
Boundary {
int id PK
string name
string type
geometry geom
string description
}
Intersection {
int id PK
int cap_alert_id FK
int boundary_id FK
float intersection_area
float coverage_percentage
}
EASMessage {
int id PK
int cap_alert_id FK
string same_header
bytea audio_data
timestamp created_at
}
RadioReceiver {
int id PK
string name
string device_type
string serial
int frequency
bool enabled
}
AudioSourceMetrics {
int id PK
string source_name
string source_type
float peak_level_db
float rms_level_db
bool silence_detected
timestamp timestamp
}
Data Flow: CAP to Broadcast
graph LR
subgraph "External"
CAP[CAP XML]
end
subgraph "Ingestion"
PARSE[XML Parser]
VALID[Validator]
end
subgraph "Storage"
ALERT[(CAPAlert)]
BOUND[(Boundary)]
INTER[(Intersection)]
end
subgraph "Processing"
SPATIAL[Spatial Engine]
FILTER[Alert Filter]
end
subgraph "Presentation"
WEB[Web Dashboard]
API[REST API]
end
subgraph "Broadcast"
WORKFLOW[EAS Workflow]
SAME[SAME Generator]
TTS[TTS Engine]
AUDIO[Audio Builder]
end
subgraph "Output"
WAV[WAV File]
TX[Transmitter]
LED[LED Sign]
end
CAP --> PARSE
PARSE --> VALID
VALID --> ALERT
ALERT --> SPATIAL
BOUND --> SPATIAL
SPATIAL --> INTER
INTER --> FILTER
FILTER --> WEB
FILTER --> API
WEB --> WORKFLOW
WORKFLOW --> SAME
WORKFLOW --> TTS
SAME --> AUDIO
TTS --> AUDIO
AUDIO --> WAV
WAV --> TX
WORKFLOW --> LED
style CAP fill:#e1f5ff
style ALERT fill:#fff3cd
style BOUND fill:#fff3cd
style INTER fill:#fff3cd
style WAV fill:#d4edda
Component Interactions
Web Request Flow
sequenceDiagram
participant Browser
participant Flask as Flask App
participant Auth as Auth Layer
participant Routes as Route Handler
participant Core as Core Module
participant DB as Database
participant Template as Jinja2 Template
Browser->>Flask: HTTP Request
Flask->>Flask: CSRF Validation
Flask->>Auth: Check Authentication
alt Authenticated Route
Auth->>Auth: Verify Session
alt Valid Session
Auth->>Routes: Allow Access
else Invalid
Auth-->>Browser: Redirect to Login
end
else Public Route
Flask->>Routes: Route Request
end
Routes->>Core: Call Business Logic
Core->>DB: Query Data
DB-->>Core: Result Set
Core-->>Routes: Processed Data
Routes->>Template: Render Template
Template-->>Routes: HTML
Routes-->>Flask: HTTP Response
Flask-->>Browser: Send Response
System Health Monitoring
flowchart TD
START([Health Check Triggered]) --> CPU[Check CPU Usage]
CPU --> MEMORY[Check Memory Usage]
MEMORY --> DISK[Check Disk Usage]
DISK --> DB_CONN[Check DB Connection]
DB_CONN --> POLLER[Check Poller Status]
POLLER --> SDR[Check SDR Status]
SDR --> AUDIO[Check Audio System]
AUDIO --> AGGREGATE[Aggregate Health Data]
AGGREGATE --> SCORE{Calculate<br>Health Score}
SCORE -->|< 50| CRITICAL[Status: Critical]
SCORE -->|50-79| WARNING[Status: Warning]
SCORE -->|>= 80| HEALTHY[Status: Healthy]
CRITICAL --> LOG
WARNING --> LOG
HEALTHY --> LOG
LOG[Log Health Status]
LOG --> STORE[Store to Database]
STORE --> UPDATE[Update UI]
UPDATE --> END([Complete])
style START fill:#e1f5ff
style CRITICAL fill:#f8d7da
style WARNING fill:#fff3cd
style HEALTHY fill:#d4edda
style END fill:#e1f5ff
Multi-Service Coordination
graph TB
subgraph "Systemd Services"
APP[app<br>Flask Web + API]
NOAA_POLL[noaa-poller<br>CAP Polling]
IPAWS_POLL[ipaws-poller<br>FEMA Polling]
SDR_SVC[sdr-service<br>SDR + Audio]
HW_SVC[hardware-service<br>GPIO/Displays]
DB[PostgreSQL<br>+ PostGIS]
REDIS[Redis<br>Cache + IPC]
ICECAST[Icecast<br>Audio Streaming]
NGINX[nginx<br>HTTPS Proxy]
end
subgraph "Shared Storage"
VOL_CONFIG["/app-config/.env<br>Configuration"]
VOL_DATA[PostgreSQL Data]
VOL_REDIS[Redis Data]
VOL_CERTS[SSL Certificates]
end
subgraph "Network"
NET[Localhost + Unix Sockets]
end
subgraph "External"
OPERATOR[Browser<br>HTTPS :443]
NOAA_API[NOAA API]
IPAWS_API[IPAWS API]
USB[USB Devices]
I2C[I2C/GPIO]
end
%% Network connections
APP --> NET
NOAA_POLL --> NET
IPAWS_POLL --> NET
SDR_SVC --> NET
HW_SVC --> NET
DB --> NET
REDIS --> NET
ICECAST --> NET
NGINX --> NET
%% Storage access
APP --> VOL_CONFIG
NOAA_POLL --> VOL_CONFIG
IPAWS_POLL --> VOL_CONFIG
SDR_SVC --> VOL_CONFIG
HW_SVC --> VOL_CONFIG
DB --> VOL_DATA
REDIS --> VOL_REDIS
NGINX --> VOL_CERTS
%% External connections
NGINX --> OPERATOR
NOAA_POLL --> NOAA_API
IPAWS_POLL --> IPAWS_API
SDR_SVC --> USB
HW_SVC --> I2C
%% Internal dependencies
APP --> REDIS
APP --> DB
SDR_SVC --> REDIS
SDR_SVC --> ICECAST
HW_SVC --> REDIS
style APP fill:#d4edda
style SDR_SVC fill:#e1f5ff
style HW_SVC fill:#fff3e0
style DB fill:#fff3cd
style REDIS fill:#f8d7da
Service Communication Patterns
sequenceDiagram
participant Browser
participant nginx
participant app
participant Redis
participant sdr-service
participant hardware-service
participant DB
Browser->>nginx: HTTPS Request
nginx->>app: HTTP Proxy
app->>DB: Query alerts
DB-->>app: Alert data
app->>Redis: Publish command
Redis-->>sdr-service: Subscribe notification
sdr-service->>sdr-service: Process SDR audio
sdr-service->>Redis: Audio metrics
app->>Redis: GPIO command
Redis-->>hardware-service: GPIO trigger
hardware-service->>hardware-service: Activate relay
hardware-service->>Redis: Status update
app-->>nginx: Response
nginx-->>Browser: HTTPS Response
Deployment Architecture
Single-Host Deployment (Raspberry Pi 5)
graph TB
subgraph "Raspberry Pi 5 Hardware"
subgraph "Software Stack"
subgraph "Systemd Services"
APP_C[Web Application<br>Python 3.12 + Flask]
POLL_C[CAP Poller<br>Background Service]
IPAWS_C[IPAWS Poller<br>Background Service]
DB_C[PostgreSQL 17<br>PostGIS 3.4]
end
end
subgraph "Peripherals"
SDR1[RTL-SDR<br>USB 3.0]
SDR2[Airspy<br>USB 3.0]
GPIO_HAT[GPIO Relay HAT<br>Transmitter Control]
AUDIO_HAT[Audio DAC HAT<br>Balanced Output]
ETH[Gigabit Ethernet]
end
subgraph "Storage"
NVME[NVMe SSD<br>PCIe Gen 2]
end
end
subgraph "External Connections"
INTERNET[Internet<br>CAP Feeds]
TX_EXT[FM Transmitter]
LED_EXT[LED Sign<br>RS-232]
MONITOR[HDMI Monitor]
end
APP_C --> DB_C
POLL_C --> DB_C
IPAWS_C --> DB_C
DB_C --> NVME
APP_C --> NVME
SDR1 --> APP_C
SDR2 --> APP_C
GPIO_HAT --> APP_C
AUDIO_HAT --> APP_C
ETH --> INTERNET
GPIO_HAT --> TX_EXT
APP_C --> LED_EXT
APP_C --> MONITOR
style APP_C fill:#d4edda
style DB_C fill:#fff3cd
style NVME fill:#f8d7da
External Database Deployment
graph TB
subgraph "Application Server"
APP[EAS Station App<br>Flask + Gunicorn]
POLL[CAP Pollers<br>NOAA + IPAWS]
end
subgraph "Database Server"
PG[PostgreSQL 17<br>Dedicated Server]
PGIS[PostGIS 3.4<br>Extension]
BACKUP[Automated Backups<br>pg_dump]
end
subgraph "Network"
FW[Firewall<br>Port 5432]
VPN[VPN Tunnel<br>Optional]
end
subgraph "Monitoring"
PROM[Prometheus<br>Metrics]
GRAF[Grafana<br>Dashboards]
end
APP --> FW
POLL --> FW
FW --> PG
PG --> PGIS
PG --> BACKUP
VPN --> FW
APP --> PROM
PG --> PROM
PROM --> GRAF
style APP fill:#d4edda
style PG fill:#fff3cd
style FW fill:#f8d7da
Summary
This architecture document provides visual representations of:
- System Overview - High-level component layout and data flows
- Core Components - Module dependencies and relationships
- Alert Processing - End-to-end CAP ingestion and validation
- Audio Ingest - Real-time audio monitoring architecture
- Broadcast Workflow - EAS message generation and transmission
- Verification System - SDR capture and SAME decoding
- Data Flows - Database entities and information routing
- Component Interactions - Service coordination and communication
- Deployment - Physical and logical deployment architectures
These diagrams serve as living documentation that should be updated as the system evolves.
Related Resources:
Professional Diagrams
For enhanced clarity and presentation, the following professional SVG diagrams are available:
Alert Processing Pipeline
Detailed flowchart showing the complete CAP alert ingestion workflow from external sources through validation, parsing, spatial processing, and database storage.

File: ../assets/diagrams/alert-processing-pipeline.svg
EAS Broadcast Workflow
Step-by-step workflow diagram illustrating the complete EAS message generation and transmission process, from alert selection through SAME encoding to broadcast completion.

File: ../assets/diagrams/broadcast-workflow.svg
Audio Source Routing Architecture
Block diagram showing multi-source audio ingestion architecture with adapters, priority selection, monitoring systems, and database integration.

File: ../assets/diagrams/audio-source-routing.svg
Hardware Deployment Architecture
Physical deployment diagram showing Raspberry Pi 5 hardware configuration with all peripherals, storage, and external connections.

File: ../assets/diagrams/system-deployment-hardware.svg
Last Updated: 2025-12-16
Diagram Format: Mermaid.js (Markdown) and SVG (Professional graphics)