B.11: LUT-кривая уровень->маска извлечена (rmse 0.072 dB, 36 точек, 3 уровня): коллапс dual+t1kq+t1k на ОДНУ кривую x=log10(L0/res); степенной закон C~L0^p НЕ работает на 0dB (p_eff 0.19 vs 0.085) - LUT насыщающая gamma из FUN_180563440 (param_1+0x188); форма резонанса стабильна; model_lut.py канонический + roadmap B.11
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""verify_lut4.py — тонкая подгонка узлов LUT под все 36 точек."""
|
||||
import numpy as np
|
||||
from scipy.interpolate import PchipInterpolator
|
||||
from scipy.optimize import least_squares
|
||||
|
||||
FS = 44100.0
|
||||
DEPTH = 0.8639736175537109
|
||||
QS = [0.1, 0.2, 0.3, 0.5, 0.7, 1.0, 1.5, 2.0, 3.0, 5.0, 10.0]
|
||||
DUAL = np.array([(10.220, 15.224), (10.219, 13.963), (10.219, 12.947),
|
||||
(10.219, 11.725), (10.218, 11.119), (10.216, 10.689),
|
||||
(10.210, 10.412), (10.203, 10.305), (10.182, 10.225),
|
||||
(10.113, 10.183), (9.822, 10.165)])
|
||||
FCS = [800.0, 900.0, 950.0, 1000.0, 1050.0, 1100.0, 1200.0]
|
||||
T1KQ = np.array([7.868, 8.536, 8.726, 8.788, 8.729, 8.575, 8.115])
|
||||
T1K = np.array([14.548, 15.332, 15.553, 15.626, 15.557, 15.378, 14.840])
|
||||
L_DUAL = 10 ** (-7.142 / 20)
|
||||
L_T1KQ = 10 ** (-18.063 / 20)
|
||||
L_T1K = 1.0
|
||||
TILT = {500: 1.414, 1000: 1.454, 2000: 1.795}
|
||||
Q, G = 0.900, 4.132
|
||||
|
||||
LUTX = np.array([-0.75, -0.50, -0.25, 0.00, 0.25, 0.50, 0.574, 0.61, 0.75, 1.00])
|
||||
LUTY0 = np.array([0.4453, 0.4551, 0.4784, 0.5041, 0.5331, 0.5715, 0.5660, 0.6470, 0.6574, 0.6636])
|
||||
|
||||
|
||||
def res_at(ft, fc, Q, g):
|
||||
w0 = fc * 2 * np.pi / FS
|
||||
c, s = np.cos(w0), np.sin(w0)
|
||||
p = (s * 0.5) / Q
|
||||
a, a2 = p * g, p / g
|
||||
A = [a + 1, -2 * c, 1 - a]
|
||||
B = [a2 + 1, -2 * c, 1 - a2]
|
||||
w = 2 * np.pi * ft / FS
|
||||
z = np.exp(-1j * w)
|
||||
return np.abs(2.0 * (B[0] + B[1] * z + B[2] * z * z) /
|
||||
(A[0] + A[1] * z + A[2] * z * z))
|
||||
|
||||
|
||||
def build_data():
|
||||
xs, meas = [], []
|
||||
for i, q in enumerate(QS):
|
||||
for f, m in ((500, DUAL[i, 0]), (2000, DUAL[i, 1])):
|
||||
xs.append(np.log10(L_DUAL / res_at(f, 500, q, G)))
|
||||
meas.append(m)
|
||||
for i, fc in enumerate(FCS):
|
||||
r = res_at(1000, fc, 0.9999978, G)
|
||||
xs.append(np.log10(L_T1KQ / r)); meas.append(T1KQ[i])
|
||||
xs.append(np.log10(L_T1K / r)); meas.append(T1K[i])
|
||||
return np.array(xs), np.array(meas)
|
||||
|
||||
|
||||
XS, MEAS = build_data()
|
||||
TILTS = np.array([1.414 if x < 0.35 else (1.795 if x < -0.05 else 1.454) for x in XS])
|
||||
# уточнение: tilt по тегу. пересоберём аккуратно
|
||||
def tags():
|
||||
ts = []
|
||||
for i, q in enumerate(QS):
|
||||
for f in (500.0, 2000.0):
|
||||
ts.append(TILT[f])
|
||||
for _ in range(7):
|
||||
ts.append(TILT[1000]); ts.append(TILT[1000])
|
||||
return np.array(ts)
|
||||
TT = tags()
|
||||
|
||||
|
||||
def model(ly):
|
||||
lut = PchipInterpolator(LUTX, ly)
|
||||
out = []
|
||||
for x, tilt in zip(XS, TT):
|
||||
C = DEPTH * tilt * lut(x)
|
||||
out.append(-20 * np.log10(max(1 - C, 1e-9)))
|
||||
return np.array(out)
|
||||
|
||||
|
||||
def run():
|
||||
r = least_squares(lambda ly: model(ly) - MEAS, LUTY0, max_nfev=30000, xtol=1e-13, ftol=1e-13)
|
||||
ly = r.x
|
||||
pred = model(ly)
|
||||
rmse = np.sqrt(np.mean((pred - MEAS) ** 2))
|
||||
print(f'LUT-knot fit rmse={rmse:.4f} dB')
|
||||
for i, (x, m, p, t) in enumerate(zip(XS, MEAS, pred, TT)):
|
||||
if abs(p - m) > 0.1:
|
||||
print(f' x={x:+.3f} tilt={t:.3f} {m:7.3f}/{p:7.3f} ({p - m:+.3f})')
|
||||
print('knots:')
|
||||
for xx, yy in zip(LUTX, ly):
|
||||
print(f' ({xx:+.3f}, {yy:.4f}),')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
Reference in New Issue
Block a user