📡 RDS AI Decoder – Documentation v2.4
📡

RDS AI Decoder

Plugin Documentation
Server Plugin: rds-ai-decoder_server.js
Client Plugin: rds-ai-decoder.js
Version: 2.4
Author: Highpoint
Date: April 2026
v2.4

Table of Contents

1What is RDS?3
2Why an AI Plugin?3
3What's New in Version 2.44
4Architecture – The Two Plugin Components6
5Simple Explanation – How the Plugin Works7
5.1Learning by Voting7
5.2fmdx.org Reference Database8
5.3PS Lock Engine and Hybrid Case9
5.4Provisional → Locked Status Display10
5.5Frequency Change and Pre-Cache11
5.6Dynamic vs. Static Stations12
5.7Alternate Frequency (AF) Decoding12
5.8RDS Follow Mode13
5.9Special / Wildcard PI Codes14
5.10Regional PI Codes (PIreg)14
5.11The Panel – Display Elements15
5.12Extended FMDX Station Data v2.416
5.13Local Database Management v2.417
6Technical Deep Dive18
6.1RDS Group Structure and Error Correction18
6.2fmdx.org Bulk Index & Smart Propagation Score v2.419
6.3PS Lock Engine – Priority Ladder21
6.4Provisional → Locked Status Engine22
6.5Voting Engine – Compact Aggregation Format23
6.6Confidence Calculation24
6.7PI Verification Pipeline25
6.8Database Format and Storage Management26
6.9WebSocket Protocol and Delete Logic v2.428
6.10dataHandler Patch – Property Locking30
6.11FMDX.ORG Panel – Frequency Chips and AF Coverage31
6.12GPS WebSocket Listener32
6.13Panel State Persistence v2.433
7REST API Reference34
8Configuration Parameters36
9Troubleshooting & FAQ37

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 collecting and statistically evaluating thousands of received packets – including those flagged with minor errors – a highly reliable probability model of the station is constructed.

Known stations are recognized instantly from the FMDX.ORG database. 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 2.4

Version 2.4 brings highly requested administrative features to the front-end, visualizes deep propagation data in the UI, and radically overhauls the database matching algorithms to favor smart propagation scoring over simple line-of-sight distance.

1. Extended Station Data Visualization v2.4

The Statistics panel now displays rich context for the matched FMDX.org reference. When a match occurs, the UI renders:

2. Smart Propagation Scoring v2.4

Previously, when multiple transmitters shared the same PI code, the algorithm simply selected the physically closest mast. Version 2.4 introduces the calculatePropagationScore() algorithm which uses real-world RF propagation physics to pick the most logical transmitter:

3. Local Database UI Management v2.4

A brand new LOCAL DB section has been added to the main panel. It actively lists all stored memory entries specifically tied to the currently tuned frequency. It shows the stored PI, the resolved PS string, and the historical seen-count.

4. Admin Database Purge Controls v2.4

For users logged in as Administrators, the new LOCAL DB list features a red next to every entry. Clicking this executes an immediate WebSocket command (rdsm_delete_pi) to safely purge phantom, ghost, or misidentified PI codes from your persistent local rdsm_memory.json. A confirmation dialog prevents accidental clicks.

5. Panel State Persistence v2.4

Dragging the RDS panel around the screen is no longer temporary. The UI now saves its exact pixel coordinates in your browser's localStorage and automatically restores the panel to your preferred screen location across page reloads.

Note on v2.3 changes: The aggressive hardware CRC collision blocking, active UI FMDX filtering, and startup sanitizeDatabaseWithFmdx routines introduced in v2.3 remain active and have been further optimized in 2.4.

4 · Architecture – The Two Plugin Components

┌──────────────────────────────────────────────────────────────────────┐ │ WEB SERVER (Node.js) │ │ │ │ ┌────────────┐ RDS data stream ┌──────────────────────────────┐ │ │ │ Receiver │ ─────────────────▶ │ datahandler.js │ │ │ │ Hardware │ │ (native decoder) │ │ │ │ TEF6686 / │ └──────────────┬───────────────┘ │ │ │ TEF6687 │ │ Patch-Hook │ │ └────────────┘ ┌────────────────────────▼─────────────┐ │ │ │ rds-ai-decoder_server.js v2.4 │ │ │ │ │ │ │ │ • fmdx.org Bulk Index (3000 km) │ │ │ │ – primary PI + PIreg indexed │ │ │ │ – fmdxByFreq + fmdxByPI │ │ │ │ • Propagation Score Sorting (v2.4) │ │ │ │ • Voting Engine + PS Lock │ │ │ │ • Provisional→Locked state engine │ │ │ │ • _aiTimer at module scope │ │ │ │ • Hybrid mixed-case PS │ │ │ │ • AF / ECC / Country Decode │ │ │ │ • Filtered AF lookup (PS-aware) │ │ │ │ • DB Management (versioned) │ │ │ │ • AI Prediction Builder │ │ │ │ • PI Verification Pipeline │ │ │ │ • dataHandler property locking │ │ │ │ • WebSocket Delete PI Handler (v2.4) │ │ │ └──────────────┬──────────��─────────────┘ │ │ │ │ │ maps.fmdx.org ◀───── HTTP fetch ──────┤ │ │ config.json ◀──── lat/lon / port ─────┤ │ │ rdsm_memory.json ◀───── persist ──────┤ │ │ rdsm_fmdx_cache.json ◀── bulk cache ──┘ │ └──────────────────────────────────────────────────────┬───────────────┘ │ WebSocket /data_plugins│ ┌──────────▼────────────┐ │ User's Browser │ │ │ │ rds-ai-decoder.js │ │ v2.4 │ │ • Panel + manual │ │ link (header) │ │ • STATUS row: │ │ WAIT→PROV→LOCKED │ │ • FMDX.ORG section: │ │ – variant chips │ │ – AF coverage % │ │ – freq chip scroll │ │ • LOCAL DB (v2.4) │ │ – inline deletion │ │ • Draggable (saved) │ └───────────────────────┘

The plugin is strictly split into two layers:

  1. The Server Layer (Node.js): This intercepts the low-level hex data arriving from the TEF chip over the serial connection. It performs all the heavy lifting: downloading the 30MB FMDX index, calculating Haversine distances, managing the persistent local JSON database, running the statistical weighting engine, and eventually forcibly overwriting the output of the native datahandler.js when RDS Follow Mode is enabled.
  2. The Client Layer (Browser): A pure frontend UI written in vanilla JavaScript. It connects directly to the server plugin via a dedicated WebSocket channel. It performs no decoding logic of its own; it exists solely to draw the beautiful debugging panels, the statistical confidences, the FMDX verification badges, and to capture Admin commands like database deletions.

5 · Simple Explanation – How the Plugin Works

5.1 · Learning by Voting

The station name (PS) consists of 8 character positions. Each position is voted on separately. The receiver hardware flags every 16-bit block with an "Error Level" from 0 (perfect) to 3 (unrecoverable).

When the AI Decoder receives a raw PS character (e.g. at position 0), it looks at the hardware error level:

Received packets for position 0: Packet 1: 'N' (error=0, weight=10) ──┐ Packet 2: 'N' (error=0, weight=10) │ Voting Packet 3: 'M' (error=1, weight= 5) │ for position 0 Packet 4: 'N' (error=0, weight=10) │ Packet 5: '?' (error=2, weight= 0) ──┘ (discarded) Result: 'N' = 30 points ← Winner 'M' = 5 points

These points are accumulated in the server's memory. As time passes, the points decay (using a 7-day half-life formula). This allows the plugin to "forget" old corrupted data while strongly favoring a consistent signal. After enough votes, a reliable station name emerges.

5.2 · fmdx.org Reference Database

On startup (and automatically every 6 hours) the plugin downloads the full FM transmitter database from maps.fmdx.org/api/?qth={lat},{lon} centred on the receiver's own location. All transmitters within a radius of 3000 km are indexed into two lightning-fast in-memory lookup tables.

Each fmdx.org entry provides:

Strict PI gate: The plugin only displays an fmdx.org reference entry when the received PI code (or its PIreg equivalent) is explicitly listed for the exact currently tuned frequency. A PI that appears on a different frequency in the database does not count as a match and is silently skipped. This prevents false positives from identical PI codes in different countries.

5.3 · PS Lock Engine and Hybrid Case

Once a PS name has been determined with high confidence, it is locked – the displayed name stops changing until the frequency or PI code changes. This totally eliminates UI flickering.

The lock is achieved through three priority levels:

Priority A – DB verified string: Previously stored psVerifiedRaw ─────▶ LOCK immediately (entire PS received error-free in one round, stored in DB) Priority B – fmdx.org match ≥ 75%: Live buffer matches a known PS variant ─▶ LOCK with hybrid PS (raw RDS case preserved where characters agree with reference) Priority C – Full raw verification: All 8 positions received with errLevel ≤ 1, all characters non-space, one complete round ─▶ LOCK + store

The Hybrid Case System: Many stations transmit their PS names in ALL CAPS (e.g. ANTENNE ) but they look terrible in the UI. FMDX.org contains beautifully formatted mixed-case variants (e.g. Antenne ). The plugin uses a Hybrid Constructor: if the live received uppercase letter mathematically matches the reference mixed-case letter at the exact same position, the plugin swaps in the beautiful mixed-case letter before sending it to the web server.

5.4 · Provisional → Locked Status Display

Before a PS name is fully locked, the decoder goes through an intermediate provisional stage. This state is communicated to the user via the STATUS row in the panel.

StateBadge colourConditionAdditional info shown
WAIT Dark grey (neutral) Default on start / after frequency change. Not enough data yet. "collecting…"
PROVISIONAL Amber (#c8a020) A candidate PS exists with confidence ≥ 55% but is not yet locked. Confidence % · stable time in seconds (e.g. "72% · stable 1.5s")
LOCKED Green (#44ff88) Confidence ≥ 90% and stable, OR strong fmdx.org match, OR raw-verified. Reason for lock (e.g. "FMDX match 100%" or "DB verified string")

5.5 · Frequency Change and Pre-Cache

When you change frequencies, the native decoder usually blanks out completely. The AI plugin behaves differently. Because it stores historical data in rdsm_memory.json, the moment you tune to a new frequency, it looks up known transmitters for that frequency.

If it finds a station in its memory that matches the frequency, it instantly prepares the "Predicted PS" and displays it in the statistics panel, even before a single RDS packet has been received from the antenna. If RDS Follow Mode is enabled, this cached prediction is immediately pushed to the web UI.

5.6 · Dynamic vs. Static Stations

Some stations unfortunately use their PS field to transmit scrolling text (song titles, phone numbers) instead of a static station name. This is officially illegal under the RDS standard but widely practiced (especially in Italy and Eastern Europe).

The AI decoder features a Dynamic Jump Engine. It maintains a rolling buffer of the last 8 full strings received. If it detects that the strings are shifting sideways (scrolling) or constantly alternating between vastly different words, it flags the PI code as isDynamic: true.

When a station is flagged as dynamic, the statistical voting engine is completely bypassed, because voting on scrolling text produces absolute garbage (a mix of letters from different words). Instead, the plugin switches to a raw pass-through mode for that station.

5.7 · Alternate Frequency (AF) Decoding

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

Code 1 = 87.6 MHz ... Code 204 = 107.9 MHz

The decoded frequencies are maintained in a Set to guarantee uniqueness. In the UI, the AF badge illuminates and displays the total count. Hovering over the badge reveals the exact list of frequencies.

5.8 · RDS Follow Mode

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). The native TEF hardware controls the main UI.

Clicking the RDS Follow button at the bottom of the stats panel takes the AI active. Once activated, the AI plugin forcefully intercepts the data pipeline and injects its mathematically stabilized, FMDX-verified, hybrid-cased PS strings and RadioText directly into the main web server UI. The flickering native decoder is completely overridden.

Admin Only: Because RDS Follow alters the main UI for all users viewing the server, the toggle button is strictly restricted to logged-in Administrators. If you are not an admin, the button will be greyed out.

5.9 · 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.

The plugin has a special bypass logic for these. If it detects 0000 or FFFF, it skips database lookup, skips FMDX verification, and skips statistical voting. It acts purely as a raw pass-through, allowing you to see the raw text of pirate stations without the AI attempting to "correct" them into known stations.

5.10 · Regional PI Codes (PIreg)

In many countries (like Germany or France), national networks broadcast identical programming on dozens of transmitters, but insert local news for 5 minutes an hour. During those 5 minutes, they switch their PI code to a "Regional" PI code.

The plugin supports this flawlessly. 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. The station will lock instantly without needing to rebuild its statistical voting profile from scratch.

5.11 · The Panel – Display Elements

The floating AI panel is divided into sections:

5.12 · Extended FMDX Station Data v2.4

A new feature in v2.4. When the statistics panel is expanded (by clicking the STATISTICS button), and a valid FMDX database match occurs, a golden highlighted section appears.

It displays:

This allows DXers to instantly verify not just what station they are receiving, but exactly where it is transmitting from, its power output, and which direction to point their directional yagi antennas.

5.13 · Local Database Management v2.4

Also new in v2.4, the main panel now features a LOCAL DB section at the very bottom.

This UI dynamically queries the server's rdsm_memory.json and extracts all stations that the plugin has ever seen on the currently tuned frequency. It shows the PI code, the locked PS string, whether it was flagged as dynamic (⚡), and how many packets it has historically received.

Deletion: Sometimes the TEF hardware generates a "Ghost PI" - a totally fake PI code generated by random noise that unfortunately passes the CRC check. If the plugin logs this, it will appear in the Local DB list. Logged-in Administrators will see a red next to the entry. Clicking this deletes the phantom station from the server's permanent memory immediately.

6 · Technical Deep Dive

6.1 · RDS Group Structure and Error Correction

An RDS data stream consists of consecutive "Groups", each 104 bits long. A group is split into 4 Blocks (A, B, C, D) of 26 bits each. Each block contains 16 bits of payload data and 10 bits for a Checkword/Offset word used for Error Detection and Correction.

The native receiver chip evaluates the 10-bit checkword and assigns an error level array (e.g. errB = [0, 0, 1, 3]) for the four blocks:

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 voting algorithms.

6.2 · fmdx.org Bulk Index & Smart Propagation Score v2.4

The plugin downloads roughly 30MB of JSON data from the FMDX APIs. It parses this immense list of global transmitters and filters out anything further than 3000km from your GPS location.

In version 2.4, selecting the correct transmitter when multiple sites share a PI code is no longer based purely on shortest distance. The new calculatePropagationScore(ref, dbEntry) function calculates a weighted score based on physics:

function calculatePropagationScore(ref, dbEntry) {
    if (!ref) return 0;
    const distKm = ref.distKm || 9999;
    let distScore = 0;
    
    // Distance Tiers
    if (distKm <= 100) distScore = 100;
    else if (distKm <= 300) distScore = 80;
    else if (distKm <= 800) distScore = 40; 
    else if (distKm <= 2500) distScore = 20; 
    else distScore = 5;
    
    // Power (ERP) factor: Logarithmic scale up to 50 points
    const erp = ref.erp || 0.1;
    const pwrScore = Math.min(50, Math.log10(Math.max(1, erp * 10)) * 15);
    
    // Site Verification: Cross-referencing active AFs
    let siteBonus = 0;
    if (ref.txName) {
        let sharedSites = 0;
        for (const f of currentState.afSet) {
            const freqRefs = getFreqRefs(f);
            if (freqRefs.some(r => r.txName === ref.txName)) sharedSites++;
        }
        siteBonus = Math.min(30, sharedSites * 10);
    }
    
    // Sporadic-E characteristic check
    let spEBonus = 0;
    if (distKm > 800 && distKm < 2500) {
        const cleanCount = countCleanRawPositions();
        if (cleanCount >= 4) spEBonus = 20;
    }
    
    // Historical confirmation bonus
    let histBonus = 0;
    if (dbEntry && dbEntry.seenCount > 10) histBonus = 15;
    
    return distScore + pwrScore + siteBonus + spEBonus + histBonus;
}

6.3 · PS Lock Engine – Priority Ladder

The checkAndLockPS(pi) function is the core supervisor of the UI state. It runs every time a new RDS packet arrives. It evaluates the current live buffer against the memory and the reference.

It uses a strict priority ladder:

  1. If a fully verified string already exists in the local database (entry.psVerifiedRaw), use it.
  2. If the live buffer characters match an FMDX.org reference string at >75%, construct a hybrid string and lock it.
  3. If 8/8 characters are received error-free directly from the airwaves, run them against the strict FMDX sanity filter (to block CRC collisions). If they pass, lock and store as psVerifiedRaw.

6.4 · Provisional → Locked Status Engine

The client UI calculates the STATUS string based on boolean flags and confidences sent by the server:

6.5 · Voting Engine – Compact Aggregation Format

Votes are stored in rdsm_memory.json in a compact format to prevent file bloat. Instead of storing an array of 5,000 "N"s, it stores an aggregated weight object:

"ps": {
  "0": {
    "N": {
      "w": 45.2,
      "count": 120,
      "firstSeen": 1712849200,
      "lastSeen": 1712850000
    }
  }
}

The weight (w) decays mathematically based on the time elapsed since lastSeen, using the VOTE_HALFLIFE_DAYS constant (7 days).

6.6 · Confidence Calculation

Confidence is a floating point number between 0.0 and 1.0. It is calculated by blending multiple factors:

6.7 · PI Verification Pipeline

To prevent a random hardware bit-flip from generating a totally fake PI code and polluting the database, the server requires a PI code to be verified multiple times before it is considered "real".

The threshold for verification is dynamic. The getConfirmThreshold(pi) function lowers the requirement if there is strong evidence the PI is real. For example, if the PI exists in the FMDX database for the tuned frequency at a distance < 500km, the threshold drops to 1 (instant verification). If it's totally unknown, it requires multiple identical packets.

6.8 · Database Format and Storage Management

The local database rdsm_memory.json is saved automatically every 60 seconds (DB_SAVE_INTERVAL). On exit (SIGINT/SIGTERM), an emergency save is fired.

To prevent infinite growth, the memory is capped at MAX_STATIONS = 2000. When saving, the server purges:

6.9 · WebSocket Protocol and Delete Logic v2.4

The client and server communicate via a standard JSON WebSocket protocol. The server broadcasts payloads of type: 'rdsm_ai' continuously.

In version 2.4, a new client-to-server command was added for Admin deletion:

{
  "type": "rdsm_delete_pi",
  "pi": "D3C3"
}

When the server receives this, it deletes the D3C3 key from the db object and sets dbDirty = true. The client optimistically updates its own UI instantly.

6.10 · dataHandler Patch – Property Locking

To override the native datahandler.js, the server plugin intercepts the variables right before they are dispatched via the main Webserver WebSockets. It overwrites dataHandler.dataToSend.ps and dataHandler.dataToSend.pi with the mathematically verified AI strings. It also forces dataHandler.dataToSend.ps_errors = '0,0,0,0,0,0,0,0' to tell the native UI to stop flickering.

6.11 · FMDX.ORG Panel – Frequency Chips and AF Coverage

The client UI calculates the "AF Coverage %". It extracts the array of Alternate Frequencies from the active FMDX.org JSON reference block. It then compares this array against the live currentState.afSet (the AFs currently being decoded from the live radio signal). The overlap percentage is rendered in the UI.

The UI creates a horizontal scrolling list of "Frequency Chips". The chips turn bright blue if the frequency is actively being received, and stay grey if it is listed in the database but currently missing from the live signal.

6.12 · GPS WebSocket Listener

The server plugin connects to its *own* local websocket at `ws://127.0.0.1:8080/data_plugins` to listen for GPS payloads. If a connected TEF receiver has a GPS module attached, or if a user inputs manual coordinates into the web interface, the server catches the type: 'GPS' payload.

To prevent noisy GPS modules from triggering a 30MB FMDX index rebuild every 5 seconds, the coordinates are rounded to 2 decimal places (`roundGps`), and a rebuild is only triggered if the distance moved exceeds FMDX_REINDEX_MIN_DIST_KM (100km).

6.13 · Panel State Persistence v2.4

The frontend Javascript hooks into the `mouseup` event of the drag handler. It reads `el.style.left` and `el.style.top` and dumps a JSON object into `window.localStorage.setItem('rdsm_panel_pos')`. On page load, this is parsed and injected directly into the DOM CSS properties, guaranteeing the panel stays exactly where you left it.

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 spaces or errors like "R L ".

8 · Configuration Parameters

Advanced users can tweak the constants at the top of rds-ai-decoder_server.js to tune the AI behavior for specific DXing styles (e.g. meteor scatter vs tropo).

Parameter Default Description
VOTE_HALFLIFE_DAYS 7 Days before a vote loses 50% of its weight. Lower to adapt faster to stations changing their PS names.
VOTE_EXPIRE_DAYS 30 Days before an old vote is completely purged from memory.
STATION_EXPIRE_DAYS 90 Days before a station you haven't received is totally deleted from rdsm_memory.json.
FMDX_RADIUS_KM 3000 Radius in km for downloading transmitters. 3000km covers Sporadic-E within Europe. Increase to 5000 if expecting double-hop SpE.
PROVISIONAL_MIN_CONF 0.55 Confidence threshold (0.0 to 1.0) to enter the Amber PROVISIONAL state.
LOCK_MIN_CONF 0.90 Confidence threshold to turn Green and LOCK the PS name permanently.
LOCK_MIN_STABLE_MS 700 Time in milliseconds the provisional string must remain completely unchanged before locking is permitted.

9 · Troubleshooting & FAQ

The FMDX match says 0% even though the station is correct.

This happens when the FMDX database variant uses special characters, differing casing, or abbreviations that mathematically clash with the raw data. Ensure you have given the station enough time to decode cleanly. If the station recently rebranded, the FMDX database might simply be out of date. The AI will eventually lock onto the Raw RDS string instead of the FMDX variant.

The panel is stuck on "WAIT collecting..." forever.

The signal is too weak, or the TEF chip is dropping every packet with Error Level 2 or 3. The plugin aggressively filters out garbage data. If the hardware cannot deliver a single Level 1 packet, the AI has nothing to vote on.

I clicked the red ✕ to delete a PI, but it came back instantly!

If you are still tuned to the frequency and the hardware is still decoding that exact PI code from the noise floor, the plugin will instantly recreate the memory entry. Tune away to a blank frequency before purging ghost PI codes.

Why is "RDS Follow" greyed out?

You must be logged into the FM-DX-Webserver as an Administrator. The standard Webserver authentication applies. Because RDS Follow forcefully overrides the main display for everyone viewing the server remotely, guest access to this toggle is strictly prohibited.

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 30MB 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.