from __future__ import annotations import numpy as np from backend.node_registry import register_node from backend.data_types import LineData, RecordTable @register_node(display_name="FFT 1D") class FFT1D: @classmethod def INPUT_TYPES(cls): return { "required": { "profile": ("LINE", { "label": "input", "accepted_types": ["LINE"], }), } } OUTPUTS = ( ("LINE", "frequency_plot"), ('RECORD_TABLE', 'max'), ) FUNCTION = "process" DESCRIPTION = ( "Returns the FFT spectrum of the line, and identifies peaks." ) KEYWORDS = ("fourier", "frequency", "spectrum", "period") def process( self, profile, ) -> tuple: line_data = np.asarray(profile, dtype=np.float64) n = len(line_data) if isinstance(profile, LineData) and profile.x_axis is not None and len(profile.x_axis) > 1: d = float(profile.x_axis[1] - profile.x_axis[0]) spatial_unit = profile.x_unit or "m" else: d = 1.0 spatial_unit = "m" spectrum = np.abs(np.fft.rfft(line_data)) freq_axis = np.fft.rfftfreq(n, d) # Exclude DC component, convert to period, sort short→long spectrum = spectrum[1:][::-1] period_axis = (1.0 / freq_axis[1:])[::-1] peak_period = float(period_axis[np.argmax(spectrum)]) table = RecordTable([ {"quantity": "Peak period", "value": peak_period, "unit": spatial_unit}, ]) return ( LineData( data=spectrum, x_axis=period_axis, x_unit=spatial_unit, y_unit=profile.y_unit if isinstance(profile, LineData) else "", ), table, )