diff --git a/public/analytics.js b/public/analytics.js
index a8b8f9ec..3b65a695 100644
--- a/public/analytics.js
+++ b/public/analytics.js
@@ -1881,6 +1881,7 @@ function destroy() { _analyticsData = {}; _channelData = null; if (_ngState && _
window._analyticsSaveChannelSort = saveChannelSort;
window._analyticsChannelTbodyHtml = channelTbodyHtml;
window._analyticsChannelTheadHtml = channelTheadHtml;
+ window._analyticsRfNFColumnChart = rfNFColumnChart;
}
// ─── Neighbor Graph Tab ─────────────────────────────────────────────────────
@@ -2932,7 +2933,7 @@ function destroy() { _analyticsData = {}; _channelData = null; if (_ngState && _
// Render noise floor chart
const nfEl = document.getElementById('rfDetailNFChart');
if (nfEl && nfData.length > 1) {
- nfEl.innerHTML = rfNFLineChart(nfData, nfEl.clientWidth || 700, 180, reboots, minT, maxT);
+ nfEl.innerHTML = rfNFColumnChart(nfData, nfEl.clientWidth || 700, 180, reboots, minT, maxT);
} else if (nfEl) {
nfEl.innerHTML = 'Not enough noise floor data';
}
@@ -3196,7 +3197,13 @@ function destroy() { _analyticsData = {}; _channelData = null; if (_ngState && _
return svg;
}
- function rfNFLineChart(data, w, h, reboots, sharedMinT, sharedMaxT) {
+ /**
+ * Noise floor column chart — color-coded bars (green/yellow/red) by threshold.
+ * Replaces the old line chart for better discrete-sample readability.
+ * Thresholds: green (< -100 dBm), yellow (-100 to -85 dBm), red (≥ -85 dBm).
+ */
+ function rfNFColumnChart(data, w, h, reboots, sharedMinT, sharedMaxT) {
+ if (!data || !data.length) return '';
reboots = reboots || [];
const pad = { top: 20, right: 40, bottom: 30, left: 55 };
const cw = w - pad.left - pad.right;
@@ -3207,34 +3214,33 @@ function destroy() { _analyticsData = {}; _channelData = null; if (_ngState && _
const maxT = sharedMaxT != null ? sharedMaxT : Math.max(...data.map(d => new Date(d.t).getTime()));
const minV = Math.min(...values);
const maxV = Math.max(...values);
- const rangeV = maxV - minV || 1;
+ // Guard against zero range (single data point or constant values):
+ // use a ±5 dBm window so bars are visible and centered in the chart
+ const rawRangeV = maxV - minV;
+ const rangeV = rawRangeV || 10;
+ const adjMinV = rawRangeV ? minV : minV - 5;
const rangeT = maxT - minT || 1;
const sx = t => pad.left + ((t - minT) / rangeT) * cw;
- const sy = v => pad.top + ch - ((v - minV) / rangeV) * ch;
+ const sy = v => pad.top + ch - ((v - adjMinV) / rangeV) * ch;
- const pts = data.map(d => `${sx(new Date(d.t).getTime()).toFixed(1)},${sy(d.v).toFixed(1)}`).join(' ');
+ // Column width: proportional to chart width / data points, min 2px, gap of 1px
+ const colW = Math.max(2, Math.floor(cw / data.length) - 1);
- let svg = `