/* lib.cpp - ECG annotation library based on continuous (CWT) and fast (FWT) wavelet transforms. Copyright (C) 2006 YURIY V. CHESNOKOV This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. You may contact the author by e-mail (chesnokov.yuriy@gmail.com or chesnokov_yuriy@mail.ru) or postal mail (Unilever Centre for Molecular Sciences Informatics, University Chemical Laboratory, Cambridge University, Lensfield Road, Cambridge, CB2 1EW, UK) */ #include "stdafx.h" #include "LIB.h" //---------------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////////// ////////////CLASS SIGNAL////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// Signal::Signal(): data(0), fp(0),fpmap(0),lpMap(0), SR(0.0),Bits(0),UmV(0),Lead(0),Len(0), hh(0),mm(0),ss(0) { wcscpy(fname,L""); } Signal::~Signal() { if(vdata.size()) { for(int i=0; i<(int)vdata.size(); i++) { data = vdata[i]; delete[] data; } } } //---------------------------------------------------------------------------- long double * Signal::ReadFile(char *name) { wchar_t ustr[256]=L""; for(int i=0; i<(int)strlen(name); i++) mbtowc(ustr+i,name+i, MB_CUR_MAX); return ReadFile(ustr); } long double * Signal::ReadFile(wchar_t *name) { wcscpy(fname,name); if(!GetInfo(name)) return 0; if(dat) { if(!ReadDat()) return 0; } else { if(!ReadTxt()) //read text file { if(!ReadMit()) //read mit-bih file return 0; } } return data; } //---------------------------------------------------------------------------- bool Signal::GetInfo(wchar_t *name) { fp = CreateFileW(name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) return false; fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,sizeof(DATAHDR),0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,sizeof(DATAHDR)); phdr = (PDATAHDR)lpMap; if(!memcmp(phdr->hdr,"DATA",4)) { Len = phdr->size; SR = phdr->sr; Bits = phdr->bits; Lead = phdr->lead; UmV = phdr->umv; hh = phdr->hh; mm = phdr->mm; ss = phdr->ss; dat = true; } else dat = false; CloseFile(); return true; } //---------------------------------------------------------------------------- bool Signal::ReadDat() { short tmp; fp = CreateFileW(fname,GENERIC_WRITE|GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) return false; fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,0,0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,0); phdr = (PDATAHDR)lpMap; hdrs.push_back(*phdr); lpf = (float *)lpMap; lps = (short *)lpMap; lpu = (unsigned short *)lpMap; lpc = (char *)lpMap; Len = phdr->size; SR = phdr->sr; Bits = phdr->bits; Lead = phdr->lead; UmV = phdr->umv; lpf += sizeof(DATAHDR)/sizeof(float); lps += sizeof(DATAHDR)/sizeof(short); lpc += sizeof(DATAHDR); data = new long double[Len]; vdata.push_back(data); for(int i=0; i 0x7ff) tmp |= 0xf000; data[i] = (long double)tmp/(long double)UmV; } else { tmp = MAKEWORD(lpc[2],(lpc[1]&0xf0)>>4); if(tmp > 0x7ff) tmp |= 0xf000; data[i] = (long double)tmp/(long double)UmV; lpc += 3; } break; case 16: //16format data[i] = (long double)lps[i]/(long double)UmV; break; case 0: case 32: //32bit float data[i] = (long double)lpf[i]; break; } CloseFile(); return true; } //---------------------------------------------------------------------------- bool Signal::ReadTxt() { vector vec1; long double tmp; int res; if((in = _wfopen(fname,L"rt")) == 0) return false; for(;;) { res = fscanf(in,"%Lf",&tmp); if(res == EOF || res == 0) break; else vec1.push_back(tmp); } fclose(in); Len = vec1.size(); if(Len < 2) return false; data = new long double[Len]; for(int i=0; isize; fp = CreateFileW(fname,GENERIC_WRITE|GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if(fp==INVALID_HANDLE_VALUE || (GetFileSize(fp,0)!=(lNum*phdr->size*phdr->bits)/8)) return false; fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,0,0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,0); lps = (short *)lpMap; lpc = (char *)lpMap; for(int i=0; isize]; vdata.push_back(data); } short tmp; for(int s=0; sbits) { case 12: //212 format 12bit if((s*lNum+n)%2 == 0) { tmp = MAKEWORD(lpc[0],lpc[1]&0x0f); if(tmp > 0x7ff) tmp |= 0xf000; tmp -= phdr->bline; data[s] = (long double)tmp/(long double)phdr->umv; } else { tmp = MAKEWORD(lpc[2],(lpc[1]&0xf0)>>4); if(tmp > 0x7ff) tmp |= 0xf000; tmp -= phdr->bline; data[s] = (long double)tmp/(long double)phdr->umv; lpc += 3; } break; case 16: //16format data[s] = (long double)(*lps++ - phdr->bline)/(long double)phdr->umv; break; } } } GetData(0); CloseFile(); return true; } else { fclose(fh); return false; } } int Signal::GetData(int index) { if(!vdata.size()) return 0; if(index > (int)vdata.size()-1) index=(int)vdata.size()-1; else if(index < 0) index = 0; phdr = &hdrs[index]; data = vdata[index]; SR = phdr->sr; Lead = phdr->lead; UmV = phdr->umv; Bits = phdr->bits; Len = phdr->size; hh = phdr->hh; mm = phdr->mm; ss = phdr->ss; return index; } int Signal::parseHdr(FILE *fh) { char leads[18][6] = {"I","II","III","aVR","aVL","aVF","v1","v2", "v3","v4","v5","v6","MLI","MLII","MLIII","vX","vY","vZ"}; char str[10][256]; if(readline(fh, str[0]) <= 0) return false; int sNum,size; float sr; int res = sscanf(str[0],"%s %s %s %s",str[1],str[2],str[3],str[4]); if(res != 4) return 0; sNum = atoi(str[2]); sr = atof(str[3]); size = atoi(str[4]); int eNum=0; for(int i=0; ibits) { case 12: if(hdr->size%2 != 0) filesize = int((long double)(hdr->size+1)*1.5); else filesize = int((long double)(hdr->size)*1.5); break; case 16: filesize = hdr->size*2; break; case 0: case 32: filesize = hdr->size*4; break; } fp = CreateFileW(name,GENERIC_WRITE|GENERIC_READ,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) return false; fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,filesize+sizeof(DATAHDR),0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,filesize+sizeof(DATAHDR)); lpf = (float *)lpMap; lps = (short *)lpMap; lpu = (unsigned short *)lpMap; lpc = (char *)lpMap; memset(lpMap,0,filesize+sizeof(DATAHDR)); memcpy(lpMap,hdr,sizeof(DATAHDR)); lpf += sizeof(DATAHDR)/sizeof(float); lps += sizeof(DATAHDR)/sizeof(short); lpc += sizeof(DATAHDR); for(unsigned int i=0; isize; i++) { switch(hdr->bits) { case 12: //212 format 12bit tmp = int(buff[i]*(long double)hdr->umv); if(tmp > 2047) tmp = 2047; if(tmp < -2048) tmp = -2048; if(i%2 == 0) { lpc[0] = LOBYTE((short)tmp); lpc[1] = 0; lpc[1] = HIBYTE((short)tmp) & 0x0f; } else { lpc[2] = LOBYTE((short)tmp); lpc[1] |= HIBYTE((short)tmp) << 4; lpc += 3; } break; case 16: //16format tmp = int(buff[i]*(long double)hdr->umv); if(tmp > 32767) tmp = 32767; if(tmp < -32768) tmp = -32768; *lps++ = (short)tmp; break; case 0: case 32: //32bit float *lpf++ = (float)buff[i]; break; } } CloseFile(); return true; } //----------------------------------------------------------------------------- bool Signal::ToTxt(wchar_t *name, long double *buff, int size) { in = _wfopen(name,L"wt"); if(in) { for(int i=0; i= (int)wcslen(path)) { wcscpy(name,path); return; } for(i=(int)wcslen(path)-1; i>=0; i--) { if(path[i] == '.') dot = i; if(path[i] == '\\') { sl = i+1; break; } } memcpy(name,&path[sl],(dot-sl)*2); name[dot-sl]=0; } void Signal::changeext(wchar_t *path, wchar_t *ext) { for(int i=(int)wcslen(path)-1; i>0; i--) { if(path[i] == '.') { path[i] = 0; wcscat(path, ext); return; } } wcscat(path, ext); } int Signal::readline(FILE *f, char *buff) { int res=0; char *pbuff = buff; while((short)res != EOF) { res = fgetc(f); if(res == 0xD || res == 0xA) { if(pbuff == buff) continue; *pbuff = 0; return 1; } if((short)res != EOF) { *pbuff++ = (char)res; } } return (short)res; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void Signal::mSecToTime(int msec, int &h, int &m, int &s, int &ms) { ms = msec%1000; msec /= 1000; if(msec < 60) { h = 0; m = 0; s = msec; //sec to final } else { long double tmp; tmp = (long double)(msec%60) / 60; tmp *= 60; s = int(tmp); msec /= 60; if(msec < 60) { h = 0; m = msec; } else { h = msec/60; tmp = (long double)(msec%60)/60; tmp *= 60; m = int(tmp); } } } //----------------------------------------------------------------------------- void Signal::MinMax(long double *buff, int size, long double &min, long double &max) { max=buff[0]; min=buff[0]; for(int i=1; i max)max=buff[i]; if(buff[i] < min)min=buff[i]; } } //----------------------------------------------------------------------------- void Signal::CloseFile() { if(lpMap) { UnmapViewOfFile(lpMap); if(fpmap) { CloseHandle(fpmap); if(fp && fp != INVALID_HANDLE_VALUE) CloseHandle(fp); } } } /////////////////////////////////////////////////////////////////////////////// //-------MATH ROUTINES-------------------------------------------------------- long double Signal::Mean(long double *mass, int size) { long double mean = 0; for(int i=0; i 5) //skip len=1 { switch(type) { case 0: TH = MINIMAX(mass,size%window); break; case 1: TH = FIXTHRES(mass,size%window); break; case 2: TH = SURE(mass,size%window); break; } if(soft) SoftTH(mass, size%window, TH); else HardTH(mass, size%window, TH); } } void Signal::HardTH(long double *mass, int size, long double TH, long double l) { for(int i=0; i 0) { mass[i] -= TH*(1-l); } else { mass[i] += TH*(1-l); } } } } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Signal::AutoCov(long double *mass, int size) { long double *rk, mu; int t; rk = new long double[size]; mu = Mean(mass,size); for(int k=0; ktype) filesize = hdr->size * (int)ceill( (logl(hdr->fmax)+hdr->fstep-logl(hdr->fmin))/hdr->fstep ); else filesize = hdr->size * (int)ceill( (hdr->fmax+hdr->fstep-hdr->fmin)/hdr->fstep ); filesize = sizeof(float)*filesize+sizeof(CWTHDR); fp = CreateFileW(name,GENERIC_WRITE | GENERIC_READ,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) return 0; fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,filesize,0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,filesize); lpf = (float *)lpMap; memset(lpMap,0,filesize); memcpy(lpMap,hdr,sizeof(CWTHDR)); return (lpf+sizeof(CWTHDR)/sizeof(float)); } //---------------------------------------------------------------------------- float * CWT::CWTReadFile(wchar_t *name) { fp = CreateFileW(name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) return 0; fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,0,0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,0); phdr = (PCWTHDR)lpMap; lpf = (float *)lpMap; if(memcmp(phdr->hdr,"WLET",4)) return 0; fmin = phdr->fmin; fmax = phdr->fmax; fstep = phdr->fstep; Len = phdr->size; SR = phdr->sr; type = phdr->type; return (lpf+sizeof(CWTHDR)/sizeof(float)); } //---------------------------------------------------------------------------- long double CWT::HzToScale(long double f, long double sr, int index, long double w) { long double k; switch(index) { case 0: k = 0.22222 * sr; break; case 1: k = 0.15833 * sr; break; case 2: case 3: k = sr; break; case 4: k = sr * w * 0.1589; break; case 5: //Gauss k = 0.2 * sr; break; case 6: //1Gauss k = 0.16 * sr; break; case 7: //2Gauss k = 0.224 * sr; break; case 8: //3Gauss k = 0.272 * sr; break; case 9: //4Gauss k = 0.316 * sr; break; case 10: //5Gauss k = 0.354 * sr; break; case 11: //6Gauss k = 0.388 * sr; break; case 12: //7Gauss k = 0.42 * sr; break; default: k = 0; } return (k/f); } int CWT::GetFreqRange() { if(type == 0) return (int)((fmax+fstep-fmin)/fstep); else return (int)((logl(fmax)+fstep-logl(fmin))/fstep); } void CWT::ConvertName(wchar_t *name, int index, long double w) { wchar_t tmp[256]; switch(index) { case 0: wcscat(name,L"(mHat).w"); break; case 1: wcscat(name,L"(Inv).w"); break; case 2: wcscat(name,L"(Morl).w"); break; case 3: wcscat(name,L"(MPow).w"); break; case 4: wcscat(name,L"(MComp"); swprintf(tmp,L"%d",(int)w); wcscat(name,tmp); wcscat(name,L").w"); break; case 5: wcscat(name,L"(Gaussian).w"); break; case 6: wcscat(name,L"(1Gauss).w"); break; case 7: wcscat(name,L"(2Gauss).w"); break; case 8: wcscat(name,L"(3Gauss).w"); break; case 9: wcscat(name,L"(4Gauss).w"); break; case 10: wcscat(name,L"(5Gauss).w"); break; case 11: wcscat(name,L"(6Gauss).w"); break; case 12: wcscat(name,L"(7Gauss).w"); break; } } //---------------------------------------------------------------------------- void CWT::InitCWT(int sz, int index, long double w, long double sr) { cwtSize = sz; if(sr) SR = sr; w0 = w; ReW = (long double *)malloc(sizeof(long double)*(2*cwtSize-1)); ImW = (long double *)malloc(sizeof(long double)*(2*cwtSize-1)); cwtBuff = (long double *)malloc(sizeof(long double)*(cwtSize)); cwIndex = index; for(int i=0; i<2*cwtSize-1; i++) { ReW[i]=0; ImW[i]=0; } } //---------------------------------------------------------------------------- void CWT::CloseCWT() { if(ReW) { free(ReW); ReW=0; }; if(ImW) { free(ImW); ImW=0; }; if(cwtBuff) {free(cwtBuff); cwtBuff=0; }; } //---------------------------------------------------------------------------- long double *CWT::CWTrans(long double *buff, long double freq, bool mr,long double lv,long double rv) { mirror = mr; lval=lv; rval=rv; prec = false; precision=0; //0,0000001 float prsision long double t,sn,cs,scale; scale = HzToScale(freq,SR,cwIndex,w0); ///////////wavelet calculation////////////////////////////////////////////////////////////////// ///////// center = csigLen-1 in wavelet mass//////////////////////////////////////////////////// for(int i=0; i 1 && cwIndex < 4) { sn = sinl(6.28*t); cs = cosl(6.28*t); } if(cwIndex == 4) { sn = sinl(w0*t); cs = cosl(w0*t); } switch(cwIndex) { case 0: ReW[(cwtSize-1)+i] = expl(-t*t/2)*(-t*t+1); break; case 1: ReW[(cwtSize-1)+i] = t*expl(-t*t/2); break; case 2: ReW[(cwtSize-1)+i] = expl(-t*t/2)*(cs-sn); break; case 3: ReW[(cwtSize-1)+i] = expl(-t*t/2)* cs; ImW[(cwtSize-1)+i] = expl(-t*t/2)* sn; break; case 4: ReW[(cwtSize-1)+i] = expl(-t*t/2)* (cs - expl(-w0*w0/2)); ImW[(cwtSize-1)+i] = expl(-t*t/2)* (sn - expl(-w0*w0/2)); break; case 5: ReW[(cwtSize-1)+i] = expl(-t*t/2); break; case 6: ReW[(cwtSize-1)+i] = -t*expl(-t*t/2); break; case 7: ReW[(cwtSize-1)+i] = (t*t-1)*expl(-t*t/2); break; case 8: ReW[(cwtSize-1)+i] = (2*t+t-t*t*t)*expl(-t*t/2); break; case 9: ReW[(cwtSize-1)+i] = (3-6*t*t+t*t*t*t)*expl(-t*t/2); break; case 10: ReW[(cwtSize-1)+i] = (-15*t+10*t*t*t-t*t*t*t*t)*expl(-t*t/2); break; case 11: ReW[(cwtSize-1)+i] = (-15+45*t*t-15*t*t*t*t+t*t*t*t*t*t)*expl(-t*t/2); break; case 12: ReW[(cwtSize-1)+i] = (105*t-105*t*t*t+21*t*t*t*t*t-t*t*t*t*t*t*t)*expl(-t*t/2); break; } if(fabsl(ReW[(cwtSize-1)+i]) < 0.0000001) precision++; if(precision > 15) { precision = i; prec = true; break; } } if(prec == false) precision = cwtSize; for(int i=-(precision-1); i<0; i++) //negative side { t = ((long double)i)/scale; if(cwIndex > 1 && cwIndex < 4) { sn = sinl(6.28*t); cs = cosl(6.28*t); } if(cwIndex == 4) { sn = sinl(w0*t); cs = cosl(w0*t); } switch(cwIndex) { case 0: ReW[(cwtSize-1)+i] = expl(-t*t/2)*(-t*t+1); break; case 1: ReW[(cwtSize-1)+i] = t*expl(-t*t/2); break; case 2: ReW[(cwtSize-1)+i] = expl(-t*t/2)*(cs-sn); break; case 3: ReW[(cwtSize-1)+i] = expl(-t*t/2)* cs; ImW[(cwtSize-1)+i] = expl(-t*t/2)* sn; break; case 4: ReW[(cwtSize-1)+i] = expl(-t*t/2)* (cs - expl(-w0*w0/2)); ImW[(cwtSize-1)+i] = expl(-t*t/2)* (sn - expl(-w0*w0/2)); break; case 5: ReW[(cwtSize-1)+i] = expl(-t*t/2); //gauss break; case 6: ReW[(cwtSize-1)+i] = -t*expl(-t*t/2); //gauss1 break; case 7: ReW[(cwtSize-1)+i] = (t*t-1)*expl(-t*t/2); //gauss2 break; case 8: ReW[(cwtSize-1)+i] = (2*t+t-t*t*t)*expl(-t*t/2); //gauss3 break; case 9: ReW[(cwtSize-1)+i] = (3-6*t*t+t*t*t*t)*expl(-t*t/2); //gauss4 break; case 10: ReW[(cwtSize-1)+i] = (-15*t+10*t*t*t-t*t*t*t*t)*expl(-t*t/2); //gauss5 break; case 11: ReW[(cwtSize-1)+i] = (-15+45*t*t-15*t*t*t*t+t*t*t*t*t*t)*expl(-t*t/2); //gauss6 break; case 12: ReW[(cwtSize-1)+i] = (105*t-105*t*t*t+21*t*t*t*t*t-t*t*t*t*t*t*t)*expl(-t*t/2); //gauss7 break; } } ///////end wavelet calculations//////////////////////////////////////////// csig = buff; for (int x = 0; x < cwtSize; x++) cwtBuff[x] = cWave(x,scale); return cwtBuff; } //---------------------------------------------------------------------------- long double CWT::cWave(int x, long double scale) { long double res=0, Re=0, Im=0; for(int t = 0; t < cwtSize; t++) //main { if(prec==true) { if(t < x-precision)t=x-(precision-1);//continue; if(t >= precision+x)break; } Re += ReW[((cwtSize-1)-x) + t] * csig[t]; if(cwIndex == 3 || cwIndex == 4) Im += ImW[((cwtSize-1)-x) + t] * csig[t]; } ////////////////////boundaries/////////////////////////////////////////////// int p=0; for(int i=(cwtSize-precision); i<(cwtSize-1)-x; i++) // Left edge calculations { if(mirror) { Re += ReW[i]*csig[(cwtSize-1)-i-x]; //mirror } else { if(lval) Re += ReW[i]*lval; else Re += ReW[i]*csig[0]; } if(cwIndex == 3 || cwIndex == 4) //Im part for complex wavelet { if(mirror) { Im += ImW[i]*csig[(cwtSize-1)-i-x]; } else { if(lval) Im += ImW[i]*lval; else Im += ImW[i]*csig[0]; } } } int q=0; for(int i=2*cwtSize-(x+1); i= size) n -= (2 + n-size); s += tH[m+thZ] * fwtBuff[n]; } for(int m=-tgZ; m= size) n -= (2 + n-size); d += tG[m+tgZ] * fwtBuff[n]; } lo[k] = s; hi[k] = d; } for(int i=0; i= size) n -= (2 + n-size); if(2*m >= -hZ && 2*m < hL-hZ) s2k += H[(2*m)+hZ]*lo[n]; if((2*m+1) >= -hZ && (2*m+1) < hL-hZ) s2k1 += H[(2*m+1)+hZ]*lo[n]; } for(int m=-gZ; m= size) n -= (2 + n-size); if(2*m >= -gZ && 2*m < gL-gZ) s2k += G[(2*m)+gZ]*hi[n]; if((2*m+1) >= -gZ && (2*m+1) < gL-gZ) s2k1 += G[(2*m+1)+gZ]*hi[n]; } fwtBuff[2*k] = 2.0*s2k; fwtBuff[2*k+1] = 2.0*s2k1; } } void FWT::fwtSynth(int scales) { for(int j=0; jJ,hdr->size,hiNum,loNum); switch(hdr->bits) { case 12: if((hiNum+loNum)%2 != 0) filesize = (long double)((hiNum+loNum)+1)*1.5; else filesize = (long double)(hiNum+loNum)*1.5; break; case 16: filesize = (hiNum+loNum)*2; break; case 0: case 32: filesize = (hiNum+loNum)*4; break; } fp = CreateFileW(name,GENERIC_WRITE | GENERIC_READ,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) { fp = 0; return false; } fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,filesize+sizeof(FWTHDR),0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,filesize+sizeof(FWTHDR)); lpf = (float *)lpMap; lps = (short *)lpMap; lpc = (char *)lpMap; memset(lpMap,0,filesize+sizeof(FWTHDR)); memcpy(lpMap,hdr,sizeof(FWTHDR)); lpf += sizeof(FWTHDR)/sizeof(float); lps += sizeof(FWTHDR)/sizeof(short); lpc += sizeof(FWTHDR); for(int i=0; iumv ); else tmp = short( lopass[i-hiNum]*(long double)hdr->umv ); switch(hdr->bits) { case 12: if(i%2 == 0) { lpc[0] = LOBYTE(tmp); lpc[1] = 0; lpc[1] = HIBYTE(tmp) & 0x0f; } else { lpc[2] = LOBYTE(tmp); lpc[1] |= HIBYTE(tmp) << 4; lpc += 3; } break; case 16: //16format *lps++ = tmp; break; case 0: case 32: if(i < hiNum) //32bit float *lpf++ = (float)hipass[i]; else *lpf++ = (float)lopass[i-hiNum]; break; } } CloseFile(); return true; } //---------------------------------------------------------------------------- bool FWT::fwtReadFile(wchar_t *name, char *appdir) { short tmp; fp = CreateFileW(name,GENERIC_WRITE | GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if(fp == INVALID_HANDLE_VALUE) { fp = 0; return false; } fpmap = CreateFileMapping(fp,0,PAGE_READWRITE,0,0,0); lpMap = MapViewOfFile(fpmap,FILE_MAP_WRITE,0,0,0); phdr = (PFWTHDR)lpMap; lpf = (float *)lpMap; lps = (short *)lpMap; lpc = (char *)lpMap; Len = phdr->size; SR = phdr->sr; Bits = phdr->bits; Lead = phdr->lead; UmV = phdr->umv; strcpy(fname,phdr->wlet); J = phdr->J; lpf += sizeof(FWTHDR)/sizeof(float); lps += sizeof(FWTHDR)/sizeof(short); lpc += sizeof(FWTHDR); HiLoNumbs(J,Len,hiNum,loNum); sigLen = loNum+hiNum; size = loNum; fwtBuff = new long double[sigLen]; tempBuff = new long double[sigLen]; memset(tempBuff,0,sizeof(long double)*sigLen); lo = fwtBuff; hi = fwtBuff+loNum; for(int i=0; i 0x7ff) tmp |= 0xf000; } else { tmp = MAKEWORD(lpc[2],(lpc[1]&0xf0)>>4); if(tmp > 0x7ff) tmp |= 0xf000; lpc += 3; } if(i QRS; //clean QRS detected int add=0; while(pmass[add]) add+=int(0.1*sr); //skip QRS in begining while(pmass[add]==0) add++; //get 1st QRS QRS.push_back(add-1); ///////////////////////////////////////////////////////////////////////////// for(int m=add; m=len) m=len-1; add=0; //noise checking/////////////////////////////////////// if(m + int(eCycle*sr) >= len) //near end of signal { QRS.pop_back(); break; } if(chkNoise(&pmass[m],eCycle*sr)) //smpl(0.10sec)+0,20sec in noise { if(lqNum != (int)QRS.size()-1) MA.push_back(QRS[QRS.size()-1]); //push MA noise location QRS.pop_back(); lqNum = QRS.size(); //find for next possible QRS start while(chkNoise(&pmass[m],eCycle*sr)) { m += int(eCycle*sr); if(m >= len-int(eCycle*sr)) break; //end of signal } if(m >= len-int(eCycle*sr)) break; else { while(pmass[m]==0) { m++; if(m >= len) break; } if(m >= len) break; else { QRS.push_back(m-1); continue; } } } //////////////////////////////////////////////////////// while(pmass[m-add]==0.0) add++; //find back for QRS end if((m-add+1) - QRS[QRS.size()-1] > ahdr.minQRS*sr) //QRS size > 0.04 sec QRS.push_back(m-add+2); //QRS end else QRS.pop_back(); m += int(eCycle*sr); //smpl + [0,20+0,10]sec 200bpm MAX if(len-m < int(sr/2)) break; while(pmass[m]==0 && (len-m)>=int(sr/2)) //find nearest QRS m++; if(len-m 0) // 46: ? { // 1: N -1: nodata in **AUX qrsANN = new int*[2*qrsNum]; // [samps] [type] [?aux data] for(int i=0; i<2*qrsNum; i++) qrsANN[i] = new int[3]; for(int i=0; i<2*qrsNum; i++) { qrsANN[i][0] = QRS[i]; //samp if(i%2 == 0) qrsANN[i][1] = 1; //type N else { qrsANN[i][1] = 40; //type QRS) //if( (qrsANN[i][0]-qrsANN[i-1][0]) >= int(sr*0.12) || (qrsANN[i][0]-qrsANN[i-1][0]) <= int(sr*0.03) ) // qrsANN[i-1][1] = 46; //N or ? beat (0.03?-0.12secs) } qrsANN[i][2] = -1; //no aux data } return qrsANN; } else return 0; } bool Annotation::chkNoise(long double *mass, int window) { for(int i=0; i0; j--) { window = (2.0*sr)/powl(2.0,(long double)j); //2.0sec interval Denoise(hi,Jnumbs[J-j],window,0,false); //hard,MINIMAX denoise [30-...Hz] hi += Jnumbs[J-j]; } for(int i=0; i RRs; for(int n=0; n ahdr.maxbpm) //if RR's within 40-200bpm continue; if(60.0/rr2 < ahdr.minbpm || 60.0/rr2 > ahdr.maxbpm) //if RR's within 40-200bpm continue; if(60.0/rr3 < ahdr.minbpm || 60.0/rr3 > ahdr.maxbpm) //if RR's within 40-200bpm continue; if(1.15*rr2 < rr1 && 1.15*rr2 < rr3) { ann[n*2+4][1] = 46; continue; } if( fabsl(rr1-rr2)<0.3 && rr1<0.8 && rr2<0.8 && rr3>2.4*(rr1+rr2) ) { ann[n*2+4][1] = 46; continue; } if( fabsl(rr1-rr2)<0.3 && rr1<0.8 && rr2<0.8 && rr3>2.4*(rr2+rr3) ) { ann[n*2+4][1] = 46; continue; } } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //out united annotation with QRS PT//////////////////////////////////////////// // **ann [PQ,JP] pairs int ** Annotation::GetPTU(long double *mass, int len, long double sr, wchar_t *fltdir, int **ann, int qrsnum) { int size, annPos; int T1=-1,T=-1,T2=-1, Twaves=0; int P1=-1,P=-1,P2=-1, Pwaves=0; CWT cwt; vector Pwave; vector Twave; //Twave [ ( , T , ) ] long double *buff; // [ 0 , 0 , 0 ] 0 no Twave long double min,max; //min,max for gaussian1 wave, center is zero crossing long double rr; //rr interval bool sign; bool twave,pwave; int add = 0;//int(sr*0.04); //prevent imprecise QRS end detection int maNum=0; for(int n=0; n (QRS) bool maNs=false; for(int i=maNum; i<(int)MA.size(); i++) { if(MA[i]>ann[n*2+1][0] && MA[i] ahdr.maxbpm-20) //check if normal RR interval (40bpm - 190bpm) { Pwave.push_back(0); Pwave.push_back(0); Pwave.push_back(0); Twave.push_back(0); Twave.push_back(0); Twave.push_back(0); continue; } ///////////////search for TWAVE/////////////////////////////////////////////////////////// if(sr*ahdr.maxQT-(ann[n*2+1][0]-ann[n*2+0][0]) > size-add) size = size - add; else size = sr*ahdr.maxQT-(ann[n*2+1][0]-ann[n*2+0][0]) - add; //long double avg = Mean(mass+annPos+add,size); //avrg extension on boundaries //long double lvl,rvl; //lvl = mass[annPos+add]; //rvl = mass[annPos+add+size-1]; cwt.InitCWT(size,6,0,sr); //6-Gauss1 wlet buff = cwt.CWTrans(mass+annPos+add,ahdr.tFreq);//,false,lvl,rvl); //3Hz transform buff = size-2*add //cwt.ToTxt(L"debugS.txt",mass+annPos+add,size); //T wave //cwt.ToTxt(L"debugC.txt",buff,size); //T wave spectrum MinMax(buff,size,min,max); for(int i=0; iT2)swap(T1,T2); //additional constraints on [T1 T T2] duration, symmetry, QT interval twave = false; if((buff[T1-annPos-add]<0 && buff[T2-annPos-add]>0) || (buff[T1-annPos-add]>0 && buff[T2-annPos-add]<0)) twave = true; if(twave) { if((long double)(T2-T1)>=0.09*sr)// && (long double)(T2-T1)<=0.24*sr) //check for T wave duration { twave = true; //QT interval = .4*sqrt(RR) if((long double)(T2-ann[n*2+0][0])>=ahdr.minQT*sr && (long double)(T2-ann[n*2+0][0])<=ahdr.maxQT*sr) twave = true; else twave = false; } else twave = false; } if(twave) { if(buff[T1-annPos-add] > 0) sign = true; else sign = false; for(int i=T1-annPos-add; i 0) continue; } else { if(buff[i] < 0) continue; } T = i+annPos + add; break; } //check for T wave symetry////////////////////////// long double ratio; if(T2-T < T-T1) ratio = (long double)(T2-T)/(long double)(T-T1); else ratio = (long double)(T-T1)/(long double)(T2-T); //////////////////////////////////////////////////// if(ratio < 0.4) //not a T wave { Twave.push_back(0); Twave.push_back(0); Twave.push_back(0); twave = false; } else { //adjust center of T wave //smooth it with gaussian, find max ? //cwt.ToTxt(L"debugS.txt",mass+annPos+add,size); int Tcntr = findTmax(mass+T1, T2-T1); if(Tcntr != -1) { Tcntr += T1; if(abs((Tcntr-T1)-((T2-T1)/2)) (QRS) { Twave.push_back(0); Twave.push_back(0); Twave.push_back(0); } T=-1; ///////////////search for TWAVE/////////////////////////////////////////////////////////// cwt.CloseCWT(); ///////////////search for PWAVE/////////////////////////////////////////////////////////// size = ann[n*2+2][0]-ann[n*2+1][0]; //n size of (QRS) <----> (QRS) if(sr*ahdr.maxPQ < size) size = sr*ahdr.maxPQ; if(twave) { if(T2 > ann[n*2+2][0]-size - int(0.04*sr) ) // pwave wnd far from Twave at least on 0.02sec size -= T2 - (ann[n*2+2][0]-size - int(0.04*sr)); } int size23 = (ann[n*2+2][0]-ann[n*2+1][0])-size; //size -= 0.02*sr; //impresize QRS begin detection if(size <= 0.03*sr) { Pwave.push_back(0); Pwave.push_back(0); Pwave.push_back(0); continue; } //avg = Mean(mass+annPos+size23,size); //avrg extension on boundaries //lvl = mass[annPos+size23]; //rvl = mass[annPos+size23+size-1]; cwt.InitCWT(size,6,0,sr); //6-Gauss1 wlet buff = cwt.CWTrans(mass+annPos+size23,ahdr.pFreq);//,false,lvl,rvl); //9Hz transform buff = size-2/3size //cwt.ToTxt(L"debugS.txt",mass+annPos+size23,size); //cwt.ToTxt(L"debugC.txt",buff,size); MinMax(buff,size,min,max); for(int i=0; iP2)swap(P1,P2); //additional constraints on [P1 P P2] duration, symmetry, PQ interval pwave = false; if((buff[P1-annPos-size23]<0 && buff[P2-annPos-size23]>0) || (buff[P1-annPos-size23]>0 && buff[P2-annPos-size23]<0)) pwave = true; if(pwave) { if((long double)(P2-P1)>=0.03*sr && (long double)(P2-P1)<=0.15*sr) //check for P wave duration 9Hz0.03 5Hz0.05 { pwave = true; //PQ interval = [0.07 - 0.12,0.20] if((long double)(ann[n*2+2][0]-P1)>=ahdr.minPQ*sr && (long double)(ann[n*2+2][0]-P1)<=ahdr.maxPQ*sr) pwave = true; else pwave = false; } else pwave = false; } if(pwave) { if(buff[P1-annPos-size23] > 0) sign = true; else sign = false; for(int i=P1-annPos-size23; i 0) continue; } else { if(buff[i] < 0) continue; } P = i+ annPos + size23; break; } //check for T wave symetry////////////////////////// long double ratio; if(P2-P < P-P1) ratio = (long double)(P2-P)/(long double)(P-P1); else ratio = (long double)(P-P1)/(long double)(P2-P); //////////////////////////////////////////////////// if(ratio < 0.4f) //not a P wave { Pwave.push_back(0); Pwave.push_back(0); Pwave.push_back(0); } else { Pwaves++; Pwave.push_back(P1); Pwave.push_back(P); Pwave.push_back(P2); } } else { Pwave.push_back(0); Pwave.push_back(0); Pwave.push_back(0); } P1=-1;P=-1;P2=-1; ///////////////search for PWAVE/////////////////////////////////////////////////////////// cwt.CloseCWT(); } /////////////////get q,r,s peaks////////////////////////////////////////////////////////// // on a denoised signal int peaksnum=0; int Q,R,S; vector qrsPeaks; //q,r,s peaks [ q , r , s ] // [ 0, R , 0 ] zero if not defined vector qrsTypes; //[q,r,s] or [_,R,s], etc... for(int n=0; n 0mV Speak < 0mV { if(S-buff[S]) { Q = S; S = -1; size = ann[n*2+1][0]-R + 1; //including Jpnt pbuff = &buff[R]; S = finds(pbuff,size,0.05); if(S != -1) S += R; } } else //check for Q { size = R-annPos + 1; //including R peak Q = findq(pbuff,size,0.05); if(Q != -1) Q += annPos; } } //else if only S else if(S!=-1) //find small r if only S detected in rS large T lead { size = S-annPos + 1; //including S peak pbuff = &buff[annPos]; R = findr(pbuff,size,0.05); if(R != -1) R += annPos; } //else if only R else if(R!=-1) //only R find small q,s { size = R-annPos + 1; //including R peak Q = findq(pbuff,size,0.05); if(Q != -1) Q += annPos; size = ann[n*2+1][0]-R + 1; //including Jpnt pbuff = &buff[R]; S = finds(pbuff,size,0.05); if(S != -1) S += R; } //put peaks to qrsPeaks vector if(R==-1 && S==-1) //no peaks { ann[n*2][1] = 16; //ARTEFACT //remove P,T if(n != 0) { if(Pwave[3*(n-1)]) { Pwaves--; Pwave[3*(n-1)] = 0; Pwave[3*(n-1)+1] = 0; Pwave[3*(n-1)+2] = 0; } } if(n != qrsnum-1) { if(Twave[3*n]) { Twaves--; Twave[3*n] = 0; Twave[3*n+1] = 0; Twave[3*n+2] = 0; } } } if(Q != -1) { peaksnum++; qrsPeaks[n*3] = Q; if(fabsl(buff[Q]) > 0.5) qrsTypes[n*3] = 17; //'Q'; else qrsTypes[n*3] = 15; //'q'; } if(R != -1) { peaksnum++; qrsPeaks[n*3+1] = R; if(fabsl(buff[R]) > 0.5) qrsTypes[n*3+1] = 48; //'R'; else qrsTypes[n*3+1] = 47; //'r'; } if(S != -1) { peaksnum++; qrsPeaks[n*3+2] = S; if(fabsl(buff[S]) > 0.5) qrsTypes[n*3+2] = 50; //'S'; else qrsTypes[n*3+2] = 49; //'s'; } } } free(buff); /////////////////get q,r,s peaks////////////////////////////////////////////////////////// ///////////////////////// complete annotation array/////////////////////////////////////// maNum = 0; //Pwave vec size = Twave vec size annNum = Pwaves*3 + qrsnum*2+peaksnum + Twaves*3 + (int)MA.size(); //P1 P P2 [QRS] T1 T T2 noise annotation if(annNum > qrsnum) //42-(p 43-p) 24-Pwave { //44-(t 45-t) 27-Twave ANN = new int*[annNum]; // [samps] [type] [?aux data] for(int i=0; iann[qindex-1][0] && MA[m]ann[qindex-1][0]) { ANN[index][0] = MA[maNum]; //Noise ANN[index][1] = 14; ANN[index++][2] = -1; } } return ANN; } else return 0; } void Annotation::findRS(long double *mass, int len, int &R, int &S, long double err) //find RS or QR { long double tmp,min,max; MinMax(mass,len, min,max); R = -1; S = -1; if(!( max<0.0 || max==mass[0] || max==mass[len-1] || max0.0 || min==mass[0] || min==mass[len-1] || -min0.0 || min==mass[0] || min==mass[len-1] || fabsl(min-mass[0])0.0 || min==mass[0] || min==mass[len-1] || fabsl(min-mass[len-1]) 1023) { sprintf(buff,"%c%c%c%c%c%c",0,0xEC,HIWORD(LOBYTE(samps)),HIWORD(HIBYTE(samps)),LOWORD(LOBYTE(samps)),LOWORD(HIBYTE(samps))); WriteFile(fp,buff,6,&bytes,0); } else anncode = samps; anncode |= (type<<10); sprintf(buff,"%c%c",(char)LOBYTE(anncode),(char)HIBYTE(anncode)); WriteFile(fp,buff,2,&bytes,0); } sprintf(buff,"%c%c",0,0); WriteFile(fp,buff,2,&bytes,0); CloseHandle(fp); return true; } bool Annotation::SaveQTseq(wchar_t *name, int **ann, int annsize, long double sr, int len) { vector QT; int q=0,t=0; for(int i=0; i PQ; int p=len,q=0; for(int i=0; i PP; int p1,p2; for(int i=0; i *RR, vector *RRpos) { int add=-1; long double rr, r1,r2; RR->clear(); RRpos->clear(); //R peak or S peak annotation bool rrs=true; int rNum=0,sNum=0; for(int i=0; i= ahdr.minbpm && rr <= ahdr.maxbpm) { RR->push_back( rr ); //in bpm RRpos->push_back( r1 ); } } add = i; } if(RR->size()) return true; else return false; } bool Annotation::SaveRRseq(wchar_t *name, int **ann, int nums, long double sr, int len) { vector RR; int add=-1; long double rr, r1,r2; //R peak or S peak annotation//////////////////////////// bool rrs=true; int rNum=0,sNum=0; for(int i=0; i= ahdr.minbpm && rr <= ahdr.maxbpm) RR.push_back( rr ); //in bpm } add = i; } if(RR.size()) { DATAHDR hdr; memset(&hdr,0,sizeof(DATAHDR)); memcpy(hdr.hdr,"DATA",4); hdr.size = RR.size(); hdr.sr = float( (long double)RR.size() / ((long double)len/sr) ); hdr.bits = 32; hdr.umv = 1; SaveFile(name,&RR[0],&hdr); return true; } else return false; } bool Annotation::SaveRRnseq(wchar_t *name, int **ann, int nums, long double sr, int len) { vector RR; int add=-1; long double rr,r1,r2; //R peak or S peak annotation//////////////////////////// bool rrs=true; int rNum=0,sNum=0; for(int i=0; i= ahdr.minbpm && rr <= ahdr.maxbpm) RR.push_back( rr ); //in bpm add = i; } if(RR.size()) { DATAHDR hdr; memset(&hdr,0,sizeof(DATAHDR)); memcpy(hdr.hdr,"DATA",4); hdr.size = RR.size(); hdr.sr = float( (long double)RR.size() / ((long double)len/sr) ); hdr.bits = 32; hdr.umv = 1; SaveFile(name,&RR[0],&hdr); return true; } else return false; } ////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ////////////////////////CLASS DENOISE/////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ECGDenoise::ECGDenoise(): tempBuff(0) { } ECGDenoise::~ECGDenoise() { if(tempBuff) free(tempBuff); } //----------------------------------------------------------------------------- void ECGDenoise::InitDenoise(wchar_t *fltdir, long double *data, int size, long double sr, bool mirror) { wcscpy(this->fltdir, fltdir); ecgBuff = data; SR = sr; Len = size; if(tempBuff) free(tempBuff); tempBuff = (long double *)malloc(sizeof(long double)*(Len+2*SR)); // [SR add] [sig] [SR add] for(int i=0; i0; j--) { window = 3.0*SR/powl(2.0,(long double)j); Denoise(hi,Jnumbs[J-j],window); hi += Jnumbs[J-j]; } //synth///////////////////////////// fwtSynth(J); //////////////////////////////////// for(int i=0; i0; j--) { window = 3.0*SR/powl(2.0,(long double)j); Denoise(hi,Jnumbs[J-j],window); hi += Jnumbs[J-j]; } //synth///////////////////////////// fwtSynth(J); //////////////////////////////////// for(int i=0; i