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

Merge pull request #154 from kkonradpl/main

RDS bugfixes & improvements
This commit is contained in:
Marek Farkaš
2025-05-30 19:18:02 +02:00
committed by GitHub
6 changed files with 103 additions and 93 deletions

View File

@@ -264,7 +264,27 @@ const resetToDefault = dataToSend => Object.assign(dataToSend, initialData);
const ServerStartTime = process.hrtime(); const ServerStartTime = process.hrtime();
var serialportUpdateTime = process.hrtime(); var serialportUpdateTime = process.hrtime();
let checkSerialport = false; let checkSerialport = false;
let rdsTimeoutTimer = null;
function rdsReceived() {
if (rdsTimeoutTimer) {
clearTimeout(rdsTimeoutTimer);
rdsTimeoutTimer = null;
}
if (serverConfig.webserver.rdsTimeout && serverConfig.webserver.rdsTimeout != 0) {
rdsTimeoutTimer = setInterval(rdsReset, serverConfig.webserver.rdsTimeout * 1000);
}
}
function rdsReset() {
resetToDefault(dataToSend);
dataToSend.af.length = 0;
rdsparser.clear(rds);
if (rdsTimeoutTimer) {
clearTimeout(rdsTimeoutTimer);
rdsTimeoutTimer = null;
}
}
function handleData(wss, receivedData, rdsWss) { function handleData(wss, receivedData, rdsWss) {
// Retrieve the last update time for this client // Retrieve the last update time for this client
@@ -280,6 +300,7 @@ function handleData(wss, receivedData, rdsWss) {
dataToSend.bw = receivedLine.substring(1); dataToSend.bw = receivedLine.substring(1);
break; break;
case receivedLine.startsWith('P'): // PI Code case receivedLine.startsWith('P'): // PI Code
rdsReceived();
modifiedData = receivedLine.slice(1); modifiedData = receivedLine.slice(1);
legacyRdsPiBuffer = modifiedData; legacyRdsPiBuffer = modifiedData;
if (dataToSend.pi.length >= modifiedData.length || dataToSend.pi == '?') { if (dataToSend.pi.length >= modifiedData.length || dataToSend.pi == '?') {
@@ -289,16 +310,11 @@ function handleData(wss, receivedData, rdsWss) {
case receivedLine.startsWith('T'): // Frequency case receivedLine.startsWith('T'): // Frequency
modifiedData = receivedLine.substring(1).split(",")[0]; modifiedData = receivedLine.substring(1).split(",")[0];
rdsReset();
if((modifiedData / 1000).toFixed(3) == dataToSend.freq) { if((modifiedData / 1000).toFixed(3) == dataToSend.freq) {
resetToDefault(dataToSend);
rdsparser.clear(rds);
dataToSend.af = [];
return; // Prevent tune spamming using scrollwheel return; // Prevent tune spamming using scrollwheel
} }
resetToDefault(dataToSend);
dataToSend.af.length = 0;
rdsparser.clear(rds);
parsedValue = parseFloat(modifiedData); parsedValue = parseFloat(modifiedData);
if (!isNaN(parsedValue)) { if (!isNaN(parsedValue)) {
@@ -315,6 +331,7 @@ function handleData(wss, receivedData, rdsWss) {
case receivedLine.startsWith('Z'): // Antenna case receivedLine.startsWith('Z'): // Antenna
dataToSend.ant = receivedLine.substring(1); dataToSend.ant = receivedLine.substring(1);
initialData.ant = receivedLine.substring(1); initialData.ant = receivedLine.substring(1);
rdsReset();
break; break;
case receivedLine.startsWith('G'): // EQ / iMS (RF+/IF+) case receivedLine.startsWith('G'): // EQ / iMS (RF+/IF+)
const mapping = filterMappings[receivedLine]; const mapping = filterMappings[receivedLine];
@@ -342,6 +359,7 @@ function handleData(wss, receivedData, rdsWss) {
processSignal(receivedLine, false, true); processSignal(receivedLine, false, true);
break; break;
case receivedLine.startsWith('R'): // RDS HEX case receivedLine.startsWith('R'): // RDS HEX
rdsReceived();
modifiedData = receivedLine.slice(1); modifiedData = receivedLine.slice(1);
dataToSend.rds = true; dataToSend.rds = true;
@@ -357,7 +375,7 @@ function handleData(wss, receivedData, rdsWss) {
// error correction, but this is a good substitute. // error correction, but this is a good substitute.
errorsNew = (legacyRdsPiBuffer.length - 4) << 6; errorsNew = (legacyRdsPiBuffer.length - 4) << 6;
} else { } else {
pi = '----'; pi = '0000';
errorsNew = (0x03 << 6); errorsNew = (0x03 << 6);
} }
@@ -371,28 +389,14 @@ function handleData(wss, receivedData, rdsWss) {
} }
rdsWss.clients.forEach((client) => { rdsWss.clients.forEach((client) => {
let dataString = modifiedData.toString(); const errors = parseInt(modifiedData.slice(-2), 16);
let lastTwoChars = dataString.slice(-2); let data = (((errors & 0xC0) == 0) ? modifiedData.slice(0, 4) : '----');
let lastByteValue = parseInt(lastTwoChars, 16); data += (((errors & 0x30) == 0) ? modifiedData.slice(4, 8) : '----');
data += (((errors & 0x0C) == 0) ? modifiedData.slice(8, 12) : '----');
let truncatedString = dataString.slice(0, -2); data += (((errors & 0x03) == 0) ? modifiedData.slice(12, 16) : '----');
if ((lastByteValue & 0x03) !== 0) {
truncatedString = truncatedString.slice(0, 4) + '----' + truncatedString.slice(8);
}
if ((lastByteValue & 0x30) !== 0) {
truncatedString = truncatedString.slice(0, 8) + '----' + truncatedString.slice(12);
}
if ((lastByteValue & 0x0C) !== 0) {
truncatedString = truncatedString.slice(0, 12) + '----';
}
let newDataString = "G:\r\n" + truncatedString + "\r\n\r\n";
let finalBuffer = Buffer.from(newDataString, 'utf-8');
const newDataString = "G:\r\n" + data + "\r\n\r\n";
const finalBuffer = Buffer.from(newDataString, 'utf-8');
client.send(finalBuffer); client.send(finalBuffer);
}); });

View File

@@ -344,6 +344,7 @@ router.get('/static_data', (req, res) => {
defaultTheme: serverConfig.webserver.defaultTheme || 'theme1', defaultTheme: serverConfig.webserver.defaultTheme || 'theme1',
bgImage: serverConfig.webserver.bgImage || '', bgImage: serverConfig.webserver.bgImage || '',
rdsMode: serverConfig.webserver.rdsMode || false, rdsMode: serverConfig.webserver.rdsMode || false,
rdsTimeout: serverConfig.webserver.rdsTimeout || 0,
tunerName: serverConfig.identification.tunerName || '', tunerName: serverConfig.identification.tunerName || '',
tunerDesc: serverConfig.identification.tunerDesc || '', tunerDesc: serverConfig.identification.tunerDesc || '',
ant: serverConfig.antennas || {} ant: serverConfig.antennas || {}

View File

@@ -214,7 +214,10 @@ if (serverConfig.xdrd.wirelessConnection === false) {
return serialport; return serialport;
} }
} }
// xdrd connection // xdrd connection
let authFlags = {};
function connectToXdrd() { function connectToXdrd() {
const { xdrd } = serverConfig; const { xdrd } = serverConfig;
@@ -222,16 +225,26 @@ function connectToXdrd() {
client.connect(xdrd.xdrdPort, xdrd.xdrdIp, () => { client.connect(xdrd.xdrdPort, xdrd.xdrdIp, () => {
logInfo('Connection to xdrd established successfully.'); logInfo('Connection to xdrd established successfully.');
let authFlags = { authFlags = {
authMsg: false, authMsg: false,
firstClient: false, firstClient: false,
receivedSalt: '', receivedSalt: '',
receivedPassword: false, receivedPassword: false,
messageCount: 0, messageCount: 0,
}; };
});
}
}
const authDataHandler = (data) => { client.on('data', (data) => {
authFlags.messageCount++ const { xdrd } = serverConfig;
helpers.resolveDataBuffer(data, wss, rdsWss);
if (authFlags.authMsg == true && authFlags.messageCount > 1) {
return;
}
authFlags.messageCount++;
const receivedData = data.toString(); const receivedData = data.toString();
const lines = receivedData.split('\n'); const lines = receivedData.split('\n');
@@ -272,25 +285,11 @@ function connectToXdrd() {
client.write('A0\n'); client.write('A0\n');
client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n'); client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n');
serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n'); serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n');
client.off('data', authDataHandler);
return; return;
} }
} }
} }
};
client.on('data', (data) => {
helpers.resolveDataBuffer(data, wss, rdsWss);
if (authFlags.authMsg == true && authFlags.messageCount > 1) {
// If the limit is reached, remove the 'data' event listener
client.off('data', authDataHandler);
return;
}
authDataHandler(data);
}); });
});
}
}
client.on('close', () => { client.on('close', () => {
if(serverConfig.autoShutdown === false) { if(serverConfig.autoShutdown === false) {

View File

@@ -31,6 +31,7 @@ let serverConfig = {
defaultTheme: "theme1", defaultTheme: "theme1",
bgImage: "", bgImage: "",
rdsMode: false, rdsMode: false,
rdsTimeout: 0,
txIdAlgorithm: 0 txIdAlgorithm: 0
}, },
xdrd: { xdrd: {

View File

@@ -14,7 +14,7 @@ function getInitialSettings() {
dataType: 'json', dataType: 'json',
success: function (data) { success: function (data) {
['qthLatitude', 'qthLongitude', 'defaultTheme', 'bgImage', 'rdsMode'].forEach(key => { ['qthLatitude', 'qthLongitude', 'defaultTheme', 'bgImage', 'rdsMode', 'rdsTimeout'].forEach(key => {
if (data[key] !== undefined) { if (data[key] !== undefined) {
localStorage.setItem(key, data[key]); localStorage.setItem(key, data[key]);
} }

View File

@@ -323,7 +323,12 @@
<div class="panel-50 p-bottom-20" style="padding-left: 20px; padding-right: 20px;"> <div class="panel-50 p-bottom-20" style="padding-left: 20px; padding-right: 20px;">
<h3>RDS Mode</h3> <h3>RDS Mode</h3>
<p>You can switch between American (RBDS) / Global (RDS) mode here.</p> <p>You can switch between American (RBDS) / Global (RDS) mode here.</p>
<%- include('_components', {component: 'checkbox', cssClass: 'bottom-20', iconClass: '', label: 'American RDS mode (RBDS)', id: 'webserver-rdsMode'}) %><br> <%- include('_components', {component: 'checkbox', cssClass: 'bottom-20', iconClass: '', label: 'American RDS mode (RBDS)', id: 'webserver-rdsMode'}) %>
<h3>RDS Timeout</h3>
<p>If no data is received, RDS will be automatically cleared after a timeout.<br>
<span class="text-gray">Enter timeout in seconds or 0 to disable.</span><br></p>
<%- include('_components', {component: 'text', cssClass: 'w-100', placeholder: '0', label: 'RDS Timeout', id: 'webserver-rdsTimeout'}) %>
</div> </div>
<div class="panel-50 p-bottom-20" style="padding-left: 20px; padding-right: 20px;"> <div class="panel-50 p-bottom-20" style="padding-left: 20px; padding-right: 20px;">
<h3>Transmitter Search Algorithm</h3> <h3>Transmitter Search Algorithm</h3>