Introduction

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.

FIR_block.jpg

Background

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.

Morse_Rx.jpg

Morse Received

Morse_noise.jpg

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.

BP_wave.jpg BS_wave.jpg

The derived coefficients and time delays for the two filters are as shown.

BP_Imp.jpg BS_Imp.jpg

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.

//
//    ' FIR filter coefficients
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 ' Filter five samples five times

For H As Integer = 0 To SAMPLES - 6 ' Apply FIR filter coefficients 
'to five samples at a time and add together
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))
                Next
                
// 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)

    Next

	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.

History

  • Version 1.0
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"