Monthly Archives: July 2017

IR Modulation Processing Algorithm Development – Part XVI

Posted 22 July 2017

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:

Channel 1 ‘raw’ amplitude response vs time

’round-trip (sweep gen -> IR Demod -> sweep gen) Channel 1 amplitude response vs input amplitude

Channel1 Freq response

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:

 

Before implementing the ‘IntervalTimer’ technique in my mainline sweep generator program, I re-ran the frequency sweep from 501 to 531Hz, with the following results.

’round-trip’ freq sweep results before implementing the ‘IntervalTimer’ technique

‘raw’ freq sweep results before implementing the ‘IntervalTimer’ technique

After implementing the new ‘IntervalTimer’ technique, I ran the sweeps again with nothing else changed, with the following results.

‘Round-trip’ and ‘raw’ FinalValue vs Frequency, with IntervalTimer technique

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! 🙁

 

So, it appears that my choices for accurate frequency generation are:

  • 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!

Stay tuned!

Frank

 

 

 

 

 

 

 

 

IR Modulation Processing Algorithm Development – Part XV

Posted 18 July 2017,

In my last post on this (somewhat long-winded) subject, I described the implementation of a Teensy-based sweep generator utility for use in testing the band-pass filter (BPF) application, and showed some results from the 52Hz version of the BPF.  As a result of this work, I believe I had validated both the test instrumentation and the 52Hz BPF implementation.

This post extends the previous work to the original goal for this project – the 520Hz BPF implementation.  When I did the original work on the 520Hz version, it was clear there was something wrong, but not what.  For reference, here are some representative plots from this 24 June 2017 post.

Computed final values vs complete input data cycles for sensor channel 1

There has been a lot of water over the dam between the 24 June post and now.

  • Replaced Trinket-based square-wave generator with Teensy 3.5 one for better frequency stability/accuracy
  • Replaced simple square-wave generator with frequency/amplitude sweep generator
  • Added ’round-trip’ measurement capability.  Sweep generator -> BPF -> sweep generator so BPF response can be directly correlated to sweep generator output.
  • Revised BPF code to eliminate all but one sensor channel
  • Replaced all in-line printouts with writes to internal arrays, with post-run printout option.
  • Added micro-second accuracy time stamps to demodulator internal data capture
  • Temporarily reduced filter center freq from 520Hz to 52Hz to eliminate any possibility of timing problems due to Teensy 3.5 internal interrupts.

As the plots from the previous post showed, the 52Hz BPF implementation works as intended.  So, it was time to try my hand (again!) with the 520Hz version.  First, I did an elapsed time study, to make sure no sample periods were being missed.  As the plot below shows, all the elapsed time values are reasonable, with only a few going to 101 μSec.  I speculate that the 101 values may be due to execution of extra-long internal Teensy ISRs.  The elapsed time plot conclusively demonstrates that something I did during the above list of changes eliminated the occasional (1 out of 68 or 69 cycles) missed sample that caused the previous ‘crappy’ results for the 520Hz version of the filter. My personal favorite for the culprit is one or more of the print statements I had in the original code for debugging.

Sample times vs time.

The next set of plots shows the results of an amplitude sweep, with the amplitude set to a fixed 50% p-p value

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

’round-trip’ Final Values vs time, fixed freq, 50% p-p

detail of ‘raw’ pot

The above plots weren’t quite what I expected; both the ‘raw’ and ’round-trip’ final values were lower than expected, and the ‘raw’ plot had a lot more ripple than I wanted to see.  This all led me to believe that the test frequency was not centered in the BPF passband.  I had not actually double-checked this, so I went back a few steps and made sure the fixed amplitude sweep frequency was indeed synched with the demodulator sampling frequency.  After that, the plots got a lot cleaner, as shown below.

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

 

In the above plot, the ‘raw’ final value is pretty steady around the value 1,320,000, which agrees well with a 50% p-p Tx level.

Next, I ran frequency sweeps, as shown in the following plots

‘raw’ Final Values vs frequency, with amplitude @ 50% p-p

’round-trip’ Final Values vs frequency, with amplitude @ 50% p-p

For reference, the same two plots for the 52Hz implementation are shown below

Captured ‘raw’ final value from IR demodulator. Frequency values added later

Frequency sweep results from sweep generator printout (D/A from IR demod, then A/D in sweep gen)

Next, I ran an amplitude sweep from 0% to 100% p-p input, as shown in the following plots

‘raw’ Final Values vs amplitude, with frequency @ 520.8Hz

’round-trip’ Final Values vs amplitude, with frequency @ 520.8Hz

So, it appears to me that the 520Hz BPF is working just as well as the 52Hz one, and it’s time to move on to multi-sensor operation.

I have placed the code for the now working 52/520Hz single-channel N-path BPF on GitHub at https://github.com/paynterf/SqWaveIQDemodV2.git.  There are three branches available on GitHub, as follows:

  1. Master – this is the original ‘root’ for the IQ Demodulator (BPF) project
  2. SingleChannel – this is the final code for the single channel version, working at 52 & 520Hz
  3. TwoChannel – this is my new branch to host the in-progress dual-channel BPF.

Stay tuned!

Frank

 

 

 

 

IR Modulation Processing Algorithm Development – Part XIV

Posted 07 July 2017

In my last post on this subject, I described a working 52Hz implementation of John Jenkins’ ‘degenerate N-Path Band-pass filter (BPF).  This filter is intended to allow Wall-E2 (my wall-following robot) to discriminate between the IR homing beam from a charging station and other ambient IR interferers.  This post describes the implementation of a basic swept-frequency square-wave function generator for use in testing BPF performance.

Now that I have a working (I think) version of the BPF algorithm, I want to implement and test a frequency and amplitude sweeping module so I can use it to test not only the working 52Hz BPF implementation, but the (so far not working) 520Hz implementation when we finally get the bugs worked out of it.   My idea is that doing this now will have two (maybe three) significant benefits

  • Allows me to fully characterize the existing 52Hz BPF implementation
  • Gives me baseline performance characteristics for comparison against 520Hz results
  • (freebie) – More fun with Teensy!

I’m already using a second Teensy 3.5 as the 52Hz square wave transmitter in my test setup, so all I need to do is to modify the existing program to sweep the desired frequencies and amplitudes.  I think I’ll do this as a command-line parameter-entry program, with user-entry parameters for start, stop, and step frequencies (Hz) and fixed amplitude (% full-scale) for the frequency sweep module, and start, stop, and step amplitudes (% full-scale) and a set center freq for the amplitude sweep module.

12-15 July 2017 Update:

Well, I finally got the sweep generator going, at least the frequency sweep part, and managed to get some good data out of the 52Hz square wave demodulator.  What I was striving for was something akin to the normal hardware setup where a frequency-sensitive ‘device under test’ (DUT) is driven from a frequency sweep generator, and a data logger of some sort records the result, something like the following block diagram.  Back in the day, the ‘datalogger’ was quite often an engineer recording results on a piece of graph paper – how quaint!  😉

Typical swept frequency test setup

In this project, the DUT is the Teensy 3.5 hosting the IR demodulator code, and the frequency sweep generator is another Teensy 3.5 hosting the code to generate a swept-frequency square wave signal.

But, what to do about the datalogger?  I could record data from inside the demodulator program, but we’re talking a lot of data; even at 52Hz that’s 52 4-byte values per second, and a sweep test might take as long as 3 minutes –> 36000 bytes.  Fortunately, the Teensy 3.x sports 192KB of RAM, so a 36KB chunk barely scratches the paint – yay!!  Another problem with logging internal to the demod module is how to correlate the captured data with the driving frequency; they are obviously correlated, but it’s not a slam-dunk to unequivocally assign particular frequencies to a particular stored data value.

So, I decided on a two-pronged approach; I will indeed save the data internally to RAM for later printout, but will also output an appropriately scaled version of the ‘final value’ calculation to one of the two DAC ports available on the Teensy 3.5 SBC.  I plan to run this signal back to the sweep generator module so I can correlate ‘final value’ values with test frequencies, as shown in the following amended block diagram.

Modified sweep test block diagram

This setup works, and now I can get frequency-correlated ‘final value’ data  from the sweep generator A/D.  However, since the demodulator module ‘free-runs’ with respect to the sweep generator, the final value storage array can fill up before the frequency sweep finishes (or even before it starts!), so something is needed to synch the two modules.  As the following block diagram shows, this is accomplished by providing a digital trigger from the sweep generator to start the demodulator.  Also added in this final setup is the ability to set the square wave amplitude, for later amplitude sweep tests (set to 50% amplitude for the swept-frequency tests)

Final sweep setup. Note DAC block for square wave amplitude adjustment and trigger signal for synchronization

With the above setup I ran a number of frequency sweeps, as shown in the plots below.  I started out with a sweep from 48 to 56Hz (+/- 4Hz from center freq), but quickly narrowed it down to +/- 2Hz.  As my friend and mentor John Jenkins pointed out in his gentle, diplomatic way (something like “What were you thinking!“), the BPF bandwith is less than 1Hz, so a +/- 4Hz sweep range will just record a lot of nothing – oops!

Frequency sweep results from sweep generator printout (D/A from IR demod, then A/D in sweep gen)

Captured ‘raw’ final value from IR demodulator. Frequency values added later

Even though the above sweeps were way too wide, it is clear that the demodulator algorithm is working (at least in the 52Hz implementation).

Frequency sweep results from sweep generator printout (D/A from IR demod, then A/D in sweep gen)

Captured ‘raw’ final value from IR demodulator. Frequency values added later

In the above plots, the sweep was reduced to +/- 2Hz, and the number of steps increased to 80.  The result is a much more detailed look at the BPF response.  As expected the general shape of the ’round-trip’ (individual ‘final value’ numbers sent from the demodulator to the sweep generator via the DAC-A/D channel and then printed) plot and the ‘raw final value’ plot match, but I didn’t expect either the flat-top region at the center of both plots or the fine ripple shown in the ‘raw’ plot.  I made a number of additional sweeps with up to 160 steps at 2 sec/step, but neither the flat-top region nor the fine ripple features changed – they are apparently real features of the BPF response.

I think I finally figured out that the flat-top region was due to the fact that there is a narrow range of frequencies for which the filter response is maximum and identical.  In this very narrow region, the digital sample points are ‘perfectly’ aligned with the incoming square wave, so that the final value is the maximum possible value for the input p-p amplitude.

As for the fine ripple features, I think these are due to the beat frequency between the incoming signal and the sample period.  If this is the case, I would expect the ripple features to be more closely spaced far away from the center frequency, and less closely spaced toward the center. This certainly seems to be the case in the above plot, but even more evident in the detail plots below, (points extracted from the main plot above)

far and near center BPF response detail

Next, I performed an amplitude sweep from 100% to 0% p-p amplitude, with the center frequency set to 51.87Hz (this value was empirically determined earlier), with the following results

‘Raw’ final values from amplitude sweep.

final values captured by sweep generator

As the above plots show, the filter response is very linear with respect to p-p amplitude variations.  The maximum value shown in the ‘raw’ plot above is a little above 2.5 x 106. The actual maximum value from the data is 2,606,752, which matches well with the theoretical maximum value of 4096*10*64 = 2,621,440.

Lastly, I performed another frequency sweep, but this time with a running-sum length of 32 cycles vs 64, to see how much the band-pass width was affected by halving the number of cycles in the running-sum circular buffer.  Comparing the 32-cycle and 64-cycle plots as shown below, the ‘flat-top bandwidth is considerably wider than the original 64-cycle sweep results  – about 1.8Hz edge-to-edge for 32 and about 0.4 for the original 64 version.  Maybe as much as 4x bandwidth increase.

Frequency sweep results from sweep generator printout (D/A from IR demod, then A/D in sweep gen)

Frequency sweep results from sweep generator printout (D/A from IR demod, then A/D in sweep gen)

Captured ‘raw’ final value from IR demodulator.

Summary and Conclusions:

This post described my efforts to characterize the frequency and amplitude responses for the 52Hz implementation of the ‘degenerate N-path band-pass filter’ described to me by friend and mentor John Jenkins some time ago.

The original plan was to use this filter technique at 520Hz to enable my Wall-E2 wall-following robot to home to a charging station using a square-wave modulated IR signal to suppress ambient IR interference.  Unfortunately, I could not get the 520Hz implementation to work properly, probably due to some unknown timing issues associated with the Teensy 3.x architecture.  So, I decided to run the filter at 1/10th speed (52Hz) instead, thereby eliminating any timing issues, and allowing me to establish a baseline for subsequent higher-speed testing.

As the data above shows, the effort to implement the N-path BPF at 52Hz was completely successful.  Both the frequency and amplitude responses were characterized.  In addition:

  • I gained a lot more experience with the Teensy 3.x line of SBCs.  They are less than half the size of an Arduino Uno, have over 10x more RAM, 50x faster, come with on-board SD (3.5 & 3.6) cost about the same, and have about the same power requirements – such a deal!
  • Implemented a Teensy 3.5 frequency/amplitude sweep square wave generator, and verified proper operation by using it to characterize the 52Hz BPF implementation.
  • Implemented a Teensy 3.5 fixed-frequency square wave generator, and verified proper operation by using it to find the correct transmit frequency for the 52Hz BPF implementation
  • Validated the efficacy of my little two-Teensy test bed.

The above tools can now be used with high confidence to investigate (and hopefully eliminate) the problems with running the BPF at 520Hz.

Stay tuned!

Frank

 

 

 

 

 

 

 

 

IR Modulation Processing Algorithm Development – Part XIII

Posted 03 July 2017

With this post, I’m nearing a new Personal Record for a subject series (the ‘Charging Station Design’ series went to 14), with no end in sight.  The last missive from my friend and mentor John Jenkins (I’m reconsidering the ‘friend’ part, as ASFAICT, he intends to drive me into an early grave over this project!) was ‘Still not there – the ‘Final Value’ output from the filter is still wrong! :-(.

Be that as it may, it is my lot, challenge, ‘opportunity to excel’ or whatever, to continue working on this thing until I kill it – or it kills me (indeterminate as to which at the moment) 😉

So, after eliminating most/all of the print statements, John is still convinced that the Teensy is missing samples on a regular basis, and this is screwing up the rest of the algorithm.  I’m just as convinced that the Teensy is way too fast to miss any samples, and that any problems must be later in the algorithm.

However, there may be a way to settle which is which, without the use of magic or a crystal ball.  The Teensy 3.5/3.6 SBC’s come with a built-in micro-SD card capability, which I think can be used to log the output of each stage in a way that doesn’t (I hope) impact sample timing.

The first step in my new master plan is to make sure I can actually write to and read from an SD card on my demod Teensy (actually my first step was to go by MicroCenter and get a micro-SD card, as all mine seem to have disappeared!).  I found and loaded  Paul Stoffregen’s nice little SD test sketch from his GitHub site, and ran it on my Teensy 3.5.  After changing the ‘chipSelect’ constant from the default 4 to ‘BUILTIN_SDCARD’, it ran successfully, and I was even able to open the ‘Test.txt’ on my PC – Yay!

The next step is to modify my IR demod program to send logging data to the SD card instead of the serial port, and to monitor my timing pulse output to make sure the SD card writes don’t interfere (can’t imagine they would, but I have a very limited imagination!).

After timing a loop that wrote 1000 short lines to the SD card, I reluctantly came to the conclusion that the stock SD card library may not be fast enough to ensure that logging output isn’t affecting the results of the algorithm.  There is a faster library (SdFat), and some other speedup techniques, but rather than going down yet another rabbit-hole, I decided it was time to try a different idea.

The original plan for the IR demod idea was to use a ~520Hz square-wave modulation frequency.  This is high enough to be well away from 60Hz line freq, and not aligned with higher line harmonics, but it may be just high enough to be causing the problems we are seeing.  Since the algorithm actually doesn’t care what the frequency is, I decided that I can put the timing issue to bed by simply reducing the modulation frequency by a factor of 10, to something like 52Hz.  Sample spacing at this frequency (assuming 20 samples/cycle) is 1000μS, which should provide more than enough room for computations (and hopefully some printouts).

To start this process, I modified both the transmit and receive Teensy 3.5 programs for the new, lower freq, and then tweaked the transmit frequency to synch with the receiver timing.  As the following short video shows, a transmit frequency of 51.87Hz and a receive frequency of 51.815Hz synched up fairly well.

 

The demodulator timing pulse train (the rising edge is at the start of the sampling/computation block,and the trailing edge is at the end) is shown in the following scope photos.

Bottom trace is sampler pulse train, top trace is square wave at ~52Hz. Time scale is 1ms/cm

Photo showing just one sampling period. Time scale is 0.1ms/cm

Expanded view of one sampling/computation period. Time scale is 5 microsec/cm

From the above photos, it is clear that the entire sampling/computation block takes less than 10μS, leaving about 990μS free before the next sample occurs.

Yesterday, ‘talking’ about this problem (‘talking’ with John can be a rather terrifying experience – sort of like ‘talking’ with Zeus – you wonder when the thunderbolt will arrive and whether or not he’s going to miss) with John, I opined that if his theory of missing samples is correct, a list of accurate sample timestamps should show some gaps; a linearly increasing timestamp value would refute the claim, and a plot with vertical jumps would validate it.

Then last night, as I was drifting off to sleep, I realized I didn’t have to actually create the necessary timestamps, as I already had them in the code.  The elapsedMicros variable type such as ‘sinceLastSample’ gets incremented by the system each time through loop(), and only gets modified from inside the sampling/computation loop.  If all samples are taken on time, this variable should be a constant when measured inside the sample/computation block just before it gets reset.  The relevant code snippet is shown below

At the point shown by “/*RECORD sinceLastSample HERE!*/” in the above snippet, the ‘sinceLastSample’ variable should be USEC_PER_SAMPLE +/- 1μS.  A dropped sample event should cause the value of this variable to change to N*USEC_PER_SAMPLE, where N is the number of consecutive dropped samples.

So, all I have to do is write the ‘sinceLastSample’ value to an array for a few seconds, and then write it back out again after stopping the program.  An Excel plot of the values should tell the tale, one way or the other

05 July 2017

Here are some preliminary results from the 52Hz implementation

A 2560-point (128 cycles) array containing ‘sinceLastSample’ values.

129-point array containing ‘Final Value’ values

Note that the ‘sinceLastSample’ value varies -0/+3 μS from ideal (the actual value is something like 964.32 μS), and there are clearly no dropped samples.

The ‘Final Value’ curve looks rather linear up to something like 680,000, which I suspect John is going to confirm is the correct value for a 0.8V p-p squarewave input at the center freq of the filter passband.

So, assuming John does indeed confirm the final value numbers are correct, pretty much validates the algorithm (unchanged from the 520Hz version), and lends credence to his idea that samples are getting dropped in the higher frequency implementation.

More to come,

Frank

 

 

IR Modulation Processing Algorithm Development – Part XII

Posted 01 July 2017

I went down a bit of a rabbit-hole between my last post on this subject and today.  I was attempting to run down the problems with my IR demodulation code when I discovered that the basic rate at which the demodulator captured samples was off by a factor of 5 or so – yikes!!  Instead of 20 samples per cycle, I was seeing more like 100, as shown below.

Raw data capture using ‘sinceLastOutput = 0’ in the first demodulation step

Raw data capture using ‘sinceLastOutput -= USEC_PER_SAMPLE’ in the first demodulation step

As you might expect, this development threw me for a bit of a loop – as the change from ‘sinceLastOutput = 0’ to ‘sinceLastOutput -= USEC_PER_SAMPLE’ was instrumental (or at least so I thought) to getting the transmit and receive frequencies matched more accurately.  So, now I had to go chase down yet another tangential problem – what I refer to as ‘going down a rabbit hole’. The only saving grace in all this is that, as a twice-retired engineer, I have no deadlines! 😉

To resolve this problem, I wound up creating an entirely new test program to isolate the issue, with just the following lines in the loop() function:

With the ‘sinceLastOutput -= USEC_PER_SAMPLE’ line active, I got about 100 samples/cycle. With this line commented out and the ‘sinceLastOutput = 0’ line active, I got the normal 20 samples/cycle, with nothing else being changed.

Once I was sure the test program was consistently producing the anomalous results I had noticed in the complete program, I posted this issue to the PJRC Forum, so I could get some help from the experts.  I knew it was something I was doing wrong – I just didn’t know what!

Within a few hours I had received several responses, and the one that hit the bullseye was the one correctly identifying a subtlety with the the ‘-=’ elapsedMicros() usage format.  When this format is used, the accompanying ‘elapsedMicros’ variable must be initialized in the setup() code; otherwise it will be some arbitrary (and possibly quite large) value when the loop() function is first entered. This will cause the ‘if’ statement to trigger repeatedly until the ‘-=’ line eventually reduces the variable to a value below USEC_PER_SAMPLE, at which point it will start behaving as expected.  This odd behavior never happens with the ‘=0’ usage, as the variable is initialized on the first pass through the ‘if’ statement.  Sure enough, when I added a line at the bottom of my setup() function to set the ‘sinceLastOutput’ variable to zero, my little test program immediately stopped mis-behaving.

Well, this little side-journey only cost me a couple of days, and a few more white hairs (oh, wait –  my hair is already completely white – no problem!)  Back to my regularly scheduled program…

Frequency Matching:

My friend and mentor John Jenkins, who has been looking over my shoulder (and occasionally whacking me on the head) during this project, was unsure that my frequency matching setup was actually 100% complete, as the video I took the last time didn’t run long enough to convince him (and there were some un-explained triggering glitches as well).  So, I thought I would re-do this part to make him happy.  To do this I modified my little test program from the above ‘rabbit-hole’ elapsedMicros issue to output a square wave from the demodulator board that could be compared to the transmitter square wave.

As shown in the above video, the transmit and demodulator frequencies are quite well matched, showing essentially zero relative drift even over the 30-40 second time of the video.  Mission accomplished! ;-).

Sample Acquisition Step:

I modified my demodulator program to properly initialize the ‘elapsedMicros’ variable being used for sample timing, and verified proper operation by commenting out everything but the sample acquisition step.  I captured several hundred samples, and plotted the first hundred in Excel as follows:

100 samples using proper ‘elapsedMicros’ variable initialization

Sample Sum Step:

Next, each group of five samples (1/4 cycle) is summed, and the ‘In-phase’ and ‘Quadrature’ components are generated using the appropriate sign sequences.  As shown in the following, this appears to be happening correctly:

Cycle Sum Accumulation:

As each sample group is summed and the I/Q components generated, accumulate the 1/4-cycle I/Q sums into a ‘Cycle Sum’.  As the following printout shows, this step also is being performed properly

 

Running Sum Accumulation:

The last step in the algorithm is to compute the N-cycle running sum of all the cycle sums.  This is done by subtracting the oldest value from the circular buffer from the current running sum, adding the current cycle sum to the running sum, and then replacing the oldest cycle sum value in the circular buffer by the current cycle sum.

  1. RunningSum = RunningSum + CurrentCycleSum – OldestCycleSum
  2. OldestCycleSum = CurrentCycleSum
  3. Circular buffer index incremented by 1 (MOD N)

This one took a while to instrument properly. I first tried just adding some more columns on to the current display setup, but that became too cumbersome, too fast.  Verifying the running sum calculation requires looking at not only the current running sum, but also its value from N cycles (or N*5*4 samples) previously.  So, I modified the code to only print one line per cycle, and this was much easier to manage.  Here’s a partial printout showing a little over 200 cycles, representing about 400mSec (200 cycles * 2mSec/cycle).

 

To analyze these results, I dropped them into Excel, and used it’s ‘Freeze Panes’ feature to juxtapose the results from cycles 0 through 4 with cycles 65, 66, and 67.  This allowed me to verify that the running sum expression was being calculated correctly and the circular buffer was being loaded and referenced correctly.  When I finished verifying these results, I plotted the final value column, as shown below:

 

New ‘Final Value’ results

As an experiment, I also momentarily blocked the IR path with my finger, and plotted the results, as shown below:

Filter response with finger blocking IR path for a few seconds, then removed

And again, just waving my finger into and out of the IR path several times over several seconds

Filter response with finger moved in and out of IR path several times over 3-5 seconds

John’s comment on all this was “still not right”, as explained below:

 

“Think I misinterpreted earlier plot as the one below makes it clearer what is happening.  The flat segments are when sample timing is proper.  So 3 good sets of cycles samplesin a row is max before there is a relative phase slip of ~1/10 of 180degs of Tx signal or ~96us = (time_for_180deg = (1/520)/2) / 10.
The slips are thus equal to 10 raw samples which are lost every ~35 Tx cycles = (51-16) for CSumQ and ~34 Tx cycles (68-34) for CSumI.  So 20 raw samples (1 full Tx cycle) are lost every ~69 Tx cycles and this results in 64-cycle running I and Q sums being only 5/64 of 64 times the actual signal magnitude because all but 5 of the 64 values in running sums are canceled out by other values in the I and Q running sums.  Applying this to the running sum of abs(I)+abs(Q) gives ~35.8K which is close to what you are seeing — that is:  ~35,823 = (5/64)*(64*7164.667)
Seems almost certain that the calcs are correct and the problem is that the Teensy can’t keep up with the needed sample rate (probably due coding approach or interrupts but less likely “magic timing code” Tx and Rx were very stable on scope).  Back to thinking ADC –> DMA may be the cure”

My take on the above was that it is the print statements that are the cause of the problem, not anything to do with Teensy ADC speed.  To test this theory, I removed all but the ‘Final Value’ print statement, and put that one statement in a block that executed only once per 64 cycles.  With this change, I could see that the print output was keeping up with real time and not lagging as before. Below shows a plot with several IR beam block/unblock cycles, and it appears that the ‘Final Value’ output is much more responsive to the block/unblock events.  Interestingly, it appears there is some sort of Gibbs phenomenon associated with the ‘fast’ cycles when the system switches between one equilibrium level and another.

The next step is to remove the print statements entirely and route the ‘Final Value’ output to one of the two DAC lines on the Teensy 3.5.  To test this idea, I adapted the HobbyTronics sine wave generator code to my project (pin A21 on the Teensy 3.5 vs A14 on the Teensy 3.2), and got the following display on my ‘scope:

Output from Teensy 3.5 DAC0 (A21) pin

So, now I know that the DAC output works – now I just need to scale the ‘Final Value’ numbers correctly and connect them to the DAC output.  Then I can watch the filter action in real time without worrying about the impact of print statements.

With an input square wave amplitude of about 0.35V p-p, or about 0.35/3.3 = 0.106 FS, the output is about 29,000.  Therefore the peak output from the FV stage should be about 29,000/0.106 ~ 273,584.  so an input of 0.35/3.3 = 0.106 should produce an output of (29,000/273584) *3.3 = 0.106*3.3 = 0.35

So, I modified my Sinewave test code to output the Final Value number, scaled by 273,584, and got the following display.  In this test, the input amplitude is about 0.35V p-p, and the output is about 0.35VDC

 

 

 

Stay tuned,

Frank