Tuesday, June 8, 2010

Heart Beat and SpO2 detection

I made some real progress this weekend in integrating the software to compute the heart rate (in bpm) and the blood oxygen saturation. I also made some real progress in reading real PPG signals from the TIA and providing dynamic gain. The pieces are coming together and a final blog update post will chronicle the past successes.

Heartbeat detection, reference:
A reference ADC sample had to be taken to determine the frequency (bpm) of the PPG signal. To do this, I used the function generator to drive a simulated PPG signal with a frequency of 2 Hz with p2p of 100 mV.

From this reference I noted that I captured 5 full periods. I then measured the index between from a peak-to-peak. In the image below, I measured from index 38 (with value 1286) to index 134 (with value 1312). This distance of 134-38 = 96 represents an index count of one full period of the simulated PPG. I also know that the repetion rate is 2 Hz so each full period represents 500 millsecond.

Now when I have a real ADC sample I compare the distance between peaks in this. In the image below, I measured from index 125 (with value 952) to index 325 (with value 967). this distance of 325-125 = 200 represents the index count of my pulse.

I now can set a basic porportion by stating that 96 index = 0.5 sec and 200 index = X seconds. Solving for X the result is 1.041 second. Therefore I noted that my pulse is 1 beat per 1.041 second or 62.5 bpm. This is a little under the average human heart rate of 72 bpm because I eyeballed last measurement.

Simulated PPG waveform

Real PPG waveform

Heartbeat detection, simulation:
Before I started implementing any code on the micro controller to analyze the ADC information I wanted to run a simulation. I started by writing my own program and creating a mock-array to mimic a PPG pulse ox signal.

I then started researching how to do peak detection in LabVIEW environment. Peak detection is very common in Signal processing and there was an abundance of resources on the topic. I quickly discovered the Peak Detector .VI in LabVIEW. This is a really cool VI which interpolates your data as a polynomial then takes derivatives to find maxim and minim points. I found a good example provided in the SDK which I just modified around my simulated pulse ox signal. Here is an image from the result:


You just specify threshold for max and min and it calculates valley and peak and puts the index and amplitudes in corresponding arrays. Knowing this, I really wanted to use it to implement the solution on my micro controller.

Heartbeat detection, practice:
I quickly ran into some problems when I tried to use this .VI on the micro controller. It appeared that it wasn't processing the data. I examined and googled the error result "-20001" and discovered this is the code for OUT_OFMEMORY error. I knew I would likely hit this point at some point as my program had really grown in size. I tried to slim down my program by deleting unused memory allocation routines but to no avail.

This is the biggest drawback in my opinion of developing in the LabVIEW environment. In a C environment I would quickly be able to diagnose this error and make a creative solution. But since I have no idea how memory is allocated by the high level (it should be dynamic and typeless to prevent memory leak) I simply cannot use this VI.

I was really disappointed so I had to create a new way to detect peak and valley. The solution I came up with is kind of elegant and will work. Here is how it works.
1. Sort the ADC array in Ascending order (low # first, high # last).
2. Reverse the order of this array to descending (high # first, low # last).
3. Index into element [0] to find maximum value in ADC array.
4. Now must check over the entire ADC array, let iteration count be represented by element j.
a. If ( (ADC[j]>ADC[j-1]) & (ADC[j]>ADC[j+1])) then element represents peak.
b. If ( (ADC[j]

I then have this basic delta detection scheme to determine distance (in index) from peak-to-peak or through-to-through. Now that you have this number you compare it to the reference spectrum calculated earlier. Like I said earlier, from reference we know what a period in index corresponds to so it's really easy to calculate Bpm from this.

I haven't finished writing code for SpO2 calculation but it's very easy from this new module. The new module can easily calculate max and min and can take ratio of these elements.

I think my biggest concern right now is time. Can I put this all together and have it working by Thursday. The one thing if I had more time is I'd make it more robust and reliable. There are odd quirks that I wish I could diagnose better.

Friday, June 4, 2010

Clock Timing nightmare

Hardware:
The clock circuit presented last time had problems. The two new clocks were non overlapping but due to the delay introduced would try demodulating the wrong channel. I tried re-working the circuit by delaying the LED driving clocks. In the end, this approach was just too complicated and I abandoned it.


As a replacement I adopted a circuit designed by Shanit. A relaxation oscillator was combined with a 555-timer in monostable operation to one shot a variable duty cycle. This output was then passed through an inverter. The result is a de-modulation clock with reduced duty cycle from the original CLK but not overlapping onto secondary channel.

What I learned: If something is starting to appear too complicated, ditch it and adopt the simpler topology.

Below is a schematic of the circuit I ended up using. The datasheet recommended I use a 1 uF with 5k POT to create a variable duty cycle. I built the circuit on the breadboard and have been using that to test the overall system. On Monday, Jon will construct an actual board which can be mounted on the POX board. Another great idea adopted by Shanit was to cut the Enable lines (EN1, EN2) and use them for the S/H demodulator clock line. In this way, the board won't look like such a hacked together nightmare.

There are a couple last minute changes Jon and I will be making to the board next week. We'll be fine tuning the gain of the TIA section and maybe making fine tuning changes to the LP filter.

Software:

Some tremendous progress was made on the software solution. In the last post I had discussed the DC detection, automatic detection and PGA gain. I've seen some other interesting solutions and am confident in my result. For instance, team Tango has a step-up PGA gain going from (1,2,5,10,20, etc.) till it reaches max rail. If I could return, I would most likely implement this solution. I like the elegance and the fact that it requires almost no error checking code.

My efforts last week:
1. Implement detection, subtraction, and PGA on both channels. Complete
2. Implement TCP/IP send back for both channels. Complete

To test this I drove a simulated PPG signal into the DC subtracter circuit and let my program run. It was actually very easy (think Copy/Paste) to implement the Ch2 code. I have a parallel system now where all actions are taken side by side in steps.

My focus this weekend and the next week is as follows:
1. Implement a peak detection scheme to calculate heart rate (in bpm).
2. Use peak detection scheme with a newly created SpO2 .VI detector to output the saturated blood oxygen level.
3. Fine tune the software to make it more/less aggressive. Right now, the output from the TIA is about 50 mV p2p so the gain is really trying to do gain 100 or 200. There are times when the output saturates which means I need to write error detection.
4. Error detection code.

I've created a thorough simulation of both of these components in LabVIEW and will update tmmrw or Sunday with a showing of the program. Basically, I wanted a test bench to test this code on my computer before I push changes to a micro controller.

I'm trying to figure out if any of these simulations or code would be helpful in an open source repository. I'd like to provide some thorough documentation and upload them as a project to Sourceforge or the LabVIEW example database (I wanted to check licensing agreement).

If anyone reads this blog and wants to contact me, my email is nlshrake@ucdavis.edu.