arccos to apply

This commit is contained in:
awe
2026-02-26 14:00:56 +03:00
parent f1652d072e
commit 00323af0f0
9 changed files with 472 additions and 74 deletions

View File

@ -227,7 +227,7 @@ class AppState:
bg_stage = "bg[sub]" if self.background is not None else "bg[missing]"
return (
f"pipeline ch{ch_txt}: parsed -> {reader_stage} -> raw -> "
f"live-calib-ref -> {calib_stage} -> {bg_stage} -> ring -> IFFT(dB)"
f"live-calib-ref -> {calib_stage} -> {bg_stage} -> ring -> IFFT(abs, depth_m)"
)
calib_stage = self.format_calib_source_status()
@ -235,7 +235,7 @@ class AppState:
return (
f"pipeline ch{ch_txt}: parsed -> {reader_stage} -> raw -> "
f"{calib_stage} -> {bg_stage} -> ring -> IFFT(dB)"
f"{calib_stage} -> {bg_stage} -> ring -> IFFT(abs, depth_m)"
)
def _format_summary(self, summary) -> str:

View File

@ -6,10 +6,11 @@ from typing import Optional, Tuple
import numpy as np
from rfg_adc_plotter.constants import (
IFFT_LEN,
FREQ_MAX_GHZ,
FREQ_MIN_GHZ,
WF_WIDTH,
)
from rfg_adc_plotter.processing.fourier import build_ifft_time_axis_ns, compute_ifft_db_profile
from rfg_adc_plotter.processing.fourier import compute_ifft_profile_from_sweep
class RingBuffer:
@ -21,7 +22,8 @@ class RingBuffer:
def __init__(self, max_sweeps: int):
self.max_sweeps = max_sweeps
self.fft_bins = IFFT_LEN # = 1953 (полная длина IFFT-результата)
# Размер IFFT-профиля теперь динамический и определяется по первому успешному свипу.
self.fft_bins = 0
# Инициализируются при первом свипе (ensure_init)
self.ring: Optional[np.ndarray] = None # (max_sweeps, WF_WIDTH)
@ -30,7 +32,7 @@ class RingBuffer:
self.head: int = 0
self.width: Optional[int] = None
self.x_shared: Optional[np.ndarray] = None
self.fft_time_axis: Optional[np.ndarray] = None # временная ось IFFT в нс
self.fft_depth_axis_m: Optional[np.ndarray] = None # ось глубины IFFT в метрах
self.y_min_fft: Optional[float] = None
self.y_max_fft: Optional[float] = None
# FFT последнего свипа (для отображения без повторного вычисления)
@ -40,19 +42,21 @@ class RingBuffer:
def is_ready(self) -> bool:
return self.ring is not None
@property
def fft_time_axis(self) -> Optional[np.ndarray]:
"""Legacy alias: старое имя поля (раньше было время в нс, теперь глубина в м)."""
return self.fft_depth_axis_m
def ensure_init(self, sweep_width: int):
"""Инициализировать буферы при первом свипе. Повторные вызовы — no-op (кроме x_shared)."""
if self.ring is None:
self.width = WF_WIDTH
self.ring = np.full((self.max_sweeps, self.width), np.nan, dtype=np.float32)
self.ring_time = np.full((self.max_sweeps,), np.nan, dtype=np.float64)
self.ring_fft = np.full((self.max_sweeps, self.fft_bins), np.nan, dtype=np.float32)
# Временная ось IFFT вынесена в processing.fourier для явного pipeline.
self.fft_time_axis = build_ifft_time_axis_ns()
self.head = 0
# Обновляем x_shared если пришёл свип большего размера
if self.x_shared is None or sweep_width > self.x_shared.size:
self.x_shared = np.linspace(3.323, 14.323, sweep_width, dtype=np.float32)
self.x_shared = np.linspace(FREQ_MIN_GHZ, FREQ_MAX_GHZ, sweep_width, dtype=np.float32)
def push(self, s: np.ndarray):
"""Добавить строку свипа в кольцевой буфер, вычислить FFT-строку."""
@ -69,8 +73,43 @@ class RingBuffer:
self._push_fft(s)
def _push_fft(self, s: np.ndarray):
fft_row = compute_ifft_db_profile(s)
depth_axis_m, fft_row = compute_ifft_profile_from_sweep(s)
fft_row = np.asarray(fft_row, dtype=np.float32).ravel()
depth_axis_m = np.asarray(depth_axis_m, dtype=np.float32).ravel()
n = min(int(fft_row.size), int(depth_axis_m.size))
if n <= 0:
return
if n != fft_row.size:
fft_row = fft_row[:n]
if n != depth_axis_m.size:
depth_axis_m = depth_axis_m[:n]
needs_reset = (
self.ring_fft is None
or self.fft_depth_axis_m is None
or self.fft_bins != n
or self.ring_fft.shape != (self.max_sweeps, n)
or self.fft_depth_axis_m.size != n
)
if (not needs_reset) and n > 0:
prev_axis = self.fft_depth_axis_m
assert prev_axis is not None
if prev_axis.size != n:
needs_reset = True
else:
# Если ось изменилась (например, изменилась длина/частотная сетка), сбрасываем FFT-водопад.
if not np.allclose(prev_axis[[0, -1]], depth_axis_m[[0, -1]], rtol=1e-6, atol=1e-9):
needs_reset = True
if needs_reset:
self.fft_bins = n
self.ring_fft = np.full((self.max_sweeps, n), np.nan, dtype=np.float32)
self.fft_depth_axis_m = depth_axis_m.astype(np.float32, copy=True)
self.y_min_fft = None
self.y_max_fft = None
assert self.ring_fft is not None
prev_head = (self.head - 1) % self.ring_fft.shape[0]
self.ring_fft[prev_head, :] = fft_row
self.last_fft_vals = fft_row