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:
2026-08-18 13:02:56 +03:00
parent 9abecb60d5
commit 32a63955f8
13 changed files with 1021 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""fit_lut2.py — логистическая LUT (B.11): C_norm = mn + (mx-mn)/(1+exp(-(x-x0)/w)),
x = log10(L0/res). Коллапс всех 36 точек на одну кривую."""
import numpy as np
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}
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 lut(x, mn, mx, x0, w):
return mn + (mx - mn) / (1.0 + np.exp(-(x - x0) / w))
def model(p):
Q, g, mn, mx, x0, w = p
out = []
for q in QS:
for f in (500.0, 2000.0):
r = res_at(f, 500, q, g)
C = DEPTH * TILT[f] * lut(np.log10(L_DUAL / r), mn, mx, x0, w)
out.append(-20 * np.log10(max(1 - C, 1e-9)))
for i, fc in enumerate(FCS):
r = res_at(1000, fc, 0.9999978, g)
C = DEPTH * TILT[1000] * lut(np.log10(L_T1KQ / r), mn, mx, x0, w)
out.append(-20 * np.log10(max(1 - C, 1e-9)))
C = DEPTH * TILT[1000] * lut(np.log10(L_T1K / r), mn, mx, x0, w)
out.append(-20 * np.log10(max(1 - C, 1e-9)))
return np.array(out)
def run():
meas = np.array(list(DUAL.ravel()) + list(T1KQ) + list(T1K))
x0 = [0.9, 4.13, 0.0, 1.0, 0.5, 0.4]
r = least_squares(lambda p: model(p) - meas, x0,
bounds=([0.1, 0.5, 0.0, 0.4, -1.0, 0.01],
[5, 12, 0.5, 5.0, 3.0, 5.0]),
max_nfev=30000, xtol=1e-12, ftol=1e-12)
Q, g, mn, mx, x0, w = r.x
rmse = np.sqrt(np.mean((model(r.x) - meas) ** 2))
print(f'LOGISTIC LUT-FIT rmse={rmse:.4f} dB Q={Q:.3f} gain={g:.3f}')
print(f'LUT: mn={mn:.3f} mx={mx:.3f} x0={x0:.3f} w={w:.3f}')
pred = model(r.x)
print('--- dual_b1q ---')
for i, q in enumerate(QS):
print(f'q={q:5.1f} {DUAL[i,0]:7.3f}/{pred[2*i]:7.3f} '
f'{DUAL[i,1]:7.3f}/{pred[2*i+1]:7.3f}')
print('--- t1kq -18dB ---')
for i, fc in enumerate(FCS):
print(f'fc={fc:5.0f} {T1KQ[i]:6.3f}/{pred[22+i]:6.3f}')
print('--- t1k 0dB ---')
for i, fc in enumerate(FCS):
print(f'fc={fc:5.0f} {T1K[i]:6.3f}/{pred[29+i]:6.3f}')
if __name__ == '__main__':
run()