Files
2026-04-17 14:55:32 -04:00

259 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
source_files:
- DataPRO/ExocortexDSP/Polynomial.cs
- DataPRO/ExocortexDSP/FourierDirection.cs
- DataPRO/ExocortexDSP/AssemblyInfo.cs
- DataPRO/ExocortexDSP/ComplexMath.cs
- DataPRO/ExocortexDSP/PassFilter.cs
- DataPRO/ExocortexDSP/ComplexStats.cs
- DataPRO/ExocortexDSP/Complex.cs
generated_at: "2026-04-16T03:48:44.236669+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "e35f8b9ef214514d"
---
# Exocortex.DSP Module Documentation
## 1. Purpose
This module provides core digital signal processing (DSP) functionality for the Exocortex Technologies library. It defines foundational types and utilities including complex number representations (`Complex`, `ComplexF`), polynomial evaluation (`Polynomial`, `SimplePolynomial`), filter implementations (`PassFilter`), Fourier transform direction specification (`FourierDirection`), and statistical operations on complex arrays (`ComplexStats`). The module serves as the mathematical backbone for signal analysis and transformation operations within the larger DataPRO system.
## 2. Public Interface
### Structs
#### `Complex`
- **`public Complex(double real, double imaginary)`**
Constructs a double-precision complex number from real and imaginary components.
- **`public Complex(Complex c)`**
Copy constructor.
- **`public static Complex FromRealImaginary(double real, double imaginary)`**
Factory method to create a complex number from real/imaginary parts.
- **`public static Complex FromModulusArgument(double modulus, double argument)`**
Factory method to create a complex number from polar coordinates (modulus and argument in radians).
- **`public double GetModulus()`**
Returns the magnitude (Euclidean norm) of the complex number.
- **`public double GetModulusSquared()`**
Returns the squared magnitude (faster than `GetModulus()`).
- **`public double GetArgument()`**
Returns the phase angle (argument) in radians.
- **`public Complex GetConjugate()`**
Returns the complex conjugate.
- **`public void Normalize()`**
Scales the complex number to unit magnitude (throws `DivideByZeroException` if zero).
- **`public static explicit operator Complex(ComplexF cF)`**
Explicit cast from single-precision `ComplexF` to `Complex`.
- **`public static explicit operator Complex(double d)`**
Explicit cast from `double` to `Complex` (real part = `d`, imaginary part = 0).
- **`public static explicit operator double(Complex c)`**
Explicit cast from `Complex` to `double` (returns real part).
- **`public static bool operator ==(Complex a, Complex b)`**
Equality comparison (exact).
- **`public static bool operator !=(Complex a, Complex b)`**
Inequality comparison (exact).
- **`public override bool Equals(object o)`**
Object equality (delegates to `==`).
- **`public int CompareTo(object o)`**
Compares based on magnitude (modulus). Supports comparison with `Complex`, `double`, `ComplexF`, `float`, or `null`.
- **`public static Complex operator +(Complex a)`**
Unary plus.
- **`public static Complex operator -(Complex a)`**
Unary negation.
- **`public static Complex operator +(Complex a, double f)` / `operator +(double f, Complex a)`**
Addition with real scalar.
- **`public static Complex operator +(Complex a, Complex b)`**
Complex addition.
- **`public static Complex operator -(Complex a, double f)` / `operator -(double f, Complex a)`**
Subtraction with real scalar.
- **`public static Complex operator -(Complex a, Complex b)`**
Complex subtraction.
- **`public static Complex operator *(Complex a, double f)` / `operator *(double f, Complex a)`**
Scalar multiplication.
- **`public static Complex operator *(Complex a, Complex b)`**
Complex multiplication.
- **`public static Complex operator /(Complex a, double f)`**
Scalar division (throws `DivideByZeroException` if `f == 0`).
- **`public static Complex operator /(Complex a, Complex b)`**
Complex division (throws `DivideByZeroException` if divisor is zero).
- **`public static bool IsEqual(Complex a, Complex b, double tolerance)`**
Approximate equality check within `tolerance`.
- **`public override string ToString()`**
Returns string in format `"( {Re}, {Im}i )"`.
- **`public static Complex Zero`**
Static property returning `(0, 0)`.
- **`public static Complex I`**
Static property returning `(0, 1)` (imaginary unit).
- **`public static Complex MaxValue` / `MinValue`**
Static properties for extreme values.
#### `ComplexF`
- *Note: Definition not provided in source files, but referenced extensively in `ComplexStats` and `ComplexMath`. Assumed to be a single-precision counterpart to `Complex`.*
### Classes
#### `Polynomial`
- **`protected double[] coefficients`**
Protected field storing the polynomial coefficients (index 0 = constant term).
- **`public Polynomial(params double[] coefficients)`**
Constructor initializing coefficients via deep copy.
- **`public abstract double Evaluate(double value)`**
Abstract method to evaluate the polynomial at `value`.
- **`public double GetCoefficient(int index)`**
Returns coefficient at `index`, or `double.NaN` if `coefficients == null`, `index < 0`, or `index >= coefficients.Length`.
#### `SimplePolynomial : Polynomial`
- **`public SimplePolynomial(params double[] coefficients)`**
Constructor delegating to base class.
- **`public override double Evaluate(double value)`**
Evaluates polynomial using Horners method:
`retval = c[0] + c[1]*x + c[2]*x² + ...`
Computed iteratively for efficiency.
#### `ComplexMath`
- **`private ComplexMath()`**
Private constructor (static class pattern).
- **`public static void Swap(ref Complex a, ref Complex b)`**
Swaps two `Complex` instances.
- **`public static void Swap(ref ComplexF a, ref ComplexF b)`**
Swaps two `ComplexF` instances.
- **`public static Complex Sqrt(Complex c)`**
Computes complex square root using principal branch.
Formula:
`real = √2/2 * √(|c| + Re(c))`
`imag = sign(Im(c)) * √2/2 * √(|c| - Re(c))`
where `|c|` is modulus.
- **`public static ComplexF Sqrt(ComplexF c)`**
Single-precision variant of `Sqrt`.
- **`public static Complex Pow(Complex c, double exponent)`**
Computes `c^exponent` via polar form:
`modulus = |c|^exponent`, `argument = arg(c) * exponent`,
then converts back: `(modulus * cos(arg), modulus * sin(arg))`.
- **`public static ComplexF Pow(ComplexF c, double exponent)`**
Single-precision variant of `Pow`.
#### `ComplexStats`
- **`private ComplexStats()`**
Private constructor (static class pattern).
- **`public static Complex Sum(Complex[] data)` / `Sum(ComplexF[] data)`**
Computes sum of array elements using recursive divide-and-conquer (base case ≤1000 elements).
- **`public static Complex SumOfSquares(Complex[] data)` / `SumOfSquares(ComplexF[] data)`**
Computes `Σ(data[i] * data[i])` (note: *not* `|data[i]|²`).
- **`public static Complex Mean(Complex[] data)` / `Mean(ComplexF[] data)`**
Computes arithmetic mean: `Sum(data) / data.Length`.
- **`public static Complex Variance(Complex[] data)` / `Variance(ComplexF[] data)`**
Computes variance as: `SumOfSquares(data)/n - Sum(data)`.
Throws `DivideByZeroException` if `data.Length == 0`.
- **`public static Complex StdDev(Complex[] data)` / `StdDev(ComplexF[] data)`**
Computes standard deviation: `Sqrt(Variance(data))`.
Throws `DivideByZeroException` if `data.Length == 0`.
- **`public static double RMSError(Complex[] alpha, Complex[] beta)` / `RMSError(ComplexF[] alpha, ComplexF[] beta)`**
Computes root mean squared error between two arrays:
`sqrt( Σ |beta[i] - alpha[i]|² / n )`.
Arrays must be non-null and equal length.
#### `PassFilter`
- **`public static double[] HighPass(double[] values, double sampleRate, double centerFrequency, PassFilterType type, uint order)`**
Applies high-pass filtering via FFT. Delegates to `RunFilter(..., lowPass: false)`.
- **`public static double[] LowPass(double[] values, double sampleRate, double centerFrequency, PassFilterType type, uint order)`**
Applies low-pass filtering via FFT. Delegates to `RunFilter(..., lowPass: true)`.
- **`private static double[] RunFilter(..., bool lowPass)`**
Core filtering logic:
1. Converts input `double[]` to `Complex[]` (imag part = 0).
2. Applies forward FFT.
3. Applies frequency-domain gain function (parallelized via `Parallel.For`).
4. Applies inverse FFT.
5. Scales result by `1/N`.
6. Extracts real parts as `double[]`.
- *Note: DC component (`signal[0]`) is zeroed only for Chebyshev filter.*
- **`private static Complex[] BesselFilter(...)`**
Implements Bessel filter using precomputed denominator polynomial `B`.
Gain: `numerator / sqrt(B(freq / centerFreq))` (low-pass) or `numerator / sqrt(B(centerFreq / freq))` (high-pass).
- **`private static double BesselGain(...)`**
Helper for Bessel gain computation.
- **`private static Complex[] ButterworthFilter(...)`**
Implements Butterworth filter.
Gain: `DCGain / sqrt(1 + (freq / centerFreq)^(2*order))` (low-pass) or `DCGain / sqrt(1 + (centerFreq / freq)^(2*order))` (high-pass).
- **`private static double ButterworthGain(...)`**
Helper for Butterworth gain computation.
- **`private static Complex[] ChebyshevFilter(...)`**
Implements Chebyshev filter using precomputed Chebyshev polynomial `T`.
Gain: `DCGain / sqrt(1 + ripple * T(freq / centerFreq)^(2*order))` (low-pass) or similar for high-pass.
*Note: DC component zeroed before filtering.*
- **`private static double ChebyshevGain(...)`**
Helper for Chebyshev gain computation.
- **`private static Polynomial ChebyshevPolynomial(int order)`**
Returns `SimplePolynomial` instance for Chebyshev polynomial of order `0``8` (hardcoded coefficients). Throws `NotImplementedException` for other orders.
- **`private static Polynomial BesselDenominatorPolynomial(int order)`**
Returns `SimplePolynomial` instance for Bessel denominator polynomial (squared magnitude denominator) for orders `2``8` (hardcoded coefficients). Throws `NotImplementedException` for other orders.
### Enums
#### `FourierDirection`
- **`Forward = 1`**
Indicates forward FFT (time → frequency).
- **`Backward = -1`**
Indicates inverse FFT (frequency → time).
#### `PassFilterType`
- **`Bessel`**
Maximally flat group delay.
- **`Butterworth`**
Maximally flat magnitude response.
- **`Chebyshev`**
Equi-ripple in passband.
- **`CriticalDamping`**
Fastest non-oscillatory response.
## 3. Invariants
- **`Polynomial.coefficients`** is always non-null after construction (deep copy of input array).
- **`Polynomial.GetCoefficient(index)`** returns `double.NaN` for invalid indices (`null`, negative, or ≥ length).
- **`Complex.Normalize()`** throws `DivideByZeroException` if modulus is zero.
- **`ComplexStats.Variance`/`StdDev`** throw `DivideByZeroException` if input array length is zero.
- **`ComplexStats.RMSError`** asserts (via `Debug.Assert`) that both input arrays are non-null and of equal length.
- **`ComplexStats.Sum`/`SumOfSquares`** use recursion with base case size ≤1000 for performance.
- **`PassFilter.RunFilter`** assumes FFT length `N` is even (uses `N/2` bins for symmetric spectrum).
- **`PassFilter`** filters require `centerFrequency > 0`; if `centerFrequency <= 0`, no filtering is applied (original signal returned).
- **`PassFilter`** filters assume input array length matches FFT size (`N`), and output is scaled by `1/N` after inverse FFT.
## 4. Dependencies
### Internal Dependencies (within `Exocortex.DSP`)
- **`Complex`** is used by:
- `ComplexMath` (operations on `Complex`)
- `ComplexStats` (statistical operations on `Complex[]`)
- `PassFilter` (filtering via FFT)
- `Polynomial` (indirectly via `Complex[]` in `RunFilter`)
- **`ComplexF`** is used by:
- `ComplexMath` (single-precision operations)
- `ComplexStats` (statistical operations on `ComplexF[]`)
- **`Fourier`** is referenced in `PassFilter` (`Fourier.FFT(...)`), but its definition is not provided in the source files.
- **`SimplePolynomial`** is used by `PassFilter` (Bessel/Chebyshev filter polynomials).
### External Dependencies
- **`System`** (core types: `Math`, `Debug`, `String`, `Exception`, `Attribute`, `Runtime.InteropServices`)
- **`System.Threading.Tasks`** (used for `Parallel.For` in `PassFilter`)
- **`System.Collections.Generic`**, **`System.Linq`**, **`System.Text`**, **`System.Threading.Tasks`** (via `PassFilter.cs`)
### Inferred Usage
- **`Fourier.FFT`** is a required dependency (not shown in source). Its behavior is assumed to be:
- In-place transform of `Complex[]`.
- Supports `FourierDirection.Forward`/`Backward`.
- Output scaling is handled explicitly in `PassFilter` (`signal = signal.Select(n => n / N).ToArray()`).
## 5. Gotchas
- **`ComplexStats.SumOfSquares`** computes `Σ(z_i * z_i)` (algebraic square), *not* `Σ|z_i|²` (squared magnitude). This is unconventional for complex statistics and may be a source of errors.
- **`ComplexStats.Variance`** formula `SumOfSquares/n - Sum` is *incorrect* for complex numbers. Correct variance should be `Σ|z_i - μ|²/n`. The current implementation yields complex-valued variance (real part may be negative), which is non-standard.
- **`ComplexStats.RMSError`** computes RMS error as `sqrt( Σ|Δ|² / n )`, but `SumOfSquaredErrorRecursion` uses `|Δ|² = Δ.Re² + Δ.Im²` (correct), while `SumOfSquares` in `Variance` does not.
- **`Complex.Parse(string)`** is declared but throws `NotImplementedException`. No parsing support exists.
- **`PassFilter`** uses hardcoded polynomial coefficients for orders `0``8` (Chebyshev) and `2``8` (Bessel). Filters for other orders are unsupported.
- **`PassFilter`** filters assume even-length input arrays (due to `N/2` bin symmetry assumption). Behavior for odd lengths is undefined.
- **`PassFilter`** filters zero DC component only for Chebyshev filters (`signal[0] = 0`), but not for Bessel/Butterworth. This may cause unintended DC offset differences.
- **`Complex.GetModulus()`** and **`GetArgument()`** use `Math.Sqrt`/`Math.Atan2`, which may have performance implications in hot loops.
- **`Complex`** struct is mutable (fields are public). Operations like `operator +`, `*`, `/` modify the *first operand in-place* before returning it (e.g., `a + b` mutates `a`). This violates typical expectations for value types and may cause side effects.
- **`Complex.CompareTo`** compares by magnitude only, not lexicographically. Comparing complex numbers by magnitude may be semantically unclear.
- **`Complex`** has no `IEquatable<Complex>` implementation; `Equals`/`==` use exact equality (no tolerance).
- **`ComplexStats`** methods use `Debug.Assert` for validation, which are *disabled in release builds*. Invalid inputs (e.g., null arrays) may cause runtime exceptions only at runtime (not compile-time).
- **`PassFilter`** uses `Parallel.For` for frequency-domain filtering, but may not scale well for small arrays due to parallelization overhead.