1
0
mirror of https://github.com/KubaPro010/fm-dx-webserver.git synced 2026-02-26 22:13:53 +01:00

TX search algorithm changes

Additional third algorithm option plus more comprehensive PS checking (thanks to @Highpoint2000 )
This commit is contained in:
Adam Wisher
2025-05-06 20:21:27 +01:00
parent 50aeb97282
commit 811ea4011d
2 changed files with 84 additions and 32 deletions

View File

@@ -18,7 +18,8 @@ let weightedErp = 10;
let weightedDist = 400;
const algorithms = [
[10, 400],
[30, 500]
[30, 500],
[5, 400]
];
const algoSetting = parseInt(serverConfig.webserver.txIdAlgorithm);
@@ -82,6 +83,41 @@ function getStateForCoordinates(lat, lon) {
return null;
}
/**
* Compares the standardized rdsPs string with the station's PS value.
* The rdsPs string is standardized by replacing spaces with underscores and converting to lowercase.
* The station's PS value is split into tokens (e.g., "__mdr___ _kultur_" -> ["__mdr___", "_kultur_"]).
* The function iterates through all tokens and checks if any token yields at least three valid (non "_" ) matches.
* Only positions where rdsPs is not an underscore are compared.
* If at least three valid matches are found for any token, the function returns true.
*/
function validPsCompare(rdsPs, stationPs) {
// 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());
// Iterate through all tokens and check if any token yields at least three valid (non "_" ) matches.
for (let token of psTokens) {
// If the token's length does not match the standardized rdsPs length, skip this token.
if (token.length !== standardizedRdsPs.length) continue;
let matchCount = 0;
for (let i = 0; i < standardizedRdsPs.length; i++) {
// Skip this position if the character in standardizedRdsPs is an underscore.
if (standardizedRdsPs[i] === '_') continue;
if (token[i] === standardizedRdsPs[i]) {
matchCount++;
}
}
if (matchCount >= 3) {
return true;
}
}
return false;
}
// Fetch data from maps
async function fetchTx(freq, piCode, rdsPs) {
const now = Date.now();
@@ -145,50 +181,42 @@ async function processData(data, piCode, rdsPs) {
currentPiCode = piCode;
currentRdsPs = rdsPs;
function evaluateStation(station, city, distance) {
let weightDistance = distance.distanceKm;
if (esMode && distance.distanceKm > 500) {
weightDistance = Math.abs(distance.distanceKm - 1500);
}
let erp = station.erp && station.erp > 0 ? station.erp : 1;
let extraWeight = erp >= weightedErp && distance.distanceKm <= weightedDist ? 0.3 : 0;
let score = 0;
// If ERP is 1W, use a simpler formula to avoid zero-scoring.
if (erp === 0.001) {
score = erp / distance.distanceKm;
} else {
score = ((10 * Math.log10(erp * 1000)) / weightDistance) + extraWeight;
}
if (score > maxScore) {
maxScore = score;
txAzimuth = distance.azimuth;
matchingStation = station;
matchingCity = city;
maxDistance = distance.distanceKm;
}
}
// First attempt: Try to match station using the piCode
// First, collect all stations that match the piCode (without PS comparison).
let stationsForPi = [];
for (const cityId in data.locations) {
const city = data.locations[cityId];
if (city.stations) {
for (const station of city.stations) {
if (station.pi === piCode.toUpperCase() && !station.extra && station.ps && station.ps.toLowerCase().includes(rdsPs.replace(/ /g, '_').replace(/^_*(.*?)_*$/, '$1').toLowerCase())) {
const distance = haversine(serverConfig.identification.lat, serverConfig.identification.lon, city.lat, city.lon);
evaluateStation(station, city, distance);
detectedByPireg = false;
if (station.pi === piCode.toUpperCase() && !station.extra) {
stationsForPi.push({ station, city });
}
}
}
}
// Fallback to pireg if no match is found
if (stationsForPi.length > 0) {
for (const { station, city } of stationsForPi) {
if (station.ps && validPsCompare(rdsPs, station.ps)) {
const distance = haversine(serverConfig.identification.lat, serverConfig.identification.lon, city.lat, city.lon);
evaluateStation(station, city, distance);
detectedByPireg = false;
}
}
}
// Fallback: Check using pireg if no match was found using the piCode (with valid PS comparison)
if (!matchingStation) {
for (const cityId in data.locations) {
const city = data.locations[cityId];
if (city.stations) {
for (const station of city.stations) {
if (station.pireg && station.pireg.toUpperCase() === piCode.toUpperCase() && !station.extra && station.ps && station.ps.toLowerCase().includes(rdsPs.replace(/ /g, '_').replace(/^_*(.*?)_*$/, '$1').toLowerCase())) {
if (
station.pireg &&
station.pireg.toUpperCase() === piCode.toUpperCase() &&
!station.extra &&
station.ps &&
validPsCompare(rdsPs, station.ps)
) {
const distance = haversine(serverConfig.identification.lat, serverConfig.identification.lon, city.lat, city.lon);
evaluateStation(station, city, distance);
detectedByPireg = true;
@@ -220,11 +248,34 @@ async function processData(data, piCode, rdsPs) {
id: matchingStation.id,
pi: matchingStation.pi,
foundStation: true,
reg: detectedByPireg
reg: detectedByPireg,
};
} else {
return;
}
function evaluateStation(station, city, distance) {
let weightDistance = distance.distanceKm;
if (esMode && distance.distanceKm > 500) {
weightDistance = Math.abs(distance.distanceKm - 1500);
}
let erp = station.erp && station.erp > 0 ? station.erp : 1;
let extraWeight = erp >= weightedErp && distance.distanceKm <= weightedDist ? 0.3 : 0;
let score = 0;
// If ERP is 1W, use a simpler formula to avoid zero-scoring.
if (erp === 0.001) {
score = erp / distance.distanceKm;
} else {
score = ((10 * Math.log10(erp * 1000)) / weightDistance) + extraWeight;
}
if (score > maxScore) {
maxScore = score;
txAzimuth = distance.azimuth;
matchingStation = station;
matchingCity = city;
maxDistance = distance.distanceKm;
}
}
}
function checkEs() {