Hi all,
I have a query related to AFE4490 module.
I have tested the attached "PulseOxy.c" file to calculate heart rate using AFE4490 and it gives me very well values using the algorithm shown in this file.
Which i got it from 
Now my query is when i take my finger from the module and put any other object , it does the same , i mean it count the HR using reflection and calculation.
So can any body help me to find a way in which it works only upon detection of human / living being and on other objects/non living being it discard the calculation ?
Please let me know if is there any documentation or any guidances are available on the same .
Also please let me know if i can provide any more details.
Thanks & Regards,
Rutvij
/*
* Copyright (C) 2013 University of Texas at Dallas
* Embedded Systems and Signal Processing Lab
* and
* Texas Instruments Incorporated
*
* All rights reserved.
*
*
*
*
*
*
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the names of Texas Instruments Incorporated nor
* The University of Texas at Dallas nor the names of
* their contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @file PulseOx_HRM.c
* @brief This class contains the signal processing algorithms
* @details Filters incoming data from AFE4400 and calculates SpO2 and Heart rate values
* @version 1.0
* @date 2012
*/
#include "PulseOx_HRM.h"
int voltageCodeCounter = 0; //Counter for how many times DRDY is received
//Variables for heart rate measurement
int transitions[2] = {-1,-1};
static unsigned int decision_hist[4] = {0, 0, 0, 0};
static unsigned int hist_sum = 0;
static unsigned int hist_count = 0;
static unsigned int delta_hr = 0;
int heartRateCalc = 0;
long previousValue = 0;
long currentValue = 0;
//Variables for SpO2 measurement
long maxIRValue = 0;
long minIRValue = 1000000000;
long maxRedValue = 0;
long minRedValue = 1000000000;
int pulseStarted = 0; //Flag to indicate if at least 1 pulse is completed
unsigned char voltageCodeBuffer[20]; //Buffer to hold voltage code values to be sent over BLE
int bufferCounter = 0; //Counter for indexing into voltage code buffer
char filtered = 1; //Flag set to choose between filtered and unfiltered streams
unsigned int heartRate = 0;
unsigned int heartRate2 = 10;
unsigned int heartReport = 10;
unsigned int pulseOx = 0;
extern long collectRED();
extern long collectIR();
extern long collectIRMinusAMBIR();
extern bool checkStreamStatus();
extern void sendBLEBuffer(unsigned char buffer[]);
extern void sendMessage(char msgByte);
const long SCALE = 33554432;
const double SOS [3][6] = {
{1, 0, -1, 1, -1.9725464130456409, 0.97381705538051955},
{1, 0, -1, 1, -1.9950869055123981, 0.99513037435061724},
{1, 0, -1, 1, -1.9688341956177746, 0.96906741719379641}};
/**
* @brief Bandpass filter between 0.5 and 3Hz
*
* @param sample a long
*
* @return long
*/
long filter(long sample)
{
// const long b_int [3][3] = {
// {(long) (SOS [0][0] * SCALE), (long) (SOS [0][1] * SCALE), (long) (SOS [0][2] * SCALE)},
// {(long) (SOS [1][0] * SCALE), (long) (SOS [1][1] * SCALE), (long) (SOS [1][2] * SCALE)},
// {(long) (SOS [2][0] * SCALE), (long) (SOS [2][1] * SCALE), (long) (SOS [2][2] * SCALE)}};
//
// const long a_int [3][3] = {
// {(long) (SOS [0][3] * SCALE), (long) (SOS [0][4] * SCALE), (long) (SOS [0][5] * SCALE)},
// {(long) (SOS [1][3] * SCALE), (long) (SOS [1][4] * SCALE), (long) (SOS [1][5] * SCALE)},
// {(long) (SOS [2][3] * SCALE), (long) (SOS [2][4] * SCALE), (long) (SOS [2][5] * SCALE)}};
long b_int [3][3] = {0,0,0,0,0,0,0,0,0};
long a_int [3][3] = {0,0,0,0,0,0,0,0,0};
int k;
int j;
for(k=0; k<3; k++)
{
for(j=0; j<3;j++)
{
b_int[k][j] = (long) (SOS [k][j] * SCALE);
a_int[k][j] = (long) (SOS [k][j+3] * SCALE);
}
}
// long s_int [4] = {
// (long) (s[0] * SCALE), (long) (s[1] * SCALE), (long) (s[2] * SCALE), (long) (s[3] * SCALE)};
long s_int[4] = {0,0,0,0};
for(j=0;j<4;j++)
{
s_int[j] = (long) (s[j] * SCALE);
}
static long dly [STAGES][2] = {{0,0}, {0,0}, {0,0}};
long result, wn;
long mysample = sample;
long wa1, wa2, wa3;
long wb1, wb2, wb3;
int i;
for (i = 0; i < STAGES; i++)
{
//2nd-order LCCDE code
//(eqn 8)
wa1 = ((long long)mysample * s_int[i]) >> (TRUNC_BITS);
wa2 = ((long long)a_int[i][1] * dly[i][0]) >> TRUNC_BITS;
wa3 = ((long long)a_int[i][2] * dly[i][1]) >> TRUNC_BITS;
wn = wa1 - wa2 - wa3;
//(eqn 9)
wb1 = ((long long)b_int[i][0] * wn) >> TRUNC_BITS;
wb2 = ((long long)b_int[i][1] * dly[i][0]) >> TRUNC_BITS;
wb3 = ((long long)b_int[i][2] * dly[i][1]) >> TRUNC_BITS;
result = wb1 + wb2 + wb3;
//Update filter buffers for stage i
dly[i][1] = dly[i][0];
dly[i][0] = wn;
mysample = result; //in case we have to loop again
}
return (long)result;
}
/**
* @brief Calculate heart rate given start and end of pulse
*
* @param start an int
*
* @param end an int
*
* @return unsigned int
*/
unsigned int calcHeartRate(int start, int end)
{
int pulseLength = 0;
if(start > end) end += 3000; //In case end index of pulse wrapped around 6 -second window
pulseLength = end - start; //Calculate length of pulse based on start and end indices
//Check if this is reasonable pulse length
if((pulseLength >= sampleRate/4) && (pulseLength < sampleRate*3))
{
double tempHeartRate = (60.00 * sampleRate)/ pulseLength;
tempHeartRate *= 100; //Multiply by 100 to maintain 2 decimal points when casting to integer
int heartRate = (int)tempHeartRate; //Cast down to integer to send over BLE
return heartRate;
}
return 0; //Return 0 for invalid pulse length
}
/**
* @brief Calculate SpO2 given max and min Red and IR values
*
* @param maxIR a long
*
* @param minIR a long
*
* @param maxRed a long
*
* @param minRed a long
*
* @return unsigned int
*/
unsigned int calcPulseOx(long maxIR, long minIR, long maxRed, long minRed)
{
double irDC = minIR;
double irAC = maxIR - minIR;
double redDC = minRed;
double redAC = maxRed - minRed;
double ratio = (redAC/redDC)/(irAC/irDC);
spO2 = 110 - 25*ratio;
double tempSpO2 = 100*spO2; //Multiply by 100 to maintain 2 decimal points when casting down to integer
int pulseOx = (int)tempSpO2; //Cast to int to transfer over BLE
return pulseOx;
}
/**
* @brief Sends a formatted pulseOx message over BLE
*
* @param pulseOx an unsigned int
*
* @return None
*/
void sendPulseOx(unsigned int pulseOx)
{
unsigned char pulseOxHigh = pulseOx >> 8;
unsigned char pulseOxLow = pulseOx;
unsigned char buffer[7];
buffer[0] = 0x0C; //Start byte
buffer[1] = 0x35; //Message length
buffer[2] = deviceCode; //Device code
buffer[3] = 0x43; //PulseOx opcode
buffer[4] = pulseOxHigh; //PulseOx high byte
buffer[5] = pulseOxLow; //PulseOx low byte
buffer[6] = 0x0D; //End byte
int i;
for(i=0; i<7; i++)
{
sendMessage(buffer[i]);
__delay_cycles(1600);
}
}
/**
* @brief Sends a formatted heart rate message over BLE
*
* @param heartRate an unsigned int
*
* @return None
*/
void sendHeartRate(unsigned int heartRate)
{
unsigned char heartRateHigh = heartRate >> 8;
unsigned char heartRateLow = heartRate;
unsigned char buffer[7];
buffer[0] = 0x0C; //Start byte
buffer[1] = 0x35; //Message length
buffer[2] = deviceCode; //Device code
buffer[3] = 0x44; //Heart rate opcode
buffer[4] = heartRateHigh; //Heart rate high byte
buffer[5] = heartRateLow; //Heart rate low byte
buffer[6] = 0x0D; //End byte
int i;
for(i=0; i<7; i++)
{
sendMessage(buffer[i]);
__delay_cycles(1600);
}
}
/**
* @brief Processes the collected Red and IR values
*
* @param None
*
* @return None
*/
void processData()
{
voltageCodeCounter++;
//Start calculating data after skipping 2 seconds
if(voltageCodeCounter > 1000)
{
int index = voltageCodeCounter - 1001;
//Read Red and IR values from AFE4400
long irSample = collectIR();
long redSample = collectRED();
//Filter the IR value
long filtIRSample = filter(irSample);
if(checkStreamStatus()) //Send data for graphing only in streaming mode
{
//Send 1 of every 5 samples for graphing
if(voltageCodeCounter % 5 == 0)
{
if(filtered) //If filtered flag is set stream filtered data
{
//Do a signed shift to get lower 2 bytes of data
voltageCodeBuffer[bufferCounter++] = filtIRSample >> 8;
voltageCodeBuffer[bufferCounter++] = filtIRSample;
//Pack data in chunks of 20 bytes
if(bufferCounter >= 20)
{
unsigned char tempBuffer[20];
//Transfer data and clear buffer
int i;
for(i=0; i<20; i++)
{
tempBuffer[i] = voltageCodeBuffer[i];
voltageCodeBuffer[i] = 0;
}
bufferCounter = 0; //Reset index counter
sendBLEBuffer(tempBuffer); //Call BLE function to send the data
}
}
//Else stream unfiltered data
else
{
//Do a signed shift to get lower 2 bytes of data
voltageCodeBuffer[bufferCounter++] = irSample >> 9;
voltageCodeBuffer[bufferCounter++] = irSample >> 1;
if(bufferCounter >= 20)
{
unsigned char tempBuffer[20];
//Transfer data and clear buffer
int i;
for(i=0; i<20; i++)
{
tempBuffer[i] = voltageCodeBuffer[i];
voltageCodeBuffer[i] = 0;
}
bufferCounter = 0; //Reset index counter
sendBLEBuffer(tempBuffer); //Call BLE function to send the data
}
}
}
}
//Find max and min IR and Red values in the current pulse
if(pulseStarted)
{
if(irSample > maxIRValue) maxIRValue = irSample;
if(irSample < minIRValue) minIRValue = irSample;
if(redSample > maxRedValue) maxRedValue = redSample;
if(redSample < minRedValue) minRedValue = redSample;
}
previousValue = currentValue;
currentValue = filtIRSample;
//If there was a transition from negative to positive in the filtered data
if(previousValue < 0 && currentValue > 0)
{
if(transitions[0] == -1) transitions[0] = index; //If this is the first transition
else if(transitions[1] == -1) //If this is the second transition
{
transitions[1] = index;
pulseStarted = 1; //Set pulse started flag only after 2 confirmed transitions
}
else
{
//Keep indices of last two transitions to estimate length of pulse
transitions[0] = transitions[1];
transitions[1] = index;
//Call signal functions to calculate heart rate and SpO2
heartRate = calcHeartRate(transitions[0], transitions[1]);
pulseOx = calcPulseOx(maxIRValue, minIRValue, maxRedValue, minRedValue);
//Average heart rate with previous history of values
if(hist_count < 4)
{
decision_hist[hist_count] = heartRate;
hist_count++;
hist_sum = hist_sum + heartRate;
heartRate2 = heartRate;
}
else
{
if (heartRate > (hist_sum/4+300))
{
heartRate2 = decision_hist[3] + (delta_hr)*100;
if(delta_hr <3)
delta_hr++;
}
else if(heartRate < (hist_sum/4-300))
{
heartRate2 = decision_hist[3] - (300 - (delta_hr)*100);
if(delta_hr > 0)
delta_hr--;
}
else
{
heartRate2 = heartRate;
}
hist_sum = hist_sum - decision_hist[0] + heartRate2;
decision_hist[0] = decision_hist[1];
decision_hist[1] = decision_hist[2];
decision_hist[2] = decision_hist[3];
decision_hist[3] = heartRate2;
heartReport = hist_sum/4;
}
//Reset maximum and minimum defaults for comparison in next cycle
maxIRValue = 0;
minIRValue = 1000000000;
maxRedValue = 0;
minRedValue = 1000000000;
}
}
if(voltageCodeCounter >= 1500 && voltageCodeCounter%500 == 0) //Send heart rate and SpO2 values every 1 seconds
{
GPIO_toggleOutputOnPin(PACKET_LED_PORT_ADDRESS,
PACKET_LED_PORT_NUMBER,
PACKET_LED_PIN
);
if(checkStreamStatus())
{
//Send values only if they are within reasonable range
if((heartReport/100) > 40 && (heartReport/100) < 150) sendHeartRate(heartReport);//decision_hist[3]);
if(((pulseOx/100) <= 100) && deviceCode == 0x32) sendPulseOx(pulseOx);
}
else
{
//Send values only if they are within reasonable range
if((heartReport/100) > 40 && (heartReport/100) < 150) sendHeartRate(heartReport);
if(((pulseOx/100) <= 100) && deviceCode == 0x32) sendPulseOx(pulseOx);
}
if(voltageCodeCounter >= 4000)
{
voltageCodeCounter = 1000;
}
}
}
//Build a filter history for the initial few samples
else
{
filter(collectIRMinusAMBIR());
}
}