Files
DP44/docs/ai/DataPRO/FftSharp.md
2026-04-17 14:55:32 -04:00

12 KiB

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
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
2026-04-17T15:50:04.390408+00:00 zai-org/GLM-5-FP8 1 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.