Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not work with higher sampling frequency #2

Closed
dzungpv opened this issue May 11, 2018 · 14 comments
Closed

Not work with higher sampling frequency #2

dzungpv opened this issue May 11, 2018 · 14 comments

Comments

@dzungpv
Copy link

dzungpv commented May 11, 2018

I test your code with 25 sampling frequency
#define FS 25
But when i change sampling frequency to 50 or 100 it not work always show invalid:
#define FS 100

@aromring
Copy link
Owner

It happens because of this:

#define FS 25 //sampling frequency
#define BUFFER_SIZE (FS*4)

then

int8_t i;
for(i=0;i<BUFFER_SIZE;++i)

The index "i" is just an 8-bit signed integer. As such, it can't exceed 127. If you want to use higher sampling rates, then you have to replace all "int8_t" by integer types with higher byte count.

@dzungpv
Copy link
Author

dzungpv commented May 12, 2018

@aromring thank you for the hint, but there is no int8_t i, it is int32_t in the code https://github.com/aromring/MAX30102_by_RF/blob/master/algorithm_by_RF.cpp

@aromring
Copy link
Owner

It's in RD117_ARDUINO.ino

@dzungpv
Copy link
Author

dzungpv commented May 12, 2018

@aromring I replace int8_t with int32_t, it crash. I tested with esp8266 and esp32, remove code with sdcard and change the setting of the sensor to no average(if(!maxim_max30102_write_reg(REG_FIFO_CONFIG,0x00f)) ) and sample rate 100hz, take 400 samples, fix n_last_peak_interval by 25 (because you use the FS for this value). Have you ever try the board with 100hz sample?

@dzungpv
Copy link
Author

dzungpv commented May 12, 2018

@aromring Never mind. Finally i can make it work. But your code not good with high sample rate, with 100 hz it give like 240 bpm, 70 % and jump to 99% in 1 minute.

@dzungpv dzungpv closed this as completed May 12, 2018
@dzungpv dzungpv reopened this May 12, 2018
@aromring
Copy link
Owner

No, I've never tried my circuit with higher sampling rates, since they offer almost no improvement at much higher computing cost. Nevertheless, if you still insist on using 100 Hz, then read on.
You should have read my Instructable, especially Step 3 on preprocessing. You would notice that in order to save some CPU time I've pre-calculated the mean-centered sum of squares:

sum_X2 = S{t=-49.5;49.5} (t^2) = 83325

because FS=25 times 4 seconds gives 100 data points. With 100 Hz sampling rate the number of points increases to 400 and, correspondingly, the sum_X2 spans "t" values from -199.5 to 199.5:

sum_X2 = S{t=-199.5;199.5} (t^2) = 5333300

Thus, update the sum_X2 parameter in algorithm_by_RF.h by the above number and your measurements should fall back into the proper range.

@dzungpv
Copy link
Author

dzungpv commented May 18, 2018

@aromring Yes, after apply your sum_X2 it work. Your code much more reliable than MAXIM code, but it only with SPO2 and not with heart rate. From my test both with 25hz and 100Hz, heart rate from 50 to 61 bpm but the real heart rate is 80.

@aromring
Copy link
Owner

Well, I am afraid this perceived discrepancy is not the result of my code, but either an error in your setup, or a misunderstanding. I have checked heart rate results by countless tests not just one, but two independent methods. My MAX30102 provides essentially identical heart rates (HR) when compared to: 1) another medical device, 2) simple count with a stopwatch.
I don't know why you claim 80 bpm as "the real heart rate" - how did you verify it? The HR range between 50 and 61 bpm certainly seems like a perfectly normal heart rate of a sleeping human, while 80 does not.
If you still suspect my code being at fault here, then please post raw waveforms - all 100 or 400 samples for both R and IR channels - and let me know why you think the calculations are wrong in this case.

@dzungpv
Copy link
Author

dzungpv commented May 21, 2018

@aromring I upload the code include the raw IR and RED data here https://www.dropbox.com/s/qakvdpi5cdz6klb/Maxim_SPO2.zip?dl=0. The raw data file RawData2018_4_21_87bpm_99SPO2.txt With the data first column is RED and second is IR.
Original your code not work with me like in #1 , the sensor not turn on so I use https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library and adapt your code. The sample data is from my heart rate it is 87bpm when take(sitting on the chair). I have an Galaxy S8 which include MAX30102 sensor, left index finger i use sensor with your code(run on NodeMCU ESP 8266) and the right hand use Galaxy S8 with Samsung Health app at the same time, Your code adapt in the file above show 56bpm/99.3% and Samsung app show 87 bpm and 99%.

@aromring
Copy link
Owner

Thank you. Hopefully (and finally), I will have some time tonight to take a look at it.
At what sampling rate was the data in RawData2018_4_21_87bpm_99SPO2.txt obtained? 100 or 25 sps?

@dzungpv
Copy link
Author

dzungpv commented May 23, 2018

It is 100Hz

@aromring
Copy link
Owner

All right, I know what happened. All the sampling parameters are OK, the problem is your unusually high heart rate (HR). 87 bpm while just sitting on a chair is very high, indeed. Mine, for example, is ~60 bpm while sitting and ~50 bpm while sleeping.

First, let me refresh you on how the HR detection algorithm works: it is supposed to detect a local maximum of the first peak in the autocorrelation graph. The X value at this maximum (lag) corresponds to the raw data shifted by the averaged distance between neighbor peaks in the raw R/IR data graphs. This distance (D) is expressed as the number of samples between peaks, therefore heart rate (in bpm) is calculated as
HR = (60*FS)/D
where FS is the sampling frequency. Looking at the autocorrelation graph from your data you can see the first peak at around 68 samples, which leads to HR = 6000/68 = 88.23 - right in the ballpark of where it should be.

Hence, a perfect algorithm would detect the correct maximum of the first peak every time. However, this means a lot of calculations which eat up precious CPU time. My simple device is not equipped with a real time clock, thus I rely on MAX30102 sampling as an approximation of real time clock. For that, all the calculations done by rf_heart_rate_and_oxygen_saturation() must be very fast in order not to delay the measure of real time. The algorithm is successful in this respect, but at the cost of some sacrifices. The D detection routine, rf_signal_periodicity(), relies on an initial guess, D0, to find the appropriate maximum. The routine looks at autocorrelation values at D0, D0+1, and D0-1 to determine direction in which the peak resides. If, for example, D0=50 in your case, then the routine marches to the right at D1=51, D2=52, etc., until reaching peak at optimal Dn=68. The optimal value is then remembered as a new D0 for the next cycle. Since HR at night does not change much, the cost of obtaining its value from the next batch of samples is quite low - no more than a few evaluations of autocorrelation function.

Which brings me back to the initial guess, D0. Ideally, it should be in the range between first two local minima, e.g., 32 and 96 in your case. I tailored it to myself setting it to 25 at the beginning of rf_heart_rate_and_oxygen_saturation() function:

static int32_t n_last_peak_interval=FS; // Initialize it to 25, which corresponds to heart rate of 60 bps, RF

because HR=60 bpm was quite a safe bet. But in your case 60 bpm is just a bit too low. D0=100 is actually on the wrong side of the local minimum and rf_signal_periodicity() actually finds the second maximum at about 130 samples, which corresponds to twice the distance between heart beats and translates to too low HR=46.

I guess, all you have to do to adjust the code to your physiology is to update the D0 to, say, 67?

static int32_t n_last_peak_interval=67;

or

static int32_t n_last_peak_interval=2*FS/3;

screen shot 2018-05-23 at 7 31 24 am
screen shot 2018-05-23 at 7 31 51 am

@aromring
Copy link
Owner

I haven't heard from you in a long time. Hence, I consider this issue to be closed.

@dzungpv
Copy link
Author

dzungpv commented Jul 16, 2019

@aromring Thanks you again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants