[This] is a very good book on digital filters, and it is available online for free. I doubt I can explain the filter operation better than the author of that book. However, I have implemented the Recursive Bandpass filter on an arduino, which might be useful to others.
Why? First of all, it is useful to remove background noise from an input signal (e.g. lights turning on, or the oscillation in mains power supplies) because it provides a better sense of the environment.
Second of all, using a filter provides a means to look for a specific frequency. Therefore, we can modulate (add a precise oscillation to) a transmission signal, and this has the effect of making it more detectable, compared to looking for a constant (flat) transmission.
This might seem strange at first, since a light that is always switched on appears brighter than a light flickered on and off. However, our brains are tremendously good at filtering for us, and we are gifted with high resolution sensors. Arduino's are not.
It is an issue of noise again. With a constant IR beam, any amplification would also amplify the background noise, and the signal would be obscured. By looking specifically for the modulation frequency, we can cut out the noise and amplify the signal. Therefore, a tiny far away signal can be extracted from lots of background noise.
This software filter is different to the 38Khz IR demodulation receiver chips, such as these:
The above devices also use a filter internally, but provide a digital output (1 or 0) to the arduino. As a sensor, they are best used to transmit data, such as an IR serial link between arduinos.
This software filter implementation provides an analogue response to a modulated IR signal, meaning it is possible to gauge the distance or strength of a transmission - good for detecting obstacles or beacons.
This filter implementation is also tuned to 38Khz, which is the common frequency of television remote controls, making it convenient to test this code.
You will need a simple passive Infra-red photodiode, they look like LED's. Plug the cathode into A0, and the anode into ground, and the code uses an internal pull up resistor in the arduino.
I conducted a quick search for Processing, to graph the output of the filter over the Serial port, and that is included at the end of this post. I only adjusted the processing code a little, so thanks goes to Tom Igoe. A screen shot of the simple graph and the response of the filter is below:
This implementation directly accesses the timer0 feature. It is necessary to read the analogue port at precise intervals. However, this means millis() and delay() will not work. If either of these are needed, it should not be too challenging to implement a counter on timer0 to create precise delays.
Do not be intimidated by the timer0 set up procedure. When code like (1 << CS00) is used, it simply means put a 1 in the position of the CS00 bit. This bit is a part of a register (a bit like switches), and configures the device.
Bitwise (logical) commands (like OR, AND) are used to write into a register. So TCCR01 |= (1 << CS00) is actually the same as TCCR01 = TCCR01 | (1 << CS00)
. Logical OR is necessary, because we want to set individual bits, but save the previous state of the register. If we used just '=', we would overwrite the register with just a single bit activated!
Which bits to set, and which registers to use, is all in the data sheet for the Atmega devices, which are free online, and there are normally example pseudo-code extracts as well.
The filter itself works on the following principle. To detect a specific frequency, we need to collect sensor readings at least twice as fast. The filter is based on some math based on frequency, which creates fixed numbers to process the input data. Therefore, it is very important that we accurately time the collection of data, or those fixed numbers based on frequency won't work.
So for a 38Khz signal, we need to read a sensor at at least 76Khz. In the code, I have used 100Khz as it is a nicer number to work with. The timer0 has to be set up to do this specifically, there are no ordinary arduino commands to do it.
The timer0 is used to automatically run some code, called an Interrupt Service Routine (ISR). The ISR reads the analogue pin A0. We want to collect lots of sensor readings over time to get a picture of the outside world, which we place in an array and call a buffer. The ISR collects 45 readings and then sets a flag.
Importantly: the ISR needs to be as short as possible, because otherwise the timer0 may try to call itself again before it has even finished, and this will crash the arduino! The ISR is therefore very fast code that runs automatically, it will happily interrupts any code in the normal loop() routine.
In the normal loop() of the program, nothing happens until the buffer flag is set by the ISR. This will mean the ISR has finished, and so the filter function is called. This crunches the numbers and returns a result. We send the result out on the Serial port. The process repeats. All of this is slow code, it is not important when or how long it takes, compared to the ISR and the filter. That is why it is in the loop().
I've prototyped this on a Duemilanove Atmega168 only, so it might throw up something interesting on another board/chip.