📡 RDS AI Decoder – Documentation v3.3
📡

RDS AI Decoder

Plugin Documentation
Server Plugin: rds-ai-decoder_server.js
Client Plugin: rds-ai-decoder.js
Author: Highpoint
Date: 2026-08-05
v3.3

Table of Contents

1What is RDS?
2Why an AI Plugin?
3What's New in Version 3.3
4Architecture – The Two Plugin Components
5Simple Explanation – How the Plugin Works
5.1Deterministic Frame Matching
5.2Dynamic fmdx.org Reference Database
5.3PS Lock Engine & Confidence
5.4Alternate Frequency (AF) Decoding
5.5RDS Follow Mode & Admin Padlock
5.6Special / Wildcard PI Codes
5.7Regional PI Codes (PIreg)
5.8The Floating Panel, Log & Real-Time List
6Technical Deep Dive
6.1RDS Group Structure and Error Correction
6.2Deterministic Logic: Perfect Frames vs. Chimeras
6.3Sporadic-E Cluster Tracking & Validation
6.4Stateless Design & Real-Time Analytics
6.5dataHandler Patch – Property Locking
6.6tx_search.js Auto-Patching & txInfo Logic
6.7GPS WebSocket Listener & Bulk Indexing
7REST API Reference
8Configuration Parameters
9Troubleshooting & FAQ
10Integration with RDS RAW Decoder
11Interactive DX Map (Leaflet)
11.1Real-Time Rendering & Legends
11.2Scatter Logic (Co-located Transmitters)
11.3Interactive Tooltips
11.4Auto-Centering & Navigation
11.5Map & List Filters
12Real-Time Station List
12.1Sortable Columns & Data
12.2Status Badges
12.3Continuous CSV Export

1 · What is RDS?

RDS (Radio Data System) is a digital communication standard that transmits data as an inaudible signal modulated onto FM broadcasts (87.5–108 MHz). It was standardised under ETSI EN 50067 and has been in use across Europe since the 1980s. In North America, a nearly identical standard exists called RBDS.

RDS carries a variety of continuous digital metadata alongside the analog audio signal. The most commonly used data fields are:

Code Name Meaning Size
PI Programme Identification Unique 16-bit station identifier (hex, e.g. D3C3). Essential for auto-tuning. 16 bit
PS Programme Service Name Station name, max. 8 characters (e.g. "NRJ ") 8 × 8 bit
RT RadioText Scrolling text, max. 64 characters (title, artist, show name) 64 × 8 bit
PTY Programme Type Programme category (0–31, e.g. 10 = Pop Music) 5 bit
TP Traffic Programme Flag indicating if the station broadcasts traffic announcements 1 bit
TA Traffic Announcement Flag indicating if a traffic announcement is currently active 1 bit
AF Alternate Frequencies List of alternative frequencies carrying the same programme 8 bit each
ECC Extended Country Code Extended country identifier (combined with PI nibble to determine country) 8 bit

RDS transmits at 1,187.5 bit/s as a BPSK signal on a 57 kHz subcarrier. A complete basic data block containing PI, PTY, TP, and two characters of the PS name requires 104 bits (26 bits per block × 4 blocks). A complete 8-character PS transmission takes approximately 0.35 seconds under perfect, error-free conditions.

2 · Why an AI Plugin?

Under local reception conditions with a strong antenna, RDS decoding is instantaneous and error-free. However, during long-distance FM reception (DX) – especially via tropospheric ducting, meteor scatter, or Sporadic-E propagation – signals are often extremely weak, rapidly fading, and severely affected by multipath interference or co-channel interference.

When the signal-to-noise ratio drops, the native RDS decoder built into the receiver chips (like TEF6686 / NXP) struggles:

Plugin Goal: Rather than relying on the receiver's real-time, volatile interpretation of the data, the AI Decoder intercepts the raw bitstream before it reaches the UI. By combining live decoded block pairs with the global FMDX.ORG reference database, it constructs a highly reliable, deterministic match. Corrupted characters are filtered out mathematically, and the web interface is fed a stable, perfectly locked station name, completely eliminating UI flicker.

3 · What's New in Version 3.3

Version 3.3 introduces a major overhaul of the deterministic matching engine, focusing on extreme accuracy, strict positional matching, and anti-jitter mechanics, while retaining the powerful Sporadic-E Cluster Tracking and Temporal Decay features introduced in earlier iterations.

1. Strict Positional Matching & Zero-Guessing

The engine no longer forces a 100% lock based purely on the PI code and a "Unique ITU" fallback. It now mandates that at least one valid PS character pair is actually received over the air, and this pair must exactly match the database in both content and display position. Without a valid over-the-air match, confidence immediately drops to 0%.

2. Space & Underscore Wildcards

Received spaces ( ) and underscores (_) are now intelligently treated as wildcards by the engine. This prevents the decoder from wrongly rejecting valid database candidates when receiving partial frames padded with placeholders (e.g., successfully verifying the characters MAXX inside a partially blank ______XX transmission).

3. Dynamic & Rolling PS Protection

By eliminating blind fallbacks, the engine now safely handles rolling PS texts (such as scrolling phone numbers or dynamic artist data). If the received characters contradict the database (like receiving a phone number instead of a station name), the AI instantly recognizes the mismatch, drops its confidence to 0%, and passes the raw rolling data through to the UI without overwriting it.

4. Anti-Jitter Scanner Fix

When operating in raw data mode (0% confidence), partial string updates previously caused the native scanner's ScanHoldTime timer to constantly reset, preventing weak stations from ever being logged. Version 3.3 introduces a fix that feeds a stable blank string to the scanner until a complete or verified frame is decoded, ensuring marginal stations are properly and reliably logged.

5. Improved PS Parsing & Dynamic PS Support

The parsePSVariants algorithm has been refined to intelligently support Dynamic PS strings. It now splits database entries containing spaces (e.g., __MDR___ _KULTUR_) into multiple valid 8-character frames, while strictly preserving underscores as literal characters (e.g., RMF_MAXX). This guarantees flawless tracking of scrolling text sequences without breaking single-word station locks.

6. Rigorous Full Lock Verification

A permanent "frozen" lock is now exclusively established only if all 8 PS characters are received error-free and match the database perfectly in their entirety.

7. Dynamic ERP Sanity Check

The baseline scoring algorithm now features a strict sanity check. If a massive local station is present on a frequency, distant low-power "mosquito" transmitters receive a heavy penalty, preventing illogical overrides.

8. Map & UI Quality of Life

All timestamps are now strictly formatted in UTC. The map's Auto-Center feature now gracefully disables itself upon any manual map interaction. Furthermore, a new EXPORT CSV button provides a continuous, running session history including algorithmic status codes.

4 · Architecture – The Two Plugin Components

┌──────────────────────────────────────────────────────────────────────┐ │ WEB SERVER (Node.js) │ │ │ │ ┌────────────┐ RDS data stream ┌──────────────────────────────┐ │ │ │ Receiver │ ─────────────────▶ │ datahandler.js │ │ │ │ Hardware │ │ (native decoder) │ │ │ │ TEF6686 / │ └──────────────┬───────────────┘ │ │ │ TEF6687 │ │ Patch-Hooks │ │ └────────────┘ ┌────────────────────────▼──────────────┐ │ │ │ rds-ai-decoder_server.js v3.3 │ │ │ │ │ │ │ │ • Strict Positional Engine │ │ │ │ • Sporadic-E Cluster Tracking │ │ │ │ • Persistent Config Management │ │ │ │ • tx_search.js Auto-Patcher │ │ │ │ • Dynamic Per-Frequency FMDX Fetch │ │ │ │ • MUF Tracker & Auto-Recording │ │ │ └──────────────┬────────────────────────┘ │ │ │ │ │ maps.fmdx.org ◀───── HTTP fetch ──────┤ │ │ config.json ◀──── lat/lon / port ─────┤ │ │ rdsm_fmdx_cache.json ◀── bulk cache ──┤ │ │ rdsm_config.json ◀── state config ────┘ │ └──────────────────────────────────────────────┬───────────────────────┘ │ WebSocket /data_plugins │ ┌───────────────▼───────────────────────┐ │ User's Browser │ │ │ │ rds-ai-decoder.js v3.3 │ │ │ │ • Draggable Resizable Panels │ │ • Flag Rendering & Rich Analytics │ │ • Sortable Real-Time List View │ │ • Cluster & Anchor Badges │ │ • Interactive Leaflet Map │ └───────────────────────────────────────┘

The plugin is strictly split into two layers:

  1. The Server Layer (Node.js): Intercepts the raw data arriving from the TEF chip. Performs all algorithmic matching using the deterministic engine, evaluates MUF state, calculates physical transmitter distances, manages Sporadic-E Cluster Anchors, and forcefully overwrites the output of the native datahandler.js when RDS Follow Mode is enabled.
  2. The Client Layer (Browser): A pure frontend UI. Connects directly to the server plugin via WebSocket. Handles rendering the detailed realtime log, country flags, PTY badges, Anchor/Cluster badges, Admin toggle UI, and recording triggers. All components are freely movable and resizable.

5 · Simple Explanation – How the Plugin Works

5.1 · Deterministic Frame Matching

Unlike earlier algorithms that collected "points" over time, the engine evaluates the raw signal instantly using a logic gate system against the global database. When an RDS PS character pair (e.g. 'R ' in segment 0) is received with Error Level 0 or 1, the AI evaluates it against the known variants of that station based on its exact positional index.

5.2 · Dynamic fmdx.org Reference Database

The plugin uses a hybrid approach to fetch reference data:

  1. Bulk Pre-Fetch: On startup, it grabs a massive snapshot of all stations based on your GPS location to serve as an instant, zero-latency local fallback layer.
  2. Dynamic Fallback: If a station is received (via Sporadic-E, for example) that falls outside this local snapshot, the plugin actively fires an API call to maps.fmdx.org/api/?freq=... to fetch the worldwide transmitters for that exact frequency, guaranteeing that ultra-long-distance signals are immediately identified.

5.3 · PS Lock Engine & Confidence

Because the logic is deterministic, the confidence score is strictly assigned based on the ambiguity of the match:

ConfidenceStatus / ColorReasoning
100% Green (#28a745) Unambiguous Lock: The received pairs perfectly fit only one station in the database. A permanent, frozen lock is established if all 8 characters match flawlessly.
50% Orange (#fd7e14) Ambiguous / Waiting: Multiple stations match the decoded PS segments perfectly so far. The AI strictly waits for more clean character segments to arrive to mathematically eliminate the remaining false candidates.
0% Red (#dc3545) Mismatch / Fallback: The received characters directly conflict with all known database entries, there is no database entry for this PI, or no valid characters have been received yet. The AI passes the raw, hard-decoded characters through to the UI.

5.4 · Alternate Frequency (AF) Decoding

The Alternate Frequency list is transmitted as a highly compressed set of 8-bit codes in Block 3 of the 0A group. The plugin decodes these codes according to the standard ETSI table (Method A).

The decoded frequencies are maintained in a Set to guarantee uniqueness. In the UI, the AF badge illuminates and displays the total count. Directly underneath the flags block, the individual frequencies are listed horizontally for quick reference. In the Realtime Log, AF data is split into its own modular component, allowing you to selectively filter it.

5.5 · RDS Follow Mode & Admin Padlock

By default, the AI Decoder operates purely as an invisible observer. It draws its own floating statistics panel but does not alter the main web interface (the large blue PS blocks and RadioText banner at the top of the FM-DX-Webserver page).

Activating RDS Follow Mode forces the AI to intercept the data pipeline and inject its mathematically stabilized, FMDX-verified PS strings and RadioText directly into the main web server UI.

UI Interaction:

5.6 · Special / Wildcard PI Codes

Certain PI codes are considered invalid or uninformative by the standard, particularly 0000 and FFFF. Cheap transmitters or pirate stations often use these.

If the plugin detects 0000 or FFFF, it bypasses the database lookup and verification completely. It acts purely as a raw pass-through, rendering whatever error-free characters the hardware provides.

5.7 · Regional PI Codes (PIreg)

In many countries, national networks broadcast identical programming on dozens of transmitters, but insert local news for 5 minutes an hour, temporarily switching their PI code to a "Regional" PI code.

The FMDX database download brings both the Primary PI and the Regional PI. If the plugin detects the Regional PI, it mathematically treats it exactly the same as the Primary PI, locking instantly without missing a beat.

5.8 · The Floating Panel, Log & Real-Time List

The AI interface offers maximum flexibility.

6 · Technical Deep Dive

6.1 · RDS Group Structure and Error Correction

An RDS data stream consists of consecutive "Groups", each 104 bits long, split into 4 Blocks (A, B, C, D). Each block contains 16 bits of payload and 10 bits of Checkword.

The native receiver chip assigns an error level from 0 to 3:

The AI decoder strictly ignores any payload data from a block that has an error level of 2 or 3. Only levels 0 and 1 are permitted into the deterministic logic engines.

6.2 · Deterministic Logic: Perfect Frames vs. Chimeras

The core relies on the runLocalAiPrediction(pi) function.

  1. Buffer Extraction: The script pulls all successfully decoded (Error ≤ 1) character pairs from the 8-character `psBuf` and notes their exact positional index.
  2. Variant Iteration: It pulls every valid 8-character string known to broadcast under the current PI code from the FMDX API.
  3. Perfect Check: It aligns the `psBuf` pairs over the variant string based on their index. If every single non-space pair from the buffer matches the variant string at the exact same index, it is flagged as a perfectFrame. (Note: Any received spaces or underscores act as wildcards and do not trigger mismatches).
  4. Chimera Check: If the station scrolls text, a single complete frame might never arrive cleanly. The logic checks if every single pair in the buffer can be found in at least one of the station's known variants at the correct index. If all pairs find a home at their respective indexes, the station is verified as a Chimera.

A permanent 'frozen' lock is now only established if all 8 PS characters are received error-free and match the database perfectly. If the result yields exactly one matching station network, the AI instantly locks (Confidence 100%) and suppresses UI flicker.

6.3 · Sporadic-E Cluster Tracking & Validation

This feature solves the "Multiple Transmitters, Same PI" problem commonly found in Italy or Spain during Es openings. When a station is locked at 100% confidence, at a distance greater than 800km, the coordinates are saved as an esAnchor with a 10-minute Time-To-Live (TTL).

┌──────────────────────────────────────────────────────────────────┐ │ SPORADIC-E CLOUD (☁️) │ │ │ │ [ ⚓ ACTIVE ANCHOR ] │ │ (100% Verified, PI: D3C3) │ │ / │ \ │ │ / │ \ 250km Radius │ │ / │ \ │ │ [ 📡 Tx B ] │ [ 📡 Tx A ] │ │ (Distance: 400km) │ (Distance: 50km) │ │ NO BONUS │ + CLUSTER BONUS │ │ │ │ │ │ │ │ [ 📻 QTH ] │ │ (Receiver) │ └──────────────────────────────────────────────────────────────────┘ Temporal Decay Multiplier: [0 min] 100% ────────────────────────── 50% [10 min expiration]

When a new ambiguous signal arrives, the algorithm calculates the Haversine distance between every possible candidate transmitter and all active Anchors. Candidates falling within a 250km radius (like Tx A) receive a dynamically scaled score boost. Because Sporadic-E clouds move rapidly, a linear time multiplier (timeMultiplier) gradually reduces the distance bonus of an active Anchor over its 10-minute TTL, forcing fresh reflections to the top of the candidate list.

6.4 · Stateless Design & Real-Time Analytics

Every single evaluation is mathematically absolute and based purely on the current state of the currentState.psBuf array combined with the live API snapshot and the active Es Anchors. If you tune away, the buffer is wiped, and the slate is completely clean.

6.5 · dataHandler Patch – Property Locking

To override the native datahandler.js, the server plugin intercepts the variables via Object.defineProperty getters/setters before they are dispatched via the main Webserver WebSockets. It overwrites dataHandler.dataToSend.ps and dataHandler.dataToSend.pi with the mathematically verified AI strings when Follow Mode is active.

6.6 · tx_search.js Auto-Patching & txInfo Logic

To fully integrate the AI's deterministic matches into the FM-DX-Webserver's native UI, the plugin utilizes the autoPatchTxSearch() routine. During initialization, the server script locates the webserver's core tx_search.js module.

If the file is not yet patched, the plugin creates a secure backup and injects a local aiOverride variable and logic into the fetchTx() function. When the AI successfully locks onto a station, it packages the exact FMDX network match and passes it to the native UI, entirely eliminating the "split-brain" scenario where the AI predicts one transmitter while the native UI guesses another.

6.7 · GPS WebSocket Listener & Bulk Indexing

To ensure basic local offline capabilities, the server plugin still connects to `ws://127.0.0.1:8080/data_plugins` to listen for your GPS payload. Coordinates are rounded to ~1km. The `buildFmdxIndex()` parses the massive JSON tree from `maps.fmdx.org` and structures it into a rapid-access `fmdxByFreq` dictionary, ensuring zero-latency lookups for standard local stations.

7 · REST API Reference

While the AI Decoder communicates primarily over WebSockets for live data, it exposes a silent footprint on the main HTTP API of the FM-DX Webserver.

When polling the /api endpoint of the server, standard RDS fields are replaced by the AI if Follow Mode is active:

{
  "freq": "104.600",
  "pi": "D3B8",
  "ps": "RTL     ",
  "pty": 10,
  "rds": true,
  "ta": 0,
  "tp": 1,
  "ms": 1,
  "af": [89000, 104600],
  "rt0": "RTL - BERLINS HITRADIO",
  "country_name": "Germany",
  "country_iso": "DE"
}

If Follow mode is disabled, the /api returns the native hardware decoding results, which may contain empty ? PI codes or missing fragments.

8 · Configuration Parameters

Advanced users can tweak the constants at the top of rds-ai-decoder_server.js to tune the caching behavior. Your UI settings are now safely stored in rdsm_config.json automatically.

Parameter Default Description
FMDX_BULK_TTL_MS 604800000 Time in milliseconds (7 days) before the local bulk FMDX cache is considered stale and re-downloaded.
FMDX_REINDEX_MIN_DIST_KM 100 Minimum physical distance the GPS must report moving before triggering a completely new bulk download to save server bandwidth.
ES_ANCHOR_TTL_MS 600000 Time in milliseconds (10 mins) that a Sporadic-E Anchor remains valid in memory before expiring.
ES_CLUSTER_RADIUS_KM 250 Maximum radius around an active Anchor to apply the geographical scoring bonus.

9 · Troubleshooting & FAQ

The AI Prediction says "No DB Match" but the station is huge!

Ensure that your FM-DX Webserver has a valid internet connection to reach maps.fmdx.org. If the live fetch for the frequency times out or fails, the AI cannot verify the frames and will fall back to raw data pass-through.

My log window disappeared or is too small!

Because the log window remembers its dimensions in `localStorage`, extreme resizing might occasionally make it hard to grab. Simply clear your browser's local storage for this site (or execute localStorage.removeItem('rdsm_log_pos') in the console) and refresh the page to restore it to the default size.

Why does the "RDS Follow" toggle text light up red?

By default, the AI Decoder operates in a secure Admin-Only mode. The red text indicates that only logged-in Administrators can turn RDS Follow on or off (via Long-Pressing the main navigation button). If an administrator unlocks the system, guest users can also use the toggle.

Why does the Record button (⏺) show a warning?

Raw RDS recording is processed on the server and saved directly to the webserver's logs folder. To prevent abuse and storage issues, only logged-in Administrators can start or stop recordings, or toggle the MUF auto-record modes (EU, NA, AU). Once manually stopped, you will receive a toast notification with the download link to the CSV file.

My GPS coordinates drifted and caused a massive lag spike.

Ensure your GPS hardware has a solid lock. The plugin mitigates GPS jitter by rounding to ~1km and requiring a 100km total movement before triggering the FMDX index rebuild. If you see constant console logs about "fmdx.org index built", your coordinates in `config.json` might be oscillating wildly due to a bad script pushing fake GPS data to the websocket.

10 · Integration with RDS RAW Decoder

The RDS AI Decoder can be used in tandem with the standalone RDS RAW Decoder tool for advanced signal diagnostics and post-reception forensics. The RAW Decoder is a client-side web application designed to visualize and analyze raw RDS hexadecimal blocks and error strings.

🌐 Access the tool here: https://highpoint.fmdx.org/webtools/rds-raw-decoder.html

10.1 · Parallel Live Usage (WebSocket Mode)

You can run the RAW Decoder simultaneously alongside the AI Decoder in your browser. While the server-side AI Decoder acts as an invisible background filter that repairs and stabilizes data for the main web UI, the RAW Decoder serves as a frontend inspector.

10.2 · Post-Event Analysis (CSV File Mode)

One of the most powerful features of the v3.3 AI Decoder is its ability to automatically (via MUF auto-record) or manually (via the Admin Record ⏺ button) capture the raw RDS data stream to a CSV file on the server.

11 · Interactive DX Map (Leaflet)

Version 3.3 integrates a fully interactive Leaflet-powered DX Map directly into the client UI. It visualizes all mathematically verified database matches, calculates signal paths, and renders Sporadic-E clusters geographically in real-time.

11.1 · Real-Time Rendering & Legends

The map uses a dynamic color-coding system to instantly convey the age and status of your reception logs:

11.2 · Scatter Logic (Co-located Transmitters)

When DXing, you frequently receive multiple different stations broadcasting from the exact same physical mast (sharing the exact same coordinates). To prevent these map points from perfectly overlapping and hiding each other, the map employs a geometric Scatter Engine.

If multiple logs share the exact same origin, the map calculates a perfect circle around the true mast location and fans the points out evenly along the edge. Thin black lines are then drawn from each individual point back to the true center of the mast, creating a clear visual web/cluster without losing coordinate accuracy. This spider-web effect triggers dynamically when zooming in (optimized to trigger gracefully at zoom level 9 and closer). This allows you to distinctly see multiple programs originating from the same site.

11.3 · Interactive Tooltips

Hovering your mouse over any point on the map reveals a clean tooltip containing the precise reception metadata without cluttering the screen permanently:

Auto TX Feature: A toggleable "AUTO TX" option in the map filter bar allows the tooltip of the most recently received station to remain permanently visible, acting as an active live-monitor that automatically tracks the freshest signal.

11.4 · Auto-Centering & Navigation

The map supports an Auto-Center feature (toggleable via the UI). When active, the map automatically adjusts its pan and zoom levels to keep both your QTH and the most recent incoming signals perfectly framed on your screen. The Auto-Center feature will now gracefully disable itself the moment you interact with the map (click, drag, or scroll), allowing you to manually explore without the camera snapping back.

11.5 · Map & List Filters

A shared filter bar allows you to refine both the Map and the Real-Time List simultaneously:

12 · Real-Time Station List

Introduced to provide a clear, tabular overview of your current DX session, the Real-Time Station List catalogs every station actively tracked by the engine (sharing the same underlying data as the DX Map).

12.1 · Sortable Columns & Data

The list provides a highly detailed breakdown of every received station. You can sort the data by clicking on any of the column headers (clicking again reverses the sort order). The table is styled with adequate column spacing for clean readability, and remains in its scroll position even as new data streams in. The list responds instantly to the global Time and ITU filters configured in the map panel.

12.2 · Status Badges

The status column utilizes the exact same color-coding logic as the DX Map points, allowing you to instantly gauge the engine's evaluation of the signal:

12.3 · Continuous CSV Export

A new EXPORT CSV button in the panel header allows you to download a continuous, running history of your DX session (up to 30,000 state changes). Instead of just showing the current list, it logs every frequency change and PS update. The export explicitly includes a Status column with algorithmic abbreviations (S = Standard, A = Active Anchor, EXP_A = Expired Anchor, C = Cluster) to make post-reception spreadsheet analysis effortless.