bw_ascii_report: parse OORANGE saturation marker + TimeSum typo
BW writes "OORANGE" (truncation of "Out Of Range") when a channel exceeds its full-scale, and uses a typo'd label "Peak Vector Sum TimeSum" for the PVS time field. Both confirmed against real ASCII files pulled from a Windows watcher PC 2026-05-27: T190LD5Q.LK0W Vert PPV = OORANGE (Normal range, 10 in/s exceeded) T438L713.RY0W All three PPVs OORANGE (Sensitive range, 1.25 in/s) K557L3YM.OE0W Tran+Vert PPV OORANGE + MicL PSPL OORANGE Previously our _parse_number() returned None for OORANGE → DB columns ended up NULL → events vanished from filters / sorts / dashboards despite being legitimate high-amplitude events. New behavior — substitute a conservative bound + set a saturation flag: - Channel PPV → geo_range_ips + ChannelStats.ppv_saturated - Peak Vector Sum → sqrt(3) * geo_range_ips + peak_vector_sum_saturated - MicL PSPL → 140 dB(L) + MicStats.pspl_saturated Flags propagate to the sidecar's bw_report block so the SFM UI can render "> 10 in/s" / "> 140 dBL" rather than treating the substituted value as exact. Same commit also accepts "Peak Vector Sum TimeSum" as an alias for "Peak Vector Sum Time" (BW always writes the typo on OORANGE PVS lines — every example file confirms it). Tests: new test_oorange_marker_treated_as_saturation (synthetic) + test_real_oorange_event_t190_parses (skips if real fixture absent). 177/177 tests pass; 16 pre-existing missing-fixture skips unchanged. Five events on prod (T190, T438, K557, plus 2 others matching the same fault pattern) will pick up correct peaks + saturation flags once watchers re-forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -385,6 +385,64 @@ def test_user_notes_extra_lines_beyond_four_are_dropped():
|
||||
assert "L5" not in r.user_note_labels.values()
|
||||
|
||||
|
||||
def test_oorange_marker_treated_as_saturation():
|
||||
"""BW writes 'OORANGE' (Out Of Range — truncated) when a channel
|
||||
exceeds its full-scale. Verify ppv_ips falls back to geo_range_ips
|
||||
+ saturated flag is set, mirroring the real T190LD5Q.LK0W,
|
||||
T438L713.RY0W, and K557L3YM.OE0W events from prod 2026-05-27.
|
||||
"""
|
||||
txt = """\
|
||||
"Event Type : Full Waveform"
|
||||
"Serial Number : BE18190"
|
||||
"Geo Range : 10.000 in/s"
|
||||
"Tran PPV : 2.140 in/s"
|
||||
"Vert PPV : OORANGE in/s"
|
||||
"Long PPV : 2.830 in/s"
|
||||
"Peak Vector Sum : OORANGE in/s"
|
||||
"Peak Vector Sum TimeSum : 0.007 s"
|
||||
"MicL PSPL : OORANGE "
|
||||
"""
|
||||
r = parse_report(txt)
|
||||
# Tran/Long parse normally
|
||||
assert r.channels["Tran"].ppv_ips == 2.14
|
||||
assert r.channels["Tran"].ppv_saturated is False
|
||||
assert r.channels["Long"].ppv_ips == 2.83
|
||||
# Vert saturated → range max + flag
|
||||
assert r.channels["Vert"].ppv_ips == 10.0
|
||||
assert r.channels["Vert"].ppv_saturated is True
|
||||
# PVS saturated → sqrt(3) * range_max as upper bound + flag
|
||||
import math
|
||||
assert r.peak_vector_sum_ips == pytest.approx(math.sqrt(3) * 10.0)
|
||||
assert r.peak_vector_sum_saturated is True
|
||||
# Mic saturated → 140 dBL conservative upper bound + flag
|
||||
assert r.mic.pspl_dbl == 140.0
|
||||
assert r.mic.pspl_saturated is True
|
||||
# PVS time still parses despite the BW typo'd label "TimeSum"
|
||||
assert r.peak_vector_sum_time_s == pytest.approx(0.007)
|
||||
|
||||
|
||||
def test_real_oorange_event_t190_parses():
|
||||
"""End-to-end against the real T190LD5Q.LK0W ASCII file pulled from
|
||||
a Windows watcher PC on 2026-05-27. This is the canonical example
|
||||
of the parser-PPV-miss bug we fixed in this iteration."""
|
||||
fixture_path = (
|
||||
Path(__file__).parent.parent / "example-events" /
|
||||
"ascii-5-27-26" / "T190LD5Q_LK0W_ASCII.TXT"
|
||||
)
|
||||
if not fixture_path.exists():
|
||||
pytest.skip("real ASCII fixture not present (local-only)")
|
||||
r = parse_report_file(fixture_path)
|
||||
assert r.serial == "BE18190"
|
||||
assert r.geo_range_ips == 10.0
|
||||
# Tran reads cleanly, Vert was OORANGE
|
||||
assert r.channels["Tran"].ppv_ips == pytest.approx(2.14)
|
||||
assert r.channels["Vert"].ppv_ips == 10.0
|
||||
assert r.channels["Vert"].ppv_saturated is True
|
||||
assert r.channels["Long"].ppv_ips == pytest.approx(2.83)
|
||||
assert r.peak_vector_sum_saturated is True
|
||||
assert r.peak_vector_sum_time_s == pytest.approx(0.007)
|
||||
|
||||
|
||||
def test_real_histogram_fixture_populates_sensor_location():
|
||||
"""End-to-end: the histogram fixture uses 'Seis. Location:' — must
|
||||
successfully populate sensor_location via position-based parsing."""
|
||||
|
||||
Reference in New Issue
Block a user