diff --git a/web/js/main.js b/web/js/main.js
index 532bce2..0645d06 100644
--- a/web/js/main.js
+++ b/web/js/main.js
@@ -918,13 +918,11 @@ function throttle(fn, wait) {
}
function buildAltTxList(txList) {
- const wrapper = '
';
let outString = '';
- outString += wrapper;
for (let i = 0; i < txList.length; i++) {
const tx = txList[i];
outString += `
-
+
${tx.station.replace("R.", "Radio ").replace(/%/g, '%25')}
@@ -935,11 +933,7 @@ function buildAltTxList(txList) {
`;
- if (i % 2 !== 0) {
- outString += `
${wrapper}`;
- }
}
- outString += '
';
return outString;
}
@@ -955,6 +949,12 @@ function updateHtmlIfChanged($element, newHtml) {
}
}
+function updateDatasetValIfChanged($element, dataLabel, newVal) {
+ if ($element.attr(dataLabel) !== newVal) {
+ $element.attr(dataLabel, newVal);
+ }
+}
+
// Main function to update data elements, optimized
const updateDataElements = throttle(function(parsedData) {
updateTextIfChanged($dataFrequency, parsedData.freq);
@@ -1015,6 +1015,7 @@ const updateDataElements = throttle(function(parsedData) {
updateTextIfChanged($('#data-station-pol'), parsedData.txInfo.pol);
updateHtmlIfChanged($('#data-station-azimuth'), parsedData.txInfo.azi + '°');
updateHtmlIfChanged($('#data-station-others'), parsedData.txInfo.otherMatches.length > 0 ? ('
+' + parsedData.txInfo.otherMatches.length +'') : '');
+ updateDatasetValIfChanged($('#data-station-container'), "data-score", parsedData.txInfo.score);
const txDistance = localStorage.getItem('imperialUnits') == "true" ? (Number(parsedData.txInfo.dist) * 0.621371192).toFixed(0) + " mi" : parsedData.txInfo.dist + " km";
const altTxInfo = buildAltTxList(parsedData.txInfo.otherMatches);
updateHtmlIfChanged($('#alternative-txes'), altTxInfo);
From dae705ccbc10d3015cc3b67054c5eee032b4ead7 Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Sat, 31 May 2025 14:38:07 +0100
Subject: [PATCH 2/9] Limit multiple matches
Only return a max of 10 extra TX matches and filter any with a score less than 1/10 of the winner
---
server/tx_search.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index 33c8eef..e17c850 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -191,9 +191,13 @@ async function fetchTx(freq, piCode, rdsPs) {
for (loc of filteredLocations) {
loc.score = evaluateStation(loc);
}
+ // Sort by score in descending order
filteredLocations.sort((a, b) => b.score - a.score);
match = filteredLocations[0];
- multiMatches = filteredLocations.slice(1);
+ // Have a maximum of 10 extra matches and remove any with less than 1/10 of the winning score
+ multiMatches = filteredLocations
+ .slice(1, 11)
+ .filter(obj => obj.score >= (match.score/10));
} else if (filteredLocations.length === 1) {
match = filteredLocations[0];
match.score = 1;
From bf011acfb41fb7253f4ae4628b4f88d98e2f661c Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Mon, 9 Jun 2025 12:30:47 +0100
Subject: [PATCH 3/9] TX Search improvements
Fix local signal IDed as distant sporadic E bug and allow PI-only match when possible
---
server/tx_search.js | 50 +++++++++++++++++++++++++++++----------------
1 file changed, 32 insertions(+), 18 deletions(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index e17c850..f00a04a 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -5,7 +5,7 @@ const consoleCmd = require('./console');
let localDb = {};
let lastFetchTime = 0;
const fetchInterval = 1000;
-const esSwitchCache = {"lastCheck":0, "esSwitch":false};
+const esSwitchCache = {"lastCheck": null, "esSwitch": false};
const esFetchInterval = 300000;
var currentPiCode = '';
var currentRdsPs = '';
@@ -135,7 +135,7 @@ function evaluateStation(station) {
let esMode = checkEs();
let weightDistance = station.distanceKm;
if (esMode && station.distanceKm > 500) {
- weightDistance = Math.abs(station.distanceKm - 1500);
+ weightDistance = Math.abs(station.distanceKm - 1500) + 200;
}
let erp = station.erp && station.erp > 0 ? station.erp : 1;
let extraWeight = erp > 30 && station.distanceKm <= 500 ? 0.3 : 0;
@@ -161,25 +161,34 @@ async function fetchTx(freq, piCode, rdsPs) {
|| serverConfig.identification.lat.length < 2
|| freq < 87
|| Object.keys(localDb).length === 0
- || (currentPiCode == piCode && currentRdsPs == rdsPs)) {
+ || (currentPiCode === piCode && currentRdsPs === rdsPs)) {
return Promise.resolve();
}
lastFetchTime = now;
+ currentPiCode = piCode;
+ currentRdsPs = rdsPs;
if (serverConfig.webserver.rdsMode === true) await loadUsStatesGeoJson();
- const filteredLocations = Object.values(localDb.locations)
- .map(locData => ({
- ...locData,
- stations: locData.stations.filter(station =>
- station.freq === freq &&
- (station.pi === piCode.toUpperCase() || station.pireg === piCode.toUpperCase() ) &&
- validPsCompare(rdsPs, station.ps)
- )
- }))
- .filter(locData => locData.stations.length > 0); // Ensure locations with at least one matching station remain
+ let filteredLocations = Object.values(localDb.locations || {})
+ .map(locData => ({
+ ...locData,
+ stations: locData.stations.filter(station =>
+ station.freq === freq &&
+ (station.pi === piCode.toUpperCase() || station.pireg === piCode.toUpperCase() )
+ )
+ }))
+ .filter(locData => locData.stations.length > 0); // Ensure locations with at least one matching station remain
+
+ // Only check PS if we have more than one match.
+ if (filteredLocations.length > 1) {
+ filteredLocations = filteredLocations.map(locData => ({
+ ...locData,
+ stations: locData.stations.filter(station => validPsCompare(rdsPs, station.ps))
+ })).filter(locData => locData.stations.length > 0);
+ }
- for (loc of filteredLocations) {
+ for (let loc of filteredLocations) {
loc = Object.assign(loc, loc.stations[0]);
delete loc.stations;
const dist = haversine(serverConfig.identification.lat, serverConfig.identification.lon, loc.lat, loc.lon);
@@ -188,7 +197,7 @@ async function fetchTx(freq, piCode, rdsPs) {
}
if (filteredLocations.length > 1) {
- for (loc of filteredLocations) {
+ for (let loc of filteredLocations) {
loc.score = evaluateStation(loc);
}
// Sort by score in descending order
@@ -210,7 +219,7 @@ async function fetchTx(freq, piCode, rdsPs) {
match.state = state; // Add state to matchingCity
}
}
- return {
+ const result = {
station: match.detectedByPireg
? `${match.station.replace("R.", "Radio ")}${match.regname ? ' ' + match.regname : ''}`
: match.station.replace("R.", "Radio "),
@@ -225,9 +234,14 @@ async function fetchTx(freq, piCode, rdsPs) {
foundStation: true,
reg: match.detectedByPireg,
score: match.score,
- others: multiMatches,
+ others: multiMatches.slice(),
};
+ filteredLocations.length = 0;
+ multiMatches.length = 0;
+ return result;
} else {
+ filteredLocations.length = 0;
+ multiMatches.length = 0;
return Promise.resolve();
}
}
@@ -236,7 +250,7 @@ function checkEs() {
const now = Date.now();
const url = "https://fmdx.org/includes/tools/get_muf.php";
- if (now - esSwitchCache.lastCheck < esFetchInterval) {
+ if (esSwitchCache.lastCheck && now - esSwitchCache.lastCheck < esFetchInterval) {
return esSwitchCache.esSwitch;
}
From fb68f26d829aaf98304c29532d1ce77d2c3923f5 Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Thu, 12 Jun 2025 09:18:59 +0100
Subject: [PATCH 4/9] Local db optimisation and GPS support
DB indexing to improve tx search performance with PI and reg PI, re-download db on failure, add websocket for GPS location update support
Co-Authored-By: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com>
Co-Authored-By: Highpoint2000 <168109804+Highpoint2000@users.noreply.github.com>
---
server/server_config.js | 1 +
server/tx_search.js | 129 +++++++++++++++++++++++++++++++++-------
2 files changed, 107 insertions(+), 23 deletions(-)
diff --git a/server/server_config.js b/server/server_config.js
index fcf4290..f03419f 100644
--- a/server/server_config.js
+++ b/server/server_config.js
@@ -58,6 +58,7 @@ let serverConfig = {
lat: "",
lon: "",
broadcastTuner: false,
+ gpsMode: false,
proxyIp: "",
contact: null,
},
diff --git a/server/tx_search.js b/server/tx_search.js
index f00a04a..2958ba5 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -3,14 +3,24 @@ const { serverConfig } = require('./server_config');
const consoleCmd = require('./console');
let localDb = {};
+let lastDownloadTime = 0; // Last DB download attempt time.
let lastFetchTime = 0;
+let piFreqIndex = {}; // Indexing for speedier PI+Freq combinations
const fetchInterval = 1000;
+const downloadInterval = 300000;
const esSwitchCache = {"lastCheck": null, "esSwitch": false};
const esFetchInterval = 300000;
var currentPiCode = '';
var currentRdsPs = '';
const usStatesGeoJsonUrl = "https://raw.githubusercontent.com/PublicaMundi/MappingAPI/master/data/geojson/us-states.json";
let usStatesGeoJson = null; // To cache the GeoJSON data for US states
+let Latitude = serverConfig.identification.lat;
+let Longitude = serverConfig.identification.lon;
+
+// Create WebSocket URL for GPS lat/lon update.
+const webserverPort = serverConfig.webserver.webserverPort || 8080; // Fallback to port 8080
+const externalWsUrl = `ws://127.0.0.1:${webserverPort}/data_plugins`;
+const WebSocket = require('ws');
// Get weighting values based on algorithm setting.
// Defaults = algorithm 1
@@ -30,16 +40,76 @@ if (typeof algorithms[algoSetting] !== 'undefined') {
// IIFE to build the local TX DB cache from the endpoint.
(async () => {
+ const now = Date.now();
+ lastDownloadTime = now;
+ await buildTxDatabase();
+})();
+
+if (serverConfig.identification.gpsMode) {
+ // 5-second delay before activation of GPS lat/lon websocket
+ setTimeout(() => {
+ const websocket = new WebSocket(externalWsUrl);
+ consoleCmd.logInfo('Set up GPS websocket for lat/lon');
+ // Event listener to receive data
+ websocket.on('message', (data) => {
+ try {
+ // Parse the received data
+ const parsedData = JSON.parse(data);
+
+ // Check if the dataset is of type GPS
+ if (parsedData.type === "GPS" && parsedData.value) {
+ const gpsData = parsedData.value;
+ const { status, time, lat, lon, alt, mode } = gpsData;
+
+ if (status === "active") {
+ Latitude = parseFloat(lat);
+ Longitude = parseFloat(lon);
+ }
+ }
+ } catch (error) {
+ consoleCmd.logError("Error processing WebSocket data:", error);
+ }
+ });
+
+ }, 5000);
+}
+
+// Function to build local TX database from FMDX Maps endpoint.
+async function buildTxDatabase() {
try {
consoleCmd.logInfo('Fetching transmitter database...');
const response = await fetch(`https://maps.fmdx.org/api?qth=${serverConfig.identification.lat},${serverConfig.identification.lon}`);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
localDb = await response.json();
+ buildPiFreqIndex();
consoleCmd.logInfo('Transmitter database successfully loaded.');
} catch (error) {
consoleCmd.logError("Failed to fetch transmitter database:", error);
}
-})();
+}
+
+// Function to build index map of PI+Freq combinations
+function buildPiFreqIndex() {
+ piFreqIndex = {}; // reset
+ for (const locData of Object.values(localDb.locations || {})) {
+ for (const station of locData.stations || []) {
+ if (!station.freq) continue;
+ const freq = station.freq;
+ const pi = station.pi?.toUpperCase();
+ const pireg = station.pireg?.toUpperCase();
+ if (pi) {
+ const key = `${freq}|${pi}`;
+ if (!piFreqIndex[key]) piFreqIndex[key] = [];
+ piFreqIndex[key].push({ ...locData, station });
+ }
+ if (pireg) {
+ const regKey = `${freq}|${pireg}`;
+ if (!piFreqIndex[regKey]) piFreqIndex[regKey] = [];
+ piFreqIndex[regKey].push({ ...locData, station });
+ }
+ }
+ }
+}
// Load the US states GeoJSON data
async function loadUsStatesGeoJson() {
@@ -105,11 +175,16 @@ function getStateForCoordinates(lat, lon) {
* If at least three valid matches are found for any token, the function returns true.
*/
function validPsCompare(rdsPs, stationPs) {
+ if (typeof stationPs !== 'string' || typeof rdsPs !== 'string') {
+ consoleCmd.logError(`Invalid TX values. stationPs: ${stationPs}, rdsPs: ${rdsPs}`);
+ return false;
+ }
+
// Standardize the rdsPs string: replace spaces with underscores and convert to lowercase.
const standardizedRdsPs = rdsPs.replace(/ /g, '_').toLowerCase();
// Split stationPs into tokens (e.g., "__mdr___ _kultur_" -> ["__mdr___", "_kultur_"])
- const psTokens = stationPs.split(/\s+/).filter(token => token.length > 0).map(token => token.toLowerCase());
+ const psTokens = stationPs.split(/\s+/).filter(token => token.length > 0).map(token => { const lower = token.toLowerCase(); return lower.length < 8 ? lower.padEnd(8, '_') : lower; });
// Iterate through all tokens and check if any token yields at least three valid (non "_" ) matches.
for (let token of psTokens) {
@@ -156,29 +231,37 @@ async function fetchTx(freq, piCode, rdsPs) {
const now = Date.now();
freq = parseFloat(freq);
- if (isNaN(freq)) return;
- if (now - lastFetchTime < fetchInterval
- || serverConfig.identification.lat.length < 2
- || freq < 87
- || Object.keys(localDb).length === 0
- || (currentPiCode === piCode && currentRdsPs === rdsPs)) {
- return Promise.resolve();
+ // If we don't have a local database and the interval has passed, re-try download.
+ if (
+ Object.keys(localDb).length === 0 &&
+ now - lastDownloadTime > downloadInterval
+ ) {
+ lastDownloadTime = now;
+ await buildTxDatabase();
}
+ if (
+ isNaN(freq) ||
+ now - lastFetchTime < fetchInterval ||
+ Latitude.length < 2 ||
+ freq < 87 ||
+ Object.keys(localDb).length === 0 ||
+ (currentPiCode === piCode && currentRdsPs === rdsPs)
+ ) return Promise.resolve();
+
lastFetchTime = now;
currentPiCode = piCode;
currentRdsPs = rdsPs;
if (serverConfig.webserver.rdsMode === true) await loadUsStatesGeoJson();
- let filteredLocations = Object.values(localDb.locations || {})
- .map(locData => ({
- ...locData,
- stations: locData.stations.filter(station =>
- station.freq === freq &&
- (station.pi === piCode.toUpperCase() || station.pireg === piCode.toUpperCase() )
- )
- }))
- .filter(locData => locData.stations.length > 0); // Ensure locations with at least one matching station remain
+ const key = `${freq}|${piCode.toUpperCase()}`;
+ let rawMatches = piFreqIndex[key] || [];
+
+ // Format the results into the same structure as before
+ let filteredLocations = rawMatches.map(({ station, ...locData }) => ({
+ ...locData,
+ stations: [station]
+ }));
// Only check PS if we have more than one match.
if (filteredLocations.length > 1) {
@@ -191,7 +274,7 @@ async function fetchTx(freq, piCode, rdsPs) {
for (let loc of filteredLocations) {
loc = Object.assign(loc, loc.stations[0]);
delete loc.stations;
- const dist = haversine(serverConfig.identification.lat, serverConfig.identification.lon, loc.lat, loc.lon);
+ const dist = haversine(Latitude, Longitude, loc.lat, loc.lon);
loc = Object.assign(loc, dist);
loc.detectedByPireg = (loc.pireg === piCode.toUpperCase());
}
@@ -206,7 +289,7 @@ async function fetchTx(freq, piCode, rdsPs) {
// Have a maximum of 10 extra matches and remove any with less than 1/10 of the winning score
multiMatches = filteredLocations
.slice(1, 11)
- .filter(obj => obj.score >= (match.score/10));
+ .filter(obj => obj.score >= (match.score / 10));
} else if (filteredLocations.length === 1) {
match = filteredLocations[0];
match.score = 1;
@@ -254,7 +337,7 @@ function checkEs() {
return esSwitchCache.esSwitch;
}
- if (serverConfig.identification.lat > 20) {
+ if (Latitude > 20) {
esSwitchCache.lastCheck = now;
fetch(url)
.then(response => {
@@ -262,8 +345,8 @@ function checkEs() {
return response.json();
})
.then(data => {
- if ((serverConfig.identification.lon < -32 && data.north_america.max_frequency !== "No data") ||
- (serverConfig.identification.lon >= -32 && data.europe.max_frequency !== "No data")) {
+ if ((Longitude < -32 && data.north_america.max_frequency !== "No data") ||
+ (Longitude >= -32 && data.europe.max_frequency !== "No data")) {
esSwitchCache.esSwitch = true;
}
})
From 72d48b0c2e1458350f6aef16f7cf5d7471e3d071 Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Thu, 12 Jun 2025 13:58:25 +0100
Subject: [PATCH 5/9] Fix algorithm choice and change Es mode distance
Fix hard-coded algorithm weighting values back to config values and increase distance for Es weighting to be applied
---
server/tx_search.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index 2958ba5..b79c766 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -209,11 +209,11 @@ function validPsCompare(rdsPs, stationPs) {
function evaluateStation(station) {
let esMode = checkEs();
let weightDistance = station.distanceKm;
- if (esMode && station.distanceKm > 500) {
+ if (esMode && station.distanceKm > 700) {
weightDistance = Math.abs(station.distanceKm - 1500) + 200;
}
let erp = station.erp && station.erp > 0 ? station.erp : 1;
- let extraWeight = erp > 30 && station.distanceKm <= 500 ? 0.3 : 0;
+ let extraWeight = erp > weightedErp && station.distanceKm <= weightDistance ? 0.3 : 0;
let score = 0;
// If ERP is 1W, use a simpler formula to avoid zero-scoring.
if (erp === 0.001) {
From eb8dbf8fd530c26a20a9cd1c65d927a1a70c77a7 Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Fri, 27 Jun 2025 14:08:47 +0100
Subject: [PATCH 6/9] Adjust spE weighting behaviour
Don't weight options for spE if a powerful station is found under the threshold
---
server/tx_search.js | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index b79c766..cdb029c 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -206,8 +206,7 @@ function validPsCompare(rdsPs, stationPs) {
return false;
}
-function evaluateStation(station) {
- let esMode = checkEs();
+function evaluateStation(station, esMode) {
let weightDistance = station.distanceKm;
if (esMode && station.distanceKm > 700) {
weightDistance = Math.abs(station.distanceKm - 1500) + 200;
@@ -280,8 +279,16 @@ async function fetchTx(freq, piCode, rdsPs) {
}
if (filteredLocations.length > 1) {
+ // Check for any 10kW+ stations within 700km, and don't Es weight if any found.
+ const tropoPriority = filteredLocations.some(
+ loc => loc.distanceKm < 700 && loc.erp >= 10
+ );
+ let esMode = false;
+ if (!tropoPriority) {
+ esMode = checkEs();
+ }
for (let loc of filteredLocations) {
- loc.score = evaluateStation(loc);
+ loc.score = evaluateStation(loc, esMode);
}
// Sort by score in descending order
filteredLocations.sort((a, b) => b.score - a.score);
From 3cedab524892b2085e5021f4df467e6de70ade4d Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Sat, 5 Jul 2025 16:23:40 +0100
Subject: [PATCH 7/9] Improve TX DB fetch retry process
Improve process for retrying when TX DB download fails
---
server/tx_search.js | 54 ++++++++++++++++++++++-----------------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index cdb029c..2fc0147 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -3,11 +3,9 @@ const { serverConfig } = require('./server_config');
const consoleCmd = require('./console');
let localDb = {};
-let lastDownloadTime = 0; // Last DB download attempt time.
let lastFetchTime = 0;
let piFreqIndex = {}; // Indexing for speedier PI+Freq combinations
const fetchInterval = 1000;
-const downloadInterval = 300000;
const esSwitchCache = {"lastCheck": null, "esSwitch": false};
const esFetchInterval = 300000;
var currentPiCode = '';
@@ -38,12 +36,8 @@ if (typeof algorithms[algoSetting] !== 'undefined') {
weightedDist = algorithms[algoSetting][1];
}
-// IIFE to build the local TX DB cache from the endpoint.
-(async () => {
- const now = Date.now();
- lastDownloadTime = now;
- await buildTxDatabase();
-})();
+// Build the TX database.
+setTimeout(buildTxDatabase, 3000);
if (serverConfig.identification.gpsMode) {
// 5-second delay before activation of GPS lat/lon websocket
@@ -76,15 +70,30 @@ if (serverConfig.identification.gpsMode) {
// Function to build local TX database from FMDX Maps endpoint.
async function buildTxDatabase() {
- try {
- consoleCmd.logInfo('Fetching transmitter database...');
- const response = await fetch(`https://maps.fmdx.org/api?qth=${serverConfig.identification.lat},${serverConfig.identification.lon}`);
- if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
- localDb = await response.json();
- buildPiFreqIndex();
- consoleCmd.logInfo('Transmitter database successfully loaded.');
- } catch (error) {
- consoleCmd.logError("Failed to fetch transmitter database:", error);
+ if (Latitude.length > 0 && Longitude.length > 0) {
+ let awaitingTxInfo = true;
+ while (awaitingTxInfo) {
+ try {
+ consoleCmd.logInfo('Fetching transmitter database...');
+ const response = await fetch(`https://maps.fmdx.org/api?qth=${serverConfig.identification.lat},${serverConfig.identification.lon}`, {
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json'
+ }
+ });
+ if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
+ localDb = await response.json();
+ buildPiFreqIndex();
+ consoleCmd.logInfo('Transmitter database successfully loaded.');
+ awaitingTxInfo = false;
+ } catch (error) {
+ consoleCmd.logError("Failed to fetch transmitter database:", error);
+ await new Promise(res => setTimeout(res, 30000));
+ consoleCmd.logInfo('Retrying transmitter database download...');
+ }
+ }
+ } else {
+ consoleCmd.logInfo('Server latitude and longitude must be set before transmitter database can be built');
}
}
@@ -230,21 +239,12 @@ async function fetchTx(freq, piCode, rdsPs) {
const now = Date.now();
freq = parseFloat(freq);
- // If we don't have a local database and the interval has passed, re-try download.
- if (
- Object.keys(localDb).length === 0 &&
- now - lastDownloadTime > downloadInterval
- ) {
- lastDownloadTime = now;
- await buildTxDatabase();
- }
-
if (
isNaN(freq) ||
now - lastFetchTime < fetchInterval ||
Latitude.length < 2 ||
freq < 87 ||
- Object.keys(localDb).length === 0 ||
+ Object.keys(piFreqIndex).length === 0 ||
(currentPiCode === piCode && currentRdsPs === rdsPs)
) return Promise.resolve();
From cd9bbaee87db04ec64f5e49e2386dae98300586c Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Sat, 5 Jul 2025 17:03:27 +0100
Subject: [PATCH 8/9] Bugfix to avoid CPU spike of checking big object length
Don't check the length of the generated indexed database as it's big and causes a lag
---
server/tx_search.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index 2fc0147..fce7019 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -244,7 +244,7 @@ async function fetchTx(freq, piCode, rdsPs) {
now - lastFetchTime < fetchInterval ||
Latitude.length < 2 ||
freq < 87 ||
- Object.keys(piFreqIndex).length === 0 ||
+ Object.keys(localDb).length === 0 ||
(currentPiCode === piCode && currentRdsPs === rdsPs)
) return Promise.resolve();
From 9b3593bc0213162fa9204d65f6bd6275337424b2 Mon Sep 17 00:00:00 2001
From: Adam Wisher <37659188+mrwish7@users.noreply.github.com>
Date: Tue, 22 Jul 2025 17:51:07 +0100
Subject: [PATCH 9/9] Valid PS compare fix for PS under 3 chars
Fix to allow stations with a PS less than 3 characters to be IDed when PS validity checked
---
server/tx_search.js | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/server/tx_search.js b/server/tx_search.js
index fce7019..d4c9b59 100644
--- a/server/tx_search.js
+++ b/server/tx_search.js
@@ -197,6 +197,9 @@ function validPsCompare(rdsPs, stationPs) {
// Iterate through all tokens and check if any token yields at least three valid (non "_" ) matches.
for (let token of psTokens) {
+ // If total non "_" length of token is less than 3, allow match based on that length instead
+ const tokenLength = token.replace(/_/g, "").length;
+ const minMatchLen = tokenLength > 2 ? 3 : tokenLength;
// If the token's length does not match the standardized rdsPs length, skip this token.
if (token.length !== standardizedRdsPs.length) continue;
@@ -208,7 +211,7 @@ function validPsCompare(rdsPs, stationPs) {
matchCount++;
}
}
- if (matchCount >= 3) {
+ if (matchCount >= minMatchLen) {
return true;
}
}
@@ -393,4 +396,4 @@ function deg2rad(deg) {
module.exports = {
fetchTx
-};
\ No newline at end of file
+};