IR Modulation Processing Algorithm Development – Part XVI

After getting the good news about the performance of my N-path filter for homing in on a 520Hz square-wave modulated IR beam, I still had two more significant tasks to accomplish; I still needed to extend the work to multiple (at least two) sensor channels for homing operation, and I needed to fully characterize the filter for frequency and amplitude response, and verify proper operation in the presence of ambient interference.

Second sensor channel:

As it turned out, add a second channel to the demodulator/filter algorithm was a piece of cake, as I didn’t have to bother with ‘array-izing’ everything – I just added a second set of input & output pins for the second channel, and a second set of variables. Basically it involved doing a global replace of ‘variable_name’ with ‘variable_name1’, and then copying ‘variable_name1’ to ‘variable_name2′ wherever it occurred. As an added bonus, the Teensy 3.5 has two DAC channels, so I could route the final value for channel2 to the second DAC pin for ’round-trip’ measurements with the sweep generator – nice

Frequency/Amplitude/Interference characterization:

In wrapping my long and arduous journey toward a working 520Hz multi-sensor square-wave detector filter, I wanted to make a series of amplitude and frequency response plots for both banpass filter channels, and also figure out how to test for rejection of 60Hz & 60Hz harmonics.

My initial amplitude sweep testing for channel 1 (original single channel) was very positive, as shown in the following plots:

However, when I tried to run the frequency response curves, I ran into trouble (again!). As shown above, the plot was shifted significantly to the right, as if the demodulator’s frequency response had somehow shifted up in frequency. However, I knew from previous work that the demodulator center freq is 520.8Hz, so that couldn’t be the answer. The other possibility was that the sweep generator wasn’t producing the frequencies that I thought it was. Once I started thinking of this possibility, I remembered that a couple of days ago when I ran the very first fixed-amplitude response curves on the new-improved 520Hz demodulator, that I had been forced to tweak the sweep gen output freq to get the flat-line curve I was expecting. For convenience, I have repeated these plots below:

‘raw’ Final Values vs time, with fixed frequency, 50% p-p

detail of ‘raw’ plot

‘raw’ final values vs time after synching Tx & Rx frequencies

When I looked back into the sweep generator code, I found that its output frequency was set to 521.1Hz to produce a 520.8Hz output. At the time I didn’t think that was particularly egregious, but apparently is was a warning that I missed – oops!

So, now I embarked on yet another tangential project – this time to correct the frequency accuracy problems with the sweep generator module (which you might recall, was being used to test the IR demodulator, which in itself was a tangential project associated with the charging station project, which (you guessed it) was a tangential project in support of my Wall-E2 wall-following robot project. Talk about recursive! ;-).

Anyway, my first attempt at a swept-frequency generation algorithm wasn’t all that brilliant – just using ‘delayMicros()’ calls to create the square-wave as shown below:

In retrospect it was clear that this was never going to produce frequency-accurate square-waves because of the additional time taken by the read/write statements. Without compensating for this additional delay, the output freq would always be below the nominal value, which is what I was experiencing.

Next, I modified the algorithm to use Teensy’s ‘elapsedMicros’ data type, as shown below, and ran a series of tests to determine its frequency accuracy.

To test for frequency accuracy, I set the sweep generator to sweep from 501 to 531Hz with 50% output level and 5 sec/step. Then I measured the actual output frequency using my trusty Tektronix 2236 ‘scope with its built-in digital frequency readout. The results are shown below:

Frequency accuracy using the ‘elapsedMicros’ algorithm

This was OK, but not great, as it showed that the output frequency could be as much as 0.5Hz off from the expected value. After some web research (mostly on Paul Stoffregen’s wonderful Teensy forum), I decided to try the FrequencyTimer2 library described here. After implementing this in a separate test program, I ran the same frequency accuracy test as above, with the following results.

Frequency accuracy using the ‘FrequencyTimer2’ library

This was much better than the ‘elapsedMicros’ algorithm, but still not great; still greater than +/-0.1Hz deviation for some target frequencies.

Back to the web for more research, where I found the ‘IntervalTimer’ object described on this page. After modifying my test program to utilize this technique, I got the following results.

Frequency accuracy using the ‘IntervalTimer’ object

Wow! a lot better! Not only is the absolute error almost an order of magnitude less, the measured output frequency appears to be a constant 0.015Hz below (positive error) the target frequency. This leads me to believe that the frequency is probably right on the mark, and the error is in the measurement, not the timing. In any case, this technique is more than good enough for my puny little sweep generator! The complete test program used to generate these results is included below:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

// Create an IntervalTimer object

IntervalTimer myTimer;

constintledPin=LED_BUILTIN;// the pin with a LED

constintOUTPUT_PIN=33;

voidsetup(void)

{

pinMode(ledPin,OUTPUT);

pinMode(OUTPUT_PIN,OUTPUT);

Serial.begin(115200);

Serial.println("Freq\tTimerVal");

for(size_ti=501;i<=531;i++)

{

floatperiod=1e6/i;

Serial.println(i);Serial.print("\t");Serial.println(period);

myTimer.begin(blinkLED,period/2);

delay(5000);

myTimer.end();

}

//myTimer.begin(blinkLED, 150000); // blinkLED to run every 0.15 seconds

As can be seen from the above plots, they are nicely centered vs frequency, but the ‘raw’ plot has a lot of clearly ‘bad’ data – huge positive and negative excursions from the expected value.

This situation led me down yet another rabbit-hole – what is causing these erroneous results? It appears that the bad data occurs at the beginning and the end of each 0.5 second frequency step – what could be causing that? After some thought, I decided to instrument the Interrupt Service Routine (ISR) that generates the square-wave transitions. As the following plot shows, there is a clear ‘glitch’ at the beginning of the first transition for each frequency step.

Square-wave transition times vs time using the IntervalTimer technique

Looking at the code that generates the sweep (shown below), it appears that the problem occurs because the IntervalTimer object must be destroyed (with ‘myTimer.end()’ and then re-created (with ‘myTimer.begin()’) with the new transition time. This inevitably causes the first transition of the next frequency step to be considerably off from nominal, and I don’t think there is anything I can do about it – bummer! 🙁

Use the ‘elapsedMicros()’ feature, with it’s inherent 0.5Hz max frequency error (due to rounding – this might be tweakable)

Use the ‘FrequencyTimer2’ library. This results in about 0.1Hz max error (again, possibly tweakable). Unfortunately, this technique requires that the output occur on a specific pin, which means I can’t output different levels without external hardware

Use the ‘IntervalTimer’ interrupt technique, which is very fast and very accurate. Unfortunately, as noted above, it also produces artifacts in the output spectra.

As I mentioned at the start of this post, this entire project is about three or four levels deep in the ‘tangential’ stack. The very last thing I wanted was yet another tangential project to learn the strengths/weaknesses of the various timing methods so I can implement the one with the best strength/weakness ratio, but yet here I am – arghhhh!