new kamil adc

This commit is contained in:
Ayzen
2026-06-11 13:02:02 +03:00
parent 21f76d7cd2
commit 9661504e51
46 changed files with 12097 additions and 1063 deletions
@@ -21,6 +21,10 @@ class AppWindowTracePlotMixin:
"""Return whether phase curves should be rendered."""
return self._show_phase_checkbox.isChecked()
def _unwrap_phase_enabled(self) -> bool:
"""Return whether the phase trace should be unwrapped (cumulative)."""
return self._unwrap_phase_checkbox.isChecked()
def _pass_through_fixed_y_range(self) -> tuple[bool, float, float]:
"""Return normalized magnitude Y-range override for pass-through mode."""
y_min = float(self._pass_through_y_min_db.value())
@@ -48,13 +52,29 @@ class AppWindowTracePlotMixin:
return None
def _configure_pass_through_magnitude_axis(self, plot: pg.PlotWidget) -> None:
"""Apply pass-through magnitude-axis autorange or fixed Y window."""
"""Apply the magnitude Y-axis: a fixed window or autorange.
The X axis is set explicitly from the data band by the caller, so this
never re-enables X autorange (which would scan all curves every frame).
"""
fixed_y_enabled, y_min, y_max = self._pass_through_fixed_y_range()
view_box = plot.getViewBox()
view_box.invertY(False)
view_box.enableAutoRange(x=True, y=not fixed_y_enabled)
if fixed_y_enabled:
view_box.enableAutoRange(y=False)
plot.setYRange(y_min, y_max, padding=0.0)
else:
view_box.enableAutoRange(y=True)
def _configure_pass_through_phase_axis(self, plot: pg.PlotWidget) -> None:
"""Apply the phase Y-axis: fixed ±180° when wrapped, autorange when unwrapped."""
view_box = plot.getViewBox()
view_box.invertY(False)
if self._unwrap_phase_enabled():
view_box.enableAutoRange(y=True)
else:
view_box.enableAutoRange(y=False)
plot.setYRange(-180.0, 180.0, padding=0.02)
def _on_trace_visibility_changed(self, *_args) -> None:
"""Redraw pass-through traces when magnitude/phase toggles changed."""
@@ -116,7 +136,6 @@ class AppWindowTracePlotMixin:
if show_magnitude:
mag_item = magnitude_plot.getPlotItem()
self._configure_pass_through_magnitude_axis(magnitude_plot)
mag_item.showAxis("left", show=True)
mag_item.showAxis("bottom", show=not show_phase)
magnitude_plot.setLabel("left", "Magnitude", units="dB")
@@ -126,8 +145,6 @@ class AppWindowTracePlotMixin:
if show_phase:
phase_item = phase_plot.getPlotItem()
phase_plot.getViewBox().invertY(False)
phase_plot.getViewBox().enableAutoRange(x=True, y=False)
phase_item.showAxis("left", show=True)
phase_item.showAxis("bottom", show=True)
phase_plot.setLabel("left", "Phase", units="deg")
@@ -179,16 +196,18 @@ class AppWindowTracePlotMixin:
x_max = max(x_max, local_x_max)
if show_magnitude:
magnitude_values = 20.0 * np.log10(np.maximum(np.abs(payload.trace), 1e-12))
mag_x, magnitude_values = self._magnitude_display_arrays(
payload.frequency_hz, payload.trace
)
active_magnitude_keys.add(curve_key)
magnitude_curve = self._trace_magnitude_curves.get(curve_key)
if magnitude_curve is None:
magnitude_curve = pg.PlotCurveItem(pen=pg.mkPen(color, width=1.4))
magnitude_curve = pg.PlotCurveItem(pen=pg.mkPen(color, width=1.4), antialias=False)
self._trace_magnitude_curves[curve_key] = magnitude_curve
magnitude_plot.addItem(magnitude_curve)
else:
magnitude_curve.setPen(pg.mkPen(color, width=1.4))
magnitude_curve.setData(payload.frequency_hz, magnitude_values)
magnitude_curve.setData(mag_x, magnitude_values)
legend_source_magnitude.setdefault(combo_key, magnitude_curve)
has_data = True
@@ -196,7 +215,7 @@ class AppWindowTracePlotMixin:
active_phase_keys.add(curve_key)
phase_curve = self._trace_phase_curves.get(curve_key)
if phase_curve is None:
phase_curve = pg.PlotCurveItem(pen=pg.mkPen(color, width=1.2))
phase_curve = pg.PlotCurveItem(pen=pg.mkPen(color, width=1.2), antialias=False)
self._trace_phase_curves[curve_key] = phase_curve
phase_plot.addItem(phase_curve)
else:
@@ -231,16 +250,17 @@ class AppWindowTracePlotMixin:
phase_sources=legend_source_phase,
)
if has_data:
if np.isfinite(x_min) and np.isfinite(x_max):
if show_magnitude:
magnitude_plot.setXRange(x_min, x_max, padding=0.02)
if show_phase:
phase_plot.setXRange(x_min, x_max, padding=0.02)
if show_phase:
phase_plot.setYRange(-180.0, 180.0, padding=0.02)
# X is the (constant) frequency band: set it explicitly rather than
# autoranging every frame. Y is configured once per draw.
if has_data and np.isfinite(x_min) and np.isfinite(x_max):
if show_magnitude:
self._configure_pass_through_magnitude_axis(magnitude_plot)
magnitude_plot.setXRange(x_min, x_max, padding=0.02)
if show_phase:
phase_plot.setXRange(x_min, x_max, padding=0.02)
if show_magnitude:
self._configure_pass_through_magnitude_axis(magnitude_plot)
if show_phase:
self._configure_pass_through_phase_axis(phase_plot)
return has_data
@staticmethod
@@ -268,15 +288,40 @@ class AppWindowTracePlotMixin:
plot.removeItem(curve)
cache.clear()
def _phase_display_arrays(self, frequency_hz: np.ndarray, trace: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""Return phase display arrays with decimation for faster rendering."""
max_points = int(getattr(self, "_trace_phase_render_max_points", 1200))
if max_points > 0 and trace.size > max_points:
step = max(1, int(np.ceil(trace.size / max_points)))
frequency_hz = frequency_hz[::step]
trace = trace[::step]
phase_values = np.arctan2(trace.imag, trace.real) * (180.0 / np.pi)
return frequency_hz, phase_values
def _render_decimation_step(self, size: int) -> int:
"""Stride that caps a rendered trace at the configured maximum point count.
Beyond ~1 point per screen pixel there is no visual gain, so decimating
keeps the line plots responsive even with many combos on screen.
"""
max_points = int(getattr(self, "_trace_render_max_points", 800))
if max_points > 0 and size > max_points:
return max(1, int(np.ceil(size / max_points)))
return 1
def _magnitude_display_arrays(
self, frequency_hz: np.ndarray, trace: np.ndarray
) -> tuple[np.ndarray, np.ndarray]:
"""Return decimated (frequency, magnitude-dB) arrays for fast rendering."""
step = self._render_decimation_step(int(trace.size))
frequency_hz = frequency_hz[::step]
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(trace[::step]), 1e-12))
return frequency_hz, magnitude_db
def _phase_display_arrays(
self, frequency_hz: np.ndarray, trace: np.ndarray
) -> tuple[np.ndarray, np.ndarray]:
"""Return decimated (frequency, phase-deg) arrays, unwrapped when enabled.
Unwrapping runs at full resolution (before decimation) so the cumulative
phase is correct, then both arrays are decimated together for rendering.
"""
phase = np.angle(trace)
if self._unwrap_phase_enabled():
phase = np.unwrap(phase)
phase_deg = np.degrees(phase)
step = self._render_decimation_step(int(trace.size))
return frequency_hz[::step], phase_deg[::step]
def _sync_trace_legends(
self,
@@ -363,26 +408,24 @@ class AppWindowTracePlotMixin:
return
if show_magnitude:
self._configure_pass_through_magnitude_axis(magnitude_plot)
magnitude_plot.getPlotItem().showAxis("bottom", show=not show_phase)
magnitude_plot.setLabel("left", "Magnitude", units="dB")
magnitude_plot.setTitle(title)
if not show_phase:
magnitude_plot.setLabel("bottom", "Frequency", units="Hz")
if show_phase:
phase_plot.getViewBox().invertY(False)
phase_plot.getViewBox().enableAutoRange(x=True, y=False)
phase_plot.getPlotItem().showAxis("bottom", show=True)
phase_plot.setLabel("left", "Phase", units="deg")
phase_plot.setLabel("bottom", "Frequency", units="Hz")
phase_plot.setTitle(title)
if show_magnitude:
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(samples), 1e-12))
mag_x, magnitude_db = self._magnitude_display_arrays(trace.frequency_hz, samples)
magnitude_curve = pg.PlotCurveItem(
trace.frequency_hz,
mag_x,
magnitude_db,
pen=pg.mkPen("#ffd166", width=1.8),
antialias=False,
)
magnitude_plot.addItem(magnitude_curve)
self._trace_magnitude_curves[
@@ -390,23 +433,26 @@ class AppWindowTracePlotMixin:
] = magnitude_curve
if show_phase:
phase_values = np.degrees(np.angle(samples))
phase_x, phase_values = self._phase_display_arrays(trace.frequency_hz, samples)
phase_curve = pg.PlotCurveItem(
trace.frequency_hz,
phase_x,
phase_values,
pen=pg.mkPen("#80ed99", width=1.4, style=Qt.PenStyle.DashLine),
antialias=False,
)
phase_plot.addItem(phase_curve)
self._trace_phase_curves[
(int(trace.combo.input), int(trace.combo.output), 0, "__single_trace__")
] = phase_curve
phase_plot.setYRange(-180.0, 180.0, padding=0.02)
if np.size(trace.frequency_hz) > 1:
x_min = float(np.min(trace.frequency_hz))
x_max = float(np.max(trace.frequency_hz))
if show_magnitude:
magnitude_plot.setXRange(x_min, x_max, padding=0.02)
self._configure_pass_through_magnitude_axis(magnitude_plot)
if show_phase:
phase_plot.setXRange(x_min, x_max, padding=0.02)
if show_magnitude:
self._configure_pass_through_magnitude_axis(magnitude_plot)
if show_phase:
self._configure_pass_through_phase_axis(phase_plot)