added reference png export
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""Render captured traces (amplitude + phase) to standalone PNG files.
|
||||
|
||||
The preprocess preview pane only shows the *last* radar variant of each combo,
|
||||
so during a multi-config reference capture the operator never sees the other
|
||||
variants. This helper renders the same amplitude/phase plot pair used in the
|
||||
preview into off-screen PNG files, letting us persist a graph for every config.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from PyQt6.QtCore import QRectF
|
||||
from pyqtgraph.exporters import ImageExporter
|
||||
|
||||
from python_app.models.dataset_model import TraceData
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Matches the preview pane styling in preprocess_dialog so saved PNGs and the
|
||||
# on-screen preview look the same.
|
||||
_BACKGROUND = "#101418"
|
||||
_MAGNITUDE_PEN = pg.mkPen("#4cc9f0", width=1.8)
|
||||
_PHASE_PEN = pg.mkPen("#f48c06", width=1.8)
|
||||
_EXPORT_SIZE = (1400, 520)
|
||||
|
||||
|
||||
def export_trace_png(
|
||||
trace: TraceData,
|
||||
output_path: Path,
|
||||
*,
|
||||
channel: str,
|
||||
title: str,
|
||||
) -> None:
|
||||
"""Render one trace's amplitude (dB) and wrapped phase (deg) panes to a PNG.
|
||||
|
||||
Builds an off-screen ``GraphicsLayoutWidget`` (never shown), plots the same
|
||||
curves as the live preview, and exports the scene as a PNG. Requires a
|
||||
running ``QApplication`` (always true inside the GUI).
|
||||
"""
|
||||
samples = trace.s11 if channel == "s11" else trace.s21
|
||||
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(samples), 1e-12))
|
||||
phase_deg = np.degrees(np.angle(samples))
|
||||
|
||||
layout = pg.GraphicsLayoutWidget(show=False, size=_EXPORT_SIZE)
|
||||
layout.setBackground(_BACKGROUND)
|
||||
try:
|
||||
layout.addLabel(title, row=0, col=0, colspan=2)
|
||||
|
||||
magnitude_plot = layout.addPlot(row=1, col=0)
|
||||
magnitude_plot.showGrid(x=True, y=True, alpha=0.2)
|
||||
magnitude_plot.setLabel("bottom", "Frequency", units="Hz")
|
||||
magnitude_plot.setLabel("left", "Magnitude", units="dB")
|
||||
magnitude_plot.plot(trace.frequency_hz, magnitude_db, pen=_MAGNITUDE_PEN)
|
||||
|
||||
phase_plot = layout.addPlot(row=1, col=1)
|
||||
phase_plot.showGrid(x=True, y=True, alpha=0.2)
|
||||
phase_plot.setLabel("bottom", "Frequency", units="Hz")
|
||||
phase_plot.setLabel("left", "Phase", units="deg")
|
||||
phase_plot.plot(trace.frequency_hz, phase_deg, pen=_PHASE_PEN)
|
||||
|
||||
# An off-screen widget never receives a resizeEvent, so its central
|
||||
# GraphicsLayout keeps its small preferred size and both plots would be
|
||||
# squeezed into the left edge of the image. Force the layout geometry to
|
||||
# the target size, doing manually what a resizeEvent normally triggers.
|
||||
layout.ci.setGeometry(QRectF(0, 0, _EXPORT_SIZE[0], _EXPORT_SIZE[1]))
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
exporter = ImageExporter(layout.scene())
|
||||
exporter.parameters()["width"] = _EXPORT_SIZE[0]
|
||||
exporter.export(str(output_path))
|
||||
finally:
|
||||
layout.close()
|
||||
layout.deleteLater()
|
||||
Reference in New Issue
Block a user