viewers: enforce minimum Y-range on histogram channels
Quiet histogram events were filling the chart panel even though the
peak was tiny (0.005 in/s rendered as 90% of chart height because
Chart.js auto-scaled to peak * 1.1). Made everything look uniformly
loud regardless of actual amplitude.
BW's solution: a near-fixed scale per channel ("Geo: 0.002 in/s/div"
from the footer). Quiet events render small, loud events render
proportionally tall.
Match the intent without copying BW's "no Y-axis labels at all"
convention. For histogram channels:
Geo (in/s): min Y range 0.05 in/s
Mic in psi: min Y range 0.001 psi
Mic in dBL: unchanged (the 60 dBL floor + peak+5 top already
gives quiet events a sensible baseline)
So a 0.005 in/s geo event renders as ~10% of chart height; a 0.05
event fills it; a 5.0 event still fills it (max(peak*1.1, 0.05) ==
peak*1.1 for any peak > 0.045).
Waveform charts unchanged — they should zoom for shape detail.
Applied to both the modal in sfm_webapp.html and the standalone
/events page in event_browser.html.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+17
-4
@@ -717,8 +717,9 @@ function renderWaveform(data) {
|
|||||||
// up AND down). Mic + histograms keep default auto-scale (always
|
// up AND down). Mic + histograms keep default auto-scale (always
|
||||||
// positive values; zero at the bottom).
|
// positive values; zero at the bottom).
|
||||||
let yBounds = {};
|
let yBounds = {};
|
||||||
const isGeoWaveform = !isHistogram && ch !== 'MicL';
|
const isGeo = ch !== 'MicL';
|
||||||
if (isGeoWaveform) {
|
if (isGeo && !isHistogram) {
|
||||||
|
// Waveform geo: symmetric around zero for full shape detail.
|
||||||
let absMax = 0;
|
let absMax = 0;
|
||||||
for (const v of values) {
|
for (const v of values) {
|
||||||
const a = Math.abs(v);
|
const a = Math.abs(v);
|
||||||
@@ -726,13 +727,25 @@ function renderWaveform(data) {
|
|||||||
}
|
}
|
||||||
const padded = (absMax || 1) * 1.10;
|
const padded = (absMax || 1) * 1.10;
|
||||||
yBounds = { min: -padded, max: padded };
|
yBounds = { min: -padded, max: padded };
|
||||||
|
} else if (isGeo && isHistogram) {
|
||||||
|
// Histogram geo: enforce minimum chart range so quiet events
|
||||||
|
// look quiet (matches BW's near-fixed-scale convention).
|
||||||
|
const HIST_GEO_MIN_INS = 0.05;
|
||||||
|
let p = 0;
|
||||||
|
for (const v of values) { const a = Math.abs(v); if (a > p) p = a; }
|
||||||
|
yBounds = { min: 0, max: Math.max(p * 1.10, HIST_GEO_MIN_INS) };
|
||||||
} else if (ch === 'MicL' && micUnit === 'dBL') {
|
} else if (ch === 'MicL' && micUnit === 'dBL') {
|
||||||
// Baseline at noise-floor minimum (matches what we floored
|
// Mic dBL: baseline at noise-floor minimum, top at peak + 5 dB.
|
||||||
// null/quiet samples to), top at peak + 5 dB headroom.
|
|
||||||
const peakDbl = (typeof peak === 'number' && isFinite(peak))
|
const peakDbl = (typeof peak === 'number' && isFinite(peak))
|
||||||
? peak + 5
|
? peak + 5
|
||||||
: 100;
|
: 100;
|
||||||
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
|
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
|
||||||
|
} else if (ch === 'MicL' && isHistogram && micUnit === 'psi') {
|
||||||
|
// Mic histogram in psi: same minimum-range treatment as geo.
|
||||||
|
const HIST_MIC_MIN_PSI = 0.001;
|
||||||
|
let p = 0;
|
||||||
|
for (const v of values) { const a = Math.abs(v); if (a > p) p = a; }
|
||||||
|
yBounds = { min: 0, max: Math.max(p * 1.10, HIST_MIC_MIN_PSI) };
|
||||||
}
|
}
|
||||||
|
|
||||||
const chart = new Chart(canvas, {
|
const chart = new Chart(canvas, {
|
||||||
|
|||||||
+23
-4
@@ -2748,8 +2748,9 @@ function _renderScWaveform(data) {
|
|||||||
// - Mic (always positive sound pressure) + histograms (per-interval
|
// - Mic (always positive sound pressure) + histograms (per-interval
|
||||||
// peaks, always positive): default auto-scale, zero at the bottom.
|
// peaks, always positive): default auto-scale, zero at the bottom.
|
||||||
let yBounds = {};
|
let yBounds = {};
|
||||||
const isGeoWaveform = !isHistogram && ch !== 'MicL';
|
const isGeo = ch !== 'MicL';
|
||||||
if (isGeoWaveform) {
|
if (isGeo && !isHistogram) {
|
||||||
|
// Waveform geo: symmetric around zero, full zoom to shape detail.
|
||||||
let absMax = 0;
|
let absMax = 0;
|
||||||
for (const v of values) {
|
for (const v of values) {
|
||||||
const a = Math.abs(v);
|
const a = Math.abs(v);
|
||||||
@@ -2757,13 +2758,31 @@ function _renderScWaveform(data) {
|
|||||||
}
|
}
|
||||||
const padded = (absMax || 1) * 1.10;
|
const padded = (absMax || 1) * 1.10;
|
||||||
yBounds = { min: -padded, max: padded };
|
yBounds = { min: -padded, max: padded };
|
||||||
|
} else if (isGeo && isHistogram) {
|
||||||
|
// Histogram geo: enforce a minimum chart range so a quiet
|
||||||
|
// 0.005 in/s event renders as ~10% of chart height instead of
|
||||||
|
// filling the panel. Matches BW's near-fixed-scale convention
|
||||||
|
// (their footer is "Geo: 0.002 in/s/div" — a chart-relative scale,
|
||||||
|
// not auto-zoom).
|
||||||
|
const HIST_GEO_MIN_INS = 0.05;
|
||||||
|
let peak = 0;
|
||||||
|
for (const v of values) { const a = Math.abs(v); if (a > peak) peak = a; }
|
||||||
|
yBounds = { min: 0, max: Math.max(peak * 1.10, HIST_GEO_MIN_INS) };
|
||||||
} else if (ch === 'MicL' && micUnit === 'dBL') {
|
} else if (ch === 'MicL' && micUnit === 'dBL') {
|
||||||
// Pin baseline at the chart floor (which matches what we flooded
|
// Mic in dBL — pin baseline at noise-floor minimum (where we floored
|
||||||
// null/quiet samples to), top at the actual peak + a few dB headroom.
|
// quiet samples), top at actual peak + a few dB headroom.
|
||||||
const peakDbl = (typeof chPeak === 'number' && isFinite(chPeak))
|
const peakDbl = (typeof chPeak === 'number' && isFinite(chPeak))
|
||||||
? chPeak + 5
|
? chPeak + 5
|
||||||
: 100;
|
: 100;
|
||||||
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
|
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
|
||||||
|
} else if (ch === 'MicL' && isHistogram && micUnit === 'psi') {
|
||||||
|
// Mic histogram in psi — same minimum-range treatment as geo.
|
||||||
|
// 0.001 psi ≈ 110 dBL — typical "loud" mic peak. Quiet events
|
||||||
|
// sit near the bottom.
|
||||||
|
const HIST_MIC_MIN_PSI = 0.001;
|
||||||
|
let peak = 0;
|
||||||
|
for (const v of values) { const a = Math.abs(v); if (a > peak) peak = a; }
|
||||||
|
yBounds = { min: 0, max: Math.max(peak * 1.10, HIST_MIC_MIN_PSI) };
|
||||||
}
|
}
|
||||||
|
|
||||||
_scCharts[ch] = new Chart(canvas, {
|
_scCharts[ch] = new Chart(canvas, {
|
||||||
|
|||||||
Reference in New Issue
Block a user