You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
device types, bugfixes
This commit is contained in:
134
datahandler.js
134
datahandler.js
@@ -6,6 +6,7 @@ const path = require('path');
|
||||
const os = require('os');
|
||||
const platform = os.platform();
|
||||
const cpuArchitecture = os.arch();
|
||||
const { configName, serverConfig, configUpdate, configSave } = require('./server_config');
|
||||
let unicode_type;
|
||||
let shared_Library;
|
||||
|
||||
@@ -207,8 +208,10 @@ var dataToSend = {
|
||||
freq: 87.500.toFixed(3),
|
||||
previousFreq: 87.500.toFixed(3),
|
||||
signal: 0,
|
||||
highestSignal: -Infinity,
|
||||
st: false,
|
||||
st_forced: false,
|
||||
rds: false,
|
||||
ps: '',
|
||||
tp: 0,
|
||||
ta: 0,
|
||||
@@ -234,6 +237,14 @@ var dataToSend = {
|
||||
users: 0,
|
||||
};
|
||||
|
||||
const filterMappings = {
|
||||
'G11': { eq: 1, ims: 1 },
|
||||
'G01': { eq: 0, ims: 1 },
|
||||
'G10': { eq: 1, ims: 0 },
|
||||
'G00': { eq: 0, ims: 0 }
|
||||
};
|
||||
|
||||
|
||||
var legacyRdsPiBuffer = null;
|
||||
const initialData = { ...dataToSend };
|
||||
const resetToDefault = dataToSend => Object.assign(dataToSend, initialData);
|
||||
@@ -243,8 +254,10 @@ function handleData(ws, receivedData) {
|
||||
// Retrieve the last update time for this client
|
||||
let lastUpdateTime = clientUpdateIntervals.get(ws) || 0;
|
||||
const currentTime = Date.now();
|
||||
|
||||
let modifiedData, parsedValue;
|
||||
const receivedLines = receivedData.split('\n');
|
||||
|
||||
for (const receivedLine of receivedLines) {
|
||||
switch (true) {
|
||||
case receivedLine.startsWith('P'):
|
||||
@@ -272,86 +285,29 @@ function handleData(ws, receivedData) {
|
||||
initialData.ant = receivedLine.substring(1);
|
||||
break;
|
||||
case receivedLine.startsWith('G'):
|
||||
switch (receivedLine) {
|
||||
case 'G11':
|
||||
initialData.eq = 1;
|
||||
dataToSend.eq = 1;
|
||||
initialData.ims = 1;
|
||||
dataToSend.ims = 1;
|
||||
break;
|
||||
case 'G01':
|
||||
initialData.eq = 0;
|
||||
dataToSend.eq = 0;
|
||||
initialData.ims = 1;
|
||||
dataToSend.ims = 1;
|
||||
break;
|
||||
case 'G10':
|
||||
initialData.eq = 1;
|
||||
dataToSend.eq = 1;
|
||||
initialData.ims = 0;
|
||||
dataToSend.ims = 0;
|
||||
break;
|
||||
case 'G00':
|
||||
initialData.eq = 0;
|
||||
initialData.ims = 0;
|
||||
dataToSend.eq = 0;
|
||||
dataToSend.ims = 0;
|
||||
break;
|
||||
}
|
||||
case receivedLine.startsWith('Sm'):
|
||||
modifiedData = receivedLine.substring(2);
|
||||
parsedValue = parseFloat(modifiedData);
|
||||
dataToSend.st = false;
|
||||
dataToSend.st_forced = false;
|
||||
initialData.st = false;
|
||||
initialData.st_forced = false;
|
||||
|
||||
if (!isNaN(parsedValue)) {
|
||||
dataToSend.signal = parsedValue.toFixed(2);
|
||||
initialData.signal = parsedValue.toFixed(2);
|
||||
const mapping = filterMappings[receivedLine];
|
||||
if (mapping) {
|
||||
initialData.eq = mapping.eq;
|
||||
initialData.ims = mapping.ims;
|
||||
dataToSend.eq = mapping.eq;
|
||||
dataToSend.ims = mapping.ims;
|
||||
}
|
||||
break;
|
||||
case receivedData.startsWith('Sm'):
|
||||
processSignal(receivedData, false, false);
|
||||
break;
|
||||
case receivedData.startsWith('Ss'):
|
||||
modifiedData = receivedData.substring(2);
|
||||
parsedValue = parseFloat(modifiedData);
|
||||
dataToSend.st = true;
|
||||
dataToSend.st_forced = false;
|
||||
initialData.st = true;
|
||||
initialData.st_forced = false;
|
||||
|
||||
if (!isNaN(parsedValue)) {
|
||||
dataToSend.signal = parsedValue.toFixed(2);
|
||||
initialData.signal = parsedValue.toFixed(2);
|
||||
}
|
||||
processSignal(receivedData, true, false);
|
||||
break;
|
||||
case receivedData.startsWith('SS'):
|
||||
modifiedData = receivedData.substring(2);
|
||||
parsedValue = parseFloat(modifiedData);
|
||||
dataToSend.st = true;
|
||||
dataToSend.st_forced = true;
|
||||
initialData.st = true;
|
||||
initialData.st_forced = true;
|
||||
|
||||
if (!isNaN(parsedValue)) {
|
||||
dataToSend.signal = parsedValue.toFixed(2);
|
||||
initialData.signal = parsedValue.toFixed(2);
|
||||
}
|
||||
processSignal(receivedData, true, true);
|
||||
break;
|
||||
case receivedData.startsWith('SM'):
|
||||
modifiedData = receivedData.substring(2);
|
||||
parsedValue = parseFloat(modifiedData);
|
||||
dataToSend.st = false;
|
||||
dataToSend.st_forced = true;
|
||||
initialData.st = false;
|
||||
initialData.st_forced = true;
|
||||
|
||||
if (!isNaN(parsedValue)) {
|
||||
dataToSend.signal = parsedValue.toFixed(2);
|
||||
initialData.signal = parsedValue.toFixed(2);
|
||||
}
|
||||
break;
|
||||
processSignal(receivedData, false, true);
|
||||
break;
|
||||
case receivedLine.startsWith('R'):
|
||||
modifiedData = receivedLine.slice(1);
|
||||
dataToSend.rds = true;
|
||||
|
||||
if (modifiedData.length == 14) {
|
||||
// Handle legacy RDS message
|
||||
@@ -411,6 +367,42 @@ function showOnlineUsers(currentUsers) {
|
||||
initialData.users = currentUsers;
|
||||
}
|
||||
|
||||
function convertSignal(dBFS, fullScaleVoltage = 1, inputImpedance = 300) {
|
||||
// Convert dBFS to voltage
|
||||
let voltage = Math.pow(10, dBFS / 20) * fullScaleVoltage;
|
||||
|
||||
// Convert voltage to microvolts
|
||||
let uV = voltage * 1e6;
|
||||
|
||||
// Convert microvolts to dBuV
|
||||
let dBf = 20 * Math.log10(uV / Math.sqrt(2) / Math.sqrt(inputImpedance));
|
||||
|
||||
return dBf.toFixed(2);
|
||||
}
|
||||
|
||||
function processSignal(receivedData, st, stForced) {
|
||||
const modifiedData = receivedData.substring(2);
|
||||
const parsedValue = parseFloat(modifiedData);
|
||||
dataToSend.st = st;
|
||||
dataToSend.st_forced = stForced;
|
||||
initialData.st = st;
|
||||
initialData.st_forced = stForced;
|
||||
|
||||
if (!isNaN(parsedValue)) {
|
||||
/*if (serverConfig.device && serverConfig.device === 'sdr') {
|
||||
dataToSend.signal = convertSignal(parsedValue);
|
||||
initialData.signal = convertSignal(parsedValue);
|
||||
} else {*/
|
||||
dataToSend.signal = parsedValue.toFixed(2);
|
||||
initialData.signal = parsedValue.toFixed(2);
|
||||
//}
|
||||
|
||||
if(dataToSend.signal > dataToSend.highestSignal) {
|
||||
dataToSend.highestSignal = dataToSend.signal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleData, showOnlineUsers, dataToSend, initialData, resetToDefault
|
||||
};
|
||||
|
||||
14
fmdx_list.js
14
fmdx_list.js
@@ -2,7 +2,8 @@
|
||||
const fs = require('fs');
|
||||
const fetch = require('node-fetch');
|
||||
const { logDebug, logError, logInfo, logWarn } = require('./console');
|
||||
const { serverConfig, configUpdate, configSave } = require('./server_config')
|
||||
const { serverConfig, configUpdate, configSave } = require('./server_config');
|
||||
var pjson = require('./package.json');
|
||||
|
||||
let timeoutID = null;
|
||||
|
||||
@@ -59,6 +60,12 @@ function sendKeepalive() {
|
||||
}
|
||||
|
||||
function sendUpdate() {
|
||||
|
||||
let bwLimit = '';
|
||||
if (serverConfig.webserver.tuningLimit === true) {
|
||||
bwLimit = serverConfig.webserver.tuningLowerLimit + ' - ' + serverConfig.webserver.tuningUpperLimit + ' Mhz';
|
||||
}
|
||||
|
||||
const request = {
|
||||
status: (serverConfig.lockToAdmin ? 2 : 1),
|
||||
coords: [serverConfig.identification.lat, serverConfig.identification.lon],
|
||||
@@ -66,7 +73,10 @@ function sendUpdate() {
|
||||
desc: serverConfig.identification.tunerDesc,
|
||||
audioChannels: serverConfig.audio.audioChannels,
|
||||
audioQuality: serverConfig.audio.audioBitrate,
|
||||
contact: serverConfig.identification.contact || ''
|
||||
contact: serverConfig.identification.contact || '',
|
||||
device: serverConfig.deviceName || '',
|
||||
bwLimit: bwLimit,
|
||||
version: pjson.version
|
||||
};
|
||||
|
||||
if (serverConfig.identification.token)
|
||||
|
||||
7
index.js
7
index.js
@@ -108,6 +108,8 @@ function connectToSerial() {
|
||||
|
||||
serialport.on('open', () => {
|
||||
logInfo('Using COM device: ' + serverConfig.xdrd.comPort);
|
||||
|
||||
serialport.write('x\n');
|
||||
serialport.write('M0\n');
|
||||
serialport.write('Y100\n');
|
||||
serialport.write('D0\n');
|
||||
@@ -128,8 +130,6 @@ function connectToSerial() {
|
||||
serialport.write('T87500\n');
|
||||
}
|
||||
|
||||
serialport.write('x\n');
|
||||
|
||||
serialport.on('data', (data) => {
|
||||
resolveDataBuffer(data);
|
||||
});
|
||||
@@ -422,6 +422,7 @@ app.get('/', (req, res) => {
|
||||
tuningLowerLimit: serverConfig.webserver.tuningLowerLimit,
|
||||
tuningUpperLimit: serverConfig.webserver.tuningUpperLimit,
|
||||
chatEnabled: serverConfig.webserver.chatEnabled,
|
||||
device: serverConfig.device
|
||||
})
|
||||
}
|
||||
});
|
||||
@@ -637,7 +638,7 @@ wss.on('connection', (ws, request) => {
|
||||
}
|
||||
}
|
||||
|
||||
if((serverConfig.publicTuner === true) || (request.session && request.session.isTuneAuthenticated === true && serverConfig.xdrd.wirelessConnection)) {
|
||||
if((serverConfig.publicTuner === true) || (request.session && request.session.isTuneAuthenticated === true && serverConfig.xdrd.wirelessConnection)) {
|
||||
|
||||
if(serverConfig.lockToAdmin === true) {
|
||||
if(request.session && request.session.isAdminAuthenticated === true) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fm-dx-webserver",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -10,6 +10,10 @@ const fetchInterval = 3000;
|
||||
function fetchTx(freq, piCode, rdsPs) {
|
||||
const now = Date.now();
|
||||
freq = parseFloat(freq);
|
||||
|
||||
if(isNaN(freq)) {
|
||||
return;
|
||||
}
|
||||
// Check if it's been at least 3 seconds since the last fetch and if the QTH is correct
|
||||
if (now - lastFetchTime < fetchInterval || serverConfig.identification.lat.length < 2 || freq < 87) {
|
||||
return Promise.resolve();
|
||||
|
||||
@@ -36,6 +36,24 @@ h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tooltiptext {
|
||||
position: absolute;
|
||||
background-color: var(--color-3);
|
||||
color: var(--color-text);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
border-radius: 30px;
|
||||
padding: 5px 25px;
|
||||
z-index: 1000;
|
||||
opacity:var(--color-main);
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
p#tuner-desc {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -78,6 +96,10 @@ label {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.highest-signal-container {
|
||||
margin-bottom: -20px !important;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
float: left;
|
||||
margin-bottom: 10px;
|
||||
@@ -215,6 +237,17 @@ label {
|
||||
#tuner-wireless {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: auto; /* Ensure that the overlay captures clicks */
|
||||
opacity: 0; /* Make the overlay invisible */
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
canvas, #flags-container {
|
||||
display: none;
|
||||
@@ -234,6 +267,10 @@ label {
|
||||
.form-group {
|
||||
float: none;
|
||||
}
|
||||
.highest-signal-container {
|
||||
margin-top: -20px !important;
|
||||
margin-bottom: 15px !important;
|
||||
}
|
||||
#data-pi {
|
||||
font-size: 24px;
|
||||
margin-top: 20px;
|
||||
|
||||
@@ -202,6 +202,10 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-select-none {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
.text-medium-big {
|
||||
font-size: 32px;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<link href="css/flags.min.css" type="text/css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" type="text/css" rel="stylesheet">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon2.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
@@ -73,12 +74,12 @@
|
||||
<div class="panel-10 no-bg h-100 m-0 m-right-20 hide-phone" style="width: 100px;margin-right: 20px !important;">
|
||||
<button class="playbutton" aria-label="Play / Stop Button"><i class="fa-solid fa-play fa-lg"></i></button>
|
||||
</div>
|
||||
<div class="panel-100 m-0 hover-brighten flex-center" id="ps-container" style="height: 90px;">
|
||||
<div class="panel-100 m-0 hover-brighten flex-center" id="ps-container" style="height: 90px;" data-tooltip="Clicking on the RDS PS will copy the RDS info into the clipboard.">
|
||||
<span class="text-big" id="data-ps"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="flags-container-desktop" class="panel-33">
|
||||
<div id="flags-container-desktop" class="panel-33 user-select-none">
|
||||
<h2 class="show-phone">
|
||||
<div class="data-pty text-color-default"></div>
|
||||
</h2>
|
||||
@@ -88,14 +89,17 @@
|
||||
<div style="display:inline-block">
|
||||
<span style="margin-left: 20px;display: block;margin-top: 2px;" class="data-flag"></span>
|
||||
</div>
|
||||
<span id="stereo-container" class="pointer"><span style="margin-left: 20px;" class="data-st">ST</span></span>
|
||||
<span id="stereo-container" class="pointer" style="position: relative;">
|
||||
<span style="margin-left: 20px;" class="data-st">ST</span>
|
||||
<span class="overlay" data-tooltip="Stereo / Mono toggle. <br><strong>Click to toggle."></span>
|
||||
</span>
|
||||
<span style="margin-left: 15px;" class="data-ms">MS</span>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-container">
|
||||
<div class="panel-33 hover-brighten" id="pi-code-container">
|
||||
<div class="panel-33 hover-brighten" id="pi-code-container" data-tooltip="Clicking on the PI code will show the current station on a map.">
|
||||
<h2>PI CODE</h2>
|
||||
<span id="data-pi" class="text-big text-uppercase"></span>
|
||||
</div>
|
||||
@@ -107,11 +111,16 @@
|
||||
|
||||
<div class="panel-33">
|
||||
<h2>SIGNAL</h2>
|
||||
<span class="text-big">
|
||||
<div class="text-small text-gray highest-signal-container">
|
||||
<i class="fa-solid fa-arrow-up"></i>
|
||||
<span id="data-signal-highest"></span>
|
||||
<span class="signal-units"></span>
|
||||
</div>
|
||||
<div class="text-big">
|
||||
<span id="data-signal"></span><!--
|
||||
--><span id="data-signal-decimal" class="text-medium-big" style="opacity:0.7;"></span>
|
||||
<span id="signal-units" class="text-medium">dBf</span>
|
||||
</span>
|
||||
<span class="signal-units text-medium">dBf</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -135,10 +144,12 @@
|
||||
<% } %>
|
||||
|
||||
<div class="panel-100 no-bg h-100 m-0 button-eq">
|
||||
<button id="data-eq" style="border-radius: 30px 0px 0px 30px;" aria-label="EQ / RF+ Filter"><span class="text-bold">EQ</span><br><span class="text-smaller">(RF+)</span></button>
|
||||
<% if (device == 'tef') { %><button id="data-eq" style="border-radius: 30px 0px 0px 30px;" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 KHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
|
||||
<% if (device == 'xdr') { %><button id="data-eq" style="border-radius: 30px 0px 0px 30px;" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
|
||||
</div>
|
||||
<div class="panel-100 no-bg h-100 m-0 button-ims">
|
||||
<button id="data-ims" style="border-radius: 0px 30px 30px 0px;" aria-label="iMS / IF+ Filter"><span class="text-bold">iMS</span><br><span class="text-smaller">(IF+)</span></button>
|
||||
<% if (device == 'tef') { %><button id="data-ims" style="border-radius: 0px 30px 30px 0px;" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
|
||||
<% if (device == 'xdr') { %><button id="data-ims" style="border-radius: 0px 30px 30px 0px;" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,7 +173,7 @@
|
||||
<hr class="hide-desktop">
|
||||
</div>
|
||||
|
||||
<div class="panel-33 hover-brighten">
|
||||
<div class="panel-33 hover-brighten" data-tooltip="This panel contains the current TX info when RDS is loaded.<br><strong>Clicking on this panel copies the info into the clipboard.</strong>">
|
||||
<div id="data-station-container">
|
||||
<h2 style="margin-top: 0;" class="mb-0">
|
||||
<span id="data-station-name"></span>
|
||||
@@ -277,7 +288,7 @@
|
||||
<a href="./setup">Setup</a> • <a class="logout-link" href="#">Logout</a>
|
||||
</p>
|
||||
<% } else if (isTuneAuthenticated) { %>
|
||||
<p>You are logged in and can control the receiver. <a class="logout-link" href="#">Logout</a></p>
|
||||
<p>You are logged in and can control the receiver.<br><a class="logout-link" href="#">Logout</a></p>
|
||||
<% } else { %>
|
||||
<form action="./login" method="post" id="login-form" class="top-25">
|
||||
<label for="password">Password:</label>
|
||||
@@ -349,5 +360,9 @@
|
||||
</div>
|
||||
|
||||
<script src="js/webserver.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/screenshot-capture/2.0.0/screenshot-capture.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -41,6 +41,10 @@ function submitData() {
|
||||
return $(this).text() === $('#audio-quality').val();
|
||||
}).data('value') || "192k");
|
||||
|
||||
const device = ($('.options .option').filter(function() {
|
||||
return $(this).text() === $('#device-type').val();
|
||||
}).data('value') || "tef");
|
||||
|
||||
const tunerName = $('#webserver-name').val() || 'FM Tuner';
|
||||
const tunerDesc = $('#webserver-desc').val() || 'Default FM tuner description';
|
||||
const broadcastTuner = $("#broadcast-tuner").is(":checked");
|
||||
@@ -94,6 +98,7 @@ function submitData() {
|
||||
tunePass,
|
||||
adminPass,
|
||||
},
|
||||
device,
|
||||
publicTuner,
|
||||
lockToAdmin,
|
||||
autoShutdown,
|
||||
@@ -179,6 +184,12 @@ function submitData() {
|
||||
$("#com-devices").val(selectedDevice.text());
|
||||
}
|
||||
|
||||
$('#device-type').val(data.device);
|
||||
var selectedDevice = $(".option[data-value='" + data.device + "']");
|
||||
if (selectedDevice.length > 0) {
|
||||
$("#device-type").val(selectedDevice.text());
|
||||
}
|
||||
|
||||
$('#audio-devices').val(data.audio.audioDevice);
|
||||
$('#audio-channels').val(data.audio.audioChannels);
|
||||
var selectedChannels = $(".option[data-value='" + data.audio.audioChannels + "']");
|
||||
|
||||
@@ -17,11 +17,17 @@ const europe_programmes = [
|
||||
"Oldies Music", "Folk Music", "Documentary", "Alarm Test"
|
||||
];
|
||||
|
||||
const usa_programmes = [
|
||||
"No PTY", "News", "Information", "Sports", "Talk", "Rock", "Classic Rock",
|
||||
"Adults Hits", "Soft Rock", "Top 40", "Country", "Oldies", "Soft Music",
|
||||
"Nostalgia", "Jazz", "Classical", "Rhythm and Blues", "Soft Rhythm and Blues",
|
||||
"Language", "Religious Music", "Religious Talk", "Personality", "Public", "College",
|
||||
"Spanish Talk", "Spanish Music", "Hip Hop", "", "", "Weather", "Emergency Test", "Emergency"
|
||||
];
|
||||
|
||||
$(document).ready(function () {
|
||||
var canvas = $('#signal-canvas')[0];
|
||||
|
||||
var signalToggle = $("#signal-units-toggle");
|
||||
|
||||
canvas.width = canvas.parentElement.clientWidth;
|
||||
canvas.height = canvas.parentElement.clientHeight;
|
||||
|
||||
@@ -135,10 +141,11 @@ $(document).ready(function () {
|
||||
$(rtContainer).on("click", copyRt);
|
||||
$(txContainer).on("click", copyTx);
|
||||
$(piCodeContainer).on("click", findOnMaps);
|
||||
$(stereoContainer).on("click", toggleForcedStereo);
|
||||
$(document).on("click", "#stereo-container", toggleForcedStereo);
|
||||
$(freqContainer).on("click", function () {
|
||||
textInput.focus();
|
||||
});
|
||||
initTooltips();
|
||||
});
|
||||
|
||||
function getServerTime() {
|
||||
@@ -377,6 +384,27 @@ function checkKey(e) {
|
||||
case 82: // RDS Reset (R key)
|
||||
tuneTo(Number(currentFreq));
|
||||
break;
|
||||
case 83: // Screenshot (S key)
|
||||
screenshotCapture.capture().then(function (dataUrl) {
|
||||
// Create an input element to hold the data URL temporarily
|
||||
var aux = $('<input>').attr({
|
||||
type: 'text',
|
||||
value: dataUrl
|
||||
});
|
||||
|
||||
// Append the input element to the body, select its contents, and copy them to the clipboard
|
||||
$('body').append(aux);
|
||||
aux.select();
|
||||
document.execCommand('copy');
|
||||
aux.remove();
|
||||
|
||||
// Alert the user that the screenshot has been copied to the clipboard
|
||||
alert('Screenshot copied to clipboard!');
|
||||
}).catch(function (error) {
|
||||
console.error('Error capturing screenshot:', error);
|
||||
});
|
||||
|
||||
break;
|
||||
case 38:
|
||||
socket.send("T" + (Math.round(currentFreq*1000) + ((currentFreq > 30) ? 10 : 1)));
|
||||
break;
|
||||
@@ -465,7 +493,7 @@ async function copyPs() {
|
||||
var ps = $('#data-ps').text();
|
||||
var signal = $('#data-signal').text();
|
||||
var signalDecimal = $('#data-signal-decimal').text();
|
||||
var signalUnit = $('#signal-units').text();
|
||||
var signalUnit = $('.signal-units').text();
|
||||
|
||||
try {
|
||||
await copyToClipboard(frequency + " - " + pi + " | " + ps + " [" + signal + signalDecimal + " " + signalUnit + "]");
|
||||
@@ -543,13 +571,14 @@ function findOnMaps() {
|
||||
function updateSignalUnits(parsedData, averageSignal) {
|
||||
const signalUnit = localStorage.getItem('signalUnit');
|
||||
let currentSignal;
|
||||
let highestSignal = parsedData.highestSignal;
|
||||
|
||||
if(localStorage.getItem("smoothSignal") == 'true') {
|
||||
currentSignal = averageSignal
|
||||
} else {
|
||||
currentSignal = parsedData.signal;
|
||||
}
|
||||
let signalText = $('#signal-units');
|
||||
let signalText = $('.signal-units');
|
||||
let signalValue;
|
||||
|
||||
switch (signalUnit) {
|
||||
@@ -572,6 +601,7 @@ function updateSignalUnits(parsedData, averageSignal) {
|
||||
const formatted = (Math.round(signalValue * 10) / 10).toFixed(1);
|
||||
const [integerPart, decimalPart] = formatted.split('.');
|
||||
|
||||
$('#data-signal-highest').text(Number(highestSignal).toFixed(1));
|
||||
$('#data-signal').text(integerPart);
|
||||
$('#data-signal-decimal').text('.' + decimalPart);
|
||||
}
|
||||
@@ -589,6 +619,7 @@ function updateDataElements(parsedData) {
|
||||
const $dataTp = $('.data-tp');
|
||||
const $dataTa = $('.data-ta');
|
||||
const $dataMs = $('.data-ms');
|
||||
const $flagDesktopCointainer = $('#flags-container-desktop');
|
||||
const $dataPty = $('.data-pty');
|
||||
|
||||
$dataFrequency.text(parsedData.freq);
|
||||
@@ -604,6 +635,12 @@ function updateDataElements(parsedData) {
|
||||
$dataRt1.html(processString(parsedData.rt1, parsedData.rt1_errors));
|
||||
$dataPty.html(europe_programmes[parsedData.pty]);
|
||||
|
||||
if(parsedData.rds === true) {
|
||||
$flagDesktopCointainer.css('background-color', 'var(--color-2');
|
||||
} else {
|
||||
$flagDesktopCointainer.css('background-color', 'var(--color-1');
|
||||
}
|
||||
|
||||
$('.data-flag').html(`<i title="${parsedData.country_name}" class="flag-sm flag-sm-${parsedData.country_iso}"></i>`);
|
||||
$('.data-flag-big').html(`<i title="${parsedData.country_name}" class="flag-md flag-md-${parsedData.country_iso}"></i>`);
|
||||
|
||||
@@ -719,4 +756,28 @@ function toggleForcedStereo() {
|
||||
var message = "B";
|
||||
message += parsedData.st_forced = (parsedData.st_forced == "1") ? "0" : "1";
|
||||
socket.send(message);
|
||||
}
|
||||
|
||||
function initTooltips() {
|
||||
$('[data-tooltip]').hover(function(e){
|
||||
var tooltipText = $(this).data('tooltip');
|
||||
var tooltip = $('<div class="tooltiptext"></div>').html(tooltipText);
|
||||
$('body').append(tooltip);
|
||||
|
||||
var tooltipWidth = tooltip.outerWidth();
|
||||
var tooltipHeight = tooltip.outerHeight();
|
||||
var posX = e.pageX - tooltipWidth / 2;
|
||||
var posY = e.pageY - tooltipHeight - 10;
|
||||
|
||||
tooltip.css({ top: posY, left: posX, opacity: 0.9 });
|
||||
}, function() {
|
||||
$('.tooltiptext').remove();
|
||||
}).mousemove(function(e){
|
||||
var tooltipWidth = $('.tooltiptext').outerWidth();
|
||||
var tooltipHeight = $('.tooltiptext').outerHeight();
|
||||
var posX = e.pageX - tooltipWidth / 2;
|
||||
var posY = e.pageY - tooltipHeight - 10;
|
||||
|
||||
$('.tooltiptext').css({ top: posY, left: posX });
|
||||
});
|
||||
}
|
||||
@@ -3,7 +3,7 @@ var day = currentDate.getDate();
|
||||
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
|
||||
var year = currentDate.getFullYear();
|
||||
var formattedDate = day + '/' + month + '/' + year;
|
||||
var currentVersion = 'v1.1.3 [' + formattedDate + ']';
|
||||
var currentVersion = 'v1.1.4 [' + formattedDate + ']';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,13 +48,13 @@ function updateWizardContent() {
|
||||
$('.btn-prev').show();
|
||||
}
|
||||
|
||||
if($('.step:visible').index() == 2) {
|
||||
if($('.step:visible').index() == 3) {
|
||||
setTimeout(function () {
|
||||
map.invalidateSize();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
if($('.step:visible').index() == 3) {
|
||||
if($('.step:visible').index() == 4) {
|
||||
$('.btn-next').text('Save');
|
||||
} else {
|
||||
$('.btn-next').text('Next')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>FM-DX Webserver</title>
|
||||
<title>Setup - FM-DX Webserver</title>
|
||||
<link href="css/entry.css" type="text/css" rel="stylesheet">
|
||||
<link href="css/flags.min.css" type="text/css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" type="text/css" rel="stylesheet">
|
||||
@@ -21,6 +21,7 @@
|
||||
<div class="panel-100">
|
||||
<ul class="nav">
|
||||
<li data-panel="dashboard" class="active">Dashboard</li>
|
||||
<li data-panel="tuner">Tuner</li>
|
||||
<li data-panel="connection">Connection</li>
|
||||
<li data-panel="audio">Audio</li>
|
||||
<li data-panel="webserver">Webserver</li>
|
||||
@@ -340,6 +341,21 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="panel-100 tab-content" id="tuner">
|
||||
<h2>Tuner Specific Settings</h2>
|
||||
<div class="form-group">
|
||||
<label for="themes"><i class="fa-solid fa-radio"></i> Device:</label>
|
||||
<div class="dropdown" id="device-selector" style="margin-right: 0;">
|
||||
<input type="text" placeholder="TEF6686 / TEA685x" id="device-type" readonly>
|
||||
<ul class="options">
|
||||
<li class="option" data-value="tef">TEF668x / TEA685x</li>
|
||||
<li class="option" data-value="xdr">XDR (F1HD / S10HDiP)</li>
|
||||
<li class="option" data-value="sdr">SDR (RTL-SDR / AirSpy)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-100 tab-content" id="identification">
|
||||
<h2>Identification & Map</h2>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>FM-DX Webserver</title>
|
||||
<title>Wizard - FM-DX Webserver</title>
|
||||
<link href="css/entry.css" type="text/css" rel="stylesheet">
|
||||
<link href="css/flags.min.css" type="text/css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" type="text/css" rel="stylesheet">
|
||||
@@ -16,7 +16,6 @@
|
||||
<% if (isAdminAuthenticated) { %>
|
||||
<div class="panel-100 no-bg">
|
||||
<img class="top-10" src="../images/openradio_logo_neutral.png" height="64px">
|
||||
<h1 class="top-10">FM-DX WebServer</h1>
|
||||
<h2 class="text-monospace text-light text-gray">[SETUP WIZARD]</h2>
|
||||
</div>
|
||||
<div class="panel-100 no-bg flex-container flex-center flex-phone">
|
||||
@@ -24,14 +23,48 @@
|
||||
<div class="btn-rounded-cube">2</div>
|
||||
<div class="btn-rounded-cube">3</div>
|
||||
<div class="btn-rounded-cube">4</div>
|
||||
<div class="btn-rounded-cube">5</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-100">
|
||||
|
||||
<!-- BASIC SETTINGS -->
|
||||
<div class="panel-100 step" id="step1">
|
||||
<h2 class="settings-heading">BASIC SETTINGS</h2>
|
||||
<h2 class="settings-heading">Basic settings</h2>
|
||||
<p class="m-0">Welcome to the setup wizard! Let's set up some basic things.</p>
|
||||
|
||||
<h3>Webserver connection:</h3>
|
||||
<p class="m-0 text-gray">Leave the IP at 0.0.0.0 unless you explicitly know you have to change it.<br>Don't enter your public IP here.</p>
|
||||
<div class="flex-center top-25">
|
||||
<div class="form-group">
|
||||
<label for="webserver-ip">Webserver IP:</label>
|
||||
<input class="input-text w-150" type="text" name="webserver-ip" id="webserver-ip" placeholder="0.0.0.0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="webserver-port">Webserver port:</label>
|
||||
<input class="input-text w-100" type="text" name="webserver-port" id="webserver-port" placeholder="8080">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BASIC SETTINGS END -->
|
||||
<!-- TUNER SETTINGS -->
|
||||
<div id="step2" class="step" style="display: none">
|
||||
<h2>Tuner settings</h2>
|
||||
|
||||
<h3>Tuner type:</h3>
|
||||
<p class="text-gray">Settings a proper device type ensures that the correct interface and settings will load.</p>
|
||||
<div class="form-group">
|
||||
<label for="themes"><i class="fa-solid fa-radio"></i> Device:</label>
|
||||
<div class="dropdown" id="device-selector" style="margin-right: 0;">
|
||||
<input type="text" placeholder="TEF6686 / TEA685x" id="device-type" readonly>
|
||||
<ul class="options">
|
||||
<li class="option" data-value="tef">TEF668x / TEA685x</li>
|
||||
<li class="option" data-value="xdr">XDR (F1HD / S10HDiP)</li>
|
||||
<li class="option" data-value="sdr">SDR (RTL-SDR / AirSpy)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Tuner connection:</h3>
|
||||
<div style="width: 300px;" class="auto top-10">
|
||||
<label class="toggleSwitch nolabel" onclick="">
|
||||
@@ -74,24 +107,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br class="top-25">
|
||||
<h3>Webserver connection:</h3>
|
||||
<p class="m-0 text-gray">Leave the IP at 0.0.0.0 unless you explicitly know you have to change it.<br>Don't enter your public IP here.</p>
|
||||
<div class="flex-center top-25">
|
||||
<div class="form-group">
|
||||
<label for="webserver-ip">Webserver IP:</label>
|
||||
<input class="input-text w-150" type="text" name="webserver-ip" id="webserver-ip" placeholder="0.0.0.0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="webserver-port">Webserver port:</label>
|
||||
<input class="input-text w-100" type="text" name="webserver-port" id="webserver-port" placeholder="8080">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BASIC SETTINGS END -->
|
||||
<!-- TUNER SETTINGS END -->
|
||||
<!-- AUDIO SETTINGS -->
|
||||
<div id="step2" class="step" style="display: none;">
|
||||
<div id="step3" class="step" style="display: none;">
|
||||
<div class="panel-100" style="min-height: 120px;margin-bottom: 0;">
|
||||
<h2 class="settings-heading">AUDIO SETTINGS</h2>
|
||||
<p class="m-0 text-gray">In this section, we will set up the audio.<br>
|
||||
@@ -143,7 +162,7 @@
|
||||
</div>
|
||||
<!-- AUDIO SETTINGS END -->
|
||||
<!-- IDENTIFICATION START -->
|
||||
<div id="step3" class="step" style="display: none;">
|
||||
<div id="step4" class="step" style="display: none;">
|
||||
<div class="panel-100" style="padding-bottom: 20px;">
|
||||
<h2 class="settings-heading">IDENTIFICATION INFO</h2>
|
||||
<p class="text-gray">In this part, we will set up your indentification info, such as the server name, description and location.</p>
|
||||
@@ -188,7 +207,7 @@
|
||||
</div>
|
||||
<!-- IDENTIFICATION END -->
|
||||
<!-- ADMIN SETTINGS START -->
|
||||
<div id="step4" class="step" style="display: none;">
|
||||
<div id="step5" class="step" style="display: none;">
|
||||
<h2 class="settings-heading">Admin panel settings</h2>
|
||||
<p>We are at the last and final step of the settings.</p>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user