From 5d5441604bae34d7b9bd8cb00a40b27316e882d4 Mon Sep 17 00:00:00 2001 From: serversdown Date: Sun, 24 May 2026 20:26:23 +0000 Subject: [PATCH] viewers: symmetric Y-axis on geo waveforms + clarify timestamp labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes from the second screenshot review: 1. Geophone waveform Y-axis now renders SYMMETRIC around zero — zero line sits in the middle of the chart, signal goes both above and below. Standard seismograph display convention; matches the Instantel printout look. Previously Chart.js auto-scaled to the data range so e.g. Vert showing values from -0.005 to -0.015 had the zero line completely off-screen. Mic channel (sound pressure, always positive) keeps the default auto-scale anchored at zero. Histograms (per-interval peaks, also always positive) likewise keep bars rising from a zero baseline. 2. Modal labels clarified to remove the 'Timestamp' vs 'Captured at' ambiguity: 'Timestamp' → 'Recorded at' (when the seismograph recorded the event — from BW report's Event Time field) 'Captured at' → 'Received by server at' (when our sfm-db inserted the row) Both have tooltips explaining the distinction. Co-Authored-By: Claude Opus 4.7 (1M context) --- sfm/event_browser.html | 17 +++++++++++++++++ sfm/sfm_webapp.html | 25 +++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/sfm/event_browser.html b/sfm/event_browser.html index 357e718..5d7a493 100644 --- a/sfm/event_browser.html +++ b/sfm/event_browser.html @@ -629,6 +629,22 @@ function renderWaveform(data) { return (Number.isInteger(v) ? String(v) : v.toFixed(1)) + xAxisUnit; }; + // Y-axis bounds. Geophone waveforms render symmetric around zero + // (seismograph convention — zero line in the middle, signal goes + // up AND down). Mic + histograms keep default auto-scale (always + // positive values; zero at the bottom). + let yBounds = {}; + const isGeoWaveform = !isHistogram && ch !== 'MicL'; + if (isGeoWaveform) { + let absMax = 0; + for (const v of values) { + const a = Math.abs(v); + if (a > absMax) absMax = a; + } + const padded = (absMax || 1) * 1.10; + yBounds = { min: -padded, max: padded }; + } + const chart = new Chart(canvas, { type: isHistogram ? 'bar' : 'line', data: { @@ -677,6 +693,7 @@ function renderWaveform(data) { grid: { color: isPrintMode ? '#e0e0e0' : '#21262d', drawTicks: showXAxis }, }, y: { + ...yBounds, ticks: { color: isPrintMode ? '#666' : '#484f58', maxTicksLimit: 5 }, grid: { color: isPrintMode ? '#e0e0e0' : '#21262d' }, title: { display: true, text: unit, diff --git a/sfm/sfm_webapp.html b/sfm/sfm_webapp.html index e072566..2c4a912 100644 --- a/sfm/sfm_webapp.html +++ b/sfm/sfm_webapp.html @@ -2670,6 +2670,24 @@ function _renderScWaveform(data) { return String(v) + xAxisLabel; }; + // Y-axis bounds. Convention: + // - Geophones (Tran/Vert/Long) on waveform-mode events: + // symmetric around zero so the zero line sits in the middle and + // positive/negative excursions are visually balanced. + // - Mic (always positive sound pressure) + histograms (per-interval + // peaks, always positive): default auto-scale, zero at the bottom. + let yBounds = {}; + const isGeoWaveform = !isHistogram && ch !== 'MicL'; + if (isGeoWaveform) { + let absMax = 0; + for (const v of values) { + const a = Math.abs(v); + if (a > absMax) absMax = a; + } + const padded = (absMax || 1) * 1.10; + yBounds = { min: -padded, max: padded }; + } + _scCharts[ch] = new Chart(canvas, { type: isHistogram ? 'bar' : 'line', data: { @@ -2709,6 +2727,7 @@ function _renderScWaveform(data) { grid: { color: '#21262d', drawTicks: showX }, }, y: { + ...yBounds, ticks: { color: '#484f58', maxTicksLimit: 4 }, grid: { color: '#21262d' }, title: { display: true, text: chData.unit || '', color: '#484f58', font: { size: 9 } }, @@ -3072,7 +3091,8 @@ if (currentSection === 'db') {

Event

Serial
-
Timestamp
+
Recorded at
+
Record type
Sample rate
Waveform key
@@ -3104,7 +3124,8 @@ if (currentSection === 'db') {
File size
File sha256
Source kind
-
Captured at
+
Received by server at
+