Skip to content

Commit

Permalink
Merge pull request #10 from hybridmachine/FFT_Filters
Browse files Browse the repository at this point in the history
Fft filters
  • Loading branch information
hybridmachine authored Jan 5, 2020
2 parents faca338 + ac399f4 commit c583398
Show file tree
Hide file tree
Showing 21 changed files with 776 additions and 182 deletions.
30 changes: 0 additions & 30 deletions SignalProcessor/Filters/HighPassWindowedSync.cs

This file was deleted.

55 changes: 0 additions & 55 deletions SignalProcessor/Filters/LowPassWindowedSync.cs

This file was deleted.

81 changes: 81 additions & 0 deletions SignalProcessor/Filters/WindowedSyncFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace SignalProcessor.Filters
{
public enum FilterType
{
LOWPASS,
HIGHPASS
}

public class WindowedSyncFilter : IWindowedSyncFilter
{
/// <summary>
/// Convenience parameter so users can name their filters for sorting, searching, etc
/// </summary>
public string Name { get; set; }
public double CutoffFrequencySamplingFrequencyPercentage { get ; set ; }
public int FilterLength { get ; set ; }
public FilterType FilterType { get; set; }

public WindowedSyncFilter()
{
FilterType = FilterType.LOWPASS; // Lowpass by default
}

/// <summary>
/// Implementation of the algorithm from P 290 Equation 16-4 of ISBN 0-9660176-3-3 "The Scientist and Engineer's Guide to Digital Signal Processing"
/// https://www.dspguide.com/CH16.PDF#page=5&zoom=auto,-310,23
/// Note this filter kernel assumes samples are normalized by the caller
/// </summary>
/// <returns></returns>
public virtual List<double> ImpulseResponse(bool normalize = true)
{
List<double> impulseResponse;
impulseResponse = new List<double>(FilterLength + 1);
double normalizationFactor = 0.0;
for (int idx = 0; idx <= FilterLength; idx++)
{
if (idx == (FilterLength / 2))
{
impulseResponse.Add(2 * Math.PI * (CutoffFrequencySamplingFrequencyPercentage / 100.0));
}
else
{
double sincNumerator = (Math.Sin(2 * Math.PI * (CutoffFrequencySamplingFrequencyPercentage / 100.0) * (idx - (FilterLength / 2))));
double sincDenominator = (idx - (FilterLength / 2));
double blackmanWindow = (0.42 - (0.5 * Math.Cos((2 * Math.PI * idx)/FilterLength)) + (0.08 * Math.Cos((4 * Math.PI * idx)/FilterLength)));
double value = (sincNumerator / sincDenominator) * blackmanWindow;
impulseResponse.Add(value);
}

normalizationFactor += impulseResponse[impulseResponse.Count - 1];
}

if (normalize)
{
// Normalize for unity gain at DC
for (int idx = 0; idx < impulseResponse.Count; idx++)
{
impulseResponse[idx] = impulseResponse[idx] / normalizationFactor;
}
}

// Spectral inversion
if (FilterType == FilterType.HIGHPASS)
{
// Spectral Inversion of the low pass filter
for (int idx = 0; idx < impulseResponse.Count; idx++)
{
impulseResponse[idx] = impulseResponse[idx] * -1;
}

impulseResponse[(impulseResponse.Count - 1) / 2] += 1;
}

return impulseResponse;
}
}
}
2 changes: 1 addition & 1 deletion SignalProcessor/IWindowedSyncFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ public interface IWindowedSyncFilter
double CutoffFrequencySamplingFrequencyPercentage { get; set; }
int FilterLength { get; set; }

List<double> ImpulseResponse();
List<double> ImpulseResponse(bool normalize = true);
}
}
14 changes: 6 additions & 8 deletions SignalProcessor/SignalProcessor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyVersion>0.1.8.0</AssemblyVersion>
<FileVersion>0.1.8.0</FileVersion>
<PackageReleaseNotes>Added low and high pass windowed sync filters based on Chapter 16 of the DSP guide: https://www.dspguide.com/CH16.PDF#page=1&amp;zoom=auto,-119,708

See SignalProcessor.Filters.LowPassWindowedSync and SignalProcessor.Filters.HighPassWindowedSync</PackageReleaseNotes>
<Copyright>2019 Brian Tabone</Copyright>
<AssemblyVersion>0.1.14.0</AssemblyVersion>
<FileVersion>0.1.14.0</FileVersion>
<PackageReleaseNotes>Add option to turn on / off normalization (default to on) for WindowedSyncFilter</PackageReleaseNotes>
<Copyright>2020 Brian Tabone</Copyright>
<Description>Digital Signal Processing (FFT and Correlation)</Description>
<Version>0.1.8</Version>
<Version>0.1.14</Version>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>SignalProcessor.pfx</AssemblyOriginatorKeyFile>
Expand All @@ -19,7 +17,7 @@ See SignalProcessor.Filters.LowPassWindowedSync and SignalProcessor.Filters.High
<RepositoryUrl>https://github.com/hybridmachine/DSP</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/hybridmachine/DSP/projects/1</PackageProjectUrl>
<PackageTags>signal processing fft analysis</PackageTags>
<PackageTags>signal processing fft analysis windowedsync</PackageTags>
</PropertyGroup>

<ItemGroup>
Expand Down
26 changes: 25 additions & 1 deletion Signals And Transforms/DAL/DatabaseInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static void Initialize(SqliteConnection con)
InitializeSignalsTable(con);
InitializeSignaTypesTable(con);
InitializeSignalValuesTable(con);
InitializeFiltersTable(con);
transaction.Commit();
} catch (Exception ex)
{
Expand Down Expand Up @@ -126,7 +127,7 @@ private static bool InitializeSignalValuesTable(SqliteConnection con)
'Id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
'SignalID' INTEGER,
'Value' REAL,
CONSTRAINT fk_signalid
CONSTRAINT fk_signalvalues_to_signal
FOREIGN KEY (SignalID)
REFERENCES Signals (Id)
ON DELETE CASCADE
Expand All @@ -137,5 +138,28 @@ ON DELETE CASCADE
cmd.ExecuteNonQuery();
return true;
}

private static bool InitializeFiltersTable(SqliteConnection con)
{
string sql = $@"
CREATE TABLE 'Filters' (
'Id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
'WorkBookId' INTEGER,
'Name' TEXT,
'IsActive' INTEGER NOT NULL CHECK (IsActive IN (0,1)),
'CutoffFrequencySamplingFrequencyPercentage' REAL NOT NULL,
'FilterLength' INTEGER NOT NULL,
'FilterType' TEXT NOT NULL,
CONSTRAINT fk_filters_to_workbook
FOREIGN KEY (WorkBookId)
REFERENCES WorkBook (Id)
ON DELETE CASCADE
)";

SqliteCommand cmd = con.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
return true;
}
}
}
42 changes: 42 additions & 0 deletions Signals And Transforms/DAL/FilterDAL.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Microsoft.Data.Sqlite;
using SignalProcessor.Filters;
using SignalsAndTransforms.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalsAndTransforms.DAL
{
public static class FilterDAL
{
public static bool Create(WorkBook workBook, Filter filter, SqliteConnection con)
{
// For now we just clear and re-create signal entries, we may update values in place later
// keep it simple for now
string deleteSQL = $@"DELETE FROM Filters WHERE [Name]=@Name"; // This will cascade to the values table
SqliteCommand cmd = con.CreateCommand();
cmd.CommandText = deleteSQL;
cmd.Parameters.AddWithValue("@Name", filter.Name);
cmd.ExecuteNonQuery();

string sql = $@"INSERT INTO Filters ([Name], [IsActive], [WorkBookId], [CutoffFrequencySamplingFrequencyPercentage], [FilterLength], [FilterType])
VALUES (@Name, @IsActive, @WorkBookId, @CutoffFrequencySamplingFrequencyPercentage, @FilterLength, @FilterType)";



cmd = con.CreateCommand();
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("@Name", filter.Name);
cmd.Parameters.AddWithValue("@IsActive", filter.IsActive);
cmd.Parameters.AddWithValue("@FilterType", filter.FilterType.ToString());
cmd.Parameters.AddWithValue("@FilterLength", filter.FilterLength);
cmd.Parameters.AddWithValue("@CutoffFrequencySamplingFrequencyPercentage", filter.CutoffFrequencySamplingFrequencyPercentage);
cmd.Parameters.AddWithValue("@WorkBookId", workBook.Id);
cmd.ExecuteNonQuery();

return true;
}
}
}
15 changes: 14 additions & 1 deletion Signals And Transforms/DAL/WorkBookDAL.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Dapper;
using Microsoft.Data.Sqlite;
using SignalProcessor.Filters;
using SignalsAndTransforms.Models;
using System;
using System.Collections.Generic;
Expand All @@ -18,7 +19,7 @@ class SignalValue
}
public static class WorkBookDAL
{
public const string SchemaVersion = "1.1";
public const string SchemaVersion = "1.2";

/// <summary>
/// Create the Workbook file, note this will delete any previously existing file if it exists
Expand Down Expand Up @@ -113,11 +114,18 @@ public static bool Update(WorkBook workBook)
cmd.Parameters.AddWithValue("@Id", workBook.Id);
cmd.ExecuteNonQuery();

// Save signals
foreach (Signal signal in workBook.Signals.Values)
{
SignalDAL.Create(workBook, signal, sqlLiteConnection);
}

// Save filters
foreach (Filter filter in workBook.Filters.Values)
{
FilterDAL.Create(workBook, filter, sqlLiteConnection);
}

transaction.Commit();
} catch (Exception ex)
{
Expand All @@ -144,6 +152,7 @@ public static WorkBook Load(string fromPath)
newWorkBook = connection.Query<WorkBook>($@"SELECT [Id], [Name], [Notes] FROM WorkBook").FirstOrDefault();

var signals = connection.Query<Signal>($"SELECT * from Signals WHERE WorkBookId = '{newWorkBook.Id}'");
var filters = connection.Query<Filter>($"SELECT * from Filters WHERE WorkBookId = '{newWorkBook.Id}'");

foreach (Signal signal in signals)
{
Expand All @@ -158,6 +167,10 @@ public static WorkBook Load(string fromPath)
newWorkBook.Signals.Add(signal.Name, signal);
}

foreach (Filter filter in filters)
{
newWorkBook.Filters.Add(filter.Name, filter);
}
}

return newWorkBook;
Expand Down
Loading

0 comments on commit c583398

Please sign in to comment.