fix
This commit is contained in:
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import signal
|
import signal
|
||||||
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from queue import Empty, Queue
|
from queue import Empty, Queue
|
||||||
@ -38,6 +39,7 @@ from rfg_adc_plotter.state import RingBuffer, RuntimeState
|
|||||||
from rfg_adc_plotter.types import SweepAuxCurves, SweepInfo, SweepPacket
|
from rfg_adc_plotter.types import SweepAuxCurves, SweepInfo, SweepPacket
|
||||||
|
|
||||||
RAW_PLOT_MAX_POINTS = 4096
|
RAW_PLOT_MAX_POINTS = 4096
|
||||||
|
RAW_WATERFALL_MAX_POINTS = 2048
|
||||||
DEBUG_FRAME_LOG_EVERY = 10
|
DEBUG_FRAME_LOG_EVERY = 10
|
||||||
|
|
||||||
|
|
||||||
@ -603,7 +605,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
if finite_f.size > 0:
|
if finite_f.size > 0:
|
||||||
f_min = float(np.min(finite_f))
|
f_min = float(np.min(finite_f))
|
||||||
f_max = float(np.max(finite_f))
|
f_max = float(np.max(finite_f))
|
||||||
img.setImage(runtime.ring.get_display_raw(), autoLevels=False)
|
img.setImage(runtime.ring.get_display_raw_decimated(RAW_WATERFALL_MAX_POINTS), autoLevels=False)
|
||||||
img.setRect(0, f_min, max_sweeps, max(1e-9, f_max - f_min))
|
img.setRect(0, f_min, max_sweeps, max(1e-9, f_max - f_min))
|
||||||
p_img.setRange(
|
p_img.setRange(
|
||||||
xRange=(0, max_sweeps - 1),
|
xRange=(0, max_sweeps - 1),
|
||||||
@ -714,7 +716,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.current_peak_width = None
|
runtime.current_peak_width = None
|
||||||
runtime.current_peak_amplitude = None
|
runtime.current_peak_amplitude = None
|
||||||
runtime.peak_candidates = []
|
runtime.peak_candidates = []
|
||||||
img.setImage(runtime.ring.get_display_raw(), autoLevels=False)
|
img.setImage(runtime.ring.get_display_raw_decimated(RAW_WATERFALL_MAX_POINTS), autoLevels=False)
|
||||||
img_fft.setImage(runtime.ring.get_display_fft_linear(), autoLevels=False)
|
img_fft.setImage(runtime.ring.get_display_fft_linear(), autoLevels=False)
|
||||||
update_physical_axes()
|
update_physical_axes()
|
||||||
|
|
||||||
@ -1242,6 +1244,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
processed_frames = 0
|
processed_frames = 0
|
||||||
|
ui_started_at = time.perf_counter()
|
||||||
|
|
||||||
def refresh_current_fft_cache(sweep_for_fft: np.ndarray, bins: int) -> None:
|
def refresh_current_fft_cache(sweep_for_fft: np.ndarray, bins: int) -> None:
|
||||||
runtime.current_fft_complex = compute_fft_complex_row(
|
runtime.current_fft_complex = compute_fft_complex_row(
|
||||||
@ -1311,10 +1314,13 @@ def run_pyqtgraph(args) -> None:
|
|||||||
queue_size = queue.qsize()
|
queue_size = queue.qsize()
|
||||||
except Exception:
|
except Exception:
|
||||||
queue_size = -1
|
queue_size = -1
|
||||||
|
elapsed_s = max(time.perf_counter() - ui_started_at, 1e-9)
|
||||||
|
frames_per_sec = float(processed_frames) / elapsed_s
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"[debug] ui frames:%d last_sweep:%s ch:%s width:%d queue:%d\n"
|
"[debug] ui frames:%d rate:%.2f/s last_sweep:%s ch:%s width:%d queue:%d\n"
|
||||||
% (
|
% (
|
||||||
processed_frames,
|
processed_frames,
|
||||||
|
frames_per_sec,
|
||||||
str(info.get("sweep") if isinstance(info, dict) else None),
|
str(info.get("sweep") if isinstance(info, dict) else None),
|
||||||
str(info.get("ch") if isinstance(info, dict) else None),
|
str(info.get("ch") if isinstance(info, dict) else None),
|
||||||
int(getattr(sweep, "size", 0)),
|
int(getattr(sweep, "size", 0)),
|
||||||
@ -1640,7 +1646,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.plot_dirty = False
|
runtime.plot_dirty = False
|
||||||
|
|
||||||
if changed and runtime.ring.ring is not None:
|
if changed and runtime.ring.ring is not None:
|
||||||
disp = runtime.ring.get_display_raw()
|
disp = runtime.ring.get_display_raw_decimated(RAW_WATERFALL_MAX_POINTS)
|
||||||
levels = _visible_levels_pyqtgraph(disp, p_img)
|
levels = _visible_levels_pyqtgraph(disp, p_img)
|
||||||
if levels is not None:
|
if levels is not None:
|
||||||
img.setImage(disp, autoLevels=False, levels=levels)
|
img.setImage(disp, autoLevels=False, levels=levels)
|
||||||
|
|||||||
@ -101,6 +101,7 @@ class SweepReader(threading.Thread):
|
|||||||
self._src: SerialLineSource | None = None
|
self._src: SerialLineSource | None = None
|
||||||
self._frames_read = 0
|
self._frames_read = 0
|
||||||
self._frames_dropped = 0
|
self._frames_dropped = 0
|
||||||
|
self._started_at = time.perf_counter()
|
||||||
|
|
||||||
def _build_parser(self):
|
def _build_parser(self):
|
||||||
if self._parser_complex_ascii:
|
if self._parser_complex_ascii:
|
||||||
@ -175,12 +176,15 @@ class SweepReader(threading.Thread):
|
|||||||
queue_size = self._queue.qsize()
|
queue_size = self._queue.qsize()
|
||||||
except Exception:
|
except Exception:
|
||||||
queue_size = -1
|
queue_size = -1
|
||||||
|
elapsed_s = max(time.perf_counter() - self._started_at, 1e-9)
|
||||||
|
frames_per_sec = float(self._frames_read) / elapsed_s
|
||||||
sweep_idx = info.get("sweep") if isinstance(info, dict) else None
|
sweep_idx = info.get("sweep") if isinstance(info, dict) else None
|
||||||
channel = info.get("ch") if isinstance(info, dict) else None
|
channel = info.get("ch") if isinstance(info, dict) else None
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"[debug] reader frames:%d last_sweep:%s ch:%s width:%d queue:%d dropped:%d\n"
|
"[debug] reader frames:%d rate:%.2f/s last_sweep:%s ch:%s width:%d queue:%d dropped:%d\n"
|
||||||
% (
|
% (
|
||||||
self._frames_read,
|
self._frames_read,
|
||||||
|
frames_per_sec,
|
||||||
str(sweep_idx),
|
str(sweep_idx),
|
||||||
str(channel),
|
str(channel),
|
||||||
int(getattr(sweep, "size", 0)),
|
int(getattr(sweep, "size", 0)),
|
||||||
|
|||||||
@ -201,6 +201,21 @@ class RingBuffer:
|
|||||||
base = self.ring if self.head == 0 else np.roll(self.ring, -self.head, axis=0)
|
base = self.ring if self.head == 0 else np.roll(self.ring, -self.head, axis=0)
|
||||||
return base.T
|
return base.T
|
||||||
|
|
||||||
|
def get_display_raw_decimated(self, max_points: int) -> np.ndarray:
|
||||||
|
"""Return a display-oriented raw waterfall with optional frequency decimation."""
|
||||||
|
if self.ring is None:
|
||||||
|
return np.zeros((1, 1), dtype=np.float32)
|
||||||
|
|
||||||
|
limit = int(max_points)
|
||||||
|
if limit <= 0 or self.width <= limit:
|
||||||
|
return self.get_display_raw()
|
||||||
|
|
||||||
|
row_order = np.arange(self.ring.shape[0], dtype=np.int64)
|
||||||
|
if self.head:
|
||||||
|
row_order = np.roll(row_order, -self.head)
|
||||||
|
col_idx = np.linspace(0, self.width - 1, limit, dtype=np.int64)
|
||||||
|
return self.ring[np.ix_(row_order, col_idx)].T
|
||||||
|
|
||||||
def get_display_fft_linear(self) -> np.ndarray:
|
def get_display_fft_linear(self) -> np.ndarray:
|
||||||
if self.ring_fft is None:
|
if self.ring_fft is None:
|
||||||
return np.zeros((1, 1), dtype=np.float32)
|
return np.zeros((1, 1), dtype=np.float32)
|
||||||
|
|||||||
@ -41,6 +41,22 @@ class RingBufferTests(unittest.TestCase):
|
|||||||
self.assertIsNotNone(ring.last_fft_db)
|
self.assertIsNotNone(ring.last_fft_db)
|
||||||
self.assertEqual(ring.last_fft_db.shape, (ring.fft_bins,))
|
self.assertEqual(ring.last_fft_db.shape, (ring.fft_bins,))
|
||||||
|
|
||||||
|
def test_ring_buffer_can_return_decimated_display_raw(self):
|
||||||
|
ring = RingBuffer(max_sweeps=3)
|
||||||
|
sweep_a = np.linspace(0.0, 1.0, 4096, dtype=np.float32)
|
||||||
|
sweep_b = np.linspace(1.0, 2.0, 4096, dtype=np.float32)
|
||||||
|
sweep_c = np.linspace(2.0, 3.0, 4096, dtype=np.float32)
|
||||||
|
freqs = np.linspace(3.3, 14.3, 4096, dtype=np.float64)
|
||||||
|
ring.push(sweep_a, freqs)
|
||||||
|
ring.push(sweep_b, freqs)
|
||||||
|
ring.push(sweep_c, freqs)
|
||||||
|
|
||||||
|
raw = ring.get_display_raw_decimated(256)
|
||||||
|
|
||||||
|
self.assertEqual(raw.shape, (256, 3))
|
||||||
|
self.assertAlmostEqual(float(raw[0, -1]), float(sweep_c[0]), places=6)
|
||||||
|
self.assertAlmostEqual(float(raw[-1, -1]), float(sweep_c[-1]), places=6)
|
||||||
|
|
||||||
def test_ring_buffer_can_switch_fft_mode_and_rebuild_fft_rows(self):
|
def test_ring_buffer_can_switch_fft_mode_and_rebuild_fft_rows(self):
|
||||||
ring = RingBuffer(max_sweeps=2)
|
ring = RingBuffer(max_sweeps=2)
|
||||||
sweep = np.linspace(0.0, 1.0, 64, dtype=np.float32)
|
sweep = np.linspace(0.0, 1.0, 64, dtype=np.float32)
|
||||||
|
|||||||
Reference in New Issue
Block a user