fft new mode
This commit is contained in:
@ -5,7 +5,7 @@ import tempfile
|
||||
import numpy as np
|
||||
import unittest
|
||||
|
||||
from rfg_adc_plotter.constants import FFT_LEN, SWEEP_FREQ_MAX_GHZ, SWEEP_FREQ_MIN_GHZ
|
||||
from rfg_adc_plotter.constants import C_M_S, FFT_LEN, SWEEP_FREQ_MAX_GHZ, SWEEP_FREQ_MIN_GHZ
|
||||
from rfg_adc_plotter.gui.pyqtgraph_backend import (
|
||||
apply_working_range,
|
||||
apply_working_range_to_aux_curves,
|
||||
@ -35,6 +35,7 @@ from rfg_adc_plotter.processing.background import (
|
||||
subtract_fft_background,
|
||||
)
|
||||
from rfg_adc_plotter.processing.fft import (
|
||||
build_positive_only_exact_centered_ifft_spectrum,
|
||||
build_positive_only_centered_ifft_spectrum,
|
||||
build_symmetric_ifft_spectrum,
|
||||
compute_distance_axis,
|
||||
@ -411,6 +412,21 @@ class ProcessingTests(unittest.TestCase):
|
||||
self.assertTrue(np.allclose(spectrum[zero_mask], 0.0))
|
||||
self.assertTrue(np.any(np.abs(spectrum[pos_idx]) > 0.0))
|
||||
|
||||
def test_positive_only_exact_spectrum_uses_direct_index_insertion_without_window(self):
|
||||
sweep = np.asarray([1.0, 2.0, 3.0], dtype=np.float32)
|
||||
freqs = np.asarray([4.0, 5.0, 6.0], dtype=np.float64)
|
||||
spectrum = build_positive_only_exact_centered_ifft_spectrum(sweep, freqs)
|
||||
|
||||
self.assertIsNotNone(spectrum)
|
||||
df = (6.0 - 4.0) / 2.0
|
||||
f_shift = np.arange(-6.0, 6.0 + (0.5 * df), df, dtype=np.float64)
|
||||
idx = np.round((freqs - f_shift[0]) / df).astype(np.int64)
|
||||
zero_mask = (f_shift > -6.0) & (f_shift < 4.0)
|
||||
|
||||
self.assertEqual(int(spectrum.size), int(f_shift.size))
|
||||
self.assertTrue(np.allclose(spectrum[zero_mask], 0.0))
|
||||
self.assertTrue(np.allclose(spectrum[idx], sweep))
|
||||
|
||||
def test_complex_symmetric_ifft_spectrum_uses_conjugate_mirror(self):
|
||||
sweep = np.exp(1j * np.linspace(0.0, np.pi, 128)).astype(np.complex64)
|
||||
freqs = np.linspace(4.0, 10.0, 128, dtype=np.float64)
|
||||
@ -442,6 +458,37 @@ class ProcessingTests(unittest.TestCase):
|
||||
self.assertTrue(np.any(np.isfinite(mag)))
|
||||
self.assertTrue(np.any(np.isfinite(row)))
|
||||
|
||||
def test_compute_fft_complex_row_positive_only_exact_matches_manual_ifftshift_ifft(self):
|
||||
sweep = np.asarray([1.0 + 1.0j, 2.0 + 0.0j, 3.0 - 1.0j], dtype=np.complex64)
|
||||
freqs = np.asarray([4.0, 5.0, 6.0], dtype=np.float64)
|
||||
bins = 16
|
||||
row = compute_fft_complex_row(sweep, freqs, bins, mode="positive_only_exact")
|
||||
|
||||
df = (6.0 - 4.0) / 2.0
|
||||
f_shift = np.arange(-6.0, 6.0 + (0.5 * df), df, dtype=np.float64)
|
||||
manual_shift = np.zeros((f_shift.size,), dtype=np.complex64)
|
||||
idx = np.round((freqs - f_shift[0]) / df).astype(np.int64)
|
||||
manual_shift[idx] = sweep
|
||||
manual_ifft = np.fft.ifft(np.fft.ifftshift(manual_shift))
|
||||
expected = np.full((bins,), np.nan + 0j, dtype=np.complex64)
|
||||
expected[: manual_ifft.size] = np.asarray(manual_ifft, dtype=np.complex64)
|
||||
|
||||
self.assertEqual(row.shape, (bins,))
|
||||
self.assertTrue(np.allclose(row, expected, equal_nan=True))
|
||||
|
||||
def test_positive_only_exact_distance_axis_uses_exact_grid_geometry(self):
|
||||
freqs = np.asarray([4.0, 5.0, 6.0], dtype=np.float64)
|
||||
bins = 8
|
||||
axis = compute_distance_axis(freqs, bins, mode="positive_only_exact")
|
||||
|
||||
df_hz = 1e9
|
||||
n_shift = int(np.arange(-6.0, 6.0 + 0.5, 1.0, dtype=np.float64).size)
|
||||
expected_step = C_M_S / (2.0 * n_shift * df_hz)
|
||||
expected = np.arange(bins, dtype=np.float64) * expected_step
|
||||
|
||||
self.assertEqual(axis.shape, (bins,))
|
||||
self.assertTrue(np.allclose(axis, expected))
|
||||
|
||||
def test_resolve_visible_fft_curves_handles_complex_mode(self):
|
||||
complex_row = np.asarray([1.0 + 2.0j, -3.0 + 4.0j], dtype=np.complex64)
|
||||
mag = np.abs(complex_row).astype(np.float32)
|
||||
|
||||
@ -91,6 +91,20 @@ class RingBufferTests(unittest.TestCase):
|
||||
self.assertEqual(ring.last_fft_db.shape, (ring.fft_bins,))
|
||||
self.assertIsNotNone(ring.distance_axis)
|
||||
|
||||
def test_ring_buffer_can_switch_to_positive_only_exact_fft_mode(self):
|
||||
ring = RingBuffer(max_sweeps=2)
|
||||
sweep = np.linspace(0.0, 1.0, 64, dtype=np.float32)
|
||||
freqs = np.linspace(3.3, 14.3, 64, dtype=np.float64)
|
||||
ring.push(sweep, freqs)
|
||||
|
||||
changed = ring.set_fft_mode("positive_only_exact")
|
||||
|
||||
self.assertTrue(changed)
|
||||
self.assertEqual(ring.fft_mode, "positive_only_exact")
|
||||
self.assertIsNotNone(ring.last_fft_db)
|
||||
self.assertEqual(ring.last_fft_db.shape, (ring.fft_bins,))
|
||||
self.assertIsNotNone(ring.distance_axis)
|
||||
|
||||
def test_ring_buffer_rebuilds_fft_from_complex_input(self):
|
||||
ring = RingBuffer(max_sweeps=2)
|
||||
freqs = np.linspace(3.3, 14.3, 64, dtype=np.float64)
|
||||
|
||||
Reference in New Issue
Block a user