main
syneffort 11 months ago
parent 493a1a61d8
commit ea656552e0
  1. 10
      OxyPlotExample/GettingStarted/Documentation/FIRFilter.md
  2. 88
      OxyPlotExample/GettingStarted/Filter/FIRFilter.cs
  3. 4
      OxyPlotExample/GettingStarted/GettingStarted.csproj
  4. 12
      OxyPlotExample/GettingStarted/MainWindow.xaml
  5. 98
      OxyPlotExample/GettingStarted/ViewModels/FIRFilterViewModel.cs
  6. 39
      OxyPlotExample/GettingStarted/Views/FIRFilterView.xaml
  7. 28
      OxyPlotExample/GettingStarted/Views/FIRFilterView.xaml.cs

@ -0,0 +1,10 @@
# FIR Low Pass Filter 개요
- 샘플링 주파수 ($f_s$: Sampling Frequency)
- 신호를 샘플링 하는 속도
- 컷오프 주파수 ($f_c$: Cutoff Frequency)
- 필터의 신호를 통과시키거나 차단하기 시작하는 주파수
- 필터 계수 설계
- 필터의 특성을 정의하는 계수. 해밍 창(Hamming Window)등을 이용해 설계
- 페이즈 지연 보상
- 필터로 인한 지연 보상

@ -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,9 +8,13 @@
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="*" />
@ -19,6 +23,8 @@
<views:MainViewControl Grid.Row="0" />
<views:MainViewControl2 Grid.Row="1" />
<views:MainViewControl3 Grid.Row="2" x:Name="vcRT"/>
<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);
}
}
}

@ -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>

@ -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();
}
}
}
Loading…
Cancel
Save