03-23-2017 01:39 PM
Hello,
No matter what I pass through to the Chebyshev low-pass filter, I am getting an attenuating sine wave back.
I have tried passing double values derived from an FFT based on the code below:
FFTFrequency = Index * ForcingFunctionSampleRate / ForcingFunctionBlockSize FFTMagnitude = ComplexDataArray(Index).Magnitude FFTGraphedMagnitude = 2 / ForcingFunctionBlockSize * FFTMagnitude
I then use the X and Y Values of "FFTFrequency" and FFTGraphedMagnitude and pass those into the LPF function. I use the FFTGraphedMagnitude values as the input for the LPF function.
I have also attempted to completely skip this code, and pass in the ComplexDouble data from the FFT directly to the LPF function. It, again, gives me an attenuating sine wave for the .Real values.
What am I doing wrong?
Sample Rate is 1623.377 Hz, block size is 8192. Passband ripple is 1 db. Filter order is 10.
I am trying to LPF (cutoff frequency) at 140 Hz.
Here is my original function, a combination of multiple sine waves at frequencies of 180.3752, 162.3377, 147.5797, 124.8751, 108.2251, 90.18759, 81.16883, 70.58159, 60.12506, 50.73052, 40.58442, 30.06253, 20.04169, and 10.02084 Hz.
Here's the FFT graph based off of the code that gives me the X and Y values I pasted above:
And here is where it always goes sideways, regardless of the input data into the LPF function:
I am completely baffled at what to do next, or what to test. Any and all help is greatly appreciated. I am using .NET 4.0 inside of Visual Studio 2013. I have the Professional edition.
Thank you,
John Lindsay
03-24-2017 03:38 PM
Hi John,
Could you post a complete small reproducing case of your code? It will help me understand better.
thanks,
Pat
03-24-2017 03:52 PM
Here's a basic set of what I'm trying to do:
Imports System.Math
Imports NationalInstruments
Imports NationalInstruments.Analysis.Dsp.Transforms
Public Class Frequency
Const ForcingFunctionBlockSize As Integer = 8192
Const Timestep As Single = 0.000616
Const ForcingFunctionSampleRate As Double = 1623.377
Friend Function GenerateSineWave() As Double()
Dim PulseAdded As Boolean = False
Dim NumberOfPulsedTimeSteps As Integer = 2
Dim ReturnedDataSet(ForcingFunctionBlockSize - 1) As Double
Dim SineIncrement(14) As Double
For BlockStep As Integer = 0 To ForcingFunctionBlockSize
Select Case BlockStep
Case 9 '180.3752
SineIncrement(0) = 2 * PI / BlockStep
Case 10 '162.3377
SineIncrement(1) = 2 * PI / BlockStep
Case 11 '147.5797
SineIncrement(2) = 2 * PI / BlockStep
Case 13 '124.8751
SineIncrement(3) = 2 * PI / BlockStep
Case 15 '108.2251
SineIncrement(4) = 2 * PI / BlockStep
Case 18 '90.18759
SineIncrement(5) = 2 * PI / BlockStep
Case 20 '81.16883
SineIncrement(6) = 2 * PI / BlockStep
Case 23 '70.58159
SineIncrement(7) = 2 * PI / BlockStep
Case 27 '60.12506
SineIncrement(8) = 2 * PI / BlockStep
Case 32 '50.73052
SineIncrement(9) = 2 * PI / BlockStep
Case 40 '40.58442
SineIncrement(10) = 2 * PI / BlockStep
Case 54 '30.06253
SineIncrement(11) = 2 * PI / BlockStep
Case 81 '20.04169
SineIncrement(12) = 2 * PI / BlockStep
Case 162 '10.02084
SineIncrement(13) = 2 * PI / BlockStep
Exit For
End Select
Next
For CurrentTimeStep As Integer = 0 To ForcingFunctionBlockSize - 1
Dim CurrentTime As Single = CurrentTimeStep * Timestep
Dim YValue As Double
For Each Value As Double In SineIncrement
YValue += Sin(Value * CurrentTimeStep)
Next
ReturnedDataSet(CurrentTimeStep) = YValue
Next
Return ReturnedDataSet
End Function
Friend Function FFTPulse(ByVal RawDataSetIn() As Double) As Double()
Dim ReturnedDataSet(RawDataSetIn.Length) As Double
Dim FFTFrequency As Double
Dim FFTMagnitude As Double
Dim FFTGraphedMagnitude As Double
Dim ComplexDataArray(RawDataSetIn.Length - 1) As ComplexDouble
Dim ComplexPartAsZero(RawDataSetIn.Length - 1) As Double
ComplexDataArray = ComplexDouble.ComposeArray(RawDataSetIn, ComplexPartAsZero)
Fft(ComplexDataArray)
For Index As Integer = 0 To ComplexDataArray.Length - 1
FFTFrequency = Index * ForcingFunctionSampleRate / ForcingFunctionBlockSize
FFTMagnitude = ComplexDataArray(Index).Magnitude
FFTGraphedMagnitude = 2 / ForcingFunctionBlockSize * FFTMagnitude
ReturnedDataSet(Index) = FFTGraphedMagnitude
Next
LPFComplex(ComplexDataArray)
Return ReturnedDataSet
End Function
Friend Function LPF(ByRef FFTMagnitudes() As Double) As Double()
Dim FilterOrder As Integer = 10
Dim CutoffFrequency As Double = 140
Dim FilterRipple As Double = 1
Dim NewFilter As New NationalInstruments.Analysis.Dsp.Filters.ChebyshevLowpassFilter(FilterOrder, ForcingFunctionSampleRate, CutoffFrequency, FilterRipple)
Dim FFTMagnitudesArray(FFTMagnitudes.Length - 1) As Double
Dim LPFReturn() As Double
Dim ReturnedRawDataSet(FFTMagnitudes.Length - 1) As Double
FFTMagnitudesArray = FFTMagnitudes
LPFReturn = NewFilter.FilterData(FFTMagnitudesArray)
For Counter As Integer = 0 To ForcingFunctionBlockSize - 1
Dim Time As Double = Counter * Timestep
Dim LPFYValue As Double
LPFYValue = LPFReturn(Counter)
ReturnedRawDataSet(Counter) = LPFYValue
Next
Return ReturnedRawDataSet
End Function
Friend Function LPFComplex(ByRef FFTComplexData() As ComplexDouble)
Dim FilterOrder As Integer = 10
Dim ForcingFunctionSampleRate As Double = 1623.377
Dim CutoffFrequency As Double = 140
Dim FilterRipple As Double = 1
Dim NewFilter As New NationalInstruments.Analysis.Dsp.Filters.ChebyshevLowpassFilterComplex(FilterOrder, ForcingFunctionSampleRate, CutoffFrequency, FilterRipple)
Dim FFTMagnitudesArray(FFTComplexData.Length) As Double
Dim LPFReturn() As ComplexDouble
Dim ReturnedDataSet(ForcingFunctionBlockSize - 1) As Double
LPFReturn = NewFilter.FilterData(FFTComplexData)
For Counter As Integer = 0 To (ForcingFunctionBlockSize - 1)
Dim Time As Double = Counter * Timestep
Dim LPFYValue As Double
LPFYValue = LPFReturn(Counter).Real
ReturnedDataSet(Counter) = LPFYValue
Next
Return ReturnedDataSet
End Function
End Class
I pass the previous function's return into the next function to continue the process along.
Note I have had to convert this code to more basic code - we are using classes to create "holding" variables that hold the X and Y values of the outputs so we can graph as you see above. This code is specific to our application and therefore wouldn't be of value if I included it, on top of being really hard to include since the supporting classes would be needed as well. I suppose I could have used a tuple or a dictionary, but I wanted to make it as easy as possible to read.