87 lines
3.4 KiB
Python
87 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
||
"""fit_level.py — расширенная B.10: три уровня входа (0, -7, -18 dBFS).
|
||
|
||
Проверяем, что резонансная форма |2B/A| одна для всех уровней, а глубина
|
||
масштабируется законом C(dB). Вопрос: степенной ли закон C ~ L0^p или
|
||
экспоненциальный по dB C ~ 10^(dB/k) с k=dB-независимым?
|
||
"""
|
||
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]
|
||
# измеренные (компонент-корреляция 1к, надёжно):
|
||
T1KQ = np.array([7.868, 8.536, 8.726, 8.788, 8.729, 8.575, 8.115]) # -18.06 dB
|
||
T1K = np.array([14.548, 15.332, 15.553, 15.626, 15.557, 15.378, 14.840]) # 0 dB
|
||
L_DUAL = 10 ** (-7.142 / 20)
|
||
L_T1KQ = 10 ** (-18.063 / 20)
|
||
L_T1K = 1.0
|
||
|
||
|
||
def res_at(f_tone, fc, Q, gain):
|
||
w0 = fc * 2 * np.pi / FS
|
||
c, s = np.cos(w0), np.sin(w0)
|
||
p = (s * 0.5) / Q
|
||
a, a2 = p * gain, p / gain
|
||
A = [a + 1, -2 * c, 1 - a]
|
||
B = [a2 + 1, -2 * c, 1 - a2]
|
||
w = 2 * np.pi * f_tone / 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 model(p):
|
||
Q, gain, p0, p1, D0, t1000 = p # p(dB) = p0 + p1*dB
|
||
out = []
|
||
for q in QS:
|
||
for f in (500.0, 2000.0):
|
||
r = res_at(f, 500, q, gain)
|
||
tilt = t1000 if f == 2000 else 1.45
|
||
c = DEPTH * tilt * D0 * (L_DUAL / r) ** (p0 + p1 * (-7.142))
|
||
out.append(-20 * np.log10(max(1 - c, 1e-9)))
|
||
for i, fc in enumerate(FCS):
|
||
r = res_at(1000, fc, 0.9999978, gain)
|
||
c = DEPTH * t1000 * D0 * (L_T1KQ / r) ** (p0 + p1 * (-18.063))
|
||
out.append(-20 * np.log10(max(1 - c, 1e-9)))
|
||
c = DEPTH * t1000 * D0 * (L_T1K / r) ** p0
|
||
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.24, 0.085, 0.003, 0.5, 1.454]
|
||
r = least_squares(lambda p: model(p) - meas, x0,
|
||
bounds=([0.1, 0.5, 0.0, -0.01, 0.01, 0.3],
|
||
[5, 12, 0.3, 0.02, 5, 5]),
|
||
max_nfev=10000, xtol=1e-12, ftol=1e-12)
|
||
Q, gain, p0, p1, D0, t1000 = r.x
|
||
rmse = np.sqrt(np.mean((model(r.x) - meas) ** 2))
|
||
print(f'FIT rmse={rmse:.4f} dB Q={Q:.3f} gain={gain:.3f} '
|
||
f'p(dB)={p0:.4f}{p1:+.4f}*dB D0={D0:.3f} t1000={t1000:.3f}')
|
||
print('p при 0/-7/-18 dB:', p0, p0+p1*-7.142, p0+p1*-18.063)
|
||
pred = model(r.x)
|
||
n = 0
|
||
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()
|