Posted 07 June 2017
I seem to have grabbed a tiger by the tail in my continued collaboration with my old friend and mentor John Jenkins on this project to extract the estimated magnitude of a square-wave modulated IR signal in the presence of ‘flooding’ from ambient light sources. The whole thing started out innocently enough when John was at our house a few weeks ago and I showed him my spiffy wall-following autonomous robot Wall-E2. I mentioned at the time that I was having some trouble getting Wall-E2 to successfully home in on an IR beam to mate with a charging station, and he suggested that a square-wave modulated signal might do the trick, as it would allow me (and Wall-E2) to discriminate between the IR beam and the other interfering signals.
I should have known right then that I was in trouble, as John had that look in his eyes – the one that says “Hmm, that’s an interesting problem…..”. I have seen that look any number of times over the 40 years or so that I have known him, and it always results in me tearing up my previous work (and my previous assumptions) and starting over again. The only saving graces in all this are a) It’s a lot of fun when it happens, b) I’m retired now so I don’t care how long it takes or how much work is involved, and c) I’m a masochist at heart! ;-).
So, here we are. John and I have been exchanging emails over the last week or so, discussing the ‘best’ way to solve this problem. One of the first things that happened was John started blabbering about ‘Degenerate N-path Bandpass Filters’, and I had no idea what he was talking about (I hate it when that happens!). Of course I couldn’t tell John that I was completely ignorant, so I made the appropriate noises and raced to educate myself before he discovered my ignorance. I found a neat video that explained the technique in a way that even I could understand. The video focused on implementing the filter technique in CMOS hardware, but the general technique is applicable to the Arduino world as well, as long as the modulation frequency is low enough to accommodate the lower clock speeds.
John came up with a brilliant graphical illustration of the algorithm used for implementing a N-path band-pass filter. Even more amazing, he did it in Excel, so the whole damned thing is live – wow! As shown in the ‘dead’ version below, there are two ‘channels’ – an in-phase (I) and quadrature (Q) ‘channel’. Both channels start with a group of samples spanning exactly 1/4 cycle that are summed together to form a ‘sample group’. In the I channel, each group is given a sign in the sequence ‘+’, ‘+’, ‘-‘, ‘-‘, and in the Q channel this same group is given a sign in the sequence ‘-‘, ‘+’, ‘+’, ‘-‘. The four groups in each channel are summed to form the I & Q ‘cycle sums’, and each such sum is added to a N-element circular buffer (one each for the I & Q channels). The running sum of all elements in each circular buffer are the band-pass filtered I & Q components of the input signal. The final result is formed by adding the absolute values of the I & Q running sums.
Page 1 of the above document shows the sign sequence for the in-phase and quadrature ‘channels’. Note that the input to both channels is the same ‘sum-of-5-samples’ group, but the sign changes in a different sequence for the I & Q ‘channels’. Each 1/4 cycle of the input signal is treated in the same fashion. The horizontal time scale is specific for the planned 500Hz modulation signal.
Page 2 depicts the real heart of the algorithm, as it shows how each ‘sample sum’ (1/4-cycle sum-of-samples) feeds into the final circular summing buffer. Four ‘sample sum’ groups (comprising one signal cycle) are summed into a ‘cycle sum’ for both the I & Q channels, and each such ‘cycle sum’ is loaded into the corresponding I or Q circular summing buffer. The latest ‘cycle sum’ element replaces the oldest element, and therefore the circular summing buffer for each channel represents a N-element band-pass filter, where N is the length of the circular summing buffer. This diagram has a lot of information in it, so it can take some time to get comfortable with it (it did for me, anyway).
Some other random details:
- This diagram is set up for a 64-element circular summing buffer, but shorter or longer is OK too. The band-pass filter bandwidth is inversely proportional to the buffer length.
- The times noted in the labels at the top are for a presumed 500Hz modulation, with a period of 2000uSec.
- The notation m = n/5 comes from the fact that (in this particular implementation) there are 20 samples/cycle, which means that each 1/4 cycle group of contains 5 samples. For 64 elements, each comprising 1 entire cycle of samples, there are 64X20 = 1280 samples. So in this implementation, m/4 = 64 ==> m = 256 = n/5 ==> n = 1280 total samples represented in the circular buffer at any one time.
Page 3 shows the contents of one of the two circular buffers, after each complete ‘rotation’ of the buffer. After 64 complete cycles the buffer is full, as shown in the ’00’ column. 64 cycles later, the buffer contents are as shown in the ’01’ column, and so on. The text below shows the procedure for start and running the buffer. Note in this text that “next cycle value” and “current cycle value” are the same thing, and the “input pointer” and “output pointer” variables are incremented MOD N, (N = 64 here).
Pages 4-12 show the input signal, the I/Q channel values, and the I & Q circular buffer running sums for different phase offsets between the transmitted and received signals (transmit & receive frequencies are the same in all cases). Page 4, for instance, shows the situation for the receiver perfectly in phase with the transmitted signal. If we look at the I & Q summed values at integral cycle boundaries (-1, 0, +1, etc) we see that the I signal is at 128, and the Q signal is at 0, giving abs(I) + abs(Q) = 128 + 0 = 128. If the same calculation is performed for all the other phase relationships (i.e. pages 5-12) at the same points, the answer will always be the same, i.e. 128. This shows that the band-pass filter implementation works as intended, even without any phase-locking requirement. This treatment assumes that the Tx & Rx clocks are identical, so that the 1/4-cycle sample groups span exactly 1/4 cycle. Any difference between the two clocks will show up as ripple on the results, proportional to the difference between the two frequencies. This error term should not be significant for the typical single-board-computer crystal-controlled clocks.
I sent this post off to John for approval, and got the following email back, with some additional clarifying comments:
- n-path filter is degenerate (my term) because it only uses two paths — I and Q — vice many as shown in video (very good video btw,learn a lot from it too)
- possible topics for more detail related to last paragraph (but beware that perfect is enemy of good enough or something like that):
- output ripple
- frequency ==> difference between tx and rx freqs should be very small
- only spec found was +/-50ppm, so 100ppm delta worst-case w/o aging, etc
- 100ppm at 500Hz ==> 0.05Hz
- believe ripple will be twice difference in freq because of this being a fullwave synchronous rectifier (not verified), so 0.1Hz
- magnitude ==> variations due to sample(s) from one 1/4-cycle group getting into an adjacent 1/4-cycle group as tx and rx phases slide past each other at <<0.1cycle/sec
- non-50/50 duty cycle is most likely cause
- with 5-samples per 1/4-cycle group normalized ripple could vary from 1.0 to 0.8;
- not a problem for the relative outputs of two sensors for steering
- could be important in cases where absolute magnitude is needed because bpf (eg, 10Hz) would not average signal for long enough to average out ripple this slow ((eg, 1 cycle every 10sec)
- increasing # of samples per 1/4-cycle group will reduce this effect
- frequency ==> difference between tx and rx freqs should be very small
- no antialias filter is used so # of samples per 1/4-cycle group needs to be significantly higher than nyquist requirement for 2/bw to avoid issues (unless clock rate is servo’d to correct freq by monitoring and setting Q value to zero)
- output ripple
More to come – stay tuned!
Frank