ECG-Kit 1.0
(9,508 bytes)
/* MEX-file for writting MIT format annotation files for ECG Signals */
/* from a MATLAB structure similar to the one returned by readdannot*/
/* (c) Juan Pablo Martinez Cortes. University of Zaragoza.
e-mail: juanpabl@tsc1.cps.unizar.es */
/*
/ INCLUDE FILES
*/
#include "mex.h"
#include <stdio.h>
#define NCODIGOS 41
#define MAXINT10 0x03FF
int codeanntyp(char letter);
int getstringfield(char** string, long i, mxChar *auxarray, long dim, int auxlength);
void mexFunction(
int nlhs,
mxArray *plhs[],
int nrhs,
const mxArray *prhs[]
)
{
/* Variable definition*/
char *filename, *fieldname[6], zero='\0', *string;
short nfields, subtypflag=0, chanflag=0, numflag=0, auxflag=0;
mxArray *mxtime, *mxanntyp, *mxsubtyp, *mxchan, *mxnum, *mxaux;
int buflen, A, II, I, nbytes, auxlength;
long dim=-1, N, M;
double *time, currtime;
long i, pos;
mxChar *anntyp;
mxChar *subtyp;
mxChar *chan, *num, *auxarray;
char **aux;
unsigned char defaultnumfield, currnumfield, defaultchanfield, currchanfield, defaultsubtyp, *defaultauxfield;
unsigned char data[2], skip[4];
FILE *fid;
/*---------------------------------------------------------------------*/
/*----------------------------------------------------------------------/
/ Check for proper number of arguments /
/----------------------------------------------------------------------*/
if (nrhs!=2)
{
mexErrMsgTxt ( "writeannot: two input arguments required" );
}
if ( nlhs > 1 )
{
mexErrMsgTxt ( "writeannot: No output arguments required" );
}
if (!mxIsChar(prhs[0]))
mexErrMsgTxt("First argument must be a string.");
else if (mxGetM(prhs[0])!=1)
mexErrMsgTxt("First argument must be a string in a row vector.");
/* Get the length of the input string. */
buflen = mxGetN(prhs[0])+1;
/* Allocate memory for input filename */
filename = mxCalloc(buflen, sizeof(char));
/* Copy argument to variable */
mxGetString(prhs[0], filename, buflen);
/* Checking annotation struct format*/
if (!mxIsStruct(prhs[1]))
mexErrMsgTxt("Second argument must be a struct.");
if ((mxGetM(prhs[1])!=1)||(mxGetN(prhs[1])!=1))
mexErrMsgTxt("Second argument must be a 1x1 struct");
if ( (nfields=mxGetNumberOfFields(prhs[1])) <2)
mexErrMsgTxt("Annotation struct must have at least two fields");
if (nfields>6)
mexErrMsgTxt("Annotation struct cannot have more than six fields");
for (i=0;i<nfields;i++)
{if (mxGetNumberOfDimensions(mxGetFieldByNumber(prhs[1],0,i))>2)
mexErrMsgTxt("Fields should have less than 3 dimensions");
if (N=mxGetN(mxGetFieldByNumber(prhs[1],0,i))!=1)
if (strcmp(mxGetFieldNameByNumber(prhs[1],i),"aux")!=0)
mexErrMsgTxt("Only aux field can have more than one column");
M = mxGetM(mxGetFieldByNumber(prhs[1],0,i));
if ((dim==-1)||(dim==M))
dim = M;
else
mexErrMsgTxt("All fields must have the same number of Columns.\n If you want to pass an empty aux field, you must create a Nx1 empty matrix. ");
}
if (dim < 1) mexErrMsgTxt ("Struct must contain at least one annotation");
mxtime = mxGetField(prhs[1],0,"time");
mxanntyp = mxGetField(prhs[1],0,"anntyp");
if ((mxtime == NULL)||(mxanntyp == NULL))
mexErrMsgTxt("Annotation struct must have at least time and anntyp fields");
mxsubtyp = mxGetField(prhs[1],0,"subtyp");
if (mxsubtyp==NULL)
mexWarnMsgTxt("subtyp field not found. Default: 0");
mxchan = mxGetField(prhs[1],0,"chan");
if (mxchan==NULL)
mexWarnMsgTxt("chan field not found. Default: 0");
mxnum = mxGetField(prhs[1],0,"num");
if (mxnum==NULL)
mexWarnMsgTxt("num field not found. Default: 0");
mxaux = mxGetField(prhs[1],0,"aux");
if (mxaux==NULL)
mexWarnMsgTxt("aux field not found. Default: No auxiliary text");
else auxlength = mxGetN(mxaux);
/* Check data types and get data from fields */
if (!mxIsNumeric(mxtime)) mexErrMsgTxt ("field \"time\" elements must be numeric");
if (!mxIsChar(mxanntyp)) mexErrMsgTxt ("field \"anntyp\" elements must be characters");
if ((mxsubtyp!=NULL) && (!mxIsChar(mxsubtyp))) mexErrMsgTxt ("field \"subtyp\" elements must be characters");
if ((mxnum!=NULL)&&(!mxIsChar(mxnum))) mexErrMsgTxt ("field \"num\" elements must be characters");
if ((mxchan!=NULL)&&(!mxIsChar(mxchan))) mexErrMsgTxt ("field \"chan\" elements must be characters");
if ((mxaux!=NULL)&&(!mxIsChar(mxaux))) mexErrMsgTxt ("field \"aux\" elements must be characters");
/* time = (double*) mxCalloc(dim, sizeof(double)); */ /* Allocates memory and initializes to zeros */
/* anntyp = (mxChar*) mxCalloc(dim, sizeof(mxChar));
subtyp = (mxChar*) mxCalloc(dim, sizeof(mxChar));
chan = (mxChar*) mxCalloc(dim, sizeof(mxChar));
num = (mxChar*) mxCalloc(dim, sizeof(mxChar));
aux = (char**) mxCalloc(dim, sizeof(char*));*/
/* Think about if these last lines are really necessary */
time = mxGetPr(mxtime);
anntyp = (mxChar*) mxGetData(mxanntyp);
if (mxsubtyp!=NULL)
subtyp = (mxChar*) mxGetData(mxsubtyp);
else subtypflag = 1;
if (mxnum!=NULL)
num = (mxChar*) mxGetData(mxnum);
else numflag=1;
if (mxchan!=NULL)
chan = (mxChar*) mxGetData(mxchan);
else chanflag=1;
aux = (char**) mxCalloc(dim, sizeof(char*));
auxflag = (mxaux==NULL);
/*-----------------------------------------------------------------------------*/
/*------------------------- Opening the file for writting ----------*/
/*-----------------------------------------------------------------------------*/
if ((fid = fopen(filename, "wb"))==NULL)
mexErrMsgTxt("Unable to open annotation file");
rewind(fid);
/***************** Variable Initialization**********************/
i=0; /* Current annotation number (index) */
pos = 0; /* Current position */
defaultnumfield ='0';
currnumfield = '0';
defaultchanfield = '0';
currchanfield = '0';
defaultsubtyp = '0';
defaultauxfield = &zero;
/**************** Writing of first annotation ************************/
for (i=0;i<dim;i++) {
if ((A = codeanntyp((char) anntyp[i]))==-1)
mexErrMsgTxt("An unknown anntyp code has been found. \n If you use new annotation codes, please revise this program");
if ((II=(long) time[i])<0) mexErrMsgTxt("A negative time value has been found");
if ((i>0)&&(II<=pos)) mexErrMsgTxt("Two annotations have been found at the same time or badly ordered annotation");
II = II - pos;
pos +=II;
if (II > MAXINT10)
/*SKIP */
{
data[1]= (char) (59<<2); data[0]= 0;
fwrite(data,sizeof(char),2,fid);
skip[0]=(char)((II&0x00ff0000)>>16); skip[1]= (char) ((II&0xff000000)>>24);
skip[2]=(char)(II&0x000000ff); skip[3]=(char)((II&0x0000ff00)>>8);
fwrite(skip, sizeof(char),4,fid);
II=0;
}
data[1]=(char) ((A<<2)+((II&0x0300)>>8)); data[0]=(char) (II&0x00ff);
fwrite(data,sizeof(char),2,fid);
if (!subtypflag)
if (subtyp[i]!=defaultsubtyp) {
I = subtyp[i]-defaultsubtyp;
data[1]= (char) (61<<2); data[0]= (char) I;
fwrite(data, sizeof(char),2,fid);
}
if (!numflag)
if (num[i]!=currnumfield) {
I = num[i]-defaultnumfield;
data[1]= (char) (60<<2); data[0]= (char) I;
fwrite(data, sizeof(char),2,fid);
currnumfield = num[i];
}
if (!chanflag)
if (chan[i]!=currchanfield) {
I = chan[i]-defaultchanfield;
data[1]= (char) (62<<2); data[0]= (char) I;
fwrite(data, sizeof(char),2,fid);
currchanfield = chan[i];
}
if ((!auxflag)&&(auxlength))
{
auxarray = (mxChar*) mxGetData(mxaux);
nbytes = getstringfield(&string, i, auxarray, dim, auxlength);
if (nbytes)
{
if (nbytes > MAXINT10) mexErrMsgTxt ("Too long an auxiliary field");
data[1] = (char) ((63<<2)+((nbytes&0x0300)>>8)); data[0]=(char)(nbytes&0x00ff);
fwrite(data, sizeof(char),2,fid);
if (nbytes & 1) string[nbytes++]='\0';
fwrite(string,sizeof(char),nbytes,fid);
}
mxFree(string);
}
}
/* Finish file with two zero bytes */
data[0]=0;data[1]=0;
fwrite(data,sizeof(char),2,fid);
fclose(fid); /*
/****************** Free Memory ******************/
mxFree(filename);
mxFree(aux);
}
int codeanntyp(char letter)
{const char* typecode = "NLRaVFJASEj/Q~ | sT*D\"=pB^t+u?![]en@xf()r";
short i=0, found =-1;
while ((found==-1)&&(i<NCODIGOS))
{if (typecode[i]==letter) found=++i;
i++;}
return found;
}
int getstringfield(char** string, long i, mxChar *array,long dim, int auxlength)
{int k=0;
*string = (char*) mxCalloc(auxlength+1, sizeof(char));
while ((k<auxlength)&&(array[k*dim+i]!='\0')&&(array[k*dim+i]!=' '))
{
(*string)[k] = (char) array[k*dim+i];
k++;
}
return k;
}