Files

254 lines
14 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
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 Welchs 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 windows 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<Complex> 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<Complex>, Span<double>)`**
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<Complex> destination, Span<double> 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<double> destination, Span<double> input)`**
Computes RMS magnitude spectrum (PSD) for real input. DC and Nyquist not doubled; others doubled.
- **`double[] FFTpower(double[] input)` / `void FFTpower(Span<double> destination, double[] input)`**
Computes PSD in dB: `20·log10(FFTmagnitude)`.
- **`double[] PSD_Welch(...)`**
Computes power spectral density using Welchs 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<T>(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.