fix(idf_waveforms): _find_waveform_body_offset() — scans every 00 02 00 magic past offset 0x0E00, runs decode_waveform_v2 on each candidate, picks the one that returns the most samples. Validated on 483 prod IDFW files: 0 preamble-only events (was ~50%), 355/483 fully decode, 126/483 partial (BW codec walker-stops-early on loud events — known issue).
IDFH now synthesises a 1-sample-per-interval array from the binary intervals and writes an .h5 so the existing renderer works unchanged. Each "sample" is the per-interval peak ADC count → h5_value = count × geo_fs/32768 yields the right bar height.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""Re-ingest a prod IDFW + IDFH via the patched save_imported_idf and
|
||||
render both PDFs to confirm charts have data."""
|
||||
from __future__ import annotations
|
||||
import sys
|
||||
import json
|
||||
import datetime
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from sfm.waveform_store import WaveformStore
|
||||
from sfm import report_pdf
|
||||
import h5py
|
||||
|
||||
|
||||
class FakeDb:
|
||||
def __init__(self, event):
|
||||
self.event = event
|
||||
def get_event(self, _id):
|
||||
return self.event
|
||||
|
||||
|
||||
def to_ts_iso(ts):
|
||||
if ts is None:
|
||||
return None
|
||||
try:
|
||||
return datetime.datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second).isoformat()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def render_case(idf_path: Path, serial: str, out_pdf: Path, h5_summary: bool = True):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
store = WaveformStore(Path(td))
|
||||
ev, rec = store.save_imported_idf(
|
||||
idf_path.read_bytes(),
|
||||
idf_path,
|
||||
idf_report_text=None, # production worst case: no .txt
|
||||
)
|
||||
print(f"=== {idf_path.name} ===")
|
||||
print(f" h5: {rec['hdf5_filename']}, sidecar: {rec['sidecar_filename']}")
|
||||
|
||||
h5p = Path(td) / serial / f"{idf_path.name}.h5"
|
||||
if h5p.exists() and h5_summary:
|
||||
with h5py.File(h5p) as h:
|
||||
for ch in ("Tran", "Vert", "Long", "MicL"):
|
||||
ds = h.get(f"samples/{ch}")
|
||||
if ds is not None:
|
||||
n = ds.shape[0]
|
||||
mx = float(abs(ds[...]).max()) if n else 0
|
||||
print(f" samples/{ch}: n={n} max_abs={mx:.5f}")
|
||||
|
||||
record_type = "Histogram" if idf_path.suffix.upper() == ".IDFH" else "Waveform"
|
||||
fake_row = {
|
||||
"serial": serial,
|
||||
"blastware_filename": rec["filename"],
|
||||
"record_type": record_type,
|
||||
"timestamp": to_ts_iso(ev.timestamp),
|
||||
"sample_rate": ev.sample_rate,
|
||||
"project": ev.project_info.project if ev.project_info else None,
|
||||
"client": ev.project_info.client if ev.project_info else None,
|
||||
"operator": ev.project_info.operator if ev.project_info else None,
|
||||
"sensor_location": ev.project_info.sensor_location if ev.project_info else None,
|
||||
"created_at": None,
|
||||
}
|
||||
rd = report_pdf.gather_report_data(FakeDb(fake_row), store, event_id="test-1")
|
||||
print(f" ReportData: channels={ {k: len(v) for k,v in rd.channels.items()} }")
|
||||
if rd.is_histogram:
|
||||
print(f" histogram n_intervals={rd.histogram_n_intervals} interval_size={rd.histogram_interval_size}")
|
||||
pdf = report_pdf.render_event_report_pdf(rd)
|
||||
out_pdf.write_bytes(pdf)
|
||||
print(f" PDF: {out_pdf} ({len(pdf)} bytes)")
|
||||
|
||||
|
||||
def main():
|
||||
out_dir = Path("/tmp/thor_render_test"); out_dir.mkdir(exist_ok=True)
|
||||
cases = [
|
||||
# IDFW that decoded to preamble-only under the old codec
|
||||
("/home/serversdown/seismo-relay-prod-snap/waveforms/UM6047/UM6047_20250804154137.IDFW", "UM6047"),
|
||||
# IDFW that worked under the old codec (validates no regression)
|
||||
("/home/serversdown/seismo-relay-prod-snap/waveforms/UM6047/UM6047_20250804104450.IDFW", "UM6047"),
|
||||
# IDFH histogram
|
||||
("/home/serversdown/seismo-relay-prod-snap/waveforms/UM6047/UM6047_20250804190047.IDFH", "UM6047"),
|
||||
]
|
||||
for path, serial in cases:
|
||||
render_case(Path(path), serial, out_dir / f"{Path(path).name}.pdf")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user