diff --git a/src/public/index.html b/src/public/index.html index fb93514..7f43d9b 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -1379,6 +1379,31 @@
Colors the connection lines by the average SNR in the worst direction. Reload to update map.
+ +
+
+
+ +
+ +
+
Only show connections where data flows in both directions. Reload to update map.
+
+ + +
+ +
Only show connections where at least one direction has SNR above this threshold. Leave empty to show all connections. Reload to update map.
+ +
+
+ +
+ +
+
If checked, all existing directions must meet the minimum SNR threshold (both directions if bidirectional, single direction if unidirectional).
+
+
@@ -1682,8 +1707,8 @@ function getConfigConnectionsTimePeriodInSeconds() { const value = localStorage.getItem("config_connections_time_period_in_seconds"); - // default to 24 hours if unset - return value != null ? parseInt(value) : 86400; + // default to 7 days if unset + return value != null ? parseInt(value) : 604800; } function setConfigConnectionsTimePeriodInSeconds(value) { @@ -1703,6 +1728,51 @@ return localStorage.setItem("config_connections_colored_lines", value); } + function getConfigConnectionsBidirectionalOnly() { + const value = localStorage.getItem("config_connections_bidirectional_only"); + // disable bidirectional filter by default + if(value === null){ + return false; + } + return value === "true"; + } + + function setConfigConnectionsBidirectionalOnly(value) { + return localStorage.setItem("config_connections_bidirectional_only", value); + } + + function getConfigConnectionsMinSnrDb() { + const value = localStorage.getItem("config_connections_min_snr_db"); + // default to null (unset) + if(value === null || value === ""){ + return null; + } + const parsed = parseFloat(value); + return isNaN(parsed) ? null : parsed; + } + + function setConfigConnectionsMinSnrDb(value) { + if(value === null || value === "" || value === undefined){ + return localStorage.removeItem("config_connections_min_snr_db"); + } + // Convert to string for localStorage (handles both number and string inputs) + const stringValue = typeof value === "number" ? value.toString() : String(value); + return localStorage.setItem("config_connections_min_snr_db", stringValue); + } + + function getConfigConnectionsBidirectionalMinSnr() { + const value = localStorage.getItem("config_connections_bidirectional_min_snr"); + // disable bidirectional minimum SNR by default + if(value === null){ + return false; + } + return value === "true"; + } + + function setConfigConnectionsBidirectionalMinSnr(value) { + return localStorage.setItem("config_connections_bidirectional_min_snr", value); + } + function isMobile() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } @@ -1726,6 +1796,9 @@ configTemperatureFormat: window.getConfigTemperatureFormat(), configConnectionsTimePeriodInSeconds: window.getConfigConnectionsTimePeriodInSeconds(), configConnectionsColoredLines: window.getConfigConnectionsColoredLines(), + configConnectionsBidirectionalOnly: window.getConfigConnectionsBidirectionalOnly(), + configConnectionsMinSnrDb: window.getConfigConnectionsMinSnrDb(), + configConnectionsBidirectionalMinSnr: window.getConfigConnectionsBidirectionalMinSnr(), isShowingHardwareModels: false, hardwareModelStats: null, @@ -2643,6 +2716,15 @@ configConnectionsColoredLines() { window.setConfigConnectionsColoredLines(this.configConnectionsColoredLines); }, + configConnectionsBidirectionalOnly() { + window.setConfigConnectionsBidirectionalOnly(this.configConnectionsBidirectionalOnly); + }, + configConnectionsMinSnrDb() { + window.setConfigConnectionsMinSnrDb(this.configConnectionsMinSnrDb); + }, + configConnectionsBidirectionalMinSnr() { + window.setConfigConnectionsBidirectionalMinSnr(this.configConnectionsBidirectionalMinSnr); + }, deviceMetricsTimeRange() { this.loadNodeDeviceMetrics(this.selectedNode.node_id); }, @@ -3214,6 +3296,48 @@ if (!otherNode || !otherNodeMarker) continue; + // Apply bidirectional filter + const configConnectionsBidirectionalOnly = getConfigConnectionsBidirectionalOnly(); + if(configConnectionsBidirectionalOnly){ + const hasDirectionAB = connection.direction_ab && connection.direction_ab.avg_snr_db != null; + const hasDirectionBA = connection.direction_ba && connection.direction_ba.avg_snr_db != null; + if(!hasDirectionAB || !hasDirectionBA){ + continue; + } + } + + // Apply minimum SNR filter + const configConnectionsMinSnrDb = getConfigConnectionsMinSnrDb(); + if(configConnectionsMinSnrDb != null){ + const snrAB = connection.direction_ab && connection.direction_ab.avg_snr_db != null ? connection.direction_ab.avg_snr_db : null; + const snrBA = connection.direction_ba && connection.direction_ba.avg_snr_db != null ? connection.direction_ba.avg_snr_db : null; + + const configConnectionsBidirectionalMinSnr = getConfigConnectionsBidirectionalMinSnr(); + let hasSnrAboveThreshold; + + if(configConnectionsBidirectionalMinSnr){ + // Bidirectional mode: ALL existing directions must meet threshold + const directionsToCheck = []; + if(snrAB != null) directionsToCheck.push(snrAB); + if(snrBA != null) directionsToCheck.push(snrBA); + + if(directionsToCheck.length === 0){ + // No SNR data in either direction, skip + hasSnrAboveThreshold = false; + } else { + // All existing directions must be above threshold + hasSnrAboveThreshold = directionsToCheck.every(snr => snr > configConnectionsMinSnrDb); + } + } else { + // Default mode: EITHER direction has SNR above threshold + hasSnrAboveThreshold = (snrAB != null && snrAB > configConnectionsMinSnrDb) || (snrBA != null && snrBA > configConnectionsMinSnrDb); + } + + if(!hasSnrAboveThreshold){ + continue; + } + } + // Calculate distance const distanceInMeters = nodeMarker.getLatLng().distanceTo(otherNodeMarker.getLatLng()).toFixed(2); const configConnectionsMaxDistanceInMeters = getConfigConnectionsMaxDistanceInMeters(); @@ -3611,6 +3735,48 @@ continue; } + // Apply bidirectional filter + const configConnectionsBidirectionalOnly = getConfigConnectionsBidirectionalOnly(); + if(configConnectionsBidirectionalOnly){ + const hasDirectionAB = connection.direction_ab && connection.direction_ab.avg_snr_db != null; + const hasDirectionBA = connection.direction_ba && connection.direction_ba.avg_snr_db != null; + if(!hasDirectionAB || !hasDirectionBA){ + continue; + } + } + + // Apply minimum SNR filter + const configConnectionsMinSnrDb = getConfigConnectionsMinSnrDb(); + if(configConnectionsMinSnrDb != null){ + const snrAB = connection.direction_ab && connection.direction_ab.avg_snr_db != null ? connection.direction_ab.avg_snr_db : null; + const snrBA = connection.direction_ba && connection.direction_ba.avg_snr_db != null ? connection.direction_ba.avg_snr_db : null; + + const configConnectionsBidirectionalMinSnr = getConfigConnectionsBidirectionalMinSnr(); + let hasSnrAboveThreshold; + + if(configConnectionsBidirectionalMinSnr){ + // Bidirectional mode: ALL existing directions must meet threshold + const directionsToCheck = []; + if(snrAB != null) directionsToCheck.push(snrAB); + if(snrBA != null) directionsToCheck.push(snrBA); + + if(directionsToCheck.length === 0){ + // No SNR data in either direction, skip + hasSnrAboveThreshold = false; + } else { + // All existing directions must be above threshold + hasSnrAboveThreshold = directionsToCheck.every(snr => snr > configConnectionsMinSnrDb); + } + } else { + // Default mode: EITHER direction has SNR above threshold + hasSnrAboveThreshold = (snrAB != null && snrAB > configConnectionsMinSnrDb) || (snrBA != null && snrBA > configConnectionsMinSnrDb); + } + + if(!hasSnrAboveThreshold){ + continue; + } + } + // Calculate distance between nodes const distanceInMeters = nodeAMarker.getLatLng().distanceTo(nodeBMarker.getLatLng()).toFixed(2);