gFrameCount++; // Increment the Global Frame Count
static uint16_t frameCountLocal = 0; // Local circular count
uint16_t loopIndexBuffer = 0; // Index that loops over the buffers
/* Obtain GUI-related Flags sent through the CLI */
VitalSignsDemo_GuiMonSel *pGuiMonSel;
pGuiMonSel = (VitalSignsDemo_GuiMonSel *) &(obj->cliCfg->vitalSigns_GuiMonSel);
/* Variables for Phase Unwrapping */
static float phasePrevFrame = 0; // Phase value of Previous frame (For phase unwrapping)
static float diffPhaseCorrectionCum = 0; // Phase correction *** (For phase unwrapping)
static float phaseUsedComputationPrev = 0; // Phase values used for the Previous frame
float phaseUsedComputation = 0; // Unwrapped Phase value used for computation
/* Variables for Detecting Motion Corrupted Segments */
uint16_t guiFlag_MotionDetection = obj->cliCfg->motionDetectionParamsCfg.enabled; // GUI Flag. Set from the configuration File
uint16_t guiFlag_GainControl = obj->cliCfg->motionDetectionParamsCfg.gainControl; // GUI Flag. Set from the configuration File
uint16_t indexMotionDetection = 0; // Temporary Index
float sumEnergy = 0; // Energy in the data-segment checked for presence of large-scale motion
/* Vital Signs Waveform */
float outputFilterBreathOut = 0; // Breathing waveform after the IIR-Filter
float outputFilterHeartOut = 0; // Cardiac waveform after the IIR-Filter
/* Variables for FFT-based Spectral Estimation */
uint16_t pPeakSortOutIndex[MAX_NUM_PEAKS_SPECTRUM] = {0}; // Sorted Peaks in the Spectrum
uint16_t numPeaks_BreathSpectrum = 0; // Number of Peaks in the Breathing Spectrum
uint16_t numPeaks_heartSpectrum = 0; // Number of Peaks in the Cardiac Spectrum
uint16_t maxIndexHeartBeatSpect = 0, maxIndexBreathSpect = 0; // Indices corresponding to the max peak in the Breathing and Cardiac Spectrum
uint16_t maxIndexHeartBeatSpect_4Hz = 0; // Indices corresponding to the max peak from [1.6 - 4.0] Hz
float breathingRateEst_FFT = 0, heartRateEst_FFT = 0; // Vital Signs Estimate based on the FFT
float heartRateEst_FFT_4Hz = 0; // Vital Signs Estimate based on the FFT
/* Confidence Metric associated with the estimates */
float confidenceMetricBreath[MAX_NUM_PEAKS_SPECTRUM] = {0}; // Confidence Metric associated with each Breathing Spectrum Peak
float confidenceMetricHeart[MAX_NUM_PEAKS_SPECTRUM] = {0}; // Confidence Metric associated with each Cardiac Spectrum Peak
float confidenceMetricBreathOut = 0, confidenceMetricHeartOut = 0; // Confidence Metric associated with the estimates
float confidenceMetricHeartOut_4Hz = 0; // Confidence Metric for the 1st Heart beat Harmonic
float confidenceMetricHeartOut_xCorr = 0; // Confidence Metric for the Autocorrelation
float confidenceMetricBreathOut_xCorr = 0; // Confidence Metric for the Autocorrelation based breathing-rate estimate
float peakValueBreathSpect = 0;
/* Variables for peak-counting */
uint16_t pPeakLocsHeart[MAX_PEAKS_ALLOWED_WFM] = {0}; // Peak locations (indices) of the Cardiac Waveform
uint16_t pPeakLocsBreath[MAX_PEAKS_ALLOWED_WFM] = {0}; // Peak locations (indices) of the Breathing Waveform
uint16_t pPeakLocsValid[MAX_PEAKS_ALLOWED_WFM] = {0}; // Peak locations after only retaining the valid peaks
uint16_t numPeaksBreath = 0, numPeaksHeart = 0; // Number of peaks in the time-domain filtered waveform
float breathingRateEst_peakCount = 0, heartRateEst_peakCount = 0; // Vital Signs Estimate based on peak-Interval
float heartRateEst_peakCount_filtered = 0; // Heart-rate peak-interval based estimate after filtering
/* Exponential smoothing filter */
static float breathWfmOutUpdated = 0;
static float heartWfmOutUpdated = 0; // Updated values after exponential smoothing
float breathWfmOutPrev = 0, heartWfmOutPrev = 0; // Exponential smoothing values at time instance (t-1)
float sumEnergyBreathWfm = 0, sumEnergyHeartWfm = 0; // These values are used to make a decision if the energy in the waveform is sufficient for it to be classfied as a valid waveform
/* Variables for Auto-Correlation */
float heartRateEst_xCorr = 0; // Heart-rate estimate from the Autocorrelation Method
float breathRateEst_xCorr = 0; // Breathing-rate estimate from the Autocorrelation Method
/* For FIR Filtering */
static float pDataIn[FIR_FILTER_SIZE] = {0};
/* Variables for Extracting the Range-FFT output */
uint16_t rangeBinIndex = 0; // Range-bin Index
float rangeBinPhase = 0; // Phase of the Range-bin selected for processing
static uint16_t rangeBinIndexPhase = 0; // Index of the Range Bin for which the phase is computed
uint16_t rangeBinMax = 0; // Index of the Strongest Range-Bin
uint16_t indexTemp = 0, indexNumPeaks = 0; // Temporary Indices
int16_t temp_real = 0, temp_imag = 0; // Temporary variables storing the Real and Imaginary part of a range-bin in the Range-FFT Output
float absVal = 0; // Absolute value based on the Real and Imaginary values
float maxVal = 0; // Maximum Value of the Range-Bin in the current range-profile
if (pGuiMonSel->guiFlag_Reset == 1) // Refresh Pushbutton on the GUI Pressed
{
gFrameCount = 1;
pGuiMonSel->guiFlag_Reset = 0;
breathWfmOutUpdated = 0;
heartWfmOutUpdated = 0;
}
// tempPtr points towards the RX-Channel to process
cmplx16ReIm_t *tempPtr = obj->fftOut1D + (obj->rxAntennaProcess - 1)*obj->numRangeBins;
// tempPtr points towards rangeBinStartIndex
tempPtr += (obj->rangeBinStartIndex) ;
for (rangeBinIndex = obj->rangeBinStartIndex; rangeBinIndex <= obj->rangeBinEndIndex; rangeBinIndex++)
{
// Points towards the real part of the current range-bin i.e. rangeBinIndex
temp_real = (int16_t) tempPtr->real;
obj->pRangeProfileCplx[rangeBinIndex - obj->rangeBinStartIndex].real = temp_real;
// Points towards the imaginary part of the current range-bin i.e. rangeBinIndex
temp_imag = (int16_t) tempPtr->imag;
obj->pRangeProfileCplx[rangeBinIndex - obj->rangeBinStartIndex].imag = temp_imag;
// Move the pointer towards the next range-bin
tempPtr ++;
if (guiFlag_ClutterRemoval == 1)
{
// Clutter Removed Range Profile
float tempReal_Curr,tempImag_Curr;
float alphaClutter = 0.01;
float currVal;
// Based on the Max value Range-bin
if (currVal > maxValClutter)
{
maxValClutter = currVal;
rangeBinMaxClutter = rangeBinIndex;
}
}
else
{
// Magnitude of the current range-bin
absVal = (float) temp_real * (float) temp_real + (float) temp_imag * (float) temp_imag;
// Maximum value range-bin of the current range-profile
if (absVal > maxVal)
{
maxVal = absVal;
rangeBinMax = rangeBinIndex;
}
}
// If the Refresh button in the GUI is pressed
if (frameCountLocal == 1)
{
if (guiFlag_ClutterRemoval == 1)
{
rangeBinIndexPhase = rangeBinMaxClutter;
}
else
{
rangeBinIndexPhase = rangeBinMax;
}
}
if (rangeBinIndex == (rangeBinIndexPhase))
{
rangeBinPhase = atan2(temp_imag, temp_real);
}
} // For Loop ends
// Only perform these steps for every obj->motionDetection_BlockSize sample
if (indexMotionDetection == 0)
{
// Check if the current segment is "Noisy"
sumEnergy = 0;
for (loopIndexBuffer = 0; loopIndexBuffer < obj->motionDetection_BlockSize; loopIndexBuffer++)
{
sumEnergy += (obj->pMotionCircularBuffer[loopIndexBuffer]*obj->pMotionCircularBuffer[loopIndexBuffer]);
}
if (sumEnergy > obj->motionDetection_Thresh)
{
obj->motionDetected = 1; // Temporary variable to send to the GUI
}
else
{
obj->motionDetected = 0; // Temporary variable to send to the GUI
}
// If NO motion detected in the current segment
if ( obj->motionDetected == 0)
{
uint16_t tempEndIndex;
// Shift the current contents of the circular Buffer
for (loopIndexBuffer = obj->motionDetection_BlockSize; loopIndexBuffer < obj->circularBufferSizeHeart; loopIndexBuffer++)
{
obj->pVitalSigns_Heart_CircularBuffer[loopIndexBuffer - obj->motionDetection_BlockSize] = obj->pVitalSigns_Heart_CircularBuffer[loopIndexBuffer] ;
}
// Copy the current data segment to the end of the Circular Buffer
for (loopIndexBuffer = 0; loopIndexBuffer < obj->motionDetection_BlockSize; loopIndexBuffer++)
{
tempEndIndex = obj->circularBufferSizeHeart - obj->motionDetection_BlockSize;
obj->pVitalSigns_Heart_CircularBuffer[ tempEndIndex + loopIndexBuffer] = obj->pMotionCircularBuffer[loopIndexBuffer] ;
}
}
}
}
// If Motion DETECTED then don't UPDATE or SHIFT the values in the buffer
else // Regular processing
{
// Copies the "Cardiac Waveform" in a circular Buffer
for (loopIndexBuffer = 1; loopIndexBuffer < obj->circularBufferSizeHeart; loopIndexBuffer++)
{
obj->pVitalSigns_Heart_CircularBuffer[loopIndexBuffer - 1] = obj->pVitalSigns_Heart_CircularBuffer[loopIndexBuffer];
}
obj->pVitalSigns_Heart_CircularBuffer[obj->circularBufferSizeHeart - 1] = outputFilterHeartOut;
}
/* Spectral Estimation based on the Inter-Peaks Distance */
numPeaksHeart = find_Peaks(obj->pVitalSigns_Heart_CircularBuffer, float_type, pPeakLocsHeart,obj->pPeakValues, 0, obj->circularBufferSizeHeart - 1);
if (numPeaksHeart != 0)
{
numPeaksHeart = filterPeaksWfm(pPeakLocsHeart, pPeakLocsValid, numPeaksHeart, obj->peakDistanceHeart_Min, obj->peakDistanceHeart_Max);
}
heartRateEst_peakCount = CONVERT_HZ_BPM * ((numPeaksHeart * obj->samplingFreq_Hz) / obj->circularBufferSizeHeart);
// Input to the FFT needs to be complex
memset((uint8_t *)obj->pVitalSignsBuffer_Cplx, 0, obj->breathingWfm_Spectrum_FftSize * sizeof(cmplx32ReIm_t));
for (loopIndexBuffer = 0; loopIndexBuffer < obj->circularBufferSizeBreath; loopIndexBuffer++)
{
obj->pVitalSignsBuffer_Cplx[loopIndexBuffer].real = (int32_t) obj->scale_breathingWfm*obj->pVitalSigns_Breath_CircularBuffer[loopIndexBuffer];
obj->pVitalSignsBuffer_Cplx[loopIndexBuffer].imag=0;
}
// Input is overwritten by the DSP_fft32x32 function
DSP_fft32x32(
(int32_t *)obj->pVitalSignsSpectrumTwiddle32x32,
obj->breathingWfm_Spectrum_FftSize,
(int32_t *) obj->pVitalSignsBuffer_Cplx,
(int32_t *) obj->pVitalSigns_SpectrumCplx);
// Pre-Processing Steps for the Cardiac Waveform
// Perform Automatic Gain Control if enabled from the GUI
if (guiFlag_GainControl == 1)
{
computeAGC ( obj->pVitalSigns_Heart_CircularBuffer, obj->circularBufferSizeHeart, obj->motionDetection_BlockSize, obj->motionDetection_Thresh);
}
// Apply Window on the Cardiac Waveform prior to FFT-based spectral estimation
// and copies the Pre-processed data to pCircularBufferHeart
if (FLAG_APPLY_WINDOW)
{
uint16_t index_win;
uint16_t index_WinEnd;
// If a peak is within [1.6 2.0] Hz then check if a harmonic is present is the cardiac spectrum region [0.8 - 2.0] Hz
if (heartRateEst_FFT_4Hz < MAX_HEART_RATE_BPM)
{
for (indexTemp =1; indexTemp<numPeaks_heartSpectrum;indexTemp++)
// Remove the First Breathing Harmonic (if present in the cardiac Spectrum)
if(FLAG_HARMONIC_CANCELLATION)
{
float diffIndex = abs(maxIndexHeartBeatSpect - BREATHING_HARMONIC_NUM*maxIndexBreathSpect);
if ( diffIndex*(obj->freqIncrement_Hz)*CONVERT_HZ_BPM < BREATHING_HAMRONIC_THRESH_BPM ) // Only cancel the 2nd Breathing Harmonic
{
maxIndexHeartBeatSpect = pPeakSortOutIndex[1]; // Pick the 2nd Largest peak in the cardiac-spectrum
confidenceMetricHeartOut = confidenceMetricHeart[1];
}
}