FIR stands for Finite Impulse Response.
In the analogue world, if you wished to filter a signal, you would use capacitors, resistors and inductors wired together to create the filter of your choice. In the digital world, you can still apply a filter to a sampled signal being received by a method of multiplying the samples by as series of coefficients and summing to create a new (filtered) sample stream.
Imagine a train of samples being received at 200msec intervals. Each sample is modified by adding or subtracting, depending on the power of the coefficient, corrected amounts of the other samples. A new filtered stream of samples is created.
I’ve written a program to decode Morse code by using the PC sound card hooked up to a radio transmitter / receiver. The issue is that these radios use a lot automatic gain control (AGC). While there is no received morse tone, the noise threshold comes up and can be miss decoded.
Noise threshold raised
To counter the effects of the AGC, I needed to filter both the morse tone (700Hz) and the out of band noise and use the latter to boost the gain of the former (i.e. lots of noise reduce gain, low noise boost the gain). The result is very good separation for the given frequency.
ScopeFir is an excellent program to use to design the filters. A 30 day trial can be downloaded from here.
Considerations to follow:
- Reducing the sample rate reduces the bandwidth of the filter. Nyquist theorem says the sample rate must be more than twice the highest frequency. I was only filtering 700Hz so my sample rate was set to 2000 samples / sec (1khz BW)
- I needed the minimum processing delay through the filter. Morse tone is measured in tens of milliseconds. The more coefficients you use, the sharper the filter but the longer the processing delay. I optimised at nine coefficients with a processing delay of 3.2msecs.
The resultant filter shapes look like this. The first passes 700hz with about 10db of noise suppression. The second filter notches out the 700Hz so passes the noise element.
The derived coefficients and time delays for the two filters are as shown.
Using the Code
Two arrays are filled with the coefficients for the bandpass and bandstop filters. Each sample is cascaded through the algorithm being multiplied by each coefficient and summed to create five new samples.
The stop filter will return the out of band noise while the band pass filter returns the desired signal, in this case 700hz. Summing the results of the filters creates effectively an inverse AGC.
Further detail of using Directx to communicate with the sound card can be found in my PPM meters project.
Dim CBP(9) As Double
CBP(0) = 0.1020135767780434
CBP(1) = -0.35600582739652703
CBP(2) = 0.41498447051618764
CBP(3) = -0.35600582739652703
CBP(4) = 0.1020135767780434
CBP(5) = 0.1020135767780434
CBP(6) = -0.35600582739652703
CBP(7) = 0.41498447051618764
CBP(8) = -0.35600582739652703
Dim CBS(9) As Double
CBS(0) = 0.1020135767780434
CBS(1) = -0.35600582739652703
CBS(2) = 0.41498447051618764
CBS(3) = -0.35600582739652703
CBS(4) = 0.1020135767780434
CBS(5) = 0.1020135767780434
CBS(6) = -0.35600582739652703
CBS(7) = 0.41498447051618764
CBS(8) = -0.35600582739652703
// Read the audio buffer
Dim samples__1 As Array = buffer.Read_
(0, GetType(Int16), LockFlag.FromWriteCursor, SAMPLE_FORMAT_ARRAY)
For J As Integer = 0 To 4
For H As Integer = 0 To SAMPLES - 6
templeftGoal(J) = templeftGoal(J) + _
(CType(samples__1.GetValue(H + J, 0, 0), Int16) * CBP(H))
templeftrange(J) = templeftrange(J) + _
(CType(samples__1.GetValue(H + J, 0, 0), Int16) * CBS(H))
temprightGoal(J) = temprightGoal(J) + _
(CType(samples__1.GetValue(H + J, 1, 0), Int16) * CBP(H))
temprightrange(J) = temprightrange(J) + _
(CType(samples__1.GetValue(H + J, 1, 0), Int16) * CBS(H))
// normalise all samples
If templeftGoal(J) < 0 Then templeftGoal(J) = 0 - templeftGoal(J)
leftGoal += templeftGoal(J)
If templeftrange(J) < 0 Then templeftrange(J) = 0 - templeftrange(J)
leftrange += templeftrange(J)
If temprightGoal(J) < 0 Then temprightGoal(J) = 0 - temprightGoal(J)
rightGoal += temprightGoal(J)
If temprightrange(J) < 0 Then temprightrange(J) = 0 - temprightrange(J)
rightrange += temprightrange(J)
leftGoal = CInt(Math.Abs(leftGoal \ (SAMPLES - 5)))
leftrange = CInt(Math.Abs(leftrange \ (SAMPLES - 5)))
rightGoal = CInt(Math.Abs(rightGoal \ (SAMPLES - 5)))
rightrange = CInt(Math.Abs(rightrange \ (SAMPLES - 5)))
Points of Interest
If you are interested in the Morse Code program, you will find it here. The program uses both the FIR filters and my PPM meters.