Files
DP44/docs/ai/DataPRO/FftSharp.md

248 lines
12 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-17T15:50:04.390408+00:00"
model: "zai-org/GLM-5-FP8"
schema_version: 1
sha256: "2a9358b1fffcd8a5"
---
# FftSharp Library Documentation
## 1. Purpose
FftSharp is a .NET library providing Fast Fourier Transform (FFT) operations and related signal processing utilities. It exists to enable frequency-domain analysis of signals through forward/inverse FFT, windowing functions, spectral filtering, and power spectral density calculations. The library serves as a core signal processing component, offering both high-performance production implementations and educational reference implementations of the DFT algorithm.
---
## 2. Public Interface
### Complex (struct)
A mutable struct representing a complex number with cached magnitude calculations.
**Properties:**
- `double Real` - Real component (setter invalidates cached magnitude values)
- `double Imaginary` - Imaginary component (setter invalidates cached magnitude values)
- `double Magnitude` - Computed as `sqrt(Real² + Imaginary²)` (cached)
- `double MagnitudeSquared` - Computed as `Real² + Imaginary²` (cached)
**Constructor:**
- `Complex(double real, double imaginary)`
**Static Methods:**
- `Complex Conjugate(Complex a)` - Returns complex conjugate with negated imaginary component
- `Complex[] FromReal(double[] real)` - Creates Complex array from real values (imaginary = 0)
- `double[] GetMagnitudes(Complex[] input)` - Extracts magnitude array from Complex array
**Operators:**
- `operator +(Complex a, Complex b)` - Complex addition
- `operator -(Complex a, Complex b)` - Complex subtraction
- `operator *(Complex a, Complex b)` - Complex multiplication
- `operator *(Complex a, double b)` - Scalar multiplication
---
### IWindow (interface)
Contract for window function implementations.
**Members:**
- `double[] Create(int size, bool normalize = false)` - Generate window array of given length
- `double[] Apply(double[] input, bool normalize = false)` - Multiply signal by window, return new array
- `void ApplyInPlace(double[] input, bool normalize = false)` - Multiply signal by window in-place
- `string Name { get; }` - Single-word window name
- `string Description { get; }` - Description of window characteristics and use cases
---
### Window (abstract class)
Base class for window implementations implementing `IWindow`.
**Properties:**
- `abstract string Name { get; }`
- `abstract string Description { get; }`
**Methods:**
- `virtual double[] Create(int size, bool normalize = false)` - Creates window array; caches result for reuse if same size/normalize requested
- `double[] Apply(double[] input, bool normalize = false)` - Applies window using `Parallel.For`
- `void ApplyInPlace(double[] input, bool normalize = false)` - In-place window application using `Parallel.For`
- `static IWindow[] GetWindows()` - Reflects over assembly to instantiate all `IWindow` implementations
- `static IWindow GetWindow(WindowType type)` - Factory method returning window by enum type
**Obsolete Static Methods (still public):**
- `static double[] Rectangular(int pointCount)`
- `static double[] Hanning(int pointCount)`
- `static double[] Hamming(int pointCount)` - **Note: Implementation creates Hanning window, not Hamming**
- `static double[] Blackman(int pointCount)`
- `static double[] BlackmanCustom(int pointCount, double a = .42, double b = .5, double c = .08)`
- `static double[] BlackmanHarris(int pointCount)`
- `static double[] FlatTop(int pointCount)`
- `static double[] Bartlett(int pointCount)`
- `static double[] Cosine(int pointCount)`
- `static double[] Kaiser(int pointCount, double beta)`
- `static double[] Apply(double[] window, double[] signal)`
- `static void ApplyInPlace(double[] window, double[] signal)`
- `static string[] GetWindowNames()`
- `static double[] GetWindowByName(string windowName, int pointCount)`
**Enums:**
- `WindowType` - Bartlett, Blackman, BlackmanHarris, Cosine, FlatTop, Hamming, Hanning, Kaiser, Rectangular, Tukey, Welch
- `WindowAveragingType` - Averaging, PeakHoldMax, PeakHoldMin
---
### Transform (static class)
Core FFT transformation operations.
**FFT Methods:**
- `void FFT(Complex[] buffer)` - In-place FFT on array; validates power-of-2 length
- `void FFT(Span<Complex> buffer)` - In-place FFT on Span; validates power-of-2 length
- `Complex[] FFT(double[] input)` - Returns FFT of real input as Complex array
- `Complex[] RFFT(double[] input)` - Returns real FFT (length N/2+1) for real input
- `void RFFT(Span<Complex> destination, Span<double> input)` - RFFT into provided destination
**Inverse FFT:**
- `void IFFT(Complex[] buffer)` - In-place inverse FFT
**Frequency Utilities:**
- `double[] FFTfreq(double sampleRate, int pointCount, bool oneSided = true)` - Returns frequency values for each FFT bin
- `double FFTfreqPeriod(int sampleRate, int pointCount)` - Returns frequency spacing between bins
**Magnitude/Power:**
- `double[] Absolute(Complex[] input)` - Returns magnitude array
- `double[] FFTmagnitude(double[] input)` - Returns RMS-scaled magnitude spectrum (length N/2+1)
- `void FFTmagnitude(Span<double> destination, Span<double> input)` - In-place magnitude calculation
- `double[] FFTpower(double[] input)` - Returns power spectrum in dB
- `void FFTpower(Span<double> destination, double[] input)` - In-place power calculation
**Power Spectral Density:**
- `double[] PSD_Welch(double[] input, long sampleRate, WindowType windowType, int windowWidth, int overlapPct, WindowAveragingType averagingType, SetReadCalcProgressValueDelegate SetProgress = null)` - Welch's method for PSD estimation
**Mel Scale:**
- `double MelToFreq(double mel)` - Convert Mel to frequency (Hz)
- `double MelFromFreq(double frequencyHz)` - Convert frequency (Hz) to Mel
- `double[] MelScale(double[] fft, int sampleRate, int melBinCount)` - Convert FFT to Mel scale
**Utilities:**
- `bool IsPowerOfTwo(int x)` - Tests if value is power of 2
- `Complex[] MakeComplex(double[] real)` - Creates Complex array from real values
- `void MakeComplex(Span<Complex> com, Span<double> real)` - Populates Complex span from real span
- `T[] SubArray<T>(this T[] array, int offset, int length)` - Extension method to extract subarray
---
### Filter (static class)
Frequency-domain filtering operations.
**Methods:**
- `double[] LowPass(double[] values, double sampleRate, double maxFrequency)` - Attenuates frequencies above threshold
- `double[] HighPass(double[] values, double sampleRate, double minFrequency)` - Attenuates frequencies below threshold
- `double[] BandPass(double[] values, double sampleRate, double minFrequency, double maxFrequency)` - Attenuates frequencies outside range
- `double[] BandStop(double[] values, double sampleRate, double minFrequency, double maxFrequency)` - Attenuates frequencies inside range
---
### Pad (static class)
Zero-padding utilities for array length adjustment.
**Methods:**
- `bool IsPowerOfTwo(int x)` - Tests if value is power of 2
- `Complex[] ZeroPad(Complex[] input)` - Pads to next power of 2 (centered padding)
- `double[] ZeroPad(double[] input)` - Pads to next power of 2 (centered padding)
- `Complex[] ZeroPad(Complex[] input, int finalLength)` - Pads to specified length (centered padding)
- `double[] ZeroPad(double[] input, int finalLength)` - Pads to specified length (centered padding)
---
### SampleData (static class)
Test data generation utilities.
**Methods:**
- `double[] Times(int sampleRate, int pointCount)` - Generates time axis values
- `double[] OddSines(int pointCount = 128, int sineCount = 2)` - Sum of odd harmonic sines
- `void AddSin(double[] data, int sampleRate, double frequency, double magnitude = 1)` - Adds sine wave to existing data
- `void AddOffset(double[] data, double offset = 0)` - Adds DC offset
- `void AddWhiteNoise(double[] data, double magnitude = 1, double offset = 0, int? seed = 0)` - Adds uniform random noise
- `double[] RandomNormal(int pointCount, double mean = .5, double stdDev = .5, int? seed = 0)` - Generates normally distributed random values (Box-Muller transform)
- `double[] SampleAudio1()` - Returns hardcoded 512-point test signal (sum = 71.52)
---
### Experimental (static class)
Educational reference implementations marked `[Obsolete]`.
**Methods:**
- `Complex[] DFT(Complex[] input, bool inverse = false)` - Naive O(N²) DFT implementation
- `Complex[] DFT(double[] input, bool inverse = false)` - DFT for real input
**Private:**
- `Complex[] FFTsimple(Complex[] input)` - Recursive FFT implementation (non-optimized, educational)
---
## 3. Invariants
1. **Power-of-2 Length Requirement**: All FFT operations (`FFT`, `IFFT`, `RFFT`, `FFTMagnitude`, `FFTPpower`, `PSD_Welch`) require input buffer lengths to be powers of 2. This is validated at runtime with `ArgumentException`.
2. **Non-Empty Buffers**: `FFT` and `IFFT` throw `ArgumentException` if buffer length is 0.
3. **RFFT Output Size**: `RFFT` output length is always `input.Length / 2 + 1`.
4. **Window Caching**: `Window.Create` caches the last generated window; subsequent calls with same size and normalize flag return the cached array.
5. **Centered Padding**: `Pad.ZeroPad` methods add zeros centered around the input data (half before, half after).
6. **Filter Frequency Bounds**: `LowPass` uses `double.NegativeInfinity` as min; `HighPass` uses `double.PositiveInfinity` as max in their `BandPass` delegations.
7. **IFFT Scaling**: `IFFT` scales output by `1/N` where N is buffer length.
8. **Magnitude Scaling**: `FFTMagnitude` scales DC component by `1/N` and all other components by `2/N` to account for positive/negative frequency combination.
---
## 4. Dependencies
**Internal Dependencies (inferred from source):**
- `Window` depends on `IWindow` interface
- `Filter` depends on `Transform.FFT`, `Transform.IFFT`, `Transform.FFTfreq`, and `Complex`
- `Transform` depends on `Complex`, `Window`, `WindowType`, `WindowAveragingType`
- `Pad` depends on `Complex`
**External Dependencies:**
- `DTS.Common.Interface.SetReadCalcProgressValueDelegate` - Used in `PSD_Welch` for progress reporting
- `DTS.Common.Strings.Strings` - Localization strings used in `PSD_Welch` progress messages
- `System.Buffers.ArrayPool<Complex>` - Used in `RFFT`, `FFTMagnitude`, `FFTPower` for memory pooling
**Standard Library Dependencies:**
- `System`, `System.Linq`, `System.Reflection`, `System.Threading.Tasks`, `System.Buffers`
**Consumers (inferred):**
- Unknown from provided source; `PSD_Welch` signature suggests integration with a larger DTS.Common framework
---
## 5. Gotchas
1. **Hamming Window Bug**: The obsolete `Window.Hamming(int pointCount)` method incorrectly instantiates `new Windows.Hanning()`, not a Hamming window. This is a copy-paste error.
2. **Console Output in DFT**: `Experimental.DFT` contains `Console.WriteLine($"REAL {mult1} {mult2}")` which produces unexpected console output when using the experimental DFT.
3. **Window.Create Returns Empty Array for Invalid Size**: When `size <= 0`, `Window.Create` returns `new double[0]` rather than throwing an exception.
4. **Cached Window Mutation Risk**: `Window.Create` returns the cached `lastWindow` array directly. Callers who mutate the returned array will corrupt the cache for subsequent calls.
5. **NormalizeInPlace Race Condition**: `Window.NormalizeInPlace` uses `Parallel.For` to sum values into a local `sum` variable without synchronization, causing a race condition. The sum calculation is non-deterministic under concurrent execution.
6. **Obsolete Methods Still Public**: Many static methods in `Window` class are marked `[Obsolete]` but remain public. The obsolete `Apply` and `ApplyInPlace` methods require window and signal to be same length but throw `ArgumentException` (not `ArgumentOutOfRangeException`) on mismatch.
7. **PSD_Welch Segment Allocation**: Creates `segments` array of size `input.Length / step`, but only populates `input.Length / step` segments. May have uninitialized trailing elements if input length is not evenly divisible.
8. **MelScale Division by Zero**: `MelScale` divides by `binScaleSum` which could be zero if `indexSpan` is 0 (when `indexHigh == indexLow`).
9. **Complex Struct Mutability**: `Complex` is a struct with mutable properties. Setting `Real` or `Imaginary` invalidates cached `_magnitude` and `_magnitudeSquared`, but if the struct is boxed or copied, mutations may not propagate correctly.