Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: MAX30101 max32630fthr
Fork of MAX30101_HR_SPO2 by
Diff: main.cpp
- Revision:
- 11:976c80cc99d5
- Parent:
- 10:fcfa9adc99a9
- Child:
- 12:ac85295f8713
--- a/main.cpp Mon Nov 20 16:36:55 2017 +0000 +++ b/main.cpp Thu Dec 14 22:13:18 2017 +0000 @@ -30,238 +30,816 @@ * ownership rights. ******************************************************************************* */ - + #include "mbed.h" #include "max32630fthr.h" #include "MAX30101.h" -#include "algorithm.h" +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +//variable for the algorithm +uint16_t sampleRate =100; +uint16_t compSpO2=1; +int16_t ir_ac_comp =0; +int16_t red_ac_comp=0; +int16_t green_ac_comp=0; +int16_t ir_ac_mag=0; +int16_t red_ac_mag=0; +int16_t green_ac_mag=0; +uint16_t HRbpm2=0; +uint16_t SpO2B=0; +uint16_t DRdy=0; +//Heart rate and SpO2 algorithm +void HRSpO2Func(uint32_t dinIR, uint32_t dinRed, uint32_t dinGreen, uint32_t ns,uint16_t SampRate,uint16_t compSpO2, + int16_t *ir_ac_comp,int16_t *red_ac_comp, int16_t *green_ac_comp, int16_t *ir_ac_mag,int16_t *red_ac_mag, int16_t *green_ac_mag, uint16_t *HRbpm2,uint16_t *SpO2B,uint16_t *DRdy ); +//helper functions for the heart rate and SpO2 function +uint16_t avg_dc_est(int32_t *p, uint16_t x); + +void lp_dfir_flt(int16_t din0,int16_t din1,int16_t din2, int16_t *dout0,int16_t *dout1,int16_t *dout2) ; +int32_t mul16(int16_t x, int16_t y); + +//set logic level to 3.3V MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3); +//IC configuration functions bool op_sensor_config(MAX30101 &op_sensor); void pmic_config(I2C & i2c_bus, DigitalOut & pmic_en); /* Op Sensor FIFO nearly full callback */ -volatile bool op_sensorIntFlag = 0; +volatile bool op_sensorIntFlag = 0; void op_sensor_callback() { op_sensorIntFlag = 1; } -//declare large variables outside of main -uint32_t redData[500];//set array to max fifo size -uint32_t irData[500];//set array to max fifo size - - +//declare large variables outside of main +uint32_t redData[500];//set array to max fifo size +uint32_t irData[500];//set array to max fifo size +uint32_t greenData[500];//set array to max fifo size + + int main() { Serial pc(USBTX, USBRX); // Use USB debug probe for serial link pc.baud(115200); // Baud rate = 115200 - - DigitalOut rLed(LED1, LED_OFF); // Debug LEDs - DigitalOut gLed(LED2, LED_OFF); - DigitalOut bLed(LED3, LED_OFF); - + + DigitalOut rLed(LED1, LED_OFF); // Debug LED + InterruptIn op_sensor_int(P3_2); // Config P3_2 as int. in for op_sensor_int.fall(&op_sensor_callback); // FIFO ready interrupt - - I2C i2cBus(I2C1_SDA, I2C1_SCL); // I2C bus, P3_4 = SDA, P3_5 = SCL + + I2C i2cBus(I2C1_SDA, I2C1_SCL); // I2C bus, P3_4 = SDA, P3_5 = SCL DigitalOut VLED_EN(P3_3,0); //Enable for VLEDs pmic_config(i2cBus, VLED_EN); - + MAX30101 op_sensor(i2cBus); // Create new MAX30101 on i2cBus int rc = op_sensor_config(op_sensor); // Config sensor, return 0 on success - + MAX30101::InterruptBitField_u ints; // Read interrupt status to clear - rc = op_sensor.getInterruptStatus(ints); // power on interrupt - + rc = op_sensor.getInterruptStatus(ints); // power on interrupt + uint8_t fifoData[MAX30101::MAX_FIFO_BYTES]; uint16_t idx, readBytes; int32_t opSample; uint32_t sample; - - int r=0; //counter for redData position - int ir=0; //counter for irData position - int c=0; //counter to print values - int32_t spo2 =0; - int8_t spo2Valid = 0; - int32_t heartRate = 0; - int8_t heartRateValid = 0; + uint16_t HRTemp; + uint16_t spo2Temp; - + int r=0; //counter for redData position + int ir=0; //counter for irData position + int g =0; //counter for greenData position + int c=0; //counter to print values + pc.printf("Starting Program...Please wait a few seconds while data is being collected.\r\n"); - while(1) - { - if( rc == 0 ) - { - + while(1) { + if( rc == 0 ) { + // Check if op_sensor interrupt asserted - if(op_sensorIntFlag) - { + if(op_sensorIntFlag) { //pc.printf("Entered op_sensorIntFlag check\r\n"); op_sensorIntFlag = 0; // Lower interrupt flag - rc = op_sensor.getInterruptStatus(ints); // Read interrupt status - - // Check if FIFO almost full interrupt asserted - if((rc == 0) && (ints.bits.a_full)) - { - // Read FIFO - rc = op_sensor.readFIFO(MAX30101::TwoLedChannels, fifoData, readBytes); - - if(rc == 0) - { - + rc = op_sensor.getInterruptStatus(ints); // Read interrupt status + //to clear interrupt + + // Confirms proper read prior to executing + if((rc == 0)) { + // Read FIFO + rc = op_sensor.readFIFO(MAX30101::ThreeLedChannels, fifoData, readBytes); + + if(rc == 0) { + // Convert read bytes into samples - for(idx = 0, c=0; idx < readBytes; idx+=3) - { + for(idx = 0, c=0; idx < readBytes; idx+=3) { sample = (fifoData[idx]<<16) | (fifoData[idx+1]<<8) | (fifoData[idx+2]); opSample = sample; opSample&=0x03FFFF; //Mask MSB [23:18] - - if(idx%2==0) + + if(idx==0) { + + redData[r] = opSample;//saves to buff for calculations + r++; + + } else if(idx==3) { + + irData[ir] = opSample; //saves to buff for calculations + ir++; + + } else { + + greenData[ir] = opSample; //saves to buff for calculations + g++; + + } + + } + + + if(r>=500 & ir>=500 & g>500)//checks to make sure there are 500 + //samples in data buffers + { + + //runs the heart rate and SpO2 algorithm + for(c=0, HRTemp = 0; c<r; c++) { + + HRSpO2Func(irData[c], redData[c],greenData[c], c,sampleRate, compSpO2, + &ir_ac_comp,&red_ac_comp, &green_ac_comp, &ir_ac_mag,&red_ac_mag, + &green_ac_mag, &HRbpm2,&SpO2B,&DRdy); + if(DRdy) + { + HRTemp = HRbpm2; + spo2Temp = SpO2B; + } + + } + + //If the above algorithm returns a valid heart rate on the last sample, it is printed + if(DRdy==1) { + pc.printf("Heart Rate = %i\r\n",HRbpm2); + pc.printf("SPO2 = %i\r\n",SpO2B); + } + else if (HRTemp!=0)//if a valid heart was calculated at all, it is printed { - redData[r] = opSample;//saves to buff for calculations - r++; - + pc.printf("Heart Rate = %i\r\n",HRTemp); + pc.printf("SPO2 = %i\r\n",spo2Temp); } else { - - irData[ir] = opSample; //saves to buff for calculations - ir++; - + pc.printf("Calculation failed...waiting for more samples...\r\n"); + pc.printf("Please keep your finger on the MAX30101 sensor with minimal movement.\r\n"); } - - } - - if(r>=450 & ir>=450)//checks to make sure there are 450 - //samples in data buffers - { - - //calculate heart rate and spo2 - maxim_heart_rate_and_oxygen_saturation( irData, ir, - redData, &spo2, &spo2Valid, &heartRate, &heartRateValid); - pc.printf("SPO2 = %i\r\n",spo2); - //heart rate is typically twice what it should be, thus divide by 2 - pc.printf("Heart Rate = %i\r\n",heartRate/2); - - for(c=100;c<450;c++)//dump first hundred samples - //after calculations + + //dump the first hundred samples after caluclaiton + for(c=100; c<500; c++) + { redData[c-100]=redData[c]; irData[c-100]=irData[c]; + greenData[c-100] = greenData[c]; } - r=350; - ir=350; - } + //reset counters + r=400; + ir=400; + g=400; + } } } } - - // If rc != 0, a communication error has occurred - } else { - + + // If rc != 0, a communication error has occurred + } else { + pc.printf("Something went wrong, " "check the I2C bus or power connections... \r\n"); - bLed = LED_OFF; - gLed = LED_OFF; - - while(1) - { + //bLed = LED_OFF; + //gLed = LED_OFF; + + while(1) { rLed = !rLed; - wait(0.5); - } - } - - } + wait(0.5); + } + } + + } } -bool op_sensor_config(MAX30101 &op_sensor) { - +bool op_sensor_config(MAX30101 &op_sensor) +{ + //Reset Device MAX30101::ModeConfiguration_u modeConfig; modeConfig.all = 0; modeConfig.bits.reset = 1; + modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets SPO2 Mode int32_t rc = op_sensor.setModeConfiguration(modeConfig); - - + + //enable MAX30101 interrupts MAX30101::InterruptBitField_u ints; - if(rc == 0) - { + if(rc == 0) { ints.all = 0; - ints.bits.die_temp = 1; // Enable internal die temp. interrupt ints.bits.a_full = 1; // Enable FIFO almost full interrupt + ints.bits.ppg_rdy =1; //Enables an interrupt when a new sample is ready rc = op_sensor.enableInterrupts(ints); } - + //configure FIFO MAX30101::FIFO_Configuration_u fifoConfig; - if(rc == 0) - { + if(rc == 0) { fifoConfig.all = 0; - fifoConfig.bits.fifo_a_full = 15; // Max level of 15 samples - fifoConfig.bits.sample_average = MAX30101::AveragedSamples_8;// Average 8 samples + fifoConfig.bits.fifo_a_full = 10; // Max level of 17 samples + fifoConfig.bits.sample_average = MAX30101::AveragedSamples_0;// Average 0 samples rc = op_sensor.setFIFOConfiguration(fifoConfig); } - + MAX30101::SpO2Configuration_u spo2Config; - if(rc == 0) - { - spo2Config.all = 0; // sets smallest LSB size - spo2Config.bits.spo2_sr = MAX30101::SR_3200_Hz; // SpO2 SR = 3200Hz - spo2Config.bits.led_pw = MAX30101::PW_3; // 18-bit ADC resolution + if(rc == 0) { + spo2Config.all = 0; // clears register + spo2Config.bits.spo2_adc_range = 1; //sets resolution to 4096 nAfs + spo2Config.bits.spo2_sr = MAX30101::SR_100_Hz; // SpO2 SR = 100Hz + spo2Config.bits.led_pw = MAX30101::PW_3; // 18-bit ADC resolution ~400us rc = op_sensor.setSpO2Configuration(spo2Config); } - - //Set LED drive currents - if(rc == 0) - { - // Heart Rate only, 1 LED channel, Pulse amp. = 0x1F - rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x1F); - //To include SPO2, 2 LED channel, Pulse amp. 0x1F - if(rc==0) - { - rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x1F); + + //Set time slots for LEDS + MAX30101::ModeControlReg_u multiLED; + if(rc==0) { + //sets timing for control register 1 + multiLED.bits.lo_slot=1; + multiLED.bits.hi_slot=2; + rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg1, multiLED); + if(rc==0) { + multiLED.bits.lo_slot=3; + multiLED.bits.hi_slot=0; + rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg2, multiLED); } } - + + //Set LED drive currents + if(rc == 0) { + // Heart Rate only, 1 LED channel, Pulse amp. = ~7mA + rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x24); + //To include SPO2, 2 LED channel, Pulse amp. ~7mA + if(rc==0) { + rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x24); + } + if(rc==0) { + rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED3_PA, 0x24); + } + + } + //Set operating mode modeConfig.all = 0; - if(rc == 0) - { - modeConfig.bits.mode = MAX30101::SpO2Mode; // Sets SPO2 Mode + if(rc == 0) { + modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets multiLED mode rc = op_sensor.setModeConfiguration(modeConfig); - } - - + } + + return rc; } void pmic_config(I2C & i2c_bus, DigitalOut & pmic_en) { - + const uint8_t PMIC_ADRS = 0x54; const uint8_t BBB_EXTRA_ADRS = 0x1C; const uint8_t BOOST_VOLTAGE = 0x05; - - char data_buff[] = {BBB_EXTRA_ADRS, 0x40}; //BBBExtra register address - //and data to enable passive - //pull down. + + char data_buff[] = {BBB_EXTRA_ADRS, 0x40}; //BBBExtra register address + //and data to enable passive + //pull down. i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register - + data_buff[0] = BOOST_VOLTAGE; - data_buff[1] = 0x14; //Boost voltage configuration - //register followed by data - //to set voltage to 4.5V - pmic_en = 0; //disables VLED + data_buff[1] = 0x08; //Boost voltage configuration + //register followed by data + //to set voltage to 4.5V 1f + pmic_en = 0; //disables VLED 08 i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register - pmic_en = 1; //enables VLED + pmic_en = 1; //enables VLED +} + + + +// +// Heart Rate/SpO2 Monitor function takes sample input 'dinIR' and dinRed. +// Other inputs: +// ns -> Sample Counter, increments with each sample input. +// SampRate -> Input data real-time sample rate. +// dinLShft -> Number of left shifts for data to be 16 bit wide. +// compSpO2 -> If '1' compute SpO2 value,else compute HR only. + +// +// Outputs: +// ir_ac_comp -> AC component of the IR signal. +// red_ac_comp -> AC component of the Red signal. +// ir_ac_mag -> Peak to Peak magnitude of the IR signal. +// red_ac_mag -> Peak to Peak magnitude of the Red signal. +// HRbpm -> Heart Rate in beats per minute. +// SpO2 -> SpO2 value as %saturation. +// DRdy -> '1' when new data is available. +// + +void HRSpO2Func(uint32_t dinIR, uint32_t dinRed, uint32_t dinGreen, uint32_t ns,uint16_t SampRate,uint16_t compSpO2, + int16_t *ir_ac_comp,int16_t *red_ac_comp, int16_t *green_ac_comp, int16_t *ir_ac_mag,int16_t *red_ac_mag, + int16_t *green_ac_mag, uint16_t *HRbpm2,uint16_t *SpO2B,uint16_t *DRdy ) +{ + static int32_t ir_avg_reg=0; + static int32_t red_avg_reg=0; + static int32_t green_avg_reg=0; + + static int16_t ir_ac_sig_cur=0; + static int16_t ir_ac_sig_pre; + static int16_t ir_ac_sig_min=0; + static int16_t ir_ac_sig_max=0; + static int16_t ir_avg_est; + + static int16_t ir_pedge=0, ir_nedge=0; + static int16_t ir_pzxic, ir_pzxip; + static int16_t ir_nzxic; + + static int16_t red_ac_sig_cur=0; + static int16_t red_ac_sig_min=0; + static int16_t red_ac_sig_max=0; + static int16_t red_avg_est; + + static int16_t green_avg_est; + static int16_t green_ac_sig_cur=0; + //static int16_t green_ac_sig_cur=0; + static int16_t green_ac_sig_pre; + static int16_t green_ac_sig_max ; + static int16_t green_ac_sig_min; + static int16_t green_mac_FIFO[5]; + int16_t meanGreenMagFIFO; + int16_t minAmpForHeartBeat ; + + uint32_t IRData,RedData, greenData , rnum,rden,rdens; + uint16_t zeros_in_HrQue =0 , posCount=0; + static uint32_t prevPeakLoc = 0 ; + static int16_t IrFIFO[100]; + static int16_t HrQue[10], lastKnownGoodHr[10]; + static int16_t SPO2Que[5]; + int16_t SPO2score[5]; + static uint16_t HrQindex=0 ,lengthOfposCountExceeding =0 ; + static uint16_t initHrQueCounter=0 , fingerOff =0; + + static int16_t HrQueSmoothing[3]; + static int16_t SPO2QueSmoothing[3]; + + int16_t k, j; + uint32_t peakLoc ; + int16_t bufferIdx1, bufferIdx2; + int16_t maxFIFO ,IdxMaxFIFO ; + int16_t HRperiod2, HRComp2 ,deltaHR; + int16_t cSpO2, SpO2; + + int16_t HrCount =0, HrSum =0 ,meanGreenMagFIFOcounter =0; + int16_t SPO2D ,meanHrQ; + int16_t dx[99] ,cumsumX[99]; + static int16_t SPO2QueCounter =0 ;//, lastDisplayedHrValue; + + int16_t validSPO2Count =0; + int16_t validSPO2Sum =0; + int16_t SPO2scoreAverage= 0; + int16_t SPO2scoreSum =0 ; +// int16_t deltaMeanLastKnownGoodHr=0,meanLastKnownGoodHr =0 ; +// int16_t counterMeanLastKnownGoodHr =0 ; + + +// clear some vars if fresh new start + if ((ns ==0) || (fingerOff >300)) { + ir_avg_reg=0; + red_avg_reg=0; + green_avg_reg=0; + + ir_ac_sig_cur=0; + ir_ac_sig_pre=0; + ir_ac_sig_min=0; + ir_ac_sig_max=0; + + ir_avg_est=0; + green_avg_est =0; + red_avg_est =0 ; + + ir_pedge=0; + ir_nedge=0; + ir_pzxic=0; + ir_pzxip =0; + ir_nzxic=0 ; + //ir_nzxip =0; + red_ac_sig_cur=0; + red_ac_sig_min=0; + red_ac_sig_max=0; + + prevPeakLoc = 0 ; + bufferIdx1=0 ; + bufferIdx2=0; + HrQindex =0; + initHrQueCounter=0; + lengthOfposCountExceeding =0 ; + fingerOff =0; + HRComp2 =0; + + for (k=0 ; k<100 ; k++) { + IrFIFO[k]= 0; + } + for (k=0 ; k<10 ; k++) { + HrQue[k]= 0; + lastKnownGoodHr[k]=0; + } + for (k=0 ; k<3 ; k++) { + HrQueSmoothing[k]= 70; + SPO2QueSmoothing[k]=97; + } + for (k=0 ; k<5 ; k++) { + SPO2Que[k] =97; + SPO2score[k] =0; + green_mac_FIFO[k] =0; + } + SPO2QueCounter =0; + *SpO2B =97; + *HRbpm2 = 0; + *DRdy =0 ; + + } + + +// Save current state + green_ac_sig_pre = green_ac_sig_cur; +// +// Process next data sample + minAmpForHeartBeat =0; + IRData = dinIR; + RedData = dinRed; + greenData = dinGreen ; + + ir_avg_est = avg_dc_est(&ir_avg_reg,IRData); + red_avg_est = avg_dc_est(&red_avg_reg,RedData); + green_avg_est = avg_dc_est(&green_avg_reg,greenData); + + lp_dfir_flt((uint16_t)(IRData - ir_avg_est),(uint16_t)(RedData - red_avg_est), + (uint16_t)(greenData - green_avg_est), &ir_ac_sig_cur,&red_ac_sig_cur,&green_ac_sig_cur); + + + *ir_ac_comp = ir_ac_sig_cur; + *red_ac_comp = red_ac_sig_cur; + *green_ac_comp = green_ac_sig_cur; + + //save to FIFO + for (k=1 ; k<100 ; k++) { + IrFIFO[100 -k]= IrFIFO[99-k]; + } + IrFIFO[0] = green_ac_sig_cur ; // invert + for (k=0 ; k<97 ; k++) + dx[k]= IrFIFO[k+2]-IrFIFO[k] ; + dx[97]= dx[96]; + dx[98]=dx[96]; + + for (k=0 ; k<99 ; k++) { + if (dx[k] > 0 ) + dx[k]=1; + else + dx[k] = 0; + } + + cumsumX[0] =0; + for (k=1; k<99 ; k++) { + if (dx[k]>0 ) + cumsumX[k]= cumsumX[k-1] + dx[k] ; + else + cumsumX[k]= 0; + } +// determine noise + // ignore less than 3 conseuctive non-zeros's + // detect # of sign change + posCount=0; + for (k=1; k<99 ; k++) { + if (cumsumX[k]>0 ) { + posCount ++ ; + } else if (cumsumX[k]==0 ) { + if (posCount<4 && k>=4) { + for ( j= k-1; j> k-posCount-1; j--) + cumsumX[j]=0 ; + } + posCount =0; + } + } + // ignore less than 3 conseuctive zeros's + + posCount=0; + for (k=1; k<99 ; k++) { + if (cumsumX[k]==0 ) { + posCount ++ ; + } else if (cumsumX[k] > 0 ) { + if (posCount<4 && k>=4) { + for ( j= k-1; j> k-posCount-1; j--) + cumsumX[j]=100 ; + } + posCount =0; + } + } + +//// detect # of sign change + posCount=0; // sign change counter + for (k=0; k<98 ; k++) { + if (cumsumX[k]==0 && cumsumX[k+1] > 0 ) { + posCount ++; + } + } + if (posCount>=4) { + lengthOfposCountExceeding ++ ; + // printf("PosCount =%i \n", posCount ); + } else + lengthOfposCountExceeding = 0 ; +// Detect IR channel positive zero crossing (rising edge) + if ((green_ac_sig_pre < 0) && (green_ac_sig_cur >= 0) && fingerOff==0 ) { + + *ir_ac_mag = ir_ac_sig_max - ir_ac_sig_min; + *red_ac_mag = red_ac_sig_max - red_ac_sig_min; + *green_ac_mag = green_ac_sig_max - green_ac_sig_min; + if ( *green_ac_mag>0 ) { + for (k=0; k<4 ; k++) + green_mac_FIFO[k]=green_mac_FIFO[k+1]; + green_mac_FIFO[4] = *green_ac_mag ; + if ( green_mac_FIFO[4] > 1000) + green_mac_FIFO[4] =1000; + } + meanGreenMagFIFO=0; + meanGreenMagFIFOcounter=0; + for (k=0; k<5 ; k++) { + if( green_mac_FIFO[k] >0) { + meanGreenMagFIFO= meanGreenMagFIFO +green_mac_FIFO[k] ; + meanGreenMagFIFOcounter++; + } + } + if (meanGreenMagFIFOcounter>=2 ) { + meanGreenMagFIFO =meanGreenMagFIFO/ meanGreenMagFIFOcounter ; + minAmpForHeartBeat= meanGreenMagFIFO /4 ; //25% of mean of past heart beat + } else + minAmpForHeartBeat = 75; + if (minAmpForHeartBeat <75) + minAmpForHeartBeat =75; + if (minAmpForHeartBeat >400) + minAmpForHeartBeat =400; + + ir_pedge = 1; + ir_nedge = 0; + ir_ac_sig_max = 0; + ir_pzxip = ir_pzxic; + ir_pzxic = ns; + bufferIdx1= ir_pzxic- ir_nzxic; + bufferIdx2 = ir_pzxic -ir_pzxip; + + + if ((*green_ac_mag)> minAmpForHeartBeat && (*green_ac_mag)< 20000 && bufferIdx1>=0 + && bufferIdx1 <100 && bufferIdx2>=0 && bufferIdx2 <100 && bufferIdx1< bufferIdx2 ) { // was <5000 + maxFIFO = -32766; + + IdxMaxFIFO = 0; + for ( j=bufferIdx1; j<= bufferIdx2; j++) { // find max peak + if (IrFIFO[j] > maxFIFO ) { + maxFIFO =IrFIFO[j]; + IdxMaxFIFO =j; + } + } + peakLoc= ir_pzxic -IdxMaxFIFO+1 ; + + if (prevPeakLoc !=0) { + HRperiod2 =( uint16_t) (peakLoc - prevPeakLoc); + if (HRperiod2>33 && HRperiod2 < 134) { + HRComp2= (6000/HRperiod2); + fingerOff =0 ; + } else + HRComp2=0 ; + } else + HRComp2 = 0 ; + + if ( initHrQueCounter<10 && HRComp2 >0 ) { + HrQue[HrQindex] =HRComp2; + HrQindex++; + initHrQueCounter ++; + if (HrQindex== 10) + HrQindex =0; + } + + if (initHrQueCounter > 7 && lengthOfposCountExceeding<=3) { + if ( HRComp2 > 0) { + + HrCount =0; + HrSum =0; + zeros_in_HrQue=0; + for (k=1 ; k<initHrQueCounter ; k++) { + if (HrQue[k] >0) { + HrSum +=HrQue[k]; + HrCount ++; + } else + zeros_in_HrQue ++; + } + meanHrQ = HrSum/HrCount ; + deltaHR= lastKnownGoodHr[0]/10; + + if ( HRComp2 > lastKnownGoodHr[0] -deltaHR && HRComp2 <lastKnownGoodHr[0] +deltaHR ) { + for (k=1 ; k<10 ; k++) { + HrQue[10 -k]= HrQue[9-k]; + } + HrQue[0] =HRComp2; + } // HR smmothing using FIFO queue - + + if (zeros_in_HrQue<=2) { + for (k=1 ; k<3 ; k++) { + HrQueSmoothing[3 -k]= HrQueSmoothing[2-k]; + } + HrQueSmoothing[0] = meanHrQ ; + HRComp2 = ( (HrQueSmoothing[0]<<2) + (HrQueSmoothing[1]<<1) + (HrQueSmoothing[2] <<1) ) >>3; + *HRbpm2 =HRComp2 ; + + for (k=1 ; k<10 ; k++) { + lastKnownGoodHr[10 -k]= lastKnownGoodHr[9-k]; + } + lastKnownGoodHr[0] =HRComp2; + } + } + + } + ///// if (initHrQueCounter > 7 && lengthOfposCountExceeding<5) + else if (initHrQueCounter < 7) { // before que is filled up, display whatever it got. + *HRbpm2 = HRComp2; + + } else { + // *HRbpm2 = 0 ; + HrCount =0; + HrSum =0; + for (k=0 ; k<10 ; k++) { + if (lastKnownGoodHr[k] >0) { + HrSum =HrSum + lastKnownGoodHr[k]; + HrCount++; + } + } + if (HrCount>0) + *HRbpm2 = HrSum/HrCount; + else + *HRbpm2 = 0; + + + + } + prevPeakLoc = peakLoc ; // save peakLoc into Static var + + + if (compSpO2) { + rnum = (ir_avg_reg >> 20)*(*red_ac_mag); + rden = (red_avg_reg >> 20)*(*ir_ac_mag); + rdens = (rden>>15); + if (rdens>0) cSpO2 = 110- (((25*rnum)/(rdens))>>15); + + if (cSpO2 >=100) SpO2 = 100; + else if (cSpO2 <= 70) SpO2 = 70; + else SpO2 = cSpO2; + + SPO2Que[SPO2QueCounter ] = SpO2; + + for (k=0 ; k<5 ; k++) { + SPO2score[k] =0; + for (j=0 ; j< 5 ; j++) + if( abs( SPO2Que[k] - SPO2Que[j] )>5) + SPO2score[k] ++; + } + + + SPO2scoreSum= 0; + for (k=0 ; k<5 ; k++) + SPO2scoreSum += SPO2score[k] ; + SPO2scoreAverage= SPO2scoreSum / 5; + for (k=1 ; k<5 ; k++) + SPO2score[k] = SPO2score[k] -SPO2scoreAverage; + + validSPO2Count =0; + validSPO2Sum =0; + for (k=1 ; k<5 ; k++) { + if (SPO2score[k]<=0 ) { // add for HR to report + validSPO2Sum +=SPO2Que[k]; + validSPO2Count ++; + } + } + if ( validSPO2Count>0) + SPO2D = (validSPO2Sum /validSPO2Count)-1; + if ( SPO2D >100) + SPO2D = 100; + + SPO2QueCounter ++; + if (SPO2QueCounter ==5) SPO2QueCounter = 0; + + for (k=1 ; k<3 ; k++) { + SPO2QueSmoothing[3 -k]= SPO2QueSmoothing[2-k]; + } + SPO2QueSmoothing[0] = SPO2D; + *SpO2B = ( (SPO2QueSmoothing[0]<<2) + (SPO2QueSmoothing[1]<<1) + (SPO2QueSmoothing[2] <<1) ) >>3; + + if (*SpO2B> 100) *SpO2B = 100 ; + + } else { + SpO2 = 0; + *SpO2B = 0; + } + *DRdy = 1; + + } + } +// Detect IR channel negative zero crossing (falling edge) + if ((green_ac_sig_pre > 0) && (green_ac_sig_cur <= 0)) { + ir_pedge = 0; + ir_nedge = 1; + ir_ac_sig_min = 0; + ir_nzxic = ns; + } +// Find Maximum IR & Red values in positive cycle + if (ir_pedge && (green_ac_sig_cur > green_ac_sig_pre)) { + ir_ac_sig_max = ir_ac_sig_cur; + red_ac_sig_max = red_ac_sig_cur; + green_ac_sig_max = green_ac_sig_cur; + } + +// Find minimum IR & Red values in negative cycle + if (ir_nedge && (green_ac_sig_cur < green_ac_sig_pre)) { + ir_ac_sig_min = ir_ac_sig_cur; + red_ac_sig_min = red_ac_sig_cur; + green_ac_sig_min = green_ac_sig_cur; + } + if (IRData <50000 ) + // finger-off + { + fingerOff++; + *DRdy = 0; + } else + fingerOff = 0 ; + /*if (IRData <50000 && fingerOff>10 ) + fingerOff = 0; */ + if ( *SpO2B == 0 || *HRbpm2 ==0) + *DRdy = 0; + + /*if (ns > 2000 ) + { + if (abs(lastDisplayedHrValue - *HRbpm2)>5) + *HRbpm2 =lastDisplayedHrValue ; + else + lastDisplayedHrValue = *HRbpm2; + }*/ + // *DRdy = minAmpForHeartBeat; + +} + +// Average DC Estimator +uint16_t avg_dc_est(int32_t *p, uint16_t x) +{ + *p += ((((int32_t) x << 15) - *p)>>4); + return (*p >> 15); +} + +// Symmetric Dual Low Pass FIR Filter +void lp_dfir_flt(int16_t din0,int16_t din1,int16_t din2, int16_t *dout0,int16_t *dout1,int16_t *dout2) +{ + static const uint16_t FIRCoeffs[12] = {688,1283,2316,3709,5439,7431, + 9561,11666,13563,15074,16047,16384 + }; + + static int16_t cbuf0[32],cbuf1[32] , cbuf2[32]; + static int16_t offset = 0; + int32_t y0,y1, y2; + int16_t i; + + cbuf0[offset] = din0; + cbuf1[offset] = din1; + cbuf2[offset] = din2; + + y0 = mul16(FIRCoeffs[11], cbuf0[(offset - 11)&0x1F]); + y1 = mul16(FIRCoeffs[11], cbuf1[(offset - 11)&0x1F]); + y2 = mul16(FIRCoeffs[11], cbuf2[(offset - 11)&0x1F]); + + + for (i=0; i<11; i++) { + y0 += mul16(FIRCoeffs[i], cbuf0[(offset-i)&0x1F] + cbuf0[(offset-22+i)&0x1F]); + y1 += mul16(FIRCoeffs[i], cbuf1[(offset-i)&0x1F] + cbuf1[(offset-22+i)&0x1F]); + y2 += mul16(FIRCoeffs[i], cbuf2[(offset-i)&0x1F] + cbuf2[(offset-22+i)&0x1F]); + } + offset = (offset + 1)&0x1F; + + *dout0 = (y0>>15); + *dout1 = (y1>>15); + *dout2 = (y2>>15); +} + +// Integer multiplier +int32_t mul16(int16_t x, int16_t y) +{ + return (int32_t)(x* y); } \ No newline at end of file