/*****************************************************************************
FILE:  bdac.cpp
AUTHOR:	Patrick S. Hamilton
REVISED:	5/13/2002
  ___________________________________________________________________________

bdac.cpp: Beat Detection And Classification
Copywrite (C) 2001 Patrick S. Hamilton

This file is free software; you can redistribute it and/or modify it under
the terms of the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option) any
later version.

This software is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU Library General Public License for more
details.

You should have received a copy of the GNU Library General Public License along
with this library; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.

You may contact the author by e-mail (pat@eplimited.edu) or postal mail
(Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
MA 02143 USA).  For updates to this software, please visit our website
(http://www.eplimited.com).
  __________________________________________________________________________

bdac.cpp contains functions for handling Beat Detection And Classification.
The primary function calls a qrs detector.  When a beat is detected it waits
until a sufficient number of samples from the beat have occurred.  When the
beat is ready, BeatDetectAndClassify passes the beat and the timing
information on to the functions that actually classify the beat.

Functions in bdac.cpp require functions in the following files:
		qrsfilt.cpp
		qrsdet.cpp
		classify.cpp
		rythmchk.cpp
		noisechk.cpp
		analbeat.cpp
		match.cpp
		postclas.cpp

 __________________________________________________________________________

	Revisions:
		5/13/02:
			Encapsulated down sampling from input stream to beat template in
			the function DownSampleBeat.

			Constants related to time are derived from SAMPLE_RATE in qrsdet
         and BEAT_SAMPLE_RATE in bcac.h.

 *******************************************************************************/
#include "qrsdet.h"	// For base SAMPLE_RATE
#include "bdac.h"
#include <wfdb/wfdb.h>

#define ECG_BUFFER_LENGTH	1000	// Should be long enough for a beat
// plus extra space to accommodate
// the maximum detection delay.
#define BEAT_QUE_LENGTH	10			// Length of que for beats awaiting
// classification.  Because of
// detection delays, Multiple beats
// can occur before there is enough data
// to classify the first beat in the que.

// Internal function prototypes.

void DownSampleBeat(int *beatOut, int *beatIn) ;

// External function prototypes.

int QRSDet( int datum, int init ) ;
int NoiseCheck(int datum, int delay, int RR, int beatBegin, int beatEnd) ;
int Classify(int *newBeat,int rr, int noiseLevel, int *beatMatch, int *fidAdj, int init) ;
int GetDominantType(void) ;
int GetBeatEnd(int type) ;
int GetBeatBegin(int type) ;
int gcd(int x, int y) ;

// Global Variables

int ECGBuffer[ECG_BUFFER_LENGTH], ECGBufferIndex = 0 ;  // Circular data buffer.
int BeatBuffer[BEATLGTH] ;
int BeatQue[BEAT_QUE_LENGTH], BeatQueCount = 0 ;  // Buffer of detection delays.
int RRCount = 0 ;
int InitBeatFlag = 1 ;

/******************************************************************************
	ResetBDAC() resets static variables required for beat detection and
	classification.
 *******************************************************************************/

void ResetBDAC( void )
{
	int dummy ;
	QRSDet(0,1) ;	// Reset the qrs detector
	RRCount = 0 ;
	Classify(BeatBuffer,0,0,&dummy,&dummy,1) ;
	InitBeatFlag = 1 ;
	BeatQueCount = 0 ;	// Flush the beat que.
}

/*****************************************************************************
Syntax:
	int BeatDetectAndClassify(int ecgSample, int *beatType, *beatMatch) ;
Description:
	BeatDetectAndClassify() implements a beat detector and classifier.
	ECG samples are passed into BeatDetectAndClassify() one sample at a
	time.  BeatDetectAndClassify has been designed for a sample rate of
	200 Hz.  When a beat has been detected and classified the detection
	delay is returned and the beat classification is returned through the
	pointer *beatType.  For use in debugging, the number of the template
   that the beat was matched to is returned in via *beatMatch.
Returns
	BeatDetectAndClassify() returns 0 if no new beat has been detected and
	classified.  If a beat has been classified, BeatDetectAndClassify returns
	the number of samples since the approximate location of the R-wave.
 ****************************************************************************/
int BeatDetectAndClassify(int ecgSample, int *beatType, int *beatMatch)
{
	int detectDelay, rr=0, i=0, j=0 ;
	int noiseEst = 0, beatBegin=0, beatEnd=0 ;
	int domType ;
	int fidAdj ;
	int tempBeat[(SAMPLE_RATE/BEAT_SAMPLE_RATE)*BEATLGTH] ;

	// Store new sample in the circular buffer.

	ECGBuffer[ECGBufferIndex] = ecgSample ;
	if(++ECGBufferIndex == ECG_BUFFER_LENGTH)
		ECGBufferIndex = 0 ;

	// Increment RRInterval count.

	++RRCount ;

	// Increment detection delays for any beats in the que.

	for(i = 0; i < BeatQueCount; ++i)
		++BeatQue[i] ;

	// Run the sample through the QRS detector.

	detectDelay = QRSDet(ecgSample,0) ;
	if(detectDelay != 0)
	{
		BeatQue[BeatQueCount] = detectDelay ;
		++BeatQueCount ;
	}

	// Return if no beat is ready for classification.

	if((BeatQue[0] < (BEATLGTH-FIDMARK)*(SAMPLE_RATE/BEAT_SAMPLE_RATE))
			|| (BeatQueCount == 0))
	{
		NoiseCheck(ecgSample,0,rr, beatBegin, beatEnd) ;	// Update noise check buffer
		return 0 ;
	}

	// Otherwise classify the beat at the head of the que.

	rr = RRCount - BeatQue[0] ;	// Calculate the R-to-R interval
	detectDelay = RRCount = BeatQue[0] ;

	// Estimate low frequency noise in the beat.
	// Might want to move this into classify().

	domType = GetDominantType() ;
	if(domType == -1)
	{
		beatBegin = MS250 ;
		beatEnd = MS300 ;
	}
	else
	{
		beatBegin = (SAMPLE_RATE/BEAT_SAMPLE_RATE)*(FIDMARK-GetBeatBegin(domType)) ;
		beatEnd = (SAMPLE_RATE/BEAT_SAMPLE_RATE)*(GetBeatEnd(domType)-FIDMARK) ;
	}
	noiseEst = NoiseCheck(ecgSample,detectDelay,rr,beatBegin,beatEnd) ;

	// Copy the beat from the circular buffer to the beat buffer
	// and reduce the sample rate by averageing pairs of data
	// points.

	j = ECGBufferIndex - detectDelay - (SAMPLE_RATE/BEAT_SAMPLE_RATE)*FIDMARK ;
	if(j < 0) j += ECG_BUFFER_LENGTH ;

	for(i = 0; i < (SAMPLE_RATE/BEAT_SAMPLE_RATE)*BEATLGTH; ++i)
	{
		tempBeat[i] = ECGBuffer[j] ;
		if(++j == ECG_BUFFER_LENGTH)
			j = 0 ;
	}

	DownSampleBeat(BeatBuffer,tempBeat) ;

	// Update the QUE.

	for(i = 0; i < BeatQueCount-1; ++i)
		BeatQue[i] = BeatQue[i+1] ;
	--BeatQueCount ;


	// Skip the first beat.

	if(InitBeatFlag)
	{
		InitBeatFlag = 0 ;
		*beatType = 13 ;
		*beatMatch = 0 ;
		fidAdj = 0 ;
	}
	// Classify all other beats.
	else
	{
		*beatType = Classify(BeatBuffer,rr,noiseEst,beatMatch,&fidAdj,0) ;
		fidAdj *= SAMPLE_RATE/BEAT_SAMPLE_RATE ;
	}

	// Ignore detection if the classifier decides that this
	// was the trailing edge of a PVC.

	if(*beatType == 100)
	{
		RRCount += rr ;
		return(0) ;
	}

	// Limit the fiducial mark adjustment in case of problems with
	// beat onset and offset estimation.

	if(fidAdj > MS80)
		fidAdj = MS80 ;
	else if(fidAdj < -MS80)
		fidAdj = -MS80 ;

	return(detectDelay-fidAdj) ;
}

void DownSampleBeat(int *beatOut, int *beatIn)
{
	int i ;

	for(i = 0; i < BEATLGTH; ++i)
		beatOut[i] = (beatIn[i<<1]+beatIn[(i<<1)+1])>>1 ;
}
