WIP on normaliser: 2e6ad24 ad to gitignore
This commit is contained in:
Binary file not shown.
@ -1,5 +1,6 @@
|
||||
WF_WIDTH = 1000 # максимальное число точек в ряду водопада
|
||||
FFT_LEN = 2048 # длина БПФ для спектра/водопада спектров
|
||||
LOG_EXP = 2.0 # основание экспоненты для опции --logscale
|
||||
# Порог для инверсии сырых данных: если среднее значение свипа ниже порога —
|
||||
# считаем, что сигнал «меньше нуля» и домножаем свип на -1
|
||||
DATA_INVERSION_THRESHOLD = 10.0
|
||||
|
||||
@ -96,6 +96,7 @@ def run_matplotlib(args):
|
||||
stop_event,
|
||||
fancy=bool(args.fancy),
|
||||
bin_mode=bool(getattr(args, "bin_mode", False)),
|
||||
logscale=bool(getattr(args, "logscale", False)),
|
||||
)
|
||||
reader.start()
|
||||
|
||||
@ -106,6 +107,7 @@ def run_matplotlib(args):
|
||||
spec_mean_sec = float(getattr(args, "spec_mean_sec", 0.0))
|
||||
fixed_ylim = _parse_ylim(getattr(args, "ylim", None))
|
||||
norm_type = str(getattr(args, "norm_type", "projector")).strip().lower()
|
||||
logscale_enabled = bool(getattr(args, "logscale", False))
|
||||
|
||||
state = AppState(norm_type=norm_type)
|
||||
ring = RingBuffer(max_sweeps)
|
||||
@ -123,6 +125,8 @@ def run_matplotlib(args):
|
||||
# График последнего свипа
|
||||
line_obj, = ax_line.plot([], [], lw=1, color="tab:blue")
|
||||
line_norm_obj, = ax_line.plot([], [], lw=1, color="tab:green")
|
||||
line_pre_exp_obj, = ax_line.plot([], [], lw=1, color="tab:red")
|
||||
line_post_exp_obj, = ax_line.plot([], [], lw=1, color="tab:green")
|
||||
line_env_lo, = ax_line.plot([], [], lw=1, color="tab:orange", linestyle="--", alpha=0.7)
|
||||
line_env_hi, = ax_line.plot([], [], lw=1, color="tab:orange", linestyle="--", alpha=0.7)
|
||||
ax_line.set_title("Сырые данные", pad=1)
|
||||
@ -271,10 +275,7 @@ def run_matplotlib(args):
|
||||
xs = ring.x_shared[: raw.size]
|
||||
else:
|
||||
xs = np.arange(raw.size, dtype=np.int32)
|
||||
def _norm_to_max(data):
|
||||
m = float(np.nanmax(np.abs(data)))
|
||||
return data / m if m > 0.0 else data
|
||||
line_obj.set_data(xs, _norm_to_max(raw))
|
||||
line_obj.set_data(xs, raw)
|
||||
if state.calib_mode == "file" and state.calib_file_envelope is not None:
|
||||
upper = state.calib_file_envelope
|
||||
lower = -upper
|
||||
@ -294,14 +295,38 @@ def run_matplotlib(args):
|
||||
else:
|
||||
line_env_lo.set_data([], [])
|
||||
line_env_hi.set_data([], [])
|
||||
if logscale_enabled:
|
||||
if state.current_sweep_pre_exp is not None:
|
||||
pre = state.current_sweep_pre_exp
|
||||
line_pre_exp_obj.set_data(xs[: pre.size], pre)
|
||||
else:
|
||||
line_pre_exp_obj.set_data([], [])
|
||||
|
||||
post = state.current_sweep_post_exp if state.current_sweep_post_exp is not None else raw
|
||||
line_post_exp_obj.set_data(xs[: post.size], post)
|
||||
|
||||
if state.current_sweep_processed is not None:
|
||||
proc = state.current_sweep_processed
|
||||
line_obj.set_data(xs[: proc.size], proc)
|
||||
else:
|
||||
line_obj.set_data([], [])
|
||||
line_norm_obj.set_data([], [])
|
||||
else:
|
||||
line_pre_exp_obj.set_data([], [])
|
||||
line_post_exp_obj.set_data([], [])
|
||||
if state.current_sweep_norm is not None:
|
||||
line_norm_obj.set_data(xs[: state.current_sweep_norm.size], _norm_to_max(state.current_sweep_norm))
|
||||
line_norm_obj.set_data(
|
||||
xs[: state.current_sweep_norm.size], state.current_sweep_norm
|
||||
)
|
||||
else:
|
||||
line_norm_obj.set_data([], [])
|
||||
ax_line.set_xlim(FREQ_MIN, FREQ_MAX)
|
||||
if fixed_ylim is None:
|
||||
ax_line.set_ylim(-1.05, 1.05)
|
||||
ax_line.set_ylabel("/ max")
|
||||
if fixed_ylim is not None:
|
||||
ax_line.set_ylim(fixed_ylim)
|
||||
else:
|
||||
ax_line.relim()
|
||||
ax_line.autoscale_view(scalex=False, scaley=True)
|
||||
ax_line.set_ylabel("Y")
|
||||
|
||||
# Спектр — используем уже вычисленный в ring IFFT (временной профиль)
|
||||
if ring.last_fft_vals is not None and ring.fft_time_axis is not None:
|
||||
@ -345,7 +370,19 @@ def run_matplotlib(args):
|
||||
status_text.set_text(format_status(state.current_info))
|
||||
channel_text.set_text(state.format_channel_label())
|
||||
|
||||
return (line_obj, line_norm_obj, line_env_lo, line_env_hi, img_obj, fft_line_obj, img_fft_obj, status_text, channel_text)
|
||||
return (
|
||||
line_obj,
|
||||
line_norm_obj,
|
||||
line_pre_exp_obj,
|
||||
line_post_exp_obj,
|
||||
line_env_lo,
|
||||
line_env_hi,
|
||||
img_obj,
|
||||
fft_line_obj,
|
||||
img_fft_obj,
|
||||
status_text,
|
||||
channel_text,
|
||||
)
|
||||
|
||||
ani = FuncAnimation(fig, update, interval=interval_ms, blit=False)
|
||||
plt.show()
|
||||
|
||||
@ -113,6 +113,7 @@ def run_pyqtgraph(args):
|
||||
stop_event,
|
||||
fancy=bool(args.fancy),
|
||||
bin_mode=bool(getattr(args, "bin_mode", False)),
|
||||
logscale=bool(getattr(args, "logscale", False)),
|
||||
)
|
||||
reader.start()
|
||||
|
||||
@ -123,6 +124,7 @@ def run_pyqtgraph(args):
|
||||
spec_mean_sec = float(getattr(args, "spec_mean_sec", 0.0))
|
||||
fixed_ylim = _parse_ylim(getattr(args, "ylim", None))
|
||||
norm_type = str(getattr(args, "norm_type", "projector")).strip().lower()
|
||||
logscale_enabled = bool(getattr(args, "logscale", False))
|
||||
|
||||
state = AppState(norm_type=norm_type)
|
||||
ring = RingBuffer(max_sweeps)
|
||||
@ -138,6 +140,8 @@ def run_pyqtgraph(args):
|
||||
p_line.showGrid(x=True, y=True, alpha=0.3)
|
||||
curve = p_line.plot(pen=pg.mkPen((80, 120, 255), width=1))
|
||||
curve_norm = p_line.plot(pen=pg.mkPen((60, 180, 90), width=1))
|
||||
curve_pre_exp = p_line.plot(pen=pg.mkPen((220, 60, 60), width=1))
|
||||
curve_post_exp = p_line.plot(pen=pg.mkPen((60, 180, 90), width=1))
|
||||
curve_env_lo = p_line.plot(pen=pg.mkPen((255, 165, 0), width=1, style=QtCore.Qt.DashLine))
|
||||
curve_env_hi = p_line.plot(pen=pg.mkPen((255, 165, 0), width=1, style=QtCore.Qt.DashLine))
|
||||
p_line.setLabel("bottom", "Частота, ГГц")
|
||||
@ -293,10 +297,7 @@ def run_pyqtgraph(args):
|
||||
if state.current_sweep_raw is not None and ring.x_shared is not None:
|
||||
raw = state.current_sweep_raw
|
||||
xs = ring.x_shared[: raw.size] if raw.size <= ring.x_shared.size else np.arange(raw.size)
|
||||
def _norm_to_max(data):
|
||||
m = float(np.nanmax(np.abs(data)))
|
||||
return data / m if m > 0.0 else data
|
||||
curve.setData(xs, _norm_to_max(raw), autoDownsample=True)
|
||||
curve.setData(xs, raw, autoDownsample=True)
|
||||
if state.calib_mode == "file" and state.calib_file_envelope is not None:
|
||||
upper = state.calib_file_envelope
|
||||
lower = -upper
|
||||
@ -316,13 +317,38 @@ def run_pyqtgraph(args):
|
||||
else:
|
||||
curve_env_lo.setData([], [])
|
||||
curve_env_hi.setData([], [])
|
||||
if logscale_enabled:
|
||||
if state.current_sweep_pre_exp is not None:
|
||||
pre = state.current_sweep_pre_exp
|
||||
curve_pre_exp.setData(xs[: pre.size], pre, autoDownsample=True)
|
||||
else:
|
||||
curve_pre_exp.setData([], [])
|
||||
|
||||
post = state.current_sweep_post_exp if state.current_sweep_post_exp is not None else raw
|
||||
curve_post_exp.setData(xs[: post.size], post, autoDownsample=True)
|
||||
|
||||
if state.current_sweep_processed is not None:
|
||||
proc = state.current_sweep_processed
|
||||
curve.setData(xs[: proc.size], proc, autoDownsample=True)
|
||||
else:
|
||||
curve.setData([], [])
|
||||
curve_norm.setData([], [])
|
||||
else:
|
||||
curve_pre_exp.setData([], [])
|
||||
curve_post_exp.setData([], [])
|
||||
if state.current_sweep_norm is not None:
|
||||
curve_norm.setData(xs[: state.current_sweep_norm.size], _norm_to_max(state.current_sweep_norm), autoDownsample=True)
|
||||
curve_norm.setData(
|
||||
xs[: state.current_sweep_norm.size],
|
||||
state.current_sweep_norm,
|
||||
autoDownsample=True,
|
||||
)
|
||||
else:
|
||||
curve_norm.setData([], [])
|
||||
if fixed_ylim is None:
|
||||
p_line.setYRange(-1.05, 1.05, padding=0)
|
||||
p_line.setLabel("left", "/ max")
|
||||
if fixed_ylim is not None:
|
||||
p_line.setYRange(fixed_ylim[0], fixed_ylim[1], padding=0)
|
||||
else:
|
||||
p_line.enableAutoRange(axis="y", enable=True)
|
||||
p_line.setLabel("left", "Y")
|
||||
|
||||
# Спектр — используем уже вычисленный в ring IFFT (временной профиль)
|
||||
if ring.last_fft_vals is not None and ring.fft_time_axis is not None:
|
||||
|
||||
@ -9,7 +9,7 @@ from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
from rfg_adc_plotter.constants import DATA_INVERSION_THRESHOLD
|
||||
from rfg_adc_plotter.constants import DATA_INVERSION_THRESHOLD, LOG_EXP
|
||||
from rfg_adc_plotter.io.serial_source import SerialChunkReader, SerialLineSource
|
||||
from rfg_adc_plotter.types import SweepInfo, SweepPacket
|
||||
|
||||
@ -25,6 +25,7 @@ class SweepReader(threading.Thread):
|
||||
stop_event: threading.Event,
|
||||
fancy: bool = False,
|
||||
bin_mode: bool = False,
|
||||
logscale: bool = False,
|
||||
):
|
||||
super().__init__(daemon=True)
|
||||
self._port_path = port_path
|
||||
@ -34,6 +35,7 @@ class SweepReader(threading.Thread):
|
||||
self._src: Optional[SerialLineSource] = None
|
||||
self._fancy = bool(fancy)
|
||||
self._bin_mode = bool(bin_mode)
|
||||
self._logscale = bool(logscale)
|
||||
self._max_width: int = 0
|
||||
self._sweep_idx: int = 0
|
||||
self._last_sweep_ts: Optional[float] = None
|
||||
@ -92,6 +94,16 @@ class SweepReader(threading.Thread):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
pre_exp_sweep = None
|
||||
if self._logscale:
|
||||
try:
|
||||
pre_exp_sweep = sweep.copy()
|
||||
with np.errstate(over="ignore", invalid="ignore"):
|
||||
sweep = np.power(LOG_EXP, np.asarray(sweep, dtype=np.float64)).astype(np.float32)
|
||||
sweep[~np.isfinite(sweep)] = np.nan
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._sweep_idx += 1
|
||||
if len(ch_list) > 1:
|
||||
sys.stderr.write(
|
||||
@ -129,6 +141,8 @@ class SweepReader(threading.Thread):
|
||||
"std": std,
|
||||
"dt_ms": dt_ms,
|
||||
}
|
||||
if pre_exp_sweep is not None:
|
||||
info["pre_exp_sweep"] = pre_exp_sweep
|
||||
|
||||
try:
|
||||
self._q.put_nowait((sweep, info))
|
||||
|
||||
@ -86,6 +86,11 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
"точки step,uint32(hi16,lo16),0x000A"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--logscale",
|
||||
action="store_true",
|
||||
help="После поправки знака применять экспоненту LOG_EXP**x (LOG_EXP=2)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ def format_status(data: Mapping[str, Any]) -> str:
|
||||
return f"{fv:.3g}"
|
||||
return f"{fv:.3f}".rstrip("0").rstrip(".")
|
||||
|
||||
parts = [f"{k}:{_fmt(v)}" for k, v in data.items()]
|
||||
parts = [f"{k}:{_fmt(v)}" for k, v in data.items() if k != "pre_exp_sweep"]
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
@ -46,6 +46,9 @@ class AppState:
|
||||
"""
|
||||
|
||||
def __init__(self, norm_type: str = "projector"):
|
||||
self.current_sweep_pre_exp: Optional[np.ndarray] = None
|
||||
self.current_sweep_post_exp: Optional[np.ndarray] = None
|
||||
self.current_sweep_processed: Optional[np.ndarray] = None
|
||||
self.current_sweep_raw: Optional[np.ndarray] = None
|
||||
self.current_sweep_norm: Optional[np.ndarray] = None
|
||||
self.last_calib_sweep: Optional[np.ndarray] = None
|
||||
@ -154,6 +157,9 @@ class AppState:
|
||||
self.current_sweep_norm = None
|
||||
else:
|
||||
self.current_sweep_norm = None
|
||||
self.current_sweep_processed = (
|
||||
self.current_sweep_norm if self.current_sweep_norm is not None else self.current_sweep_raw
|
||||
)
|
||||
|
||||
def drain_queue(self, q: "Queue[SweepPacket]", ring: RingBuffer) -> int:
|
||||
"""Вытащить все ожидающие свипы из очереди, обновить state и ring.
|
||||
@ -168,7 +174,10 @@ class AppState:
|
||||
break
|
||||
drained += 1
|
||||
self.current_sweep_raw = s
|
||||
self.current_sweep_post_exp = s
|
||||
self.current_info = info
|
||||
pre_exp = info.get("pre_exp_sweep") if isinstance(info, dict) else None
|
||||
self.current_sweep_pre_exp = pre_exp if isinstance(pre_exp, np.ndarray) else None
|
||||
|
||||
ch = 0
|
||||
try:
|
||||
@ -204,6 +213,7 @@ class AppState:
|
||||
self.current_sweep_norm = sweep_for_ring
|
||||
|
||||
self._last_sweep_for_ring = sweep_for_ring
|
||||
self.current_sweep_processed = sweep_for_ring
|
||||
ring.ensure_init(s.size)
|
||||
ring.push(sweep_for_ring)
|
||||
return drained
|
||||
|
||||
Reference in New Issue
Block a user