Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Second translate kr #69

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions EmbeddedResources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Runtime.CompilerServices;

namespace SignalAnalysis;

/// <summary>
/// Load graphics resources from disk
/// </summary>
public class GraphicsResources
{
public const string AppLogo = @"images\logo.ico";
public const string AppLogo256 = @"images\logo@256.png";
public const string IconExit = @"images\exit.ico";
public const string IconOpen = @"images\openfolder.ico";
public const string IconExport = @"images\save.ico";
public const string IconSettings = @"images\settings.ico";
public const string IconAbout = @"images\about.ico";

/// <summary>
/// Loads a graphics resource from a disk location
/// </summary>
/// <typeparam name="T">Type of resource to be loaded</typeparam>
/// <param name="fileName">File name (absolute or relative to the working directory) to load resource from</param>
/// <returns>The graphics resource</returns>
public static T? Load<T> (string fileName)
{
T? resource = default;
try
{
if (File.Exists(fileName))
{
if (typeof(T).Equals(typeof(System.Drawing.Image)))
resource = (T)(object)Image.FromFile(fileName);
else if (typeof(T).Equals(typeof(Icon)))
resource = (T)(object)new Icon(fileName);
else if (typeof(T).Equals(typeof(Cursor)))
resource = (T)(object)new Cursor(fileName);
}
}
catch (Exception ex)
{
MessageBox.Show(
$"Unexpected error while loading the {fileName} graphics resource.{Environment.NewLine}{ex.Message}",
"Loading error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
return resource;
}

/// <summary>
/// Creates a bitmap from an icon file
/// </summary>
/// <param name="fileName">File name (absolute or relative to the working directory) to load resource from</param>
/// <param name="width">Width in pixels</param>
/// <param name="height">Height in pixels. If omitted, the width is used instead</param>
/// <returns>Bitmap from the icon resource</returns>
public static Bitmap? LoadIcon(string fileName, int width, int? height = null)
{
Bitmap? resource = null;
try
{
if (File.Exists(fileName))
{
resource = new Icon(fileName, width, height ?? width).ToBitmap();
}
}
catch (Exception ex)
{
MessageBox.Show(
$"Unexpected error while loading the {fileName} icon resource.{Environment.NewLine}{ex.Message}",
"Loading error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
return resource;

}
}
145 changes: 145 additions & 0 deletions FftSharp/BluesteinOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System;

namespace FftSharp;

internal static class BluesteinOperations
{
/*
* Computes the discrete Fourier transform (DFT) or inverse transform of the given complex vector, storing the result back into the vector.
* The vector can have any length. This is a wrapper function. The inverse transform does not perform scaling, so it is not a true inverse.
*/
public static void Transform(System.Numerics.Complex[] vec, bool inverse)
{
int n = vec.Length;
if (n == 0)
return;
else if ((n & (n - 1)) == 0) // Is power of 2
TransformRadix2(vec, inverse);
else // More complicated algorithm for arbitrary sizes
TransformBluestein(vec, inverse);
}


/*
* Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
* The vector's length must be a power of 2. Uses the Cooley-Tukey decimation-in-time radix-2 algorithm.
*/
public static void TransformRadix2(System.Numerics.Complex[] vec, bool inverse)
{
// Length variables
int n = vec.Length;
int levels = 0; // compute levels = floor(log2(n))
for (int temp = n; temp > 1; temp >>= 1)
levels++;
if (1 << levels != n)
throw new ArgumentException("Length is not a power of 2");

// Trigonometric table
System.Numerics.Complex[] expTable = new System.Numerics.Complex[n / 2];
double coef = 2 * Math.PI / n * (inverse ? 1 : -1);
for (int i = 0; i < n / 2; i++)
expTable[i] = System.Numerics.Complex.FromPolarCoordinates(1, i * coef);

// Bit-reversed addressing permutation
for (int i = 0; i < n; i++)
{
int j = ReverseBits(i, levels);
if (j > i)
{
System.Numerics.Complex temp = vec[i];
vec[i] = vec[j];
vec[j] = temp;
}
}

// Cooley-Tukey decimation-in-time radix-2 FFT
for (int size = 2; size <= n; size *= 2)
{
int halfsize = size / 2;
int tablestep = n / size;
for (int i = 0; i < n; i += size)
{
for (int j = i, k = 0; j < i + halfsize; j++, k += tablestep)
{
System.Numerics.Complex temp = vec[j + halfsize] * expTable[k];
vec[j + halfsize] = vec[j] - temp;
vec[j] += temp;
}
}
if (size == n) // Prevent overflow in 'size *= 2'
break;
}
}


/*
* Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
* The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function.
* Uses Bluestein's chirp z-transform algorithm.
*/
public static void TransformBluestein(System.Numerics.Complex[] vec, bool inverse)
{
// Find a power-of-2 convolution length m such that m >= n * 2 + 1
int n = vec.Length;
if (n >= 0x20000000)
throw new ArgumentException("Array too large");
int m = 1;
while (m < n * 2 + 1)
m *= 2;

// Trigonometric table
System.Numerics.Complex[] expTable = new System.Numerics.Complex[n];
double coef = Math.PI / n * (inverse ? 1 : -1);
for (int i = 0; i < n; i++)
{
int j = (int)((long)i * i % (n * 2)); // This is more accurate than j = i * i
expTable[i] = System.Numerics.Complex.Exp(new System.Numerics.Complex(0, j * coef));
}

// Temporary vectors and preprocessing
System.Numerics.Complex[] avec = new System.Numerics.Complex[m];
for (int i = 0; i < n; i++)
avec[i] = vec[i] * expTable[i];
System.Numerics.Complex[] bvec = new System.Numerics.Complex[m];
bvec[0] = expTable[0];
for (int i = 1; i < n; i++)
bvec[i] = bvec[m - i] = System.Numerics.Complex.Conjugate(expTable[i]);

// Convolution
System.Numerics.Complex[] cvec = new System.Numerics.Complex[m];
Convolve(avec, bvec, cvec);

// Postprocessing
for (int i = 0; i < n; i++)
vec[i] = cvec[i] * expTable[i];
}


/*
* Computes the circular convolution of the given complex vectors. Each vector's length must be the same.
*/
public static void Convolve(System.Numerics.Complex[] xvec, System.Numerics.Complex[] yvec, System.Numerics.Complex[] outvec)
{
int n = xvec.Length;
if (n != yvec.Length || n != outvec.Length)
throw new ArgumentException("Mismatched lengths");
xvec = (System.Numerics.Complex[])xvec.Clone();
yvec = (System.Numerics.Complex[])yvec.Clone();
Transform(xvec, false);
Transform(yvec, false);
for (int i = 0; i < n; i++)
xvec[i] *= yvec[i];
Transform(xvec, true);
for (int i = 0; i < n; i++) // Scaling (because this FFT implementation omits it)
outvec[i] = xvec[i] / n;
}


private static int ReverseBits(int val, int width)
{
int result = 0;
for (int i = 0; i < width; i++, val >>= 1)
result = (result << 1) | (val & 1);
return result;
}
}
79 changes: 79 additions & 0 deletions FftSharp/Complex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FftSharp
{
[Obsolete("Use System.Numerics.Complex")]
public struct Complex
{
public double Real;
public double Imaginary;
public double MagnitudeSquared => Real * Real + Imaginary * Imaginary;
public double Magnitude => Math.Sqrt(MagnitudeSquared);
public double Phase => Math.Atan2(Imaginary, Real);

public Complex(double real, double imaginary)
{
Real = real;
Imaginary = imaginary;
}

public override string ToString()
{
if (Imaginary < 0)
return $"{Real}-{-Imaginary}j";
else
return $"{Real}+{Imaginary}j";
}

public static Complex operator +(Complex a, Complex b)
{
return new Complex(a.Real + b.Real, a.Imaginary + b.Imaginary);
}

public static Complex operator -(Complex a, Complex b)
{
return new Complex(a.Real - b.Real, a.Imaginary - b.Imaginary);
}

public static Complex operator *(Complex a, Complex b)
{
return new Complex(
real: (a.Real * b.Real) - (a.Imaginary * b.Imaginary),
imaginary: (a.Real * b.Imaginary) + (a.Imaginary * b.Real));
}

public static Complex operator *(Complex a, double b)
{
return new Complex(a.Real * b, a.Imaginary * b);
}

public static Complex[] FromReal(double[] real)
{
Complex[] complex = new Complex[real.Length];
for (int i = 0; i < real.Length; i++)
complex[i].Real = real[i];
return complex;
}

public static double[] GetMagnitudes(Complex[] input)
{
double[] output = new double[input.Length];
for (int i = 0; i < input.Length; i++)
output[i] = input[i].Magnitude;
return output;
}

public System.Numerics.Complex ToNumerics()
{
return new(Real, Imaginary);
}

public static System.Numerics.Complex[] ToNumerics(Complex[] values)
{
return values.Select(x => x.ToNumerics()).ToArray();
}
}
}