Files

259 lines
15 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
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.