Not work with higher sampling frequency
Closed this issue · 14 comments
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
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.
@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
It's in RD117_ARDUINO.ino
@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?
@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.
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.
@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.
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.
@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%.
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?
It is 100Hz
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;
I haven't heard from you in a long time. Hence, I consider this issue to be closed.