"""USBM/OSMRE compliance curve + scatter logic (sfm.compliance). Rendering is verified visually against Blastware reports.""" import math import numpy as np from sfm.compliance import limit_at, channel_compliance_points def test_osmre_velocity_segments(): assert abs(limit_at(6.0) - 0.75) < 1e-9 # 3.5–12 Hz flat assert abs(limit_at(50.0) - 2.00) < 1e-9 # 30–100 Hz flat def test_displacement_segments(): assert abs(limit_at(2.0) - 2 * math.pi * 2.0 * 0.030) < 1e-9 # low-freq 0.030 in assert abs(limit_at(20.0) - 2 * math.pi * 20.0 * 0.008) < 1e-9 # rising diagonal 0.008 in def test_limit_clamps_below_1hz(): assert limit_at(0.1) == limit_at(1.0) def test_scatter_ceiling_is_ppv_at_dominant_freq(): # ~27 Hz blast-like trace whose energy peaks mid-record (inside full cycles, # as a real event does): the scatter cloud's ceiling is the trace PPV and the # top point sits near the dominant frequency. sps, n = 1024.0, 3328 t = np.arange(n) / sps env = np.exp(-((t - 1.5) ** 2) / (2 * 0.3 ** 2)) x = 0.9 * env * np.sin(2 * np.pi * 27.0 * t) f, v = channel_compliance_points(x, sps) assert len(f) > 20 assert v.max() >= 0.99 * np.abs(x).max() assert 20.0 < f[int(np.argmax(v))] < 35.0