Skip to main content

swx-advisories

A Python package for parsing and analyzing space weather advisories from ICAO and NOAA/SWPC sources.

Features

  • ICAO Advisory Parsing: Parse ICAO space weather advisories (fnxx01/fnxx02 files) with support for:

    • Blank-line and ZCZC/NNNN delimited formats
    • Latitude bands (HNH, MNH, EQN, EQS, MSH, HSH) and polygon coordinates
    • Multi-region parsing: OBS/FCST fields with multiple severity-region pairs (e.g., MOD north + SEV south)
    • Non-contiguous latitude band splitting (e.g., HNH + HSH becomes two separate regions)
    • Event chain building via NR RPLC linking
    • HF COM, GNSS, and RADIATION effect types with MOD/SEV severity
    • Data validation to detect date/time errors and inconsistencies
  • NOAA Alert Parsing: Parse NOAA Space Weather Prediction Center alerts with:

    • Live API fetching from services.swpc.noaa.gov
    • G/S/R scale extraction (geomagnetic, solar radiation, radio blackout)
    • Message type classification (ALERT, WARNING, WATCH, SUMMARY)
    • K-index, X-ray class, and proton flux metrics
  • DataFrame Adapters: Convert advisories to pandas DataFrames (one row per advisory/event)

  • GeoJSON Output: Convert advisory regions to GeoJSON FeatureCollections for map visualization

Installation

pip install swx-advisories

Or install from source:

git clone https://gitlab.com/KNMI-OSS/spaceweather/swx-advisories.git
cd swx-advisories
pip install -e .

Quick Start

ICAO Advisories

from swx_advisories import ICAOFetcher

# Load advisories from a file or directory
fetcher = ICAOFetcher("/path/to/advisories/")
result = fetcher.fetch()

# Access advisories and events directly from the result
for advisory in result.advisories:
    print(f"{advisory.advisory_id}: {advisory.effect}")

for event in result.events:
    print(f"{event.event_id}: {event.effect} ({event.peak_severity})")
    print(f"  Duration: {event.duration}")
    print(f"  Advisories: {event.num_advisories}")

# Convert to DataFrames — three tiers of detail
df_advisories = result.to_dataframe()                  # tier 1: one row per advisory
df_events = result.to_dataframe(detail="event")        # tier 2: one row per event
df_regions = result.to_dataframe(detail="region")      # tier 3: one row per region/timestep

# Convert to GeoJSON for map visualization
geojson = result.to_geojson()

NOAA Alerts

from swx_advisories import NOAAFetcher

# Fetch from live NOAA API
fetcher = NOAAFetcher()
result = fetcher.fetch()

for alert in result:
    print(f"{alert.advisory_id}: {alert.event_type.value}")
    print(f"  Scale: {alert.noaa_scale}")
    print(f"  Severity: {alert.severity}")

# Convert to DataFrame
df = result.to_dataframe()

ICAO Advisories

ICAO space weather advisories are issued by designated Space Weather Centers (SWXCs) to warn aviation of space weather impacts on HF communications and GNSS navigation.

Fetching Advisories

from swx_advisories import ICAOFetcher

# From a single file
fetcher = ICAOFetcher("/path/to/fnxx01_advisory.txt")
result = fetcher.fetch()

# From a directory (searches for fnxx01*.txt and fnxx02*.txt)
fetcher = ICAOFetcher("/path/to/advisory_directory/")
result = fetcher.fetch()

# Access advisories
for advisory in result:
    print(f"{advisory.advisory_id}: {advisory.effect} - {advisory.severity}")

# Check for errors
if not result.success:
    print(f"Errors: {result.errors}")

Building Event Chains

Advisories are linked via the NR RPLC field. The fetcher automatically builds these into event chains:

from swx_advisories import ICAOFetcher

fetcher = ICAOFetcher("/path/to/advisories/")
result = fetcher.fetch()

# Events are automatically built and available on result.events
for event in result.events:
    print(f"\nEvent: {event.event_id}")
    print(f"  Effect: {event.effect}")
    print(f"  Peak severity: {event.peak_severity}")
    print(f"  Start: {event.issue_start}")
    print(f"  End: {event.issue_end}")
    print(f"  Duration: {event.duration}")
    print(f"  Centers: {', '.join(event.centers)}")
    print(f"  Advisories in chain: {event.num_advisories}")

# Skip event building if not needed
result = fetcher.fetch(build_events=False)
assert result.events is None

Filtering by Date Range

from datetime import datetime, timezone
from swx_advisories import ICAOFetcher

fetcher = ICAOFetcher("/path/to/advisories/")

# Filter by date range
since = datetime(2024, 1, 1, tzinfo=timezone.utc)
until = datetime(2024, 6, 30, tzinfo=timezone.utc)
result = fetcher.fetch(since=since, until=until)

# Only advisories with issue_time in range are returned
print(f"Found {len(result.advisories)} advisories in date range")

Advisory Structure

Each ICAOAdvisory contains:

advisory.advisory_id      # e.g., "2026/0042"
advisory.center           # e.g., "PECASUS", "ACFJ"
advisory.effect           # "HF COM", "GNSS", or "RADIATION"
advisory.severity         # "MOD", "SEV", or None (max across regions)
advisory.issue_time       # datetime
advisory.observation      # TemporalObservation with time, location, and regions
advisory.forecasts        # List of 4 Forecast objects (+6h, +12h, +18h, +24h)
advisory.replaces_id      # ID of advisory this replaces, or None if opening
advisory.is_opening       # True if this starts a new event chain
advisory.remark           # Free-text remarks

# Multi-region data (observation and each forecast)
advisory.observation.regions   # list[SeverityRegion] - per-region detail
advisory.observation.location  # GeographicRegion - bounding box (backward compat)
advisory.forecasts[0].regions  # list[SeverityRegion] - per-region detail

Multi-Region Parsing

Since late November 2025, ICAO advisories can contain multiple geographic regions per time step, each with its own severity level. For example:

OBS SWX: 19/1118Z MOD N50 W180...N90 W180 SEV S55 W180...S90 E180

This encodes two regions: northern polar cap at MOD and southern polar cap at SEV. The parser splits these into individual SeverityRegion objects, each with its own severity and GeographicRegion:

from swx_advisories import ICAOFetcher

result = ICAOFetcher("/path/to/advisories/").fetch()

for adv in result.advisories:
    for region in adv.observation.regions:
        print(f"  {region.severity}: lat {region.location.lat_min} to {region.location.lat_max}")

Non-contiguous latitude bands (e.g., SEV HNH HSH where mid-latitudes are unaffected) are automatically split into separate regions.

Backward compatibility is maintained: advisory.severity returns the max severity across all regions, and advisory.observation.location returns the bounding box of all regions.

Data Validation

ICAO advisories from the early operational period (2019-2021) sometimes contain data entry errors such as switched dates or typos. The validation system automatically detects these issues:

from swx_advisories import ICAOFetcher, ValidationIssue

fetcher = ICAOFetcher("/path/to/advisories/")
result = fetcher.fetch()  # Validation runs automatically

# Filter to only clean data
clean_advisories = [a for a in result.advisories if a.is_valid]
clean_events = [e for e in result.events if e.is_valid]

print(f"Valid: {len(clean_advisories)}/{len(result.advisories)} advisories")

# Inspect validation issues on a specific advisory
for adv in result.advisories:
    if not adv.is_valid:
        print(f"{adv.advisory_id}: {adv.validation_issues}")
        for msg in adv.validation_messages:
            print(f"  - {msg}")

Validation issues detected:

Issue Level Description
NEGATIVE_LATENCY Advisory Observation time is after issue time
EXCESSIVE_LATENCY Advisory Observation time is >6h before issue time
FORECAST_TIME_MISMATCH Advisory Forecast time deviates >2h from expected
ORPHANED_ADVISORY Advisory References non-existent parent advisory
DUPLICATE_ADVISORY_ID Advisory Same ID used multiple times for same effect
OBSERVATION_TIME_REVERSAL Event Observation times go backwards in chain
ISSUE_TIME_REVERSAL Event Issue times go backwards in chain
EVENT_EXCESSIVE_DURATION Event Event duration exceeds 7 days

Customizing validation:

from swx_advisories import ICAOFetcher
from swx_advisories.validators import ICAOValidator, validate_advisory

# Disable automatic validation
result = fetcher.fetch(validate=False)

# Run validation manually with custom thresholds
validator = ICAOValidator(
    max_latency_hours=4.0,        # Stricter latency threshold
    forecast_tolerance_hours=1.0,  # Stricter forecast tolerance
    max_event_duration_days=5,     # Stricter duration threshold
)
for event in result.events:
    validator.validate_event(event)

# Check validation summary
print(validator.summary())

Parsing Raw Text

from swx_advisories import ICAOFetcher

text = """FNXX01 EFKL 201549
SWX ADVISORY
DTG:              20260120/1549Z
SWXC:             PECASUS
SWX EFFECT:       GNSS
ADVISORY NR:      2026/99
OBS SWX:          20/1542Z SEV N55 E055 - N85 E055 - N85 E005 - N55 E005 - N55 E055
FCST SWX +6 HR:   20/2200Z NOT AVBL
FCST SWX +12 HR:  21/0400Z NOT AVBL
FCST SWX +18 HR:  21/1000Z NOT AVBL
FCST SWX +24 HR:  21/1600Z NOT AVBL
RMK:              SPACE WEATHER EVENT IN PROGRESS
NXT ADVISORY:     NO FURTHER ADVISORIES=
"""

result = ICAOFetcher.from_text(text)
for adv in result:
    print(f"{adv.advisory_id}: {adv.effect}")

NOAA Alerts

NOAA Space Weather Prediction Center (SWPC) issues alerts, warnings, and watches for space weather events using the G/S/R scale system.

Fetching from API

from swx_advisories import NOAAFetcher

# Fetch all recent alerts from NOAA API
fetcher = NOAAFetcher()
result = fetcher.fetch()

print(f"Fetched {result.count} alerts")
for alert in result:
    print(f"{alert.advisory_id}: {alert.event_type.value} ({alert.noaa_scale})")

Fetching from Local File

from swx_advisories import NOAAFetcher

# Load from a local JSON file (same format as NOAA API)
fetcher = NOAAFetcher(from_file="/path/to/alerts.json")
result = fetcher.fetch()

Filtering Alerts

from datetime import datetime, timezone
from swx_advisories import NOAAFetcher, EventType, MessageType

fetcher = NOAAFetcher()

# Filter by event type
geomag = fetcher.fetch_by_type(EventType.GEOMAGNETIC_STORM)
radio = fetcher.fetch_by_type(EventType.RADIO_BLACKOUT)

# Filter by message type
alerts = fetcher.fetch_active_alerts()    # ALERT messages only
warnings = fetcher.fetch_warnings()        # WARNING messages only
watches = fetcher.fetch_watches()          # WATCH messages only

# Filter by date range
since = datetime(2024, 1, 1, tzinfo=timezone.utc)
until = datetime(2024, 12, 31, tzinfo=timezone.utc)
result = fetcher.fetch(since=since, until=until)

Alert Structure

Each SpaceWeatherAdvisory contains:

alert.advisory_id        # e.g., "ALTK06-690"
alert.event_type         # EventType enum (GEOMAGNETIC_STORM, RADIO_BLACKOUT, etc.)
alert.message_type       # MessageType enum (ALERT, WARNING, WATCH, SUMMARY)
alert.severity           # Severity enum (MINOR, MODERATE, SEVERE, EXTREME)
alert.noaa_scale         # e.g., "G2", "S1", "R3"
alert.issue_time         # datetime
alert.begin_time         # datetime (if available)
alert.end_time           # datetime (if available)
alert.short_description  # Brief description of the event
alert.potential_impacts  # Description of potential impacts

NOAA Scales

The package extracts NOAA space weather scales:

Scale Type Levels
G Geomagnetic Storm G1 (Minor) to G5 (Extreme)
S Solar Radiation Storm S1 (Minor) to S5 (Extreme)
R Radio Blackout R1 (Minor) to R5 (Extreme)

DataFrame Adapters

Convert advisories and events to pandas DataFrames for analysis. The recommended approach is to call to_dataframe() directly on the FetchResult:

from swx_advisories import ICAOFetcher, NOAAFetcher

# ICAO — three tiers of detail
result = ICAOFetcher("/path/to/advisories/").fetch()
df_advisories = result.to_dataframe()                  # tier 1: one row per advisory
df_events = result.to_dataframe(detail="event")        # tier 2: one row per event
df_regions = result.to_dataframe(detail="region")      # tier 3: one row per region/timestep

# NOAA — tier 1 (per-alert)
result = NOAAFetcher().fetch()
df = result.to_dataframe()

The free function to_dataframe() is also available for direct use with model objects:

from swx_advisories import to_dataframe

df_advisories = to_dataframe(result.advisories)            # tier 1 (auto-detected)
df_events = to_dataframe(result.events)                    # tier 2 (auto-detected)
df_regions = to_dataframe(result.advisories, detail="region")  # tier 3 (explicit)

ICAO DataFrames

from swx_advisories import icao_advisories_to_dataframe, icao_events_to_dataframe

# Advisories DataFrame (one row per advisory)
df = icao_advisories_to_dataframe(advisories)
# Columns: advisory_id, center, effect, severity, issue_time, obs_time,
#          lat_min, lat_max, lon_min, lon_max, replaces_id, ...,
#          is_valid, validation_issues

# Events DataFrame (one row per event)
df = icao_events_to_dataframe(events)
# Columns: event_id, effect, peak_severity, num_advisories, duration_hours,
#          issue_start, issue_end, centers, ..., is_valid, validation_issues

# Filter to valid data
clean_df = df[df["is_valid"]]

Regions DataFrame

A flattened DataFrame with one row per region per time step, designed for map visualization and time-series analysis:

from swx_advisories import ICAOFetcher, icao_regions_to_dataframe

result = ICAOFetcher("/path/to/advisories/").fetch()
df = icao_regions_to_dataframe(result.advisories)

# Each row has start_time/end_time defining the validity interval
print(df[["advisory_id", "severity", "region_index",
          "lat_min", "lat_max", "start_time", "end_time"]])

Key features:

  • One row per affected region per time step (obs, +6h, +12h, +18h, +24h)
  • start_time / end_time define each region's validity interval
  • Per-region severity (MOD/SEV), not the advisory-level max
  • geojson column with GeoJSON dict for each region (None for dayside/no-impact)
  • Time steps with NO SWX EXP are excluded (absence = no impact)
Column Description
advisory_id Advisory identifier
center, center_country Issuing center
effect HF COM, GNSS, RADIATION
time_type "obs" or "fcst"
start_time, end_time Validity interval
hours_ahead 0 for obs, 6/12/18/24 for fcst
severity Per-region severity (MOD/SEV)
region_index 0-based index within the time step
lat_min, lat_max, lon_min, lon_max Region bounds
bands Comma-joined latitude band codes
is_daylight_side True for DAYSIDE/DAYLIGHT SIDE regions
above_fl Flight level if specified
geojson GeoJSON Feature dict for this region

NOAA DataFrames

from swx_advisories import noaa_alerts_to_dataframe

df = noaa_alerts_to_dataframe(alerts)
# Columns: advisory_id, event_type, message_type, severity, noaa_scale,
#          issue_time, begin_time, end_time, short_description, ...

GeoJSON Output

Convert advisory regions to GeoJSON FeatureCollections for map visualization. Each region becomes a GeoJSON Feature with [lon, lat] coordinates per RFC 7946.

from swx_advisories import ICAOFetcher

result = ICAOFetcher("/path/to/advisories/").fetch()

# All advisories -> single FeatureCollection
geojson = result.to_geojson()

# Each Feature includes properties:
# advisory_id, center, effect, severity, time_type, time, hours_ahead, region_index

The free functions advisory_to_geojson() and advisories_to_geojson() are also available for direct use:

from swx_advisories import advisory_to_geojson, advisories_to_geojson

geojson = advisory_to_geojson(result.advisories[0])    # single advisory
geojson = advisories_to_geojson(result.advisories)     # multiple advisories

Regions that are daylight-side, no-impact, or undefined produce no Feature. For DAYSIDE regions the is_daylight_side property is set to True on the GeographicRegion, but no polygon geometry is generated since the daylit hemisphere depends on the observation time.

Individual regions also support GeoJSON conversion directly:

region = advisory.observation.regions[0]
feature = region.location.to_geojson()  # GeoJSON Feature dict, or None

Data Models

Enums

from swx_advisories import EventType, MessageType, Severity, Source, ValidationIssue

# Event types
EventType.HF_BLACKOUT           # ICAO HF COM
EventType.GNSS_DEGRADATION      # ICAO GNSS
EventType.GEOMAGNETIC_STORM     # NOAA G-scale
EventType.SOLAR_RADIATION_STORM # NOAA S-scale
EventType.RADIO_BLACKOUT        # NOAA R-scale
EventType.SOLAR_FLARE
EventType.CME

# Message types (NOAA)
MessageType.ALERT
MessageType.WARNING
MessageType.WATCH
MessageType.SUMMARY

# Severity levels
Severity.MINOR
Severity.MODERATE
Severity.SEVERE
Severity.EXTREME

# Validation issues (ICAO)
ValidationIssue.NEGATIVE_LATENCY
ValidationIssue.EXCESSIVE_LATENCY
ValidationIssue.FORECAST_TIME_MISMATCH
ValidationIssue.ORPHANED_ADVISORY
ValidationIssue.DUPLICATE_ADVISORY_ID
ValidationIssue.OBSERVATION_TIME_REVERSAL
ValidationIssue.ISSUE_TIME_REVERSAL
ValidationIssue.EVENT_EXCESSIVE_DURATION

Location Models

from swx_advisories import GeographicRegion, LatitudeBand, SeverityRegion

# Latitude bands used in ICAO advisories
LatitudeBand.HNH  # High Northern Hemisphere (60-90N)
LatitudeBand.MNH  # Middle Northern Hemisphere (30-60N)
LatitudeBand.EQN  # Equatorial Northern (0-30N)
LatitudeBand.EQS  # Equatorial Southern (0-30S)
LatitudeBand.MSH  # Middle Southern Hemisphere (30-60S)
LatitudeBand.HSH  # High Southern Hemisphere (60-90S)

# Geographic region with bounds
region = GeographicRegion(
    lat_min=-90, lat_max=90,
    lon_min=-180, lon_max=180,
    text="Global coverage"
)

# Convert to GeoJSON Feature (RFC 7946, [lon, lat] order)
feature = region.to_geojson()  # dict or None for daylight-side/no-impact

# Severity region pairs a severity level with a geographic region
sev_region = SeverityRegion(severity="MOD", location=region)

API Reference

Fetchers

Class Description
ICAOFetcher(path) Load ICAO advisories from file or directory
NOAAFetcher(from_file=None) Fetch NOAA alerts from API or local file
NOAAArchiveFetcher Fetch NOAA alerts from HTML/FTP archives
FetchResult Container with advisories, metadata, errors, and output methods

Key fetch() parameters:

  • build_events (bool): Build event chains (default: True)
  • validate (bool): Run data validation (default: True, ICAO only)
  • since (datetime): Filter by minimum issue_time
  • until (datetime): Filter by maximum issue_time

Parsers

Class Description
ICAOTextParser Parse raw ICAO advisory text
NOAAParser Parse NOAA JSON alert data

Validators

Class/Function Description
ValidationIssue Enum of validation issue types
ICAOValidator Batch validator with configurable thresholds
validate_advisory(advisory) Validate single advisory
validate_event(event) Validate event and its advisories

FetchResult Methods

Method Description
result.to_dataframe() Tier 1: one row per advisory/alert
result.to_dataframe(detail="event") Tier 2: one row per event chain (ICAO only)
result.to_dataframe(detail="region") Tier 3: one row per region per timestep (ICAO only)
result.to_geojson() GeoJSON FeatureCollection (ICAO only)

Adapters (free functions)

Function Description
to_dataframe(data, detail=None) Auto-detect and convert to DataFrame
icao_advisories_to_dataframe(advisories) ICAO advisories to DataFrame (one row per advisory)
icao_events_to_dataframe(events) ICAO events to DataFrame (one row per event)
icao_regions_to_dataframe(advisories) ICAO regions to DataFrame (one row per region per time step)
noaa_alerts_to_dataframe(alerts) NOAA alerts to DataFrame
advisory_to_geojson(advisory) Single advisory to GeoJSON FeatureCollection
advisories_to_geojson(advisories) Multiple advisories to GeoJSON FeatureCollection

Note: The detail parameter on to_dataframe() selects the output tier: None (default, auto-detected), "event" (tier 2), or "region" (tier 3). The data_type parameter allows creating typed empty DataFrames with expected columns when input is empty.

Development

Running Tests

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

Test Coverage

The package includes 146 tests covering:

  • ICAO text parsing and event building
  • Multi-region parsing and non-contiguous band splitting
  • NOAA message parsing and alert extraction
  • Fetcher functionality
  • DataFrame adapter conversions (advisory, event, and region DataFrames)
  • GeoJSON output
  • Data validation

License

MIT License - see LICENSE file for details.

Acknowledgments

  • ICAO for the space weather advisory format specification
  • NOAA Space Weather Prediction Center for the alerts API

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

swx_advisories-0.3.0.tar.gz (132.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

swx_advisories-0.3.0-py3-none-any.whl (53.6 kB view details)

Uploaded Python 3

File details

Details for the file swx_advisories-0.3.0.tar.gz.

File metadata

  • Download URL: swx_advisories-0.3.0.tar.gz
  • Upload date:
  • Size: 132.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for swx_advisories-0.3.0.tar.gz
Algorithm Hash digest
SHA256 17b438fbcda146d52ad1cbf1986d8a7447b12e81f164aff1c44a653a00820648
MD5 f025dd8223bb72ce91b630043318471d
BLAKE2b-256 c57b9f07120569b54ffe30422f303067e01e1057dedb908b9510be10699f968f

See more details on using hashes here.

File details

Details for the file swx_advisories-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: swx_advisories-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 53.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for swx_advisories-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2f9e342d7db044bb0360a11edff5396b177e4a3d6288935d9ed6e1c9ae404c67
MD5 b3644d368218d181bad2e71f741ba946
BLAKE2b-256 7ff53f5888889cab356c973a2b74282c75b51fd8fefec64c4b8caf946a9fc19f

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page