FIR FIlter
This commit is contained in:
10
OxyPlotExample/GettingStarted/Documentation/FIRFilter.md
Normal file
10
OxyPlotExample/GettingStarted/Documentation/FIRFilter.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# FIR Low Pass Filter 개요
|
||||
|
||||
- 샘플링 주파수 ($f_s$: Sampling Frequency)
|
||||
- 신호를 샘플링 하는 속도
|
||||
- 컷오프 주파수 ($f_c$: Cutoff Frequency)
|
||||
- 필터의 신호를 통과시키거나 차단하기 시작하는 주파수
|
||||
- 필터 계수 설계
|
||||
- 필터의 특성을 정의하는 계수. 해밍 창(Hamming Window)등을 이용해 설계
|
||||
- 페이즈 지연 보상
|
||||
- 필터로 인한 지연 보상
|
88
OxyPlotExample/GettingStarted/Filter/FIRFilter.cs
Normal file
88
OxyPlotExample/GettingStarted/Filter/FIRFilter.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GettingStarted.Filter
|
||||
{
|
||||
internal class FIRFilter
|
||||
{
|
||||
private double[] _coefficients;
|
||||
private double[] _buffer;
|
||||
private int _bufferIndex;
|
||||
private int _delay;
|
||||
|
||||
public FIRFilter(double[] coefficients)
|
||||
{
|
||||
_coefficients = coefficients;
|
||||
_buffer = new double[coefficients.Length];
|
||||
_bufferIndex = 0;
|
||||
_delay = (_coefficients.Length - 1) / 2;
|
||||
}
|
||||
|
||||
public double Process(double input)
|
||||
{
|
||||
_buffer[_bufferIndex] = input;
|
||||
double result = 0d;
|
||||
|
||||
int index = _bufferIndex;
|
||||
for (int i = 0; i < _coefficients.Length; i++)
|
||||
{
|
||||
result += _coefficients[i] * _buffer[index];
|
||||
index = (index > 0) ? index - 1 : _buffer.Length - 1;
|
||||
}
|
||||
|
||||
_bufferIndex = (_bufferIndex + 1) % _buffer.Length;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public double[] ProcessSignal(double[] inputSignal)
|
||||
{
|
||||
int signalLength = inputSignal.Length;
|
||||
double[] outputSignal = new double[signalLength];
|
||||
|
||||
for (int i = 0; i < signalLength; i++)
|
||||
{
|
||||
outputSignal[i] = Process(inputSignal[i]);
|
||||
}
|
||||
|
||||
// 페이즈 지연 보상
|
||||
for (int i = 0; i < signalLength; i++)
|
||||
{
|
||||
if (i + _delay < signalLength)
|
||||
{
|
||||
outputSignal[i] = outputSignal[i + _delay];
|
||||
}
|
||||
else
|
||||
{
|
||||
outputSignal[i] = 0; // 지연 보상 후 남은 부분은 0으로 채움
|
||||
}
|
||||
}
|
||||
|
||||
return outputSignal;
|
||||
}
|
||||
|
||||
public static double[] DesignLowPassFilter(int length, double cutoffFrequnecy, double sampleRate)
|
||||
{
|
||||
double[] coefficients = new double[length];
|
||||
double omega_c = 2 * Math.PI * cutoffFrequnecy / sampleRate;
|
||||
int M = length - 1;
|
||||
|
||||
for (int n = 0; n <= M; n++)
|
||||
{
|
||||
if (n - M / 2 == 0)
|
||||
{
|
||||
coefficients[n] = omega_c / Math.PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
coefficients[n] = Math.Sin(omega_c * (n - M / 2)) / (Math.PI * (n - M / 2)) * (0.54 - 0.46 * Math.Cos(2 * Math.PI * n / M));
|
||||
}
|
||||
}
|
||||
|
||||
return coefficients;
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,8 +14,4 @@
|
||||
<PackageReference Include="OxyPlot.Wpf" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Documentation\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -8,17 +8,23 @@
|
||||
Title="MainWindow"
|
||||
Width="800"
|
||||
Height="450"
|
||||
mc:Ignorable="d"
|
||||
Closing="Window_Closing">
|
||||
Closing="Window_Closing"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="3*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<views:MainViewControl Grid.Row="0"/>
|
||||
<views:MainViewControl2 Grid.Row="1"/>
|
||||
<views:MainViewControl3 Grid.Row="2" x:Name="vcRT"/>
|
||||
<views:MainViewControl Grid.Row="0" />
|
||||
<views:MainViewControl2 Grid.Row="1" />
|
||||
<views:MainViewControl3 x:Name="vcRT" Grid.Row="2" />
|
||||
|
||||
<views:FIRFilterView Grid.Column="1" Grid.RowSpan="3" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
@@ -0,0 +1,98 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using GettingStarted.Filter;
|
||||
using OxyPlot;
|
||||
using OxyPlot.Series;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GettingStarted.ViewModels
|
||||
{
|
||||
internal partial class FIRFilterViewModel : ObservableObject
|
||||
{
|
||||
|
||||
[ObservableProperty]
|
||||
private PlotModel _myModel;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private LineSeries _inputSeries;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private LineSeries _outputSeries;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _sampleRate;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private double _cutoffFrequency;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private int _filterLength;
|
||||
|
||||
double[] _coefficients;
|
||||
|
||||
FIRFilter _filter;
|
||||
|
||||
double[] _inputSignal;
|
||||
|
||||
double _noiseLevel = 0.3;
|
||||
public FIRFilterViewModel()
|
||||
{
|
||||
MyModel = new PlotModel() { Title = "FIR Low Pass Filter" };
|
||||
InputSeries = new LineSeries() { Title = "Input" };
|
||||
OutputSeries = new LineSeries() { Title = "Output" };
|
||||
MyModel.Series.Add(InputSeries);
|
||||
MyModel.Series.Add(OutputSeries);
|
||||
|
||||
SampleRate = 1000d;
|
||||
CutoffFrequency = 100d;
|
||||
FilterLength = 21;
|
||||
|
||||
Random rand = new Random();
|
||||
List<double> inputSimulate = new List<double>();
|
||||
int x = 0;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
double noise = (rand.NextDouble() - 0.5) * _noiseLevel;
|
||||
double value = Math.Sin(x++ * 0.1) + noise;
|
||||
inputSimulate.Add(value);
|
||||
}
|
||||
|
||||
_inputSignal = inputSimulate.ToArray(); ;
|
||||
|
||||
ProcessSignal();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ProcessSignal()
|
||||
{
|
||||
_coefficients = FIRFilter.DesignLowPassFilter(FilterLength, CutoffFrequency, SampleRate);
|
||||
_filter = new FIRFilter(_coefficients);
|
||||
|
||||
double[] outputSignal = _filter.ProcessSignal(_inputSignal);
|
||||
|
||||
{
|
||||
InputSeries.Points.Clear();
|
||||
int x = 0;
|
||||
DataPoint[] dataPoints = _inputSignal.Select(val => new DataPoint(++x, val)).ToArray();
|
||||
InputSeries.Points.AddRange(dataPoints);
|
||||
}
|
||||
|
||||
{
|
||||
OutputSeries.Points.Clear();
|
||||
int x = 0;
|
||||
DataPoint[] dataPoints = outputSignal.Select(val => new DataPoint(++x, val)).ToArray();
|
||||
OutputSeries.Points.AddRange(dataPoints);
|
||||
}
|
||||
|
||||
MyModel.InvalidatePlot(true);
|
||||
}
|
||||
}
|
||||
}
|
39
OxyPlotExample/GettingStarted/Views/FIRFilterView.xaml
Normal file
39
OxyPlotExample/GettingStarted/Views/FIRFilterView.xaml
Normal file
@@ -0,0 +1,39 @@
|
||||
<UserControl x:Class="GettingStarted.Views.FIRFilterView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:GettingStarted.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:oxy="http://oxyplot.org/wpf"
|
||||
xmlns:viewModels="clr-namespace:GettingStarted.ViewModels"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.DataContext>
|
||||
<viewModels:FIRFilterViewModel />
|
||||
</UserControl.DataContext>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="250" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<oxy:PlotView Model="{Binding MyModel}" />
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Width="150" Content="SampleRate" />
|
||||
<TextBox Width="80" Text="{Binding SampleRate}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Width="150" Content="CutoffFrequency" />
|
||||
<TextBox Width="80" Text="{Binding CutoffFrequency}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Width="150" Content="FilterLength" />
|
||||
<TextBox Width="80" Text="{Binding FilterLength}" />
|
||||
</StackPanel>
|
||||
<Button Command="{Binding ProcessSignalCommand}" Content="Simulate" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
28
OxyPlotExample/GettingStarted/Views/FIRFilterView.xaml.cs
Normal file
28
OxyPlotExample/GettingStarted/Views/FIRFilterView.xaml.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace GettingStarted.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// FIRFilterView.xaml에 대한 상호 작용 논리
|
||||
/// </summary>
|
||||
public partial class FIRFilterView : UserControl
|
||||
{
|
||||
public FIRFilterView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user