Help & Operations Guide

Complete documentation for operating and maintaining your EAS Station™

Operations Flow

  • 1

    Monitor & Capture

    CAP polling + SDR ingest keep alerts flowing in.

  • 2

    Analyze & Verify

    Rule filters, analytics, and operator review.

  • 3

    Broadcast & Notify

    Builder, audio playout, GPIO, and displays.

  • 4

    Audit & Improve

    Archive, compliance, and analytics feedback.

Safety Expectations

  • Isolated Networks: Operate the stack in isolated development or staging networks disconnected from broadcast transmitter controls.
  • No Live Credentials: Do not ingest live IPAWS credentials, dispatch feeds, or mission-critical telemetry into this environment.
  • Validated Workflows: Validate any workflows on certified FCC equipment before using them in real-world alerting scenarios.
  • Legal Review: Review the repository Terms of Use and Privacy Policy with your operators prior to onboarding.

Getting Started

  1. Review Documentation
    The About page covers system goals, core services, and the complete software stack. The Attribution & Credits page lists every open-source dependency, data source, and licensing detail.
  2. Run Installation Script
    Run cd bare-metal && sudo bash scripts/install.sh to install all dependencies and set up systemd services.
  3. Configure Environment
    Edit /opt/eas-station/.env, set secure secrets, and update database connection details.
  4. Start Services
    Run sudo systemctl start eas-station.target to launch all EAS Station™ services.

Routine Operations

Navigate to http://<host>:5000 in a modern browser.

Log in with administrator credentials created during initial setup.

The main dashboard (/) opens with a System Status strip above the map — four at-a-glance tiles so you don't have to navigate away to know whether the station is actually working:

  • Active Alerts — count of currently active CAP products; links to Alerts.
  • EAS Decoder — whether the decoder monitor is running and audio is flowing or silent; links to EAS Decoder Monitor.
  • System Health — overall application health from /health; links to System Health.
  • Radio Receivers — number of configured SDR receivers; links to Radio Receivers.

The strip refreshes itself every 30 seconds, independent of the map's own refresh cycle.

Tip: Bookmark the dashboard for quick access during operations.

EAS Station™ lets each operator pick how positional and motion values are displayed across the UI. Selections are saved in your browser's localStorage (per-device, per-browser) and apply immediately wherever the affected values appear.

Configurable units

  • Coordinates: D.dddd (decimal degrees, e.g. 41.450123°) or DMS (degrees / minutes / seconds, e.g. 41°27'00.4"N).
  • Altitude: meters (m) or feet (ft).
  • Speed: knots (kn), miles per hour (mph), kilometers per hour (km/h) or meters per second (m/s).
  • Distance: meters (m), feet (ft), miles (mi) or nautical miles (nmi).

Where to change them

  1. Your username menu (top-right) → Display Units opens the global picker from any page. Unit choices are personal to your browser, which is why they live in your account menu rather than under Settings.
  2. Each affected page also has an inline Units button:
Tip: Changes take effect instantly — every dashboard with cached telemetry re-renders the moment you flip a unit, so you don't need to wait for the next poll or reload the page.
Scope: the preference is stored in your browser only — it's not synced server-side, so each browser/device you use can have its own setting, and clearing site data will reset to defaults (D.dddd, m, kn, m).

  1. Open the Dashboard to view active CAP products on the interactive map.
  2. Use the Statistics tab to analyze severity, event types, and historical counts.
  3. Check System Health for CPU, memory, disk, receiver, and audio pipeline heartbeat metrics.

Every map follows the theme you pick from the palette icon in the navigation bar. The basemap is deliberately toned down and the alert area is drawn with a glow and a white outline, so the warning is the only thing competing for your attention — the same treatment used on the shareable alert images. Switching themes restyles an open map instantly; no reload needed.

Interactive Map: Geographic visualization
Statistics: Historical analysis

Weather alerts (a category of Met — Severe Thunderstorm/Tornado Warnings, Special Weather Statements, and the like) get a Radar toggle on both the dashboard map and the alert detail page's Coverage Map, overlaying NEXRAD Level III base reflectivity (product N0Q) from Iowa Environmental Mesonet's public WMS-T mosaic — showing conditions as of the alert's issue time for a past alert, or the live scan for one still active. The alert detail page also has a Radar Loop card below the map: click Generate Loop to render a disk-cached sequence of frames from that same Level III mosaic, spanning the alert's full duration (5-minute cadence, capped around 3 hours), so you can step or play through how the weather actually moved rather than seeing only one frozen moment. First generation takes a few seconds per frame; instant on every view after that.

Weather alerts can also be shared as an animated video — from the alert detail page's Actions → Export Social Image menu, pick any size under the Animated Video · MP4 heading. It plays the same radar loop described above, but always starts with a few frames from before the alert was issued (the storm on approach) and only reveals the warning polygon on the frame matching the moment the alert actually went out — it can never show the polygon earlier than the real issue time. MP4 rather than an animated GIF: Facebook and most other social platforms re-encode an uploaded GIF into a video anyway, so this skips that lossy round-trip and produces a sharper, smaller file for the same loop.

The map behind every export defaults to OpenStreetMap tiles, darkened to match the card. Settings → Map Tiles can switch it to CARTO Dark Matter instead — a basemap style that's authored dark and minimal from the start rather than a light map force-darkened after the fact, so roads and place names read far more cleanly. It needs a free CARTO API key (a signup link is right on the settings page); with no key configured, or if a tile fetch ever fails, exports fall back to the default OpenStreetMap look automatically.

A separate High-Resolution Radar Loop card sits right below the standard one, offering raw NEXRAD Level II data (from NOAA's public archive, decoded on the appliance) instead of the Level III mosaic — a noticeably finer-grained image from the single nearest radar site, plus a Reflectivity / Velocity selector: velocity (motion toward or away from the radar) is a Level II–only product with no Level III equivalent, useful for spotting rotation. This is a deliberately separate, distinctly labeled view, not a replacement for the standard loop above it — the two use different data sources and can look different for the same alert, which is exactly why they're not merged into one toggle. Only available within roughly 230 miles of a radar site; outside that range (or if the site's data has a temporary gap) it reports no coverage rather than silently showing an empty frame. First generation is noticeably slower than the standard loop — each frame is a real multi-megabyte volume-scan download, not a small map tile.

High-Resolution Radar Loop card for a Tornado Warning roughly 90 miles from its radar site, showing real reflectivity structure with some beam-to-beam banding softened by the renderer's smoothing pass
A real High-Resolution (Level II) Radar Loop example, ~90 miles from its radar site — distance from the transmitter is the dominant factor in how sharp a frame looks. Near a site, expect crisp, RadarScope-like detail; at range like this, expect a somewhat softer image (a light smoothing pass keeps real beam-to-beam gaps from reading as a harsh grid) — both are genuine Level II data, not a rendering shortcut.

As a life-safety appliance, EAS Station has several checks specifically aimed at catching failures that a normal "is the process still running?" check misses.

  • Hang watchdog: the audio-processing and alert-poller services now send periodic heartbeats to systemd (WatchdogSec=). A crashed process was already restarted by Restart=always; now a process that's still running but deadlocked is killed and restarted too. No configuration needed.
  • Uptime Heartbeat (Settings → Uptime Heartbeat, /admin/heartbeat/): every other health check on this system is inward-facing. This one pings an external monitoring service (e.g. healthchecks.io) on a schedule, so total loss of power, network, or a wedged OS — conditions this box cannot report on itself — still raises an alarm, from outside the box.
  • Tickstem Uptime Monitor (Settings → Uptime Monitoring, /admin/tickstem/): the opposite direction from Uptime Heartbeat above — instead of this box pinging out, Tickstem polls a public URL (normally this box's own /health) from the outside and alerts if it stops answering. Create, pause, resume, and delete the monitor from this page using a Tickstem account API key.
  • healthchecks.io Per-Service Heartbeats (also on Settings → Uptime Monitoring): a third outbound direction, same shape as Uptime Heartbeat above but one check per critical service (from the System Health page) rather than one combined ping. Each service's check is pinged only while that specific service is active, so a missed ping on healthchecks.io's side names the exact subsystem that failed. Create, pause, resume, and delete checks from this page using a healthchecks.io account API key — unlike Tickstem's Monitors API, healthchecks.io has no "poll a public URL" product, so it only ever manages this per-service heartbeat signal, not the URL-polling one above.
  • Backup restore verification (Admin → Backups): a backup that's never been restored isn't a proven backup. This restores the latest backup's database into a throwaway scratch database, checks it, and always deletes the scratch database afterward. Run it manually with "Verify Latest Backup Now", or enable "Verify after each backup" on the auto-backup schedule.
  • Admin Operations (Settings → Admin Operations, /admin/operations): a quick-action maintenance page distinct from the full Backups page above. It covers four things — database health and a "Optimize Database" (VACUUM ANALYZE) button; a quick one-click backup shortcut (visit Admin → Backups instead to browse, restore, or schedule backups); alert-boundary intersection recalculation, for when coverage percentages look wrong; and the one-click System Upgrade, which runs git pull, updates this venv's Python dependencies, applies pending database migrations, and restarts every EAS Station service — equivalent to the manual bare-metal upgrade steps, without needing shell access. Because that restart includes the web service itself, expect the page to disconnect briefly during an upgrade; check Admin → Backups or the system logs afterward to confirm it landed. Take a backup first.
  • USB sound card auto-configuration: every Raspberry Pi's onboard audio (the HDMI outputs) is playback-only — there is no line-in or mic on the board, so it can never be an ingest source. When exactly one USB sound card (a DAC such as an HS100B, a HAT, etc.) is plugged in and nothing has been configured yet, the station wires it up automatically at startup: an ALSA source named usb-audio-<card> appears under Monitor → Audio & Radio → Audio Ingestion (manage or remove it there like any other source), and it becomes the target for local EAS alert playback (Settings → EAS Settings → Audio Player) if that field was still at its untouched default. If more than one external card is present, or a source/output is already configured, nothing is changed automatically — add or edit it manually instead.
  • Clock sync monitoring (System Health): SAME timing and Required Weekly Test scheduling both depend on correct system time. The Clock Sync card shows chrony/NTP synchronization status and offset, and a compliance alert fires if the clock drifts or loses sync.
  • Combined feed-loss alarm (System Health + Settings → Alert Poller): the Alert Feeds card shows NOAA and IPAWS polling staleness independently, so one dead feed is never hidden behind the other one still succeeding. An alert fires only when both feeds have stalled past the configurable threshold.

Admin → GPIO → Interlocks (/admin/gpio/interlocks) defines mutual-exclusion groups: two or more relay pins that must never be energized at the same time — the classic case is two PTT lines that would key two transmitters into each other if both fired.

  • By default, activating one pin while another group member is already active is refused and recorded as a failure in the GPIO Activation Log; the already-active relay is left running.
  • Force-release conflict (per group, off by default) de-energizes the active relay before activating the new one instead of refusing.
  • If two grouped pins are both assigned a behavior that holds for the whole broadcast (Transmitter PTT, Audio Playout, Duration of Alert, or Audio Mute), only one will ever actually key during a real alert — a warning for this appears on the Interlocks page and on GPIO Control's diagnostics.
  • Like the rest of GPIO configuration, a new or edited group takes effect only after restarting the eas-station-gpio service.

A GPIO pin can be configured as an Input instead of an Output relay — a physical button or contact closure wired into the Pi that triggers an action when pressed. Configure this on Admin → GPIO → Pin Map: select Input for the pin, then choose its action.

  • Run RWT Now — the same action as the manual "Run Test Now" button on the RWT Schedule page. Skipped (and logged) if a broadcast is already in progress.
  • Forward Last Alert — re-broadcasts whichever EAS message was most recently generated, the same way a message's "Resend" button does. Skipped (and logged) if nothing broadcastable exists yet, or if a broadcast is already in progress.
  • Acknowledge Dead Air — silences the dead-air rack buzzer, the same way the Acknowledge button on the Audio Health dashboard does (the tower light stays lit until audio actually returns). Station-wide — acknowledgement is scoped to the current alarm episode, not a single source, so no source selection is needed. Skipped (and logged) if no dead-air alarm is currently active.
  • Dump / Abort Broadcast — forcibly stops whatever is currently on air: terminates the playback process to cut the in-progress message, then always plays the required End-Of-Message tone burst before releasing the relay (47 CFR 11.61(a) — an EAS message must never simply go silent). Only after that EOM attempt completes does it release the transmitter relay the same way a broadcast ending normally does, and write an entry to the tamper-evident audit ledger (including whether the EOM burst actually sent). No-ops (and logged) if nothing is currently playing. Requires a sustained 3-second hold, not a tap (configurable 1–10s per pin) — a momentary bump must never be able to abort a live broadcast.
  • Each action may only be assigned to one pin — assigning the same action to two pins is rejected when you save.
  • Like the rest of GPIO configuration, a new or edited input pin takes effect only after restarting the eas-station-gpio service.

When enabled, lower-priority alerts (watches, advisories, routine updates) are held for a configurable timer before broadcasting instead of airing immediately, giving an operator a window to review them first.

  • Immediate urgency or Extreme severity alerts always bypass the gate and broadcast right away, exactly as they do today — gating never delays a Tornado Warning or similarly urgent product.
  • Held alerts appear on Broadcast → Pending Alerts with a live countdown.
  • Approve broadcasts a pending alert immediately, skipping the rest of the wait.
  • Cancel permanently blocks a pending alert — it will never air, even after its timer expires.
  • If nobody acts, the alert auto-releases and broadcasts normally once the hold-off timer reaches zero.
  • A non-empty queue also shows on the hardware: the USB tower/stack light (and the navbar's stack-light widget) shows a distinct color — blue by default, configurable under Admin → Hardware Settings — and a "Pending Review" scene rotates onto any connected OLED, LED sign, or VFD display, so an operator away from the browser still sees that something needs review.

Configure whether gating is enabled and the hold-off duration under Settings → Alert Gating. Disabled by default — turning it off restores today's immediate-broadcast behavior for every alert.

  • Navigate to Alert Verification (/admin/alert-verification) to inspect delivery timelines and per-target outcomes.
  • Upload captured WAV or MP3 files to decode SAME bursts directly in the browser.
  • Store decoded results for future comparison from the sidebar list.
Note: The verification dashboard helps validate that generated alerts match expected EAS formats.

Every audit log entry — logins, configuration changes, alerts received, EAS broadcasts, manual activations — is hash-chained to the entry before it and signed with the station's Ed25519 key. Any edit, deletion, or insertion (even directly against the database) is cryptographically detectable.

  • Navigate to System Logs → Audit tab (/logs?type=audit).
  • To see who changed a setting: use the Reports → Logs → Configuration Changes menu shortcut, or pick Configuration Changes in the Audit Action dropdown on the Audit tab. This shows every config.updated event with the user, timestamp, IP address, and which fields changed. The filter is preserved in CSV/PDF exports.
  • In the Chain Integrity card, choose a scope (entire chain, or the newest N rows on very large logs) and click Verify Chain Integrity.
  • A green result proves the log has not been modified; a red result reports the first bad row and why it failed.
  • Expand any entry's details to see its entry_hash, prev_hash, and signature.
  • Each verification run is itself recorded in the chain as audit.chain.verified — your receipt that the check happened.
Ephemeral key warning: if verification warns that an ephemeral signing key is in use, the station has no persistent key and signatures will not survive a restart. The installer normally provisions one at /opt/eas-station/secrets/audit_signing.key; the path is configurable under Settings → Configuration → Environment Variables.
Full design, threat model, and key management: Audit Log Integrity documentation.

Reports → Analytics → Repository Statistics (/repo-stats) reports what is actually installed on this appliance — code size, registered routes and component counts — measured from the running checkout each time you load it. It reads like the System Health and GNSS dashboards: live status pills in the page header, a strip of at-a-glance readouts, then detail cards below.

  • At-a-glance readouts — lines of code, source files, routes, comments, templates, test modules and the vendored share, in the same tile strip the other dashboards use.
  • Language breakdown — every language with its brand logo, file and line counts, and its share of the codebase.
  • What gets counted — every tracked file lands in exactly one bucket: first-party, documentation, or vendored third-party libraries (Bootstrap, Chart.js and friends under static/vendor/). Vendored code is reported separately so it never inflates the headline figures.
  • Routes by module — read from the running application's URL map, so the count matches what the server actually serves.
  • Component inventory — Flask blueprints, database models, Alembic migrations, systemd units and operator scripts.

Figures are cached for ten minutes. Click Recalculate in the page header to force a fresh measurement — no command line required. Signed-in users also get a JSON button exposing the same snapshot at /api/repo-stats.

Reports → Analytics → API Dashboard (/api-dashboard) shows live usage for every /api/* route: request volume, error rate, and p50/p95 latency, broken out per route rather than lumped into one number. It's the usage companion to API Reference (/api-reference), which documents what each route does but not how it's actually being used.

  • Summary cards — total requests, error rate, and p50/p95 latency for the selected window.
  • Volume & errors over time — a line chart of request count and error count, hourly for short windows and daily for longer ones.
  • Routes table — sortable by requests, error rate, or latency, grouped by the same module grouping API Reference uses, with an auth badge showing whether the route requires a permission.
  • Slowest routes & top errors — quick call-outs for what's worth investigating first.

Built entirely from data the server already records for the Traffic Analytics dashboard, so there's no extra instrumentation to enable — just pick a time window (24 hours to 90 days).

The Security Center (Reports → Security → Security Center, /security/center) brings four related security views together on one tabbed page:

  • Traffic — the full Traffic Analytics dashboard (described below), embedded inline.
  • Malicious Logins — SQL/command-injection login attempts, with per-IP attack statistics and a one-click Ban button for any attacking source.
  • Global Ban List — the single source of truth for banned IPs, enforced by multiple layers (the application gate, the host firewall, and fail2ban). Manage the allowlist and blocklist: ban an IP or CIDR range (permanently or for a set number of hours), toggle entries on/off, and clean up expired ones. Each entry shows a Source badge (Manual, Login Brute Force, SSH Brute Force, Malicious Request, …) and a Location column (country flag, city, region) when a MaxMind GeoLite2 database is configured under Traffic Analytics — IPv4 and IPv6. The tab also shows an Enforcement Status card, Security Metrics (failed logins 24h, IPs banned 24h, active bans, SSH attacks blocked, mirrored-to-firewall), and a firewall sync indicator with one-click Resync. A ban blocks the IP from the entire application, not just the login page. Loopback (127.0.0.1) is always exempt for bans so you can never lock the appliance out of itself. Allowlist caution: adding any allowlist entry switches sign-in to allowlist-only mode — only listed IPs may log in, and loopback is not exempt there. To prevent self-lockout, the system refuses an allowlist entry that doesn't cover your current IP (you can confirm to override); add your own IP first if you intend to restrict access.
  • fail2ban — optional host-firewall enforcement of the single ban list. fail2ban ships pre-installed; there is only one ban list (the Banned IPs tab), and when you enable firewall enforcement here every application ban (manual or automatic) is also dropped at the host firewall before traffic reaches the web app. Toggle enforcement from the UI — no SSH needed; nothing is managed twice. You can also enable an optional sshd jail to protect the host's SSH daemon; because an IP hammering port 22 is a bad actor everywhere, those offenders are automatically added to the global ban list (blocked at the web layer and all firewall ports), listed here, and cleared globally when you unban them here.

The remainder of this section describes the Traffic tab in detail.

A webalizer/awstats-style dashboard for the station's web traffic. Use the time-range selector in the header to switch between the last 24 hours and the last year, or pick Custom range… to choose explicit start/end dates.

  • Custom date ranges & drill-down: pick a custom start/end window, then click any country, path, status code, browser, OS, HTTP method, or visitor IP to filter the entire dashboard to matching traffic. Active filters appear as removable chips at the top; click ✕ on a chip (or "Clear all") to remove them. The big tables (Top Pages, Top Visitors, Recent Requests, Error URLs) also have a live text filter box.
  • Anomaly detection: a banner highlights operationally interesting deviations — elevated error rate, 5xx surges, likely vulnerability scanners (many 4xx from one IP), login brute-force bursts, and traffic spikes. Thresholds are tunable under Settings → Anomaly Detection, and the same data is available at /api/traffic/anomalies.
  • Privacy / GDPR tools (Settings → Privacy): turn on Anonymize visitor IPs to mask addresses at capture time (so a raw IP is never stored), or erase/mask the records for a specific visitor on request — Anonymize keeps the rows but masks the IP/hostname, Purge deletes them entirely. No shell access needed.
  • At-a-glance strip & charts: page views, unique visitors, total hits, successful logins, active sessions, average response time, bandwidth served, and bounce rate. Key tiles show a "vs previous period" delta and a sparkline. Charts include traffic-over-time, status codes, browsers, operating systems, device types (desktop/mobile/tablet), HTTP methods, authenticated vs anonymous, search engines, hour-of-day, day-of-week, and login activity.
  • Visitor map: a world map with a marker over each country sized by traffic (needs a GeoLite2 Country/City database for non-local visitors).
  • Visitor detail tables: top pages, slowest endpoints, top visitor hosts (with reverse-DNS hostname when enabled), operating systems and browsers (with brand logos), screen resolutions, countries/networks (with flags), cities with state/region (e.g. "Springfield, IL"), languages (with flags), referrers and search engines (with brand logos), file types and robots/spiders (with logos).
  • Errors & scanners: top error URLs (4xx/5xx) and the source IPs generating them — each error source also shows the single path it errors on most and that status code, so a scanner probing /wp-login.php (404) is obvious versus a dashboard hammering a 403/500 endpoint. Plus a Recent Requests table showing the full User-Agent string and parsed browser version.
  • Login security: successful vs. failed logins over time, top login source IPs, and the most recent login attempts — so you can spot brute-force activity.

IPv6 visitors: country, city, ASN/ISP and reverse-DNS all work for IPv6. Because IPv6 privacy addresses rotate per device, unique-visitor counts group IPv6 by its /64 network so one device isn't counted as many visitors (IPv4 is counted per address). An IPv4 vs IPv6 breakdown panel shows hits, visitors and distinct addresses per family alongside a reverse-DNS coverage figure (resolved / addresses) — handy because most IPv6 hosts publish no PTR record, so a low v6 coverage number is normal, not a fault. Reverse-DNS is also resilient: an authoritative "no PTR record" is remembered, but a slow or timed-out IPv6 lookup is retried on a later background pass (with a longer v6 timeout) instead of being given up on permanently.

Why source IPs now read correctly: the app sits behind nginx, so it now trusts the proxy's X-Forwarded-For header. Logins and sessions show the real client address instead of localhost.

Configure collection from the dashboard's Settings button: enable/disable logging, set the retention window, choose whether to record API requests or authenticated traffic only, exclude bots, turn on reverse-DNS hostname lookups, and enable country/flag resolution by either pointing at a MaxMind GeoLite2 .mmdb path or uploading the database right from the browser (validated and stored automatically — no shell access needed).

Keeping it fast & purging data: traffic is stored in the station database (the web_request_logs table), so a busy site can build up a large table that makes the dashboard slow to load. Two controls in Settings keep it in check: "Auto-purge records older than (days)" deletes old rows automatically (the background recorder prunes hourly — lower it, e.g. 30, for a smaller, faster table), and the Danger Zone → "Purge all traffic data" button wipes every recorded request in one click for an immediate reset. Both keep your settings and GeoIP databases; purging cannot be undone. The dashboard also caches its assembled view for a few seconds so repeated/auto-refreshes and multiple open tabs don't re-run every query.

Reverse DNS & flags (awstats-style): enabling "Resolve hostnames (reverse DNS)" shows each visitor's hostname instead of a bare IP. With a GeoLite2 database configured, public visitors also show their country flag. Hostname lookups happen in the background (off the request path) and are cached; leave them off if you'd rather not send visitor IPs to your DNS resolver. Hostnames also backfill automatically: rows recorded before you enabled this (or while DNS was briefly unreachable) get filled in by a background pass over the following minutes. An IP that simply has no PTR record will always show as a bare address — that's expected, not a bug.

Export: use the Export menu to download every report as a multi-section CSV (opens in Excel) or as a multi-page PDF report (charts and tables included).

Traffic data is stored locally in the station database and pruned automatically past the retention window; nothing is sent to any third party.
Credential protection (background, no setup needed): every stored API key, streaming/SMTP password, SNMP community string, and MFA secret is encrypted at rest, and admin password hashes are peppered (a server-side secret independent of the database) in addition to being salted. Nothing to configure — see Credential Encryption at Rest for details.

The Firewall page (Reports → Security → Firewall, /admin/firewall) is the single place to open or close every host-firewall (UFW) port EAS Station™ manages — no SSH required. It replaced a mix of a manual ufw allow 8000/tcp instruction buried in a setup guide and an inline firewall widget on the NTP server settings page; both are now one page.

  • Host Firewall Baseline — reports whether UFW is active, default-deny-incoming, and has the fixed baseline (22 SSH, 80 HTTP, 443 HTTPS) allowed. Apply Baseline Fix reproduces install.sh's own provisioning step on a host that has drifted from it — a deployment provisioned before UFW auto-configuration existed, or one where the firewall was later disabled or reconfigured by hand.
  • LAN NTP Server (UDP/123) — list the subnet(s) (CIDR ranges or single IPs) allowed to query this host for time. Applying writes the chrony configuration and opens exactly those subnets at the firewall in one step; clearing the list disables serving and removes every rule this feature added. Detected local subnets are offered as one-click suggestions, never applied automatically — on a cloud host, an interface's own subnet is usually the provider's internal range, not your real LAN.
  • Icecast Streaming (TCP, configured port) — the same pattern for the Icecast port configured in Icecast settings: list the subnet(s) that should be able to reach the stream, or 0.0.0.0/0 if the stream is meant to be public. Fixed the case where Icecast auto-streaming worked internally but was unreachable from any other device on the network because the port had never been opened at the host firewall.
A rule added here only makes a port reachable on this LAN. If you also need it reachable from outside your network, your router's own NAT still has to forward the port separately — see Router Port Forwarding for which ports to forward and which to leave closed.
This page is EAS Station™'s implementation of the network-segmentation control in FCC 26-38 (47 CFR § 11.35(d)(3)) — see FCC 26-38 EAS Cybersecurity Requirements for the full compliance mapping and operator checklist.

The search box at the top of the Settings hub (/settings) finds a setting by its field name or its currently-stored value — not just the label of the page it lives on. Typing a port number, hostname, or zone code shows exactly which field has it and which page to go to, with no need to remember which of the settings pages holds it.

  • Matches both the settings page cards (by label/description) and individual fields within them, shown as a separate "matching fields" list — click a result to jump straight to that page.
  • Covers the pages backed by a simple field/value settings form (Icecast, TTS, Notifications, Hardware, Location & Alert Filtering, and similar) — action or record pages like Backups, User Accounts, and Environment Variables aren't included, since there's nothing to match a stored "value" against.
Encrypted credentials — Icecast/SMTP/SNMP source passwords, API keys, VPN auth keys — are never included, searchable or not, regardless of what you type.

Importing Boundary Data

The system supports two methods for importing geographic boundary data:

  • GeoJSON Files: Upload GeoJSON files directly via the "Upload Files" tab
  • Shapefiles: Upload ESRI Shapefiles (ZIP archives with .shp, .shx, .dbf files) or import existing shapefiles from the server

Using Shapefiles

Navigate to Admin → Upload Files and scroll to the "Upload Shapefiles" section:

  1. View Server Files: Click "Refresh List" to see shapefiles already on the server (e.g., in /streams and ponds)
  2. Import Existing: Click "Import" next to any complete shapefile to convert and load it into the database
  3. Upload New: Upload a ZIP file containing all shapefile components and select the boundary type
Tip: The system automatically detects water features (rivers, lakes) from TIGER/Line shapefiles. Water boundary files for your configured county can be pre-loaded in the streams and ponds directory.

Loading Cities & Villages Automatically

Rather than uploading a shapefile by hand, the Load Cities & Villages (Census) card on the same page downloads incorporated city and village boundaries from the US Census Bureau for this station's configured Weekly Test Automation coverage counties and loads them automatically as Villages boundaries. Once loaded, alerts that intersect them show named cities and villages in the alert detail page's affected-boundaries list — no manual shapefile upload needed. Unincorporated Census Designated Places (CDPs) are skipped since they aren't real municipalities. Re-running the load only adds new/changed boundaries unless "Reload (Replace Existing)" is used.

Supported Boundary Types

  • 🌊 Rivers & Streams - Linear water features
  • 💧 Lakes & Ponds - Area water bodies
  • Electric Providers - Utility service areas
  • 🏘️ Villages & Townships - Municipal boundaries
  • 🏫 School Districts
  • 🔥 Fire Districts
  • 🚑 EMS Districts
  • 🚆 Railroads - Rail corridors
  • 📞 Telephone Providers
  • 🗺️ County Outlines

Additional Configuration

  • Alert Configuration: Set up alert routing and notification preferences in the admin panel
  • Filter Settings: Configure geographic and event-based alert filtering
  • Intersection Calculation: After uploading boundaries, calculate intersections via Operations → Calculate Intersections

Filtering the Received Alerts List

The Received Alerts page (Monitor → Alerts → Received Alerts) supports multi-select filtering with include/exclude modes:

  • Audio Source & Event Type are checkbox dropdowns — tick several values at once, and flip the Include / Exclude toggle to hide a noisy source or filter weekly tests (RWT/RMT) out of the list entirely. Each option shows how many alerts it accounts for.
  • Active filter chips above the table show every applied value; click a chip's ✕ to remove just that value.
  • Statistics cards (Total / Forwarded / Ignored / Errors) are clickable shortcuts that filter by forwarding decision.
  • Filters combine and are carried through pagination, so a filtered view can be bookmarked or shared as a URL.

Originating Equipment

Every received alert's detail page (click into any row from the Received Alerts list) shows an Originating Equipment badge in the Reception panel — a best-effort guess at which ENDEC model produced the SAME transmission (SAGE Digital 3644, NWS BMH, Trilithic EASyPLUS, etc.), fingerprinted from transmission characteristics like terminator bytes and inter-burst gap timing rather than any field carried in the alert itself. The same detection also runs on manually uploaded recordings in the Audio Decoder tool.


Purging Received Alerts & Audio

Busy monitoring sources can record thousands of received alerts, and the raw WAV audio captured with each one is the largest storage cost. The Alert Purge page (Settings → Admin Dashboard → Alert Purge, also reachable from the Purge button on the Received Alerts page) is the single place to clean this up.

  • Filter what to remove by age (older than N days), source, forwarding decision (e.g. everything not forwarded), and/or SAME event code.
  • Choose the scope: Audio only strips the stored WAV but keeps the record for the FCC compliance log; Entire alert deletes the record, and can also delete the generated broadcast message and its on-disk audio so no instance of the audio remains.
  • Preview first to see how many records and how much audio a purge would affect before running it.
  • Automatic Purge can run the same rules in the background (shortly after startup, then roughly every 6 hours).
Purging alerts never touches system, audit, or compliance logs — every purge writes an audit entry recording what was removed.

US County Boundaries (IPAWS SAME Coverage)

The County Boundaries page (Settings → Data & Storage → County Boundaries) manages the Census TIGER/Line county shapefile used to draw highlighted county outlines on IPAWS alert coverage maps. IPAWS/NWS alerts carry 6-digit SAME geocodes (e.g. 039137) instead of polygon geometry; EAS Station™ converts those codes to county outlines by looking up this table.

Page Workflow

Sections are ordered top-to-bottom for a natural workflow:

  1. Status Cards — Shows whether the us_county_boundaries table exists, how many counties are loaded, how many states, and whether the bundled Census shapefile is present.
  2. Load County Boundaries — Import data from the bundled Census shapefile (≈3,235 counties, optionally filtered to one state) or upload a custom ZIP shapefile.
  3. Loaded States — Table listing every state that has county data in the database. Each row has two action buttons:
    • View on Map — loads that state's county outlines on the interactive map directly below this table.
    • Delete — removes all county records for that state.
  4. County Boundary Map — Interactive Leaflet map. Use the state dropdown in the card header to choose a state, or click the button in the Loaded States table to jump directly to a state. Click any county outline to see its name, GEOID, and SAME code. Use Clear to reset the map.
  5. Search Counties — Real-time search by county name, 5-digit GEOID, or state abbreviation. Clicking a result loads that state on the map.
  6. Table Lookup / Diagnostics — Enter one or more 6-digit SAME codes or 5-digit GEOIDs (comma-separated) to verify they are present in the database. Useful for confirming coverage before an activation.
One-time setup: EAS Station™ auto-loads the bundled Census shapefile on first startup if the table is empty. You only need to use this page if the auto-load did not run, if you need to reload data, or if you want to inspect or verify the coverage for specific counties.

  • Toggle Auto Start or Enabled to control which receivers the radio manager spins up during poller runs.
  • Use the action menu to request synchronized IQ/PCM captures.
  • Captured files are surfaced alongside status updates in the compliance dashboard.

Deep-dive tools for troubleshooting a receiver's RF front-end and audio pipeline, in one place per receiver.

  • Live Waterfall: a continuously scrolling time-vs-frequency-vs-power view, updated roughly twice a second.
  • Spectrum Scope: a classic frequency-vs-power line trace with a peak-hold overlay, sharing the waterfall's data feed so running both costs nothing extra.
  • Zoom & pan (Live Waterfall / Spectrum Scope): use the magnifier buttons in either panel header, the mouse wheel to zoom about the cursor, or drag to pan; double-click resets to the full span. Zoom is shared between the waterfall and the scope, and the axis labels and status line always describe the window actually on screen — e.g. 64.0 kHz of 256.0 kHz · 125 Hz/bin. This crops the published FFT rather than retuning the receiver, so it costs nothing extra and stops at the FFT's real resolution instead of inventing detail.
  • MPX Scope (FM/WFM receivers): unlike the two views above, which show the RF spectrum around the tuned carrier, this shows the actual demodulated FM multiplex signal — the composite baseband a broadcast-analyzer screen displays — fixed to the 0–100 kHz range where the interesting structure lives. Dashed marker lines call out the 19 kHz stereo pilot, 38 kHz stereo subcarrier, and 57 kHz RDS subcarrier so you can read injection/presence at a glance; polls independently at roughly 2 Hz. No zoom/pan on this view (the fixed window already covers everything relevant). Below the spectrum, two oscilloscope traces round out the picture: an MPX oscilloscope (the raw composite multiplex waveform itself, not the FFT above) and an audio oscilloscope (decoded L/R channels, shown once stereo is locked and replaced with a “Stereo not locked” note otherwise).
  • Dead-air monitoring: watches monitored audio for loss of programme content and raises the tower light, optionally sounding a rack alarm buzzer. Configure the thresholds under Monitor → Audio & Radio → Audio Ingestion; the tower-light colour and rack-buzzer pin under Monitor → Station Hardware → Hardware Settings; and see live state (with the Acknowledge button) on the Audio Health dashboard. It checks the audio rather than the service, so it catches the case every other health check misses: the SDR locked and healthy while the station you are monitoring is off the air. Because an off-air FM receiver outputs full-scale hiss rather than silence, detection uses spectral flatness as well as level — leave “detect an unmodulated carrier” on for radio sources. Acknowledging the alarm silences the buzzer but leaves the light lit until audio actually returns.
  • Historical Trends: charts of signal strength, lock percentage, sample-rate health, and buffer overflow/underflow over a selectable window (1h/6h/24h/7d), plus an always-on sparkline strip for an at-a-glance read.
  • Signal Quality (RBDS-capable receivers): alongside the RBDS History button, a second history view charting stereo pilot lock strength, discriminator click rate (a multipath/impulse-noise indicator), peak composite deviation, 19 kHz pilot / 57 kHz RDS subcarrier injection level (kHz), modulation power (dBr, referenced to 100% modulation at 75 kHz), and stereo balance (dB, R relative to L) over the last hour/6h/24h. The same modal also includes a frequency deviation histogram — a distribution (not a time series) of every deviation sample logged in the selected window, with a compliance readout showing what percentage exceeded the 75 kHz broadcast-FM limit. Unlike a dedicated hardware analyzer, no separate “Acquire” step is needed — the histogram is built instantly from history the system is already logging continuously.
  • Run Full Diagnostics: a pass/warn/fail checklist covering the SDR service heartbeat, per-receiver health, ring-buffer drops, and spectrum-cache freshness — the same checks available from the command line via scripts/diagnostics/check_sdr_status.py.
  • Snapshot Waterfall & Run Diagnostic: capture a short IQ window and get a clipping/front-end-health verdict plus a subcarrier inventory (pilot, stereo, RBDS, SCA).
  • Bandscan: unlike every other view on this page, this is an active operation — it sweeps the receiver across the whole FM broadcast band (87.5–108.0 MHz, 100 kHz steps) to survey what's actually on the air, filling in a live band plot as it goes. A full sweep takes roughly 30–45 seconds, during which the receiver is retuned away from its assigned station and will not monitor for real alerts — the confirmation dialog says so explicitly before starting. This is the same risk tier as the existing Restart Receiver button, just disruptive for longer than an instant. The receiver always returns to its assigned frequency when the scan finishes, is cancelled, or errors. The receiver's own audio is muted for the whole sweep (and no dead-air alarm fires) so listeners don't hear the channel-hopping noise.
  • Identify Stations: appears once a Bandscan finds at least one peak. A second, explicit pass — retunes to each detected peak in turn and dwells roughly 6 seconds on it to attempt an RDS decode: call-letters usually resolve within the first couple of seconds once synced, with the full PS marquee text (assembled from several separate over-the-air segments) sometimes taking the rest of the window to complete. Results appear as a list below the chart (frequency, level, station name or call sign); a peak that doesn't broadcast RDS, or that this pass couldn't sync onto in time, shows “No RDS decoded” rather than an error. Same risk tier and audio-muting as Bandscan itself, and adds to its own confirmation dialog how much extra time the pass will take before starting.
Access: Navigate to Diagnostics → SDR Diagnostics or visit /admin/radio/diagnostics

Manage and control all display outputs from a unified interface.

  • Unified Dashboard: Access LED sign, VFD display, and OLED screen controls from one central page
  • Status Monitoring: View connection status and current content for all displays at a glance
  • Quick Actions: Jump to full control pages for detailed configuration and testing
  • Recent Activity: See a consolidated feed of all display output history
  • Individual Controls: Access advanced features like LED animations, VFD graphics, and OLED screen rotation
Access: Navigate to Broadcast → Display Controls from the main menu or visit /displays
Tip: Use the unified page for quick status checks and the individual control pages (LED, VFD, OLED) for detailed configuration and testing.

Create dynamic content templates for LED signs and VFD displays with API data integration.

  • Screen Templates: Define custom layouts with variable substitution from API endpoints like /api/system_status, /api/alerts, /api/monitoring/radio, and /api/gps_status
  • Visual Screen Editor: The drag-and-drop editor (Screens → New/Edit) exposes the full element palette per display type — including LED's "Message Type" toggle for switching a screen between scrolling text and Dots/graphics mode — and its Preview button renders through the same real device pipeline (icons, gauges, compasses, segmented meters and all) instead of an approximation, so what you design matches what the hardware shows
  • LED Displays: Configure text content (4 lines × 20 chars) with color, animation mode, and speed settings, or use graphics mode (below) for a single icon+hero-value screen using the sign's real Picture File command
  • VFD & OLED Graphics: Both displays share the same graphics engine — icons, a speedometer-style gauge, a compass/heading dial, a multi-value bar chart (e.g. per-satellite signal strength), a segmented VU-meter primitive (VFD), plus the OLED's analog clock face — not just plain text and progress bars. The VFD ships all 11 default screens the OLED does (Status, Alert Status, System Meters, Audio VU Meter, GPS Status, Network Status, GPIO Status, EAS Decoder, Audio Health, IPAWS Poller, Receivers)
  • LED Graphics (Dots) Mode: The Alpha 9120C sign can also render icon+text screens on its 160×16 Picture File canvas — a smaller icon/text vocabulary than VFD/OLED (no gauge/compass, since the canvas is half the VFD's height), used by the 3 default graphics screens (Status, Alert, System). Requires a one-time, Admin-only, destructive setup step — see the "Sign Memory" panel on the LED control page's Dots tab — before graphics screens will display correctly; it allocates a dedicated file on the sign and erases all currently stored messages
  • Active-Alert Visibility: The VFD's Status screen shows the current active-alert count alongside the time and date, and Alert Status shows the alert's event type and area — the VFD rotation keeps cycling through both while an alert is active instead of pausing
  • Screen Rotation: Set up automatic cycling between multiple screens with configurable durations
  • Dynamic Data: Display system health, CPU/memory usage, alert counts, network info, signal strength, GPS position/fix status, and more
  • Priority Management: Emergency screens override rotation; alerts pause normal displays
Access: Navigate to System → Custom Screens from the main menu or visit /screens

Template Variables

Use these variables in your screen templates:

Variable Description Example
{status.status} System health status healthy, warning, critical
{status.active_alerts_count} Number of active alerts 3
{status.system_resources.cpu_usage_percent} CPU usage percentage 45.2
{alerts.features[0].properties.event} Latest alert event type Tornado Warning
{now.time} Current time (12-hour) 03:45 PM
{now.date} Current date 11/06/2025

Available Data Sources

  • /api/system_status - System health and resources
  • /api/alerts - Active emergency alerts
  • /api/monitoring/radio - Radio receiver status
  • /health - Basic health check
  • /version - System version info

LED Screen Example

{
  "lines": [
    "SYSTEM STATUS",
    "Health: {status.status}",
    "CPU: {status.system_resources.cpu_usage_percent}%",
    "Alerts: {status.active_alerts_count}"
  ],
  "color": "GREEN",
  "mode": "HOLD",
  "speed": "SPEED_3"
}

VFD Screen Example (VU Meters)

{
  "elements": [
    {
      "type": "text",
      "x": 2,
      "y": 1,
      "text": "SYSTEM RESOURCES"
    },
    {
      "type": "progress_bar",
      "x": 10,
      "y": 8,
      "width": 120,
      "height": 6,
      "value": "{status.system_resources.cpu_usage_percent}",
      "label": "CPU"
    }
  ]
}

Broadcast Builder Console:

Accessible from the top navigation once logged in. Use the browser-based workflow to:

  • Pick a state or territory
  • Choose county/parish or statewide SAME codes
  • Select originator code (EAS, CIV, WXR, PEP)
  • Choose from authorized 47 CFR §11.31(d-e) event types
  • Preview the SAME header in real-time
  • Generate complete packages with SAME bursts, attention tones, and narration
Command Line: For automation, use: python tools/generate_sample_audio.py

When an over-the-air (OTA) alert is decoded, the system captures the voice narration that follows the attention tone and can relay it during auto-forwarding. Some upstream ENDECs and air-chain processors gate their record input, transmitting narration that stutters — words chopped by near-silence gaps with abrupt restarts. The system analyzes every captured narration for this signature and shows the verdict on the received-alert detail page.

The Relay Narration Audio setting (Admin → EAS Encoder Settings) controls what goes to air when relaying:

  • Auto (recommended) — relay the captured off-air narration unless it is detected as gate-chopped, in which case a clean narration is synthesized with the configured TTS provider.
  • Captured off-air audio — always relay the captured narration exactly as received (legacy behavior).
  • Always local TTS — always synthesize the narration locally.
TTS substitution requires a configured TTS provider (Admin → TTS). Without one, the captured audio is always relayed — degraded narration beats silence.

The same alert often arrives from more than one source — a CAP feed and an OTA relay, or two relay streams — within minutes of each other. Auto-forwarding suppresses these cross-source duplicates using two configurable windows (Admin → EAS Encoder Settings → Deduplication & Audio Detection):

  • Cross-Source Dedup Window (default 15 min) — used when there is no usable SAME header to compare, only a matching event code and FIPS set.
  • Header-Key Dedup Window (default 1440 min / 24 hours) — used when a SAME header is present, matched independently of which station/callsign sent it. This is the longer window because the same NWS issuance can legitimately be re-relayed by different stations hours apart.

Min Confidence to Log (default 0, disabled) filters the Received Audio Alerts log itself, not forwarding. It only applies to detections with no decoded event code — low-confidence noise from a marginal signal. A real decode is always logged regardless of confidence, even one reporting an alert outside this station's coverage area (e.g. from a nationwide relay network) — that is a true decode being correctly filtered downstream, not noise.

If the Received Audio Alerts page shows a lot of low-confidence, blank-header entries from one source, raising Min Confidence to Log (e.g. to 70%) is usually the fix — check the source's SNR/signal quality first, since a persistently low-confidence source may indicate an antenna or receiver-gain problem worth addressing at the hardware level.

Before alert text is sent to the TTS engine it passes through a four-layer normalization pipeline that converts abbreviated and machine-formatted text into natural spoken language.

The Four Layers

  1. Time expansion — compact clock times are converted to fully-spoken equivalents so every TTS backend gives the same natural result.
    1100 PMeleven o'clock PM  ·  9:30 AMnine thirty AM
  2. NWS-specific cleanup — three rules handle formatting conventions unique to NOAA/NWS alert text:
    • Alternate-timezone slash notation — NWS writes /5 PM CDT/ to show a deadline in a second timezone; the slashes are stripped so TTS does not read them literally.
    • Saint abbreviationST. is expanded to Saint before proper nouns (e.g. Saint Joseph).
    • Indiana county disambiguation — NWS appends the state code IN after a county name that appears in more than one watch state (e.g. ALLEN INALLEN Indiana, CASS INCASS Indiana). The substitution is applied only when the preceding word is a recognised Indiana county name and the following word is not a directional word or state name, so common phrases like IN EFFECT and section headers like IN MICHIGAN are never altered.
  3. Built-in acronym table — hard-coded expansions for EAS/NWS tokens, all US timezone abbreviations, and US state codes used as county-name markers:
    NWSNational Weather Service  ·  EDTEastern Daylight Time  ·  MIMichigan  ·  OHOhio  ·  AFDAir Force Depot  ·  and more
  4. Custom pronunciation dictionary — your own word-substitution rules, applied last and longest-first so multi-word entries are never masked by shorter ones.

Accessing the Tools

  • TTS Settings & Pronunciation Preview — Navigate to Admin → TTS Settings or visit /admin/tts. Paste any alert text into the Pronunciation Preview panel to see exactly what the TTS engine will receive before going live.
  • Custom Pronunciation Rules — Navigate to Admin → TTS Settings then click Pronunciation Dictionary, or visit /admin/tts/pronunciation. Add entries for place names, call letters, or any term your TTS engine mispronounces. Rules support optional case-sensitive matching and a note field for documentation.
Tip: Use the Normalize & Speak button on the TTS Settings page to hear the normalised text through your configured TTS provider before deploying a change.

Required Weekly Test (RWT) broadcasts are scheduled automatically: one test per week, on one of the days you allow, at a random time inside your window.

Accessing Weekly Test Automation

  1. Navigate to Diagnostics → Tests & Verification → Weekly Test Schedule from the navigation menu
  2. Or visit /rwt-schedule directly

Configuration Options

  • Enable/Disable: Toggle automatic RWT broadcasts on or off
  • Allowed Days: The days the test is permitted to land on (Monday through Sunday). The scheduler picks one of them each week — ticking Sunday and Tuesday means one test, on a Sunday or a Tuesday, not one on each. Allowing more days makes the test less predictable, which is what 47 CFR §11.61 asks for; it does not send more tests.
  • Time Window: Set start and end times (e.g., 8:00 AM to 4:00 PM). The exact minute is drawn at random from inside this window each week.
  • Coverage: The scheduler always follows the shared Default RWT Counties list that sits next to the form—update it once and Quick RWT, manual defaults, and automation all stay aligned.
  • Spoken Announcements: Optionally play a station courtesy announcement before the test ("This station is conducting a test of the Emergency Alert System...") and/or after it ("This concludes this test..."). Each is synthesized with your configured TTS provider and can be toggled and edited independently.
Originator & Station: RWT automation reuses the EAS_ORIGINATOR and EAS_STATION_ID environment variables that also feed the Broadcast Builder console.

RWT Broadcast Behavior

Automatic RWT broadcasts are designed to be lean and FCC-compliant:

  • No TTS Narration: RWT contains only SAME headers and EOM tones — any spoken announcements you enable play outside the encoded burst, before the lead-in silence and after the EOM's trailing silence, so they never affect the required RWT format under 47 CFR §11.61(a)(1)(ii)
  • No Attention Tones: Silent except for required signaling
  • Guaranteed Lead-In Silence: The composite always opens with at least a second of true silence before the SAME header begins, mirroring the second of silence that already follows the EOM before the air-chain returns to normal programming
  • Once Per Week: Exactly one RWT per week, however many days you allow. If the station is down during the chosen slot, the test is caught up on a later allowed day in the same week.
  • Time Window: Broadcasts only during configured hours
  • Logged: All broadcasts stored in database for compliance tracking

Testing Your Configuration

Use the "Send Test RWT Now" button to immediately trigger a test broadcast and verify your configuration.

Scheduling Note: The system checks every minute whether to send an RWT. Once configured, it runs automatically in the background.
Default Counties: Systems ship with Allen, Defiance, Hancock, Henry, Paulding, Van Wert, and Wood Counties in Ohio (FIPS codes 039003, 039039, 039063, 039069, 039125, 039161, 039173). Update the Default RWT Counties manager to write your own list into Location Settings—Quick RWT, the Broadcast Builder defaults, and the scheduler will all use it automatically.

Whenever the station is on air — a manual send, an RWT, a resend, or an auto-forwarded alert — a full-screen overlay takes over every open browser tab so no one accidentally starts a second broadcast on top of the first. It shows the event name, a live countdown, and a progress bar.

Phase Indicator

A small label above the countdown shows where the broadcast currently is:

  • Sending Header — the SAME data burst (repeated three times) is transmitting.
  • Narration — the attention tone and/or spoken message (if any) is playing. RWTs skip this phase almost entirely, since 47 CFR §11.61(a)(1)(ii) forbids a narration on a Required Weekly Test.
  • Sending EOM — the End-Of-Message tone burst is transmitting.

The phase label only appears when the broadcast published its phase timing (every current broadcast path does); it stays hidden rather than showing a wrong phase if that data is ever missing.

Aborting a Broadcast from the Screen

A Hold to Abort Broadcast button sits below the countdown. Press and hold it for 3 continuous seconds — the same sustained-hold safety requirement as the physical GPIO Dump/Abort input button, so a stray click or tap can never end a live broadcast. Releasing early cancels the hold with no effect.

Aborting cuts the in-progress message but always plays the required End-Of-Message tone burst (47 CFR §11.61(a)) before releasing the transmitter relay, and writes an entry to the tamper-evident audit ledger recording who aborted it and whether the EOM burst actually sent. Requires the Cancel EAS Broadcasts permission (Admin and Operator roles have it by default).

Same underlying action: This button calls the identical abort logic as a physically-held GPIO Dump/Abort Broadcast input pin — see GPIO Input Triggers for the hardware equivalent.

If the overlay is still showing a broadcast that has actually already finished — most often after the tab was backgrounded on a phone or tablet, which can stall the live updates — holding Abort simply confirms with the server that nothing is on the air and closes the screen; no separate action is needed. Switching back to the tab also re-checks automatically.

Troubleshooting

  1. Check Logs: Run sudo journalctl -u eas-station-web.service -f to see service startup messages.
  2. Database Connection: Verify POSTGRES_* settings in /opt/eas-station/.env match your database deployment.
  3. Secret Key: Ensure SECRET_KEY is set to a non-empty value.
  4. Port Conflicts: Confirm port 5000 isn't already in use.
Common Issue: Database connectivity problems account for 80% of startup failures.

  • PostGIS Extension: Ensure your database has the PostGIS extension enabled: CREATE EXTENSION IF NOT EXISTS postgis;
  • Spatial Indexes: Check that boundary tables have spatial indexes for performance.
  • Geometry Types: Verify imported GeoJSON files use valid geometry types.

  • Audio Drivers: Verify sound card drivers are working on the host system.
  • File Permissions: Check that the application has write access to audio output directories.
  • Azure Speech: If using Azure TTS, verify API keys and regional endpoints.

  • Serial Connection: Verify the serial port device exists and is accessible.
  • Protocol Settings: Check that baud rate and protocol settings match your sign model.
  • Network Access: Ensure the application can reach the sign's IP address if using network control.

Reference Commands

Service Management

# View logs
sudo journalctl -u eas-station-web.service -f

# Restart services
sudo systemctl restart eas-station.target

# Check service status
sudo systemctl status eas-station.target

Database Operations

# Reset database
python tools/reset_database.py

# Check health
python tools/check_db_health.py

Audio & Testing

# Generate test audio
python tools/generate_sample_audio.py

# Test receivers
python tools/test_receivers.py

System Status

# System health
curl http://localhost:5000/api/system_health

# Version info
curl http://localhost:5000/version

Getting Help

  1. 1
    Check Documentation: Review this guide and the About page for system information.
  2. 2
    Review Logs: Run sudo journalctl -u eas-station.target -f to identify error messages.
  3. 3
    Check System Health: Visit System Health for status overview.
  4. 4
    Search Issues: Look for similar problems on GitHub Issues.
  5. 5
    Create New Issue: Provide logs, configuration (redact secrets), and steps to reproduce.

Trusted Field Resources

Curated references from NOAA, FEMA IPAWS, and ARRL to keep operations sharp.

NOAA FEMA IPAWS ARRL

NOAA Weather Service

Primary CAP documentation, glossary, and sample payloads.

FEMA IPAWS Lab

Interoperability, architecture, and test schedule references.

ARRL Field Playbooks

ARES task books, ICS-213 templates, and readiness checklists.