new fft
This commit is contained in:
@ -10,7 +10,13 @@ import numpy as np
|
||||
WF_WIDTH = 1000
|
||||
|
||||
# Длина БПФ для спектра/водопада спектров
|
||||
FFT_LEN = 1024
|
||||
FFT_LEN = 2048
|
||||
|
||||
# Частотный диапазон для FFT (в ГГц)
|
||||
FREQ_MIN_GHZ = -10.0 # Начало частотной оси
|
||||
FREQ_MAX_GHZ = 10.0 # Конец частотной оси
|
||||
DATA_FREQ_START_GHZ = 1.0 # Начало реальных данных
|
||||
DATA_FREQ_END_GHZ = 10.0 # Конец реальных данных
|
||||
|
||||
# Порог для инверсии сырых данных: если среднее значение свипа ниже порога —
|
||||
# считаем, что сигнал «меньше нуля» и домножаем свип на -1
|
||||
|
||||
@ -18,7 +18,16 @@ try:
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Нужны matplotlib и ее зависимости: {e}")
|
||||
|
||||
from ..config import FFT_LEN, WF_WIDTH, SweepInfo, SweepPacket
|
||||
from ..config import (
|
||||
FFT_LEN,
|
||||
WF_WIDTH,
|
||||
SweepInfo,
|
||||
SweepPacket,
|
||||
FREQ_MIN_GHZ,
|
||||
FREQ_MAX_GHZ,
|
||||
DATA_FREQ_START_GHZ,
|
||||
DATA_FREQ_END_GHZ,
|
||||
)
|
||||
from ..data_acquisition.sweep_reader import SweepReader
|
||||
from ..signal_processing.phase_analysis import apply_temporal_unwrap, phase_to_distance
|
||||
from ..utils.formatting import format_status_kv, parse_spec_clip
|
||||
@ -49,8 +58,8 @@ def run_matplotlib(args):
|
||||
ring_time = None # type: Optional[np.ndarray]
|
||||
head = 0
|
||||
# Авто-уровни цветовой шкалы водопада сырых данных пересчитываются по видимой области.
|
||||
# FFT состояние
|
||||
fft_bins = FFT_LEN // 2 + 1
|
||||
# FFT состояние (полное FFT для отрицательных частот)
|
||||
fft_bins = FFT_LEN
|
||||
ring_fft = None # type: Optional[np.ndarray]
|
||||
y_min_fft, y_max_fft = None, None
|
||||
freq_shared: Optional[np.ndarray] = None
|
||||
@ -86,7 +95,7 @@ def run_matplotlib(args):
|
||||
# Линейный график спектра текущего свипа
|
||||
fft_line_obj, = ax_fft.plot([], [], lw=1)
|
||||
ax_fft.set_title("FFT", pad=1)
|
||||
ax_fft.set_xlabel("X")
|
||||
ax_fft.set_xlabel("Частота, ГГц")
|
||||
ax_fft.set_ylabel("Амплитуда, дБ")
|
||||
|
||||
# Диапазон по Y для последнего свипа: авто по умолчанию (поддерживает отрицательные значения)
|
||||
@ -128,7 +137,7 @@ def run_matplotlib(args):
|
||||
)
|
||||
ax_spec.set_title("B-scan (дБ)", pad=12)
|
||||
ax_spec.set_xlabel("")
|
||||
ax_spec.set_ylabel("расстояние")
|
||||
ax_spec.set_ylabel("Частота, ГГц")
|
||||
# Не показываем численные значения по времени на B-scan
|
||||
try:
|
||||
ax_spec.tick_params(axis="x", labelbottom=False)
|
||||
@ -138,7 +147,7 @@ def run_matplotlib(args):
|
||||
# График фазы текущего свипа
|
||||
phase_line_obj, = ax_phase.plot([], [], lw=1)
|
||||
ax_phase.set_title("Фаза спектра (развернутая)", pad=1)
|
||||
ax_phase.set_xlabel("Бин")
|
||||
ax_phase.set_xlabel("Частота, ГГц")
|
||||
ax_phase.set_ylabel("Фаза, радианы")
|
||||
|
||||
# Добавим второй Y axis для расстояния
|
||||
@ -155,7 +164,7 @@ def run_matplotlib(args):
|
||||
)
|
||||
ax_phase_wf.set_title("Водопад фазы", pad=12)
|
||||
ax_phase_wf.set_xlabel("")
|
||||
ax_phase_wf.set_ylabel("Бин")
|
||||
ax_phase_wf.set_ylabel("Частота, ГГц")
|
||||
# Не показываем численные значения по времени
|
||||
try:
|
||||
ax_phase_wf.tick_params(axis="x", labelbottom=False)
|
||||
@ -166,14 +175,14 @@ def run_matplotlib(args):
|
||||
ax_smin = fig.add_axes([0.92, 0.55, 0.02, 0.35])
|
||||
ax_smax = fig.add_axes([0.95, 0.55, 0.02, 0.35])
|
||||
ax_sctr = fig.add_axes([0.98, 0.55, 0.02, 0.35])
|
||||
ymin_slider = Slider(ax_smin, "Y min", 0, max(1, fft_bins - 1), valinit=0, valstep=1, orientation="vertical")
|
||||
ymax_slider = Slider(ax_smax, "Y max", 0, max(1, fft_bins - 1), valinit=max(1, fft_bins - 1), valstep=1, orientation="vertical")
|
||||
ymin_slider = Slider(ax_smin, "Y min", FREQ_MIN_GHZ, FREQ_MAX_GHZ, valinit=FREQ_MIN_GHZ, valstep=0.1, orientation="vertical")
|
||||
ymax_slider = Slider(ax_smax, "Y max", FREQ_MIN_GHZ, FREQ_MAX_GHZ, valinit=FREQ_MAX_GHZ, valstep=0.1, orientation="vertical")
|
||||
contrast_slider = Slider(ax_sctr, "Int max", 0, 100, valinit=100, valstep=1, orientation="vertical")
|
||||
|
||||
def _on_ylim_change(_val):
|
||||
try:
|
||||
y0 = int(min(ymin_slider.val, ymax_slider.val))
|
||||
y1 = int(max(ymin_slider.val, ymax_slider.val))
|
||||
y0 = float(min(ymin_slider.val, ymax_slider.val))
|
||||
y1 = float(max(ymin_slider.val, ymax_slider.val))
|
||||
ax_spec.set_ylim(y0, y1)
|
||||
fig.canvas.draw_idle()
|
||||
except Exception:
|
||||
@ -209,18 +218,18 @@ def run_matplotlib(args):
|
||||
# FFT буферы: время по X, бин по Y
|
||||
ring_fft = np.full((max_sweeps, fft_bins), np.nan, dtype=np.float32)
|
||||
img_fft_obj.set_data(np.zeros((fft_bins, max_sweeps), dtype=np.float32))
|
||||
img_fft_obj.set_extent((0, max_sweeps - 1, 0, fft_bins - 1))
|
||||
img_fft_obj.set_extent((0, max_sweeps - 1, FREQ_MIN_GHZ, FREQ_MAX_GHZ))
|
||||
ax_spec.set_xlim(0, max_sweeps - 1)
|
||||
ax_spec.set_ylim(0, max(1, fft_bins - 1))
|
||||
freq_shared = np.arange(fft_bins, dtype=np.int32)
|
||||
ax_spec.set_ylim(FREQ_MIN_GHZ, FREQ_MAX_GHZ)
|
||||
freq_shared = np.linspace(FREQ_MIN_GHZ, FREQ_MAX_GHZ, fft_bins, dtype=np.float32)
|
||||
# Phase буферы: время по X, бин по Y
|
||||
ring_phase = np.full((max_sweeps, fft_bins), np.nan, dtype=np.float32)
|
||||
prev_phase_per_bin = np.zeros(fft_bins, dtype=np.float32)
|
||||
phase_offset_per_bin = np.zeros(fft_bins, dtype=np.float32)
|
||||
img_phase_obj.set_data(np.zeros((fft_bins, max_sweeps), dtype=np.float32))
|
||||
img_phase_obj.set_extent((0, max_sweeps - 1, 0, fft_bins - 1))
|
||||
img_phase_obj.set_extent((0, max_sweeps - 1, FREQ_MIN_GHZ, FREQ_MAX_GHZ))
|
||||
ax_phase_wf.set_xlim(0, max_sweeps - 1)
|
||||
ax_phase_wf.set_ylim(0, max(1, fft_bins - 1))
|
||||
ax_phase_wf.set_ylim(FREQ_MIN_GHZ, FREQ_MAX_GHZ)
|
||||
|
||||
def _visible_levels_matplotlib(data: np.ndarray, axis) -> Optional[Tuple[float, float]]:
|
||||
"""(vmin, vmax) по текущей видимой области imshow (без накопления по времени)."""
|
||||
@ -277,21 +286,41 @@ def run_matplotlib(args):
|
||||
fft_row = np.full((bins,), np.nan, dtype=np.float32)
|
||||
phase_row = np.full((bins,), np.nan, dtype=np.float32)
|
||||
else:
|
||||
# Создаем буфер для полного FFT (с отрицательными частотами)
|
||||
fft_in = np.zeros((FFT_LEN,), dtype=np.float32)
|
||||
seg = s[:take_fft]
|
||||
|
||||
# Вычисляем индексы для размещения данных (1-10 ГГц в диапазоне -10 до +10 ГГц)
|
||||
freq_range_total = FREQ_MAX_GHZ - FREQ_MIN_GHZ # 20 ГГц
|
||||
freq_range_data = DATA_FREQ_END_GHZ - DATA_FREQ_START_GHZ # 9 ГГц
|
||||
|
||||
# Начальный индекс для данных в FFT буфере
|
||||
start_idx = int((DATA_FREQ_START_GHZ - FREQ_MIN_GHZ) / freq_range_total * FFT_LEN)
|
||||
# Количество точек для данных
|
||||
data_points = int(freq_range_data / freq_range_total * FFT_LEN)
|
||||
data_points = min(data_points, take_fft, FFT_LEN - start_idx)
|
||||
|
||||
# Подготовка данных
|
||||
seg = s[:data_points]
|
||||
if isinstance(seg, np.ndarray):
|
||||
seg = np.nan_to_num(seg, nan=0.0).astype(np.float32, copy=False)
|
||||
else:
|
||||
seg = np.asarray(seg, dtype=np.float32)
|
||||
seg = np.nan_to_num(seg, nan=0.0)
|
||||
|
||||
# Окно Хэннинга
|
||||
win = np.hanning(take_fft).astype(np.float32)
|
||||
fft_in[:take_fft] = seg * win
|
||||
spec = np.fft.rfft(fft_in)
|
||||
win = np.hanning(data_points).astype(np.float32)
|
||||
|
||||
# Размещаем данные в правильной позиции
|
||||
fft_in[start_idx:start_idx + data_points] = seg * win
|
||||
|
||||
# Полное FFT (включая отрицательные частоты)
|
||||
spec = np.fft.fft(fft_in)
|
||||
# Сдвигаем для центрирования нулевой частоты
|
||||
spec = np.fft.fftshift(spec)
|
||||
|
||||
mag = np.abs(spec).astype(np.float32)
|
||||
fft_row = 20.0 * np.log10(mag + 1e-9)
|
||||
if fft_row.shape[0] != bins:
|
||||
# rfft длиной FFT_LEN даёт bins == FFT_LEN//2+1
|
||||
fft_row = fft_row[:bins]
|
||||
|
||||
# Расчет фазы
|
||||
@ -398,11 +427,31 @@ def run_matplotlib(args):
|
||||
# Обновление спектра и фазы текущего свипа
|
||||
take_fft = min(int(current_sweep.size), FFT_LEN)
|
||||
if take_fft > 0 and freq_shared is not None:
|
||||
# Создаем буфер для полного FFT (с отрицательными частотами)
|
||||
fft_in = np.zeros((FFT_LEN,), dtype=np.float32)
|
||||
seg = np.nan_to_num(current_sweep[:take_fft], nan=0.0).astype(np.float32, copy=False)
|
||||
win = np.hanning(take_fft).astype(np.float32)
|
||||
fft_in[:take_fft] = seg * win
|
||||
spec = np.fft.rfft(fft_in)
|
||||
|
||||
# Вычисляем индексы для размещения данных (1-10 ГГц в диапазоне -10 до +10 ГГц)
|
||||
freq_range_total = FREQ_MAX_GHZ - FREQ_MIN_GHZ # 20 ГГц
|
||||
freq_range_data = DATA_FREQ_END_GHZ - DATA_FREQ_START_GHZ # 9 ГГц
|
||||
|
||||
# Начальный индекс для данных в FFT буфере
|
||||
start_idx = int((DATA_FREQ_START_GHZ - FREQ_MIN_GHZ) / freq_range_total * FFT_LEN)
|
||||
# Количество точек для данных
|
||||
data_points = int(freq_range_data / freq_range_total * FFT_LEN)
|
||||
data_points = min(data_points, take_fft, FFT_LEN - start_idx)
|
||||
|
||||
# Подготовка данных с окном Хэннинга
|
||||
seg = np.nan_to_num(current_sweep[:data_points], nan=0.0).astype(np.float32, copy=False)
|
||||
win = np.hanning(data_points).astype(np.float32)
|
||||
|
||||
# Размещаем данные в правильной позиции
|
||||
fft_in[start_idx:start_idx + data_points] = seg * win
|
||||
|
||||
# Полное FFT (включая отрицательные частоты)
|
||||
spec = np.fft.fft(fft_in)
|
||||
# Сдвигаем для центрирования нулевой частоты
|
||||
spec = np.fft.fftshift(spec)
|
||||
|
||||
mag = np.abs(spec).astype(np.float32)
|
||||
fft_vals = 20.0 * np.log10(mag + 1e-9)
|
||||
xs_fft = freq_shared
|
||||
@ -411,7 +460,7 @@ def run_matplotlib(args):
|
||||
fft_line_obj.set_data(xs_fft[: fft_vals.size], fft_vals)
|
||||
# Авто-диапазон по Y для спектра
|
||||
if np.isfinite(np.nanmin(fft_vals)) and np.isfinite(np.nanmax(fft_vals)):
|
||||
ax_fft.set_xlim(0, max(1, xs_fft.size - 1))
|
||||
ax_fft.set_xlim(FREQ_MIN_GHZ, FREQ_MAX_GHZ)
|
||||
ax_fft.set_ylim(float(np.nanmin(fft_vals)), float(np.nanmax(fft_vals)))
|
||||
|
||||
# Расчет и отображение фазы текущего свипа
|
||||
@ -423,7 +472,7 @@ def run_matplotlib(args):
|
||||
phase_line_obj.set_data(xs_fft[: phase_unwrapped.size], phase_unwrapped)
|
||||
# Авто-диапазон по Y для фазы
|
||||
if np.isfinite(np.nanmin(phase_unwrapped)) and np.isfinite(np.nanmax(phase_unwrapped)):
|
||||
ax_phase.set_xlim(0, max(1, xs_fft.size - 1))
|
||||
ax_phase.set_xlim(FREQ_MIN_GHZ, FREQ_MAX_GHZ)
|
||||
phase_min = float(np.nanmin(phase_unwrapped))
|
||||
phase_max = float(np.nanmax(phase_unwrapped))
|
||||
ax_phase.set_ylim(phase_min, phase_max)
|
||||
|
||||
@ -23,7 +23,16 @@ except Exception:
|
||||
"pyqtgraph/PyQt5(Pyside6) не найдены. Установите: pip install pyqtgraph PyQt5"
|
||||
) from e
|
||||
|
||||
from ..config import FFT_LEN, WF_WIDTH, SweepInfo, SweepPacket
|
||||
from ..config import (
|
||||
FFT_LEN,
|
||||
WF_WIDTH,
|
||||
SweepInfo,
|
||||
SweepPacket,
|
||||
FREQ_MIN_GHZ,
|
||||
FREQ_MAX_GHZ,
|
||||
DATA_FREQ_START_GHZ,
|
||||
DATA_FREQ_END_GHZ,
|
||||
)
|
||||
from ..data_acquisition.sweep_reader import SweepReader
|
||||
from ..signal_processing.phase_analysis import apply_temporal_unwrap, phase_to_distance
|
||||
from ..utils.formatting import format_status_kv, parse_spec_clip
|
||||
@ -72,7 +81,7 @@ def run_pyqtgraph(args):
|
||||
p_fft = win.addPlot(row=1, col=0, title="FFT")
|
||||
p_fft.showGrid(x=True, y=True, alpha=0.3)
|
||||
curve_fft = p_fft.plot(pen=pg.mkPen((255, 120, 80), width=1))
|
||||
p_fft.setLabel("bottom", "Бин")
|
||||
p_fft.setLabel("bottom", "Частота, ГГц")
|
||||
p_fft.setLabel("left", "Амплитуда, дБ")
|
||||
|
||||
# Водопад спектров (справа-средний ряд)
|
||||
@ -84,7 +93,7 @@ def run_pyqtgraph(args):
|
||||
p_spec.getAxis("bottom").setStyle(showValues=False)
|
||||
except Exception:
|
||||
pass
|
||||
p_spec.setLabel("left", "Бин (0 снизу)")
|
||||
p_spec.setLabel("left", "Частота, ГГц (0 снизу)")
|
||||
img_fft = pg.ImageItem()
|
||||
p_spec.addItem(img_fft)
|
||||
|
||||
@ -92,7 +101,7 @@ def run_pyqtgraph(args):
|
||||
p_phase = win.addPlot(row=2, col=0, title="Фаза спектра (развернутая)")
|
||||
p_phase.showGrid(x=True, y=True, alpha=0.3)
|
||||
curve_phase = p_phase.plot(pen=pg.mkPen((120, 255, 80), width=1))
|
||||
p_phase.setLabel("bottom", "Бин")
|
||||
p_phase.setLabel("bottom", "Частота, ГГц")
|
||||
p_phase.setLabel("left", "Фаза, радианы")
|
||||
# Добавим вторую ось Y для расстояния
|
||||
p_phase_dist_axis = pg.ViewBox()
|
||||
@ -121,7 +130,7 @@ def run_pyqtgraph(args):
|
||||
p_phase_wf.getAxis("bottom").setStyle(showValues=False)
|
||||
except Exception:
|
||||
pass
|
||||
p_phase_wf.setLabel("left", "Бин (0 снизу)")
|
||||
p_phase_wf.setLabel("left", "Частота, ГГц (0 снизу)")
|
||||
img_phase = pg.ImageItem()
|
||||
p_phase_wf.addItem(img_phase)
|
||||
|
||||
@ -137,8 +146,8 @@ def run_pyqtgraph(args):
|
||||
current_sweep: Optional[np.ndarray] = None
|
||||
current_info: Optional[SweepInfo] = None
|
||||
# Авто-уровни цветовой шкалы водопада сырых данных пересчитываются по видимой области.
|
||||
# Для спектров
|
||||
fft_bins = FFT_LEN // 2 + 1
|
||||
# Для спектров (полное FFT для отрицательных частот)
|
||||
fft_bins = FFT_LEN
|
||||
ring_fft: Optional[np.ndarray] = None
|
||||
freq_shared: Optional[np.ndarray] = None
|
||||
y_min_fft, y_max_fft = None, None
|
||||
@ -177,8 +186,8 @@ def run_pyqtgraph(args):
|
||||
ring_fft = np.full((max_sweeps, fft_bins), np.nan, dtype=np.float32)
|
||||
img_fft.setImage(ring_fft.T, autoLevels=False)
|
||||
p_spec.setRange(xRange=(0, max_sweeps - 1), yRange=(0, max(1, fft_bins - 1)), padding=0)
|
||||
p_fft.setXRange(0, max(1, fft_bins - 1), padding=0)
|
||||
freq_shared = np.arange(fft_bins, dtype=np.int32)
|
||||
p_fft.setXRange(FREQ_MIN_GHZ, FREQ_MAX_GHZ, padding=0)
|
||||
freq_shared = np.linspace(FREQ_MIN_GHZ, FREQ_MAX_GHZ, fft_bins, dtype=np.float32)
|
||||
# Phase: время по оси X, бин по оси Y
|
||||
ring_phase = np.full((max_sweeps, fft_bins), np.nan, dtype=np.float32)
|
||||
prev_phase_per_bin = np.zeros(fft_bins, dtype=np.float32)
|
||||
@ -234,11 +243,33 @@ def run_pyqtgraph(args):
|
||||
bins = ring_fft.shape[1]
|
||||
take_fft = min(int(s.size), FFT_LEN)
|
||||
if take_fft > 0:
|
||||
# Создаем буфер для полного FFT (с отрицательными частотами)
|
||||
fft_in = np.zeros((FFT_LEN,), dtype=np.float32)
|
||||
seg = np.nan_to_num(s[:take_fft], nan=0.0).astype(np.float32, copy=False)
|
||||
win = np.hanning(take_fft).astype(np.float32)
|
||||
fft_in[:take_fft] = seg * win
|
||||
spec = np.fft.rfft(fft_in)
|
||||
|
||||
# Вычисляем индексы для размещения данных (1-10 ГГц в диапазоне -10 до +10 ГГц)
|
||||
# Диапазон данных: от DATA_FREQ_START_GHZ (1) до DATA_FREQ_END_GHZ (10)
|
||||
# Полный диапазон: от FREQ_MIN_GHZ (-10) до FREQ_MAX_GHZ (10)
|
||||
freq_range_total = FREQ_MAX_GHZ - FREQ_MIN_GHZ # 20 ГГц
|
||||
freq_range_data = DATA_FREQ_END_GHZ - DATA_FREQ_START_GHZ # 9 ГГц
|
||||
|
||||
# Начальный индекс для данных в FFT буфере
|
||||
start_idx = int((DATA_FREQ_START_GHZ - FREQ_MIN_GHZ) / freq_range_total * FFT_LEN)
|
||||
# Количество точек для данных
|
||||
data_points = int(freq_range_data / freq_range_total * FFT_LEN)
|
||||
data_points = min(data_points, take_fft, FFT_LEN - start_idx)
|
||||
|
||||
# Подготовка данных с окном Хэннинга
|
||||
seg = np.nan_to_num(s[:data_points], nan=0.0).astype(np.float32, copy=False)
|
||||
win = np.hanning(data_points).astype(np.float32)
|
||||
|
||||
# Размещаем данные в правильной позиции (от -10 до 1 ГГц - нули, от 1 до 10 ГГц - данные)
|
||||
fft_in[start_idx:start_idx + data_points] = seg * win
|
||||
|
||||
# Полное FFT (включая отрицательные частоты)
|
||||
spec = np.fft.fft(fft_in)
|
||||
# Сдвигаем для центрирования нулевой частоты
|
||||
spec = np.fft.fftshift(spec)
|
||||
|
||||
mag = np.abs(spec).astype(np.float32)
|
||||
fft_row = 20.0 * np.log10(mag + 1e-9)
|
||||
if fft_row.shape[0] != bins:
|
||||
@ -320,11 +351,31 @@ def run_pyqtgraph(args):
|
||||
# Обновим спектр и фазу
|
||||
take_fft = min(int(current_sweep.size), FFT_LEN)
|
||||
if take_fft > 0 and freq_shared is not None:
|
||||
# Создаем буфер для полного FFT (с отрицательными частотами)
|
||||
fft_in = np.zeros((FFT_LEN,), dtype=np.float32)
|
||||
seg = np.nan_to_num(current_sweep[:take_fft], nan=0.0).astype(np.float32, copy=False)
|
||||
win = np.hanning(take_fft).astype(np.float32)
|
||||
fft_in[:take_fft] = seg * win
|
||||
spec = np.fft.rfft(fft_in)
|
||||
|
||||
# Вычисляем индексы для размещения данных (1-10 ГГц в диапазоне -10 до +10 ГГц)
|
||||
freq_range_total = FREQ_MAX_GHZ - FREQ_MIN_GHZ # 20 ГГц
|
||||
freq_range_data = DATA_FREQ_END_GHZ - DATA_FREQ_START_GHZ # 9 ГГц
|
||||
|
||||
# Начальный индекс для данных в FFT буфере
|
||||
start_idx = int((DATA_FREQ_START_GHZ - FREQ_MIN_GHZ) / freq_range_total * FFT_LEN)
|
||||
# Количество точек для данных
|
||||
data_points = int(freq_range_data / freq_range_total * FFT_LEN)
|
||||
data_points = min(data_points, take_fft, FFT_LEN - start_idx)
|
||||
|
||||
# Подготовка данных с окном Хэннинга
|
||||
seg = np.nan_to_num(current_sweep[:data_points], nan=0.0).astype(np.float32, copy=False)
|
||||
win = np.hanning(data_points).astype(np.float32)
|
||||
|
||||
# Размещаем данные в правильной позиции
|
||||
fft_in[start_idx:start_idx + data_points] = seg * win
|
||||
|
||||
# Полное FFT (включая отрицательные частоты)
|
||||
spec = np.fft.fft(fft_in)
|
||||
# Сдвигаем для центрирования нулевой частоты
|
||||
spec = np.fft.fftshift(spec)
|
||||
|
||||
mag = np.abs(spec).astype(np.float32)
|
||||
fft_vals = 20.0 * np.log10(mag + 1e-9)
|
||||
xs_fft = freq_shared
|
||||
|
||||
Reference in New Issue
Block a user