diff --git a/sfm/event_browser.html b/sfm/event_browser.html index bbd960f..ca19794 100644 --- a/sfm/event_browser.html +++ b/sfm/event_browser.html @@ -499,6 +499,14 @@ async function loadEvent(eventId) { renderEventList(); setStatus('Loading waveform…'); try { + // Sidecar fetch runs in parallel — its bw_report block carries ZC + // Freq + above-range flags + sensor-check results that the per- + // channel stats table surfaces. Failures are non-fatal (legacy + // events without a preserved .TXT have no sidecar bw_report). + const sidecarP = fetch(`${apiBase}/db/events/${eventId}/sidecar`) + .then(r => r.ok ? r.json() : null) + .catch(() => null); + const r = await fetch(`${apiBase}/db/events/${eventId}/waveform.json`); if (!r.ok) { if (r.status === 404) { @@ -511,7 +519,8 @@ async function loadEvent(eventId) { renderWaveform(data); // Also fetch metadata from the events list for richer header const ev = allEvents.find(e => e.id === eventId); - renderMeta(data, ev); + const sidecar = await sidecarP; + renderMeta(data, ev, sidecar); setStatus(`Event loaded.`, 'ok'); } catch (e) { setStatus(`Failed to load event: ${e.message}`, 'error'); @@ -528,7 +537,7 @@ function showEmpty(msg) { charts = {}; } -function renderMeta(data, ev) { +function renderMeta(data, ev, sidecar) { const metaDiv = document.getElementById('event-meta'); const fields = [ ['Serial', data.serial || ev?.serial || '—'], @@ -543,14 +552,20 @@ function renderMeta(data, ev) { ]; // Per-channel stats table mirroring the printout's middle block. - // Pulls per-channel PPV from the events row (DB columns) and additional - // details (peak time, peak accel, peak displacement, sensor check) from - // bw_report when present. + // PPV from the events DB row; ZC Freq + saturation flags from the + // sidecar's bw_report block (when a .TXT was preserved on ingest). + const bwrPeaks = (sidecar?.bw_report || {}).peaks || {}; + const bwrMic = (sidecar?.bw_report || {}).mic || {}; const fmt = v => (v == null ? '—' : (typeof v === 'number' ? v.toFixed(3) : v)); + const fmtZc = bwr => { + if (!bwr || bwr.zc_freq_hz == null) return '—'; + const prefix = bwr.zc_freq_above_range ? '>' : ''; + return `${prefix}${Math.round(bwr.zc_freq_hz)} Hz`; + }; const rows = [ - ['Tran', ev?.tran_ppv], - ['Vert', ev?.vert_ppv], - ['Long', ev?.long_ppv], + ['Tran', ev?.tran_ppv, fmtZc(bwrPeaks.tran)], + ['Vert', ev?.vert_ppv, fmtZc(bwrPeaks.vert)], + ['Long', ev?.long_ppv, fmtZc(bwrPeaks.long)], ]; // Mic display honors the current user preference (dBL default). // mic_ppv is stored as raw psi on series3 events; convert when needed. @@ -568,11 +583,11 @@ function renderMeta(data, ev) { const statsHtml = `
| Channel | PPV (in/s) | |
|---|---|---|
| Channel | PPV (in/s) | ZC Freq |
| ${ch} | ${fmt(ppv)} | |
| MicL | ${micStr} | |
| ${ch} | ${fmt(ppv)} | ${zc} |
| MicL | ${micStr} | ${fmtZc(bwrMic)} |