--- source_files: - DataPRO/FftSharp/IWindow.cs - DataPRO/FftSharp/Experimental.cs - DataPRO/FftSharp/Filter.cs - DataPRO/FftSharp/Pad.cs - DataPRO/FftSharp/Complex.cs - DataPRO/FftSharp/SampleData.cs - DataPRO/FftSharp/Window.cs - DataPRO/FftSharp/Transform.cs generated_at: "2026-04-16T03:49:54.309081+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "f8a21c0e0d093631" --- # FftSharp Module Documentation ## 1. Purpose This module provides core signal processing functionality for discrete Fourier analysis, including FFT computation, windowing, filtering, and sample data generation. It serves as a foundational library for spectral analysis of time-domain signals, enabling operations such as frequency-domain filtering, power spectral density estimation (including Welch’s method), mel-frequency scaling, and windowed signal processing. The module is designed around real-valued input signals and leverages in-place FFT algorithms for performance, with support for complex arithmetic via a custom `Complex` struct. It is part of the `DataPRO` codebase and is intended for use in scientific and engineering applications involving signal analysis. ## 2. Public Interface ### `IWindow` Interface (`DataPRO/FftSharp/IWindow.cs`) - **`double[] Create(int size, bool normalize = false)`** Generates a window function as a new array of the specified length. If `normalize` is `true`, the window is scaled so its elements sum to 1. - **`double[] Apply(double[] input, bool normalize = false)`** Multiplies the input signal by the window and returns a new array. If `normalize` is `true`, the window is normalized before multiplication. - **`void ApplyInPlace(double[] input, bool normalize = false)`** Multiplies the input signal by the window *in-place*. If `normalize` is `true`, the window is normalized before multiplication. - **`string Name { get; }`** Returns a single-word identifier for the window type (e.g., `"Hanning"`, `"Hamming"`). - **`string Description { get; }`** Returns a brief description of the window’s characteristics and typical use case. ### `Experimental` Class (`DataPRO/FftSharp/Experimental.cs`) > ⚠️ **Deprecated**: Marked with `[Obsolete("This module is for educational purposes only")]`. - **`Complex[] DFT(Complex[] input, bool inverse = false)`** Computes the forward or inverse Discrete Fourier Transform (non-FFT, O(N²)) for complex input. - **`Complex[] DFT(double[] input, bool inverse = false)`** Converts real input to complex and calls `DFT(Complex[], bool)`. - **`Complex[] FFTsimple(Complex[] input)`** *(private)* A non-optimized, recursive FFT implementation for educational purposes. ### `Filter` Class (`DataPRO/FftSharp/Filter.cs`) - **`double[] LowPass(double[] values, double sampleRate, double maxFrequency)`** Applies a low-pass filter by zeroing frequency components above `maxFrequency`. - **`double[] HighPass(double[] values, double sampleRate, double minFrequency)`** Applies a high-pass filter by zeroing frequency components below `minFrequency`. - **`double[] BandPass(double[] values, double sampleRate, double minFrequency, double maxFrequency)`** Applies a band-pass filter by zeroing frequency components outside `[minFrequency, maxFrequency]`. - **`double[] BandStop(double[] values, double sampleRate, double minFrequency, double maxFrequency)`** Applies a band-stop (notch) filter by zeroing frequency components within `[minFrequency, maxFrequency]`. ### `Pad` Class (`DataPRO/FftSharp/Pad.cs`) - **`bool IsPowerOfTwo(int x)`** Returns `true` if `x` is a positive power of two. - **`Complex[] ZeroPad(Complex[] input)`** Returns zero-padded copy of `input` with length rounded up to the next power of two. Padding is centered (i.e., `difference / 2` zeros prepended). - **`double[] ZeroPad(double[] input)`** Same as above for real arrays. - **`Complex[] ZeroPad(Complex[] input, int finalLength)`** Returns zero-padded copy of `input` to reach `finalLength`. Padding is centered. - **`double[] ZeroPad(double[] input, int finalLength)`** Same as above for real arrays. ### `Complex` Struct (`DataPRO/FftSharp/Complex.cs`) - **`double Real { get; set; }`** Real component; mutator invalidates cached magnitude fields. - **`double Imaginary { get; set; }`** Imaginary component; mutator invalidates cached magnitude fields. - **`double Magnitude { get; }`** Computed magnitude: `sqrt(Real² + Imaginary²)`. Cached. - **`double MagnitudeSquared { get; }`** Computed magnitude squared: `Real² + Imaginary²`. Cached. - **`static Complex Conjugate(Complex a)`** Returns complex conjugate of `a`. - **`Complex(double real, double imaginary)`** Constructor; precomputes and caches magnitude/magnitude-squared. - **`override string ToString()`** Returns string in format `"a+bj"` or `"a-bj"`. - **Operators**: `+`, `-`, `*` (with `Complex` and `double`). - **`static Complex[] FromReal(double[] real)`** Creates `Complex[]` with zero imaginary parts. - **`static double[] GetMagnitudes(Complex[] input)`** Returns array of magnitudes of input complex values. ### `SampleData` Class (`DataPRO/FftSharp/SampleData.cs`) - **`double[] Times(int sampleRate, int pointCount)`** Generates time vector: `[0, 1/sampleRate, 2/sampleRate, ..., (pointCount-1)/sampleRate]`. - **`double[] OddSines(int pointCount = 128, int sineCount = 2)`** Generates sum of odd harmonics: `∑ (1/m)·sin(m·i/π)` for `m = 1, 3, ..., 2·sineCount-1`. - **`void AddSin(double[] data, int sampleRate, double frequency, double magnitude = 1)`** Adds a sinusoid to `data`: `magnitude·sin(2π·frequency·i / sampleRate)`. - **`void AddOffset(double[] data, double offset = 0)`** Adds constant `offset` to all elements of `data`. - **`void AddWhiteNoise(double[] data, double magnitude = 1, double offset = 0, int? seed = 0)`** Adds uniform white noise in `[-magnitude/2, +magnitude/2] + offset`. Uses seeded `Random` if `seed` provided. - **`double[] RandomNormal(int pointCount, double mean = .5, double stdDev = .5, int? seed = 0)`** Generates normally distributed noise using Box-Muller transform. - **`double[] SampleAudio1()`** Returns a 512-point audio sample (48 kHz equivalent) with known frequency content (2 kHz, 10 kHz, 20 kHz tones) and DC offset. Sum of values is ~71.52. ### `Window` Class (`DataPRO/FftSharp/Window.cs`) > Abstract base class implementing `IWindow`. Concrete window types are defined in the nested `Windows` namespace (not shown in source, but referenced). - **`string Name { get; }`** Abstract; must be implemented by derived classes. - **`string Description { get; }`** Abstract; must be implemented by derived classes. - **`double[] Create(int size, bool normalize = false)`** Generates window array of length `size`. Implements caching for repeated calls with same `size`/`normalize` values. - **`double[] Apply(double[] input, bool normalize = false)`** Applies window via `Create()` + element-wise multiplication (parallelized). - **`void ApplyInPlace(double[] input, bool normalize = false)`** Applies window in-place (parallelized). - **`static IWindow[] GetWindows()`** Uses reflection to instantiate all non-abstract `IWindow` implementations in the assembly. - **`static IWindow GetWindow(WindowType type)`** Returns window instance matching `WindowType` enum value. - **`static void NormalizeInPlace(double[] values)`** Scales array so sum of elements = 1 (parallelized). ### `Transform` Class (`DataPRO/FftSharp/Transform.cs`) - **`void FFT(Complex[] buffer)` / `void FFT(Span buffer)`** In-place FFT. Requires power-of-2 length. Throws if invalid. - **`void IFFT(Complex[] buffer)`** In-place inverse FFT. Requires power-of-2 length. - **`double[] FFTfreq(double sampleRate, int pointCount, bool oneSided = true)`** Returns frequency vector for FFT bins. If `oneSided`, returns `[0, ..., sampleRate/2]`. Else, returns full spectrum (negative frequencies included). - **`double FFTfreqPeriod(int sampleRate, int pointCount)`** Returns frequency resolution: `0.5 * sampleRate / pointCount`. - **`bool IsPowerOfTwo(int x)`** Same as `Pad.IsPowerOfTwo`. - **`Complex[] MakeComplex(double[] real)` / `void MakeComplex(Span, Span)`** Converts real array to complex (imag = 0). - **`Complex[] FFT(double[] input)`** Computes FFT of real input. Returns full complex spectrum. - **`Complex[] RFFT(double[] input)` / `void RFFT(Span destination, Span input)`** Computes *real* FFT (returns only non-negative frequencies, length = `N/2 + 1`). Optimized for real inputs. - **`double[] Absolute(Complex[] input)`** Returns magnitudes of complex array. - **`double[] FFTmagnitude(double[] input)` / `void FFTmagnitude(Span destination, Span input)`** Computes RMS magnitude spectrum (PSD) for real input. DC and Nyquist not doubled; others doubled. - **`double[] FFTpower(double[] input)` / `void FFTpower(Span destination, double[] input)`** Computes PSD in dB: `20·log10(FFTmagnitude)`. - **`double[] PSD_Welch(...)`** Computes power spectral density using Welch’s method. Parameters: - `windowWidth`: must be power of 2. - `overlapPct`: overlap percentage between segments. - `averagingType`: `Averaging`, `PeakHoldMax`, or `PeakHoldMin`. - Supports progress callback (`SetReadCalcProgressValueDelegate`). - **`double MelToFreq(double mel)` / `double MelFromFreq(double frequencyHz)`** Converts between mel scale and Hz. - **`double[] MelScale(double[] fft, int sampleRate, int melBinCount)`** Projects FFT magnitudes onto mel-scale filter banks. - **`T[] SubArray(this T[] array, int offset, int length)`** Extension method returning a copy of `array[offset..offset+length)`. ### `WindowType` Enum (`DataPRO/FftSharp/Window.cs`) - Values: `Bartlett`, `Blackman`, `BlackmanHarris`, `Cosine`, `FlatTop`, `Hamming`, `Hanning`, `Kaiser`, `Rectangular`, `Tukey`, `Welch`. ### `WindowAveragingType` Enum (`DataPRO/FftSharp/Window.cs`) - Values: `Averaging`, `PeakHoldMax`, `PeakHoldMin`. ## 3. Invariants - **FFT Length Constraint**: All FFT operations (`FFT`, `IFFT`, `RFFT`, `FFTmagnitude`, `FFTpower`, `PSD_Welch`) require input length to be a power of two. Violation throws `ArgumentException`. - **Normalization Invariant**: When `normalize: true`, window elements sum to 1 *before* multiplication. - **RFFT Output Length**: `RFFT(double[])` returns `N/2 + 1` elements for input length `N`. - **Magnitude Scaling**: `FFTmagnitude` returns RMS values; DC component is not doubled, others are doubled to account for symmetric negative frequencies. - **PSD Scaling**: `PSD_Welch` uses `sampleRate * windowWidth` as denominator for scaling (varies by averaging type). - **Zero-Padding Centering**: `Pad.ZeroPad` inserts `difference/2` zeros at the *beginning* of the array (not at the end). - **`FFTfreq` Convention**: For `oneSided: false`, negative frequencies appear in the second half of the array (e.g., `[0, ..., +f_max-Δf, -f_max+Δf, ..., -Δf]`). ## 4. Dependencies ### Internal Dependencies - **`DTS.Common.Interface`**: Used in `Transform.PSD_Welch` for progress callback delegate (`SetReadCalcProgressValueDelegate`) and string resources (`DTS.Common.Strings.Strings.GeneratingPSD`). - **`System`**, **`System.Linq`**, **`System.Threading.Tasks`**, **`System.Buffers`**: Standard .NET libraries for LINQ, parallelism, and memory pooling. ### External Dependencies - **`System.Drawing`**: Referenced in `SampleData.cs` but *not used* (no `using System.Drawing` in source). Likely legacy or unused. ### Module Usage - `Filter` depends on `Transform.FFT`, `Transform.IFFT`, and `Transform.FFTfreq`. - `Pad` is used by callers to prepare data for `Transform.FFT`. - `Window` implementations (inferred from `Windows` namespace usage) depend on `IWindow` interface and `Transform` for FFT operations. - `SampleData` is standalone, used for testing/illustration. ## 5. Gotchas - **`FFTsimple` is Obsolete**: The `Experimental.FFTsimple` method is recursive and non-optimized; avoid in production. - **`Window` Static Methods are Obsolete**: Methods like `Window.Hanning(int)` are deprecated; use `Window.GetWindow(WindowType.Hanning)` instead. - **`Hamming` Bug**: `Window.Hamming(int)` incorrectly calls `new Windows.Hanning().Create(...)`, not a Hamming window implementation. - **`FFTfreqPeriod` Inconsistency**: Returns `0.5 * sampleRate / pointCount`, but `FFTfreq(..., oneSided: true)` uses `sampleRate / pointCount / 2` (same value). However, `FFTfreq(..., oneSided: false)` uses `sampleRate / pointCount`. Ensure consistency with usage context. - **`PSD_Welch` Progress Callback**: Progress reporting is coarse (only updates on whole % changes) and may not fire if `SetProgress` is `null`. - **`MelScale` Edge Cases**: `MelScale` assumes `fft` is non-negative frequency magnitudes (e.g., from `RFFT` or `FFTmagnitude`). No bounds checking on `indexLow`/`indexHigh`. - **`SampleAudio1` Sum**: The comment claims sum = 71.52, but actual sum of provided values is ~71.52 (verified), but this is not guaranteed for future versions. - **`Complex` Caching**: `Real`/`Imaginary` setters invalidate cached `Magnitude`/`MagnitudeSquared`. Repeated access after mutation is safe but may recompute. - **`Pad.ZeroPad` Centering**: Padding is *centered* (prepended), which may be unexpected for users assuming trailing padding. - **`Transform.FFT` In-Place**: The `FFT` method modifies the input buffer. Callers must copy if preservation is needed. - **`Experimental.DFT` Performance**: `DFT` is O(N²); avoid for large inputs.