feat: render ">100" for above-range ZC Freq instead of "—"
BW writes ">100 Hz" for ZC Freq when the zero-crossing algorithm sees a peak too fast to count — the device's reporting ceiling is 100 Hz on V10.72. Our parser fell back to None via _parse_number (which requires a leading digit), so the PDF rendered "—" where BW shows ">100". Mirrors the OORANGE/saturated pattern already used for PPV and PSPL: parser stores the threshold (100.0) on zc_freq_hz + sets a new zc_freq_above_range flag. Projection carries the flag through to the sidecar; PDF renderer prepends ">" when set. Affects both per-channel stats tables (waveform + histogram variants) and the mic block's ZC Freq row. Verified on the real T190LD5Q.LK0W fixture: Tran zc_freq_hz=100.0 above_range=True; Vert/Long (normal values) above_range=False; "N/A" still produces zc_freq_hz=None which renders as "—" (unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,11 @@ class ChannelStats:
|
||||
# to render "> 10 in/s" or "saturated" instead of trusting the
|
||||
# value as an exact measurement.
|
||||
ppv_saturated: bool = False
|
||||
# Set when BW writes ">100 Hz" for ZC Freq — the zero-crossing
|
||||
# algorithm's peak frequency exceeded the device's reporting
|
||||
# ceiling (typically 100 Hz on V10.72). zc_freq_hz gets the
|
||||
# threshold (100.0) as a lower bound; downstream UI renders ">100".
|
||||
zc_freq_above_range: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -81,6 +86,9 @@ class MicStats:
|
||||
# 140 dBL (typical NL-43 max; some units cap at 148). Consumers
|
||||
# should render "> 140 dB(L)" or similar when this flag is set.
|
||||
pspl_saturated: bool = False
|
||||
# Same semantics as ChannelStats.zc_freq_above_range — mic ZC
|
||||
# peak exceeded device reporting ceiling.
|
||||
zc_freq_above_range: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -119,6 +127,20 @@ def _is_oorange(value: str) -> bool:
|
||||
return any(m in s for m in _OORANGE_MARKERS)
|
||||
|
||||
|
||||
def _parse_above_range(value: str) -> Optional[float]:
|
||||
"""For BW "above-range" markers like ">100 Hz", return the threshold.
|
||||
|
||||
BW writes ZC Freq as ">100 Hz" when the zero-crossing algorithm sees
|
||||
a peak too fast to count (device cuts off at 100 Hz). Returns the
|
||||
numeric portion after the '>' (e.g. 100.0), or None if `value` is
|
||||
not an above-range marker.
|
||||
"""
|
||||
s = value.strip()
|
||||
if not s.startswith(">"):
|
||||
return None
|
||||
return _parse_number(s[1:])
|
||||
|
||||
|
||||
@dataclass
|
||||
class BwAsciiReport:
|
||||
"""Structured representation of one BW per-event ASCII export."""
|
||||
@@ -527,10 +549,17 @@ def parse_report(text: Union[str, bytes], *, parse_samples: bool = False) -> BwA
|
||||
cs.ppv_saturated = True
|
||||
else:
|
||||
cs.ppv_ips = _parse_number(value)
|
||||
elif stat == "ZC Freq":
|
||||
# ">100 Hz" → store threshold + flag; numeric → parse normally
|
||||
threshold = _parse_above_range(value)
|
||||
if threshold is not None:
|
||||
cs.zc_freq_hz = threshold
|
||||
cs.zc_freq_above_range = True
|
||||
else:
|
||||
cs.zc_freq_hz = _parse_number(value)
|
||||
else:
|
||||
num = _parse_number(value)
|
||||
if stat == "ZC Freq": cs.zc_freq_hz = num
|
||||
elif stat == "Time of Peak": cs.time_of_peak_s = num
|
||||
if stat == "Time of Peak": cs.time_of_peak_s = num
|
||||
elif stat == "Peak Acceleration": cs.peak_accel_g = num
|
||||
elif stat == "Peak Displacement": cs.peak_disp_in = num
|
||||
|
||||
@@ -627,9 +656,15 @@ def parse_report(text: Union[str, bytes], *, parse_samples: bool = False) -> BwA
|
||||
cs = report.channels.setdefault("MicL", ChannelStats())
|
||||
cs.time_of_peak_s = report.mic.time_of_peak_s
|
||||
elif key == "MicL ZC Freq":
|
||||
report.mic.zc_freq_hz = _parse_number(value)
|
||||
threshold = _parse_above_range(value)
|
||||
if threshold is not None:
|
||||
report.mic.zc_freq_hz = threshold
|
||||
report.mic.zc_freq_above_range = True
|
||||
else:
|
||||
report.mic.zc_freq_hz = _parse_number(value)
|
||||
cs = report.channels.setdefault("MicL", ChannelStats())
|
||||
cs.zc_freq_hz = report.mic.zc_freq_hz
|
||||
cs.zc_freq_hz = report.mic.zc_freq_hz
|
||||
cs.zc_freq_above_range = report.mic.zc_freq_above_range
|
||||
|
||||
# ── Sensor self-check ────────────────────────────────────────────────
|
||||
elif key in (
|
||||
|
||||
Reference in New Issue
Block a user