viewers: smoother mic dBL chart + restore binary/TXT download links
Two issues spotted in the modal:
1. Mic dBL chart looked spikey/discontinuous — isolated bars at 80-95
with gaps in between. Cause: _psiToDbl() returns null for zero or
negative samples, and most mic samples on a quiet event sit at the
digitization noise floor where they're effectively zero. Result:
the chart only renders the moments when instantaneous SPL exceeded
the Y-axis bottom — looks like a sound trigger gate.
Fix: new _psiToDblForChart() rectifies the AC waveform (abs), then
converts to dBL, then floors at MIC_DBL_FLOOR=60 dBL. Chart now
has a continuous 60 dBL baseline with peaks above it — matches how
acoustic engineers expect SPL-vs-time. Y-axis bottom pinned to
MIC_DBL_FLOOR, top to peak + 5 dB headroom. Peak label still uses
the unrectified _psiToDbl so the displayed peak value is exact.
2. Filename in Source/Files block was unlinked. Endpoint exists
(/db/events/{id}/blastware_file) — just wasn't wired to the modal.
Made it a clickable download link. Same treatment for the
preserved .TXT — added "(download .TXT)" link next to source kind
when source.txt_filename is populated (events ingested after the
.TXT preservation feature landed; older events show no link).
Applied to both the inline 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:
+23
-1
@@ -356,6 +356,18 @@ function _psiToDbl(psi) {
|
||||
return 20 * Math.log10(psi / DBL_REF);
|
||||
}
|
||||
|
||||
// Per-sample mic chart conversion — rectify the AC waveform, dBL,
|
||||
// floor below the noise-floor minimum. Gives a continuous baseline
|
||||
// instead of the spikey/discontinuous look you get from raw _psiToDbl.
|
||||
const MIC_DBL_FLOOR = 60;
|
||||
function _psiToDblForChart(psi) {
|
||||
if (psi == null) return MIC_DBL_FLOOR;
|
||||
const a = Math.abs(psi);
|
||||
if (a === 0) return MIC_DBL_FLOOR;
|
||||
const dbl = 20 * Math.log10(a / DBL_REF);
|
||||
return dbl > MIC_DBL_FLOOR ? dbl : MIC_DBL_FLOOR;
|
||||
}
|
||||
|
||||
// Format an ISO timestamp in the browser's local timezone — UTC values
|
||||
// (with 'Z' suffix) convert; naive values are interpreted as local clock.
|
||||
// Returns '—' for null/empty/unparseable.
|
||||
@@ -638,7 +650,10 @@ function renderWaveform(data) {
|
||||
let peak = chData.peak;
|
||||
const peakT = chData.peak_t_ms;
|
||||
if (ch === 'MicL' && unit === 'psi' && micUnit === 'dBL') {
|
||||
values = values.map(_psiToDbl);
|
||||
// Per-sample chart uses rectified-and-floored conversion so the
|
||||
// baseline is continuous; the peak label uses the unrectified
|
||||
// converter to preserve the true measurement.
|
||||
values = values.map(_psiToDblForChart);
|
||||
peak = _psiToDbl(peak);
|
||||
unit = 'dB(L)';
|
||||
}
|
||||
@@ -711,6 +726,13 @@ function renderWaveform(data) {
|
||||
}
|
||||
const padded = (absMax || 1) * 1.10;
|
||||
yBounds = { min: -padded, max: padded };
|
||||
} else if (ch === 'MicL' && micUnit === 'dBL') {
|
||||
// Baseline at noise-floor minimum (matches what we floored
|
||||
// null/quiet samples to), top at peak + 5 dB headroom.
|
||||
const peakDbl = (typeof peak === 'number' && isFinite(peak))
|
||||
? peak + 5
|
||||
: 100;
|
||||
yBounds = { min: MIC_DBL_FLOOR, max: Math.max(peakDbl, MIC_DBL_FLOOR + 20) };
|
||||
}
|
||||
|
||||
const chart = new Chart(canvas, {
|
||||
|
||||
Reference in New Issue
Block a user