The SignalMath Class

The SignalMath class provides static methods for performing common 1D signal processing operations on arrays and spans. It offers a convenient, high-performance API for working with real and complex signals without the need to create specialized processor objects.

Overview

SignalMath is designed as a static utility class that provides direct access to signal processing algorithms. It supports both real-valued and complex-valued signals with single precision (float) and double precision operations.

The class includes methods for:

  • Fourier Transforms: Forward and inverse discrete Fourier transforms for real and complex signals

  • Convolution and Correlation: Linear convolution, cross-correlation, and autocorrelation operations

  • Window Functions: Application of various window functions (Hann, Hamming, Blackman, Kaiser, etc.) to signals

  • Spectral Analysis: Periodogram computation for power spectral density estimation

Fourier Transforms

The SignalMath class provides methods for computing the discrete Fourier transform (DFT) and its inverse.

Real-to-Complex Transform: The FourierTransform method computes the DFT of a real signal, producing a complex spectrum. For a real signal of length N, the output is a complex array of length N/2+1, exploiting the Hermitian symmetry of the spectrum.

Complex-to-Complex Transform: The FourierTransform method computes the DFT of a complex signal, producing a complex spectrum of the same length.

Inverse Transforms: The InverseFourierTransform and related methods compute the inverse DFT, reconstructing time-domain signals from frequency-domain spectra. The inverse transform is scaled by 1/N.

The following example demonstrates computing the FFT of a simple sinusoidal signal:

C#
// Create a simple sinusoidal signal using Vector
int n = 128;
var signal = Vector.FromFunction(n, i => Math.Sin(2 * Math.PI * 5 * i / n));

// Compute the FFT
var spectrum = SignalMath.FourierTransform(signal);

// Find the peak frequency
int peakIndex = 0;
double maxMagnitude = 0;
for (int i = 0; i < spectrum.Length; i++)
{
    double magnitude = spectrum[i].Magnitude;
    if (magnitude > maxMagnitude)
    {
        maxMagnitude = magnitude;
        peakIndex = i;
    }
}
Console.WriteLine($"Peak frequency bin: {peakIndex}");

// Compute the inverse FFT
var reconstructed = SignalMath.InverseFourierTransform(spectrum);

Convolution and Correlation

The SignalMath class provides convenient methods for computing convolution and correlation of signals. These operations are fundamental in digital signal processing for filtering, feature detection, and signal analysis.

Convolution: The Convolve method computes the linear convolution of two signals. The output mode can be specified as:

  • FullLength: Returns the full convolution (length M + N - 1)

  • SameAsSignal: Returns the central part of the convolution with the same length as the signal

  • NoPadding: Returns only the part of the convolution computed without zero-padding (length M - N + 1)

Cross-Correlation: The Correlate method computes the cross-correlation of two signals, which measures their similarity at different time lags.

Autocorrelation: The AutoCorrelate method computes the autocorrelation of a signal with itself, useful for detecting periodicities and analyzing signal structure.

The implementation automatically chooses between direct (time-domain) and FFT-based (frequency-domain) algorithms based on the signal and kernel sizes. The threshold can be controlled using the ConvolutionFftThreshold property.

The following example demonstrates basic convolution:

C#
// Create a signal using Vector
var signal = Vector.Create<double>(1, 2, 3, 4, 5);

// Create a smoothing kernel (moving average)
var kernel = Vector.Create<double>(0.25, 0.5, 0.25);

// Compute convolution with 'same' mode
var result = SignalMath.Convolve(signal, kernel, ConvolutionMode.SameAsSignal);

Console.WriteLine("Smoothed signal:");
for (int i = 0; i < result.Length; i++)
{
    Console.WriteLine($"  {i}: {result[i]:F4}");
}

Window Functions

Window functions are commonly applied to signals before computing Fourier transforms to reduce spectral leakage. The SignalMath class provides convenient methods for applying the most common window functions:

Each window function is available in both in-place and out-of-place variants, and supports both real and complex signals.

The following example shows applying a Hann window before computing an FFT:

C#
// Create a signal using Vector
int n = 256;
var signal = Vector.FromFunction(n, i =>
{
    // Mix of two frequencies
    return Math.Sin(2 * Math.PI * 10 * i / n) 
         + 0.5 * Math.Sin(2 * Math.PI * 30 * i / n);
});

// Apply Hann window before FFT
SignalMath.ApplyHannWindowInPlace(signal);

// Compute FFT of windowed signal
var spectrum = SignalMath.FourierTransform(signal);

Console.WriteLine("First 10 spectrum values:");
for (int i = 0; i < 10; i++)
{
    Console.WriteLine($"  Bin {i}: {spectrum[i].Magnitude:F4}");
}

Spectral Analysis

The Periodogram method computes the periodogram, which is a basic estimate of the power spectral density (PSD) of a signal. The periodogram is computed as the squared magnitude of the FFT, normalized by the signal length:

P(f)=|X(f)|2/N

where X(f) is the discrete Fourier transform and N is the signal length. For a real signal of length N, the output is an array of length N/2+1 containing the power at each frequency.

The following example demonstrates periodogram computation:

C#
// Create a noisy sinusoidal signal using Vector
int n = 512;
Random rng = new Random(42);
var signal = Vector.FromFunction(n, i =>
{
    // Signal with noise
    return Math.Sin(2 * Math.PI * 15 * i / n) 
         + 0.3 * (rng.NextDouble() - 0.5);
});

// Compute periodogram (power spectral density estimate)
var psd = SignalMath.Periodogram(signal);

// Find dominant frequency
int dominantBin = 0;
double maxPower = 0;
for (int i = 1; i < psd.Length; i++)
{
    if (psd[i] > maxPower)
    {
        maxPower = psd[i];
        dominantBin = i;
    }
}

Console.WriteLine($"Dominant frequency bin: {dominantBin}");
Console.WriteLine($"Peak power: {maxPower:F6}");

Performance Considerations

The SignalMath class is designed for high performance:

  • Span-based API: All methods use ReadOnlySpan<T> and Span<T> for zero-allocation operation on existing memory buffers.

  • Automatic Algorithm Selection: For convolution and correlation operations, the implementation automatically selects between direct and FFT-based algorithms based on signal sizes.

  • Native Acceleration: When available, operations use optimized native libraries (Intel MKL) for maximum performance on supported platforms.

  • Generic Implementation: Methods are generic over the element type (typically float or double), allowing the same code to be used for both single and double precision computations.

Vector-based Overloads

In addition to the span-based methods, SignalMath also provides overloads that work with Vector<T> objects. These methods provide a higher-level API that is more convenient when working with the library's vector and matrix types:

  • Convolve: Computes convolution and returns a new vector

  • Correlate: Computes cross-correlation and returns a new vector

  • Periodogram: Computes periodogram and returns a new vector

These vector-based methods are particularly useful when integrating signal processing operations with other vector and matrix computations.

See Also