diff --git a/handoff/NOTES_LEVEL.md b/handoff/NOTES_LEVEL.md index 057cc18..dce5eff 100644 --- a/handoff/NOTES_LEVEL.md +++ b/handoff/NOTES_LEVEL.md @@ -3832,3 +3832,23 @@ multi6 = идеальный валидатор каскадного симуля шесть пиков с разными (am,res) комбинациями, самосогласованные B=exp(scratch). После оп-за-оп транскрипции симулятор ОБЯЗАН воспроизвести эти пять глубин из am/res тракта — это и есть критерий bit-exact детектора. + +## ============ ДОПОЛНЕНИЕ 24ll3: пер-биновая парадигма am/res ОТВЕРГНУТА для внецентровых пиков ============ + +### Единый фит cut=α·ln1p((am·s/res^p)/β)+c по 28 точкам (multi6+tt): +параметр p упёрся в НИЖНЮЮ границу 0.05 (хотел БЕЗ зависимости от res), +rms 2.28 дБ. Разрешение отрицательных p не спасает. +⇒ **уровень внецентрового пика НЕ выражается через am/res в любой степени**. +Первые четыре тона multi6 имеют ОДИНАКОВЫЙ am≈0.21, но каты 2.33→7.18 +растут монотонно с res ⇒ детекторный уровень зависит от res ПОЛОЖИТЕЛЬНО, +отдельно от am — двухаргументная структура или каскадная связь. + +### Что это значит +Наш lvl_raw=am/res·scale как вход закона правилен ТОЛЬКО у центра полосы. +Для остальных позиций нужна либо (а) полная каскадная симуляция шагов 9–19, +либо (б) эмпирическая поверхность cut(am,res,pos) по датасетам кампании. +Оба пути определены; (а) = bit-exact маршрут. + +### Датасет multi6 — главный актив следующего раунда +6 нотчей один прогон, самосогласованные B/am/res тройки + полный спектр +am(f)/res(f) в tract_multi6.txt — идеальная мишень для валидации симулятора. diff --git a/scripts/unify.py b/scripts/unify.py new file mode 100644 index 0000000..1d23519 --- /dev/null +++ b/scripts/unify.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""unify.py — global unified-law fit: cut = alpha*ln1p(am*s/res^p / beta)+c +across multi6 (6 peaks) + tt-series (8 points, both peaks).""" +import numpy as np, glob, wave +from scipy.optimize import least_squares + +def deepest(dirname,b): + best=None + for fn in sorted(glob.glob(dirname+'/ph*.npz')): + d=np.load(fn) + if '0x540628' not in d.files: continue + s=d['0x540628'].astype(np.float64) + if best is None or s[b]=0x800000,s-0x1000000,s).astype(np.float64).reshape(-1,ch).mean(1)/8388608 + +def ta(x,f,sr=44100,L=None): + x=x[-L:]; t=np.arange(len(x))/sr; c=np.cos(2*np.pi*f*t); sn=np.sin(2*np.pi*f*t) + return np.hypot(2*(x*c).sum(),2*(x*sn).sum())/len(x) + +pts=[] +# multi6 six peaks +S=deepest('/tmp/opencode/sc_multi6',85) +cut=np.maximum(-S*8.685889638,0) +am=np.zeros(len(S)); rs=np.zeros(len(S)) +for ln in open('/tmp/opencode/tract_multi6.txt'): + if ln.startswith('#'): continue + p=ln.split(); kk=int(p[0]) + if kk1.0 and am[kk]>0.01: + pts.append(('m6',am[kk],rs[kk],cut[kk])) +# tt-series 4 drives x 2 peaks (ref-domain cuts via Goertzel) +inp0=loadwav('/tmp/opencode/tt0_in.wav') +rc={0:(10.34,11.84),6:(8.12,9.57),12:(6.12,7.45),18:(4.40,5.55)} +for att,(c1v,c2v) in rc.items(): + tf='/tmp/opencode/tract_tt%d.txt'%att + d43=d171=None; r43=r171=None + for ln in open(tf): + if ln.startswith('#'): continue + p=ln.split(); kk=int(p[0]) + if kk==43: r43=float(p[2]); d43=am0=None; d43=float(p[1]) + if kk==171: r171=float(p[2]); d171=float(p[1]) + pts.append(('tt%d-pk1'%att,d43,r43,c1v)) + pts.append(('tt%d-pk2'%att,d171,r171,c2v)) +print('points:',len(pts)) + +def resid(p): + al,be,cc,pp,ss=p + out=[] + for nm,a,r,c in pts: + X=a*ss/max(r**pp,1e-12) + out.append(al*np.log1p(X/be)+cc-c) + return np.array(out) + +p0=[3.28,0.56,0.77,1.25,1.0] +lb=[0.5,0.01,-5,0.05,0.05] +ub=[8,5,5,4,50] +r=least_squares(resid,p0,bounds=(lb,ub),max_nfev=8000) +mm=-resid(r.x)+np.array([c for _,_,_,c in pts]) +tgt=np.array([c for _,_,_,c in pts]) +rms=np.sqrt(((mm-tgt)**2).mean()) +print('UNIFIED: alpha=%.3f beta=%.4f c=%+.3f p=%.3f s=%.3f ; rms=%.4f max=%.3f' % ( + *r.x,rms,np.abs(mm-tgt).max())) +for (nm,a,rr,c),pv in zip(pts,mm): + print(' %-10s am=%.4f res=%.4f cut=%6.2f pred=%6.2f err=%+.2f' % (nm,a,rr,c,pv,c-pv))