Skip to main content

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.

CAP Alert Management

Architecture

Alert Lifecycle

When a CAP alert is received (via HTTP POST or feed poll):

  1. The CAP XML parser extracts the alert envelope, info blocks, warning areas (polygons), and CB parameters
  2. 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)
  3. If require_approval is true, the alert is queued as pending and appears in the operator LiveView UI for preview and approval
  4. If require_approval is false, the alert is auto-approved and immediately dispatched to the CBC engine
  5. 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

ParameterTypeRequiredDefaultDescription
require_approvalbooleanNotrueWhen true, alerts are queued for operator approval before broadcast. When false, alerts are auto-approved and broadcast immediately.
plmnmapNo%{mcc: "001", mnc: "01"}PLMN identity (MCC/MNC) used in broadcast messages. Must match the serving network.
plmn.mccstringYes"001"Mobile Country Code (3 digits).
plmn.mncstringYes"01"Mobile Network Code (2-3 digits).
coverage_awarebooleanNofalseWhen 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.
feedslistNo[]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}
]
ParameterTypeRequiredDefaultDescription
urlstringYes--URL of the CAP Atom feed. Must return Atom XML (RFC 4287) with embedded CAP alert entries.
poll_interval_secondsintegerNo60Seconds 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

FieldTypeDescription
tacintegerTracking Area Code. Used for 4G SBC-AP broadcasts. Cells with a TAC are included in the TAI list sent to MMEs.
lacintegerLocation 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.
ratstringRadio 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

ElementDescription
<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:

ElementDescription
<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 NameDescription
CBMessageIdentifier16-bit CB message ID for the broadcast
CBRepetitionIntervalSeconds between broadcast repetitions
CBNumberOfBroadcastsTotal 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:

ElementDescription
<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>"
}
ParameterTypeRequiredDescription
xmlstringYesComplete 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

StatusReason
400"xml field is required"
422Parse 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

ParameterTypeDescription
idstringAlert UUID

Response (200)

Returns the alert object.

Error Response

StatusReason
404"Alert not found: <id>"

PUT /api/cap/:id

Approve or reject a pending alert.

Request Body

{
"action": "approve",
"operator": "operator1"
}
ParameterTypeRequiredDescription
actionstringYes"approve" or "reject"
operatorstringNoOperator name for audit trail. Defaults to "unknown".

Response (200)

Returns the updated alert object with new status ("sent", "broadcasting", or "rejected").

Error Responses

StatusReason
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:

CardDescription
Pending ApprovalNumber of alerts awaiting operator action (amber when > 0)
Active BroadcastsNumber of currently broadcasting alerts
Total ProcessedTotal alerts in history (sent + rejected + failed)
Approval ModeCurrent mode: "Manual" or "Auto"

Pending Alerts Panel

Only visible when require_approval is true. Shows alerts awaiting operator approval.

ColumnDescription
TIMEWhen the alert was received (HH:MM:SS)
EVENTAlert event type (e.g., "Tornado Warning")
SEVERITYAlert severity level
CELLSNumber of cells matched by polygon resolution
STATUSPending badge (amber)
ACTIONSPreview, Approve, and Reject buttons

Preview expands the row to show:

DetailDescription
DescriptionFull alert description text (the broadcast message content)
SourceHow the alert was received (http_post or feed_poll)
TACsMatched Tracking Area Codes for 4G broadcast
LACsMatched Location Area Codes for 2G and 3G broadcast
Message IDCB message identifier
PLMNMCC/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:

ColumnDescription
EVENTAlert event type
MSG IDCB message identifier
TACsTargeted Tracking Area Codes
STARTEDBroadcast start time
STATUSBroadcasting (blue) or Sent (green) badge

Alert History Panel

Shows sent, rejected, and failed alerts (last 200):

ColumnDescription
TIMESent or received timestamp
EVENTAlert event type
SEVERITYAlert severity level
CELLSMatched cell count
TACs/LACsTargeted TACs and LACs
STATUSSent (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:

ProcessDescription
OmniLcs.Cap.AlertManagerAlert lifecycle GenServer. Manages pending/active/history state, polygon resolution, and broadcast dispatch.
OmniLcs.Cap.FeedPollerAtom 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 tac and lac fields
  • Polygon coordinates in the CAP XML are in the wrong order or format

Resolution:

  1. Verify cells in the warning area have tac and/or lac values set via GET /api/cells
  2. Check that cell positions (latitude/longitude) are correct
  3. Try enabling coverage_aware: true if cells are near polygon edges
  4. Validate the CAP polygon coordinates against a map

Alert Stuck in Pending

Symptoms: Alert appears as pending but operator cannot approve.

Possible causes:

  • require_approval is true and no operator has approved the alert
  • LiveView page not connected (check browser WebSocket connection)

Resolution:

  1. Navigate to /cap in the Control Panel and click Approve
  2. 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:

  1. Check logs for CAP FeedPoller: Failed to fetch messages
  2. Verify the feed URL returns valid Atom XML
  3. Confirm the feed contains <entry> elements with <content> containing <alert> XML
  4. 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:

  1. Verify TAC values match the MME's tracking area configuration
  2. Check the CBC 4G page for Write-Replace-Warning-Response status
  3. Check the CBC page for WRITE-REPLACE COMPLETE/FAILURE messages
  4. Review broadcast results in the alert history (expand the alert row)

3GPP and Standards References

SpecificationTitle
OASIS CAP v1.2Common Alerting Protocol Version 1.2
RFC 4287The Atom Syndication Format
TS 29.168Cell Broadcast Centre interfaces with the Evolved Packet Core (SBC-AP)
TS 48.049Cell Broadcast Centre Protocol (CBSP)
TS 23.041Technical realization of Cell Broadcast Service (CBS)
TS 23.038Alphabets and language-specific information