CAP Alert Operations Guide
OmniLCS accepts Common Alerting Protocol (CAP) v1.2 XML messages from external alerting authorities (weather services, emergency management agencies, etc.), resolves geographic warning areas to the network's cell infrastructure, and triggers cell broadcasts via the existing CBC engine across 4G (SBC-AP), 3G (SABP), and 2G (CBSP) interfaces.
Two ingestion methods are supported: HTTP POST (push) for direct integration and Atom feed polling (pull) for monitoring standard CAP feed sources. A configurable manual approval step allows operators to preview alerts before broadcast.

Architecture
Alert Lifecycle
When a CAP alert is received (via HTTP POST or feed poll):
- The CAP XML parser extracts the alert envelope, info blocks, warning areas (polygons), and CB parameters
- The geometry engine matches each polygon against the cell database using ray-casting (point-in-polygon), resolving warning areas to lists of TACs (4G), SAIs (3G), and LACs (2G)
- If
require_approvalistrue, the alert is queued as pending and appears in the operator LiveView UI for preview and approval - If
require_approvalisfalse, the alert is auto-approved and immediately dispatched to the CBC engine - On broadcast, the CBC engine sends Write-Replace-Warning-Request (4G SBC-AP), Write-Replace (3G SABP), and/or WRITE-REPLACE (2G CBSP) to all connected MMEs, RNCs, and BSCs
Configuration
CAP Settings
config :omnilcs, :cap,
# Require operator approval before broadcasting
require_approval: true,
# PLMN identity for broadcast messages
plmn: %{mcc: "001", mnc: "01"},
# Use cell coverage radius for polygon matching (vs center-point only)
coverage_aware: false,
# Atom feed sources to poll (empty = no polling)
feeds: []
CAP Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
require_approval | boolean | No | true | When true, alerts are queued for operator approval before broadcast. When false, alerts are auto-approved and broadcast immediately. |
plmn | map | No | %{mcc: "001", mnc: "01"} | PLMN identity (MCC/MNC) used in broadcast messages. Must match the serving network. |
plmn.mcc | string | Yes | "001" | Mobile Country Code (3 digits). |
plmn.mnc | string | Yes | "01" | Mobile Network Code (2-3 digits). |
coverage_aware | boolean | No | false | When true, polygon matching uses each cell's coverage radius to determine intersection rather than just the center point. Useful for large cells at polygon edges. |
feeds | list | No | [] | List of CAP Atom feed configurations to poll. See Feed Configuration. |
Feed Configuration
Each entry in the feeds list defines a CAP Atom feed source to monitor.
config :omnilcs, :cap,
feeds: [
%{url: "https://alerts.weather.gov/cap/us.php?x=1", poll_interval_seconds: 60},
%{url: "https://feeds.meteoalarm.org/api/v1/warnings/atom", poll_interval_seconds: 120}
]
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | -- | URL of the CAP Atom feed. Must return Atom XML (RFC 4287) with embedded CAP alert entries. |
poll_interval_seconds | integer | No | 60 | Seconds between feed polls. Lower values provide faster alert ingestion but increase network traffic. |
The feed poller tracks seen alert identifiers to avoid reprocessing. Each new <entry> in the feed has its embedded <alert> XML extracted and submitted to the Alert Manager.
Configuration Example: Full CAP Setup
config :omnilcs, :cap,
require_approval: true,
plmn: %{mcc: "001", mnc: "01"},
coverage_aware: true,
feeds: [
%{url: "https://alerts.weather.gov/cap/us.php?x=1", poll_interval_seconds: 60}
]
How it works: CAP alerts from the NWS weather feed are checked every 60 seconds. New alerts are parsed and their polygon warning areas are matched against the cell database using coverage-aware intersection. Matched alerts appear in the CAP Alerts LiveView page for operator review. The operator can preview the alert details (message text, severity, matched cells, TACs/LACs), then approve to broadcast or reject to discard.
Cell Database: TAC, LAC, and RAT Fields
CAP polygon resolution depends on cells in the database having TAC (4G), LAC (2G and 3G), SAC (3G), and RAT fields populated. These fields determine which broadcast interface is used for each cell.
Cell Fields for CAP
| Field | Type | Description |
|---|---|---|
tac | integer | Tracking Area Code. Used for 4G SBC-AP broadcasts. Cells with a TAC are included in the TAI list sent to MMEs. |
lac | integer | Location Area Code. Used for 2G CBSP and 3G SABP broadcasts. Cells with a LAC are included in the cell list sent to BSCs and the SAI list sent to RNCs. |
rat | string | Radio Access Technology: "4g", "3g", or "2g". Informational; the presence of tac or lac determines broadcast targeting. |
These fields can be set via:
- REST API when creating or updating cells (
POST /api/cells,PUT /api/cells/:id) - JSON import (
priv/cells.json) with"tac","lac", and"rat"fields per cell - Manual entry in the Cell Database LiveView page
When syncing cells from InfluxDB, existing TAC/LAC/RAT values are preserved (InfluxDB does not provide these fields, so they are not overwritten with nil).
Cell JSON Example
[
{
"cell_id": "eNB-001-cell-01",
"latitude": 40.7128,
"longitude": -74.0060,
"pci": 100,
"earfcn": 1300,
"radius": 500,
"tac": 100,
"lac": null,
"rat": "4g"
},
{
"cell_id": "BTS-001-cell-01",
"latitude": 40.7130,
"longitude": -74.0065,
"pci": null,
"earfcn": null,
"radius": 2000,
"tac": null,
"lac": 5001,
"rat": "2g"
}
]
Polygon Resolution
When a CAP alert contains polygon warning areas, the geometry engine determines which cells fall inside each polygon.
How Polygon Matching Works
Standard mode (coverage_aware: false): A cell matches if its center point (latitude/longitude) falls inside the polygon, using the ray-casting algorithm.
Coverage-aware mode (coverage_aware: true): A cell matches if its coverage area (modeled as a circle with the cell's radius field) intersects the polygon. This catches cells whose center is just outside the polygon but whose coverage extends into it.
CAP Polygon Format
CAP v1.2 defines polygons as space-separated lat,lon pairs. The first and last points should be identical (closed polygon):
38.47,-120.14 38.34,-119.95 38.52,-119.74 38.62,-119.89 38.47,-120.14
CAP XML Format
The parser handles CAP v1.2 per the OASIS standard. Key elements extracted:
Alert Envelope
| Element | Description |
|---|---|
<identifier> | Unique alert identifier |
<sender> | Alert originator |
<sent> | Timestamp of alert issuance |
<status> | Actual, Exercise, System, Test, Draft |
<msgType> | Alert, Update, Cancel, Ack, Error |
<scope> | Public, Restricted, Private |
Info Block
Each <info> block contains the alert content for a specific language/audience:
| Element | Description |
|---|---|
<category> | Alert category (Geo, Met, Safety, Security, etc.) |
<event> | Event type description (e.g., "Tornado Warning") |
<urgency> | Immediate, Expected, Future, Past, Unknown |
<severity> | Extreme, Severe, Moderate, Minor, Unknown |
<certainty> | Observed, Likely, Possible, Unlikely, Unknown |
<headline> | Short alert headline |
<description> | Full alert description text (used as broadcast message) |
<instruction> | Recommended action |
CB Parameters
Cell broadcast parameters are extracted from <parameter> elements within each <info> block:
| Parameter Name | Description |
|---|---|
CBMessageIdentifier | 16-bit CB message ID for the broadcast |
CBRepetitionInterval | Seconds between broadcast repetitions |
CBNumberOfBroadcasts | Total number of times to broadcast |
If these parameters are not present in the CAP XML, defaults are used (message ID 0x1112, repetition period 30 seconds, 10 broadcasts).
Area Block
Each <area> within an <info> can contain:
| Element | Description |
|---|---|
<areaDesc> | Human-readable area description |
<polygon> | Space-separated lat,lon coordinate pairs defining the warning area |
<circle> | Center point and radius (for future use) |
REST API
POST /api/cap
Submit a CAP XML alert for processing.
Request Body
{
"xml": "<alert xmlns=\"urn:oasis:names:tc:emergency:cap:1.2\">...</alert>"
}
| Parameter | Type | Required | Description |
|---|---|---|---|
xml | string | Yes | Complete CAP v1.2 XML alert document |
Response (201) -- Alert submitted successfully
{
"status": "ok",
"data": {
"id": "a1b2c3d4-e5f6-...",
"status": "pending",
"source": "http_post",
"received_at": "2025-01-15T10:30:00Z",
"matched_cells": 42,
"tacs": [100, 101, 102],
"lacs": [5001, 5002],
"mcc": "001",
"mnc": "01",
"broadcast_params": {
"message_id": 4370,
"repetition_period": 30,
"num_broadcasts": 10,
"message_text": "Tornado Warning for Springfield County...",
"event": "Tornado Warning",
"severity": "Extreme",
"urgency": "Immediate"
}
}
}
The status field will be "pending" if require_approval is true, or "sent" if auto-approved.
Error Responses
| Status | Reason |
|---|---|
| 400 | "xml field is required" |
| 422 | Parse error details |
GET /api/cap
List all alerts across all states.
Response (200)
{
"status": "ok",
"data": {
"pending": [...],
"active": [...],
"history": [...]
}
}
Each array contains alert objects in the same format as the POST response.
GET /api/cap/:id
Get a single alert by ID.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Alert UUID |
Response (200)
Returns the alert object.
Error Response
| Status | Reason |
|---|---|
| 404 | "Alert not found: <id>" |
PUT /api/cap/:id
Approve or reject a pending alert.
Request Body
{
"action": "approve",
"operator": "operator1"
}
| Parameter | Type | Required | Description |
|---|---|---|---|
action | string | Yes | "approve" or "reject" |
operator | string | No | Operator name for audit trail. Defaults to "unknown". |
Response (200)
Returns the updated alert object with new status ("sent", "broadcasting", or "rejected").
Error Responses
| Status | Reason |
|---|---|
| 400 | "action must be 'approve' or 'reject'" |
| 404 | "Alert not found: <id>" |
Web Interface: CAP Alerts Page
Path: /cap
Refresh: Every 3 seconds + real-time PubSub updates
The CAP Alerts page provides the operator approval workflow and broadcast monitoring interface.
Statistics Cards
Four summary cards across the top:
| Card | Description |
|---|---|
| Pending Approval | Number of alerts awaiting operator action (amber when > 0) |
| Active Broadcasts | Number of currently broadcasting alerts |
| Total Processed | Total alerts in history (sent + rejected + failed) |
| Approval Mode | Current mode: "Manual" or "Auto" |
Pending Alerts Panel
Only visible when require_approval is true. Shows alerts awaiting operator approval.
| Column | Description |
|---|---|
| TIME | When the alert was received (HH:MM:SS) |
| EVENT | Alert event type (e.g., "Tornado Warning") |
| SEVERITY | Alert severity level |
| CELLS | Number of cells matched by polygon resolution |
| STATUS | Pending badge (amber) |
| ACTIONS | Preview, Approve, and Reject buttons |
Preview expands the row to show:
| Detail | Description |
|---|---|
| Description | Full alert description text (the broadcast message content) |
| Source | How the alert was received (http_post or feed_poll) |
| TACs | Matched Tracking Area Codes for 4G broadcast |
| LACs | Matched Location Area Codes for 2G and 3G broadcast |
| Message ID | CB message identifier |
| PLMN | MCC/MNC used for broadcast |
Approve triggers immediate broadcast via the CBC engine to all connected MMEs (4G), RNCs (3G), and BSCs (2G).
Reject moves the alert to history with :rejected status.
Active Broadcasts Panel
Shows alerts currently being broadcast:
| Column | Description |
|---|---|
| EVENT | Alert event type |
| MSG ID | CB message identifier |
| TACs | Targeted Tracking Area Codes |
| STARTED | Broadcast start time |
| STATUS | Broadcasting (blue) or Sent (green) badge |
Alert History Panel
Shows sent, rejected, and failed alerts (last 200):
| Column | Description |
|---|---|
| TIME | Sent or received timestamp |
| EVENT | Alert event type |
| SEVERITY | Alert severity level |
| CELLS | Matched cell count |
| TACs/LACs | Targeted TACs and LACs |
| STATUS | Sent (green), Rejected (red), or Failed (red) badge |
Real-Time Updates
The page subscribes to the cap:alerts PubSub topic. When a new alert arrives or an alert status changes, the page auto-refreshes. A toast notification appears when a new pending alert is received ("New CAP alert received — awaiting approval").
Alert State Persistence
Alert state is persisted to priv/cap_alerts.json in JSON format, following the same pattern as the CBC Engine's priv/active_broadcasts.json. On startup, the Alert Manager reloads this file to restore pending and active alerts.
The history is capped at 200 entries to prevent unbounded growth.
Atom Feed Polling
The feed poller supports standard CAP Atom feeds as used by national weather services and alerting authorities. Feed format follows RFC 4287 (Atom Syndication) with CAP alert entries.
Expected Feed Format
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<id>urn:oid:2.49.0.1.840.0.abc123</id>
<title>Tornado Warning</title>
<content type="text/xml">
<alert xmlns="urn:oasis:names:tc:emergency:cap:1.2">
<!-- Full CAP alert XML -->
</alert>
</content>
</entry>
</feed>
Deduplication
The poller tracks the <id> of every entry it has seen. Entries with previously-seen IDs are skipped. The dedup set is maintained in memory and resets on application restart.
Error Handling
Feed fetch failures (network errors, HTTP errors, XML parse errors) are logged as warnings and do not affect subsequent poll cycles. The poller continues on its configured interval regardless of individual fetch outcomes.
Supervision
The CAP subsystem adds two processes to the OmniLCS supervisor:
| Process | Description |
|---|---|
OmniLcs.Cap.AlertManager | Alert lifecycle GenServer. Manages pending/active/history state, polygon resolution, and broadcast dispatch. |
OmniLcs.Cap.FeedPoller | Atom feed polling GenServer. Schedules periodic fetches for each configured feed URL. |
Both start automatically and are supervised with :one_for_one strategy alongside the existing CBC and E-SMLC processes.
Troubleshooting
No Cells Matched by Polygon
Symptoms: Alert is accepted but matched_cells is 0 and TAC/LAC lists are empty.
Possible causes:
- Cell database has no cells with geographic positions inside the warning polygon
- Cells exist in the polygon area but lack
tacandlacfields - Polygon coordinates in the CAP XML are in the wrong order or format
Resolution:
- Verify cells in the warning area have
tacand/orlacvalues set viaGET /api/cells - Check that cell positions (latitude/longitude) are correct
- Try enabling
coverage_aware: trueif cells are near polygon edges - Validate the CAP polygon coordinates against a map
Alert Stuck in Pending
Symptoms: Alert appears as pending but operator cannot approve.
Possible causes:
require_approvalistrueand no operator has approved the alert- LiveView page not connected (check browser WebSocket connection)
Resolution:
- Navigate to
/capin the Control Panel and click Approve - Alternatively, use the REST API:
PUT /api/cap/<id>with{"action": "approve", "operator": "operator1"}
Feed Poller Not Picking Up Alerts
Symptoms: Configured feed URL has new alerts but they don't appear in OmniLCS.
Possible causes:
- Feed URL is incorrect or unreachable
- Feed format is not standard Atom with embedded CAP
- TLS certificate issues
- Alert entries don't contain
<content>with embedded<alert>XML
Resolution:
- Check logs for
CAP FeedPoller: Failed to fetchmessages - Verify the feed URL returns valid Atom XML
- Confirm the feed contains
<entry>elements with<content>containing<alert>XML - For TLS issues, check that the feed URL is accessible from the OmniLCS server
Broadcast Sent but Not Received by UEs
Symptoms: Alert status shows "Sent" but no UEs receive the broadcast message.
Possible causes:
- TAC/LAC values don't match the actual network configuration
- MMEs or BSCs rejected the broadcast (check CBC 4G / CBC pages for response details)
- Message encoding issues
Resolution:
- Verify TAC values match the MME's tracking area configuration
- Check the CBC 4G page for Write-Replace-Warning-Response status
- Check the CBC page for WRITE-REPLACE COMPLETE/FAILURE messages
- Review broadcast results in the alert history (expand the alert row)
3GPP and Standards References
| Specification | Title |
|---|---|
| OASIS CAP v1.2 | Common Alerting Protocol Version 1.2 |
| RFC 4287 | The Atom Syndication Format |
| TS 29.168 | Cell Broadcast Centre interfaces with the Evolved Packet Core (SBC-AP) |
| TS 48.049 | Cell Broadcast Centre Protocol (CBSP) |
| TS 23.041 | Technical realization of Cell Broadcast Service (CBS) |
| TS 23.038 | Alphabets and language-specific information |