This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1 @@
12

View File

@@ -0,0 +1 @@
12

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>8.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{1904A41B-D1B8-40E5-ADBB-728DDCF7C816}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Exocortex.DSP.v1</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Exocortex.DSP</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<DocumentationFile>doc\Exocortex.DSP.xml</DocumentationFile>
<Optimize>true</Optimize>
<FileAlignment>4096</FileAlignment>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<DocumentationFile>doc\Exocortex.DSP.xml</DocumentationFile>
<Optimize>true</Optimize>
<FileAlignment>4096</FileAlignment>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<Optimize>false</Optimize>
<FileAlignment>4096</FileAlignment>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<DocumentationFile>doc\Exocortex.DSP.xml</DocumentationFile>
<Optimize>true</Optimize>
<FileAlignment>4096</FileAlignment>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Complex.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ComplexArray.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ComplexF.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ComplexMath.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ComplexStats.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Fourier.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FourierDirection.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PassFilter.cs" />
<Compile Include="Polynomial.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<None Include="Design\Exocortex.DSPClassDiagram.cd" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,58 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

View File

@@ -0,0 +1,764 @@
/*
* BSD Licence:
* Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ]
* Exocortex Technologies [ www.exocortex.org ]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
using System;
using System.Diagnostics;
using Exocortex.DSP;
namespace Exocortex.DSP
{
// Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org
// Version: May 4, 2002
/// <summary>
/// <p>A set of array utilities for complex number arrays</p>
/// </summary>
public class ComplexArray
{
//---------------------------------------------------------------------------------------------
private ComplexArray()
{
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Clamp length (modulus) of the elements in the complex array
/// </summary>
/// <param name="array"></param>
/// <param name="fMinimum"></param>
/// <param name="fMaximum"></param>
static public void ClampLength(Complex[] array, double fMinimum, double fMaximum)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = Complex.FromModulusArgument(Math.Max(fMinimum, Math.Min(fMaximum, array[i].GetModulus())), array[i].GetArgument());
}
}
/// <summary>
/// Clamp elements in the complex array to range [minimum,maximum]
/// </summary>
/// <param name="array"></param>
/// <param name="minimum"></param>
/// <param name="maximum"></param>
static public void Clamp(Complex[] array, Complex minimum, Complex maximum)
{
for (int i = 0; i < array.Length; i++)
{
array[i].Re = Math.Min(Math.Max(array[i].Re, minimum.Re), maximum.Re);
array[i].Im = Math.Min(Math.Max(array[i].Re, minimum.Im), maximum.Im);
}
}
/// <summary>
/// Clamp elements in the complex array to real unit range (i.e. [0,1])
/// </summary>
/// <param name="array"></param>
static public void ClampToRealUnit(Complex[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i].Re = Math.Min(Math.Max(array[i].Re, 0), 1);
array[i].Im = 0;
}
}
//---------------------------------------------------------------------------------------------
static private bool _workspaceFLocked = false;
static private ComplexF[] _workspaceF = new ComplexF[0];
static private void LockWorkspaceF(int length, ref ComplexF[] workspace)
{
Debug.Assert(_workspaceFLocked == false);
_workspaceFLocked = true;
if (length >= _workspaceF.Length)
{
_workspaceF = new ComplexF[length];
}
workspace = _workspaceF;
}
static private void UnlockWorkspaceF(ref ComplexF[] workspace)
{
Debug.Assert(_workspaceF == workspace);
Debug.Assert(_workspaceFLocked == true);
_workspaceFLocked = false;
workspace = null;
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Shift (offset) the elements in the array
/// </summary>
/// <param name="array"></param>
/// <param name="offset"></param>
static public void Shift(Complex[] array, int offset)
{
Debug.Assert(array != null);
Debug.Assert(offset >= 0);
Debug.Assert(offset < array.Length);
if (offset == 0)
{
return;
}
int length = array.Length;
Complex[] temp = new Complex[length];
for (int i = 0; i < length; i++)
{
temp[(i + offset) % length] = array[i];
}
for (int i = 0; i < length; i++)
{
array[i] = temp[i];
}
}
/// <summary>
/// Shift (offset) the elements in the array
/// </summary>
/// <param name="array"></param>
/// <param name="offset"></param>
static public void Shift(ComplexF[] array, int offset)
{
Debug.Assert(array != null);
Debug.Assert(offset >= 0);
Debug.Assert(offset < array.Length);
if (offset == 0)
{
return;
}
int length = array.Length;
ComplexF[] workspace = null;
ComplexArray.LockWorkspaceF(length, ref workspace);
for (int i = 0; i < length; i++)
{
workspace[(i + offset) % length] = array[i];
}
for (int i = 0; i < length; i++)
{
array[i] = workspace[i];
}
ComplexArray.UnlockWorkspaceF(ref workspace);
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Get the range of element lengths
/// </summary>
/// <param name="array"></param>
/// <param name="minimum"></param>
/// <param name="maximum"></param>
static public void GetLengthRange(Complex[] array, ref double minimum, ref double maximum)
{
minimum = +double.MaxValue;
maximum = -double.MaxValue;
for (int i = 0; i < array.Length; i++)
{
double temp = array[i].GetModulus();
minimum = Math.Min(temp, minimum);
maximum = Math.Max(temp, maximum);
}
}
/// <summary>
/// Get the range of element lengths
/// </summary>
/// <param name="array"></param>
/// <param name="minimum"></param>
/// <param name="maximum"></param>
static public void GetLengthRange(ComplexF[] array, ref float minimum, ref float maximum)
{
minimum = +float.MaxValue;
maximum = -float.MaxValue;
for (int i = 0; i < array.Length; i++)
{
float temp = array[i].GetModulus();
minimum = Math.Min(temp, minimum);
maximum = Math.Max(temp, maximum);
}
}
// // <summary>
// // Conver the complex array to a double array
// // </summary>
// // <param name="array"></param>
// // <param name="style"></param>
// // <returns></returns>
/* static public double[] ConvertToDoubleArray( Complex[] array, ConversionStyle style ) {
double[] newArray = new double[ array.Length ];
switch( style ) {
case ConversionStyle.Length:
for( int i = 0; i < array.Length; i ++ ) {
newArray[i] = (double) array[i].GetModulus();
}
break;
case ConversionStyle.Real:
for( int i = 0; i < array.Length; i ++ ) {
newArray[i] = (double) array[i].Re;
}
break;
case ConversionStyle.Imaginary:
for( int i = 0; i < array.Length; i ++ ) {
newArray[i] = (double) array[i].Im;
}
break;
default:
Debug.Assert( false );
break;
}
return newArray;
} */
//---------------------------------------------------------------------------------------------
/// <summary>
/// Determine whether the elements in the two arrays are the same
/// </summary>
/// <param name="array1"></param>
/// <param name="array2"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
static public bool IsEqual(Complex[] array1, Complex[] array2, double tolerance)
{
if (array1.Length != array2.Length)
{
return false;
}
for (int i = 0; i < array1.Length; i++)
{
if (Complex.IsEqual(array1[i], array2[i], tolerance) == false)
{
return false;
}
}
return true;
}
/// <summary>
/// Determine whether the elements in the two arrays are the same
/// </summary>
/// <param name="array1"></param>
/// <param name="array2"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
static public bool IsEqual(ComplexF[] array1, ComplexF[] array2, float tolerance)
{
if (array1.Length != array2.Length)
{
return false;
}
for (int i = 0; i < array1.Length; i++)
{
if (ComplexF.IsEqual(array1[i], array2[i], tolerance) == false)
{
return false;
}
}
return true;
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Add a specific value to each element in the array
/// </summary>
/// <param name="array"></param>
/// <param name="offset"></param>
static public void Offset(Complex[] array, double offset)
{
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i].Re += offset;
}
}
/// <summary>
/// Add a specific value to each element in the array
/// </summary>
/// <param name="array"></param>
/// <param name="offset"></param>
static public void Offset(Complex[] array, Complex offset)
{
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i] += offset;
}
}
/// <summary>
/// Add a specific value to each element in the array
/// </summary>
/// <param name="array"></param>
/// <param name="offset"></param>
static public void Offset(ComplexF[] array, float offset)
{
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i].Re += offset;
}
}
/// <summary>
/// Add a specific value to each element in the array
/// </summary>
/// <param name="array"></param>
/// <param name="offset"></param>
static public void Offset(ComplexF[] array, ComplexF offset)
{
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i] += offset;
}
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
static public void Scale(Complex[] array, double scale)
{
Debug.Assert(array != null);
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
/// <param name="start"></param>
/// <param name="length"></param>
static public void Scale(Complex[] array, double scale, int start, int length)
{
Debug.Assert(array != null);
Debug.Assert(start >= 0);
Debug.Assert(length >= 0);
Debug.Assert((start + length) < array.Length);
for (int i = 0; i < length; i++)
{
array[i + start] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
static public void Scale(Complex[] array, Complex scale)
{
Debug.Assert(array != null);
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
/// <param name="start"></param>
/// <param name="length"></param>
static public void Scale(Complex[] array, Complex scale, int start, int length)
{
Debug.Assert(array != null);
Debug.Assert(start >= 0);
Debug.Assert(length >= 0);
Debug.Assert((start + length) < array.Length);
for (int i = 0; i < length; i++)
{
array[i + start] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
static public void Scale(ComplexF[] array, float scale)
{
Debug.Assert(array != null);
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
/// <param name="start"></param>
/// <param name="length"></param>
static public void Scale(ComplexF[] array, float scale, int start, int length)
{
Debug.Assert(array != null);
Debug.Assert(start >= 0);
Debug.Assert(length >= 0);
Debug.Assert((start + length) < array.Length);
for (int i = 0; i < length; i++)
{
array[i + start] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
static public void Scale(ComplexF[] array, ComplexF scale)
{
Debug.Assert(array != null);
int length = array.Length;
for (int i = 0; i < length; i++)
{
array[i] *= scale;
}
}
/// <summary>
/// Multiply each element in the array by a specific value
/// </summary>
/// <param name="array"></param>
/// <param name="scale"></param>
/// <param name="start"></param>
/// <param name="length"></param>
static public void Scale(ComplexF[] array, ComplexF scale, int start, int length)
{
Debug.Assert(array != null);
Debug.Assert(start >= 0);
Debug.Assert(length >= 0);
Debug.Assert((start + length) < array.Length);
for (int i = 0; i < length; i++)
{
array[i + start] *= scale;
}
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Multiply each element in target array with corresponding element in rhs array
/// </summary>
/// <param name="target"></param>
/// <param name="rhs"></param>
static public void Multiply(Complex[] target, Complex[] rhs)
{
ComplexArray.Multiply(target, rhs, target);
}
/// <summary>
/// Multiply each element in lhs array with corresponding element in rhs array and
/// put product in result array
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <param name="result"></param>
static public void Multiply(Complex[] lhs, Complex[] rhs, Complex[] result)
{
Debug.Assert(lhs != null);
Debug.Assert(rhs != null);
Debug.Assert(result != null);
Debug.Assert(lhs.Length == rhs.Length);
Debug.Assert(lhs.Length == result.Length);
int length = lhs.Length;
for (int i = 0; i < length; i++)
{
result[i] = lhs[i] * rhs[i];
}
}
/// <summary>
/// Multiply each element in target array with corresponding element in rhs array
/// </summary>
/// <param name="target"></param>
/// <param name="rhs"></param>
static public void Multiply(ComplexF[] target, ComplexF[] rhs)
{
ComplexArray.Multiply(target, rhs, target);
}
/// <summary>
/// Multiply each element in lhs array with corresponding element in rhs array and
/// put product in result array
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <param name="result"></param>
static public void Multiply(ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result)
{
Debug.Assert(lhs != null);
Debug.Assert(rhs != null);
Debug.Assert(result != null);
Debug.Assert(lhs.Length == rhs.Length);
Debug.Assert(lhs.Length == result.Length);
int length = lhs.Length;
for (int i = 0; i < length; i++)
{
result[i] = lhs[i] * rhs[i];
}
}
//---------------------------------------------------------------------------------------------
/// <summary>
/// Divide each element in target array with corresponding element in rhs array
/// </summary>
/// <param name="target"></param>
/// <param name="rhs"></param>
static public void Divide(Complex[] target, Complex[] rhs)
{
ComplexArray.Divide(target, rhs, target);
}
/// <summary>
/// Divide each element in lhs array with corresponding element in rhs array and
/// put product in result array
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <param name="result"></param>
static public void Divide(Complex[] lhs, Complex[] rhs, Complex[] result)
{
Debug.Assert(lhs != null);
Debug.Assert(rhs != null);
Debug.Assert(result != null);
Debug.Assert(lhs.Length == rhs.Length);
Debug.Assert(lhs.Length == result.Length);
int length = lhs.Length;
for (int i = 0; i < length; i++)
{
result[i] = lhs[i] / rhs[i];
}
}
/// <summary>
/// Divide each element in target array with corresponding element in rhs array
/// </summary>
/// <param name="target"></param>
/// <param name="rhs"></param>
static public void Divide(ComplexF[] target, ComplexF[] rhs)
{
ComplexArray.Divide(target, rhs, target);
}
/// <summary>
/// Divide each element in lhs array with corresponding element in rhs array and
/// put product in result array
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <param name="result"></param>
static public void Divide(ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result)
{
Debug.Assert(lhs != null);
Debug.Assert(rhs != null);
Debug.Assert(result != null);
Debug.Assert(lhs.Length == rhs.Length);
Debug.Assert(lhs.Length == result.Length);
ComplexF zero = ComplexF.Zero;
int length = lhs.Length;
for (int i = 0; i < length; i++)
{
if (rhs[i] != zero)
{
result[i] = lhs[i] / rhs[i];
}
else
{
result[i] = zero;
}
}
}
//---------------------------------------------------------------------------------------------
/*static public void Flip( ComplexF[] array, Size3 size ) {
Debug.Assert( array != null );
ComplexF[] workspace = null;
ComplexArray.LockWorkspaceF( size.GetTotalLength(), ref workspace );
for( int z = 0; z < size.Depth; z ++ ) {
for( int y = 0; y < size.Height; y ++ ) {
int xyzOffset = 0 + y * size.Width + z * size.Width * size.Height;
int abcOffset = size.Width - 1 + ( size.Height - y - 1 ) * size.Width + ( size.Depth - z - 1 ) * size.Width * size.Height;
for( int x = 0; x < size.Width; x ++ ) {
workspace[ xyzOffset ++ ] = array[ abcOffset -- ];
}
}
}
for( int i = 0; i < size.GetTotalLength(); i ++ ) {
array[ i ] = workspace[ i ];
}
ComplexArray.UnlockWorkspaceF( ref workspace );
} */
/// <summary>
/// Copy an array
/// </summary>
/// <param name="dest"></param>
/// <param name="source"></param>
static public void Copy(Complex[] dest, Complex[] source)
{
Debug.Assert(dest != null);
Debug.Assert(source != null);
Debug.Assert(dest.Length == source.Length);
for (int i = 0; i < dest.Length; i++)
{
dest[i] = source[i];
}
}
/// <summary>
/// Copy an array
/// </summary>
/// <param name="dest"></param>
/// <param name="source"></param>
static public void Copy(ComplexF[] dest, ComplexF[] source)
{
Debug.Assert(dest != null);
Debug.Assert(source != null);
Debug.Assert(dest.Length == source.Length);
for (int i = 0; i < dest.Length; i++)
{
dest[i] = source[i];
}
}
/// <summary>
/// Reverse the elements in the array
/// </summary>
/// <param name="array"></param>
static public void Reverse(Complex[] array)
{
Complex temp;
int length = array.Length;
for (int i = 0; i < length / 2; i++)
{
temp = array[i];
array[i] = array[length - 1 - i];
array[length - 1 - i] = temp;
}
}
/// <summary>
/// Scale and offset the elements in the array so that the
/// overall range is [0, 1]
/// </summary>
/// <param name="array"></param>
static public void Normalize(Complex[] array)
{
double min = 0, max = 0;
GetLengthRange(array, ref min, ref max);
Scale(array, (1 / (max - min)));
Offset(array, (-min / (max - min)));
}
/// <summary>
/// Scale and offset the elements in the array so that the
/// overall range is [0, 1]
/// </summary>
/// <param name="array"></param>
static public void Normalize(ComplexF[] array)
{
float min = 0, max = 0;
GetLengthRange(array, ref min, ref max);
Scale(array, (1 / (max - min)));
Offset(array, (-min / (max - min)));
}
/// <summary>
/// Invert each element in the array
/// </summary>
/// <param name="array"></param>
static public void Invert(Complex[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = ((Complex)1) / array[i];
}
}
/// <summary>
/// Invert each element in the array
/// </summary>
/// <param name="array"></param>
static public void Invert(ComplexF[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = ((ComplexF)1) / array[i];
}
}
//----------------------------------------------------------------------------------------
}
}

View File

@@ -0,0 +1,43 @@
namespace Exocortex.DSP
{
public class SimplePolynomial : Polynomial
{
public SimplePolynomial(params double[] coefficients) : base(coefficients) { }
public override double Evaluate(double value)
{
var retval = coefficients[0];
var f = value;
for (int i = 1; i < coefficients.Length; i++)
{
retval += coefficients[i] * f;
f *= value;
}
return retval;
}
}
public abstract class Polynomial
{
public Polynomial(params double[] coefficients)
{
this.coefficients = new double[coefficients.Length];
for (int i = 0; i < coefficients.Length; i++)
this.coefficients[i] = coefficients[i];
}
public abstract double Evaluate(double value);
protected double[] coefficients = null;
public double GetCoefficient(int index)
{
if (null == coefficients || index < 0 || index >= coefficients.Length)
{
return double.NaN;
}
return coefficients[index];
}
}
}

View File

@@ -0,0 +1,570 @@
/*
* BSD Licence:
* Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ]
* Exocortex Technologies [ www.exocortex.org ]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Exocortex.DSP
{
// Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org
// Version: May 4, 2002
/// <summary>
/// <p>A single-precision complex number representation.</p>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ComplexF : IComparable, ICloneable
{
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// The real component of the complex number
/// </summary>
public float Re;
/// <summary>
/// The imaginary component of the complex number
/// </summary>
public float Im;
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Create a complex number from a real and an imaginary component
/// </summary>
/// <param name="real"></param>
/// <param name="imaginary"></param>
public ComplexF(float real, float imaginary)
{
this.Re = real;
this.Im = imaginary;
}
/// <summary>
/// Create a complex number based on an existing complex number
/// </summary>
/// <param name="c"></param>
public ComplexF(ComplexF c)
{
this.Re = c.Re;
this.Im = c.Im;
}
/// <summary>
/// Create a complex number from a real and an imaginary component
/// </summary>
/// <param name="real"></param>
/// <param name="imaginary"></param>
/// <returns></returns>
static public ComplexF FromRealImaginary(float real, float imaginary)
{
ComplexF c;
c.Re = real;
c.Im = imaginary;
return c;
}
/// <summary>
/// Create a complex number from a modulus (length) and an argument (radian)
/// </summary>
/// <param name="modulus"></param>
/// <param name="argument"></param>
/// <returns></returns>
static public ComplexF FromModulusArgument(float modulus, float argument)
{
ComplexF c;
c.Re = (float)(modulus * System.Math.Cos(argument));
c.Im = (float)(modulus * System.Math.Sin(argument));
return c;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
object ICloneable.Clone()
{
return new ComplexF(this);
}
/// <summary>
/// Clone the complex number
/// </summary>
/// <returns></returns>
public ComplexF Clone()
{
return new ComplexF(this);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// The modulus (length) of the complex number
/// </summary>
/// <returns></returns>
public float GetModulus()
{
float x = this.Re;
float y = this.Im;
return (float)Math.Sqrt(x * x + y * y);
}
/// <summary>
/// The squared modulus (length^2) of the complex number
/// </summary>
/// <returns></returns>
public float GetModulusSquared()
{
float x = this.Re;
float y = this.Im;
return (float)x * x + y * y;
}
/// <summary>
/// The argument (radians) of the complex number
/// </summary>
/// <returns></returns>
public float GetArgument()
{
return (float)Math.Atan2(this.Im, this.Re);
}
//-----------------------------------------------------------------------------------
/// <summary>
/// Get the conjugate of the complex number
/// </summary>
/// <returns></returns>
public ComplexF GetConjugate()
{
return FromRealImaginary(this.Re, -this.Im);
}
//-----------------------------------------------------------------------------------
/// <summary>
/// Scale the complex number to 1.
/// </summary>
public void Normalize()
{
double modulus = this.GetModulus();
if (modulus == 0)
{
throw new DivideByZeroException("Can not normalize a complex number that is zero.");
}
this.Re = (float)(this.Re / modulus);
this.Im = (float)(this.Im / modulus);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Convert to a from double precision complex number to a single precison complex number
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public static explicit operator ComplexF(Complex c)
{
ComplexF cF;
cF.Re = (float)c.Re;
cF.Im = (float)c.Im;
return cF;
}
/// <summary>
/// Convert from a single precision real number to a complex number
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static explicit operator ComplexF(float f)
{
ComplexF c;
c.Re = f;
c.Im = 0;
return c;
}
/// <summary>
/// Convert from a single precision complex to a real number
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public static explicit operator float(ComplexF c)
{
return c.Re;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Are these two complex numbers equivalent?
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator ==(ComplexF a, ComplexF b)
{
return (a.Re == b.Re) && (a.Im == b.Im);
}
/// <summary>
/// Are these two complex numbers different?
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(ComplexF a, ComplexF b)
{
return (a.Re != b.Re) || (a.Im != b.Im);
}
/// <summary>
/// Get the hash code of the complex number
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return (this.Re.GetHashCode() ^ this.Im.GetHashCode());
}
/// <summary>
/// Is this complex number equivalent to another object?
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public override bool Equals(object o)
{
if (o is ComplexF)
{
ComplexF c = (ComplexF)o;
return (this == c);
}
return false;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Compare to other complex numbers or real numbers
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public int CompareTo(object o)
{
if (o == null)
{
return 1; // null sorts before current
}
if (o is ComplexF)
{
return this.GetModulus().CompareTo(((ComplexF)o).GetModulus());
}
if (o is float)
{
return this.GetModulus().CompareTo((float)o);
}
if (o is Complex)
{
return this.GetModulus().CompareTo(((Complex)o).GetModulus());
}
if (o is double)
{
return this.GetModulus().CompareTo((double)o);
}
throw new ArgumentException();
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// This operator doesn't do much. :-)
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static ComplexF operator +(ComplexF a)
{
return a;
}
/// <summary>
/// Negate the complex number
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static ComplexF operator -(ComplexF a)
{
a.Re = -a.Re;
a.Im = -a.Im;
return a;
}
/// <summary>
/// Add a complex number to a real
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static ComplexF operator +(ComplexF a, float f)
{
a.Re = a.Re + f;
return a;
}
/// <summary>
/// Add a real to a complex number
/// </summary>
/// <param name="f"></param>
/// <param name="a"></param>
/// <returns></returns>
public static ComplexF operator +(float f, ComplexF a)
{
a.Re = a.Re + f;
return a;
}
/// <summary>
/// Add to complex numbers
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static ComplexF operator +(ComplexF a, ComplexF b)
{
a.Re = a.Re + b.Re;
a.Im = a.Im + b.Im;
return a;
}
/// <summary>
/// Subtract a real from a complex number
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static ComplexF operator -(ComplexF a, float f)
{
a.Re = a.Re - f;
return a;
}
/// <summary>
/// Subtract a complex number from a real
/// </summary>
/// <param name="f"></param>
/// <param name="a"></param>
/// <returns></returns>
public static ComplexF operator -(float f, ComplexF a)
{
a.Re = f - a.Re;
a.Im = 0 - a.Im;
return a;
}
/// <summary>
/// Subtract two complex numbers
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static ComplexF operator -(ComplexF a, ComplexF b)
{
a.Re = a.Re - b.Re;
a.Im = a.Im - b.Im;
return a;
}
/// <summary>
/// Multiply a complex number by a real
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static ComplexF operator *(ComplexF a, float f)
{
a.Re = a.Re * f;
a.Im = a.Im * f;
return a;
}
/// <summary>
/// Multiply a real by a complex number
/// </summary>
/// <param name="f"></param>
/// <param name="a"></param>
/// <returns></returns>
public static ComplexF operator *(float f, ComplexF a)
{
a.Re = a.Re * f;
a.Im = a.Im * f;
return a;
}
/// <summary>
/// Multiply two complex numbers together
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static ComplexF operator *(ComplexF a, ComplexF b)
{
// (x + yi)(u + vi) = (xu yv) + (xv + yu)i.
double x = a.Re, y = a.Im;
double u = b.Re, v = b.Im;
a.Re = (float)(x * u - y * v);
a.Im = (float)(x * v + y * u);
return a;
}
/// <summary>
/// Divide a complex number by a real number
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static ComplexF operator /(ComplexF a, float f)
{
if (f == 0)
{
throw new DivideByZeroException();
}
a.Re = a.Re / f;
a.Im = a.Im / f;
return a;
}
/// <summary>
/// Divide a complex number by a complex number
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static ComplexF operator /(ComplexF a, ComplexF b)
{
double x = a.Re, y = a.Im;
double u = b.Re, v = b.Im;
double denom = u * u + v * v;
if (denom == 0)
{
throw new DivideByZeroException();
}
a.Re = (float)((x * u + y * v) / denom);
a.Im = (float)((y * u - x * v) / denom);
return a;
}
/// <summary>
/// Parse a complex representation in this fashion: "( %f, %f )"
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
static public ComplexF Parse(string s)
{
throw new NotImplementedException("ComplexF ComplexF.Parse( string s ) is not implemented.");
}
/// <summary>
/// Get the string representation
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("( {0}, {1}i )", this.Re, this.Im);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Determine whether two complex numbers are almost (i.e. within the tolerance) equivalent.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
static public bool IsEqual(ComplexF a, ComplexF b, float tolerance)
{
return
(Math.Abs(a.Re - b.Re) < tolerance) &&
(Math.Abs(a.Im - b.Im) < tolerance);
}
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
/// <summary>
/// Represents zero
/// </summary>
static public ComplexF Zero
{
get { return new ComplexF(0, 0); }
}
/// <summary>
/// Represents the result of sqrt( -1 )
/// </summary>
static public ComplexF I
{
get { return new ComplexF(0, 1); }
}
/// <summary>
/// Represents the largest possible value of ComplexF.
/// </summary>
static public ComplexF MaxValue
{
get { return new ComplexF(float.MaxValue, float.MaxValue); }
}
/// <summary>
/// Represents the smallest possible value of ComplexF.
/// </summary>
static public ComplexF MinValue
{
get { return new ComplexF(float.MinValue, float.MinValue); }
}
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
}
}

View File

@@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exocortex.DSP
{
public enum PassFilterType
{
Bessel,
Butterworth,
Chebyshev,
CriticalDamping
}
public static class PassFilter
{
public static double[] HighPass(double[] values, double sampleRate, double centerFrequency, PassFilterType type, uint order)
{
return RunFilter(values, sampleRate, centerFrequency, type, order, false);
}
public static double[] LowPass(double[] values, double sampleRate, double centerFrequency, PassFilterType type, uint order)
{
return RunFilter(values, sampleRate, centerFrequency, type, order, true);
}
private static double[] RunFilter(double[] values, double sampleRate, double centerFrequency, PassFilterType type, uint order, bool lowPass)
{
Complex[] data = new Complex[values.Length];
for (int i = 0; i < values.Length; i++)
{
data[i] = (Complex)values[i];
}
Complex[] result;
switch (type)
{
case PassFilterType.Bessel:
result = BesselFilter(data, sampleRate, centerFrequency, order, lowPass);
break;
case PassFilterType.Chebyshev:
result = ChebyshevFilter(data, sampleRate, centerFrequency, order, 1D, 0.005, lowPass);
break;
case PassFilterType.Butterworth:
default:
result = ButterworthFilter(data, sampleRate, centerFrequency, order, 1D, lowPass);
break;
}
return result.Select(c => c.Re).ToArray();
}
private static Complex[] BesselFilter(Complex[] values, double sampleRate, double centerFrequency, uint order, bool lowPass)
{
var signal = (Complex[])values.Clone();
var N = signal.Length;
// Get the reverse Bessel polynomial-to-gain function denominator
Polynomial B = BesselDenominatorPolynomial((int)order);
var numerator = Math.Sqrt(B.GetCoefficient(0));
// Apply forward FFT
Fourier.FFT(signal, N, FourierDirection.Forward);
// Apply gain function
if (centerFrequency > 0)
{
var numBins = N / 2; // Half the length of the FFT by symmetry
var binWidth = sampleRate / N; // Hz
// Filter
System.Threading.Tasks.Parallel.For(1, N / 2, i =>
{
var binFreq = binWidth * i;
var gain = BesselGain(numerator, B, binFreq, centerFrequency, lowPass);
signal[i] *= gain;
signal[N - i] *= gain;
});
}
// Reverse filtered signal
Fourier.FFT(signal, N, FourierDirection.Backward);
signal = signal.Select(n => n / N).ToArray(); // scale result by length
return signal;
}
private static double BesselGain(double numerator, Polynomial B, double freq, double centerFreq, bool lowPass)
{
return numerator / Math.Sqrt(B.Evaluate(lowPass ? freq / centerFreq : centerFreq / freq)); // low-to-high pass is inverting f/fc
}
// uses logic from https://www.centerspace.net/butterworth-filter-csharp
private static Complex[] ButterworthFilter(Complex[] values, double sampleRate, double centerFrequency, uint order, double DCGain, bool lowPass)
{
var signal = (Complex[])values.Clone();
var N = signal.Length;
// Apply forward FFT
Fourier.FFT(signal, N, FourierDirection.Forward);
// Apply gain function
if (centerFrequency > 0)
{
var numBins = N / 2; // Half the length of the FFT by symmetry
var binWidth = sampleRate / N; // Hz
// Filter
System.Threading.Tasks.Parallel.For(1, N / 2, i =>
{
var binFreq = binWidth * i;
var gain = ButterworthGain(DCGain, binFreq, centerFrequency, order, lowPass);
signal[i] *= gain;
signal[N - i] *= gain;
});
}
// Reverse filtered signal
Fourier.FFT(signal, N, FourierDirection.Backward);
// scale result by length
signal = signal.Select(n => n / N).ToArray();
return signal;
}
private static double ButterworthGain(double DCGain, double freq, double centerFreq, uint order, bool lowPass)
{
return DCGain / Math.Sqrt(1 + Math.Pow(lowPass ? freq / centerFreq : centerFreq / freq, 2.0 * order)); // low-to-high pass is just inverting f/fc
}
// uses logic from https://www.centerspace.net/chebyshev-filter-csharp
private static Complex[] ChebyshevFilter(Complex[] values, double sampleRate, double centerFrequency, uint order, double DCGain, double ripple, bool lowPass)
{
var signal = (Complex[])values.Clone();
var N = signal.Length;
// Get the Chebyshev polynomial
Polynomial T = ChebyshevPolynomial((int)order);
// Apply forward FFT
Fourier.FFT(signal, N, FourierDirection.Forward);
// Remove DC offset
signal[0] = (Complex)0;
// Apply gain function
if (centerFrequency > 0)
{
var numBins = N / 2; // Half the length of the FFT by symmetry
var binWidth = sampleRate / N; // Hz
// Filter
System.Threading.Tasks.Parallel.For(1, N / 2, i =>
{
var binFreq = binWidth * i;
var gain = ChebyshevGain(DCGain, ripple, T, binFreq, centerFrequency, order, lowPass);
signal[i] *= gain;
signal[N - i] *= gain;
});
}
// Reverse filtered signal
Fourier.FFT(signal, N, FourierDirection.Backward);
// scale result by length
signal = signal.Select(n => n / N).ToArray();
return signal;
}
private static double ChebyshevGain(double DCGain, double ripple, Polynomial T, double freq, double centerFreq, uint order, bool lowPass)
{
return DCGain / Math.Sqrt(1 + ripple * Math.Pow(T.Evaluate(lowPass ? freq / centerFreq : centerFreq / freq), 2.0 * order));
}
private static Polynomial ChebyshevPolynomial(int order)
{
switch (order)
{
case 0:
return new SimplePolynomial(new double[] { 1 });
case 1:
return new SimplePolynomial(new double[] { 0, 1 });
case 2:
return new SimplePolynomial(new double[] { -1, 0, 2 });
case 3:
return new SimplePolynomial(new double[] { 0, -3, 0, 4 });
case 4:
return new SimplePolynomial(new double[] { 1, 0, -8, 0, 8 });
case 5:
return new SimplePolynomial(new double[] { 0, 5, 0, -20, 0, 16 });
case 6:
return new SimplePolynomial(new double[] { -1, 0, 1, 8, 0, -48, 0, 32 });
case 7:
return new SimplePolynomial(new double[] { 0, 7, 0, 56, 0, -112, 0, 64 });
case 8:
return new SimplePolynomial(new double[] { 1, 0, -32, 0, 160, 0, -256, 0, 128 });
default:
throw new NotImplementedException();
}
}
private static Polynomial BesselDenominatorPolynomial(int order)
{
// Polynomials in the square-rooted denominator of bessel gain functions
// Obtained via magnitude calc of H(iw) of each Bessel H(s): https://en.wikipedia.org/wiki/Bessel_filter#The_transfer_function
// at https://www.symbolab.com/solver/complex-numbers-magnitude-calculator/
switch (order)
{
case 2:
return new SimplePolynomial(new double[] { 9, 0, 3, 0, 1 });
case 3:
return new SimplePolynomial(new double[] { 225, 0, 45, 0, 6, 0, 1 });
case 4:
return new SimplePolynomial(new double[] { 11025, 0, 1575, 0, 135, 0, 10, 0, 1 });
case 5:
return new SimplePolynomial(new double[] { 893025, 0, 99225, 0, 6300, 0, 315, 0, 15, 0, 1 });
case 6:
return new SimplePolynomial(new double[] { 108056025, 0, 9823275, 0, 496125, 0, 18900, 0, 630, 0, 21, 0, 1 });
case 7:
return new SimplePolynomial(new double[] { 18261468225, 0, 1404728325, 0, 58939650, 0, 1819125, 0, 47250, 0, 1134, 0, 28, 0, 1 });
case 8:
return new SimplePolynomial(new double[] { 4108830350625, 0, 273922023375, 0, 9833098275, 0, 255405150, 0, 5457375, 0, 103950, 0, 1890, 0, 36, 0, 1 });
default:
throw new NotImplementedException();
}
}
}
}

View File

@@ -0,0 +1,577 @@
/*
* BSD Licence:
* Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ]
* Exocortex Technologies [ www.exocortex.org ]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Exocortex.DSP
{
// Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org
// Version: May 4, 2002
/// <summary>
/// <p>A double-precision complex number representation.</p>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Complex : IComparable, ICloneable
{
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// The real component of the complex number
/// </summary>
public double Re;
/// <summary>
/// The imaginary component of the complex number
/// </summary>
public double Im;
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Create a complex number from a real and an imaginary component
/// </summary>
/// <param name="real"></param>
/// <param name="imaginary"></param>
public Complex(double real, double imaginary)
{
this.Re = real;
this.Im = imaginary;
}
/// <summary>
/// Create a complex number based on an existing complex number
/// </summary>
/// <param name="c"></param>
public Complex(Complex c)
{
this.Re = c.Re;
this.Im = c.Im;
}
/// <summary>
/// Create a complex number from a real and an imaginary component
/// </summary>
/// <param name="real"></param>
/// <param name="imaginary"></param>
/// <returns></returns>
static public Complex FromRealImaginary(double real, double imaginary)
{
Complex c;
c.Re = real;
c.Im = imaginary;
return c;
}
/// <summary>
/// Create a complex number from a modulus (length) and an argument (radian)
/// </summary>
/// <param name="modulus"></param>
/// <param name="argument"></param>
/// <returns></returns>
static public Complex FromModulusArgument(double modulus, double argument)
{
Complex c;
c.Re = modulus * System.Math.Cos(argument);
c.Im = modulus * System.Math.Sin(argument);
return c;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
object ICloneable.Clone()
{
return new Complex(this);
}
/// <summary>
/// Clone the complex number
/// </summary>
/// <returns></returns>
public Complex Clone()
{
return new Complex(this);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// The modulus (length) of the complex number
/// </summary>
/// <returns></returns>
public double GetModulus()
{
double x = this.Re;
double y = this.Im;
return (double)Math.Sqrt(x * x + y * y);
}
/// <summary>
/// The squared modulus (length^2) of the complex number
/// </summary>
/// <returns></returns>
public double GetModulusSquared()
{
double x = this.Re;
double y = this.Im;
return (double)x * x + y * y;
}
/// <summary>
/// The argument (radians) of the complex number
/// </summary>
/// <returns></returns>
public double GetArgument()
{
return (double)Math.Atan2(this.Im, this.Re);
}
//-----------------------------------------------------------------------------------
/// <summary>
/// Get the conjugate of the complex number
/// </summary>
/// <returns></returns>
public Complex GetConjugate()
{
return FromRealImaginary(this.Re, -this.Im);
}
//-----------------------------------------------------------------------------------
/// <summary>
/// Scale the complex number to 1.
/// </summary>
public void Normalize()
{
double modulus = this.GetModulus();
if (modulus == 0)
{
throw new DivideByZeroException("Can not normalize a complex number that is zero.");
}
this.Re = this.Re / modulus;
this.Im = this.Im / modulus;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Convert to a from double precision complex number to a single precison complex number
/// </summary>
/// <param name="cF"></param>
/// <returns></returns>
public static explicit operator Complex(ComplexF cF)
{
Complex c;
c.Re = cF.Re;
c.Im = cF.Im;
return c;
}
/// <summary>
/// Convert from a single precision real number to a complex number
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static explicit operator Complex(double d)
{
Complex c;
c.Re = d;
c.Im = 0;
return c;
}
/// <summary>
/// Convert from a single precision complex to a real number
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public static explicit operator double(Complex c)
{
return c.Re;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Are these two complex numbers equivalent?
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator ==(Complex a, Complex b)
{
return (a.Re == b.Re) && (a.Im == b.Im);
}
/// <summary>
/// Are these two complex numbers different?
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool operator !=(Complex a, Complex b)
{
return (a.Re != b.Re) || (a.Im != b.Im);
}
/// <summary>
/// Get the hash code of the complex number
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return (this.Re.GetHashCode() ^ this.Im.GetHashCode());
}
/// <summary>
/// Is this complex number equivalent to another object?
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public override bool Equals(object o)
{
if (o is Complex)
{
Complex c = (Complex)o;
return (this == c);
}
return false;
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Compare to other complex numbers or real numbers
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public int CompareTo(object o)
{
if (o == null)
{
return 1; // null sorts before current
}
if (o is Complex)
{
return this.GetModulus().CompareTo(((Complex)o).GetModulus());
}
if (o is double)
{
return this.GetModulus().CompareTo((double)o);
}
if (o is ComplexF)
{
return this.GetModulus().CompareTo(((ComplexF)o).GetModulus());
}
if (o is float)
{
return this.GetModulus().CompareTo((float)o);
}
throw new ArgumentException();
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// This operator doesn't do much. :-)
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static Complex operator +(Complex a)
{
return a;
}
/// <summary>
/// Negate the complex number
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static Complex operator -(Complex a)
{
a.Re = -a.Re;
a.Im = -a.Im;
return a;
}
/// <summary>
/// Add a complex number to a real
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static Complex operator +(Complex a, double f)
{
a.Re = a.Re + f;
return a;
}
/// <summary>
/// Add a real to a complex number
/// </summary>
/// <param name="f"></param>
/// <param name="a"></param>
/// <returns></returns>
public static Complex operator +(double f, Complex a)
{
a.Re = a.Re + f;
return a;
}
/// <summary>
/// Add to complex numbers
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Complex operator +(Complex a, Complex b)
{
a.Re = a.Re + b.Re;
a.Im = a.Im + b.Im;
return a;
}
/// <summary>
/// Subtract a real from a complex number
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static Complex operator -(Complex a, double f)
{
a.Re = a.Re - f;
return a;
}
/// <summary>
/// Subtract a complex number from a real
/// </summary>
/// <param name="f"></param>
/// <param name="a"></param>
/// <returns></returns>
public static Complex operator -(double f, Complex a)
{
a.Re = (float)(f - a.Re);
a.Im = (float)(0 - a.Im);
return a;
}
/// <summary>
/// Subtract two complex numbers
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Complex operator -(Complex a, Complex b)
{
a.Re = a.Re - b.Re;
a.Im = a.Im - b.Im;
return a;
}
/// <summary>
/// Multiply a complex number by a real
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static Complex operator *(Complex a, double f)
{
a.Re = a.Re * f;
a.Im = a.Im * f;
return a;
}
/// <summary>
/// Multiply a real by a complex number
/// </summary>
/// <param name="f"></param>
/// <param name="a"></param>
/// <returns></returns>
public static Complex operator *(double f, Complex a)
{
a.Re = a.Re * f;
a.Im = a.Im * f;
return a;
}
/// <summary>
/// Multiply two complex numbers together
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Complex operator *(Complex a, Complex b)
{
// (x + yi)(u + vi) = (xu yv) + (xv + yu)i.
double x = a.Re, y = a.Im;
double u = b.Re, v = b.Im;
a.Re = x * u - y * v;
a.Im = x * v + y * u;
return a;
}
/// <summary>
/// Divide a complex number by a real number
/// </summary>
/// <param name="a"></param>
/// <param name="f"></param>
/// <returns></returns>
public static Complex operator /(Complex a, double f)
{
if (f == 0)
{
throw new DivideByZeroException();
}
a.Re = a.Re / f;
a.Im = a.Im / f;
return a;
}
/// <summary>
/// Divide a complex number by a complex number
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static Complex operator /(Complex a, Complex b)
{
double x = a.Re, y = a.Im;
double u = b.Re, v = b.Im;
double denom = u * u + v * v;
if (denom == 0)
{
throw new DivideByZeroException();
}
a.Re = (x * u + y * v) / denom;
a.Im = (y * u - x * v) / denom;
return a;
}
/// <summary>
/// Parse a complex representation in this fashion: "( %f, %f )"
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
static public Complex Parse(string s)
{
throw new NotImplementedException("Complex Complex.Parse( string s ) is not implemented.");
}
/// <summary>
/// Get the string representation
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("( {0}, {1}i )", this.Re, this.Im);
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
/// <summary>
/// Determine whether two complex numbers are almost (i.e. within the tolerance) equivalent.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
static public bool IsEqual(Complex a, Complex b, double tolerance)
{
return
(Math.Abs(a.Re - b.Re) < tolerance) &&
(Math.Abs(a.Im - b.Im) < tolerance);
}
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
/// <summary>
/// Represents zero
/// </summary>
static public Complex Zero
{
get { return new Complex(0, 0); }
}
/// <summary>
/// Represents the result of sqrt( -1 )
/// </summary>
static public Complex I
{
get { return new Complex(0, 1); }
}
/// <summary>
/// Represents the largest possible value of Complex.
/// </summary>
static public Complex MaxValue
{
get { return new Complex(double.MaxValue, double.MaxValue); }
}
/// <summary>
/// Represents the smallest possible value of Complex.
/// </summary>
static public Complex MinValue
{
get { return new Complex(double.MinValue, double.MinValue); }
}
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
}
}

View File

@@ -0,0 +1,347 @@
/*
* BSD Licence:
* Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ]
* Exocortex Technologies [ www.exocortex.org ]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
using System;
using System.Diagnostics;
namespace Exocortex.DSP
{
// Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org
// Version: May 4, 2002
/// <summary>
/// <p>A set of statistical utilities for complex number arrays</p>
/// </summary>
public class ComplexStats
{
//---------------------------------------------------------------------------------------------
private ComplexStats()
{
}
//---------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
/// <summary>
/// Calculate the sum
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public ComplexF Sum(ComplexF[] data)
{
Debug.Assert(data != null);
return SumRecursion(data, 0, data.Length);
}
static private ComplexF SumRecursion(ComplexF[] data, int start, int end)
{
Debug.Assert(0 <= start, "start = " + start);
Debug.Assert(start < end, "start = " + start + " and end = " + end);
Debug.Assert(end <= data.Length, "end = " + end + " and data.Length = " + data.Length);
if ((end - start) <= 1000)
{
ComplexF sum = ComplexF.Zero;
for (int i = start; i < end; i++)
{
sum += data[i];
}
return sum;
}
else
{
int middle = (start + end) >> 1;
return SumRecursion(data, start, middle) + SumRecursion(data, middle, end);
}
}
/// <summary>
/// Calculate the sum
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public Complex Sum(Complex[] data)
{
Debug.Assert(data != null);
return SumRecursion(data, 0, data.Length);
}
static private Complex SumRecursion(Complex[] data, int start, int end)
{
Debug.Assert(0 <= start, "start = " + start);
Debug.Assert(start < end, "start = " + start + " and end = " + end);
Debug.Assert(end <= data.Length, "end = " + end + " and data.Length = " + data.Length);
if ((end - start) <= 1000)
{
Complex sum = Complex.Zero;
for (int i = start; i < end; i++)
{
sum += data[i];
}
return sum;
}
else
{
int middle = (start + end) >> 1;
return SumRecursion(data, start, middle) + SumRecursion(data, middle, end);
}
}
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
/// <summary>
/// Calculate the sum of squares
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public ComplexF SumOfSquares(ComplexF[] data)
{
Debug.Assert(data != null);
return SumOfSquaresRecursion(data, 0, data.Length);
}
static private ComplexF SumOfSquaresRecursion(ComplexF[] data, int start, int end)
{
Debug.Assert(0 <= start, "start = " + start);
Debug.Assert(start < end, "start = " + start + " and end = " + end);
Debug.Assert(end <= data.Length, "end = " + end + " and data.Length = " + data.Length);
if ((end - start) <= 1000)
{
ComplexF sumOfSquares = ComplexF.Zero;
for (int i = start; i < end; i++)
{
sumOfSquares += data[i] * data[i];
}
return sumOfSquares;
}
else
{
int middle = (start + end) >> 1;
return SumOfSquaresRecursion(data, start, middle) + SumOfSquaresRecursion(data, middle, end);
}
}
/// <summary>
/// Calculate the sum of squares
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public Complex SumOfSquares(Complex[] data)
{
Debug.Assert(data != null);
return SumOfSquaresRecursion(data, 0, data.Length);
}
static private Complex SumOfSquaresRecursion(Complex[] data, int start, int end)
{
Debug.Assert(0 <= start, "start = " + start);
Debug.Assert(start < end, "start = " + start + " and end = " + end);
Debug.Assert(end <= data.Length, "end = " + end + " and data.Length = " + data.Length);
if ((end - start) <= 1000)
{
Complex sumOfSquares = Complex.Zero;
for (int i = start; i < end; i++)
{
sumOfSquares += data[i] * data[i];
}
return sumOfSquares;
}
else
{
int middle = (start + end) >> 1;
return SumOfSquaresRecursion(data, start, middle) + SumOfSquaresRecursion(data, middle, end);
}
}
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
/// <summary>
/// Calculate the mean (average)
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public ComplexF Mean(ComplexF[] data)
{
return ComplexStats.Sum(data) / data.Length;
}
/// <summary>
/// Calculate the mean (average)
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public Complex Mean(Complex[] data)
{
return ComplexStats.Sum(data) / data.Length;
}
/// <summary>
/// Calculate the variance
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public ComplexF Variance(ComplexF[] data)
{
Debug.Assert(data != null);
if (data.Length == 0)
{
throw new DivideByZeroException("length of data is zero");
}
return ComplexStats.SumOfSquares(data) / data.Length - ComplexStats.Sum(data);
}
/// <summary>
/// Calculate the variance
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public Complex Variance(Complex[] data)
{
Debug.Assert(data != null);
if (data.Length == 0)
{
throw new DivideByZeroException("length of data is zero");
}
return ComplexStats.SumOfSquares(data) / data.Length - ComplexStats.Sum(data);
}
/// <summary>
/// Calculate the standard deviation
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public ComplexF StdDev(ComplexF[] data)
{
Debug.Assert(data != null);
if (data.Length == 0)
{
throw new DivideByZeroException("length of data is zero");
}
return ComplexMath.Sqrt(ComplexStats.Variance(data));
}
/// <summary>
/// Calculate the standard deviation
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
static public Complex StdDev(Complex[] data)
{
Debug.Assert(data != null);
if (data.Length == 0)
{
throw new DivideByZeroException("length of data is zero");
}
return ComplexMath.Sqrt(ComplexStats.Variance(data));
}
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
/// <summary>
/// Calculate the root mean squared (RMS) error between two sets of data.
/// </summary>
/// <param name="alpha"></param>
/// <param name="beta"></param>
/// <returns></returns>
static public float RMSError(ComplexF[] alpha, ComplexF[] beta)
{
Debug.Assert(alpha != null);
Debug.Assert(beta != null);
Debug.Assert(beta.Length == alpha.Length);
return (float)Math.Sqrt(SumOfSquaredErrorRecursion(alpha, beta, 0, alpha.Length));
}
static private float SumOfSquaredErrorRecursion(ComplexF[] alpha, ComplexF[] beta, int start, int end)
{
Debug.Assert(0 <= start, "start = " + start);
Debug.Assert(start < end, "start = " + start + " and end = " + end);
Debug.Assert(end <= alpha.Length, "end = " + end + " and alpha.Length = " + alpha.Length);
Debug.Assert(beta.Length == alpha.Length);
if ((end - start) <= 1000)
{
float sumOfSquaredError = 0;
for (int i = start; i < end; i++)
{
ComplexF delta = beta[i] - alpha[i];
sumOfSquaredError += (delta.Re * delta.Re) + (delta.Im * delta.Im);
}
return sumOfSquaredError;
}
else
{
int middle = (start + end) >> 1;
return SumOfSquaredErrorRecursion(alpha, beta, start, middle) + SumOfSquaredErrorRecursion(alpha, beta, middle, end);
}
}
/// <summary>
/// Calculate the root mean squared (RMS) error between two sets of data.
/// </summary>
/// <param name="alpha"></param>
/// <param name="beta"></param>
/// <returns></returns>
static public double RMSError(Complex[] alpha, Complex[] beta)
{
Debug.Assert(alpha != null);
Debug.Assert(beta != null);
Debug.Assert(beta.Length == alpha.Length);
return Math.Sqrt(SumOfSquaredErrorRecursion(alpha, beta, 0, alpha.Length));
}
static private double SumOfSquaredErrorRecursion(Complex[] alpha, Complex[] beta, int start, int end)
{
Debug.Assert(0 <= start, "start = " + start);
Debug.Assert(start < end, "start = " + start + " and end = " + end);
Debug.Assert(end <= alpha.Length, "end = " + end + " and alpha.Length = " + alpha.Length);
Debug.Assert(beta.Length == alpha.Length);
if ((end - start) <= 1000)
{
double sumOfSquaredError = 0;
for (int i = start; i < end; i++)
{
Complex delta = beta[i] - alpha[i];
sumOfSquaredError += (delta.Re * delta.Re) + (delta.Im * delta.Im);
}
return sumOfSquaredError;
}
else
{
int middle = (start + end) >> 1;
return SumOfSquaredErrorRecursion(alpha, beta, start, middle) + SumOfSquaredErrorRecursion(alpha, beta, middle, end);
}
}
}
}

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Exocortex.DSP.ComplexArray" Collapsed="true">
<Position X="0.5" Y="1" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAEADAQAAAAgAEQIhACAAAgAAEAAEAAACAAAAgAA=</HashCode>
<FileName>ComplexArray.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Exocortex.DSP.ComplexMath" Collapsed="true">
<Position X="2.25" Y="1" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAASBAAAAAAAAAAEAAAA=</HashCode>
<FileName>ComplexMath.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Exocortex.DSP.ComplexStats" Collapsed="true">
<Position X="0.5" Y="2" Width="1.5" />
<TypeIdentifier>
<HashCode>IAAAAAAAAAAAAAAAAQAAAAABAAABBAAEgAAAAAAAAAQ=</HashCode>
<FileName>ComplexStats.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Exocortex.DSP.Fourier" Collapsed="true">
<Position X="2.25" Y="2" Width="1.5" />
<TypeIdentifier>
<HashCode>gWgFAKgAgAgQBAICCIAGgBRCABMBAEAABAAAAgAgAKA=</HashCode>
<FileName>Fourier.cs</FileName>
</TypeIdentifier>
</Class>
<Struct Name="Exocortex.DSP.Complex" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="0.5" Y="3.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAgAAAQABAABgwM9QUAAAAAAwEAEIAAAAAgACAAAzA=</HashCode>
<FileName>Complex.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Struct>
<Struct Name="Exocortex.DSP.ComplexF" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="2.25" Y="3.25" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAgAAAQAAAABgwM9QcAAAAAAwFAEIAAAAAgACAAAzA=</HashCode>
<FileName>ComplexF.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Struct>
<Enum Name="Exocortex.DSP.FourierDirection" Collapsed="true">
<Position X="0.5" Y="4.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=</HashCode>
<FileName>FourierDirection.cs</FileName>
</TypeIdentifier>
</Enum>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5497D233-0E72-45C2-8A2B-9264ECD2AE31}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Exocortex</RootNamespace>
<AssemblyName>Exocortex</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Complex.cs" />
<Compile Include="ComplexArray.cs" />
<Compile Include="ComplexF.cs" />
<Compile Include="ComplexMath.cs" />
<Compile Include="ComplexStats.cs" />
<Compile Include="Fourier.cs" />
<Compile Include="FourierDirection.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,56 @@
/*
* BSD Licence:
* Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ]
* Exocortex Technologies [ www.exocortex.org ]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
using System;
namespace Exocortex.DSP
{
// Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org
// Version: May 4, 2002
/// <summary>
/// <p>The direction of the fourier transform.</p>
/// </summary>
public enum FourierDirection : int
{
/// <summary>
/// Forward direction. Usually in reference to moving from temporal
/// representation to frequency representation
/// </summary>
Forward = 1,
/// <summary>
/// Backward direction. Usually in reference to moving from frequency
/// representation to temporal representation
/// </summary>
Backward = -1,
}
}

View File

@@ -0,0 +1,167 @@
/*
* BSD Licence:
* Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ]
* Exocortex Technologies [ www.exocortex.org ]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
using System;
using System.Diagnostics;
namespace Exocortex.DSP
{
// Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org
// Version: May 4, 2002
/// <summary>
/// <p>Various mathematical functions for complex numbers.</p>
/// </summary>
public class ComplexMath
{
//---------------------------------------------------------------------------------------------------
private ComplexMath()
{
}
//---------------------------------------------------------------------------------------------------
/// <summary>
/// Swap two complex numbers
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
static public void Swap(ref Complex a, ref Complex b)
{
Complex temp = a;
a = b;
b = temp;
}
/// <summary>
/// Swap two complex numbers
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
static public void Swap(ref ComplexF a, ref ComplexF b)
{
ComplexF temp = a;
a = b;
b = temp;
}
//---------------------------------------------------------------------------------------------------
static private double _halfOfRoot2 = 0.5 * Math.Sqrt(2);
/// <summary>
/// Calculate the square root of a complex number
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
static public ComplexF Sqrt(ComplexF c)
{
double x = c.Re;
double y = c.Im;
double modulus = Math.Sqrt(x * x + y * y);
int sign = (y < 0) ? -1 : 1;
c.Re = (float)(_halfOfRoot2 * Math.Sqrt(modulus + x));
c.Im = (float)(_halfOfRoot2 * sign * Math.Sqrt(modulus - x));
return c;
}
/// <summary>
/// Calculate the square root of a complex number
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
static public Complex Sqrt(Complex c)
{
double x = c.Re;
double y = c.Im;
double modulus = Math.Sqrt(x * x + y * y);
int sign = (y < 0) ? -1 : 1;
c.Re = _halfOfRoot2 * Math.Sqrt(modulus + x);
c.Im = _halfOfRoot2 * sign * Math.Sqrt(modulus - x);
return c;
}
//---------------------------------------------------------------------------------------------------
/// <summary>
/// Calculate the power of a complex number
/// </summary>
/// <param name="c"></param>
/// <param name="exponent"></param>
/// <returns></returns>
static public ComplexF Pow(ComplexF c, double exponent)
{
double x = c.Re;
double y = c.Im;
double modulus = Math.Pow(x * x + y * y, exponent * 0.5);
double argument = Math.Atan2(y, x) * exponent;
c.Re = (float)(modulus * System.Math.Cos(argument));
c.Im = (float)(modulus * System.Math.Sin(argument));
return c;
}
/// <summary>
/// Calculate the power of a complex number
/// </summary>
/// <param name="c"></param>
/// <param name="exponent"></param>
/// <returns></returns>
static public Complex Pow(Complex c, double exponent)
{
double x = c.Re;
double y = c.Im;
double modulus = Math.Pow(x * x + y * y, exponent * 0.5);
double argument = Math.Atan2(y, x) * exponent;
c.Re = modulus * System.Math.Cos(argument);
c.Im = modulus * System.Math.Sin(argument);
return c;
}
//---------------------------------------------------------------------------------------------------
}
}

Binary file not shown.

View File