113 lines
4.5 KiB
Python
113 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""fir_probe.py — численная реплика FIR-цепи (Этап A3) против захватов.
|
|
|
|
Структура по дизасму (BLOCKMAP 24mm6/24mm8 + wrap/worker декод этого раунда):
|
|
copy: FIR[2j]=scr[j], FIR[2j+1]=0 (18004d900, 2049 пар)
|
|
opA: th2180 = INVERSE real-FFT (план buf548, N=4096, scale 1/4096)
|
|
scale: float[1..2047] *= 2.0 ; float[2049..4095] = 0 (52d920/52db50)
|
|
opB: th1a90 = FORWARD real-FFT
|
|
EXP: expf in-place по первым 2049 ФЛОАТАМ (140b30, 52b708-716)
|
|
opC: th2180 = INVERSE
|
|
window: float[0..2047] *= WINfreq[2048..4095] (52d990, падающий Hann)
|
|
float[2048..4095] = 0 (52db50)
|
|
opD: th1a90 = FORWARD
|
|
fix: FIR[0]=1.0, FIR[1]=0 (52b7cd-e1)
|
|
df0: track_i := track_i ⊗ FIR (комплексное умножение, 18000b3c0)
|
|
Цель: воспроизвести cur@678 из trk@688 без свободных параметров.
|
|
"""
|
|
import numpy as np
|
|
import glob
|
|
import os
|
|
import sys
|
|
|
|
NFLOAT = 4098 # 2049 пар
|
|
NBINS = 2049 # n/2+1, n=[ctx+0x540534]=4096
|
|
|
|
|
|
def load_frame(npz):
|
|
d = np.load(npz)
|
|
S = {}
|
|
for k in d.keys():
|
|
if k.startswith('0x'):
|
|
S[int(k[2:], 16)] = d[k]
|
|
return S
|
|
|
|
|
|
def win_periodic_hann(N):
|
|
return 0.5 * (1.0 - np.cos(2.0 * np.pi * np.arange(N) / N)).astype(np.float64)
|
|
|
|
|
|
def fir_chain(scr, winfall, variant='flat'):
|
|
"""scr: 2049 float (log-домен). Возвращает halfcomplex-спектр ядра F[2049]."""
|
|
# copy/pack: пары (re=scr, im=0) -> inverse rfft вход (numpy: complex[2049])
|
|
H = scr.astype(np.float64).astype(np.complex128)
|
|
# opA: inverse real FFT, нормировка 1/N (план scale=2^-12 при активном флаге)
|
|
y = np.fft.irfft(H, n=4096) # уже содержит деление на 4096
|
|
# scale/zero по asm: float[1..2047]*=2, float[2049..]=0 (f[2048] не трогаем)
|
|
y[1:2048] *= 2.0
|
|
y[2049:] = 0.0
|
|
# opB: forward
|
|
Y = np.fft.rfft(y, n=4096) # complex[2049]
|
|
# EXP по первым 2049 флоатам плоского массива
|
|
flat = np.empty(NFLOAT)
|
|
flat[0::2] = Y.real
|
|
flat[1::2] = Y.imag
|
|
if variant == 'flat':
|
|
flat[:2049] = np.exp(flat[:2049])
|
|
elif variant == 'cplx':
|
|
Y = np.exp(Y.astype(np.complex128))
|
|
flat[0::2] = Y.real
|
|
flat[1::2] = Y.imag
|
|
Y2 = flat[0::2] + 1j * flat[1::2]
|
|
# opC: inverse
|
|
w = np.fft.irfft(Y2, n=4096)
|
|
# window: float[0..2047] *= падающая половина; хвост = 0
|
|
w[:2048] *= winfall
|
|
w[2048:] = 0.0
|
|
# opD: forward
|
|
F = np.fft.rfft(w, n=4096)
|
|
# fix: FIR[0]=1.0, FIR[1]=0
|
|
F[0] = 1.0 + 0.0j
|
|
return F
|
|
|
|
|
|
def evaluate(ds, ph_file, verbose=True):
|
|
S = load_frame(os.path.join(ds, ph_file))
|
|
scr = S[0x540628][:NBINS].astype(np.float64)
|
|
trk = S[0x540688][:NBINS].astype(np.float64)
|
|
cur = S[0x540678][:NBINS].astype(np.float64)
|
|
# проверка trk == exp(scr)
|
|
m_ok = trk > 1e-30
|
|
err_trk = np.abs(np.log(trk[m_ok]) - scr[m_ok]).max()
|
|
# фит gamma
|
|
sel = np.abs(scr) > 0.05
|
|
g = float(np.sum(scr[sel] * np.log(cur[sel])) / np.sum(scr[sel] ** 2))
|
|
rms_fit = float(np.sqrt(np.mean((np.log(cur[sel]) - g * scr[sel]) ** 2)))
|
|
winfall = win_periodic_hann(4096)[2048:]
|
|
out = []
|
|
for variant in ('flat', 'cplx'):
|
|
F = fir_chain(scr, winfall, variant)
|
|
# маска = track ⊗ F (df0), берём реальную часть как применённую маску
|
|
mask_sim = np.abs(trk * F) if variant == 'cplx' else trk * F.real
|
|
mm = (cur > 1e-6) & np.isfinite(mask_sim)
|
|
e_db = 20.0 / np.log(10) * np.log(np.abs(mask_sim[mm])) - \
|
|
20.0 / np.log(10) * np.log(cur[mm])
|
|
rms_db = float(np.sqrt(np.mean(e_db ** 2)))
|
|
out.append((variant, rms_db, int(mm.sum())))
|
|
if verbose:
|
|
print(f'{ph_file} [{variant}] gamma_fit={g:.6f} (rms {rms_fit:.1e}) '
|
|
f'trk_err={err_trk:.2e} MASK rms={rms_db:.4f} дБ / {mm.sum()} бинов')
|
|
return out
|
|
|
|
|
|
if __name__ == '__main__':
|
|
jobs = [
|
|
('/tmp/opencode/sc_multi4b', 'ph073.npz'),
|
|
('/tmp/opencode/sc_multi6', 'ph037.npz'),
|
|
('/tmp/opencode/sc_multi6', 'ph034.npz'),
|
|
]
|
|
if len(sys.argv) > 1:
|
|
jobs = [(os.path.dirname(sys.argv[1]), os.path.basename(sys.argv[1]))]
|
|
for ds, ph in jobs:
|
|
evaluate(ds, ph)
|